#![allow(dead_code)]
use crate::edge_type::EdgeType;
use crate::operation::Operation;
use crate::sweep_event::SweepEvent;
pub(crate) fn compute_fields(
arena: &mut [SweepEvent],
event_idx: usize,
prev_idx: Option<usize>,
operation: Operation,
) {
match prev_idx {
None => {
arena[event_idx].in_out = false;
arena[event_idx].other_in_out = true;
}
Some(prev) => {
let prev_is_subject = arena[prev].is_subject();
let prev_in_out = arena[prev].in_out;
let prev_other_in_out = arena[prev].other_in_out;
let prev_other_idx = arena[prev]
.other_event
.expect("compute_fields: prev has no peer");
let prev_other_point_x = arena[prev_other_idx].point[0];
let prev_is_vertical = arena[prev].point[0] == prev_other_point_x;
let prev_prev_in_result = arena[prev].prev_in_result;
let event_is_subject = arena[event_idx].is_subject();
if event_is_subject == prev_is_subject {
arena[event_idx].in_out = !prev_in_out;
arena[event_idx].other_in_out = prev_other_in_out;
} else {
arena[event_idx].in_out = !prev_other_in_out;
arena[event_idx].other_in_out = if prev_is_vertical {
!prev_in_out
} else {
prev_in_out
};
}
let prev_was_in_result = in_result(arena, prev, operation);
arena[event_idx].prev_in_result = if !prev_was_in_result || prev_is_vertical {
prev_prev_in_result
} else {
Some(prev)
};
}
}
let is_in_result = in_result(arena, event_idx, operation);
arena[event_idx].result_transition = if is_in_result {
determine_result_transition(arena, event_idx, operation)
} else {
0
};
}
fn determine_result_transition(arena: &[SweepEvent], idx: usize, operation: Operation) -> i32 {
let e = &arena[idx];
let this_in = !e.in_out;
let that_in = !e.other_in_out;
let is_in = match operation {
Operation::Intersection => this_in && that_in,
Operation::Union => this_in || that_in,
Operation::Xor => this_in != that_in,
Operation::Difference => {
if e.is_subject() {
this_in && !that_in
} else {
that_in && !this_in
}
}
};
if is_in {
1
} else {
-1
}
}
fn in_result(arena: &[SweepEvent], idx: usize, operation: Operation) -> bool {
let e = &arena[idx];
match e.edge_type {
EdgeType::Normal => match operation {
Operation::Intersection => !e.other_in_out,
Operation::Union => e.other_in_out,
Operation::Difference => {
(e.is_subject() && e.other_in_out) || (!e.is_subject() && !e.other_in_out)
}
Operation::Xor => true,
},
EdgeType::SameTransition => {
matches!(operation, Operation::Intersection | Operation::Union)
}
EdgeType::DifferentTransition => matches!(operation, Operation::Difference),
EdgeType::NonContributing => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sweep_event::PolygonType;
fn add_segment(
arena: &mut Vec<SweepEvent>,
left_pt: [f64; 2],
right_pt: [f64; 2],
polygon_type: PolygonType,
) -> usize {
let left_idx = arena.len();
arena.push(SweepEvent::new(
left_pt,
true,
polygon_type,
EdgeType::Normal,
));
let right_idx = arena.len();
arena.push(SweepEvent::new(
right_pt,
false,
polygon_type,
EdgeType::Normal,
));
arena[left_idx].other_event = Some(right_idx);
arena[right_idx].other_event = Some(left_idx);
left_idx
}
#[test]
fn no_predecessor_sets_default_in_out_flags() {
let mut arena = Vec::new();
let e = add_segment(&mut arena, [0.0, 0.0], [1.0, 1.0], PolygonType::Subject);
compute_fields(&mut arena, e, None, Operation::Intersection);
assert!(!arena[e].in_out);
assert!(arena[e].other_in_out);
}
#[test]
fn same_polygon_flips_in_out_preserves_other_in_out() {
let mut arena = Vec::new();
let prev = add_segment(&mut arena, [0.0, 0.0], [2.0, 2.0], PolygonType::Subject);
let cur = add_segment(&mut arena, [1.0, 0.0], [3.0, 2.0], PolygonType::Subject);
arena[prev].in_out = true;
arena[prev].other_in_out = false;
compute_fields(&mut arena, cur, Some(prev), Operation::Union);
assert!(!arena[cur].in_out);
assert!(!arena[cur].other_in_out);
}
#[test]
fn different_polygon_non_vertical_cross_couples() {
let mut arena = Vec::new();
let prev = add_segment(&mut arena, [0.0, 0.0], [2.0, 2.0], PolygonType::Clipping);
let cur = add_segment(&mut arena, [1.0, 0.0], [3.0, 2.0], PolygonType::Subject);
arena[prev].in_out = false;
arena[prev].other_in_out = true;
compute_fields(&mut arena, cur, Some(prev), Operation::Intersection);
assert!(!arena[cur].in_out);
assert!(!arena[cur].other_in_out);
}
#[test]
fn different_polygon_vertical_predecessor_flips_other_in_out() {
let mut arena = Vec::new();
let prev = add_segment(&mut arena, [0.0, 0.0], [0.0, 5.0], PolygonType::Clipping);
let cur = add_segment(&mut arena, [1.0, 0.0], [3.0, 2.0], PolygonType::Subject);
arena[prev].in_out = true;
arena[prev].other_in_out = true;
compute_fields(&mut arena, cur, Some(prev), Operation::Intersection);
assert!(!arena[cur].in_out);
assert!(!arena[cur].other_in_out);
}
#[test]
fn intersection_result_transition_is_plus_one_when_both_inside() {
let mut arena = Vec::new();
let e = add_segment(&mut arena, [0.0, 0.0], [1.0, 1.0], PolygonType::Subject);
compute_fields(&mut arena, e, None, Operation::Intersection);
assert_eq!(arena[e].result_transition, 0);
}
#[test]
fn xor_normal_event_always_in_result_with_transition() {
let mut arena = Vec::new();
let e = add_segment(&mut arena, [0.0, 0.0], [1.0, 1.0], PolygonType::Subject);
compute_fields(&mut arena, e, None, Operation::Xor);
assert_ne!(arena[e].result_transition, 0);
}
#[test]
fn non_contributing_edge_never_in_result() {
let mut arena = Vec::new();
let e = add_segment(&mut arena, [0.0, 0.0], [1.0, 1.0], PolygonType::Subject);
arena[e].edge_type = EdgeType::NonContributing;
compute_fields(&mut arena, e, None, Operation::Union);
assert_eq!(arena[e].result_transition, 0);
}
#[test]
fn same_transition_in_result_for_intersection_and_union_only() {
for op in [Operation::Intersection, Operation::Union] {
let mut arena = Vec::new();
let e = add_segment(&mut arena, [0.0, 0.0], [1.0, 1.0], PolygonType::Subject);
arena[e].edge_type = EdgeType::SameTransition;
compute_fields(&mut arena, e, None, op);
assert_ne!(
arena[e].result_transition, 0,
"{op:?} should include SameTransition"
);
}
for op in [Operation::Difference, Operation::Xor] {
let mut arena = Vec::new();
let e = add_segment(&mut arena, [0.0, 0.0], [1.0, 1.0], PolygonType::Subject);
arena[e].edge_type = EdgeType::SameTransition;
compute_fields(&mut arena, e, None, op);
assert_eq!(
arena[e].result_transition, 0,
"{op:?} should exclude SameTransition"
);
}
}
#[test]
fn different_transition_in_result_for_difference_only() {
for op in [Operation::Intersection, Operation::Union, Operation::Xor] {
let mut arena = Vec::new();
let e = add_segment(&mut arena, [0.0, 0.0], [1.0, 1.0], PolygonType::Subject);
arena[e].edge_type = EdgeType::DifferentTransition;
compute_fields(&mut arena, e, None, op);
assert_eq!(arena[e].result_transition, 0);
}
let mut arena = Vec::new();
let e = add_segment(&mut arena, [0.0, 0.0], [1.0, 1.0], PolygonType::Subject);
arena[e].edge_type = EdgeType::DifferentTransition;
compute_fields(&mut arena, e, None, Operation::Difference);
assert_ne!(arena[e].result_transition, 0);
}
#[test]
fn prev_in_result_chains_through_non_contributing_predecessor() {
let mut arena = Vec::new();
let ancestor = add_segment(&mut arena, [0.0, 0.0], [10.0, 0.0], PolygonType::Subject);
let prev = add_segment(&mut arena, [1.0, 1.0], [9.0, 1.0], PolygonType::Subject);
let cur = add_segment(&mut arena, [2.0, 2.0], [8.0, 2.0], PolygonType::Subject);
arena[prev].edge_type = EdgeType::NonContributing;
arena[prev].prev_in_result = Some(ancestor);
compute_fields(&mut arena, cur, Some(prev), Operation::Union);
assert_eq!(arena[cur].prev_in_result, Some(ancestor));
}
#[test]
fn prev_in_result_points_to_prev_when_prev_contributes() {
let mut arena = Vec::new();
let prev = add_segment(&mut arena, [0.0, 0.0], [5.0, 0.0], PolygonType::Subject);
let cur = add_segment(&mut arena, [1.0, 1.0], [4.0, 1.0], PolygonType::Subject);
arena[prev].edge_type = EdgeType::Normal;
arena[prev].in_out = false;
arena[prev].other_in_out = true;
compute_fields(&mut arena, cur, Some(prev), Operation::Union);
assert_eq!(arena[cur].prev_in_result, Some(prev));
}
}