Skip to main content

path_traits/transform/
concat.rs

1//! Join two paths end-to-end.
2//!
3//! [`Concat`] connects two compatible paths into a single continuous path.
4//! The combined length is the sum of the inner lengths, and sampling delegates
5//! to the appropriate segment. When both inner paths are
6//! [`SegmentedPath`](crate::SegmentedPath), the concatenation is itself a
7//! [`SegmentedPath`](crate::SegmentedPath).
8
9use crate::{Path, PathSegment, SegmentedPath};
10
11/// Two paths joined end-to-end into a single continuous path.
12///
13/// The combined path has length `first.length() + second.length()`. Sampling
14/// below `first.length()` delegates to the first path; the remainder delegates
15/// to the second with a shifted arc-length.
16pub struct Concat<A, B> {
17    /// The first (prefix) path.
18    first: A,
19    /// The second (suffix) path.
20    second: B,
21}
22
23impl<A, B> Concat<A, B> {
24    /// Create a new concatenated path.
25    pub fn new(first: A, second: B) -> Self {
26        Self { first, second }
27    }
28}
29
30impl<A: Path, B: Path<Scalar = A::Scalar, Point = A::Point, Error = A::Error>> Path
31    for Concat<A, B>
32{
33    type Scalar = A::Scalar;
34    type Point = A::Point;
35    type Error = A::Error;
36
37    fn length(&self) -> Self::Scalar {
38        self.first.length() + self.second.length()
39    }
40
41    fn sample_at(&self, s: Self::Scalar) -> Result<Self::Point, Self::Error> {
42        let first_len = self.first.length();
43        if s <= first_len {
44            self.first.sample_at(s)
45        } else {
46            self.second.sample_at(s - first_len)
47        }
48    }
49}
50
51impl<A: SegmentedPath, B: SegmentedPath<Segment = A::Segment>> SegmentedPath for Concat<A, B>
52where
53    A: Path,
54    B: Path<Scalar = A::Scalar, Point = A::Point, Error = A::Error>,
55    A::Segment: PathSegment<Scalar = A::Scalar, Point = A::Point, Error = A::Error>,
56{
57    type Segment = A::Segment;
58
59    fn segment_count(&self) -> usize {
60        self.first.segment_count() + self.second.segment_count()
61    }
62
63    fn segments(&self) -> impl Iterator<Item = &Self::Segment> + '_ {
64        self.first.segments().chain(self.second.segments())
65    }
66
67    fn locate(&self, s: Self::Scalar) -> Result<(usize, Self::Scalar), Self::Error> {
68        let first_len = self.first.length();
69        if s <= first_len {
70            self.first.locate(s)
71        } else {
72            let (idx, local) = self.second.locate(s - first_len)?;
73            Ok((self.first.segment_count() + idx, local))
74        }
75    }
76}