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}