Skip to main content

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}