kittycad_modeling_cmds/
shared.rs

1use enum_iterator::Sequence;
2use parse_display_derive::{Display, FromStr};
3use schemars::{schema::SchemaObject, JsonSchema};
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7#[cfg(feature = "cxx")]
8use crate::impl_extern_type;
9use crate::{length_unit::LengthUnit, output::ExtrusionFaceInfo, units::UnitAngle};
10
11pub use point::{Point2d, Point3d, Point4d, Quaternion};
12
13mod point;
14
15/// What kind of cut to do
16#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
17#[serde(rename_all = "snake_case")]
18#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
19#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
20pub enum CutType {
21    /// Round off an edge.
22    #[default]
23    Fillet,
24    /// Cut away an edge.
25    Chamfer,
26}
27
28/// A rotation defined by an axis, origin of rotation, and an angle.
29#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
30#[serde(rename_all = "snake_case")]
31#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
32#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
33pub struct Rotation {
34    /// Rotation axis.
35    /// Defaults to (0, 0, 1) (i.e. the Z axis).
36    pub axis: Point3d<f64>,
37    /// Rotate this far about the rotation axis.
38    /// Defaults to zero (i.e. no rotation).
39    pub angle: Angle,
40    /// Origin of the rotation. If one isn't provided, the object will rotate about its own bounding box center.
41    pub origin: OriginType,
42}
43
44impl Default for Rotation {
45    /// z-axis, 0 degree angle, and local origin.
46    fn default() -> Self {
47        Self {
48            axis: z_axis(),
49            angle: Angle::default(),
50            origin: OriginType::Local,
51        }
52    }
53}
54
55/// Ways to transform each solid being replicated in a repeating pattern.
56#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
57#[serde(rename_all = "snake_case")]
58#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
59#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
60pub struct Transform {
61    /// Translate the replica this far along each dimension.
62    /// Defaults to zero vector (i.e. same position as the original).
63    #[serde(default)]
64    pub translate: Point3d<LengthUnit>,
65    /// Scale the replica's size along each axis.
66    /// Defaults to (1, 1, 1) (i.e. the same size as the original).
67    #[serde(default = "same_scale")]
68    pub scale: Point3d<f64>,
69    /// Rotate the replica about the specified rotation axis and origin.
70    /// Defaults to no rotation.
71    #[serde(default)]
72    pub rotation: Rotation,
73    /// Whether to replicate the original solid in this instance.
74    #[serde(default = "bool_true")]
75    pub replicate: bool,
76}
77
78impl Default for Transform {
79    fn default() -> Self {
80        Self {
81            scale: same_scale(),
82            replicate: true,
83            translate: Default::default(),
84            rotation: Rotation::default(),
85        }
86    }
87}
88
89/// Options for annotations
90#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
91#[serde(rename_all = "snake_case")]
92#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
93#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
94pub struct AnnotationOptions {
95    /// Text displayed on the annotation
96    pub text: Option<AnnotationTextOptions>,
97    /// How to style the start and end of the line
98    pub line_ends: Option<AnnotationLineEndOptions>,
99    /// Width of the annotation's line
100    pub line_width: Option<f32>,
101    /// Color to render the annotation
102    pub color: Option<Color>,
103    /// Position to put the annotation
104    pub position: Option<Point3d<f32>>,
105}
106
107/// Options for annotation text
108#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
109#[serde(rename_all = "snake_case")]
110#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
111#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
112pub struct AnnotationLineEndOptions {
113    /// How to style the start of the annotation line.
114    pub start: AnnotationLineEnd,
115    /// How to style the end of the annotation line.
116    pub end: AnnotationLineEnd,
117}
118
119/// Options for annotation text
120#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
121#[serde(rename_all = "snake_case")]
122#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
123#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
124pub struct AnnotationTextOptions {
125    /// Alignment along the X axis
126    pub x: AnnotationTextAlignmentX,
127    /// Alignment along the Y axis
128    pub y: AnnotationTextAlignmentY,
129    /// Text displayed on the annotation
130    pub text: String,
131    /// Text font's point size
132    pub point_size: u32,
133}
134
135/// The type of distance
136/// Distances can vary depending on
137/// the objects used as input.
138#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
139#[serde(rename_all = "snake_case", tag = "type")]
140#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
141#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
142pub enum DistanceType {
143    /// Euclidean Distance.
144    Euclidean {},
145    /// The distance between objects along the specified axis
146    OnAxis {
147        /// Global axis
148        axis: GlobalAxis,
149    },
150}
151
152/// The type of origin
153#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
154#[serde(rename_all = "snake_case", tag = "type")]
155#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
156#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
157pub enum OriginType {
158    /// Local Origin (center of object bounding box).
159    #[default]
160    Local,
161    /// Global Origin (0, 0, 0).
162    Global,
163    /// Custom Origin (user specified point).
164    Custom {
165        /// Custom origin point.
166        origin: Point3d<f64>,
167    },
168}
169
170/// An RGBA color
171#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
172#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
173#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
174pub struct Color {
175    /// Red
176    pub r: f32,
177    /// Green
178    pub g: f32,
179    /// Blue
180    pub b: f32,
181    /// Alpha
182    pub a: f32,
183}
184
185/// Horizontal Text alignment
186#[allow(missing_docs)]
187#[derive(
188    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
189)]
190#[serde(rename_all = "lowercase")]
191#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
192#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
193pub enum AnnotationTextAlignmentX {
194    Left,
195    Center,
196    Right,
197}
198
199/// Vertical Text alignment
200#[allow(missing_docs)]
201#[derive(
202    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
203)]
204#[serde(rename_all = "lowercase")]
205#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
206#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
207pub enum AnnotationTextAlignmentY {
208    Bottom,
209    Center,
210    Top,
211}
212
213/// Annotation line end type
214#[allow(missing_docs)]
215#[derive(
216    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
217)]
218#[serde(rename_all = "lowercase")]
219#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
220#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
221pub enum AnnotationLineEnd {
222    None,
223    Arrow,
224}
225
226/// The type of annotation
227#[derive(
228    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
229)]
230#[serde(rename_all = "lowercase")]
231#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
232#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
233pub enum AnnotationType {
234    /// 2D annotation type (screen or planar space)
235    T2D,
236    /// 3D annotation type
237    T3D,
238}
239
240/// The type of camera drag interaction.
241#[derive(
242    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
243)]
244#[serde(rename_all = "lowercase")]
245#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
246#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
247pub enum CameraDragInteractionType {
248    /// Camera pan
249    Pan,
250    /// Camera rotate (spherical camera revolve/orbit)
251    Rotate,
252    /// Camera rotate (trackball with 3 degrees of freedom)
253    RotateTrackball,
254    /// Camera zoom (increase or decrease distance to reference point center)
255    Zoom,
256}
257
258/// A segment of a path.
259/// Paths are composed of many segments.
260#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)]
261#[serde(rename_all = "snake_case", tag = "type")]
262#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
263#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
264pub enum PathSegment {
265    /// A straight line segment.
266    /// Goes from the current path "pen" to the given endpoint.
267    Line {
268        /// End point of the line.
269        end: Point3d<LengthUnit>,
270        ///Whether or not this line is a relative offset
271        relative: bool,
272    },
273    /// A circular arc segment.
274    /// Arcs can be drawn clockwise when start > end.
275    Arc {
276        /// Center of the circle
277        center: Point2d<LengthUnit>,
278        /// Radius of the circle
279        radius: LengthUnit,
280        /// Start of the arc along circle's perimeter.
281        start: Angle,
282        /// End of the arc along circle's perimeter.
283        end: Angle,
284        ///Whether or not this arc is a relative offset
285        relative: bool,
286    },
287    /// A cubic bezier curve segment.
288    /// Start at the end of the current line, go through control point 1 and 2, then end at a
289    /// given point.
290    Bezier {
291        /// First control point.
292        control1: Point3d<LengthUnit>,
293        /// Second control point.
294        control2: Point3d<LengthUnit>,
295        /// Final control point.
296        end: Point3d<LengthUnit>,
297        ///Whether or not this bezier is a relative offset
298        relative: bool,
299    },
300    /// Adds a tangent arc from current pen position with the given radius and angle.
301    TangentialArc {
302        /// Radius of the arc.
303        /// Not to be confused with Raiders of the Lost Ark.
304        radius: LengthUnit,
305        /// Offset of the arc. Negative values will arc clockwise.
306        offset: Angle,
307    },
308    /// Adds a tangent arc from current pen position to the new position.
309    /// Arcs will choose a clockwise or counter-clockwise direction based on the arc end position.
310    TangentialArcTo {
311        /// Where the arc should end.
312        /// Must lie in the same plane as the current path pen position.
313        /// Must not be colinear with current path pen position.
314        to: Point3d<LengthUnit>,
315        /// 0 will be interpreted as none/null.
316        angle_snap_increment: Option<Angle>,
317    },
318    ///Adds an arc from the current position that goes through the given interior point and ends at the given end position
319    ArcTo {
320        /// Interior point of the arc.
321        interior: Point3d<LengthUnit>,
322        /// End point of the arc.
323        end: Point3d<LengthUnit>,
324        ///Whether or not interior and end are relative to the previous path position
325        relative: bool,
326    },
327}
328
329/// An angle, with a specific unit.
330#[derive(Clone, Copy, PartialEq, Debug, JsonSchema, Deserialize, Serialize)]
331#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
332#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
333pub struct Angle {
334    /// What unit is the measurement?
335    pub unit: UnitAngle,
336    /// The size of the angle, measured in the chosen unit.
337    pub value: f64,
338}
339
340impl Angle {
341    /// Converts a given angle to degrees.
342    pub fn to_degrees(self) -> f64 {
343        match self.unit {
344            UnitAngle::Degrees => self.value,
345            UnitAngle::Radians => self.value.to_degrees(),
346        }
347    }
348    /// Converts a given angle to radians.
349    pub fn to_radians(self) -> f64 {
350        match self.unit {
351            UnitAngle::Degrees => self.value.to_radians(),
352            UnitAngle::Radians => self.value,
353        }
354    }
355    /// Create an angle in degrees.
356    pub const fn from_degrees(value: f64) -> Self {
357        Self {
358            unit: UnitAngle::Degrees,
359            value,
360        }
361    }
362    /// Create an angle in radians.
363    pub const fn from_radians(value: f64) -> Self {
364        Self {
365            unit: UnitAngle::Radians,
366            value,
367        }
368    }
369    /// 360 degrees.
370    pub const fn turn() -> Self {
371        Self::from_degrees(360.0)
372    }
373    /// 180 degrees.
374    pub const fn half_circle() -> Self {
375        Self::from_degrees(180.0)
376    }
377    /// 90 degrees.
378    pub const fn quarter_circle() -> Self {
379        Self::from_degrees(90.0)
380    }
381    /// 0 degrees.
382    pub const fn zero() -> Self {
383        Self::from_degrees(0.0)
384    }
385}
386
387/// 0 degrees.
388impl Default for Angle {
389    /// 0 degrees.
390    fn default() -> Self {
391        Self::zero()
392    }
393}
394
395impl PartialOrd for Angle {
396    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
397        match (self.unit, other.unit) {
398            // Avoid unnecessary floating point operations.
399            (UnitAngle::Degrees, UnitAngle::Degrees) => self.value.partial_cmp(&other.value),
400            (UnitAngle::Radians, UnitAngle::Radians) => self.value.partial_cmp(&other.value),
401            _ => self.to_degrees().partial_cmp(&other.to_degrees()),
402        }
403    }
404}
405
406impl std::ops::Add for Angle {
407    type Output = Self;
408
409    fn add(self, rhs: Self) -> Self::Output {
410        Self {
411            unit: UnitAngle::Degrees,
412            value: self.to_degrees() + rhs.to_degrees(),
413        }
414    }
415}
416
417impl std::ops::AddAssign for Angle {
418    fn add_assign(&mut self, rhs: Self) {
419        match self.unit {
420            UnitAngle::Degrees => {
421                self.value += rhs.to_degrees();
422            }
423            UnitAngle::Radians => {
424                self.value += rhs.to_radians();
425            }
426        }
427    }
428}
429
430/// The type of scene selection change
431#[derive(
432    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
433)]
434#[serde(rename_all = "lowercase")]
435#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
436#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
437pub enum SceneSelectionType {
438    /// Replaces the selection
439    Replace,
440    /// Adds to the selection
441    Add,
442    /// Removes from the selection
443    Remove,
444}
445
446/// The type of scene's active tool
447#[allow(missing_docs)]
448#[derive(
449    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
450)]
451#[serde(rename_all = "snake_case")]
452#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
453#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
454pub enum SceneToolType {
455    CameraRevolve,
456    Select,
457    Move,
458    SketchLine,
459    SketchTangentialArc,
460    SketchCurve,
461    SketchCurveMod,
462}
463
464/// The path component constraint bounds type
465#[allow(missing_docs)]
466#[derive(
467    Display,
468    FromStr,
469    Copy,
470    Eq,
471    PartialEq,
472    Debug,
473    JsonSchema,
474    Deserialize,
475    Serialize,
476    Sequence,
477    Clone,
478    Ord,
479    PartialOrd,
480    Default,
481)]
482#[serde(rename_all = "snake_case")]
483#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
484#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
485pub enum PathComponentConstraintBound {
486    #[default]
487    Unconstrained,
488    PartiallyConstrained,
489    FullyConstrained,
490}
491
492/// The path component constraint type
493#[allow(missing_docs)]
494#[derive(
495    Display,
496    FromStr,
497    Copy,
498    Eq,
499    PartialEq,
500    Debug,
501    JsonSchema,
502    Deserialize,
503    Serialize,
504    Sequence,
505    Clone,
506    Ord,
507    PartialOrd,
508    Default,
509)]
510#[serde(rename_all = "snake_case")]
511#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
512#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
513pub enum PathComponentConstraintType {
514    #[default]
515    Unconstrained,
516    Vertical,
517    Horizontal,
518    EqualLength,
519    Parallel,
520    AngleBetween,
521}
522
523/// The path component command type (within a Path)
524#[allow(missing_docs)]
525#[derive(
526    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
527)]
528#[serde(rename_all = "snake_case")]
529#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
530#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
531pub enum PathCommand {
532    MoveTo,
533    LineTo,
534    BezCurveTo,
535    NurbsCurveTo,
536    AddArc,
537}
538
539/// The type of entity
540#[allow(missing_docs)]
541#[derive(
542    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
543)]
544#[serde(rename_all = "lowercase")]
545#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
546#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
547#[repr(u8)]
548pub enum EntityType {
549    Entity,
550    Object,
551    Path,
552    Curve,
553    Solid2D,
554    Solid3D,
555    Edge,
556    Face,
557    Plane,
558    Vertex,
559}
560
561/// The type of Curve (embedded within path)
562#[allow(missing_docs)]
563#[derive(
564    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
565)]
566#[serde(rename_all = "snake_case")]
567#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
568#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
569pub enum CurveType {
570    Line,
571    Arc,
572    Nurbs,
573}
574
575/// A file to be exported to the client.
576#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
577pub struct ExportFile {
578    /// The name of the file.
579    pub name: String,
580    /// The contents of the file, base64 encoded.
581    pub contents: crate::base64::Base64Data,
582}
583
584/// The valid types of output file formats.
585#[derive(
586    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd, Sequence,
587)]
588#[serde(rename_all = "lowercase")]
589#[display(style = "lowercase")]
590#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
591#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
592pub enum FileExportFormat {
593    /// Autodesk Filmbox (FBX) format. <https://en.wikipedia.org/wiki/FBX>
594    Fbx,
595    /// Binary glTF 2.0.
596    ///
597    /// This is a single binary with .glb extension.
598    ///
599    /// This is better if you want a compressed format as opposed to the human readable
600    /// glTF that lacks compression.
601    Glb,
602    /// glTF 2.0.
603    /// Embedded glTF 2.0 (pretty printed).
604    ///
605    /// Single JSON file with .gltf extension binary data encoded as
606    /// base64 data URIs.
607    ///
608    /// The JSON contents are pretty printed.
609    ///
610    /// It is human readable, single file, and you can view the
611    /// diff easily in a git commit.
612    Gltf,
613    /// The OBJ file format. <https://en.wikipedia.org/wiki/Wavefront_.obj_file>
614    /// It may or may not have an an attached material (mtl // mtllib) within the file,
615    /// but we interact with it as if it does not.
616    Obj,
617    /// The PLY file format. <https://en.wikipedia.org/wiki/PLY_(file_format)>
618    Ply,
619    /// The STEP file format. <https://en.wikipedia.org/wiki/ISO_10303-21>
620    Step,
621    /// The STL file format. <https://en.wikipedia.org/wiki/STL_(file_format)>
622    Stl,
623}
624
625/// The valid types of 2D output file formats.
626#[derive(
627    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd, Sequence,
628)]
629#[serde(rename_all = "lowercase")]
630#[display(style = "lowercase")]
631#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
632#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
633pub enum FileExportFormat2d {
634    /// AutoCAD drawing interchange format.
635    Dxf,
636}
637
638/// The valid types of source file formats.
639#[derive(
640    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd, Sequence,
641)]
642#[serde(rename_all = "lowercase")]
643#[display(style = "lowercase")]
644#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
645#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
646pub enum FileImportFormat {
647    /// Autodesk Filmbox (FBX) format. <https://en.wikipedia.org/wiki/FBX>
648    Fbx,
649    /// glTF 2.0.
650    Gltf,
651    /// The OBJ file format. <https://en.wikipedia.org/wiki/Wavefront_.obj_file>
652    /// It may or may not have an an attached material (mtl // mtllib) within the file,
653    /// but we interact with it as if it does not.
654    Obj,
655    /// The PLY file format. <https://en.wikipedia.org/wiki/PLY_(file_format)>
656    Ply,
657    /// SolidWorks part (SLDPRT) format.
658    Sldprt,
659    /// The STEP file format. <https://en.wikipedia.org/wiki/ISO_10303-21>
660    Step,
661    /// The STL file format. <https://en.wikipedia.org/wiki/STL_(file_format)>
662    Stl,
663}
664
665/// The type of error sent by the KittyCAD graphics engine.
666#[derive(Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd)]
667#[serde(rename_all = "snake_case")]
668#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
669#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
670pub enum EngineErrorCode {
671    /// User requested something geometrically or graphically impossible.
672    /// Don't retry this request, as it's inherently impossible. Instead, read the error message
673    /// and change your request.
674    BadRequest = 1,
675    /// Graphics engine failed to complete request, consider retrying
676    InternalEngine,
677}
678
679impl From<EngineErrorCode> for http::StatusCode {
680    fn from(e: EngineErrorCode) -> Self {
681        match e {
682            EngineErrorCode::BadRequest => Self::BAD_REQUEST,
683            EngineErrorCode::InternalEngine => Self::INTERNAL_SERVER_ERROR,
684        }
685    }
686}
687
688/// IDs for the extruded faces.
689#[derive(Debug, PartialEq, Serialize, Deserialize, JsonSchema, Clone)]
690#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
691#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
692pub struct ExtrudedFaceInfo {
693    /// The face made from the original 2D shape being extruded.
694    /// If the solid is extruded from a shape which already has an ID
695    /// (e.g. extruding something which was sketched on a face), this
696    /// doesn't need to be sent.
697    pub bottom: Option<Uuid>,
698    /// Top face of the extrusion (parallel and further away from the original 2D shape being extruded).
699    pub top: Uuid,
700    /// Any intermediate sides between the top and bottom.
701    pub sides: Vec<SideFace>,
702}
703
704/// IDs for a side face, extruded from the path of some sketch/2D shape.
705#[derive(Debug, PartialEq, Serialize, Deserialize, JsonSchema, Clone)]
706#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
707#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
708pub struct SideFace {
709    /// ID of the path this face is being extruded from.
710    pub path_id: Uuid,
711    /// Desired ID for the resulting face.
712    pub face_id: Uuid,
713}
714
715/// Camera settings including position, center, fov etc
716#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
717#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
718#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
719pub struct CameraSettings {
720    ///Camera position (vantage)
721    pub pos: Point3d,
722
723    ///Camera's look-at center (center-pos gives viewing vector)
724    pub center: Point3d,
725
726    ///Camera's world-space up vector
727    pub up: Point3d,
728
729    ///The Camera's orientation (in the form of a quaternion)
730    pub orientation: Quaternion,
731
732    ///Camera's field-of-view angle (if ortho is false)
733    pub fov_y: Option<f32>,
734
735    ///The camera's ortho scale (derived from viewing distance if ortho is true)
736    pub ortho_scale: Option<f32>,
737
738    ///Whether or not the camera is in ortho mode
739    pub ortho: bool,
740}
741
742#[allow(missing_docs)]
743#[repr(u8)]
744#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
745#[serde(rename_all = "snake_case")]
746#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
747#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
748pub enum WorldCoordinateSystem {
749    #[default]
750    RightHandedUpZ,
751    RightHandedUpY,
752}
753
754#[allow(missing_docs)]
755#[repr(C)]
756#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
757#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
758#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
759pub struct CameraViewState {
760    pub pivot_rotation: [f32; 4],
761    pub pivot_position: [f32; 3],
762    pub eye_offset: f32,
763    pub fov_y: f32,
764    pub ortho_scale_factor: f32,
765    pub is_ortho: bool,
766    pub ortho_scale_enabled: bool,
767    pub world_coord_system: WorldCoordinateSystem,
768}
769
770impl Default for CameraViewState {
771    fn default() -> Self {
772        CameraViewState {
773            pivot_rotation: [0.0, 0.0, 0.0, 1.0],
774            pivot_position: Default::default(),
775            eye_offset: 10.0,
776            fov_y: 45.0,
777            ortho_scale_factor: 1.6,
778            is_ortho: false,
779            ortho_scale_enabled: true,
780            world_coord_system: WorldCoordinateSystem::default(),
781        }
782    }
783}
784
785#[cfg(feature = "cxx")]
786impl_extern_type! {
787    [Trivial]
788    CameraViewState = "Endpoints::CameraViewState"
789}
790
791impl From<CameraSettings> for crate::output::DefaultCameraZoom {
792    fn from(settings: CameraSettings) -> Self {
793        Self { settings }
794    }
795}
796impl From<CameraSettings> for crate::output::CameraDragMove {
797    fn from(settings: CameraSettings) -> Self {
798        Self { settings }
799    }
800}
801impl From<CameraSettings> for crate::output::CameraDragEnd {
802    fn from(settings: CameraSettings) -> Self {
803        Self { settings }
804    }
805}
806impl From<CameraSettings> for crate::output::DefaultCameraGetSettings {
807    fn from(settings: CameraSettings) -> Self {
808        Self { settings }
809    }
810}
811impl From<CameraSettings> for crate::output::ZoomToFit {
812    fn from(settings: CameraSettings) -> Self {
813        Self { settings }
814    }
815}
816impl From<CameraSettings> for crate::output::OrientToFace {
817    fn from(settings: CameraSettings) -> Self {
818        Self { settings }
819    }
820}
821impl From<CameraSettings> for crate::output::ViewIsometric {
822    fn from(settings: CameraSettings) -> Self {
823        Self { settings }
824    }
825}
826
827/// Defines a perspective view.
828#[derive(Copy, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, PartialOrd, Default)]
829#[serde(rename_all = "snake_case")]
830#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
831#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
832pub struct PerspectiveCameraParameters {
833    /// Camera frustum vertical field of view.
834    pub fov_y: Option<f32>,
835    /// Camera frustum near plane.
836    pub z_near: Option<f32>,
837    /// Camera frustum far plane.
838    pub z_far: Option<f32>,
839}
840
841/// A type of camera movement applied after certain camera operations
842#[derive(
843    Default,
844    Display,
845    FromStr,
846    Copy,
847    Eq,
848    PartialEq,
849    Debug,
850    JsonSchema,
851    Deserialize,
852    Serialize,
853    Sequence,
854    Clone,
855    Ord,
856    PartialOrd,
857)]
858#[serde(rename_all = "snake_case")]
859#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
860#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
861pub enum CameraMovement {
862    /// Adjusts the camera position during the camera operation
863    #[default]
864    Vantage,
865    /// Keeps the camera position in place
866    None,
867}
868
869/// The global axes.
870#[derive(
871    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
872)]
873#[serde(rename_all = "lowercase")]
874#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
875#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
876pub enum GlobalAxis {
877    /// The X axis
878    X,
879    /// The Y axis
880    Y,
881    /// The Z axis
882    Z,
883}
884
885/// Possible types of faces which can be extruded from a 3D solid.
886#[derive(
887    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
888)]
889#[serde(rename_all = "snake_case")]
890#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
891#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
892#[repr(u8)]
893pub enum ExtrusionFaceCapType {
894    /// Uncapped.
895    None,
896    /// Capped on top.
897    Top,
898    /// Capped below.
899    Bottom,
900    /// Capped on both ends.
901    Both,
902}
903
904/// Post effect type
905#[allow(missing_docs)]
906#[derive(
907    Display,
908    FromStr,
909    Copy,
910    Eq,
911    PartialEq,
912    Debug,
913    JsonSchema,
914    Deserialize,
915    Serialize,
916    Sequence,
917    Clone,
918    Ord,
919    PartialOrd,
920    Default,
921)]
922#[serde(rename_all = "lowercase")]
923#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
924#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
925pub enum PostEffectType {
926    Phosphor,
927    Ssao,
928    #[default]
929    NoEffect,
930}
931
932// Enum: Connect Rust Enums to Cpp
933// add our native c++ names for our cxx::ExternType implementation
934#[cfg(feature = "cxx")]
935impl_extern_type! {
936    [Trivial]
937    // File
938    FileImportFormat = "Enums::_FileImportFormat"
939    FileExportFormat = "Enums::_FileExportFormat"
940    // Camera
941    CameraDragInteractionType = "Enums::_CameraDragInteractionType"
942    // Scene
943    SceneSelectionType = "Enums::_SceneSelectionType"
944    SceneToolType = "Enums::_SceneToolType"
945    EntityType = "Enums::_EntityType"
946    AnnotationType = "Enums::_AnnotationType"
947    AnnotationTextAlignmentX = "Enums::_AnnotationTextAlignmentX"
948    AnnotationTextAlignmentY = "Enums::_AnnotationTextAlignmentY"
949    AnnotationLineEnd = "Enums::_AnnotationLineEnd"
950
951    CurveType = "Enums::_CurveType"
952    PathCommand = "Enums::_PathCommand"
953    PathComponentConstraintBound = "Enums::_PathComponentConstraintBound"
954    PathComponentConstraintType = "Enums::_PathComponentConstraintType"
955    ExtrusionFaceCapType  = "Enums::_ExtrusionFaceCapType"
956
957    // Utils
958    EngineErrorCode = "Enums::_ErrorCode"
959    GlobalAxis = "Enums::_GlobalAxis"
960    OriginType = "Enums::_OriginType"
961
962    // Graphics engine
963    PostEffectType = "Enums::_PostEffectType"
964}
965
966fn bool_true() -> bool {
967    true
968}
969fn same_scale() -> Point3d<f64> {
970    Point3d::uniform(1.0)
971}
972
973fn z_axis() -> Point3d<f64> {
974    Point3d { x: 0.0, y: 0.0, z: 1.0 }
975}
976
977impl ExtrudedFaceInfo {
978    /// Converts from the representation used in the Extrude modeling command,
979    /// to a flat representation.
980    pub fn list_faces(self) -> Vec<ExtrusionFaceInfo> {
981        let mut face_infos: Vec<_> = self
982            .sides
983            .into_iter()
984            .map(|side| ExtrusionFaceInfo {
985                curve_id: Some(side.path_id),
986                face_id: Some(side.face_id),
987                cap: ExtrusionFaceCapType::None,
988            })
989            .collect();
990        face_infos.push(ExtrusionFaceInfo {
991            curve_id: None,
992            face_id: Some(self.top),
993            cap: ExtrusionFaceCapType::Top,
994        });
995        if let Some(bottom) = self.bottom {
996            face_infos.push(ExtrusionFaceInfo {
997                curve_id: None,
998                face_id: Some(bottom),
999                cap: ExtrusionFaceCapType::Bottom,
1000            });
1001        }
1002        face_infos
1003    }
1004}
1005
1006#[cfg(test)]
1007mod tests {
1008    use super::*;
1009
1010    #[test]
1011    fn test_angle_comparison() {
1012        let a = Angle::from_degrees(90.0);
1013        assert!(a < Angle::from_degrees(91.0));
1014        assert!(a > Angle::from_degrees(89.0));
1015        assert!(a <= Angle::from_degrees(90.0));
1016        assert!(a >= Angle::from_degrees(90.0));
1017        let b = Angle::from_radians(std::f64::consts::FRAC_PI_4);
1018        assert!(b < Angle::from_radians(std::f64::consts::FRAC_PI_2));
1019        assert!(b > Angle::from_radians(std::f64::consts::FRAC_PI_8));
1020        assert!(b <= Angle::from_radians(std::f64::consts::FRAC_PI_4));
1021        assert!(b >= Angle::from_radians(std::f64::consts::FRAC_PI_4));
1022        // Mixed units.
1023        assert!(a > b);
1024        assert!(a >= b);
1025        assert!(b < a);
1026        assert!(b <= a);
1027        let c = Angle::from_radians(std::f64::consts::FRAC_PI_2 * 3.0);
1028        assert!(a < c);
1029        assert!(a <= c);
1030        assert!(c > a);
1031        assert!(c >= a);
1032    }
1033}
1034
1035/// How a property of an object should be transformed.
1036#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1037#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1038#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1039pub struct TransformBy<T> {
1040    /// The scale, or rotation, or translation.
1041    pub property: T,
1042    /// If true, overwrite the previous value with this.
1043    /// If false, the previous value will be modified.
1044    /// E.g. when translating, `set=true` will set a new location,
1045    /// and `set=false` will translate the current location by the given X/Y/Z.
1046    pub set: bool,
1047    /// If true, the transform is applied in local space.
1048    /// If false, the transform is applied in global space.
1049    pub is_local: bool,
1050}
1051
1052impl<T: JsonSchema> JsonSchema for TransformBy<T> {
1053    fn schema_name() -> String {
1054        format!("TransformByFor{}", T::schema_name())
1055    }
1056
1057    fn schema_id() -> std::borrow::Cow<'static, str> {
1058        std::borrow::Cow::Owned(format!("{}::TransformBy<{}>", module_path!(), T::schema_id()))
1059    }
1060
1061    fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1062        SchemaObject {
1063            instance_type: Some(schemars::schema::InstanceType::String.into()),
1064            ..Default::default()
1065        }
1066        .into()
1067    }
1068}
1069
1070/// Container that holds a translate, rotate and scale.
1071#[derive(Clone, Debug, PartialEq, Deserialize, JsonSchema, Serialize)]
1072#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1073#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1074pub struct ComponentTransform {
1075    /// Translate component of the transform.
1076    pub translate: Option<TransformBy<Point3d<LengthUnit>>>,
1077    /// Rotate component of the transform.
1078    /// The rotation is specified as a roll, pitch, yaw.
1079    pub rotate_rpy: Option<TransformBy<Point3d<f64>>>,
1080    /// Rotate component of the transform.
1081    /// The rotation is specified as an axis and an angle (xyz are the components of the axis, w is
1082    /// the angle in degrees).
1083    pub rotate_angle_axis: Option<TransformBy<Point4d<f64>>>,
1084    /// Scale component of the transform.
1085    pub scale: Option<TransformBy<Point3d<f64>>>,
1086}