alpha2_geometry/
spatial.rs1use crate::arclen;
6use kurbo::{CubicBez, PathSeg, Point, Vec2};
7
8#[must_use]
16pub fn point_along(
17 from: Point,
18 out_tangent: Vec2,
19 in_tangent: Vec2,
20 to: Point,
21 amount: f64,
22) -> Point {
23 let curve = CubicBez::new(from, from + out_tangent, to + in_tangent, to);
24 let segment = PathSeg::Cubic(curve);
25 let total = arclen::length(&segment);
26 if total <= 0.0 {
27 return from;
28 }
29 if amount < 0.0 {
30 let direction = unit(from - (from + out_tangent), from - to);
31 return from + direction * (-amount * total);
32 }
33 if amount > 1.0 {
34 let direction = unit(to - (to + in_tangent), to - from);
35 return to + direction * ((amount - 1.0) * total);
36 }
37 let t = arclen::parameter_at(&segment, amount * total, total);
38 arclen::point_at(&segment, t)
39}
40
41fn unit(primary: Vec2, fallback: Vec2) -> Vec2 {
44 for vector in [primary, fallback] {
45 let length = crate::affine::length(vector);
46 if length > 0.0 {
47 return vector / length;
48 }
49 }
50 Vec2::ZERO
51}
52
53#[must_use]
56pub fn motion_polyline(keys: &[(Point, Vec2, Vec2)], samples: u32) -> Vec<Point> {
57 let mut points = Vec::new();
58 let Some(first) = keys.first() else {
59 return points;
60 };
61 points.push(first.0);
62 let samples = samples.max(1);
63 for pair in keys.windows(2) {
64 let (from, out_tangent, _) = pair[0];
65 let (to, _, in_tangent) = pair[1];
66 let curve = CubicBez::new(from, from + out_tangent, to + in_tangent, to);
67 for step in 1..=samples {
68 points.push(kurbo::ParamCurve::eval(
69 &curve,
70 f64::from(step) / f64::from(samples),
71 ));
72 }
73 }
74 points
75}