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