Skip to main content

geop_core_editor/editing/
event.rs

1//! [`StepEditEvent`]: what the user did, as an operation receives it.
2
3use geop_core_math::{
4    geop_error::GeopResult,
5    primitives::{Motion, Ray},
6    scalars::{Scalar, as_f64},
7    vector::Vector3,
8};
9use serde::{Deserialize, Serialize};
10
11use super::{Gizmo, GizmoDrag};
12use geop_core_program::{element::PickedElement, numeric::NumericInput};
13
14/// Dragged values snap to this: a drag is for rough shaping, a dialog for
15/// exact values.
16pub const DRAG_SNAP: f64 = 0.01;
17
18/// How far from its ray a [`Pointer`] reaches: what counts as under it, at
19/// any distance along the ray. Everything a viewer draws at a constant size
20/// on screen — a handle, a label, a frame datum — is laid out in reaches,
21/// so it is hit where it is drawn however far away or zoomed in the view is.
22#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
23#[serde(tag = "type", rename_all = "snake_case")]
24pub enum Reach {
25    /// Seen in perspective: a cone around the ray, from its origin — the
26    /// eye — widening by `slope` per unit of distance.
27    Cone {
28        #[serde(with = "as_f64")]
29        slope: Scalar,
30    },
31    /// Seen orthographically: a tube of `radius` around the ray.
32    Tube {
33        #[serde(with = "as_f64")]
34        radius: Scalar,
35    },
36}
37
38impl Reach {
39    /// How far from the ray it reaches, `t` along it.
40    pub fn at(&self, t: Scalar) -> Scalar {
41        match *self {
42            Reach::Cone { slope } => slope.mul(t),
43            Reach::Tube { radius } => radius,
44        }
45    }
46}
47
48/// Where the pointer is: the ray from the eye through it, how far from
49/// that ray it reaches, and which of what lies under it it means.
50#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
51pub struct Pointer {
52    pub ray: Ray,
53    pub reach: Reach,
54    /// Which of what lies under the pointer it means, counted front to
55    /// back from the one a click picks first (see
56    /// [`super::hit::Scene::under`]), wrapping round: a secondary click
57    /// steps one further behind, and any move of the pointer starts again
58    /// from the front. A pick, a hover and a click alike take the one
59    /// this names, so what is lit is what a click holds.
60    #[serde(default)]
61    pub cycle: usize,
62}
63
64impl Pointer {
65    /// The pointer moved by `motion`: into the frame of a part placed in
66    /// the part drawn, for a step of it to be edited where it is placed. A
67    /// rigid motion leaves its reach as it is.
68    pub fn moved(&self, motion: &Motion) -> GeopResult<Self> {
69        let ray = Ray::try_new(
70            motion.apply(self.ray.origin()),
71            motion.rotate(self.ray.dir()),
72        )?;
73        Ok(Pointer { ray, ..*self })
74    }
75
76    /// The same pointer, meaning the `cycle`-th of what lies under it (see
77    /// [`Pointer::cycle`]).
78    pub fn cycled(self, cycle: usize) -> Self {
79        Pointer { cycle, ..self }
80    }
81
82    /// `reaches` of the pointer's reach, `t` along its ray.
83    pub fn reach_at(&self, reaches: f64, t: Scalar) -> Scalar {
84        self.reach.at(t).mul(Scalar::from_f64(reaches))
85    }
86
87    /// Whether something `dist` from the ray, `t` along it, could lie within
88    /// `reaches` of it.
89    pub fn within(&self, dist: Scalar, t: Scalar, reaches: f64) -> bool {
90        !dist.definitely_greater(self.reach_at(reaches, t))
91    }
92}
93
94/// What a field was set to — or that it was pressed: in the dialog, by a
95/// pick in the viewport, or by dragging a handle.
96#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
97#[serde(tag = "type", content = "value", rename_all = "snake_case")]
98pub enum Value {
99    /// A button, a list item or a reference field was pressed.
100    Press,
101    /// A list item's remove button was pressed.
102    Remove,
103    Bool(bool),
104    Number(f64),
105    /// Text typed: a value given as a formula, a colour.
106    Text(String),
107    /// An action, or a select's option, by its value.
108    Choice(String),
109    /// The element at this index was taken out of a reference field.
110    RemoveAt(usize),
111    /// Everything was taken out of a reference field.
112    Clear,
113    /// What a reference field holds now: all an operation is ever sent for
114    /// one (see [`super::Reference`]).
115    Elements(Vec<PickedElement>),
116    /// Something done to a table field (see [`super::Table`]).
117    Table(TableEdit),
118}
119
120/// What is done to a [`super::Table`], one thing at a time; rows and
121/// columns are named by their index in the table as it was shown.
122#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
123#[serde(tag = "edit", rename_all = "snake_case")]
124pub enum TableEdit {
125    /// The cell at `row`, `column` typed into.
126    Cell {
127        row: usize,
128        column: usize,
129        text: String,
130    },
131    /// A block of text pasted at `row`, `column` — from a spreadsheet, a
132    /// row of cells per line — filling the cells from there on, and adding
133    /// rows below the last for what runs over. An operation never sees
134    /// this: its form takes it apart into the rows added and the cells set
135    /// (see [`crate::edit_form::EditForm::table`]).
136    Paste {
137        row: usize,
138        column: usize,
139        cells: Vec<Vec<String>>,
140    },
141    /// A row added, after the last.
142    AddRow,
143    RemoveRow {
144        row: usize,
145    },
146    /// The row `row` moved to be the row `to`.
147    MoveRow {
148        row: usize,
149        to: usize,
150    },
151    /// The row chosen, of a table one row of which is.
152    SelectRow {
153        row: usize,
154    },
155    /// The header of the column `column` typed into.
156    Column {
157        column: usize,
158        text: String,
159    },
160    /// A column added, after the last.
161    AddColumn,
162    RemoveColumn {
163        column: usize,
164    },
165}
166
167impl Value {
168    /// The [`NumericInput`] a number field set to it gives: a number slid
169    /// or dragged plain, text typed plain if it is a number and a formula
170    /// otherwise. `None` for anything else, and for empty text. A formula
171    /// that does not evaluate is kept, as typed: the step then fails naming
172    /// it, and the field shows the text to correct.
173    pub fn numeric_input(&self) -> Option<NumericInput> {
174        match self {
175            Value::Number(v) => Some(NumericInput::Plain(*v)),
176            Value::Text(text) if !text.trim().is_empty() => Some(NumericInput::from(text.as_str())),
177            _ => None,
178        }
179    }
180}
181
182/// One thing the user did while editing a step.
183#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
184#[serde(tag = "type", rename_all = "snake_case")]
185pub enum StepEditEvent {
186    /// The dialog field `key` was used.
187    Dialog { key: String, value: Value },
188    /// The pointer moved over the viewport with no button held — `shift`
189    /// held or not, which turns snapping off.
190    Hover {
191        pointer: Pointer,
192        #[serde(default)]
193        shift: bool,
194    },
195    /// The pointer left the viewport.
196    Leave,
197    /// A click in the viewport: a press and release of the primary button
198    /// without moving. (The secondary button's click is no event of its
199    /// own: it steps the pointer's [`Pointer::cycle`].)
200    Click {
201        pointer: Pointer,
202        /// The second click of a double click.
203        #[serde(default)]
204        double: bool,
205        #[serde(default)]
206        shift: bool,
207    },
208    /// A drag with the primary button that started where the last
209    /// presentation offered a grab (see [`super::Presentation::grab`]): the
210    /// ray where it went down and the ray where the pointer is now. `done`
211    /// on release.
212    Drag {
213        from: Pointer,
214        to: Pointer,
215        #[serde(default)]
216        done: bool,
217        #[serde(default)]
218        shift: bool,
219    },
220    /// A box drawn by a drag with the primary button from where the last
221    /// presentation offered no grab, while it offered a box (see
222    /// [`super::Presentation::select_box`]): the rays through its corners,
223    /// in order round it, released. `crossing` — drawn right to left — it
224    /// selects what it touches, else what lies wholly in it; `add` — Shift
225    /// or Ctrl held — it adds to the selection rather than replacing it.
226    SelectBox {
227        corners: Box<[Pointer; 4]>,
228        #[serde(default)]
229        crossing: bool,
230        #[serde(default)]
231        add: bool,
232    },
233    /// A key, as the browser names it: `l`, `Escape`, `Enter`, `Delete`.
234    Key { key: String },
235    /// The view settled: `up` is the direction in the world that points up
236    /// the screen. Sent when the camera comes to rest and when the pointer
237    /// enters the viewport, so a step can name what it shows as it is seen
238    /// (a sketch's horizontal is the screen's).
239    View { up: Vector3 },
240}
241
242impl StepEditEvent {
243    /// The event with its pointers moved by `motion` (see
244    /// [`Pointer::moved`]); one without a pointer, as it is.
245    pub fn moved(&self, motion: &Motion) -> GeopResult<Self> {
246        Ok(match self {
247            StepEditEvent::Hover { pointer, shift } => StepEditEvent::Hover {
248                pointer: pointer.moved(motion)?,
249                shift: *shift,
250            },
251            StepEditEvent::Click {
252                pointer,
253                double,
254                shift,
255            } => StepEditEvent::Click {
256                pointer: pointer.moved(motion)?,
257                double: *double,
258                shift: *shift,
259            },
260            StepEditEvent::Drag {
261                from,
262                to,
263                done,
264                shift,
265            } => StepEditEvent::Drag {
266                from: from.moved(motion)?,
267                to: to.moved(motion)?,
268                done: *done,
269                shift: *shift,
270            },
271            StepEditEvent::View { up } => StepEditEvent::View {
272                up: motion.rotate(up),
273            },
274            StepEditEvent::SelectBox {
275                corners,
276                crossing,
277                add,
278            } => {
279                let [a, b, c, d] = &**corners;
280                StepEditEvent::SelectBox {
281                    corners: Box::new([
282                        a.moved(motion)?,
283                        b.moved(motion)?,
284                        c.moved(motion)?,
285                        d.moved(motion)?,
286                    ]),
287                    crossing: *crossing,
288                    add: *add,
289                }
290            }
291            other => other.clone(),
292        })
293    }
294
295    /// The dialog field this event uses, and its value.
296    pub fn dialog(&self) -> Option<(&str, &Value)> {
297        match self {
298            StepEditEvent::Dialog { key, value } => Some((key, value)),
299            _ => None,
300        }
301    }
302
303    /// Where the pointer is, for any event that has one — for a drag, where
304    /// it is now.
305    pub fn pointer(&self) -> Option<&Pointer> {
306        match self {
307            StepEditEvent::Hover { pointer, .. } | StepEditEvent::Click { pointer, .. } => {
308                Some(pointer)
309            }
310            StepEditEvent::Drag { to, .. } => Some(to),
311            _ => None,
312        }
313    }
314}
315
316/// A pointer or key event the editor passes on to an operation, having
317/// taken what is its own: picks, selecting, dragging handles (see
318/// the step editor, [`crate::step_editor`]).
319#[derive(Clone, Debug, PartialEq)]
320pub enum CanvasEvent {
321    /// The pointer moved over the viewport with no button held, `shift`
322    /// held or not.
323    Hover { pointer: Pointer, shift: bool },
324    /// The pointer left the viewport.
325    Leave,
326    /// A primary click that selected nothing: a double one, one while the
327    /// operation has a tool in hand, one on nothing selectable.
328    Click {
329        pointer: Pointer,
330        double: bool,
331        shift: bool,
332    },
333    /// What Escape does: end what is in progress — a chain being drawn,
334    /// the points picked for a tool — and, with nothing in progress, put
335    /// the tool in hand down. The selection is cleared already.
336    Cancel,
337    /// What Enter does: finish what is in progress, a
338    /// chain being drawn, a constraint to apply; the tool stays in hand.
339    Confirm,
340    /// What Delete and Backspace do: delete the selection.
341    Delete,
342    /// The draggable visual `key` dragged in the plane worked in, from
343    /// where it was grabbed to where the pointer is now — `pointer`, with
344    /// `shift` held or not. `done` on release.
345    Move {
346        key: String,
347        from: Vector3,
348        to: Vector3,
349        pointer: Pointer,
350        done: bool,
351        shift: bool,
352    },
353    /// A drag of the tool in hand, where it strokes (see
354    /// [`super::InHand::Strokes`]): the ray where it went down and the ray
355    /// where the pointer is now. `done` on release.
356    Stroke {
357        from: Pointer,
358        to: Pointer,
359        done: bool,
360        shift: bool,
361    },
362    /// A drag of the form's gizmo (see [`super::Gizmo`]): what it did
363    /// since it started, to apply to the step's arguments as they were
364    /// then — which they are again whenever this is sent. `done` on
365    /// release.
366    /// `from` is the gizmo as the step asked for it when the drag started:
367    /// where it stood, along what it stands on's own axes.
368    Gizmo {
369        drag: GizmoDrag,
370        from: Gizmo,
371        done: bool,
372    },
373    /// A selection box, with nothing in hand (see
374    /// [`StepEditEvent::SelectBox`]): what it takes of the selectable
375    /// visuals is the operation's to decide, from what they stand for, and
376    /// to select — added to the selection, if `add`.
377    SelectBox {
378        corners: [Pointer; 4],
379        crossing: bool,
380        add: bool,
381    },
382    /// Any other key, as the browser names it, a letter lowercased: a
383    /// tool's shortcut.
384    Key { key: String },
385    /// The view settled, `up` pointing up the screen (see
386    /// [`StepEditEvent::View`]).
387    View { up: Vector3 },
388}