use togo::prelude::Point;
fn point_strictly_inside_polygon(p: &Point, poly: &[Point]) -> bool {
if poly.len() < 3 {
return false;
}
let tolerance = 1e-6;
for i in 0..poly.len() {
let p1 = poly[i];
let p2 = poly[(i + 1) % poly.len()];
let edge_x = p2.x - p1.x;
let edge_y = p2.y - p1.y;
let edge_len_sq = edge_x * edge_x + edge_y * edge_y;
if edge_len_sq > tolerance * tolerance {
let to_p_x = p.x - p1.x;
let to_p_y = p.y - p1.y;
let t = (to_p_x * edge_x + to_p_y * edge_y) / edge_len_sq;
if t >= -tolerance && t <= 1.0 + tolerance {
let closest_x = p1.x + t * edge_x;
let closest_y = p1.y + t * edge_y;
let dist_sq = (p.x - closest_x) * (p.x - closest_x) + (p.y - closest_y) * (p.y - closest_y);
if dist_sq < tolerance * tolerance {
return false; }
}
}
}
let mut inside = false;
let mut p1 = poly[poly.len() - 1];
for &p2 in poly {
if (p2.y > p.y) != (p1.y > p.y)
&& p.x < (p1.x - p2.x) * (p.y - p2.y) / (p1.y - p2.y) + p2.x
{
inside = !inside;
}
p1 = p2;
}
inside
}
fn polygons_overlap(a: &[Point], b: &[Point]) -> bool {
let tolerance = 1e-9;
for &b_v in b {
if point_strictly_inside_polygon(&b_v, a) {
return true;
}
}
for &a_v in a {
if point_strictly_inside_polygon(&a_v, b) {
return true;
}
}
for i in 0..a.len() {
let a1 = a[i];
let a2 = a[(i + 1) % a.len()];
for j in 0..b.len() {
let b1 = b[j];
let b2 = b[(j + 1) % b.len()];
if segments_intersect(&a1, &a2, &b1, &b2, tolerance) {
return true;
}
}
}
false
}
fn segments_intersect(p1: &Point, p2: &Point, p3: &Point, p4: &Point, tolerance: f64) -> bool {
let d1 = ccw(p3, p1, p2);
let d2 = ccw(p4, p1, p2);
let d3 = ccw(p1, p3, p4);
let d4 = ccw(p2, p3, p4);
let has_opposite_sign = |a: f64, b: f64| -> bool { a * b < -tolerance };
has_opposite_sign(d1, d2) && has_opposite_sign(d3, d4)
}
fn ccw(a: &Point, b: &Point, c: &Point) -> f64 {
(b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)
}
fn translate_polygon(poly: &[Point], offset: &Point) -> Vec<Point> {
poly
.iter()
.map(|p| Point {
x: p.x + offset.x,
y: p.y + offset.y,
})
.collect()
}
pub fn validate_nfp_point(nfp_point: &Point, a: &[Point], b: &[Point]) -> bool {
let b_translated = translate_polygon(b, nfp_point);
!polygons_overlap(a, &b_translated)
}
pub fn validate_nfp(nfp: &[Point], a: &[Point], b: &[Point]) -> (bool, String) {
for (idx, &nfp_point) in nfp.iter().enumerate() {
if !validate_nfp_point(&nfp_point, a, b) {
return (
false,
format!("NFP point {} at ({:.3}, {:.3}) is invalid - B overlaps A at this position",
idx, nfp_point.x, nfp_point.y)
);
}
}
(true, "NFP is valid".to_string())
}
#[cfg(test)]
mod tests {
}