kittycad_execution_plan/
sketch_types.rs

1//! Types for sketching models.
2use crate::{instruction::InstructionKind, Destination, Instruction};
3use kittycad_execution_plan_macros::ExecutionPlanValue;
4use kittycad_execution_plan_traits::{Address, Value};
5use kittycad_modeling_cmds::shared::{Point2d, Point3d, Point4d};
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9/// A sketch group is a collection of paths.
10#[derive(Clone, ExecutionPlanValue, PartialEq, Debug, Deserialize, Serialize)]
11pub struct SketchGroup {
12    // NOTE to developers
13    // Do NOT reorder these fields without updating the  _offset() methods below.
14    /// The id of the sketch group.
15    pub id: Uuid,
16    /// What the sketch is on (can be a plane or a face).
17    pub on: SketchSurface,
18    /// The position of the sketch group.
19    pub position: Point3d,
20    /// The rotation of the sketch group base plane.
21    pub rotation: Point4d,
22    /// The X, Y and Z axes of this sketch's base plane, in 3D space.
23    pub axes: Axes,
24    /// The plane id or face id of the sketch group.
25    pub entity_id: Option<Uuid>,
26    /// The base path.
27    pub path_first: BasePath,
28    /// Paths after the first path, if any.
29    pub path_rest: Vec<PathSegment>,
30}
31
32impl SketchGroup {
33    /// The `to` end of the last path segment.
34    /// i.e. the point from which the next segment will start.
35    pub fn last_point(&self) -> Point2d<f64> {
36        self.get_last_path_segment().to
37    }
38
39    /// Get the last path segment, find its base path.
40    fn get_last_path_segment(&self) -> &BasePath {
41        match self.path_rest.last() {
42            Some(segment) => segment.get_base(),
43            None => &self.path_first,
44        }
45    }
46
47    /// Get the offset for the `id` field.
48    pub fn path_id_offset() -> usize {
49        0
50    }
51    /// Set the base path of the sketch group.
52    /// `sketch_group` is the starting address of the sketch group.
53    /// `start_point` is the address of the base path's `start` geometric point.
54    /// `tag` is the address of the base path's `tag`.
55    pub fn set_base_path(&self, sketch_group: Address, start_point: Address, tag: Option<Address>) -> Vec<Instruction> {
56        let base_path_addr = sketch_group
57            + self.id.into_parts().len()
58            + self.on.into_parts().len()
59            + self.position.into_parts().len()
60            + self.rotation.into_parts().len()
61            + self.axes.into_parts().len()
62            + self.entity_id.into_parts().len()
63            + self.entity_id.into_parts().len();
64        let mut out = vec![
65            // Copy over the `from` field.
66            Instruction::from(InstructionKind::Copy {
67                source: start_point,
68                destination: Destination::Address(base_path_addr),
69                length: 1,
70            }),
71            // Copy over the `to` field.
72            Instruction::from(InstructionKind::Copy {
73                source: start_point,
74                destination: Destination::Address(base_path_addr + self.path_first.from.into_parts().len()),
75                length: 1,
76            }),
77        ];
78        if let Some(tag) = tag {
79            // Copy over the `name` field.
80            out.push(Instruction::from(InstructionKind::Copy {
81                source: tag,
82                destination: Destination::Address(
83                    base_path_addr + self.path_first.from.into_parts().len() + self.path_first.to.into_parts().len(),
84                ),
85                length: 1,
86            }));
87        }
88        out
89    }
90}
91
92/// The X, Y and Z axes.
93#[derive(Debug, Clone, Copy, ExecutionPlanValue, PartialEq, Deserialize, Serialize)]
94pub struct Axes {
95    #[allow(missing_docs)]
96    pub x: Point3d,
97    #[allow(missing_docs)]
98    pub y: Point3d,
99    #[allow(missing_docs)]
100    pub z: Point3d,
101}
102
103/// A path which starts a SketchGroup.
104#[derive(Debug, Clone, ExecutionPlanValue, PartialEq, Deserialize, Serialize)]
105pub struct BasePath {
106    /// Where the path starts.
107    pub from: Point2d<f64>,
108    /// Where the path ends.
109    pub to: Point2d<f64>,
110    /// The name of the path.
111    pub name: String,
112}
113
114/// Paths are made up of multiple segments, laid out top-to-tail
115/// (i.e. the end of one segment is the start of the next).
116#[derive(Debug, Clone, ExecutionPlanValue, PartialEq, Deserialize, Serialize)]
117#[serde(rename_all = "snake_case", tag = "type")]
118pub enum PathSegment {
119    /// A path that goes to a point.
120    ToPoint {
121        /// Defines the end point, and where the path segment to it started.
122        base: BasePath,
123    },
124    /// A arc that is tangential to the last path segment that goes to a point
125    TangentialArcTo {
126        /// Defines the end point, and where the path segment to it started.
127        base: BasePath,
128        /// the arc's center
129        center: Point2d,
130        /// arc's direction
131        ccw: bool,
132    },
133    /// A path that is horizontal.
134    Horizontal {
135        /// Defines the end point, and where the line to it started.
136        base: BasePath,
137        /// The x coordinate.
138        x: f64,
139    },
140    /// An angled line to.
141    AngledLineTo {
142        /// Defines the end point, and where the line to it started.
143        base: BasePath,
144        /// The x coordinate.
145        x: Option<f64>,
146        /// The y coordinate.
147        y: Option<f64>,
148    },
149    /// A base path.
150    Base {
151        /// Defines the end point, and where the line to it started.
152        base: BasePath,
153    },
154}
155
156impl PathSegment {
157    /// What kind of segment?
158    pub fn segment_kind(&self) -> &'static str {
159        match self {
160            PathSegment::ToPoint { .. } => "ToPoint",
161            PathSegment::TangentialArcTo { .. } => "TangentialArcTo",
162            PathSegment::Horizontal { .. } => "Horizontal",
163            PathSegment::AngledLineTo { .. } => "AngledLineTo",
164            PathSegment::Base { .. } => "Base",
165        }
166    }
167
168    /// Get the BasePath from a segment.
169    fn get_base(&self) -> &BasePath {
170        match self {
171            PathSegment::ToPoint { base } => base,
172            PathSegment::TangentialArcTo { base, .. } => base,
173            PathSegment::Horizontal { base, .. } => base,
174            PathSegment::AngledLineTo { base, .. } => base,
175            PathSegment::Base { base } => base,
176        }
177    }
178}
179
180/// What is being sketched on?
181#[derive(Debug, Clone, Copy, ExecutionPlanValue, PartialEq, Deserialize, Serialize)]
182#[serde(rename_all = "snake_case", tag = "type")]
183pub enum SketchSurface {
184    /// A plane.
185    Plane(Plane),
186}
187
188/// A plane.
189#[derive(Debug, Clone, Copy, ExecutionPlanValue, PartialEq, Deserialize, Serialize)]
190pub struct Plane {
191    /// The id of the plane.
192    pub id: Uuid,
193    /// The code for the plane either a string or custom.
194    pub value: PlaneType,
195    /// Origin of the plane.
196    pub origin: Point3d,
197    /// The plane's axes.
198    pub axes: Axes,
199}
200
201/// Type for a plane.
202#[derive(Debug, Clone, Copy, ExecutionPlanValue, PartialEq, Deserialize, Serialize)]
203#[serde(rename_all = "snake_case", tag = "type")]
204pub enum PlaneType {
205    #[allow(missing_docs)]
206    XY,
207    #[allow(missing_docs)]
208    XZ,
209    #[allow(missing_docs)]
210    YZ,
211    /// A custom plane.
212    Custom,
213}