Skip to main content

fast_mesh_scene/
report.rs

1//! Change reports: what happened to each mesh since a subscriber last took
2//! its changes.
3
4use fast_mesh_store::{ChangeSet, VertexId};
5
6use crate::MeshId;
7
8/// A consumer of change reports, from [`Scene::subscribe`](crate::Scene::subscribe).
9///
10/// Every subscriber has its own pending reports, merged the same way, and
11/// drains them with
12/// [`Scene::take_changes_for`](crate::Scene::take_changes_for). Ids are never
13/// reused within a scene.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct SubscriberId(pub(crate) u64);
16
17impl SubscriberId {
18    /// The default subscriber, behind
19    /// [`Scene::take_changes`](crate::Scene::take_changes). It always exists.
20    pub(crate) const DEFAULT: SubscriberId = SubscriberId(0);
21}
22
23/// A field registered on a mesh of the scene.
24///
25/// Opaque: field ids are handed out by the scene and only compared.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct FieldId(pub(crate) u32);
28
29impl FieldId {
30    /// The raw value, for passing the id across an FFI boundary. Turn it
31    /// back into an id with [`Scene::field_id`](crate::Scene::field_id).
32    pub fn raw(self) -> u32 {
33        self.0
34    }
35}
36
37/// A plane section of the scene, from
38/// [`Scene::add_section`](crate::Scene::add_section).
39///
40/// Opaque: section ids are handed out by the scene, never reused, and only
41/// compared.
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct SectionId(pub(crate) u32);
44
45impl SectionId {
46    /// The raw value, for passing the id across an FFI boundary. Turn it
47    /// back into an id with [`Scene::section_id`](crate::Scene::section_id).
48    pub fn raw(self) -> u32 {
49        self.0
50    }
51}
52
53/// What happened to one mesh since the subscriber's last
54/// [`Scene::take_changes_for`](crate::Scene::take_changes_for) (or
55/// [`Scene::take_changes`](crate::Scene::take_changes) for the default
56/// subscriber).
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct MeshChanges {
59    /// The mesh. For a removed mesh this id no longer matches.
60    pub id: MeshId,
61    /// The merged changes of every commit, undo and redo since the last
62    /// call. `None` means "treat everything as changed": the mesh was added
63    /// or compacted, or an undo or redo touching it failed with
64    /// [`SceneError::Corrupted`](crate::SceneError::Corrupted).
65    pub changes: Option<ChangeSet>,
66    /// True if the placement changed at any point since the last call, even
67    /// if it was changed back.
68    pub placement_changed: bool,
69    /// The mesh was added since the last call.
70    pub added: bool,
71    /// The mesh was removed since the last call.
72    pub removed: bool,
73    /// The vertices whose colours changed since the last call, ascending
74    /// without duplicates. `Some` of an empty list when no colour changed;
75    /// `None` means "every colour may have changed" (redraw them all).
76    pub colors_dirty: Option<Vec<VertexId>>,
77    /// The vertices whose normals changed since the last call, with the
78    /// same semantics as [`MeshChanges::colors_dirty`]: ascending without
79    /// duplicates, `None` for "every normal may have changed". Only
80    /// reported while normals are on (see
81    /// [`Scene::set_normals`](crate::Scene::set_normals)), apart from the
82    /// `None` of an added or fully changed mesh.
83    pub normals_dirty: Option<Vec<VertexId>>,
84    /// The fields on this mesh whose values changed since the last call,
85    /// each once.
86    pub fields_changed: Vec<FieldId>,
87    /// The sections whose result for this mesh changed since the last call
88    /// (recomputed to a different result, removed, or no longer selecting
89    /// the mesh), each once, in the order first reported.
90    pub sections_changed: Vec<SectionId>,
91    /// The channels written through
92    /// [`Scene::derived_channel_mut`](crate::Scene::derived_channel_mut),
93    /// added with [`Scene::add_channel`](crate::Scene::add_channel) or
94    /// removed with [`Scene::remove_channel`](crate::Scene::remove_channel),
95    /// or dropped or restored as a whole by an edit, its undo or redo
96    /// ([`ChangeSet::channels_toggled`]),
97    /// since the last call, each name once, in the order first reported. A
98    /// `derived_channel_mut` call counts as a write whether or not the slice
99    /// is then written.
100    pub channels_written: Vec<String>,
101}
102
103/// The pending report of a live mesh, for one subscriber.
104#[derive(Clone)]
105pub(crate) struct Report {
106    /// Merged changes; `None` means "everything".
107    pub(crate) changes: Option<ChangeSet>,
108    pub(crate) placement_changed: bool,
109    pub(crate) added: bool,
110    /// Recoloured vertices; `None` means "all".
111    pub(crate) colors_dirty: Option<Vec<VertexId>>,
112    /// Vertices with new normals; `None` means "all".
113    pub(crate) normals_dirty: Option<Vec<VertexId>>,
114    pub(crate) fields_changed: Vec<FieldId>,
115    pub(crate) sections_changed: Vec<SectionId>,
116    pub(crate) channels_written: Vec<String>,
117}
118
119impl Report {
120    /// The report of a mesh that was just added: everything is new,
121    /// colours and normals included.
122    pub(crate) fn added() -> Report {
123        Report {
124            changes: None,
125            added: true,
126            colors_dirty: None,
127            normals_dirty: None,
128            ..Report::clean()
129        }
130    }
131
132    /// A report with nothing in it.
133    pub(crate) fn clean() -> Report {
134        Report {
135            changes: Some(ChangeSet::default()),
136            placement_changed: false,
137            added: false,
138            colors_dirty: Some(Vec::new()),
139            normals_dirty: Some(Vec::new()),
140            fields_changed: Vec::new(),
141            sections_changed: Vec::new(),
142            channels_written: Vec::new(),
143        }
144    }
145
146    /// True when there is nothing to report.
147    pub(crate) fn is_clean(&self) -> bool {
148        !self.added
149            && !self.placement_changed
150            && self.changes.as_ref().is_some_and(ChangeSet::is_empty)
151            && self.colors_dirty.as_ref().is_some_and(Vec::is_empty)
152            && self.normals_dirty.as_ref().is_some_and(Vec::is_empty)
153            && self.fields_changed.is_empty()
154            && self.sections_changed.is_empty()
155            && self.channels_written.is_empty()
156    }
157
158    /// Folds the changes of a later commit into the report. A report that
159    /// already says "everything" stays that way.
160    ///
161    /// `ChangeSet::merge` cannot fail: its allocations abort the process on
162    /// exhaustion rather than return an error, so this never falls back to
163    /// `None`. That only happens on `add_mesh`, `compact` and a `Corrupted`
164    /// undo or redo.
165    pub(crate) fn merge(&mut self, later: &ChangeSet) {
166        if let Some(c) = &mut self.changes {
167            c.merge(later);
168        }
169    }
170
171    /// Notes a write to channel `name`, once per name.
172    pub(crate) fn channel_written(&mut self, name: &str) {
173        if !self.channels_written.iter().any(|n| n == name) {
174            self.channels_written.push(name.to_owned());
175        }
176    }
177
178    /// Notes a change of field `id`'s values, once per field.
179    pub(crate) fn field_changed(&mut self, id: FieldId) {
180        if !self.fields_changed.contains(&id) {
181            self.fields_changed.push(id);
182        }
183    }
184
185    /// Notes a change of section `id`'s result for the mesh, once per
186    /// section.
187    pub(crate) fn section_changed(&mut self, id: SectionId) {
188        if !self.sections_changed.contains(&id) {
189            self.sections_changed.push(id);
190        }
191    }
192
193    /// Notes recoloured vertices (`ids` ascending without duplicates), or
194    /// every vertex for `None`.
195    pub(crate) fn colors_changed(&mut self, ids: Option<&[VertexId]>) {
196        merge_dirty(&mut self.colors_dirty, ids);
197    }
198
199    /// Notes vertices with new normals (`ids` ascending without
200    /// duplicates), or every vertex for `None`.
201    pub(crate) fn normals_changed(&mut self, ids: Option<&[VertexId]>) {
202        merge_dirty(&mut self.normals_dirty, ids);
203    }
204
205    /// Turns the report into its public form and resets it to clean.
206    pub(crate) fn take(&mut self, id: MeshId, removed: bool) -> MeshChanges {
207        let r = std::mem::replace(self, Report::clean());
208        MeshChanges {
209            id,
210            changes: r.changes,
211            placement_changed: r.placement_changed,
212            added: r.added,
213            removed,
214            colors_dirty: r.colors_dirty,
215            normals_dirty: r.normals_dirty,
216            fields_changed: r.fields_changed,
217            sections_changed: r.sections_changed,
218            channels_written: r.channels_written,
219        }
220    }
221}
222
223/// Folds dirty vertex ids (ascending without duplicates, `None` for all)
224/// into a dirty list that stays ascending without duplicates. A list that
225/// already says "all" stays that way.
226fn merge_dirty(dirty: &mut Option<Vec<VertexId>>, ids: Option<&[VertexId]>) {
227    match (ids, dirty) {
228        (_, None) => {}
229        (None, dirty) => *dirty = None,
230        (Some(ids), Some(dirty)) => {
231            if dirty.is_empty() {
232                dirty.extend_from_slice(ids);
233            } else if !ids.is_empty() {
234                dirty.extend_from_slice(ids);
235                dirty.sort_unstable();
236                dirty.dedup();
237            }
238        }
239    }
240}