1use std::time::Duration;
13
14use crate::{Animation, Curve, Spring};
15
16fn curve(index: u32) -> Curve {
18 Curve::all()
19 .get(index as usize)
20 .map_or_else(Curve::default, |(_, curve)| *curve)
21}
22
23#[expect(unsafe_code, reason = "exporting a C symbol for the preview")]
25#[unsafe(no_mangle)]
26pub extern "C" fn solium_curve_count() -> u32 {
27 Curve::all().len() as u32
28}
29
30#[expect(unsafe_code, reason = "exporting a C symbol for the preview")]
32#[unsafe(no_mangle)]
33pub extern "C" fn solium_curve_at(index: u32, t: f64) -> f64 {
34 curve(index).at(t)
35}
36
37#[expect(unsafe_code, reason = "exporting a C symbol for the preview")]
39#[unsafe(no_mangle)]
40pub extern "C" fn solium_spring_at(
41 stiffness: f64,
42 damping: f64,
43 mass: f64,
44 initial_velocity: f64,
45 seconds: f64,
46) -> f64 {
47 spring(stiffness, damping, mass, initial_velocity).value_at(seconds)
48}
49
50#[expect(unsafe_code, reason = "exporting a C symbol for the preview")]
52#[unsafe(no_mangle)]
53pub extern "C" fn solium_spring_settle_ms(
54 stiffness: f64,
55 damping: f64,
56 mass: f64,
57 initial_velocity: f64,
58) -> f64 {
59 spring(stiffness, damping, mass, initial_velocity)
60 .settle_time()
61 .as_secs_f64()
62 * 1000.0
63}
64
65#[expect(unsafe_code, reason = "exporting a C symbol for the preview")]
68#[unsafe(no_mangle)]
69pub extern "C" fn solium_progress(index: u32, duration_ms: f64, elapsed_ms: f64) -> f64 {
70 let animation = Animation::new(
71 Duration::ZERO,
72 Duration::from_secs_f64(duration_ms.max(0.0) / 1000.0),
73 curve(index),
74 );
75 animation.progress(Duration::from_secs_f64(elapsed_ms.max(0.0) / 1000.0))
76}
77
78fn spring(stiffness: f64, damping: f64, mass: f64, initial_velocity: f64) -> Spring {
79 Spring {
80 stiffness,
81 damping,
82 mass,
83 initial_velocity,
84 ..Spring::default()
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn the_exported_surface_agrees_with_the_engine() {
94 for index in 0..solium_curve_count() {
97 for step in 0..=10 {
98 let t = f64::from(step) / 10.0;
99 assert!((solium_curve_at(index, t) - curve(index).at(t)).abs() < f64::EPSILON);
100 }
101 }
102 }
103
104 #[test]
105 fn an_out_of_range_curve_falls_back_rather_than_panicking() {
106 assert!((solium_curve_at(9999, 1.0) - Curve::default().at(1.0)).abs() < f64::EPSILON);
108 }
109
110 #[test]
111 fn settling_is_reported_in_milliseconds() {
112 let millis = solium_spring_settle_ms(300.0, 25.0, 1.0, 0.0);
113 assert!(millis > 50.0 && millis < 3000.0, "got {millis}ms");
114 }
115}