Skip to main content

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}