use crate::Point;
#[derive(Clone, Copy, Debug, Default)]
pub(super) struct P {
pub(super) x: f32,
pub(super) y: f32,
}
impl P {
pub(super) fn mid(self, b: Self) -> Self {
Self {
x: (self.x + b.x) * 0.5,
y: (self.y + b.y) * 0.5,
}
}
pub(super) fn sub(self, b: Self) -> Self {
Self {
x: self.x - b.x,
y: self.y - b.y,
}
}
pub(super) fn dot(self, b: Self) -> f32 {
self.x.mul_add(b.x, self.y * b.y)
}
pub(super) fn len(self) -> f32 {
self.dot(self).sqrt()
}
pub(super) fn lerp(self, b: Self, t: f32) -> Self {
Self {
x: (b.x - self.x).mul_add(t, self.x),
y: (b.y - self.y).mul_add(t, self.y),
}
}
}
impl From<Point> for P {
fn from(p: Point) -> Self {
Self {
x: p.x as f32,
y: p.y as f32,
}
}
}
pub(super) struct Quad {
p: [P; 3],
segments: Vec<(f32, u16)>,
pub(super) length: f32,
}
impl Quad {
pub(super) fn new(a: P, b: P, c: P) -> Self {
let mut q = Self {
p: [a, b, c],
segments: Vec::new(),
length: 0.,
};
q.split([a, b, c], 0, 32767);
q
}
fn split(&mut self, p: [P; 3], t0: u16, t1: u16) {
let mid = p[0].mid(p[2]);
let dx = p[1].x.mul_add(0.5, mid.x * -0.5).abs();
let dy = p[1].y.mul_add(0.5, mid.y * -0.5).abs();
if t1 - t0 >= 1024 && dx.max(dy) > 0.5 {
let a = p[0].mid(p[1]);
let b = p[1].mid(p[2]);
let c = a.mid(b);
let t = (t0 + t1) / 2;
self.split([p[0], a, c], t0, t);
self.split([c, b, p[2]], t, t1);
} else {
let next = self.length + p[0].sub(p[2]).len();
if next > self.length {
self.length = next;
self.segments.push((next, t1));
}
}
}
fn parameter(&self, d: f32) -> f32 {
let i = self
.segments
.partition_point(|&(end, _)| end < d)
.min(self.segments.len() - 1);
let (start, t0) = if i == 0 {
(0., 0)
} else {
self.segments[i - 1]
};
let (end, t1) = self.segments[i];
let scale = f32::from_bits(0x3800_00fd);
let t0 = t0 as f32 * scale;
let t1 = t1 as f32 * scale;
t0 + (d - start) * (t1 - t0) / (end - start)
}
pub(super) fn tangent(&self, d: f32) -> P {
self.p[1]
.sub(self.p[0])
.lerp(self.p[2].sub(self.p[1]), self.parameter(d))
}
pub(super) fn normalized_tangent(&self, d: f32) -> P {
let t = self.parameter(d);
let component =
|a: f32, b: f32, c: f32| 2. * ((-2.0_f32).mul_add(b, c) + a).mul_add(t, b - a);
let mut v = P {
x: component(self.p[0].x, self.p[1].x, self.p[2].x),
y: component(self.p[0].y, self.p[1].y, self.p[2].y),
};
let squared = v.dot(v);
if squared > 2.0_f32.powi(-24) {
let inverse = 1. / squared.sqrt();
v.x *= inverse;
v.y *= inverse;
}
v
}
pub(super) fn at(&self, d: f32) -> P {
let t = self.parameter(d);
self.p[0]
.lerp(self.p[1], t)
.lerp(self.p[1].lerp(self.p[2], t), t)
}
}