use glam::Vec2;
use crate::{math_segment::Segment, polygon_clipping::EPS};
pub(super) fn is_valid_segment(
segment_index: usize,
segs: &[Segment],
bvh_end_at: usize,
) -> bool {
let segment = segs[segment_index];
let midpoint = segment.from.midpoint(segment.to);
let mut winding_num = 0.;
let _ = bvh_end_at;
for (i, seg) in segs.iter().enumerate() {
if i == segment_index {
continue;
}
winding_num += if (segment.from.y - segment.to.y).abs() < EPS {
winding_contribution_y(midpoint, seg)
} else {
winding_contribution_x(midpoint, seg)
};
}
let self_winding = if segment.from.y == segment.to.y {
winding_contribution_y(midpoint - glam::vec2(0., 1.), &segment)
} else {
winding_contribution_x(midpoint - glam::vec2(1., 0.), &segment)
};
let wind_before = zero_one_or_minusone(winding_num);
let wind_after = zero_one_or_minusone(winding_num + self_winding);
wind_before != wind_after
}
fn zero_one_or_minusone(x: f32) -> f32 {
if x > 0.1 {
1.
} else if x < -0.1 {
-1.
} else {
0.
}
}
fn winding_contribution_x(p: Vec2, seg: &Segment) -> f32 {
const EPS: f32 = 1e-6;
let mut a = seg.from;
let mut b = seg.to;
if a.y > b.y {
std::mem::swap(&mut a, &mut b);
}
if (a.y - b.y).abs() < EPS {
return 0.;
}
if p.y < a.y - EPS || p.y > b.y + EPS {
return 0.;
}
let mult = if p.y <= a.y + EPS || p.y >= b.y - EPS {
0.5
} else {
1.
};
let t = (p.y - a.y) / (b.y - a.y);
let x_intersect = a.x + t * (b.x - a.x);
if x_intersect <= p.x {
return 0.;
}
let cross = (seg.to - seg.from).perp_dot(p - seg.from);
if cross > 0.0 { mult } else { -mult }
}
fn winding_contribution_y(p: Vec2, seg: &Segment) -> f32 {
const EPS: f32 = 1e-6;
let mut a = seg.from;
let mut b = seg.to;
if a.x > b.x {
std::mem::swap(&mut a, &mut b);
}
if (a.x - b.x).abs() < EPS {
return 0.;
}
if p.x < a.x - EPS || p.x > b.x + EPS {
return 0.;
}
let mult = if p.x <= a.x + EPS || p.x >= b.x - EPS {
0.5
} else {
1.
};
let t = (p.x - a.x) / (b.x - a.x);
let y_intersect = a.y + t * (b.y - a.y);
if y_intersect <= p.y {
return 0.;
}
let cross = (seg.to - seg.from).perp_dot(p - seg.from);
if cross > 0.0 { mult } else { -mult }
}
#[cfg(test)]
mod test {
use glam::{Vec2, vec2};
use crate::{SegmentCollector, math_segment::Segment};
fn make_polys(polys: Vec<Vec<Vec2>>) -> Vec<Segment> {
let mut segs = vec![];
let mut collector = SegmentCollector::new(&mut segs, 1.);
for poly in polys {
let mut point_iter = poly.into_iter();
if let Some(first) = point_iter.next() {
collector.move_to(first.x, first.y);
}
for point in point_iter {
collector.line_to(point.x, point.y);
}
collector.close();
}
segs
}
#[test]
fn valid_edge_check_contained() {
let poly = make_polys(vec![
vec![
vec2(0.0, 0.0),
vec2(0.0, 10.0),
vec2(10.0, 10.0),
vec2(10.0, 0.0),
],
vec![vec2(1., 1.), vec2(1., 9.), vec2(9., 9.), vec2(9., 1.)],
]);
for (i, s) in poly.iter().enumerate() {
if s.from.x == 1. || s.from.x == 9. {
assert!(
!super::is_valid_segment(i, &poly, poly.len()),
"Segment {s:?} failed"
);
} else {
assert!(
super::is_valid_segment(i, &poly, poly.len()),
"Segment {s:?} failed"
);
}
}
}
}