1use polydat::compile::fusion::{DecomposedGraph, DecomposedWire, FusedNode};
7
8#[polydat::polydat_node(category = Interpolation)]
23fn lerp(
24 t: f64,
25 #[poly_default(0.0f64)] a: polydat::derive_support::Const<f64>,
26 #[poly_default(1.0f64)] b: polydat::derive_support::Const<f64>,
27) -> f64 {
28 *a + t * (*b - *a)
29}
30
31#[polydat::polydat_node(
47 category = Interpolation,
48 jit_constants = scale_range_jit_constants,
49)]
50fn scale_range(
51 input: u64,
52 #[poly_default(0.0f64)] min: polydat::derive_support::Const<f64>,
53 #[poly_default(1.0f64)] max: polydat::derive_support::Const<f64>,
54) -> f64 {
55 let t = input as f64 / u64::MAX as f64;
56 *min + t * (*max - *min)
57}
58
59fn scale_range_jit_constants(node: &ScaleRange) -> Vec<u64> {
63 vec![node.min.to_bits(), (node.max - node.min).to_bits()]
64}
65
66impl FusedNode for ScaleRange {
67 fn decomposed(&self) -> DecomposedGraph {
69 use crate::sampling::icd::UnitInterval;
70 let mut g = DecomposedGraph::new(1);
71 let ui = g.add_node(
72 Box::new(UnitInterval::new()),
73 vec![DecomposedWire::Input(0)],
74 );
75 let lerp = g.add_node(
76 Box::new(Lerp::new(self.min, self.max)),
77 vec![DecomposedWire::Node(ui, 0)],
78 );
79 g.set_outputs(vec![DecomposedWire::Node(lerp, 0)]);
80 g
81 }
82}
83
84#[polydat::polydat_node(category = Interpolation)]
99fn inv_lerp(
100 input: f64,
101 #[poly_default(0.0f64)] a: polydat::derive_support::Const<f64>,
102 #[poly_default(1.0f64)] b: polydat::derive_support::Const<f64>,
103) -> f64 {
104 let inv_range = 1.0 / (*b - *a);
105 let t = (input - *a) * inv_range;
106 t.clamp(0.0, 1.0)
107}
108
109#[polydat::polydat_node(category = Interpolation)]
124fn remap(
125 input: f64,
126 #[poly_default(0.0f64)] in_min: polydat::derive_support::Const<f64>,
127 #[poly_default(1.0f64)] in_max: polydat::derive_support::Const<f64>,
128 #[poly_default(0.0f64)] out_min: polydat::derive_support::Const<f64>,
129 #[poly_default(1.0f64)] out_max: polydat::derive_support::Const<f64>,
130) -> f64 {
131 let t = (input - *in_min) / (*in_max - *in_min);
132 *out_min + t * (*out_max - *out_min)
133}
134
135#[polydat::polydat_node(category = Interpolation)]
153fn quantize(input: f64, #[poly_default(1.0f64)] step: polydat::derive_support::Const<f64>) -> f64 {
154 (input / *step).round() * *step
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use polydat::ast::{PolydatNode, Value};
161
162 #[test]
163 fn lerp_endpoints() {
164 let node = Lerp::new(10.0, 20.0);
165 let mut out = [Value::None];
166 node.eval(&[Value::F64(0.0)], &mut out);
167 assert_eq!(out[0].as_f64(), 10.0);
168 node.eval(&[Value::F64(1.0)], &mut out);
169 assert_eq!(out[0].as_f64(), 20.0);
170 }
171
172 #[test]
173 fn lerp_midpoint() {
174 let node = Lerp::new(0.0, 100.0);
175 let mut out = [Value::None];
176 node.eval(&[Value::F64(0.5)], &mut out);
177 assert_eq!(out[0].as_f64(), 50.0);
178 }
179
180 #[test]
181 fn scale_range_bounds() {
182 let node = ScaleRange::new(10.0, 20.0);
183 let mut out = [Value::None];
184 node.eval(&[Value::U64(0)], &mut out);
185 assert!((out[0].as_f64() - 10.0).abs() < 0.001);
186 node.eval(&[Value::U64(u64::MAX)], &mut out);
187 assert!((out[0].as_f64() - 20.0).abs() < 0.001);
188 }
189
190 #[test]
191 fn scale_range_jit_constants_layout() {
192 let node = ScaleRange::new(10.0, 25.0);
195 let consts = node.jit_constants();
196 assert_eq!(consts.len(), 2);
197 assert_eq!(f64::from_bits(consts[0]), 10.0);
198 assert_eq!(f64::from_bits(consts[1]), 15.0); }
200
201 #[test]
202 fn inv_lerp_basic() {
203 let node = InvLerp::new(10.0, 20.0);
204 let mut out = [Value::None];
205 node.eval(&[Value::F64(10.0)], &mut out);
206 assert!((out[0].as_f64() - 0.0).abs() < 0.001);
207 node.eval(&[Value::F64(15.0)], &mut out);
208 assert!((out[0].as_f64() - 0.5).abs() < 0.001);
209 node.eval(&[Value::F64(20.0)], &mut out);
210 assert!((out[0].as_f64() - 1.0).abs() < 0.001);
211 }
212
213 #[test]
214 fn inv_lerp_clamps() {
215 let node = InvLerp::new(0.0, 100.0);
216 let mut out = [Value::None];
217 node.eval(&[Value::F64(-50.0)], &mut out);
218 assert_eq!(out[0].as_f64(), 0.0);
219 node.eval(&[Value::F64(200.0)], &mut out);
220 assert_eq!(out[0].as_f64(), 1.0);
221 }
222
223 #[test]
224 fn remap_basic() {
225 let node = Remap::new(0.0, 100.0, 0.0, 1.0);
226 let mut out = [Value::None];
227 node.eval(&[Value::F64(50.0)], &mut out);
228 assert!((out[0].as_f64() - 0.5).abs() < 0.001);
229 }
230
231 #[test]
232 fn remap_different_ranges() {
233 let node = Remap::new(32.0, 212.0, 0.0, 100.0);
235 let mut out = [Value::None];
236 node.eval(&[Value::F64(32.0)], &mut out);
237 assert!((out[0].as_f64() - 0.0).abs() < 0.001);
238 node.eval(&[Value::F64(212.0)], &mut out);
239 assert!((out[0].as_f64() - 100.0).abs() < 0.001);
240 node.eval(&[Value::F64(72.0)], &mut out);
241 assert!((out[0].as_f64() - 22.22).abs() < 0.1);
242 }
243
244 #[test]
245 fn quantize_basic() {
246 let node = Quantize::new(10.0);
247 let mut out = [Value::None];
248 node.eval(&[Value::F64(13.0)], &mut out);
249 assert_eq!(out[0].as_f64(), 10.0);
250 node.eval(&[Value::F64(17.0)], &mut out);
251 assert_eq!(out[0].as_f64(), 20.0);
252 node.eval(&[Value::F64(15.0)], &mut out);
253 assert_eq!(out[0].as_f64(), 20.0); }
255
256 #[test]
257 fn quantize_small_step() {
258 let node = Quantize::new(0.25);
259 let mut out = [Value::None];
260 node.eval(&[Value::F64(1.3)], &mut out);
261 assert!((out[0].as_f64() - 1.25).abs() < 0.001);
262 }
263}
264
265use polydat::compile::fusion::{FusionPattern, FusionRule, FusionRuleRegistration};
268
269fn unit_lerp_to_scale_range() -> FusionRule {
273 FusionRule {
274 name: "unit_lerp_to_scale_range",
275 pattern: FusionPattern::node(
276 "lerp",
277 vec![FusionPattern::node(
278 "unit_interval",
279 vec![FusionPattern::any("x")],
280 "ui_node",
281 )],
282 "lerp_node",
283 ),
284 replacement: |m| {
285 let consts = m.const_vec("lerp_node");
286 let lo = f64::from_bits(consts[0]);
287 let hi = f64::from_bits(consts[1]);
288 Box::new(ScaleRange::new(lo, hi))
289 },
290 input_bindings: &["x"],
291 }
292}
293
294polydat::inventory::submit! {
295 FusionRuleRegistration { priority: 30, build: unit_lerp_to_scale_range }
296}