geop_ops/ui/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 primitives::CoordinateSystem,
6 scalars::Scalar,
7 vector::{Vector2, Vector3},
8};
9use serde::Serialize;
10
11use super::Dialog;
12use crate::operation::{EntityRef, Role};
13
14/// What a visual is.
15#[derive(Clone, Debug, PartialEq, Serialize)]
16#[serde(tag = "shape", rename_all = "snake_case", bound = "S: Scalar")]
17pub enum Shape<S: Scalar> {
18 Point {
19 at: Vector3<S>,
20 },
21 Polyline {
22 points: Vec<Vector3<S>>,
23 },
24 /// A filled area.
25 Triangles {
26 triangles: Vec<[Vector3<S>; 3]>,
27 },
28 /// Text at `at`, moved by `offset` — measured in the pointer's reaches
29 /// (see [`super::Reach`]), so it keeps its size on screen — so that
30 /// labels of one point do not stack.
31 Label {
32 at: Vector3<S>,
33 text: String,
34 offset: Vector3<S>,
35 },
36 /// The number field of the visual's key, as something to drag along
37 /// `direction`: moving it by `direction` adds one to the field. Only the
38 /// editor draws these, for a field with a handle (see
39 /// [`super::Track`]).
40 Handle {
41 at: Vector3<S>,
42 direction: Vector3<S>,
43 },
44}
45
46impl<S: Scalar> Shape<S> {
47 /// The area of `plane`'s `u`/`v` plane inside `outer` but outside
48 /// `holes`, as triangles — to draw a region filled. No triangles for
49 /// loops that do not triangulate.
50 pub fn region(
51 plane: &CoordinateSystem<S>,
52 outer: &[Vector2<S>],
53 holes: &[Vec<Vector2<S>>],
54 ) -> Self {
55 let triangles =
56 geop_ops_rasterize::polygon_triangulate::triangulate_with_holes(outer, holes)
57 .map(|tris| {
58 tris.into_iter()
59 .map(|(a, b, c)| [a, b, c].map(|p| plane.uv_to_xyz(&p)))
60 .collect()
61 })
62 .unwrap_or_default();
63 Shape::Triangles { triangles }
64 }
65}
66
67/// What a visual means, which decides how it is drawn.
68#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
69#[serde(rename_all = "snake_case")]
70pub enum Style {
71 /// Can still move: an under-constrained sketch entity.
72 #[default]
73 Free,
74 /// Cannot move any more: a fully constrained one.
75 Fixed,
76 /// Selected — drawn so by the editor, whatever its own style.
77 Selected,
78 /// What a click would take — drawn so by the editor.
79 Hover,
80 /// Part of what cannot be satisfied.
81 Failed,
82 /// Reference geometry that takes part in nothing but constraints.
83 Construction,
84 /// What a tool would draw next.
85 Draft,
86 /// A filled area: a closed region of a sketch.
87 Region,
88 /// A help line: a spline's control polygon.
89 Guide,
90 Handle,
91}
92
93/// Something an operation draws in the viewport, under a key the hit tests
94/// report (see [`super::hit::hit_visuals`]) — and what the user can do with
95/// it, which the editor handles alike for every operation (see
96/// [`super::StepEditor`]).
97#[derive(Clone, Debug, PartialEq, Serialize)]
98#[serde(bound = "S: Scalar")]
99pub struct Visual<S: Scalar> {
100 pub key: String,
101 #[serde(flatten)]
102 pub shape: Shape<S>,
103 pub style: Style,
104 /// A click on it selects it, or takes it out of the selection again.
105 #[serde(skip)]
106 pub selectable: bool,
107 /// It can be dragged in the plane worked in: the operation is sent
108 /// where to (see [`super::CanvasEvent::Move`]).
109 #[serde(skip)]
110 pub draggable: bool,
111}
112
113impl<S: Scalar> Visual<S> {
114 pub fn new(key: impl Into<String>, shape: Shape<S>, style: Style) -> Self {
115 Self {
116 key: key.into(),
117 shape,
118 style,
119 selectable: false,
120 draggable: false,
121 }
122 }
123
124 pub fn selectable(mut self) -> Self {
125 self.selectable = true;
126 self
127 }
128
129 pub fn draggable(mut self) -> Self {
130 self.draggable = true;
131 self
132 }
133}
134
135/// What an editor shows for a step being edited: the operation's
136/// [`Form`], and what the editor adds to it — what is picked, selected and
137/// hovered, what a click would pick, and the handles of number fields.
138#[derive(Clone, Debug, PartialEq, Serialize)]
139#[serde(bound = "S: Scalar")]
140pub struct Presentation<S: Scalar> {
141 pub dialog: Dialog<S>,
142 pub visuals: Vec<Visual<S>>,
143 /// Entities of the part to draw lit: what is picked, what a click would
144 /// pick.
145 pub highlights: Vec<EntityRef>,
146 /// What a click in the viewport picks right now: entities that can
147 /// fill one of these roles. The viewer shows datums that can, and fades
148 /// the rest.
149 pub pickable: Vec<Role>,
150 /// A plane to work in, its `u`/`v` plane: the viewer faces it head on,
151 /// stops orbiting, and draws a grid on it.
152 pub focus: Option<CoordinateSystem<S>>,
153 /// Whether a press where the pointer last hovered starts a drag (sent
154 /// as [`super::StepEditEvent::Drag`]) rather than moving the camera.
155 pub grab: bool,
156}