| 1 | /// Rectangle edges as (left, right, top, bottom). |
| 2 | pub type Edges = [f64; 4]; |
| 3 | |
| 4 | /// Initial progress slope for the first phase of a replanned motion group. |
| 5 | /// |
| 6 | /// Each item is a window's current edge velocity (units per second) and its |
| 7 | /// edge displacement over the new first phase. The fastest mover with a real |
| 8 | /// displacement decides the slope, because windows sharing an edge must share |
| 9 | /// a curve. Returns `None` when that mover would have to reverse, so the group |
| 10 | /// starts from rest instead. |
| 11 | pub fn group_initial_slope( |
| 12 | movers: impl IntoIterator<Item = (Edges, Edges)>, |
| 13 | phase_secs: f64, |
| 14 | ) -> Option<f64> { |
| 15 | let mut best: Option<(f64, Edges, Edges)> = None; |
| 16 | for (velocity, displacement) in movers { |
| 17 | if dot(&displacement, &displacement) < 1.0 { |
| 18 | continue; |
| 19 | } |
| 20 | let speed = dot(&velocity, &velocity); |
| 21 | if best.is_none_or(|(s, _, _)| speed > s) { |
| 22 | best = Some((speed, velocity, displacement)); |
| 23 | } |
| 24 | } |
| 25 | let (_, velocity, displacement) = best?; |
| 26 | let slope = dot(&velocity, &displacement) / dot(&displacement, &displacement) * phase_secs; |
| 27 | (slope > 0.05).then_some(slope) |
| 28 | } |
| 29 | |
| 30 | fn dot(a: &Edges, b: &Edges) -> f64 { |
| 31 | a.iter().zip(b).map(|(x, y)| x * y).sum() |
| 32 | } |
| 33 | |
| 34 | #[cfg(test)] |
| 35 | mod tests { |
| 36 | use super::*; |
| 37 | |
| 38 | #[test] |
| 39 | fn continues_same_direction() { |
| 40 | let slope = group_initial_slope([([100.0, 100.0, 0.0, 0.0], [50.0, 50.0, 0.0, 0.0])], 0.5); |
| 41 | assert_eq!(slope, Some(1.0)); |
| 42 | } |
| 43 | |
| 44 | #[test] |
| 45 | fn reversal_starts_from_rest() { |
| 46 | let slope = |
| 47 | group_initial_slope([([100.0, 100.0, 0.0, 0.0], [-50.0, -50.0, 0.0, 0.0])], 0.5); |
| 48 | assert_eq!(slope, None); |
| 49 | } |
| 50 | |
| 51 | #[test] |
| 52 | fn ignores_windows_that_do_not_move_this_phase() { |
| 53 | let movers = [ |
| 54 | ([500.0, 500.0, 0.0, 0.0], [0.0; 4]), |
| 55 | ([10.0, 10.0, 0.0, 0.0], [20.0, 20.0, 0.0, 0.0]), |
| 56 | ]; |
| 57 | assert_eq!(group_initial_slope(movers, 1.0), Some(0.5)); |
| 58 | } |
| 59 | } |