atagen

monolith

public
Star0
Watch0
b563d2da1c80c99c30a88deecf70fa7ba5c00d79
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/motion/src/velocity.rsRust59 lines1.9 KB
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
}