Siphon/src/geom/arc.rs

42 lines
1.4 KiB
Rust

use crate::geom::Point;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Arc {
pub start: Point,
pub mid: Point,
pub end: Point,
}
impl Arc {
pub const fn new(start: Point, mid: Point, end: Point) -> Self {
Self { start, mid, end }
}
pub fn centre_and_radius(&self) -> Option<(Point, f64)> {
let (ax, ay) = (self.start.x, self.start.y);
let (bx, by) = (self.mid.x, self.mid.y);
let (cx, cy) = (self.end.x, self.end.y);
let d = 2.0 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
if d.abs() < f64::EPSILON {
return None;
}
let ax2_ay2 = ax * ax + ay * ay;
let bx2_by2 = bx * bx + by * by;
let cx2_cy2 = cx * cx + cy * cy;
let ux = (ax2_ay2 * (by - cy) + bx2_by2 * (cy - ay) + cx2_cy2 * (ay - by)) / d;
let uy = (ax2_ay2 * (cx - bx) + bx2_by2 * (ax - cx) + cx2_cy2 * (bx - ax)) / d;
let centre = Point::new(ux, uy);
let r = centre.distance_to(self.start);
Some((centre, r))
}
/// Sweep direction from start → end through mid, as +1 (CCW) or -1 (CW).
pub fn sweep_sign(&self) -> f64 {
let (ax, ay) = (self.start.x, self.start.y);
let (bx, by) = (self.mid.x, self.mid.y);
let (cx, cy) = (self.end.x, self.end.y);
let cross = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
if cross >= 0.0 { 1.0 } else { -1.0 }
}
}