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}