kcl_lib/frontend/
sketch.rs

1#![allow(async_fn_in_trait)]
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    ExecutorContext,
7    frontend::api::{
8        Expr, FileId, Number, ObjectId, Plane, ProjectId, Result, SceneGraph, SceneGraphDelta, SourceDelta, Version,
9    },
10};
11
12pub trait SketchApi {
13    /// Execute the sketch in mock mode, without changing anything. This is
14    /// useful after editing segments, and the user releases the mouse button.
15    async fn execute_mock(
16        &mut self,
17        ctx: &ExecutorContext,
18        version: Version,
19        sketch: ObjectId,
20    ) -> Result<(SourceDelta, SceneGraphDelta)>;
21
22    async fn new_sketch(
23        &mut self,
24        ctx: &ExecutorContext,
25        project: ProjectId,
26        file: FileId,
27        version: Version,
28        args: SketchArgs,
29    ) -> Result<(SourceDelta, SceneGraphDelta, ObjectId)>;
30
31    // Enters sketch mode
32    async fn edit_sketch(
33        &mut self,
34        ctx: &ExecutorContext,
35        project: ProjectId,
36        file: FileId,
37        version: Version,
38        sketch: ObjectId,
39    ) -> Result<SceneGraphDelta>;
40
41    async fn exit_sketch(&mut self, ctx: &ExecutorContext, version: Version, sketch: ObjectId) -> Result<SceneGraph>;
42
43    async fn delete_sketch(
44        &mut self,
45        ctx: &ExecutorContext,
46        version: Version,
47        sketch: ObjectId,
48    ) -> Result<(SourceDelta, SceneGraphDelta)>;
49
50    async fn add_segment(
51        &mut self,
52        ctx: &ExecutorContext,
53        version: Version,
54        sketch: ObjectId,
55        segment: SegmentCtor,
56        label: Option<String>,
57    ) -> Result<(SourceDelta, SceneGraphDelta)>;
58
59    async fn edit_segments(
60        &mut self,
61        ctx: &ExecutorContext,
62        version: Version,
63        sketch: ObjectId,
64        segments: Vec<ExistingSegmentCtor>,
65    ) -> Result<(SourceDelta, SceneGraphDelta)>;
66
67    async fn delete_objects(
68        &mut self,
69        ctx: &ExecutorContext,
70        version: Version,
71        sketch: ObjectId,
72        constraint_ids: Vec<ObjectId>,
73        segment_ids: Vec<ObjectId>,
74    ) -> Result<(SourceDelta, SceneGraphDelta)>;
75
76    async fn add_constraint(
77        &mut self,
78        ctx: &ExecutorContext,
79        version: Version,
80        sketch: ObjectId,
81        constraint: Constraint,
82    ) -> Result<(SourceDelta, SceneGraphDelta)>;
83
84    async fn chain_segment(
85        &mut self,
86        ctx: &ExecutorContext,
87        version: Version,
88        sketch: ObjectId,
89        previous_segment_end_point_id: ObjectId,
90        segment: SegmentCtor,
91        label: Option<String>,
92    ) -> Result<(SourceDelta, SceneGraphDelta)>;
93
94    async fn edit_constraint(
95        &mut self,
96        ctx: &ExecutorContext,
97        version: Version,
98        sketch: ObjectId,
99        constraint_id: ObjectId,
100        constraint: Constraint,
101    ) -> Result<(SourceDelta, SceneGraphDelta)>;
102}
103
104#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
105#[ts(export, export_to = "FrontendApi.ts", rename = "ApiSketch")]
106pub struct Sketch {
107    pub args: SketchArgs,
108    pub segments: Vec<ObjectId>,
109    pub constraints: Vec<ObjectId>,
110}
111
112#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
113#[ts(export, export_to = "FrontendApi.ts")]
114pub struct SketchArgs {
115    pub on: Plane,
116}
117
118#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
119#[ts(export, export_to = "FrontendApi.ts", rename = "ApiPoint")]
120pub struct Point {
121    pub position: Point2d<Number>,
122    pub ctor: Option<PointCtor>,
123    pub owner: Option<ObjectId>,
124    pub freedom: Option<Freedom>,
125    pub constraints: Vec<ObjectId>,
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize, ts_rs::TS)]
129#[ts(export, export_to = "FrontendApi.ts")]
130pub enum Freedom {
131    Free,
132    Fixed,
133    Conflict,
134}
135
136impl Freedom {
137    /// Merges two Freedom values. For example, a point has a solver variable
138    /// for each dimension, x and y. If one dimension is `Free` and the other is
139    /// `Fixed`, the point overall is `Free` since it isn't fully constrained.
140    /// `Conflict` infects the most, followed by `Free`. An object must be fully
141    /// `Fixed` to be `Fixed` overall.
142    pub fn merge(self, other: Self) -> Self {
143        match (self, other) {
144            (Self::Conflict, _) | (_, Self::Conflict) => Self::Conflict,
145            (Self::Free, _) | (_, Self::Free) => Self::Free,
146            (Self::Fixed, Self::Fixed) => Self::Fixed,
147        }
148    }
149}
150
151#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
152#[ts(export, export_to = "FrontendApi.ts", rename = "ApiSegment")]
153#[serde(tag = "type")]
154pub enum Segment {
155    Point(Point),
156    Line(Line),
157    Arc(Arc),
158    Circle(Circle),
159}
160
161#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
162#[ts(export, export_to = "FrontendApi.ts")]
163pub struct ExistingSegmentCtor {
164    pub id: ObjectId,
165    pub ctor: SegmentCtor,
166}
167
168#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
169#[ts(export, export_to = "FrontendApi.ts")]
170#[serde(tag = "type")]
171pub enum SegmentCtor {
172    Point(PointCtor),
173    Line(LineCtor),
174    Arc(ArcCtor),
175    TangentArc(TangentArcCtor),
176    Circle(CircleCtor),
177}
178
179#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
180#[ts(export, export_to = "FrontendApi.ts")]
181pub struct PointCtor {
182    pub position: Point2d<Expr>,
183}
184
185#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
186#[ts(export, export_to = "FrontendApi.ts", rename = "ApiPoint2d")]
187pub struct Point2d<U: std::fmt::Debug + Clone + ts_rs::TS> {
188    pub x: U,
189    pub y: U,
190}
191
192#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
193#[ts(export, export_to = "FrontendApi.ts", rename = "ApiLine")]
194pub struct Line {
195    pub start: ObjectId,
196    pub end: ObjectId,
197    // Invariant: Line or MidPointLine
198    pub ctor: SegmentCtor,
199    // The constructor is applicable if changing the values of the constructor will change the rendering
200    // of the segment (modulo multiple valid solutions). I.e., whether the object is constrained with
201    // respect to the constructor inputs.
202    // The frontend should only display handles for the constructor inputs if the ctor is applicable.
203    // (Or because they are the (locked) start/end of the segment).
204    pub ctor_applicable: bool,
205}
206
207#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
208#[ts(export, export_to = "FrontendApi.ts")]
209pub struct LineCtor {
210    pub start: Point2d<Expr>,
211    pub end: Point2d<Expr>,
212}
213
214#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
215#[ts(export, export_to = "FrontendApi.ts", rename = "ApiStartOrEnd")]
216#[serde(tag = "type")]
217pub enum StartOrEnd<T> {
218    Start(T),
219    End(T),
220}
221
222#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
223#[ts(export, export_to = "FrontendApi.ts", rename = "ApiArc")]
224pub struct Arc {
225    pub start: ObjectId,
226    pub end: ObjectId,
227    pub center: ObjectId,
228    // Invariant: Arc or TangentArc
229    pub ctor: SegmentCtor,
230    pub ctor_applicable: bool,
231}
232
233#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
234#[ts(export, export_to = "FrontendApi.ts")]
235pub struct ArcCtor {
236    pub start: Point2d<Expr>,
237    pub end: Point2d<Expr>,
238    pub center: Point2d<Expr>,
239}
240
241#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
242#[ts(export, export_to = "FrontendApi.ts")]
243pub struct TangentArcCtor {
244    pub start: Point2d<Expr>,
245    pub end: Point2d<Expr>,
246    pub tangent: StartOrEnd<ObjectId>,
247}
248
249#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
250#[ts(export, export_to = "FrontendApi.ts", rename = "ApiCircle")]
251pub struct Circle {
252    pub start: ObjectId,
253    pub radius: Number,
254    // Invariant: Circle or ThreePointCircle
255    pub ctor: SegmentCtor,
256    pub ctor_applicable: bool,
257}
258
259#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
260#[ts(export, export_to = "FrontendApi.ts")]
261pub struct CircleCtor {
262    pub center: Point2d<Expr>,
263    pub radius: Expr,
264}
265
266#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
267#[ts(export, export_to = "FrontendApi.ts", rename = "ApiConstraint")]
268#[serde(tag = "type")]
269pub enum Constraint {
270    Coincident(Coincident),
271    Distance(Distance),
272    Horizontal(Horizontal),
273    LinesEqualLength(LinesEqualLength),
274    Parallel(Parallel),
275    Perpendicular(Perpendicular),
276    Vertical(Vertical),
277}
278
279#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
280#[ts(export, export_to = "FrontendApi.ts")]
281pub struct Coincident {
282    pub segments: Vec<ObjectId>,
283}
284
285#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
286#[ts(export, export_to = "FrontendApi.ts")]
287pub struct Distance {
288    pub points: Vec<ObjectId>,
289    pub distance: Number,
290}
291
292#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
293#[ts(export, export_to = "FrontendApi.ts")]
294pub struct Horizontal {
295    pub line: ObjectId,
296}
297
298#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
299#[ts(export, export_to = "FrontendApi.ts")]
300pub struct LinesEqualLength {
301    pub lines: Vec<ObjectId>,
302}
303
304#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
305#[ts(export, export_to = "FrontendApi.ts")]
306pub struct Vertical {
307    pub line: ObjectId,
308}
309
310#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
311#[ts(export, export_to = "FrontendApi.ts", optional_fields)]
312pub struct Parallel {
313    pub lines: Vec<ObjectId>,
314}
315
316#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
317#[ts(export, export_to = "FrontendApi.ts", optional_fields)]
318pub struct Perpendicular {
319    pub lines: Vec<ObjectId>,
320}