curves_rs/
b_spline_curve.rs

1use serde::{Serialize, Deserialize};
2
3use crate::errors::{Error, check_condition};
4use crate::parametric_curve::ParametricCurve;
5use crate::point::Point;
6
7#[derive(Debug, Serialize, Deserialize)]
8pub struct BSplineCurve {
9    degree: u8,
10    control_points: Vec<Point>,
11    knots: Knots,
12}
13
14impl BSplineCurve {
15    pub fn new(degree: u8, control_points: Vec<Point>, knots: Knots) -> Self {
16        check_condition(degree > 0, Error::InvalidArguments, 
17            "degree must be 1 or more");
18        check_condition(!control_points.is_empty(), Error::InvalidArguments, 
19            "control_points can't be empty");
20        check_condition(knots.len() == control_points.len() + degree as usize + 1, Error::InvalidArguments, 
21            "knots length must be control_points_length + degree + 1");
22
23        BSplineCurve { degree, control_points, knots }
24    }
25
26    pub fn knots(&self) -> &Knots {
27        &self.knots
28    }
29
30    pub fn basis(knots: &Knots, degree: u8, index: usize, param: f32) -> f32 {
31        if degree == 0 {
32            return match knots[index] <= param && param < knots[index + 1] {
33                true => 1.0, 
34                false => 0.0,
35            };
36        }
37
38        let denom = knots[index + degree as usize] - knots[index];
39        let left = match denom == 0.0 {
40            true => 0.0, 
41            false => BSplineCurve::basis(knots, degree - 1, index, param) * (param - knots[index]) / denom
42        };
43
44        let denom = knots[index + degree as usize + 1] - knots[index + 1];
45        let right = match denom == 0.0 {
46            true => 0.0, 
47            false => BSplineCurve::basis(knots, degree - 1, index + 1, param) * (knots[index + degree as usize + 1] - param) / denom
48        };
49
50        left + right
51    }
52
53    pub fn basis_all(degree: u8, index: usize, knots: &Knots, quantity: u32) -> Vec<f32> {
54        let start = knots[0];
55        let end = knots[knots.len() - 1];
56        let interval = (end - start) / (quantity - 1) as f32;
57        let mut result = Vec::with_capacity(quantity as usize);
58        const ERROR: f32 = 0.000_01;
59
60        for i in 0..quantity {
61            let param = match i == (quantity - 1) {
62                true => end - ERROR, 
63                false => start + i as f32 * interval,
64            };
65
66            result.push(Self::basis(knots, degree, index, param));
67        }
68
69        result
70    }
71}
72
73impl ParametricCurve for BSplineCurve {
74    fn evaluate(&self, param: f32) -> crate::point::Point {
75        let cps = self.control_points();
76        let n = self.degree() as u8;
77        let mut x = 0.0;
78        let mut y = 0.0;
79
80        for (i, point) in cps.iter().enumerate() {
81          let w = Self::basis(self.knots(), n, i, param);
82          x += w * point.x();
83          y += w * point.y();
84        }
85
86        Point::new(x as f32, y as f32)
87    }
88
89    fn degree(&self) -> u8 {
90        self.degree
91    }
92
93    fn control_points(&self) -> &Vec<crate::point::Point> {
94        &self.control_points
95    }
96
97    fn domain(&self) -> std::ops::Range<f32> {
98        self.knots()[self.degree as usize]..self.knots()[self.control_points().len() as usize]
99    }
100}
101
102#[derive(Debug, Serialize, Deserialize)]
103pub struct Knots {
104    values: Vec<f32>,
105}
106
107impl Knots {
108    pub fn new(init_values: &[f32]) -> Self {
109        check_condition(!init_values.is_empty(), Error::InvalidArguments,
110            "init_values is empty");
111        check_condition(!init_values.is_empty(), Error::InvalidArguments,
112            "init_values must be ascending order");
113
114        Knots { values: init_values.to_vec() }
115    }
116
117    pub fn len(&self) -> usize {
118        self.values.len()
119    }
120
121    pub fn front(&self) -> f32 {
122        self.values[0]
123    }
124
125    pub fn back(&self) -> f32 {
126        self.values[self.len() - 1]
127    }
128}
129
130impl core::ops::Index<usize> for Knots {
131    type Output = f32;
132
133    fn index(&self, index: usize) -> &Self::Output {
134        &self.values[index]
135    }
136}