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    ///Adds a circular involute from the current position that goes through the given end_radius
328    ///and is rotated around the current point by angle.
329    CircularInvolute {
330        ///The involute is described between two circles, start_radius is the radius of the inner
331        ///circle.
332        start_radius: LengthUnit,
333        ///The involute is described between two circles, end_radius is the radius of the outer
334        ///circle.
335        end_radius: LengthUnit,
336        ///The angle to rotate the involute by. A value of zero will produce a curve with a tangent
337        ///along the x-axis at the start point of the curve.
338        angle: Angle,
339        ///If reverse is true, the segment will start
340        ///from the end of the involute, otherwise it will start from that start.
341        reverse: bool,
342    },
343}
344
345/// An angle, with a specific unit.
346#[derive(Clone, Copy, PartialEq, Debug, JsonSchema, Deserialize, Serialize)]
347#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
348#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
349pub struct Angle {
350    /// What unit is the measurement?
351    pub unit: UnitAngle,
352    /// The size of the angle, measured in the chosen unit.
353    pub value: f64,
354}
355
356impl Angle {
357    /// Converts a given angle to degrees.
358    pub fn to_degrees(self) -> f64 {
359        match self.unit {
360            UnitAngle::Degrees => self.value,
361            UnitAngle::Radians => self.value.to_degrees(),
362        }
363    }
364    /// Converts a given angle to radians.
365    pub fn to_radians(self) -> f64 {
366        match self.unit {
367            UnitAngle::Degrees => self.value.to_radians(),
368            UnitAngle::Radians => self.value,
369        }
370    }
371    /// Create an angle in degrees.
372    pub const fn from_degrees(value: f64) -> Self {
373        Self {
374            unit: UnitAngle::Degrees,
375            value,
376        }
377    }
378    /// Create an angle in radians.
379    pub const fn from_radians(value: f64) -> Self {
380        Self {
381            unit: UnitAngle::Radians,
382            value,
383        }
384    }
385    /// 360 degrees.
386    pub const fn turn() -> Self {
387        Self::from_degrees(360.0)
388    }
389    /// 180 degrees.
390    pub const fn half_circle() -> Self {
391        Self::from_degrees(180.0)
392    }
393    /// 90 degrees.
394    pub const fn quarter_circle() -> Self {
395        Self::from_degrees(90.0)
396    }
397    /// 0 degrees.
398    pub const fn zero() -> Self {
399        Self::from_degrees(0.0)
400    }
401}
402
403/// 0 degrees.
404impl Default for Angle {
405    /// 0 degrees.
406    fn default() -> Self {
407        Self::zero()
408    }
409}
410
411impl PartialOrd for Angle {
412    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
413        match (self.unit, other.unit) {
414            // Avoid unnecessary floating point operations.
415            (UnitAngle::Degrees, UnitAngle::Degrees) => self.value.partial_cmp(&other.value),
416            (UnitAngle::Radians, UnitAngle::Radians) => self.value.partial_cmp(&other.value),
417            _ => self.to_degrees().partial_cmp(&other.to_degrees()),
418        }
419    }
420}
421
422impl std::ops::Add for Angle {
423    type Output = Self;
424
425    fn add(self, rhs: Self) -> Self::Output {
426        Self {
427            unit: UnitAngle::Degrees,
428            value: self.to_degrees() + rhs.to_degrees(),
429        }
430    }
431}
432
433impl std::ops::AddAssign for Angle {
434    fn add_assign(&mut self, rhs: Self) {
435        match self.unit {
436            UnitAngle::Degrees => {
437                self.value += rhs.to_degrees();
438            }
439            UnitAngle::Radians => {
440                self.value += rhs.to_radians();
441            }
442        }
443    }
444}
445
446/// The type of scene selection change
447#[derive(
448    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
449)]
450#[serde(rename_all = "lowercase")]
451#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
452#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
453pub enum SceneSelectionType {
454    /// Replaces the selection
455    Replace,
456    /// Adds to the selection
457    Add,
458    /// Removes from the selection
459    Remove,
460}
461
462/// The type of scene's active tool
463#[allow(missing_docs)]
464#[derive(
465    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
466)]
467#[serde(rename_all = "snake_case")]
468#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
469#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
470pub enum SceneToolType {
471    CameraRevolve,
472    Select,
473    Move,
474    SketchLine,
475    SketchTangentialArc,
476    SketchCurve,
477    SketchCurveMod,
478}
479
480/// The path component constraint bounds type
481#[allow(missing_docs)]
482#[derive(
483    Display,
484    FromStr,
485    Copy,
486    Eq,
487    PartialEq,
488    Debug,
489    JsonSchema,
490    Deserialize,
491    Serialize,
492    Sequence,
493    Clone,
494    Ord,
495    PartialOrd,
496    Default,
497)]
498#[serde(rename_all = "snake_case")]
499#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
500#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
501pub enum PathComponentConstraintBound {
502    #[default]
503    Unconstrained,
504    PartiallyConstrained,
505    FullyConstrained,
506}
507
508/// The path component constraint type
509#[allow(missing_docs)]
510#[derive(
511    Display,
512    FromStr,
513    Copy,
514    Eq,
515    PartialEq,
516    Debug,
517    JsonSchema,
518    Deserialize,
519    Serialize,
520    Sequence,
521    Clone,
522    Ord,
523    PartialOrd,
524    Default,
525)]
526#[serde(rename_all = "snake_case")]
527#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
528#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
529pub enum PathComponentConstraintType {
530    #[default]
531    Unconstrained,
532    Vertical,
533    Horizontal,
534    EqualLength,
535    Parallel,
536    AngleBetween,
537}
538
539/// The path component command type (within a Path)
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 = "snake_case")]
545#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
546#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
547pub enum PathCommand {
548    MoveTo,
549    LineTo,
550    BezCurveTo,
551    NurbsCurveTo,
552    AddArc,
553}
554
555/// The type of entity
556#[allow(missing_docs)]
557#[derive(
558    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
559)]
560#[serde(rename_all = "lowercase")]
561#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
562#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
563#[repr(u8)]
564pub enum EntityType {
565    Entity,
566    Object,
567    Path,
568    Curve,
569    Solid2D,
570    Solid3D,
571    Edge,
572    Face,
573    Plane,
574    Vertex,
575}
576
577/// The type of Curve (embedded within path)
578#[allow(missing_docs)]
579#[derive(
580    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
581)]
582#[serde(rename_all = "snake_case")]
583#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
584#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
585pub enum CurveType {
586    Line,
587    Arc,
588    Nurbs,
589}
590
591/// A file to be exported to the client.
592#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
593pub struct ExportFile {
594    /// The name of the file.
595    pub name: String,
596    /// The contents of the file, base64 encoded.
597    pub contents: crate::base64::Base64Data,
598}
599
600/// The valid types of output file formats.
601#[derive(
602    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd, Sequence,
603)]
604#[serde(rename_all = "lowercase")]
605#[display(style = "lowercase")]
606#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
607#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
608pub enum FileExportFormat {
609    /// Autodesk Filmbox (FBX) format. <https://en.wikipedia.org/wiki/FBX>
610    Fbx,
611    /// Binary glTF 2.0.
612    ///
613    /// This is a single binary with .glb extension.
614    ///
615    /// This is better if you want a compressed format as opposed to the human readable
616    /// glTF that lacks compression.
617    Glb,
618    /// glTF 2.0.
619    /// Embedded glTF 2.0 (pretty printed).
620    ///
621    /// Single JSON file with .gltf extension binary data encoded as
622    /// base64 data URIs.
623    ///
624    /// The JSON contents are pretty printed.
625    ///
626    /// It is human readable, single file, and you can view the
627    /// diff easily in a git commit.
628    Gltf,
629    /// The OBJ file format. <https://en.wikipedia.org/wiki/Wavefront_.obj_file>
630    /// It may or may not have an an attached material (mtl // mtllib) within the file,
631    /// but we interact with it as if it does not.
632    Obj,
633    /// The PLY file format. <https://en.wikipedia.org/wiki/PLY_(file_format)>
634    Ply,
635    /// The STEP file format. <https://en.wikipedia.org/wiki/ISO_10303-21>
636    Step,
637    /// The STL file format. <https://en.wikipedia.org/wiki/STL_(file_format)>
638    Stl,
639}
640
641/// The valid types of 2D output file formats.
642#[derive(
643    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd, Sequence,
644)]
645#[serde(rename_all = "lowercase")]
646#[display(style = "lowercase")]
647#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
648#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
649pub enum FileExportFormat2d {
650    /// AutoCAD drawing interchange format.
651    Dxf,
652}
653
654/// The valid types of source file formats.
655#[derive(
656    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd, Sequence,
657)]
658#[serde(rename_all = "lowercase")]
659#[display(style = "lowercase")]
660#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
661#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
662pub enum FileImportFormat {
663    /// Autodesk Filmbox (FBX) format. <https://en.wikipedia.org/wiki/FBX>
664    Fbx,
665    /// glTF 2.0.
666    Gltf,
667    /// The OBJ file format. <https://en.wikipedia.org/wiki/Wavefront_.obj_file>
668    /// It may or may not have an an attached material (mtl // mtllib) within the file,
669    /// but we interact with it as if it does not.
670    Obj,
671    /// The PLY file format. <https://en.wikipedia.org/wiki/PLY_(file_format)>
672    Ply,
673    /// SolidWorks part (SLDPRT) format.
674    Sldprt,
675    /// The STEP file format. <https://en.wikipedia.org/wiki/ISO_10303-21>
676    Step,
677    /// The STL file format. <https://en.wikipedia.org/wiki/STL_(file_format)>
678    Stl,
679}
680
681/// The type of error sent by the KittyCAD graphics engine.
682#[derive(Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd)]
683#[serde(rename_all = "snake_case")]
684#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
685#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
686pub enum EngineErrorCode {
687    /// User requested something geometrically or graphically impossible.
688    /// Don't retry this request, as it's inherently impossible. Instead, read the error message
689    /// and change your request.
690    BadRequest = 1,
691    /// Graphics engine failed to complete request, consider retrying
692    InternalEngine,
693}
694
695impl From<EngineErrorCode> for http::StatusCode {
696    fn from(e: EngineErrorCode) -> Self {
697        match e {
698            EngineErrorCode::BadRequest => Self::BAD_REQUEST,
699            EngineErrorCode::InternalEngine => Self::INTERNAL_SERVER_ERROR,
700        }
701    }
702}
703
704/// IDs for the extruded faces.
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 ExtrudedFaceInfo {
709    /// The face made from the original 2D shape being extruded.
710    /// If the solid is extruded from a shape which already has an ID
711    /// (e.g. extruding something which was sketched on a face), this
712    /// doesn't need to be sent.
713    pub bottom: Option<Uuid>,
714    /// Top face of the extrusion (parallel and further away from the original 2D shape being extruded).
715    pub top: Uuid,
716    /// Any intermediate sides between the top and bottom.
717    pub sides: Vec<SideFace>,
718}
719
720/// IDs for a side face, extruded from the path of some sketch/2D shape.
721#[derive(Debug, PartialEq, Serialize, Deserialize, JsonSchema, Clone)]
722#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
723#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
724pub struct SideFace {
725    /// ID of the path this face is being extruded from.
726    pub path_id: Uuid,
727    /// Desired ID for the resulting face.
728    pub face_id: Uuid,
729}
730
731/// Camera settings including position, center, fov etc
732#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
733#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
734#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
735pub struct CameraSettings {
736    ///Camera position (vantage)
737    pub pos: Point3d,
738
739    ///Camera's look-at center (center-pos gives viewing vector)
740    pub center: Point3d,
741
742    ///Camera's world-space up vector
743    pub up: Point3d,
744
745    ///The Camera's orientation (in the form of a quaternion)
746    pub orientation: Quaternion,
747
748    ///Camera's field-of-view angle (if ortho is false)
749    pub fov_y: Option<f32>,
750
751    ///The camera's ortho scale (derived from viewing distance if ortho is true)
752    pub ortho_scale: Option<f32>,
753
754    ///Whether or not the camera is in ortho mode
755    pub ortho: bool,
756}
757
758#[allow(missing_docs)]
759#[repr(u8)]
760#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
761#[serde(rename_all = "snake_case")]
762#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
763#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
764pub enum WorldCoordinateSystem {
765    #[default]
766    RightHandedUpZ,
767    RightHandedUpY,
768}
769
770#[allow(missing_docs)]
771#[repr(C)]
772#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
773#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
774#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
775pub struct CameraViewState {
776    pub pivot_rotation: Quaternion,
777    pub pivot_position: Point3d,
778    pub eye_offset: f32,
779    pub fov_y: f32,
780    pub ortho_scale_factor: f32,
781    pub is_ortho: bool,
782    pub ortho_scale_enabled: bool,
783    pub world_coord_system: WorldCoordinateSystem,
784}
785
786impl Default for CameraViewState {
787    fn default() -> Self {
788        CameraViewState {
789            pivot_rotation: Default::default(),
790            pivot_position: Default::default(),
791            eye_offset: 10.0,
792            fov_y: 45.0,
793            ortho_scale_factor: 1.6,
794            is_ortho: false,
795            ortho_scale_enabled: true,
796            world_coord_system: Default::default(),
797        }
798    }
799}
800
801#[cfg(feature = "cxx")]
802impl_extern_type! {
803    [Trivial]
804    CameraViewState = "Endpoints::CameraViewState"
805}
806
807impl From<CameraSettings> for crate::output::DefaultCameraZoom {
808    fn from(settings: CameraSettings) -> Self {
809        Self { settings }
810    }
811}
812impl From<CameraSettings> for crate::output::CameraDragMove {
813    fn from(settings: CameraSettings) -> Self {
814        Self { settings }
815    }
816}
817impl From<CameraSettings> for crate::output::CameraDragEnd {
818    fn from(settings: CameraSettings) -> Self {
819        Self { settings }
820    }
821}
822impl From<CameraSettings> for crate::output::DefaultCameraGetSettings {
823    fn from(settings: CameraSettings) -> Self {
824        Self { settings }
825    }
826}
827impl From<CameraSettings> for crate::output::ZoomToFit {
828    fn from(settings: CameraSettings) -> Self {
829        Self { settings }
830    }
831}
832impl From<CameraSettings> for crate::output::OrientToFace {
833    fn from(settings: CameraSettings) -> Self {
834        Self { settings }
835    }
836}
837impl From<CameraSettings> for crate::output::ViewIsometric {
838    fn from(settings: CameraSettings) -> Self {
839        Self { settings }
840    }
841}
842
843/// Defines a perspective view.
844#[derive(Copy, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, PartialOrd, Default)]
845#[serde(rename_all = "snake_case")]
846#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
847#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
848pub struct PerspectiveCameraParameters {
849    /// Camera frustum vertical field of view.
850    pub fov_y: Option<f32>,
851    /// Camera frustum near plane.
852    pub z_near: Option<f32>,
853    /// Camera frustum far plane.
854    pub z_far: Option<f32>,
855}
856
857/// A type of camera movement applied after certain camera operations
858#[derive(
859    Default,
860    Display,
861    FromStr,
862    Copy,
863    Eq,
864    PartialEq,
865    Debug,
866    JsonSchema,
867    Deserialize,
868    Serialize,
869    Sequence,
870    Clone,
871    Ord,
872    PartialOrd,
873)]
874#[serde(rename_all = "snake_case")]
875#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
876#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
877pub enum CameraMovement {
878    /// Adjusts the camera position during the camera operation
879    #[default]
880    Vantage,
881    /// Keeps the camera position in place
882    None,
883}
884
885/// The global axes.
886#[derive(
887    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
888)]
889#[serde(rename_all = "lowercase")]
890#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
891#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
892pub enum GlobalAxis {
893    /// The X axis
894    X,
895    /// The Y axis
896    Y,
897    /// The Z axis
898    Z,
899}
900
901/// Possible types of faces which can be extruded from a 3D solid.
902#[derive(
903    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
904)]
905#[serde(rename_all = "snake_case")]
906#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
907#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
908#[repr(u8)]
909pub enum ExtrusionFaceCapType {
910    /// Uncapped.
911    None,
912    /// Capped on top.
913    Top,
914    /// Capped below.
915    Bottom,
916    /// Capped on both ends.
917    Both,
918}
919
920/// Post effect type
921#[allow(missing_docs)]
922#[derive(
923    Display,
924    FromStr,
925    Copy,
926    Eq,
927    PartialEq,
928    Debug,
929    JsonSchema,
930    Deserialize,
931    Serialize,
932    Sequence,
933    Clone,
934    Ord,
935    PartialOrd,
936    Default,
937)]
938#[serde(rename_all = "lowercase")]
939#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
940#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
941pub enum PostEffectType {
942    Phosphor,
943    Ssao,
944    #[default]
945    NoEffect,
946}
947
948// Enum: Connect Rust Enums to Cpp
949// add our native c++ names for our cxx::ExternType implementation
950#[cfg(feature = "cxx")]
951impl_extern_type! {
952    [Trivial]
953    // File
954    FileImportFormat = "Enums::_FileImportFormat"
955    FileExportFormat = "Enums::_FileExportFormat"
956    // Camera
957    CameraDragInteractionType = "Enums::_CameraDragInteractionType"
958    // Scene
959    SceneSelectionType = "Enums::_SceneSelectionType"
960    SceneToolType = "Enums::_SceneToolType"
961    EntityType = "Enums::_EntityType"
962    AnnotationType = "Enums::_AnnotationType"
963    AnnotationTextAlignmentX = "Enums::_AnnotationTextAlignmentX"
964    AnnotationTextAlignmentY = "Enums::_AnnotationTextAlignmentY"
965    AnnotationLineEnd = "Enums::_AnnotationLineEnd"
966
967    CurveType = "Enums::_CurveType"
968    PathCommand = "Enums::_PathCommand"
969    PathComponentConstraintBound = "Enums::_PathComponentConstraintBound"
970    PathComponentConstraintType = "Enums::_PathComponentConstraintType"
971    ExtrusionFaceCapType  = "Enums::_ExtrusionFaceCapType"
972
973    // Utils
974    EngineErrorCode = "Enums::_ErrorCode"
975    GlobalAxis = "Enums::_GlobalAxis"
976    OriginType = "Enums::_OriginType"
977
978    // Graphics engine
979    PostEffectType = "Enums::_PostEffectType"
980}
981
982fn bool_true() -> bool {
983    true
984}
985fn same_scale() -> Point3d<f64> {
986    Point3d::uniform(1.0)
987}
988
989fn z_axis() -> Point3d<f64> {
990    Point3d { x: 0.0, y: 0.0, z: 1.0 }
991}
992
993impl ExtrudedFaceInfo {
994    /// Converts from the representation used in the Extrude modeling command,
995    /// to a flat representation.
996    pub fn list_faces(self) -> Vec<ExtrusionFaceInfo> {
997        let mut face_infos: Vec<_> = self
998            .sides
999            .into_iter()
1000            .map(|side| ExtrusionFaceInfo {
1001                curve_id: Some(side.path_id),
1002                face_id: Some(side.face_id),
1003                cap: ExtrusionFaceCapType::None,
1004            })
1005            .collect();
1006        face_infos.push(ExtrusionFaceInfo {
1007            curve_id: None,
1008            face_id: Some(self.top),
1009            cap: ExtrusionFaceCapType::Top,
1010        });
1011        if let Some(bottom) = self.bottom {
1012            face_infos.push(ExtrusionFaceInfo {
1013                curve_id: None,
1014                face_id: Some(bottom),
1015                cap: ExtrusionFaceCapType::Bottom,
1016            });
1017        }
1018        face_infos
1019    }
1020}
1021
1022#[cfg(test)]
1023mod tests {
1024    use super::*;
1025
1026    #[test]
1027    fn test_angle_comparison() {
1028        let a = Angle::from_degrees(90.0);
1029        assert!(a < Angle::from_degrees(91.0));
1030        assert!(a > Angle::from_degrees(89.0));
1031        assert!(a <= Angle::from_degrees(90.0));
1032        assert!(a >= Angle::from_degrees(90.0));
1033        let b = Angle::from_radians(std::f64::consts::FRAC_PI_4);
1034        assert!(b < Angle::from_radians(std::f64::consts::FRAC_PI_2));
1035        assert!(b > Angle::from_radians(std::f64::consts::FRAC_PI_8));
1036        assert!(b <= Angle::from_radians(std::f64::consts::FRAC_PI_4));
1037        assert!(b >= Angle::from_radians(std::f64::consts::FRAC_PI_4));
1038        // Mixed units.
1039        assert!(a > b);
1040        assert!(a >= b);
1041        assert!(b < a);
1042        assert!(b <= a);
1043        let c = Angle::from_radians(std::f64::consts::FRAC_PI_2 * 3.0);
1044        assert!(a < c);
1045        assert!(a <= c);
1046        assert!(c > a);
1047        assert!(c >= a);
1048    }
1049}
1050
1051/// How a property of an object should be transformed.
1052#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1053#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1054#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1055pub struct TransformBy<T> {
1056    /// The scale, or rotation, or translation.
1057    pub property: T,
1058    /// If true, overwrite the previous value with this.
1059    /// If false, the previous value will be modified.
1060    /// E.g. when translating, `set=true` will set a new location,
1061    /// and `set=false` will translate the current location by the given X/Y/Z.
1062    pub set: bool,
1063    /// If true, the transform is applied in local space.
1064    /// If false, the transform is applied in global space.
1065    pub is_local: bool,
1066}
1067
1068impl<T: JsonSchema> JsonSchema for TransformBy<T> {
1069    fn schema_name() -> String {
1070        format!("TransformByFor{}", T::schema_name())
1071    }
1072
1073    fn schema_id() -> std::borrow::Cow<'static, str> {
1074        std::borrow::Cow::Owned(format!("{}::TransformBy<{}>", module_path!(), T::schema_id()))
1075    }
1076
1077    fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1078        SchemaObject {
1079            instance_type: Some(schemars::schema::InstanceType::String.into()),
1080            ..Default::default()
1081        }
1082        .into()
1083    }
1084}
1085
1086/// Container that holds a translate, rotate and scale.
1087#[derive(Clone, Debug, PartialEq, Deserialize, JsonSchema, Serialize)]
1088#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1089#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1090pub struct ComponentTransform {
1091    /// Translate component of the transform.
1092    pub translate: Option<TransformBy<Point3d<LengthUnit>>>,
1093    /// Rotate component of the transform.
1094    /// The rotation is specified as a roll, pitch, yaw.
1095    pub rotate_rpy: Option<TransformBy<Point3d<f64>>>,
1096    /// Rotate component of the transform.
1097    /// The rotation is specified as an axis and an angle (xyz are the components of the axis, w is
1098    /// the angle in degrees).
1099    pub rotate_angle_axis: Option<TransformBy<Point4d<f64>>>,
1100    /// Scale component of the transform.
1101    pub scale: Option<TransformBy<Point3d<f64>>>,
1102}
1103
1104///If bidirectional or symmetric operations are needed this enum encapsulates the required
1105///information.
1106#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
1107#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1108#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1109pub enum Opposite<T> {
1110    /// No opposite. The operation will only occur on one side.
1111    #[default]
1112    None,
1113    /// Operation will occur from both sides, with the same value.
1114    Symmetric,
1115    /// Operation will occur from both sides, with this value for the opposite.
1116    Other(T),
1117}
1118
1119impl<T: JsonSchema> JsonSchema for Opposite<T> {
1120    fn schema_name() -> String {
1121        format!("OppositeFor{}", T::schema_name())
1122    }
1123
1124    fn schema_id() -> std::borrow::Cow<'static, str> {
1125        std::borrow::Cow::Owned(format!("{}::Opposite<{}>", module_path!(), T::schema_id()))
1126    }
1127
1128    fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1129        SchemaObject {
1130            instance_type: Some(schemars::schema::InstanceType::String.into()),
1131            ..Default::default()
1132        }
1133        .into()
1134    }
1135}