brep_simulation/
sample.rs1use crate::model::{ClipBody, Drive, Motor, Shape, Study, DEFAULT_ACCEL};
12use std::collections::BTreeMap;
13
14#[derive(Debug, Clone, Copy, PartialEq, Default)]
16pub struct MateOverride {
17 pub value: Option<f64>,
19 pub enabled: Option<bool>,
21}
22
23#[derive(Debug, Clone, PartialEq)]
25pub struct CameraSample {
26 pub from: String,
27 pub to: String,
28 pub t: f64,
29}
30
31#[derive(Debug, Clone, PartialEq)]
32pub struct SectionSample {
33 pub view: String,
34 pub offset: f64,
36}
37
38#[derive(Debug, Clone, PartialEq)]
39pub struct ExplodeSample {
40 pub view: String,
41 pub progress: f64,
43}
44
45#[derive(Debug, Clone, PartialEq, Default)]
46pub struct FrameSample {
47 pub mates: BTreeMap<String, MateOverride>,
49 pub camera: Option<CameraSample>,
50 pub hidden: Vec<String>,
52 pub opacity: BTreeMap<String, f64>,
54 pub section: Option<SectionSample>,
55 pub explode: Vec<ExplodeSample>,
57}
58
59pub fn shape_at(shape: Shape, accel: Option<f64>, u: f64) -> f64 {
61 let u = u.clamp(0.0, 1.0);
62 match shape {
63 Shape::Linear => u,
64 Shape::Ease => u * u * (3.0 - 2.0 * u),
65 Shape::Trapezoid => {
66 let a = accel.unwrap_or(DEFAULT_ACCEL).clamp(1e-6, 0.5);
67 let peak = 1.0 / (1.0 - a);
69 if u < a {
70 0.5 * peak * u * u / a
71 } else if u <= 1.0 - a {
72 peak * (u - 0.5 * a)
73 } else {
74 1.0 - 0.5 * peak * (1.0 - u) * (1.0 - u) / a
75 }
76 }
77 }
78}
79
80impl Motor {
81 pub fn value_at(&self, time: f64, base: f64) -> Option<f64> {
84 if time < self.from {
85 return None;
86 }
87 let elapsed = time.min(self.to) - self.from;
88 Some(match &self.drive {
89 Drive::Speed { speed } => base + speed * elapsed,
90 Drive::Profile { shape, start, end, accel } => start + (end - start) * shape_at(*shape, *accel, elapsed / (self.to - self.from)),
91 })
92 }
93}
94
95impl Study {
96 pub fn sample(&self, time: f64, base: &dyn Fn(&str) -> Option<f64>) -> FrameSample {
100 let mut out = FrameSample::default();
101 for motor in &self.motors {
102 if let Some(value) = motor.value_at(time, base(&motor.constraint).unwrap_or(0.0)) {
104 let entry = out.mates.entry(motor.constraint.clone()).or_default();
105 let later = self
106 .motors
107 .iter()
108 .any(|other| other.constraint == motor.constraint && other.from > motor.from && other.from <= time);
109 if !later {
110 entry.value = Some(value);
111 }
112 }
113 }
114 let mut clips: Vec<_> = self.clips.iter().collect();
116 clips.sort_by(|a, b| a.from.total_cmp(&b.from));
117 let mut camera: Option<CameraSample> = None;
118 for clip in clips {
119 let inside = time >= clip.from && time < clip.to;
120 let u = ((time - clip.from) / (clip.to - clip.from)).clamp(0.0, 1.0);
121 match &clip.body {
122 ClipBody::Suppress { constraint } if inside => {
123 out.mates.entry(constraint.clone()).or_default().enabled = Some(false);
124 }
125 ClipBody::Camera { views } if time >= clip.from => {
126 camera = Some(if views.len() == 1 {
127 CameraSample { from: views[0].clone(), to: views[0].clone(), t: 0.0 }
128 } else {
129 let along = u * (views.len() - 1) as f64;
130 let at = (along.floor() as usize).min(views.len() - 2);
131 CameraSample { from: views[at].clone(), to: views[at + 1].clone(), t: along - at as f64 }
132 });
133 }
134 ClipBody::Visibility { occurrences } if inside => {
135 for occurrence in occurrences {
136 if !out.hidden.contains(occurrence) {
137 out.hidden.push(occurrence.clone());
138 }
139 }
140 }
141 ClipBody::Transparency { occurrences, opacity } if inside => {
142 let value = opacity[0] + (opacity[1] - opacity[0]) * u;
143 for occurrence in occurrences {
144 out.opacity.insert(occurrence.clone(), value);
145 }
146 }
147 ClipBody::Section { view, sweep } if inside => {
148 let offset = sweep.map_or(0.0, |s| s[0] + (s[1] - s[0]) * u);
149 out.section = Some(SectionSample { view: view.clone(), offset });
150 }
151 ClipBody::Explode { view, collapse } if time >= clip.from => {
152 let progress = if *collapse { 1.0 - u } else { u };
153 out.explode.retain(|e| e.view != *view);
154 if progress > 0.0 {
155 out.explode.push(ExplodeSample { view: view.clone(), progress });
156 }
157 }
158 _ => {}
159 }
160 }
161 out.camera = camera;
162 out
163 }
164}
165