1mod quality;
16mod simplification;
17mod smoothing;
18
19use crate::error::{SpatialError, SpatialResult};
20use std::collections::{HashMap, HashSet};
21use std::fmt;
22
23#[derive(Debug, Clone, Copy, PartialEq)]
25pub struct Vertex {
26 pub x: f64,
27 pub y: f64,
28 pub z: f64,
29}
30
31impl Vertex {
32 pub fn new(x: f64, y: f64, z: f64) -> Self {
34 Self { x, y, z }
35 }
36
37 pub fn distance_sq(&self, other: &Vertex) -> f64 {
39 let dx = self.x - other.x;
40 let dy = self.y - other.y;
41 let dz = self.z - other.z;
42 dx * dx + dy * dy + dz * dz
43 }
44
45 pub fn distance(&self, other: &Vertex) -> f64 {
47 self.distance_sq(other).sqrt()
48 }
49
50 pub fn sub(&self, other: &Vertex) -> [f64; 3] {
52 [self.x - other.x, self.y - other.y, self.z - other.z]
53 }
54
55 pub fn midpoint(&self, other: &Vertex) -> Vertex {
57 Vertex {
58 x: (self.x + other.x) * 0.5,
59 y: (self.y + other.y) * 0.5,
60 z: (self.z + other.z) * 0.5,
61 }
62 }
63}
64
65impl fmt::Display for Vertex {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 write!(f, "({}, {}, {})", self.x, self.y, self.z)
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
73pub struct Face {
74 pub v0: usize,
75 pub v1: usize,
76 pub v2: usize,
77}
78
79impl Face {
80 pub fn new(v0: usize, v1: usize, v2: usize) -> Self {
82 Self { v0, v1, v2 }
83 }
84
85 pub fn indices(&self) -> [usize; 3] {
87 [self.v0, self.v1, self.v2]
88 }
89
90 pub fn contains_vertex(&self, v: usize) -> bool {
92 self.v0 == v || self.v1 == v || self.v2 == v
93 }
94
95 pub fn replace_vertex(&mut self, old: usize, new: usize) {
97 if self.v0 == old {
98 self.v0 = new;
99 }
100 if self.v1 == old {
101 self.v1 = new;
102 }
103 if self.v2 == old {
104 self.v2 = new;
105 }
106 }
107
108 pub fn is_degenerate(&self) -> bool {
110 self.v0 == self.v1 || self.v1 == self.v2 || self.v0 == self.v2
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
116pub struct Edge {
117 pub a: usize,
118 pub b: usize,
119}
120
121impl Edge {
122 pub fn new(v0: usize, v1: usize) -> Self {
124 if v0 <= v1 {
125 Self { a: v0, b: v1 }
126 } else {
127 Self { a: v1, b: v0 }
128 }
129 }
130}
131
132#[derive(Debug, Clone)]
158pub struct TriangleMesh {
159 pub vertices: Vec<Vertex>,
161 pub faces: Vec<Face>,
163}
164
165impl TriangleMesh {
166 pub fn new(vertices: Vec<Vertex>, faces: Vec<Face>) -> SpatialResult<Self> {
177 let n = vertices.len();
179 for (i, face) in faces.iter().enumerate() {
180 for &idx in &[face.v0, face.v1, face.v2] {
181 if idx >= n {
182 return Err(SpatialError::ValueError(format!(
183 "Face {} references vertex index {} but only {} vertices exist",
184 i, idx, n
185 )));
186 }
187 }
188 if face.is_degenerate() {
189 return Err(SpatialError::ValueError(format!(
190 "Face {} is degenerate (has duplicate vertices)",
191 i
192 )));
193 }
194 }
195
196 Ok(Self { vertices, faces })
197 }
198
199 pub fn empty() -> Self {
201 Self {
202 vertices: Vec::new(),
203 faces: Vec::new(),
204 }
205 }
206
207 pub fn num_vertices(&self) -> usize {
209 self.vertices.len()
210 }
211
212 pub fn num_faces(&self) -> usize {
214 self.faces.len()
215 }
216
217 pub fn num_edges(&self) -> usize {
219 self.edges().len()
220 }
221
222 pub fn edges(&self) -> HashSet<Edge> {
224 let mut edges = HashSet::new();
225 for face in &self.faces {
226 edges.insert(Edge::new(face.v0, face.v1));
227 edges.insert(Edge::new(face.v1, face.v2));
228 edges.insert(Edge::new(face.v0, face.v2));
229 }
230 edges
231 }
232
233 pub fn vertex_faces(&self) -> HashMap<usize, Vec<usize>> {
235 let mut vf: HashMap<usize, Vec<usize>> = HashMap::new();
236 for (fi, face) in self.faces.iter().enumerate() {
237 for &vi in &[face.v0, face.v1, face.v2] {
238 vf.entry(vi).or_default().push(fi);
239 }
240 }
241 vf
242 }
243
244 pub fn vertex_neighbors(&self) -> HashMap<usize, HashSet<usize>> {
246 let mut vn: HashMap<usize, HashSet<usize>> = HashMap::new();
247 for face in &self.faces {
248 let indices = face.indices();
249 for i in 0..3 {
250 let vi = indices[i];
251 for j in 0..3 {
252 if i != j {
253 vn.entry(vi).or_default().insert(indices[j]);
254 }
255 }
256 }
257 }
258 vn
259 }
260
261 pub fn face_normal_raw(&self, face_idx: usize) -> SpatialResult<[f64; 3]> {
263 if face_idx >= self.faces.len() {
264 return Err(SpatialError::ValueError(format!(
265 "Face index {} out of range (num faces = {})",
266 face_idx,
267 self.faces.len()
268 )));
269 }
270
271 let face = &self.faces[face_idx];
272 let v0 = &self.vertices[face.v0];
273 let v1 = &self.vertices[face.v1];
274 let v2 = &self.vertices[face.v2];
275
276 let e1 = v1.sub(v0);
277 let e2 = v2.sub(v0);
278
279 let nx = e1[1] * e2[2] - e1[2] * e2[1];
281 let ny = e1[2] * e2[0] - e1[0] * e2[2];
282 let nz = e1[0] * e2[1] - e1[1] * e2[0];
283
284 Ok([nx, ny, nz])
285 }
286
287 pub fn face_normal(&self, face_idx: usize) -> SpatialResult<[f64; 3]> {
289 let n = self.face_normal_raw(face_idx)?;
290 let len = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
291 if len < 1e-15 {
292 return Ok([0.0, 0.0, 0.0]);
293 }
294 Ok([n[0] / len, n[1] / len, n[2] / len])
295 }
296
297 pub fn compute_face_normals(&self) -> SpatialResult<Vec<[f64; 3]>> {
299 let mut normals = Vec::with_capacity(self.faces.len());
300 for i in 0..self.faces.len() {
301 normals.push(self.face_normal(i)?);
302 }
303 Ok(normals)
304 }
305
306 pub fn compute_vertex_normals(&self) -> SpatialResult<Vec<[f64; 3]>> {
309 let n = self.vertices.len();
310 let mut normals = vec![[0.0_f64; 3]; n];
311
312 for (fi, face) in self.faces.iter().enumerate() {
313 let fn_raw = self.face_normal_raw(fi)?;
314 for &vi in &[face.v0, face.v1, face.v2] {
317 normals[vi][0] += fn_raw[0];
318 normals[vi][1] += fn_raw[1];
319 normals[vi][2] += fn_raw[2];
320 }
321 }
322
323 for normal in &mut normals {
325 let len =
326 (normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]).sqrt();
327 if len > 1e-15 {
328 normal[0] /= len;
329 normal[1] /= len;
330 normal[2] /= len;
331 }
332 }
333
334 Ok(normals)
335 }
336
337 pub fn face_area(&self, face_idx: usize) -> SpatialResult<f64> {
339 let n = self.face_normal_raw(face_idx)?;
340 let len = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
341 Ok(len * 0.5)
342 }
343
344 pub fn surface_area(&self) -> SpatialResult<f64> {
346 let mut total = 0.0;
347 for i in 0..self.faces.len() {
348 total += self.face_area(i)?;
349 }
350 Ok(total)
351 }
352
353 pub fn bounding_box(&self) -> Option<(Vertex, Vertex)> {
355 if self.vertices.is_empty() {
356 return None;
357 }
358
359 let mut min_v = self.vertices[0];
360 let mut max_v = self.vertices[0];
361
362 for v in &self.vertices {
363 min_v.x = min_v.x.min(v.x);
364 min_v.y = min_v.y.min(v.y);
365 min_v.z = min_v.z.min(v.z);
366 max_v.x = max_v.x.max(v.x);
367 max_v.y = max_v.y.max(v.y);
368 max_v.z = max_v.z.max(v.z);
369 }
370
371 Some((min_v, max_v))
372 }
373
374 pub fn to_stl_ascii(&self, name: &str) -> SpatialResult<String> {
380 let mut out = format!("solid {}\n", name);
381
382 for (fi, face) in self.faces.iter().enumerate() {
383 let n = self.face_normal(fi)?;
384 out.push_str(&format!(" facet normal {} {} {}\n", n[0], n[1], n[2]));
385 out.push_str(" outer loop\n");
386
387 let v0 = &self.vertices[face.v0];
388 let v1 = &self.vertices[face.v1];
389 let v2 = &self.vertices[face.v2];
390
391 out.push_str(&format!(" vertex {} {} {}\n", v0.x, v0.y, v0.z));
392 out.push_str(&format!(" vertex {} {} {}\n", v1.x, v1.y, v1.z));
393 out.push_str(&format!(" vertex {} {} {}\n", v2.x, v2.y, v2.z));
394
395 out.push_str(" endloop\n");
396 out.push_str(" endfacet\n");
397 }
398
399 out.push_str(&format!("endsolid {}\n", name));
400 Ok(out)
401 }
402
403 pub fn from_stl_ascii(stl_data: &str) -> SpatialResult<Self> {
409 let mut vertices: Vec<Vertex> = Vec::new();
410 let mut faces: Vec<Face> = Vec::new();
411 let mut vertex_map: HashMap<[u64; 3], usize> = HashMap::new();
412 let mut current_face_verts: Vec<usize> = Vec::new();
413
414 for line in stl_data.lines() {
415 let trimmed = line.trim();
416
417 if let Some(rest) = trimmed.strip_prefix("vertex") {
418 let parts: Vec<&str> = rest.split_whitespace().collect();
419 if parts.len() < 3 {
420 return Err(SpatialError::ValueError(
421 "Invalid vertex line in STL".to_string(),
422 ));
423 }
424
425 let x: f64 = parts[0].parse().map_err(|e| {
426 SpatialError::ValueError(format!("Failed to parse vertex x: {}", e))
427 })?;
428 let y: f64 = parts[1].parse().map_err(|e| {
429 SpatialError::ValueError(format!("Failed to parse vertex y: {}", e))
430 })?;
431 let z: f64 = parts[2].parse().map_err(|e| {
432 SpatialError::ValueError(format!("Failed to parse vertex z: {}", e))
433 })?;
434
435 let key = [x.to_bits(), y.to_bits(), z.to_bits()];
437
438 let idx = if let Some(&existing) = vertex_map.get(&key) {
439 existing
440 } else {
441 let idx = vertices.len();
442 vertices.push(Vertex::new(x, y, z));
443 vertex_map.insert(key, idx);
444 idx
445 };
446
447 current_face_verts.push(idx);
448 } else if trimmed == "endloop" {
449 if current_face_verts.len() == 3 {
450 let v0 = current_face_verts[0];
451 let v1 = current_face_verts[1];
452 let v2 = current_face_verts[2];
453
454 if v0 != v1 && v1 != v2 && v0 != v2 {
456 faces.push(Face::new(v0, v1, v2));
457 }
458 }
459 current_face_verts.clear();
460 }
461 }
462
463 Ok(Self { vertices, faces })
464 }
465
466 pub fn is_manifold(&self) -> bool {
468 let mut edge_count: HashMap<Edge, usize> = HashMap::new();
469 for face in &self.faces {
470 *edge_count.entry(Edge::new(face.v0, face.v1)).or_insert(0) += 1;
471 *edge_count.entry(Edge::new(face.v1, face.v2)).or_insert(0) += 1;
472 *edge_count.entry(Edge::new(face.v0, face.v2)).or_insert(0) += 1;
473 }
474 edge_count.values().all(|&c| c == 1 || c == 2)
475 }
476
477 pub fn is_closed(&self) -> bool {
479 let mut edge_count: HashMap<Edge, usize> = HashMap::new();
480 for face in &self.faces {
481 *edge_count.entry(Edge::new(face.v0, face.v1)).or_insert(0) += 1;
482 *edge_count.entry(Edge::new(face.v1, face.v2)).or_insert(0) += 1;
483 *edge_count.entry(Edge::new(face.v0, face.v2)).or_insert(0) += 1;
484 }
485 edge_count.values().all(|&c| c == 2)
486 }
487
488 pub fn boundary_edges(&self) -> Vec<Edge> {
490 let mut edge_count: HashMap<Edge, usize> = HashMap::new();
491 for face in &self.faces {
492 *edge_count.entry(Edge::new(face.v0, face.v1)).or_insert(0) += 1;
493 *edge_count.entry(Edge::new(face.v1, face.v2)).or_insert(0) += 1;
494 *edge_count.entry(Edge::new(face.v0, face.v2)).or_insert(0) += 1;
495 }
496 edge_count
497 .into_iter()
498 .filter(|&(_, c)| c == 1)
499 .map(|(e, _)| e)
500 .collect()
501 }
502
503 pub fn euler_characteristic(&self) -> i64 {
505 let v = self.num_vertices() as i64;
506 let e = self.num_edges() as i64;
507 let f = self.num_faces() as i64;
508 v - e + f
509 }
510}
511
512pub use quality::{face_aspect_ratio, face_min_angle, mesh_quality_stats, QualityStats};
514pub use simplification::simplify_mesh;
515pub use smoothing::{laplacian_smooth, taubin_smooth};
516
517#[cfg(test)]
518mod tests {
519 use super::*;
520
521 fn tetrahedron() -> TriangleMesh {
522 let vertices = vec![
523 Vertex::new(0.0, 0.0, 0.0),
524 Vertex::new(1.0, 0.0, 0.0),
525 Vertex::new(0.5, 1.0, 0.0),
526 Vertex::new(0.5, 0.5, 1.0),
527 ];
528 let faces = vec![
529 Face::new(0, 1, 2),
530 Face::new(0, 1, 3),
531 Face::new(1, 2, 3),
532 Face::new(0, 2, 3),
533 ];
534 TriangleMesh::new(vertices, faces).expect("tetrahedron should be valid")
535 }
536
537 #[test]
538 fn test_mesh_creation() {
539 let mesh = tetrahedron();
540 assert_eq!(mesh.num_vertices(), 4);
541 assert_eq!(mesh.num_faces(), 4);
542 assert_eq!(mesh.num_edges(), 6);
543 }
544
545 #[test]
546 fn test_mesh_invalid_face() {
547 let vertices = vec![Vertex::new(0.0, 0.0, 0.0), Vertex::new(1.0, 0.0, 0.0)];
548 let faces = vec![Face::new(0, 1, 5)]; let result = TriangleMesh::new(vertices, faces);
550 assert!(result.is_err());
551 }
552
553 #[test]
554 fn test_degenerate_face() {
555 let vertices = vec![Vertex::new(0.0, 0.0, 0.0), Vertex::new(1.0, 0.0, 0.0)];
556 let faces = vec![Face::new(0, 0, 1)]; let result = TriangleMesh::new(vertices, faces);
558 assert!(result.is_err());
559 }
560
561 #[test]
562 fn test_face_normals() {
563 let mesh = tetrahedron();
564 let normals = mesh.compute_face_normals().expect("compute normals failed");
565 assert_eq!(normals.len(), 4);
566
567 let n0 = normals[0];
569 assert!((n0[0].abs()) < 1e-10);
570 assert!((n0[1].abs()) < 1e-10);
571 assert!((n0[2].abs() - 1.0) < 1e-10);
572 }
573
574 #[test]
575 fn test_vertex_normals() {
576 let mesh = tetrahedron();
577 let normals = mesh
578 .compute_vertex_normals()
579 .expect("compute normals failed");
580 assert_eq!(normals.len(), 4);
581
582 for n in &normals {
584 let len = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
585 assert!((len - 1.0).abs() < 1e-10, "Normal length: {}", len);
586 }
587 }
588
589 #[test]
590 fn test_surface_area() {
591 let mesh = tetrahedron();
592 let area = mesh.surface_area().expect("area failed");
593 assert!(area > 0.0);
594 }
595
596 #[test]
597 fn test_bounding_box() {
598 let mesh = tetrahedron();
599 let (min_v, max_v) = mesh.bounding_box().expect("non-empty mesh");
600 assert!((min_v.x - 0.0).abs() < 1e-10);
601 assert!((min_v.y - 0.0).abs() < 1e-10);
602 assert!((min_v.z - 0.0).abs() < 1e-10);
603 assert!((max_v.x - 1.0).abs() < 1e-10);
604 assert!((max_v.y - 1.0).abs() < 1e-10);
605 assert!((max_v.z - 1.0).abs() < 1e-10);
606 }
607
608 #[test]
609 fn test_manifold_closed() {
610 let mesh = tetrahedron();
611 assert!(mesh.is_manifold());
612 assert!(mesh.is_closed());
613 assert!(mesh.boundary_edges().is_empty());
614 }
615
616 #[test]
617 fn test_euler_characteristic() {
618 let mesh = tetrahedron();
619 assert_eq!(mesh.euler_characteristic(), 2);
621 }
622
623 #[test]
624 fn test_stl_roundtrip() {
625 let mesh = tetrahedron();
626 let stl_str = mesh.to_stl_ascii("test").expect("STL export failed");
627 assert!(stl_str.starts_with("solid test"));
628 assert!(stl_str.contains("facet normal"));
629 assert!(stl_str.contains("vertex"));
630
631 let mesh2 = TriangleMesh::from_stl_ascii(&stl_str).expect("STL import failed");
632 assert_eq!(mesh2.num_faces(), mesh.num_faces());
634 }
635
636 #[test]
637 fn test_vertex_neighbors() {
638 let mesh = tetrahedron();
639 let neighbors = mesh.vertex_neighbors();
640
641 for i in 0..4 {
643 let nbs = neighbors.get(&i).expect("vertex should have neighbors");
644 assert_eq!(nbs.len(), 3);
645 }
646 }
647
648 #[test]
649 fn test_open_mesh() {
650 let vertices = vec![
652 Vertex::new(0.0, 0.0, 0.0),
653 Vertex::new(1.0, 0.0, 0.0),
654 Vertex::new(0.5, 1.0, 0.0),
655 ];
656 let faces = vec![Face::new(0, 1, 2)];
657 let mesh = TriangleMesh::new(vertices, faces).expect("valid");
658
659 assert!(mesh.is_manifold());
660 assert!(!mesh.is_closed());
661 assert_eq!(mesh.boundary_edges().len(), 3);
662 }
663}