Skip to main content

geop_ops_sketch/
add_sketch.rs

1//! [`AddSketch`]: place a sketch in the part.
2
3use geop_core_math::{
4    geop_error::{GeopError, GeopResult, WithContext},
5    scalars::Scalar,
6    with_context,
7};
8use geop_core_sketch::Sketch;
9use geop_ops::{
10    Part, PlacedSketch,
11    operation::{EntityRef, Operation},
12    ui::{CanvasEvent, Edit, Form},
13};
14use serde::{Deserialize, Serialize};
15
16use crate::editor::{self, SketchSession};
17
18/// Adds a sketch on a plane to the part, named by the operation's id: a
19/// planar face, a datum plane or a frame's plane (see [`EntityRef`]). The plane is
20/// resolved when the sketch is added: a sketch on a face stays where the
21/// face was, whatever later operations do to the face.
22#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
23pub struct AddSketch;
24
25#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
26pub struct AddSketchArgs {
27    /// The plane to sketch on: a planar face, a datum plane or a frame's
28    /// plane; none yet, for a new sketch waiting for one to be picked.
29    #[serde(default)]
30    pub plane: Option<EntityRef>,
31    /// The sketch as drawn; solve it first for its constraints to hold.
32    pub sketch: Sketch,
33}
34
35impl Operation for AddSketch {
36    type Args = AddSketchArgs;
37    type Session = SketchSession;
38
39    /// An empty sketch, on no plane yet: where to sketch is the first thing
40    /// a new sketch asks for.
41    fn new_args<S: Scalar>(&self, _before: &Part<S>) -> AddSketchArgs {
42        AddSketchArgs {
43            plane: None,
44            sketch: Sketch::new(),
45        }
46    }
47
48    fn apply<S: Scalar>(
49        &self,
50        mut part: Part<S>,
51        operation_id: &str,
52        args: &AddSketchArgs,
53    ) -> GeopResult<Part<S>> {
54        let ctx = with_context!("add_sketch({operation_id}, plane={:?})", args.plane);
55        args.sketch.validate().with_context(ctx)?;
56        let Some(plane) = &args.plane else {
57            return Err(GeopError::new("pick a plane to sketch on")).with_context(ctx);
58        };
59        let plane = plane.resolve_plane(&part).with_context(ctx)?;
60        let placed = PlacedSketch {
61            plane,
62            sketch: args.sketch.clone(),
63        };
64        part.add_sketch(placed, operation_id).with_context(ctx)?;
65        Ok(part)
66    }
67
68    /// The plane, picked, and the sketch drawn in it: see
69    /// [`crate::editor`].
70    fn form<'a, S: Scalar>(
71        &self,
72        before: &'a Part<S>,
73        args: &AddSketchArgs,
74        s: &SketchSession,
75        selection: &[String],
76    ) -> Form<'a, S, AddSketchArgs, SketchSession> {
77        editor::form(before, args, s, selection)
78    }
79
80    fn event<S: Scalar>(
81        &self,
82        before: &Part<S>,
83        args: &mut AddSketchArgs,
84        session: &mut SketchSession,
85        selection: &mut Vec<String>,
86        event: &CanvasEvent<S>,
87    ) {
88        let edit = Edit {
89            args,
90            session,
91            selection,
92        };
93        editor::event(before, edit, event);
94    }
95}