use arael::vect::vect2d;
use arael_sketch_solver::*;
pub fn point_to_segment_dist(p: vect2d, a: vect2d, b: vect2d) -> f64 {
let dx = b.x - a.x;
let dy = b.y - a.y;
let len2 = dx * dx + dy * dy;
if len2 < 1e-12 {
return ((p.x - a.x).powi(2) + (p.y - a.y).powi(2)).sqrt();
}
let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
let t = t.clamp(0.0, 1.0);
let proj_x = a.x + t * dx;
let proj_y = a.y + t * dy;
((p.x - proj_x).powi(2) + (p.y - proj_y).powi(2)).sqrt()
}
pub fn circumscribed_arc(p1: vect2d, p2: vect2d, p3: vect2d) -> Option<(vect2d, f64, f64, f64, bool)> {
let ax = p1.x; let ay = p1.y;
let bx = p2.x; let by = p2.y;
let cx = p3.x; let cy = p3.y;
let d = 2.0 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
if d.abs() < 1e-12 { return None; } let aa = ax * ax + ay * ay;
let bb = bx * bx + by * by;
let cc = cx * cx + cy * cy;
let ux = (aa * (by - cy) + bb * (cy - ay) + cc * (ay - by)) / d;
let uy = (aa * (cx - bx) + bb * (ax - cx) + cc * (bx - ax)) / d;
let center = vect2d::new(ux, uy);
let radius = ((ax - ux).powi(2) + (ay - uy).powi(2)).sqrt();
let sa = (ay - uy).atan2(ax - ux);
let ea = (by - uy).atan2(bx - ux);
let ma = (cy - uy).atan2(cx - ux);
let norm = |a: f64| -> f64 { let r = a % std::f64::consts::TAU; if r < 0.0 { r + std::f64::consts::TAU } else { r } };
let span_ccw = norm(ea - sa);
let mid_ccw = norm(ma - sa);
if mid_ccw < span_ccw {
Some((center, radius, sa, ea, false))
} else {
Some((center, radius, ea, sa, true))
}
}
pub fn point_to_arc_dist(p: vect2d, a: &Arc) -> (f64, vect2d) {
let dx = p.x - a.center.value.x;
let dy = p.y - a.center.value.y;
let dist_to_center = (dx * dx + dy * dy).sqrt();
let angle = dy.atan2(dx);
let r = a.radius.value;
if a.closed {
if dist_to_center < 1e-12 {
return (r, vect2d::new(a.center.value.x + r, a.center.value.y));
}
let proj = vect2d::new(
a.center.value.x + r * dx / dist_to_center,
a.center.value.y + r * dy / dist_to_center,
);
((dist_to_center - r).abs(), proj)
} else {
let sa = a.start_angle.value;
let ea = a.end_angle.value;
let norm = |v: f64| -> f64 { let rv = v % std::f64::consts::TAU; if rv < 0.0 { rv + std::f64::consts::TAU } else { rv } };
let span = norm(ea - sa);
let a_norm = norm(angle - sa);
if a_norm <= span {
if dist_to_center < 1e-12 {
let proj = vect2d::new(
a.center.value.x + r * angle.cos(),
a.center.value.y + r * angle.sin(),
);
return (r, proj);
}
let proj = vect2d::new(
a.center.value.x + r * dx / dist_to_center,
a.center.value.y + r * dy / dist_to_center,
);
((dist_to_center - r).abs(), proj)
} else {
let sp = arc_start_pos(a);
let ep = arc_end_pos(a);
let ds = ((p.x - sp.x).powi(2) + (p.y - sp.y).powi(2)).sqrt();
let de = ((p.x - ep.x).powi(2) + (p.y - ep.y).powi(2)).sqrt();
if ds < de { (ds, sp) } else { (de, ep) }
}
}
}
pub fn arc_start_pos(a: &Arc) -> vect2d {
vect2d::new(
a.center.value.x + a.radius.value * a.start_angle.value.cos(),
a.center.value.y + a.radius.value * a.start_angle.value.sin(),
)
}
pub fn arc_end_pos(a: &Arc) -> vect2d {
vect2d::new(
a.center.value.x + a.radius.value * a.end_angle.value.cos(),
a.center.value.y + a.radius.value * a.end_angle.value.sin(),
)
}
pub fn project_onto_segment(p: vect2d, a: vect2d, b: vect2d) -> vect2d {
let dx = b.x - a.x;
let dy = b.y - a.y;
let len2 = dx * dx + dy * dy;
if len2 < 1e-12 { return a; }
let t = (((p.x - a.x) * dx + (p.y - a.y) * dy) / len2).clamp(0.0, 1.0);
vect2d::new(a.x + t * dx, a.y + t * dy)
}
pub fn line_line_intersection(p1: vect2d, p2: vect2d, p3: vect2d, p4: vect2d) -> vect2d {
let d1x = p2.x - p1.x;
let d1y = p2.y - p1.y;
let d2x = p4.x - p3.x;
let d2y = p4.y - p3.y;
let denom = d1x * d2y - d1y * d2x;
if denom.abs() < 1e-12 {
return vect2d::new((p1.x + p3.x) / 2.0, (p1.y + p3.y) / 2.0);
}
let t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / denom;
vect2d::new(p1.x + t * d1x, p1.y + t * d1y)
}