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