Skip to main content

cadmpeg_ir/
features.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Neutral construction-feature taxonomy.
3
4use std::collections::BTreeMap;
5
6use crate::ids::{
7    BodyId, CurveId, EdgeId, FaceId, FeatureInputTopologyId, HistoricalBodyId, HistoricalEdgeId,
8    HistoricalFaceId, SubdId, VertexId,
9};
10use crate::math::{Point3, Vector3};
11use schemars::JsonSchema;
12use serde::{Deserialize, Serialize};
13
14/// Identifies a neutral construction feature.
15#[derive(
16    Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
17)]
18#[serde(transparent)]
19pub struct FeatureId(pub String);
20
21impl FeatureId {
22    /// Borrow the underlying id string.
23    pub fn as_str(&self) -> &str {
24        &self.0
25    }
26}
27
28impl std::fmt::Display for FeatureId {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        f.write_str(&self.0)
31    }
32}
33
34impl<S: Into<String>> From<S> for FeatureId {
35    fn from(value: S) -> Self {
36        Self(value.into())
37    }
38}
39
40/// Identifies a neutral design configuration.
41#[derive(
42    Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
43)]
44#[serde(transparent)]
45pub struct ConfigurationId(pub String);
46
47#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
48#[serde(untagged)]
49/// Resolution state of one configuration's complete body membership.
50pub enum ConfigurationBodies {
51    /// Complete ordered body membership.
52    Resolved(Vec<BodyId>),
53    /// Source configuration exists but its body membership is not established.
54    #[default]
55    Unresolved,
56}
57
58impl ConfigurationBodies {
59    /// Return complete body membership when resolved.
60    pub fn resolved(&self) -> Option<&[BodyId]> {
61        match self {
62            Self::Resolved(value) => Some(value),
63            Self::Unresolved => None,
64        }
65    }
66    /// Whether body membership remains unresolved.
67    pub fn is_unresolved(&self) -> bool {
68        matches!(self, Self::Unresolved)
69    }
70    /// Iterate over resolved membership; unresolved membership yields no values.
71    pub fn iter(&self) -> std::slice::Iter<'_, BodyId> {
72        self.resolved().unwrap_or_default().iter()
73    }
74    /// Number of resolved members; zero when unresolved.
75    pub fn len(&self) -> usize {
76        self.resolved().map_or(0, <[BodyId]>::len)
77    }
78    /// Whether resolved membership is empty; false when unresolved.
79    pub fn is_empty(&self) -> bool {
80        self.resolved().is_some_and(<[BodyId]>::is_empty)
81    }
82}
83
84impl PartialEq<Vec<BodyId>> for ConfigurationBodies {
85    fn eq(&self, other: &Vec<BodyId>) -> bool {
86        self.resolved() == Some(other.as_slice())
87    }
88}
89
90impl<'a> IntoIterator for &'a ConfigurationBodies {
91    type Item = &'a BodyId;
92    type IntoIter = std::slice::Iter<'a, BodyId>;
93    fn into_iter(self) -> Self::IntoIter {
94        self.iter()
95    }
96}
97
98/// A named parametric model variant.
99#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
100pub struct DesignConfiguration {
101    /// Globally unique configuration id.
102    pub id: ConfigurationId,
103    /// Position in the design configuration list.
104    #[serde(default)]
105    pub ordinal: u32,
106    /// Whether this configuration supplies the document's active model state.
107    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
108    pub active: bool,
109    /// Format-native configuration slot, when distinct from list order.
110    #[serde(default, skip_serializing_if = "Option::is_none")]
111    pub source_index: Option<u32>,
112    /// Source display name.
113    pub name: String,
114    /// Material override, when present.
115    #[serde(default, skip_serializing_if = "Option::is_none")]
116    pub material: Option<String>,
117    /// Configuration-local named values not otherwise represented.
118    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
119    pub properties: BTreeMap<String, String>,
120    /// Configuration-specific source expressions keyed by the overridden parameter.
121    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
122    pub parameter_overrides: BTreeMap<ParameterId, String>,
123    /// Features suppressed when this configuration is active.
124    #[serde(default, skip_serializing_if = "Vec::is_empty")]
125    pub suppressed_features: Vec<FeatureId>,
126    /// Bodies present when this configuration is active.
127    #[serde(default, skip_serializing_if = "ConfigurationBodies::is_unresolved")]
128    pub bodies: ConfigurationBodies,
129    /// Evaluated parameter state when this configuration is active.
130    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
131    pub parameter_values: BTreeMap<ParameterId, ParameterValue>,
132    /// Evaluated feature operation state when this configuration is active.
133    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
134    pub feature_states: BTreeMap<FeatureId, ConfigurationFeatureState>,
135    /// Identifier of the full-fidelity record in a native namespace.
136    #[serde(default, skip_serializing_if = "Option::is_none")]
137    pub native_ref: Option<String>,
138}
139
140/// Configuration-local evaluation state for one construction feature.
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
142pub struct ConfigurationFeatureState {
143    /// Whether evaluation of this feature is disabled in the configuration.
144    #[serde(default)]
145    pub suppressed: bool,
146    /// Earlier features consumed during regeneration in source operand order.
147    #[serde(default, skip_serializing_if = "Vec::is_empty")]
148    pub dependencies: Vec<FeatureId>,
149    /// Bodies produced or modified in the configuration.
150    #[serde(default, skip_serializing_if = "Vec::is_empty")]
151    pub outputs: Vec<BodyId>,
152    /// Evaluated construction semantics in the configuration.
153    pub definition: FeatureDefinition,
154}
155
156/// Identifies a neutral design parameter.
157#[derive(
158    Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
159)]
160#[serde(transparent)]
161pub struct ParameterId(pub String);
162
163/// A named design expression, optionally owned by a construction feature.
164#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
165pub struct DesignParameter {
166    /// Globally unique parameter id.
167    pub id: ParameterId,
168    /// Feature that consumes this parameter; absent for a document parameter.
169    #[serde(default, skip_serializing_if = "Option::is_none")]
170    pub owner: Option<FeatureId>,
171    /// Position among parameters in the same ownership scope.
172    #[serde(default)]
173    pub ordinal: u32,
174    /// Source parameter name.
175    pub name: String,
176    /// Literal or expression text used by the source system.
177    pub expression: String,
178    /// Geometric display semantics carried by the dimension expression.
179    #[serde(default, skip_serializing_if = "Option::is_none")]
180    pub display: Option<DimensionDisplay>,
181    /// Evaluated scalar when available.
182    #[serde(default, skip_serializing_if = "Option::is_none")]
183    pub value: Option<ParameterValue>,
184    /// Parameters referenced by `expression`, in source expression order.
185    #[serde(default, skip_serializing_if = "Vec::is_empty")]
186    pub dependencies: Vec<ParameterId>,
187    /// Source dimension properties not represented by another field.
188    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
189    pub properties: BTreeMap<String, String>,
190    /// Product-manufacturing dimension semantics, when present.
191    #[serde(default, skip_serializing_if = "Option::is_none")]
192    pub pmi: Option<ParameterPmi>,
193    /// Identifier of the full-fidelity source parameter record.
194    #[serde(default, skip_serializing_if = "Option::is_none")]
195    pub native_ref: Option<String>,
196}
197
198/// Product-manufacturing semantics attached to a design parameter.
199#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
200pub struct ParameterPmi {
201    /// Semantic dimension family.
202    pub subtype: PmiDimensionSubtype,
203    /// Display precision carried by the semantic annotation.
204    pub precision: i64,
205    /// Native formatted dimension text.
206    #[serde(default, skip_serializing_if = "Option::is_none")]
207    pub display_text: Option<String>,
208    /// Basic-dimension flag.
209    pub basic: bool,
210    /// Inspection-dimension flag.
211    pub inspection: bool,
212    /// Reference-only flag.
213    pub reference_only: bool,
214    /// Identifier of the full-fidelity semantic record.
215    pub native_ref: String,
216}
217
218/// Semantic PMI dimension family.
219#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
220#[serde(tag = "kind", content = "native_kind", rename_all = "snake_case")]
221pub enum PmiDimensionSubtype {
222    /// Linear distance.
223    Linear,
224    /// Angular extent in radians.
225    Angle,
226    /// Diameter.
227    Diameter,
228    /// Radius.
229    Radial,
230    /// Source-native family without a neutral equivalent.
231    Native(String),
232}
233
234/// Geometric interpretation requested by a dimension display modifier.
235#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
236#[serde(rename_all = "snake_case")]
237pub enum DimensionDisplay {
238    /// Displays the dimension as a diameter.
239    Diameter,
240    /// Displays the dimension as a radius.
241    Radius,
242}
243
244/// Canonical scalar value of a literal design parameter.
245#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
246#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
247pub enum ParameterValue {
248    /// Length in canonical millimeters.
249    Length(Length),
250    /// Angle in canonical radians.
251    Angle(Angle),
252    /// Dimensionless real scalar.
253    Real(f64),
254    /// Integer scalar.
255    Integer(i64),
256    /// Boolean scalar.
257    Boolean(bool),
258    /// Literal text value.
259    String(String),
260}
261
262/// A length in canonical millimeters.
263#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
264#[serde(transparent)]
265pub struct Length(pub f64);
266
267/// An angle in canonical radians.
268#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
269#[serde(transparent)]
270pub struct Angle(pub f64);
271
272/// An ordered neutral construction feature and its resulting bodies.
273#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
274pub struct Feature {
275    /// Globally unique feature id.
276    pub id: FeatureId,
277    /// Stable construction order within the source history.
278    pub ordinal: u64,
279    /// Source display name.
280    #[serde(default, skip_serializing_if = "Option::is_none")]
281    pub name: Option<String>,
282    /// Whether evaluation of this feature is disabled.
283    #[serde(default)]
284    pub suppressed: Option<bool>,
285    /// Containing or logically preceding feature, when represented by the source.
286    #[serde(default, skip_serializing_if = "Option::is_none")]
287    pub parent: Option<FeatureId>,
288    /// Earlier features consumed during regeneration, in source operand order.
289    #[serde(default, skip_serializing_if = "Vec::is_empty")]
290    pub dependencies: Vec<FeatureId>,
291    /// Source operation attributes not consumed by the neutral definition.
292    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
293    pub source_properties: BTreeMap<String, String>,
294    /// Source XML element name for the operation record.
295    #[serde(default, skip_serializing_if = "Option::is_none")]
296    pub source_tag: Option<String>,
297    /// Text payload of a source leaf operation.
298    #[serde(default, skip_serializing_if = "Option::is_none")]
299    pub source_text: Option<String>,
300    /// Ordered source text, parameter, and child-feature content.
301    #[serde(default, skip_serializing_if = "Vec::is_empty")]
302    pub source_content: Vec<FeatureSourceContent>,
303    /// Bodies produced or modified by the feature.
304    #[serde(default, skip_serializing_if = "Vec::is_empty")]
305    pub outputs: Vec<BodyId>,
306    /// Neutral construction semantics.
307    pub definition: FeatureDefinition,
308    /// Identifier of the full-fidelity record in a native namespace.
309    #[serde(default, skip_serializing_if = "Option::is_none")]
310    pub native_ref: Option<String>,
311}
312
313/// Typed topology membership at one feature's evaluation input.
314#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
315pub struct FeatureInputTopology {
316    /// Globally unique state id.
317    pub id: FeatureInputTopologyId,
318    /// Feature evaluated from this state.
319    pub input_of: FeatureId,
320    /// Bodies present in this state.
321    #[serde(default, skip_serializing_if = "Vec::is_empty")]
322    pub bodies: Vec<HistoricalBodyId>,
323    /// Faces present in this state.
324    #[serde(default, skip_serializing_if = "Vec::is_empty")]
325    pub faces: Vec<HistoricalFaceId>,
326    /// Edges present in this state.
327    #[serde(default, skip_serializing_if = "Vec::is_empty")]
328    pub edges: Vec<HistoricalEdgeId>,
329    /// Full-fidelity source state reference.
330    #[serde(default, skip_serializing_if = "Option::is_none")]
331    pub native_ref: Option<String>,
332}
333
334/// One item in a source feature's mixed-content sequence.
335#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
336#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
337pub enum FeatureSourceContent {
338    /// Literal text between child records.
339    Text(String),
340    /// Dimension or equation parameter at this position.
341    Parameter(ParameterId),
342    /// Nested feature record at this position.
343    Feature(FeatureId),
344}
345
346/// Neutral construction semantics, with an explicit native escape hatch.
347#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
348#[serde(tag = "definition", rename_all = "snake_case")]
349pub enum FeatureDefinition {
350    /// Non-modeling node retained in the ordered feature tree.
351    TreeNode {
352        /// Structural or presentation role of the node.
353        role: FeatureTreeNodeRole,
354        /// Ordered features owned by this node.
355        #[serde(default, skip_serializing_if = "Vec::is_empty")]
356        children: Vec<FeatureId>,
357        /// Active child, when the source design identifies one.
358        #[serde(default, skip_serializing_if = "Option::is_none")]
359        active_child: Option<FeatureId>,
360    },
361    /// Direct-modeling session represented by its captured result bodies.
362    BaseFeature {
363        /// Bodies copied into the parametric timeline when the session closed.
364        bodies: BodySelection,
365    },
366    /// Independent bodies introduced by a copy-and-paste operation.
367    InsertBodies {
368        /// Newly created body copies in source order.
369        bodies: BodySelection,
370    },
371    /// Freeform modeling session represented by its final subdivision cages.
372    Form {
373        /// Ordered control cages committed by the session.
374        cages: Vec<SubdId>,
375    },
376    /// Non-geometric thread annotation attached to a cylindrical face.
377    CosmeticThread {
378        /// Cylindrical face carrying the annotation.
379        face: FaceSelection,
380        /// Nominal thread diameter, when resolved.
381        #[serde(default, skip_serializing_if = "Option::is_none")]
382        diameter: Option<Length>,
383        /// Axial extent of the annotation, when resolved.
384        #[serde(default, skip_serializing_if = "Option::is_none")]
385        extent: Option<CosmeticThreadExtent>,
386    },
387    /// Built-in world-origin reference plane.
388    DatumPrincipalPlane {
389        /// Canonical principal-plane role.
390        plane: PrincipalPlane,
391    },
392    /// Constructed reference plane.
393    DatumPlane {
394        /// Plane origin in model space.
395        origin: Point3,
396        /// Plane normal.
397        normal: Vector3,
398        /// In-plane u-axis.
399        u_axis: Vector3,
400    },
401    /// Constructed reference-plane family whose model-space frame is unresolved.
402    DatumPlaneUnresolved,
403    /// Reference plane offset from another datum plane.
404    DatumOffsetPlane {
405        /// Source plane, when its feature reference is available.
406        #[serde(default, skip_serializing_if = "Option::is_none")]
407        reference: Option<FeatureId>,
408        /// Signed normal offset from the source plane.
409        distance: Length,
410    },
411    /// Constructed reference axis.
412    DatumAxis {
413        /// Point on the axis in model space.
414        origin: Point3,
415        /// Axis direction.
416        direction: Vector3,
417    },
418    /// Constructed reference point.
419    DatumPoint {
420        /// Point position in model space.
421        position: Point3,
422    },
423    /// Datum point whose model-space position is unresolved.
424    DatumPointUnresolved,
425    /// Standalone model vertex constructed at one point.
426    PointGeometry {
427        /// Vertex position in the feature's local construction frame.
428        position: Point3,
429    },
430    /// Straight edge between two finite points.
431    LineSegment {
432        /// Start point.
433        start: Point3,
434        /// End point.
435        end: Point3,
436    },
437    /// Circular edge over an angular interval.
438    CircularArc {
439        /// Circle center in the feature's local construction frame.
440        center: Point3,
441        /// Circle-plane normal.
442        normal: Vector3,
443        /// Circle radius.
444        radius: Length,
445        /// Start parameter angle.
446        start_angle: Angle,
447        /// End parameter angle.
448        end_angle: Angle,
449    },
450    /// Elliptic edge over an angular interval.
451    EllipticArc {
452        /// Ellipse center in the feature's local construction frame.
453        center: Point3,
454        /// Circle-plane normal.
455        normal: Vector3,
456        /// Major-axis direction in the ellipse plane.
457        major_axis: Vector3,
458        /// Major semiaxis radius.
459        major_radius: Length,
460        /// Minor semiaxis radius.
461        minor_radius: Length,
462        /// Start parameter angle.
463        start_angle: Angle,
464        /// End parameter angle.
465        end_angle: Angle,
466    },
467    /// Ordered straight-edge chain.
468    Polyline {
469        /// Ordered vertices in the feature's local construction frame.
470        points: Vec<Point3>,
471        /// Whether the last point connects back to the first.
472        closed: bool,
473    },
474    /// Regular planar polygon centered at the local origin.
475    RegularPolygonCurve {
476        /// Number of polygon sides.
477        sides: u32,
478        /// Center-to-vertex distance.
479        circumradius: Length,
480    },
481    /// Rectangular bounded planar face in the local XY plane.
482    PlanarPatch {
483        /// Length along the local x-axis.
484        length: Length,
485        /// Width along the local y-axis.
486        width: Length,
487    },
488    /// Faces built from an ordered set of source shapes.
489    FaceFromShapes {
490        /// Complete ordered source-shape selection.
491        sources: BodySelection,
492        /// Extensible native face-building algorithm identifier.
493        face_maker_class: String,
494    },
495    /// Constructed model-space coordinate system.
496    DatumCoordinateSystem {
497        /// Frame origin.
498        origin: Point3,
499        /// Unit x-axis.
500        x_axis: Vector3,
501        /// Unit y-axis.
502        y_axis: Vector3,
503        /// Unit z-axis.
504        z_axis: Vector3,
505    },
506    /// Coordinate system whose model-space frame is unresolved.
507    DatumCoordinateSystemUnresolved,
508    /// Rectangular solid primitive.
509    Block {
510        /// Ordered local x, y, and z dimensions, when resolved.
511        dimensions: Option<[Length; 3]>,
512        /// Local-to-model placement, when resolved.
513        placement: Option<crate::transform::Transform>,
514    },
515    /// Parametric model-space curve defined by coordinate expressions.
516    EquationCurve {
517        /// Independent parameter symbol used by the coordinate expressions.
518        parameter: String,
519        /// Model-space x-coordinate expression.
520        x_expression: String,
521        /// Model-space y-coordinate expression.
522        y_expression: String,
523        /// Model-space z-coordinate expression.
524        z_expression: String,
525        /// Inclusive lower parameter bound.
526        start: f64,
527        /// Inclusive upper parameter bound.
528        end: f64,
529    },
530    /// Curve produced by projecting a source path onto target faces.
531    ProjectedCurve {
532        /// Sketch or model-space path being projected.
533        source: PathRef,
534        /// Faces receiving the projected curve.
535        target_faces: FaceSelection,
536        /// Direction law used by the projection.
537        #[serde(default)]
538        direction: CurveProjectionDirection,
539        /// Whether projection proceeds in both directions, when resolved.
540        #[serde(default, skip_serializing_if = "Option::is_none")]
541        bidirectional: Option<bool>,
542    },
543    /// Shapes projected along a direction onto one support surface.
544    ProjectOnSurface {
545        /// Ordered shapes and subelements projected onto the support.
546        sources: PathRef,
547        /// Single support face receiving the projection.
548        support_face: FaceSelection,
549        /// Unit projection direction.
550        direction: Vector3,
551        /// Result topology retained from the projected shapes.
552        mode: SurfaceProjectionMode,
553        /// Normal extrusion height used to turn projected faces into solids.
554        height: Length,
555        /// Normal offset applied to the projected result.
556        offset: Length,
557    },
558    /// Ordered chain of source paths exposed as one construction curve.
559    CompositeCurve {
560        /// Source segments in traversal order.
561        segments: Vec<PathRef>,
562        /// Whether the final segment joins the first.
563        #[serde(default)]
564        closed: bool,
565    },
566    /// Circular helix or planar spiral constructed around an axis.
567    Helix {
568        /// Point on the construction axis at the curve start.
569        axis_origin: Point3,
570        /// Construction-axis direction.
571        axis_direction: Vector3,
572        /// Initial radial distance from the axis.
573        radius: Length,
574        /// Signed axial rise per revolution; zero produces a planar spiral.
575        pitch: Length,
576        /// Positive number of revolutions.
577        revolutions: f64,
578        /// Angular position at the curve start.
579        #[serde(default)]
580        start_angle: Angle,
581        /// Whether angular travel is clockwise when viewed along the axis.
582        clockwise: bool,
583        /// Radial growth per revolution for a planar spiral, when non-cylindrical.
584        #[serde(default, skip_serializing_if = "Option::is_none")]
585        radial_growth: Option<Length>,
586        /// Cone half-angle for a conical helix, when non-cylindrical.
587        #[serde(default, skip_serializing_if = "Option::is_none")]
588        cone_angle: Option<Angle>,
589        /// Number of turns per generated curve subdivision, when requested.
590        #[serde(default, skip_serializing_if = "Option::is_none")]
591        segment_turns: Option<f64>,
592        /// Persisted construction algorithm generation, when selectable.
593        #[serde(default, skip_serializing_if = "Option::is_none")]
594        construction_style: Option<HelixConstructionStyle>,
595    },
596    /// Circular helix with retained native axis placement.
597    HelixNativeAxis {
598        /// Source-native record carrying the unresolved construction axis.
599        axis_native_ref: String,
600        /// Signed total rise along the axis.
601        #[serde(alias = "radius")]
602        axial_rise: Length,
603        /// Signed axial rise per revolution.
604        #[serde(alias = "height")]
605        pitch: Length,
606        /// Positive number of revolutions.
607        revolutions: f64,
608        /// Angular position at the curve start.
609        start_angle: Angle,
610        /// Whether angular travel is clockwise when viewed along the axis.
611        clockwise: bool,
612    },
613    /// Solid primitive formed by sweeping a generated section along a helix or spiral.
614    Coil {
615        /// Complete geometric and parametric construction definition.
616        construction: CoilConstruction,
617        /// Result-body semantics.
618        result: CoilResult,
619    },
620    /// Solid sphere primitive.
621    Sphere {
622        /// Sphere center in model space.
623        center: Point3,
624        /// Positive sphere radius.
625        radius: Length,
626        /// Boolean combination with existing bodies.
627        op: BooleanOp,
628    },
629    /// Solid torus primitive.
630    Torus {
631        /// Torus center in model space.
632        center: Point3,
633        /// Unit normal of the torus center plane.
634        axis: Vector3,
635        /// Positive distance from the center to the tube centerline.
636        major_radius: Length,
637        /// Positive tube radius.
638        minor_radius: Length,
639        /// Boolean combination with existing bodies.
640        op: BooleanOp,
641    },
642    /// Profile mapped onto a target face.
643    Wrap {
644        /// Sketch or face profile mapped onto the target.
645        profile: ProfileRef,
646        /// Face receiving the mapped profile.
647        face: FaceSelection,
648        /// Material or imprint operation performed by the mapping.
649        mode: WrapMode,
650        /// Normal offset for emboss and deboss operations.
651        #[serde(default, skip_serializing_if = "Option::is_none")]
652        depth: Option<Length>,
653    },
654    /// Solved sketch node in the construction history.
655    Sketch {
656        /// Coordinate space containing the sketch geometry.
657        #[serde(default)]
658        space: SketchSpace,
659        /// Neutral planar sketch geometry owned by this history node, when resolved.
660        /// Neutral sketch geometry owned by this history node, when resolved.
661        #[serde(default, skip_serializing_if = "Option::is_none")]
662        sketch: Option<crate::sketches::SketchId>,
663    },
664    /// Solved spatial-sketch node in the construction history.
665    SpatialSketch {
666        /// Neutral model-space sketch geometry owned by this history node, when resolved.
667        #[serde(default, skip_serializing_if = "Option::is_none")]
668        sketch: Option<crate::sketches::SpatialSketchId>,
669    },
670    /// Reusable planar sketch geometry.
671    SketchBlockDefinition {
672        /// Neutral sketch geometry owned by the block, when resolved.
673        #[serde(default, skip_serializing_if = "Option::is_none")]
674        sketch: Option<crate::sketches::SketchId>,
675    },
676    /// Placement of one reusable sketch-block definition.
677    SketchBlockInstance {
678        /// Referenced block definition, when resolved.
679        #[serde(default, skip_serializing_if = "Option::is_none")]
680        block: Option<FeatureId>,
681        /// Affine placement in the owning sketch space, when resolved.
682        #[serde(default, skip_serializing_if = "Option::is_none")]
683        placement: Option<crate::transform::Transform>,
684    },
685    /// Directly stored geometry with no replayable parametric construction.
686    ///
687    /// The feature's `outputs` identify the retained bodies when geometry is present.
688    StoredGeometry,
689    /// Body geometry copied from existing bodies.
690    ExtractBody {
691        /// Bodies supplying the copied geometry.
692        source: BodySelection,
693    },
694    /// Geometry copied from an earlier feature without an additional modeling operation.
695    DerivedGeometry {
696        /// Feature supplying the copied geometry.
697        source: FeatureId,
698    },
699    /// Geometry imported from an external model file.
700    ImportedGeometry {
701        /// External source path exactly as persisted by the design.
702        path: String,
703        /// Model format read from the external file.
704        format: GeometryImportFormat,
705    },
706    /// Parametric analytic solid primitive.
707    Primitive {
708        /// Primitive dimensions and angular bounds.
709        solid: PrimitiveSolid,
710        /// Boolean combination with an existing `PartDesign` body.
711        op: BooleanOp,
712    },
713    /// Linear extrusion of a profile.
714    Extrude {
715        /// Profile swept along `direction`.
716        profile: ProfileRef,
717        /// Direction in which the profile is swept.
718        #[serde(default, skip_serializing_if = "ExtrudeDirection::is_profile_normal")]
719        direction: ExtrudeDirection,
720        /// Plane or face from which the extrusion begins.
721        #[serde(default)]
722        start: ExtrudeStart,
723        /// How far the extrusion travels on each of its sides.
724        extent: ExtrudeExtent,
725        /// Boolean combination with existing bodies.
726        op: BooleanOp,
727        /// Persisted source used to resolve the extrusion direction.
728        #[serde(default, skip_serializing_if = "Option::is_none")]
729        direction_source: Option<ExtrusionDirectionSource>,
730        /// Whether the result is a solid (`true`) or sheet (`false`), when selectable.
731        #[serde(default, skip_serializing_if = "Option::is_none")]
732        solid: Option<bool>,
733        /// Native face-building policy used to turn closed wires into faces.
734        #[serde(default, skip_serializing_if = "Option::is_none")]
735        face_maker: Option<ExtrusionFaceMaker>,
736        /// Taper orientation used for inner wires, when selectable.
737        #[serde(default, skip_serializing_if = "Option::is_none")]
738        inner_wire_taper: Option<InnerWireTaper>,
739        /// Whether stored lengths are measured along the profile normal instead of the sweep axis.
740        #[serde(default, skip_serializing_if = "Option::is_none")]
741        length_along_profile_normal: Option<bool>,
742        /// Whether a profile containing multiple faces is accepted as one operation.
743        #[serde(default, skip_serializing_if = "Option::is_none")]
744        allow_multi_profile_faces: Option<bool>,
745    },
746    /// Revolution of a profile around an axis.
747    Revolve {
748        /// Independently resolved construction inputs.
749        construction: RevolutionConstruction,
750        /// Boolean combination with existing bodies.
751        op: BooleanOp,
752    },
753    /// Sweep of a profile along a path.
754    Sweep {
755        /// Cross-section swept along the path, when resolved.
756        #[serde(default, skip_serializing_if = "Option::is_none")]
757        profile: Option<ProfileRef>,
758        /// Additional cross-sections after the primary profile, in path order.
759        #[serde(default, skip_serializing_if = "Vec::is_empty")]
760        sections: Vec<ProfileRef>,
761        /// Trajectory followed by the profile, when resolved.
762        #[serde(default, skip_serializing_if = "Option::is_none")]
763        path: Option<PathRef>,
764        /// Result family and solid Boolean operation.
765        mode: SweepMode,
766        /// Rule used to orient cross-sections along the path.
767        #[serde(default, skip_serializing_if = "Option::is_none")]
768        orientation: Option<SweepOrientation>,
769        /// Corner continuation used where path segments meet.
770        #[serde(default, skip_serializing_if = "Option::is_none")]
771        transition: Option<SweepTransition>,
772        /// Interpolation law used between multiple cross-sections.
773        #[serde(default, skip_serializing_if = "Option::is_none")]
774        transformation: Option<SweepTransformation>,
775        /// Whether tangent-connected edges are included in the primary path.
776        #[serde(default)]
777        path_tangent: bool,
778        /// Whether linear edges and planar faces are simplified after construction.
779        #[serde(default)]
780        linearize: bool,
781        /// Total profile twist along the path, when specified.
782        #[serde(default, skip_serializing_if = "Option::is_none")]
783        twist: Option<Angle>,
784        /// End-to-start profile scale ratio, when specified.
785        #[serde(default, skip_serializing_if = "Option::is_none")]
786        scale: Option<f64>,
787        /// Whether a profile containing multiple faces is accepted as one operation.
788        #[serde(default, skip_serializing_if = "Option::is_none")]
789        allow_multi_profile_faces: Option<bool>,
790    },
791    /// Solid sweep of a profile along a parametrically defined helix or spiral.
792    HelicalSweep {
793        /// Complete helix path and profile construction.
794        construction: HelicalSweepConstruction,
795        /// Boolean combination with the existing body.
796        op: BooleanOp,
797    },
798    /// Live or frozen reference geometry imported from other design features.
799    Binder {
800        /// Ordered source objects and selected subelements.
801        sources: Vec<BinderSource>,
802        /// Binding and derived-shape construction semantics.
803        construction: BinderConstruction,
804    },
805    /// Loft-family skin whose section semantics remain unresolved.
806    LoftUnresolved,
807    /// Freeform surface whose control geometry remains unresolved.
808    FreeformSurfaceUnresolved,
809    /// Loft through an ordered sequence of profile or point sections.
810    Loft {
811        /// Ordered cross-sections from the loft start to end.
812        #[serde(alias = "profiles")]
813        sections: Vec<LoftSection>,
814        /// Optional ordered guide trajectories.
815        #[serde(default, skip_serializing_if = "Vec::is_empty")]
816        guides: Vec<PathRef>,
817        /// Optional centerline to which the loft sections remain normal.
818        #[serde(default, skip_serializing_if = "Option::is_none")]
819        centerline: Option<PathRef>,
820        /// Boolean combination with existing bodies.
821        op: BooleanOp,
822        /// Whether the loft closes from the last section to the first.
823        #[serde(default)]
824        closed: bool,
825        /// Whether the sections bound a solid instead of a sheet body.
826        #[serde(default = "default_true")]
827        solid: bool,
828        /// Whether adjacent sections are connected by straight ruled spans.
829        #[serde(default)]
830        ruled: bool,
831        /// Maximum polynomial degree used to interpolate the sections, when constrained.
832        #[serde(default, skip_serializing_if = "Option::is_none")]
833        max_degree: Option<u32>,
834        /// Whether section topology is checked and adjusted for compatibility, when carried.
835        #[serde(default, skip_serializing_if = "Option::is_none")]
836        check_compatibility: Option<bool>,
837        /// Whether profiles containing multiple faces are accepted as one operation.
838        #[serde(default, skip_serializing_if = "Option::is_none")]
839        allow_multi_profile_faces: Option<bool>,
840    },
841    /// Thin rib grown from a profile.
842    Rib {
843        /// Independently resolved construction inputs.
844        construction: RibConstruction,
845        /// Boolean combination with existing bodies.
846        op: BooleanOp,
847    },
848    /// Planar sheet-metal body created from a closed profile.
849    SheetMetalBaseFlange {
850        /// Closed profile defining the planar sheet boundary.
851        profile: ProfileRef,
852        /// Finished sheet thickness.
853        thickness: Length,
854        /// Distribution of thickness relative to the profile plane.
855        side: SheetMetalThicknessSide,
856    },
857    /// Edge fillet.
858    Fillet {
859        /// Ordered edge groups and their radius laws.
860        groups: Vec<FilletGroup>,
861    },
862    /// Blend constructed between two face sets.
863    FaceBlend {
864        /// First support-face set.
865        first_faces: FaceSelection,
866        /// Second support-face set.
867        second_faces: FaceSelection,
868        /// Radius law along the face intersection.
869        radius: RadiusSpec,
870    },
871    /// Edge chamfer.
872    Chamfer {
873        /// Ordered edge groups and their dimensional specifications.
874        groups: Vec<ChamferGroup>,
875        /// Whether the dimensional reference side is reversed.
876        #[serde(default)]
877        flip_direction: bool,
878    },
879    /// Thin-wall shell operation.
880    Shell {
881        /// Faces removed to open the shell.
882        removed_faces: FaceSelection,
883        /// Wall thickness left after shelling, when resolved.
884        #[serde(default, skip_serializing_if = "Option::is_none")]
885        thickness: Option<Length>,
886        /// Whether the wall is grown outward from the original boundary,
887        /// as opposed to inward, when resolved.
888        #[serde(default, skip_serializing_if = "Option::is_none")]
889        outward: Option<bool>,
890        /// Offset construction used to generate the wall.
891        #[serde(default, skip_serializing_if = "Option::is_none")]
892        mode: Option<ShellMode>,
893        /// Corner continuation law used between offset faces.
894        #[serde(default, skip_serializing_if = "Option::is_none")]
895        join: Option<ShellJoin>,
896        /// Whether intersecting offset regions are resolved during construction.
897        #[serde(default, skip_serializing_if = "Option::is_none")]
898        resolve_intersections: Option<bool>,
899        /// Whether self-intersecting offset regions may be retained.
900        #[serde(default, skip_serializing_if = "Option::is_none")]
901        allow_self_intersections: Option<bool>,
902    },
903    /// Offsets an entire source shape without removing opening faces.
904    OffsetShape {
905        /// Source shape or body to offset.
906        source: BodySelection,
907        /// Signed normal offset in canonical millimeters.
908        distance: Length,
909        /// Offset construction mode.
910        mode: ShellMode,
911        /// Corner continuation law.
912        join: ShellJoin,
913        /// Whether intersecting regions are resolved.
914        resolve_intersections: bool,
915        /// Whether self-intersecting regions may be retained.
916        allow_self_intersections: bool,
917        /// Whether open offset boundaries are filled.
918        fill: bool,
919        /// Whether planar two-dimensional offset rules are used.
920        planar: bool,
921    },
922    /// Builds one compound topology node from ordered source shapes.
923    Compound {
924        /// Ordered source members retained as a native or resolved selection.
925        members: BodySelection,
926    },
927    /// Removes redundant splitter topology from a source shape.
928    RefineShape {
929        /// Source shape whose coincident boundaries are simplified.
930        source: BodySelection,
931    },
932    /// Reverses the topological orientation of a source shape.
933    ReverseShape {
934        /// Source shape whose complete orientation is reversed.
935        source: BodySelection,
936    },
937    /// Ruled sheet connecting two ordered boundary curves.
938    RuledBetweenCurves {
939        /// First source boundary.
940        first: PathRef,
941        /// Second source boundary.
942        second: PathRef,
943        /// Traversal relationship between the two boundaries.
944        orientation: RuledCurveOrientation,
945    },
946    /// Intersection curves produced where two source shapes meet.
947    SectionShape {
948        /// First intersected source shape.
949        first: BodySelection,
950        /// Second intersected source shape.
951        second: BodySelection,
952        /// Whether the resulting section edges are approximated.
953        approximate: bool,
954    },
955    /// Reflects one source shape across a model-space plane.
956    MirrorShape {
957        /// Shape transformed into the mirrored result.
958        source: BodySelection,
959        /// Point on the persisted resolved mirror plane.
960        plane_origin: Point3,
961        /// Unit normal of the persisted resolved mirror plane.
962        plane_normal: Vector3,
963        /// Native plane, face, or circle reference that supplied the resolved plane.
964        #[serde(default, skip_serializing_if = "Option::is_none")]
965        plane_reference: Option<FaceSelection>,
966    },
967    /// Adds material normal to selected faces.
968    Thicken {
969        /// Faces offset by the operation.
970        faces: FaceSelection,
971        /// Finished added thickness, when resolved.
972        #[serde(default, skip_serializing_if = "Option::is_none")]
973        thickness: Option<Length>,
974        /// Distribution of thickness relative to the selected faces, when resolved.
975        #[serde(default, skip_serializing_if = "Option::is_none")]
976        side: Option<ThickenSide>,
977    },
978    /// Surface copied at a signed normal offset from selected support faces.
979    OffsetSurface {
980        /// Faces supplying the source surface geometry.
981        faces: FaceSelection,
982        /// Signed normal offset in canonical millimeters.
983        distance: Option<Length>,
984    },
985    /// Joins selected surface bodies along coincident or near-coincident boundaries.
986    KnitSurface {
987        /// Faces participating in the knit operation.
988        faces: FaceSelection,
989        /// Whether coincident face and edge entities are merged.
990        merge_entities: Option<bool>,
991        /// Whether a closed result is converted to a solid body.
992        create_solid: Option<bool>,
993        /// Maximum boundary gap accepted by the operation.
994        #[serde(default, skip_serializing_if = "Option::is_none")]
995        gap_tolerance: Option<Length>,
996    },
997    /// Joins sheet or solid bodies along coincident boundaries.
998    SewBodies {
999        /// Bodies participating in the sew operation.
1000        bodies: BodySelection,
1001        /// Maximum accepted boundary gap, when resolved.
1002        gap_tolerance: Option<Length>,
1003    },
1004    /// Surface patch spanning a selected edge boundary.
1005    FilledSurface {
1006        /// Closed boundary of the generated patch.
1007        boundary: SurfaceBoundary,
1008        /// Adjacent faces supplying tangent or curvature conditions.
1009        support_faces: FaceSelection,
1010        /// Continuity imposed against the support faces, when resolved.
1011        #[serde(default, skip_serializing_if = "Option::is_none")]
1012        continuity: Option<SurfaceContinuity>,
1013        /// Whether the generated patch is merged into adjacent surface bodies,
1014        /// when resolved.
1015        #[serde(default, skip_serializing_if = "Option::is_none")]
1016        merge_result: Option<bool>,
1017    },
1018    /// Boundary-surface operation whose curve networks remain unresolved.
1019    BoundarySurfaceUnresolved,
1020    /// Restricts selected surface faces to one side of a trimming path.
1021    TrimSurface {
1022        /// Surface faces modified by the operation.
1023        faces: FaceSelection,
1024        /// Sketch or model-space path defining the trim boundary.
1025        tool: PathRef,
1026        /// Region retained after trimming.
1027        keep: TrimRegion,
1028    },
1029    /// Extends selected surface boundaries by a fixed distance.
1030    ExtendSurface {
1031        /// Surface faces whose boundaries are extended.
1032        faces: FaceSelection,
1033        /// Positive extension distance in canonical millimeters.
1034        distance: Option<Length>,
1035        /// Geometric continuation law.
1036        method: SurfaceExtension,
1037    },
1038    /// Ruled surface grown from selected boundary edges.
1039    RuledSurface {
1040        /// Boundary edges from which the surface is generated.
1041        edges: EdgeSelection,
1042        /// Adjacent faces supplying normal or tangent context.
1043        support_faces: FaceSelection,
1044        /// Direction law and extension distance.
1045        mode: RuledSurfaceMode,
1046    },
1047    /// Taper applied to selected faces about a neutral plane.
1048    Draft {
1049        /// Faces whose angle is modified.
1050        faces: FaceSelection,
1051        /// Neutral plane that remains fixed during the operation.
1052        neutral_plane: FaceSelection,
1053        /// Pull direction used to measure the draft angle.
1054        pull_direction: Option<Vector3>,
1055        /// Signed draft angle.
1056        angle: Option<Angle>,
1057        /// Whether material is added away from the pull direction.
1058        outward: Option<bool>,
1059    },
1060    /// Draft family whose operands remain unresolved.
1061    DraftUnresolved,
1062    /// Boolean operation between existing bodies.
1063    Combine {
1064        /// Body modified by the operation.
1065        target: BodySelection,
1066        /// Bodies consumed as Boolean tools.
1067        tools: BodySelection,
1068        /// Join, cut, or intersection operation.
1069        op: BooleanOp,
1070    },
1071    /// Creates solid bodies from selected cells enclosed by boundary bodies.
1072    BoundaryFill {
1073        /// Bodies whose faces partition space into candidate cells.
1074        tools: BodySelection,
1075        /// Enclosed cells retained as result bodies, in source order.
1076        cells: Vec<BodySelection>,
1077    },
1078    /// Removes one side of selected bodies using selected surface faces.
1079    CutWithSurface {
1080        /// Bodies cut by the operation.
1081        targets: BodySelection,
1082        /// Oriented surface faces defining the cut.
1083        tools: FaceSelection,
1084        /// Whether the side opposite the default tool orientation is removed.
1085        reverse: bool,
1086    },
1087    /// Removes one side of target bodies using ordered tool bodies.
1088    TrimBodies {
1089        /// Bodies modified by the operation.
1090        targets: BodySelection,
1091        /// Bodies defining the trimming boundary.
1092        tools: BodySelection,
1093        /// Side retained by the trim.
1094        keep: BodyTrimSide,
1095    },
1096    /// Partitions selected bodies with selected surface faces while retaining
1097    /// every resulting side.
1098    SplitBody {
1099        /// Bodies partitioned by the operation.
1100        targets: BodySelection,
1101        /// Surface faces extended as necessary to partition the targets.
1102        tools: FaceSelection,
1103    },
1104    /// Deletes bodies directly or retains only the selected bodies.
1105    DeleteBody {
1106        /// Bodies selected by the operation.
1107        bodies: BodySelection,
1108        /// Whether selected bodies are deleted or retained.
1109        mode: BodyRetentionMode,
1110    },
1111    /// Removal of selected faces from an existing body.
1112    DeleteFace {
1113        /// Faces removed by the operation.
1114        faces: FaceSelection,
1115        /// Whether adjacent faces extend to heal the resulting boundary.
1116        heal: bool,
1117    },
1118    /// Replaces selected faces with another face set.
1119    ReplaceFace {
1120        /// Faces removed from the target body.
1121        targets: FaceSelection,
1122        /// Faces whose underlying geometry supplies the replacement.
1123        replacements: FaceSelection,
1124    },
1125    /// Direct motion of selected faces.
1126    MoveFace {
1127        /// Faces modified by the operation.
1128        faces: FaceSelection,
1129        /// Motion applied to the selected faces.
1130        motion: FaceMotion,
1131    },
1132    /// Rigid translation or rotation of selected bodies, optionally creating copies.
1133    MoveBody {
1134        /// Bodies transformed by the operation.
1135        bodies: BodySelection,
1136        /// Model-space translation vector in canonical millimeters.
1137        translation: Vector3,
1138        /// Axis-angle rotation applied with the translation.
1139        #[serde(default, skip_serializing_if = "Option::is_none")]
1140        rotation: Option<AxisAngle>,
1141        /// Number of transformed copies; zero moves the selected bodies.
1142        #[serde(default)]
1143        copies: u32,
1144    },
1145    /// Dome grown from selected planar faces.
1146    Dome {
1147        /// Faces that bound the dome base.
1148        faces: FaceSelection,
1149        /// Dome height measured normal to the base, when resolved.
1150        #[serde(default, skip_serializing_if = "Option::is_none")]
1151        height: Option<Length>,
1152        /// Whether the profile is elliptical rather than spherical, when resolved.
1153        #[serde(default, skip_serializing_if = "Option::is_none")]
1154        elliptical: Option<bool>,
1155        /// Whether growth opposes the selected-face normal, when resolved.
1156        #[serde(default, skip_serializing_if = "Option::is_none")]
1157        reverse: Option<bool>,
1158    },
1159    /// Deformation of existing geometry about a feature axis.
1160    Flex {
1161        /// Flex axis direction in model space, when resolved.
1162        #[serde(default, skip_serializing_if = "Option::is_none")]
1163        axis: Option<Vector3>,
1164        /// Applied deformation mode and magnitude.
1165        mode: FlexMode,
1166    },
1167    /// Scales selected bodies about a model-space point.
1168    Scale {
1169        /// Bodies transformed by the operation.
1170        bodies: BodySelection,
1171        /// Fixed locus of the scale transform.
1172        #[serde(default, skip_serializing_if = "Option::is_none")]
1173        center: Option<ScaleCenter>,
1174        /// Independently decoded uniform and axis scale factors.
1175        factors: ScaleFactors,
1176    },
1177    /// Drilled or machined hole.
1178    Hole {
1179        /// Sketch or profile supplying one or more hole locations.
1180        #[serde(default, skip_serializing_if = "Option::is_none")]
1181        profile: Option<ProfileRef>,
1182        /// Geometry families in the profile that generate hole locations.
1183        #[serde(default, skip_serializing_if = "Option::is_none")]
1184        profile_filter: Option<HoleProfileFilter>,
1185        /// Face the hole is placed on, when known.
1186        #[serde(default, skip_serializing_if = "Option::is_none")]
1187        face: Option<FaceSelection>,
1188        /// Shared hole entry position when the construction carries one location separately.
1189        #[serde(default, skip_serializing_if = "Option::is_none")]
1190        position: Option<Point3>,
1191        /// Shared drilling direction when carried independently of complete placements.
1192        #[serde(default, skip_serializing_if = "Option::is_none")]
1193        direction: Option<Vector3>,
1194        /// Complete one-or-many hole placements. Empty when placement is unresolved.
1195        #[serde(default, skip_serializing_if = "Vec::is_empty")]
1196        placements: Vec<HolePlacement>,
1197        /// Structural drilling, entry-treatment, and threading form.
1198        kind: HoleKind,
1199        /// Exit treatment at the far side, when distinct from the entry treatment.
1200        #[serde(default, skip_serializing_if = "Option::is_none")]
1201        exit_kind: Option<HoleKind>,
1202        /// Hole diameter, when resolved.
1203        #[serde(default, skip_serializing_if = "Option::is_none")]
1204        diameter: Option<Length>,
1205        /// How deep the hole extends, when resolved. Holes travel on one side
1206        /// only, so the termination law needs no sidedness wrapper.
1207        #[serde(default, skip_serializing_if = "Option::is_none")]
1208        extent: Option<Termination>,
1209        /// Shape and depth convention at the blind end of the hole.
1210        #[serde(default, skip_serializing_if = "Option::is_none")]
1211        bottom: Option<HoleBottom>,
1212        /// Included taper angle for a conical hole, when enabled.
1213        #[serde(default, skip_serializing_if = "Option::is_none")]
1214        taper_angle: Option<Angle>,
1215        /// Standard sizing and thread construction, when specified.
1216        #[serde(default, skip_serializing_if = "Option::is_none")]
1217        specification: Option<Box<HoleSpecification>>,
1218        /// Whether a profile containing multiple faces is accepted as one operation.
1219        #[serde(default, skip_serializing_if = "Option::is_none")]
1220        allow_multi_profile_faces: Option<bool>,
1221    },
1222    /// Repetition or reflection of existing features.
1223    Pattern {
1224        /// Geometry being repeated or reflected; empty when the source selection is unresolved.
1225        seeds: Vec<PatternSeed>,
1226        /// Spatial transform defining the repetition or reflection.
1227        pattern: PatternKind,
1228    },
1229    /// Operation followed by source-requested topology cleanup.
1230    PostProcess {
1231        /// Underlying construction whose result is post-processed.
1232        operation: Box<FeatureDefinition>,
1233        /// Whether redundant splitter boundaries are removed.
1234        refine: bool,
1235        /// Boolean-operation tolerance selection carried by the feature family.
1236        fuzzy_tolerance: FuzzyTolerance,
1237    },
1238    /// Source-native operation without neutral semantics.
1239    Native {
1240        /// Native feature-type tag (e.g. `"Extrude"`, `"Fillet"`).
1241        kind: String,
1242        /// Source parametric input values keyed by parameter name.
1243        #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
1244        parameters: BTreeMap<String, String>,
1245        /// Source operation attributes that are not dimensional parameters.
1246        #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
1247        properties: BTreeMap<String, String>,
1248    },
1249}
1250
1251/// Direction in which an extrusion sweeps its profile.
1252#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
1253#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
1254pub enum ExtrudeDirection {
1255    /// Sweep along the profile's positive normal.
1256    #[default]
1257    ProfileNormal,
1258    /// Sweep opposite the profile's positive normal.
1259    ReversedProfileNormal,
1260    /// Sweep along an explicit model-space vector.
1261    Explicit(Vector3),
1262}
1263
1264impl ExtrudeDirection {
1265    /// Whether this is the default positive profile-normal direction.
1266    pub fn is_profile_normal(&self) -> bool {
1267        matches!(self, Self::ProfileNormal)
1268    }
1269}
1270/// One complete spatial placement in a hole operation.
1271#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1272#[serde(tag = "kind", rename_all = "snake_case")]
1273pub enum HolePlacement {
1274    /// Position and directed drilling vector recorded by the feature definition.
1275    Directed {
1276        /// Hole entry position in model space.
1277        position: Point3,
1278        /// Directed drilling vector.
1279        direction: Vector3,
1280    },
1281    /// Unoriented geometric axis inferred from a generated cylindrical surface.
1282    Axis {
1283        /// Point on the cylinder axis in model space.
1284        origin: Point3,
1285        /// Unoriented cylinder-axis vector; its sign has no semantic meaning.
1286        axis: Vector3,
1287    },
1288}
1289
1290/// One geometric selection repeated or reflected by a pattern operation.
1291#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1292#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
1293pub enum PatternSeed {
1294    /// Complete result of a preceding construction-history feature.
1295    Feature(FeatureId),
1296    /// Selected faces, including faces in an intermediate regenerated result.
1297    Faces(FaceSelection),
1298    /// Selected bodies, including bodies in an intermediate regenerated result.
1299    Bodies(BodySelection),
1300}
1301
1302/// External model format consumed by an imported-geometry feature.
1303#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1304#[serde(rename_all = "snake_case")]
1305pub enum GeometryImportFormat {
1306    /// ISO 10303 STEP model data.
1307    Step,
1308    /// IGES model data.
1309    Iges,
1310    /// Native boundary-representation model data.
1311    Brep,
1312}
1313
1314/// Selection policy for Boolean-operation fuzzy tolerance.
1315#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
1316#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
1317pub enum FuzzyTolerance {
1318    /// Let the modeling kernel use its default tolerance.
1319    KernelDefault,
1320    /// Determine a suitable tolerance from the participating shapes.
1321    Automatic,
1322    /// Use the supplied positive model-unit tolerance.
1323    Explicit(f64),
1324}
1325
1326const fn default_true() -> bool {
1327    true
1328}
1329
1330/// Geometric offset construction used by a thin-wall shell operation.
1331#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1332#[serde(rename_all = "snake_case")]
1333pub enum ShellMode {
1334    /// Offsets the selected boundary as a skin.
1335    Skin,
1336    /// Extends the offset along boundary edges as a pipe-like wall.
1337    Pipe,
1338    /// Builds wall material on both sides of the original boundary.
1339    BothSides,
1340}
1341
1342/// Corner continuation law for adjacent shell offset faces.
1343#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1344#[serde(rename_all = "snake_case")]
1345pub enum ShellJoin {
1346    /// Continues corners with rounded arcs.
1347    Arc,
1348    /// Extends adjacent faces tangentially to meet.
1349    Tangent,
1350    /// Intersects adjacent offset faces to form sharp corners.
1351    Intersection,
1352}
1353
1354/// Traversal relationship between ruled-surface boundary curves.
1355#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1356#[serde(rename_all = "snake_case")]
1357pub enum RuledCurveOrientation {
1358    /// Select curve traversal automatically from endpoint proximity.
1359    Automatic,
1360    /// Retain both persisted curve traversal directions.
1361    Forward,
1362    /// Reverse the second curve relative to the first.
1363    Reversed,
1364}
1365
1366/// Canonical dimensions of an analytic solid primitive.
1367#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1368#[serde(tag = "kind", rename_all = "snake_case")]
1369pub enum PrimitiveSolid {
1370    /// Rectangular solid aligned to its feature frame.
1371    Box {
1372        /// Size along the local x-axis.
1373        length: Length,
1374        /// Size along the local y-axis.
1375        width: Length,
1376        /// Size along the local z-axis.
1377        height: Length,
1378    },
1379    /// Circular cylinder aligned to its feature-frame z-axis.
1380    Cylinder {
1381        /// Circular base radius.
1382        radius: Length,
1383        /// Axial height.
1384        height: Length,
1385        /// Angular sweep around the axis.
1386        angle: Angle,
1387    },
1388    /// Circular cone or frustum aligned to its feature-frame z-axis.
1389    Cone {
1390        /// Radius at the local-frame origin.
1391        radius1: Length,
1392        /// Radius at the opposite end.
1393        radius2: Length,
1394        /// Axial height.
1395        height: Length,
1396        /// Angular sweep around the axis.
1397        angle: Angle,
1398    },
1399    /// Spherical segment.
1400    Sphere {
1401        /// Sphere radius.
1402        radius: Length,
1403        /// Lower latitude bound.
1404        latitude1: Angle,
1405        /// Upper latitude bound.
1406        latitude2: Angle,
1407        /// Longitudinal sweep.
1408        longitude: Angle,
1409    },
1410    /// Ellipsoidal segment aligned to its feature frame.
1411    Ellipsoid {
1412        /// Radius along local x.
1413        x_radius: Length,
1414        /// Radius along local y.
1415        y_radius: Length,
1416        /// Radius along local z.
1417        z_radius: Length,
1418        /// Lower latitude bound.
1419        latitude1: Angle,
1420        /// Upper latitude bound.
1421        latitude2: Angle,
1422        /// Longitudinal sweep.
1423        longitude: Angle,
1424    },
1425    /// Toroidal segment aligned to its feature frame.
1426    Torus {
1427        /// Distance from the axis to the tube center.
1428        major_radius: Length,
1429        /// Tube radius.
1430        minor_radius: Length,
1431        /// Lower tube-angle bound.
1432        latitude1: Angle,
1433        /// Upper tube-angle bound.
1434        latitude2: Angle,
1435        /// Sweep around the torus axis.
1436        longitude: Angle,
1437    },
1438    /// Regular polygonal prism aligned to its feature frame.
1439    Prism {
1440        /// Number of polygon sides.
1441        sides: u32,
1442        /// Distance from polygon center to each vertex.
1443        circumradius: Length,
1444        /// Axial height.
1445        height: Length,
1446    },
1447    /// General wedge defined by two x-z profiles across a y interval.
1448    Wedge {
1449        /// Lower x bound.
1450        xmin: Length,
1451        /// Lower y bound.
1452        ymin: Length,
1453        /// Lower z bound.
1454        zmin: Length,
1455        /// Inner x coordinate on the lower-y profile.
1456        x2min: Length,
1457        /// Inner z coordinate on the lower-y profile.
1458        z2min: Length,
1459        /// Upper x bound.
1460        xmax: Length,
1461        /// Upper y bound.
1462        ymax: Length,
1463        /// Upper z bound.
1464        zmax: Length,
1465        /// Inner x coordinate on the upper-y profile.
1466        x2max: Length,
1467        /// Inner z coordinate on the upper-y profile.
1468        z2max: Length,
1469    },
1470}
1471
1472/// Independently decoded inputs of a profile revolution.
1473#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1474pub struct RevolutionConstruction {
1475    /// Profile revolved about the axis, when resolved.
1476    #[serde(default, skip_serializing_if = "Option::is_none")]
1477    pub profile: Option<ProfileRef>,
1478    /// Placed revolution axis, when resolved from an axis-bearing selection.
1479    #[serde(default, skip_serializing_if = "Option::is_none")]
1480    pub axis: Option<RevolutionAxis>,
1481    /// Angular extent, when resolved.
1482    #[serde(default, skip_serializing_if = "Option::is_none")]
1483    pub extent: Option<RevolveExtent>,
1484    /// Native edge, datum, or sketch-axis selection used to resolve the axis.
1485    #[serde(default, skip_serializing_if = "Option::is_none")]
1486    pub axis_reference: Option<PathRef>,
1487    /// Whether a standalone revolution creates a solid rather than a sheet.
1488    #[serde(default, skip_serializing_if = "Option::is_none")]
1489    pub solid: Option<bool>,
1490    /// Face-building algorithm used for a standalone solid revolution.
1491    #[serde(default, skip_serializing_if = "Option::is_none")]
1492    pub face_maker_class: Option<String>,
1493    /// Compatibility ordering for fusing a `PartDesign` revolution into its body.
1494    #[serde(default, skip_serializing_if = "Option::is_none")]
1495    pub fuse_order: Option<RevolutionFuseOrder>,
1496    /// Whether a profile containing multiple faces is accepted as one operation.
1497    #[serde(default, skip_serializing_if = "Option::is_none")]
1498    pub allow_multi_profile_faces: Option<bool>,
1499}
1500
1501/// Operand ordering used to fuse a `PartDesign` revolution result.
1502#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1503#[serde(rename_all = "snake_case")]
1504pub enum RevolutionFuseOrder {
1505    /// Existing body is the first fuse operand.
1506    BaseFirst,
1507    /// Newly revolved feature is the first fuse operand.
1508    FeatureFirst,
1509}
1510
1511/// Complete line placement used as a revolution axis.
1512#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
1513pub struct RevolutionAxis {
1514    /// A point on the axis.
1515    pub origin: Point3,
1516    /// Unit axis direction.
1517    pub direction: Vector3,
1518}
1519
1520/// Independently decoded inputs of a thin rib operation.
1521#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1522pub struct RibConstruction {
1523    /// Rib centerline or open profile, when resolved.
1524    #[serde(default, skip_serializing_if = "Option::is_none")]
1525    pub profile: Option<ProfileRef>,
1526    /// Rib growth direction, when resolved.
1527    #[serde(default, skip_serializing_if = "Option::is_none")]
1528    pub direction: Option<Vector3>,
1529    /// Finished rib thickness, when resolved.
1530    #[serde(default, skip_serializing_if = "Option::is_none")]
1531    pub thickness: Option<Length>,
1532    /// Distribution of thickness around the profile, when resolved.
1533    #[serde(default, skip_serializing_if = "Option::is_none")]
1534    pub side: Option<RibSide>,
1535    /// Draft state applied to the rib walls.
1536    #[serde(default)]
1537    pub draft: RibDraft,
1538}
1539
1540/// Distribution of rib thickness around its profile.
1541#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1542#[serde(rename_all = "snake_case")]
1543pub enum RibSide {
1544    /// Thickness lies on one side of the profile.
1545    OneSided,
1546    /// Thickness is split equally around the profile.
1547    Centered,
1548}
1549
1550/// Draft state of a rib construction.
1551#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
1552#[serde(tag = "kind", content = "angle", rename_all = "snake_case")]
1553pub enum RibDraft {
1554    /// Draft semantics are present but unresolved.
1555    #[default]
1556    Unresolved,
1557    /// Rib walls have no draft.
1558    None,
1559    /// Rib walls use the specified draft angle.
1560    Angle(Angle),
1561}
1562
1563/// Canonical role of a non-modeling feature-tree node.
1564#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1565#[serde(rename_all = "snake_case")]
1566pub enum FeatureTreeNodeRole {
1567    /// Annotation container.
1568    Annotations,
1569    /// Ambient scene light.
1570    AmbientLight,
1571    /// Comment container.
1572    Comments,
1573    /// Cross-section container.
1574    CrossSections,
1575    /// Design-binder container.
1576    DesignBinder,
1577    /// Detail-item container.
1578    Details,
1579    /// Profile-selection handle generated from a dissectable sketch.
1580    DissectedProfile,
1581    /// Directional scene light.
1582    DirectionalLight,
1583    /// Equation container.
1584    Equations,
1585    /// Exploded-view container.
1586    ExplodedViews,
1587    /// Favorites container.
1588    Favorites,
1589    /// User-created feature folder.
1590    FeatureFolder,
1591    /// Generic history folder.
1592    History,
1593    /// Lights, cameras, and scene container.
1594    LightsAndCameras,
1595    /// Markup container.
1596    Markups,
1597    /// Built-in model origin node.
1598    ModelOrigin,
1599    /// Point scene light.
1600    PointLight,
1601    /// Material container or assignment node.
1602    Materials,
1603    /// Note container.
1604    Notes,
1605    /// Selection-set container.
1606    SelectionSets,
1607    /// Sensor container.
1608    Sensors,
1609    /// Built-in sheet-metal state root.
1610    SheetMetal,
1611    /// Solid-body container.
1612    SolidBodies,
1613    /// Spot scene light.
1614    SpotLight,
1615    /// Surface-body container.
1616    SurfaceBodies,
1617    /// Table container.
1618    Tables,
1619}
1620
1621/// Axial termination of a cosmetic-thread annotation.
1622#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
1623#[serde(tag = "kind", rename_all = "snake_case")]
1624pub enum CosmeticThreadExtent {
1625    /// Fixed thread length along the cylindrical face.
1626    Blind {
1627        /// Positive axial thread length.
1628        length: Length,
1629    },
1630    /// Thread annotation spans the complete cylindrical face.
1631    Through,
1632}
1633
1634/// Canonical role of a built-in reference plane.
1635#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1636#[serde(rename_all = "snake_case")]
1637pub enum PrincipalPlane {
1638    /// Front plane through the model origin.
1639    Front,
1640    /// Top plane through the model origin.
1641    Top,
1642    /// Right plane through the model origin.
1643    Right,
1644}
1645
1646#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1647#[serde(rename_all = "snake_case")]
1648/// Coordinate space of a sketch history node.
1649pub enum SketchSpace {
1650    /// Planar or spatial coordinates are not established.
1651    Unresolved,
1652    /// Geometry lies on one plane.
1653    #[default]
1654    Planar,
1655    /// Geometry occupies model-space coordinates.
1656    Spatial,
1657}
1658
1659#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1660#[serde(rename_all = "snake_case")]
1661/// Side retained by a body-trim operation.
1662pub enum BodyTrimSide {
1663    /// Retained side is unresolved.
1664    Unresolved,
1665    /// Retain the side selected by tool orientation.
1666    Forward,
1667    /// Retain the side opposite tool orientation.
1668    Reverse,
1669}
1670
1671#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
1672#[serde(untagged)]
1673/// Direction law for projected curves.
1674pub enum CurveProjectionDirection {
1675    /// One explicit model-space vector.
1676    Vector(Vector3),
1677    /// Direction state without one explicit vector.
1678    State(CurveProjectionDirectionState),
1679}
1680
1681impl Default for CurveProjectionDirection {
1682    fn default() -> Self {
1683        Self::State(CurveProjectionDirectionState::TargetNormal)
1684    }
1685}
1686
1687#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1688#[serde(rename_all = "snake_case")]
1689/// Direction state for projection without an explicit vector.
1690pub enum CurveProjectionDirectionState {
1691    /// Projection direction remains unresolved.
1692    Unresolved,
1693    /// Project along each target face's normal.
1694    TargetNormal,
1695}
1696
1697/// Selection interpretation for a delete/keep-body operation.
1698#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1699#[serde(rename_all = "snake_case")]
1700pub enum BodyRetentionMode {
1701    /// The operation family is known but the selected retention mode is unavailable.
1702    Unresolved,
1703    /// Delete the selected bodies.
1704    DeleteSelected,
1705    /// Delete every body except the selected bodies.
1706    KeepSelected,
1707}
1708
1709/// Material effect of a wrapped profile.
1710#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1711#[serde(rename_all = "snake_case")]
1712pub enum WrapMode {
1713    /// Add material above the target face.
1714    Emboss,
1715    /// Remove material below the target face.
1716    Deboss,
1717    /// Imprint the profile without adding or removing material.
1718    Scribe,
1719}
1720
1721/// Continuity order imposed at a generated surface boundary.
1722#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1723#[serde(rename_all = "snake_case")]
1724pub enum SurfaceContinuity {
1725    /// Positional continuity only.
1726    Contact,
1727    /// First-derivative continuity.
1728    Tangent,
1729    /// Second-derivative continuity.
1730    Curvature,
1731}
1732
1733/// Boundary input accepted by a filled-surface operation.
1734#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1735#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
1736pub enum SurfaceBoundary {
1737    /// Boundary selected as topological edges.
1738    Edges(EdgeSelection),
1739    /// Boundary selected as a sketch, curve, or mixed path collection.
1740    Path(PathRef),
1741}
1742
1743/// Region retained by a trim-surface operation.
1744#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1745#[serde(rename_all = "snake_case")]
1746pub enum TrimRegion {
1747    /// Source trim exists but the retained region is unresolved.
1748    Unresolved,
1749    /// Retain the region enclosed by the trimming path.
1750    Inside,
1751    /// Retain the region outside the trimming path.
1752    Outside,
1753}
1754
1755/// Geometric law used to extend a surface boundary.
1756#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1757#[serde(rename_all = "snake_case")]
1758pub enum SurfaceExtension {
1759    /// Extension law is unresolved.
1760    Unresolved,
1761    /// Continue the source surface parameterization.
1762    Natural,
1763    /// Extend boundary tangents as ruled linear strips.
1764    Linear,
1765}
1766
1767/// Direction law for a ruled-surface operation.
1768#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1769#[serde(tag = "kind", rename_all = "snake_case")]
1770pub enum RuledSurfaceMode {
1771    /// Extend normal to the support faces.
1772    Normal {
1773        /// Positive extension distance.
1774        distance: Length,
1775    },
1776    /// Extend tangent to the support faces.
1777    Tangent {
1778        /// Positive extension distance.
1779        distance: Length,
1780    },
1781    /// Extend along one explicit model-space direction.
1782    Direction {
1783        /// Extension direction.
1784        direction: Vector3,
1785        /// Positive extension distance.
1786        distance: Length,
1787    },
1788}
1789
1790/// Fixed locus of a body-scale transform.
1791#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1792#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
1793pub enum ScaleCenter {
1794    /// Combined centroid of the selected bodies.
1795    Centroid,
1796    /// Model coordinate-system origin.
1797    ModelOrigin,
1798    /// Explicit model-space point.
1799    Point(Point3),
1800    /// Format-native coordinate-system or reference identifier.
1801    Native(String),
1802}
1803
1804/// Independently decoded factors of a body-scale transform.
1805#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
1806pub struct ScaleFactors {
1807    /// Uniform factor, when resolved.
1808    #[serde(default, skip_serializing_if = "Option::is_none")]
1809    pub uniform: Option<f64>,
1810    /// Model-space x factor, when resolved independently.
1811    #[serde(default, skip_serializing_if = "Option::is_none")]
1812    pub x: Option<f64>,
1813    /// Model-space y factor, when resolved independently.
1814    #[serde(default, skip_serializing_if = "Option::is_none")]
1815    pub y: Option<f64>,
1816    /// Model-space z factor, when resolved independently.
1817    #[serde(default, skip_serializing_if = "Option::is_none")]
1818    pub z: Option<f64>,
1819}
1820
1821impl ScaleFactors {
1822    /// Resolve the effective model-space factors when construction is complete.
1823    #[must_use]
1824    pub fn resolved(self) -> Option<Vector3> {
1825        if let Some(factor) = self.uniform {
1826            return Some(Vector3::new(factor, factor, factor));
1827        }
1828        Some(Vector3::new(self.x?, self.y?, self.z?))
1829    }
1830}
1831
1832/// Direction in which a thicken feature adds material.
1833#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1834#[serde(rename_all = "snake_case")]
1835pub enum ThickenSide {
1836    /// Add material along the selected-face normal.
1837    Forward,
1838    /// Add material opposite the selected-face normal.
1839    Reverse,
1840    /// Split the thickness equally across both sides.
1841    Both,
1842}
1843
1844/// Distribution of sheet thickness relative to its construction plane.
1845#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1846#[serde(rename_all = "snake_case")]
1847pub enum SheetMetalThicknessSide {
1848    /// Thickness lies along the profile plane's positive normal.
1849    Forward,
1850    /// Thickness lies opposite the profile plane's positive normal.
1851    Reverse,
1852    /// Thickness is split equally across both sides of the profile plane.
1853    Symmetric,
1854}
1855
1856/// Edge operands resolved by the decoder or retained in native form.
1857#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1858#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
1859pub enum EdgeSelection {
1860    /// Selection exists semantically but its operands are not resolved.
1861    Unresolved,
1862    /// Every edge of the operation's input body.
1863    All,
1864    /// Resolved topological edges.
1865    Edges(Vec<EdgeId>),
1866    /// Resolved edges paired with the format-native selection required for rewrite.
1867    Resolved {
1868        /// Resolved topological edges.
1869        edges: Vec<EdgeId>,
1870        /// Format-native selection reference.
1871        native: String,
1872    },
1873    /// Edges resolved in the containing feature's input topology.
1874    Historical {
1875        /// Input topology containing every selected edge.
1876        state: FeatureInputTopologyId,
1877        /// State-local edge identities in operand order.
1878        edges: Vec<HistoricalEdgeId>,
1879        /// Format-native selection reference.
1880        native: String,
1881    },
1882    /// Proven historical edges plus source operands whose edge identity is unresolved.
1883    /// `edges` is empty when the input state is known but no member identity resolves.
1884    HistoricalPartial {
1885        /// Input topology containing every resolved edge.
1886        state: FeatureInputTopologyId,
1887        /// Proven state-local edge identities in source operand order.
1888        edges: Vec<HistoricalEdgeId>,
1889        /// Stable native identities of unresolved source operands.
1890        unresolved: Vec<String>,
1891        /// Format-native group selection reference.
1892        native: String,
1893    },
1894    /// Edges in intermediate regenerated feature results, paired with the
1895    /// format-native selection required for rewrite.
1896    Generated {
1897        /// Feature-local edge identities.
1898        edges: Vec<GeneratedEdgeRef>,
1899        /// Format-native persistent selection reference.
1900        native: String,
1901    },
1902    /// Format-native selection reference.
1903    Native(String),
1904}
1905
1906/// Persistent identity of an edge in one regenerated feature result.
1907#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1908pub struct GeneratedEdgeRef {
1909    /// Feature whose regenerated result owns the edge.
1910    pub feature: FeatureId,
1911    /// Feature-local persistent edge identity.
1912    pub local_id: String,
1913}
1914
1915/// Persistent identity of a face in one regenerated feature result.
1916#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1917pub struct GeneratedFaceRef {
1918    /// Feature whose regenerated result owns the face.
1919    pub feature: FeatureId,
1920    /// Feature-local persistent face identity.
1921    pub local_id: String,
1922}
1923
1924/// Persistent identity of a vertex in one regenerated feature result.
1925#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1926pub struct GeneratedVertexRef {
1927    /// Feature whose regenerated result owns the vertex.
1928    pub feature: FeatureId,
1929    /// Feature-local persistent vertex identity.
1930    pub local_id: String,
1931}
1932
1933/// Vertex operand resolved by the decoder or retained in native form.
1934#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1935#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
1936pub enum VertexSelection {
1937    /// Selection exists semantically but its operand is not resolved.
1938    Unresolved,
1939    /// Vertex in an intermediate regenerated feature result, paired with the
1940    /// format-native selection required for rewrite.
1941    Generated {
1942        /// Feature-local vertex identity.
1943        vertex: GeneratedVertexRef,
1944        /// Format-native persistent selection reference.
1945        native: String,
1946    },
1947    /// Format-native selection reference.
1948    Native(String),
1949}
1950
1951/// Face operands resolved by the decoder or retained in native form.
1952#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1953#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
1954pub enum FaceSelection {
1955    /// Selection exists semantically but its operands are not resolved.
1956    Unresolved,
1957    /// Resolved topological faces; empty for no selected faces.
1958    Faces(Vec<FaceId>),
1959    /// Resolved faces paired with the format-native selection required for rewrite.
1960    Resolved {
1961        /// Resolved topological faces.
1962        faces: Vec<FaceId>,
1963        /// Format-native selection reference.
1964        native: String,
1965    },
1966    /// Faces resolved in the containing feature's input topology.
1967    Historical {
1968        /// Input topology containing every selected face.
1969        state: FeatureInputTopologyId,
1970        /// State-local face identities in operand order.
1971        faces: Vec<HistoricalFaceId>,
1972        /// Format-native selection reference.
1973        native: String,
1974    },
1975    /// Historical faces proven for part of a native selection.
1976    HistoricalPartial {
1977        /// Input topology containing every resolved face.
1978        state: FeatureInputTopologyId,
1979        /// Proven state-local face identities in source operand order.
1980        faces: Vec<HistoricalFaceId>,
1981        /// Stable native identities of unresolved source operands.
1982        unresolved: Vec<String>,
1983        /// Format-native selection reference.
1984        native: String,
1985    },
1986    /// Faces in an intermediate regenerated feature result, paired with the
1987    /// format-native selection required for rewrite.
1988    Generated {
1989        /// Feature-local face identities.
1990        faces: Vec<GeneratedFaceRef>,
1991        /// Format-native persistent selection reference.
1992        native: String,
1993    },
1994    /// Format-native selection reference.
1995    Native(String),
1996}
1997
1998/// Body operands resolved by the decoder or retained in native form.
1999#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2000#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
2001pub enum BodySelection {
2002    /// Selection exists semantically but its operands are not resolved.
2003    Unresolved,
2004    /// Resolved topological bodies.
2005    Bodies(Vec<BodyId>),
2006    /// Resolved bodies paired with the format-native selection required for rewrite.
2007    Resolved {
2008        /// Resolved topological bodies.
2009        bodies: Vec<BodyId>,
2010        /// Format-native selection expression.
2011        native: String,
2012    },
2013    /// Bodies resolved in the containing feature's input topology.
2014    Historical {
2015        /// Input topology containing every selected body.
2016        state: FeatureInputTopologyId,
2017        /// State-local body identities in operand order.
2018        bodies: Vec<HistoricalBodyId>,
2019        /// Format-native selection expression.
2020        native: String,
2021    },
2022    /// Bodies in intermediate regenerated feature results, paired with the
2023    /// format-native selection required for rewrite.
2024    Generated {
2025        /// Feature-local body identities.
2026        bodies: Vec<GeneratedBodyRef>,
2027        /// Format-native persistent selection reference.
2028        native: String,
2029    },
2030    /// Persistent bodies in the consuming feature's regeneration input state.
2031    Local {
2032        /// Ordered feature-input-local body identities.
2033        bodies: Vec<String>,
2034        /// Format-native persistent selection reference.
2035        native: String,
2036    },
2037    /// Format-native selection expression.
2038    Native(String),
2039}
2040
2041/// Persistent identity of a body in one regenerated feature result.
2042#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2043pub struct GeneratedBodyRef {
2044    /// Feature whose regenerated result owns the body.
2045    pub feature: FeatureId,
2046    /// Feature-local persistent body identity.
2047    pub local_id: String,
2048}
2049
2050/// Direct face-motion law.
2051#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2052#[serde(tag = "kind", rename_all = "snake_case")]
2053pub enum FaceMotion {
2054    /// Offset along each face normal.
2055    Offset {
2056        /// Signed offset distance.
2057        distance: Length,
2058    },
2059    /// Translation along one direction.
2060    Translate {
2061        /// Translation direction.
2062        direction: Vector3,
2063        /// Signed translation distance.
2064        distance: Length,
2065    },
2066    /// Rotation about an axis.
2067    Rotate {
2068        /// Point on the rotation axis.
2069        axis_origin: Point3,
2070        /// Rotation-axis direction.
2071        axis_dir: Vector3,
2072        /// Signed rotation angle.
2073        angle: Angle,
2074    },
2075}
2076
2077/// Model-space axis-angle rotation.
2078#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2079pub struct AxisAngle {
2080    /// Point on the rotation axis.
2081    pub origin: Point3,
2082    /// Rotation-axis direction.
2083    pub direction: Vector3,
2084    /// Signed rotation angle.
2085    pub angle: Angle,
2086}
2087
2088/// Start condition of a linear extrusion.
2089#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
2090#[serde(tag = "kind", rename_all = "snake_case")]
2091pub enum ExtrudeStart {
2092    /// Begin on the profile's own plane.
2093    #[default]
2094    ProfilePlane,
2095    /// Begin on a plane parallel to the profile plane at a signed offset.
2096    OffsetProfilePlane {
2097        /// Signed offset along the profile normal in canonical millimeters.
2098        offset: Length,
2099    },
2100    /// Begin on a selected face, optionally displaced along the extrusion direction.
2101    FromFace {
2102        /// Face defining the start plane.
2103        face: FaceSelection,
2104        /// Signed displacement from the selected face.
2105        #[serde(default, skip_serializing_if = "Option::is_none")]
2106        offset: Option<Length>,
2107    },
2108}
2109
2110/// One-sided termination law of a linear or angular sweep. Sidedness around
2111/// the profile plane is stated by the owning feature's extent type, never by
2112/// the law itself.
2113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2114#[serde(tag = "kind", rename_all = "snake_case")]
2115pub enum Termination {
2116    /// Native termination is present structurally but unresolved.
2117    Unresolved,
2118    /// Fixed travel distance.
2119    Blind {
2120        /// Fixed travel distance.
2121        length: Length,
2122    },
2123    /// Extends through all material.
2124    ThroughAll,
2125    /// Extends until it exits the next material region.
2126    ThroughNext,
2127    /// Extends until the first encountered model face.
2128    ToFirst,
2129    /// Extends until the last encountered model face.
2130    ToLast,
2131    /// Extends until it reaches a target face.
2132    ToFace {
2133        /// Face terminating the operation.
2134        face: FaceSelection,
2135        /// Signed displacement from the terminating face.
2136        #[serde(default, skip_serializing_if = "Option::is_none")]
2137        offset: Option<Length>,
2138    },
2139    /// Extends until it reaches a target vertex.
2140    ToVertex {
2141        /// Vertex terminating the operation.
2142        vertex: VertexSelection,
2143    },
2144    /// Extends to a fixed offset from a target face.
2145    OffsetFromFace {
2146        /// Face the termination is measured from.
2147        face: FaceSelection,
2148        /// Offset distance from the face.
2149        offset: Length,
2150    },
2151    /// Extends until one of the faces in a selected target shape.
2152    ToShape {
2153        /// Native or resolved target shape selection.
2154        target: FaceSelection,
2155    },
2156    /// Fixed angular travel.
2157    Angle {
2158        /// Angular travel.
2159        angle: Angle,
2160    },
2161}
2162
2163/// One side of an extrusion: its termination law and side-local modifiers.
2164/// Drafts are measured from the profile plane outward along the side's
2165/// travel; an absent draft leaves the side walls parallel.
2166#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2167pub struct ExtrudeSide {
2168    /// Where this side's travel terminates.
2169    pub termination: Termination,
2170    /// Draft angle applied to this side's walls, when present.
2171    #[serde(default, skip_serializing_if = "Option::is_none")]
2172    pub draft: Option<Angle>,
2173    /// Signed offset from this side's terminating geometry, when present.
2174    #[serde(default, skip_serializing_if = "Option::is_none")]
2175    pub offset: Option<Length>,
2176}
2177
2178/// Extrusion sidedness around the profile plane.
2179#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2180#[serde(tag = "kind", rename_all = "snake_case")]
2181pub enum ExtrudeExtent {
2182    /// Travel on the oriented side only.
2183    OneSided {
2184        /// The single traveled side.
2185        side: ExtrudeSide,
2186    },
2187    /// Independent sides on each side of the profile plane.
2188    TwoSided {
2189        /// Side along the extrusion direction.
2190        first: ExtrudeSide,
2191        /// Side opposite the extrusion direction.
2192        second: ExtrudeSide,
2193    },
2194    /// One side mirrored across the profile plane. A blind length or angular
2195    /// travel states the total travel split evenly around the plane.
2196    Symmetric {
2197        /// The mirrored side.
2198        side: ExtrudeSide,
2199    },
2200}
2201
2202/// Revolution sidedness around the profile plane. Revolution sides carry no
2203/// side-local modifiers, only their termination laws.
2204#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2205#[serde(tag = "kind", rename_all = "snake_case")]
2206pub enum RevolveExtent {
2207    /// Travel on the oriented side only.
2208    OneSided {
2209        /// The single traveled side's termination.
2210        termination: Termination,
2211    },
2212    /// Independent terminations on each side of the profile plane.
2213    TwoSided {
2214        /// Termination along the revolution direction.
2215        first: Termination,
2216        /// Termination opposite the revolution direction.
2217        second: Termination,
2218    },
2219    /// One termination mirrored across the profile plane. An angular travel
2220    /// states the total travel split evenly around the plane.
2221    Symmetric {
2222        /// The mirrored side's termination.
2223        termination: Termination,
2224    },
2225}
2226
2227/// Persisted source of a resolved linear-extrusion direction.
2228#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2229#[serde(tag = "kind", rename_all = "snake_case")]
2230pub enum ExtrusionDirectionSource {
2231    /// Direction comes from the persisted direction vector.
2232    Custom,
2233    /// Direction comes from a selected straight edge.
2234    Edge {
2235        /// Native edge selection used as the direction axis.
2236        reference: PathRef,
2237    },
2238    /// Direction comes from the source profile's plane normal.
2239    ProfileNormal,
2240}
2241
2242/// Native face-building policy for a solid linear extrusion.
2243#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2244pub struct ExtrusionFaceMaker {
2245    /// Runtime face-maker class, retained as an extensible semantic identifier.
2246    pub class: String,
2247    /// Persisted enumeration value corresponding to the class, when carried.
2248    #[serde(default, skip_serializing_if = "Option::is_none")]
2249    pub mode: Option<u32>,
2250}
2251
2252/// Relationship between outer-wire and inner-wire taper directions.
2253#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2254#[serde(rename_all = "snake_case")]
2255pub enum InnerWireTaper {
2256    /// Inner wires taper opposite to outer wires.
2257    Inverted,
2258    /// Inner wires taper in the same direction as outer wires.
2259    SameAsOuter,
2260}
2261
2262/// Persisted construction algorithm used for a parametric helix.
2263#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2264#[serde(rename_all = "snake_case")]
2265pub enum HelixConstructionStyle {
2266    /// Historical construction retained for document compatibility.
2267    Legacy,
2268    /// Corrected construction used by newly created features.
2269    Corrected,
2270}
2271
2272/// Result topology retained by a projection-on-surface operation.
2273#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2274#[serde(rename_all = "snake_case")]
2275pub enum SurfaceProjectionMode {
2276    /// Retain all projected result shapes.
2277    All,
2278    /// Retain projected faces only.
2279    Faces,
2280    /// Retain projected edges only.
2281    Edges,
2282}
2283
2284/// Boolean effect of a solid-producing feature.
2285#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2286#[serde(rename_all = "snake_case")]
2287pub enum BooleanOp {
2288    /// Source operation is retained but not semantically resolved.
2289    Unresolved,
2290    /// Union with existing bodies.
2291    Join,
2292    /// Subtraction from existing bodies.
2293    Cut,
2294    /// Intersection with existing bodies.
2295    Intersect,
2296    /// Creates an independent new body without combining.
2297    NewBody,
2298}
2299
2300/// Placement and parameterization of a solid Coil primitive.
2301#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2302pub struct CoilConstruction {
2303    /// Axis frame and angular origin.
2304    pub placement: CoilPlacement,
2305    /// Diameter of the reference trajectory at its start.
2306    pub diameter: Length,
2307    /// Independent driving dimensions retained from the source feature.
2308    pub extent: CoilExtent,
2309    /// Generated section swept along the trajectory.
2310    pub section: CoilSection,
2311    /// Radial position of the section relative to the reference trajectory.
2312    pub section_placement: CoilSectionPlacement,
2313    /// Angular travel direction when viewed from the axis origin along the positive axis.
2314    pub clockwise: bool,
2315    /// Signed cone half-angle of an axial coil; zero produces a cylindrical helix.
2316    pub taper: Angle,
2317}
2318
2319/// Geometric placement of a Coil trajectory.
2320#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2321#[serde(tag = "kind", rename_all = "snake_case")]
2322pub enum CoilPlacement {
2323    /// Complete right-handed model-space frame.
2324    Explicit {
2325        /// Center of the trajectory on its base plane.
2326        origin: Point3,
2327        /// Positive trajectory-axis direction.
2328        axis: Vector3,
2329        /// Direction from `origin` to angular position zero.
2330        radial: Vector3,
2331    },
2332    /// Complete placement retained in one source-native construction aggregate.
2333    Native {
2334        /// Native record or scope containing the placement semantics.
2335        native_ref: String,
2336    },
2337}
2338
2339/// Independent driving dimensions of a Coil trajectory.
2340#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
2341#[serde(tag = "kind", rename_all = "snake_case")]
2342pub enum CoilExtent {
2343    /// Axial coil driven by revolution count and total signed height.
2344    RevolutionsHeight {
2345        /// Positive angular-turn count.
2346        revolutions: f64,
2347        /// Signed axial travel.
2348        height: Length,
2349    },
2350    /// Axial coil driven by revolution count and signed pitch per revolution.
2351    RevolutionsPitch {
2352        /// Positive angular-turn count.
2353        revolutions: f64,
2354        /// Signed axial travel per revolution.
2355        pitch: Length,
2356    },
2357    /// Axial coil driven by total signed height and signed pitch per revolution.
2358    HeightPitch {
2359        /// Signed axial travel.
2360        height: Length,
2361        /// Signed axial travel per revolution.
2362        pitch: Length,
2363    },
2364    /// Planar spiral driven by revolution count and signed radial pitch.
2365    Spiral {
2366        /// Positive angular-turn count.
2367        revolutions: f64,
2368        /// Signed radial growth per revolution.
2369        radial_pitch: Length,
2370    },
2371}
2372
2373/// Generated cross-section of a Coil primitive.
2374#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
2375#[serde(tag = "kind", rename_all = "snake_case")]
2376pub enum CoilSection {
2377    /// Circular section whose size is its diameter.
2378    Circular {
2379        /// Circle diameter.
2380        diameter: Length,
2381    },
2382    /// Square section whose size is its edge length.
2383    Square {
2384        /// Edge length.
2385        size: Length,
2386    },
2387    /// Equilateral triangle pointing radially away from the axis.
2388    ExternalTriangle {
2389        /// Edge length.
2390        size: Length,
2391    },
2392    /// Equilateral triangle pointing radially toward the axis.
2393    InternalTriangle {
2394        /// Edge length.
2395        size: Length,
2396    },
2397}
2398
2399/// Radial placement of a generated Coil section.
2400#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2401#[serde(rename_all = "snake_case")]
2402pub enum CoilSectionPlacement {
2403    /// Section lies inside the reference trajectory.
2404    Inside,
2405    /// Section centroid lies on the reference trajectory.
2406    Center,
2407    /// Section lies outside the reference trajectory.
2408    Outside,
2409}
2410
2411/// Result semantics of a solid Coil primitive.
2412#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2413#[serde(tag = "kind", rename_all = "snake_case")]
2414pub enum CoilResult {
2415    /// Create an independent body.
2416    NewBody,
2417    /// Combine the swept volume with selected existing bodies.
2418    Boolean {
2419        /// Join, cut, or intersection operation.
2420        operation: BooleanOp,
2421        /// Existing bodies participating in the operation.
2422        targets: BodySelection,
2423    },
2424}
2425
2426/// Result semantics of a swept profile.
2427#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2428#[serde(tag = "mode", rename_all = "snake_case")]
2429pub enum SweepMode {
2430    /// Native sweep family is known but its result subtype is unresolved.
2431    Unresolved,
2432    /// Sweep creates or modifies a solid body.
2433    Solid {
2434        /// Boolean combination with existing bodies.
2435        op: BooleanOp,
2436    },
2437    /// Sweep creates a sheet body.
2438    Surface,
2439}
2440
2441/// One directed use of a solved sketch curve in an arrangement boundary.
2442#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2443pub struct SketchProfileBoundaryUse {
2444    /// Sketch entity supplying the curve geometry.
2445    pub entity: crate::sketches::SketchEntityId,
2446    /// Parameter endpoints on the source curve, ordered in the entity's stored direction.
2447    pub parameter_range: [f64; 2],
2448    /// Whether boundary traversal opposes the interval's stored direction.
2449    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
2450    pub reversed: bool,
2451}
2452
2453/// One connected planar region bounded by solved sketch curves.
2454///
2455/// Whole-loop regions retain compact profile indices. Arrangement regions
2456/// carry exact trimmed curve uses when their boundary switches source loops at
2457/// intersections. The untagged representation preserves the established JSON
2458/// shape of whole-loop regions.
2459#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2460#[serde(untagged)]
2461pub enum SketchProfileRegion {
2462    /// Exterior and holes are complete entries in the sketch profile table.
2463    Loops {
2464        /// Exterior-loop index in the referenced sketch's profile-loop table.
2465        outer: u32,
2466        /// Immediate child loops removed from the exterior interior.
2467        #[serde(default, skip_serializing_if = "Vec::is_empty")]
2468        holes: Vec<u32>,
2469    },
2470    /// Boundary rings switch source curves at arrangement intersections.
2471    Trimmed {
2472        /// Directed exterior boundary ring.
2473        outer_boundary: Vec<SketchProfileBoundaryUse>,
2474        /// Directed hole boundary rings.
2475        #[serde(default, skip_serializing_if = "Vec::is_empty")]
2476        hole_boundaries: Vec<Vec<SketchProfileBoundaryUse>>,
2477    },
2478}
2479
2480/// Cross-section orientation law along a sweep path.
2481#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2482#[serde(tag = "kind", rename_all = "snake_case")]
2483pub enum SweepOrientation {
2484    /// Rotation-minimizing corrected-Frenet frame.
2485    CorrectedFrenet,
2486    /// Fixed section frame.
2487    Fixed,
2488    /// Exact Frenet frame from path derivatives.
2489    Frenet,
2490    /// Frame constrained by a secondary path.
2491    Auxiliary {
2492        /// Secondary orientation path.
2493        path: PathRef,
2494        /// Whether tangent-connected edges extend the secondary path.
2495        tangent: bool,
2496        /// Whether corresponding points use curvilinear rather than parameter distance.
2497        curvilinear: bool,
2498    },
2499    /// Frame constrained by a fixed binormal direction.
2500    Binormal {
2501        /// Unit binormal direction.
2502        direction: Vector3,
2503    },
2504}
2505
2506/// Corner continuation used by a sweep path.
2507#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2508#[serde(rename_all = "snake_case")]
2509pub enum SweepTransition {
2510    /// Transform the section continuously across the corner.
2511    Transformed,
2512    /// Form a sharp right-corner intersection.
2513    RightCorner,
2514    /// Insert a rounded corner transition.
2515    RoundCorner,
2516}
2517
2518/// Cross-section interpolation law for a multi-section sweep.
2519#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2520#[serde(rename_all = "snake_case")]
2521pub enum SweepTransformation {
2522    /// Keep one constant section along the path.
2523    Constant,
2524    /// Interpolate through explicit ordered sections.
2525    MultiSection,
2526    /// Apply linear section interpolation.
2527    Linear,
2528    /// Apply an S-shaped interpolation law.
2529    SShape,
2530    /// Apply the native smooth interpolation law.
2531    Interpolation,
2532}
2533
2534/// Complete construction of a solid helical sweep.
2535#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2536pub struct HelicalSweepConstruction {
2537    /// Profile swept along the helical path.
2538    pub profile: ProfileRef,
2539    /// Point at the start of the helix axis.
2540    pub axis_origin: Point3,
2541    /// Unit direction of positive axial travel.
2542    pub axis_direction: Vector3,
2543    /// Persisted authoring law identifying the independent parameters.
2544    pub law: HelicalSweepLaw,
2545    /// Positive axial advance per turn; zero is permitted for a planar spiral.
2546    pub pitch: Length,
2547    /// Signed total axial travel.
2548    pub height: Length,
2549    /// Positive number of turns.
2550    pub turns: f64,
2551    /// Signed radial change per turn.
2552    pub radial_growth: Length,
2553    /// Cone half-angle corresponding to radial growth.
2554    pub cone_angle: Angle,
2555    /// Whether angular travel is left-handed along the positive axis.
2556    pub left_handed: bool,
2557    /// Whether path travel runs opposite the declared axis direction.
2558    pub reversed: bool,
2559    /// Relative tolerance used while joining the generated sweep.
2560    pub tolerance: f64,
2561    /// Whether a profile containing multiple faces is accepted as one operation.
2562    #[serde(default, skip_serializing_if = "Option::is_none")]
2563    pub allow_multi_profile_faces: Option<bool>,
2564}
2565
2566/// Independent-parameter law used to author a helical sweep.
2567#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2568#[serde(rename_all = "snake_case")]
2569pub enum HelicalSweepLaw {
2570    /// Pitch, height, and cone angle are independent.
2571    PitchHeightAngle,
2572    /// Pitch, turn count, and cone angle are independent.
2573    PitchTurnsAngle,
2574    /// Height, turn count, and cone angle are independent.
2575    HeightTurnsAngle,
2576    /// Height, turn count, and radial growth are independent.
2577    HeightTurnsGrowth,
2578}
2579
2580/// One object or subelement selection consumed by a design binder.
2581#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2582pub struct BinderSource {
2583    /// Bound object identity.
2584    pub target: BinderTarget,
2585    /// Ordered native subelement selectors; empty selects the complete object.
2586    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2587    pub subelements: Vec<String>,
2588}
2589
2590/// Resolved or externally scoped binder target.
2591#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2592#[serde(tag = "kind", rename_all = "snake_case")]
2593pub enum BinderTarget {
2594    /// Feature in this CADIR document.
2595    Feature {
2596        /// Target feature identity.
2597        feature: FeatureId,
2598    },
2599    /// Object in another source document.
2600    External {
2601        /// Source document identity.
2602        document: String,
2603        /// Object identity within the source document.
2604        object: String,
2605    },
2606    /// Source-native target identity that cannot be resolved further.
2607    Native {
2608        /// Opaque source-native target identity.
2609        reference: String,
2610    },
2611}
2612
2613/// Binding behavior and optional derived-shape construction.
2614#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2615#[serde(tag = "kind", rename_all = "snake_case")]
2616pub enum BinderConstruction {
2617    /// Simple binder over one support object.
2618    Shape {
2619        /// Transform support geometry between its container and the binder container.
2620        trace_support: bool,
2621    },
2622    /// Multi-object subshape binder.
2623    SubShape {
2624        /// Live-update lifecycle.
2625        lifecycle: BinderLifecycle,
2626        /// Placement interpretation for linked subobjects.
2627        placement: BinderPlacement,
2628        /// Copy-on-change state.
2629        copy_on_change: BinderCopyOnChange,
2630        /// Whether linked objects are claimed as children in the tree.
2631        claim_children: bool,
2632        /// Whether multiple resulting solids are fused.
2633        fuse: bool,
2634        /// Whether bound wires are promoted to faces.
2635        make_face: bool,
2636        /// Whether external documents may remain partially loaded.
2637        partial_load: bool,
2638        /// Whether redundant edges are removed from the result.
2639        refine: bool,
2640        /// Optional two-dimensional offset construction.
2641        #[serde(default, skip_serializing_if = "Option::is_none")]
2642        offset: Option<BinderOffset>,
2643        /// Context object used to interpret relative placement.
2644        #[serde(default, skip_serializing_if = "Option::is_none")]
2645        context: Option<BinderTarget>,
2646    },
2647}
2648
2649/// Update lifecycle of a subshape binder.
2650#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2651#[serde(rename_all = "snake_case")]
2652pub enum BinderLifecycle {
2653    /// Automatically tracks changes to its sources.
2654    Synchronized,
2655    /// Retains links but updates only when explicitly requested.
2656    Frozen,
2657    /// Stores a copied shape and no longer retains live binding behavior.
2658    Detached,
2659}
2660
2661/// Placement interpretation for bound subobjects.
2662#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2663#[serde(rename_all = "snake_case")]
2664pub enum BinderPlacement {
2665    /// Interpret source placement relative to the binder context.
2666    Relative,
2667    /// Preserve source placement in global coordinates.
2668    Global,
2669}
2670
2671/// Copy-on-change state of a subshape binder.
2672#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2673#[serde(rename_all = "snake_case")]
2674pub enum BinderCopyOnChange {
2675    /// Do not clone configurable source properties.
2676    Disabled,
2677    /// Clone configurable source properties when they change.
2678    Enabled,
2679    /// A private source copy has already been mutated.
2680    Mutated,
2681}
2682
2683/// Two-dimensional offset applied to bound faces or wires.
2684#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
2685pub struct BinderOffset {
2686    /// Signed offset distance.
2687    pub distance: Length,
2688    /// Join law at offset corners.
2689    pub join: BinderOffsetJoin,
2690    /// Whether to fill between original and offset wires.
2691    pub fill: bool,
2692    /// Whether open input wires produce open offset results.
2693    pub open_result: bool,
2694    /// Whether child-wire intersections are resolved together.
2695    pub intersection: bool,
2696}
2697
2698/// Corner join law of a binder offset.
2699#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2700#[serde(rename_all = "snake_case")]
2701pub enum BinderOffsetJoin {
2702    /// Circular corner arcs.
2703    Arcs,
2704    /// Tangent continuation.
2705    Tangent,
2706    /// Sharp line-line intersections.
2707    Intersection,
2708}
2709
2710/// Profile consumed by a profile-driven feature.
2711#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2712#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
2713pub enum ProfileRef {
2714    /// A profile is required by the identified native owner but its carrier is unresolved.
2715    Unresolved(String),
2716    /// Opaque reference into a native feature-input record; no neutral geometry given.
2717    Native(String),
2718    /// Solved neutral sketch profile.
2719    Sketch(crate::sketches::SketchId),
2720    /// Specific solved profile loops within one neutral sketch.
2721    SketchProfiles {
2722        /// Sketch containing the selected loops.
2723        sketch: crate::sketches::SketchId,
2724        /// Zero-based indices into [`crate::sketches::Sketch::profiles`].
2725        profiles: Vec<u32>,
2726    },
2727    /// Exact union of bounded atomic regions within one neutral sketch.
2728    SketchRegions {
2729        /// Sketch containing every referenced boundary loop.
2730        sketch: crate::sketches::SketchId,
2731        /// Connected regions in source selection order.
2732        regions: Vec<SketchProfileRegion>,
2733    },
2734    /// Source-native selection within a known neutral sketch.
2735    SketchSelection {
2736        /// Sketch containing the unresolved selected geometry.
2737        sketch: crate::sketches::SketchId,
2738        /// Full-fidelity native selection records in source order.
2739        selections: Vec<String>,
2740    },
2741    /// Specific solved profile loops within one neutral spatial sketch.
2742    SpatialSketchProfiles {
2743        /// Spatial sketch containing the selected loops.
2744        sketch: crate::sketches::SpatialSketchId,
2745        /// Zero-based indices into [`crate::sketches::SpatialSketch::profiles`].
2746        profiles: Vec<u32>,
2747    },
2748    /// Source-native selection within a known neutral spatial sketch.
2749    SpatialSketchSelection {
2750        /// Spatial sketch containing the unresolved selected geometry.
2751        sketch: crate::sketches::SpatialSketchId,
2752        /// Full-fidelity native selection records in source order.
2753        selections: Vec<String>,
2754    },
2755    /// Profile given by faces in the consuming feature's input topology.
2756    HistoricalFaces {
2757        /// Input topology containing every selected face.
2758        state: FeatureInputTopologyId,
2759        /// State-local face identities in source selection order.
2760        faces: Vec<HistoricalFaceId>,
2761        /// Full-fidelity source selection groups in source order.
2762        native: Vec<String>,
2763    },
2764    /// Complete curve result of an earlier construction-history feature.
2765    Feature(FeatureId),
2766    /// Curves in an intermediate regenerated feature result, paired with the
2767    /// format-native persistent reference required for rewrite.
2768    Generated {
2769        /// Persistent feature-local curve identities.
2770        curves: Vec<GeneratedCurveRef>,
2771        /// Format-native persistent profile reference.
2772        native: String,
2773    },
2774    /// Profile given directly as a set of solved B-rep faces.
2775    Faces(Vec<FaceId>),
2776}
2777
2778/// One ordered cross-section consumed by a loft operation.
2779#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2780#[serde(untagged)]
2781pub enum LoftSection {
2782    /// Planar or face-backed section profile.
2783    Profile(ProfileRef),
2784    /// Point-like terminal section.
2785    Point(LoftPointSection),
2786}
2787
2788/// Point-like cross-section consumed by a loft operation.
2789#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2790#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
2791pub enum LoftPointSection {
2792    /// Source-native point-selection record whose position is not resolved.
2793    #[serde(rename = "native_point")]
2794    Native(String),
2795    /// Solved model-space point section.
2796    Point(Point3),
2797    /// Solved B-rep vertex section.
2798    Vertex(VertexId),
2799}
2800
2801/// Persistent identity of a curve in one regenerated feature result.
2802#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2803pub struct GeneratedCurveRef {
2804    /// Feature whose regenerated result owns the curve.
2805    pub feature: FeatureId,
2806    /// Complete ordered feature-local component identity.
2807    pub local_id: String,
2808}
2809
2810/// Trajectory consumed by a sweep or path-driven operation.
2811#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2812#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
2813pub enum PathRef {
2814    /// Source path exists but its neutral members remain unresolved.
2815    Unresolved(String),
2816    /// Opaque reference into a native path record.
2817    Native(String),
2818    /// Ordered geometry from a neutral sketch.
2819    Sketch(crate::sketches::SketchId),
2820    /// Source-native curve selection within a known neutral spatial sketch.
2821    SpatialSketchSelection {
2822        /// Spatial sketch containing the selected curves.
2823        sketch: crate::sketches::SpatialSketchId,
2824        /// Full-fidelity native selection records in path order.
2825        selections: Vec<String>,
2826    },
2827    /// Path resolved as ordered topological edges.
2828    Edges(Vec<EdgeId>),
2829    /// Path resolved as ordered geometric curves.
2830    Curves(Vec<CurveId>),
2831    /// Path resolved as ordered edges in the consuming feature's input topology.
2832    HistoricalEdges {
2833        /// Input topology containing every path edge.
2834        state: FeatureInputTopologyId,
2835        /// State-local edge identities in path order.
2836        edges: Vec<HistoricalEdgeId>,
2837        /// Full-fidelity source path selection.
2838        native: String,
2839    },
2840}
2841
2842/// Radius assignment along filleted edges.
2843#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2844#[serde(tag = "kind", rename_all = "snake_case")]
2845pub enum RadiusSpec {
2846    /// Radius law is retained but not fully resolved.
2847    Unresolved {
2848        /// Structural law form, when independently identified.
2849        #[serde(default, skip_serializing_if = "Option::is_none")]
2850        form: Option<RadiusForm>,
2851    },
2852    /// Same radius along the whole edge chain.
2853    Constant {
2854        /// The fillet radius.
2855        radius: Length,
2856    },
2857    /// Constant transverse chord length across the fillet surface.
2858    Chordal {
2859        /// Distance between the fillet's two support boundaries.
2860        chord_length: Length,
2861    },
2862    /// Radius varying along the edge chain per explicit control points.
2863    Variable {
2864        /// Radius samples along the edge chain, in chain-parameter order.
2865        points: Vec<VariableRadius>,
2866    },
2867}
2868
2869/// One independently dimensioned group of filleted edges.
2870#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2871pub struct FilletGroup {
2872    /// Edges sharing this radius law.
2873    pub edges: EdgeSelection,
2874    /// Radius assignment along the edges.
2875    pub radius: RadiusSpec,
2876    /// Dimensionless tangency weight, when specified.
2877    #[serde(default, skip_serializing_if = "Option::is_none")]
2878    pub tangency_weight: Option<f64>,
2879}
2880
2881/// One independently dimensioned group of chamfered edges.
2882#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
2883pub struct ChamferGroup {
2884    /// Edges sharing this dimensional specification.
2885    pub edges: EdgeSelection,
2886    /// Dimensional definition applied to the edges.
2887    pub spec: ChamferSpec,
2888}
2889
2890/// Structural form of a fillet radius law.
2891#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2892#[serde(rename_all = "snake_case")]
2893pub enum RadiusForm {
2894    /// One radius applies to the entire edge chain.
2895    Constant,
2896    /// Constant transverse chord length defines the fillet width.
2897    Chordal,
2898    /// Radius varies along the edge chain.
2899    Variable,
2900}
2901
2902/// Radius at a normalized position along a filleted edge chain.
2903#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
2904pub struct VariableRadius {
2905    /// Position in `[0, 1]` along the edge chain.
2906    pub parameter: f64,
2907    /// Fillet radius at this position.
2908    pub radius: Length,
2909}
2910
2911/// Dimensional definition of an edge chamfer.
2912#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
2913#[serde(tag = "kind", rename_all = "snake_case")]
2914pub enum ChamferSpec {
2915    /// Dimensional specification is retained but not fully resolved.
2916    Unresolved {
2917        /// Structural specification form, when independently identified.
2918        #[serde(default, skip_serializing_if = "Option::is_none")]
2919        form: Option<ChamferForm>,
2920    },
2921    /// Equal setback distance on both faces meeting the edge.
2922    Distance {
2923        /// Setback distance from the edge.
2924        distance: Length,
2925    },
2926    /// Independent setback distances on each face meeting the edge.
2927    TwoDistances {
2928        /// Setback distance on the first face.
2929        first: Length,
2930        /// Setback distance on the second face.
2931        second: Length,
2932    },
2933    /// A setback distance on one face plus an angle from it to the other.
2934    DistanceAngle {
2935        /// Setback distance on the reference face.
2936        distance: Length,
2937        /// Chamfer angle measured from the reference face.
2938        angle: Angle,
2939    },
2940}
2941
2942/// Structural form of a chamfer dimensional specification.
2943#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2944#[serde(rename_all = "snake_case")]
2945pub enum ChamferForm {
2946    /// One equal setback distance.
2947    Distance,
2948    /// Independent setback distances on each adjacent face.
2949    TwoDistances,
2950    /// One setback distance and one angle.
2951    DistanceAngle,
2952}
2953
2954/// Structural drilling, entry-treatment, and threading form of a hole.
2955#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
2956#[serde(tag = "kind", rename_all = "snake_case")]
2957pub enum HoleKind {
2958    /// Entry treatment fields whose complete form is unresolved.
2959    Unresolved {
2960        /// Entry-treatment family, when established.
2961        #[serde(default, skip_serializing_if = "Option::is_none")]
2962        form: Option<HoleForm>,
2963        /// Resolved counterbore diameter.
2964        #[serde(default, skip_serializing_if = "Option::is_none")]
2965        counterbore_diameter: Option<Length>,
2966        /// Resolved counterbore depth.
2967        #[serde(default, skip_serializing_if = "Option::is_none")]
2968        counterbore_depth: Option<Length>,
2969        /// Resolved countersink diameter.
2970        #[serde(default, skip_serializing_if = "Option::is_none")]
2971        countersink_diameter: Option<Length>,
2972        /// Resolved countersink included angle.
2973        #[serde(default, skip_serializing_if = "Option::is_none")]
2974        countersink_angle: Option<Angle>,
2975    },
2976    /// Plain cylindrical hole with no entry feature.
2977    Simple,
2978    /// Hole with a chamfered entry.
2979    Chamfer {
2980        /// Entry chamfer diameter.
2981        diameter: Length,
2982        /// Included chamfer angle.
2983        angle: Angle,
2984    },
2985    /// Plain cylindrical hole terminating in a conical drill point.
2986    SimpleDrilled {
2987        /// Included angle of the conical drill point.
2988        drill_point_angle: Angle,
2989    },
2990    /// Hole with a wider, flat-bottomed counterbore at the entry.
2991    Counterbore {
2992        /// Counterbore diameter, wider than the hole diameter.
2993        diameter: Length,
2994        /// Counterbore depth.
2995        depth: Length,
2996    },
2997    /// Counterbored hole terminating in a conical drill point.
2998    CounterboreDrilled {
2999        /// Counterbore diameter, wider than the hole diameter.
3000        diameter: Length,
3001        /// Axial depth of the counterbore.
3002        depth: Length,
3003        /// Included angle of the conical drill point.
3004        drill_point_angle: Angle,
3005    },
3006    /// Hole with a conical countersink at the entry.
3007    Countersink {
3008        /// Countersink diameter at the surface, wider than the hole diameter.
3009        diameter: Length,
3010        /// Countersink included angle.
3011        angle: Angle,
3012    },
3013    /// Internally threaded hole terminating in a conical drill point.
3014    Threaded {
3015        /// Nominal major diameter of the internal thread.
3016        major_diameter: Length,
3017        /// Axial length over which the thread is cut.
3018        thread_depth: Length,
3019        /// Thread pitch, when carried independently of the nominal designation.
3020        #[serde(default, skip_serializing_if = "Option::is_none")]
3021        pitch: Option<Length>,
3022        /// Included angle of the conical drill point.
3023        drill_point_angle: Angle,
3024    },
3025    /// Hole with a conical entry followed by a wider cylindrical recess.
3026    Counterdrill {
3027        /// Entry-recess diameter.
3028        diameter: Length,
3029        /// Cylindrical recess depth.
3030        depth: Length,
3031        /// Included conical entry angle.
3032        angle: Angle,
3033    },
3034}
3035
3036/// Profile geometry families accepted as hole-location generators.
3037#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
3038pub struct HoleProfileFilter {
3039    /// Profile points generate holes.
3040    pub points: bool,
3041    /// Profile circles generate holes at their centers.
3042    pub circles: bool,
3043    /// Profile circular arcs generate holes at their centers.
3044    pub arcs: bool,
3045}
3046
3047/// Blind-end construction of a drilled hole.
3048#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
3049#[serde(tag = "kind", rename_all = "snake_case")]
3050pub enum HoleBottom {
3051    /// Flat-bottomed cylindrical end.
3052    Flat,
3053    /// Conical drill point.
3054    Angled {
3055        /// Included drill-point angle.
3056        included_angle: Angle,
3057        /// Whether the declared blind depth reaches the tip instead of the shoulder.
3058        depth_to_tip: bool,
3059    },
3060}
3061
3062/// Standard sizing and optional physical-thread construction for a hole.
3063#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
3064pub struct HoleSpecification {
3065    /// Named thread or fastener standard family.
3066    pub standard: String,
3067    /// Nominal size designation within the standard.
3068    #[serde(default, skip_serializing_if = "Option::is_none")]
3069    pub designation: Option<String>,
3070    /// Tolerance or thread class.
3071    #[serde(default, skip_serializing_if = "Option::is_none")]
3072    pub class: Option<String>,
3073    /// Clearance-hole fit class when the hole is not threaded.
3074    #[serde(default, skip_serializing_if = "Option::is_none")]
3075    pub fit: Option<String>,
3076    /// Whether the hole is internally threaded rather than a clearance hole.
3077    pub threaded: bool,
3078    /// Whether exact helical thread geometry is modeled.
3079    pub modeled: bool,
3080    /// Whether cosmetic thread presentation is requested.
3081    pub cosmetic: bool,
3082    /// Thread pitch in canonical millimeters.
3083    #[serde(default, skip_serializing_if = "Option::is_none")]
3084    pub pitch: Option<Length>,
3085    /// Nominal major thread diameter.
3086    #[serde(default, skip_serializing_if = "Option::is_none")]
3087    pub major_diameter: Option<Length>,
3088    /// Thread handedness.
3089    pub hand: ThreadHand,
3090    /// Axial thread-depth construction.
3091    pub depth: HoleThreadDepth,
3092    /// Additional radial thread clearance used for modeled geometry.
3093    #[serde(default, skip_serializing_if = "Option::is_none")]
3094    pub clearance: Option<Length>,
3095}
3096
3097/// Thread handedness.
3098#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
3099#[serde(rename_all = "snake_case")]
3100pub enum ThreadHand {
3101    /// Right-hand thread.
3102    Right,
3103    /// Left-hand thread.
3104    Left,
3105}
3106
3107/// Axial extent rule for a hole thread.
3108#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
3109#[serde(tag = "kind", rename_all = "snake_case")]
3110pub enum HoleThreadDepth {
3111    /// Thread follows the complete hole depth.
3112    HoleDepth,
3113    /// Explicit thread length.
3114    Blind {
3115        /// Explicit axial thread length.
3116        depth: Length,
3117    },
3118    /// Standard tapped-hole runout is subtracted from the hole depth.
3119    TappedStandard,
3120}
3121
3122/// Structural form of a hole entry treatment.
3123#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
3124#[serde(rename_all = "snake_case")]
3125pub enum HoleForm {
3126    /// Chamfered entry.
3127    Chamfer,
3128    /// Wider, flat-bottomed entry.
3129    Counterbore,
3130    /// Conical entry followed by a cylindrical recess.
3131    Counterdrill,
3132    /// Conical entry.
3133    Countersink,
3134}
3135
3136/// Deformation applied by a flex feature.
3137#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
3138#[serde(tag = "kind", rename_all = "snake_case")]
3139pub enum FlexMode {
3140    /// Mode fields whose complete deformation is unresolved.
3141    Unresolved {
3142        /// Deformation family, when established.
3143        #[serde(default, skip_serializing_if = "Option::is_none")]
3144        form: Option<FlexForm>,
3145        /// Resolved bending or twisting magnitude.
3146        #[serde(default, skip_serializing_if = "Option::is_none")]
3147        angle: Option<Angle>,
3148        /// Resolved taper factor.
3149        #[serde(default, skip_serializing_if = "Option::is_none")]
3150        factor: Option<f64>,
3151        /// Resolved stretching distance.
3152        #[serde(default, skip_serializing_if = "Option::is_none")]
3153        distance: Option<Length>,
3154    },
3155    /// Bend through a signed angle.
3156    Bending {
3157        /// Total bend angle.
3158        angle: Angle,
3159    },
3160    /// Twist through a signed angle.
3161    Twisting {
3162        /// Total twist angle.
3163        angle: Angle,
3164    },
3165    /// Scale transverse sections by a dimensionless factor.
3166    Tapering {
3167        /// End-to-start transverse scale ratio.
3168        factor: f64,
3169    },
3170    /// Extend or contract along the flex axis.
3171    Stretching {
3172        /// Signed change in length.
3173        distance: Length,
3174    },
3175}
3176
3177/// Structural form of a flex deformation.
3178#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
3179#[serde(rename_all = "snake_case")]
3180pub enum FlexForm {
3181    /// Angular bending.
3182    Bending,
3183    /// Angular twisting.
3184    Twisting,
3185    /// Transverse tapering.
3186    Tapering,
3187    /// Axial stretching.
3188    Stretching,
3189}
3190
3191/// Spatial transform used to repeat or reflect seed features.
3192#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
3193#[serde(tag = "kind", rename_all = "snake_case")]
3194pub enum PatternKind {
3195    /// Pattern construction whose form or required operands are unresolved.
3196    Unresolved {
3197        /// Native pattern form, when identified independently of its operands.
3198        #[serde(default, skip_serializing_if = "Option::is_none")]
3199        form: Option<PatternForm>,
3200    },
3201    /// Repeats seeds evenly along a straight direction.
3202    Linear {
3203        /// Repetition direction, when resolved.
3204        #[serde(default, skip_serializing_if = "Option::is_none")]
3205        direction: Option<Vector3>,
3206        /// Distance between consecutive instances.
3207        spacing: Length,
3208        /// Total number of instances, including the original.
3209        count: u32,
3210        /// Optional complete second translation direction.
3211        #[serde(default, skip_serializing_if = "Option::is_none")]
3212        second: Option<LinearPatternDirection>,
3213    },
3214    /// Repeats seeds at explicitly located distances along a straight direction.
3215    LinearOffsets {
3216        /// Repetition direction, when resolved.
3217        #[serde(default, skip_serializing_if = "Option::is_none")]
3218        direction: Option<Vector3>,
3219        /// Cumulative distances from the original instance, beginning with zero.
3220        offsets: Vec<Length>,
3221    },
3222    /// Repeats seeds evenly around an axis.
3223    Circular {
3224        /// A point on the pattern axis.
3225        axis_origin: Point3,
3226        /// Unit direction of the pattern axis.
3227        axis_dir: Vector3,
3228        /// Angular span covered by the pattern.
3229        angle: Angle,
3230        /// Total number of instances, including the original.
3231        count: u32,
3232    },
3233    /// Repeats seeds at explicitly located angles around an axis.
3234    CircularAngles {
3235        /// A point on the pattern axis.
3236        axis_origin: Point3,
3237        /// Unit direction of the pattern axis.
3238        axis_dir: Vector3,
3239        /// Cumulative angles from the original instance, beginning with zero.
3240        angles: Vec<Angle>,
3241    },
3242    /// Repeats seeds at fixed arc-length spacing along a curve.
3243    CurveDriven {
3244        /// Pattern path, when its native reference is available.
3245        #[serde(default, skip_serializing_if = "Option::is_none")]
3246        path: Option<PathRef>,
3247        /// Arc-length spacing between consecutive instances.
3248        spacing: Length,
3249        /// Total number of instances, including the original.
3250        count: u32,
3251    },
3252    /// Reflects seeds across a plane.
3253    Mirror {
3254        /// A point on the mirror plane.
3255        plane_origin: Point3,
3256        /// Unit normal of the mirror plane.
3257        plane_normal: Vector3,
3258    },
3259    /// Repeats seeds using progressive uniform scales.
3260    Scale {
3261        /// Fixed locus used by every scale transform.
3262        center: PatternScaleCenter,
3263        /// Scale factor of the final instance relative to the original.
3264        final_factor: f64,
3265        /// Total number of instances, including the original.
3266        count: u32,
3267    },
3268    /// Applies an ordered sequence of pattern stages.
3269    Composite {
3270        /// Stages in application order.
3271        stages: Vec<PatternStage>,
3272    },
3273}
3274
3275/// Fixed locus for a progressive pattern scale.
3276#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
3277#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
3278pub enum PatternScaleCenter {
3279    /// Volume centroid of the first seed feature.
3280    FirstSeedCentroid,
3281    /// Explicit model-space point.
3282    Point(Point3),
3283    /// Format-native center reference.
3284    Native(String),
3285}
3286
3287/// One stage of an ordered composite pattern.
3288#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
3289pub struct PatternStage {
3290    /// Pattern transform sequence contributed by this stage.
3291    pub pattern: Box<PatternKind>,
3292    /// Rule used to combine this stage with preceding stages.
3293    pub combination: PatternStageCombination,
3294}
3295
3296/// Combination rule for a composite-pattern stage.
3297#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
3298#[serde(rename_all = "snake_case")]
3299pub enum PatternStageCombination {
3300    /// Establishes the initial transform sequence.
3301    Initialize,
3302    /// Applies each new transform to every preceding transform.
3303    CartesianProduct,
3304    /// Aligns transforms with equally sized slices of preceding occurrences.
3305    AlignedSlices,
3306}
3307
3308/// Complete secondary direction of a two-direction linear pattern.
3309#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
3310pub struct LinearPatternDirection {
3311    /// Unit translation direction.
3312    pub direction: Vector3,
3313    /// Distance between consecutive instances.
3314    pub spacing: Length,
3315    /// Total number of instances, including the original.
3316    pub count: u32,
3317}
3318
3319/// Structural form of a repeated or reflected feature operation.
3320#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
3321#[serde(rename_all = "snake_case")]
3322pub enum PatternForm {
3323    /// Translation along a straight direction.
3324    Linear,
3325    /// Rotation around an axis.
3326    Circular,
3327    /// Translation along a curve.
3328    CurveDriven,
3329    /// Reflection across a plane.
3330    Mirror,
3331    /// Progressive uniform scaling.
3332    Scale,
3333    /// Ordered composition of multiple pattern forms.
3334    Composite,
3335}