1mod build;
57mod check;
58mod edit;
59
60use core::fmt;
61
62use axiolid_core::Point3;
63
64use crate::TriMesh;
65
66pub use build::HalfedgeBuildError;
67pub use check::HalfedgeInvariantError;
68pub use edit::HalfedgeEditError;
69
70const NONE: u32 = u32::MAX;
72
73macro_rules! element_id {
74 ($(#[$meta:meta])* $name:ident) => {
75 $(#[$meta])*
76 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
77 pub struct $name(u32);
78
79 impl $name {
80 #[must_use]
82 pub const fn new(index: u32) -> Self {
83 Self(index)
84 }
85
86 #[must_use]
88 pub const fn index(self) -> usize {
89 self.0 as usize
90 }
91
92 #[must_use]
94 pub const fn get(self) -> u32 {
95 self.0
96 }
97 }
98
99 impl fmt::Display for $name {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 write!(f, "{}", self.0)
102 }
103 }
104 };
105}
106
107element_id!(
108 VertexId
110);
111element_id!(
112 HalfedgeId
114);
115element_id!(
116 EdgeId
118);
119element_id!(
120 FaceId
122);
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126struct Link {
127 next: u32,
128 prev: u32,
129 target: u32,
130 face: u32,
132}
133
134const DEAD_LINK: Link = Link {
135 next: NONE,
136 prev: NONE,
137 target: NONE,
138 face: NONE,
139};
140
141#[derive(Debug, Clone, PartialEq)]
150pub struct HalfedgeMesh {
151 positions: Vec<Point3>,
152 vertex_out: Vec<u32>,
154 vertex_live: Vec<bool>,
155 links: Vec<Link>,
156 edge_live: Vec<bool>,
157 face_halfedge: Vec<u32>,
158 face_live: Vec<bool>,
159 live_vertices: usize,
160 live_edges: usize,
161 live_faces: usize,
162}
163
164#[derive(Debug, Clone, Default, PartialEq, Eq)]
169pub struct HalfedgeRemap {
170 pub vertices: Vec<Option<VertexId>>,
172 pub edges: Vec<Option<EdgeId>>,
174 pub faces: Vec<Option<FaceId>>,
176}
177
178impl HalfedgeMesh {
179 pub fn from_tri_mesh(mesh: &TriMesh) -> Result<Self, HalfedgeBuildError> {
190 if !mesh.indices.len().is_multiple_of(3) {
191 return Err(HalfedgeBuildError::IncompleteTriangle {
192 index_count: mesh.indices.len(),
193 });
194 }
195 let triangles: Vec<[u32; 3]> = mesh.triangles().collect();
196 Self::from_faces(mesh.positions.clone(), &triangles)
197 }
198
199 pub fn from_faces<F: AsRef<[u32]>>(
207 positions: Vec<Point3>,
208 faces: &[F],
209 ) -> Result<Self, HalfedgeBuildError> {
210 build::build(positions, faces)
211 }
212
213 pub fn to_tri_mesh(&self) -> Result<TriMesh, HalfedgeEditError> {
226 let mut remap = vec![NONE; self.positions.len()];
227 let mut positions = Vec::with_capacity(self.live_vertices);
228 for v in self.vertices() {
229 remap[v.index()] = positions.len() as u32;
230 positions.push(self.positions[v.index()]);
231 }
232 let mut indices = Vec::with_capacity(self.live_faces * 3);
233 for f in self.faces() {
234 let degree = self.face_degree(f);
235 if degree != 3 {
236 return Err(HalfedgeEditError::NotATriangle { face: f, degree });
237 }
238 let h = self.face_halfedge(f);
239 for corner in [self.source(h), self.target(h), self.target(self.next(h))] {
240 indices.push(remap[corner.index()]);
241 }
242 }
243 Ok(TriMesh::new(positions, indices))
244 }
245
246 #[must_use]
250 pub fn vertex_count(&self) -> usize {
251 self.live_vertices
252 }
253
254 #[must_use]
256 pub fn edge_count(&self) -> usize {
257 self.live_edges
258 }
259
260 #[must_use]
262 pub fn halfedge_count(&self) -> usize {
263 self.live_edges * 2
264 }
265
266 #[must_use]
268 pub fn face_count(&self) -> usize {
269 self.live_faces
270 }
271
272 #[must_use]
274 pub fn euler_characteristic(&self) -> i64 {
275 self.live_vertices as i64 - self.live_edges as i64 + self.live_faces as i64
276 }
277
278 #[must_use]
280 pub fn contains_vertex(&self, v: VertexId) -> bool {
281 self.vertex_live.get(v.index()).copied().unwrap_or(false)
282 }
283
284 #[must_use]
286 pub fn contains_edge(&self, e: EdgeId) -> bool {
287 self.edge_live.get(e.index()).copied().unwrap_or(false)
288 }
289
290 #[must_use]
292 pub fn contains_halfedge(&self, h: HalfedgeId) -> bool {
293 self.contains_edge(EdgeId(h.0 / 2))
294 }
295
296 #[must_use]
298 pub fn contains_face(&self, f: FaceId) -> bool {
299 self.face_live.get(f.index()).copied().unwrap_or(false)
300 }
301
302 pub fn vertices(&self) -> impl Iterator<Item = VertexId> + '_ {
306 live_ids(&self.vertex_live).map(VertexId)
307 }
308
309 pub fn edges(&self) -> impl Iterator<Item = EdgeId> + '_ {
311 live_ids(&self.edge_live).map(EdgeId)
312 }
313
314 pub fn halfedges(&self) -> impl Iterator<Item = HalfedgeId> + '_ {
316 self.edges()
317 .flat_map(|e| [HalfedgeId(e.0 * 2), HalfedgeId(e.0 * 2 + 1)])
318 }
319
320 pub fn faces(&self) -> impl Iterator<Item = FaceId> + '_ {
322 live_ids(&self.face_live).map(FaceId)
323 }
324
325 pub fn boundary_halfedges(&self) -> impl Iterator<Item = HalfedgeId> + '_ {
327 self.halfedges().filter(|&h| self.is_boundary_halfedge(h))
328 }
329
330 #[must_use]
332 pub fn boundary_loops(&self) -> Vec<HalfedgeId> {
333 let mut seen = vec![false; self.links.len()];
334 let mut loops = Vec::new();
335 for h in self.boundary_halfedges() {
336 if seen[h.index()] {
337 continue;
338 }
339 loops.push(h);
340 for g in self.loop_halfedges(h) {
341 seen[g.index()] = true;
342 }
343 }
344 loops
345 }
346
347 #[must_use]
351 pub fn position(&self, v: VertexId) -> Point3 {
352 self.positions[v.index()]
353 }
354
355 pub fn set_position(&mut self, v: VertexId, position: Point3) {
357 self.positions[v.index()] = position;
358 }
359
360 #[must_use]
362 pub fn positions(&self) -> &[Point3] {
363 &self.positions
364 }
365
366 #[must_use]
368 pub fn next(&self, h: HalfedgeId) -> HalfedgeId {
369 HalfedgeId(self.links[h.index()].next)
370 }
371
372 #[must_use]
374 pub fn prev(&self, h: HalfedgeId) -> HalfedgeId {
375 HalfedgeId(self.links[h.index()].prev)
376 }
377
378 #[must_use]
380 pub const fn opposite(&self, h: HalfedgeId) -> HalfedgeId {
381 HalfedgeId(h.0 ^ 1)
382 }
383
384 #[must_use]
386 pub fn target(&self, h: HalfedgeId) -> VertexId {
387 VertexId(self.links[h.index()].target)
388 }
389
390 #[must_use]
392 pub fn source(&self, h: HalfedgeId) -> VertexId {
393 VertexId(self.links[(h.0 ^ 1) as usize].target)
394 }
395
396 #[must_use]
398 pub fn face(&self, h: HalfedgeId) -> Option<FaceId> {
399 let face = self.links[h.index()].face;
400 (face != NONE).then_some(FaceId(face))
401 }
402
403 #[must_use]
405 pub const fn edge(&self, h: HalfedgeId) -> EdgeId {
406 EdgeId(h.0 / 2)
407 }
408
409 #[must_use]
415 pub fn edge_halfedge(&self, e: EdgeId, side: u32) -> HalfedgeId {
416 assert!(side < 2, "an edge has two halfedges");
417 HalfedgeId(e.0 * 2 + side)
418 }
419
420 #[must_use]
422 pub fn edge_vertices(&self, e: EdgeId) -> [VertexId; 2] {
423 let h = self.edge_halfedge(e, 0);
424 [self.source(h), self.target(h)]
425 }
426
427 #[must_use]
431 pub fn vertex_halfedge(&self, v: VertexId) -> Option<HalfedgeId> {
432 let h = self.vertex_out[v.index()];
433 (h != NONE).then_some(HalfedgeId(h))
434 }
435
436 #[must_use]
438 pub fn face_halfedge(&self, f: FaceId) -> HalfedgeId {
439 HalfedgeId(self.face_halfedge[f.index()])
440 }
441
442 #[must_use]
444 pub fn is_boundary_halfedge(&self, h: HalfedgeId) -> bool {
445 self.links[h.index()].face == NONE
446 }
447
448 #[must_use]
450 pub fn is_boundary_edge(&self, e: EdgeId) -> bool {
451 self.is_boundary_halfedge(HalfedgeId(e.0 * 2))
452 || self.is_boundary_halfedge(HalfedgeId(e.0 * 2 + 1))
453 }
454
455 #[must_use]
457 pub fn is_boundary_vertex(&self, v: VertexId) -> bool {
458 self.vertex_halfedge(v)
459 .is_some_and(|h| self.is_boundary_halfedge(h))
460 }
461
462 #[must_use]
464 pub fn is_isolated(&self, v: VertexId) -> bool {
465 self.vertex_out[v.index()] == NONE
466 }
467
468 #[must_use]
470 pub fn find_halfedge(&self, from: VertexId, to: VertexId) -> Option<HalfedgeId> {
471 self.outgoing_halfedges(from)
472 .find(|&h| self.target(h) == to)
473 }
474
475 pub fn outgoing_halfedges(&self, v: VertexId) -> impl Iterator<Item = HalfedgeId> + '_ {
481 let start = self.vertex_halfedge(v);
482 let mut current = start;
483 core::iter::from_fn(move || {
484 let h = current?;
485 let rotated = self.rotate(h);
486 current = (Some(rotated) != start).then_some(rotated);
487 Some(h)
488 })
489 }
490
491 pub fn incoming_halfedges(&self, v: VertexId) -> impl Iterator<Item = HalfedgeId> + '_ {
494 self.outgoing_halfedges(v).map(|h| self.opposite(h))
495 }
496
497 pub fn vertex_vertices(&self, v: VertexId) -> impl Iterator<Item = VertexId> + '_ {
499 self.outgoing_halfedges(v).map(|h| self.target(h))
500 }
501
502 pub fn vertex_faces(&self, v: VertexId) -> impl Iterator<Item = FaceId> + '_ {
504 self.outgoing_halfedges(v).filter_map(|h| self.face(h))
505 }
506
507 #[must_use]
509 pub fn degree(&self, v: VertexId) -> usize {
510 self.outgoing_halfedges(v).count()
511 }
512
513 pub fn loop_halfedges(&self, h: HalfedgeId) -> impl Iterator<Item = HalfedgeId> + '_ {
515 let mut current = Some(h);
516 core::iter::from_fn(move || {
517 let g = current?;
518 let next = self.next(g);
519 current = (next != h).then_some(next);
520 Some(g)
521 })
522 }
523
524 pub fn face_halfedges(&self, f: FaceId) -> impl Iterator<Item = HalfedgeId> + '_ {
526 self.loop_halfedges(self.face_halfedge(f))
527 }
528
529 pub fn face_vertices(&self, f: FaceId) -> impl Iterator<Item = VertexId> + '_ {
532 self.face_halfedges(f).map(|h| self.source(h))
533 }
534
535 pub fn face_faces(&self, f: FaceId) -> impl Iterator<Item = Option<FaceId>> + '_ {
537 self.face_halfedges(f).map(|h| self.face(self.opposite(h)))
538 }
539
540 #[must_use]
542 pub fn face_degree(&self, f: FaceId) -> usize {
543 self.face_halfedges(f).count()
544 }
545
546 pub fn compact(&mut self) -> HalfedgeRemap {
552 let vertices = dense_map(&self.vertex_live);
553 let edges = dense_map(&self.edge_live);
554 let faces = dense_map(&self.face_live);
555 let map_halfedge = |h: u32| -> u32 {
556 if h == NONE {
557 return NONE;
558 }
559 edges[(h / 2) as usize].map_or(NONE, |e| e * 2 + (h & 1))
560 };
561 let map = |table: &[Option<u32>], i: u32| -> u32 {
562 if i == NONE {
563 NONE
564 } else {
565 table[i as usize].unwrap_or(NONE)
566 }
567 };
568
569 let mut positions = Vec::with_capacity(self.live_vertices);
570 let mut vertex_out = Vec::with_capacity(self.live_vertices);
571 for (v, slot) in vertices.iter().enumerate() {
572 if slot.is_some() {
573 positions.push(self.positions[v]);
574 vertex_out.push(map_halfedge(self.vertex_out[v]));
575 }
576 }
577 let mut links = Vec::with_capacity(self.live_edges * 2);
578 for (h, link) in self.links.iter().enumerate() {
579 if edges[h / 2].is_some() {
580 links.push(Link {
581 next: map_halfedge(link.next),
582 prev: map_halfedge(link.prev),
583 target: map(&vertices, link.target),
584 face: map(&faces, link.face),
585 });
586 }
587 }
588 let mut face_halfedge = Vec::with_capacity(self.live_faces);
589 for (f, slot) in faces.iter().enumerate() {
590 if slot.is_some() {
591 face_halfedge.push(map_halfedge(self.face_halfedge[f]));
592 }
593 }
594
595 self.vertex_live = vec![true; positions.len()];
596 self.edge_live = vec![true; links.len() / 2];
597 self.face_live = vec![true; face_halfedge.len()];
598 self.positions = positions;
599 self.vertex_out = vertex_out;
600 self.links = links;
601 self.face_halfedge = face_halfedge;
602
603 HalfedgeRemap {
604 vertices: vertices.into_iter().map(|v| v.map(VertexId)).collect(),
605 edges: edges.into_iter().map(|e| e.map(EdgeId)).collect(),
606 faces: faces.into_iter().map(|f| f.map(FaceId)).collect(),
607 }
608 }
609
610 pub fn validate(&self) -> Result<(), HalfedgeInvariantError> {
617 check::validate(self)
618 }
619
620 fn rotate(&self, h: HalfedgeId) -> HalfedgeId {
624 self.opposite(self.prev(h))
625 }
626
627 fn link(&mut self, from: u32, to: u32) {
628 self.links[from as usize].next = to;
629 self.links[to as usize].prev = from;
630 }
631}
632
633fn live_ids(live: &[bool]) -> impl Iterator<Item = u32> + '_ {
634 live.iter()
635 .enumerate()
636 .filter(|(_, &alive)| alive)
637 .map(|(i, _)| i as u32)
638}
639
640fn dense_map(live: &[bool]) -> Vec<Option<u32>> {
641 let mut next = 0u32;
642 live.iter()
643 .map(|&alive| {
644 alive.then(|| {
645 next += 1;
646 next - 1
647 })
648 })
649 .collect()
650}