axiolid_curve_evaluate_contract/
measure.rs

1//! How a caller's number locates a place on a curve.
2
3use axiolid_core::Scalar;
4
5/// Where along a curve to evaluate, and by WHICH method of measurement.
6///
7/// Two different quantities are routinely written into exchange files for
8/// the same purpose, and they are not interchangeable:
9///
10/// - a LENGTH from the start of the curve, and
11/// - a dimensionless native PARAMETER, which is not a distance at all.
12///
13/// IFC4x3 makes the choice explicit in the file
14/// (`IfcCurveMeasureSelect`, either `IfcNonNegativeLengthMeasure` or
15/// `IfcParameterValue`), and STEP carries the same distinction. A
16/// consumer maps the authored value to this enum ONCE and passes it
17/// through; it never has to decide which evaluator to call.
18///
19/// Why this is a value and not two method names: with separate
20/// `*_at_parameter` methods the caller still branches, and a caller that
21/// branches can branch wrongly. Passing `1.5` as a distance to a circle
22/// authored in radians places an object 1.5 m along a curve where the
23/// author meant about 86 degrees around it -- wrong, finite, and
24/// plausible, which is the worst combination. Carrying the method of
25/// measurement in the value makes that mistake unrepresentable.
26#[non_exhaustive]
27#[derive(Debug, Clone, Copy, PartialEq)]
28pub enum CurveMeasure {
29    /// A length from the start of the curve.
30    ///
31    /// Interpreted in the provider's
32    /// [`DistanceConvention`](crate::DistanceConvention) for that curve,
33    /// which is why the convention must be consulted before trusting it.
34    Distance(Scalar),
35    /// The curve's own parameter: an angle for a circle, a knot value for
36    /// a spline, arc length for an intrinsic curve.
37    ///
38    /// Meaningful for every curve family, including those whose arc length
39    /// has no closed form, so this route stays available where
40    /// [`Distance`](Self::Distance) is refused.
41    Parameter(Scalar),
42}
43
44impl CurveMeasure {
45    /// The carried number, whichever method of measurement it uses.
46    ///
47    /// For validity checks such as finiteness that apply to both.
48    #[must_use]
49    pub fn value(self) -> Scalar {
50        match self {
51            Self::Distance(v) | Self::Parameter(v) => v,
52        }
53    }
54
55    /// Whether this is a length rather than a native parameter.
56    #[must_use]
57    pub fn is_distance(self) -> bool {
58        matches!(self, Self::Distance(_))
59    }
60}