1use super::{
14 build_names, BinBuildEnvironment, BinDescription, Calculator,
15 FetchItem, GetCalibration, Item, Iteration, Result, Scope, SinkBin,
16 SinkNames, SourceBin, SourceId, SourceNames, SourceSinkBinDescription,
17 WriteDotSimple, SINK_X, SINK_Y,
18};
19use crate::error;
20use crate::R64;
21use indexmap::{IndexMap, IndexSet};
22
23static BIN_TYPE: &str = "last_calm_point";
24static SINK_Y_MAX_DELTA: &str = "y_max_delta";
25static SOURCE_CALM_X: &str = "calm_x";
26static SOURCE_CALM_Y: &str = "calm_y";
27
28#[derive(Debug, Clone)]
29struct Point {
30 x: R64,
31 y: R64,
32}
33
34#[derive(Debug)]
36pub struct Bin {
37 scope: Scope,
38
39 source_x: Box<dyn FetchItem>,
40 source_y: Box<dyn FetchItem>,
41 source_y_max_delta: Box<dyn FetchItem>,
42
43 curve: IndexMap<R64, R64>,
44 min: Option<Point>,
45 max: Option<Point>,
46 last_calm: Option<Point>,
47
48 result_calm_x: Item,
49 result_calm_y: Item,
50}
51
52impl SinkBin for Bin {}
53
54impl SourceBin for Bin {
55 fn get_source_data(&self, source: &SourceId) -> Result<Item> {
56 if source.id == SOURCE_CALM_X {
57 Ok(self.result_calm_x.clone())
58 } else if source.id == SOURCE_CALM_Y {
59 Ok(self.result_calm_y.clone())
60 } else {
61 error::MissingSourceName {
62 scope: self.scope.clone(),
63 name: source.id.to_string(),
64 bin_type: BIN_TYPE.to_string(),
65 }
66 .fail()
67 }
68 }
69}
70
71impl Calculator for Bin {
72 fn calculate(&mut self, _iteration: &Iteration) -> Result<()> {
73 let x = self.source_x.fetch_item(&self.scope)?;
74 let y = self.source_y.fetch_item(&self.scope)?;
75 let y_max_delta =
76 self.source_y_max_delta.fetch_item(&self.scope)?;
77
78 match (x.to_float(), y.to_float(), y_max_delta.to_float()) {
79 (Ok(_), Ok(_), Ok(y_max_delta)) if y_max_delta <= 0f64 => {
80 self.clear();
82 }
83 (Ok(_), Err(_), _)
84 | (Err(_), Ok(_), _)
85 | (Err(_), Err(_), _) => {
86 }
88 (Ok(_), Ok(_), Err(_)) => {
89 self.clear();
91 }
92 (Ok(x), Ok(y), Ok(y_max_delta)) => {
93 let point = Point { x, y };
94 self.add_point(&point);
95
96 if self.current_delta() > y_max_delta
97 || self.last_calm.is_none()
98 {
99 self.update_calculation(y_max_delta);
100 }
101 }
102 }
103
104 let (calm_x, calm_y) = match self.last_calm {
105 Some(Point { ref x, ref y }) => {
106 (Item::from(*x), Item::from(*y))
107 }
108 None => (Item::Nothing, Item::Nothing),
109 };
110 self.result_calm_x = calm_x;
111 self.result_calm_y = calm_y;
112 Ok(())
113 }
114}
115
116impl Bin {
117 fn clear(&mut self) {
118 self.curve.clear();
119 self.min = None;
120 self.max = None;
121 self.last_calm = None;
122 }
123
124 fn add_point(&mut self, p: &Point) {
125 self.curve.insert(p.x, p.y);
126 self.curve.sort_keys();
127
128 self.min = Self::min_point(&self.min, &Some(p.clone()));
129 self.max = Self::max_point(&self.max, &Some(p.clone()));
130 }
131
132 fn min_point(a: &Option<Point>, b: &Option<Point>) -> Option<Point> {
133 match (a, b) {
134 (&Some(ref a), &Some(ref b)) => {
135 if a.y < b.y {
136 Some(a.clone())
137 } else {
138 Some(b.clone())
139 }
140 }
141 (&Some(ref a), &None) => Some(a.clone()),
142 (&None, &Some(ref b)) => Some(b.clone()),
143 (&None, &None) => None,
144 }
145 }
146
147 fn max_point(a: &Option<Point>, b: &Option<Point>) -> Option<Point> {
148 match (a, b) {
149 (&Some(ref a), &Some(ref b)) => {
150 if a.y > b.y {
151 Some(a.clone())
152 } else {
153 Some(b.clone())
154 }
155 }
156 (&Some(ref a), &None) => Some(a.clone()),
157 (&None, &Some(ref b)) => Some(b.clone()),
158 (&None, &None) => None,
159 }
160 }
161
162 fn delta(a: &Option<Point>, b: &Option<Point>) -> R64 {
163 use decorum::Real;
164 match (a, b) {
165 (&Some(ref a), &Some(ref b)) => {
166 (a.clone().y - b.clone().y).abs()
167 }
168 _ => R64::from(0f64),
169 }
170 }
171
172 fn current_delta(&self) -> R64 {
173 use decorum::Real;
174 match (&self.min, &self.max) {
175 (
176 &Some(Point {
177 x: ref _min_x,
178 y: ref min_y,
179 }),
180 &Some(Point {
181 x: ref _max_x,
182 y: ref max_y,
183 }),
184 ) => (*max_y - *min_y).abs(),
185 _ => R64::from(0f64),
186 }
187 }
188
189 fn update_calculation(&mut self, max_delta: R64) {
190 let mut min = None;
191 let mut max = None;
192
193 let mut keep = true;
194
195 let mut recalculated = self
196 .curve
197 .iter()
198 .rev()
199 .filter_map(|(x, y)| {
200 if keep {
201 let point = Point { x: *x, y: *y };
202
203 let delta: R64 = {
204 let mut delta = R64::from(0f64);
205
206 delta = delta
207 .max(Self::delta(&Some(point.clone()), &min));
208 delta = delta
209 .max(Self::delta(&Some(point.clone()), &max));
210 delta = delta.max(Self::delta(&min, &max));
211 delta
212 };
213 min = Self::min_point(&min, &Some(point.clone()));
214 max = Self::max_point(&max, &Some(point.clone()));
215
216 keep = delta <= max_delta;
217 if keep {
218 Some((*x, *y))
219 } else {
220 None
221 }
222 } else {
223 None
224 }
225 })
226 .collect::<IndexMap<R64, R64>>();
227 recalculated.sort_keys();
228
229 self.min = min;
230 self.max = max;
231 self.curve = recalculated;
232 self.last_calm = self
233 .curve
234 .iter()
235 .map(|(x, y)| Point { x: *x, y: *y })
236 .next();
237 }
238}
239
240#[derive(Clone, Debug, Serialize, Deserialize)]
242pub struct Description;
243
244impl BinDescription for Description {
245 type Bin = Bin;
246
247 fn check_validity(
248 &self,
249 _scope: &Scope,
250 _get_calibration: &mut dyn GetCalibration,
251 ) -> Result<()> {
252 Ok(())
253 }
254
255 fn bin_type(&self) -> &'static str {
256 BIN_TYPE
257 }
258}
259
260impl SinkNames for Description {
261 fn sink_names(&self) -> IndexSet<String> {
262 build_names(&[SINK_X, SINK_Y, SINK_Y_MAX_DELTA])
263 }
264}
265
266impl SourceNames for Description {
267 fn source_names(&self) -> Result<IndexSet<String>> {
268 Ok(build_names(&[SOURCE_CALM_X, SOURCE_CALM_Y]))
269 }
270}
271
272impl SourceSinkBinDescription for Description {
273 fn build_bin(
274 &self,
275 scope: &Scope,
276 env: &mut dyn BinBuildEnvironment,
277 ) -> Result<Self::Bin> {
278 Ok(Bin {
279 scope: scope.clone(),
280
281 source_x: env.resolve(SINK_X)?,
282 source_y: env.resolve(SINK_Y)?,
283 source_y_max_delta: env.resolve(SINK_Y_MAX_DELTA)?,
284
285 curve: IndexMap::new(),
286 min: None,
287 max: None,
288 last_calm: None,
289 result_calm_x: Item::Nothing,
290 result_calm_y: Item::Nothing,
291 })
292 }
293}
294
295impl WriteDotSimple for Description {}
296
297#[cfg(test)]
298mod tests {
299 use super::Description;
300 use crate::bins::{directsource, verificationsink};
301 use crate::Item as I;
302 use crate::{run_bin, Result};
303 use indexmap::indexset;
304
305 #[test]
306 fn simulate() -> Result<()> {
307 let input = directsource::Description {
308 columns: indexset![
309 "x".to_string(),
310 "y".to_string(),
311 "y_max_delta".to_string(),
312 ],
313 rows: vec![
314 vec![I::from(0.0f64), I::from(9.0f64), I::from(3.0f64)],
315 vec![I::from(1.0f64), I::from(0.0f64), I::from(3.0f64)],
316 vec![I::from(2.0f64), I::from(3.0f64), I::from(3.0f64)],
317 vec![I::from(3.0f64), I::from(7.0f64), I::from(3.0f64)],
318 vec![I::from(4.0f64), I::from(4.0f64), I::from(3.0f64)],
319 vec![I::from(5.0f64), I::from(5.0f64), I::from(3.0f64)],
320 vec![I::from(6.0f64), I::from(5.0f64), I::from(3.0f64)],
321 vec![I::from(7.0f64), I::from(5.0f64), I::from(3.0f64)],
322 vec![I::from(8.0f64), I::from(5.0f64), I::from(3.0f64)],
323 vec![I::from(9.0f64), I::from(5.0f64), I::from(3.0f64)],
324 vec![I::from(10.0f64), I::from(0.0f64), I::from(3.0f64)],
325 vec![I::from(11.0f64), I::from(0.0f64), I::from(3.0f64)],
326 vec![I::from(12.0f64), I::from(0.0f64), I::from(3.0f64)],
327 vec![I::from(13.0f64), I::from(0.0f64), I::from(3.0f64)],
328 vec![I::from(14.0f64), I::from(0.0f64), I::from(3.0f64)],
329 vec![I::from(15.0f64), I::from(3.0f64), I::from(3.0f64)],
330 vec![I::from(16.0f64), I::from(0.0f64), I::from(3.0f64)],
331 vec![I::from(17.0f64), I::from(3.0f64), I::from(3.0f64)],
332 vec![I::from(18.0f64), I::from(0.0f64), I::from(3.0f64)],
333 vec![I::from(19.0f64), I::from(3.0f64), I::from(3.0f64)],
334 ]
335 .into(),
336 };
337 let verification = verificationsink::Description {
338 columns: indexset!["calm_x".to_string(), "calm_y".to_string()],
339 expected: vec![
340 vec![I::from(0.0f64), I::from(9.0f64)],
341 vec![I::from(1.0f64), I::from(0.0f64)],
342 vec![I::from(1.0f64), I::from(0.0f64)],
343 vec![I::from(3.0f64), I::from(7.0f64)],
344 vec![I::from(3.0f64), I::from(7.0f64)],
345 vec![I::from(3.0f64), I::from(7.0f64)],
346 vec![I::from(3.0f64), I::from(7.0f64)],
347 vec![I::from(3.0f64), I::from(7.0f64)],
348 vec![I::from(3.0f64), I::from(7.0f64)],
349 vec![I::from(3.0f64), I::from(7.0f64)],
350 vec![I::from(10.0f64), I::from(0.0f64)],
351 vec![I::from(10.0f64), I::from(0.0f64)],
352 vec![I::from(10.0f64), I::from(0.0f64)],
353 vec![I::from(10.0f64), I::from(0.0f64)],
354 vec![I::from(10.0f64), I::from(0.0f64)],
355 vec![I::from(10.0f64), I::from(0.0f64)],
356 vec![I::from(10.0f64), I::from(0.0f64)],
357 vec![I::from(10.0f64), I::from(0.0f64)],
358 vec![I::from(10.0f64), I::from(0.0f64)],
359 vec![I::from(10.0f64), I::from(0.0f64)],
360 ]
361 .into(),
362 };
363
364 run_bin(&input, &Description {}, &verification)
365 }
366}