curves_rs/
b_spline_curve.rs1use 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}