1use crate::channel::ChannelKind;
2use crate::column::{Prepared, try_reserve_exact};
3use crate::mesh::set_bit;
4use crate::patch::{ChangeSet, Patch};
5use crate::{Mesh, Result, StoreError, TriangleId, VertexId};
6
7pub struct Transaction<'m> {
15 mesh: &'m mut Mesh,
16 allocated_vertices: Vec<VertexId>,
17 removed_vertices: Vec<VertexId>,
18 allocated_triangles: Vec<TriangleId>,
19 removed_triangles: Vec<TriangleId>,
20 popped_vertices: Vec<VertexId>,
22 popped_triangles: Vec<TriangleId>,
23 live_at_start: (usize, usize),
24 dropped_channels: Vec<u64>,
26 outdates_bound_channels: bool,
28 keeping_bound_channels: bool,
30 poisoned: bool,
31 finished: bool,
32}
33
34impl Mesh {
35 pub fn begin_edit(&mut self) -> Transaction<'_> {
37 self.positions.begin();
38 self.triangles.begin();
39 self.vertex_live.begin();
40 self.triangle_live.begin();
41 self.vertex_refs.begin();
42 for c in &mut self.channels {
43 c.data.begin();
44 }
45 Transaction {
46 live_at_start: (self.live_vertices, self.live_triangles),
47 mesh: self,
48 allocated_vertices: Vec::new(),
49 removed_vertices: Vec::new(),
50 allocated_triangles: Vec::new(),
51 removed_triangles: Vec::new(),
52 popped_vertices: Vec::new(),
53 popped_triangles: Vec::new(),
54 dropped_channels: Vec::new(),
55 outdates_bound_channels: false,
56 keeping_bound_channels: false,
57 poisoned: false,
58 finished: false,
59 }
60 }
61}
62
63fn sorted(mut ids: Vec<u32>) -> Vec<u32> {
64 ids.sort_unstable();
65 ids.dedup();
66 ids
67}
68
69fn without(ids: Vec<u32>, a: &[u32], b: &[u32]) -> Vec<u32> {
70 ids.into_iter()
71 .filter(|i| a.binary_search(i).is_err() && b.binary_search(i).is_err())
72 .collect()
73}
74
75fn check_unique(ids: &[u32], what: &'static str) -> Result<()> {
76 let mut s = ids.to_vec();
77 s.sort_unstable();
78 if s.windows(2).any(|w| w[0] == w[1]) {
79 return Err(StoreError::InvalidArgument(what));
80 }
81 Ok(())
82}
83
84struct PreparedColumns {
86 positions: Prepared<[f32; 3]>,
87 triangles: Prepared<[u32; 3]>,
88 vertex_live: Prepared<u64>,
89 triangle_live: Prepared<u64>,
90 vertex_refs: Prepared<u32>,
91 channels: Vec<Option<Prepared<f32>>>,
93}
94
95impl PreparedColumns {
96 fn new(m: &Mesh) -> Result<Self> {
97 let mut channels = Vec::new();
98 try_reserve_exact(&mut channels, m.channels.len())?;
99 for c in &m.channels {
100 channels.push(match c.desc.kind {
101 ChannelKind::Source => Some(c.data.prepare_finish()?),
102 ChannelKind::Derived => None,
103 });
104 }
105 Ok(Self {
106 positions: m.positions.prepare_finish()?,
107 triangles: m.triangles.prepare_finish()?,
108 vertex_live: m.vertex_live.prepare_finish()?,
109 triangle_live: m.triangle_live.prepare_finish()?,
110 vertex_refs: m.vertex_refs.prepare_finish()?,
111 channels,
112 })
113 }
114}
115
116impl Transaction<'_> {
117 pub fn mesh(&self) -> &Mesh {
119 self.mesh
120 }
121
122 fn check_vertex(&self, v: VertexId) -> Result<()> {
123 if self.mesh.is_vertex_alive(v) {
124 Ok(())
125 } else {
126 Err(StoreError::InvalidVertex(v))
127 }
128 }
129
130 fn check_triangle(&self, t: TriangleId) -> Result<()> {
131 if self.mesh.is_triangle_alive(t) {
132 Ok(())
133 } else {
134 Err(StoreError::InvalidTriangle(t))
135 }
136 }
137
138 fn write<R>(&mut self, f: impl FnOnce(&mut Self) -> Result<R>) -> Result<R> {
140 if self.poisoned {
141 return Err(StoreError::TransactionPoisoned);
142 }
143 let r = f(self);
144 if r.is_err() {
145 self.poisoned = true;
146 }
147 r
148 }
149
150 fn alloc_vertex(
151 &mut self,
152 position: [f32; 3],
153 parents: &[(VertexId, f32)],
154 ) -> Result<VertexId> {
155 let m = &mut *self.mesh;
156 let id = match m.vertex_free.pop() {
157 Some(id) => {
158 self.popped_vertices.push(id);
159 debug_assert!(!m.is_vertex_alive(id), "free list held alive vertex {id}");
160 m.positions.set(id as usize, position);
161 id
162 }
163 None => {
164 let id = m.positions.push(position)?;
165 m.vertex_refs.push(0)?;
166 for c in &mut m.channels {
167 for _ in 0..c.desc.width {
168 c.data.push(c.desc.default)?;
169 }
170 }
171 u32::try_from(id).map_err(|_| StoreError::OutOfMemory)?
172 }
173 };
174 for c in &mut m.channels {
175 let w = c.desc.width;
176 for k in 0..w {
177 let value = c.initial_value(parents, k);
178 c.data.set(id as usize * w + k, value);
179 }
180 }
181 set_bit(&mut m.vertex_live, id as usize, true)?;
182 m.live_vertices += 1;
183 self.allocated_vertices.push(id);
184 Ok(id)
185 }
186
187 pub fn keeping_bound_channels<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
192 let outer = std::mem::replace(&mut self.keeping_bound_channels, true);
193 let r = f(self);
194 self.keeping_bound_channels = outer;
195 r
196 }
197
198 fn alloc_triangle(&mut self, tri: [VertexId; 3]) -> Result<TriangleId> {
199 self.outdates_bound_channels |= !self.keeping_bound_channels;
200 let m = &mut *self.mesh;
201 m.on_topology_write();
202 let id = match m.triangle_free.pop() {
203 Some(id) => {
204 self.popped_triangles.push(id);
205 debug_assert!(
206 !m.is_triangle_alive(id),
207 "free list held alive triangle {id}"
208 );
209 m.triangles.set(id as usize, tri);
210 id
211 }
212 None => {
213 let id = m.triangles.push(tri)?;
214 u32::try_from(id).map_err(|_| StoreError::OutOfMemory)?
215 }
216 };
217 for v in tri {
218 let r = m.vertex_refs.get(v as usize);
219 m.vertex_refs.set(v as usize, r + 1);
220 }
221 set_bit(&mut m.triangle_live, id as usize, true)?;
222 m.live_triangles += 1;
223 self.allocated_triangles.push(id);
224 Ok(id)
225 }
226
227 fn release_corners(&mut self, tri: [VertexId; 3]) {
228 let m = &mut *self.mesh;
229 for v in tri {
230 let r = m.vertex_refs.get(v as usize);
231 m.vertex_refs.set(v as usize, r - 1);
232 }
233 }
234
235 pub fn add_vertices(&mut self, positions: &[[f32; 3]]) -> Result<Vec<VertexId>> {
237 self.write(|tx| positions.iter().map(|&p| tx.alloc_vertex(p, &[])).collect())
238 }
239
240 pub fn add_vertex_with_parents(
244 &mut self,
245 position: [f32; 3],
246 parents: &[(VertexId, f32)],
247 ) -> Result<VertexId> {
248 for &(v, w) in parents {
249 self.check_vertex(v)?;
250 if !w.is_finite() || w < 0.0 {
251 return Err(StoreError::InvalidArgument(
252 "parent weights must be finite and non-negative",
253 ));
254 }
255 }
256 self.write(|tx| tx.alloc_vertex(position, parents))
257 }
258
259 pub fn set_positions(&mut self, ids: &[VertexId], positions: &[[f32; 3]]) -> Result<()> {
261 if ids.len() != positions.len() {
262 return Err(StoreError::LengthMismatch {
263 expected: ids.len(),
264 got: positions.len(),
265 });
266 }
267 for &v in ids {
268 self.check_vertex(v)?;
269 }
270 self.write(|tx| {
271 for (&v, &p) in ids.iter().zip(positions) {
272 tx.mesh.positions.set(v as usize, p);
273 }
274 Ok(())
275 })
276 }
277
278 pub fn remove_vertices(&mut self, ids: &[VertexId]) -> Result<()> {
280 check_unique(ids, "duplicate vertex id")?;
281 for &v in ids {
282 self.check_vertex(v)?;
283 let corners = self.mesh.corner_count(v);
284 if corners > 0 {
285 return Err(StoreError::VertexInUse { vertex: v, corners });
286 }
287 }
288 self.write(|tx| {
289 for &v in ids {
290 set_bit(&mut tx.mesh.vertex_live, v as usize, false)?;
291 tx.mesh.live_vertices -= 1;
292 }
293 tx.removed_vertices.extend_from_slice(ids);
294 Ok(())
295 })
296 }
297
298 pub fn add_triangles(&mut self, triangles: &[[VertexId; 3]]) -> Result<Vec<TriangleId>> {
300 for t in triangles {
301 for &v in t {
302 self.check_vertex(v)?;
303 }
304 }
305 self.write(|tx| triangles.iter().map(|&t| tx.alloc_triangle(t)).collect())
306 }
307
308 pub fn remove_triangles(&mut self, ids: &[TriangleId]) -> Result<()> {
310 check_unique(ids, "duplicate triangle id")?;
311 for &t in ids {
312 self.check_triangle(t)?;
313 }
314 self.write(|tx| {
315 if !ids.is_empty() {
316 tx.outdates_bound_channels |= !tx.keeping_bound_channels;
317 tx.mesh.on_topology_write();
318 }
319 for &t in ids {
320 let tri = tx.mesh.triangles.get(t as usize);
321 tx.release_corners(tri);
322 tx.mesh.triangles.set(t as usize, [0; 3]);
323 set_bit(&mut tx.mesh.triangle_live, t as usize, false)?;
324 tx.mesh.live_triangles -= 1;
325 }
326 tx.removed_triangles.extend_from_slice(ids);
327 Ok(())
328 })
329 }
330
331 pub fn set_triangles(&mut self, ids: &[TriangleId], triangles: &[[VertexId; 3]]) -> Result<()> {
333 if ids.len() != triangles.len() {
334 return Err(StoreError::LengthMismatch {
335 expected: ids.len(),
336 got: triangles.len(),
337 });
338 }
339 check_unique(ids, "duplicate triangle id")?;
340 for (&t, tri) in ids.iter().zip(triangles) {
341 self.check_triangle(t)?;
342 for &v in tri {
343 self.check_vertex(v)?;
344 }
345 }
346 self.write(|tx| {
347 if !ids.is_empty() {
348 tx.outdates_bound_channels |= !tx.keeping_bound_channels;
349 tx.mesh.on_topology_write();
350 }
351 for (&t, &tri) in ids.iter().zip(triangles) {
352 let old = tx.mesh.triangles.get(t as usize);
353 tx.release_corners(old);
354 for v in tri {
355 let r = tx.mesh.vertex_refs.get(v as usize);
356 tx.mesh.vertex_refs.set(v as usize, r + 1);
357 }
358 tx.mesh.triangles.set(t as usize, tri);
359 }
360 Ok(())
361 })
362 }
363
364 pub fn write_channel(&mut self, name: &str, ids: &[VertexId], values: &[f32]) -> Result<()> {
366 let ci = self
367 .mesh
368 .channel_index(name)
369 .ok_or_else(|| StoreError::UnknownChannel(name.into()))?;
370 let desc = &self.mesh.channels[ci].desc;
371 if desc.kind != ChannelKind::Source {
372 return Err(StoreError::NotSourceChannel(name.into()));
373 }
374 let w = desc.width;
375 if values.len() != ids.len() * w {
376 return Err(StoreError::LengthMismatch {
377 expected: ids.len() * w,
378 got: values.len(),
379 });
380 }
381 for &v in ids {
382 self.check_vertex(v)?;
383 }
384 self.write(|tx| {
385 let data = &mut tx.mesh.channels[ci].data;
386 for (n, &v) in ids.iter().enumerate() {
387 for k in 0..w {
388 data.set(v as usize * w + k, values[n * w + k]);
389 }
390 }
391 Ok(())
392 })
393 }
394
395 pub fn drop_channel(&mut self, name: &str) -> Result<()> {
407 let ci = self
408 .mesh
409 .channel_index(name)
410 .ok_or_else(|| StoreError::UnknownChannel(name.into()))?;
411 if self.mesh.channels[ci].desc.kind != ChannelKind::Source {
412 return Err(StoreError::NotSourceChannel(name.into()));
413 }
414 self.write(|tx| {
415 try_reserve_exact(&mut tx.dropped_channels, 1)?;
416 let channel = &mut tx.mesh.channels[ci];
417 channel.hidden = true;
418 tx.dropped_channels.push(channel.id);
419 Ok(())
420 })
421 }
422
423 pub fn commit(mut self) -> Result<Patch> {
427 self.finished = true;
428 if self.poisoned {
429 self.rollback_inner();
430 return Err(StoreError::TransactionPoisoned);
431 }
432 if self.outdates_bound_channels
435 && try_reserve_exact(&mut self.dropped_channels, self.mesh.channels.len()).is_err()
436 {
437 self.rollback_inner();
438 return Err(StoreError::OutOfMemory);
439 }
440 let prepared = match PreparedColumns::new(self.mesh) {
441 Ok(p) => p,
442 Err(e) => {
443 self.rollback_inner();
444 return Err(e);
445 }
446 };
447 let m = &mut *self.mesh;
449 if self.outdates_bound_channels {
450 for c in &mut m.channels {
451 if c.desc.topology_bound && !c.hidden {
452 c.hidden = true;
453 self.dropped_channels.push(c.id);
454 }
455 }
456 }
457
458 let allocated_v = sorted(std::mem::take(&mut self.allocated_vertices));
461 let (created_v, transient_v): (Vec<_>, Vec<_>) = allocated_v
462 .iter()
463 .copied()
464 .partition(|&v| m.is_vertex_alive(v));
465 let allocated_t = sorted(std::mem::take(&mut self.allocated_triangles));
466 let (created_t, transient_t): (Vec<_>, Vec<_>) = allocated_t
467 .iter()
468 .copied()
469 .partition(|&t| m.is_triangle_alive(t));
470 m.vertex_free.extend_from_slice(&transient_v);
471 m.triangle_free.extend_from_slice(&transient_t);
472
473 let freed_v = without(
474 sorted(std::mem::take(&mut self.removed_vertices)),
475 &allocated_v,
476 &[],
477 );
478 let freed_t = without(
479 sorted(std::mem::take(&mut self.removed_triangles)),
480 &allocated_t,
481 &[],
482 );
483 let moved = without(
488 sorted(m.positions.journal_ids().to_vec()),
489 &allocated_v,
490 &freed_v,
491 );
492 let changed_t = without(
493 sorted(m.triangles.journal_ids().to_vec()),
494 &allocated_t,
495 &freed_t,
496 );
497
498 let mut channels = Vec::new();
499 let mut channel_data = Vec::new();
500 for (c, prepared) in m.channels.iter_mut().zip(prepared.channels) {
501 let Some(prepared) = prepared else {
502 c.data.end_journal(); continue;
504 };
505 let w = c.desc.width as u32;
506 let written = c.data.journal_ids().iter().map(|&i| i / w).collect();
507 let written = without(sorted(written), &allocated_v, &freed_v);
508 if !written.is_empty() && !c.hidden {
509 channels.push((c.desc.name.clone(), written));
510 }
511 let patch = c.data.seal(prepared);
512 if !patch.is_empty() {
513 channel_data.push((c.id, patch));
514 }
515 }
516
517 let dropped_channels = std::mem::take(&mut self.dropped_channels);
518 let dropped = |c: &&crate::channel::Channel| dropped_channels.contains(&c.id);
519 let mut channels_toggled: Vec<String> = m
520 .channels
521 .iter()
522 .filter(dropped)
523 .map(|c| c.desc.name.clone())
524 .collect();
525 channels_toggled.sort_unstable();
526 channels_toggled.dedup();
527 let dropped_bytes = m
528 .channels
529 .iter()
530 .filter(dropped)
531 .map(|c| c.data.len() * size_of::<f32>())
532 .sum();
533 let changes = ChangeSet {
534 vertices_added: created_v,
535 vertices_removed: freed_v,
536 vertices_moved: moved,
537 triangles_added: created_t,
538 triangles_removed: freed_t,
539 triangles_changed: changed_t,
540 channels,
541 channels_toggled,
542 };
543 if m.incidence_pending {
544 m.incidence_pending = false;
546 let c = &changes;
547 let ids = [&c.triangles_added[..], &c.triangles_changed[..]];
548 m.with_incidence(|inc, m| {
549 for (t, tri) in m.triangles.journal_entries() {
550 inc.remove(t, tri)?;
551 }
552 inc.insert_alive(m, &ids)
553 });
554 }
555 let base_revision = m.revision;
556 if !changes.is_empty() {
557 m.next_revision += 1;
558 m.revision = m.next_revision;
559 }
560 let patch = Patch {
561 mesh_uid: m.uid,
562 epoch: m.epoch,
563 base_revision,
564 revision: m.revision,
565 positions: m.positions.seal(prepared.positions),
566 triangles: m.triangles.seal(prepared.triangles),
567 vertex_live: m.vertex_live.seal(prepared.vertex_live),
568 triangle_live: m.triangle_live.seal(prepared.triangle_live),
569 vertex_refs: m.vertex_refs.seal(prepared.vertex_refs),
570 channel_data,
571 dropped_channels,
572 dropped_bytes,
573 channels_before: m.next_channel_id,
574 changes,
575 };
576 Ok(patch)
577 }
578
579 pub fn rollback(mut self) {
581 self.finished = true;
582 self.rollback_inner();
583 }
584
585 fn rollback_inner(&mut self) {
586 let m = &mut *self.mesh;
587 m.positions.rollback();
588 m.triangles.rollback();
589 m.vertex_live.rollback();
590 m.triangle_live.rollback();
591 m.vertex_refs.rollback();
592 for c in &mut m.channels {
593 c.data.rollback();
594 if self.dropped_channels.contains(&c.id) {
595 c.hidden = false;
596 }
597 }
598 while let Some(v) = self.popped_vertices.pop() {
599 m.vertex_free.push(v);
600 }
601 while let Some(t) = self.popped_triangles.pop() {
602 m.triangle_free.push(t);
603 }
604 (m.live_vertices, m.live_triangles) = self.live_at_start;
605 m.incidence_pending = false;
607 }
608}
609
610impl Drop for Transaction<'_> {
611 fn drop(&mut self) {
612 if !self.finished {
613 self.rollback_inner();
614 }
615 }
616}
617
618#[cfg(test)]
619mod tests {
620 use crate::column::fail_allocations_after;
621 use crate::{ChannelDesc, Mesh, StoreError, TopologyPolicy};
622
623 fn quad() -> Mesh {
624 Mesh::from_buffers(
625 &[0., 0., 0., 1., 0., 0., 1., 1., 0., 0., 1., 0.],
626 &[0, 1, 2, 0, 2, 3],
627 )
628 .unwrap()
629 }
630
631 fn quad_uv() -> Mesh {
633 let mut m = quad();
634 m.add_uvs(&[0., 0., 10., 1., 20., 2., 30., 3.]).unwrap();
635 m
636 }
637
638 #[test]
639 fn add_uvs_checks_its_input() {
640 let mut m = quad();
641 assert_eq!(
642 m.add_uvs(&[0.; 7]),
643 Err(StoreError::LengthMismatch {
644 expected: 8,
645 got: 7
646 })
647 );
648 let mut bad = [0.; 8];
649 bad[3] = f32::NAN;
650 assert!(matches!(
651 m.add_uvs(&bad),
652 Err(StoreError::InvalidArgument(_))
653 ));
654 assert!(m.uvs().is_none(), "a failed add leaves no channel");
655 m.add_uvs(&[0.; 8]).unwrap();
656 assert_eq!(
657 m.add_uvs(&[0.; 8]),
658 Err(StoreError::ChannelExists("uv".into()))
659 );
660 assert!(matches!(
661 quad().add_channel(ChannelDesc::source("uv", 3)),
662 Err(StoreError::InvalidArgument(_))
663 ));
664 }
665
666 #[test]
667 fn dropping_a_channel_undoes_and_redoes_with_its_values() {
668 let mut m = quad_uv();
669 let before = m.uvs().unwrap().to_vec();
670 let mut tx = m.begin_edit();
671 tx.drop_channel("uv").unwrap();
672 let v = tx.add_vertices(&[[5.; 3]]).unwrap()[0];
674 tx.add_triangles(&[[0, 1, v]]).unwrap();
675 assert!(tx.mesh().uvs().is_none());
676 let patch = tx.commit().unwrap();
677 assert_eq!(patch.changes().channels_toggled, vec!["uv".to_string()]);
678 assert!(m.uvs().is_none());
679 assert!(patch.byte_size() >= before.len() * 4);
680
681 let undo = patch.revert(&mut m).unwrap();
682 assert_eq!(undo.channels_toggled, vec!["uv".to_string()]);
683 let back = m.uvs().unwrap();
684 assert_eq!(&back[..8], &before[..]);
685 assert_eq!(back.len(), 2 * m.vertex_slots());
686
687 patch.apply(&mut m).unwrap();
688 assert!(m.uvs().is_none());
689 }
690
691 #[test]
692 fn a_drop_alone_is_a_change_and_rolls_back() {
693 let mut m = quad_uv();
694 let revision = m.revision();
695 let mut tx = m.begin_edit();
696 tx.drop_channel("uv").unwrap();
697 tx.rollback();
698 assert!(m.uvs().is_some());
699 assert_eq!(m.revision(), revision);
700 {
701 let mut tx = m.begin_edit();
702 tx.drop_channel("uv").unwrap();
703 }
704 assert!(m.uvs().is_some(), "dropping the transaction rolls back");
705 let mut tx = m.begin_edit();
706 tx.drop_channel("uv").unwrap();
707 assert!(tx.drop_channel("uv").is_err(), "already gone");
708 let patch = tx.commit().unwrap();
709 assert!(!patch.is_empty());
710 assert_ne!(m.revision(), revision);
711 }
712
713 #[test]
714 fn a_derived_channel_cannot_be_dropped() {
715 let mut m = quad();
716 m.add_channel(ChannelDesc::derived("d", 1)).unwrap();
717 let mut tx = m.begin_edit();
718 assert_eq!(
719 tx.drop_channel("d"),
720 Err(StoreError::NotSourceChannel("d".into()))
721 );
722 assert_eq!(
723 tx.drop_channel("nope"),
724 Err(StoreError::UnknownChannel("nope".into()))
725 );
726 }
727
728 #[test]
729 fn undoing_a_drop_replaces_a_newer_channel_of_that_name() {
730 let mut m = quad_uv();
731 let before = m.uvs().unwrap().to_vec();
732 let mut tx = m.begin_edit();
733 tx.drop_channel("uv").unwrap();
734 let patch = tx.commit().unwrap();
735 m.add_uvs(&[7.; 8]).unwrap();
736 patch.revert(&mut m).unwrap();
737 assert_eq!(m.uvs().unwrap(), &before[..]);
738 }
739
740 #[test]
741 fn releasing_a_drop_frees_the_values_and_compact_forgets_them() {
742 use crate::PatchSide;
743 let mut m = quad_uv();
744 let mut tx = m.begin_edit();
745 tx.drop_channel("uv").unwrap();
746 let patch = tx.commit().unwrap();
747 assert_eq!(m.channels.len(), 1, "hidden, kept for undo");
748 patch.release(&mut m, PatchSide::Undoable);
749 assert!(m.channels.is_empty());
750
751 let mut m = quad_uv();
752 let mut tx = m.begin_edit();
753 tx.drop_channel("uv").unwrap();
754 tx.commit().unwrap();
755 m.compact().unwrap();
756 assert!(m.channels.is_empty());
757 }
758
759 #[test]
760 fn uvs_interpolate_and_undo_like_any_source_channel() {
761 let mut m = quad_uv();
762 let mut tx = m.begin_edit();
763 let v = tx
764 .add_vertex_with_parents([0.5, 0.0, 0.0], &[(0, 0.5), (1, 0.5)])
765 .unwrap();
766 tx.write_channel("uv", &[2], &[-1., -2.]).unwrap();
767 let patch = tx.commit().unwrap();
768 assert_eq!(&m.uvs().unwrap()[2 * v as usize..][..2], &[5., 0.5]);
769 assert_eq!(&m.uvs().unwrap()[4..6], &[-1., -2.]);
770 patch.revert(&mut m).unwrap();
771 assert_eq!(&m.uvs().unwrap()[4..6], &[20., 2.]);
772 }
773
774 #[test]
775 fn moving_vertices_commits_a_reversible_patch() {
776 let mut m = quad();
777 let mut tx = m.begin_edit();
778 tx.set_positions(&[2], &[[5., 5., 5.]]).unwrap();
779 let patch = tx.commit().unwrap();
780 assert_eq!(m.position(2), Some([5., 5., 5.]));
781 assert_eq!(patch.changes().vertices_moved, vec![2]);
782 assert!(!patch.changes().topology_changed());
783
784 let undo = patch.revert(&mut m).unwrap();
785 assert_eq!(m.position(2), Some([1., 1., 0.]));
786 assert_eq!(undo.vertices_moved, vec![2]);
787 patch.apply(&mut m).unwrap();
788 assert_eq!(m.position(2), Some([5., 5., 5.]));
789 }
790
791 #[test]
792 fn adding_and_removing_topology_round_trips() {
793 let mut m = quad();
794 let mut tx = m.begin_edit();
795 let v = tx.add_vertices(&[[2., 0., 0.]]).unwrap()[0];
796 let t = tx.add_triangles(&[[1, v, 2]]).unwrap()[0];
797 tx.remove_triangles(&[0]).unwrap();
798 let patch = tx.commit().unwrap();
799 assert_eq!((v, t), (4, 2));
800 assert_eq!((m.vertex_count(), m.triangle_count()), (5, 2));
801 assert_eq!(
802 m.triangles()[0],
803 [0, 0, 0],
804 "removed triangles read as degenerate"
805 );
806 assert_eq!(m.corner_count(1), 1);
807
808 let undo = patch.revert(&mut m).unwrap();
809 assert_eq!(undo.triangles_added, vec![0]);
810 assert_eq!(undo.triangles_removed, vec![2]);
811 assert_eq!(undo.vertices_removed, vec![4]);
812 assert_eq!((m.vertex_count(), m.triangle_count()), (4, 2));
813 assert_eq!(m.triangle(0), Some([0, 1, 2]));
814 assert!(!m.is_vertex_alive(4));
815 assert_eq!(m.corner_count(1), 1);
816 }
817
818 #[test]
819 fn validation_errors_leave_the_transaction_usable() {
820 let mut m = quad();
821 let mut tx = m.begin_edit();
822 assert_eq!(
823 tx.set_positions(&[9], &[[0.; 3]]),
824 Err(StoreError::InvalidVertex(9))
825 );
826 assert_eq!(
827 tx.remove_vertices(&[0]),
828 Err(StoreError::VertexInUse {
829 vertex: 0,
830 corners: 2
831 })
832 );
833 assert!(matches!(
834 tx.remove_triangles(&[0, 0]),
835 Err(StoreError::InvalidArgument(_))
836 ));
837 tx.set_positions(&[0], &[[1., 2., 3.]]).unwrap();
838 tx.commit().unwrap();
839 assert_eq!(m.position(0), Some([1., 2., 3.]));
840 }
841
842 #[test]
843 fn rollback_and_drop_discard_everything() {
844 let mut m = quad();
845 let before = m.revision();
846 let mut tx = m.begin_edit();
847 tx.add_vertices(&[[9.; 3]]).unwrap();
848 tx.remove_triangles(&[1]).unwrap();
849 tx.rollback();
850 assert_eq!(
851 (m.vertex_slots(), m.vertex_count(), m.triangle_count()),
852 (4, 4, 2)
853 );
854 assert_eq!(m.triangle(1), Some([0, 2, 3]));
855 assert_eq!(m.revision(), before);
856
857 {
858 let mut tx = m.begin_edit();
859 tx.set_positions(&[0], &[[7.; 3]]).unwrap();
860 }
861 assert_eq!(m.position(0), Some([0., 0., 0.]));
862 }
863
864 #[test]
865 fn empty_transactions_keep_the_revision() {
866 let mut m = quad();
867 let patch = m.begin_edit().commit().unwrap();
868 assert!(patch.is_empty());
869 assert_eq!(m.revision(), 0);
870 }
871
872 #[test]
873 fn new_vertices_take_channel_values_from_parents() {
874 let mut m = quad();
875 m.add_channel(ChannelDesc::source("mask", 1)).unwrap();
876 m.add_channel(ChannelDesc::source("id", 1).with_policy(TopologyPolicy::Nearest))
877 .unwrap();
878 let mut tx = m.begin_edit();
879 tx.write_channel("mask", &[0, 1], &[2.0, 4.0]).unwrap();
880 tx.write_channel("id", &[0, 1], &[10.0, 20.0]).unwrap();
881 let v = tx
882 .add_vertex_with_parents([0.5, 0., 0.], &[(0, 0.75), (1, 0.25)])
883 .unwrap();
884 let patch = tx.commit().unwrap();
885 assert_eq!(m.channel("mask").unwrap()[v as usize], 2.5);
886 assert_eq!(m.channel("id").unwrap()[v as usize], 10.0);
887 assert_eq!(
888 patch.changes().channels,
889 vec![
890 ("mask".to_string(), vec![0, 1]),
891 ("id".to_string(), vec![0, 1])
892 ]
893 );
894 patch.revert(&mut m).unwrap();
895 assert_eq!(&m.channel("mask").unwrap()[..2], &[0.0, 0.0]);
896 }
897
898 #[test]
899 fn patches_skip_a_channel_that_was_removed_and_re_added_under_the_same_name() {
900 for new_width in [1, 4] {
902 let mut m = quad();
903 m.add_channel(ChannelDesc::source("c", 4)).unwrap();
904 let mut tx = m.begin_edit();
905 tx.write_channel("c", &[0, 1], &[1.0; 8]).unwrap();
906 let patch = tx.commit().unwrap();
907 m.remove_channel("c").unwrap();
908 m.add_channel(ChannelDesc::source("c", new_width).with_default(9.0))
909 .unwrap();
910 let untouched = vec![9.0; 4 * new_width];
911 patch.revert(&mut m).unwrap();
912 assert_eq!(m.channel("c").unwrap(), &untouched[..], "width {new_width}");
913 patch.apply(&mut m).unwrap();
914 assert_eq!(m.channel("c").unwrap(), &untouched[..], "width {new_width}");
915 }
916 }
917
918 #[test]
919 fn commit_that_runs_out_of_memory_rolls_back_everything() {
920 type State = (Vec<[f32; 3]>, Vec<[u32; 3]>, Vec<f32>, usize, usize, u64);
921 fn state(m: &Mesh) -> State {
922 (
923 m.positions()[..4].to_vec(),
924 m.triangles()[..2].to_vec(),
925 m.channel("c").unwrap()[..4].to_vec(),
926 m.vertex_count(),
927 m.triangle_count(),
928 m.revision(),
929 )
930 }
931 for moved in [vec![1], vec![0, 1, 2, 3]] {
933 let mut m = quad();
934 m.add_channel(ChannelDesc::source("c", 1)).unwrap();
935 m.add_channel(ChannelDesc::derived("d", 1)).unwrap();
936 let before = state(&m);
937 for k in 0.. {
938 let mut tx = m.begin_edit();
940 tx.set_positions(&moved, &vec![[7.; 3]; moved.len()])
941 .unwrap();
942 let v = tx.add_vertices(&[[1.; 3]]).unwrap()[0];
943 tx.add_triangles(&[[0, 1, v]]).unwrap();
944 tx.remove_triangles(&[1]).unwrap();
945 tx.write_channel("c", &[0], &[3.]).unwrap();
946 fail_allocations_after(Some(k));
947 let result = tx.commit();
948 fail_allocations_after(None);
949 match result {
950 Err(e) => {
951 assert_eq!(e, StoreError::OutOfMemory);
952 assert_eq!(state(&m), before, "failed after {k} allocations");
953 assert_eq!(m.vertex_slots(), 4, "appended slots truncated");
954 assert_eq!(m.channel("d").unwrap().len(), 4);
955 }
956 Ok(patch) => {
957 assert!(k > 0, "commit needs to allocate");
958 assert_eq!(m.position(1), Some([7.; 3]));
959 assert_eq!(m.triangle(1), None);
960 assert_eq!(m.channel("c").unwrap()[0], 3.);
961 patch.revert(&mut m).unwrap();
963 assert_eq!(state(&m), before);
964 break;
965 }
966 }
967 }
968 }
969 }
970
971 #[test]
972 fn repeated_writes_report_each_element_once() {
973 let mut m = quad();
974 m.add_channel(ChannelDesc::source("rgb", 3)).unwrap();
975 let mut tx = m.begin_edit();
977 tx.remove_triangles(&[1]).unwrap();
978 tx.remove_vertices(&[3]).unwrap();
979 let p = tx.commit().unwrap();
980 crate::History::new(0).push(&mut m, p).unwrap();
982 assert_eq!(m.free_vertex_slots(), &[3]);
983
984 let mut tx = m.begin_edit();
985 for round in 0..200 {
986 let x = round as f32;
987 tx.set_positions(&[2, 0, 2], &[[x; 3]; 3]).unwrap();
988 tx.write_channel("rgb", &[1, 2], &[x; 6]).unwrap();
989 tx.set_triangles(&[0], &[[0, 1, 2]]).unwrap();
990 }
991 let v = tx.add_vertices(&[[9.; 3]]).unwrap()[0];
992 let t = tx.add_triangles(&[[0, 2, v]]).unwrap()[0];
993 tx.set_positions(&[v], &[[8.; 3]]).unwrap();
994 tx.write_channel("rgb", &[v], &[1.; 3]).unwrap();
995 tx.set_triangles(&[t], &[[0, 1, v]]).unwrap();
996 let patch = tx.commit().unwrap();
997 assert_eq!((v, t), (3, 1), "free slots were reused");
998 let c = patch.changes();
999 assert_eq!(c.vertices_added, vec![3]);
1000 assert_eq!(c.vertices_moved, vec![0, 2]);
1001 assert_eq!(c.triangles_added, vec![1]);
1002 assert_eq!(c.triangles_changed, vec![0]);
1003 assert_eq!(c.channels, vec![("rgb".to_string(), vec![1, 2])]);
1004 let undo = patch.revert(&mut m).unwrap();
1005 assert_eq!(undo.vertices_moved, vec![0, 2]);
1006 assert_eq!(undo.vertices_removed, vec![3]);
1007 }
1008
1009 #[test]
1010 fn topology_writes_drop_the_adjacency_cache_inside_the_transaction() {
1011 type Edit = fn(&mut crate::Transaction<'_>);
1012 let edits: [Edit; 3] = [
1013 |tx| {
1014 tx.add_triangles(&[[1, 2, 3]]).unwrap();
1015 },
1016 |tx| tx.remove_triangles(&[1]).unwrap(),
1017 |tx| tx.set_triangles(&[1], &[[1, 2, 3]]).unwrap(),
1018 ];
1019 for edit in edits {
1020 let mut m = quad();
1021 m.ensure_adjacency().unwrap();
1022 let mut tx = m.begin_edit();
1023 tx.set_positions(&[0], &[[0., 0., 1.]]).unwrap();
1024 assert!(tx.mesh().adjacency().is_some(), "moves keep the cache");
1025 edit(&mut tx);
1026 assert!(tx.mesh().adjacency().is_none());
1027 tx.rollback();
1028 m.ensure_adjacency().unwrap();
1029 assert_eq!(
1030 m.adjacency().unwrap().edge_kind(1),
1031 crate::EdgeKind::Interior,
1032 "rebuilt from the rolled-back mesh"
1033 );
1034 }
1035 }
1036
1037 #[test]
1038 fn writing_a_derived_channel_in_a_transaction_is_rejected() {
1039 let mut m = quad();
1040 m.add_channel(ChannelDesc::derived("dist", 1)).unwrap();
1041 let mut tx = m.begin_edit();
1042 assert_eq!(
1043 tx.write_channel("dist", &[0], &[1.0]),
1044 Err(StoreError::NotSourceChannel("dist".into()))
1045 );
1046 }
1047
1048 #[test]
1049 fn slots_created_and_removed_in_one_transaction_are_reused() {
1050 let mut m = quad();
1051 let mut tx = m.begin_edit();
1052 let v = tx.add_vertices(&[[0.; 3]]).unwrap()[0];
1053 tx.remove_vertices(&[v]).unwrap();
1054 let patch = tx.commit().unwrap();
1055 assert!(patch.is_empty());
1056 assert_eq!(m.free_vertex_slots(), &[v]);
1057 let mut tx = m.begin_edit();
1058 assert_eq!(tx.add_vertices(&[[1.; 3]]).unwrap(), vec![v]);
1059 }
1060
1061 #[test]
1062 fn undoing_an_append_past_a_bitset_word_kills_the_slot() {
1063 let positions = vec![0.0f32; 64 * 3];
1065 let mut m = Mesh::from_buffers(&positions, &[]).unwrap();
1066 let mut tx = m.begin_edit();
1067 let v = tx.add_vertices(&[[1.; 3]]).unwrap()[0];
1068 let t = tx.add_triangles(&[[0, 1, v]]).unwrap()[0];
1069 let patch = tx.commit().unwrap();
1070 assert_eq!(v, 64);
1071 patch.revert(&mut m).unwrap();
1072 assert!(!m.is_vertex_alive(64));
1073 assert!(!m.is_triangle_alive(t));
1074 assert_eq!(m.triangles()[t as usize], [0, 0, 0]);
1075 assert_eq!(m.vertex_count(), 64);
1076 patch.apply(&mut m).unwrap();
1077 assert!(m.is_vertex_alive(64));
1078 assert_eq!(m.triangle(t), Some([0, 1, 64]));
1079 }
1080
1081 #[test]
1082 fn patches_only_apply_at_the_matching_revision() {
1083 let mut m = quad();
1084 let mut tx = m.begin_edit();
1085 tx.set_positions(&[0], &[[1.; 3]]).unwrap();
1086 let first = tx.commit().unwrap();
1087 let mut tx = m.begin_edit();
1088 tx.set_positions(&[0], &[[2.; 3]]).unwrap();
1089 let _second = tx.commit().unwrap();
1090 assert_eq!(first.revert(&mut m).err(), Some(StoreError::PatchMismatch));
1091 let mut other = quad();
1092 assert_eq!(
1093 first.apply(&mut other).err(),
1094 Some(StoreError::PatchMismatch)
1095 );
1096 }
1097}