path_traits/transform/offset.rs
1//! Offset a path by a constant distance.
2//!
3//! [`Offset`] wraps a path and displaces every sampled point by a fixed
4//! distance. Requires the inner path to implement
5//! [`Tangent`](crate::Tangent) and [`Heading`](crate::Heading).
6
7use crate::{Heading, Path, Point, Tangent};
8
9/// A path displaced by a constant distance from the original.
10///
11/// Every sampled point is shifted by `distance` units. The offset direction
12/// is derived from the path's heading; in 2D this produces a parallel curve.
13///
14/// **Note:** The current implementation offsets along the tangent direction as
15/// a placeholder. A proper 2D-specific implementation should offset along the
16/// surface normal (heading + π/2).
17pub struct Offset<P, S> {
18 /// The inner path being offset.
19 inner: P,
20 /// The constant offset distance.
21 distance: S,
22}
23
24impl<P, S> Offset<P, S> {
25 /// Create a new offset path.
26 ///
27 /// The `distance` is in the same units as the path's scalar type.
28 pub fn new(inner: P, distance: S) -> Self {
29 Self { inner, distance }
30 }
31}
32
33impl<P: Path + Tangent + Heading> Path for Offset<P, P::Scalar> {
34 type Scalar = P::Scalar;
35 type Point = P::Point;
36 type Error = P::Error;
37
38 fn length(&self) -> Self::Scalar {
39 self.inner.length()
40 }
41
42 fn sample_at(&self, s: Self::Scalar) -> Result<Self::Point, Self::Error> {
43 let pos = self.inner.sample_at(s)?;
44 let tan = self.inner.tangent_at(s)?;
45 let offset_vec = tan * self.distance;
46 Ok(pos.translate(offset_vec))
47 }
48}