const RIM_NOISE_OF_SIZE: f64 = 1.0 / 8192.0;
const RIM_NOISE_CAP: f64 = 1.0 / 4096.0;
pub(super) fn weld_near_coincident_2d(
ring: &[nalgebra::Point2<f64>],
) -> Vec<nalgebra::Point2<f64>> {
let n = ring.len();
if n < 4 {
return ring.to_vec();
}
let (mut minx, mut miny, mut maxx, mut maxy) = (f64::MAX, f64::MAX, f64::MIN, f64::MIN);
for p in ring {
minx = minx.min(p.x);
miny = miny.min(p.y);
maxx = maxx.max(p.x);
maxy = maxy.max(p.y);
}
let extent = (maxx - minx).max(maxy - miny);
if !extent.is_finite() || extent <= 0.0 {
return ring.to_vec();
}
let eps = (floor_pow2(extent) * RIM_NOISE_OF_SIZE).min(RIM_NOISE_CAP);
let eps2 = eps * eps;
let mut kept: Vec<nalgebra::Point2<f64>> = Vec::with_capacity(n);
for &p in ring {
let dup = kept.last().is_some_and(|q| {
let dx = p.x - q.x;
let dy = p.y - q.y;
dx * dx + dy * dy < eps2
});
if !dup {
kept.push(p);
}
}
if kept.len() >= 2 {
let (first, last) = (kept[0], *kept.last().unwrap());
let dx = last.x - first.x;
let dy = last.y - first.y;
if dx * dx + dy * dy < eps2 {
kept.pop();
}
}
if kept.len() >= 3 {
kept
} else {
ring.to_vec()
}
}
pub(super) fn simplify_2d_collinear(ring: &[nalgebra::Point2<f64>]) -> Vec<nalgebra::Point2<f64>> {
let n = ring.len();
if n < 4 {
return ring.to_vec();
}
let mut keep = vec![true; n];
let mut changed = true;
while changed {
changed = false;
for i in 0..n {
if !keep[i] {
continue;
}
let prev = (1..n).map(|k| (i + n - k) % n).find(|&k| keep[k]);
let next = (1..n).map(|k| (i + k) % n).find(|&k| keep[k]);
let (prev, next) = match (prev, next) {
(Some(p), Some(n)) if p != i && n != i && p != n => (p, n),
_ => continue,
};
let a = ring[prev];
let b = ring[i];
let c = ring[next];
let e1x = b.x - a.x;
let e1y = b.y - a.y;
let e2x = c.x - b.x;
let e2y = c.y - b.y;
let cross = e1x * e2y - e1y * e2x;
let len1 = (e1x * e1x + e1y * e1y).sqrt();
let len2 = (e2x * e2x + e2y * e2y).sqrt();
let denom = len1 * len2;
if denom < 1.0e-18 || (cross.abs() / denom) < 1.0e-4 {
keep[i] = false;
changed = true;
}
}
}
ring.iter()
.zip(keep.iter())
.filter_map(|(p, k)| if *k { Some(*p) } else { None })
.collect()
}
pub(super) fn clean_ring(
ring: &[[f64; 2]],
plane_area: f64,
length_unit_scale: f64,
) -> Option<Vec<nalgebra::Point2<f64>>> {
let pts: Vec<_> = ring.iter().map(|p| nalgebra::Point2::new(p[0], p[1])).collect();
let simplified = simplify_2d_collinear(&weld_near_coincident_2d(&pts));
(!ring_is_noise(&simplified, plane_area, length_unit_scale)).then_some(simplified)
}
pub(super) fn ring_is_noise(
ring: &[nalgebra::Point2<f64>],
plane_area: f64,
length_unit_scale: f64,
) -> bool {
let n = ring.len();
if n < 3 {
return true;
}
let edges = || (0..n).map(|i| (ring[i], ring[(i + 1) % n]));
let twice_signed_area: f64 = edges().map(|(a, b)| a.x * b.y - b.x * a.y).sum();
let area = (twice_signed_area * 0.5).abs();
if area < NOISE_AREA {
return true;
}
if !(area < plane_area * NOISE_PLANE_SHARE) {
return false;
}
let perimeter: f64 = edges().map(|(a, b)| (b - a).norm()).sum();
2.0 * area * length_unit_scale < perimeter * RIM_NOISE_CAP
}
const NOISE_AREA: f64 = 1.0e-8;
const NOISE_PLANE_SHARE: f64 = 1.0e-4;
pub(super) fn floor_pow2(x: f64) -> f64 {
if !x.is_finite() || x <= 0.0 {
return 0.0;
}
let exp = x.to_bits() >> 52 & 0x7ff; let unbiased = exp as i64 - 1023;
2.0_f64.powi(unbiased as i32)
}