1use crate::arrangement::{Arrangement, InEdge};
4use crate::geom::{Point, PointF, Polygon};
5use crate::node::node_exact;
6use crate::predicates::{crossing_f64, orient};
7use crate::query::{Location, locate_in_ring, ring_area2};
8use core::fmt;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
13pub struct RingId {
14 pub polygon: usize,
16 pub ring: usize,
18}
19
20#[derive(Clone, Debug, PartialEq)]
22#[non_exhaustive]
23pub enum ValidityError {
24 CoordinateOutOfRange(Point),
26 TooFewVertices(RingId),
28 DuplicateVertex(RingId, Point),
30 ZeroArea(RingId),
32 SelfIntersection(PointF),
34 SelfTouch(RingId, Point),
36 OverlappingEdges(Point, Point),
38 InvalidNesting(Point),
41 DisconnectedInterior(Point),
43 WrongOrientation(RingId),
45 NotCanonical(&'static str),
47}
48
49impl fmt::Display for ValidityError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 use ValidityError::*;
52 match self {
53 CoordinateOutOfRange(p) => write!(f, "coordinate out of range at ({}, {})", p.x, p.y),
54 TooFewVertices(r) => {
55 write!(f, "ring {}/{} has fewer than 3 vertices", r.polygon, r.ring)
56 }
57 DuplicateVertex(r, p) => write!(
58 f,
59 "ring {}/{} repeats vertex ({}, {})",
60 r.polygon, r.ring, p.x, p.y
61 ),
62 ZeroArea(r) => write!(f, "ring {}/{} has zero area", r.polygon, r.ring),
63 SelfIntersection(p) => write!(f, "self-intersection at ({}, {})", p.x, p.y),
64 SelfTouch(r, p) => write!(
65 f,
66 "ring {}/{} touches itself at ({}, {})",
67 r.polygon, r.ring, p.x, p.y
68 ),
69 OverlappingEdges(a, b) => write!(
70 f,
71 "overlapping edges along ({}, {})-({}, {})",
72 a.x, a.y, b.x, b.y
73 ),
74 InvalidNesting(p) => write!(f, "invalid nesting near ({}, {})", p.x, p.y),
75 DisconnectedInterior(p) => write!(f, "interior disconnected at ({}, {})", p.x, p.y),
76 WrongOrientation(r) => write!(
77 f,
78 "ring {}/{} has non-canonical orientation",
79 r.polygon, r.ring
80 ),
81 NotCanonical(s) => write!(f, "not canonical: {s}"),
82 }
83 }
84}
85
86impl std::error::Error for ValidityError {}
87
88pub fn validate(poly: &Polygon) -> Result<(), ValidityError> {
93 validate_set(core::slice::from_ref(poly))
94}
95
96pub fn validate_set(polys: &[Polygon]) -> Result<(), ValidityError> {
99 let mut edges: Vec<InEdge> = Vec::new();
101 let mut ring_of_edge: Vec<(usize, usize)> = Vec::new();
102 for (pi, poly) in polys.iter().enumerate() {
103 for (ri, ring) in poly.rings().enumerate() {
104 let id = RingId {
105 polygon: pi,
106 ring: ri,
107 };
108 let pts = &ring.0;
109 if let Some(&p) = pts.iter().find(|p| !p.in_range()) {
110 return Err(ValidityError::CoordinateOutOfRange(p));
111 }
112 if pts.len() < 3 {
113 return Err(ValidityError::TooFewVertices(id));
114 }
115 for i in 0..pts.len() {
116 if pts[i] == pts[(i + 1) % pts.len()] {
117 return Err(ValidityError::DuplicateVertex(id, pts[i]));
118 }
119 }
120 let a = ring_area2(pts);
121 if a == 0 {
122 return Err(ValidityError::ZeroArea(id));
123 }
124 let flip = (ri == 0) != (a > 0);
126 let n = pts.len();
127 for i in 0..n {
128 let (p, q) = (pts[i], pts[(i + 1) % n]);
129 let (p, q) = if flip { (q, p) } else { (p, q) };
130 edges.push(InEdge {
131 a: p,
132 b: q,
133 tag: ((pi as u64) << 32) | ri as u64,
134 operand: 0,
135 });
136 ring_of_edge.push((pi, ri));
137 }
138 }
139 }
140 let segs: Vec<(Point, Point)> = edges.iter().map(|e| (e.a, e.b)).collect();
141 let frags = match node_exact(&segs) {
142 Ok(f) => f,
143 Err(c) => {
144 let (a, b) = (segs[c.i as usize], segs[c.j as usize]);
145 let (x, y) = crossing_f64(a.0, a.1, b.0, b.1);
146 return Err(ValidityError::SelfIntersection(PointF::new(x, y)));
147 }
148 };
149 drop(segs);
150 let fr: Vec<(Point, Point)> = frags
152 .iter()
153 .map(|f| if f.a < f.b { (f.a, f.b) } else { (f.b, f.a) })
154 .collect();
155 let fr = crate::par::bucket_sort_by_x(fr, |e| e.0.x, |a, b| a.cmp(b));
156 for w in fr.windows(2) {
157 if w[0] == w[1] {
158 return Err(ValidityError::OverlappingEdges(w[0].0, w[0].1));
159 }
160 }
161 drop(fr);
162 let mut ring_ids: Vec<(usize, usize)> = ring_of_edge.clone();
165 ring_ids.dedup();
166 let mut gid_of_edge: Vec<u32> = Vec::with_capacity(ring_of_edge.len());
167 let mut g = 0u32;
168 for (k, r) in ring_of_edge.iter().enumerate() {
169 if k > 0 && ring_of_edge[k - 1] != *r {
170 g += 1;
171 }
172 gid_of_edge.push(g);
173 }
174 let mut vr: Vec<(Point, u32)> = Vec::with_capacity(frags.len() * 2);
175 for f in &frags {
176 let r = gid_of_edge[f.src as usize];
177 vr.push((f.a, r));
178 vr.push((f.b, r));
179 }
180 let vr = crate::par::bucket_sort_by_x(vr, |e| e.0.x, |a, b| a.cmp(b));
181 let vr: Vec<(Point, (usize, usize))> = vr
182 .into_iter()
183 .map(|(p, g)| (p, ring_ids[g as usize]))
184 .collect();
185 let mut uf = UnionFind::new(0);
188 let mut ring_node: std::collections::HashMap<(usize, usize), usize> =
189 std::collections::HashMap::new();
190 let mut rings_here: Vec<(usize, usize)> = Vec::new();
191 let mut i = 0;
192 while i < vr.len() {
193 let p = vr[i].0;
194 let mut j = i;
195 rings_here.clear();
196 while j < vr.len() && vr[j].0 == p {
197 let r = vr[j].1;
198 let mut k = j;
199 while k < vr.len() && vr[k].0 == p && vr[k].1 == r {
200 k += 1;
201 }
202 if k - j > 2 {
204 return Err(ValidityError::SelfTouch(
205 RingId {
206 polygon: r.0,
207 ring: r.1,
208 },
209 p,
210 ));
211 }
212 rings_here.push(r);
213 j = k;
214 }
215 let mut a = 0;
216 while a < rings_here.len() {
217 let mut b = a;
218 while b < rings_here.len() && rings_here[b].0 == rings_here[a].0 {
219 b += 1;
220 }
221 if b - a >= 2 {
222 let pn = uf.add();
223 for r in &rings_here[a..b] {
224 let rn = *ring_node.entry(*r).or_insert_with(|| uf.add());
225 if !uf.union(pn, rn) {
226 return Err(ValidityError::DisconnectedInterior(p));
227 }
228 }
229 }
230 a = b;
231 }
232 i = j;
233 }
234 drop(vr);
235 let arr = Arrangement::from_frags(&edges, frags);
236 for (k, e) in arr.edges.iter().enumerate() {
238 let wb = arr.below[k][0];
239 let wa = wb + e.delta[0];
240 if !(0..=1).contains(&wb) || !(0..=1).contains(&wa) {
241 return Err(ValidityError::InvalidNesting(e.lo));
242 }
243 }
244 for poly in polys {
246 for h in &poly.holes {
247 if !hole_inside(&poly.outer.0, &h.0) {
248 return Err(ValidityError::InvalidNesting(h.0[0]));
249 }
250 }
251 }
252 Ok(())
253}
254
255fn hole_inside(outer: &[Point], hole: &[Point]) -> bool {
258 for &p in hole {
259 match locate_in_ring(outer, p) {
260 Location::Inside => return true,
261 Location::Outside => return false,
262 Location::OnBoundary => {}
263 }
264 }
265 let dbl: Vec<Point> = outer.iter().map(|p| Point::new(2 * p.x, 2 * p.y)).collect();
267 let n = hole.len();
268 for i in 0..n {
269 let (a, b) = (hole[i], hole[(i + 1) % n]);
270 let m = Point::new(a.x + b.x, a.y + b.y);
271 match locate_in_ring(&dbl, m) {
272 Location::Inside => return true,
273 Location::Outside => return false,
274 Location::OnBoundary => {}
275 }
276 }
277 false
278}
279
280struct UnionFind {
281 parent: Vec<usize>,
282}
283
284impl UnionFind {
285 fn new(n: usize) -> Self {
286 UnionFind {
287 parent: (0..n).collect(),
288 }
289 }
290 fn add(&mut self) -> usize {
291 self.parent.push(self.parent.len());
292 self.parent.len() - 1
293 }
294 fn find(&mut self, mut x: usize) -> usize {
295 while self.parent[x] != x {
296 self.parent[x] = self.parent[self.parent[x]];
297 x = self.parent[x];
298 }
299 x
300 }
301 fn union(&mut self, a: usize, b: usize) -> bool {
303 let (ra, rb) = (self.find(a), self.find(b));
304 if ra == rb {
305 return false;
306 }
307 self.parent[ra] = rb;
308 true
309 }
310}
311
312pub fn check_canonical(polys: &[Polygon], collinear_removed: bool) -> Result<(), ValidityError> {
318 validate_set(polys)?;
319 let mut shared: Vec<Point> = Vec::new();
320 if collinear_removed {
321 let all: Vec<Point> = polys
322 .iter()
323 .flat_map(|p| p.rings().flat_map(|r| r.0.iter().copied()))
324 .collect();
325 let all = crate::par::bucket_sort_by_x(all, |p| p.x, |a, b| a.cmp(b));
326 for w in all.windows(2) {
327 if w[0] == w[1] && shared.last() != Some(&w[0]) {
328 shared.push(w[0]);
329 }
330 }
331 }
332 for (pi, poly) in polys.iter().enumerate() {
333 for (ri, ring) in poly.rings().enumerate() {
334 let id = RingId {
335 polygon: pi,
336 ring: ri,
337 };
338 if (ring_area2(&ring.0) > 0) != (ri == 0) {
339 return Err(ValidityError::WrongOrientation(id));
340 }
341 let min = ring.0.iter().min().copied();
342 if ring.0.first().copied() != min {
343 return Err(ValidityError::NotCanonical(
344 "ring does not start at its minimum vertex",
345 ));
346 }
347 if collinear_removed {
348 let n = ring.0.len();
349 for i in 0..n {
350 let (a, v, b) = (ring.0[(i + n - 1) % n], ring.0[i], ring.0[(i + 1) % n]);
351 if orient(a, v, b) == 0 && shared.binary_search(&v).is_err() {
352 return Err(ValidityError::NotCanonical("collinear vertex"));
353 }
354 }
355 }
356 }
357 if poly.holes.windows(2).any(|w| w[0].0 >= w[1].0) {
358 return Err(ValidityError::NotCanonical("holes not sorted"));
359 }
360 }
361 if polys.windows(2).any(|w| w[0].outer.0 >= w[1].outer.0) {
362 return Err(ValidityError::NotCanonical("polygons not sorted"));
363 }
364 Ok(())
365}