use geo::{Coord, LineString, MultiPolygon, Polygon};
use crate::dcel::{Dcel, HalfEdgeId};
use super::Region;
impl Region {
pub fn simplified_geometries(&self, tolerance: f64) -> Vec<MultiPolygon<f64>> {
if tolerance == 0.0 {
return self.geometries.clone();
}
let dcel = &self.dcel;
let n_he = dcel.num_half_edges();
let mut out_degree: Vec<u32> = vec![0; dcel.num_vertices()];
for i in 0..n_he {
out_degree[dcel.half_edge(HalfEdgeId(i)).origin.0] += 1;
}
let mut arc_id: Vec<usize> = vec![usize::MAX; n_he];
let mut arc_fwd: Vec<bool> = vec![true; n_he];
let mut arc_store: Vec<Vec<Coord<f64>>> = Vec::new();
let mut arc_by_start: Vec<usize> = vec![usize::MAX; n_he];
let mut visited: Vec<bool> = vec![false; n_he];
for start_id in 0..n_he {
if visited[start_id] { continue; }
let start = HalfEdgeId(start_id);
let origin = dcel.half_edge(start).origin;
if out_degree[origin.0] == 2 { continue; }
let mut raw: Vec<Coord<f64>> = vec![dcel.vertex(origin).coords];
let mut cur = start;
loop {
let twin = dcel.half_edge(cur).twin;
visited[cur.0] = true;
visited[twin.0] = true;
arc_id[cur.0] = start_id;
arc_fwd[cur.0] = true;
arc_id[twin.0] = start_id;
arc_fwd[twin.0] = false;
let dest_v = dcel.dest(cur);
raw.push(dcel.vertex(dest_v).coords);
if out_degree[dest_v.0] != 2 { break; } cur = dcel.half_edge(cur).next;
if cur == start { break; } }
let simplified = dp_simplify_open(&raw, tolerance);
let idx = arc_store.len();
arc_store.push(simplified);
arc_by_start[start_id] = idx;
}
for start_id in 0..n_he {
if visited[start_id] { continue; }
let mut raw: Vec<Coord<f64>> = Vec::new();
let mut cur = HalfEdgeId(start_id);
loop {
let twin = dcel.half_edge(cur).twin;
visited[cur.0] = true;
visited[twin.0] = true; arc_id[cur.0] = start_id;
arc_fwd[cur.0] = true;
arc_id[twin.0] = start_id;
arc_fwd[twin.0] = false;
raw.push(dcel.vertex(dcel.half_edge(cur).origin).coords);
cur = dcel.half_edge(cur).next;
if cur.0 == start_id { break; }
}
let simplified = dp_simplify_closed(&raw, tolerance);
let idx = arc_store.len();
arc_store.push(simplified);
arc_by_start[start_id] = idx;
}
let mut result: Vec<MultiPolygon<f64>> = Vec::with_capacity(self.num_units());
for unit in self.unit_ids() {
let mut polygons: Vec<Polygon<f64>> = Vec::new();
for &face_id in &self.unit_to_faces[unit.0 as usize] {
let face = dcel.face(face_id);
let primary_start = match face.half_edge {
Some(he) => he,
None => continue,
};
let mut cycle_starts: Vec<HalfEdgeId> = vec![primary_start];
for &inner in &self.face_inner_cycles[face_id.0] {
cycle_starts.push(inner);
}
let mut rings: Vec<LineString<f64>> = Vec::new();
for cycle_start in cycle_starts {
let ring = collect_ring(
dcel, cycle_start,
&arc_id, &arc_fwd, &arc_store, &arc_by_start,
);
if ring.len() >= 4 {
rings.push(LineString::from(ring));
}
}
if !rings.is_empty() {
let exterior = rings.remove(0);
polygons.push(Polygon::new(exterior, rings));
}
}
result.push(MultiPolygon(polygons));
}
result
}
}
fn collect_ring(
dcel: &Dcel<Coord<f64>>,
start: HalfEdgeId,
arc_id: &[usize],
arc_fwd: &[bool],
arc_store: &[Vec<Coord<f64>>],
arc_by_start: &[usize],
) -> Vec<Coord<f64>> {
let mut coords = Vec::new();
let mut cur = start;
loop {
let this_arc = arc_id[cur.0];
let this_fwd = arc_fwd[cur.0];
let prev_he = dcel.half_edge(cur).prev;
if arc_id[prev_he.0] != this_arc || arc_fwd[prev_he.0] != this_fwd {
let idx = arc_by_start[this_arc];
let all = &arc_store[idx];
let n = all.len();
if this_fwd {
for c in all.iter().take(n.saturating_sub(1)) {
coords.push(*c);
}
} else {
for c in all.iter().rev().take(n.saturating_sub(1)) {
coords.push(*c);
}
}
}
cur = dcel.half_edge(cur).next;
if cur == start { break; }
}
if coords.is_empty() {
let this_arc = arc_id[start.0];
let this_fwd = arc_fwd[start.0];
let idx = arc_by_start[this_arc];
let all = &arc_store[idx];
if this_fwd {
for c in all.iter() { coords.push(*c); }
} else {
for c in all.iter().rev() { coords.push(*c); }
}
}
if let Some(&first) = coords.first() {
coords.push(first);
}
coords
}
fn dp_simplify_open(coords: &[Coord<f64>], tolerance: f64) -> Vec<Coord<f64>> {
if coords.len() <= 2 || tolerance == 0.0 {
return coords.to_vec();
}
let mut result = Vec::new();
result.push(coords[0]);
dp_recurse(coords, 0, coords.len() - 1, tolerance, &mut result);
result.push(*coords.last().unwrap());
result
}
fn dp_simplify_closed(coords: &[Coord<f64>], tolerance: f64) -> Vec<Coord<f64>> {
if coords.len() <= 2 || tolerance == 0.0 {
return coords.to_vec();
}
let mut extended = coords.to_vec();
extended.push(coords[0]);
let mut result = dp_simplify_open(&extended, tolerance);
result.pop(); result
}
fn dp_recurse(
coords: &[Coord<f64>],
start: usize,
end: usize,
tolerance: f64,
result: &mut Vec<Coord<f64>>,
) {
if end <= start + 1 { return; }
let p1 = coords[start];
let p2 = coords[end];
let mut max_dist = 0.0f64;
let mut max_idx = start;
for i in (start + 1)..end {
let d = dist_to_segment(coords[i], p1, p2);
if d > max_dist { max_dist = d; max_idx = i; }
}
if max_dist > tolerance {
dp_recurse(coords, start, max_idx, tolerance, result);
result.push(coords[max_idx]);
dp_recurse(coords, max_idx, end, tolerance, result);
}
}
#[inline]
fn dist_to_segment(p: Coord<f64>, a: Coord<f64>, b: Coord<f64>) -> f64 {
let dx = b.x - a.x;
let dy = b.y - a.y;
let len2 = dx * dx + dy * dy;
if len2 == 0.0 {
let ex = p.x - a.x;
let ey = p.y - a.y;
return (ex * ex + ey * ey).sqrt();
}
let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
let t = t.clamp(0.0, 1.0);
let ex = p.x - (a.x + t * dx);
let ey = p.y - (a.y + t * dy);
(ex * ex + ey * ey).sqrt()
}