Skip to main content

geop_core_part/
sketch.rs

1//! A [`Part`]'s sketches: named like any other entity, and stored with the
2//! plane they were placed on.
3
4use geop_core_math::{
5    geop_error::{GeopError, GeopResult},
6    primitives::CoordinateSystem,
7    scalars::Scalar,
8};
9use geop_core_sketch::Sketch;
10
11use crate::ids::SketchId;
12use crate::part::Part;
13
14/// A sketch together with the plane it lies on: `plane.u`/`plane.v` are the
15/// sketch's `x`/`y` (unit, orthogonal), `plane.w = u x v` its unit normal.
16///
17/// The plane is resolved once, when the sketch is added — a sketch drawn on a
18/// face stays where that face *was*, even after a later operation reshapes or
19/// consumes the face.
20#[derive(Clone, Debug)]
21pub struct PlacedSketch<S: Scalar> {
22    pub plane: CoordinateSystem<S>,
23    pub sketch: Sketch,
24}
25
26impl<S: Scalar> Part<S> {
27    /// Adds `placed` to the part under `name`. Fails, leaving the part
28    /// unchanged, if `name` is already taken.
29    pub fn add_sketch(
30        &mut self,
31        placed: PlacedSketch<S>,
32        name: impl Into<String>,
33    ) -> GeopResult<SketchId> {
34        let id = SketchId(self.fresh_id());
35        self.names.insert(id, name)?;
36        self.sketches.insert(id, placed);
37        Ok(id)
38    }
39
40    pub fn remove_sketch(&mut self, id: SketchId) -> GeopResult<()> {
41        if self.sketches.remove(&id).is_none() {
42            return Err(GeopError::new(format!("Part has no sketch {id}")));
43        }
44        self.names.remove(id);
45        Ok(())
46    }
47
48    pub fn sketch(&self, id: SketchId) -> GeopResult<&PlacedSketch<S>> {
49        self.sketches
50            .get(&id)
51            .ok_or_else(|| GeopError::new(format!("Part has no sketch {id}")))
52    }
53
54    /// Every sketch, in the order they were added.
55    pub fn sketches(&self) -> impl Iterator<Item = (SketchId, &PlacedSketch<S>)> {
56        self.sketches.iter().map(|(&id, s)| (id, s))
57    }
58}