geo_booleanop/boolean/
compute_fields.rs

1use super::helper::Float;
2use super::sweep_event::{EdgeType, ResultTransition, SweepEvent};
3use super::Operation;
4use std::rc::Rc;
5
6pub fn compute_fields<F>(event: &Rc<SweepEvent<F>>, maybe_prev: Option<&Rc<SweepEvent<F>>>, operation: Operation)
7where
8    F: Float,
9{
10    if let Some(prev) = maybe_prev {
11        if event.is_subject == prev.is_subject {
12            event.set_in_out(!prev.is_in_out(), prev.is_other_in_out());
13        } else if prev.is_vertical() {
14            event.set_in_out(!prev.is_other_in_out(), !prev.is_in_out());
15        } else {
16            event.set_in_out(!prev.is_other_in_out(), prev.is_in_out());
17        }
18
19        // Connect to previous in result: Only use the given `prev` if it is
20        // part of the result and not a vertical segment. Otherwise connect
21        // to its previous in result if any.
22        if prev.is_in_result() && !prev.is_vertical() {
23            event.set_prev_in_result(prev);
24        } else if let Some(prev_of_prev) = prev.get_prev_in_result() {
25            event.set_prev_in_result(&prev_of_prev);
26        } else {
27            // Clearing prev_in_result is necessary for re-computations, if the first
28            // computation has already set prev_in_result, but it is no longer valid now.
29            event.unset_prev_in_result();
30        }
31    } else {
32        event.set_in_out(false, true);
33        // Clearing prev_in_result is necessary for re-computations, if the first
34        // computation has already set prev_in_result, but it is no longer valid now.
35        event.unset_prev_in_result();
36    }
37
38    // Determine whether segment is in result, and if so, whether it is an
39    // in-out or out-in transition.
40    let in_result = in_result(event, operation);
41    let result_transition = if !in_result {
42        ResultTransition::None
43    } else {
44        determine_result_transition(&event, operation)
45    };
46    event.set_result_transition(result_transition);
47
48    #[cfg(feature = "debug-booleanop")]
49    {
50        println!(
51            "{{\"computeFields\": {{\"inOut\": {}, \"otherOut\": {}, \"resultTransition\": \"{:?}\", \"edgeType\": \"{:?}\"}}}}",
52            event.is_in_out(),
53            event.is_other_in_out(),
54            event.get_result_transition(),
55            event.get_edge_type(),
56        );
57    }
58}
59
60fn in_result<F>(event: &SweepEvent<F>, operation: Operation) -> bool
61where
62    F: Float,
63{
64    match event.get_edge_type() {
65        EdgeType::Normal => match operation {
66            Operation::Intersection => !event.is_other_in_out(),
67            Operation::Union => event.is_other_in_out(),
68            Operation::Difference => {
69                (event.is_subject && event.is_other_in_out()) || (!event.is_subject && !event.is_other_in_out())
70            }
71            Operation::Xor => true,
72        },
73        EdgeType::SameTransition => operation == Operation::Intersection || operation == Operation::Union,
74        EdgeType::DifferentTransition => operation == Operation::Difference,
75        EdgeType::NonContributing => false,
76    }
77}
78
79fn determine_result_transition<F>(event: &SweepEvent<F>, operation: Operation) -> ResultTransition
80where
81    F: Float,
82{
83    let this_in = !event.is_in_out();
84    let that_in = !event.is_other_in_out();
85    let is_in = match operation {
86        Operation::Intersection => this_in && that_in,
87        Operation::Union => this_in || that_in,
88        Operation::Xor => this_in ^ that_in,
89        Operation::Difference =>
90        // Difference is assymmetric, so subject vs clipping matters.
91        {
92            if event.is_subject {
93                this_in && !that_in
94            } else {
95                that_in && !this_in
96            }
97        }
98    };
99    if is_in {
100        ResultTransition::OutIn
101    } else {
102        ResultTransition::InOut
103    }
104}