1use crate::FromDynamic;
3use fidget_core::{
4 context::{Tree, TreeOp},
5 var::Var,
6};
7use rhai::{EvalAltResult, NativeCallContext};
8
9impl FromDynamic for Tree {
10 fn from_dynamic(
11 ctx: &rhai::NativeCallContext,
12 d: rhai::Dynamic,
13 _default: Option<&Tree>,
14 ) -> Result<Self, Box<EvalAltResult>> {
15 if let Some(t) = d.clone().try_cast::<Tree>() {
16 Ok(t)
17 } else if let Ok(v) = f64::from_dynamic(ctx, d.clone(), None) {
18 Ok(Tree::constant(v))
19 } else if let Ok(v) = <Vec<Tree>>::from_dynamic(ctx, d.clone(), None) {
20 Ok(fidget_shapes::Union { input: v }.into())
21 } else {
22 Err(Box::new(rhai::EvalAltResult::ErrorMismatchDataType(
23 "tree".to_string(),
24 d.type_name().to_string(),
25 ctx.position(),
26 )))
27 }
28 }
29}
30
31impl FromDynamic for Vec<Tree> {
32 fn from_dynamic(
33 ctx: &rhai::NativeCallContext,
34 d: rhai::Dynamic,
35 _default: Option<&Vec<Tree>>,
36 ) -> Result<Self, Box<EvalAltResult>> {
37 if let Ok(d) = d.clone().into_array() {
38 d.into_iter()
39 .map(|v| Tree::from_dynamic(ctx, v, None))
40 .collect::<Result<Vec<_>, _>>()
41 } else {
42 Err(Box::new(rhai::EvalAltResult::ErrorMismatchDataType(
43 "Vec<tree>".to_string(),
44 d.type_name().to_string(),
45 ctx.position(),
46 )))
47 }
48 }
49}
50
51fn register_tree(engine: &mut rhai::Engine) {
52 engine
53 .register_type_with_name::<Tree>("Tree")
54 .register_fn("to_string", |t: &mut Tree| {
55 match &**t {
56 TreeOp::Input(Var::X) => "x",
57 TreeOp::Input(Var::Y) => "y",
58 TreeOp::Input(Var::Z) => "z",
59 _ => "Tree(..)",
60 }
61 .to_owned()
62 })
63 .register_fn("remap", remap_xyz)
64 .register_fn("remap", remap_xy);
65}
66
67pub fn register(engine: &mut rhai::Engine) {
71 register_tree(engine);
72 engine.register_fn("axes", || {
73 use fidget_core::context::Tree;
74 let mut out = rhai::Map::new();
75 out.insert("x".into(), rhai::Dynamic::from(Tree::x()));
76 out.insert("y".into(), rhai::Dynamic::from(Tree::y()));
77 out.insert("z".into(), rhai::Dynamic::from(Tree::z()));
78 out
79 });
80
81 macro_rules! register_binary_fns {
82 ($op:literal, $name:ident, $engine:ident) => {
83 $engine.register_fn($op, $name::tree_dyn);
84 $engine.register_fn($op, $name::dyn_tree);
85 };
86 }
87 macro_rules! register_unary_fns {
88 ($op:literal, $name:ident, $engine:ident) => {
89 $engine.register_fn($op, $name::tree);
90 };
91 }
92
93 register_binary_fns!("+", add, engine);
94 register_binary_fns!("-", sub, engine);
95 register_binary_fns!("*", mul, engine);
96 register_binary_fns!("/", div, engine);
97 register_binary_fns!("%", modulo, engine);
98 register_binary_fns!("min", min, engine);
99 register_binary_fns!("max", max, engine);
100 register_binary_fns!("compare", compare, engine);
101 register_binary_fns!("and", and, engine);
102 register_binary_fns!("or", or, engine);
103 register_binary_fns!("atan2", atan2, engine);
104 register_unary_fns!("abs", abs, engine);
105 register_unary_fns!("sqrt", sqrt, engine);
106 register_unary_fns!("square", square, engine);
107 register_unary_fns!("sin", sin, engine);
108 register_unary_fns!("cos", cos, engine);
109 register_unary_fns!("tan", tan, engine);
110 register_unary_fns!("asin", asin, engine);
111 register_unary_fns!("acos", acos, engine);
112 register_unary_fns!("atan", atan, engine);
113 register_unary_fns!("exp", exp, engine);
114 register_unary_fns!("ln", ln, engine);
115 register_unary_fns!("not", not, engine);
116 register_unary_fns!("ceil", ceil, engine);
117 register_unary_fns!("floor", floor, engine);
118 register_unary_fns!("round", round, engine);
119 register_unary_fns!("-", neg, engine);
120
121 for op in ["==", "!=", "<", ">", "<=", ">="] {
123 engine.register_fn(op, bad_cmp_tree_dyn);
124 engine.register_fn(op, bad_cmp_dyn_tree);
125 }
126}
127
128fn remap_xyz(
129 ctx: NativeCallContext,
130 shape: rhai::Dynamic,
131 x: Tree,
132 y: Tree,
133 z: Tree,
134) -> Result<Tree, Box<EvalAltResult>> {
135 let shape = Tree::from_dynamic(&ctx, shape, None)?;
136 Ok(shape.remap_xyz(x, y, z))
137}
138
139fn remap_xy(
140 ctx: NativeCallContext,
141 shape: rhai::Dynamic,
142 x: Tree,
143 y: Tree,
144) -> Result<Tree, Box<EvalAltResult>> {
145 let shape = Tree::from_dynamic(&ctx, shape, None)?;
146 Ok(shape.remap_xyz(x, y, Tree::z()))
147}
148
149macro_rules! define_binary_fns {
150 ($name:ident $(, $op:ident)?) => {
151 mod $name {
152 use super::*;
153 use NativeCallContext;
154 $(
155 use std::ops::$op;
156 )?
157 pub fn tree_dyn(
158 ctx: NativeCallContext,
159 a: Tree,
160 b: rhai::Dynamic,
161 ) -> Result<Tree, Box<rhai::EvalAltResult>> {
162 let b = Tree::from_dynamic(&ctx, b, None)?;
163 Ok(a.$name(b))
164 }
165 pub fn dyn_tree(
166 ctx: NativeCallContext,
167 a: rhai::Dynamic,
168 b: Tree,
169 ) -> Result<Tree, Box<rhai::EvalAltResult>> {
170 let a = Tree::from_dynamic(&ctx, a, None)?;
171 Ok(a.$name(b))
172 }
173 }
174 };
175}
176
177macro_rules! define_unary_fns {
178 ($name:ident) => {
179 mod $name {
180 use super::*;
181 pub fn tree(
182 ctx: NativeCallContext,
183 a: rhai::Dynamic,
184 ) -> Result<Tree, Box<EvalAltResult>> {
185 let a = Tree::from_dynamic(&ctx, a, None)?;
186 Ok(a.$name())
187 }
188 }
189 };
190}
191
192fn bad_cmp_tree_dyn(
193 _ctx: NativeCallContext,
194 _a: Tree,
195 _b: rhai::Dynamic,
196) -> Result<Tree, Box<rhai::EvalAltResult>> {
197 let e = "cannot compare Tree types during function tracing";
198 Err(e.into())
199}
200
201fn bad_cmp_dyn_tree(
202 _ctx: NativeCallContext,
203 _a: rhai::Dynamic,
204 _b: Tree,
205) -> Result<Tree, Box<rhai::EvalAltResult>> {
206 let e = "cannot compare Tree types during function tracing";
207 Err(e.into())
208}
209
210define_binary_fns!(add, Add);
211define_binary_fns!(sub, Sub);
212define_binary_fns!(mul, Mul);
213define_binary_fns!(div, Div);
214define_binary_fns!(min);
215define_binary_fns!(max);
216define_binary_fns!(compare);
217define_binary_fns!(modulo);
218define_binary_fns!(and);
219define_binary_fns!(or);
220define_binary_fns!(atan2);
221define_unary_fns!(sqrt);
222define_unary_fns!(square);
223define_unary_fns!(neg);
224define_unary_fns!(sin);
225define_unary_fns!(cos);
226define_unary_fns!(tan);
227define_unary_fns!(asin);
228define_unary_fns!(acos);
229define_unary_fns!(atan);
230define_unary_fns!(exp);
231define_unary_fns!(ln);
232define_unary_fns!(not);
233define_unary_fns!(abs);
234define_unary_fns!(floor);
235define_unary_fns!(ceil);
236define_unary_fns!(round);
237
238#[cfg(test)]
239mod test {
240 use super::*;
241
242 #[test]
243 fn tree_build_print() {
244 let mut e = rhai::Engine::new();
245 register(&mut e);
246 assert_eq!(e.eval::<String>("to_string(axes().x)").unwrap(), "x");
247 assert_eq!(
248 e.eval::<String>("to_string(axes().x + 1)").unwrap(),
249 "Tree(..)"
250 );
251 }
252}