Skip to main content

geop_core_editor/
step_editor.rs

1//! [`StepEditor`]: editing one step — the interaction every operation
2//! shares, so a pick, a selection or a drag means the same whatever is
3//! edited — against a [`Scene`]: the part as drawn, which picks are made in.
4
5use std::{any::Any, rc::Rc};
6
7use geop_core_math::{geop_error::GeopResult, vector::Vector3};
8
9use crate::{
10    editable::{Against, Editors},
11    editing::{
12        Action, CanvasEvent, Control, DRAG_SNAP, Dialog, Event, Form, Gizmo, GizmoView, InHand,
13        Orientation, Picked, Pointer, Presentation, Reference, Shape, StepEditEvent, Style, Value,
14        Visual,
15        gizmo::GizmoGrab,
16        hit::{Cycle, Scene, cycled, hit_visuals, visuals_under},
17    },
18};
19use geop_core_program::{element::PickedElement, program::Program, reader::Reader, runner::Runner};
20
21/// The key of the field the editor adds to a dialog whose gizmo has an
22/// orientation of its own: the world's axes or its own, as
23/// [`Value::Choice`] `world` or `local`.
24pub const GIZMO_ORIENTATION: &str = "gizmo_orientation";
25
26/// The step's arguments and the operation's editing state, as a drag
27/// found them when it grabbed what it drags.
28type Start = (serde_json::Value, Rc<dyn Any>);
29
30/// What a drag holds, as it was grabbed.
31enum Grab {
32    /// The handle of the number field `key`, and the value that had.
33    Handle { key: String, value: f64 },
34    /// A draggable visual of the operation's, dragged in the plane worked
35    /// in — or, with none, in the plane through where it was grabbed,
36    /// facing the eye: `(point, normal)` — and, for one dragged from where
37    /// it was grabbed (see [`Visual::from_grab`]), the step as it was then.
38    Visual {
39        key: String,
40        plane: Option<(Vector3, Vector3)>,
41        start: Option<Start>,
42    },
43    /// A stroke of the tool in hand, from where it went down.
44    Stroke,
45    /// A part of the form's gizmo, with the step's arguments and editing
46    /// state as they were when it was pressed — what every event of the
47    /// drag applies its change to — and what the drag has done so far, in
48    /// words.
49    Gizmo {
50        grab: GizmoGrab,
51        from: Box<Gizmo>,
52        start: Start,
53        readout: Option<String>,
54    },
55}
56
57/// A step being edited: the operation's editing state, and the editor's
58/// own: which reference field waits for a click and what a click would
59/// pick for it, where the pointer is, what is being dragged. The step's
60/// arguments are the program's: every edit writes them back to it.
61///
62/// It turns every event into what the operation understands, the same for
63/// every operation:
64///
65/// - A reference field (see [`Reference`]) is armed by pressing it, and
66///   then a click in the viewport picks for it — one entity, or, for a
67///   field of several, one more or one less. Removing one of its entities
68///   or clearing it is done here too. The operation is only ever sent what
69///   the field holds now, as [`Value::Entities`].
70/// - A number field with a handle is dragged by it, snapped to
71///   [`DRAG_SNAP`], and set as a [`Value::Number`].
72/// - A click on a selectable visual selects it, or takes it out of the
73///   selection again; a click on nothing clears the selection, unless shift
74///   is held, and Escape clears it. The operation keeps the selection: it
75///   is sent the new one ([`Event::Select`]), and says what it is in its
76///   form ([`Form::selection`]).
77/// - A draggable visual is dragged in the plane worked in — or, where there
78///   is none, in the plane through where it was grabbed, facing the eye —
79///   as a [`CanvasEvent::Move`]; one dragged from where it was grabbed (see
80///   [`Visual::from_grab`]) is sent to the step as it was then, as the
81///   gizmo is. With a tool in hand that strokes, a drag from anywhere else
82///   is the tool's, as a [`CanvasEvent::Stroke`].
83/// - The form's gizmo, if it has one, is drawn over everything and grabbed
84///   first: a drag of one of its parts is followed and snapped (see
85///   [`crate::editing::gizmo`]) and sent as a [`CanvasEvent::Gizmo`], to
86///   the step as it was when it was pressed, so each event applies the
87///   whole drag once. A click on it does nothing, unless a field waits for
88///   a pick: then it picks what is under it. Where it has an orientation
89///   of its own, the dialog gets a [`GIZMO_ORIENTATION`] field to choose
90///   the world's axes or its own.
91/// - Of what lies under the pointer, a hover lights and a click takes the
92///   one its [`Pointer::cycle`] names, front to back: what a reference
93///   field would hold of each counted once (see [`candidates`]), or each
94///   visual.
95/// - Escape, Enter and Delete mean the same in every tool, and reach it as
96///   [`CanvasEvent::Cancel`], [`CanvasEvent::Confirm`] and
97///   [`CanvasEvent::Delete`]; any other key, lowercased, is a tool's
98///   shortcut.
99///
100/// Whatever else the pointer and the keys do goes to the operation as a
101/// [`CanvasEvent`]: clicks while it has a tool in hand, and those on
102/// nothing, hovers, keys.
103///
104/// An edit is made against the part as the last run left it: the editor
105/// runs the program again after each, for the step to be shown as it is
106/// now.
107pub struct StepEditor {
108    step: String,
109    editing: Rc<dyn Any>,
110    /// The reference field waiting for a click.
111    armed: Option<String>,
112    /// What a click would pick for it.
113    hover: Option<PickedElement>,
114    /// Which of what a click could pick for it the pointer is over (see
115    /// [`Pointer::cycle`]).
116    cycle: Option<Cycle>,
117    /// Where the pointer last was, if over the viewport.
118    pointer: Option<Pointer>,
119    /// Where the drag under way went down, and what it holds.
120    grab: Option<(Pointer, Grab)>,
121    /// Which way the gizmo is turned, where it has a way of its own.
122    orientation: Orientation,
123    /// The keys of the step's reference fields after the last event it
124    /// took — none known before the first, or after a restart: what the
125    /// next event is compared with, without building the form again.
126    references: Option<Vec<String>>,
127}
128
129/// Takes `x` out of `xs`, or adds it at the end.
130fn toggle<T: PartialEq>(xs: &mut Vec<T>, x: T) {
131    match xs.iter().position(|y| *y == x) {
132        Some(i) => {
133            xs.remove(i);
134        }
135        None => xs.push(x),
136    }
137}
138
139/// The handles of `dialog`'s number fields, as visuals under their keys.
140fn handles(dialog: &Dialog) -> Vec<Visual> {
141    (dialog.0.iter())
142        .filter_map(|f| match &f.control {
143            Control::Number(n) => n.handle.map(|track| {
144                let shape = Shape::Handle {
145                    at: track.at,
146                    direction: track.direction,
147                };
148                Visual::new(f.key.clone(), shape, Style::Handle)
149            }),
150            _ => None,
151        })
152        .collect()
153}
154
155fn is_handle(visual: &Visual) -> bool {
156    matches!(visual.shape, Shape::Handle { .. })
157}
158
159/// Whether a press on `visual` grabs it: a handle always, a draggable
160/// visual unless a tool is in hand.
161fn grabs(visual: &Visual, tool: InHand) -> bool {
162    is_handle(visual) || (visual.draggable && tool == InHand::Nothing)
163}
164
165/// The keys of `form`'s reference fields that wait for a pick.
166fn waiting(form: &Form) -> impl Iterator<Item = &str> {
167    (form.dialog.0.iter()).filter_map(|f| match &f.control {
168        Control::Reference(r) if r.waiting() => Some(f.key.as_str()),
169        _ => None,
170    })
171}
172
173/// The keys of `form`'s reference fields.
174fn references(form: &Form) -> Vec<String> {
175    (form.dialog.0.iter())
176        .filter(|f| matches!(f.control, Control::Reference(_)))
177        .map(|f| f.key.clone())
178        .collect()
179}
180
181/// What a click with `pointer` could pick for the reference field `key` of
182/// `dialog`, in what `scene` draws, read through `reader`, front to back
183/// (see [`Scene::under`]), each with what the field would hold of it: the
184/// element in front, whatever it holds — a click on the plate in front of
185/// a hole picks no hole behind it — then each element behind it that
186/// holds something not held already: two faces of one feature are one
187/// feature to a field of features.
188fn candidates(
189    scene: &dyn Scene,
190    reader: &Reader,
191    dialog: &Dialog,
192    key: &str,
193    reference: &Reference,
194    pointer: &Pointer,
195) -> Vec<(PickedElement, Option<Picked>)> {
196    let mut found: Vec<(PickedElement, Option<Picked>)> = Vec::new();
197    for hit in scene.under(pointer, &reference.accepts) {
198        let held = dialog.previewed(reader, key, &hit.element);
199        let behind = !found.is_empty();
200        let known = |held: &Picked| {
201            (found.iter()).any(|(_, h)| h.as_ref().is_some_and(|h| h.element == held.element))
202        };
203        if behind && held.as_ref().is_none_or(known) {
204            continue;
205        }
206        found.push((hit.element, held));
207    }
208    found
209}
210
211/// The program a step editor edits, the operations it is edited with, the
212/// runner and the part it was last run on, and the scene picks are made in
213/// — what every
214/// event is handled against.
215pub struct Editing<'a, Sc> {
216    pub editors: &'a Editors,
217    pub runner: &'a Runner,
218    pub part: Against<'a>,
219    pub program: &'a mut Program,
220    pub scene: &'a Sc,
221}
222
223impl StepEditor {
224    /// Editing the step `step` of `program`. A new step whose first
225    /// reference field is still empty starts by picking for it: what it is
226    /// built on is what it needs first. One that already holds something —
227    /// the newest sketch, say — needs no click, and leaves the pointer to
228    /// the rest of the step: its handles, its drawing.
229    pub fn new<'a>(
230        editors: &Editors,
231        runner: &Runner,
232        part: impl Into<Against<'a>>,
233        program: &Program,
234        step: &str,
235        new: bool,
236    ) -> GeopResult<Self> {
237        let editing: Rc<dyn Any> = Rc::from(editors.initial_editing(program, step)?);
238        let armed = match new {
239            true => {
240                let form = editors.presentation(runner, part, program, step, &*editing)?;
241                waiting(&form).next().map(str::to_string)
242            }
243            false => None,
244        };
245        Ok(Self {
246            step: step.to_string(),
247            editing,
248            armed,
249            hover: None,
250            cycle: None,
251            pointer: None,
252            grab: None,
253            orientation: Orientation::default(),
254            references: None,
255        })
256    }
257
258    /// The step edited, by id.
259    pub fn step(&self) -> &str {
260        &self.step
261    }
262
263    /// The operation's editing state: its own, to look into.
264    pub fn editing(&self) -> &dyn Any {
265        &*self.editing
266    }
267
268    /// Starts the operation's editing afresh, as for a step just opened —
269    /// nothing picked for or dragged — after the step's arguments were put
270    /// back as an edit found them: undoing one. What the old editing state
271    /// held may not be in the step any more. What is the user's, not the
272    /// operation's, stays: the field waiting for a pick still waits, if the
273    /// step still has it, and the gizmo stays turned as it was.
274    pub fn restart<'a>(
275        &mut self,
276        editors: &Editors,
277        runner: &Runner,
278        part: impl Into<Against<'a>>,
279        program: &Program,
280    ) -> GeopResult<()> {
281        self.editing = Rc::from(editors.initial_editing(program, &self.step)?);
282        self.hover = None;
283        self.cycle = None;
284        self.grab = None;
285        let references = references(&self.form(editors, runner, part, program)?);
286        self.armed = (self.armed.take()).filter(|armed| references.contains(armed));
287        self.references = Some(references);
288        Ok(())
289    }
290
291    /// The step's form.
292    pub fn form<'a>(
293        &self,
294        editors: &Editors,
295        runner: &Runner,
296        part: impl Into<Against<'a>>,
297        program: &Program,
298    ) -> GeopResult<Form> {
299        editors.presentation(runner, part, program, &self.step, &*self.editing)
300    }
301
302    /// Whether nothing is in progress in `form`, the step's form: no field
303    /// waits for a pick, nothing is selected or dragged, no value is asked
304    /// for in place, and the operation has no tool in hand — so Escape has
305    /// nothing to end, and Enter nothing to finish, but the step itself
306    /// (see [`crate::editor::Editor::handle`]).
307    pub fn at_rest(&self, form: &Form) -> bool {
308        self.armed.is_none()
309            && self.grab.is_none()
310            && form.selection.is_empty()
311            && form.prompt.is_none()
312            && form.tool == InHand::Nothing
313    }
314
315    /// Sends `event` to the operation, and keeps what it made of the step:
316    /// its arguments in the program, its editing state here. A reference
317    /// field that is gone after it waits for no pick any more, and one that
318    /// appeared empty waits for one — what the step needs picked next, as
319    /// for a new step.
320    fn send<Sc: Scene>(&mut self, e: &mut Editing<'_, Sc>, event: Event) -> GeopResult<()> {
321        let before = match self.references.take() {
322            Some(known) => known,
323            None => references(&self.form(e.editors, e.runner, e.part.clone(), e.program)?),
324        };
325        let scene: &dyn Scene = e.scene;
326        let edited = (e.editors).process_user_event(
327            e.runner,
328            e.part.clone(),
329            e.program,
330            &self.step,
331            &*self.editing,
332            event,
333            Some(scene),
334        )?;
335        e.program.step_mut(&self.step)?.args = edited.args;
336        self.editing = Rc::from(edited.editing);
337        let form = self.form(e.editors, e.runner, e.part.clone(), e.program)?;
338        self.references = Some(references(&form));
339        if let Some(armed) = &self.armed
340            && !references(&form).contains(armed)
341        {
342            self.armed = None;
343            self.hover = None;
344        }
345        if let Some(key) = waiting(&form).find(|key| !before.iter().any(|b| b == key)) {
346            self.armed = Some(key.to_string());
347            self.hover = None;
348        }
349        Ok(())
350    }
351
352    fn pass<Sc: Scene>(&mut self, e: &mut Editing<'_, Sc>, event: CanvasEvent) -> GeopResult<()> {
353        self.send(e, Event::Canvas(event))
354    }
355
356    /// The selection is `selection` now.
357    fn select<Sc: Scene>(
358        &mut self,
359        e: &mut Editing<'_, Sc>,
360        form: &Form,
361        selection: Vec<String>,
362    ) -> GeopResult<()> {
363        match selection == form.selection {
364            true => Ok(()),
365            false => self.send(e, Event::Select(selection)),
366        }
367    }
368
369    /// The step as it is now, for a drag to start from.
370    fn start<Sc: Scene>(&self, e: &Editing<'_, Sc>) -> Start {
371        let args = (e.program.step(&self.step))
372            .map(|s| s.args.clone())
373            .unwrap_or_default();
374        (args, self.editing.clone())
375    }
376
377    /// The step back as a drag found it, `start`.
378    fn restore<Sc: Scene>(&mut self, e: &mut Editing<'_, Sc>, start: &Start) {
379        if let Ok(step) = e.program.step_mut(&self.step) {
380            step.args = start.0.clone();
381        }
382        self.editing = start.1.clone();
383    }
384
385    /// The form's gizmo, turned as chosen.
386    fn gizmo(&self, form: &Form) -> Option<GizmoView> {
387        (form.gizmo.as_ref()).map(|gizmo| GizmoView::of(gizmo, self.orientation))
388    }
389
390    /// Applies `event`, picking in `e.scene`: the part before the step as
391    /// drawn, or, for an operation whose picks are made in what it builds,
392    /// that.
393    pub fn handle<Sc: Scene>(
394        &mut self,
395        mut e: Editing<'_, Sc>,
396        event: &StepEditEvent,
397    ) -> GeopResult<()> {
398        let e = &mut e;
399        let form = self.form(e.editors, e.runner, e.part.clone(), e.program)?;
400        if let StepEditEvent::Dialog { key, value } = event {
401            return self.dialog(e, &form, key, value.clone());
402        }
403        // How the view is seen is the operation's, whatever a field waits for.
404        if let StepEditEvent::View { up } = event {
405            return self.pass(e, CanvasEvent::View { up: *up });
406        }
407        // A click on the gizmo is no click on what is under it — unless a
408        // field waits for a pick, which a click is for, as for a handle. A
409        // double click is the operation's: the gizmo stands at the centre
410        // of what the first click selected, right where the second lands.
411        if let (
412            StepEditEvent::Click {
413                pointer,
414                double: false,
415                ..
416            },
417            Some(gizmo),
418            None,
419        ) = (event, self.gizmo(&form), &self.armed)
420            && gizmo.hit(pointer).is_some()
421        {
422            self.pointer = Some(*pointer);
423            return Ok(());
424        }
425        match event {
426            StepEditEvent::Hover { pointer, .. } | StepEditEvent::Click { pointer, .. } => {
427                self.pointer = Some(*pointer)
428            }
429            StepEditEvent::Leave => self.pointer = None,
430            _ => {}
431        }
432        if let Some(key) = self.armed.clone() {
433            if let Some(Control::Reference(reference)) = form.dialog.get(&key) {
434                // A drag is no pick: while the field waits, it can only be
435                // of a handle (see `presentation`).
436                return match event {
437                    StepEditEvent::Drag {
438                        from,
439                        to,
440                        done,
441                        shift,
442                    } => self.drag(e, &form, from, to, *done, *shift, true),
443                    _ => self.pick(e, &form.dialog, event, &key, reference),
444                };
445            }
446            self.armed = None;
447        }
448        match event {
449            StepEditEvent::Dialog { .. } | StepEditEvent::View { .. } => Ok(()),
450            StepEditEvent::Hover { pointer, shift } => self.pass(
451                e,
452                CanvasEvent::Hover {
453                    pointer: *pointer,
454                    shift: *shift,
455                },
456            ),
457            StepEditEvent::Leave => self.pass(e, CanvasEvent::Leave),
458            StepEditEvent::Click {
459                pointer,
460                double,
461                shift,
462            } => {
463                let in_hand = form.tool != InHand::Nothing;
464                let hit = (!*double && !in_hand)
465                    .then(|| {
466                        let instance = |name: &str, p: &Pointer| e.scene.hit_instance(name, p);
467                        hit_visuals(&form.visuals, pointer, instance, |v| v.selectable)
468                            .map(|hit| hit.visual.key.clone())
469                    })
470                    .flatten();
471                match hit {
472                    Some(key) => {
473                        let mut selection = form.selection.clone();
474                        toggle(&mut selection, key);
475                        self.select(e, &form, selection)
476                    }
477                    None => {
478                        if !*shift && !in_hand {
479                            self.select(e, &form, Vec::new())?;
480                        }
481                        self.pass(
482                            e,
483                            CanvasEvent::Click {
484                                pointer: *pointer,
485                                double: *double,
486                                shift: *shift,
487                            },
488                        )
489                    }
490                }
491            }
492            StepEditEvent::Drag {
493                from,
494                to,
495                done,
496                shift,
497            } => self.drag(e, &form, from, to, *done, *shift, false),
498            StepEditEvent::SelectBox {
499                corners,
500                crossing,
501                add,
502            } => match form.select_box && form.tool == InHand::Nothing {
503                true => self.pass(
504                    e,
505                    CanvasEvent::SelectBox {
506                        corners: **corners,
507                        crossing: *crossing,
508                        add: *add,
509                    },
510                ),
511                false => Ok(()),
512            },
513            StepEditEvent::Key { key } => match key.as_str() {
514                "Escape" => {
515                    self.select(e, &form, Vec::new())?;
516                    self.pass(e, CanvasEvent::Cancel)
517                }
518                "Enter" => self.pass(e, CanvasEvent::Confirm),
519                "Delete" | "Backspace" => self.pass(e, CanvasEvent::Delete),
520                _ => {
521                    let key = key.to_lowercase();
522                    self.pass(e, CanvasEvent::Key { key })
523                }
524            },
525        }
526    }
527
528    /// The dialog field `key` used: a reference field armed, or an entity
529    /// taken out of it, or all of them; any other field set.
530    fn dialog<Sc: Scene>(
531        &mut self,
532        e: &mut Editing<'_, Sc>,
533        form: &Form,
534        key: &str,
535        value: Value,
536    ) -> GeopResult<()> {
537        if key == GIZMO_ORIENTATION {
538            match value {
539                Value::Choice(c) if c == "world" => self.orientation = Orientation::World,
540                Value::Choice(c) if c == "local" => self.orientation = Orientation::Local,
541                _ => {}
542            }
543            return Ok(());
544        }
545        let Some(Control::Reference(reference)) = form.dialog.get(key) else {
546            let key = key.to_string();
547            return self.send(e, Event::Field { key, value });
548        };
549        let mut elements: Vec<PickedElement> = reference.elements().cloned().collect();
550        match value {
551            Value::Press => {
552                self.armed = (self.armed.as_deref() != Some(key)).then(|| key.to_string());
553                self.hover = None;
554                return Ok(());
555            }
556            Value::RemoveAt(i) if i < elements.len() => {
557                elements.remove(i);
558            }
559            Value::Clear => elements.clear(),
560            Value::Elements(set) => elements = set,
561            _ => return Ok(()),
562        }
563        let (key, value) = (key.to_string(), Value::Elements(elements));
564        self.send(e, Event::Field { key, value })
565    }
566
567    /// A pointer event while the reference field `key` of `dialog` waits
568    /// for a click: a hover finds what a click would pick, a click picks it
569    /// — and, for a field of one entity, is done. Of what lies under the
570    /// pointer, each is taken by what the field would hold of it (see
571    /// [`candidates`]), and the pointer's cycle names which. Escape stops
572    /// picking.
573    fn pick<Sc: Scene>(
574        &mut self,
575        e: &mut Editing<'_, Sc>,
576        dialog: &Dialog,
577        event: &StepEditEvent,
578        key: &str,
579        reference: &Reference,
580    ) -> GeopResult<()> {
581        let reader = self.picked_in(e.editors, e.part.clone(), e.program)?;
582        let scene: &dyn Scene = e.scene;
583        let under = |pointer: &Pointer| {
584            let found = candidates(scene, &reader, dialog, key, reference, pointer);
585            let cycle = Cycle::of(found.len(), pointer);
586            (cycled(found, pointer.cycle).map(|(hit, _)| hit), cycle)
587        };
588        match event {
589            StepEditEvent::Hover { pointer, .. } => (self.hover, self.cycle) = under(pointer),
590            StepEditEvent::Leave => (self.hover, self.cycle) = (None, None),
591            StepEditEvent::Click { pointer, .. } => {
592                let (picked, _) = under(pointer);
593                (self.hover, self.cycle) = (None, None);
594                if let Some(element) = picked {
595                    let mut elements: Vec<PickedElement> = reference.elements().cloned().collect();
596                    if reference.multiple {
597                        toggle(&mut elements, element);
598                    } else {
599                        elements = vec![element];
600                        self.armed = None;
601                    }
602                    let (key, value) = (key.to_string(), Value::Elements(elements));
603                    self.send(e, Event::Field { key, value })?;
604                    // Picked and done: what the step needs next waits for
605                    // the next click, as it did when the step was started.
606                    if self.armed.is_none() {
607                        let form = self.form(e.editors, e.runner, e.part.clone(), e.program)?;
608                        self.armed = waiting(&form).next().map(str::to_string);
609                    }
610                    return Ok(());
611                }
612            }
613            StepEditEvent::Key { key } if key == "Escape" => {
614                self.armed = None;
615                (self.hover, self.cycle) = (None, None);
616            }
617            _ => {}
618        }
619        Ok(())
620    }
621
622    /// The part the step's picks are made in, and what they are read
623    /// through: what the step builds, for an operation that picks in that
624    /// (see [`crate::editable::OperationInfo::picks_built`]); the whole
625    /// part, for one that picks in that
626    /// ([`crate::editable::OperationInfo::picks_whole`]); else the part before
627    /// the step.
628    fn picked_in<'a>(
629        &self,
630        editors: &Editors,
631        part: Against<'a>,
632        program: &Program,
633    ) -> GeopResult<Reader<'a>> {
634        let info = (program.step(&self.step).ok()).and_then(|step| editors.info(&step.operation));
635        match info.map(|listed| (listed.info.picks_whole, listed.info.picks_built)) {
636            Some((true, _)) => Ok(part.current()),
637            Some((_, true)) => Ok(part.after(&self.step)),
638            _ => part.before(program, &self.step),
639        }
640    }
641
642    /// A drag: of a number field's handle — its value when grabbed, moved
643    /// by how far the pointer has moved along the handle's track, snapped
644    /// to [`DRAG_SNAP`] — or of a draggable visual, in the plane worked in
645    /// or, with none, in the plane through where it was grabbed, facing
646    /// the eye.
647    ///
648    /// What is dragged is hit-tested where the drag started, once: while
649    /// dragged, it moves away from there. After that it is found by its key.
650    /// A drag that went down somewhere else is a new one, even if the last
651    /// one's release never came. A drag that did not start on either is a
652    /// stroke of the tool in hand,
653    /// if it strokes — else nothing, as for a pointer that cannot be
654    /// followed (looking straight along a handle's track, or along the
655    /// plane). With `handles_only` — while a field waits for a pick — only a
656    /// handle is grabbed.
657    #[allow(clippy::too_many_arguments)]
658    fn drag<Sc: Scene>(
659        &mut self,
660        e: &mut Editing<'_, Sc>,
661        form: &Form,
662        from: &Pointer,
663        to: &Pointer,
664        done: bool,
665        shift: bool,
666        handles_only: bool,
667    ) -> GeopResult<()> {
668        let handles = handles(&form.dialog);
669        if self.grab.as_ref().is_some_and(|(down, _)| down != from) {
670            self.grab = None;
671        }
672        if self.grab.is_none()
673            && let (Some(gizmo), Some(asked)) = (self.gizmo(form), &form.gizmo)
674            && let Some(part) = gizmo.hit(from)
675        {
676            let grab = Grab::Gizmo {
677                grab: GizmoGrab::new(gizmo, part, from),
678                from: Box::new(asked.clone()),
679                start: self.start(e),
680                readout: None,
681            };
682            self.grab = Some((*from, grab));
683        }
684        if self.grab.is_none() {
685            let visuals: Vec<Visual> = form.visuals.iter().chain(&handles).cloned().collect();
686            let instance = |name: &str, p: &Pointer| e.scene.hit_instance(name, p);
687            let hit = hit_visuals(&visuals, from, instance, |v| match handles_only {
688                true => is_handle(v),
689                false => grabs(v, form.tool),
690            });
691            let grab = match hit {
692                Some(hit) => {
693                    let key = hit.visual.key.clone();
694                    match form.dialog.get(&key) {
695                        Some(Control::Number(n)) if is_handle(hit.visual) => Grab::Handle {
696                            key,
697                            value: n.value,
698                        },
699                        _ => Grab::Visual {
700                            key,
701                            plane: (form.focus.is_none())
702                                .then(|| (from.ray.at(hit.t), *from.ray.dir())),
703                            start: hit.visual.from_grab.then(|| self.start(e)),
704                        },
705                    }
706                }
707                None if form.tool == InHand::Strokes && !handles_only => Grab::Stroke,
708                None => return Ok(()),
709            };
710            self.grab = Some((*from, grab));
711        }
712        let Some((down, mut grab)) = self.grab.take() else {
713            return Ok(());
714        };
715        let moved = self.drag_by(e, form, &handles, &mut grab, from, to, done, shift);
716        // Released: the next drag grabs afresh.
717        if !done {
718            self.grab = Some((down, grab));
719        }
720        moved
721    }
722
723    /// What dragging `grab` from `from` to `to` does.
724    #[allow(clippy::too_many_arguments)]
725    fn drag_by<Sc: Scene>(
726        &mut self,
727        e: &mut Editing<'_, Sc>,
728        form: &Form,
729        handles: &[Visual],
730        grab: &mut Grab,
731        from: &Pointer,
732        to: &Pointer,
733        done: bool,
734        shift: bool,
735    ) -> GeopResult<()> {
736        match grab {
737            Grab::Handle { key, value } => {
738                let track =
739                    (handles.iter())
740                        .filter(|v| v.key == *key)
741                        .find_map(|v| match v.shape {
742                            Shape::Handle { at, direction } => Some((at, direction)),
743                            _ => None,
744                        });
745                let moved = track.and_then(|(at, direction)| {
746                    let along = |p: &Pointer| p.ray.line_parameter(&at, &direction);
747                    Some(along(to)?.sub(along(from)?))
748                });
749                match moved {
750                    Some(moved) => {
751                        let value = *value + moved.to_f64();
752                        let snapped = (value / DRAG_SNAP).round() * DRAG_SNAP;
753                        let (key, value) = (key.clone(), Value::Number(snapped));
754                        self.send(e, Event::Field { key, value })
755                    }
756                    None => Ok(()),
757                }
758            }
759            Grab::Visual { key, plane, start } => {
760                let in_plane = |pointer: &Pointer| -> Option<Vector3> {
761                    let (origin, normal) = match (&form.focus, &plane) {
762                        (Some(focus), _) => (focus.origin(), focus.w()),
763                        (None, Some((point, normal))) => (point, normal),
764                        (None, None) => return None,
765                    };
766                    pointer
767                        .ray
768                        .intersect_plane(origin, normal)
769                        .map(|(_, at)| at)
770                };
771                match (in_plane(from), in_plane(to)) {
772                    (Some(from), Some(at)) => {
773                        if let Some(start) = start {
774                            self.restore(e, start);
775                        }
776                        let event = CanvasEvent::Move {
777                            key: key.clone(),
778                            from,
779                            to: at,
780                            pointer: *to,
781                            done,
782                            shift,
783                        };
784                        self.pass(e, event)
785                    }
786                    _ => Ok(()),
787                }
788            }
789            Grab::Gizmo {
790                grab,
791                from: asked,
792                start,
793                readout,
794            } => {
795                let Some((drag, text)) = grab.drag(from, to, shift) else {
796                    return Ok(());
797                };
798                self.restore(e, start);
799                *readout = Some(text);
800                let from = Gizmo::clone(asked);
801                self.pass(e, CanvasEvent::Gizmo { drag, from, done })
802            }
803            Grab::Stroke => {
804                let event = CanvasEvent::Stroke {
805                    from: *from,
806                    to: *to,
807                    done,
808                    shift,
809                };
810                self.pass(e, event)
811            }
812        }
813    }
814
815    /// What to show: the step's form, with its reference fields saying what
816    /// they hold in `scene` — what they are picked from — the handles of
817    /// its number fields, what is selected and what the pointer is over
818    /// drawn so, what the reference fields hold and what a click would pick
819    /// lit, and what a click picks now. While a field waits for a pick,
820    /// there is no plane to work in: what can be picked is shown in the
821    /// part as it stands — and of the visuals, only the handles can be
822    /// grabbed.
823    pub fn presentation<'a>(
824        &self,
825        editors: &Editors,
826        runner: &Runner,
827        part: impl Into<Against<'a>>,
828        program: &Program,
829        scene: &impl Scene,
830    ) -> GeopResult<Presentation> {
831        let part = part.into();
832        let mut form = self.form(editors, runner, part.clone(), program)?;
833        let mut pickable = Vec::new();
834        for (key, reference) in form.dialog.references_mut() {
835            if self.armed.as_deref() == Some(key) {
836                reference.armed = true;
837                pickable = reference.accepts.clone();
838            }
839        }
840        let mut gizmo = self.gizmo(&form);
841        if form.gizmo.as_ref().is_some_and(|g| g.axes.is_some()) {
842            let choice = |value: &str, label: &str, orientation: Orientation, title: &str| {
843                Action::new(value, label)
844                    .group("Gizmo")
845                    .title(title)
846                    .active(self.orientation == orientation)
847            };
848            let actions = vec![
849                choice(
850                    "world",
851                    "World",
852                    Orientation::World,
853                    "Move, turn and scale along the world's axes",
854                ),
855                choice(
856                    "local",
857                    "Local",
858                    Orientation::Local,
859                    "Move, turn and scale along its own axes",
860                ),
861            ];
862            form.dialog.actions(GIZMO_ORIENTATION, actions);
863        }
864        let mut visuals = form.visuals;
865        visuals.extend(handles(&form.dialog));
866        // What the reference fields hold, and what a click would pick for
867        // the one waiting, drawn and lit as it would be held — a frame as a
868        // triad, on what it is put on — in the part it is picked in: as the
869        // step built it, where what the step places is, for a step picking
870        // in that; else as it was before.
871        let picked_in = self.picked_in(editors, part.clone(), program)?;
872        let previewed = (self.armed.as_deref().zip(self.hover.as_ref()))
873            .and_then(|(key, element)| form.dialog.previewed(&picked_in, key, element));
874        let shown = form.dialog.shown(previewed.as_ref());
875        let tool = form.tool;
876        // While a field waits for a pick, only a handle is hovered: no pick
877        // takes one, so a press on it can only mean dragging it. Anything
878        // else under the pointer is the field's to pick.
879        let waiting = self.armed.is_some();
880        if let Some(gizmo) = &mut gizmo {
881            match &self.grab {
882                Some((_, Grab::Gizmo { grab, readout, .. })) => {
883                    gizmo.active = Some(grab.part);
884                    gizmo.readout = readout.clone();
885                }
886                _ => gizmo.hover = self.pointer.and_then(|pointer| gizmo.hit(&pointer)),
887            }
888        }
889        let on_gizmo = gizmo.as_ref().is_some_and(|g| g.hover.is_some());
890        let under = self.pointer.filter(|_| !on_gizmo).map(|pointer| {
891            let instance = |name: &str, p: &Pointer| scene.hit_instance(name, p);
892            let under = visuals_under(&visuals, &pointer, instance, |v| match waiting {
893                true => is_handle(v),
894                false => v.selectable || grabs(v, tool),
895            });
896            let cycle = Cycle::of(under.len(), &pointer);
897            let hovered = cycled(under, pointer.cycle)
898                .map(|hit| (hit.visual.key.clone(), grabs(hit.visual, tool)));
899            (hovered, cycle)
900        });
901        let (hovered, cycle) = under.unwrap_or_default();
902        // While a field waits, what the pointer is over is what it picks.
903        let cycle = match waiting {
904            true => self.cycle,
905            false => cycle,
906        };
907        for visual in &mut visuals {
908            if is_handle(visual) {
909                continue;
910            }
911            if form.selection.contains(&visual.key) {
912                visual.style = Style::Selected;
913            } else if hovered.as_ref().is_some_and(|(key, _)| *key == visual.key) {
914                visual.style = Style::Hover;
915            }
916        }
917        visuals.extend(shown);
918        let highlights = (form.dialog.picked().cloned())
919            .chain(previewed.into_iter().flat_map(|p| p.drawn))
920            .collect();
921        Ok(Presentation {
922            dialog: form.dialog,
923            layout: form.layout,
924            visuals,
925            highlights,
926            pickable,
927            focus: form.focus.filter(|_| !waiting),
928            sheet: form.sheet.filter(|_| !waiting),
929            // A tool that strokes takes a press anywhere.
930            grab: self.grab.is_some()
931                || on_gizmo
932                || hovered.is_some_and(|(_, grab)| grab)
933                || (tool == InHand::Strokes && !waiting),
934            select_box: form.select_box && tool == InHand::Nothing && !waiting,
935            prompt: form.prompt,
936            gizmo,
937            hidden: form.hidden,
938            cycle,
939        })
940    }
941}
942
943#[cfg(test)]
944mod tests;