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, HslColor};
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 {
25 registry: DelegateExpressionFfi,
26}
27
28fn lerp(a: f32, b: f32, p: f32) -> f32 {
29 (a * (1.0 - p)) + (b * p)
30}
31
32fn lerp_f64(a: f64, b: f64, p: f64) -> f64 {
33 (a * (1.0 - p)) + (b * p)
34}
35
36fn color_hsl(hue_radians: f32, saturation: f32, lightness: f32) -> Color {
39 HslColor::hsl_to_rgb((hue_radians as f64).to_degrees(), saturation as f64, lightness as f64, 1.0)
40}
41
42fn color_lerp_hsl(color1: Color, color2: Color, progress: f32) -> Color {
52 ColorInterpolator::lerp_hsl(color1, color2, progress)
53}
54
55fn smooth_step(edge0: f32, edge1: f32, x: f32) -> f32 {
56 let t = MathUtilities::clamp_f32((x - edge0) / (edge1 - edge0), 0.0, 1.0);
57 t * t * (3.0 - 2.0 * t)
58}
59
60fn smooth_step_f64(edge0: f64, edge1: f64, x: f64) -> f64 {
61 let t = MathUtilities::clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
62 t * t * (3.0 - 2.0 * t)
63}
64
65fn smooth_step_vector2(edge0: Vector2, edge1: Vector2, x: Vector2) -> Vector2 {
66 Vector2::new(
67 smooth_step(edge0.x, edge1.x, x.x),
68 smooth_step(edge0.y, edge1.y, x.y),
69 )
70}
71
72fn smooth_step_vector(edge0: Vector, edge1: Vector, x: Vector) -> Vector {
73 Vector::new(
74 smooth_step_f64(edge0.x, edge1.x, x.x),
75 smooth_step_f64(edge0.y, edge1.y, x.y),
76 )
77}
78
79fn smooth_step_vector3(edge0: Vector3, edge1: Vector3, x: Vector3) -> Vector3 {
80 Vector3::new(
81 smooth_step(edge0.x, edge1.x, x.x),
82 smooth_step(edge0.y, edge1.y, x.y),
83 smooth_step(edge0.z, edge1.z, x.z),
84 )
85}
86
87fn smooth_step_vector3d(edge0: Vector3D, edge1: Vector3D, x: Vector3D) -> Vector3D {
88 Vector3D::new(
89 smooth_step_f64(edge0.x, edge1.x, x.x),
90 smooth_step_f64(edge0.y, edge1.y, x.y),
91 smooth_step_f64(edge0.z, edge1.z, x.z),
92 )
93}
94
95fn smooth_step_vector4(edge0: Vector4, edge1: Vector4, x: Vector4) -> Vector4 {
96 Vector4::new(
97 smooth_step(edge0.x, edge1.x, x.x),
98 smooth_step(edge0.y, edge1.y, x.y),
99 smooth_step(edge0.z, edge1.z, x.z),
100 smooth_step(edge0.w, edge1.w, x.w),
101 )
102}
103
104#[inline]
106fn via_f64(value: f32, function: impl FnOnce(f64) -> f64) -> f32 {
107 function(value as f64) as f32
108}
109
110impl BuiltInExpressionFfi {
111 fn new() -> Self {
112 let mut r = DelegateExpressionFfi::new_ignoring_case();
113
114 r.add1("Abs", |f: f32| f.abs());
115 r.add1("Abs", |v: Vector2| Vector2::abs(v));
116 r.add1("Abs", |v: Vector| v.abs());
117 r.add1("Abs", |v: Vector3| Vector3::abs(v));
118 r.add1("Abs", |v: Vector3D| v.abs());
119 r.add1("Abs", |v: Vector4| Vector4::abs(v));
120
121 r.add1("ACos", |f: f32| via_f64(f, f64::acos));
122 r.add1("ASin", |f: f32| via_f64(f, f64::asin));
123 r.add1("ATan", |f: f32| via_f64(f, f64::atan));
124 r.add1("Ceil", |f: f32| via_f64(f, f64::ceil));
125
126 r.add3("Clamp", |a1: f32, a2: f32, a3: f32| MathUtilities::clamp_f32(a1, a2, a3));
127 r.add3("Clamp", |a1: Vector2, a2: Vector2, a3: Vector2| Vector2::clamp(a1, a2, a3));
128 r.add3("Clamp", |a1: Vector, a2: Vector, a3: Vector| Vector::clamp(a1, a2, a3));
129 r.add3("Clamp", |a1: Vector3, a2: Vector3, a3: Vector3| Vector3::clamp(a1, a2, a3));
130 r.add3("Clamp", |a1: Vector3D, a2: Vector3D, a3: Vector3D| {
131 Vector3D::clamp(a1, a2, a3)
132 });
133 r.add3("Clamp", |a1: Vector4, a2: Vector4, a3: Vector4| Vector4::clamp(a1, a2, a3));
134
135 r.add2("Concatenate", |a1: Quaternion, a2: Quaternion| {
136 Quaternion::concatenate(a1, a2)
137 });
138 r.add1("Cos", |a: f32| via_f64(a, f64::cos));
139
140 r.add3("ColorHsl", color_hsl);
141 r.add3("ColorLerp", |to: Color, from: Color, progress: f32| {
142 ColorInterpolator::lerp_rgb(to, from, progress)
143 });
144 r.add3("ColorLerpHSL", color_lerp_hsl);
145 r.add3("ColorHslLerp", color_lerp_hsl);
147 r.add3("ColorLerpRGB", |to: Color, from: Color, progress: f32| {
148 ColorInterpolator::lerp_rgb(to, from, progress)
149 });
150 r.add3("ColorRgbLerp", |to: Color, from: Color, progress: f32| {
152 ColorInterpolator::lerp_rgb(to, from, progress)
153 });
154 r.add4("ColorRGB", |a: f32, r: f32, g: f32, b: f32| {
155 Color::from_argb(
156 MathUtilities::clamp_f32(a, 0.0, 255.0) as u8,
157 MathUtilities::clamp_f32(r, 0.0, 255.0) as u8,
158 MathUtilities::clamp_f32(g, 0.0, 255.0) as u8,
159 MathUtilities::clamp_f32(b, 0.0, 255.0) as u8,
160 )
161 });
162
163 r.add2("Distance", |a1: f32, a2: f32| (a1 - a2).abs());
164 r.add2("Distance", |a1: Vector2, a2: Vector2| Vector2::distance(a1, a2));
165 r.add2("Distance", |a1: Vector, a2: Vector| Vector::distance(a1, a2));
166 r.add2("Distance", |a1: Vector3, a2: Vector3| Vector3::distance(a1, a2));
167 r.add2("Distance", |a1: Vector3D, a2: Vector3D| Vector3D::distance(a1, a2));
168 r.add2("Distance", |a1: Vector4, a2: Vector4| Vector4::distance(a1, a2));
169
170 r.add2("DistanceSquared", |a1: f32, a2: f32| (a1 - a2) * (a1 - a2));
171 r.add2("DistanceSquared", |a1: Vector2, a2: Vector2| {
172 Vector2::distance_squared(a1, a2)
173 });
174 r.add2("DistanceSquared", |a1: Vector, a2: Vector| {
175 Vector::distance_squared(a1, a2)
176 });
177 r.add2("DistanceSquared", |a1: Vector3, a2: Vector3| {
178 Vector3::distance_squared(a1, a2)
179 });
180 r.add2("DistanceSquared", |a1: Vector3D, a2: Vector3D| {
181 Vector3D::distance_squared(a1, a2)
182 });
183 r.add2("DistanceSquared", |a1: Vector4, a2: Vector4| {
184 Vector4::distance_squared(a1, a2)
185 });
186
187 r.add1("Floor", |v: f32| via_f64(v, f64::floor));
188
189 r.add1("Inverse", |v: Matrix3x2| Matrix3x2::invert_or_nan(v));
190 r.add1("Inverse", |v: Matrix4x4| Matrix4x4::invert_or_nan(v));
191
192 r.add1("Length", |a1: f32| a1.abs());
193 r.add1("Length", |a1: Vector2| a1.length());
194 r.add1("Length", |a1: Vector| a1.length());
195 r.add1("Length", |a1: Vector3| a1.length());
196 r.add1("Length", |a1: Vector3D| a1.length());
197 r.add1("Length", |a1: Vector4| a1.length());
198 r.add1("Length", |a1: Quaternion| a1.length());
199
200 r.add1("LengthSquared", |a1: f32| a1 * a1);
201 r.add1("LengthSquared", |a1: Vector2| a1.length_squared());
202 r.add1("LengthSquared", |a1: Vector| a1 * a1);
203 r.add1("LengthSquared", |a1: Vector3| a1.length_squared());
204 r.add1("LengthSquared", |a1: Vector3D| Vector3D::dot(a1, a1));
205 r.add1("LengthSquared", |a1: Vector4| a1.length_squared());
206 r.add1("LengthSquared", |a1: Quaternion| a1.length_squared());
207
208 r.add3("Lerp", |a1: f32, a2: f32, a3: f32| lerp(a1, a2, a3));
209 r.add3("Lerp", |a1: Vector2, a2: Vector2, a3: f32| Vector2::lerp(a1, a2, a3));
210 r.add3("Lerp", |a1: Vector, a2: Vector, a3: f32| {
211 Vector::new(
212 lerp_f64(a1.x, a2.x, a3 as f64),
213 lerp_f64(a1.y, a2.y, a3 as f64),
214 )
215 });
216 r.add3("Lerp", |a1: Vector3, a2: Vector3, a3: f32| Vector3::lerp(a1, a2, a3));
217 r.add3("Lerp", |a1: Vector3D, a2: Vector3D, a3: f32| {
218 Vector3D::new(
219 lerp_f64(a1.x, a2.x, a3 as f64),
220 lerp_f64(a1.y, a2.y, a3 as f64),
221 lerp_f64(a1.z, a2.z, a3 as f64),
222 )
223 });
224 r.add3("Lerp", |a1: Vector4, a2: Vector4, a3: f32| Vector4::lerp(a1, a2, a3));
225 r.add3("Lerp", |a1: Matrix3x2, a2: Matrix3x2, a3: f32| Matrix3x2::lerp(a1, a2, a3));
226 r.add3("Lerp", |a1: Matrix4x4, a2: Matrix4x4, a3: f32| Matrix4x4::lerp(a1, a2, a3));
227
228 r.add1("Ln", |f: f32| via_f64(f, f64::ln));
229 r.add1("Log10", |f: f32| via_f64(f, f64::log10));
230
231 r.add1("Matrix3x2.CreateFromScale", |v: Vector2| Matrix3x2::create_scale(v));
234 r.add1("Matrix3x2.CreateFromTranslation", |v: Vector2| {
235 Matrix3x2::create_translation(v)
236 });
237 r.add1("Matrix3x2.CreateRotation", |v: f32| Matrix3x2::create_rotation(v));
238 r.add2("Matrix3x2.CreateRotation", |v: f32, center: Vector2| {
239 Matrix3x2::create_rotation_at(v, center)
240 });
241 r.add1("Matrix3x2.CreateScale", |v: Vector2| Matrix3x2::create_scale(v));
242 r.add1("Matrix3x2.CreateScale", |v: f32| Matrix3x2::create_scale_uniform(v));
243 r.add2("Matrix3x2.CreateScale", |x: f32, y: f32| Matrix3x2::create_scale_xy(x, y));
244 r.add2("Matrix3x2.CreateScale", |v: Vector2, center: Vector2| {
245 Matrix3x2::create_scale_at(v, center)
246 });
247 r.add2("Matrix3x2.CreateSkew", |a1: f32, a2: f32| Matrix3x2::create_skew(a1, a2));
248 r.add3("Matrix3x2.CreateSkew", |a1: f32, a2: f32, a3: Vector2| {
249 Matrix3x2::create_skew_at(a1, a2, a3)
250 });
251 r.add1("Matrix3x2.CreateTranslation", |v: Vector2| Matrix3x2::create_translation(v));
254 r.add2("Matrix3x2.CreateTranslation", |x: f32, y: f32| {
255 Matrix3x2::create_translation_xy(x, y)
256 });
257 r.add6(
258 "Matrix3x2",
259 |m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32| Matrix3x2::new(m11, m12, m21, m22, m31, m32),
260 );
261 r.add2("Matrix4x4.CreateFromAxisAngle", |v: Vector3, angle: f32| {
262 Matrix4x4::create_from_axis_angle(v, angle)
263 });
264 r.add1("Matrix4x4.CreateFromScale", |v: Vector3| Matrix4x4::create_scale(v));
265 r.add1("Matrix4x4.CreateFromTranslation", |v: Vector3| {
266 Matrix4x4::create_translation(v)
267 });
268 r.add1("Matrix4x4.CreateRotationX", |angle: f32| Matrix4x4::create_rotation_x(angle));
269 r.add2("Matrix4x4.CreateRotationX", |angle: f32, center: Vector3| {
270 Matrix4x4::create_rotation_x_at(angle, center)
271 });
272 r.add1("Matrix4x4.CreateRotationY", |angle: f32| Matrix4x4::create_rotation_y(angle));
273 r.add2("Matrix4x4.CreateRotationY", |angle: f32, center: Vector3| {
274 Matrix4x4::create_rotation_y_at(angle, center)
275 });
276 r.add1("Matrix4x4.CreateRotationZ", |angle: f32| Matrix4x4::create_rotation_z(angle));
277 r.add2("Matrix4x4.CreateRotationZ", |angle: f32, center: Vector3| {
278 Matrix4x4::create_rotation_z_at(angle, center)
279 });
280 r.add1("Matrix4x4.CreateScale", |v: Vector3| Matrix4x4::create_scale(v));
281 r.add1("Matrix4x4.CreateScale", |v: f32| Matrix4x4::create_scale_uniform(v));
282 r.add3("Matrix4x4.CreateScale", |x: f32, y: f32, z: f32| {
283 Matrix4x4::create_scale_xyz(x, y, z)
284 });
285 r.add2("Matrix4x4.CreateScale", |v: Vector3, center: Vector3| {
286 Matrix4x4::create_scale_at(v, center)
287 });
288 r.add1("Matrix4x4.CreateTranslation", |v: Vector3| Matrix4x4::create_translation(v));
291 r.add3("Matrix4x4.CreateTranslation", |x: f32, y: f32, z: f32| {
292 Matrix4x4::create_translation_xyz(x, y, z)
293 });
294 r.add1("Matrix4x4", |m: Matrix3x2| Matrix4x4::from_matrix3x2(m));
295 #[allow(clippy::too_many_arguments)]
296 r.add16(
297 "Matrix4x4",
298 |m11: f32,
299 m12: f32,
300 m13: f32,
301 m14: f32,
302 m21: f32,
303 m22: f32,
304 m23: f32,
305 m24: f32,
306 m31: f32,
307 m32: f32,
308 m33: f32,
309 m34: f32,
310 m41: f32,
311 m42: f32,
312 m43: f32,
313 m44: f32| {
314 Matrix4x4::new(
315 m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44,
316 )
317 },
318 );
319
320 r.add2("Max", |a1: f32, a2: f32| max_f32(a1, a2));
321 r.add2("Max", |a1: Vector2, a2: Vector2| Vector2::max(a1, a2));
322 r.add2("Max", |a1: Vector, a2: Vector| Vector::max(a1, a2));
323 r.add2("Max", |a1: Vector3, a2: Vector3| Vector3::max(a1, a2));
324 r.add2("Max", |a1: Vector3D, a2: Vector3D| Vector3D::max(a1, a2));
325 r.add2("Max", |a1: Vector4, a2: Vector4| Vector4::max(a1, a2));
326
327 r.add2("Min", |a1: f32, a2: f32| min_f32(a1, a2));
328 r.add2("Min", |a1: Vector2, a2: Vector2| Vector2::min(a1, a2));
329 r.add2("Min", |a1: Vector, a2: Vector| Vector::min(a1, a2));
330 r.add2("Min", |a1: Vector3, a2: Vector3| Vector3::min(a1, a2));
331 r.add2("Min", |a1: Vector3D, a2: Vector3D| Vector3D::min(a1, a2));
332 r.add2("Min", |a1: Vector4, a2: Vector4| Vector4::min(a1, a2));
333
334 r.add2("Mod", |a: f32, b: f32| a % b);
335 r.add2("Mod", |a: Vector2, b: Vector2| Vector2::new(a.x % b.x, a.y % b.y));
336 r.add2("Mod", |a: Vector, b: Vector| Vector::new(a.x % b.x, a.y % b.y));
337 r.add2("Mod", |a: Vector3, b: Vector3| Vector3::new(a.x % b.x, a.y % b.y, a.z % b.z));
338 r.add2("Mod", |a: Vector3D, b: Vector3D| {
339 Vector3D::new(a.x % b.x, a.y % b.y, a.z % b.z)
340 });
341 r.add2("Mod", |a: Vector4, b: Vector4| {
342 Vector4::new(a.x % b.x, a.y % b.y, a.z % b.z, a.w % b.w)
343 });
344
345 r.add1("Normalize", |a: Quaternion| Quaternion::normalize(a));
346 r.add1("Normalize", |a: Vector2| Vector2::normalize(a));
347 r.add1("Normalize", |a: Vector| Vector::normalize_vector(a));
348 r.add1("Normalize", |a: Vector3| Vector3::normalize(a));
349 r.add1("Normalize", |a: Vector3D| Vector3D::normalize(a));
350 r.add1("Normalize", |a: Vector4| Vector4::normalize(a));
351
352 r.add2("Pow", |a: f32, b: f32| (a as f64).powf(b as f64) as f32);
353 r.add2("Quaternion.CreateFromAxisAngle", |a: Vector3, b: f32| {
354 Quaternion::create_from_axis_angle(a, b)
355 });
356 r.add2("Quaternion.CreateFromAxisAngle", |a: Vector3D, b: f32| {
357 Quaternion::create_from_axis_angle(a.into(), b)
358 });
359 r.add4("Quaternion", |a: f32, b: f32, c: f32, d: f32| Quaternion::new(a, b, c, d));
360
361 r.add1("Round", |a: f32| via_f64(a, f64::round_ties_even));
362
363 r.add2("Scale", |a: f32, b: f32| a * b);
364 r.add2("Scale", |a: Matrix3x2, b: f32| a * b);
365 r.add2("Scale", |a: Matrix4x4, b: f32| a * b);
366 r.add2("Scale", |a: Vector2, b: f32| a * b);
367 r.add2("Scale", |a: Vector, b: f32| a * (b as f64));
368 r.add2("Scale", |a: Vector3, b: f32| a * b);
369 r.add2("Scale", |a: Vector3D, b: f32| Vector3D::multiply_scalar(a, b as f64));
370 r.add2("Scale", |a: Vector4, b: f32| a * b);
371
372 r.add1("Sin", |a: f32| via_f64(a, f64::sin));
373
374 r.add3("SmoothStep", |a1: f32, a2: f32, a3: f32| smooth_step(a1, a2, a3));
375 r.add3("SmoothStep", |a1: Vector2, a2: Vector2, a3: Vector2| {
376 smooth_step_vector2(a1, a2, a3)
377 });
378 r.add3("SmoothStep", |a1: Vector, a2: Vector, a3: Vector| {
379 smooth_step_vector(a1, a2, a3)
380 });
381 r.add3("SmoothStep", |a1: Vector3, a2: Vector3, a3: Vector3| {
382 smooth_step_vector3(a1, a2, a3)
383 });
384 r.add3("SmoothStep", |a1: Vector3D, a2: Vector3D, a3: Vector3D| {
385 smooth_step_vector3d(a1, a2, a3)
386 });
387 r.add3("SmoothStep", |a1: Vector4, a2: Vector4, a3: Vector4| {
388 smooth_step_vector4(a1, a2, a3)
389 });
390
391 r.add3("Slerp", |a1: f32, a2: f32, a3: f32| lerp(a1, a2, a3));
393 r.add3("Slerp", |a1: Quaternion, a2: Quaternion, a3: f32| {
394 Quaternion::slerp(a1, a2, a3)
395 });
396
397 r.add1("Sqrt", |a: f32| via_f64(a, f64::sqrt));
398 r.add1("Square", |a: f32| a * a);
399 r.add1("Tan", |a: f32| via_f64(a, f64::tan));
400
401 r.add1("ToRadians", |a: f32| (a as f64 * std::f64::consts::PI / 180.0) as f32);
402 r.add1("ToDegrees", |a: f32| (a as f64 * 180.0 / std::f64::consts::PI) as f32);
403
404 r.add2("Transform", |a: Vector2, b: Matrix3x2| Vector2::transform(a, b));
405 r.add2("Transform", |a: Vector3, b: Matrix4x4| Vector3::transform(a, b));
406 r.add2("Transform", |a: Vector4, b: Matrix4x4| Vector4::transform(a, b));
407
408 r.add0("RelativeUnit.Absolute", || RelativeUnit::Absolute);
409 r.add0("RelativeUnit.Relative", || RelativeUnit::Relative);
410 r.add3("RelativePoint", |x: f32, y: f32, unit: RelativeUnit| {
411 RelativePoint::new(x as f64, y as f64, unit)
412 });
413 r.add3("RelativePoint", |x: f64, y: f64, unit: RelativeUnit| {
414 RelativePoint::new(x, y, unit)
415 });
416 r.add2("RelativeScalar", |value: f32, unit: RelativeUnit| {
417 RelativeScalar::new(value as f64, unit)
418 });
419 r.add2("RelativeScalar", |value: f64, unit: RelativeUnit| {
420 RelativeScalar::new(value, unit)
421 });
422
423 r.add2("Vector2", |a: f32, b: f32| Vector::new(a as f64, b as f64));
424 r.add2("Vector2", |a: f64, b: f64| Vector::new(a, b));
425 r.add3("Vector3", |a: f32, b: f32, c: f32| {
426 Vector3D::new(a as f64, b as f64, c as f64)
427 });
428 r.add3("Vector3", |a: f64, b: f64, c: f64| Vector3D::new(a, b, c));
429 r.add2("Vector3", |v2: Vector2, z: f32| Vector3::from_vector2(v2, z));
430 r.add2("Vector3", |v2: Vector, z: f32| Vector3D::new(v2.x, v2.y, z as f64));
431 r.add2("Vector3", |v2: Vector, z: f64| Vector3D::new(v2.x, v2.y, z));
432 r.add4("Vector4", |a: f32, b: f32, c: f32, d: f32| Vector4::new(a, b, c, d));
433 r.add3("Vector4", |v2: Vector2, z: f32, w: f32| Vector4::from_vector2(v2, z, w));
434 r.add2("Vector4", |v3: Vector3, w: f32| Vector4::from_vector3(v3, w));
435
436 Self { registry: r }
437 }
438
439 pub fn instance() -> &'static BuiltInExpressionFfi {
441 static INSTANCE: OnceLock<BuiltInExpressionFfi> = OnceLock::new();
442 INSTANCE.get_or_init(BuiltInExpressionFfi::new)
443 }
444}
445
446impl IExpressionForeignFunctionInterface for BuiltInExpressionFfi {
447 fn call(&self, name: &str, arguments: &[ExpressionVariant]) -> Option<ExpressionVariant> {
448 self.registry.call(name, arguments)
449 }
450}