use geometry_trait::Point;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Method {
#[default]
None,
Disjoint,
Crosses,
Touch,
TouchInterior,
Collinear,
Equal,
Start,
Error,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum OperationType {
#[default]
None,
Union,
Intersection,
Blocked,
Continue,
Opposite,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RingKind {
Exterior,
Interior(usize),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SegmentId {
pub source_index: usize,
pub ring: RingKind,
pub segment_index: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Operation {
pub operation: OperationType,
pub seg_id: SegmentId,
}
impl Operation {
#[must_use]
pub const fn new(seg_id: SegmentId) -> Self {
Self {
operation: OperationType::None,
seg_id,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Turn<P: Point> {
pub point: P,
pub method: Method,
pub operations: [Operation; 2],
pub touch_only: bool,
}
impl<P: Point> Turn<P> {
#[must_use]
pub fn both(&self, op: OperationType) -> bool {
self.operations[0].operation == op && self.operations[1].operation == op
}
#[must_use]
pub fn has(&self, op: OperationType) -> bool {
self.operations[0].operation == op || self.operations[1].operation == op
}
#[must_use]
pub fn blocked(&self) -> bool {
self.both(OperationType::Blocked)
}
}
#[cfg(test)]
mod tests {
use super::{Method, Operation, OperationType, RingKind, SegmentId, Turn};
use geometry_cs::Cartesian;
use geometry_model::Point2D;
use geometry_trait::Point as _;
type P = Point2D<f64, Cartesian>;
fn seg(src: usize, ring: RingKind, idx: usize) -> SegmentId {
SegmentId {
source_index: src,
ring,
segment_index: idx,
}
}
#[test]
#[allow(clippy::float_cmp, reason = "Turn coordinates are exact literals.")]
fn turn_round_trips_its_fields() {
let t = Turn {
point: P::new(2.0, 3.0),
method: Method::Crosses,
operations: [
Operation::new(seg(0, RingKind::Exterior, 4)),
Operation::new(seg(1, RingKind::Interior(2), 7)),
],
touch_only: false,
};
assert_eq!(t.point.get::<0>(), 2.0);
assert_eq!(t.point.get::<1>(), 3.0);
assert_eq!(t.method, Method::Crosses);
assert_eq!(t.operations[0].seg_id.source_index, 0);
assert_eq!(t.operations[0].seg_id.ring, RingKind::Exterior);
assert_eq!(t.operations[1].seg_id.ring, RingKind::Interior(2));
assert_eq!(t.operations[1].seg_id.segment_index, 7);
assert_eq!(t.operations[0].operation, OperationType::None);
}
#[test]
fn both_has_blocked_predicates() {
let mut t = Turn {
point: P::new(0.0, 0.0),
method: Method::Touch,
operations: [
Operation::new(seg(0, RingKind::Exterior, 0)),
Operation::new(seg(1, RingKind::Exterior, 0)),
],
touch_only: true,
};
assert!(!t.has(OperationType::Union));
t.operations[0].operation = OperationType::Union;
assert!(t.has(OperationType::Union));
assert!(!t.both(OperationType::Union));
t.operations[0].operation = OperationType::Blocked;
t.operations[1].operation = OperationType::Blocked;
assert!(t.blocked());
assert!(t.both(OperationType::Blocked));
}
#[test]
fn default_enums() {
assert_eq!(Method::default(), Method::None);
assert_eq!(OperationType::default(), OperationType::None);
}
}