Skip to main content

geop_ops_parts/operation/
add_sketch.rs

1//! [`AddSketch`]: place a sketch in the part.
2
3use geop_core_math::{
4    geop_error::{GeopResult, WithContext},
5    scalars::Scalar,
6    vector::Vector2,
7    with_context,
8};
9use geop_core_part::{Part, PlacedSketch};
10use geop_core_sketch::Sketch;
11use geop_ops_parts_derive::OperationArgs;
12use serde::{Deserialize, Serialize};
13
14use super::{
15    EntityRef, Handle, HandleGroup, HandleMotion, Operation, arg_path, entity::resolve_plane,
16    extrude::to_f64,
17};
18
19/// Adds a sketch on a plane to the part, named by the operation's id: a base
20/// plane, a planar face or a datum plane (see [`EntityRef`]). The plane is
21/// resolved when the sketch is added: a sketch on a face stays where the
22/// face was, whatever later operations do to the face.
23#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
24pub struct AddSketch;
25
26#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, OperationArgs)]
27pub struct AddSketchArgs {
28    /// The plane to sketch on: a base plane, a planar face or a datum plane.
29    #[arg(Plane)]
30    pub plane: EntityRef,
31    /// The sketch as drawn; solve it first for its constraints to hold.
32    #[arg(Drawing { plane: "plane" })]
33    pub sketch: Sketch,
34}
35
36impl<S: Scalar> Operation<S> for AddSketch {
37    type Args = AddSketchArgs;
38
39    fn apply(
40        &self,
41        mut part: Part<S>,
42        operation_id: &str,
43        args: &AddSketchArgs,
44    ) -> GeopResult<Part<S>> {
45        let ctx = with_context!("add_sketch({operation_id}, plane={:?})", args.plane);
46        args.sketch.validate().with_context(ctx)?;
47        let plane = resolve_plane(&part, &args.plane).with_context(ctx)?;
48        let placed = PlacedSketch {
49            plane,
50            sketch: args.sketch.clone(),
51        };
52        part.add_sketch(placed, operation_id).with_context(ctx)?;
53        Ok(part)
54    }
55
56    /// Every point, as a handle that slides in the sketch plane.
57    fn handles(&self, before: &Part<S>, args: &AddSketchArgs) -> GeopResult<Vec<Handle>> {
58        let plane = resolve_plane(before, &args.plane)?;
59        let (u, v) = (to_f64(plane.u()), to_f64(plane.v()));
60        Ok(args
61            .sketch
62            .points
63            .iter()
64            .map(|(id, p)| {
65                let at = plane.uv_to_xyz(&Vector2::from_array([p.x, p.y].map(S::from_f64)));
66                let path = |c: &str| arg_path(&["sketch", "points", &id.0.to_string(), c]);
67                Handle {
68                    label: id.to_string(),
69                    group: HandleGroup::Sketch,
70                    position: to_f64(&at),
71                    motion: HandleMotion::Planar {
72                        u,
73                        v,
74                        x: path("x"),
75                        y: path("y"),
76                        value: [p.x, p.y],
77                    },
78                }
79            })
80            .collect())
81    }
82}