1use nalgebra::{Point3, Vector3};
8
9#[derive(Debug, Clone, Copy, Default)]
12pub struct CoordinateShift {
13 pub x: f64,
15 pub y: f64,
17 pub z: f64,
19}
20
21impl CoordinateShift {
22 #[inline]
24 pub fn new(x: f64, y: f64, z: f64) -> Self {
25 Self { x, y, z }
26 }
27
28 #[inline]
30 pub fn from_point(point: Point3<f64>) -> Self {
31 Self {
32 x: point.x,
33 y: point.y,
34 z: point.z,
35 }
36 }
37
38 #[inline]
40 pub fn is_significant(&self) -> bool {
41 const THRESHOLD: f64 = 10000.0; self.x.abs() > THRESHOLD || self.y.abs() > THRESHOLD || self.z.abs() > THRESHOLD
43 }
44
45 #[inline]
47 pub fn is_zero(&self) -> bool {
48 self.x == 0.0 && self.y == 0.0 && self.z == 0.0
49 }
50}
51
52#[derive(Debug, Clone)]
54pub struct Mesh {
55 pub positions: Vec<f32>,
57 pub normals: Vec<f32>,
59 pub indices: Vec<u32>,
61 pub rtc_applied: bool,
65}
66
67#[derive(Debug, Clone)]
71pub struct SubMesh {
72 pub geometry_id: u32,
74 pub mesh: Mesh,
76}
77
78impl SubMesh {
79 pub fn new(geometry_id: u32, mesh: Mesh) -> Self {
81 Self { geometry_id, mesh }
82 }
83}
84
85#[derive(Debug, Clone, Default)]
87pub struct SubMeshCollection {
88 pub sub_meshes: Vec<SubMesh>,
89}
90
91impl SubMeshCollection {
92 pub fn new() -> Self {
94 Self {
95 sub_meshes: Vec::new(),
96 }
97 }
98
99 pub fn add(&mut self, geometry_id: u32, mesh: Mesh) {
101 if !mesh.is_empty() {
102 self.sub_meshes.push(SubMesh::new(geometry_id, mesh));
103 }
104 }
105
106 pub fn is_empty(&self) -> bool {
108 self.sub_meshes.is_empty()
109 }
110
111 pub fn len(&self) -> usize {
113 self.sub_meshes.len()
114 }
115
116 pub fn into_combined_mesh(self) -> Mesh {
118 let mut combined = Mesh::new();
119 for sub in self.sub_meshes {
120 combined.merge(&sub.mesh);
121 }
122 combined
123 }
124
125 pub fn iter(&self) -> impl Iterator<Item = &SubMesh> {
127 self.sub_meshes.iter()
128 }
129}
130
131impl Mesh {
132 pub fn new() -> Self {
134 Self {
135 positions: Vec::new(),
136 normals: Vec::new(),
137 indices: Vec::new(),
138 rtc_applied: false,
139 }
140 }
141
142 pub fn with_capacity(vertex_count: usize, index_count: usize) -> Self {
144 Self {
145 positions: Vec::with_capacity(vertex_count * 3),
146 normals: Vec::with_capacity(vertex_count * 3),
147 indices: Vec::with_capacity(index_count),
148 rtc_applied: false,
149 }
150 }
151
152 pub fn from_triangle(
154 v0: &Point3<f64>,
155 v1: &Point3<f64>,
156 v2: &Point3<f64>,
157 normal: &Vector3<f64>,
158 ) -> Self {
159 let mut mesh = Self::with_capacity(3, 3);
160 mesh.positions = vec![
161 v0.x as f32,
162 v0.y as f32,
163 v0.z as f32,
164 v1.x as f32,
165 v1.y as f32,
166 v1.z as f32,
167 v2.x as f32,
168 v2.y as f32,
169 v2.z as f32,
170 ];
171 mesh.normals = vec![
172 normal.x as f32,
173 normal.y as f32,
174 normal.z as f32,
175 normal.x as f32,
176 normal.y as f32,
177 normal.z as f32,
178 normal.x as f32,
179 normal.y as f32,
180 normal.z as f32,
181 ];
182 mesh.indices = vec![0, 1, 2];
183 mesh
184 }
185
186 #[inline]
188 pub fn add_vertex(&mut self, position: Point3<f64>, normal: Vector3<f64>) {
189 self.positions.push(position.x as f32);
190 self.positions.push(position.y as f32);
191 self.positions.push(position.z as f32);
192
193 self.normals.push(normal.x as f32);
194 self.normals.push(normal.y as f32);
195 self.normals.push(normal.z as f32);
196 }
197
198 #[inline]
211 pub fn add_vertex_with_shift(
212 &mut self,
213 position: Point3<f64>,
214 normal: Vector3<f64>,
215 shift: &CoordinateShift,
216 ) {
217 let shifted_x = position.x - shift.x;
220 let shifted_y = position.y - shift.y;
221 let shifted_z = position.z - shift.z;
222
223 self.positions.push(shifted_x as f32);
224 self.positions.push(shifted_y as f32);
225 self.positions.push(shifted_z as f32);
226
227 self.normals.push(normal.x as f32);
228 self.normals.push(normal.y as f32);
229 self.normals.push(normal.z as f32);
230 }
231
232 #[inline]
235 pub fn apply_shift(&mut self, shift: &CoordinateShift) {
236 if shift.is_zero() {
237 return;
238 }
239 for chunk in self.positions.chunks_exact_mut(3) {
240 chunk[0] = (chunk[0] as f64 - shift.x) as f32;
242 chunk[1] = (chunk[1] as f64 - shift.y) as f32;
243 chunk[2] = (chunk[2] as f64 - shift.z) as f32;
244 }
245 self.rtc_applied = true;
246 }
247
248 #[inline]
250 pub fn add_triangle(&mut self, i0: u32, i1: u32, i2: u32) {
251 self.indices.push(i0);
252 self.indices.push(i1);
253 self.indices.push(i2);
254 }
255
256 #[inline]
258 pub fn merge(&mut self, other: &Mesh) {
259 if other.is_empty() {
260 return;
261 }
262
263 let vertex_offset = (self.positions.len() / 3) as u32;
264
265 self.positions.reserve(other.positions.len());
267 self.normals.reserve(other.normals.len());
268 self.indices.reserve(other.indices.len());
269
270 self.positions.extend_from_slice(&other.positions);
271 self.normals.extend_from_slice(&other.normals);
272
273 self.indices
275 .extend(other.indices.iter().map(|&i| i + vertex_offset));
276
277 if other.rtc_applied {
279 self.rtc_applied = true;
280 }
281 }
282
283 #[inline]
285 pub fn merge_all(&mut self, meshes: &[Mesh]) {
286 let total_positions: usize = meshes.iter().map(|m| m.positions.len()).sum();
288 let total_indices: usize = meshes.iter().map(|m| m.indices.len()).sum();
289
290 self.positions.reserve(total_positions);
292 self.normals.reserve(total_positions);
293 self.indices.reserve(total_indices);
294
295 for mesh in meshes {
297 if !mesh.is_empty() {
298 let vertex_offset = (self.positions.len() / 3) as u32;
299 self.positions.extend_from_slice(&mesh.positions);
300 self.normals.extend_from_slice(&mesh.normals);
301 self.indices
302 .extend(mesh.indices.iter().map(|&i| i + vertex_offset));
303
304 if mesh.rtc_applied {
306 self.rtc_applied = true;
307 }
308 }
309 }
310 }
311
312 #[inline]
314 pub fn vertex_count(&self) -> usize {
315 self.positions.len() / 3
316 }
317
318 #[inline]
320 pub fn triangle_count(&self) -> usize {
321 self.indices.len() / 3
322 }
323
324 #[inline]
327 pub fn validate_indices(&mut self) {
328 let vertex_count = self.positions.len() / 3;
329 if vertex_count == 0 {
330 self.indices.clear();
331 return;
332 }
333 let mut valid = Vec::with_capacity(self.indices.len());
334 for chunk in self.indices.chunks(3) {
335 if chunk.len() == 3
336 && (chunk[0] as usize) < vertex_count
337 && (chunk[1] as usize) < vertex_count
338 && (chunk[2] as usize) < vertex_count
339 {
340 valid.extend_from_slice(chunk);
341 }
342 }
343 self.indices = valid;
344 }
345
346 #[inline]
348 pub fn is_empty(&self) -> bool {
349 self.positions.is_empty()
350 }
351
352 #[inline]
354 pub fn bounds(&self) -> (Point3<f32>, Point3<f32>) {
355 if self.is_empty() {
356 return (Point3::origin(), Point3::origin());
357 }
358
359 let mut min = Point3::new(f32::MAX, f32::MAX, f32::MAX);
360 let mut max = Point3::new(f32::MIN, f32::MIN, f32::MIN);
361
362 self.positions.chunks_exact(3).for_each(|chunk| {
364 let (x, y, z) = (chunk[0], chunk[1], chunk[2]);
365 min.x = min.x.min(x);
366 min.y = min.y.min(y);
367 min.z = min.z.min(z);
368 max.x = max.x.max(x);
369 max.y = max.y.max(y);
370 max.z = max.z.max(z);
371 });
372
373 (min, max)
374 }
375
376 #[inline]
379 pub fn centroid_f64(&self) -> Point3<f64> {
380 if self.is_empty() {
381 return Point3::origin();
382 }
383
384 let mut sum = Point3::new(0.0f64, 0.0f64, 0.0f64);
385 let count = self.positions.len() / 3;
386
387 self.positions.chunks_exact(3).for_each(|chunk| {
388 sum.x += chunk[0] as f64;
389 sum.y += chunk[1] as f64;
390 sum.z += chunk[2] as f64;
391 });
392
393 Point3::new(
394 sum.x / count as f64,
395 sum.y / count as f64,
396 sum.z / count as f64,
397 )
398 }
399
400 #[inline]
402 pub fn clear(&mut self) {
403 self.positions.clear();
404 self.normals.clear();
405 self.indices.clear();
406 self.rtc_applied = false;
407 }
408
409 pub fn filter_stretched_triangles(&mut self, max_edge_length: f32) -> usize {
422 if self.is_empty() {
423 return 0;
424 }
425
426 let max_edge_sq = max_edge_length * max_edge_length;
427 let mut valid_indices = Vec::new();
428 let mut removed_count = 0;
429
430 for i in (0..self.indices.len()).step_by(3) {
432 if i + 2 >= self.indices.len() {
433 break;
434 }
435 let i0 = self.indices[i] as usize;
436 let i1 = self.indices[i + 1] as usize;
437 let i2 = self.indices[i + 2] as usize;
438
439 if i0 * 3 + 2 >= self.positions.len()
440 || i1 * 3 + 2 >= self.positions.len()
441 || i2 * 3 + 2 >= self.positions.len()
442 {
443 removed_count += 1;
445 continue;
446 }
447
448 let p0 = (
449 self.positions[i0 * 3],
450 self.positions[i0 * 3 + 1],
451 self.positions[i0 * 3 + 2],
452 );
453 let p1 = (
454 self.positions[i1 * 3],
455 self.positions[i1 * 3 + 1],
456 self.positions[i1 * 3 + 2],
457 );
458 let p2 = (
459 self.positions[i2 * 3],
460 self.positions[i2 * 3 + 1],
461 self.positions[i2 * 3 + 2],
462 );
463
464 let edge01_sq = (p1.0 - p0.0).powi(2) + (p1.1 - p0.1).powi(2) + (p1.2 - p0.2).powi(2);
466 let edge12_sq = (p2.0 - p1.0).powi(2) + (p2.1 - p1.1).powi(2) + (p2.2 - p1.2).powi(2);
467 let edge20_sq = (p0.0 - p2.0).powi(2) + (p0.1 - p2.1).powi(2) + (p0.2 - p2.2).powi(2);
468
469 if edge01_sq <= max_edge_sq && edge12_sq <= max_edge_sq && edge20_sq <= max_edge_sq {
471 valid_indices.push(self.indices[i]);
473 valid_indices.push(self.indices[i + 1]);
474 valid_indices.push(self.indices[i + 2]);
475 } else {
476 removed_count += 1;
478 }
479 }
480
481 self.indices = valid_indices;
482 removed_count
483 }
484}
485
486impl Default for Mesh {
487 fn default() -> Self {
488 Self::new()
489 }
490}
491
492#[cfg(test)]
493mod tests {
494 use super::*;
495
496 #[test]
497 fn test_mesh_creation() {
498 let mesh = Mesh::new();
499 assert!(mesh.is_empty());
500 assert_eq!(mesh.vertex_count(), 0);
501 assert_eq!(mesh.triangle_count(), 0);
502 }
503
504 #[test]
505 fn test_add_vertex() {
506 let mut mesh = Mesh::new();
507 mesh.add_vertex(Point3::new(1.0, 2.0, 3.0), Vector3::new(0.0, 0.0, 1.0));
508 assert_eq!(mesh.vertex_count(), 1);
509 assert_eq!(mesh.positions, vec![1.0, 2.0, 3.0]);
510 assert_eq!(mesh.normals, vec![0.0, 0.0, 1.0]);
511 }
512
513 #[test]
514 fn test_merge() {
515 let mut mesh1 = Mesh::new();
516 mesh1.add_vertex(Point3::new(0.0, 0.0, 0.0), Vector3::z());
517 mesh1.add_triangle(0, 1, 2);
518
519 let mut mesh2 = Mesh::new();
520 mesh2.add_vertex(Point3::new(1.0, 1.0, 1.0), Vector3::y());
521 mesh2.add_triangle(0, 1, 2);
522
523 mesh1.merge(&mesh2);
524 assert_eq!(mesh1.vertex_count(), 2);
525 assert_eq!(mesh1.triangle_count(), 2);
526 }
527
528 #[test]
529 fn test_coordinate_shift_creation() {
530 let shift = CoordinateShift::new(500000.0, 5000000.0, 100.0);
531 assert!(shift.is_significant());
532 assert!(!shift.is_zero());
533
534 let zero_shift = CoordinateShift::default();
535 assert!(!zero_shift.is_significant());
536 assert!(zero_shift.is_zero());
537 }
538
539 #[test]
540 fn test_add_vertex_with_shift_preserves_precision() {
541 let mut mesh = Mesh::new();
546
547 let p1 = Point3::new(2679012.123456, 1247892.654321, 432.111);
549 let p2 = Point3::new(2679012.223456, 1247892.754321, 432.211);
550
551 let shift = CoordinateShift::new(2679012.0, 1247892.0, 432.0);
553
554 mesh.add_vertex_with_shift(p1, Vector3::z(), &shift);
555 mesh.add_vertex_with_shift(p2, Vector3::z(), &shift);
556
557 assert!((mesh.positions[0] - 0.123456).abs() < 0.0001); assert!((mesh.positions[1] - 0.654321).abs() < 0.0001); assert!((mesh.positions[2] - 0.111).abs() < 0.0001); assert!((mesh.positions[3] - 0.223456).abs() < 0.0001); assert!((mesh.positions[4] - 0.754321).abs() < 0.0001); assert!((mesh.positions[5] - 0.211).abs() < 0.0001); let dx = mesh.positions[3] - mesh.positions[0];
569 let dy = mesh.positions[4] - mesh.positions[1];
570 let dz = mesh.positions[5] - mesh.positions[2];
571
572 assert!((dx - 0.1).abs() < 0.0001);
574 assert!((dy - 0.1).abs() < 0.0001);
575 assert!((dz - 0.1).abs() < 0.0001);
576 }
577
578 #[test]
579 fn test_apply_shift_to_existing_mesh() {
580 let mut mesh = Mesh::new();
581
582 mesh.positions = vec![500000.0, 5000000.0, 0.0, 500010.0, 5000010.0, 10.0];
584 mesh.normals = vec![0.0, 0.0, 1.0, 0.0, 0.0, 1.0];
585
586 let shift = CoordinateShift::new(500000.0, 5000000.0, 0.0);
588 mesh.apply_shift(&shift);
589
590 assert!((mesh.positions[0] - 0.0).abs() < 0.001);
592 assert!((mesh.positions[1] - 0.0).abs() < 0.001);
593 assert!((mesh.positions[3] - 10.0).abs() < 0.001);
594 assert!((mesh.positions[4] - 10.0).abs() < 0.001);
595 }
596
597 #[test]
598 fn test_centroid_f64() {
599 let mut mesh = Mesh::new();
600 mesh.positions = vec![0.0, 0.0, 0.0, 10.0, 10.0, 10.0, 20.0, 20.0, 20.0];
601 mesh.normals = vec![0.0; 9];
602
603 let centroid = mesh.centroid_f64();
604 assert!((centroid.x - 10.0).abs() < 0.001);
605 assert!((centroid.y - 10.0).abs() < 0.001);
606 assert!((centroid.z - 10.0).abs() < 0.001);
607 }
608
609 #[test]
610 fn test_precision_comparison_shifted_vs_unshifted() {
611 let base_x = 2679012.0;
616 let base_y = 1247892.0;
617 let offset = 0.001; let p1 = Point3::new(base_x, base_y, 0.0);
620 let p2 = Point3::new(base_x + offset, base_y, 0.0);
621
622 let p1_f32_direct = (p1.x as f32, p1.y as f32);
624 let p2_f32_direct = (p2.x as f32, p2.y as f32);
625 let diff_direct = p2_f32_direct.0 - p1_f32_direct.0;
626
627 let shift = CoordinateShift::new(base_x, base_y, 0.0);
629 let p1_shifted = ((p1.x - shift.x) as f32, (p1.y - shift.y) as f32);
630 let p2_shifted = ((p2.x - shift.x) as f32, (p2.y - shift.y) as f32);
631 let diff_shifted = p2_shifted.0 - p1_shifted.0;
632
633 println!("Direct f32 difference (should be ~0.001): {}", diff_direct);
634 println!(
635 "Shifted f32 difference (should be ~0.001): {}",
636 diff_shifted
637 );
638
639 let error_direct = (diff_direct - offset as f32).abs();
641 let error_shifted = (diff_shifted - offset as f32).abs();
642
643 println!("Error without shift: {}m", error_direct);
644 println!("Error with shift: {}m", error_shifted);
645
646 assert!(
649 error_shifted < error_direct || error_shifted < 0.0001,
650 "Shifted precision should be better than direct conversion"
651 );
652 }
653
654 #[test]
655 fn test_validate_indices_strips_out_of_bounds() {
656 let mut mesh = Mesh {
657 positions: vec![0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0], normals: vec![],
659 indices: vec![
660 0, 1, 2, 0, 1, 5, 3, 4, 5, ],
664 rtc_applied: false,
665 };
666 mesh.validate_indices();
667 assert_eq!(mesh.indices, vec![0, 1, 2]);
668 }
669
670 #[test]
671 fn test_validate_indices_empty_positions() {
672 let mut mesh = Mesh {
673 positions: vec![],
674 normals: vec![],
675 indices: vec![0, 1, 2],
676 rtc_applied: false,
677 };
678 mesh.validate_indices();
679 assert!(mesh.indices.is_empty());
680 }
681
682 #[test]
683 fn test_validate_indices_incomplete_triangle() {
684 let mut mesh = Mesh {
685 positions: vec![0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
686 normals: vec![],
687 indices: vec![0, 1, 2, 0, 1], rtc_applied: false,
689 };
690 mesh.validate_indices();
691 assert_eq!(mesh.indices, vec![0, 1, 2]);
692 }
693
694 #[test]
695 fn test_validate_indices_all_valid() {
696 let mut mesh = Mesh {
697 positions: vec![0.0; 12], normals: vec![],
699 indices: vec![0, 1, 2, 1, 2, 3],
700 rtc_applied: false,
701 };
702 mesh.validate_indices();
703 assert_eq!(mesh.indices, vec![0, 1, 2, 1, 2, 3]);
704 }
705}