Skip to main content

fast_mesh_scene/
scene.rs

1//! The scene: a registry of meshes with BVHs and placements.
2
3use fast_mesh_bvh::{Aabb, BuildOptions, Bvh, BvhBuild, UpdatePolicy};
4use fast_mesh_store::{ChangeSet, ChannelDesc, Mesh, Remap, StoreError, Transaction, VertexId};
5
6use crate::fields::{ColorState, CurvField, Field, FieldHook, FieldUnit, ThickField};
7use crate::history::{Change, Done, History};
8use crate::id::SlotMap;
9use crate::normals::{NormalBuild, NormalState};
10use crate::query::{CachedBox, ClosestCandidate, RayCandidate};
11use crate::report::Report;
12use crate::sections::{Section, SectionHook};
13use crate::{FieldId, MeshChanges, MeshId, Placement, Result, SceneError, SectionId, SubscriberId};
14
15/// When a mesh's BVH is brought up to date after the mesh changes.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub enum UpdateMode {
18    /// Right after every commit, undo and redo of that mesh.
19    #[default]
20    Eager,
21    /// Before the next query that needs it, or on `flush` / `update`.
22    Lazy,
23    /// Only on an explicit `update`; queries that need it fail instead.
24    Manual,
25}
26
27/// Options for adding a mesh to a [`Scene`].
28#[derive(Debug, Clone, Copy, PartialEq, Default)]
29pub struct MeshOptions {
30    /// How the mesh's BVH is built.
31    pub build: BuildOptions,
32    /// When the BVH falls back to heavier update strategies.
33    pub bvh_policy: UpdatePolicy,
34    /// When the BVH is brought up to date.
35    pub mode: UpdateMode,
36    /// Where the mesh starts out in the world.
37    pub placement: Placement,
38    /// Build the BVH on first use instead of up front. Only Lazy and
39    /// Manual meshes defer (Eager builds up front regardless); until the
40    /// first build the mesh is not current and [`Scene::bvh`] refuses it
41    /// with [`SceneError::StaleBvh`], like a stale BVH after an edit. Only
42    /// [`Scene::add_mesh`] defers; [`Scene::add_mesh_built`] finishes the
43    /// build it was given.
44    pub defer_bvh: bool,
45}
46
47/// Options for a [`Scene`].
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub struct SceneOptions {
50    /// Most bytes the shared undo history may hold.
51    pub history_budget: usize,
52}
53
54impl Default for SceneOptions {
55    /// A history budget of 256 MiB.
56    fn default() -> Self {
57        SceneOptions {
58            history_budget: 256 << 20,
59        }
60    }
61}
62
63/// One mesh of the scene with everything attached to it.
64pub(crate) struct Entry {
65    pub(crate) mesh: Mesh,
66    pub(crate) bvh: Bvh,
67    pub(crate) placement: Placement,
68    pub(crate) mode: UpdateMode,
69    /// Changes not yet applied to the BVH, merged into one set. `None` when
70    /// none are waiting.
71    pub(crate) pending: Option<ChangeSet>,
72    /// The BVH must be rebuilt from scratch: the mesh's content is not
73    /// described by `pending` (after a [`SceneError::Corrupted`] undo, or
74    /// a deferred add whose BVH was never built).
75    pub(crate) rebuild: bool,
76    /// One pending report per subscriber, in the order of
77    /// `Scene::subscribers`.
78    pub(crate) reports: Vec<Report>,
79    /// One pending change accumulator per field this mesh is part of (as
80    /// mesh `on` or `to`): the internal hook field updates read.
81    pub(crate) hooks: Vec<FieldHook>,
82    /// One pending change accumulator per section selecting this mesh.
83    pub(crate) section_hooks: Vec<SectionHook>,
84    /// The mesh's colouring and its colours, if it has one.
85    pub(crate) coloring: Option<ColorState>,
86    /// The mesh's vertex normals, if they are on.
87    pub(crate) normals: Option<NormalState>,
88    /// Goes up whenever the mesh's positions, triangles or normals change
89    /// (see [`Scene::content_stamp`]): every such change is reported, and
90    /// every report of one bumps it.
91    pub(crate) content: u64,
92    /// The mesh's world box, cached across queries (see
93    /// [`CachedBox`](crate::query::CachedBox)).
94    pub(crate) wbox: Option<CachedBox>,
95}
96
97impl Entry {
98    /// Marks the mesh as changed in unknown ways, after a failed recovery:
99    /// it is reported as fully changed, drops its cached world box, and the
100    /// next BVH update rebuilds from scratch even if the revision happens to
101    /// match. The caller then calls [`Entry::after_change`], which rebuilds
102    /// an Eager BVH right away.
103    pub(crate) fn flag_unknown(&mut self) {
104        self.pending = Some(ChangeSet::default());
105        self.rebuild = true;
106        self.wbox = None;
107        self.report_everything();
108    }
109
110    /// Reports the mesh as fully changed to every subscriber, colours and
111    /// normals included (vertex ids may have been renumbered), and to every
112    /// field and section hook; the normals are recomputed in full on their
113    /// next update.
114    pub(crate) fn report_everything(&mut self) {
115        self.content += 1;
116        for r in &mut self.reports {
117            r.changes = None;
118            r.colors_dirty = None;
119            r.normals_dirty = None;
120        }
121        if let Some(ns) = &mut self.normals {
122            ns.mark_all();
123        }
124        for h in &mut self.hooks {
125            h.changes = None;
126        }
127        for h in &mut self.section_hooks {
128            h.changes = None;
129        }
130    }
131
132    /// Reports a placement change to every subscriber, field hook and
133    /// section hook.
134    pub(crate) fn report_placement(&mut self) {
135        for r in &mut self.reports {
136            r.placement_changed = true;
137        }
138        for h in &mut self.hooks {
139            h.placement_changed = true;
140        }
141        for h in &mut self.section_hooks {
142            h.placement_changed = true;
143        }
144    }
145
146    /// Reports recoloured vertices to every subscriber (`None`: all).
147    pub(crate) fn report_colors(&mut self, ids: Option<&[VertexId]>) {
148        for r in &mut self.reports {
149            r.colors_changed(ids);
150        }
151    }
152
153    /// Reports a change of field `id`'s values to every subscriber.
154    pub(crate) fn report_field(&mut self, id: FieldId) {
155        for r in &mut self.reports {
156            r.field_changed(id);
157        }
158    }
159
160    /// Reports a change of section `id`'s result for this mesh to every
161    /// subscriber.
162    pub(crate) fn report_section(&mut self, id: SectionId) {
163        for r in &mut self.reports {
164            r.section_changed(id);
165        }
166    }
167
168    /// Reports a write to channel `name` to every subscriber.
169    fn report_channel(&mut self, name: &str) {
170        for r in &mut self.reports {
171            r.channel_written(name);
172        }
173    }
174
175    /// Folds a commit's (or undo's or redo's) changes into the BVH's pending
176    /// changes, into every subscriber's report, into every field and
177    /// section hook and into the normals' pending changes.
178    pub(crate) fn record_changes(&mut self, changes: &ChangeSet) {
179        self.content += 1;
180        match &mut self.pending {
181            Some(p) => p.merge(changes),
182            None => self.pending = Some(changes.clone()),
183        }
184        for r in &mut self.reports {
185            r.merge(changes);
186        }
187        for name in &changes.channels_toggled {
188            self.report_channel(name);
189        }
190        for h in &mut self.hooks {
191            if let Some(c) = &mut h.changes {
192                c.merge(changes);
193            }
194        }
195        for h in &mut self.section_hooks {
196            if let Some(c) = &mut h.changes {
197                c.merge(changes);
198            }
199        }
200        if let Some(ns) = &mut self.normals {
201            ns.record(changes, self.mesh.vertex_slots());
202        }
203        // Slots only grow through commits, undo, redo and cancel, all of
204        // which pass here.
205        self.grow_colors(&changes.vertices_added);
206    }
207
208    /// Whether the BVH is up to date with the mesh: not flagged for a
209    /// rebuild, and current by the BVH's own check.
210    pub(crate) fn is_current(&self) -> bool {
211        !self.rebuild && self.bvh.is_current(&self.mesh)
212    }
213
214    /// Brings the BVH up to date with the pending changes. On error the
215    /// pending changes are kept, so a later call can retry.
216    pub(crate) fn update_bvh(&mut self) -> Result<()> {
217        if self.rebuild {
218            #[cfg(test)]
219            fault::before_bvh_update()?;
220            #[cfg(test)]
221            fault::count_rebuild();
222            let mut bvh = Bvh::build(&self.mesh, self.bvh.options())?;
223            bvh.set_policy(*self.bvh.policy())?;
224            self.bvh = bvh;
225            self.rebuild = false;
226            self.pending = None;
227            return Ok(());
228        }
229        if self.bvh.is_current(&self.mesh) {
230            self.pending = None;
231            return Ok(());
232        }
233        #[cfg(test)]
234        fault::before_bvh_update()?;
235        // Invariant: whenever the BVH is stale, `pending` is `Some` (every
236        // commit, undo, redo and cancel goes through `record_changes`, and
237        // `compact`, which changes the epoch, sets it). The BVH rebuilds on an
238        // epoch change whatever the changes say, so the empty fallback is
239        // only a guard.
240        debug_assert!(self.pending.is_some(), "stale BVH without pending changes");
241        let none = ChangeSet::default();
242        let changes = self.pending.as_ref().unwrap_or(&none);
243        self.bvh.update(&mut self.mesh, changes)?;
244        self.pending = None;
245        Ok(())
246    }
247
248    /// After a change: brings an Eager BVH and Eager normals up to date. A
249    /// failure is not an error of the change (§15.1): the pending changes
250    /// stay and the BVH or normals are treated like Lazy until an update
251    /// succeeds.
252    pub(crate) fn after_change(&mut self) {
253        if self.mode == UpdateMode::Eager {
254            let _ = self.update_bvh();
255            let _ = self.update_normals();
256        }
257    }
258}
259
260/// A consumer of change reports.
261struct Subscriber {
262    id: SubscriberId,
263    /// Reports of meshes removed since its last `take_changes_for`.
264    removed: Vec<MeshChanges>,
265}
266
267/// A mesh being prepared for [`Scene::add_mesh_built`] outside any scene:
268/// its BVH, and its vertex normals when asked for
269/// ([`MeshBuild::set_normals`]), are built in steps ([`MeshBuild::step`]),
270/// so that a caller can yield between them (the Worker engine adds large
271/// meshes this way). Nothing of it is in a scene until it is added.
272#[derive(Debug)]
273pub struct MeshBuild {
274    mesh: Mesh,
275    options: MeshOptions,
276    bvh: BvhBuild,
277    /// The normals being computed, when they are on.
278    normals: Option<NormalBuild>,
279}
280
281impl MeshBuild {
282    /// Starts preparing `mesh` for [`Scene::add_mesh`] with `options`.
283    ///
284    /// # Errors
285    /// [`SceneError::InvalidInput`] for an invalid placement; a BVH option
286    /// or memory error.
287    pub fn new(mesh: Mesh, options: MeshOptions) -> Result<MeshBuild> {
288        options.placement.validate()?;
289        options.bvh_policy.validate()?;
290        let bvh = BvhBuild::new(&mesh, &options.build)?;
291        Ok(MeshBuild {
292            mesh,
293            options,
294            bvh,
295            normals: None,
296        })
297    }
298
299    /// Turns the mesh's vertex normals on or off (off by default), as
300    /// [`Scene::set_normals`] would right after the add: with them on, the
301    /// steps compute them too, once the BVH is built, and the mesh is added
302    /// with them on. Turning them off drops what was computed.
303    pub fn set_normals(&mut self, on: bool) {
304        if !on {
305            self.normals = None;
306        } else if self.normals.is_none() {
307            self.normals = Some(NormalBuild::new());
308        }
309    }
310
311    /// Builds about `budget` more units of the BVH (see
312    /// [`BvhBuild::step`]), then of the normals when they are on (a unit:
313    /// 4 triangles or 16 vertices). Returns whether everything is built.
314    ///
315    /// # Errors
316    /// A memory error; the build is unusable after it.
317    pub fn step(&mut self, budget: usize) -> Result<bool> {
318        if !self.bvh.is_done() {
319            self.bvh.step(&self.mesh, budget)?;
320        } else if let Some(n) = &mut self.normals {
321            n.step(&self.mesh, budget)?;
322        }
323        Ok(self.is_done())
324    }
325
326    /// Whether the BVH, and the normals when they are on, are built.
327    pub fn is_done(&self) -> bool {
328        self.bvh.is_done() && self.normals.as_ref().is_none_or(NormalBuild::is_done)
329    }
330
331    /// The vertex normals, once built with them on; `None` before or when
332    /// they are off.
333    pub fn normals(&self) -> Option<&[[f32; 3]]> {
334        self.normals
335            .as_ref()
336            .filter(|n| n.is_done())
337            .map(NormalBuild::normals)
338    }
339
340    /// The mesh being prepared.
341    pub fn mesh(&self) -> &Mesh {
342        &self.mesh
343    }
344}
345
346/// Several meshes, each with a BVH and a placement in the world.
347pub struct Scene {
348    pub(crate) entries: SlotMap<Entry>,
349    /// The subscribers in ascending id order; the default one comes first
350    /// and is never removed. Each entry's `reports` follow this order.
351    subscribers: Vec<Subscriber>,
352    /// The id the next `subscribe` hands out.
353    next_subscriber: u64,
354    /// The shared undo history across all meshes.
355    history: History,
356    /// The registered fields, in registration order.
357    pub(crate) fields: Vec<Field>,
358    /// The registered thickness fields, in registration order.
359    pub(crate) thick: Vec<ThickField>,
360    /// The registered curvature fields, in registration order.
361    pub(crate) curv: Vec<CurvField>,
362    /// The id the next `add_distance_field` hands out.
363    pub(crate) next_field: u32,
364    /// The registered sections, in registration order.
365    pub(crate) sections: Vec<Section>,
366    /// The id the next `add_section` hands out.
367    pub(crate) next_section: u32,
368    /// Eager field updates are left due (see [`Scene::set_defer_fields`]).
369    pub(crate) defer_fields: bool,
370    /// The interaction tier's budget (see [`Scene::set_field_interaction`]).
371    pub(crate) field_interaction: Option<usize>,
372    /// The open field step unit (see [`Scene::field_step`]).
373    pub(crate) field_unit: Option<FieldUnit>,
374    /// Reusable broadphase buffers for [`Scene::raycast`] and
375    /// [`Scene::closest_point`]: each call takes its own out and puts it
376    /// back cleared, so queries after the first do not allocate.
377    pub(crate) order_ray: Vec<RayCandidate>,
378    pub(crate) order_closest: Vec<ClosestCandidate>,
379    /// Test-only count of section entry recomputes.
380    #[cfg(test)]
381    pub(crate) section_recomputes: usize,
382    /// Test-only count of merged-change plane reachability re-checks.
383    #[cfg(test)]
384    pub(crate) section_reach_checks: usize,
385}
386
387// Every public `&mut self` method of `Scene`, in this file and the others,
388// starts by dropping an open field step unit with `drop_field_unit` (the
389// step calls aside), and is listed in the test
390// `fields::tests::mutating_calls_abandon_open_unit`. New ones must do both.
391impl Scene {
392    /// An empty scene.
393    pub fn new(options: SceneOptions) -> Scene {
394        Scene {
395            entries: SlotMap::new(),
396            subscribers: vec![Subscriber {
397                id: SubscriberId::DEFAULT,
398                removed: Vec::new(),
399            }],
400            next_subscriber: SubscriberId::DEFAULT.0 + 1,
401            history: History::new(options.history_budget),
402            fields: Vec::new(),
403            thick: Vec::new(),
404            curv: Vec::new(),
405            next_field: 0,
406            sections: Vec::new(),
407            next_section: 0,
408            defer_fields: false,
409            field_interaction: None,
410            field_unit: None,
411            order_ray: Vec::new(),
412            order_closest: Vec::new(),
413            #[cfg(test)]
414            section_recomputes: 0,
415            #[cfg(test)]
416            section_reach_checks: 0,
417        }
418    }
419
420    pub(crate) fn entry_mut(&mut self, id: MeshId) -> Result<&mut Entry> {
421        self.entries.get_mut(id).ok_or(SceneError::UnknownMesh)
422    }
423
424    /// Adds a mesh, building its BVH, and returns its id. With
425    /// [`MeshOptions::defer_bvh`] a Lazy or Manual mesh builds its BVH on
426    /// first use instead (its first update, query or flush), since nothing
427    /// needs it before then.
428    ///
429    /// # Errors
430    /// [`SceneError::InvalidInput`] for an invalid placement, or a BVH or
431    /// memory error from the build. An invalid policy is refused before
432    /// anything is built. The scene is unchanged on error.
433    pub fn add_mesh(&mut self, mesh: Mesh, options: MeshOptions) -> Result<MeshId> {
434        self.drop_field_unit();
435        options.placement.validate()?;
436        options.bvh_policy.validate()?;
437        if !options.defer_bvh || options.mode == UpdateMode::Eager {
438            let bvh = Bvh::build(&mesh, &options.build)?;
439            return self.insert_mesh(mesh, bvh, options);
440        }
441        // Deferred: a placeholder over an empty mesh, replaced by the first
442        // update. Building it validates the build options, as for Eager.
443        let bvh = Bvh::build(&Mesh::new(), &options.build)?;
444        let id = self.insert_mesh(mesh, bvh, options)?;
445        let entry = self.entry_mut(id)?;
446        entry.pending = Some(ChangeSet::default());
447        entry.rebuild = true;
448        Ok(id)
449    }
450
451    /// Adds a mesh built in steps by `build` (see [`MeshBuild`]), first
452    /// finishing what is left of its BVH and normals, and returns its id.
453    /// The mesh, its BVH, its normals and everything reported are the same
454    /// as from [`Scene::add_mesh`] with the same mesh and options, followed
455    /// by [`Scene::set_normals`] when the build has normals on.
456    ///
457    /// # Errors
458    /// As for [`Scene::add_mesh`]. The scene is unchanged on error.
459    pub fn add_mesh_built(&mut self, mut build: MeshBuild) -> Result<MeshId> {
460        self.drop_field_unit();
461        while !build.step(usize::MAX)? {}
462        let MeshBuild {
463            mesh,
464            options,
465            bvh,
466            normals,
467        } = build;
468        let bvh = bvh.finish().ok_or(SceneError::Corrupted)?;
469        let normals = match normals {
470            Some(n) => Some(n.finish().ok_or(SceneError::Corrupted)?),
471            None => None,
472        };
473        let id = self.insert_mesh(mesh, bvh, options)?;
474        if let Some(n) = normals {
475            let entry = self.entry_mut(id)?;
476            entry.normals = Some(n);
477            entry.report_normals(None);
478        }
479        Ok(id)
480    }
481
482    /// Inserts a mesh with its freshly built BVH.
483    fn insert_mesh(&mut self, mesh: Mesh, mut bvh: Bvh, options: MeshOptions) -> Result<MeshId> {
484        bvh.set_policy(options.bvh_policy)?;
485        self.entries.insert(Entry {
486            mesh,
487            bvh,
488            placement: options.placement,
489            mode: options.mode,
490            pending: None,
491            rebuild: false,
492            reports: vec![Report::added(); self.subscribers.len()],
493            hooks: Vec::new(),
494            section_hooks: Vec::new(),
495            coloring: None,
496            normals: None,
497            content: 0,
498            wbox: None,
499        })
500    }
501
502    /// Removes a mesh and returns it. The id never matches again. Its
503    /// history entries (undo, redo and in an open step) are dropped, steps
504    /// left empty with them, and its patches release the slots they reserve.
505    /// The removal shows up in every subscriber's next
506    /// [`Scene::take_changes_for`], unless the mesh was added since that
507    /// subscriber's last call.
508    ///
509    /// The fields on the mesh and those measured to it alone are removed
510    /// with it (see [`Scene::remove_field`]), and so are colourings by them.
511    /// A field measured to it and to other meshes keeps the others: it is
512    /// recomputed in full, at once when it is Eager. The
513    /// mesh leaves every section selecting it, which is reported in its
514    /// final [`MeshChanges::sections_changed`].
515    ///
516    /// # Errors
517    /// [`SceneError::UnknownMesh`] for a stale id.
518    pub fn remove_mesh(&mut self, id: MeshId) -> Result<Mesh> {
519        self.drop_field_unit();
520        self.entry(id)?;
521        // Reserve first so no report can be lost after the removal.
522        for s in &mut self.subscribers {
523            s.removed
524                .try_reserve(1)
525                .map_err(|_| SceneError::Store(StoreError::OutOfMemory))?;
526        }
527        let kept_on = self.drop_fields_of(id);
528        self.drop_sections_of(id);
529        let mut entry = self.entries.remove(id).ok_or(SceneError::UnknownMesh)?;
530        self.history.forget(id, Some(&mut entry.mesh));
531        for (s, r) in self.subscribers.iter_mut().zip(&mut entry.reports) {
532            if !r.added {
533                s.removed.push(r.take(id, true));
534            }
535        }
536        // The fields that lost a target are due in full.
537        self.run_eager_fields(Some(&kept_on));
538        Ok(entry.mesh)
539    }
540
541    /// Runs `f` in a transaction on mesh `id`, commits it, and records the
542    /// patch in the history: in the open step, or as a step of its own when
543    /// none is open. Recording drops the redo stack. The changes are added to
544    /// the mesh's report and to its BVH's pending changes; an Eager BVH is
545    /// then brought up to date, and so are the Eager fields and section
546    /// entries involving the mesh (a failed update leaves them stale and
547    /// never fails the edit).
548    ///
549    /// An empty transaction records and reports nothing.
550    ///
551    /// If the Eager BVH update fails (for example out of memory), the edit
552    /// still succeeds: the BVH keeps its pending changes and is treated like
553    /// Lazy until [`Scene::update`], [`Scene::flush`] or a query updates it.
554    ///
555    /// `f` may fail with any error that converts into [`SceneError`]: a
556    /// store error, or an [`OpsError`](fast_mesh_ops::OpsError) from a repair
557    /// operation, which becomes [`SceneError::Ops`] (out-of-memory becomes
558    /// [`SceneError::Store`], however it was raised). A closure whose body
559    /// never fails cannot infer its error type (E0283) and must name it, for
560    /// example `scene.edit(id, |_| Ok::<(), StoreError>(()))`.
561    ///
562    /// # Errors
563    /// [`SceneError::UnknownMesh`] for a stale id; `f`'s error, converted
564    /// (for example [`SceneError::Store`] or [`SceneError::Ops`]), when it
565    /// fails; or [`SceneError::Store`] when the commit fails or there is no
566    /// memory to record the patch. The transaction is then rolled back and
567    /// the scene (including an open step) is unchanged.
568    pub fn edit<R, E: Into<SceneError>>(
569        &mut self,
570        id: MeshId,
571        f: impl FnOnce(&mut Transaction) -> std::result::Result<R, E>,
572    ) -> Result<R> {
573        self.drop_field_unit();
574        let entry = self.entries.get_mut(id).ok_or(SceneError::UnknownMesh)?;
575        // Make room first, so recording cannot fail after the commit.
576        self.history.reserve()?;
577        let mut tx = entry.mesh.begin_edit();
578        // Dropping the transaction on error rolls it back.
579        let r = f(&mut tx).map_err(Into::into)?;
580        let patch = tx.commit()?;
581        if patch.is_empty() {
582            return Ok(r);
583        }
584        entry.record_changes(patch.changes());
585        self.history
586            .record(&mut self.entries, Change::Edit(id, patch));
587        if let Some(entry) = self.entries.get_mut(id) {
588            entry.after_change();
589        }
590        self.run_eager(Some(&[id]));
591        Ok(r)
592    }
593
594    /// Opens a history step, or one more nesting level of the open step.
595    /// Edits and placement changes until the matching [`Scene::end_step`]
596    /// are undone and redone together, across meshes.
597    pub fn begin_step(&mut self) {
598        self.drop_field_unit();
599        self.history.begin();
600    }
601
602    /// Ends one nesting level of the open step; the outermost call closes
603    /// it. A step with no entries is not recorded. Closing may evict the
604    /// oldest steps to meet the history budget.
605    ///
606    /// # Errors
607    /// [`SceneError::StepNotOpen`] when no step is open.
608    pub fn end_step(&mut self) -> Result<()> {
609        self.drop_field_unit();
610        self.history.end(&mut self.entries)
611    }
612
613    /// Reverts the open step's entries, in reverse order, and discards the
614    /// step, however deeply nested (for example on Esc mid-drag). The
615    /// reverts are reported and update BVHs like an undo.
616    ///
617    /// # Errors
618    /// [`SceneError::StepNotOpen`] when no step is open. A store error (for
619    /// example out of memory) leaves the meshes and the open step unchanged;
620    /// [`SceneError::Corrupted`] means that even restoring them failed, with
621    /// the consequences described for [`Scene::undo`].
622    pub fn cancel_step(&mut self) -> Result<()> {
623        self.drop_field_unit();
624        let done = self.history.cancel(&mut self.entries);
625        let done = self.after_corrupted(done)?;
626        self.absorb(done);
627        Ok(())
628    }
629
630    /// Passes a history result through; after [`SceneError::Corrupted`]
631    /// (meshes flagged as fully changed) runs every Eager field and section
632    /// entry first.
633    fn after_corrupted<T>(&mut self, r: Result<T>) -> Result<T> {
634        if r.as_ref().err() == Some(&SceneError::Corrupted) {
635            self.run_eager(None);
636        }
637        r
638    }
639
640    /// Updates the Eager fields and section entries involving `touched`
641    /// (all with `None`), swallowing failures (§6.3).
642    fn run_eager(&mut self, touched: Option<&[MeshId]>) {
643        self.run_eager_fields(touched);
644        self.run_eager_sections(touched);
645    }
646
647    /// Closes an open step, then undoes the latest step: its entries are
648    /// reverted in reverse order, all or nothing. Returns `false` when there
649    /// is nothing to undo. The changes are reported and reach the BVHs as
650    /// for [`Scene::edit`]; a placement entry gets its old value back and is
651    /// reported as `placement_changed`.
652    ///
653    /// A non-empty open step is the latest step, and it is undone even when
654    /// it is larger than the whole history budget (Ctrl+Z mid-drag undoes
655    /// the drag). The budget is applied afterwards, which may evict the
656    /// oldest undo steps and, for a step larger than the budget, drop it
657    /// from the redo stack again.
658    ///
659    /// # Errors
660    /// A store error (for example out of memory): the meshes, both stacks
661    /// and an open step are then unchanged (the step stays open).
662    /// [`SceneError::Corrupted`] if restoring them after such an error
663    /// failed too: the failed step is dropped from its stack (an open step
664    /// is discarded), the meshes it edits may be partly changed, each of
665    /// them is reported as fully changed (`changes: None`) and its BVH is
666    /// rebuilt from scratch on its next update (right away for Eager), and
667    /// each mesh with a placement entry in it is reported as
668    /// `placement_changed`. The history may no longer match the meshes, so
669    /// the caller should consider [`Scene::clear_history`].
670    pub fn undo(&mut self) -> Result<bool> {
671        self.drop_field_unit();
672        let done = self.history.undo(&mut self.entries);
673        let done = self.after_corrupted(done)?;
674        Ok(done.map(|d| self.absorb(d)).is_some())
675    }
676
677    /// Closes an open step, then redoes the latest undone step: its entries
678    /// are re-applied in order, all or nothing. Returns `false` when there is
679    /// nothing to redo. Reporting and errors as for [`Scene::undo`].
680    ///
681    /// # Errors
682    /// As for [`Scene::undo`].
683    pub fn redo(&mut self) -> Result<bool> {
684        self.drop_field_unit();
685        let done = self.history.redo(&mut self.entries);
686        let done = self.after_corrupted(done)?;
687        Ok(done.map(|d| self.absorb(d)).is_some())
688    }
689
690    /// True if [`Scene::undo`] would undo something, counting a non-empty
691    /// open step.
692    pub fn can_undo(&self) -> bool {
693        self.history.can_undo()
694    }
695
696    /// True if [`Scene::redo`] would redo something.
697    pub fn can_redo(&self) -> bool {
698        self.history.can_redo()
699    }
700
701    /// Closes an open step, then drops every undo and redo step, releasing
702    /// the slots their patches reserve. The meshes are not changed.
703    ///
704    /// # Errors
705    /// None at present; the `Result` leaves room for future checks.
706    pub fn clear_history(&mut self) -> Result<()> {
707        self.drop_field_unit();
708        self.history.clear(&mut self.entries);
709        Ok(())
710    }
711
712    /// Feeds the outcome of an undo, redo or cancel into the reports and
713    /// pending changes, then runs the Eager updates (BVHs, then fields and
714    /// sections), as after an edit.
715    fn absorb(&mut self, done: Vec<Done>) {
716        for (id, changes) in &done {
717            if let Some(e) = self.entries.get_mut(*id) {
718                match changes {
719                    Some(c) => e.record_changes(c),
720                    None => e.report_placement(),
721                }
722            }
723        }
724        // Placement entries leave the BVHs alone.
725        for (id, _) in done.iter().filter(|(_, c)| c.is_some()) {
726            if let Some(e) = self.entries.get_mut(*id) {
727                e.after_change();
728            }
729        }
730        let touched: Vec<MeshId> = done.iter().map(|(id, _)| *id).collect();
731        self.run_eager(Some(&touched));
732    }
733
734    /// Sets when the mesh's BVH is brought up to date. Switching to
735    /// [`UpdateMode::Eager`] updates a stale BVH right away; if that update
736    /// fails, the mode is still set and the BVH stays pending, as after a
737    /// failed Eager update in [`Scene::edit`].
738    ///
739    /// # Errors
740    /// [`SceneError::UnknownMesh`] for a stale id.
741    pub fn set_mode(&mut self, id: MeshId, mode: UpdateMode) -> Result<()> {
742        self.drop_field_unit();
743        let entry = self.entry_mut(id)?;
744        entry.mode = mode;
745        entry.after_change();
746        self.run_eager(Some(&[id]));
747        Ok(())
748    }
749
750    /// When the mesh's BVH is brought up to date (see [`Scene::set_mode`]).
751    ///
752    /// # Errors
753    /// [`SceneError::UnknownMesh`] for a stale id.
754    pub fn mode(&self, id: MeshId) -> Result<UpdateMode> {
755        Ok(self.entry(id)?.mode)
756    }
757
758    /// Brings the mesh's BVH, and its normals if they are on, up to date
759    /// now, whatever its mode. Eager fields involving the mesh that were
760    /// left stale (for example by this BVH being a stale Manual one) then
761    /// catch up, unless Eager field updates are deferred (see
762    /// [`Scene::set_defer_fields`]): they then stay due. Every stale section
763    /// entry of the mesh catches up too, whatever its mode.
764    ///
765    /// # Errors
766    /// [`SceneError::UnknownMesh`], or a BVH or memory error from the update
767    /// of the BVH or the normals (both are attempted). On error they keep
768    /// their pending changes. Then the first section error (for example out
769    /// of memory); such entries stay stale.
770    pub fn update(&mut self, id: MeshId) -> Result<()> {
771        self.drop_field_unit();
772        let entry = self.entry_mut(id)?;
773        let bvh = entry.update_bvh();
774        let normals = entry.update_normals();
775        bvh.and(normals)?;
776        self.run_eager_fields(Some(&[id]));
777        self.run_sections(|_| true, Some(&[id]))
778    }
779
780    /// Brings every Lazy BVH up to date, and every Eager one left stale by a
781    /// failed update, and the same for the normals (see
782    /// [`Scene::set_normals`]); then does the same for the fields, which
783    /// follow the mode of the mesh they are on (see
784    /// [`Scene::update_fields`]), and for the section entries, which follow
785    /// the mode of their mesh (see [`Scene::update_sections`]). Manual BVHs,
786    /// and fields and section entries on Manual meshes, are left alone.
787    ///
788    /// # Errors
789    /// The first BVH, memory or field error, for example
790    /// [`SceneError::StaleBvh`] for a field measured to a stale Manual mesh:
791    /// such a field makes every `flush` return `StaleBvh` until that BVH is
792    /// brought up to date with [`Scene::update`]. Every mesh and field is
793    /// still attempted; those that failed keep their pending changes.
794    pub fn flush(&mut self) -> Result<()> {
795        self.drop_field_unit();
796        let mut first = Ok(());
797        for (_, entry) in self.entries.iter_mut() {
798            if entry.mode != UpdateMode::Manual {
799                let bvh = entry.update_bvh();
800                let normals = entry.update_normals();
801                if first.is_ok() {
802                    first = bvh.and(normals);
803                }
804            }
805        }
806        let r = self.run_fields(|mode| mode != UpdateMode::Manual, None);
807        if first.is_ok() {
808            first = r;
809        }
810        let r = self.run_sections(|mode| mode != UpdateMode::Manual, None);
811        if first.is_ok() {
812            first = r;
813        }
814        first
815    }
816
817    /// Everything that happened to the meshes since the last call, one entry
818    /// per affected mesh in id order, then resets every report of the
819    /// default subscriber. Other subscribers keep theirs (see
820    /// [`Scene::subscribe`]).
821    ///
822    /// A mesh added and removed between two calls is not reported, and
823    /// neither is a mesh whose changes cancel out (for example vertices added
824    /// and removed again).
825    pub fn take_changes(&mut self) -> Vec<MeshChanges> {
826        self.drop_field_unit();
827        // The default subscriber is always first.
828        self.take_at(0)
829    }
830
831    /// Adds a consumer of change reports and returns its id. It starts with
832    /// a clean report for every mesh, so it sees only what happens from now
833    /// on, except for colours: a mesh that already has a colouring starts
834    /// with `colors_dirty: None`, so the subscriber reads every colour once.
835    /// It drains its reports with [`Scene::take_changes_for`]. Every
836    /// subscriber is told about the same changes, merged the same way.
837    /// A subscriber that never drains keeps accumulating reports (merged
838    /// change sets and removals) until it is drained or unsubscribed.
839    pub fn subscribe(&mut self) -> SubscriberId {
840        self.drop_field_unit();
841        let id = SubscriberId(self.next_subscriber);
842        self.next_subscriber += 1;
843        for (_, entry) in self.entries.iter_mut() {
844            let mut report = Report::clean();
845            if entry.coloring.is_some() {
846                report.colors_dirty = None;
847            }
848            entry.reports.push(report);
849        }
850        self.subscribers.push(Subscriber {
851            id,
852            removed: Vec::new(),
853        });
854        id
855    }
856
857    /// Removes a subscriber and drops its pending reports. Its id is
858    /// rejected from then on.
859    ///
860    /// # Errors
861    /// [`SceneError::InvalidInput`] for an unknown or already removed id, or
862    /// for the default subscriber (which backs [`Scene::take_changes`]).
863    pub fn unsubscribe(&mut self, id: SubscriberId) -> Result<()> {
864        self.drop_field_unit();
865        if id == SubscriberId::DEFAULT {
866            return Err(SceneError::InvalidInput(
867                "the default subscriber cannot be removed",
868            ));
869        }
870        let i = self.subscriber_index(id)?;
871        self.subscribers.remove(i);
872        for (_, entry) in self.entries.iter_mut() {
873            entry.reports.remove(i);
874        }
875        Ok(())
876    }
877
878    /// [`Scene::take_changes`] for subscriber `id`: everything that happened
879    /// to the meshes since its last call (or since it subscribed), one entry
880    /// per affected mesh in id order, then resets its reports.
881    ///
882    /// # Errors
883    /// [`SceneError::InvalidInput`] for an unknown or removed subscriber.
884    pub fn take_changes_for(&mut self, id: SubscriberId) -> Result<Vec<MeshChanges>> {
885        self.drop_field_unit();
886        let i = self.subscriber_index(id)?;
887        Ok(self.take_at(i))
888    }
889
890    fn subscriber_index(&self, id: SubscriberId) -> Result<usize> {
891        self.subscribers
892            .binary_search_by_key(&id, |s| s.id)
893            .map_err(|_| SceneError::InvalidInput("unknown or removed subscriber"))
894    }
895
896    /// Drains the reports of the subscriber at index `i`.
897    fn take_at(&mut self, i: usize) -> Vec<MeshChanges> {
898        let mut out = std::mem::take(&mut self.subscribers[i].removed);
899        for (id, entry) in self.entries.iter_mut() {
900            let r = &mut entry.reports[i];
901            if r.is_clean() {
902                *r = Report::clean();
903            } else {
904                out.push(r.take(id, false));
905            }
906        }
907        out.sort_by_key(|c| c.id);
908        out
909    }
910
911    /// Adds a channel to the mesh, filled with its default value. Not
912    /// undoable, as in the store; reported in
913    /// [`MeshChanges::channels_written`]. Names starting with `field:` are
914    /// reserved for the scene's fields.
915    ///
916    /// # Errors
917    /// [`SceneError::InvalidInput`] for a reserved name,
918    /// [`SceneError::UnknownMesh`], or the store's error (for example a
919    /// duplicate name). The scene is unchanged on error.
920    pub fn add_channel(&mut self, id: MeshId, desc: ChannelDesc) -> Result<()> {
921        self.drop_field_unit();
922        check_name(&desc.name)?;
923        let entry = self.entry_mut(id)?;
924        let name = desc.name.clone();
925        entry.mesh.add_channel(desc)?;
926        entry.report_channel(&name);
927        Ok(())
928    }
929
930    /// Records the mesh's rest sizes for
931    /// [`split_stretched`](fast_mesh_ops::stretch::split_stretched); see
932    /// [`capture_rest_sizes`](fast_mesh_ops::stretch::capture_rest_sizes). Not
933    /// an edit: no undo step and no change report, since no geometry changed
934    /// and nothing draws this channel. With `only_if_missing`, valid rest sizes
935    /// stay as they are ([`ensure_rest_sizes`](fast_mesh_ops::stretch::ensure_rest_sizes)).
936    ///
937    /// # Errors
938    /// [`SceneError::UnknownMesh`], or [`SceneError::Ops`] when memory runs out.
939    pub fn capture_rest_sizes(&mut self, id: MeshId, only_if_missing: bool) -> Result<()> {
940        self.drop_field_unit();
941        let entry = self.entry_mut(id)?;
942        if only_if_missing {
943            fast_mesh_ops::stretch::ensure_rest_sizes(&mut entry.mesh)?;
944        } else {
945            fast_mesh_ops::stretch::capture_rest_sizes(&mut entry.mesh)?;
946        }
947        Ok(())
948    }
949
950    /// Builds the mesh's vertex index if it is not cached, so that an operation
951    /// run next can follow it instead of scanning every triangle.
952    ///
953    /// # Errors
954    /// [`SceneError::UnknownMesh`], or the store's error when memory runs out.
955    pub fn ensure_incidence(&mut self, id: MeshId) -> Result<()> {
956        self.entry_mut(id)?.mesh.ensure_incidence()?;
957        Ok(())
958    }
959
960    /// Removes a channel from the mesh and returns its description. Not
961    /// undoable, as in the store; reported in
962    /// [`MeshChanges::channels_written`]. Recorded patches skip the removed
963    /// channel from then on, so undo and redo will not restore the values of
964    /// a removed source channel.
965    ///
966    /// # Errors
967    /// [`SceneError::InvalidInput`] for a reserved name (starting with
968    /// `field:`), [`SceneError::UnknownMesh`], or the store's error for an
969    /// unknown name.
970    pub fn remove_channel(&mut self, id: MeshId, name: &str) -> Result<ChannelDesc> {
971        self.drop_field_unit();
972        check_name(name)?;
973        let entry = self.entry_mut(id)?;
974        let desc = entry.mesh.remove_channel(name)?;
975        entry.report_channel(name);
976        Ok(desc)
977    }
978
979    /// Writable values of a derived channel. Writes are not undoable, as in
980    /// the store. The call itself is reported in
981    /// [`MeshChanges::channels_written`], since the slice may be written
982    /// after it returns.
983    ///
984    /// # Errors
985    /// [`SceneError::InvalidInput`] for a reserved name (starting with
986    /// `field:`), [`SceneError::UnknownMesh`], or the store's error for an
987    /// unknown or non-derived channel.
988    pub fn derived_channel_mut(&mut self, id: MeshId, name: &str) -> Result<&mut [f32]> {
989        self.drop_field_unit();
990        check_name(name)?;
991        let entry = self.entry_mut(id)?;
992        // Look the channel up first, so a failed call reports nothing.
993        entry.mesh.derived_channel_mut(name)?;
994        entry.report_channel(name);
995        Ok(entry.mesh.derived_channel_mut(name)?)
996    }
997
998    /// Removes the mesh's dead slots and renumbers the rest (see
999    /// [`Mesh::compact`]). The mesh's recorded patches no longer apply, so
1000    /// its edit entries leave the undo and redo stacks and an open step;
1001    /// steps left empty are dropped. Its placement entries stay valid and
1002    /// are kept. Nothing is released: the reserved slots no longer exist.
1003    /// Its BVH is rebuilt whatever its mode, and the next
1004    /// [`Scene::take_changes`] reports it as fully changed (`changes: None`,
1005    /// `colors_dirty: None`, `normals_dirty: None`). Its field channels and
1006    /// colours are remapped with it, and its normals recomputed in full,
1007    /// whatever its mode; the fields involving it are recomputed in full on
1008    /// their next update, and so are its section entries.
1009    ///
1010    /// If the rebuild (or the normals) fails, compaction still succeeds and
1011    /// the BVH (or the normals) stays pending, as after a failed Eager
1012    /// update in [`Scene::edit`].
1013    ///
1014    /// # Errors
1015    /// [`SceneError::UnknownMesh`], or a store error from the compaction; the
1016    /// scene is then unchanged.
1017    pub fn compact(&mut self, id: MeshId) -> Result<Remap> {
1018        self.drop_field_unit();
1019        let entry = self.entries.get_mut(id).ok_or(SceneError::UnknownMesh)?;
1020        let remap = entry.mesh.compact()?;
1021        // The slots of dropped patches no longer exist: nothing to release.
1022        self.history.forget(id, None);
1023        // Every slot id changed; the BVH update rebuilds from scratch because
1024        // the mesh's epoch changed, whatever the pending changes say.
1025        entry.pending = Some(ChangeSet::default());
1026        entry.report_everything();
1027        let _ = entry.update_bvh();
1028        // The slot count changed under the normals: recompute them now.
1029        let _ = entry.update_normals();
1030        // The field channels were remapped with the mesh; so are the colours
1031        // and the fields' nearest targets.
1032        entry.recolor_all();
1033        for f in self.fields.iter_mut().filter(|f| f.desc.on == id) {
1034            let old = std::mem::take(&mut f.nearest);
1035            let mut nearest = Vec::new();
1036            // Without the memory, it stays empty (every slot "none") until
1037            // the field's next update, which recomputes every vertex.
1038            if nearest.try_reserve_exact(entry.mesh.vertex_slots()).is_ok() {
1039                nearest.resize(entry.mesh.vertex_slots(), u32::MAX);
1040                for (v, n) in old.into_iter().enumerate() {
1041                    if let Some(w) = remap.vertex(v as VertexId) {
1042                        nearest[w as usize] = n;
1043                    }
1044                }
1045            }
1046            f.nearest = nearest;
1047        }
1048        self.run_eager(Some(&[id]));
1049        Ok(remap)
1050    }
1051
1052    pub(crate) fn entry(&self, id: MeshId) -> Result<&Entry> {
1053        self.entries.get(id).ok_or(SceneError::UnknownMesh)
1054    }
1055
1056    /// The mesh behind `id`.
1057    ///
1058    /// # Errors
1059    /// [`SceneError::UnknownMesh`] for a stale id.
1060    pub fn mesh(&self, id: MeshId) -> Result<&Mesh> {
1061        Ok(&self.entry(id)?.mesh)
1062    }
1063
1064    /// The ids of all meshes, in index order.
1065    pub fn ids(&self) -> impl Iterator<Item = MeshId> {
1066        self.entries.iter().map(|(id, _)| id)
1067    }
1068
1069    /// The id of the live mesh with this [`MeshId::index`] and
1070    /// [`MeshId::generation`], or `None` when there is none (never added,
1071    /// or removed). For ids passed back across an FFI boundary: a stale or
1072    /// made-up pair cannot become a live id.
1073    pub fn mesh_id(&self, index: u32, generation: u32) -> Option<MeshId> {
1074        self.entries.live_id(index, generation)
1075    }
1076
1077    /// Where the mesh sits in the world.
1078    ///
1079    /// # Errors
1080    /// [`SceneError::UnknownMesh`] for a stale id.
1081    pub fn placement(&self, id: MeshId) -> Result<Placement> {
1082        Ok(self.entry(id)?.placement)
1083    }
1084
1085    /// Moves the mesh. Stores the placement, reports it as
1086    /// `placement_changed` and records the old and new value in the history
1087    /// (in the open step, or as a step of its own), which drops the redo
1088    /// stack. Setting the current placement again records and reports
1089    /// nothing. No BVH is touched; Eager fields and section entries
1090    /// involving the mesh are updated (a failure leaves them stale).
1091    ///
1092    /// Within an open step, consecutive placement changes of the same mesh
1093    /// merge into one entry that keeps the first old value and the last new
1094    /// one, so a drag of any length records one entry and undo still
1095    /// restores the placement from before the drag. Any other entry in
1096    /// between (an edit, or another mesh's placement) starts a new entry.
1097    ///
1098    /// # Errors
1099    /// [`SceneError::UnknownMesh`], [`SceneError::InvalidInput`] when the
1100    /// rotation is not a proper rotation or the scale is not finite and
1101    /// positive, or [`SceneError::Store`] when there is no memory to record
1102    /// it. The scene is unchanged on error.
1103    pub fn set_placement(&mut self, id: MeshId, placement: Placement) -> Result<()> {
1104        self.drop_field_unit();
1105        placement.validate()?;
1106        let entry = self.entries.get_mut(id).ok_or(SceneError::UnknownMesh)?;
1107        let old = entry.placement;
1108        if old == placement {
1109            return Ok(());
1110        }
1111        self.history.reserve()?;
1112        entry.placement = placement;
1113        entry.report_placement();
1114        self.history
1115            .record_placement(&mut self.entries, id, old, placement);
1116        self.run_eager(Some(&[id]));
1117        Ok(())
1118    }
1119
1120    /// Whether the mesh's BVH is up to date with the mesh.
1121    ///
1122    /// # Errors
1123    /// [`SceneError::UnknownMesh`] for a stale id.
1124    pub fn is_current(&self, id: MeshId) -> Result<bool> {
1125        Ok(self.entry(id)?.is_current())
1126    }
1127
1128    /// The box around the vertices the live triangles use, in the mesh's
1129    /// stored local coordinates, or `None` without live triangles. Dead
1130    /// slots keep stale positions, and a live vertex no live triangle uses
1131    /// is not drawn either, so neither widens it.
1132    ///
1133    /// # Errors
1134    /// [`SceneError::UnknownMesh`] for a stale id.
1135    pub fn bounds(&self, id: MeshId) -> Result<Option<Aabb>> {
1136        let mesh = self.mesh(id)?;
1137        let positions = mesh.positions();
1138        let mut out = Aabb::EMPTY;
1139        for t in 0..mesh.triangle_slots() as u32 {
1140            // Dead slots read `0, 0, 0`, which would pull in vertex 0, so
1141            // only alive triangles count.
1142            if let Some(tri) = mesh.triangle(t) {
1143                out.grow(&Aabb::of_triangle(positions, tri));
1144            }
1145        }
1146        if out.is_empty() {
1147            Ok(None)
1148        } else {
1149            Ok(Some(out))
1150        }
1151    }
1152
1153    /// Makes the BVHs a query needs current (§15.4): those of `only`, or of
1154    /// every mesh when `None`. Every non-Manual BVH among them is updated
1155    /// (Lazy ones, and Eager ones left stale by a failed update); then a
1156    /// stale Manual BVH, including one flagged for a rebuild, fails the
1157    /// query with [`SceneError::StaleBvh`] for the lowest such id. Ids in
1158    /// `only` must be known.
1159    ///
1160    /// # Errors
1161    /// The first BVH or memory error from an update, then
1162    /// [`SceneError::StaleBvh`].
1163    pub(crate) fn prepare(&mut self, only: Option<&[MeshId]>) -> Result<()> {
1164        let wanted = |id: MeshId| only.is_none_or(|ids| ids.contains(&id));
1165        let mut stale = None;
1166        for (id, entry) in self.entries.iter_mut() {
1167            if !wanted(id) {
1168                continue;
1169            }
1170            if entry.mode != UpdateMode::Manual {
1171                entry.update_bvh()?;
1172            } else if stale.is_none() && !entry.is_current() {
1173                stale = Some(id);
1174            }
1175        }
1176        match stale {
1177            Some(id) => Err(SceneError::StaleBvh(id)),
1178            None => Ok(()),
1179        }
1180    }
1181}
1182
1183/// The channel-name prefix reserved for the scene's fields.
1184pub(crate) const FIELD_PREFIX: &str = "field:";
1185
1186/// Rejects a channel name in the reserved `field:` namespace.
1187fn check_name(name: &str) -> Result<()> {
1188    if name.starts_with(FIELD_PREFIX) {
1189        return Err(SceneError::InvalidInput(
1190            "channel names starting with `field:` are reserved",
1191        ));
1192    }
1193    Ok(())
1194}
1195
1196/// Test-only fault injection for the Scene's calls into lower crates, whose
1197/// own fault hooks are crate-private.
1198#[cfg(test)]
1199pub(crate) mod fault {
1200    use std::cell::Cell;
1201
1202    use fast_mesh_bvh::BvhError;
1203    use fast_mesh_store::StoreError;
1204
1205    thread_local! {
1206        /// Calls to `Bvh::update` left before the one that fails.
1207        static BVH_UPDATE: Cell<Option<u32>> = const { Cell::new(None) };
1208    }
1209
1210    /// Makes the `n`th coming `Bvh::update` call (1 = the next) fail with
1211    /// `OutOfMemory` without reaching the BVH. Fires once.
1212    pub(crate) fn fail_bvh_update_on(n: u32) {
1213        assert!(n > 0);
1214        BVH_UPDATE.with(|c| c.set(Some(n - 1)));
1215    }
1216
1217    thread_local! {
1218        /// Patch apply/revert calls counted since the first pending fault.
1219        static PATCH_CALLS: Cell<u32> = const { Cell::new(0) };
1220        /// Bit `k` set: call `k` (counted in `PATCH_CALLS`) fails.
1221        static PATCH_FAILS: Cell<u64> = const { Cell::new(0) };
1222    }
1223
1224    /// Makes the `n`th coming patch apply or revert of a history entry
1225    /// (1 = the next) fail with the store's `OutOfMemory` without reaching
1226    /// the store. Fires once; several calls arm several failures, each
1227    /// counted from now.
1228    pub(crate) fn fail_patch_on(n: u32) {
1229        let at = PATCH_CALLS.with(Cell::get) + n;
1230        assert!(n > 0 && at < 64);
1231        PATCH_FAILS.with(|f| f.set(f.get() | 1 << at));
1232    }
1233
1234    /// Checked just before every patch apply or revert of a history entry.
1235    pub(crate) fn before_patch() -> Result<(), StoreError> {
1236        let fails = PATCH_FAILS.with(Cell::get);
1237        if fails == 0 {
1238            return Ok(());
1239        }
1240        let call = PATCH_CALLS.with(|c| {
1241            c.set(c.get() + 1);
1242            c.get()
1243        });
1244        if fails & 1 << call == 0 {
1245            return Ok(());
1246        }
1247        let left = fails & !(1 << call);
1248        PATCH_FAILS.with(|f| f.set(left));
1249        if left == 0 {
1250            PATCH_CALLS.with(|c| c.set(0));
1251        }
1252        Err(StoreError::OutOfMemory)
1253    }
1254
1255    thread_local! {
1256        /// Rebuilds of a BVH flagged for one (after `Corrupted`).
1257        static REBUILDS: Cell<u32> = const { Cell::new(0) };
1258    }
1259
1260    /// Counts one rebuild of a flagged BVH.
1261    pub(crate) fn count_rebuild() {
1262        REBUILDS.with(|c| c.set(c.get() + 1));
1263    }
1264
1265    /// Rebuilds of flagged BVHs on this thread since the last call.
1266    pub(crate) fn take_rebuilds() -> u32 {
1267        REBUILDS.with(|c| c.replace(0))
1268    }
1269
1270    /// Checked just before every `Bvh::update` call.
1271    pub(crate) fn before_bvh_update() -> Result<(), BvhError> {
1272        BVH_UPDATE.with(|c| match c.get() {
1273            Some(0) => {
1274                c.set(None);
1275                Err(BvhError::OutOfMemory)
1276            }
1277            Some(n) => {
1278                c.set(Some(n - 1));
1279                Ok(())
1280            }
1281            None => Ok(()),
1282        })
1283    }
1284
1285    thread_local! {
1286        /// History reservations left before the one that fails.
1287        static RESERVE: Cell<Option<u32>> = const { Cell::new(None) };
1288    }
1289
1290    /// Makes the `n`th coming history reservation (1 = the next) fail with
1291    /// `OutOfMemory`. Fires once.
1292    pub(crate) fn fail_reserve_on(n: u32) {
1293        assert!(n > 0);
1294        RESERVE.with(|c| c.set(Some(n - 1)));
1295    }
1296
1297    /// Checked just before every history reservation.
1298    pub(crate) fn before_reserve() -> Result<(), StoreError> {
1299        RESERVE.with(|c| match c.get() {
1300            Some(0) => {
1301                c.set(None);
1302                Err(StoreError::OutOfMemory)
1303            }
1304            Some(n) => {
1305                c.set(Some(n - 1));
1306                Ok(())
1307            }
1308            None => Ok(()),
1309        })
1310    }
1311
1312    thread_local! {
1313        /// `find_contacts` narrow phases left before the one that fails.
1314        static CONTACT: Cell<Option<u32>> = const { Cell::new(None) };
1315    }
1316
1317    /// Makes the `n`th coming `find_contacts` narrow phase (1 = the next)
1318    /// fail with `OutOfMemory`. Fires once.
1319    pub(crate) fn fail_contact_on(n: u32) {
1320        assert!(n > 0);
1321        CONTACT.with(|c| c.set(Some(n - 1)));
1322    }
1323
1324    /// Checked just before every `find_contacts` narrow phase.
1325    pub(crate) fn before_contact() -> Result<(), StoreError> {
1326        CONTACT.with(|c| match c.get() {
1327            Some(0) => {
1328                c.set(None);
1329                Err(StoreError::OutOfMemory)
1330            }
1331            Some(n) => {
1332                c.set(Some(n - 1));
1333                Ok(())
1334            }
1335            None => Ok(()),
1336        })
1337    }
1338}
1339
1340#[cfg(test)]
1341mod tests {
1342    use super::*;
1343
1344    fn tri() -> Mesh {
1345        Mesh::from_buffers(&[0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0], &[0, 1, 2]).unwrap()
1346    }
1347
1348    #[test]
1349    fn stale_ids_are_rejected() {
1350        let mut scene = Scene::new(SceneOptions::default());
1351        let a = scene.add_mesh(tri(), MeshOptions::default()).unwrap();
1352        assert!(scene.mesh(a).is_ok());
1353        assert!(scene.remove_mesh(a).is_ok());
1354        assert_eq!(scene.mesh(a).err(), Some(SceneError::UnknownMesh));
1355        assert_eq!(scene.placement(a).err(), Some(SceneError::UnknownMesh));
1356        assert_eq!(
1357            scene.set_placement(a, Placement::IDENTITY),
1358            Err(SceneError::UnknownMesh)
1359        );
1360        assert_eq!(scene.is_current(a).err(), Some(SceneError::UnknownMesh));
1361        assert_eq!(scene.remove_mesh(a).err(), Some(SceneError::UnknownMesh));
1362        // The slot is reused, but the old id still does not match.
1363        let b = scene.add_mesh(tri(), MeshOptions::default()).unwrap();
1364        assert_ne!(a, b);
1365        assert_eq!(scene.mesh(a).err(), Some(SceneError::UnknownMesh));
1366        assert!(scene.mesh(b).is_ok());
1367    }
1368
1369    #[test]
1370    fn mesh_id_from_parts_only_for_live_meshes() {
1371        let mut scene = Scene::new(SceneOptions::default());
1372        let a = scene.add_mesh(tri(), MeshOptions::default()).unwrap();
1373        assert_eq!(scene.mesh_id(a.index(), a.generation()), Some(a));
1374        assert_eq!(scene.mesh_id(a.index(), a.generation() + 1), None);
1375        assert_eq!(scene.mesh_id(a.index() + 1, 0), None);
1376        scene.remove_mesh(a).unwrap();
1377        assert_eq!(scene.mesh_id(a.index(), a.generation()), None);
1378        // The reused slot answers only for its new generation.
1379        let b = scene.add_mesh(tri(), MeshOptions::default()).unwrap();
1380        assert_eq!(b.index(), a.index());
1381        assert_eq!(scene.mesh_id(b.index(), b.generation()), Some(b));
1382        assert_eq!(scene.mesh_id(a.index(), a.generation()), None);
1383    }
1384
1385    #[test]
1386    fn ids_iterate_in_index_order() {
1387        let mut scene = Scene::new(SceneOptions::default());
1388        let ids: Vec<_> = (0..4)
1389            .map(|_| scene.add_mesh(tri(), MeshOptions::default()).unwrap())
1390            .collect();
1391        scene.remove_mesh(ids[1]).unwrap();
1392        assert_eq!(scene.ids().collect::<Vec<_>>(), [ids[0], ids[2], ids[3]]);
1393        let again = scene.add_mesh(tri(), MeshOptions::default()).unwrap();
1394        // Reuses index 1, so it iterates between ids[0] and ids[2].
1395        assert_eq!(
1396            scene.ids().collect::<Vec<_>>(),
1397            [ids[0], again, ids[2], ids[3]]
1398        );
1399    }
1400
1401    #[test]
1402    fn placement_round_trip_and_validation() {
1403        let mut scene = Scene::new(SceneOptions::default());
1404        let bad = Placement {
1405            translation: [f64::NAN, 0.0, 0.0],
1406            ..Placement::IDENTITY
1407        };
1408        let opts = MeshOptions {
1409            placement: bad,
1410            ..MeshOptions::default()
1411        };
1412        assert!(matches!(
1413            scene.add_mesh(tri(), opts),
1414            Err(SceneError::InvalidInput(_))
1415        ));
1416        assert_eq!(scene.ids().count(), 0);
1417        let id = scene.add_mesh(tri(), MeshOptions::default()).unwrap();
1418        let moved = Placement {
1419            translation: [1.0, 2.0, 3.0],
1420            ..Placement::IDENTITY
1421        };
1422        scene.set_placement(id, moved).unwrap();
1423        assert_eq!(scene.placement(id), Ok(moved));
1424        assert!(scene.set_placement(id, bad).is_err());
1425        assert_eq!(scene.placement(id), Ok(moved));
1426        assert_eq!(scene.is_current(id), Ok(true));
1427    }
1428
1429    #[test]
1430    fn scaled_placement_undoes_and_rejects_bad_scales() {
1431        let mut scene = Scene::new(SceneOptions::default());
1432        let id = scene.add_mesh(tri(), MeshOptions::default()).unwrap();
1433        scene.take_changes();
1434        let scaled = Placement {
1435            translation: [1.0, 2.0, 3.0],
1436            scale: [2.0, 0.5, 1.0],
1437            ..Placement::IDENTITY
1438        };
1439        scene.set_placement(id, scaled).unwrap();
1440        assert_eq!(scene.placement(id), Ok(scaled));
1441        assert!(scene.take_changes()[0].placement_changed);
1442        // Undo restores the identity scale; redo brings the scaled one back.
1443        assert_eq!(scene.undo(), Ok(true));
1444        assert_eq!(scene.placement(id), Ok(Placement::IDENTITY));
1445        assert_eq!(scene.redo(), Ok(true));
1446        assert_eq!(scene.placement(id), Ok(scaled));
1447        // A drag's consecutive scales merge into one history entry.
1448        scene.begin_step();
1449        scene
1450            .set_placement(
1451                id,
1452                Placement {
1453                    scale: [3.0, 0.5, 1.0],
1454                    ..scaled
1455                },
1456            )
1457            .unwrap();
1458        scene
1459            .set_placement(
1460                id,
1461                Placement {
1462                    scale: [4.0, 0.5, 1.0],
1463                    ..scaled
1464                },
1465            )
1466            .unwrap();
1467        scene.end_step().unwrap();
1468        assert_eq!(scene.undo(), Ok(true));
1469        assert_eq!(scene.placement(id), Ok(scaled));
1470        // Zero, negative and non-finite scales are rejected.
1471        for scale in [
1472            [0.0, 1.0, 1.0],
1473            [1.0, -1.0, 1.0],
1474            [f64::NAN, 1.0, 1.0],
1475            [1.0, f64::INFINITY, 1.0],
1476        ] {
1477            let bad = Placement {
1478                scale,
1479                ..Placement::IDENTITY
1480            };
1481            assert!(
1482                matches!(
1483                    scene.set_placement(id, bad),
1484                    Err(SceneError::InvalidInput(_))
1485                ),
1486                "scale {scale:?}"
1487            );
1488            assert_eq!(scene.placement(id), Ok(scaled));
1489        }
1490    }
1491
1492    #[test]
1493    fn mode_getter_follows_set_mode() {
1494        let mut scene = Scene::new(SceneOptions::default());
1495        let id = scene.add_mesh(tri(), MeshOptions::default()).unwrap();
1496        assert_eq!(scene.mode(id), Ok(UpdateMode::Eager));
1497        scene.set_mode(id, UpdateMode::Lazy).unwrap();
1498        assert_eq!(scene.mode(id), Ok(UpdateMode::Lazy));
1499        scene.set_mode(id, UpdateMode::Manual).unwrap();
1500        assert_eq!(scene.mode(id), Ok(UpdateMode::Manual));
1501        scene.remove_mesh(id).unwrap();
1502        assert_eq!(scene.mode(id).err(), Some(SceneError::UnknownMesh));
1503    }
1504
1505    #[test]
1506    fn bounds_cover_live_triangles_only() {
1507        let mut scene = Scene::new(SceneOptions::default());
1508        let id = scene.add_mesh(tri(), MeshOptions::default()).unwrap();
1509        let b = scene.bounds(id).unwrap().unwrap();
1510        assert_eq!(b.min, [0.0, 0.0, 0.0]);
1511        assert_eq!(b.max, [1.0, 1.0, 0.0]);
1512        // A live vertex no live triangle uses does not widen them.
1513        scene
1514            .edit(id, |tx| tx.add_vertices(&[[50.0, 50.0, 50.0]]))
1515            .unwrap();
1516        let b = scene.bounds(id).unwrap().unwrap();
1517        assert_eq!((b.min, b.max), ([0.0, 0.0, 0.0], [1.0, 1.0, 0.0]));
1518        // Without live triangles there are no bounds, whatever the slots hold.
1519        scene.edit(id, |tx| tx.remove_triangles(&[0])).unwrap();
1520        assert_eq!(scene.bounds(id), Ok(None));
1521        scene.remove_mesh(id).unwrap();
1522        assert_eq!(scene.bounds(id).err(), Some(SceneError::UnknownMesh));
1523    }
1524
1525    #[test]
1526    fn lazy_and_manual_adds_defer_the_bvh_build() {
1527        let mut scene = Scene::new(SceneOptions::default());
1528        let deferred = |mode| MeshOptions {
1529            mode,
1530            defer_bvh: true,
1531            ..MeshOptions::default()
1532        };
1533        // Without the flag, and for Eager with it, the BVH builds up front.
1534        let eager = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
1535        assert_eq!(scene.is_current(eager), Ok(true));
1536        let eager_flagged = scene
1537            .add_mesh(grid(2), deferred(UpdateMode::Eager))
1538            .unwrap();
1539        assert_eq!(scene.is_current(eager_flagged), Ok(true));
1540        let lazy = scene.add_mesh(grid(4), deferred(UpdateMode::Lazy)).unwrap();
1541        // No BVH was built: not current, and the raw BVH is refused.
1542        assert_eq!(scene.mode(lazy), Ok(UpdateMode::Lazy));
1543        assert_eq!(scene.is_current(lazy), Ok(false));
1544        assert_eq!(scene.bvh(lazy).err(), Some(SceneError::StaleBvh(lazy)));
1545        // A query builds the Lazy BVH on demand.
1546        let hit = scene
1547            .raycast(
1548                [2.0, 2.0, 10.0],
1549                [0.0, 0.0, -1.0],
1550                &crate::SceneRayOptions::default(),
1551            )
1552            .unwrap();
1553        assert!(hit.is_some());
1554        assert_eq!(scene.is_current(lazy), Ok(true));
1555        assert!(scene.bvh(lazy).is_ok());
1556        // Manual waits for an explicit `update`.
1557        let manual = scene
1558            .add_mesh(grid(4), deferred(UpdateMode::Manual))
1559            .unwrap();
1560        assert_eq!(scene.is_current(manual), Ok(false));
1561        scene.update(manual).unwrap();
1562        assert_eq!(scene.is_current(manual), Ok(true));
1563        let manual2 = scene
1564            .add_mesh(grid(2), deferred(UpdateMode::Manual))
1565            .unwrap();
1566        scene.set_mode(manual2, UpdateMode::Eager).unwrap();
1567        assert_eq!(scene.is_current(manual2), Ok(true));
1568        assert_valid(&scene, lazy);
1569        assert_valid(&scene, manual);
1570        assert_valid(&scene, manual2);
1571    }
1572
1573    /// A flat `n` x `n` grid of quads, two triangles each.
1574    fn grid(n: u32) -> Mesh {
1575        let mut pos = Vec::new();
1576        for y in 0..=n {
1577            for x in 0..=n {
1578                pos.extend_from_slice(&[x as f32, y as f32, 0.0]);
1579            }
1580        }
1581        let mut idx = Vec::new();
1582        for y in 0..n {
1583            for x in 0..n {
1584                let a = y * (n + 1) + x;
1585                let b = a + 1;
1586                let c = a + n + 1;
1587                idx.extend_from_slice(&[a, b, c, b, c + 1, c]);
1588            }
1589        }
1590        Mesh::from_buffers(&pos, &idx).unwrap()
1591    }
1592
1593    fn with_mode(mode: UpdateMode) -> MeshOptions {
1594        MeshOptions {
1595            mode,
1596            ..MeshOptions::default()
1597        }
1598    }
1599
1600    fn lift(scene: &mut Scene, id: MeshId, v: u32, z: f32) {
1601        scene
1602            .edit(id, |tx| {
1603                let p = tx.mesh().position(v).unwrap();
1604                tx.set_positions(&[v], &[[p[0], p[1], z]])
1605            })
1606            .unwrap();
1607    }
1608
1609    fn assert_valid(scene: &Scene, id: MeshId) {
1610        let e = scene.entries.get(id).unwrap();
1611        e.bvh.validate(&e.mesh).unwrap();
1612    }
1613
1614    #[test]
1615    fn eager_bvh_stays_current() {
1616        let mut scene = Scene::new(SceneOptions::default());
1617        let id = scene.add_mesh(grid(4), MeshOptions::default()).unwrap();
1618        let r = scene
1619            .edit(id, |tx| {
1620                tx.set_positions(&[6], &[[1.0, 1.0, 2.0]]).map(|()| 7)
1621            })
1622            .unwrap();
1623        assert_eq!(r, 7);
1624        assert_eq!(scene.is_current(id), Ok(true));
1625        assert_valid(&scene, id);
1626        assert!(scene.entries.get(id).unwrap().pending.is_none());
1627        assert_eq!(scene.history.undo_len(), 1);
1628    }
1629
1630    #[test]
1631    fn failed_edit_changes_nothing() {
1632        let mut scene = Scene::new(SceneOptions::default());
1633        let id = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
1634        scene.take_changes();
1635        let before = scene.mesh(id).unwrap().revision();
1636        let err = scene.edit(id, |tx| {
1637            tx.set_positions(&[0], &[[5.0, 5.0, 5.0]])?;
1638            tx.remove_vertices(&[1])
1639        });
1640        assert!(matches!(err, Err(SceneError::Store(_))));
1641        assert_eq!(scene.mesh(id).unwrap().revision(), before);
1642        assert_eq!(scene.mesh(id).unwrap().position(0), Some([0.0, 0.0, 0.0]));
1643        assert_eq!(scene.history.undo_len(), 0);
1644        assert!(scene.take_changes().is_empty());
1645        assert_eq!(scene.is_current(id), Ok(true));
1646        scene.remove_mesh(id).unwrap();
1647        assert_eq!(
1648            scene.edit(id, |_| Ok::<(), StoreError>(())).err(),
1649            Some(SceneError::UnknownMesh)
1650        );
1651    }
1652
1653    #[test]
1654    fn lazy_waits_for_flush() {
1655        let mut scene = Scene::new(SceneOptions::default());
1656        let id = scene
1657            .add_mesh(grid(4), with_mode(UpdateMode::Lazy))
1658            .unwrap();
1659        lift(&mut scene, id, 6, 1.0);
1660        assert_eq!(scene.is_current(id), Ok(false));
1661        scene.flush().unwrap();
1662        assert_eq!(scene.is_current(id), Ok(true));
1663        assert_valid(&scene, id);
1664    }
1665
1666    #[test]
1667    fn manual_waits_for_update() {
1668        let mut scene = Scene::new(SceneOptions::default());
1669        let id = scene
1670            .add_mesh(grid(4), with_mode(UpdateMode::Manual))
1671            .unwrap();
1672        lift(&mut scene, id, 6, 1.0);
1673        assert_eq!(scene.is_current(id), Ok(false));
1674        scene.flush().unwrap();
1675        assert_eq!(scene.is_current(id), Ok(false));
1676        scene.update(id).unwrap();
1677        assert_eq!(scene.is_current(id), Ok(true));
1678        assert_valid(&scene, id);
1679        // Switching to Eager brings a stale BVH up to date.
1680        lift(&mut scene, id, 7, 1.0);
1681        assert_eq!(scene.is_current(id), Ok(false));
1682        scene.set_mode(id, UpdateMode::Eager).unwrap();
1683        assert_eq!(scene.is_current(id), Ok(true));
1684        scene.remove_mesh(id).unwrap();
1685        assert_eq!(
1686            scene.set_mode(id, UpdateMode::Lazy),
1687            Err(SceneError::UnknownMesh)
1688        );
1689        assert_eq!(scene.update(id), Err(SceneError::UnknownMesh));
1690    }
1691
1692    #[test]
1693    fn pending_changes_merge_across_commits() {
1694        let mut scene = Scene::new(SceneOptions::default());
1695        let id = scene
1696            .add_mesh(grid(6), with_mode(UpdateMode::Lazy))
1697            .unwrap();
1698        lift(&mut scene, id, 8, 1.5);
1699        scene
1700            .edit(id, |tx| tx.remove_triangles(&[0, 1, 5]))
1701            .unwrap();
1702        let added = scene
1703            .edit(id, |tx| {
1704                let v = tx.add_vertices(&[[3.5, 3.5, 2.0]])?;
1705                tx.add_triangles(&[[v[0], 10, 11], [v[0], 11, 18]])
1706            })
1707            .unwrap();
1708        lift(&mut scene, id, 8, -0.5);
1709        scene
1710            .edit(id, |tx| {
1711                tx.remove_triangles(&[added[1]])?;
1712                tx.set_triangles(&[7], &[[0, 1, 2]])
1713            })
1714            .unwrap();
1715        assert_eq!(scene.history.undo_len(), 5);
1716        assert_eq!(scene.is_current(id), Ok(false));
1717        scene.flush().unwrap();
1718        assert_eq!(scene.is_current(id), Ok(true));
1719        assert_valid(&scene, id);
1720        let e = scene.entries.get(id).unwrap();
1721        let fresh = Bvh::build(&e.mesh, &BuildOptions::default()).unwrap();
1722        for (lo, hi) in [
1723            ([-1.0, -1.0, -1.0], [7.0, 7.0, 3.0]),
1724            ([0.5, 0.5, -1.0], [2.5, 2.5, 3.0]),
1725            ([2.0, 2.0, 0.5], [4.0, 4.0, 2.5]),
1726            ([3.0, 0.0, -1.0], [3.2, 6.0, 0.0]),
1727        ] {
1728            let (mut a, mut b) = (Vec::new(), Vec::new());
1729            e.bvh.query_aabb(&e.mesh, lo, hi, &mut a);
1730            fresh.query_aabb(&e.mesh, lo, hi, &mut b);
1731            a.sort_unstable();
1732            b.sort_unstable();
1733            assert!(!b.is_empty());
1734            assert_eq!(a, b);
1735        }
1736    }
1737
1738    #[test]
1739    fn eager_update_failure_leaves_bvh_pending() {
1740        let mut scene = Scene::new(SceneOptions::default());
1741        let id = scene.add_mesh(grid(4), MeshOptions::default()).unwrap();
1742        fault::fail_bvh_update_on(1);
1743        lift(&mut scene, id, 6, 1.0);
1744        assert_eq!(scene.mesh(id).unwrap().position(6), Some([1.0, 1.0, 1.0]));
1745        assert_eq!(scene.history.undo_len(), 1);
1746        assert_eq!(scene.is_current(id), Ok(false));
1747        assert!(scene.entries.get(id).unwrap().pending.is_some());
1748        // A second failing edit merges into the pending changes.
1749        fault::fail_bvh_update_on(1);
1750        lift(&mut scene, id, 12, 1.0);
1751        assert_eq!(scene.is_current(id), Ok(false));
1752        scene.update(id).unwrap();
1753        assert_eq!(scene.is_current(id), Ok(true));
1754        assert!(scene.entries.get(id).unwrap().pending.is_none());
1755        assert_valid(&scene, id);
1756        // A failing explicit update reports the error and keeps the changes.
1757        fault::fail_bvh_update_on(1);
1758        lift(&mut scene, id, 7, 1.0);
1759        fault::fail_bvh_update_on(1);
1760        assert_eq!(
1761            scene.update(id),
1762            Err(SceneError::Bvh(fast_mesh_bvh::BvhError::OutOfMemory))
1763        );
1764        assert_eq!(scene.is_current(id), Ok(false));
1765        // Treated as Lazy until updated: flush picks it up.
1766        scene.flush().unwrap();
1767        assert_eq!(scene.is_current(id), Ok(true));
1768        assert_valid(&scene, id);
1769    }
1770
1771    #[test]
1772    fn take_changes_reports_and_resets() {
1773        let mut scene = Scene::new(SceneOptions::default());
1774        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
1775        let b = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
1776        let first = scene.take_changes();
1777        assert_eq!(first.len(), 2);
1778        assert_eq!(first[0].id, a);
1779        assert_eq!(first[1].id, b);
1780        assert!(first.iter().all(|c| c.added && c.changes.is_none()));
1781        assert!(scene.take_changes().is_empty());
1782
1783        lift(&mut scene, b, 4, 1.0);
1784        lift(&mut scene, b, 1, 1.0);
1785        let moved = Placement {
1786            translation: [1.0, 0.0, 0.0],
1787            ..Placement::IDENTITY
1788        };
1789        scene.set_placement(a, moved).unwrap();
1790        scene.set_placement(a, Placement::IDENTITY).unwrap();
1791        let ch = scene.take_changes();
1792        assert_eq!(ch.len(), 2);
1793        assert_eq!(ch[0].id, a);
1794        assert!(ch[0].placement_changed && !ch[0].added && !ch[0].removed);
1795        assert_eq!(ch[0].changes.as_ref().map(ChangeSet::is_empty), Some(true));
1796        assert_eq!(ch[1].id, b);
1797        assert!(!ch[1].placement_changed);
1798        assert_eq!(ch[1].changes.as_ref().unwrap().vertices_moved, [1, 4]);
1799        assert!(scene.take_changes().is_empty());
1800
1801        // An empty edit is neither recorded nor reported.
1802        let recorded = scene.history.undo_len();
1803        scene.edit(a, |_| Ok::<(), StoreError>(())).unwrap();
1804        assert_eq!(scene.history.undo_len(), recorded);
1805        assert!(scene.take_changes().is_empty());
1806
1807        // Removal is reported, with the changes made before it.
1808        lift(&mut scene, a, 0, 1.0);
1809        scene.remove_mesh(a).unwrap();
1810        let ch = scene.take_changes();
1811        assert_eq!(ch.len(), 1);
1812        assert_eq!(ch[0].id, a);
1813        assert!(ch[0].removed && !ch[0].added);
1814        assert_eq!(ch[0].changes.as_ref().unwrap().vertices_moved, [0]);
1815        assert!(scene.take_changes().is_empty());
1816    }
1817
1818    #[test]
1819    fn added_then_removed_is_not_reported() {
1820        let mut scene = Scene::new(SceneOptions::default());
1821        let keep = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
1822        scene.take_changes();
1823        let gone = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
1824        lift(&mut scene, gone, 0, 1.0);
1825        scene.remove_mesh(gone).unwrap();
1826        assert!(scene.take_changes().is_empty());
1827
1828        // Vertices added and removed again between two calls are not reported.
1829        let v = scene
1830            .edit(keep, |tx| tx.add_vertices(&[[9.0, 9.0, 9.0]]))
1831            .unwrap();
1832        scene.edit(keep, |tx| tx.remove_vertices(&v)).unwrap();
1833        let ch = scene.take_changes();
1834        assert!(ch.is_empty(), "{ch:?}");
1835    }
1836
1837    #[test]
1838    fn compact_reports_everything() {
1839        let mut scene = Scene::new(SceneOptions::default());
1840        let id = scene.add_mesh(grid(3), MeshOptions::default()).unwrap();
1841        scene.take_changes();
1842        scene.edit(id, |tx| tx.remove_triangles(&[0])).unwrap();
1843        scene.edit(id, |tx| tx.remove_vertices(&[0])).unwrap();
1844        let remap = scene.compact(id).unwrap();
1845        assert_eq!(remap.vertex(0), None);
1846        assert_eq!(remap.vertex(1), Some(0));
1847        assert_eq!(remap.triangle(0), None);
1848        assert_eq!(remap.triangle(1), Some(0));
1849        assert_eq!(scene.mesh(id).unwrap().vertex_slots(), 15);
1850        assert_eq!(scene.is_current(id), Ok(true));
1851        assert_valid(&scene, id);
1852        let ch = scene.take_changes();
1853        assert_eq!(ch.len(), 1);
1854        assert!(ch[0].changes.is_none() && !ch[0].added);
1855        assert!(scene.take_changes().is_empty());
1856        // Even a Manual mesh gets its BVH rebuilt.
1857        let m = scene
1858            .add_mesh(grid(2), with_mode(UpdateMode::Manual))
1859            .unwrap();
1860        scene.edit(m, |tx| tx.remove_triangles(&[3])).unwrap();
1861        scene.compact(m).unwrap();
1862        assert_eq!(scene.is_current(m), Ok(true));
1863        assert_valid(&scene, m);
1864    }
1865
1866    #[test]
1867    fn channel_wrappers() {
1868        let mut scene = Scene::new(SceneOptions::default());
1869        let id = scene.add_mesh(grid(1), MeshOptions::default()).unwrap();
1870        scene
1871            .add_channel(id, ChannelDesc::derived("curv", 1))
1872            .unwrap();
1873        scene.derived_channel_mut(id, "curv").unwrap()[2] = 4.0;
1874        assert_eq!(scene.mesh(id).unwrap().channel("curv").unwrap()[2], 4.0);
1875        assert!(matches!(
1876            scene.add_channel(id, ChannelDesc::derived("curv", 1)),
1877            Err(SceneError::Store(_))
1878        ));
1879        assert_eq!(scene.remove_channel(id, "curv").unwrap().name, "curv");
1880        assert!(scene.derived_channel_mut(id, "curv").is_err());
1881        assert_eq!(scene.is_current(id), Ok(true));
1882    }
1883
1884    fn z(scene: &Scene, id: MeshId, v: u32) -> f32 {
1885        scene.mesh(id).unwrap().position(v).unwrap()[2]
1886    }
1887
1888    fn moved(x: f64) -> Placement {
1889        Placement {
1890            translation: [x, 0.0, 0.0],
1891            ..Placement::IDENTITY
1892        }
1893    }
1894
1895    #[test]
1896    fn step_groups_edits_across_meshes() {
1897        let mut scene = Scene::new(SceneOptions::default());
1898        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
1899        let b = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
1900        assert!(!scene.can_undo() && !scene.can_redo());
1901        scene.begin_step();
1902        lift(&mut scene, a, 0, 1.0);
1903        lift(&mut scene, b, 1, 2.0);
1904        scene.set_placement(a, moved(3.0)).unwrap();
1905        lift(&mut scene, a, 0, 4.0);
1906        assert!(scene.can_undo());
1907        scene.end_step().unwrap();
1908        assert_eq!(scene.history.undo_len(), 1);
1909        scene.take_changes();
1910
1911        assert_eq!(scene.undo(), Ok(true));
1912        assert_eq!((z(&scene, a, 0), z(&scene, b, 1)), (0.0, 0.0));
1913        assert_eq!(scene.placement(a), Ok(Placement::IDENTITY));
1914        assert!(!scene.can_undo() && scene.can_redo());
1915        let ch = scene.take_changes();
1916        assert_eq!(ch.len(), 2);
1917        assert!(ch[0].placement_changed);
1918        assert_eq!(ch[0].changes.as_ref().unwrap().vertices_moved, [0]);
1919        assert_eq!(ch[1].changes.as_ref().unwrap().vertices_moved, [1]);
1920        assert_eq!(scene.undo(), Ok(false));
1921
1922        assert_eq!(scene.redo(), Ok(true));
1923        assert_eq!((z(&scene, a, 0), z(&scene, b, 1)), (4.0, 2.0));
1924        assert_eq!(scene.placement(a), Ok(moved(3.0)));
1925        assert!(scene.can_undo() && !scene.can_redo());
1926        assert_eq!(scene.redo(), Ok(false));
1927        assert_valid(&scene, a);
1928        assert_valid(&scene, b);
1929    }
1930
1931    #[test]
1932    fn implicit_steps_per_operation() {
1933        let mut scene = Scene::new(SceneOptions::default());
1934        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
1935        let b = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
1936        lift(&mut scene, a, 0, 1.0);
1937        lift(&mut scene, b, 0, 1.0);
1938        scene.set_placement(a, moved(1.0)).unwrap();
1939        // Empty edits, unchanged placements and empty steps record nothing.
1940        scene.edit(a, |_| Ok::<(), StoreError>(())).unwrap();
1941        scene.set_placement(a, moved(1.0)).unwrap();
1942        scene.begin_step();
1943        scene.end_step().unwrap();
1944        assert_eq!(scene.history.undo_len(), 3);
1945        scene.take_changes();
1946
1947        assert_eq!(scene.undo(), Ok(true));
1948        assert_eq!(scene.placement(a), Ok(Placement::IDENTITY));
1949        assert_eq!((z(&scene, a, 0), z(&scene, b, 0)), (1.0, 1.0));
1950        let ch = scene.take_changes();
1951        assert_eq!(ch.len(), 1);
1952        assert!(ch[0].placement_changed);
1953        assert_eq!(ch[0].changes.as_ref().map(ChangeSet::is_empty), Some(true));
1954        assert_eq!(scene.undo(), Ok(true));
1955        assert_eq!((z(&scene, a, 0), z(&scene, b, 0)), (1.0, 0.0));
1956        assert_eq!(scene.undo(), Ok(true));
1957        assert_eq!(z(&scene, a, 0), 0.0);
1958        assert_eq!(scene.undo(), Ok(false));
1959
1960        // Nested steps: only the outermost end closes the step.
1961        scene.begin_step();
1962        scene.begin_step();
1963        lift(&mut scene, a, 1, 1.0);
1964        scene.end_step().unwrap();
1965        lift(&mut scene, b, 1, 1.0);
1966        scene.end_step().unwrap();
1967        assert_eq!(scene.end_step(), Err(SceneError::StepNotOpen));
1968        assert_eq!(scene.history.undo_len(), 1);
1969        assert_eq!(scene.undo(), Ok(true));
1970        assert_eq!((z(&scene, a, 1), z(&scene, b, 1)), (0.0, 0.0));
1971    }
1972
1973    #[test]
1974    fn undo_closes_open_step() {
1975        let mut scene = Scene::new(SceneOptions::default());
1976        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
1977        lift(&mut scene, a, 4, 9.0);
1978        scene.begin_step();
1979        lift(&mut scene, a, 0, 1.0);
1980        lift(&mut scene, a, 0, 2.0);
1981        assert_eq!(scene.undo(), Ok(true));
1982        assert_eq!((z(&scene, a, 0), z(&scene, a, 4)), (0.0, 9.0));
1983        assert_eq!(scene.end_step(), Err(SceneError::StepNotOpen));
1984        assert_eq!(scene.redo(), Ok(true));
1985        assert_eq!(z(&scene, a, 0), 2.0);
1986        // redo and clear_history close an open step too.
1987        scene.begin_step();
1988        assert_eq!(scene.redo(), Ok(false));
1989        assert_eq!(scene.end_step(), Err(SceneError::StepNotOpen));
1990        scene.begin_step();
1991        lift(&mut scene, a, 1, 1.0);
1992        scene.clear_history().unwrap();
1993        assert_eq!(scene.end_step(), Err(SceneError::StepNotOpen));
1994        assert!(!scene.can_undo() && !scene.can_redo());
1995        assert_eq!(z(&scene, a, 1), 1.0);
1996    }
1997
1998    #[test]
1999    fn cancel_step_reverts_open_entries() {
2000        let mut scene = Scene::new(SceneOptions::default());
2001        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2002        lift(&mut scene, a, 4, 9.0);
2003        assert_eq!(scene.cancel_step(), Err(SceneError::StepNotOpen));
2004        scene.take_changes();
2005        scene.begin_step();
2006        scene.begin_step();
2007        lift(&mut scene, a, 0, 1.0);
2008        scene.set_placement(a, moved(2.0)).unwrap();
2009        let v = scene
2010            .edit(a, |tx| tx.add_vertices(&[[5.0, 5.0, 5.0]]))
2011            .unwrap();
2012        scene.cancel_step().unwrap();
2013        // Cancel discards the whole step, however deeply nested.
2014        assert_eq!(scene.end_step(), Err(SceneError::StepNotOpen));
2015        assert_eq!(scene.cancel_step(), Err(SceneError::StepNotOpen));
2016        assert_eq!((z(&scene, a, 0), z(&scene, a, 4)), (0.0, 9.0));
2017        assert_eq!(scene.placement(a), Ok(Placement::IDENTITY));
2018        assert!(!scene.mesh(a).unwrap().is_vertex_alive(v[0]));
2019        assert_eq!(scene.history.undo_len(), 1);
2020        assert!(!scene.can_redo());
2021        assert_valid(&scene, a);
2022        let ch = scene.take_changes();
2023        assert_eq!(ch.len(), 1);
2024        assert!(ch[0].placement_changed);
2025        // The cancelled vertex's slot was released, so it is reused.
2026        let again = scene
2027            .edit(a, |tx| tx.add_vertices(&[[6.0, 6.0, 6.0]]))
2028            .unwrap();
2029        assert_eq!(again, v);
2030    }
2031
2032    #[test]
2033    fn new_step_clears_redo() {
2034        let mut scene = Scene::new(SceneOptions::default());
2035        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2036        let b = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2037        let v = scene
2038            .edit(a, |tx| tx.add_vertices(&[[5.0, 5.0, 5.0]]))
2039            .unwrap();
2040        lift(&mut scene, a, 1, 1.0);
2041        scene.undo().unwrap();
2042        scene.undo().unwrap();
2043        assert!(scene.can_redo());
2044        // An empty step keeps the redo stack.
2045        scene.begin_step();
2046        scene.end_step().unwrap();
2047        assert!(scene.can_redo());
2048        // A recorded entry, even on another mesh, drops it.
2049        lift(&mut scene, b, 0, 1.0);
2050        assert!(!scene.can_redo());
2051        assert_eq!(scene.redo(), Ok(false));
2052        assert_eq!(z(&scene, a, 1), 0.0);
2053        // The dropped redo released the slot its add had created.
2054        let again = scene
2055            .edit(a, |tx| tx.add_vertices(&[[6.0, 6.0, 6.0]]))
2056            .unwrap();
2057        assert_eq!(again, v);
2058        // A placement change drops the redo stack too.
2059        scene.undo().unwrap();
2060        assert!(scene.can_redo());
2061        scene.set_placement(b, moved(1.0)).unwrap();
2062        assert!(!scene.can_redo());
2063    }
2064
2065    #[test]
2066    fn step_undo_is_atomic() {
2067        let mut scene = Scene::new(SceneOptions::default());
2068        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2069        let b = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2070        scene.begin_step();
2071        lift(&mut scene, a, 0, 1.0);
2072        scene.set_placement(b, moved(1.0)).unwrap();
2073        lift(&mut scene, b, 1, 2.0);
2074        scene.end_step().unwrap();
2075        scene.take_changes();
2076        let revs = |s: &Scene| (s.mesh(a).unwrap().revision(), s.mesh(b).unwrap().revision());
2077        let before = revs(&scene);
2078        let oom = Err(SceneError::Store(StoreError::OutOfMemory));
2079
2080        // Undo reverts b first, then a: the second mesh's revert fails, so
2081        // b's revert is re-applied.
2082        fault::fail_patch_on(2);
2083        assert_eq!(scene.undo(), oom);
2084        assert_eq!(revs(&scene), before);
2085        assert_eq!((z(&scene, a, 0), z(&scene, b, 1)), (1.0, 2.0));
2086        assert_eq!(scene.placement(b), Ok(moved(1.0)));
2087        assert!(scene.can_undo() && !scene.can_redo());
2088        assert_eq!(scene.history.undo_len(), 1);
2089        assert!(scene.take_changes().is_empty());
2090        assert_eq!(
2091            (scene.is_current(a), scene.is_current(b)),
2092            (Ok(true), Ok(true))
2093        );
2094        assert_valid(&scene, a);
2095        assert_valid(&scene, b);
2096
2097        // A failure on the very first entry changes nothing either.
2098        fault::fail_patch_on(1);
2099        assert_eq!(scene.undo(), oom);
2100        assert_eq!(revs(&scene), before);
2101
2102        assert_eq!(scene.undo(), Ok(true));
2103        assert_eq!((z(&scene, a, 0), z(&scene, b, 1)), (0.0, 0.0));
2104        let undone = revs(&scene);
2105        scene.take_changes();
2106
2107        // Redo applies a, then b: b's apply fails, so a is reverted again.
2108        fault::fail_patch_on(2);
2109        assert_eq!(scene.redo(), oom);
2110        assert_eq!(revs(&scene), undone);
2111        assert_eq!((z(&scene, a, 0), z(&scene, b, 1)), (0.0, 0.0));
2112        assert_eq!(scene.placement(b), Ok(Placement::IDENTITY));
2113        assert!(!scene.can_undo() && scene.can_redo());
2114        assert!(scene.take_changes().is_empty());
2115        assert_eq!(scene.redo(), Ok(true));
2116        assert_eq!((z(&scene, a, 0), z(&scene, b, 1)), (1.0, 2.0));
2117
2118        // Cancelling an open step is all-or-nothing as well.
2119        scene.begin_step();
2120        lift(&mut scene, a, 2, 1.0);
2121        lift(&mut scene, b, 2, 1.0);
2122        fault::fail_patch_on(2);
2123        assert_eq!(scene.cancel_step(), oom.map(|_| ()));
2124        assert_eq!((z(&scene, a, 2), z(&scene, b, 2)), (1.0, 1.0));
2125        scene.cancel_step().unwrap();
2126        assert_eq!((z(&scene, a, 2), z(&scene, b, 2)), (0.0, 0.0));
2127    }
2128
2129    #[test]
2130    fn failed_recovery_reports_corrupted() {
2131        let mut scene = Scene::new(SceneOptions::default());
2132        let a = scene
2133            .add_mesh(grid(2), with_mode(UpdateMode::Lazy))
2134            .unwrap();
2135        let b = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2136        let c = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2137        scene.begin_step();
2138        lift(&mut scene, a, 0, 1.0);
2139        scene.set_placement(c, moved(1.0)).unwrap();
2140        lift(&mut scene, b, 1, 2.0);
2141        scene.end_step().unwrap();
2142        scene.flush().unwrap();
2143        scene.take_changes();
2144        let rev_a = scene.mesh(a).unwrap().revision();
2145        // Undo order: b, c, a. Call 2 (a's revert) fails, then call 3 (b's
2146        // recovery apply) too, so b stays reverted and a unchanged.
2147        fault::fail_patch_on(2);
2148        fault::fail_patch_on(3);
2149        assert_eq!(scene.undo(), Err(SceneError::Corrupted));
2150        assert_eq!(scene.mesh(a).unwrap().revision(), rev_a);
2151        assert_eq!((z(&scene, a, 0), z(&scene, b, 1)), (1.0, 0.0));
2152
2153        // Every mesh with an edit in the step is reported fully changed; the
2154        // placement-only mesh is reported as moved, since its placement may
2155        // be left at either value (here recovery put it back).
2156        let ch = scene.take_changes();
2157        assert_eq!(ch.iter().map(|c| c.id).collect::<Vec<_>>(), [a, b, c]);
2158        assert!(ch[..2].iter().all(|c| c.changes.is_none()));
2159        assert!(ch[2].placement_changed);
2160        assert_eq!(ch[2].changes.as_ref().map(ChangeSet::is_empty), Some(true));
2161        assert_eq!(scene.placement(c), Ok(moved(1.0)));
2162        // b (Eager) was rebuilt right away. a (Lazy) waits for a rebuild,
2163        // even though its revision still matches its BVH.
2164        assert_eq!(scene.is_current(b), Ok(true));
2165        assert_valid(&scene, b);
2166        assert_eq!(scene.is_current(a), Ok(false));
2167        scene.flush().unwrap();
2168        assert_eq!(scene.is_current(a), Ok(true));
2169        assert_valid(&scene, a);
2170        assert!(!scene.entries.get(a).unwrap().rebuild);
2171
2172        // The caller recovers with clear_history; editing goes on normally.
2173        scene.clear_history().unwrap();
2174        assert!(!scene.can_undo() && !scene.can_redo());
2175        lift(&mut scene, b, 2, 1.0);
2176        assert_eq!(scene.undo(), Ok(true));
2177        assert_eq!(z(&scene, b, 2), 0.0);
2178        assert_valid(&scene, b);
2179    }
2180
2181    /// A step that fails with `Corrupted` is dropped from its stack, so the
2182    /// same failure does not repeat; editing goes on without a
2183    /// `clear_history`.
2184    #[test]
2185    fn corrupted_step_is_dropped() {
2186        let mut scene = Scene::new(SceneOptions::default());
2187        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2188        scene.begin_step();
2189        lift(&mut scene, a, 0, 1.0);
2190        lift(&mut scene, a, 1, 2.0);
2191        scene.end_step().unwrap();
2192        scene.take_changes();
2193        // Undo order: vertex 1, then vertex 0. Call 2 (vertex 0's revert)
2194        // fails, then call 3 (vertex 1's recovery apply) too.
2195        fault::fail_patch_on(2);
2196        fault::fail_patch_on(3);
2197        assert_eq!(scene.undo(), Err(SceneError::Corrupted));
2198        // The mesh is still reported fully changed, but the failed step is
2199        // gone: nothing is left to undo, and its bytes are released.
2200        let ch = scene.take_changes();
2201        assert_eq!(ch.len(), 1);
2202        assert!(ch[0].changes.is_none());
2203        assert!(!scene.can_undo() && !scene.can_redo());
2204        assert_eq!(scene.history.bytes(), 0);
2205        // The failed step's meshes dropped their cached world boxes; the
2206        // next query repopulates them.
2207        assert!(scene.entries.get(a).unwrap().wbox.is_none());
2208        scene
2209            .raycast(
2210                [0.0, 0.0, 5.0],
2211                [0.0, 0.0, -1.0],
2212                &crate::SceneRayOptions::default(),
2213            )
2214            .unwrap();
2215        assert!(scene.entries.get(a).unwrap().wbox.is_some());
2216        lift(&mut scene, a, 2, 1.0);
2217        assert_eq!(scene.undo(), Ok(true));
2218        assert_eq!(z(&scene, a, 2), 0.0);
2219    }
2220
2221    /// A redo that fails with `Corrupted` drops its step from the redo stack.
2222    #[test]
2223    fn corrupted_redo_step_is_dropped() {
2224        let mut scene = Scene::new(SceneOptions::default());
2225        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2226        scene.begin_step();
2227        lift(&mut scene, a, 0, 1.0);
2228        lift(&mut scene, a, 1, 2.0);
2229        scene.end_step().unwrap();
2230        assert_eq!(scene.undo(), Ok(true));
2231        assert_eq!((z(&scene, a, 0), z(&scene, a, 1)), (0.0, 0.0));
2232        scene.take_changes();
2233        // Redo order: vertex 0, then vertex 1. Call 2 (vertex 1's apply)
2234        // fails, then call 3 (vertex 0's recovery revert) too.
2235        fault::fail_patch_on(2);
2236        fault::fail_patch_on(3);
2237        assert_eq!(scene.redo(), Err(SceneError::Corrupted));
2238        let ch = scene.take_changes();
2239        assert_eq!(ch.len(), 1);
2240        assert!(ch[0].changes.is_none());
2241        assert!(!scene.can_undo() && !scene.can_redo());
2242        assert_eq!(scene.history.bytes(), 0);
2243        lift(&mut scene, a, 2, 1.0);
2244        assert_eq!(scene.undo(), Ok(true));
2245        assert_eq!(z(&scene, a, 2), 0.0);
2246    }
2247
2248    /// Cancelling an open step that fails with `Corrupted` discards it.
2249    #[test]
2250    fn corrupted_cancel_discards_the_open_step() {
2251        let mut scene = Scene::new(SceneOptions::default());
2252        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2253        scene.begin_step();
2254        lift(&mut scene, a, 0, 1.0);
2255        lift(&mut scene, a, 1, 2.0);
2256        scene.take_changes();
2257        fault::fail_patch_on(2);
2258        fault::fail_patch_on(3);
2259        assert_eq!(scene.cancel_step(), Err(SceneError::Corrupted));
2260        let ch = scene.take_changes();
2261        assert_eq!(ch.len(), 1);
2262        assert!(ch[0].changes.is_none());
2263        // The open step is gone, not left open.
2264        assert_eq!(scene.end_step(), Err(SceneError::StepNotOpen));
2265        assert!(!scene.can_undo() && !scene.can_redo());
2266        assert_eq!(scene.history.bytes(), 0);
2267        lift(&mut scene, a, 2, 1.0);
2268        assert_eq!(scene.undo(), Ok(true));
2269    }
2270
2271    /// Undoing an open step that fails with `Corrupted` discards it.
2272    #[test]
2273    fn corrupted_undo_of_open_step_discards_it() {
2274        let mut scene = Scene::new(SceneOptions::default());
2275        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2276        scene.begin_step();
2277        lift(&mut scene, a, 0, 1.0);
2278        lift(&mut scene, a, 1, 2.0);
2279        scene.take_changes();
2280        fault::fail_patch_on(2);
2281        fault::fail_patch_on(3);
2282        assert_eq!(scene.undo(), Err(SceneError::Corrupted));
2283        let ch = scene.take_changes();
2284        assert_eq!(ch.len(), 1);
2285        assert!(ch[0].changes.is_none());
2286        assert_eq!(scene.end_step(), Err(SceneError::StepNotOpen));
2287        assert!(!scene.can_undo() && !scene.can_redo());
2288        assert_eq!(scene.history.bytes(), 0);
2289        lift(&mut scene, a, 2, 1.0);
2290        assert_eq!(scene.undo(), Ok(true));
2291    }
2292
2293    /// A dropped `Corrupted` step releases the slots of the patches whose
2294    /// end state the failed run determines, and keeps the rest reserved.
2295    #[test]
2296    fn corrupted_step_releases_only_known_slots() {
2297        let mut scene = Scene::new(SceneOptions::default());
2298        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2299        scene.begin_step();
2300        // A removes triangle 0; B and C remove triangles 1 and 2; D adds a
2301        // vertex (slot 9: the grid's slots are all taken).
2302        scene.edit(a, |tx| tx.remove_triangles(&[0])).unwrap();
2303        scene.edit(a, |tx| tx.remove_triangles(&[1])).unwrap();
2304        scene.edit(a, |tx| tx.remove_triangles(&[2])).unwrap();
2305        let v = scene
2306            .edit(a, |tx| tx.add_vertices(&[[9.0, 9.0, 9.0]]))
2307            .unwrap()[0];
2308        assert_eq!(v, 9);
2309        scene.end_step().unwrap();
2310        scene.take_changes();
2311        // Undo order: D, C, B, A. Call 3 (B's revert) fails, then call 4
2312        // (C's recovery apply) too: D is reverted and stays so, C and B
2313        // are left unknown, and A never ran.
2314        fault::fail_patch_on(3);
2315        fault::fail_patch_on(4);
2316        assert_eq!(scene.undo(), Err(SceneError::Corrupted));
2317        assert!(!scene.can_undo());
2318        let mesh = scene.mesh(a).unwrap();
2319        assert!(mesh.free_vertex_slots().contains(&9));
2320        assert!(mesh.free_triangle_slots().contains(&0));
2321        assert!(!mesh.free_triangle_slots().contains(&1));
2322        assert!(!mesh.free_triangle_slots().contains(&2));
2323    }
2324
2325    /// A failed recovery rebuilds each touched Eager BVH once, however many
2326    /// entries its mesh has in the step, and reports a placement the
2327    /// recovery never got back to.
2328    #[test]
2329    fn corrupted_step_rebuilds_once_and_reports_placements() {
2330        let mut scene = Scene::new(SceneOptions::default());
2331        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2332        let b = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2333        let c = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2334        scene.begin_step();
2335        for k in 0..3 {
2336            lift(&mut scene, a, k, 1.0);
2337            lift(&mut scene, b, k, 1.0);
2338        }
2339        scene.set_placement(c, moved(1.0)).unwrap();
2340        scene.end_step().unwrap();
2341        scene.take_changes();
2342        fault::take_rebuilds();
2343        // Undo order: c's placement, then b (call 1), then a (call 2, fails).
2344        // Recovery re-applies b (call 3, fails), so c stays reverted.
2345        fault::fail_patch_on(2);
2346        fault::fail_patch_on(3);
2347        assert_eq!(scene.undo(), Err(SceneError::Corrupted));
2348        assert_eq!(scene.placement(c), Ok(Placement::IDENTITY));
2349        // Six edit entries on two Eager meshes: two rebuilds, not six.
2350        assert_eq!(fault::take_rebuilds(), 2);
2351        for id in [a, b] {
2352            assert_eq!(scene.is_current(id), Ok(true));
2353            assert_valid(&scene, id);
2354        }
2355        let ch = scene.take_changes();
2356        assert_eq!(ch.iter().map(|c| c.id).collect::<Vec<_>>(), [a, b, c]);
2357        assert!(ch[..2].iter().all(|c| c.changes.is_none()));
2358        assert!(ch[2].placement_changed);
2359        assert_eq!(ch[2].changes.as_ref().map(ChangeSet::is_empty), Some(true));
2360    }
2361
2362    /// Ctrl+Z on an open step larger than the whole budget undoes it; only
2363    /// then is it dropped from the redo stack to meet the budget.
2364    #[test]
2365    fn undo_of_over_budget_open_step_still_undoes() {
2366        let mut scene = Scene::new(SceneOptions {
2367            history_budget: 1000,
2368        });
2369        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2370        let b = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2371        // The reported repro: 20 placements of one mesh in an open step.
2372        scene.begin_step();
2373        for k in 1..=20 {
2374            scene.set_placement(a, moved(f64::from(k))).unwrap();
2375        }
2376        assert!(scene.can_undo());
2377        assert_eq!(scene.undo(), Ok(true));
2378        assert_eq!(scene.placement(a), Ok(Placement::IDENTITY));
2379        assert_eq!(scene.end_step(), Err(SceneError::StepNotOpen));
2380
2381        // Entries on alternating meshes: 20 x 192 bytes, far over budget.
2382        lift(&mut scene, a, 4, 3.0);
2383        scene.begin_step();
2384        for k in 1..=10 {
2385            scene.set_placement(a, moved(f64::from(k))).unwrap();
2386            scene.set_placement(b, moved(f64::from(-k))).unwrap();
2387        }
2388        assert!(scene.history.bytes() > 1000);
2389        assert!(scene.can_undo());
2390        scene.take_changes();
2391        assert_eq!(scene.undo(), Ok(true));
2392        assert_eq!(scene.placement(a), Ok(Placement::IDENTITY));
2393        assert_eq!(scene.placement(b), Ok(Placement::IDENTITY));
2394        assert_eq!(scene.end_step(), Err(SceneError::StepNotOpen));
2395        let ch = scene.take_changes();
2396        assert_eq!(ch.len(), 2);
2397        assert!(ch.iter().all(|c| c.placement_changed));
2398        // Recording evicted the older edit step; meeting the budget after the
2399        // undo dropped the undone step from the redo stack.
2400        assert!(!scene.can_undo() && !scene.can_redo());
2401        assert_eq!(scene.history.bytes(), 0);
2402        assert_eq!(z(&scene, a, 4), 3.0);
2403
2404        // An open step within budget stays redoable after the undo.
2405        scene.begin_step();
2406        scene.set_placement(b, moved(2.0)).unwrap();
2407        assert_eq!(scene.undo(), Ok(true));
2408        assert!(scene.can_redo());
2409        assert_eq!(scene.redo(), Ok(true));
2410        assert_eq!(scene.placement(b), Ok(moved(2.0)));
2411    }
2412
2413    /// Setting the current placement again is neither recorded nor
2414    /// reported.
2415    #[test]
2416    fn unchanged_placement_is_not_reported() {
2417        let mut scene = Scene::new(SceneOptions::default());
2418        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2419        scene.take_changes();
2420        scene.set_placement(a, Placement::IDENTITY).unwrap();
2421        assert!(scene.take_changes().is_empty());
2422        assert!(!scene.can_undo());
2423        scene.set_placement(a, moved(1.0)).unwrap();
2424        scene.take_changes();
2425        scene.set_placement(a, moved(1.0)).unwrap();
2426        assert!(scene.take_changes().is_empty());
2427        assert_eq!(scene.history.undo_len(), 1);
2428    }
2429
2430    /// A drag inside an open step records one placement entry per run.
2431    #[test]
2432    fn placement_runs_coalesce_in_open_step() {
2433        let mut scene = Scene::new(SceneOptions::default());
2434        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2435        let b = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2436        let bytes = crate::history::PLACEMENT_BYTES;
2437
2438        // 300 frames of a drag: one entry, one entry's bytes.
2439        scene.begin_step();
2440        for k in 1..=300 {
2441            scene.set_placement(a, moved(f64::from(k))).unwrap();
2442        }
2443        assert_eq!(scene.history.bytes(), bytes);
2444        scene.end_step().unwrap();
2445        assert_eq!(step_bytes(&scene), [bytes]);
2446        assert_eq!(scene.undo(), Ok(true));
2447        assert_eq!(scene.placement(a), Ok(Placement::IDENTITY));
2448        assert_eq!(scene.redo(), Ok(true));
2449        assert_eq!(scene.placement(a), Ok(moved(300.0)));
2450
2451        // An edit or another mesh's placement between them breaks the run.
2452        scene.begin_step();
2453        scene.set_placement(a, moved(1.0)).unwrap();
2454        scene.set_placement(a, moved(2.0)).unwrap();
2455        lift(&mut scene, a, 0, 1.0);
2456        scene.set_placement(a, moved(3.0)).unwrap();
2457        scene.set_placement(b, moved(4.0)).unwrap();
2458        scene.set_placement(a, moved(5.0)).unwrap();
2459        scene.set_placement(a, moved(6.0)).unwrap();
2460        scene.end_step().unwrap();
2461        let last = *step_bytes(&scene).last().unwrap();
2462        let edit = last - 4 * bytes;
2463        assert!(edit > 0 && edit != bytes, "{last}");
2464        // Undo goes back through every run to the placement before the step.
2465        assert_eq!(scene.undo(), Ok(true));
2466        assert_eq!(scene.placement(a), Ok(moved(300.0)));
2467        assert_eq!(scene.placement(b), Ok(Placement::IDENTITY));
2468        assert_eq!(z(&scene, a, 0), 0.0);
2469        assert_eq!(scene.redo(), Ok(true));
2470        assert_eq!(scene.placement(a), Ok(moved(6.0)));
2471        assert_eq!(scene.placement(b), Ok(moved(4.0)));
2472
2473        // Without an open step every placement is a step of its own.
2474        let before = scene.history.undo_len();
2475        scene.set_placement(a, moved(7.0)).unwrap();
2476        scene.set_placement(a, moved(8.0)).unwrap();
2477        assert_eq!(scene.history.undo_len(), before + 2);
2478
2479        // Cancelling a coalesced drag puts the placement back.
2480        scene.begin_step();
2481        for k in 0..10 {
2482            scene.set_placement(b, moved(f64::from(k) + 0.5)).unwrap();
2483        }
2484        scene.cancel_step().unwrap();
2485        assert_eq!(scene.placement(b), Ok(moved(4.0)));
2486
2487        // A drag back to where it started still leaves one entry, whose
2488        // undo is reported as a placement change.
2489        scene.begin_step();
2490        scene.set_placement(b, moved(9.0)).unwrap();
2491        scene.set_placement(b, moved(4.0)).unwrap();
2492        scene.end_step().unwrap();
2493        scene.take_changes();
2494        assert_eq!(scene.undo(), Ok(true));
2495        assert_eq!(scene.placement(b), Ok(moved(4.0)));
2496        let ch = scene.take_changes();
2497        assert_eq!(ch.len(), 1);
2498        assert!(ch[0].placement_changed);
2499    }
2500
2501    /// A failed undo or redo leaves the open step open and unchanged.
2502    #[test]
2503    fn failed_undo_or_redo_keeps_open_step() {
2504        let mut scene = Scene::new(SceneOptions::default());
2505        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2506        let oom = Err(SceneError::Store(StoreError::OutOfMemory));
2507
2508        // A non-empty open step: its own undo fails.
2509        lift(&mut scene, a, 0, 1.0);
2510        scene.begin_step();
2511        lift(&mut scene, a, 1, 1.0);
2512        fault::fail_patch_on(1);
2513        assert_eq!(scene.undo(), oom);
2514        assert_eq!((z(&scene, a, 0), z(&scene, a, 1)), (1.0, 1.0));
2515        lift(&mut scene, a, 2, 1.0);
2516        assert_eq!(scene.end_step(), Ok(()));
2517        assert_eq!(scene.history.undo_len(), 2);
2518        assert_eq!(scene.undo(), Ok(true));
2519        assert_eq!((z(&scene, a, 1), z(&scene, a, 2)), (0.0, 0.0));
2520
2521        // An empty open step: the undo of the latest closed step fails.
2522        scene.begin_step();
2523        fault::fail_patch_on(1);
2524        assert_eq!(scene.undo(), oom);
2525        assert_eq!(scene.end_step(), Ok(()));
2526        assert_eq!(z(&scene, a, 0), 1.0);
2527
2528        // An empty open step: the redo fails.
2529        scene.begin_step();
2530        fault::fail_patch_on(1);
2531        assert_eq!(scene.redo(), oom);
2532        assert_eq!(scene.end_step(), Ok(()));
2533        assert_eq!((z(&scene, a, 1), z(&scene, a, 2)), (0.0, 0.0));
2534        assert_eq!(scene.redo(), Ok(true));
2535        assert_eq!((z(&scene, a, 1), z(&scene, a, 2)), (1.0, 1.0));
2536    }
2537
2538    /// A failed history reservation leaves an edit or placement change undone.
2539    #[test]
2540    fn failed_reservation_changes_nothing() {
2541        let mut scene = Scene::new(SceneOptions::default());
2542        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2543        lift(&mut scene, a, 0, 1.0);
2544        scene.take_changes();
2545        let oom: Result<()> = Err(SceneError::Store(StoreError::OutOfMemory));
2546
2547        // The edit fails before its transaction starts: the mesh, the
2548        // reports and the history are unchanged.
2549        fault::fail_reserve_on(1);
2550        assert_eq!(
2551            scene.edit(a, |tx| tx.set_positions(&[1], &[[1.0, 0.0, 2.0]])),
2552            oom
2553        );
2554        assert_eq!((z(&scene, a, 0), z(&scene, a, 1)), (1.0, 0.0));
2555        assert!(scene.take_changes().is_empty());
2556        assert_eq!(scene.history.undo_len(), 1);
2557
2558        // The placement change fails before anything is recorded either.
2559        fault::fail_reserve_on(1);
2560        assert_eq!(scene.set_placement(a, moved(1.0)), oom);
2561        assert_eq!(scene.placement(a), Ok(Placement::IDENTITY));
2562        assert!(scene.take_changes().is_empty());
2563        assert_eq!(scene.history.undo_len(), 1);
2564
2565        // The hook fires once: later edits record normally.
2566        lift(&mut scene, a, 1, 2.0);
2567        assert_eq!(z(&scene, a, 1), 2.0);
2568        assert_eq!(scene.history.undo_len(), 2);
2569        assert_eq!(scene.undo(), Ok(true));
2570        assert_eq!(z(&scene, a, 1), 0.0);
2571    }
2572
2573    /// Bytes of each undo step, oldest first.
2574    fn step_bytes(scene: &Scene) -> Vec<usize> {
2575        scene.history.undo_step_bytes().collect()
2576    }
2577
2578    #[test]
2579    fn budget_evicts_oldest_steps_and_releases_slots() {
2580        let remove_three = |scene: &mut Scene, id: MeshId| {
2581            for t in 0..3 {
2582                scene.edit(id, |tx| tx.remove_triangles(&[t])).unwrap();
2583            }
2584        };
2585        let mut probe = Scene::new(SceneOptions::default());
2586        let p = probe.add_mesh(grid(3), MeshOptions::default()).unwrap();
2587        remove_three(&mut probe, p);
2588        let sizes = step_bytes(&probe);
2589        assert_eq!(sizes.len(), 3);
2590        assert!(sizes.iter().all(|&s| s > 0));
2591
2592        // Room for the newest two steps only.
2593        let mut scene = Scene::new(SceneOptions {
2594            history_budget: sizes[1] + sizes[2],
2595        });
2596        let id = scene.add_mesh(grid(3), MeshOptions::default()).unwrap();
2597        remove_three(&mut scene, id);
2598        assert_eq!(step_bytes(&scene), sizes[1..]);
2599        assert_eq!(scene.history.bytes(), sizes[1] + sizes[2]);
2600        // The evicted step released the slot it had freed; the others not.
2601        assert_eq!(scene.mesh(id).unwrap().free_triangle_slots(), [0]);
2602        assert_eq!(scene.undo(), Ok(true));
2603        assert_eq!(scene.undo(), Ok(true));
2604        assert_eq!(scene.undo(), Ok(false));
2605        let m = scene.mesh(id).unwrap();
2606        assert!(!m.is_triangle_alive(0) && m.is_triangle_alive(1) && m.is_triangle_alive(2));
2607        // The released slot is reused by the next add.
2608        let t = scene.edit(id, |tx| tx.add_triangles(&[[0, 1, 4]])).unwrap();
2609        assert_eq!(t, [0]);
2610
2611        // With no budget, every step is evicted as soon as it closes.
2612        let mut zero = Scene::new(SceneOptions { history_budget: 0 });
2613        let id = zero.add_mesh(grid(3), MeshOptions::default()).unwrap();
2614        zero.begin_step();
2615        zero.edit(id, |tx| tx.remove_triangles(&[4])).unwrap();
2616        assert!(zero.can_undo());
2617        zero.end_step().unwrap();
2618        assert!(!zero.can_undo());
2619        assert_eq!(zero.history.bytes(), 0);
2620        assert_eq!(zero.mesh(id).unwrap().free_triangle_slots(), [4]);
2621
2622        // clear_history releases what the stacks reserved.
2623        let mut scene = Scene::new(SceneOptions::default());
2624        let id = scene.add_mesh(grid(3), MeshOptions::default()).unwrap();
2625        scene.edit(id, |tx| tx.remove_triangles(&[2])).unwrap();
2626        let v = scene
2627            .edit(id, |tx| tx.add_vertices(&[[9.0, 9.0, 9.0]]))
2628            .unwrap();
2629        scene.undo().unwrap();
2630        scene.clear_history().unwrap();
2631        assert!(!scene.can_undo() && !scene.can_redo());
2632        assert_eq!(scene.history.bytes(), 0);
2633        let m = scene.mesh(id).unwrap();
2634        assert_eq!(m.free_triangle_slots(), [2]);
2635        assert_eq!(m.free_vertex_slots(), v);
2636
2637        // A placement entry counts a constant.
2638        scene.set_placement(id, moved(1.0)).unwrap();
2639        assert_eq!(scene.history.bytes(), crate::history::PLACEMENT_BYTES);
2640    }
2641
2642    #[test]
2643    fn history_survives_remove_and_compact() {
2644        let mut scene = Scene::new(SceneOptions::default());
2645        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2646        let b = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2647        let c = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2648        scene.begin_step();
2649        lift(&mut scene, a, 0, 1.0);
2650        scene.edit(b, |tx| tx.remove_triangles(&[0])).unwrap();
2651        lift(&mut scene, c, 0, 1.0);
2652        scene.end_step().unwrap();
2653        lift(&mut scene, b, 1, 1.0);
2654        let vb = scene
2655            .edit(b, |tx| tx.add_vertices(&[[7.0, 7.0, 7.0]]))
2656            .unwrap();
2657        scene.undo().unwrap();
2658        assert_eq!(scene.history.undo_len(), 2);
2659
2660        // b leaves: its entries go, the step left empty with them, and the
2661        // slots they reserved (undo and redo side) are released.
2662        let mb = scene.remove_mesh(b).unwrap();
2663        assert_eq!(mb.free_triangle_slots(), [0]);
2664        assert_eq!(mb.free_vertex_slots(), vb);
2665        assert_eq!(scene.history.undo_len(), 1);
2666        assert!(!scene.can_redo());
2667
2668        // c compacts: its edit entries go (nothing to release), and the
2669        // step left empty with them; its placement entry stays.
2670        lift(&mut scene, c, 1, 1.0);
2671        scene.set_placement(c, moved(1.0)).unwrap();
2672        assert_eq!(scene.history.undo_len(), 3);
2673        scene.compact(c).unwrap();
2674        assert_eq!(scene.history.undo_len(), 2);
2675        assert_eq!(scene.undo(), Ok(true));
2676        assert_eq!(scene.placement(c), Ok(Placement::IDENTITY));
2677        assert_eq!(scene.undo(), Ok(true));
2678        assert_eq!(z(&scene, a, 0), 0.0);
2679        assert_eq!((z(&scene, c, 0), z(&scene, c, 1)), (1.0, 1.0));
2680        assert_eq!(scene.undo(), Ok(false));
2681        assert_eq!(scene.redo(), Ok(true));
2682        assert_eq!(z(&scene, a, 0), 1.0);
2683        assert_eq!(scene.redo(), Ok(true));
2684        assert_eq!(scene.placement(c), Ok(moved(1.0)));
2685
2686        // Removing a mesh while a step is open drops its open entries.
2687        scene.begin_step();
2688        lift(&mut scene, a, 1, 1.0);
2689        lift(&mut scene, c, 2, 1.0);
2690        scene.remove_mesh(c).unwrap();
2691        scene.end_step().unwrap();
2692        assert_eq!(scene.undo(), Ok(true));
2693        assert_eq!(z(&scene, a, 1), 0.0);
2694        assert_eq!(scene.history.undo_len(), 1);
2695        assert_valid(&scene, a);
2696    }
2697
2698    /// Undo can bring the mesh back to the revision a stale BVH was built
2699    /// for: the BVH is current while `pending` is still `Some`. A later edit
2700    /// and update must still leave a valid BVH that answers like a fresh one.
2701    #[test]
2702    fn manual_bvh_current_by_revision_with_pending_changes() {
2703        let mut scene = Scene::new(SceneOptions::default());
2704        let m = scene
2705            .add_mesh(grid(4), with_mode(UpdateMode::Manual))
2706            .unwrap();
2707        lift(&mut scene, m, 6, 1.0);
2708        assert_eq!(scene.is_current(m), Ok(false));
2709        scene.undo().unwrap();
2710        assert_eq!(scene.is_current(m), Ok(true));
2711        assert!(scene.entries.get(m).unwrap().pending.is_some());
2712
2713        lift(&mut scene, m, 7, 2.0);
2714        assert_eq!(scene.is_current(m), Ok(false));
2715        scene.update(m).unwrap();
2716        assert_eq!(scene.is_current(m), Ok(true));
2717        assert_valid(&scene, m);
2718
2719        let points = [[1.0, 1.0, 3.0], [2.5, 1.5, -1.0], [0.0, 4.0, 0.5]];
2720        let e = scene.entries.get(m).unwrap();
2721        let fresh = Bvh::build(&e.mesh, &BuildOptions::default()).unwrap();
2722        let wants = points.map(|p| {
2723            fast_mesh_query::closest_point(
2724                &e.mesh,
2725                &fresh,
2726                p,
2727                &fast_mesh_query::ClosestOptions::default(),
2728            )
2729            .unwrap()
2730            .map(|h| (h.triangle, h.distance))
2731        });
2732        for (p, want) in points.into_iter().zip(wants) {
2733            let got = scene
2734                .closest_point_on(m, p.map(f64::from), &crate::SceneClosestOptions::default())
2735                .unwrap()
2736                .map(|h| (h.hit.triangle, h.hit.distance));
2737            assert_eq!(got, want);
2738        }
2739    }
2740
2741    #[test]
2742    fn undo_updates_bvhs_per_mode() {
2743        let mut scene = Scene::new(SceneOptions::default());
2744        let e = scene.add_mesh(grid(4), MeshOptions::default()).unwrap();
2745        let l = scene
2746            .add_mesh(grid(4), with_mode(UpdateMode::Lazy))
2747            .unwrap();
2748        let m = scene
2749            .add_mesh(grid(4), with_mode(UpdateMode::Manual))
2750            .unwrap();
2751        scene.begin_step();
2752        for id in [e, l, m] {
2753            lift(&mut scene, id, 6, 1.0);
2754        }
2755        scene.end_step().unwrap();
2756        scene.flush().unwrap();
2757        scene.update(m).unwrap();
2758        scene.take_changes();
2759
2760        let check = |scene: &mut Scene| {
2761            assert_eq!(scene.is_current(e), Ok(true));
2762            assert_eq!(scene.is_current(l), Ok(false));
2763            assert_eq!(scene.is_current(m), Ok(false));
2764            let ch = scene.take_changes();
2765            assert_eq!(ch.len(), 3);
2766            assert!(
2767                ch.iter()
2768                    .all(|c| c.changes.as_ref().unwrap().vertices_moved == [6])
2769            );
2770            scene.flush().unwrap();
2771            assert_eq!(scene.is_current(l), Ok(true));
2772            assert_eq!(scene.is_current(m), Ok(false));
2773            scene.update(m).unwrap();
2774            for id in [e, l, m] {
2775                assert_valid(scene, id);
2776            }
2777        };
2778        scene.undo().unwrap();
2779        check(&mut scene);
2780        scene.redo().unwrap();
2781        check(&mut scene);
2782
2783        // A failing Eager update does not fail the undo (§15.1).
2784        fault::fail_bvh_update_on(1);
2785        assert_eq!(scene.undo(), Ok(true));
2786        assert_eq!(scene.is_current(e), Ok(false));
2787        assert!(scene.entries.get(e).unwrap().pending.is_some());
2788        scene.flush().unwrap();
2789        assert_eq!(scene.is_current(e), Ok(true));
2790        assert_valid(&scene, e);
2791    }
2792
2793    #[test]
2794    fn two_subscribers_each_get_all_changes() {
2795        let mut scene = Scene::new(SceneOptions::default());
2796        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2797        let b = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2798        // A new subscriber does not see the adds made before it.
2799        let s = scene.subscribe();
2800        assert_eq!(scene.take_changes_for(s), Ok(Vec::new()));
2801        assert_eq!(scene.take_changes().len(), 2);
2802
2803        lift(&mut scene, a, 4, 1.0);
2804        scene.set_placement(b, moved(1.0)).unwrap();
2805        let mine = scene.take_changes();
2806        assert_eq!(mine.len(), 2);
2807        // Draining the default subscriber leaves the other one's reports.
2808        lift(&mut scene, a, 1, 1.0);
2809        let theirs = scene.take_changes_for(s).unwrap();
2810        assert_eq!(theirs.len(), 2);
2811        assert_eq!(theirs[0].id, a);
2812        assert_eq!(theirs[0].changes.as_ref().unwrap().vertices_moved, [1, 4]);
2813        assert_eq!(theirs[0].colors_dirty, Some(Vec::new()));
2814        assert!(theirs[0].fields_changed.is_empty());
2815        assert!(theirs[1].placement_changed && theirs[1].id == b);
2816        let mine = scene.take_changes();
2817        assert_eq!(mine.len(), 1);
2818        assert_eq!(mine[0].changes.as_ref().unwrap().vertices_moved, [1]);
2819
2820        // Removals, compaction and new meshes reach every subscriber.
2821        let s2 = scene.subscribe();
2822        assert_ne!(s, s2);
2823        let c = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2824        scene.compact(b).unwrap();
2825        scene.remove_mesh(a).unwrap();
2826        for got in [
2827            scene.take_changes(),
2828            scene.take_changes_for(s).unwrap(),
2829            scene.take_changes_for(s2).unwrap(),
2830        ] {
2831            assert_eq!(got.len(), 3, "{got:?}");
2832            assert!(got[0].id == a && got[0].removed);
2833            assert!(got[1].id == b && got[1].changes.is_none() && !got[1].added);
2834            assert!(got[2].id == c && got[2].added);
2835        }
2836        assert!(scene.take_changes_for(s2).unwrap().is_empty());
2837    }
2838
2839    /// Whether a removal is reported depends on each subscriber's own report:
2840    /// one that saw the add gets `removed`, one that did not gets nothing.
2841    #[test]
2842    fn add_then_remove_is_per_subscriber() {
2843        let mut scene = Scene::new(SceneOptions::default());
2844        let s = scene.subscribe();
2845        let id = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2846        let added = scene.take_changes_for(s).unwrap();
2847        assert!(added[0].added && added[0].colors_dirty.is_none());
2848        lift(&mut scene, id, 0, 1.0);
2849        scene.remove_mesh(id).unwrap();
2850        let got = scene.take_changes_for(s).unwrap();
2851        assert_eq!(got.len(), 1);
2852        assert!(got[0].id == id && got[0].removed && !got[0].added);
2853        assert_eq!(got[0].changes.as_ref().unwrap().vertices_moved, [0]);
2854        assert!(scene.take_changes().is_empty());
2855    }
2856
2857    #[test]
2858    fn unsubscribed_id_rejected() {
2859        let mut scene = Scene::new(SceneOptions::default());
2860        let a = scene.add_mesh(grid(2), MeshOptions::default()).unwrap();
2861        scene.take_changes();
2862        let s = scene.subscribe();
2863        let keep = scene.subscribe();
2864        scene.unsubscribe(s).unwrap();
2865        assert!(matches!(
2866            scene.take_changes_for(s),
2867            Err(SceneError::InvalidInput(_))
2868        ));
2869        assert!(matches!(
2870            scene.unsubscribe(s),
2871            Err(SceneError::InvalidInput(_))
2872        ));
2873        // The others keep working, and a removal still cannot fail.
2874        lift(&mut scene, a, 0, 1.0);
2875        scene.remove_mesh(a).unwrap();
2876        assert_eq!(scene.take_changes_for(keep).unwrap().len(), 1);
2877        assert_eq!(scene.take_changes().len(), 1);
2878        // Ids are never reused.
2879        assert!(scene.subscribe() != s);
2880    }
2881
2882    #[test]
2883    fn channel_writes_are_reported() {
2884        let mut scene = Scene::new(SceneOptions::default());
2885        let id = scene.add_mesh(grid(1), MeshOptions::default()).unwrap();
2886        let s = scene.subscribe();
2887        scene.take_changes();
2888        scene
2889            .add_channel(id, ChannelDesc::derived("curv", 1))
2890            .unwrap();
2891        scene
2892            .add_channel(id, ChannelDesc::derived("other", 1))
2893            .unwrap();
2894        scene.derived_channel_mut(id, "curv").unwrap()[0] = 1.0;
2895        scene.derived_channel_mut(id, "curv").unwrap()[1] = 1.0;
2896        let ch = scene.take_changes();
2897        assert_eq!(ch.len(), 1);
2898        assert_eq!(ch[0].channels_written, ["curv", "other"]);
2899        assert_eq!(ch[0].changes.as_ref().map(ChangeSet::is_empty), Some(true));
2900        assert!(!ch[0].placement_changed && !ch[0].added);
2901        scene.remove_channel(id, "other").unwrap();
2902        // Failed calls report nothing.
2903        assert!(scene.derived_channel_mut(id, "nope").is_err());
2904        assert!(scene.remove_channel(id, "nope").is_err());
2905        assert_eq!(scene.take_changes()[0].channels_written, ["other"]);
2906        assert!(scene.take_changes().is_empty());
2907        assert_eq!(
2908            scene.take_changes_for(s).unwrap()[0].channels_written,
2909            ["curv", "other"]
2910        );
2911    }
2912
2913    #[test]
2914    fn field_prefix_is_reserved() {
2915        let mut scene = Scene::new(SceneOptions::default());
2916        let id = scene.add_mesh(grid(1), MeshOptions::default()).unwrap();
2917        scene.take_changes();
2918        assert!(matches!(
2919            scene.add_channel(id, ChannelDesc::derived("field:d", 1)),
2920            Err(SceneError::InvalidInput(_))
2921        ));
2922        assert!(scene.mesh(id).unwrap().channel("field:d").is_none());
2923        assert!(matches!(
2924            scene.remove_channel(id, "field:d"),
2925            Err(SceneError::InvalidInput(_))
2926        ));
2927        assert!(matches!(
2928            scene.derived_channel_mut(id, "field:d"),
2929            Err(SceneError::InvalidInput(_))
2930        ));
2931        assert!(scene.take_changes().is_empty());
2932        // Only the exact prefix is reserved.
2933        scene
2934            .add_channel(id, ChannelDesc::derived("fields", 1))
2935            .unwrap();
2936    }
2937
2938    /// A wavy `n × n` grid sheet.
2939    fn sheet(n: u32) -> Mesh {
2940        let mut positions = Vec::new();
2941        for y in 0..=n {
2942            for x in 0..=n {
2943                let (fx, fy) = (x as f32 * 0.1, y as f32 * 0.1);
2944                positions.extend([fx, fy, (fx * 3.1).sin() * (fy * 1.7).cos() * 0.2]);
2945            }
2946        }
2947        let mut indices = Vec::new();
2948        for y in 0..n {
2949            for x in 0..n {
2950                let a = y * (n + 1) + x;
2951                indices.extend([a, a + 1, a + n + 2, a, a + n + 2, a + n + 1]);
2952            }
2953        }
2954        Mesh::from_buffers(&positions, &indices).unwrap()
2955    }
2956
2957    #[test]
2958    fn mesh_built_in_steps_matches_add_mesh() {
2959        // Twins: one adds the sheet at once; the other builds it in steps
2960        // while the scene changes (another mesh moved and edited, a removal
2961        // freeing a slot before), then adds it.
2962        let opts = MeshOptions {
2963            placement: Placement {
2964                rotation: [[0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]],
2965                translation: [0.5, 0.1, -0.2],
2966                ..Placement::IDENTITY
2967            },
2968            ..MeshOptions::default()
2969        };
2970        for budget in [1, 97, 5000, usize::MAX] {
2971            let mut twins: Vec<(Scene, MeshId)> = (0..2)
2972                .map(|_| {
2973                    let mut scene = Scene::new(SceneOptions::default());
2974                    let gone = scene.add_mesh(tri(), MeshOptions::default()).unwrap();
2975                    let other = scene.add_mesh(sheet(6), MeshOptions::default()).unwrap();
2976                    scene.remove_mesh(gone).unwrap();
2977                    scene.take_changes();
2978                    (scene, other)
2979                })
2980                .collect();
2981            let mut build = MeshBuild::new(sheet(30), opts).unwrap();
2982            let mut steps = 0;
2983            while !build.step(budget).unwrap() {
2984                steps += 1;
2985                if steps == 3 {
2986                    let (scene, other) = &mut twins[1];
2987                    scene
2988                        .set_placement(
2989                            *other,
2990                            Placement {
2991                                translation: [0.0, 0.0, 0.3],
2992                                ..Placement::IDENTITY
2993                            },
2994                        )
2995                        .unwrap();
2996                    scene
2997                        .edit(*other, |tx| tx.set_positions(&[0], &[[0.0, 0.0, 0.1]]))
2998                        .unwrap();
2999                }
3000            }
3001            if steps < 3 {
3002                let (scene, other) = &mut twins[1];
3003                scene
3004                    .set_placement(
3005                        *other,
3006                        Placement {
3007                            translation: [0.0, 0.0, 0.3],
3008                            ..Placement::IDENTITY
3009                        },
3010                    )
3011                    .unwrap();
3012                scene
3013                    .edit(*other, |tx| tx.set_positions(&[0], &[[0.0, 0.0, 0.1]]))
3014                    .unwrap();
3015            }
3016            if budget == 1 {
3017                assert!(steps > 100, "{steps}");
3018            }
3019            let built = twins[1].0.add_mesh_built(build).unwrap();
3020            let (scene, other) = &mut twins[0];
3021            scene
3022                .set_placement(
3023                    *other,
3024                    Placement {
3025                        translation: [0.0, 0.0, 0.3],
3026                        ..Placement::IDENTITY
3027                    },
3028                )
3029                .unwrap();
3030            scene
3031                .edit(*other, |tx| tx.set_positions(&[0], &[[0.0, 0.0, 0.1]]))
3032                .unwrap();
3033            let added = scene.add_mesh(sheet(30), opts).unwrap();
3034            assert_eq!(added, built, "the same slot");
3035            let [(a, _), (b, _)] = &mut twins[..] else {
3036                unreachable!()
3037            };
3038            assert_eq!(a.take_changes(), b.take_changes());
3039            let (ba, bb) = (a.bvh(added).unwrap(), b.bvh(built).unwrap());
3040            assert_eq!((ba.node_count(), ba.depth()), (bb.node_count(), bb.depth()));
3041            assert_eq!(ba.root_bounds(), bb.root_bounds());
3042            bb.validate(b.mesh(built).unwrap()).unwrap();
3043            for k in 0..20 {
3044                let p = [0.1 * f64::from(k) - 0.5, 0.15 * f64::from(k), 0.3];
3045                let o = crate::SceneClosestOptions::default();
3046                assert_eq!(
3047                    a.closest_point(p, &o).unwrap(),
3048                    b.closest_point(p, &o).unwrap()
3049                );
3050            }
3051        }
3052        // An invalid placement is refused before anything is built.
3053        let bad = MeshOptions {
3054            placement: Placement {
3055                rotation: [[2.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
3056                translation: [0.0; 3],
3057                ..Placement::IDENTITY
3058            },
3059            ..MeshOptions::default()
3060        };
3061        assert!(matches!(
3062            MeshBuild::new(tri(), bad),
3063            Err(SceneError::InvalidInput(_))
3064        ));
3065    }
3066
3067    #[test]
3068    fn invalid_bvh_policy_is_refused_before_the_build() {
3069        let bad = MeshOptions {
3070            bvh_policy: UpdatePolicy {
3071                full_rebuild_fraction: 2.0,
3072                ..UpdatePolicy::default()
3073            },
3074            ..MeshOptions::default()
3075        };
3076        // `add_mesh` rejects the policy like the build would, without building.
3077        let mut scene = Scene::new(SceneOptions::default());
3078        assert!(matches!(
3079            scene.add_mesh(tri(), bad),
3080            Err(SceneError::Bvh(_))
3081        ));
3082        assert!(scene.ids().next().is_none());
3083        // So does a stepped build, before its first step.
3084        assert!(matches!(
3085            MeshBuild::new(tri(), bad),
3086            Err(SceneError::Bvh(_))
3087        ));
3088    }
3089
3090    #[test]
3091    fn mesh_built_with_normals_in_steps_matches_add_mesh_and_set_normals() {
3092        // Twins: one adds the sheet and turns its normals on; the other
3093        // computes the normals in the build's steps, outside the scene, and
3094        // adds it with them already on. Same normals bit for bit, same
3095        // reports, and turning them on again changes nothing.
3096        let bits =
3097            |n: &[[f32; 3]]| -> Vec<u32> { n.iter().flatten().map(|x| x.to_bits()).collect() };
3098        let opts = MeshOptions::default();
3099        let plain_steps = {
3100            let mut build = MeshBuild::new(sheet(30), opts).unwrap();
3101            let mut steps = 0;
3102            while !build.step(97).unwrap() {
3103                steps += 1;
3104            }
3105            steps
3106        };
3107        // `None`: the build is added without a step (the add finishes it).
3108        for budget in [Some(1), Some(97), Some(5000), Some(usize::MAX), None] {
3109            let mut a = Scene::new(SceneOptions::default());
3110            let mut b = Scene::new(SceneOptions::default());
3111            let added = a.add_mesh(sheet(30), opts).unwrap();
3112            a.set_normals(added, true).unwrap();
3113            let mut build = MeshBuild::new(sheet(30), opts).unwrap();
3114            build.set_normals(true);
3115            if let Some(budget) = budget {
3116                let mut steps = 0;
3117                while !build.step(budget).unwrap() {
3118                    steps += 1;
3119                }
3120                assert!(build.is_done());
3121                if budget == 97 {
3122                    assert!(
3123                        steps > plain_steps,
3124                        "{steps} vs {plain_steps}: normals take steps"
3125                    );
3126                }
3127            }
3128            let built = b.add_mesh_built(build).unwrap();
3129            assert_eq!(added, built);
3130            assert_eq!(a.take_changes(), b.take_changes(), "{budget:?}");
3131            assert_eq!(
3132                bits(a.normals(added).unwrap().unwrap()),
3133                bits(b.normals(built).unwrap().unwrap()),
3134                "{budget:?}"
3135            );
3136            b.set_normals(built, true).unwrap();
3137            assert!(b.take_changes().is_empty(), "already on");
3138            // They follow an edit alike (the triangle snapshot is the same).
3139            for s in [&mut a, &mut b] {
3140                s.edit(added, |tx| {
3141                    tx.remove_triangles(&[3])?;
3142                    tx.set_positions(&[40], &[[0.5, 0.5, 0.4]])
3143                })
3144                .unwrap();
3145            }
3146            assert_eq!(a.take_changes(), b.take_changes());
3147            assert_eq!(
3148                bits(a.normals(added).unwrap().unwrap()),
3149                bits(b.normals(built).unwrap().unwrap())
3150            );
3151        }
3152        // Turned off again before the add: no normals, as from `add_mesh`.
3153        let mut b = Scene::new(SceneOptions::default());
3154        let mut build = MeshBuild::new(sheet(4), opts).unwrap();
3155        build.set_normals(true);
3156        build.set_normals(false);
3157        let id = b.add_mesh_built(build).unwrap();
3158        assert_eq!(b.normals(id).unwrap(), None);
3159    }
3160}