use super::info::{Method, OperationType, Turn};
use crate::predicate::segment_intersection::SegmentIntersection;
use geometry_trait::Point;
#[must_use]
pub fn method_of<P>(outcome: &SegmentIntersection<P>) -> Method {
match outcome {
SegmentIntersection::Single(_) => Method::Crosses,
SegmentIntersection::Collinear { .. } => Method::Collinear,
SegmentIntersection::Disjoint | SegmentIntersection::OutOfRange => Method::Disjoint,
}
}
#[must_use]
pub fn refine_touch<P>(point: &P, a0: &P, a1: &P, b0: &P, b1: &P) -> Method
where
P: Point,
P::Scalar: PartialEq,
{
let at_endpoint = eq(point, a0) || eq(point, a1) || eq(point, b0) || eq(point, b1);
if at_endpoint {
Method::Touch
} else {
Method::Crosses
}
}
#[must_use]
pub const fn operations_for_crossing() -> [OperationType; 2] {
[OperationType::Union, OperationType::Intersection]
}
pub fn set_from_outcome<P>(
turn: &mut Turn<P>,
outcome: &SegmentIntersection<P>,
a0: &P,
a1: &P,
b0: &P,
b1: &P,
) where
P: Point,
P::Scalar: PartialEq,
{
let base = method_of(outcome);
turn.method = match base {
Method::Crosses => refine_touch(&turn.point, a0, a1, b0, b1),
other => other,
};
match turn.method {
Method::Crosses => {
let ops = operations_for_crossing();
turn.operations[0].operation = ops[0];
turn.operations[1].operation = ops[1];
turn.touch_only = false;
}
Method::Touch => {
turn.operations[0].operation = OperationType::Union;
turn.operations[1].operation = OperationType::Union;
turn.touch_only = true;
}
Method::Collinear => {
turn.operations[0].operation = OperationType::Continue;
turn.operations[1].operation = OperationType::Continue;
turn.touch_only = false;
}
_ => {}
}
}
fn eq<P: Point>(a: &P, b: &P) -> bool
where
P::Scalar: PartialEq,
{
a.get::<0>() == b.get::<0>() && a.get::<1>() == b.get::<1>()
}
#[cfg(test)]
mod tests {
use super::{method_of, operations_for_crossing, refine_touch, set_from_outcome};
use crate::predicate::segment_intersection::SegmentIntersection;
use crate::turn::info::{Method, Operation, OperationType, RingKind, SegmentId, Turn};
use geometry_cs::Cartesian;
use geometry_model::Point2D;
type P = Point2D<f64, Cartesian>;
fn sid(src: usize) -> SegmentId {
SegmentId {
source_index: src,
ring: RingKind::Exterior,
segment_index: 0,
}
}
fn turn_at(x: f64, y: f64) -> Turn<P> {
Turn {
point: P::new(x, y),
method: Method::None,
operations: [Operation::new(sid(0)), Operation::new(sid(1))],
touch_only: false,
}
}
#[test]
fn method_from_outcome() {
assert_eq!(
method_of(&SegmentIntersection::Single(P::new(0.0, 0.0))),
Method::Crosses
);
assert_eq!(
method_of(&SegmentIntersection::Collinear {
from: P::new(0.0, 0.0),
to: P::new(1.0, 0.0)
}),
Method::Collinear
);
assert_eq!(
method_of::<P>(&SegmentIntersection::Disjoint),
Method::Disjoint
);
}
#[test]
fn crossing_at_interior_is_cross() {
let m = refine_touch(
&P::new(1.0, 1.0),
&P::new(0.0, 0.0),
&P::new(2.0, 2.0),
&P::new(0.0, 2.0),
&P::new(2.0, 0.0),
);
assert_eq!(m, Method::Crosses);
}
#[test]
fn crossing_at_endpoint_is_touch() {
let m = refine_touch(
&P::new(2.0, 0.0),
&P::new(0.0, 0.0),
&P::new(4.0, 0.0),
&P::new(2.0, 0.0),
&P::new(2.0, 3.0),
);
assert_eq!(m, Method::Touch);
}
#[test]
fn crossing_sets_opposite_operations() {
let mut t = turn_at(1.0, 1.0);
set_from_outcome(
&mut t,
&SegmentIntersection::Single(P::new(1.0, 1.0)),
&P::new(0.0, 0.0),
&P::new(2.0, 2.0),
&P::new(0.0, 2.0),
&P::new(2.0, 0.0),
);
assert_eq!(t.method, Method::Crosses);
let ops = operations_for_crossing();
assert_eq!(t.operations[0].operation, ops[0]);
assert_eq!(t.operations[1].operation, ops[1]);
assert_eq!(t.operations[0].operation, OperationType::Union);
assert_eq!(t.operations[1].operation, OperationType::Intersection);
}
#[test]
fn collinear_sets_continue() {
let mut t = turn_at(2.0, 0.0);
set_from_outcome(
&mut t,
&SegmentIntersection::Collinear {
from: P::new(2.0, 0.0),
to: P::new(4.0, 0.0),
},
&P::new(0.0, 0.0),
&P::new(4.0, 0.0),
&P::new(2.0, 0.0),
&P::new(6.0, 0.0),
);
assert_eq!(t.method, Method::Collinear);
assert_eq!(t.operations[0].operation, OperationType::Continue);
}
}