use axiolid_core::{Point2, Scalar};
use axiolid_curve::implicit::{bound_simple, partial, Cell, Range};
use axiolid_curve::{Axis, ImplicitCurve2};
pub(crate) fn joins(curve: &ImplicitCurve2) -> Vec<Scalar> {
(1..curve.cells.len()).map(|k| k as Scalar).collect()
}
pub(crate) fn chord_bound(curve: &ImplicitCurve2, lo: Scalar, hi: Scalar) -> Option<Scalar> {
let count = curve.cells.len();
if count == 0 || !(lo.is_finite() && hi.is_finite()) || lo < 0.0 || hi > count as Scalar {
return None;
}
let index = (lo.floor() as usize).min(count - 1);
if hi > (index + 1) as Scalar {
return None;
}
let cell = curve.cells[index];
let local = |t: Scalar| (t - index as Scalar).clamp(0.0, 1.0);
let part = cell.part(local(lo), local(hi));
let place = |free: Scalar, solved: Scalar| match cell.axis {
Axis::U => Point2::new(free, solved),
Axis::V => Point2::new(solved, free),
};
let span = part.to - part.from;
if let Some((m0, m1)) = part.bridge {
let controls = [
place(part.from, part.low),
place(part.from + span / 3.0, part.low + m0 * span / 3.0),
place(part.to - span / 3.0, part.high - m1 * span / 3.0),
place(part.to, part.high),
];
return Some(
controls
.iter()
.map(|&p| distance_to_line(p, controls[0], controls[3]))
.fold(0.0, Scalar::max),
);
}
let (start, end) = (curve.point(lo)?, curve.point(hi)?);
let solved = |p: Point2| match cell.axis {
Axis::U => p.y,
Axis::V => p.x,
};
if span == 0.0 {
return Some((solved(end) - solved(start)).abs());
}
let along_u = cell.axis == Axis::U;
let (a, b) = (
place(part.from.min(part.to), cell.low.min(cell.high)),
place(part.from.max(part.to), cell.low.max(cell.high)),
);
let corner = Cell {
lo: a.min(b),
hi: a.max(b),
};
let d_free = partial(&curve.field, along_u);
let d_solved = partial(&curve.field, !along_u);
let solved_rate = bound_simple(&d_solved, &corner);
if solved_rate.straddles_zero() {
return None;
}
let most = |r: Range| r.lo.abs().max(r.hi.abs());
let floor = solved_rate.lo.abs().min(solved_rate.hi.abs());
let lipschitz = most(bound_simple(&d_free, &corner)) / floor;
let slope = ((solved(end) - solved(start)) / span).abs();
let first = 0.5 * (lipschitz + slope) * span.abs();
let ff = most(bound_simple(&partial(&d_free, along_u), &corner));
let fw = most(bound_simple(&partial(&d_free, !along_u), &corner));
let ww = most(bound_simple(&partial(&d_solved, !along_u), &corner));
let bend = (ff + 2.0 * fw * lipschitz + ww * lipschitz * lipschitz) / floor;
let second = span * span / 8.0 * bend;
let bound = first.min(second);
bound.is_finite().then_some(bound)
}
fn distance_to_line(p: Point2, a: Point2, b: Point2) -> Scalar {
let d = b - a;
let length = d.length();
if length == 0.0 {
return (p - a).length();
}
(d.perp_dot(p - a) / length).abs()
}