axiolid_reference/
polygon.rs1use axiolid_contracts::{GeomError, GeomResult, Sign};
7use axiolid_core::{Point2, Scalar};
8
9use crate::orient2d;
10
11pub fn signed_area2(ring: &[Point2]) -> Scalar {
13 let mut acc = 0.0;
14 for i in 0..ring.len() {
15 let a = ring[i];
16 let b = ring[(i + 1) % ring.len()];
17 acc += a.x * b.y - b.x * a.y;
18 }
19 acc
20}
21
22pub fn ring_orientation(ring: &[Point2]) -> Option<Sign> {
24 if ring.len() < 3 {
25 return None;
26 }
27 match signed_area2(ring) {
28 a if a > 0.0 => Some(Sign::Positive),
29 a if a < 0.0 => Some(Sign::Negative),
30 _ => None,
31 }
32}
33
34fn sign_of(a: Point2, b: Point2, c: Point2) -> Sign {
40 orient2d(a, b, c)
41 .sign()
42 .expect("orient2d is certain for finite coordinates, which the caller checked")
43}
44
45fn blocks_ear(a: Point2, b: Point2, c: Point2, p: Point2) -> bool {
52 let s1 = sign_of(a, b, p);
53 let s2 = sign_of(b, c, p);
54 let s3 = sign_of(c, a, p);
55 s1 != Sign::Negative && s2 != Sign::Negative && s3 != Sign::Negative
56}
57
58fn is_ear(ring: &[Point2], indices: &[usize], at: usize) -> bool {
60 let n = indices.len();
61 let (ia, ib, ic) = (
62 indices[(at + n - 1) % n],
63 indices[at],
64 indices[(at + 1) % n],
65 );
66 let (a, b, c) = (ring[ia], ring[ib], ring[ic]);
67
68 if sign_of(a, b, c) != Sign::Positive {
71 return false;
72 }
73
74 !indices
81 .iter()
82 .filter(|&&idx| idx != ia && idx != ib && idx != ic)
83 .map(|&idx| ring[idx])
84 .filter(|q| *q != a && *q != b && *q != c)
85 .any(|q| blocks_ear(a, b, c, q))
86}
87
88pub fn triangulate_simple(ring: &[Point2]) -> GeomResult<Vec<[u32; 3]>> {
99 if ring.iter().any(|p| !(p.x.is_finite() && p.y.is_finite())) {
100 return Err(GeomError::InvalidInput(
101 "ring has a non-finite coordinate".to_owned(),
102 ));
103 }
104 if ring.len() < 3 {
105 return Err(GeomError::Degenerate(format!(
106 "ring has {} vertices, need at least 3",
107 ring.len()
108 )));
109 }
110 let mut indices: Vec<usize> = (0..ring.len()).collect();
111 let mut out = Vec::with_capacity(ring.len().saturating_sub(2));
112
113 let mut at = 0usize;
117 let mut guard = 0usize;
118 while indices.len() > 3 {
119 if guard > indices.len() {
120 return Err(GeomError::Degenerate(
121 "no ear found; polygon is self-intersecting or degenerate".to_owned(),
122 ));
123 }
124 if is_ear(ring, &indices, at % indices.len()) {
125 let n = indices.len();
126 let cur = at % n;
127 out.push([
128 indices[(cur + n - 1) % n] as u32,
129 indices[cur] as u32,
130 indices[(cur + 1) % n] as u32,
131 ]);
132 indices.remove(cur);
133 at = cur;
134 guard = 0;
135 } else {
136 at += 1;
137 guard += 1;
138 }
139 }
140 let (a, b, c) = (ring[indices[0]], ring[indices[1]], ring[indices[2]]);
143 if sign_of(a, b, c) != Sign::Positive {
144 return Err(GeomError::Degenerate(
145 "final triangle is not positively wound; polygon is self-intersecting or degenerate"
146 .to_owned(),
147 ));
148 }
149 out.push([indices[0] as u32, indices[1] as u32, indices[2] as u32]);
150 Ok(out)
151}