1use std::fmt;
2use std::sync::atomic::{AtomicU64, Ordering};
3
4use crate::adjacency::Adjacency;
5use crate::channel::{Channel, ChannelDesc, ChannelKind, UV_CHANNEL};
6use crate::column::{Column, try_reserve, try_reserve_exact, try_vec};
7use crate::incidence::Incidence;
8use crate::{Patch, PatchSide, PatchState, Result, StoreError, TriangleId, VertexId};
9
10static NEXT_MESH_UID: AtomicU64 = AtomicU64::new(1);
11
12pub struct Mesh {
20 pub(crate) uid: u64,
21 pub(crate) epoch: u32,
22 pub(crate) revision: u64,
23 pub(crate) next_revision: u64,
24 pub(crate) origin: [f64; 3],
25 pub(crate) positions: Column<[f32; 3]>,
26 pub(crate) triangles: Column<[u32; 3]>,
27 pub(crate) vertex_live: Column<u64>,
28 pub(crate) triangle_live: Column<u64>,
29 pub(crate) vertex_refs: Column<u32>,
31 pub(crate) channels: Vec<Channel>,
32 pub(crate) next_channel_id: u64,
34 pub(crate) vertex_free: Vec<VertexId>,
35 pub(crate) triangle_free: Vec<TriangleId>,
36 pub(crate) live_vertices: usize,
37 pub(crate) live_triangles: usize,
38 pub(crate) adjacency: Option<Adjacency>,
40 pub(crate) incidence: Option<Incidence>,
42 pub(crate) incidence_pending: bool,
45}
46
47pub(crate) fn bit(bits: &Column<u64>, i: usize) -> bool {
48 let word = i / 64;
49 word < bits.len() && (bits.get(word) >> (i % 64)) & 1 == 1
50}
51
52pub(crate) fn set_bit(bits: &mut Column<u64>, i: usize, on: bool) -> Result<()> {
53 let word = i / 64;
54 bits.grow_to(word + 1, 0)?;
55 let old = bits.get(word);
56 let mask = 1u64 << (i % 64);
57 let new = if on { old | mask } else { old & !mask };
58 if new != old {
59 bits.set(word, new);
60 }
61 Ok(())
62}
63
64pub(crate) fn full_bits(len: usize) -> Result<Vec<u64>> {
65 let mut words = try_vec(len.div_ceil(64), u64::MAX)?;
66 if let Some(last) = words.last_mut()
67 && !len.is_multiple_of(64)
68 {
69 *last = (1u64 << (len % 64)) - 1;
70 }
71 Ok(words)
72}
73
74impl fmt::Debug for Mesh {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 f.debug_struct("Mesh")
78 .field("uid", &self.uid)
79 .field("revision", &self.revision)
80 .field("origin", &self.origin)
81 .field("vertices", &self.live_vertices)
82 .field("vertex_slots", &self.vertex_slots())
83 .field("triangles", &self.live_triangles)
84 .field("triangle_slots", &self.triangle_slots())
85 .field(
86 "channels",
87 &self
88 .channels
89 .iter()
90 .filter(|c| !c.hidden)
91 .map(|c| c.desc.name.as_str())
92 .collect::<Vec<_>>(),
93 )
94 .finish_non_exhaustive()
95 }
96}
97
98impl Default for Mesh {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104impl Mesh {
105 pub fn new() -> Self {
107 Self::assemble(
109 [0.0; 3],
110 Vec::new(),
111 Vec::new(),
112 Vec::new(),
113 Vec::new(),
114 Vec::new(),
115 )
116 }
117
118 pub fn from_buffers(positions: &[f32], indices: &[u32]) -> Result<Self> {
120 if !positions.len().is_multiple_of(3) {
121 return Err(StoreError::InvalidArgument(
122 "positions length must be a multiple of 3",
123 ));
124 }
125 let mut points = try_vec(positions.len() / 3, [0.0f32; 3])?;
126 for (p, c) in points.iter_mut().zip(positions.as_chunks::<3>().0) {
127 *p = *c;
128 }
129 Self::build([0.0; 3], points, indices)
130 }
131
132 pub fn from_buffers_f64(positions: &[f64], indices: &[u32]) -> Result<Self> {
137 if !positions.len().is_multiple_of(3) {
138 return Err(StoreError::InvalidArgument(
139 "positions length must be a multiple of 3",
140 ));
141 }
142 let not_finite = StoreError::InvalidArgument("positions must be finite");
143 if !positions.iter().all(|x| x.is_finite()) {
144 return Err(not_finite);
145 }
146 let mut min = [f64::INFINITY; 3];
147 let mut max = [f64::NEG_INFINITY; 3];
148 for c in positions.as_chunks::<3>().0 {
149 for k in 0..3 {
150 min[k] = min[k].min(c[k]);
151 max[k] = max[k].max(c[k]);
152 }
153 }
154 let origin = if positions.is_empty() {
155 [0.0; 3]
156 } else {
157 [0, 1, 2].map(|k| (min[k] + max[k]) * 0.5)
158 };
159 Self::with_origin_checked(positions, indices, origin)
160 }
161
162 pub fn from_buffers_f64_with_origin(
166 positions: &[f64],
167 indices: &[u32],
168 origin: [f64; 3],
169 ) -> Result<Self> {
170 if !positions.len().is_multiple_of(3) {
171 return Err(StoreError::InvalidArgument(
172 "positions length must be a multiple of 3",
173 ));
174 }
175 if !positions.iter().all(|x| x.is_finite()) {
176 return Err(StoreError::InvalidArgument("positions must be finite"));
177 }
178 Self::with_origin_checked(positions, indices, origin)
179 }
180
181 fn with_origin_checked(positions: &[f64], indices: &[u32], origin: [f64; 3]) -> Result<Self> {
185 let not_finite = StoreError::InvalidArgument("positions must be finite");
186 if !origin.iter().all(|x| x.is_finite()) {
187 return Err(not_finite);
188 }
189 let mut points = try_vec(positions.len() / 3, [0.0f32; 3])?;
190 for (p, c) in points.iter_mut().zip(positions.as_chunks::<3>().0) {
191 *p = [0, 1, 2].map(|k| (c[k] - origin[k]) as f32);
192 if !p.iter().all(|x| x.is_finite()) {
193 return Err(not_finite);
194 }
195 }
196 Self::build(origin, points, indices)
197 }
198
199 fn build(origin: [f64; 3], points: Vec<[f32; 3]>, indices: &[u32]) -> Result<Self> {
200 if !indices.len().is_multiple_of(3) {
201 return Err(StoreError::InvalidArgument(
202 "indices length must be a multiple of 3",
203 ));
204 }
205 let vertex_count = points.len();
206 let mut refs = try_vec(vertex_count, 0u32)?;
207 let mut tris = try_vec(indices.len() / 3, [0u32; 3])?;
208 for (t, c) in tris.iter_mut().zip(indices.as_chunks::<3>().0) {
209 for &v in c {
210 if v as usize >= vertex_count {
211 return Err(StoreError::InvalidVertex(v));
212 }
213 refs[v as usize] += 1;
214 }
215 *t = *c;
216 }
217 Self::from_parts(origin, points, tris, refs)
218 }
219
220 fn from_parts(
222 origin: [f64; 3],
223 points: Vec<[f32; 3]>,
224 tris: Vec<[u32; 3]>,
225 refs: Vec<u32>,
226 ) -> Result<Self> {
227 let vertex_live = full_bits(points.len())?;
228 let triangle_live = full_bits(tris.len())?;
229 Ok(Self::assemble(
230 origin,
231 points,
232 tris,
233 refs,
234 vertex_live,
235 triangle_live,
236 ))
237 }
238
239 fn assemble(
240 origin: [f64; 3],
241 points: Vec<[f32; 3]>,
242 tris: Vec<[u32; 3]>,
243 refs: Vec<u32>,
244 vertex_live: Vec<u64>,
245 triangle_live: Vec<u64>,
246 ) -> Self {
247 let (v, t) = (points.len(), tris.len());
248 Self {
249 uid: NEXT_MESH_UID.fetch_add(1, Ordering::Relaxed),
250 epoch: 0,
251 revision: 0,
252 next_revision: 0,
253 origin,
254 positions: Column::from_vec(points),
255 triangles: Column::from_vec(tris),
256 vertex_live: Column::from_vec(vertex_live),
257 triangle_live: Column::from_vec(triangle_live),
258 vertex_refs: Column::from_vec(refs),
259 channels: Vec::new(),
260 next_channel_id: 0,
261 vertex_free: Vec::new(),
262 triangle_free: Vec::new(),
263 live_vertices: v,
264 live_triangles: t,
265 adjacency: None,
266 incidence: None,
267 incidence_pending: false,
268 }
269 }
270
271 pub fn uid(&self) -> u64 {
273 self.uid
274 }
275
276 pub fn revision(&self) -> u64 {
278 self.revision
279 }
280
281 pub fn epoch(&self) -> u32 {
284 self.epoch
285 }
286
287 pub fn origin(&self) -> [f64; 3] {
289 self.origin
290 }
291
292 pub fn vertex_slots(&self) -> usize {
294 self.positions.len()
295 }
296
297 pub fn triangle_slots(&self) -> usize {
299 self.triangles.len()
300 }
301
302 pub fn vertex_count(&self) -> usize {
304 self.live_vertices
305 }
306
307 pub fn triangle_count(&self) -> usize {
309 self.live_triangles
310 }
311
312 pub fn is_vertex_alive(&self, v: VertexId) -> bool {
313 bit(&self.vertex_live, v as usize)
314 }
315
316 pub fn is_triangle_alive(&self, t: TriangleId) -> bool {
317 bit(&self.triangle_live, t as usize)
318 }
319
320 pub fn position(&self, v: VertexId) -> Option<[f32; 3]> {
322 self.is_vertex_alive(v)
323 .then(|| self.positions.get(v as usize))
324 }
325
326 pub fn world_position(&self, v: VertexId) -> Option<[f64; 3]> {
328 self.position(v)
329 .map(|p| [0, 1, 2].map(|k| self.origin[k] + p[k] as f64))
330 }
331
332 pub fn positions(&self) -> &[[f32; 3]] {
334 self.positions.as_slice()
335 }
336
337 pub fn triangle(&self, t: TriangleId) -> Option<[VertexId; 3]> {
339 self.is_triangle_alive(t)
340 .then(|| self.triangles.get(t as usize))
341 }
342
343 pub fn triangles(&self) -> &[[VertexId; 3]] {
345 self.triangles.as_slice()
346 }
347
348 pub fn corner_count(&self, v: VertexId) -> u32 {
350 if (v as usize) < self.vertex_refs.len() {
351 self.vertex_refs.get(v as usize)
352 } else {
353 0
354 }
355 }
356
357 pub fn free_vertex_slots(&self) -> &[VertexId] {
359 &self.vertex_free
360 }
361
362 pub fn free_triangle_slots(&self) -> &[TriangleId] {
364 &self.triangle_free
365 }
366
367 pub fn add_channel(&mut self, desc: ChannelDesc) -> Result<()> {
369 if !(1..=4).contains(&desc.width) {
370 return Err(StoreError::InvalidArgument("channel width must be 1 to 4"));
371 }
372 if desc.name == UV_CHANNEL && (desc.width != 2 || desc.kind != ChannelKind::Source) {
373 return Err(StoreError::InvalidArgument(
374 "the uv channel must be a source channel of width 2",
375 ));
376 }
377 if self.channel_index(&desc.name).is_some() {
378 return Err(StoreError::ChannelExists(desc.name));
379 }
380 let data = try_vec(self.vertex_slots() * desc.width, desc.default)?;
381 try_reserve(&mut self.channels, 1)?;
382 let id = self.next_channel_id;
383 self.next_channel_id += 1;
384 self.channels.push(Channel {
385 id,
386 desc,
387 data: Column::from_vec(data),
388 hidden: false,
389 });
390 Ok(())
391 }
392
393 pub fn add_channel_with_values(&mut self, desc: ChannelDesc, values: &[f32]) -> Result<()> {
401 let expected = self.vertex_slots() * desc.width;
402 if values.len() != expected {
403 return Err(StoreError::LengthMismatch {
404 expected,
405 got: values.len(),
406 });
407 }
408 self.add_channel(desc)?;
409 if let Some(channel) = self.channels.last_mut() {
410 channel.data.as_mut_slice().copy_from_slice(values);
411 }
412 Ok(())
413 }
414
415 pub fn add_uvs(&mut self, uvs: &[f32]) -> Result<()> {
425 let expected = self.vertex_slots() * 2;
426 if uvs.len() != expected {
427 return Err(StoreError::LengthMismatch {
428 expected,
429 got: uvs.len(),
430 });
431 }
432 if !uvs.iter().all(|x| x.is_finite()) {
433 return Err(StoreError::InvalidArgument("uvs must be finite"));
434 }
435 if self.channel_index(UV_CHANNEL).is_some() {
436 return Err(StoreError::ChannelExists(UV_CHANNEL.into()));
437 }
438 let mut data: Vec<f32> = Vec::new();
439 try_reserve_exact(&mut data, uvs.len())?;
440 data.extend_from_slice(uvs);
441 try_reserve(&mut self.channels, 1)?;
442 let id = self.next_channel_id;
443 self.next_channel_id += 1;
444 self.channels.push(Channel {
445 id,
446 desc: ChannelDesc::uv(),
447 data: Column::from_vec(data),
448 hidden: false,
449 });
450 Ok(())
451 }
452
453 pub fn uvs(&self) -> Option<&[f32]> {
456 self.channel(UV_CHANNEL)
457 }
458
459 pub fn remove_channel(&mut self, name: &str) -> Result<ChannelDesc> {
461 let i = self
462 .channel_index(name)
463 .ok_or_else(|| StoreError::UnknownChannel(name.into()))?;
464 Ok(self.channels.remove(i).desc)
465 }
466
467 pub fn channel_desc(&self, name: &str) -> Option<&ChannelDesc> {
468 self.channel_index(name).map(|i| &self.channels[i].desc)
469 }
470
471 pub fn channel(&self, name: &str) -> Option<&[f32]> {
473 self.channel_index(name)
474 .map(|i| self.channels[i].data.as_slice())
475 }
476
477 pub fn derived_channel_mut(&mut self, name: &str) -> Result<&mut [f32]> {
479 let i = self
480 .channel_index(name)
481 .ok_or_else(|| StoreError::UnknownChannel(name.into()))?;
482 if self.channels[i].desc.kind != ChannelKind::Derived {
483 return Err(StoreError::NotDerivedChannel(name.into()));
484 }
485 Ok(self.channels[i].data.as_mut_slice())
486 }
487
488 pub(crate) fn channel_index(&self, name: &str) -> Option<usize> {
489 self.channels
490 .iter()
491 .position(|c| !c.hidden && c.desc.name == name)
492 }
493
494 pub(crate) fn channel_index_by_id(&self, id: u64) -> Option<usize> {
497 self.channels.iter().position(|c| c.id == id)
498 }
499
500 pub(crate) fn release_vertices(&mut self, ids: &[VertexId]) {
503 for &v in ids {
504 if (v as usize) < self.vertex_slots() && !self.is_vertex_alive(v) {
505 self.vertex_free.push(v);
506 }
507 }
508 }
509
510 pub(crate) fn release_triangles(&mut self, ids: &[TriangleId]) {
512 for &t in ids {
513 if (t as usize) < self.triangle_slots() && !self.is_triangle_alive(t) {
514 self.triangle_free.push(t);
515 }
516 }
517 }
518
519 pub fn release_patch_slots(&mut self, patch: &Patch, state: PatchState) -> Result<()> {
534 if patch.mesh_uid != self.uid {
535 return Err(StoreError::PatchMismatch);
536 }
537 if patch.epoch != self.epoch {
538 return Err(StoreError::StalePatch);
539 }
540 let side = match state {
541 PatchState::Applied => PatchSide::Undoable,
542 PatchState::Undone => PatchSide::Redoable,
543 };
544 patch.release(self, side);
545 Ok(())
546 }
547
548 pub(crate) fn on_topology_write(&mut self) {
550 self.adjacency = None;
551 self.incidence_pending = true;
552 }
553
554 pub(crate) fn on_slots_renumbered(&mut self) {
556 self.adjacency = None;
557 self.incidence = None;
558 self.incidence_pending = false;
559 }
560}
561
562#[derive(Debug, Clone, Copy, PartialEq, Eq)]
564enum BuildPhase {
565 Finite,
567 Bounds,
569 Convert,
571 Indices,
573 Done,
575}
576
577#[derive(Debug)]
590pub struct MeshBuilder {
591 vertices: usize,
592 triangles: usize,
593 origin: [f64; 3],
596 auto_origin: bool,
598 positions: Vec<f64>,
599 indices: Vec<u32>,
600 phase: BuildPhase,
601 min: [f64; 3],
602 max: [f64; 3],
603 points: Vec<[f32; 3]>,
604 tris: Vec<[u32; 3]>,
605 refs: Vec<u32>,
607 vertex_live: Vec<u64>,
608 triangle_live: Vec<u64>,
609 cursor: usize,
612}
613
614impl MeshBuilder {
615 pub fn new(vertices: usize, triangles: usize, origin: Option<[f64; 3]>) -> Result<Self> {
624 let not_finite = StoreError::InvalidArgument("positions must be finite");
625 if let Some(o) = origin
626 && !o.iter().all(|x| x.is_finite())
627 {
628 return Err(not_finite);
629 }
630 let flat_vertices = vertices.checked_mul(3).ok_or(StoreError::OutOfMemory)?;
632 let flat_triangles = triangles.checked_mul(3).ok_or(StoreError::OutOfMemory)?;
633 let mut positions = Vec::new();
634 try_reserve_exact(&mut positions, flat_vertices)?;
635 let mut indices = Vec::new();
636 try_reserve_exact(&mut indices, flat_triangles)?;
637 let mut points = Vec::new();
638 try_reserve_exact(&mut points, vertices)?;
639 let mut tris = Vec::new();
640 try_reserve_exact(&mut tris, triangles)?;
641 let mut refs = Vec::new();
642 try_reserve_exact(&mut refs, vertices)?;
643 Ok(MeshBuilder {
644 vertices,
645 triangles,
646 origin: origin.unwrap_or([0.0; 3]),
647 auto_origin: origin.is_none(),
648 positions,
649 indices,
650 phase: BuildPhase::Finite,
651 min: [f64::INFINITY; 3],
652 max: [f64::NEG_INFINITY; 3],
653 points,
654 tris,
655 refs,
656 vertex_live: Vec::new(),
657 triangle_live: Vec::new(),
658 cursor: 0,
659 })
660 }
661
662 pub fn push_positions(&mut self, chunk: &[f64]) -> Result<()> {
668 if chunk.len() > 3 * self.vertices - self.positions.len() {
669 return Err(StoreError::InvalidArgument(
670 "positions exceed the declared vertex count",
671 ));
672 }
673 self.positions.extend_from_slice(chunk);
675 Ok(())
676 }
677
678 pub fn push_indices(&mut self, chunk: &[u32]) -> Result<()> {
685 if chunk.len() > 3 * self.triangles - self.indices.len() {
686 return Err(StoreError::InvalidArgument(
687 "indices exceed the declared triangle count",
688 ));
689 }
690 self.indices.extend_from_slice(chunk);
691 Ok(())
692 }
693
694 pub fn is_done(&self) -> bool {
696 self.phase == BuildPhase::Done
697 }
698
699 pub fn step(&mut self, budget: usize) -> Result<bool> {
711 if self.positions.len() != 3 * self.vertices || self.indices.len() != 3 * self.triangles {
712 return Err(StoreError::InvalidArgument("upload incomplete"));
713 }
714 let not_finite = StoreError::InvalidArgument("positions must be finite");
715 let n = budget.max(1);
716 match self.phase {
717 BuildPhase::Finite => {
718 let end = self.cursor.saturating_add(n).min(self.vertices);
719 if !self.positions[3 * self.cursor..3 * end]
720 .iter()
721 .all(|x| x.is_finite())
722 {
723 return Err(not_finite);
724 }
725 self.cursor = end;
726 if end == self.vertices {
727 self.phase = BuildPhase::Bounds;
728 self.cursor = 0;
729 }
730 }
731 BuildPhase::Bounds => {
732 let end = self.cursor.saturating_add(n).min(self.vertices);
733 for c in self.positions[3 * self.cursor..3 * end].as_chunks::<3>().0 {
734 for (k, &x) in c.iter().enumerate() {
735 self.min[k] = self.min[k].min(x);
736 self.max[k] = self.max[k].max(x);
737 }
738 }
739 self.cursor = end;
740 if end == self.vertices {
741 if self.auto_origin {
742 self.origin = if self.vertices == 0 {
743 [0.0; 3]
744 } else {
745 [0, 1, 2].map(|k| (self.min[k] + self.max[k]) * 0.5)
746 };
747 self.auto_origin = false;
748 }
749 if !self.origin.iter().all(|x| x.is_finite()) {
750 return Err(not_finite);
751 }
752 self.phase = BuildPhase::Convert;
753 self.cursor = 0;
754 }
755 }
756 BuildPhase::Convert => {
757 let end = self.cursor.saturating_add(n).min(self.vertices);
758 for c in self.positions[3 * self.cursor..3 * end].as_chunks::<3>().0 {
759 let p = [0, 1, 2].map(|k| (c[k] - self.origin[k]) as f32);
760 if !p.iter().all(|x| x.is_finite()) {
761 return Err(not_finite);
762 }
763 self.points.push(p);
764 }
765 self.refs.resize(end, 0);
766 self.cursor = end;
767 if end == self.vertices {
768 self.phase = BuildPhase::Indices;
769 self.cursor = 0;
770 }
771 }
772 BuildPhase::Indices => {
773 let end = self.cursor.saturating_add(n).min(self.triangles);
774 for c in self.indices[3 * self.cursor..3 * end].as_chunks::<3>().0 {
775 for &v in c {
776 if v as usize >= self.vertices {
777 return Err(StoreError::InvalidVertex(v));
778 }
779 self.refs[v as usize] += 1;
780 }
781 self.tris.push(*c);
782 }
783 self.cursor = end;
784 if end == self.triangles {
785 self.vertex_live = full_bits(self.vertices)?;
786 self.triangle_live = full_bits(self.triangles)?;
787 self.phase = BuildPhase::Done;
788 }
789 }
790 BuildPhase::Done => return Ok(true),
791 }
792 Ok(self.phase == BuildPhase::Done)
793 }
794
795 pub fn finish(self) -> Option<Mesh> {
797 if self.phase != BuildPhase::Done {
798 return None;
799 }
800 Some(Mesh::assemble(
801 self.origin,
802 self.points,
803 self.tris,
804 self.refs,
805 self.vertex_live,
806 self.triangle_live,
807 ))
808 }
809}
810
811#[cfg(test)]
812mod tests {
813 use super::*;
814 use crate::PatchState;
815
816 type TwinCase = (Vec<f64>, Vec<u32>, Option<[f64; 3]>);
818 type FailCase = (Vec<f64>, Vec<u32>, Option<[f64; 3]>, StoreError);
820
821 fn quad() -> Mesh {
822 Mesh::from_buffers(
823 &[0., 0., 0., 1., 0., 0., 1., 1., 0., 0., 1., 0.],
824 &[0, 1, 2, 0, 2, 3],
825 )
826 .unwrap()
827 }
828
829 #[test]
830 fn builds_from_flat_buffers() {
831 let m = quad();
832 assert_eq!((m.vertex_count(), m.triangle_count()), (4, 2));
833 assert_eq!(m.position(2), Some([1., 1., 0.]));
834 assert_eq!(m.triangle(1), Some([0, 2, 3]));
835 assert_eq!(m.corner_count(0), 2);
836 assert_eq!(m.corner_count(1), 1);
837 }
838
839 #[test]
840 fn rejects_bad_buffers() {
841 assert!(matches!(
842 Mesh::from_buffers(&[0., 0.], &[]),
843 Err(StoreError::InvalidArgument(_))
844 ));
845 assert_eq!(
846 Mesh::from_buffers(&[0., 0., 0.], &[0, 0, 5]).err(),
847 Some(StoreError::InvalidVertex(5))
848 );
849 }
850
851 #[test]
852 fn f64_input_keeps_precision_through_the_origin() {
853 let far = 1.0e7;
854 let m = Mesh::from_buffers_f64(&[far, far, 0.0, far + 0.001, far, 0.0], &[]).unwrap();
855 let o = m.origin();
856 assert!((o[0] - (far + 0.0005)).abs() < 1e-6 && o[1] == far && o[2] == 0.0);
857 let p = m.world_position(1).unwrap();
858 assert!((p[0] - (far + 0.001)).abs() < 1e-6, "got {}", p[0]);
859 }
860
861 #[test]
862 fn f64_input_must_be_finite() {
863 let bad = StoreError::InvalidArgument("positions must be finite");
864 for x in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
865 assert_eq!(
866 Mesh::from_buffers_f64(&[0., 0., 0., x, 1., 2.], &[]).err(),
867 Some(bad.clone())
868 );
869 }
870 assert_eq!(
872 Mesh::from_buffers_f64(&[f64::MAX, 0., 0., f64::MAX, 0., 0.], &[]).err(),
873 Some(bad)
874 );
875 }
876
877 #[test]
878 fn explicit_origin_stores_offsets() {
879 let o = [1.0e6; 3];
880 let offsets = [[0.25, -0.5, 1.0], [-3.0, 2.125, 0.0], [10.0, 0.0, -7.5]];
881 let flat: Vec<f64> = offsets
882 .iter()
883 .flat_map(|d| [0, 1, 2].map(|k| o[k] + d[k]))
884 .collect();
885 let m = Mesh::from_buffers_f64_with_origin(&flat, &[0, 1, 2], o).unwrap();
886 assert_eq!(m.origin(), o);
887 for (v, d) in offsets.iter().enumerate() {
888 let p = m.position(v as u32).unwrap();
889 for k in 0..3 {
890 assert!((p[k] as f64 - d[k]).abs() < 1e-3, "{v}: {p:?} vs {d:?}");
891 }
892 }
893 let bad = StoreError::InvalidArgument("positions must be finite");
894 assert_eq!(
895 Mesh::from_buffers_f64_with_origin(&flat, &[], [f64::NAN, 0., 0.]).err(),
896 Some(bad.clone())
897 );
898 assert_eq!(
899 Mesh::from_buffers_f64_with_origin(&[f64::INFINITY, 0., 0.], &[], [0.; 3]).err(),
900 Some(bad)
901 );
902 assert!(matches!(
903 Mesh::from_buffers_f64_with_origin(&[0., 0.], &[], [0.; 3]),
904 Err(StoreError::InvalidArgument(_))
905 ));
906 }
907
908 #[test]
909 fn offsets_overflowing_f32_are_rejected() {
910 let bad = StoreError::InvalidArgument("positions must be finite");
911 assert_eq!(
913 Mesh::from_buffers_f64_with_origin(&[1.0e39, 0., 0.], &[], [0.; 3]).err(),
914 Some(bad.clone())
915 );
916 assert_eq!(
917 Mesh::from_buffers_f64_with_origin(&[0., 0., 0.], &[], [0., -1.0e39, 0.]).err(),
918 Some(bad.clone())
919 );
920 assert_eq!(
922 Mesh::from_buffers_f64(&[-1.0e300, 0., 0., 1.0e300, 0., 0.], &[]).err(),
923 Some(bad)
924 );
925 }
926
927 #[test]
928 fn out_of_range_ids_read_as_dead() {
929 let m = quad();
930 assert!(!m.is_vertex_alive(99));
931 assert_eq!(m.position(99), None);
932 assert_eq!(m.triangle(99), None);
933 }
934
935 #[test]
936 fn channels_are_slot_aligned_and_unique() {
937 let mut m = quad();
938 m.add_channel(ChannelDesc::source("mask", 1).with_default(0.5))
939 .unwrap();
940 assert_eq!(m.channel("mask"), Some(&[0.5; 4][..]));
941 assert_eq!(
942 m.add_channel(ChannelDesc::source("mask", 1)),
943 Err(StoreError::ChannelExists("mask".into()))
944 );
945 assert!(matches!(
946 m.add_channel(ChannelDesc::source("wide", 5)),
947 Err(StoreError::InvalidArgument(_))
948 ));
949 assert_eq!(
950 m.derived_channel_mut("mask").err(),
951 Some(StoreError::NotDerivedChannel("mask".into()))
952 );
953 m.add_channel(ChannelDesc::derived("dist", 1)).unwrap();
954 m.derived_channel_mut("dist").unwrap()[3] = 7.0;
955 assert_eq!(m.channel("dist").unwrap()[3], 7.0);
956 assert_eq!(m.remove_channel("dist").unwrap().name, "dist");
957 assert_eq!(m.channel("dist"), None);
958 }
959
960 #[test]
961 fn debug_output_shows_counts() {
962 let mut m = quad();
963 m.ensure_adjacency().unwrap();
964 let s = format!("{m:?}");
965 assert!(
966 s.contains("vertices: 4") && s.contains("triangles: 2"),
967 "{s}"
968 );
969 assert!(format!("{:?}", m.adjacency().unwrap()).starts_with("Adjacency"));
970 }
971
972 #[test]
973 fn every_mesh_gets_a_distinct_uid() {
974 assert_ne!(Mesh::new().uid(), Mesh::new().uid());
975 }
976
977 #[test]
978 fn release_patch_slots_follows_history_rules() {
979 let mut m = quad();
980 let mut tx = m.begin_edit();
981 tx.remove_triangles(&[1]).unwrap();
982 tx.remove_vertices(&[3]).unwrap();
983 let removal = tx.commit().unwrap();
984 assert!(m.free_vertex_slots().is_empty(), "reserved by the patch");
985 m.release_patch_slots(&removal, PatchState::Undone).unwrap();
987 assert!(m.free_vertex_slots().is_empty());
988 m.release_patch_slots(&removal, PatchState::Applied)
989 .unwrap();
990 assert_eq!(m.free_vertex_slots(), &[3]);
991 assert_eq!(m.free_triangle_slots(), &[1]);
992
993 let mut tx = m.begin_edit();
994 tx.add_vertices(&[[0.; 3]]).unwrap();
995 let creation = tx.commit().unwrap();
996 creation.revert(&mut m).unwrap();
997 assert!(m.free_vertex_slots().is_empty(), "reserved for redo");
998 m.release_patch_slots(&creation, PatchState::Applied)
999 .unwrap();
1000 assert!(m.free_vertex_slots().is_empty());
1001 m.release_patch_slots(&creation, PatchState::Undone)
1002 .unwrap();
1003 assert_eq!(m.free_vertex_slots(), &[3]);
1004 }
1005
1006 #[test]
1007 fn release_patch_slots_rejects_a_foreign_mesh() {
1008 let mut m = quad();
1009 let mut other = quad();
1010 let mut tx = other.begin_edit();
1011 tx.remove_triangles(&[1]).unwrap();
1012 tx.remove_vertices(&[3]).unwrap();
1013 let patch = tx.commit().unwrap();
1014 assert_eq!(
1015 m.release_patch_slots(&patch, PatchState::Applied).err(),
1016 Some(StoreError::PatchMismatch)
1017 );
1018 assert!(m.free_vertex_slots().is_empty());
1019 assert!(m.free_triangle_slots().is_empty());
1020 }
1021
1022 #[test]
1023 fn release_patch_slots_rejects_an_earlier_epoch() {
1024 let mut m = quad();
1025 let mut tx = m.begin_edit();
1026 tx.remove_triangles(&[1]).unwrap();
1027 tx.remove_vertices(&[3]).unwrap();
1028 let patch = tx.commit().unwrap();
1029 m.compact().unwrap();
1030 assert_eq!(
1031 m.release_patch_slots(&patch, PatchState::Applied).err(),
1032 Some(StoreError::StalePatch)
1033 );
1034 assert!(m.free_vertex_slots().is_empty());
1035 assert!(m.free_triangle_slots().is_empty());
1036 }
1037
1038 #[test]
1039 fn release_patch_slots_skips_alive_slots() {
1040 let mut m = quad();
1041 let mut tx = m.begin_edit();
1042 tx.remove_triangles(&[1]).unwrap();
1043 tx.remove_vertices(&[3]).unwrap();
1044 let patch = tx.commit().unwrap();
1045 m.release_patch_slots(&patch, PatchState::Applied).unwrap();
1046 assert_eq!(m.free_vertex_slots(), &[3]);
1047 let mut tx = m.begin_edit();
1050 assert_eq!(tx.add_vertices(&[[0.; 3]]).unwrap(), vec![3]);
1051 assert_eq!(tx.add_triangles(&[[0, 1, 2]]).unwrap(), vec![1]);
1052 tx.commit().unwrap();
1053 m.release_patch_slots(&patch, PatchState::Applied).unwrap();
1054 assert!(m.free_vertex_slots().is_empty());
1055 assert!(m.free_triangle_slots().is_empty());
1056 let mut tx = m.begin_edit();
1057 assert_eq!(tx.add_vertices(&[[0.; 3]]).unwrap(), vec![4]);
1058 assert_eq!(tx.add_triangles(&[[0, 1, 2]]).unwrap(), vec![2]);
1059 tx.rollback();
1060 }
1061
1062 #[test]
1063 fn release_patch_slots_with_history_holding_the_patch() {
1064 use crate::History;
1065 let mut m = quad();
1066 let mut tx = m.begin_edit();
1067 tx.remove_triangles(&[1]).unwrap();
1068 tx.remove_vertices(&[3]).unwrap();
1069 let patch = tx.commit().unwrap();
1070 let mut h = History::new(1 << 20);
1071 h.push(&mut m, patch.clone()).unwrap();
1072 m.release_patch_slots(&patch, PatchState::Applied).unwrap();
1076 let mut tx = m.begin_edit();
1077 assert_eq!(tx.add_vertices(&[[0.; 3]]).unwrap(), vec![3]);
1078 assert_eq!(tx.add_triangles(&[[0, 1, 2]]).unwrap(), vec![1]);
1079 tx.commit().unwrap();
1080 h.clear(&mut m).unwrap();
1081 assert!(m.free_vertex_slots().is_empty());
1082 assert!(m.free_triangle_slots().is_empty());
1083 let mut tx = m.begin_edit();
1084 assert_eq!(tx.add_vertices(&[[0.; 3]]).unwrap(), vec![4]);
1085 assert_eq!(tx.add_triangles(&[[0, 1, 2]]).unwrap(), vec![2]);
1086 tx.rollback();
1087 }
1088 fn grid_buffers(n: u32) -> (Vec<f64>, Vec<u32>) {
1091 let mut positions = Vec::new();
1092 for j in 0..n {
1093 for i in 0..n {
1094 let (x, y) = (f64::from(i), f64::from(j));
1095 positions.extend([x, y, 0.3 * (1.7 * x).sin() * (1.3 * y).cos()]);
1096 }
1097 }
1098 let mut indices = Vec::new();
1099 for j in 0..n - 1 {
1100 for i in 0..n - 1 {
1101 let a = j * n + i;
1102 indices.extend([a, a + 1, a + n + 1, a, a + n + 1, a + n]);
1103 }
1104 }
1105 (positions, indices)
1106 }
1107
1108 fn assert_same_mesh(got: &Mesh, want: &Mesh) {
1109 assert_eq!(
1110 got.origin().map(f64::to_bits),
1111 want.origin().map(f64::to_bits)
1112 );
1113 assert_eq!(got.positions(), want.positions());
1114 assert_eq!(got.triangles(), want.triangles());
1115 assert_eq!(got.vertex_count(), want.vertex_count());
1116 assert_eq!(got.triangle_count(), want.triangle_count());
1117 assert_eq!(got.vertex_slots(), want.vertex_slots());
1118 assert_eq!(got.triangle_slots(), want.triangle_slots());
1119 for v in 0..got.vertex_slots() as VertexId {
1120 assert_eq!(got.is_vertex_alive(v), want.is_vertex_alive(v));
1121 assert_eq!(got.corner_count(v), want.corner_count(v));
1122 }
1123 for t in 0..got.triangle_slots() as TriangleId {
1124 assert_eq!(got.is_triangle_alive(t), want.is_triangle_alive(t));
1125 }
1126 }
1127
1128 fn build_stepped(
1129 positions: &[f64],
1130 indices: &[u32],
1131 origin: Option<[f64; 3]>,
1132 budget: usize,
1133 chunk: usize,
1134 ) -> Result<Mesh> {
1135 let mut build = MeshBuilder::new(positions.len() / 3, indices.len() / 3, origin)?;
1136 for c in positions.chunks(chunk) {
1137 build.push_positions(c)?;
1138 }
1139 for c in indices.chunks(chunk) {
1140 build.push_indices(c)?;
1141 }
1142 assert!(!build.is_done());
1143 while !build.step(budget)? {}
1144 assert!(build.is_done());
1145 Ok(build.finish().expect("done builds finish"))
1146 }
1147
1148 #[test]
1149 fn mesh_builder_matches_from_buffers_f64() {
1150 let far = 1.0e7;
1151 let (grid_p, grid_t) = grid_buffers(30);
1152 let cases: Vec<TwinCase> = vec![
1153 (
1154 vec![0., 0., 0., 1., 0., 0., 1., 1., 0., 0., 1., 0.],
1155 vec![0, 1, 2, 0, 2, 3],
1156 None,
1157 ),
1158 (
1159 vec![0., 0., 0., 1., 0., 0., 1., 1., 0., 0., 1., 0.],
1160 vec![0, 1, 2, 0, 2, 3],
1161 Some([0.5, 0.5, 0.0]),
1162 ),
1163 (vec![], vec![], None),
1164 (vec![], vec![], Some([1., 2., 3.])),
1165 (vec![1., 2., 3.], vec![], None),
1166 (vec![far, far, 0.0, far + 0.001, far, 0.0], vec![], None),
1167 (grid_p.clone(), grid_t.clone(), None),
1168 (grid_p, grid_t, Some([15.0, 15.0, 0.0])),
1169 ];
1170 for (positions, indices, origin) in &cases {
1171 let reference = match origin {
1172 None => Mesh::from_buffers_f64(positions, indices).unwrap(),
1173 Some(o) => Mesh::from_buffers_f64_with_origin(positions, indices, *o).unwrap(),
1174 };
1175 for budget in [1, 2, 7, 100, usize::MAX] {
1176 for chunk in [1, 5, 100] {
1178 let got = build_stepped(positions, indices, *origin, budget, chunk).unwrap();
1179 assert_same_mesh(&got, &reference);
1180 }
1181 }
1182 }
1183 }
1184
1185 #[test]
1186 fn mesh_builder_fails_like_from_buffers_f64() {
1187 let bad = StoreError::InvalidArgument("positions must be finite");
1188 let (mut grid_p, mut grid_t) = grid_buffers(10);
1189 grid_p[3 * 50 + 1] = f64::NAN;
1191 grid_t[3 * 60 + 2] = 10_000;
1192 let cases: Vec<FailCase> = vec![
1193 (grid_p, grid_t.clone(), None, bad.clone()),
1194 {
1195 let (p, _) = grid_buffers(10);
1196 (p, grid_t, None, StoreError::InvalidVertex(10_000))
1197 },
1198 (
1199 vec![f64::MAX, 0., 0., f64::MAX, 0., 0.],
1200 vec![],
1201 None,
1202 bad.clone(),
1203 ),
1204 (vec![1.0e39, 0., 0.], vec![], Some([0.; 3]), bad.clone()),
1205 (
1206 vec![0., 0., 0.],
1207 vec![],
1208 Some([f64::NAN, 0., 0.]),
1209 bad.clone(),
1210 ),
1211 (vec![], vec![7, 8, 9], None, StoreError::InvalidVertex(7)),
1212 ];
1213 for (positions, indices, origin, expected) in &cases {
1214 let reference = match origin {
1215 None => Mesh::from_buffers_f64(positions, indices).err(),
1216 Some(o) => Mesh::from_buffers_f64_with_origin(positions, indices, *o).err(),
1217 };
1218 assert_eq!(reference, Some(expected.clone()));
1219 for budget in [1, 7, usize::MAX] {
1220 let err = build_stepped(positions, indices, *origin, budget, 5).err();
1221 assert_eq!(err, Some(expected.clone()));
1222 }
1223 }
1224 }
1225
1226 #[test]
1227 fn mesh_builder_upload_protocol() {
1228 let mut build = MeshBuilder::new(1, 1, None).unwrap();
1230 build.push_positions(&[0., 0., 0.]).unwrap();
1231 assert!(matches!(
1232 build.push_positions(&[1.]),
1233 Err(StoreError::InvalidArgument(_))
1234 ));
1235 assert!(matches!(
1236 build.push_indices(&[0, 0, 0, 0]),
1237 Err(StoreError::InvalidArgument(_))
1238 ));
1239 assert!(matches!(
1241 build.step(100),
1242 Err(StoreError::InvalidArgument(_))
1243 ));
1244 assert!(!build.is_done());
1245 assert!(MeshBuilder::new(1, 1, None).unwrap().finish().is_none());
1246 build.push_positions(&[]).unwrap();
1248 build.push_indices(&[]).unwrap();
1249 build.push_indices(&[0, 0, 0]).unwrap();
1250 while !build.step(1).unwrap() {}
1251 assert!(build.step(1).unwrap());
1252 let mesh = build.finish().expect("done builds finish");
1253 assert_eq!((mesh.vertex_count(), mesh.triangle_count()), (1, 1));
1254 assert_eq!(
1256 MeshBuilder::new(usize::MAX / 3 + 1, 0, None).err(),
1257 Some(StoreError::OutOfMemory)
1258 );
1259 assert_eq!(
1260 MeshBuilder::new(0, usize::MAX / 3 + 1, None).err(),
1261 Some(StoreError::OutOfMemory)
1262 );
1263 }
1264}