Coverage Report

Created: 2026-05-24 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/kurbo-0.13.1/src/quadspline.rs
Line
Count
Source
1
// Copyright 2021 the Kurbo Authors
2
// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4
//! Quadratic Bézier splines.
5
use crate::Point;
6
7
use crate::QuadBez;
8
use alloc::vec::Vec;
9
10
/// A quadratic Bézier spline in [B-spline](https://en.wikipedia.org/wiki/B-spline) format.
11
#[derive(Clone, Debug, PartialEq)]
12
pub struct QuadSpline(Vec<Point>);
13
14
impl QuadSpline {
15
    /// Construct a new `QuadSpline` from an array of [`Point`]s.
16
    #[inline(always)]
17
0
    pub fn new(points: Vec<Point>) -> Self {
18
0
        Self(points)
19
0
    }
20
21
    /// Return the spline's control [`Point`]s.
22
    #[inline(always)]
23
0
    pub fn points(&self) -> &[Point] {
24
0
        &self.0
25
0
    }
26
27
    /// Return an iterator over the implied [`QuadBez`] sequence.
28
    ///
29
    /// The returned quads are guaranteed to be G1 continuous.
30
    #[inline(always)]
31
0
    pub fn to_quads(&self) -> impl Iterator<Item = QuadBez> + '_ {
32
0
        ToQuadBez {
33
0
            idx: 0,
34
0
            points: &self.0,
35
0
        }
36
0
    }
37
}
38
39
struct ToQuadBez<'a> {
40
    idx: usize,
41
    points: &'a Vec<Point>,
42
}
43
44
impl Iterator for ToQuadBez<'_> {
45
    type Item = QuadBez;
46
47
0
    fn next(&mut self) -> Option<Self::Item> {
48
0
        let [mut p0, p1, mut p2]: [Point; 3] =
49
0
            self.points.get(self.idx..=self.idx + 2)?.try_into().ok()?;
50
51
0
        if self.idx != 0 {
52
0
            p0 = p0.midpoint(p1);
53
0
        }
54
0
        if self.idx + 2 < self.points.len() - 1 {
55
0
            p2 = p1.midpoint(p2);
56
0
        }
57
58
0
        self.idx += 1;
59
60
0
        Some(QuadBez { p0, p1, p2 })
61
0
    }
62
}
63
64
#[cfg(test)]
65
mod tests {
66
    use crate::{Point, QuadBez, QuadSpline};
67
68
    #[test]
69
    fn no_points_no_quads() {
70
        assert!(QuadSpline::new(Vec::new()).to_quads().next().is_none());
71
    }
72
73
    #[test]
74
    fn one_point_no_quads() {
75
        assert!(
76
            QuadSpline::new(vec![Point::new(1.0, 1.0)])
77
                .to_quads()
78
                .next()
79
                .is_none()
80
        );
81
    }
82
83
    #[test]
84
    fn two_points_no_quads() {
85
        assert!(
86
            QuadSpline::new(vec![Point::new(1.0, 1.0), Point::new(1.0, 1.0)])
87
                .to_quads()
88
                .next()
89
                .is_none()
90
        );
91
    }
92
93
    #[test]
94
    fn three_points_same_quad() {
95
        let p0 = Point::new(1.0, 1.0);
96
        let p1 = Point::new(2.0, 2.0);
97
        let p2 = Point::new(3.0, 3.0);
98
        assert_eq!(
99
            vec![QuadBez { p0, p1, p2 }],
100
            QuadSpline::new(vec![p0, p1, p2])
101
                .to_quads()
102
                .collect::<Vec<_>>()
103
        );
104
    }
105
106
    #[test]
107
    fn four_points_implicit_on_curve() {
108
        let p0 = Point::new(1.0, 1.0);
109
        let p1 = Point::new(3.0, 3.0);
110
        let p2 = Point::new(5.0, 5.0);
111
        let p3 = Point::new(8.0, 8.0);
112
        assert_eq!(
113
            vec![
114
                QuadBez {
115
                    p0,
116
                    p1,
117
                    p2: p1.midpoint(p2)
118
                },
119
                QuadBez {
120
                    p0: p1.midpoint(p2),
121
                    p1: p2,
122
                    p2: p3
123
                }
124
            ],
125
            QuadSpline::new(vec![p0, p1, p2, p3])
126
                .to_quads()
127
                .collect::<Vec<_>>()
128
        );
129
    }
130
}