atagen

monolith

public
Star0
Watch0
7e9270240c4669af7607f071f886e6799fb88211
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/motion/src/velocity.rsRust44 lines1.5 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 reversal_starts_from_rest() {
40
        let slope =
41
            group_initial_slope([([100.0, 100.0, 0.0, 0.0], [-50.0, -50.0, 0.0, 0.0])], 0.5);
42
        assert_eq!(slope, None);
43
    }
44
}