fidget_core/context/
op.rs

1use crate::{
2    context::{Node, indexed::Index},
3    var::Var,
4};
5use ordered_float::OrderedFloat;
6
7/// A one-argument math operation
8#[allow(missing_docs)]
9#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
10pub enum UnaryOpcode {
11    Neg,
12    Abs,
13    Recip,
14    Sqrt,
15    Square,
16    Floor,
17    Ceil,
18    Round,
19    Sin,
20    Cos,
21    Tan,
22    Asin,
23    Acos,
24    Atan,
25    Exp,
26    Ln,
27    Not,
28}
29
30/// A two-argument math operation
31#[allow(missing_docs)]
32#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
33pub enum BinaryOpcode {
34    Add,
35    Sub,
36    Mul,
37    Div,
38    Atan,
39    Min,
40    Max,
41    Compare,
42    Mod,
43    And,
44    Or,
45}
46
47/// An operation in a math expression
48///
49/// `Op`s should be constructed by calling functions on
50/// [`Context`](crate::context::Context), e.g.
51/// [`Context::add`](crate::context::Context::add) will generate an
52/// `Op::Binary(BinaryOpcode::Add, .., ..)` node and return an opaque handle.
53///
54/// Each `Op` is tightly coupled to the [`Context`](crate::context::Context)
55/// which generated it, and will not be valid for a different `Context`.
56#[allow(missing_docs)]
57#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
58pub enum Op {
59    Input(Var),
60    Const(OrderedFloat<f64>),
61    Binary(BinaryOpcode, Node, Node),
62    Unary(UnaryOpcode, Node),
63}
64
65fn dot_color_to_rgb(s: &str) -> &'static str {
66    match s {
67        "red" => "#FF0000",
68        "green" => "#00FF00",
69        "goldenrod" => "#DAA520",
70        "dodgerblue" => "#1E90FF",
71        s => panic!("Unknown X11 color '{s}'"),
72    }
73}
74
75impl Op {
76    /// Returns the color to be used in a GraphViz drawing for this node
77    pub fn dot_node_color(&self) -> &str {
78        match self {
79            Op::Const(..) => "green",
80            Op::Input(..) => "red",
81            Op::Binary(BinaryOpcode::Min | BinaryOpcode::Max, ..) => {
82                "dodgerblue"
83            }
84            Op::Binary(..) | Op::Unary(..) => "goldenrod",
85        }
86    }
87
88    /// Returns the shape to be used in a GraphViz drawing for this node
89    pub fn dot_node_shape(&self) -> &str {
90        match self {
91            Op::Const(..) => "oval",
92            Op::Input(..) => "circle",
93            Op::Binary(..) | Op::Unary(..) => "box",
94        }
95    }
96
97    /// Iterates over children, producing 0, 1, or 2 values
98    pub fn iter_children(&self) -> impl Iterator<Item = Node> {
99        let out = match self {
100            Op::Binary(_, a, b) => [Some(*a), Some(*b)],
101            Op::Unary(_, a) => [Some(*a), None],
102            Op::Input(..) | Op::Const(..) => [None, None],
103        };
104        out.into_iter().flatten()
105    }
106
107    /// Returns a GraphViz string of edges from this node to its children
108    pub fn dot_edges(&self, i: Node) -> String {
109        let mut out = String::new();
110        for c in self.iter_children() {
111            out += &self.dot_edge(i, c, "FF");
112        }
113        out
114    }
115
116    /// Returns a single edge with user-specified transparency
117    pub fn dot_edge(&self, a: Node, b: Node, alpha: &str) -> String {
118        let color = dot_color_to_rgb(self.dot_node_color()).to_owned() + alpha;
119        format!("n{} -> n{} [color = \"{color}\"]\n", a.get(), b.get(),)
120    }
121}