use crate::arrangement::{Arrangement, InEdge, Noding};
use crate::assemble::{DirEdge, assemble};
use crate::error::{Error, Result};
use crate::geom::{
Path, Point, PolyTree, Polygon, PolygonSet, Ring, TaggedPath, TaggedPolygon, TaggedRing,
};
use crate::sweep::sweep_events;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FillRule {
EvenOdd,
#[default]
NonZero,
Positive,
Negative,
}
impl FillRule {
#[inline]
pub fn is_inside(self, w: i32) -> bool {
match self {
FillRule::EvenOdd => w & 1 != 0,
FillRule::NonZero => w != 0,
FillRule::Positive => w > 0,
FillRule::Negative => w < 0,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Op {
#[default]
Union,
Intersection,
Difference,
Xor,
}
impl Op {
#[inline]
fn apply(self, s: bool, c: bool) -> bool {
match self {
Op::Union => s || c,
Op::Intersection => s && c,
Op::Difference => s && !c,
Op::Xor => s != c,
}
}
}
pub type VertexVisitor<'a> = dyn FnMut(&[Point], Option<&[u64]>) + 'a;
pub trait RingSource {
fn visit_rings(&self, f: &mut VertexVisitor<'_>);
}
impl RingSource for Ring {
fn visit_rings(&self, f: &mut VertexVisitor<'_>) {
f(&self.0, None)
}
}
impl RingSource for Vec<Point> {
fn visit_rings(&self, f: &mut VertexVisitor<'_>) {
f(self, None)
}
}
impl RingSource for TaggedRing {
fn visit_rings(&self, f: &mut VertexVisitor<'_>) {
f(&self.points, Some(&self.tags))
}
}
impl RingSource for Polygon {
fn visit_rings(&self, f: &mut VertexVisitor<'_>) {
for r in self.rings() {
f(&r.0, None)
}
}
}
impl RingSource for TaggedPolygon {
fn visit_rings(&self, f: &mut VertexVisitor<'_>) {
f(&self.outer.points, Some(&self.outer.tags));
for h in &self.holes {
f(&h.points, Some(&h.tags));
}
}
}
impl RingSource for PolyTree {
fn visit_rings(&self, f: &mut VertexVisitor<'_>) {
for n in &self.nodes {
f(&n.ring.0, Some(&n.tags))
}
}
}
impl<T: RingSource> RingSource for [T] {
fn visit_rings(&self, f: &mut VertexVisitor<'_>) {
for x in self {
x.visit_rings(f)
}
}
}
impl<T: RingSource> RingSource for Vec<T> {
fn visit_rings(&self, f: &mut VertexVisitor<'_>) {
self.as_slice().visit_rings(f)
}
}
impl<T: RingSource, const N: usize> RingSource for [T; N] {
fn visit_rings(&self, f: &mut VertexVisitor<'_>) {
self.as_slice().visit_rings(f)
}
}
impl<T: RingSource + ?Sized> RingSource for &T {
fn visit_rings(&self, f: &mut VertexVisitor<'_>) {
(**self).visit_rings(f)
}
}
#[derive(Clone, Debug, Default)]
pub struct Boolean {
edges: Vec<InEdge>,
fill: [FillRule; 2],
op: Op,
keep_collinear: bool,
error: Option<Error>,
}
impl Boolean {
pub fn new() -> Self {
Self::default()
}
pub fn clear(&mut self) {
self.edges.clear();
self.error = None;
}
pub fn subject(mut self, rings: &(impl RingSource + ?Sized), rule: FillRule) -> Self {
self.add_subject(rings, rule);
self
}
pub fn clip(mut self, rings: &(impl RingSource + ?Sized), rule: FillRule) -> Self {
self.add_clip(rings, rule);
self
}
pub fn op(mut self, op: Op) -> Self {
self.op = op;
self
}
pub fn keep_collinear(mut self, keep: bool) -> Self {
self.keep_collinear = keep;
self
}
pub fn add_subject(&mut self, rings: &(impl RingSource + ?Sized), rule: FillRule) -> &mut Self {
self.fill[0] = rule;
self.add_rings(rings, 0);
self
}
pub fn add_clip(&mut self, rings: &(impl RingSource + ?Sized), rule: FillRule) -> &mut Self {
self.fill[1] = rule;
self.add_rings(rings, 1);
self
}
pub fn set_op(&mut self, op: Op) -> &mut Self {
self.op = op;
self
}
pub fn set_keep_collinear(&mut self, keep: bool) -> &mut Self {
self.keep_collinear = keep;
self
}
fn add_rings(&mut self, rings: &(impl RingSource + ?Sized), operand: u8) {
let edges = &mut self.edges;
let error = &mut self.error;
rings.visit_rings(&mut |pts, tags| {
let n = pts.len();
if error.is_none()
&& let Some(&p) = pts.iter().find(|p| !p.in_range())
{
*error = Some(Error::CoordinateOutOfRange(p));
}
for i in 0..n {
let a = pts[i];
let b = pts[(i + 1) % n];
if a != b {
let tag = tags.and_then(|t| t.get(i)).copied().unwrap_or(0);
edges.push(InEdge { a, b, tag, operand });
}
}
});
}
pub fn execute(&mut self) -> Result<PolygonSet> {
Ok(self.execute_tree()?.to_polygon_set())
}
pub fn execute_tagged(&mut self) -> Result<Vec<TaggedPolygon>> {
Ok(self.execute_tree()?.to_tagged_polygons())
}
pub fn execute_tree(&mut self) -> Result<PolyTree> {
if let Some(e) = &self.error {
return Err(e.clone());
}
if self.edges.len() > u32::MAX as usize / 2 {
return Err(Error::TooLarge);
}
let (fill, op) = (self.fill, self.op);
let boundary = compute(&self.edges, |w| {
op.apply(fill[0].is_inside(w[0]), fill[1].is_inside(w[1]))
});
Ok(assemble(boundary, self.keep_collinear))
}
}
struct Regions {
parent: Vec<u32>,
}
impl Regions {
fn add(&mut self) -> u32 {
self.parent.push(self.parent.len() as u32);
self.parent.len() as u32 - 1
}
fn find(&mut self, mut x: u32) -> u32 {
while self.parent[x as usize] != x {
let p = self.parent[self.parent[x as usize] as usize];
self.parent[x as usize] = p;
x = p;
}
x
}
fn union(&mut self, a: u32, b: u32) {
let (a, b) = (self.find(a), self.find(b));
if a != b {
let (lo, hi) = if a < b { (a, b) } else { (b, a) };
self.parent[hi as usize] = lo;
}
}
}
fn compute(edges: &[InEdge], inside: impl Fn([i32; 2]) -> bool) -> Vec<DirEdge> {
let arr = Arrangement::build_unwound(edges);
let n = arr.edges.len();
let segs: Vec<(Point, Point)> = arr.edges.iter().map(|e| (e.lo, e.hi)).collect();
let mut below_w = vec![[0i32; 2]; n];
let mut gap_above = vec![0u32; n];
let mut reg = Regions { parent: vec![0] };
let mut out: Vec<DirEdge> = Vec::new();
let delta = |k: usize| arr.edges[k].delta;
sweep_events(&segs, |below, _, ending, starting| {
let g_below = below.map_or(0, |b| gap_above[b as usize]);
let g_above = ending.last().map_or(g_below, |&e| gap_above[e as usize]);
if starting.is_empty() {
if !ending.is_empty() {
reg.union(g_below, g_above);
}
return;
}
let mut gb = g_below;
let mut wb = below.map_or([0, 0], |b| {
let b = b as usize;
[below_w[b][0] + delta(b)[0], below_w[b][1] + delta(b)[1]]
});
let last = starting.end - 1;
for k in starting {
let ku = k as usize;
let d = delta(ku);
let wa = [wb[0] + d[0], wb[1] + d[1]];
let ga = if k == last { g_above } else { reg.add() };
below_w[ku] = wb;
gap_above[ku] = ga;
let (ib, ia) = (inside(wb), inside(wa));
if ib == ia {
reg.union(gb, ga);
} else {
let e = &arr.edges[ku];
let (from, to) = if ia { (e.lo, e.hi) } else { (e.hi, e.lo) };
out.push(DirEdge {
from,
to,
tag: e.tag,
below: gb,
above: ga,
});
}
gb = ga;
wb = wa;
}
});
for e in out.iter_mut() {
e.below = reg.find(e.below);
e.above = reg.find(e.above);
}
out
}
pub fn boolean(
op: Op,
subject: &(impl RingSource + ?Sized),
clip: &(impl RingSource + ?Sized),
rule: FillRule,
) -> Result<PolygonSet> {
Boolean::new()
.subject(subject, rule)
.clip(clip, rule)
.op(op)
.execute()
}
pub fn union_all(rings: &(impl RingSource + ?Sized), rule: FillRule) -> Result<PolygonSet> {
Boolean::new().subject(rings, rule).execute()
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ClippedPaths {
pub inside: Vec<TaggedPath>,
pub outside: Vec<TaggedPath>,
}
impl ClippedPaths {
pub fn inside_paths(&self) -> Vec<Path> {
self.inside.iter().map(|p| Path(p.points.clone())).collect()
}
pub fn outside_paths(&self) -> Vec<Path> {
self.outside
.iter()
.map(|p| Path(p.points.clone()))
.collect()
}
}
pub trait PathSource {
fn visit_paths(&self, f: &mut VertexVisitor<'_>);
}
impl PathSource for Path {
fn visit_paths(&self, f: &mut VertexVisitor<'_>) {
f(&self.0, None)
}
}
impl PathSource for TaggedPath {
fn visit_paths(&self, f: &mut VertexVisitor<'_>) {
f(&self.points, Some(&self.tags))
}
}
impl<T: PathSource> PathSource for [T] {
fn visit_paths(&self, f: &mut VertexVisitor<'_>) {
for x in self {
x.visit_paths(f)
}
}
}
impl<T: PathSource> PathSource for Vec<T> {
fn visit_paths(&self, f: &mut VertexVisitor<'_>) {
self.as_slice().visit_paths(f)
}
}
impl<T: PathSource + ?Sized> PathSource for &T {
fn visit_paths(&self, f: &mut VertexVisitor<'_>) {
(**self).visit_paths(f)
}
}
pub fn clip_paths(
paths: &(impl PathSource + ?Sized),
clip: &(impl RingSource + ?Sized),
rule: FillRule,
) -> Result<ClippedPaths> {
let mut eng = Boolean::new();
eng.add_clip(clip, rule);
if let Some(e) = eng.error.take() {
return Err(e);
}
let mut edges = core::mem::take(&mut eng.edges);
let mut path_starts: Vec<usize> = Vec::new();
let mut err = None;
paths.visit_paths(&mut |pts, tags| {
path_starts.push(edges.len());
if err.is_none()
&& let Some(&p) = pts.iter().find(|p| !p.in_range())
{
err = Some(Error::CoordinateOutOfRange(p));
}
for (i, w) in pts.windows(2).enumerate() {
if w[0] != w[1] {
let tag = tags.and_then(|t| t.get(i)).copied().unwrap_or(0);
edges.push(InEdge {
a: w[0],
b: w[1],
tag,
operand: 2,
});
}
}
});
if let Some(e) = err {
return Err(e);
}
let Ok(arr) = Arrangement::build(&edges, Noding::Snap) else {
unreachable!("snap rounding never fails")
};
let mut inside_flag = vec![false; arr.open_frags.len()];
for (k, e) in arr.edges.iter().enumerate() {
if let Some(o) = e.open {
let (wb, wa) = arr.sides(k);
inside_flag[o as usize] = rule.is_inside(wb[1]) || rule.is_inside(wa[1]);
}
}
let mut res = ClippedPaths::default();
let mut cur: Option<(bool, TaggedPath)> = None;
let mut path_idx = 0usize;
let mut cur_path = usize::MAX;
for (k, fr) in arr.open_frags.iter().enumerate() {
let o = (fr.a, fr.b, edges[fr.src as usize].tag, fr.src);
while path_idx < path_starts.len() && path_starts[path_idx] <= o.3 as usize {
path_idx += 1;
}
let pid = path_idx - 1;
let ins = inside_flag[k];
let cont = matches!(&cur, Some((f, p)) if *f == ins && cur_path == pid && p.points.last() == Some(&o.0));
if !cont {
if let Some((f, p)) = cur.take() {
if f {
res.inside.push(p)
} else {
res.outside.push(p)
}
}
cur = Some((
ins,
TaggedPath {
points: vec![o.0],
tags: Vec::new(),
},
));
cur_path = pid;
}
let p = &mut cur.as_mut().unwrap().1;
p.points.push(o.1);
p.tags.push(o.2);
}
if let Some((f, p)) = cur.take() {
if f {
res.inside.push(p)
} else {
res.outside.push(p)
}
}
Ok(res)
}