Skip to main content

kittycad_modeling_cmds/
shared.rs

1use bon::Builder;
2use enum_iterator::Sequence;
3use parse_display_derive::{Display, FromStr};
4pub use point::{Point2d, Point3d, Point4d, Quaternion};
5use schemars::{schema::SchemaObject, JsonSchema};
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9#[cfg(feature = "cxx")]
10use crate::impl_extern_type;
11use crate::{def_enum::negative_one, length_unit::LengthUnit, output::ExtrusionFaceInfo, units::UnitAngle};
12
13mod point;
14
15/// An edge can be referenced by its uuid or by the faces that uniquely define it.
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
17#[serde(rename_all = "snake_case")]
18#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
19#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
20#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
21#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
22pub struct EdgeSpecifier {
23    /// Side face ids that uniquely identify the edge.
24    pub side_faces: Vec<Uuid>,
25    /// Optional end face ids for ambiguous edge matches.
26    #[serde(default, skip_serializing_if = "Vec::is_empty")]
27    #[builder(default)]
28    pub end_faces: Vec<Uuid>,
29    /// Optional index for disambiguation when multiple edges share the same faces.
30    /// If not provided (None), all matching edges will be used.
31    /// If provided (Some(n)), only the edge at index n will be used.
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub index: Option<u32>,
34}
35
36/// Optional fallback when primary UUIDs are missing from the client artifact graph (e.g. stale or
37/// engine-only ids). Identifies the same topology via a **parent** entity UUID and a **primitive
38/// index** on that parent.
39///
40/// Semantics by selection kind (aligned with engine BREP topology):
41///
42/// - **Face / Edge (3D)**: `parent_id` is the owning [`EntityType::Solid3D`] body UUID; `primitive_index`
43///   matches the index returned by **EntityGetPrimitiveIndex** for that face or edge (and matches
44///   **EntityGetParentId** → parent + **EntityGetPrimitiveIndex** → index).
45/// - **Vertex (3D)**: same `parent_id` (solid); `primitive_index` is the BREP vertex index on that solid.
46/// - **Solid2dEdge**: `parent_id` is the **Solid2D** profile UUID; `primitive_index` is the curve index
47///   within that profile.
48/// - **Segment**: `parent_id` is the **Path** UUID; `primitive_index` is the curve index within that path.
49///
50/// Other [`EntityReference`] variants may omit this field or leave it unset when not applicable.
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
52#[serde(rename_all = "snake_case")]
53#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
54#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
55#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
56pub struct PrimitiveTopologyFallback {
57    /// UUID of the parent entity that owns the primitive (solid3d, solid2d, or path).
58    pub parent_id: Uuid,
59    /// Index of the face, edge, vertex, profile curve, or path segment on `parent_id`.
60    pub primitive_index: u32,
61}
62
63/// An edge/vertex can be defined by the faces that it is connected to.
64#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
65#[serde(tag = "type", rename_all = "snake_case")]
66#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
67#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
68#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
69pub enum EntityReference {
70    /// A uuid referencing a plane.
71    Plane {
72        /// Id of the plane being referenced.
73        plane_id: Uuid,
74        /// Optional primitive topology on a parent (not used for planes today).
75        #[serde(default, skip_serializing_if = "Option::is_none")]
76        topology_fallback: Option<PrimitiveTopologyFallback>,
77    },
78    /// A uuid referencing a face.
79    Face {
80        /// Id of the face being referenced.
81        face_id: Uuid,
82        /// Fallback: solid3d UUID + face index on that body when `face_id` cannot be resolved client-side.
83        #[serde(default, skip_serializing_if = "Option::is_none")]
84        topology_fallback: Option<PrimitiveTopologyFallback>,
85    },
86    /// A collection of ids that uniquely identify an edge.
87    Edge {
88        /// Flattened edge reference (side_faces, end_faces, index).
89        #[serde(flatten)]
90        inner: EdgeSpecifier,
91        /// Fallback: solid3d UUID + edge index on that body for 3D BREP edges (distinct from `inner.index`).
92        #[serde(default, skip_serializing_if = "Option::is_none")]
93        topology_fallback: Option<PrimitiveTopologyFallback>,
94    },
95    /// A collection of ids that uniquely identify an vertex.
96    Vertex {
97        /// Side face ids that identify the vertex.
98        side_faces: Vec<Uuid>,
99        /// Optional index among the filtered candidates.
100        #[serde(skip_serializing_if = "Option::is_none")]
101        index: Option<u32>,
102        /// Fallback: solid3d UUID + vertex index on that body.
103        #[serde(default, skip_serializing_if = "Option::is_none")]
104        topology_fallback: Option<PrimitiveTopologyFallback>,
105    },
106    /// A uuid referencing a solid2d (profile).
107    Solid2d {
108        /// Id of the solid2d being referenced.
109        solid2d_id: Uuid,
110        /// Typically omitted: `solid2d_id` is already the owning profile. Present for schema parity with other variants.
111        #[serde(default, skip_serializing_if = "Option::is_none")]
112        topology_fallback: Option<PrimitiveTopologyFallback>,
113    },
114    /// A uuid referencing a solid3d (body).
115    Solid3d {
116        /// Id of the solid3d being referenced.
117        solid3d_id: Uuid,
118        /// Typically omitted: `solid3d_id` is already the owning body. Present for schema parity with other variants.
119        #[serde(default, skip_serializing_if = "Option::is_none")]
120        topology_fallback: Option<PrimitiveTopologyFallback>,
121    },
122    /// A uuid referencing an edge on a solid2d (profile) - used for raw sketch/profile edges.
123    /// This is distinct from the face-based Edge reference which is used for BRep/swept body edges.
124    Solid2dEdge {
125        /// Id of the edge being referenced.
126        edge_id: Uuid,
127        /// Fallback: solid2d UUID + curve index in that profile.
128        #[serde(default, skip_serializing_if = "Option::is_none")]
129        topology_fallback: Option<PrimitiveTopologyFallback>,
130    },
131    /// A single segment (curve) within a path.
132    Segment {
133        /// Id of the path containing the segment.
134        path_id: Uuid,
135        /// Id of the segment (curve) being referenced.
136        segment_id: Uuid,
137        /// Fallback: path UUID + segment curve index.
138        #[serde(default, skip_serializing_if = "Option::is_none")]
139        topology_fallback: Option<PrimitiveTopologyFallback>,
140    },
141    /// A closed sketch region/profile area.
142    Region {
143        /// Id of the region being referenced.
144        region_id: Uuid,
145        /// Fallback: path UUID + region index on that path.
146        #[serde(default, skip_serializing_if = "Option::is_none")]
147        topology_fallback: Option<PrimitiveTopologyFallback>,
148    },
149}
150
151/// What kind of cut to do
152#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
153#[serde(rename_all = "snake_case")]
154#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
155#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
156#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
157pub enum CutType {
158    /// Round off an edge.
159    #[default]
160    Fillet,
161    /// Cut away an edge.
162    Chamfer,
163}
164
165/// What to reflect mirrored geometry across
166#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
167#[serde(rename_all = "snake_case")]
168#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
169#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
170#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
171pub enum MirrorAcross {
172    /// Reflect across an edge
173    /// If used with a 3D mirror, the edge will define the normal of the mirror plane.
174    Edge {
175        /// Edge ID.
176        id: Uuid,
177    },
178    /// Reflect across an axis (that goes through a point)
179    /// If used with a 3D mirror, the axis will define the normal of the mirror plane.
180    Axis {
181        /// Axis to use as mirror.
182        axis: Point3d<f64>,
183        /// Point through which the mirror axis passes.
184        point: Point3d<LengthUnit>,
185    },
186    /// Reflect across a plane (which gives two axes)
187    /// Cannot be used with 2D mirrors.
188    Plane {
189        /// Plane ID.
190        id: Uuid,
191    },
192}
193
194/// What kind of cut to perform when cutting an edge.
195#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
196#[serde(rename_all = "snake_case")]
197#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
198#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
199#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
200#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
201pub enum CutTypeV2 {
202    /// Round off an edge.
203    Fillet {
204        /// The radius of the fillet.
205        radius: LengthUnit,
206        /// The second length affects the edge length of the second face of the cut. This will
207        /// cause the fillet to take on the shape of a conic section, instead of an arc.
208        second_length: Option<LengthUnit>,
209    },
210    /// Cut away an edge.
211    Chamfer {
212        /// The distance from the edge to cut on each face.
213        distance: LengthUnit,
214        /// The second distance affects the edge length of the second face of the cut.
215        second_distance: Option<LengthUnit>,
216        /// The angle of the chamfer, default is 45deg.
217        angle: Option<Angle>,
218        /// If true, the second distance or angle is applied to the other face of the cut.
219        swap: bool,
220    },
221    /// A custom cut profile.
222    Custom {
223        /// The path that will be used for the custom profile.
224        path: Uuid,
225    },
226}
227
228/// A rotation defined by an axis, origin of rotation, and an angle.
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
230#[serde(rename_all = "snake_case")]
231#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
232#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
233#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
234#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
235pub struct Rotation {
236    /// Rotation axis.
237    /// Defaults to (0, 0, 1) (i.e. the Z axis).
238    pub axis: Point3d<f64>,
239    /// Rotate this far about the rotation axis.
240    /// Defaults to zero (i.e. no rotation).
241    pub angle: Angle,
242    /// Origin of the rotation. If one isn't provided, the object will rotate about its own bounding box center.
243    pub origin: OriginType,
244}
245
246impl Default for Rotation {
247    /// z-axis, 0 degree angle, and local origin.
248    fn default() -> Self {
249        Self {
250            axis: z_axis(),
251            angle: Angle::default(),
252            origin: OriginType::Local,
253        }
254    }
255}
256
257/// Ways to transform each solid being replicated in a repeating pattern.
258#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
259#[serde(rename_all = "snake_case")]
260#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
261#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
262#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
263#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
264pub struct Transform {
265    /// Translate the replica this far along each dimension.
266    /// Defaults to zero vector (i.e. same position as the original).
267    #[serde(default)]
268    #[builder(default)]
269    pub translate: Point3d<LengthUnit>,
270    /// Scale the replica's size along each axis.
271    /// Defaults to (1, 1, 1) (i.e. the same size as the original).
272    #[serde(default = "same_scale")]
273    #[builder(default = same_scale())]
274    pub scale: Point3d<f64>,
275    /// Rotate the replica about the specified rotation axis and origin.
276    /// Defaults to no rotation.
277    #[serde(default)]
278    #[builder(default)]
279    pub rotation: Rotation,
280    /// Whether to replicate the original solid in this instance.
281    #[serde(default = "bool_true")]
282    #[builder(default = bool_true())]
283    pub replicate: bool,
284}
285
286impl Default for Transform {
287    fn default() -> Self {
288        Self {
289            scale: same_scale(),
290            replicate: true,
291            translate: Default::default(),
292            rotation: Rotation::default(),
293        }
294    }
295}
296
297/// Options for annotations
298#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
299#[serde(rename_all = "snake_case")]
300#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
301#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
302#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
303#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
304pub struct AnnotationOptions {
305    /// Text displayed on the annotation
306    pub text: Option<AnnotationTextOptions>,
307    /// How to style the start and end of the line
308    pub line_ends: Option<AnnotationLineEndOptions>,
309    /// Width of the annotation's line
310    pub line_width: Option<f32>,
311    /// Color to render the annotation
312    pub color: Option<Color>,
313    /// Position to put the annotation
314    pub position: Option<Point3d<f32>>,
315    /// Set as an MBD measured basic dimension annotation
316    pub dimension: Option<AnnotationBasicDimension>,
317    /// Set as an MBD Feature control annotation
318    pub feature_control: Option<AnnotationFeatureControl>,
319    /// Set as a feature tag annotation
320    pub feature_tag: Option<AnnotationFeatureTag>,
321}
322
323/// Options for annotation text
324#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
325#[serde(rename_all = "snake_case")]
326#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
327#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
328#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
329#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
330pub struct AnnotationLineEndOptions {
331    /// How to style the start of the annotation line.
332    pub start: AnnotationLineEnd,
333    /// How to style the end of the annotation line.
334    pub end: AnnotationLineEnd,
335}
336
337/// Options for annotation text
338#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
339#[serde(rename_all = "snake_case")]
340#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
341#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
342#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
343#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
344pub struct AnnotationTextOptions {
345    /// Alignment along the X axis
346    pub x: AnnotationTextAlignmentX,
347    /// Alignment along the Y axis
348    pub y: AnnotationTextAlignmentY,
349    /// Text displayed on the annotation
350    pub text: String,
351    /// Text font's point size
352    pub point_size: u32,
353}
354
355/// Parameters for defining an MBD Geometric control frame
356#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
357#[serde(rename_all = "snake_case")]
358#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
359#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
360#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
361#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
362pub struct AnnotationMbdControlFrame {
363    ///Geometric symbol, the type of geometric control specified
364    pub symbol: MbdSymbol,
365    /// Diameter symbol (if required) whether the geometric control requires a cylindrical or diameter tolerance
366    pub diameter_symbol: Option<MbdSymbol>,
367    /// Tolerance value - the total tolerance of the geometric control.  The unit is based on the drawing standard.
368    pub tolerance: f64,
369    /// Feature of size or tolerance modifiers
370    pub modifier: Option<MbdSymbol>,
371    /// Primary datum
372    pub primary_datum: Option<char>,
373    /// Secondary datum
374    pub secondary_datum: Option<char>,
375    /// Tertiary datum
376    pub tertiary_datum: Option<char>,
377}
378
379/// Parameters for defining an MBD basic dimension
380#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
381#[serde(rename_all = "snake_case")]
382#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
383#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
384#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
385#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
386pub struct AnnotationMbdBasicDimension {
387    /// Type of symbol to use for this dimension (if required)
388    pub symbol: Option<MbdSymbol>,
389    /// The explicitly defined dimension.  Only required if the measurement is not automatically calculated.
390    pub dimension: Option<f64>,
391    /// The tolerance of the dimension
392    pub tolerance: f64,
393}
394
395/// Parameters for defining an MBD Basic Dimension Annotation state which is measured between two positions in 3D
396#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
397#[serde(rename_all = "snake_case")]
398#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
399#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
400#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
401#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
402pub struct AnnotationBasicDimension {
403    /// Entity to measure the dimension from
404    pub from_entity_id: Uuid,
405
406    /// Normalized position within the entity to position the dimension from
407    pub from_entity_pos: Point2d<f64>,
408
409    /// Entity to measure the dimension to
410    pub to_entity_id: Uuid,
411
412    /// Normalized position within the entity to position the dimension to
413    pub to_entity_pos: Point2d<f64>,
414
415    /// Basic dimension parameters (symbol and tolerance)
416    pub dimension: AnnotationMbdBasicDimension,
417
418    /// Orientation plane.  The annotation will lie in this plane which is positioned about the leader position as its origin.
419    pub plane_id: Uuid,
420
421    /// 2D Position offset of the annotation within the plane.
422    pub offset: Point2d<f64>,
423
424    /// Number of decimal places to use when displaying tolerance and dimension values
425    pub precision: u32,
426
427    /// The scale of the font label in 3D space
428    pub font_scale: f32,
429
430    /// The point size of the fonts used to generate the annotation label.  Very large values can negatively affect performance.
431    pub font_point_size: u32,
432
433    /// The scale of the dimension arrows. Defaults to 1.
434    #[serde(default = "one")]
435    pub arrow_scale: f32,
436}
437
438/// Parameters for defining an MBD Feature Control Annotation state
439#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
440#[serde(rename_all = "snake_case")]
441#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
442#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
443#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
444#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
445pub struct AnnotationFeatureControl {
446    /// Entity to place the annotation leader from
447    pub entity_id: Uuid,
448
449    /// Normalized position within the entity to position the annotation leader from
450    pub entity_pos: Point2d<f64>,
451
452    /// Type of leader to use
453    pub leader_type: AnnotationLineEnd,
454
455    /// Basic dimensions
456    pub dimension: Option<AnnotationMbdBasicDimension>,
457
458    /// MBD Control frame for geometric control
459    pub control_frame: Option<AnnotationMbdControlFrame>,
460
461    /// Set if this annotation is defining a datum
462    pub defined_datum: Option<char>,
463
464    /// Prefix text which will appear before the basic dimension
465    pub prefix: Option<String>,
466
467    /// Suffix text which will appear after the basic dimension
468    pub suffix: Option<String>,
469
470    /// Orientation plane.  The annotation will lie in this plane which is positioned about the leader position as its origin.
471    pub plane_id: Uuid,
472
473    /// 2D Position offset of the annotation within the plane.
474    pub offset: Point2d<f64>,
475
476    /// Number of decimal places to use when displaying tolerance and dimension values
477    pub precision: u32,
478
479    /// The scale of the font label in 3D space
480    pub font_scale: f32,
481
482    /// The point size of the fonts used to generate the annotation label.  Very large values can negatively affect performance.
483    pub font_point_size: u32,
484
485    /// The scale of the leader (dot or arrow). Defaults to 1.
486    #[serde(default = "one")]
487    pub leader_scale: f32,
488}
489
490/// Parameters for defining an MBD Feature Tag Annotation state
491#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
492#[serde(rename_all = "snake_case")]
493#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
494#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
495#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
496#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
497pub struct AnnotationFeatureTag {
498    /// Entity to place the annotation leader from
499    pub entity_id: Uuid,
500
501    /// Normalized position within the entity to position the annotation leader from
502    pub entity_pos: Point2d<f64>,
503
504    /// Type of leader to use
505    pub leader_type: AnnotationLineEnd,
506
507    /// Tag key
508    pub key: String,
509
510    /// Tag value
511    pub value: String,
512
513    /// Whether or not to display the key on the annotation label
514    pub show_key: bool,
515
516    /// Orientation plane.  The annotation will lie in this plane which is positioned about the leader position as its origin.
517    pub plane_id: Uuid,
518
519    /// 2D Position offset of the annotation within the plane.
520    pub offset: Point2d<f64>,
521
522    /// The scale of the font label in 3D space
523    pub font_scale: f32,
524
525    /// The point size of the fonts used to generate the annotation label.  Very large values can negatively affect performance.
526    pub font_point_size: u32,
527
528    /// The scale of the leader (dot or arrow). Defaults to 1.
529    #[serde(default = "one")]
530    pub leader_scale: f32,
531}
532
533/// The type of distance
534/// Distances can vary depending on
535/// the objects used as input.
536#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
537#[serde(rename_all = "snake_case", tag = "type")]
538#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
539#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
540#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
541#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
542pub enum DistanceType {
543    /// Euclidean Distance.
544    Euclidean {},
545    /// The distance between objects along the specified axis
546    OnAxis {
547        /// Global axis
548        axis: GlobalAxis,
549    },
550}
551
552/// The type of origin
553#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
554#[serde(rename_all = "snake_case", tag = "type")]
555#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
556#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
557#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
558#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
559pub enum OriginType {
560    /// Local Origin (center of object bounding box).
561    #[default]
562    Local,
563    /// Global Origin (0, 0, 0).
564    Global,
565    /// Custom Origin (user specified point).
566    Custom {
567        /// Custom origin point.
568        origin: Point3d<f64>,
569    },
570}
571
572/// An RGBA color
573#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
574#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
575#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
576#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
577#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
578pub struct Color {
579    /// Red
580    pub r: f32,
581    /// Green
582    pub g: f32,
583    /// Blue
584    pub b: f32,
585    /// Alpha
586    pub a: f32,
587}
588
589impl Color {
590    /// Assign the red, green, blue and alpha (transparency) channels.
591    pub fn from_rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
592        Self { r, g, b, a }
593    }
594}
595
596/// Horizontal Text alignment
597#[allow(missing_docs)]
598#[derive(
599    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
600)]
601#[serde(rename_all = "lowercase")]
602#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
603#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
604#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
605#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
606pub enum AnnotationTextAlignmentX {
607    Left,
608    Center,
609    Right,
610}
611
612/// Vertical Text alignment
613#[allow(missing_docs)]
614#[derive(
615    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
616)]
617#[serde(rename_all = "lowercase")]
618#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
619#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
620#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
621#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
622pub enum AnnotationTextAlignmentY {
623    Bottom,
624    Center,
625    Top,
626}
627
628/// Annotation line end type
629#[allow(missing_docs)]
630#[derive(
631    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
632)]
633#[serde(rename_all = "lowercase")]
634#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
635#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
636#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
637#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
638pub enum AnnotationLineEnd {
639    None,
640    Arrow,
641    Dot,
642}
643
644/// The type of annotation
645#[derive(
646    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
647)]
648#[serde(rename_all = "lowercase")]
649#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
650#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
651#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
652#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
653pub enum AnnotationType {
654    /// 2D annotation type (screen or planar space)
655    T2D,
656    /// 3D annotation type
657    T3D,
658}
659
660/// MBD standard
661#[derive(
662    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
663)]
664#[serde(rename_all = "lowercase")]
665#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
666#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
667#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
668#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
669pub enum MbdStandard {
670    /// ASME Y14.5 GD&T
671    AsmeY14_5,
672}
673
674//SEE MIKE BEFORE MAKING ANY CHANGES TO THIS ENUM
675/// MBD symbol type
676#[allow(missing_docs)]
677#[derive(
678    Default,
679    Display,
680    FromStr,
681    Copy,
682    Eq,
683    PartialEq,
684    Debug,
685    JsonSchema,
686    Deserialize,
687    Serialize,
688    Sequence,
689    Clone,
690    Ord,
691    PartialOrd,
692)]
693#[serde(rename_all = "lowercase")]
694#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
695#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
696#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
697#[repr(u16)]
698#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
699pub enum MbdSymbol {
700    #[default]
701    None = 0,
702    ArcLength = 174,
703    Between = 175,
704    Degrees = 176,
705    PlusMinus = 177,
706    Angularity = 178,
707    Cylindricity = 179,
708    Roundness = 180,
709    Concentricity = 181,
710    Straightness = 182,
711    Parallelism = 183,
712    Flatness = 184,
713    ProfileOfLine = 185,
714    SurfaceProfile = 186,
715    Symmetry = 187,
716    Perpendicularity = 188,
717    Runout = 189,
718    TotalRunout = 190,
719    Position = 191,
720    CenterLine = 192,
721    PartingLine = 193,
722    IsoEnvelope = 195,
723    IsoEnvelopeNonY145M = 196,
724    FreeState = 197,
725    StatisticalTolerance = 198,
726    ContinuousFeature = 199,
727    Independency = 200,
728    Depth = 201,
729    Start = 202,
730    LeastCondition = 203,
731    MaxCondition = 204,
732    ConicalTaper = 205,
733    Projected = 206,
734    Slope = 207,
735    Micro = 208,
736    TangentPlane = 210,
737    Unilateral = 211,
738    SquareFeature = 212,
739    Countersink = 213,
740    SpotFace = 214,
741    Target = 215,
742    Diameter = 216,
743    Radius = 217,
744    SphericalRadius = 218,
745    SphericalDiameter = 219,
746    ControlledRadius = 220,
747    BoxStart = 123,
748    BoxBar = 162,
749    BoxBarBetween = 124,
750    LetterBackwardUnderline = 95,
751    PunctuationBackwardUnderline = 92,
752    ModifierBackwardUnderline = 126,
753    NumericBackwardUnderline = 96,
754    BoxEnd = 125,
755    DatumUp = 166,
756    DatumLeft = 168,
757    DatumRight = 167,
758    DatumDown = 165,
759    DatumTriangle = 295,
760    HalfSpace = 236,
761    QuarterSpace = 237,
762    EighthSpace = 238,
763    ModifierSpace = 239,
764}
765
766/// The type of camera drag interaction.
767#[derive(
768    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
769)]
770#[serde(rename_all = "lowercase")]
771#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
772#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
773#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
774#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
775pub enum CameraDragInteractionType {
776    /// Camera pan
777    Pan,
778    /// Camera rotate (spherical camera revolve/orbit)
779    Rotate,
780    /// Camera rotate (trackball with 3 degrees of freedom)
781    RotateTrackball,
782    /// Camera zoom (increase or decrease distance to reference point center)
783    Zoom,
784}
785
786/// A segment of a path.
787/// Paths are composed of many segments.
788#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)]
789#[serde(rename_all = "snake_case", tag = "type")]
790#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
791#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
792#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
793#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
794pub enum PathSegment {
795    /// A straight line segment.
796    /// Goes from the current path "pen" to the given endpoint.
797    Line {
798        /// End point of the line.
799        end: Point3d<LengthUnit>,
800        ///Whether or not this line is a relative offset
801        relative: bool,
802    },
803    /// A circular arc segment.
804    /// Arcs can be drawn clockwise when start > end.
805    Arc {
806        /// Center of the circle
807        center: Point2d<LengthUnit>,
808        /// Radius of the circle
809        radius: LengthUnit,
810        /// Start of the arc along circle's perimeter.
811        start: Angle,
812        /// End of the arc along circle's perimeter.
813        end: Angle,
814        ///Whether or not this arc is a relative offset
815        relative: bool,
816    },
817    /// A cubic bezier curve segment.
818    /// Start at the end of the current line, go through control point 1 and 2, then end at a
819    /// given point.
820    Bezier {
821        /// First control point.
822        control1: Point3d<LengthUnit>,
823        /// Second control point.
824        control2: Point3d<LengthUnit>,
825        /// Final control point.
826        end: Point3d<LengthUnit>,
827        ///Whether or not this bezier is a relative offset
828        relative: bool,
829    },
830    /// Adds a tangent arc from current pen position with the given radius and angle.
831    TangentialArc {
832        /// Radius of the arc.
833        /// Not to be confused with Raiders of the Lost Ark.
834        radius: LengthUnit,
835        /// Offset of the arc. Negative values will arc clockwise.
836        offset: Angle,
837    },
838    /// Adds a tangent arc from current pen position to the new position.
839    /// Arcs will choose a clockwise or counter-clockwise direction based on the arc end position.
840    TangentialArcTo {
841        /// Where the arc should end.
842        /// Must lie in the same plane as the current path pen position.
843        /// Must not be colinear with current path pen position.
844        to: Point3d<LengthUnit>,
845        /// 0 will be interpreted as none/null.
846        angle_snap_increment: Option<Angle>,
847    },
848    ///Adds an arc from the current position that goes through the given interior point and ends at the given end position
849    ArcTo {
850        /// Interior point of the arc.
851        interior: Point3d<LengthUnit>,
852        /// End point of the arc.
853        end: Point3d<LengthUnit>,
854        ///Whether or not interior and end are relative to the previous path position
855        relative: bool,
856    },
857    ///Adds a circular involute from the current position that goes through the given end_radius
858    ///and is rotated around the current point by angle.
859    CircularInvolute {
860        ///The involute is described between two circles, start_radius is the radius of the inner
861        ///circle.
862        start_radius: LengthUnit,
863        ///The involute is described between two circles, end_radius is the radius of the outer
864        ///circle.
865        end_radius: LengthUnit,
866        ///The angle to rotate the involute by. A value of zero will produce a curve with a tangent
867        ///along the x-axis at the start point of the curve.
868        angle: Angle,
869        ///If reverse is true, the segment will start
870        ///from the end of the involute, otherwise it will start from that start.
871        reverse: bool,
872    },
873    ///Adds an elliptical arc segment.
874    Ellipse {
875        /// The center point of the ellipse.
876        center: Point2d<LengthUnit>,
877        /// Major axis of the ellipse.
878        major_axis: Point2d<LengthUnit>,
879        /// Minor radius of the ellipse.
880        minor_radius: LengthUnit,
881        /// Start of the path along the perimeter of the ellipse.
882        start_angle: Angle,
883        /// End of the path along the perimeter of the ellipse.
884        end_angle: Angle,
885    },
886    ///Adds a generic conic section specified by the end point, interior point and tangents at the
887    ///start and end of the section.
888    ConicTo {
889        /// Interior point that lies on the conic.
890        interior: Point2d<LengthUnit>,
891        /// End point of the conic.
892        end: Point2d<LengthUnit>,
893        /// Tangent at the start of the conic.
894        start_tangent: Point2d<LengthUnit>,
895        /// Tangent at the end of the conic.
896        end_tangent: Point2d<LengthUnit>,
897        /// Whether or not the interior and end points are relative to the previous path position.
898        relative: bool,
899    },
900}
901
902/// An angle, with a specific unit.
903#[derive(Clone, Copy, PartialEq, Debug, JsonSchema, Deserialize, Serialize)]
904#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
905#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
906#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
907#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
908pub struct Angle {
909    /// What unit is the measurement?
910    pub unit: UnitAngle,
911    /// The size of the angle, measured in the chosen unit.
912    pub value: f64,
913}
914
915impl Angle {
916    /// Converts a given angle to degrees.
917    pub fn to_degrees(self) -> f64 {
918        match self.unit {
919            UnitAngle::Degrees => self.value,
920            UnitAngle::Radians => self.value.to_degrees(),
921        }
922    }
923    /// Converts a given angle to radians.
924    pub fn to_radians(self) -> f64 {
925        match self.unit {
926            UnitAngle::Degrees => self.value.to_radians(),
927            UnitAngle::Radians => self.value,
928        }
929    }
930    /// Create an angle in degrees.
931    pub const fn from_degrees(value: f64) -> Self {
932        Self {
933            unit: UnitAngle::Degrees,
934            value,
935        }
936    }
937    /// Create an angle in radians.
938    pub const fn from_radians(value: f64) -> Self {
939        Self {
940            unit: UnitAngle::Radians,
941            value,
942        }
943    }
944    /// 360 degrees.
945    pub const fn turn() -> Self {
946        Self::from_degrees(360.0)
947    }
948    /// 180 degrees.
949    pub const fn half_circle() -> Self {
950        Self::from_degrees(180.0)
951    }
952    /// 90 degrees.
953    pub const fn quarter_circle() -> Self {
954        Self::from_degrees(90.0)
955    }
956    /// 0 degrees.
957    pub const fn zero() -> Self {
958        Self::from_degrees(0.0)
959    }
960}
961
962/// 0 degrees.
963impl Default for Angle {
964    /// 0 degrees.
965    fn default() -> Self {
966        Self::zero()
967    }
968}
969
970impl PartialOrd for Angle {
971    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
972        match (self.unit, other.unit) {
973            // Avoid unnecessary floating point operations.
974            (UnitAngle::Degrees, UnitAngle::Degrees) => self.value.partial_cmp(&other.value),
975            (UnitAngle::Radians, UnitAngle::Radians) => self.value.partial_cmp(&other.value),
976            _ => self.to_degrees().partial_cmp(&other.to_degrees()),
977        }
978    }
979}
980
981impl std::ops::Add for Angle {
982    type Output = Self;
983
984    fn add(self, rhs: Self) -> Self::Output {
985        Self {
986            unit: UnitAngle::Degrees,
987            value: self.to_degrees() + rhs.to_degrees(),
988        }
989    }
990}
991
992impl std::ops::AddAssign for Angle {
993    fn add_assign(&mut self, rhs: Self) {
994        match self.unit {
995            UnitAngle::Degrees => {
996                self.value += rhs.to_degrees();
997            }
998            UnitAngle::Radians => {
999                self.value += rhs.to_radians();
1000            }
1001        }
1002    }
1003}
1004
1005/// The type of scene selection change
1006#[derive(
1007    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
1008)]
1009#[serde(rename_all = "lowercase")]
1010#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1011#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1012#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1013#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1014pub enum SceneSelectionType {
1015    /// Replaces the selection
1016    Replace,
1017    /// Adds to the selection
1018    Add,
1019    /// Removes from the selection
1020    Remove,
1021}
1022
1023/// The type of scene's active tool
1024#[allow(missing_docs)]
1025#[derive(
1026    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
1027)]
1028#[serde(rename_all = "snake_case")]
1029#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1030#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1031#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1032#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1033pub enum SceneToolType {
1034    CameraRevolve,
1035    Select,
1036    Move,
1037    SketchLine,
1038    SketchTangentialArc,
1039    SketchCurve,
1040    SketchCurveMod,
1041}
1042
1043/// The path component constraint bounds type
1044#[allow(missing_docs)]
1045#[derive(
1046    Display,
1047    FromStr,
1048    Copy,
1049    Eq,
1050    PartialEq,
1051    Debug,
1052    JsonSchema,
1053    Deserialize,
1054    Serialize,
1055    Sequence,
1056    Clone,
1057    Ord,
1058    PartialOrd,
1059    Default,
1060)]
1061#[serde(rename_all = "snake_case")]
1062#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1063#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1064#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1065#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1066pub enum PathComponentConstraintBound {
1067    #[default]
1068    Unconstrained,
1069    PartiallyConstrained,
1070    FullyConstrained,
1071}
1072
1073/// The path component constraint type
1074#[allow(missing_docs)]
1075#[derive(
1076    Display,
1077    FromStr,
1078    Copy,
1079    Eq,
1080    PartialEq,
1081    Debug,
1082    JsonSchema,
1083    Deserialize,
1084    Serialize,
1085    Sequence,
1086    Clone,
1087    Ord,
1088    PartialOrd,
1089    Default,
1090)]
1091#[serde(rename_all = "snake_case")]
1092#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1093#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1094#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1095#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1096pub enum PathComponentConstraintType {
1097    #[default]
1098    Unconstrained,
1099    Vertical,
1100    Horizontal,
1101    EqualLength,
1102    Parallel,
1103    AngleBetween,
1104}
1105
1106/// The path component command type (within a Path)
1107#[allow(missing_docs)]
1108#[derive(
1109    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
1110)]
1111#[serde(rename_all = "snake_case")]
1112#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1113#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1114#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1115#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1116pub enum PathCommand {
1117    MoveTo,
1118    LineTo,
1119    BezCurveTo,
1120    NurbsCurveTo,
1121    AddArc,
1122}
1123
1124/// The type of entity
1125#[allow(missing_docs)]
1126#[derive(
1127    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
1128)]
1129#[serde(rename_all = "lowercase")]
1130#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1131#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1132#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1133#[repr(u8)]
1134#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1135pub enum EntityType {
1136    Entity,
1137    Object,
1138    Path,
1139    Segment,
1140    Curve,
1141    Solid2D,
1142    Solid3D,
1143    Edge,
1144    Face,
1145    Plane,
1146    Vertex,
1147    Region,
1148}
1149
1150/// The type of Curve (embedded within path)
1151#[allow(missing_docs)]
1152#[derive(
1153    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
1154)]
1155#[serde(rename_all = "snake_case")]
1156#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1157#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1158#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1159#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1160pub enum CurveType {
1161    Line,
1162    Arc,
1163    Nurbs,
1164}
1165
1166/// A file to be exported to the client.
1167#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Builder)]
1168#[cfg_attr(feature = "python", pyo3::pyclass, pyo3_stub_gen::derive::gen_stub_pyclass)]
1169#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1170pub struct ExportFile {
1171    /// The name of the file.
1172    pub name: String,
1173    /// The contents of the file, base64 encoded.
1174    pub contents: crate::base64::Base64Data,
1175}
1176
1177#[cfg(feature = "python")]
1178#[pyo3_stub_gen::derive::gen_stub_pymethods]
1179#[pyo3::pymethods]
1180impl ExportFile {
1181    #[getter]
1182    fn contents(&self) -> Vec<u8> {
1183        self.contents.0.clone()
1184    }
1185
1186    #[getter]
1187    fn name(&self) -> String {
1188        self.name.clone()
1189    }
1190}
1191
1192/// The valid types of output file formats.
1193#[derive(
1194    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd, Sequence,
1195)]
1196#[serde(rename_all = "lowercase")]
1197#[display(style = "lowercase")]
1198#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1199#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1200#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1201#[cfg_attr(feature = "python", pyo3::pyclass, pyo3_stub_gen::derive::gen_stub_pyclass_enum)]
1202#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1203pub enum FileExportFormat {
1204    /// Autodesk Filmbox (FBX) format. <https://en.wikipedia.org/wiki/FBX>
1205    Fbx,
1206    /// Binary glTF 2.0.
1207    ///
1208    /// This is a single binary with .glb extension.
1209    ///
1210    /// This is better if you want a compressed format as opposed to the human readable
1211    /// glTF that lacks compression.
1212    Glb,
1213    /// glTF 2.0.
1214    /// Embedded glTF 2.0 (pretty printed).
1215    ///
1216    /// Single JSON file with .gltf extension binary data encoded as
1217    /// base64 data URIs.
1218    ///
1219    /// The JSON contents are pretty printed.
1220    ///
1221    /// It is human readable, single file, and you can view the
1222    /// diff easily in a git commit.
1223    Gltf,
1224    /// The OBJ file format. <https://en.wikipedia.org/wiki/Wavefront_.obj_file>
1225    /// It may or may not have an an attached material (mtl // mtllib) within the file,
1226    /// but we interact with it as if it does not.
1227    Obj,
1228    /// The PLY file format. <https://en.wikipedia.org/wiki/PLY_(file_format)>
1229    Ply,
1230    /// The STEP file format. <https://en.wikipedia.org/wiki/ISO_10303-21>
1231    Step,
1232    /// The STL file format. <https://en.wikipedia.org/wiki/STL_(file_format)>
1233    Stl,
1234}
1235
1236/// The valid types of 2D output file formats.
1237#[derive(
1238    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd, Sequence,
1239)]
1240#[serde(rename_all = "lowercase")]
1241#[display(style = "lowercase")]
1242#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1243#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1244#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1245#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1246pub enum FileExportFormat2d {
1247    /// AutoCAD drawing interchange format.
1248    Dxf,
1249}
1250
1251/// The valid types of source file formats.
1252#[derive(
1253    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd, Sequence,
1254)]
1255#[serde(rename_all = "lowercase")]
1256#[display(style = "lowercase")]
1257#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1258#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1259#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1260#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1261pub enum FileImportFormat {
1262    /// Autodesk Filmbox (FBX) format. <https://en.wikipedia.org/wiki/FBX>
1263    Fbx,
1264    /// glTF 2.0.
1265    Gltf,
1266    /// The OBJ file format. <https://en.wikipedia.org/wiki/Wavefront_.obj_file>
1267    /// It may or may not have an an attached material (mtl // mtllib) within the file,
1268    /// but we interact with it as if it does not.
1269    Obj,
1270    /// The PLY file format. <https://en.wikipedia.org/wiki/PLY_(file_format)>
1271    Ply,
1272    /// SolidWorks part (SLDPRT) format.
1273    Sldprt,
1274    /// The STEP file format. <https://en.wikipedia.org/wiki/ISO_10303-21>
1275    Step,
1276    /// The STL file format. <https://en.wikipedia.org/wiki/STL_(file_format)>
1277    Stl,
1278}
1279
1280/// The type of error sent by the KittyCAD graphics engine.
1281#[derive(Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, Ord, PartialOrd)]
1282#[serde(rename_all = "snake_case")]
1283#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1284#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1285#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1286#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1287pub enum EngineErrorCode {
1288    /// User requested something geometrically or graphically impossible.
1289    /// Don't retry this request, as it's inherently impossible. Instead, read the error message
1290    /// and change your request.
1291    BadRequest = 1,
1292    /// Graphics engine failed to complete request, consider retrying
1293    InternalEngine,
1294}
1295
1296impl From<EngineErrorCode> for http::StatusCode {
1297    fn from(e: EngineErrorCode) -> Self {
1298        match e {
1299            EngineErrorCode::BadRequest => Self::BAD_REQUEST,
1300            EngineErrorCode::InternalEngine => Self::INTERNAL_SERVER_ERROR,
1301        }
1302    }
1303}
1304
1305/// What kind of blend to do
1306#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
1307#[serde(rename_all = "snake_case")]
1308#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1309#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1310#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1311pub enum BlendType {
1312    /// Use the tangent of the surfaces to calculate the blend.
1313    #[default]
1314    Tangent,
1315}
1316
1317/// Body type determining if the operation will create a manifold (solid) body or a non-manifold collection of surfaces.
1318#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
1319#[serde(rename_all = "snake_case")]
1320#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1321#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1322#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1323#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1324pub enum BodyType {
1325    ///Defines a body that is manifold.
1326    #[default]
1327    Solid,
1328    ///Defines a body that is non-manifold (an open collection of connected surfaces).
1329    Surface,
1330}
1331
1332/// Extrusion method determining if the extrusion will be part of the existing object or an
1333/// entirely new object.
1334#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
1335#[serde(rename_all = "snake_case")]
1336#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1337#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1338#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1339#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1340pub enum ExtrudeMethod {
1341    /// Create a new object that is not connected to the object it is extruded from. This will
1342    /// result in two objects after the operation.
1343    New,
1344    /// This extrusion will be part of object it is extruded from. This will result in one object
1345    /// after the operation.
1346    #[default]
1347    Merge,
1348}
1349
1350/// Type of reference geometry to extrude to.
1351#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
1352#[serde(rename_all = "snake_case")]
1353#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1354#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1355#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1356#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1357pub enum ExtrudeReference {
1358    /// Extrudes along the normal of the top face until it is as close to the entity as possible.
1359    /// An entity can be a solid, a path, a face, an edge (via `entity_reference`), etc.
1360    EntityReference {
1361        /// Legacy UUID of the entity to extrude to. If both `entity_id` and `entity_reference` are provided, `entity_reference` takes precedence.
1362        #[serde(default, skip_serializing_if = "Option::is_none")]
1363        entity_id: Option<Uuid>,
1364        /// Entity reference (e.g. edge by side_faces). If both `entity_id` and `entity_reference` are provided, `entity_reference` takes precedence.
1365        #[serde(default, skip_serializing_if = "Option::is_none")]
1366        entity_reference: Option<EntityReference>,
1367    },
1368    /// Extrudes until the top face is as close as possible to this given axis.
1369    Axis {
1370        /// The axis to extrude to.
1371        axis: Point3d<f64>,
1372        /// Point the axis goes through.
1373        /// Defaults to (0, 0, 0).
1374        #[serde(default)]
1375        point: Point3d<LengthUnit>,
1376    },
1377    /// Extrudes until the top face is as close as possible to this given point.
1378    Point {
1379        /// The point to extrude to.
1380        point: Point3d<LengthUnit>,
1381    },
1382}
1383
1384/// IDs for the extruded faces.
1385#[derive(Debug, PartialEq, Serialize, Deserialize, JsonSchema, Clone, Builder)]
1386#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1387#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1388#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1389#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1390pub struct ExtrudedFaceInfo {
1391    /// The face made from the original 2D shape being extruded.
1392    /// If the solid is extruded from a shape which already has an ID
1393    /// (e.g. extruding something which was sketched on a face), this
1394    /// doesn't need to be sent.
1395    pub bottom: Option<Uuid>,
1396    /// Top face of the extrusion (parallel and further away from the original 2D shape being extruded).
1397    pub top: Uuid,
1398    /// Any intermediate sides between the top and bottom.
1399    pub sides: Vec<SideFace>,
1400}
1401
1402/// IDs for a side face, extruded from the path of some sketch/2D shape.
1403#[derive(Debug, PartialEq, Serialize, Deserialize, JsonSchema, Clone, Builder)]
1404#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1405#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1406#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1407#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1408pub struct SideFace {
1409    /// ID of the path this face is being extruded from.
1410    pub path_id: Uuid,
1411    /// Desired ID for the resulting face.
1412    pub face_id: Uuid,
1413}
1414
1415/// Camera settings including position, center, fov etc
1416#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Builder)]
1417#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1418#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1419#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1420#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1421pub struct CameraSettings {
1422    ///Camera position (vantage)
1423    pub pos: Point3d,
1424
1425    ///Camera's look-at center (center-pos gives viewing vector)
1426    pub center: Point3d,
1427
1428    ///Camera's world-space up vector
1429    pub up: Point3d,
1430
1431    ///The Camera's orientation (in the form of a quaternion)
1432    pub orientation: Quaternion,
1433
1434    ///Camera's field-of-view angle (if ortho is false)
1435    pub fov_y: Option<f32>,
1436
1437    ///The camera's ortho scale (derived from viewing distance if ortho is true)
1438    pub ortho_scale: Option<f32>,
1439
1440    ///Whether or not the camera is in ortho mode
1441    pub ortho: bool,
1442}
1443
1444#[allow(missing_docs)]
1445#[repr(u8)]
1446#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
1447#[serde(rename_all = "snake_case")]
1448#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1449#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1450#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1451#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1452pub enum WorldCoordinateSystem {
1453    #[default]
1454    RightHandedUpZ,
1455    RightHandedUpY,
1456}
1457
1458#[allow(missing_docs)]
1459#[repr(C)]
1460#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
1461#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1462#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1463#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1464#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1465pub struct CameraViewState {
1466    pub pivot_rotation: Quaternion,
1467    pub pivot_position: Point3d,
1468    pub eye_offset: f32,
1469    pub fov_y: f32,
1470    pub ortho_scale_factor: f32,
1471    pub is_ortho: bool,
1472    pub ortho_scale_enabled: bool,
1473    pub world_coord_system: WorldCoordinateSystem,
1474}
1475
1476impl Default for CameraViewState {
1477    fn default() -> Self {
1478        CameraViewState {
1479            pivot_rotation: Default::default(),
1480            pivot_position: Default::default(),
1481            eye_offset: 10.0,
1482            fov_y: 45.0,
1483            ortho_scale_factor: 1.6,
1484            is_ortho: false,
1485            ortho_scale_enabled: true,
1486            world_coord_system: Default::default(),
1487        }
1488    }
1489}
1490
1491#[cfg(feature = "cxx")]
1492impl_extern_type! {
1493    [Trivial]
1494    CameraViewState = "Endpoints::CameraViewState"
1495}
1496
1497impl From<CameraSettings> for crate::output::DefaultCameraZoom {
1498    fn from(settings: CameraSettings) -> Self {
1499        Self { settings }
1500    }
1501}
1502impl From<CameraSettings> for crate::output::CameraDragMove {
1503    fn from(settings: CameraSettings) -> Self {
1504        Self { settings }
1505    }
1506}
1507impl From<CameraSettings> for crate::output::CameraDragEnd {
1508    fn from(settings: CameraSettings) -> Self {
1509        Self { settings }
1510    }
1511}
1512impl From<CameraSettings> for crate::output::DefaultCameraGetSettings {
1513    fn from(settings: CameraSettings) -> Self {
1514        Self { settings }
1515    }
1516}
1517impl From<CameraSettings> for crate::output::ZoomToFit {
1518    fn from(settings: CameraSettings) -> Self {
1519        Self { settings }
1520    }
1521}
1522impl From<CameraSettings> for crate::output::OrientToFace {
1523    fn from(settings: CameraSettings) -> Self {
1524        Self { settings }
1525    }
1526}
1527impl From<CameraSettings> for crate::output::ViewIsometric {
1528    fn from(settings: CameraSettings) -> Self {
1529        Self { settings }
1530    }
1531}
1532
1533/// Defines a perspective view.
1534#[derive(Copy, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Clone, PartialOrd, Default, Builder)]
1535#[serde(rename_all = "snake_case")]
1536#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1537#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1538#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1539#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1540pub struct PerspectiveCameraParameters {
1541    /// Camera frustum vertical field of view.
1542    pub fov_y: Option<f32>,
1543    /// Camera frustum near plane.
1544    pub z_near: Option<f32>,
1545    /// Camera frustum far plane.
1546    pub z_far: Option<f32>,
1547}
1548
1549/// A type of camera movement applied after certain camera operations
1550#[derive(
1551    Default,
1552    Display,
1553    FromStr,
1554    Copy,
1555    Eq,
1556    PartialEq,
1557    Debug,
1558    JsonSchema,
1559    Deserialize,
1560    Serialize,
1561    Sequence,
1562    Clone,
1563    Ord,
1564    PartialOrd,
1565)]
1566#[serde(rename_all = "snake_case")]
1567#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1568#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1569#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1570#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1571pub enum CameraMovement {
1572    /// Adjusts the camera position during the camera operation
1573    #[default]
1574    Vantage,
1575    /// Keeps the camera position in place
1576    None,
1577}
1578
1579/// The global axes.
1580#[derive(
1581    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
1582)]
1583#[serde(rename_all = "lowercase")]
1584#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1585#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1586#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1587#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1588pub enum GlobalAxis {
1589    /// The X axis
1590    X,
1591    /// The Y axis
1592    Y,
1593    /// The Z axis
1594    Z,
1595}
1596
1597/// Possible types of faces which can be extruded from a 3D solid.
1598#[derive(
1599    Display, FromStr, Copy, Eq, PartialEq, Debug, JsonSchema, Deserialize, Serialize, Sequence, Clone, Ord, PartialOrd,
1600)]
1601#[serde(rename_all = "snake_case")]
1602#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1603#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1604#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1605#[repr(u8)]
1606#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1607pub enum ExtrusionFaceCapType {
1608    /// Uncapped.
1609    None,
1610    /// Capped on top.
1611    Top,
1612    /// Capped below.
1613    Bottom,
1614    /// Capped on both ends.
1615    Both,
1616}
1617
1618/// Post effect type
1619#[allow(missing_docs)]
1620#[derive(
1621    Display,
1622    FromStr,
1623    Copy,
1624    Eq,
1625    PartialEq,
1626    Debug,
1627    JsonSchema,
1628    Deserialize,
1629    Serialize,
1630    Sequence,
1631    Clone,
1632    Ord,
1633    PartialOrd,
1634    Default,
1635)]
1636#[serde(rename_all = "lowercase")]
1637#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1638#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1639#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1640#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1641pub enum PostEffectType {
1642    Phosphor,
1643    Ssao,
1644    #[default]
1645    NoEffect,
1646}
1647
1648// Enum: Connect Rust Enums to Cpp
1649// add our native c++ names for our cxx::ExternType implementation
1650#[cfg(feature = "cxx")]
1651impl_extern_type! {
1652    [Trivial]
1653    // File
1654    FileImportFormat = "Enums::_FileImportFormat"
1655    FileExportFormat = "Enums::_FileExportFormat"
1656    // Camera
1657    CameraDragInteractionType = "Enums::_CameraDragInteractionType"
1658    // Scene
1659    SceneSelectionType = "Enums::_SceneSelectionType"
1660    SceneToolType = "Enums::_SceneToolType"
1661    BlendType = "Enums::_BlendType"
1662    BodyType = "Enums::_BodyType"
1663    EntityType = "Enums::_EntityType"
1664    AnnotationType = "Enums::_AnnotationType"
1665    AnnotationTextAlignmentX = "Enums::_AnnotationTextAlignmentX"
1666    AnnotationTextAlignmentY = "Enums::_AnnotationTextAlignmentY"
1667    AnnotationLineEnd = "Enums::_AnnotationLineEnd"
1668    MbdStandard = "Enums::_MBDStandard"
1669    MbdSymbol = "Enums::_MBDSymbol"
1670
1671    CurveType = "Enums::_CurveType"
1672    PathCommand = "Enums::_PathCommand"
1673    PathComponentConstraintBound = "Enums::_PathComponentConstraintBound"
1674    PathComponentConstraintType = "Enums::_PathComponentConstraintType"
1675    ExtrusionFaceCapType  = "Enums::_ExtrusionFaceCapType"
1676
1677    // Utils
1678    EngineErrorCode = "Enums::_ErrorCode"
1679    GlobalAxis = "Enums::_GlobalAxis"
1680    OriginType = "Enums::_OriginType"
1681
1682    // Graphics engine
1683    PostEffectType = "Enums::_PostEffectType"
1684}
1685
1686fn bool_true() -> bool {
1687    true
1688}
1689fn same_scale() -> Point3d<f64> {
1690    Point3d::uniform(1.0)
1691}
1692
1693fn z_axis() -> Point3d<f64> {
1694    Point3d { x: 0.0, y: 0.0, z: 1.0 }
1695}
1696
1697impl ExtrudedFaceInfo {
1698    /// Converts from the representation used in the Extrude modeling command,
1699    /// to a flat representation.
1700    pub fn list_faces(self) -> Vec<ExtrusionFaceInfo> {
1701        let mut face_infos: Vec<_> = self
1702            .sides
1703            .into_iter()
1704            .map(|side| ExtrusionFaceInfo {
1705                curve_id: Some(side.path_id),
1706                face_id: Some(side.face_id),
1707                cap: ExtrusionFaceCapType::None,
1708            })
1709            .collect();
1710        face_infos.push(ExtrusionFaceInfo {
1711            curve_id: None,
1712            face_id: Some(self.top),
1713            cap: ExtrusionFaceCapType::Top,
1714        });
1715        if let Some(bottom) = self.bottom {
1716            face_infos.push(ExtrusionFaceInfo {
1717                curve_id: None,
1718                face_id: Some(bottom),
1719                cap: ExtrusionFaceCapType::Bottom,
1720            });
1721        }
1722        face_infos
1723    }
1724}
1725
1726#[cfg(test)]
1727mod tests {
1728    use super::*;
1729
1730    #[test]
1731    fn test_angle_comparison() {
1732        let a = Angle::from_degrees(90.0);
1733        assert!(a < Angle::from_degrees(91.0));
1734        assert!(a > Angle::from_degrees(89.0));
1735        assert!(a <= Angle::from_degrees(90.0));
1736        assert!(a >= Angle::from_degrees(90.0));
1737        let b = Angle::from_radians(std::f64::consts::FRAC_PI_4);
1738        assert!(b < Angle::from_radians(std::f64::consts::FRAC_PI_2));
1739        assert!(b > Angle::from_radians(std::f64::consts::FRAC_PI_8));
1740        assert!(b <= Angle::from_radians(std::f64::consts::FRAC_PI_4));
1741        assert!(b >= Angle::from_radians(std::f64::consts::FRAC_PI_4));
1742        // Mixed units.
1743        assert!(a > b);
1744        assert!(a >= b);
1745        assert!(b < a);
1746        assert!(b <= a);
1747        let c = Angle::from_radians(std::f64::consts::FRAC_PI_2 * 3.0);
1748        assert!(a < c);
1749        assert!(a <= c);
1750        assert!(c > a);
1751        assert!(c >= a);
1752    }
1753}
1754
1755/// How a property of an object should be transformed.
1756#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, Builder)]
1757#[schemars(rename = "TransformByFor{T}")]
1758#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1759#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1760#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1761#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1762pub struct TransformBy<T> {
1763    /// The scale, or rotation, or translation.
1764    pub property: T,
1765    /// If true, overwrite the previous value with this.
1766    /// If false, the previous value will be modified.
1767    /// E.g. when translating, `set=true` will set a new location,
1768    /// and `set=false` will translate the current location by the given X/Y/Z.
1769    pub set: bool,
1770    /// What to use as the origin for the transformation.
1771    #[serde(default)]
1772    #[builder(default)]
1773    pub origin: OriginType,
1774}
1775
1776impl<T> TransformBy<T> {
1777    /// Get the origin of this transformation.
1778    /// Reads from the `origin` field if it's set, otherwise
1779    /// falls back to the `is_local` field.
1780    pub fn get_origin(&self) -> OriginType {
1781        self.origin
1782    }
1783}
1784
1785/// Container that holds a translate, rotate and scale.
1786/// Defaults to no change, everything stays the same (i.e. the identity function).
1787#[derive(Clone, Debug, PartialEq, Deserialize, JsonSchema, Serialize, Default, Builder)]
1788#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1789#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1790#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1791#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1792pub struct ComponentTransform {
1793    /// Translate component of the transform.
1794    pub translate: Option<TransformBy<Point3d<LengthUnit>>>,
1795    /// Rotate component of the transform.
1796    /// The rotation is specified as a roll, pitch, yaw.
1797    pub rotate_rpy: Option<TransformBy<Point3d<f64>>>,
1798    /// Rotate component of the transform.
1799    /// The rotation is specified as an axis and an angle (xyz are the components of the axis, w is
1800    /// the angle in degrees).
1801    pub rotate_angle_axis: Option<TransformBy<Point4d<f64>>>,
1802    /// Scale component of the transform.
1803    pub scale: Option<TransformBy<Point3d<f64>>>,
1804}
1805
1806///If bidirectional or symmetric operations are needed this enum encapsulates the required
1807///information.
1808#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
1809#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1810#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1811#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1812#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1813pub enum Opposite<T> {
1814    /// No opposite. The operation will only occur on one side.
1815    #[default]
1816    None,
1817    /// Operation will occur from both sides, with the same value.
1818    Symmetric,
1819    /// Operation will occur from both sides, with this value for the opposite.
1820    Other(T),
1821}
1822
1823impl<T: JsonSchema> JsonSchema for Opposite<T> {
1824    fn schema_name() -> String {
1825        format!("OppositeFor{}", T::schema_name())
1826    }
1827
1828    fn schema_id() -> std::borrow::Cow<'static, str> {
1829        std::borrow::Cow::Owned(format!("{}::Opposite<{}>", module_path!(), T::schema_id()))
1830    }
1831
1832    fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1833        SchemaObject {
1834            instance_type: Some(schemars::schema::InstanceType::String.into()),
1835            ..Default::default()
1836        }
1837        .into()
1838    }
1839}
1840
1841/// What strategy (algorithm) should be used for cutting?
1842/// Defaults to Automatic.
1843#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
1844#[serde(rename_all = "snake_case")]
1845#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1846#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1847#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1848#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1849pub enum CutStrategy {
1850    /// Basic fillet cut. This has limitations, like the filletted edges
1851    /// can't touch each other. But it's very fast and simple.
1852    Basic,
1853    /// More complicated fillet cut. It works for more use-cases, like
1854    /// edges that touch each other. But it's slower than the Basic method.
1855    Csg,
1856    /// Tries the Basic method, and if that doesn't work, tries the CSG strategy.
1857    #[default]
1858    Automatic,
1859}
1860
1861/// What is the given geometry relative to?
1862#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
1863#[serde(rename_all = "snake_case")]
1864#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1865#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1866#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1867#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1868pub enum RelativeTo {
1869    /// Local/relative to a position centered within the plane being sketched on
1870    #[default]
1871    SketchPlane,
1872    /// Local/relative to the trajectory curve
1873    TrajectoryCurve,
1874}
1875
1876/// The region a user clicked on.
1877#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema, Builder)]
1878#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1879#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1880#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1881pub struct SelectedRegion {
1882    /// First segment to follow to find the region.
1883    pub segment: Uuid,
1884    /// Second segment to follow to find the region.
1885    /// Intersects the first segment.
1886    pub intersection_segment: Uuid,
1887    /// At which intersection between `segment` and `intersection_segment`
1888    /// should we stop following the `segment` and start following `intersection_segment`?
1889    /// Defaults to -1, which means the last intersection.
1890    #[serde(default = "negative_one")]
1891    pub intersection_index: i32,
1892    /// By default (when this is false), curve counterclockwise at intersections.
1893    /// If this is true, instead curve clockwise.
1894    #[serde(default)]
1895    pub curve_clockwise: bool,
1896}
1897
1898impl Default for SelectedRegion {
1899    fn default() -> Self {
1900        Self {
1901            segment: Default::default(),
1902            intersection_segment: Default::default(),
1903            intersection_index: -1,
1904            curve_clockwise: Default::default(),
1905        }
1906    }
1907}
1908
1909/// An edge id and an upper and lower percentage bound of the edge.
1910#[derive(Builder, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1911#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1912#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1913#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1914#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1915pub struct FractionOfEdge {
1916    /// The id of the edge (legacy). If both `edge_id` and `edge_specifier` are provided, `edge_specifier` takes precedence.
1917    #[serde(default, skip_serializing_if = "Option::is_none")]
1918    pub edge_id: Option<Uuid>,
1919    /// Edge specifier (side_faces, end_faces, index) identifying the edge. If both `edge_id` and `edge_specifier` are provided, `edge_specifier` takes precedence.
1920    #[serde(default, skip_serializing_if = "Option::is_none")]
1921    pub edge_specifier: Option<EdgeSpecifier>,
1922    /// A value between [0.0, 1.0] (default 0.0) that is a percentage along the edge. This bound
1923    /// will control how much of the edge is used during the blend.
1924    /// If lower_bound is larger than upper_bound, the edge is effectively "flipped".
1925    #[serde(default)]
1926    #[builder(default)]
1927    #[schemars(range(min = 0, max = 1))]
1928    pub lower_bound: f32,
1929    /// A value between [0.0, 1.0] (default 1.0) that is a percentage along the edge. This bound
1930    /// will control how much of the edge is used during the blend.
1931    /// If lower_bound is larger than upper_bound, the edge is effectively "flipped".
1932    #[serde(default = "one")]
1933    #[builder(default = one())]
1934    #[schemars(range(min = 0, max = 1))]
1935    pub upper_bound: f32,
1936}
1937
1938/// An object id, that corresponds to a surface body, and a list of edges of the surface.
1939#[derive(Builder, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1940#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1941#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1942#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1943#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1944pub struct SurfaceEdgeReference {
1945    /// The id of the body.
1946    pub object_id: Uuid,
1947    /// A list of the edge ids that belong to the body.
1948    pub edges: Vec<FractionOfEdge>,
1949}
1950
1951/// List of bodies that were created by an operation.
1952#[derive(Builder, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
1953#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1954#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1955#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1956#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1957pub struct BodiesCreated {
1958    /// All bodies created by this operation.
1959    pub bodies: Vec<BodyCreated>,
1960}
1961
1962impl BodiesCreated {
1963    /// Are there any bodies in this list?
1964    pub fn is_empty(&self) -> bool {
1965        self.bodies.is_empty()
1966    }
1967}
1968
1969/// List of bodies that were updated by an operation.
1970#[derive(Builder, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
1971#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1972#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1973#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1974#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1975pub struct BodiesUpdated {
1976    /// All bodies created by this operation.
1977    pub bodies: Vec<BodyUpdated>,
1978}
1979
1980impl BodiesUpdated {
1981    /// Are there any bodies in this list?
1982    pub fn is_empty(&self) -> bool {
1983        self.bodies.is_empty()
1984    }
1985}
1986
1987/// Details of a body that was created.
1988#[derive(Builder, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1989#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
1990#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1991#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
1992#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
1993pub struct BodyCreated {
1994    /// The body's ID.
1995    pub id: Uuid,
1996    /// Surfaces this body contains.
1997    pub surfaces: Vec<SurfaceCreated>,
1998}
1999
2000/// Details of a body that was updated.
2001#[derive(Builder, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2002#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
2003#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2004#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
2005#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
2006pub struct BodyUpdated {
2007    /// The body's ID.
2008    pub id: Uuid,
2009    /// Surfaces added to this body.
2010    pub surfaces: Vec<SurfaceCreated>,
2011}
2012
2013/// Details of a surface that was created under some body.
2014#[derive(Builder, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2015#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
2016#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2017#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
2018#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
2019pub struct SurfaceCreated {
2020    /// The surface's ID.
2021    pub id: Uuid,
2022    /// Which number face of the parent body is this?
2023    pub primitive_face_index: u32,
2024    /// Which segment IDs was this surface swept from?
2025    pub from_segments: Vec<Uuid>,
2026}
2027
2028/// Region-creation algorithm version.
2029#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
2030#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
2031#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
2032#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
2033#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)]
2034pub enum RegionVersion {
2035    /// The original region creation method. This should NOT be used anymore,
2036    /// but is maintained to avoid breaking old models.
2037    #[default]
2038    V0,
2039    /// Fixes the bug in V0 where creating a region would shuffle the mapping
2040    /// from segment names/IDs to actual segment geometry.
2041    V1,
2042}
2043
2044impl RegionVersion {
2045    /// Is the version V0?
2046    pub fn is_zero(&self) -> bool {
2047        matches!(self, Self::V0)
2048    }
2049}
2050
2051impl From<BodyCreated> for BodyUpdated {
2052    fn from(body: BodyCreated) -> Self {
2053        Self {
2054            id: body.id,
2055            surfaces: body.surfaces,
2056        }
2057    }
2058}
2059
2060impl From<BodyUpdated> for BodyCreated {
2061    fn from(body: BodyUpdated) -> Self {
2062        Self {
2063            id: body.id,
2064            surfaces: body.surfaces,
2065        }
2066    }
2067}
2068
2069impl From<BodiesCreated> for BodiesUpdated {
2070    fn from(bodies: BodiesCreated) -> Self {
2071        Self {
2072            bodies: bodies.bodies.into_iter().map(Into::into).collect(),
2073        }
2074    }
2075}
2076
2077impl From<BodiesUpdated> for BodiesCreated {
2078    fn from(bodies: BodiesUpdated) -> Self {
2079        Self {
2080            bodies: bodies.bodies.into_iter().map(Into::into).collect(),
2081        }
2082    }
2083}
2084
2085fn one() -> f32 {
2086    1.0
2087}