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}