axiolid_model/surface_relation.rs
1//! Surface relationships that compose curve and surface nodes.
2
3use axiolid_core::{Interval, Point3, Scalar, Vec3};
4
5use crate::{NodeId, SectionAtStation, StationFrame, StationedOpenSection};
6
7/// One side of a ruled surface (#402).
8///
9/// A side is read over `u` in `[0, 1]`; the two sides of a
10/// [`SurfaceRelation::Ruled`] are joined at equal `u`.
11#[non_exhaustive]
12#[derive(Debug, Clone, Copy, PartialEq)]
13#[cfg_attr(
14 feature = "serde",
15 derive(serde::Serialize, serde::Deserialize),
16 serde(deny_unknown_fields)
17)]
18pub enum RuledSide {
19 /// An atomic `Curve3` node run over `interval`, oriented (a reversed
20 /// interval is allowed): `u` reads the curve at
21 /// `interval.start + u (interval.end - interval.start)`.
22 Curve { curve: NodeId, interval: Interval },
23 /// Every ruling ends at this point.
24 Apex(Point3),
25}
26
27/// Relationship between surface nodes.
28#[non_exhaustive]
29#[derive(Debug, Clone, PartialEq)]
30#[cfg_attr(
31 feature = "serde",
32 derive(serde::Serialize, serde::Deserialize),
33 serde(deny_unknown_fields)
34)]
35pub enum SurfaceRelation {
36 /// Surface bounded by oriented curve nodes.
37 CurveBounded {
38 basis: NodeId,
39 boundaries: Vec<NodeId>,
40 implicit_outer: bool,
41 },
42 /// Rectangular parameter-space trim.
43 RectangularTrimmed {
44 basis: NodeId,
45 u: (Scalar, Scalar),
46 v: (Scalar, Scalar),
47 u_sense: bool,
48 v_sense: bool,
49 },
50 /// Constant-distance offset surface.
51 Offset {
52 basis: NodeId,
53 distance: Scalar,
54 self_intersect: Option<bool>,
55 },
56 /// Surface generated by translating a curve: `S(u, v) = c(u) + v d`,
57 /// `u` the curve's own parameter and `v` in units of `direction`'s
58 /// length (it is not normalised).
59 LinearExtrusion {
60 swept_curve: NodeId,
61 direction: Vec3,
62 },
63 /// Surface generated by revolving a curve around an axis:
64 /// `S(u, v) = O + R(u) (c(v) - O)`, `O` the `axis_origin`, `c` the swept
65 /// curve and `R(u)` the right-handed turn by `u` radians about the unit
66 /// `axis_direction` (its length is not read). `u = 0` is the swept curve
67 /// itself and `v` is the curve's own parameter.
68 Revolution {
69 swept_curve: NodeId,
70 axis_origin: axiolid_core::Point3,
71 axis_direction: Vec3,
72 },
73 /// Open sections standing at stations along a directrix, joined by tag
74 /// (#241).
75 ///
76 /// Each section's open polyline is placed in the directrix's `frame` at
77 /// its station (profile `x` to the lateral axis, `y` to up; see
78 /// [`crate::station`]). Between two stations, points with equal tags
79 /// are joined and interpolated linearly in distance, as are the
80 /// offsets. At least two sections, strictly increasing in distance,
81 /// with one tag sequence (or none) are required.
82 SectionedSurface {
83 directrix: NodeId,
84 sections: Vec<StationedOpenSection>,
85 frame: StationFrame,
86 },
87 /// Open sections standing at stations along a directrix, each
88 /// optionally explicitly oriented (#246): the general form of
89 /// [`SurfaceRelation::SectionedSurface`].
90 ///
91 /// Each section's open polyline is placed as in a `SectionedSurface`,
92 /// its plane turned by the section's orientation. Sections are joined
93 /// by tag, or by index when none is tagged (see [`crate::station`]).
94 /// At least two sections, strictly increasing in distance, all tagged
95 /// or none, are required.
96 OpenSectionsAtStations {
97 directrix: NodeId,
98 sections: Vec<SectionAtStation>,
99 frame: StationFrame,
100 },
101 /// Surface ruled between two sides (#402):
102 /// `p(u, v) = (1 - v) first(u) + v second(u)` for `u`, `v` in `[0, 1]`.
103 ///
104 /// The sides are atomic `Curve3` nodes over oriented intervals, or an
105 /// apex point (not both). Two sides that are the same curve over the
106 /// same interval would give no surface and are refused.
107 Ruled { first: RuledSide, second: RuledSide },
108}
109
110impl SurfaceRelation {
111 pub(crate) fn references(&self, out: &mut Vec<NodeId>) {
112 match self {
113 Self::CurveBounded {
114 basis, boundaries, ..
115 } => {
116 out.push(*basis);
117 out.extend(boundaries.iter().copied());
118 }
119 Self::RectangularTrimmed { basis, .. } | Self::Offset { basis, .. } => out.push(*basis),
120 Self::LinearExtrusion { swept_curve, .. } | Self::Revolution { swept_curve, .. } => {
121 out.push(*swept_curve)
122 }
123 Self::SectionedSurface {
124 directrix,
125 sections,
126 ..
127 } => {
128 out.push(*directrix);
129 out.extend(sections.iter().map(|section| section.profile));
130 }
131 Self::OpenSectionsAtStations {
132 directrix,
133 sections,
134 ..
135 } => {
136 out.push(*directrix);
137 out.extend(sections.iter().map(|section| section.profile));
138 }
139 Self::Ruled { first, second } => {
140 for side in [first, second] {
141 if let RuledSide::Curve { curve, .. } = side {
142 out.push(*curve);
143 }
144 }
145 }
146 }
147 }
148}