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 {
75 at: crossed(x, index, high, previous, current),
76 rising: true,
77 });
78 state = Level::Above;
79 }
80 Level::Above if current < low => {
81 found.push(Edge {
82 at: crossed(x, index, low, previous, current),
83 rising: false,
84 });
85 state = Level::Beneath;
86 }
87 Level::Between => state = level(current, low, high),
88 Level::Beneath | Level::Above => {}
89 }
90 }
91
92 found
93}
94
95#[must_use]
96pub fn crossing(x: &[f64], y: &[f64], target: f64) -> Option<f64> {
97 let length = x.len().min(y.len());
98
99 (1..length).find_map(|index| {
100 let (previous, current) = (y[index - 1], y[index]);
101 let straddles = (previous <= target && target <= current) || (current <= target && target <= previous);
102
103 straddles.then(|| crossed(x, index, target, previous, current))
104 })
105}
106
107fn level(value: f64, low: f64, high: f64) -> Level {
110 if value > high {
111 Level::Above
112 } else if value < low {
113 Level::Beneath
114 } else {
115 Level::Between
116 }
117}
118
119fn crossed(x: &[f64], index: usize, target: f64, previous: f64, current: f64) -> f64 {
120 let (before, after) = (x[index - 1], x[index]);
121 let rise = current - previous;
122
123 if rise.abs() < f64::EPSILON {
124 return before;
125 }
126
127 before + (target - previous) / rise * (after - before)
128}
129
130