use super::STEPS;
use super::shape::{Rib, Shape};
pub(super) fn arc(from: (f32, f32), to: (f32, f32), bulge: f32) -> Vec<(f32, f32)> {
let (side, sag) = (bulge.signum(), bulge.abs());
let (dx, dy) = (to.0 - from.0, to.1 - from.1);
let span = dx.hypot(dy);
let half = span / 2.0;
let radius = half.mul_add(half, sag * sag) / (2.0 * sag);
let (nx, ny) = (-dy / span * side, dx / span * side);
let centre = (
(from.0 + to.0).mul_add(0.5, -nx * (radius - sag)),
(from.1 + to.1).mul_add(0.5, -ny * (radius - sag)),
);
let start = (from.1 - centre.1).atan2(from.0 - centre.0);
let sweep = 2.0 * half.atan2(radius - sag);
(0..=STEPS)
.map(|step| {
let along = f32::from(step) / f32::from(STEPS);
let angle = side.mul_add(-(sweep * along), start);
(
radius.mul_add(angle.cos(), centre.0),
radius.mul_add(angle.sin(), centre.1),
)
})
.collect()
}
pub(super) fn ribs(path: &[(f32, f32)], width: f32) -> Vec<Rib> {
let half = width / 2.0;
let mut ribs = Vec::new();
for (index, point) in (0u16..).zip(path) {
let at = usize::from(index);
let behind = path.get(at.saturating_sub(1)).copied().unwrap_or(*point);
let ahead = path.get(at + 1).copied().unwrap_or(*point);
let (dx, dy) = (ahead.0 - behind.0, ahead.1 - behind.1);
let span = dx.hypot(dy).max(f32::EPSILON);
let (nx, ny) = (-dy / span * half, dx / span * half);
ribs.push(Rib {
out: (point.0 + nx, point.1 + ny),
back: (point.0 - nx, point.1 - ny),
});
}
ribs
}
pub(super) fn lune(
from: (f32, f32),
to: (f32, f32),
out: f32,
back: f32,
fill: egui::Color32,
) -> Shape {
Shape::Lune {
ribs: arc(from, to, out)
.into_iter()
.zip(arc(from, to, back))
.map(|(out, back)| Rib { out, back })
.collect(),
fill,
}
}