1use std::sync::OnceLock;
4
5use super::delegate_expression_ffi::DelegateExpressionFfi;
6use super::expression_evaluation_context::IExpressionForeignFunctionInterface;
7use super::expression_variant::ExpressionVariant;
8use crate::media::Color;
9use crate::numerics::{Matrix3x2, Matrix4x4, Quaternion, Vector2, Vector3, Vector4};
10use crate::rendering::composition::animations::ColorInterpolator;
11use crate::utilities::math_utilities::{max_f32, min_f32};
12use crate::utilities::MathUtilities;
13use crate::{RelativePoint, RelativeScalar, RelativeUnit, Vector, Vector3D};
14
15pub struct BuiltInExpressionFfi {
17 registry: DelegateExpressionFfi,
18}
19
20fn lerp(a: f32, b: f32, p: f32) -> f32 {
21 (a * (1.0 - p)) + (b * p)
22}
23
24fn lerp_f64(a: f64, b: f64, p: f64) -> f64 {
25 (a * (1.0 - p)) + (b * p)
26}
27
28fn smooth_step(edge0: f32, edge1: f32, x: f32) -> f32 {
29 let t = MathUtilities::clamp_f32((x - edge0) / (edge1 - edge0), 0.0, 1.0);
30 t * t * (3.0 - 2.0 * t)
31}
32
33fn smooth_step_f64(edge0: f64, edge1: f64, x: f64) -> f64 {
34 let t = MathUtilities::clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
35 t * t * (3.0 - 2.0 * t)
36}
37
38fn smooth_step_vector2(edge0: Vector2, edge1: Vector2, x: Vector2) -> Vector2 {
39 Vector2::new(
40 smooth_step(edge0.x, edge1.x, x.x),
41 smooth_step(edge0.y, edge1.y, x.y),
42 )
43}
44
45fn smooth_step_vector(edge0: Vector, edge1: Vector, x: Vector) -> Vector {
46 Vector::new(
47 smooth_step_f64(edge0.x, edge1.x, x.x),
48 smooth_step_f64(edge0.y, edge1.y, x.y),
49 )
50}
51
52fn smooth_step_vector3(edge0: Vector3, edge1: Vector3, x: Vector3) -> Vector3 {
53 Vector3::new(
54 smooth_step(edge0.x, edge1.x, x.x),
55 smooth_step(edge0.y, edge1.y, x.y),
56 smooth_step(edge0.z, edge1.z, x.z),
57 )
58}
59
60fn smooth_step_vector3d(edge0: Vector3D, edge1: Vector3D, x: Vector3D) -> Vector3D {
61 Vector3D::new(
62 smooth_step_f64(edge0.x, edge1.x, x.x),
63 smooth_step_f64(edge0.y, edge1.y, x.y),
64 smooth_step_f64(edge0.z, edge1.z, x.z),
65 )
66}
67
68fn smooth_step_vector4(edge0: Vector4, edge1: Vector4, x: Vector4) -> Vector4 {
69 Vector4::new(
70 smooth_step(edge0.x, edge1.x, x.x),
71 smooth_step(edge0.y, edge1.y, x.y),
72 smooth_step(edge0.z, edge1.z, x.z),
73 smooth_step(edge0.w, edge1.w, x.w),
74 )
75}
76
77#[inline]
79fn via_f64(value: f32, function: impl FnOnce(f64) -> f64) -> f32 {
80 function(value as f64) as f32
81}
82
83impl BuiltInExpressionFfi {
84 fn new() -> Self {
85 let mut r = DelegateExpressionFfi::new();
86
87 r.add1("Abs", |f: f32| f.abs());
88 r.add1("Abs", |v: Vector2| Vector2::abs(v));
89 r.add1("Abs", |v: Vector| v.abs());
90 r.add1("Abs", |v: Vector3| Vector3::abs(v));
91 r.add1("Abs", |v: Vector3D| v.abs());
92 r.add1("Abs", |v: Vector4| Vector4::abs(v));
93
94 r.add1("ACos", |f: f32| via_f64(f, f64::acos));
95 r.add1("ASin", |f: f32| via_f64(f, f64::asin));
96 r.add1("ATan", |f: f32| via_f64(f, f64::atan));
97 r.add1("Ceil", |f: f32| via_f64(f, f64::ceil));
98
99 r.add3("Clamp", |a1: f32, a2: f32, a3: f32| MathUtilities::clamp_f32(a1, a2, a3));
100 r.add3("Clamp", |a1: Vector2, a2: Vector2, a3: Vector2| Vector2::clamp(a1, a2, a3));
101 r.add3("Clamp", |a1: Vector, a2: Vector, a3: Vector| Vector::clamp(a1, a2, a3));
102 r.add3("Clamp", |a1: Vector3, a2: Vector3, a3: Vector3| Vector3::clamp(a1, a2, a3));
103 r.add3("Clamp", |a1: Vector3D, a2: Vector3D, a3: Vector3D| {
104 Vector3D::clamp(a1, a2, a3)
105 });
106 r.add3("Clamp", |a1: Vector4, a2: Vector4, a3: Vector4| Vector4::clamp(a1, a2, a3));
107
108 r.add2("Concatenate", |a1: Quaternion, a2: Quaternion| {
109 Quaternion::concatenate(a1, a2)
110 });
111 r.add1("Cos", |a: f32| via_f64(a, f64::cos));
112
113 r.add3("ColorLerp", |to: Color, from: Color, progress: f32| {
120 ColorInterpolator::lerp_rgb(to, from, progress)
121 });
122 r.add3("ColorLerpRGB", |to: Color, from: Color, progress: f32| {
123 ColorInterpolator::lerp_rgb(to, from, progress)
124 });
125 r.add4("ColorRGB", |a: f32, r: f32, g: f32, b: f32| {
126 Color::from_argb(
127 MathUtilities::clamp_f32(a, 0.0, 255.0) as u8,
128 MathUtilities::clamp_f32(r, 0.0, 255.0) as u8,
129 MathUtilities::clamp_f32(g, 0.0, 255.0) as u8,
130 MathUtilities::clamp_f32(b, 0.0, 255.0) as u8,
131 )
132 });
133
134 r.add2("Distance", |a1: Vector2, a2: Vector2| Vector2::distance(a1, a2));
135 r.add2("Distance", |a1: Vector, a2: Vector| Vector::distance(a1, a2));
136 r.add2("Distance", |a1: Vector3, a2: Vector3| Vector3::distance(a1, a2));
137 r.add2("Distance", |a1: Vector3D, a2: Vector3D| Vector3D::distance(a1, a2));
138 r.add2("Distance", |a1: Vector4, a2: Vector4| Vector4::distance(a1, a2));
139
140 r.add2("DistanceSquared", |a1: Vector2, a2: Vector2| {
141 Vector2::distance_squared(a1, a2)
142 });
143 r.add2("DistanceSquared", |a1: Vector, a2: Vector| {
144 Vector::distance_squared(a1, a2)
145 });
146 r.add2("DistanceSquared", |a1: Vector3, a2: Vector3| {
147 Vector3::distance_squared(a1, a2)
148 });
149 r.add2("DistanceSquared", |a1: Vector3D, a2: Vector3D| {
150 Vector3D::distance_squared(a1, a2)
151 });
152 r.add2("DistanceSquared", |a1: Vector4, a2: Vector4| {
153 Vector4::distance_squared(a1, a2)
154 });
155
156 r.add1("Floor", |v: f32| via_f64(v, f64::floor));
157
158 r.add1("Inverse", |v: Matrix3x2| Matrix3x2::invert_or_nan(v));
159 r.add1("Inverse", |v: Matrix4x4| Matrix4x4::invert_or_nan(v));
160
161 r.add1("Length", |a1: Vector2| a1.length());
162 r.add1("Length", |a1: Vector| a1.length());
163 r.add1("Length", |a1: Vector3| a1.length());
164 r.add1("Length", |a1: Vector3D| a1.length());
165 r.add1("Length", |a1: Vector4| a1.length());
166 r.add1("Length", |a1: Quaternion| a1.length());
167
168 r.add1("LengthSquared", |a1: Vector2| a1.length_squared());
169 r.add1("LengthSquared", |a1: Vector| a1 * a1);
170 r.add1("LengthSquared", |a1: Vector3| a1.length_squared());
171 r.add1("LengthSquared", |a1: Vector3D| Vector3D::dot(a1, a1));
172 r.add1("LengthSquared", |a1: Vector4| a1.length_squared());
173 r.add1("LengthSquared", |a1: Quaternion| a1.length_squared());
174
175 r.add3("Lerp", |a1: f32, a2: f32, a3: f32| lerp(a1, a2, a3));
176 r.add3("Lerp", |a1: Vector2, a2: Vector2, a3: f32| Vector2::lerp(a1, a2, a3));
177 r.add3("Lerp", |a1: Vector, a2: Vector, a3: f32| {
178 Vector::new(
179 lerp_f64(a1.x, a2.x, a3 as f64),
180 lerp_f64(a1.y, a2.y, a3 as f64),
181 )
182 });
183 r.add3("Lerp", |a1: Vector3, a2: Vector3, a3: f32| Vector3::lerp(a1, a2, a3));
184 r.add3("Lerp", |a1: Vector3D, a2: Vector3D, a3: f32| {
185 Vector3D::new(
186 lerp_f64(a1.x, a2.x, a3 as f64),
187 lerp_f64(a1.y, a2.y, a3 as f64),
188 lerp_f64(a1.z, a2.z, a3 as f64),
189 )
190 });
191 r.add3("Lerp", |a1: Vector4, a2: Vector4, a3: f32| Vector4::lerp(a1, a2, a3));
192
193 r.add1("Ln", |f: f32| via_f64(f, f64::ln));
194 r.add1("Log10", |f: f32| via_f64(f, f64::log10));
195
196 r.add1("Matrix3x2.CreateFromScale", |v: Vector2| Matrix3x2::create_scale(v));
197 r.add1("Matrix3x2.CreateFromTranslation", |v: Vector2| {
198 Matrix3x2::create_translation(v)
199 });
200 r.add1("Matrix3x2.CreateRotation", |v: f32| Matrix3x2::create_rotation(v));
201 r.add1("Matrix3x2.CreateScale", |v: Vector2| Matrix3x2::create_scale(v));
202 r.add3("Matrix3x2.CreateSkew", |a1: f32, a2: f32, a3: Vector2| {
203 Matrix3x2::create_skew_at(a1, a2, a3)
204 });
205 r.add1("Matrix3x2.CreateTranslation", |v: Vector2| Matrix3x2::create_scale(v));
207 r.add6(
208 "Matrix3x2",
209 |m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32| Matrix3x2::new(m11, m12, m21, m22, m31, m32),
210 );
211 r.add2("Matrix4x4.CreateFromAxisAngle", |v: Vector3, angle: f32| {
212 Matrix4x4::create_from_axis_angle(v, angle)
213 });
214 r.add1("Matrix4x4.CreateFromScale", |v: Vector3| Matrix4x4::create_scale(v));
215 r.add1("Matrix4x4.CreateFromTranslation", |v: Vector3| {
216 Matrix4x4::create_translation(v)
217 });
218 r.add1("Matrix4x4.CreateScale", |v: Vector3| Matrix4x4::create_scale(v));
219 r.add1("Matrix4x4.CreateTranslation", |v: Vector3| Matrix4x4::create_scale(v));
221 r.add1("Matrix4x4", |m: Matrix3x2| Matrix4x4::from_matrix3x2(m));
222 #[allow(clippy::too_many_arguments)]
223 r.add16(
224 "Matrix4x4",
225 |m11: f32,
226 m12: f32,
227 m13: f32,
228 m14: f32,
229 m21: f32,
230 m22: f32,
231 m23: f32,
232 m24: f32,
233 m31: f32,
234 m32: f32,
235 m33: f32,
236 m34: f32,
237 m41: f32,
238 m42: f32,
239 m43: f32,
240 m44: f32| {
241 Matrix4x4::new(
242 m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44,
243 )
244 },
245 );
246
247 r.add2("Max", |a1: f32, a2: f32| max_f32(a1, a2));
248 r.add2("Max", |a1: Vector2, a2: Vector2| Vector2::max(a1, a2));
249 r.add2("Max", |a1: Vector, a2: Vector| Vector::max(a1, a2));
250 r.add2("Max", |a1: Vector3, a2: Vector3| Vector3::max(a1, a2));
251 r.add2("Max", |a1: Vector3D, a2: Vector3D| Vector3D::max(a1, a2));
252 r.add2("Max", |a1: Vector4, a2: Vector4| Vector4::max(a1, a2));
253
254 r.add2("Min", |a1: f32, a2: f32| min_f32(a1, a2));
255 r.add2("Min", |a1: Vector2, a2: Vector2| Vector2::min(a1, a2));
256 r.add2("Min", |a1: Vector, a2: Vector| Vector::min(a1, a2));
257 r.add2("Min", |a1: Vector3, a2: Vector3| Vector3::min(a1, a2));
258 r.add2("Min", |a1: Vector3D, a2: Vector3D| Vector3D::min(a1, a2));
259 r.add2("Min", |a1: Vector4, a2: Vector4| Vector4::min(a1, a2));
260
261 r.add2("Mod", |a: f32, b: f32| a % b);
262
263 r.add1("Normalize", |a: Quaternion| Quaternion::normalize(a));
264 r.add1("Normalize", |a: Vector2| Vector2::normalize(a));
265 r.add1("Normalize", |a: Vector| Vector::normalize_vector(a));
266 r.add1("Normalize", |a: Vector3| Vector3::normalize(a));
267 r.add1("Normalize", |a: Vector3D| Vector3D::normalize(a));
268 r.add1("Normalize", |a: Vector4| Vector4::normalize(a));
269
270 r.add2("Pow", |a: f32, b: f32| (a as f64).powf(b as f64) as f32);
271 r.add2("Quaternion.CreateFromAxisAngle", |a: Vector3, b: f32| {
272 Quaternion::create_from_axis_angle(a, b)
273 });
274 r.add2("Quaternion.CreateFromAxisAngle", |a: Vector3D, b: f32| {
275 Quaternion::create_from_axis_angle(a.into(), b)
276 });
277 r.add4("Quaternion", |a: f32, b: f32, c: f32, d: f32| Quaternion::new(a, b, c, d));
278
279 r.add1("Round", |a: f32| via_f64(a, f64::round_ties_even));
280
281 r.add2("Scale", |a: Matrix3x2, b: f32| a * b);
282 r.add2("Scale", |a: Matrix4x4, b: f32| a * b);
283 r.add2("Scale", |a: Vector2, b: f32| a * b);
284 r.add2("Scale", |a: Vector, b: f32| a * (b as f64));
285 r.add2("Scale", |a: Vector3, b: f32| a * b);
286 r.add2("Scale", |a: Vector3D, b: f32| Vector3D::multiply_scalar(a, b as f64));
287 r.add2("Scale", |a: Vector4, b: f32| a * b);
288
289 r.add1("Sin", |a: f32| via_f64(a, f64::sin));
290
291 r.add3("SmoothStep", |a1: f32, a2: f32, a3: f32| smooth_step(a1, a2, a3));
292 r.add3("SmoothStep", |a1: Vector2, a2: Vector2, a3: Vector2| {
293 smooth_step_vector2(a1, a2, a3)
294 });
295 r.add3("SmoothStep", |a1: Vector, a2: Vector, a3: Vector| {
296 smooth_step_vector(a1, a2, a3)
297 });
298 r.add3("SmoothStep", |a1: Vector3, a2: Vector3, a3: Vector3| {
299 smooth_step_vector3(a1, a2, a3)
300 });
301 r.add3("SmoothStep", |a1: Vector3D, a2: Vector3D, a3: Vector3D| {
302 smooth_step_vector3d(a1, a2, a3)
303 });
304 r.add3("SmoothStep", |a1: Vector4, a2: Vector4, a3: Vector4| {
305 smooth_step_vector4(a1, a2, a3)
306 });
307
308 r.add3("Slerp", |a1: f32, a2: f32, a3: f32| lerp(a1, a2, a3));
310 r.add3("Slerp", |a1: Quaternion, a2: Quaternion, a3: f32| {
311 Quaternion::slerp(a1, a2, a3)
312 });
313
314 r.add1("Sqrt", |a: f32| via_f64(a, f64::sqrt));
315 r.add1("Square", |a: f32| a * a);
316 r.add1("Tan", |a: f32| via_f64(a, f64::tan));
317
318 r.add1("ToRadians", |a: f32| (a as f64 * std::f64::consts::PI / 180.0) as f32);
319 r.add1("ToDegrees", |a: f32| (a as f64 * 180.0 / std::f64::consts::PI) as f32);
320
321 r.add2("Transform", |a: Vector2, b: Matrix3x2| Vector2::transform(a, b));
322 r.add2("Transform", |a: Vector3, b: Matrix4x4| Vector3::transform(a, b));
323
324 r.add0("RelativeUnit.Absolute", || RelativeUnit::Absolute);
325 r.add0("RelativeUnit.Relative", || RelativeUnit::Relative);
326 r.add3("RelativePoint", |x: f32, y: f32, unit: RelativeUnit| {
327 RelativePoint::new(x as f64, y as f64, unit)
328 });
329 r.add3("RelativePoint", |x: f64, y: f64, unit: RelativeUnit| {
330 RelativePoint::new(x, y, unit)
331 });
332 r.add2("RelativeScalar", |value: f32, unit: RelativeUnit| {
333 RelativeScalar::new(value as f64, unit)
334 });
335 r.add2("RelativeScalar", |value: f64, unit: RelativeUnit| {
336 RelativeScalar::new(value, unit)
337 });
338
339 r.add2("Vector2", |a: f32, b: f32| Vector::new(a as f64, b as f64));
340 r.add2("Vector2", |a: f64, b: f64| Vector::new(a, b));
341 r.add3("Vector3", |a: f32, b: f32, c: f32| {
342 Vector3D::new(a as f64, b as f64, c as f64)
343 });
344 r.add3("Vector3", |a: f64, b: f64, c: f64| Vector3D::new(a, b, c));
345 r.add2("Vector3", |v2: Vector2, z: f32| Vector3::from_vector2(v2, z));
346 r.add2("Vector3", |v2: Vector, z: f32| Vector3D::new(v2.x, v2.y, z as f64));
347 r.add2("Vector3", |v2: Vector, z: f64| Vector3D::new(v2.x, v2.y, z));
348 r.add4("Vector4", |a: f32, b: f32, c: f32, d: f32| Vector4::new(a, b, c, d));
349 r.add3("Vector4", |v2: Vector2, z: f32, w: f32| Vector4::from_vector2(v2, z, w));
350 r.add2("Vector4", |v3: Vector3, w: f32| Vector4::from_vector3(v3, w));
351
352 Self { registry: r }
353 }
354
355 pub fn instance() -> &'static BuiltInExpressionFfi {
357 static INSTANCE: OnceLock<BuiltInExpressionFfi> = OnceLock::new();
358 INSTANCE.get_or_init(BuiltInExpressionFfi::new)
359 }
360}
361
362impl IExpressionForeignFunctionInterface for BuiltInExpressionFfi {
363 fn call(&self, name: &str, arguments: &[ExpressionVariant]) -> Option<ExpressionVariant> {
364 self.registry.call(name, arguments)
365 }
366}