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