Skip to main content

geop_core_editor/editing/
visual.rs

1//! [`Visual`]: what an operation draws in the viewport — and
2//! [`Presentation`], what an editor shows of a step.
3
4use geop_core_math::{
5    geop_error::GeopResult,
6    primitives::{CoordinateSystem, Motion},
7    scalars::{Scalar, as_f64},
8    vector::Vector3,
9};
10use serde::Serialize;
11
12use super::{Dialog, GizmoView, Layout};
13use geop_core_program::element::PickedElement;
14
15/// What a visual is.
16#[derive(Clone, Debug, PartialEq, Serialize)]
17#[serde(tag = "shape", rename_all = "snake_case")]
18pub enum Shape {
19    Point {
20        at: Vector3,
21    },
22    Polyline {
23        points: Vec<Vector3>,
24    },
25    /// A filled area.
26    Triangles {
27        triangles: Vec<[Vector3; 3]>,
28    },
29    /// Text at `at`, moved by `offset` — measured in the pointer's reaches
30    /// (see [`super::Reach`]), so it keeps its size on screen — so that
31    /// labels of one point do not stack.
32    Label {
33        at: Vector3,
34        text: String,
35        offset: Vector3,
36    },
37    /// The number field of the visual's key, as something to drag along
38    /// `direction`: moving it by `direction` adds one to the field. Only the
39    /// editor draws these, for a field with a handle (see
40    /// [`super::Track`]).
41    Handle {
42        at: Vector3,
43        direction: Vector3,
44    },
45    /// A part placed in the part, by its instance's name, as the viewer
46    /// draws it already (see [`super::hit::Scene::hit_instance`]): lit when
47    /// hovered or selected, hit where its faces are.
48    Instance {
49        name: String,
50    },
51    /// Three arrows from `at`, along `axes` — the world's `x`, `y` and `z`,
52    /// or a frame's — drawn at a constant size on screen ([`TRIAD`] reaches
53    /// long): what lines are drawn along, to see them by, or the frame a
54    /// mate would put. Hit by nothing.
55    Triad {
56        at: Vector3,
57        axes: [Vector3; 3],
58    },
59}
60
61/// How long a [`Shape::Triad`]'s arrows are, in reaches.
62pub const TRIAD: f64 = 6.0;
63
64/// How far a sheet reaches: a square of `size` about `center`.
65#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
66pub struct Extent {
67    pub center: Vector3,
68    #[serde(with = "as_f64")]
69    pub size: Scalar,
70}
71
72/// What a visual means, which decides how it is drawn.
73#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
74#[serde(rename_all = "snake_case")]
75pub enum Style {
76    /// Can still move: an under-constrained sketch entity.
77    #[default]
78    Free,
79    /// Cannot move any more: a fully constrained one.
80    Fixed,
81    /// Selected — drawn so by the editor, whatever its own style.
82    Selected,
83    /// What a click would take — drawn so by the editor.
84    Hover,
85    /// Part of what cannot be satisfied.
86    Failed,
87    /// Geometry that takes part in nothing but constraints.
88    Construction,
89    /// Geometry given from outside rather than drawn — a sketch's own axes,
90    /// what it projects of the part: constrained against, never moved.
91    Reference,
92    /// What a tool would draw next.
93    Draft,
94    /// A filled area: a closed region of a sketch.
95    Region,
96    /// A help line: a spline's control polygon.
97    Guide,
98    Handle,
99    /// Where what is drawn or dragged would snap to.
100    Snap,
101    /// What the tool in hand would remove.
102    Removed,
103    /// Text printed on a sheet of its own: a drawing's title block, its
104    /// parts list, its views' captions.
105    Paper,
106}
107
108/// A value asked for in place, at `at` in the viewport: a field of the
109/// dialog's, by `key`, typed into as text — what is typed sent back as
110/// [`super::Value::Text`] — starting from `value`.
111#[derive(Clone, Debug, PartialEq, Serialize)]
112pub struct Prompt {
113    pub key: String,
114    pub label: String,
115    pub value: String,
116    pub at: Vector3,
117}
118
119/// Something an operation draws in the viewport, under a key the hit tests
120/// report (see [`super::hit::hit_visuals`]) — and what the user can do with
121/// it, which the editor handles alike for every operation (see
122/// the step editor, [`crate::step_editor`]).
123#[derive(Clone, Debug, PartialEq, Serialize)]
124pub struct Visual {
125    pub key: String,
126    #[serde(flatten)]
127    pub shape: Shape,
128    pub style: Style,
129    /// A click on it selects it, or takes it out of the selection again.
130    #[serde(skip)]
131    pub selectable: bool,
132    /// It can be dragged in the plane worked in: the operation is sent
133    /// where to (see [`super::CanvasEvent::Move`]).
134    #[serde(skip)]
135    pub draggable: bool,
136    /// Each event of a drag of it reaches the step as it was when it was
137    /// grabbed, as a gizmo's do: the operation applies the whole move, from
138    /// where it was grabbed to where the pointer is, to that, and keeps no
139    /// record of where the drag started. Without, it reaches the step as
140    /// the drag's last event left it.
141    #[serde(skip)]
142    pub from_grab: bool,
143}
144
145impl Shape {
146    /// The shape moved by `motion`, out of the part placed as `instance`
147    /// in the part drawn: drawn where that part is placed.
148    pub fn placed(&self, instance: &str, motion: &Motion) -> Self {
149        let at = |p: &Vector3| motion.apply(p);
150        match self {
151            Shape::Point { at: p } => Shape::Point { at: at(p) },
152            Shape::Polyline { points } => Shape::Polyline {
153                points: points.iter().map(at).collect(),
154            },
155            Shape::Triangles { triangles } => Shape::Triangles {
156                triangles: triangles.iter().map(|t| t.map(|p| at(&p))).collect(),
157            },
158            Shape::Label {
159                at: p,
160                text,
161                offset,
162            } => Shape::Label {
163                at: at(p),
164                text: text.clone(),
165                offset: motion.rotate(offset),
166            },
167            Shape::Handle { at: p, direction } => Shape::Handle {
168                at: at(p),
169                direction: motion.rotate(direction),
170            },
171            Shape::Instance { name } => Shape::Instance {
172                name: geop_core_program::placed::instance_path(instance, name),
173            },
174            Shape::Triad { at: p, axes } => Shape::Triad {
175                at: at(p),
176                axes: axes.map(|axis| motion.rotate(&axis)),
177            },
178        }
179    }
180}
181
182impl Presentation {
183    /// The presentation of a step of the part placed as `instance` in the
184    /// part drawn, `motion` where it is placed: what it draws, its gizmo,
185    /// its prompt and its plane moved there, and what it lights the same
186    /// elements of that instance. What it asks to hide is of its own part,
187    /// which is drawn as an instance: it is drawn.
188    pub fn placed(mut self, instance: &str, motion: &Motion) -> GeopResult<Self> {
189        for visual in &mut self.visuals {
190            visual.shape = visual.shape.placed(instance, motion);
191        }
192        self.highlights = (self.highlights.iter())
193            .map(|picked| picked.behind(instance))
194            .collect();
195        self.focus = self
196            .focus
197            .map(|focus| motion.apply_frame(&focus))
198            .transpose()?;
199        if let Some(sheet) = &mut self.sheet {
200            sheet.center = motion.apply(&sheet.center);
201        }
202        if let Some(prompt) = &mut self.prompt {
203            prompt.at = motion.apply(&prompt.at);
204        }
205        self.gizmo = self.gizmo.map(|gizmo| gizmo.moved(motion));
206        self.hidden.clear();
207        Ok(self)
208    }
209}
210
211impl Visual {
212    pub fn new(key: impl Into<String>, shape: Shape, style: Style) -> Self {
213        Self {
214            key: key.into(),
215            shape,
216            style,
217            selectable: false,
218            draggable: false,
219            from_grab: false,
220        }
221    }
222
223    pub fn selectable(mut self) -> Self {
224        self.selectable = true;
225        self
226    }
227
228    pub fn draggable(mut self) -> Self {
229        self.draggable = true;
230        self
231    }
232
233    /// Draggable, each event of a drag applied to the step as it was when
234    /// it was grabbed (see [`Visual::from_grab`]).
235    pub fn draggable_from_grab(mut self) -> Self {
236        self.draggable = true;
237        self.from_grab = true;
238        self
239    }
240}
241
242/// What an editor shows for a step being edited: the operation's
243/// [`super::Form`], and what the editor adds to it — what is picked, selected and
244/// hovered, what a click would pick, and the handles of number fields.
245#[derive(Clone, Debug, PartialEq, Serialize)]
246pub struct Presentation {
247    pub dialog: Dialog,
248    /// Where the dialog is shown (see [`Layout`]).
249    pub layout: Layout,
250    pub visuals: Vec<Visual>,
251    /// Elements of the part to draw lit: what is picked, what a click would
252    /// pick. Not sent: the editor sends what they light
253    /// ([`crate::editor::Update::lit`]).
254    #[serde(skip)]
255    pub highlights: Vec<PickedElement>,
256    /// What a click in the viewport picks right now: elements of these
257    /// kinds.
258    pub pickable: Vec<String>,
259    /// A plane to work in, its `u`/`v` plane: the viewer faces it head on,
260    /// stops orbiting, and draws a grid on it.
261    pub focus: Option<CoordinateSystem>,
262    /// A sheet of its own the step is edited on, in `focus`, and how far
263    /// it reaches: the viewer shows the visuals alone, framed to it, and
264    /// is sent no part to draw (see [`super::Form::sheet`]).
265    pub sheet: Option<Extent>,
266    /// Whether a press where the pointer last hovered starts a drag (sent
267    /// as [`super::StepEditEvent::Drag`]) rather than moving the camera.
268    pub grab: bool,
269    /// Whether a press where the pointer last hovered, offering no grab,
270    /// draws a selection box when dragged (sent as
271    /// [`super::StepEditEvent::SelectBox`]) rather than doing nothing.
272    pub select_box: bool,
273    /// A value asked for in place.
274    pub prompt: Option<Prompt>,
275    /// The gizmo to move, turn or scale by, as drawn.
276    pub gizmo: Option<GizmoView>,
277    /// Solids and faces of the part, by name, the step asks not to draw
278    /// while it is edited (see [`super::Form::hidden`]).
279    pub hidden: Vec<String>,
280    /// Which of what lies under the pointer is lit, and of how many: what
281    /// a front end says while its user steps through them (see
282    /// [`super::Pointer::cycle`]). None where the pointer is over nothing
283    /// a click takes.
284    pub cycle: Option<super::hit::Cycle>,
285}