1mod elements;
48pub mod integrations;
49mod iter;
50mod ops;
51mod plane_slice;
52pub mod primitives;
53mod selection;
54#[cfg(feature = "serde")]
55mod serialize;
56pub mod utils;
57
58pub use elements::*;
59pub use iter::*;
60pub use plane_slice::*;
61pub use selection::*;
62
63use hashbrown::HashMap;
64use parry3d::partitioning::{Bvh, BvhWorkspace};
65
66use glam::Vec3;
67use slotmap::{SecondaryMap, SlotMap};
68use tracing::{error, instrument};
69
70#[cfg(feature = "rerun")]
71lazy_static::lazy_static! {
72 pub static ref RR: rerun::RecordingStream = rerun::RecordingStreamBuilder::new("mesh_graph").spawn().unwrap();
73}
74
75#[derive(Clone, Default)]
79#[cfg_attr(feature = "bevy", derive(bevy::prelude::Component))]
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81#[cfg_attr(
82 feature = "serde",
83 serde(from = "crate::serialize::MeshGraphIntermediate")
84)]
85pub struct MeshGraph {
86 #[cfg_attr(feature = "serde", serde(skip))]
88 pub bvh: Bvh,
89 #[cfg_attr(feature = "serde", serde(skip))]
91 pub bvh_workspace: BvhWorkspace,
92 #[cfg_attr(feature = "serde", serde(skip))]
94 pub index_to_face_id: HashMap<u32, FaceId>,
95 #[cfg_attr(feature = "serde", serde(skip))]
97 pub next_index: u32,
98
99 pub vertices: SlotMap<VertexId, Vertex>,
101 pub halfedges: SlotMap<HalfedgeId, Halfedge>,
103 pub faces: SlotMap<FaceId, Face>,
105
106 pub positions: SecondaryMap<VertexId, Vec3>,
108 pub vertex_normals: Option<SecondaryMap<VertexId, Vec3>>,
110}
111
112impl MeshGraph {
113 #[inline]
115 pub fn new() -> Self {
116 Self::default()
117 }
118
119 pub fn triangles(vertex_positions: &[Vec3]) -> Self {
124 assert!(
125 vertex_positions.len().is_multiple_of(3),
126 "Number of vertex positions should be a multiple of 3"
127 );
128
129 let mut unique_positions: Vec<Vec3> = Vec::with_capacity(vertex_positions.len() / 3);
131 let mut face_indices = Vec::with_capacity(vertex_positions.len());
132
133 for vertex_pos in vertex_positions {
134 let mut idx = None;
136 for (j, pos) in unique_positions.iter().enumerate() {
137 const EPSILON: f32 = 1e-5;
138
139 if pos.distance_squared(*vertex_pos) < EPSILON {
140 idx = Some(j);
141 break;
142 }
143 }
144
145 let vertex_idx = if let Some(idx) = idx {
147 idx
148 } else {
149 let new_idx = unique_positions.len();
150 unique_positions.push(*vertex_pos);
151
152 #[cfg(feature = "rerun")]
153 RR.log(
154 "meshgraph/construct/vertices",
155 &rerun::Points3D::new(unique_positions.iter().map(crate::utils::vec3_array)),
156 )
157 .unwrap();
158
159 new_idx
160 };
161
162 face_indices.push(vertex_idx);
164 }
165
166 Self::indexed_triangles(&unique_positions, &face_indices)
168 }
169
170 pub fn indexed_triangles(vertex_positions: &[Vec3], face_indices: &[usize]) -> Self {
173 let mut mesh_graph = Self {
174 bvh: Bvh::new(),
175 bvh_workspace: BvhWorkspace::default(),
176 index_to_face_id: HashMap::with_capacity(face_indices.len() / 3),
177 next_index: 0,
178
179 vertices: SlotMap::with_capacity_and_key(vertex_positions.len()),
180 halfedges: SlotMap::with_capacity_and_key(face_indices.len()),
181 faces: SlotMap::with_capacity_and_key(face_indices.len() / 3),
182
183 positions: SecondaryMap::with_capacity(vertex_positions.len()),
184 vertex_normals: None,
185 };
186
187 let mut vertex_ids = Vec::with_capacity(vertex_positions.len());
188
189 for pos in vertex_positions {
190 vertex_ids.push(mesh_graph.insert_vertex(*pos));
191 }
192
193 let mut start_end_vertex_to_halfedge =
194 HashMap::<(VertexId, VertexId), HalfedgeId>::default();
195
196 for chunk in face_indices.chunks_exact(3) {
197 let a = vertex_ids[chunk[0]];
198 let b = vertex_ids[chunk[1]];
199 let c = vertex_ids[chunk[2]];
200
201 if a == b || b == c || c == a {
202 #[cfg(feature = "rerun")]
203 RR.log(
204 "meshgraph/construct/zero_face",
205 &rerun::Points3D::new(
206 [
207 mesh_graph.positions[a],
208 mesh_graph.positions[b],
209 mesh_graph.positions[c],
210 ]
211 .iter()
212 .map(crate::utils::vec3_array),
213 ),
214 )
215 .unwrap();
216
217 continue;
218 }
219
220 let he_a_id = mesh_graph.insert_halfedge(b);
221 let he_b_id = mesh_graph.insert_halfedge(c);
222 let he_c_id = mesh_graph.insert_halfedge(a);
223
224 start_end_vertex_to_halfedge.insert((b, c), he_b_id);
225 start_end_vertex_to_halfedge.insert((c, a), he_c_id);
226
227 let face_id = mesh_graph.insert_face(he_a_id);
228
229 let he_a = &mut mesh_graph.halfedges[he_a_id];
230 he_a.next = Some(he_b_id);
231 he_a.face = Some(face_id);
232
233 if let Some(twin_a_id) = start_end_vertex_to_halfedge.get(&(b, a)) {
234 he_a.twin = Some(*twin_a_id);
235 mesh_graph.halfedges[*twin_a_id].twin = Some(he_a_id);
236 } else {
237 start_end_vertex_to_halfedge.insert((a, b), he_a_id);
238 }
239
240 let he_b = &mut mesh_graph.halfedges[he_b_id];
241 he_b.next = Some(he_c_id);
242 he_b.face = Some(face_id);
243
244 if let Some(twin_b_id) = start_end_vertex_to_halfedge.get(&(c, b)) {
245 he_b.twin = Some(*twin_b_id);
246 mesh_graph.halfedges[*twin_b_id].twin = Some(he_b_id);
247 } else {
248 start_end_vertex_to_halfedge.insert((b, c), he_b_id);
249 }
250
251 let he_c = &mut mesh_graph.halfedges[he_c_id];
252 he_c.next = Some(he_a_id);
253 he_c.face = Some(face_id);
254
255 if let Some(twin_c_id) = start_end_vertex_to_halfedge.get(&(a, c)) {
256 he_c.twin = Some(*twin_c_id);
257 mesh_graph.halfedges[*twin_c_id].twin = Some(he_c_id);
258 } else {
259 start_end_vertex_to_halfedge.insert((c, a), he_c_id);
260 }
261
262 mesh_graph.vertices[a].outgoing_halfedge = Some(he_a_id);
263 mesh_graph.vertices[b].outgoing_halfedge = Some(he_b_id);
264 mesh_graph.vertices[c].outgoing_halfedge = Some(he_c_id);
265 }
266
267 mesh_graph.rebuild_bvh();
268
269 mesh_graph
270 }
271
272 #[instrument(skip(self))]
274 pub fn compute_vertex_normals(&mut self) {
275 let mut normals = SecondaryMap::with_capacity(self.vertices.len());
276
277 for face in self.faces.values() {
278 let ha_a_id = face.halfedge;
279 let he_a = self.halfedges[ha_a_id];
280
281 let he_b_id = he_a
282 .next
283 .expect("Halfedge has definitely a face and thus a next halfedge");
284 let he_b = self.halfedges[he_b_id];
285
286 let a = match he_a.start_vertex(self) {
287 Some(v) => v,
288 None => {
289 error!("Start vertex not found");
290 continue;
291 }
292 };
293 let b = he_a.end_vertex;
294 let c = he_b.end_vertex;
295
296 let diff_a = self.positions[c] - self.positions[a];
297 let diff_b = self.positions[c] - self.positions[b];
298
299 let face_normal = diff_a.cross(diff_b);
301
302 *normals.entry(a).unwrap().or_default() += face_normal;
303 *normals.entry(b).unwrap().or_default() += face_normal;
304 *normals.entry(c).unwrap().or_default() += face_normal;
305 }
306
307 self.vertex_normals = Some(normals);
308 self.normalize_vertex_normals();
309 }
310
311 pub fn normalize_vertex_normals(&mut self) {
313 if let Some(normals) = &mut self.vertex_normals {
314 for normal in normals.values_mut() {
315 *normal = normal.normalize();
316 }
317 }
318 }
319
320 #[inline]
322 pub fn optimize_bvh_incremental(&mut self) {
323 self.bvh.optimize_incremental(&mut self.bvh_workspace);
324 }
325
326 #[inline]
328 pub fn refit_bvh(&mut self) {
329 self.bvh.refit(&mut self.bvh_workspace);
330 }
331
332 #[inline]
334 pub fn rebuild_bvh(&mut self) {
335 for face in self.faces.values() {
336 self.bvh
337 .insert_or_update_partially(face.aabb(self), face.index, 0.0);
338 }
339 self.bvh
340 .rebuild(&mut self.bvh_workspace, Default::default());
341 }
342}
343
344#[cfg(feature = "rerun")]
345impl MeshGraph {
346 pub fn log_selection_rerun(&self, name: &str, selection: &Selection) {
347 use itertools::Itertools;
348
349 use crate::RR;
350 use crate::utils::*;
351
352 RR.log(
353 format!("meshgraph/selection/{name}/points"),
354 &rerun::Points3D::new(
355 selection
356 .vertices
357 .iter()
358 .map(|v| vec3_array(self.positions[*v]))
359 .collect_vec(),
360 ),
361 )
362 .unwrap();
363
364 self.log_hes_rerun_with_name(
365 format!("meshgraph/selection/{name}/halfedges"),
366 &selection.halfedges.iter().copied().collect::<Vec<_>>(),
367 );
368
369 let face_centers = selection
370 .faces
371 .iter()
372 .map(|face_id| {
373 let face = self.faces[*face_id];
374
375 let center = face
376 .vertices(self)
377 .into_iter()
378 .map(|v_id| self.positions[v_id])
379 .reduce(|acc, p| acc + p)
380 .unwrap()
381 / 3.0;
382
383 vec3_array(center)
384 })
385 .collect_vec();
386
387 RR.log(
388 format!("meshgraph/selection/{name}/faces"),
389 &rerun::Points3D::new(face_centers),
390 )
391 .unwrap();
392 }
393
394 pub fn log_vert_rerun(&self, name: &str, vert: VertexId) {
395 use crate::RR;
396 use crate::utils::*;
397
398 let pos = self.positions[vert];
399
400 RR.log(
401 format!("meshgraph/vertex/{name}"),
402 &rerun::Points3D::new(vec![vec3_array(pos)]),
403 )
404 .unwrap();
405 }
406
407 pub fn log_he_rerun(&self, name: &str, halfedge: HalfedgeId) {
408 use crate::RR;
409 use crate::utils::*;
410
411 let he = self.halfedges[halfedge];
412
413 let start = self.positions[he.start_vertex(self).unwrap()];
414 let end = self.positions[he.end_vertex];
415
416 RR.log(
417 format!("meshgraph/halfedge/{name}"),
418 &rerun::Arrows3D::from_vectors([vec3_array(end - start)])
419 .with_origins([vec3_array(start)]),
420 )
421 .unwrap();
422 }
423
424 pub fn log_hes_rerun(&self, name: &str, halfedges: &[HalfedgeId]) {
425 self.log_hes_rerun_with_name(format!("meshgraph/halfedge/{name}"), halfedges);
426 }
427
428 fn log_hes_rerun_with_name(&self, name: String, halfedges: &[HalfedgeId]) {
429 use crate::RR;
430 use crate::utils::*;
431
432 let mut origins = Vec::with_capacity(halfedges.len());
433 let mut vectors = Vec::with_capacity(halfedges.len());
434
435 for &he_id in halfedges {
436 let he = self.halfedges[he_id];
437
438 let start = self.positions[he.start_vertex(self).unwrap()];
439 let end = self.positions[he.end_vertex];
440
441 origins.push(vec3_array(start));
442 vectors.push(vec3_array(end - start));
443 }
444
445 RR.log(
446 name,
447 &rerun::Arrows3D::from_vectors(vectors).with_origins(origins),
448 )
449 .unwrap();
450 }
451
452 pub fn log_face_rerun(&self, name: &str, face: FaceId) {
453 use itertools::Itertools;
454
455 use crate::RR;
456 use crate::utils::*;
457
458 let mut origins = Vec::with_capacity(3);
459 let mut vectors = Vec::with_capacity(3);
460
461 let face = self.faces[face];
462
463 let pos = face.vertices(self).map(|v| self.positions[v]).collect_vec();
464
465 let center = pos.iter().copied().reduce(|a, b| a + b).unwrap() / pos.len() as f32;
466
467 let pos = face
468 .vertices(self)
469 .into_iter()
470 .zip(pos)
471 .map(|(v, p)| (v, center * 0.1 + p * 0.9))
472 .collect::<HashMap<_, _>>();
473
474 for he_id in face.halfedges(self) {
475 let he = self.halfedges[he_id];
476
477 let start = pos[&he.start_vertex(self).unwrap()];
478 let end = pos[&he.end_vertex];
479
480 origins.push(vec3_array(start));
481 vectors.push(vec3_array(end - start));
482 }
483
484 #[cfg(feature = "rerun")]
485 {
486 RR.log(
487 format!("meshgraph/face/{name}"),
488 &rerun::Arrows3D::from_vectors(vectors).with_origins(origins),
489 )
490 .unwrap();
491 }
492 }
493
494 pub fn log_rerun(&self) {
495 use crate::RR;
496 use crate::utils::*;
497
498 let buffers = crate::integrations::VertexIndexBuffers::from(self.clone());
499 RR.log(
500 "meshgraph/mesh",
501 &rerun::Mesh3D::new(
502 buffers
503 .positions
504 .into_iter()
505 .zip(buffers.normals.iter().cloned())
506 .map(|(pos, norm)| vec3_array(pos - norm * 0.1)),
507 )
508 .with_triangle_indices(
509 buffers
510 .indices
511 .chunks(3)
512 .map(|chunk| rerun::datatypes::UVec3D::new(chunk[0], chunk[1], chunk[2])),
513 )
514 .with_vertex_colors(
515 buffers
516 .normals
517 .into_iter()
518 .map(|v| {
519 [
520 (100.0 + v.x * 100.0) as u8,
521 (100.0 + v.y * 100.0) as u8,
522 (100.0 + v.z * 100.0) as u8,
523 ]
524 })
525 .collect::<Vec<_>>(),
526 ),
527 )
528 .unwrap();
529
530 RR.log(
531 "meshgraph/positions",
532 &rerun::Points3D::new(self.positions.values().map(vec3_array))
533 .with_radii(self.positions.iter().map(|_| 0.01)),
534 )
535 .unwrap();
536
537 let mut origins = Vec::with_capacity(self.faces.len() * 3);
538 let mut vectors = Vec::with_capacity(self.faces.len() * 3);
539
540 let mut he_to_pos = HashMap::<HalfedgeId, (Vec3, Vec3)>::default();
541
542 for face in self.faces.values() {
543 use itertools::Itertools;
544
545 let pos = face.vertices(self).map(|v| self.positions[v]).collect_vec();
546
547 let center = pos.iter().copied().reduce(|a, b| a + b).unwrap() / pos.len() as f32;
548
549 let pos = face
550 .vertices(self)
551 .into_iter()
552 .zip(pos)
553 .map(|(v, p)| (v, center * 0.1 + p * 0.9))
554 .collect::<HashMap<_, _>>();
555
556 for he_id in face.halfedges(self) {
557 let he = self.halfedges[he_id];
558
559 let start = pos[&he.start_vertex(self).unwrap()];
560 let end = pos[&he.end_vertex];
561
562 he_to_pos.insert(he_id, (start, end));
563
564 origins.push(vec3_array(start));
565 vectors.push(vec3_array(end - start));
566 }
567 }
568
569 for (he_id, he) in &self.halfedges {
570 if he.is_boundary() {
571 let start_vertex = he.start_vertex(self).unwrap();
572 let end_vertex = he.end_vertex;
573
574 let start = self.positions[start_vertex];
575 let end = self.positions[end_vertex];
576
577 let offset = if let Some(normals) = self.vertex_normals.as_ref() {
578 normals[start_vertex]
579 .lerp(normals[end_vertex], 0.5)
580 .cross(end - start)
581 .normalize()
582 * 0.1
583 } else {
584 Vec3::ZERO
585 };
586
587 let (start, end) = (start.lerp(end, 0.1) + offset, end.lerp(start, 0.1) + offset);
588
589 he_to_pos.insert(he_id, (start, end));
590
591 origins.push(vec3_array(start));
592 vectors.push(vec3_array(end - start));
593 }
594 }
595
596 RR.log(
597 "meshgraph/halfedges",
598 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
599 )
600 .unwrap();
601
602 origins.clear();
603 vectors.clear();
604
605 for (he_id, he) in &self.halfedges {
606 let twin = he.twin.unwrap();
607
608 let (he_start, he_end) = he_to_pos[&he_id];
609 let (tw_start, tw_end) = he_to_pos[&twin];
610
611 let start = he_start * 0.8 + he_end * 0.2;
612 let end = tw_start * 0.2 + tw_end * 0.8;
613
614 origins.push(vec3_array(start));
615 vectors.push(vec3_array(end - start));
616 }
617
618 RR.log(
619 "meshgraph/twins",
620 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
621 )
622 .unwrap();
623
624 origins.clear();
625 vectors.clear();
626
627 for (v_id, v) in &self.vertices {
628 if let Some(he) = v.outgoing_halfedge.as_ref() {
629 let start = self.positions[v_id];
630
631 RR.log(
632 "meshgraph/the_vertex",
633 &rerun::Points3D::new([vec3_array(start)]),
634 )
635 .unwrap();
636
637 let (start_he, end_he) = he_to_pos[he];
638
639 let end = start_he.lerp(end_he, 0.05);
640
641 origins.push(vec3_array(start));
642 vectors.push(vec3_array(end - start));
643 }
644 }
645
646 RR.log(
647 "meshgraph/outgoing_halfedges",
648 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
649 )
650 .unwrap();
651
652 origins.clear();
653 vectors.clear();
654
655 for face in self.faces.values() {
656 let start = face.center(self);
657
658 let (he_start, he_end) = he_to_pos[&face.halfedge];
659 let end = he_start * 0.6 + he_end * 0.4;
660
661 origins.push(vec3_array(start));
662 vectors.push(vec3_array(end - start));
663 }
664
665 RR.log(
666 "meshgraph/face_halfedges",
667 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
668 )
669 .unwrap();
670
671 origins.clear();
672 vectors.clear();
673
674 for (he_id, he) in &self.halfedges {
675 if let Some(face_id) = he.face {
676 let (he_start, he_end) = he_to_pos[&he_id];
677 let start = he_start * 0.4 + he_end * 0.6;
678
679 let end = self.faces[face_id].center(self);
680
681 origins.push(vec3_array(start));
682 vectors.push(vec3_array((end - start) * 0.9));
683 }
684 }
685
686 RR.log(
687 "meshgraph/halfedge_faces",
688 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
689 )
690 .unwrap();
691
692 origins.clear();
693 vectors.clear();
694
695 for (he_id, he) in &self.halfedges {
696 if let Some(next_id) = he.next {
697 let (he_start, he_end) = he_to_pos[&he_id];
698 let start = he_start * 0.15 + he_end * 0.85;
699
700 let (next_start, next_end) = he_to_pos[&next_id];
701 let end = next_start * 0.85 + next_end * 0.15;
702
703 origins.push(vec3_array(start));
704 vectors.push(vec3_array(end - start));
705 }
706 }
707
708 RR.log(
709 "meshgraph/halfedge_next",
710 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
711 )
712 .unwrap();
713 }
714}