#![deny(rust_2018_idioms)]
#![warn(missing_docs)]
pub mod operation;
pub mod types;
mod compare_events;
mod compare_segments;
mod compute_fields;
mod connect_edges;
mod contour;
mod divide_segment;
mod edge_type;
mod equals;
mod event_queue;
mod fill_queue;
mod possible_intersection;
mod segment_intersection;
mod signed_area;
mod subdivide_segments;
mod sweep_event;
mod sweep_line;
use crate::connect_edges::connect_edges;
use crate::event_queue::EventQueue;
use crate::fill_queue::fill_queue;
use crate::operation::Operation;
use crate::subdivide_segments::subdivide_segments;
use crate::sweep_event::SweepEvent;
pub use crate::types::{BBox, Geometry, MultiPolygon, Polygon, Position, Ring};
pub fn intersection(subject: Geometry, clipping: Geometry) -> Option<MultiPolygon> {
boolean_op(subject, clipping, Operation::Intersection)
}
pub fn union(subject: Geometry, clipping: Geometry) -> Option<MultiPolygon> {
boolean_op(subject, clipping, Operation::Union)
}
pub fn difference(subject: Geometry, clipping: Geometry) -> Option<MultiPolygon> {
boolean_op(subject, clipping, Operation::Difference)
}
pub fn xor(subject: Geometry, clipping: Geometry) -> Option<MultiPolygon> {
boolean_op(subject, clipping, Operation::Xor)
}
fn boolean_op(subject: Geometry, clipping: Geometry, operation: Operation) -> Option<MultiPolygon> {
let subject_mp = subject.into_multi();
let clipping_mp = clipping.into_multi();
if subject_mp.is_empty() || clipping_mp.is_empty() {
return match operation {
Operation::Intersection => None,
Operation::Difference => Some(subject_mp),
Operation::Union | Operation::Xor => Some(if subject_mp.is_empty() {
clipping_mp
} else {
subject_mp
}),
};
}
let mut sbbox: BBox = [
f64::INFINITY,
f64::INFINITY,
f64::NEG_INFINITY,
f64::NEG_INFINITY,
];
let mut cbbox: BBox = [
f64::INFINITY,
f64::INFINITY,
f64::NEG_INFINITY,
f64::NEG_INFINITY,
];
let mut arena: Vec<SweepEvent> = Vec::new();
let mut queue = EventQueue::new();
fill_queue(
&mut arena,
&mut queue,
&subject_mp,
&clipping_mp,
&mut sbbox,
&mut cbbox,
operation,
);
let disjoint =
sbbox[0] > cbbox[2] || cbbox[0] > sbbox[2] || sbbox[1] > cbbox[3] || cbbox[1] > sbbox[3];
if disjoint {
return match operation {
Operation::Intersection => None,
Operation::Difference => Some(subject_mp),
Operation::Union | Operation::Xor => {
let mut combined = subject_mp;
combined.extend(clipping_mp);
Some(combined)
}
};
}
let sorted_events = subdivide_segments(&mut arena, &mut queue, sbbox, cbbox, operation);
let contours = connect_edges(&mut arena, &sorted_events);
let mut polygons: MultiPolygon = Vec::new();
for contour in &contours {
if !contour.is_exterior() {
continue;
}
let mut rings: Polygon = Vec::with_capacity(1 + contour.hole_ids.len());
rings.push(contour.points.clone());
for &hole_id in &contour.hole_ids {
rings.push(contours[hole_id].points.clone());
}
polygons.push(rings);
}
Some(polygons)
}