Skip to main content

geop_core_editor/
selectable.rs

1//! [`Selectable`]: what a reference field of a dialog holds — an element of
2//! a kind the user picks ([`SelectableElement`]), or one of several
3//! ([`one_of!`](crate::one_of)) — and how a click on what the editor draws
4//! selects one, with [`selected`], the values a reference field's picks
5//! come to.
6//!
7//! An editor hit-tests what it draws: vertices, edges, faces, sketch
8//! curves. What an operation asks for may be something else drawn as
9//! those — the tangent chain of edges an edge is part of, the solid a
10//! face bounds, the line a straight edge runs along. The crate defining
11//! the element says how a click on what is drawn selects it, and what of
12//! it to light while it is selected; the editor stays the same for all of
13//! them.
14
15use geop_core_math::vector::Vector3;
16use geop_core_program::{
17    element::{ElementReference, HasElements, Kind, PickedElement},
18    reader::Reader,
19};
20
21use crate::editing::{Picked, Pointer, Visual, hit::Scene};
22
23/// A kind of element of the target `T` a reference field picks.
24pub trait SelectableElement<T: HasElements<Self>>: Kind + Sized {
25    /// The kinds of what an editor draws that a click picks one from (see
26    /// [`crate::editing::hit::Scene::pick`]): its own kind, for an element
27    /// drawn as itself.
28    const PICKED_FROM: &'static [&'static str];
29
30    /// The element a click on `hit`, of one of [`Self::PICKED_FROM`],
31    /// selects, read through `reader`: `hit` itself, for an element drawn
32    /// as itself; none, for a hit that selects none.
33    fn select(reader: &Reader, hit: &PickedElement) -> Option<ElementReference<T, Self>>;
34
35    /// What an editor draws of `element`, to light while it is selected:
36    /// the element itself, by default.
37    fn drawn(reader: &Reader, element: &ElementReference<T, Self>) -> Vec<PickedElement> {
38        let _ = reader;
39        vec![element.picked()]
40    }
41}
42
43/// What a reference field holds, one value per pick.
44pub trait Selectable: Clone + PartialEq + 'static {
45    /// The kinds of what an editor draws that a click picks one from.
46    fn picked_from() -> Vec<&'static str>;
47
48    /// The value `picked` names as [`Selectable::picked`] does: what a
49    /// field sent back held already.
50    fn from_picked(picked: &PickedElement) -> Option<Self>;
51
52    /// The value as a pick names it: what the front end shows held.
53    fn picked(&self) -> PickedElement;
54
55    /// The value a click on `hit` selects, read through `reader`: none, for
56    /// a hit that selects none.
57    fn select(reader: &Reader, hit: &PickedElement) -> Option<Self>;
58
59    /// What an editor draws of it, to light while it is held.
60    fn drawn(&self, reader: &Reader) -> Vec<PickedElement>;
61
62    /// What an editor draws of it besides lighting what it is picked from
63    /// ([`Selectable::drawn`]), while it is held or a click would pick it:
64    /// nothing, for an element drawn as itself; a frame, as a triad along
65    /// its axes. In the world, as the part is drawn; styled by the editor
66    /// as held or as hovered.
67    fn visuals(&self, reader: &Reader) -> Vec<Visual> {
68        let _ = reader;
69        Vec::new()
70    }
71}
72
73/// An element of an instance is selected and drawn as the instance's own
74/// part selects and draws it, behind the instance's path: what the part is
75/// placed in does not discriminate where an element is from.
76impl<T: HasElements<K>, K: SelectableElement<T>> Selectable for ElementReference<T, K> {
77    fn picked_from() -> Vec<&'static str> {
78        K::PICKED_FROM.to_vec()
79    }
80
81    fn from_picked(picked: &PickedElement) -> Option<Self> {
82        ElementReference::from_picked(picked)
83    }
84
85    fn picked(&self) -> PickedElement {
86        ElementReference::picked(self)
87    }
88
89    fn select(reader: &Reader, hit: &PickedElement) -> Option<Self> {
90        let instance = hit.instance();
91        if instance.is_empty() {
92            return K::select(reader, hit);
93        }
94        let inside = reader.placed_part(instance).ok()?;
95        K::select(&inside, &hit.local()).map(|selected| selected.behind(instance))
96    }
97
98    fn drawn(&self, reader: &Reader) -> Vec<PickedElement> {
99        let instance = self.instance();
100        if instance.is_empty() {
101            return K::drawn(reader, self);
102        }
103        let Ok(inside) = reader.placed_part(instance) else {
104            return vec![self.picked()];
105        };
106        (K::drawn(&inside, &self.local()).into_iter())
107            .map(|drawn| drawn.behind(instance))
108            .collect()
109    }
110}
111
112/// The values a reference field holds after an event sent it `picks`: what
113/// it held, as it held it, and the clicks an editor added to it, each the
114/// value it selects (see [`Selectable::select`]). A click that selects a
115/// value held already takes it out again; one that selects none is
116/// dropped.
117pub fn selected<P: Selectable>(reader: &Reader, picks: &[PickedElement]) -> Vec<P> {
118    let mut held: Vec<P> = Vec::new();
119    for pick in picks {
120        if let Some(value) = P::from_picked(pick) {
121            held.push(value);
122            continue;
123        }
124        let Some(value) = P::select(reader, pick) else {
125            continue;
126        };
127        match held.iter().position(|h| *h == value) {
128            Some(at) => {
129                held.remove(at);
130            }
131            None => held.push(value),
132        }
133    }
134    held
135}
136
137/// What a click on `hit` would pick as a `P` in the part `reader` reads,
138/// lit and drawn as it is once held (see [`Selectable::drawn`],
139/// [`Selectable::visuals`]): so what the pointer lights is what a click
140/// holds. None, for a hit that selects none.
141pub fn previewed<P: Selectable>(reader: &Reader, hit: &PickedElement) -> Option<Picked> {
142    let value = P::from_picked(hit).or_else(|| P::select(reader, hit))?;
143    Some(Picked {
144        drawn: value.drawn(reader),
145        visuals: value.visuals(reader),
146        ..Picked::new(value.picked())
147    })
148}
149
150/// What a click with `pointer` on what the editor draws, `scene`, selects
151/// as a `P` in the part `reader` reads — for a step that picks on the
152/// canvas itself rather than through a reference field — and where it hit
153/// what is drawn.
154pub fn pick<P: Selectable>(
155    scene: &dyn Scene,
156    reader: &Reader,
157    pointer: &Pointer,
158) -> Option<(P, Vector3)> {
159    let accepts: Vec<String> = (P::picked_from().into_iter()).map(String::from).collect();
160    let hit = scene.pick(pointer, &accepts)?;
161    Some((P::select(reader, &hit.element)?, hit.point))
162}
163
164/// Declares an enum of references, each variant one source of what an
165/// operation takes — a line from a straight edge, a datum axis or a sketch
166/// line — given `-> Out`, all resolving to the same value, `Out`
167/// (`resolve`). Saved by its variant's
168/// name: `{"edge": "extrude(e,end)"}`. It is [`Selectable`]: a click
169/// selects the first variant that selects it. A value held is told from a
170/// click by its kind, so no variant may be of a kind drawn as itself that
171/// another variant selects from — a field of edges or chains would take a
172/// click on an edge for an edge held. The crate using it depends on
173/// `serde`.
174///
175/// ```ignore
176/// one_of! {
177///     /// What a revolve turns about.
178///     pub enum AxisRef -> Axis {
179///         Edge(ElementReference<NamedModel, Line>),
180///         Datum(ElementReference<Datums, Line>),
181///     }
182/// }
183/// ```
184#[macro_export]
185macro_rules! one_of {
186    (
187        $(#[$meta:meta])*
188        $vis:vis enum $name:ident -> $out:ty {
189            $($(#[$variant_meta:meta])* $variant:ident($reference:ty)),+ $(,)?
190        }
191    ) => {
192        $crate::one_of! {
193            $(#[$meta])*
194            $vis enum $name {
195                $($(#[$variant_meta])* $variant($reference)),+
196            }
197        }
198
199        impl $name {
200            /// What it refers to, read through `reader`, of the part itself
201            /// (see [`geop_core_program::reader::Reader::retrieve_element`]).
202            pub fn resolve(
203                &self,
204                reader: &$crate::__program::reader::Reader,
205            ) -> $crate::__GeopResult<$out> {
206                match self {
207                    $(Self::$variant(reference) => {
208                        reader.retrieve_element(reference).map(::std::convert::Into::into)
209                    })+
210                }
211            }
212
213            /// What it refers to, of the part itself or of an instance
214            /// placed in it, as it is in its own part (see
215            /// [`geop_core_program::reader::Reader::retrieve_placed`]).
216            #[allow(dead_code)]
217            pub fn resolve_placed(
218                &self,
219                reader: &$crate::__program::reader::Reader,
220            ) -> $crate::__GeopResult<$crate::__program::placed::InInstance<$out>> {
221                match self {
222                    $(Self::$variant(reference) => {
223                        let placed = reader.retrieve_placed(reference)?;
224                        Ok($crate::__program::placed::InInstance {
225                            element: placed.element.into(),
226                            instance: placed.instance,
227                        })
228                    })+
229                }
230            }
231        }
232    };
233    (
234        $(#[$meta:meta])*
235        $vis:vis enum $name:ident {
236            $($(#[$variant_meta:meta])* $variant:ident($reference:ty)),+ $(,)?
237        }
238    ) => {
239        $(#[$meta])*
240        #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
241        #[serde(rename_all = "snake_case")]
242        $vis enum $name {
243            $($(#[$variant_meta])* $variant($reference)),+
244        }
245
246        #[allow(dead_code)]
247        impl $name {
248            /// The path of the instance it is of: empty for the part itself.
249            pub fn instance(&self) -> &str {
250                match self {
251                    $(Self::$variant(reference) => reference.instance(),)+
252                }
253            }
254
255            /// The same, as its instance's own part names it.
256            pub fn local(&self) -> Self {
257                match self {
258                    $(Self::$variant(reference) => Self::$variant(reference.local()),)+
259                }
260            }
261
262            /// The same, of the instance `instance` of the part.
263            pub fn behind(&self, instance: &str) -> Self {
264                match self {
265                    $(Self::$variant(reference) => Self::$variant(reference.behind(instance)),)+
266                }
267            }
268        }
269
270        impl $crate::selectable::Selectable for $name {
271            fn picked_from() -> ::std::vec::Vec<&'static str> {
272                let mut kinds = ::std::vec::Vec::new();
273                $(for kind in <$reference as $crate::selectable::Selectable>::picked_from() {
274                    if !kinds.contains(&kind) {
275                        kinds.push(kind);
276                    }
277                })+
278                kinds
279            }
280
281            fn from_picked(picked: &$crate::__program::element::PickedElement) -> Option<Self> {
282                $(if let Some(reference) =
283                    <$reference as $crate::selectable::Selectable>::from_picked(picked)
284                {
285                    return Some(Self::$variant(reference));
286                })+
287                None
288            }
289
290            fn picked(&self) -> $crate::__program::element::PickedElement {
291                match self {
292                    $(Self::$variant(reference) => {
293                        $crate::selectable::Selectable::picked(reference)
294                    })+
295                }
296            }
297
298            fn select(
299                reader: &$crate::__program::reader::Reader,
300                hit: &$crate::__program::element::PickedElement,
301            ) -> Option<Self> {
302                $(if let Some(reference) =
303                    <$reference as $crate::selectable::Selectable>::select(reader, hit)
304                {
305                    return Some(Self::$variant(reference));
306                })+
307                None
308            }
309
310            fn drawn(
311                &self,
312                reader: &$crate::__program::reader::Reader,
313            ) -> ::std::vec::Vec<$crate::__program::element::PickedElement> {
314                match self {
315                    $(Self::$variant(reference) => {
316                        $crate::selectable::Selectable::drawn(reference, reader)
317                    })+
318                }
319            }
320
321            fn visuals(
322                &self,
323                reader: &$crate::__program::reader::Reader,
324            ) -> ::std::vec::Vec<$crate::editing::Visual> {
325                match self {
326                    $(Self::$variant(reference) => {
327                        $crate::selectable::Selectable::visuals(reference, reader)
328                    })+
329                }
330            }
331        }
332    };
333}