Skip to main content

tess2_rust/mesh/
tessellate.rs

1// Monotone-region and interior tessellation for Mesh.
2// Split from mod.rs to keep that file under the 967-line limit.
3
4use super::{Mesh, F_HEAD, INVALID};
5
6impl Mesh {
7    /// Tessellate a single monotone region (face).
8    /// The face must be a CCW-oriented simple polygon.
9    pub fn tessellate_mono_region(&mut self, face: super::FaceIdx) -> bool {
10        use crate::geom::{edge_sign, vert_leq};
11
12        let mut up = match self.face_edge(face) {
13            Some(edge) => edge,
14            None => return false,
15        };
16        let up_lnext = match self.edge_lnext(up) {
17            Some(edge) => edge,
18            None => return false,
19        };
20        let up_lnext_lnext = match self.edge_lnext(up_lnext) {
21            Some(edge) => edge,
22            None => return false,
23        };
24        if up_lnext == up || up_lnext_lnext == up {
25            return false; // degenerate face (< 3 edges) — skip instead of panic
26        }
27
28        // Find the edge whose origin vertex is rightmost (largest s).
29        // VertLeq(Dst, Org) means Dst <= Org (going left = bad), so we want
30        // to find an edge where the Org is the rightmost.
31        let max_ring_iters = self.edges.len() + 2;
32        let mut ring_iter = 0usize;
33        loop {
34            let (up_dst_s, up_dst_t) = match self.dst_coords(up) {
35                Some(coords) => coords,
36                None => return false,
37            };
38            let (up_org_s, up_org_t) = match self.org_coords(up) {
39                Some(coords) => coords,
40                None => return false,
41            };
42            if !vert_leq(up_dst_s, up_dst_t, up_org_s, up_org_t) {
43                break;
44            }
45            up = match self.lprev_checked(up) {
46                Some(edge) => edge,
47                None => return false,
48            };
49            ring_iter += 1;
50            if ring_iter > max_ring_iters {
51                return false; // degenerate face — all vertices co-sorted
52            }
53        }
54        ring_iter = 0;
55        loop {
56            let (up_org_s, up_org_t) = match self.org_coords(up) {
57                Some(coords) => coords,
58                None => return false,
59            };
60            let (up_dst_s, up_dst_t) = match self.dst_coords(up) {
61                Some(coords) => coords,
62                None => return false,
63            };
64            if !vert_leq(up_org_s, up_org_t, up_dst_s, up_dst_t) {
65                break;
66            }
67            up = match self.edge_lnext(up) {
68                Some(edge) => edge,
69                None => return false,
70            };
71            ring_iter += 1;
72            if ring_iter > max_ring_iters {
73                return false; // degenerate face — all vertices co-sorted
74            }
75        }
76
77        let mut lo = match self.lprev_checked(up) {
78            Some(edge) => edge,
79            None => return false,
80        };
81
82        let max_tess_iters = self.edges.len() * 2 + 4;
83        let mut outer_iter = 0usize;
84        while match self.edge_lnext(up) {
85            Some(edge) => edge != lo,
86            None => return false,
87        } {
88            outer_iter += 1;
89            if outer_iter > max_tess_iters {
90                return false; // degenerate region — guard against infinite triangulation
91            }
92            let (up_dst_s, up_dst_t) = match self.dst_coords(up) {
93                Some(coords) => coords,
94                None => return false,
95            };
96            let (lo_org_s, lo_org_t) = match self.org_coords(lo) {
97                Some(coords) => coords,
98                None => return false,
99            };
100            if vert_leq(up_dst_s, up_dst_t, lo_org_s, lo_org_t) {
101                // up->Dst is on the left; make triangles from lo->Org
102                let mut inner_iter = 0usize;
103                while match self.edge_lnext(lo) {
104                    Some(edge) => edge != up,
105                    None => return false,
106                } {
107                    inner_iter += 1;
108                    if inner_iter > max_tess_iters {
109                        return false;
110                    }
111                    let lo_lnext = match self.edge_lnext(lo) {
112                        Some(edge) => edge,
113                        None => return false,
114                    };
115                    let (lo_lnext_dst_s, lo_lnext_dst_t) = match self.dst_coords(lo_lnext) {
116                        Some(coords) => coords,
117                        None => return false,
118                    };
119                    let (lo_org2_s, lo_org2_t) = match self.org_coords(lo) {
120                        Some(coords) => coords,
121                        None => return false,
122                    };
123                    let (lo_dst_s, lo_dst_t) = match self.dst_coords(lo) {
124                        Some(coords) => coords,
125                        None => return false,
126                    };
127                    let goes_left = match self.edge_goes_left_checked(lo_lnext) {
128                        Some(value) => value,
129                        None => return false,
130                    };
131                    let sign_val = edge_sign(
132                        lo_org2_s,
133                        lo_org2_t,
134                        lo_dst_s,
135                        lo_dst_t,
136                        lo_lnext_dst_s,
137                        lo_lnext_dst_t,
138                    );
139                    if !goes_left && sign_val > 0.0 {
140                        break;
141                    }
142                    let temp = match self.connect(lo_lnext, lo) {
143                        Some(e) => e,
144                        None => return false,
145                    };
146                    lo = match self.valid_edge(temp ^ 1) {
147                        Some(edge) => edge,
148                        None => return false,
149                    };
150                }
151                lo = match self.lprev_checked(lo) {
152                    Some(edge) => edge,
153                    None => return false,
154                };
155            } else {
156                // lo->Org is on the left; make CCW triangles from up->Dst
157                let mut inner_iter = 0usize;
158                while match self.edge_lnext(lo) {
159                    Some(edge) => edge != up,
160                    None => return false,
161                } {
162                    inner_iter += 1;
163                    if inner_iter > max_tess_iters {
164                        return false;
165                    }
166                    let up_lprev = match self.lprev_checked(up) {
167                        Some(edge) => edge,
168                        None => return false,
169                    };
170                    let (up_lprev_org_s, up_lprev_org_t) = match self.org_coords(up_lprev) {
171                        Some(coords) => coords,
172                        None => return false,
173                    };
174                    let (up_dst2_s, up_dst2_t) = match self.dst_coords(up) {
175                        Some(coords) => coords,
176                        None => return false,
177                    };
178                    let (up_org2_s, up_org2_t) = match self.org_coords(up) {
179                        Some(coords) => coords,
180                        None => return false,
181                    };
182                    let goes_right = match self.edge_goes_right_checked(up_lprev) {
183                        Some(value) => value,
184                        None => return false,
185                    };
186                    let sign_val = edge_sign(
187                        up_dst2_s,
188                        up_dst2_t,
189                        up_org2_s,
190                        up_org2_t,
191                        up_lprev_org_s,
192                        up_lprev_org_t,
193                    );
194                    if !goes_right && sign_val < 0.0 {
195                        break;
196                    }
197                    let temp = match self.connect(up, up_lprev) {
198                        Some(e) => e,
199                        None => return false,
200                    };
201                    up = match self.valid_edge(temp ^ 1) {
202                        Some(edge) => edge,
203                        None => return false,
204                    };
205                }
206                up = match self.edge_lnext(up) {
207                    Some(edge) => edge,
208                    None => return false,
209                };
210            }
211        }
212
213        // Tessellate the remaining fan from the leftmost vertex.
214        let mut lo_lnext = match self.edge_lnext(lo) {
215            Some(edge) => edge,
216            None => return false,
217        };
218        if lo_lnext == up {
219            return false; // degenerate — no fan to tessellate
220        }
221        let mut fan_iter = 0usize;
222        while match self.edge_lnext(lo_lnext) {
223            Some(edge) => edge != up,
224            None => return false,
225        } {
226            fan_iter += 1;
227            if fan_iter > max_tess_iters {
228                return false;
229            }
230            let temp = match self.connect(lo_lnext, lo) {
231                Some(e) => e,
232                None => return false,
233            };
234            lo = match self.valid_edge(temp ^ 1) {
235                Some(edge) => edge,
236                None => return false,
237            };
238            lo_lnext = match self.edge_lnext(lo) {
239                Some(edge) => edge,
240                None => return false,
241            };
242        }
243
244        true
245    }
246
247    /// Tessellate all interior monotone regions.
248    pub fn tessellate_interior(&mut self) -> bool {
249        let mut f = self.faces[F_HEAD as usize].next;
250        while f != F_HEAD {
251            if f == INVALID || f as usize >= self.faces.len() {
252                return false;
253            }
254            let next = self.faces[f as usize].next;
255            if self.faces[f as usize].inside {
256                if !self.tessellate_mono_region(f) {
257                    // Mark as outside so the output extraction skips this face.
258                    // Leaving it inside=true would cause degenerate triangles with
259                    // wrong vertices to be emitted (the untriangulated polygon edges
260                    // get read as triangle vertices during output).
261                    self.faces[f as usize].inside = false;
262                }
263            }
264            f = next;
265        }
266        true
267    }
268
269    fn valid_edge(&self, edge: super::EdgeIdx) -> Option<super::EdgeIdx> {
270        if edge != INVALID && (edge as usize) < self.edges.len() {
271            Some(edge)
272        } else {
273            None
274        }
275    }
276
277    fn valid_vert(&self, vert: super::VertIdx) -> Option<super::VertIdx> {
278        if vert != INVALID && (vert as usize) < self.verts.len() {
279            Some(vert)
280        } else {
281            None
282        }
283    }
284
285    fn face_edge(&self, face: super::FaceIdx) -> Option<super::EdgeIdx> {
286        if face == INVALID || (face as usize) >= self.faces.len() {
287            return None;
288        }
289        self.valid_edge(self.faces[face as usize].an_edge)
290    }
291
292    fn edge_lnext(&self, edge: super::EdgeIdx) -> Option<super::EdgeIdx> {
293        let edge = self.valid_edge(edge)?;
294        self.valid_edge(self.edges[edge as usize].lnext)
295    }
296
297    fn dst_checked(&self, edge: super::EdgeIdx) -> Option<super::VertIdx> {
298        let sym = self.valid_edge(edge ^ 1)?;
299        self.valid_vert(self.edges[sym as usize].org)
300    }
301
302    fn org_coords(&self, edge: super::EdgeIdx) -> Option<(crate::geom::Real, crate::geom::Real)> {
303        let edge = self.valid_edge(edge)?;
304        let org = self.valid_vert(self.edges[edge as usize].org)?;
305        let vert = &self.verts[org as usize];
306        Some((vert.s, vert.t))
307    }
308
309    fn dst_coords(&self, edge: super::EdgeIdx) -> Option<(crate::geom::Real, crate::geom::Real)> {
310        let dst = self.dst_checked(edge)?;
311        let vert = &self.verts[dst as usize];
312        Some((vert.s, vert.t))
313    }
314
315    fn lprev_checked(&self, edge: super::EdgeIdx) -> Option<super::EdgeIdx> {
316        let edge = self.valid_edge(edge)?;
317        let onext = self.valid_edge(self.edges[edge as usize].onext)?;
318        self.valid_edge(onext ^ 1)
319    }
320
321    fn edge_goes_left_checked(&self, edge: super::EdgeIdx) -> Option<bool> {
322        let (ds, dt) = self.dst_coords(edge)?;
323        let (os, ot) = self.org_coords(edge)?;
324        Some(crate::geom::vert_leq(ds, dt, os, ot))
325    }
326
327    fn edge_goes_right_checked(&self, edge: super::EdgeIdx) -> Option<bool> {
328        let (os, ot) = self.org_coords(edge)?;
329        let (ds, dt) = self.dst_coords(edge)?;
330        Some(crate::geom::vert_leq(os, ot, ds, dt))
331    }
332}
333
334#[cfg(test)]
335mod tests {
336    use super::*;
337    use crate::mesh::{Face, HalfEdge};
338
339    #[test]
340    fn invalid_face_edge_returns_false() {
341        let mut mesh = Mesh::new();
342        mesh.faces.push(Face {
343            an_edge: INVALID,
344            inside: true,
345            ..Face::default()
346        });
347
348        assert!(!mesh.tessellate_mono_region(1));
349    }
350
351    #[test]
352    fn invalid_destination_vertex_returns_false() {
353        let mut mesh = Mesh::new();
354        mesh.faces.push(Face {
355            an_edge: 2,
356            inside: true,
357            ..Face::default()
358        });
359        mesh.edges.resize_with(7, HalfEdge::default);
360        mesh.edges[2].lnext = 4;
361        mesh.edges[4].lnext = 6;
362        mesh.edges[3].org = INVALID;
363
364        assert!(!mesh.tessellate_mono_region(1));
365    }
366}