axiolid_profile/
center_line.rs

1//! Centre-line profiles: an open path plus a constant width.
2
3use axiolid_core::Scalar;
4
5use crate::contour::Contour;
6
7/// An open path swept by a constant width, centred on the path.
8///
9/// This is a *representation*, not a contour: the closed area it denotes is
10/// the set of points within `half_width` of the path. Resolving it into a
11/// boundary is an offsetting algorithm, which belongs above this tier.
12///
13/// Keeping it exact matters because offsetting is lossy in a way that cannot
14/// be undone. Once a centre line is flattened into an outline, the fact that
15/// it *was* a constant-width path is gone, and with it the ability to change
16/// the width, re-offset at a different tolerance, or round-trip the source
17/// unchanged. Sheet metal, curtain-wall mullions and pipe centre lines are all
18/// authored this way.
19///
20/// The path is held as a [`Contour`] because a centre line is a sequence of
21/// bounded curve segments exactly like a boundary is; the difference is that
22/// this one is not closed and does not enclose anything on its own.
23#[derive(Debug, Clone, PartialEq)]
24pub struct CenterLineProfile {
25    /// Open path the width is measured from.
26    pub path: Contour,
27    /// Half the constant width, measured perpendicular to the path.
28    ///
29    /// Stored as a half-width rather than a full thickness so the two offset
30    /// sides are symmetric by construction: a consumer offsets by `+half` and
31    /// `-half` without having to remember to halve anything. Halving at the
32    /// boundary is the kind of factor-of-two error that produces geometry
33    /// which looks plausible and is wrong.
34    pub half_width: Scalar,
35}
36
37impl CenterLineProfile {
38    /// Construct from an open path and the full width across it.
39    pub fn from_width(path: Contour, width: Scalar) -> Self {
40        Self {
41            path,
42            half_width: width / 2.0,
43        }
44    }
45
46    /// The full width across the path.
47    pub fn width(&self) -> Scalar {
48        self.half_width * 2.0
49    }
50}