axiolid_model/curve_relation.rs
1//! Curve relationships that require graph references.
2
3use axiolid_core::{Point2, Point3, Scalar, Vec3};
4
5use crate::NodeId;
6
7/// One trim selector preserved from a source representation.
8#[non_exhaustive]
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub enum TrimSelector {
11 /// Curve parameter.
12 Parameter(Scalar),
13 /// Two-dimensional point.
14 Point2(Point2),
15 /// Three-dimensional point.
16 Point3(Point3),
17}
18
19/// Preference when both parameter and Cartesian trim selectors exist.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub enum TrimmingPreference {
22 /// Prefer parameter values.
23 Parameter,
24 /// Prefer Cartesian points.
25 Cartesian,
26 /// Use source order when no preference was stated.
27 Unspecified,
28}
29
30/// Continuity declared between consecutive composite segments.
31#[non_exhaustive]
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33pub enum Transition {
34 /// Discontinuous.
35 Discontinuous,
36 /// Position continuous.
37 Continuous,
38 /// Position and tangent continuous.
39 ContinuousSameGradient,
40 /// Position, tangent, and curvature continuous.
41 ContinuousSameGradientSameCurvature,
42}
43
44/// One oriented curve in a composite.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub struct CurveSegment {
47 /// Child curve.
48 pub curve: NodeId,
49 /// Whether child parameterization agrees with composite orientation.
50 pub same_sense: bool,
51 /// Transition from the preceding segment.
52 pub transition: Transition,
53}
54
55/// Relationship between curve nodes.
56#[non_exhaustive]
57#[derive(Debug, Clone, PartialEq)]
58pub enum CurveRelation {
59 /// Ordered composite curve.
60 Composite { segments: Vec<CurveSegment> },
61 /// Trimmed view of a basis curve.
62 Trimmed {
63 basis: NodeId,
64 start: Vec<TrimSelector>,
65 end: Vec<TrimSelector>,
66 sense_agreement: bool,
67 preference: TrimmingPreference,
68 },
69 /// Constant-distance offset.
70 Offset {
71 basis: NodeId,
72 distance: Scalar,
73 reference_direction: Option<Vec3>,
74 },
75 /// Three-dimensional curve associated with one or more surfaces/pcurves.
76 SurfaceCurve {
77 curve_3d: NodeId,
78 /// The parametric sides, each pairing a surface with its own p-curve.
79 sides: SurfaceSides,
80 master: MasterRepresentation,
81 },
82 /// Two-dimensional parameter curve on a surface.
83 ParameterCurve {
84 basis_surface: NodeId,
85 reference_curve: NodeId,
86 },
87}
88
89/// Which representation governs a redundant surface-curve definition.
90#[non_exhaustive]
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
92pub enum MasterRepresentation {
93 /// Three-dimensional curve.
94 Curve3d,
95 /// The p-curve on the FIRST parametric side governs.
96 ParameterCurveS1,
97 /// The p-curve on the SECOND parametric side governs.
98 ///
99 /// Only meaningful on a two-sided curve; naming it on a single-sided one
100 /// is contradictory rather than merely unusual, and is refused.
101 ParameterCurveS2,
102 /// Both are authoritative and must agree.
103 Both,
104 /// Unspecified.
105 Unspecified,
106}
107
108/// Which parametric side of a surface curve a p-curve belongs to.
109///
110/// A surface curve is the intersection of two surfaces, so each side owns one
111/// surface and the p-curve that is that curve's image in the surface's own
112/// parameter domain. Pairing them here is what stops a consumer from having to
113/// guess which p-curve to trim with -- a guess that would otherwise require
114/// re-inverting the surface, the exact operation the p-curve exists to avoid.
115///
116/// A single side is legitimate: not every edge has two parametric images.
117#[derive(Debug, Clone, PartialEq, Eq, Hash)]
118pub struct SurfaceSides {
119 first: (NodeId, NodeId),
120 second: Option<(NodeId, NodeId)>,
121}
122
123impl SurfaceSides {
124 /// One parametric side: a surface and this curve's image in it.
125 #[must_use]
126 pub const fn one(surface: NodeId, pcurve: NodeId) -> Self {
127 Self {
128 first: (surface, pcurve),
129 second: None,
130 }
131 }
132
133 /// Both parametric sides, in the order the authoring format states them.
134 ///
135 /// The order is load-bearing: it is what `ParameterCurveS1` and
136 /// `ParameterCurveS2` name.
137 #[must_use]
138 pub const fn two(
139 first_surface: NodeId,
140 first_pcurve: NodeId,
141 second_surface: NodeId,
142 second_pcurve: NodeId,
143 ) -> Self {
144 Self {
145 first: (first_surface, first_pcurve),
146 second: Some((second_surface, second_pcurve)),
147 }
148 }
149
150 /// The first side as `(surface, pcurve)`.
151 #[must_use]
152 pub const fn first(&self) -> (NodeId, NodeId) {
153 self.first
154 }
155
156 /// The second side as `(surface, pcurve)`, if this curve has one.
157 #[must_use]
158 pub const fn second(&self) -> Option<(NodeId, NodeId)> {
159 self.second
160 }
161
162 /// Whether both parametric sides are present.
163 #[must_use]
164 pub const fn is_two_sided(&self) -> bool {
165 self.second.is_some()
166 }
167
168 /// Every node this pairing references, surfaces and p-curves alike.
169 #[must_use]
170 pub fn references(&self) -> Vec<NodeId> {
171 let mut out = vec![self.first.0, self.first.1];
172 if let Some((surface, pcurve)) = self.second {
173 out.extend([surface, pcurve]);
174 }
175 out
176 }
177}
178
179impl CurveRelation {
180 pub(crate) fn references(&self, out: &mut Vec<NodeId>) {
181 match self {
182 Self::Composite { segments } => out.extend(segments.iter().map(|item| item.curve)),
183 Self::Trimmed { basis, .. } | Self::Offset { basis, .. } => out.push(*basis),
184 Self::SurfaceCurve {
185 curve_3d, sides, ..
186 } => {
187 out.push(*curve_3d);
188 out.extend(sides.references());
189 }
190 Self::ParameterCurve {
191 basis_surface,
192 reference_curve,
193 } => out.extend([*basis_surface, *reference_curve]),
194 }
195 }
196}