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}