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}