#![allow(dead_code)]
use crate::compare_events::compare_events;
use crate::edge_type::EdgeType;
use crate::event_queue::EventQueue;
use crate::operation::Operation;
use crate::sweep_event::{PolygonType, SweepEvent};
use crate::types::{BBox, MultiPolygon, Ring};
use std::cmp::Ordering;
pub(crate) fn fill_queue(
arena: &mut Vec<SweepEvent>,
queue: &mut EventQueue,
subject: &MultiPolygon,
clipping: &MultiPolygon,
sbbox: &mut BBox,
cbbox: &mut BBox,
operation: Operation,
) {
let mut contour_id: i32 = 0;
for polygon in subject {
for (ring_idx, ring) in polygon.iter().enumerate() {
let is_exterior_ring = ring_idx == 0;
if is_exterior_ring {
contour_id += 1;
}
process_polygon(
arena,
queue,
ring,
PolygonType::Subject,
contour_id,
sbbox,
is_exterior_ring,
);
}
}
for polygon in clipping {
for (ring_idx, ring) in polygon.iter().enumerate() {
let mut is_exterior_ring = ring_idx == 0;
if operation == Operation::Difference {
is_exterior_ring = false;
}
if is_exterior_ring {
contour_id += 1;
}
process_polygon(
arena,
queue,
ring,
PolygonType::Clipping,
contour_id,
cbbox,
is_exterior_ring,
);
}
}
}
fn process_polygon(
arena: &mut Vec<SweepEvent>,
queue: &mut EventQueue,
ring: &Ring,
polygon_type: PolygonType,
contour_id: i32,
bbox: &mut BBox,
is_exterior_ring: bool,
) {
if ring.len() < 2 {
return;
}
for window in ring.windows(2) {
let s1 = window[0];
let s2 = window[1];
if s1[0] == s2[0] && s1[1] == s2[1] {
continue;
}
let e1_idx = arena.len();
arena.push(SweepEvent::new(s1, false, polygon_type, EdgeType::Normal));
let e2_idx = arena.len();
arena.push(SweepEvent::new(s2, false, polygon_type, EdgeType::Normal));
arena[e1_idx].other_event = Some(e2_idx);
arena[e2_idx].other_event = Some(e1_idx);
arena[e1_idx].contour_id = Some(contour_id);
arena[e2_idx].contour_id = Some(contour_id);
if !is_exterior_ring {
arena[e1_idx].is_exterior_ring = false;
arena[e2_idx].is_exterior_ring = false;
}
if compare_events(arena, e1_idx, e2_idx) == Ordering::Greater {
arena[e2_idx].left = true;
} else {
arena[e1_idx].left = true;
}
let (x, y) = (s1[0], s1[1]);
bbox[0] = bbox[0].min(x);
bbox[1] = bbox[1].min(y);
bbox[2] = bbox[2].max(x);
bbox[3] = bbox[3].max(y);
queue.push(arena, e1_idx);
queue.push(arena, e2_idx);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn empty_bbox() -> BBox {
[
f64::INFINITY,
f64::INFINITY,
f64::NEG_INFINITY,
f64::NEG_INFINITY,
]
}
fn unit_triangle() -> MultiPolygon {
vec![vec![vec![[0.0, 0.0], [4.0, 0.0], [2.0, 3.0], [0.0, 0.0]]]]
}
#[test]
fn empty_inputs_produce_no_events() {
let mut arena = Vec::new();
let mut q = EventQueue::new();
let mut sbb = empty_bbox();
let mut cbb = empty_bbox();
fill_queue(
&mut arena,
&mut q,
&vec![],
&vec![],
&mut sbb,
&mut cbb,
Operation::Intersection,
);
assert!(arena.is_empty());
assert!(q.is_empty());
}
#[test]
fn single_subject_triangle_produces_six_events_and_correct_bbox() {
let mut arena = Vec::new();
let mut q = EventQueue::new();
let mut sbb = empty_bbox();
let mut cbb = empty_bbox();
fill_queue(
&mut arena,
&mut q,
&unit_triangle(),
&vec![],
&mut sbb,
&mut cbb,
Operation::Intersection,
);
assert_eq!(arena.len(), 6);
assert_eq!(q.len(), 6);
assert_eq!(sbb, [0.0, 0.0, 4.0, 3.0]);
assert_eq!(cbb, empty_bbox());
}
#[test]
fn collapsed_edges_are_skipped() {
let ring = vec![[0.0, 0.0], [1.0, 1.0], [1.0, 1.0], [2.0, 0.0], [0.0, 0.0]];
let poly: MultiPolygon = vec![vec![ring]];
let mut arena = Vec::new();
let mut q = EventQueue::new();
let mut sbb = empty_bbox();
let mut cbb = empty_bbox();
fill_queue(
&mut arena,
&mut q,
&poly,
&vec![],
&mut sbb,
&mut cbb,
Operation::Intersection,
);
assert_eq!(arena.len(), 6);
}
#[test]
fn each_edge_yields_one_left_and_one_right_event() {
let mut arena = Vec::new();
let mut q = EventQueue::new();
let mut sbb = empty_bbox();
let mut cbb = empty_bbox();
fill_queue(
&mut arena,
&mut q,
&unit_triangle(),
&vec![],
&mut sbb,
&mut cbb,
Operation::Intersection,
);
for pair in (0..arena.len()).step_by(2) {
let left_count = (arena[pair].left as u8) + (arena[pair + 1].left as u8);
assert_eq!(
left_count,
1,
"edge events at indices {pair}, {} should be one left + one right",
pair + 1
);
assert_eq!(arena[pair].other_event, Some(pair + 1));
assert_eq!(arena[pair + 1].other_event, Some(pair));
}
}
#[test]
fn holes_have_is_exterior_ring_false() {
let exterior = vec![
[0.0, 0.0],
[10.0, 0.0],
[10.0, 10.0],
[0.0, 10.0],
[0.0, 0.0],
];
let hole = vec![[2.0, 2.0], [4.0, 2.0], [4.0, 4.0], [2.0, 4.0], [2.0, 2.0]];
let poly: MultiPolygon = vec![vec![exterior, hole]];
let mut arena = Vec::new();
let mut q = EventQueue::new();
let mut sbb = empty_bbox();
let mut cbb = empty_bbox();
fill_queue(
&mut arena,
&mut q,
&poly,
&vec![],
&mut sbb,
&mut cbb,
Operation::Intersection,
);
for ev in arena.iter().take(8) {
assert!(ev.is_exterior_ring);
}
for ev in arena.iter().skip(8) {
assert!(!ev.is_exterior_ring);
}
}
#[test]
fn difference_flips_clipping_exterior_to_non_exterior() {
let subject = unit_triangle();
let clipping = unit_triangle();
let mut arena = Vec::new();
let mut q = EventQueue::new();
let mut sbb = empty_bbox();
let mut cbb = empty_bbox();
fill_queue(
&mut arena,
&mut q,
&subject,
&clipping,
&mut sbb,
&mut cbb,
Operation::Difference,
);
for ev in arena.iter().take(6) {
assert!(
ev.is_exterior_ring,
"subject events should keep exterior flag"
);
}
for ev in arena.iter().skip(6) {
assert!(
!ev.is_exterior_ring,
"clipping events should be flipped under DIFFERENCE"
);
}
}
#[test]
fn each_polygon_gets_distinct_contour_id() {
let mp: MultiPolygon = vec![
vec![vec![[0.0, 0.0], [1.0, 0.0], [0.5, 1.0], [0.0, 0.0]]],
vec![vec![[2.0, 0.0], [3.0, 0.0], [2.5, 1.0], [2.0, 0.0]]],
];
let mut arena = Vec::new();
let mut q = EventQueue::new();
let mut sbb = empty_bbox();
let mut cbb = empty_bbox();
fill_queue(
&mut arena,
&mut q,
&mp,
&vec![],
&mut sbb,
&mut cbb,
Operation::Intersection,
);
for ev in arena.iter().take(6) {
assert_eq!(ev.contour_id, Some(1));
}
for ev in arena.iter().skip(6) {
assert_eq!(ev.contour_id, Some(2));
}
}
}