1use crate::{
2 context::{Node, indexed::Index},
3 types::FloatExt,
4 var::Var,
5};
6use ordered_float::OrderedFloat;
7
8#[allow(missing_docs)]
10#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
11pub enum UnaryOpcode {
12 Neg,
13 Abs,
14 Recip,
15 Sqrt,
16 Square,
17 Floor,
18 Ceil,
19 Round,
20 Sin,
21 Cos,
22 Tan,
23 Asin,
24 Acos,
25 Atan,
26 Exp,
27 Ln,
28 Not,
29 Rand,
30}
31
32#[allow(missing_docs)]
34#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
35pub enum BinaryOpcode {
36 Add,
37 Sub,
38 Mul,
39 Div,
40 Atan,
41 Min,
42 Max,
43 Compare,
44 Mod,
45 And,
46 Or,
47 Mix,
48}
49
50impl BinaryOpcode {
51 pub fn eval(&self, a: f32, b: f32) -> f32 {
53 match self {
54 BinaryOpcode::Add => a + b,
55 BinaryOpcode::Sub => a - b,
56 BinaryOpcode::Mul => a * b,
57 BinaryOpcode::Div => a / b,
58 BinaryOpcode::Atan => a.atan2(b),
59 BinaryOpcode::Min => a.min_choice(b).0,
60 BinaryOpcode::Max => a.max_choice(b).0,
61 BinaryOpcode::Compare => a.compare(b),
62 BinaryOpcode::Mod => a.rem_euclid(b),
63 BinaryOpcode::And => a.and_choice(b).0,
64 BinaryOpcode::Or => a.or_choice(b).0,
65 BinaryOpcode::Mix => a.mix(b),
66 }
67 }
68}
69
70impl UnaryOpcode {
71 pub fn eval(&self, a: f32) -> f32 {
73 match self {
74 UnaryOpcode::Neg => -a,
75 UnaryOpcode::Abs => a.abs(),
76 UnaryOpcode::Recip => 1.0 / a,
77 UnaryOpcode::Sqrt => a.sqrt(),
78 UnaryOpcode::Square => a * a,
79 UnaryOpcode::Floor => a.floor(),
80 UnaryOpcode::Ceil => a.ceil(),
81 UnaryOpcode::Round => a.round(),
82 UnaryOpcode::Sin => a.sin(),
83 UnaryOpcode::Cos => a.cos(),
84 UnaryOpcode::Tan => a.tan(),
85 UnaryOpcode::Asin => a.asin(),
86 UnaryOpcode::Acos => a.acos(),
87 UnaryOpcode::Atan => a.atan(),
88 UnaryOpcode::Exp => a.exp(),
89 UnaryOpcode::Ln => a.ln(),
90 UnaryOpcode::Not => a.not(),
91 UnaryOpcode::Rand => a.rand(),
92 }
93 }
94}
95
96#[allow(missing_docs)]
106#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
107pub enum Op {
108 Input(Var),
109 Const(OrderedFloat<f32>),
110 Binary(BinaryOpcode, Node, Node),
111 Unary(UnaryOpcode, Node),
112}
113
114fn dot_color_to_rgb(s: &str) -> &'static str {
115 match s {
116 "red" => "#FF0000",
117 "green" => "#00FF00",
118 "goldenrod" => "#DAA520",
119 "dodgerblue" => "#1E90FF",
120 s => panic!("Unknown X11 color '{s}'"),
121 }
122}
123
124impl Op {
125 pub fn dot_node_color(&self) -> &str {
127 match self {
128 Op::Const(..) => "green",
129 Op::Input(..) => "red",
130 Op::Binary(BinaryOpcode::Min | BinaryOpcode::Max, ..) => {
131 "dodgerblue"
132 }
133 Op::Binary(..) | Op::Unary(..) => "goldenrod",
134 }
135 }
136
137 pub fn dot_node_shape(&self) -> &str {
139 match self {
140 Op::Const(..) => "oval",
141 Op::Input(..) => "circle",
142 Op::Binary(..) | Op::Unary(..) => "box",
143 }
144 }
145
146 pub fn iter_children(&self) -> impl Iterator<Item = Node> {
148 let out = match self {
149 Op::Binary(_, a, b) => [Some(*a), Some(*b)],
150 Op::Unary(_, a) => [Some(*a), None],
151 Op::Input(..) | Op::Const(..) => [None, None],
152 };
153 out.into_iter().flatten()
154 }
155
156 pub fn dot_edges(&self, i: Node) -> String {
158 let mut out = String::new();
159 for c in self.iter_children() {
160 out += &self.dot_edge(i, c, "FF");
161 }
162 out
163 }
164
165 pub fn dot_edge(&self, a: Node, b: Node, alpha: &str) -> String {
167 let color = dot_color_to_rgb(self.dot_node_color()).to_owned() + alpha;
168 format!("n{} -> n{} [color = \"{color}\"]\n", a.get(), b.get(),)
169 }
170}