axiolid_exact_arrangement/
faces.rs1use axiolid_contracts::{ExecutionOptions, GeomError, GeomResult, Sign};
28
29use crate::arrangement::{Arrangement, PairBudget, PieceId, VertexId};
30use crate::points::{inconsistent, ExactPoints, Location, PointId, RingBox};
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub struct FaceId(pub usize);
35
36#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct Faces {
39 sides: Vec<[FaceId; 2]>,
41 count: usize,
42}
43
44impl Faces {
45 pub const UNBOUNDED: FaceId = FaceId(0);
47
48 pub fn count(&self) -> usize {
50 self.count
51 }
52
53 pub fn left(&self, piece: PieceId) -> FaceId {
57 self.sides[piece.0][0]
58 }
59
60 pub fn right(&self, piece: PieceId) -> FaceId {
63 self.sides[piece.0][1]
64 }
65}
66
67fn half_edge(piece: PieceId, forward: bool) -> usize {
70 2 * piece.0 + usize::from(!forward)
71}
72
73fn find(parent: &mut [usize], mut item: usize) -> usize {
75 while parent[item] != item {
76 parent[item] = parent[parent[item]];
77 item = parent[item];
78 }
79 item
80}
81
82pub fn faces(
94 points: &ExactPoints,
95 arrangement: &Arrangement,
96 budget: &mut PairBudget,
97 options: &ExecutionOptions,
98) -> GeomResult<Faces> {
99 let piece_count = arrangement.pieces.len();
100 let mut next = vec![usize::MAX; 2 * piece_count];
101 let mut fans = Vec::with_capacity(arrangement.vertices.len());
102 for vertex in 0..arrangement.vertices.len() {
103 options.check_cancelled()?;
104 let fan = arrangement.fan(points, VertexId(vertex))?;
105 for (index, &(piece, forward)) in fan.iter().enumerate() {
106 let (turn, turn_forward) = fan[(index + fan.len() - 1) % fan.len()];
109 next[half_edge(piece, forward) ^ 1] = half_edge(turn, turn_forward);
110 }
111 fans.push(fan);
112 }
113 let tail = |half: usize| {
114 let ends = arrangement.pieces[half / 2].ends;
115 ends[usize::from(half % 2 == 1)].0
116 };
117
118 let mut contour_of = vec![usize::MAX; 2 * piece_count];
120 let mut contours = Vec::<Vec<usize>>::new();
121 for start in 0..2 * piece_count {
122 if contour_of[start] != usize::MAX {
123 continue;
124 }
125 let mut walk = Vec::new();
126 let mut half = start;
127 loop {
128 contour_of[half] = contours.len();
129 walk.push(half);
130 half = next[half];
131 if half == start {
132 break;
133 }
134 if half == usize::MAX || contour_of[half] != usize::MAX {
135 return Err(inconsistent());
136 }
137 }
138 contours.push(walk);
139 }
140
141 let mut parent: Vec<usize> = (0..arrangement.vertices.len()).collect();
143 for piece in &arrangement.pieces {
144 let (low, high) = (
145 find(&mut parent, piece.ends[0].0),
146 find(&mut parent, piece.ends[1].0),
147 );
148 parent[low.max(high)] = low.min(high);
149 }
150 let mut extreme = vec![usize::MAX; arrangement.vertices.len()];
151 for (vertex, fan) in fans.iter().enumerate() {
152 if fan.is_empty() {
153 continue;
154 }
155 let root = find(&mut parent, vertex);
156 let best = extreme[root];
157 if best == usize::MAX
158 || points
159 .lexicographic(
160 arrangement.vertices[vertex].point,
161 arrangement.vertices[best].point,
162 )?
163 .is_lt()
164 {
165 extreme[root] = vertex;
166 }
167 }
168 let roots: Vec<usize> = (0..extreme.len())
169 .filter(|&root| extreme[root] != usize::MAX)
170 .collect();
171
172 let mut is_outer = vec![false; contours.len()];
174 for &root in &roots {
175 let vertex = extreme[root];
176 let fan = &fans[vertex];
177 let here = arrangement.vertices[vertex].point;
178 let far = |&(piece, forward): &(PieceId, bool)| {
179 arrangement.vertices[arrangement.pieces[piece.0].ends[usize::from(forward)].0].point
180 };
181 let mut wide = None;
182 for (index, spoke) in fan.iter().enumerate() {
183 let after = &fan[(index + 1) % fan.len()];
184 let turns_clockwise =
185 fan.len() == 1 || points.orient(here, far(spoke), far(after))? == Sign::Negative;
186 if turns_clockwise {
187 if wide.is_some() {
188 return Err(GeomError::Degenerate(
189 "a component has two wedges wider than a half turn at its extreme vertex"
190 .into(),
191 ));
192 }
193 wide = Some(index);
194 }
195 }
196 let index = wide.ok_or_else(|| {
197 GeomError::Degenerate("a component has no wedge wider than a half turn".into())
198 })?;
199 let (piece, forward) = fan[index];
200 is_outer[contour_of[half_edge(piece, forward)]] = true;
201 }
202
203 let mut face_of = vec![0usize; contours.len()];
205 let mut count = 1;
206 for (contour, outer) in is_outer.iter().enumerate() {
207 if !outer {
208 face_of[contour] = count;
209 count += 1;
210 }
211 }
212 if roots.len() > 1 {
213 let walks: Vec<Vec<PointId>> = contours
214 .iter()
215 .map(|walk| {
216 walk.iter()
217 .map(|&half| arrangement.vertices[tail(half)].point)
218 .collect()
219 })
220 .collect();
221 let boxes: Vec<RingBox> = walks.iter().map(|walk| points.ring_box(walk)).collect();
222 let component_of = |contour: usize| find_root(&parent, tail(contours[contour][0]));
223 let outer_contours: Vec<usize> = (0..contours.len()).filter(|&c| is_outer[c]).collect();
224 for &outer in &outer_contours {
225 options.check_cancelled()?;
226 let root = component_of(outer);
227 let vertex = extreme[root];
228 let here = arrangement.vertices[vertex].point;
229 let here_box = points.ring_box(&[here]);
230 let mut innermost: Option<(usize, usize)> = None;
231 for (contour, walk) in walks.iter().enumerate() {
232 if is_outer[contour]
233 || component_of(contour) == root
234 || !here_box.possibly_within(&boxes[contour])
235 {
236 continue;
237 }
238 options.check_cancelled()?;
239 budget.count()?;
240 match points.locate(here, walk)? {
241 Location::Inside => {}
242 Location::Outside => continue,
243 Location::Boundary => return Err(inconsistent()),
244 }
245 let holder = extreme[component_of(contour)];
246 let deeper = match innermost {
247 None => true,
248 Some((_, known)) => points
249 .lexicographic(
250 arrangement.vertices[holder].point,
251 arrangement.vertices[known].point,
252 )?
253 .is_gt(),
254 };
255 if deeper {
256 innermost = Some((contour, holder));
257 }
258 }
259 if let Some((contour, _)) = innermost {
260 face_of[outer] = face_of[contour];
261 }
262 }
263 }
264
265 let sides = (0..piece_count)
266 .map(|piece| {
267 [
268 FaceId(face_of[contour_of[2 * piece]]),
269 FaceId(face_of[contour_of[2 * piece + 1]]),
270 ]
271 })
272 .collect();
273 Ok(Faces { sides, count })
274}
275
276fn find_root(parent: &[usize], mut item: usize) -> usize {
278 while parent[item] != item {
279 item = parent[item];
280 }
281 item
282}