#![allow(dead_code)]
use std::cmp::Ordering;
use crate::compare_events::compare_events;
use crate::contour::Contour;
use crate::sweep_event::SweepEvent;
pub(crate) fn connect_edges(arena: &mut [SweepEvent], sorted_events: &[usize]) -> Vec<Contour> {
let result_events = order_events(arena, sorted_events);
let mut processed: Vec<bool> = vec![false; result_events.len()];
let mut contours: Vec<Contour> = Vec::new();
for i in 0..result_events.len() {
if processed[i] {
continue;
}
let contour_id = contours.len() as i32;
let mut contour =
initialize_contour_from_context(arena, result_events[i], &mut contours, contour_id);
let mut pos = i;
let orig_pos = i;
let initial_point = arena[result_events[pos]].point;
contour.points.push(initial_point);
loop {
mark_processed(arena, &result_events, &mut processed, pos, contour_id);
let next_pos_idx = arena[result_events[pos]].other_pos;
debug_assert!(next_pos_idx >= 0, "other_pos was never initialized");
pos = next_pos_idx as usize;
mark_processed(arena, &result_events, &mut processed, pos, contour_id);
contour.points.push(arena[result_events[pos]].point);
match next_pos(arena, pos, &result_events, &processed, orig_pos) {
None => break,
Some(np) if np == orig_pos => break,
Some(np) => pos = np,
}
}
contours.push(contour);
}
contours
}
fn initialize_contour_from_context(
arena: &[SweepEvent],
event_idx: usize,
contours: &mut [Contour],
contour_id: i32,
) -> Contour {
let mut contour = Contour::new();
let Some(prev_in_result_idx) = arena[event_idx].prev_in_result else {
contour.hole_of = None;
contour.depth = 0;
return contour;
};
let prev = &arena[prev_in_result_idx];
let lower_contour_id = prev.output_contour_id;
let lower_result_transition = prev.result_transition;
if lower_contour_id < 0 {
contour.hole_of = None;
contour.depth = 0;
return contour;
}
let lower_contour_id = lower_contour_id as usize;
if lower_result_transition > 0 {
let lower_hole_of = contours[lower_contour_id].hole_of;
let lower_depth = contours[lower_contour_id].depth;
if let Some(parent_contour_id) = lower_hole_of {
contours[parent_contour_id]
.hole_ids
.push(contour_id as usize);
contour.hole_of = Some(parent_contour_id);
contour.depth = lower_depth;
} else {
contours[lower_contour_id]
.hole_ids
.push(contour_id as usize);
contour.hole_of = Some(lower_contour_id);
contour.depth = lower_depth + 1;
}
} else {
contour.hole_of = None;
contour.depth = contours[lower_contour_id].depth;
}
contour
}
fn mark_processed(
arena: &mut [SweepEvent],
result_events: &[usize],
processed: &mut [bool],
pos: usize,
contour_id: i32,
) {
if pos < processed.len() {
processed[pos] = true;
}
if pos < result_events.len() {
arena[result_events[pos]].output_contour_id = contour_id;
}
}
fn next_pos(
arena: &[SweepEvent],
pos: usize,
result_events: &[usize],
processed: &[bool],
orig_pos: usize,
) -> Option<usize> {
let p = arena[result_events[pos]].point;
let mut new_pos = pos + 1;
while new_pos < result_events.len() {
let p1 = arena[result_events[new_pos]].point;
if p1[0] != p[0] || p1[1] != p[1] {
break;
}
if !processed[new_pos] {
return Some(new_pos);
}
new_pos += 1;
}
let mut new_pos = pos.checked_sub(1)?;
while processed[new_pos] && new_pos > orig_pos {
new_pos = new_pos.checked_sub(1)?;
}
Some(new_pos)
}
fn order_events(arena: &mut [SweepEvent], sorted_events: &[usize]) -> Vec<usize> {
let mut result_events: Vec<usize> = Vec::new();
for &idx in sorted_events {
let event = &arena[idx];
let other_idx = event.other_event.expect("order_events: event has no peer");
let include =
(event.left && event.in_result()) || (!event.left && arena[other_idx].in_result());
if include {
result_events.push(idx);
}
}
let mut sorted = false;
while !sorted {
sorted = true;
for i in 0..result_events.len().saturating_sub(1) {
if compare_events(arena, result_events[i], result_events[i + 1]) == Ordering::Greater {
result_events.swap(i, i + 1);
sorted = false;
}
}
}
for (i, &idx) in result_events.iter().enumerate() {
arena[idx].other_pos = i as i32;
}
let right_event_indices: Vec<usize> = result_events
.iter()
.copied()
.filter(|&idx| !arena[idx].left)
.collect();
for idx in right_event_indices {
let other_idx = arena[idx].other_event.unwrap();
let tmp = arena[idx].other_pos;
arena[idx].other_pos = arena[other_idx].other_pos;
arena[other_idx].other_pos = tmp;
}
result_events
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_input_returns_no_contours() {
let mut arena: Vec<SweepEvent> = Vec::new();
let contours = connect_edges(&mut arena, &[]);
assert!(contours.is_empty());
}
#[test]
fn no_events_contribute_returns_no_contours() {
use crate::edge_type::EdgeType;
use crate::sweep_event::PolygonType;
let mut arena = Vec::new();
for i in 0..4 {
let pt = [i as f64, 0.0];
let mut ev = SweepEvent::new(pt, true, PolygonType::Subject, EdgeType::Normal);
ev.result_transition = 0;
arena.push(ev);
}
let indices: Vec<usize> = (0..arena.len()).collect();
let _ = indices;
let contours = connect_edges(&mut arena, &[]);
assert!(contours.is_empty());
}
}