harmos_storage/signal/waveform/
edge.rs1#[derive(Clone, Copy, Debug, PartialEq)]
31pub struct Edge {
32 pub at: f64,
33 pub rising: bool,
34}
35
36#[derive(Clone, Copy)]
39enum Level {
40 Beneath,
41 Between,
42 Above,
43}
44
45#[must_use]
52pub fn edges(x: &[f64], y: &[f64], low: f64, high: f64) -> Vec<Edge> {
53 let length = x.len().min(y.len());
54 if length < 2 {
55 return Vec::new();
56 }
57
58 let lowest = y[..length].iter().copied().fold(f64::INFINITY, f64::min);
59 let highest = y[..length].iter().copied().fold(f64::NEG_INFINITY, f64::max);
60 let range = highest - lowest;
61 if !range.is_finite() || range < f64::EPSILON {
62 return Vec::new();
63 }
64
65 let (low, high) = (lowest + range * low, lowest + range * high);
66 let mut state = level(y[0], low, high);
67 let mut found = Vec::new();
68
69 for index in 1..length {
70 let (previous, current) = (y[index - 1], y[index]);
71
72 match state {
73 Level::Beneath if current > high => {
74 found.push(Edge { at: crossed(x, index, high, previous, current), rising: true });
75 state = Level::Above;
76 }
77 Level::Above if current < low => {
78 found.push(Edge { at: crossed(x, index, low, previous, current), rising: false });
79 state = Level::Beneath;
80 }
81 Level::Between => state = level(current, low, high),
82 Level::Beneath | Level::Above => {}
83 }
84 }
85
86 found
87}
88
89#[must_use]
90pub fn crossing(x: &[f64], y: &[f64], target: f64) -> Option<f64> {
91 let length = x.len().min(y.len());
92
93 (1..length).find_map(|index| {
94 let (previous, current) = (y[index - 1], y[index]);
95 let straddles = (previous <= target && target <= current) || (current <= target && target <= previous);
96
97 straddles.then(|| crossed(x, index, target, previous, current))
98 })
99}
100
101fn level(value: f64, low: f64, high: f64) -> Level {
104 if value > high {
105 Level::Above
106 } else if value < low {
107 Level::Beneath
108 } else {
109 Level::Between
110 }
111}
112
113fn crossed(x: &[f64], index: usize, target: f64, previous: f64, current: f64) -> f64 {
114 let (before, after) = (x[index - 1], x[index]);
115 let rise = current - previous;
116
117 if rise.abs() < f64::EPSILON {
118 return before;
119 }
120
121 before + (target - previous) / rise * (after - before)
122}
123
124