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_to_outgoing_halfedges = SecondaryMap::with_capacity(vertex_positions.len());
188
189 let mut vertex_ids = Vec::with_capacity(vertex_positions.len());
190
191 for pos in vertex_positions {
192 vertex_ids.push(mesh_graph.insert_vertex(*pos));
193 }
194
195 for chunk in face_indices.chunks_exact(3) {
196 let a = vertex_ids[chunk[0]];
197 let b = vertex_ids[chunk[1]];
198 let c = vertex_ids[chunk[2]];
199
200 if a == b || b == c || c == a {
201 #[cfg(feature = "rerun")]
202 RR.log(
203 "meshgraph/construct/zero_face",
204 &rerun::Points3D::new(
205 [
206 mesh_graph.positions[a],
207 mesh_graph.positions[b],
208 mesh_graph.positions[c],
209 ]
210 .iter()
211 .map(crate::utils::vec3_array),
212 ),
213 )
214 .unwrap();
215
216 continue;
217 }
218
219 let (he_a_id, _) =
220 mesh_graph.insert_or_get_edge(a, b, &mut vertex_to_outgoing_halfedges);
221 let (he_b_id, _) =
222 mesh_graph.insert_or_get_edge(b, c, &mut vertex_to_outgoing_halfedges);
223 let (he_c_id, _) =
224 mesh_graph.insert_or_get_edge(c, a, &mut vertex_to_outgoing_halfedges);
225
226 let _face_id = mesh_graph.insert_face(he_a_id, he_b_id, he_c_id);
227
228 }
236
237 mesh_graph.make_all_outgoing_halfedges_boundary_if_possible();
238 mesh_graph.rebuild_bvh();
239
240 mesh_graph
241 }
242
243 #[instrument(skip(self))]
245 pub fn compute_vertex_normals(&mut self) {
246 let mut normals = SecondaryMap::with_capacity(self.vertices.len());
247
248 for face in self.faces.values() {
249 let ha_a_id = face.halfedge;
250 let he_a = self.halfedges[ha_a_id];
251
252 let he_b_id = he_a
253 .next
254 .expect("Halfedge has definitely a face and thus a next halfedge");
255 let he_b = self.halfedges[he_b_id];
256
257 let a = match he_a.start_vertex(self) {
258 Some(v) => v,
259 None => {
260 error!("Start vertex not found");
261 continue;
262 }
263 };
264 let b = he_a.end_vertex;
265 let c = he_b.end_vertex;
266
267 let diff_a = self.positions[c] - self.positions[a];
268 let diff_b = self.positions[c] - self.positions[b];
269
270 let face_normal = diff_a.cross(diff_b);
272
273 *normals.entry(a).unwrap().or_default() += face_normal;
274 *normals.entry(b).unwrap().or_default() += face_normal;
275 *normals.entry(c).unwrap().or_default() += face_normal;
276 }
277
278 self.vertex_normals = Some(normals);
279 self.normalize_vertex_normals();
280 }
281
282 pub fn normalize_vertex_normals(&mut self) {
284 if let Some(normals) = &mut self.vertex_normals {
285 for normal in normals.values_mut() {
286 *normal = normal.normalize();
287 }
288 }
289 }
290
291 #[inline]
293 pub fn optimize_bvh_incremental(&mut self) {
294 self.bvh.optimize_incremental(&mut self.bvh_workspace);
295 }
296
297 #[inline]
299 pub fn refit_bvh(&mut self) {
300 self.bvh.refit(&mut self.bvh_workspace);
301 }
302
303 #[inline]
305 pub fn rebuild_bvh(&mut self) {
306 for face in self.faces.values() {
307 self.bvh
308 .insert_or_update_partially(face.aabb(self), face.index, 0.0);
309 }
310 self.bvh
311 .rebuild(&mut self.bvh_workspace, Default::default());
312 }
313}
314
315#[cfg(feature = "rerun")]
316impl MeshGraph {
317 pub fn log_selection_rerun(&self, name: &str, selection: &Selection) {
318 use itertools::Itertools;
319
320 use crate::RR;
321 use crate::utils::*;
322
323 RR.log(
324 format!("meshgraph/selection/{name}/points"),
325 &rerun::Points3D::new(
326 selection
327 .vertices
328 .iter()
329 .map(|v| vec3_array(self.positions[*v]))
330 .collect_vec(),
331 ),
332 )
333 .unwrap();
334
335 self.log_hes_rerun_with_name(
336 format!("meshgraph/selection/{name}/halfedges"),
337 &selection.halfedges.iter().copied().collect::<Vec<_>>(),
338 );
339
340 let face_centers = selection
341 .faces
342 .iter()
343 .map(|face_id| {
344 let face = self.faces[*face_id];
345
346 let center = face
347 .vertices(self)
348 .map(|v_id| self.positions[v_id])
349 .reduce(|acc, p| acc + p)
350 .unwrap()
351 / 3.0;
352
353 vec3_array(center)
354 })
355 .collect_vec();
356
357 RR.log(
358 format!("meshgraph/selection/{name}/faces"),
359 &rerun::Points3D::new(face_centers),
360 )
361 .unwrap();
362 }
363
364 pub fn log_vert_rerun(&self, name: &str, vert: VertexId) {
365 use crate::RR;
366 use crate::utils::*;
367
368 let pos = self.positions[vert];
369
370 RR.log(
371 format!("meshgraph/vertex/{name}"),
372 &rerun::Points3D::new(vec![vec3_array(pos)]),
373 )
374 .unwrap();
375 }
376
377 pub fn log_he_rerun(&self, name: &str, halfedge: HalfedgeId) {
378 use crate::RR;
379 use crate::utils::*;
380
381 let he = self.halfedges[halfedge];
382
383 let start = self.positions[he.start_vertex(self).unwrap()];
384 let end = self.positions[he.end_vertex];
385
386 RR.log(
387 format!("meshgraph/halfedge/{name}"),
388 &rerun::Arrows3D::from_vectors([vec3_array(end - start)])
389 .with_origins([vec3_array(start)]),
390 )
391 .unwrap();
392 }
393
394 pub fn log_hes_rerun(&self, name: &str, halfedges: &[HalfedgeId]) {
395 self.log_hes_rerun_with_name(format!("meshgraph/halfedge/{name}"), halfedges);
396 }
397
398 fn log_hes_rerun_with_name(&self, name: String, halfedges: &[HalfedgeId]) {
399 use crate::RR;
400 use crate::utils::*;
401
402 let mut origins = Vec::with_capacity(halfedges.len());
403 let mut vectors = Vec::with_capacity(halfedges.len());
404
405 for &he_id in halfedges {
406 let he = self.halfedges[he_id];
407
408 let start = self.positions[he.start_vertex(self).unwrap()];
409 let end = self.positions[he.end_vertex];
410
411 origins.push(vec3_array(start));
412 vectors.push(vec3_array(end - start));
413 }
414
415 RR.log(
416 name,
417 &rerun::Arrows3D::from_vectors(vectors).with_origins(origins),
418 )
419 .unwrap();
420 }
421
422 pub fn log_face_rerun(&self, name: &str, face: FaceId) {
423 use itertools::Itertools;
424
425 use crate::RR;
426 use crate::utils::*;
427
428 let mut origins = Vec::with_capacity(3);
429 let mut vectors = Vec::with_capacity(3);
430
431 let face = self.faces[face];
432
433 let pos = face.vertices(self).map(|v| self.positions[v]).collect_vec();
434
435 let center = pos.iter().copied().reduce(|a, b| a + b).unwrap() / pos.len() as f32;
436
437 let pos = face
438 .vertices(self)
439 .zip(pos)
440 .map(|(v, p)| (v, center * 0.1 + p * 0.9))
441 .collect::<HashMap<_, _>>();
442
443 for he_id in face.halfedges(self) {
444 let he = self.halfedges[he_id];
445
446 let start = pos[&he.start_vertex(self).unwrap()];
447 let end = pos[&he.end_vertex];
448
449 origins.push(vec3_array(start));
450 vectors.push(vec3_array(end - start));
451 }
452
453 #[cfg(feature = "rerun")]
454 {
455 RR.log(
456 format!("meshgraph/face/{name}"),
457 &rerun::Arrows3D::from_vectors(vectors).with_origins(origins),
458 )
459 .unwrap();
460 }
461 }
462
463 pub fn log_rerun(&self) {
464 use crate::RR;
465 use crate::utils::*;
466
467 let buffers = crate::integrations::VertexIndexBuffers::from(self.clone());
468 RR.log(
469 "meshgraph/mesh",
470 &rerun::Mesh3D::new(
471 buffers
472 .positions
473 .into_iter()
474 .zip(buffers.normals.iter().cloned())
475 .map(|(pos, norm)| vec3_array(pos - norm * 0.1)),
476 )
477 .with_triangle_indices(
478 buffers
479 .indices
480 .chunks(3)
481 .map(|chunk| rerun::datatypes::UVec3D::new(chunk[0], chunk[1], chunk[2])),
482 )
483 .with_vertex_colors(
484 buffers
485 .normals
486 .into_iter()
487 .map(|v| {
488 [
489 (100.0 + v.x * 100.0) as u8,
490 (100.0 + v.y * 100.0) as u8,
491 (100.0 + v.z * 100.0) as u8,
492 ]
493 })
494 .collect::<Vec<_>>(),
495 ),
496 )
497 .unwrap();
498
499 RR.log(
500 "meshgraph/positions",
501 &rerun::Points3D::new(self.positions.values().map(vec3_array))
502 .with_radii(self.positions.iter().map(|_| 0.01)),
503 )
504 .unwrap();
505
506 let mut origins = Vec::with_capacity(self.faces.len() * 3);
507 let mut vectors = Vec::with_capacity(self.faces.len() * 3);
508
509 let mut he_to_pos = HashMap::<HalfedgeId, (Vec3, Vec3)>::default();
510
511 for face in self.faces.values() {
512 use itertools::Itertools;
513
514 let pos = face.vertices(self).map(|v| self.positions[v]).collect_vec();
515
516 let center = pos.iter().copied().reduce(|a, b| a + b).unwrap() / pos.len() as f32;
517
518 let pos = face
519 .vertices(self)
520 .zip(pos)
521 .map(|(v, p)| (v, center * 0.1 + p * 0.9))
522 .collect::<HashMap<_, _>>();
523
524 for he_id in face.halfedges(self) {
525 let he = self.halfedges[he_id];
526
527 let start = pos[&he.start_vertex(self).unwrap()];
528 let end = pos[&he.end_vertex];
529
530 he_to_pos.insert(he_id, (start, end));
531
532 origins.push(vec3_array(start));
533 vectors.push(vec3_array(end - start));
534 }
535 }
536
537 for (he_id, he) in &self.halfedges {
538 if he.is_boundary() {
539 let start_vertex = he.start_vertex(self).unwrap();
540 let end_vertex = he.end_vertex;
541
542 let start = self.positions[start_vertex];
543 let end = self.positions[end_vertex];
544
545 let offset = if let Some(normals) = self.vertex_normals.as_ref() {
546 normals[start_vertex]
547 .lerp(normals[end_vertex], 0.5)
548 .cross(end - start)
549 .normalize()
550 * 0.1
551 } else {
552 Vec3::ZERO
553 };
554
555 let (start, end) = (start.lerp(end, 0.1) + offset, end.lerp(start, 0.1) + offset);
556
557 he_to_pos.insert(he_id, (start, end));
558
559 origins.push(vec3_array(start));
560 vectors.push(vec3_array(end - start));
561 }
562 }
563
564 RR.log(
565 "meshgraph/halfedges",
566 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
567 )
568 .unwrap();
569
570 origins.clear();
571 vectors.clear();
572
573 for (he_id, he) in &self.halfedges {
574 let twin = he.twin.unwrap();
575
576 let (he_start, he_end) = he_to_pos[&he_id];
577 let (tw_start, tw_end) = he_to_pos[&twin];
578
579 let start = he_start * 0.8 + he_end * 0.2;
580 let end = tw_start * 0.2 + tw_end * 0.8;
581
582 origins.push(vec3_array(start));
583 vectors.push(vec3_array(end - start));
584 }
585
586 RR.log(
587 "meshgraph/twins",
588 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
589 )
590 .unwrap();
591
592 origins.clear();
593 vectors.clear();
594
595 for (v_id, v) in &self.vertices {
596 if let Some(he) = v.outgoing_halfedge.as_ref() {
597 let start = self.positions[v_id];
598
599 RR.log(
600 "meshgraph/the_vertex",
601 &rerun::Points3D::new([vec3_array(start)]),
602 )
603 .unwrap();
604
605 let (start_he, end_he) = he_to_pos[he];
606
607 let end = start_he.lerp(end_he, 0.05);
608
609 origins.push(vec3_array(start));
610 vectors.push(vec3_array(end - start));
611 }
612 }
613
614 RR.log(
615 "meshgraph/outgoing_halfedges",
616 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
617 )
618 .unwrap();
619
620 origins.clear();
621 vectors.clear();
622
623 for face in self.faces.values() {
624 let start = face.center(self);
625
626 let (he_start, he_end) = he_to_pos[&face.halfedge];
627 let end = he_start * 0.6 + he_end * 0.4;
628
629 origins.push(vec3_array(start));
630 vectors.push(vec3_array(end - start));
631 }
632
633 RR.log(
634 "meshgraph/face_halfedges",
635 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
636 )
637 .unwrap();
638
639 origins.clear();
640 vectors.clear();
641
642 for (he_id, he) in &self.halfedges {
643 if let Some(face_id) = he.face {
644 let (he_start, he_end) = he_to_pos[&he_id];
645 let start = he_start * 0.4 + he_end * 0.6;
646
647 let end = self.faces[face_id].center(self);
648
649 origins.push(vec3_array(start));
650 vectors.push(vec3_array((end - start) * 0.9));
651 }
652 }
653
654 RR.log(
655 "meshgraph/halfedge_faces",
656 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
657 )
658 .unwrap();
659
660 origins.clear();
661 vectors.clear();
662
663 for (he_id, he) in &self.halfedges {
664 if let Some(next_id) = he.next {
665 let (he_start, he_end) = he_to_pos[&he_id];
666 let start = he_start * 0.15 + he_end * 0.85;
667
668 let (next_start, next_end) = he_to_pos[&next_id];
669 let end = next_start * 0.85 + next_end * 0.15;
670
671 origins.push(vec3_array(start));
672 vectors.push(vec3_array(end - start));
673 }
674 }
675
676 RR.log(
677 "meshgraph/halfedge_next",
678 &rerun::Arrows3D::from_vectors(&vectors).with_origins(&origins),
679 )
680 .unwrap();
681 }
682}