proof_engine/geometry/
bezier.rs1use glam::{Vec2, Vec3, Vec4};
4use super::{GeoMesh, parametric::ParametricSurface};
5
6#[derive(Debug, Clone, Copy)]
8pub struct ControlPoint {
9 pub position: Vec3,
10 pub weight: f32,
11}
12
13impl ControlPoint {
14 pub fn new(pos: Vec3) -> Self { Self { position: pos, weight: 1.0 } }
15 pub fn weighted(pos: Vec3, w: f32) -> Self { Self { position: pos, weight: w } }
16}
17
18#[derive(Debug, Clone)]
20pub struct BezierSurface {
21 pub control_points: [[ControlPoint; 4]; 4],
22}
23
24impl BezierSurface {
25 pub fn new(points: [[Vec3; 4]; 4]) -> Self {
26 let mut cp = [[ControlPoint::new(Vec3::ZERO); 4]; 4];
27 for i in 0..4 { for j in 0..4 { cp[i][j] = ControlPoint::new(points[i][j]); } }
28 Self { control_points: cp }
29 }
30
31 fn bernstein(t: f32) -> [f32; 4] {
32 let s = 1.0 - t;
33 [s * s * s, 3.0 * s * s * t, 3.0 * s * t * t, t * t * t]
34 }
35}
36
37impl ParametricSurface for BezierSurface {
38 fn evaluate(&self, u: f32, v: f32) -> Vec3 {
39 let bu = Self::bernstein(u);
40 let bv = Self::bernstein(v);
41 let mut pos = Vec3::ZERO;
42 for i in 0..4 {
43 for j in 0..4 {
44 pos += self.control_points[i][j].position * bu[i] * bv[j];
45 }
46 }
47 pos
48 }
49}
50
51#[derive(Debug, Clone)]
53pub struct NurbsSurface {
54 pub control_points: Vec<Vec<ControlPoint>>,
55 pub knots_u: Vec<f32>,
56 pub knots_v: Vec<f32>,
57 pub degree_u: usize,
58 pub degree_v: usize,
59}
60
61impl NurbsSurface {
62 pub fn new(
63 control_points: Vec<Vec<ControlPoint>>,
64 knots_u: Vec<f32>, knots_v: Vec<f32>,
65 degree_u: usize, degree_v: usize,
66 ) -> Self {
67 Self { control_points, knots_u, knots_v, degree_u, degree_v }
68 }
69
70 fn basis(knots: &[f32], i: usize, degree: usize, t: f32) -> f32 {
71 if degree == 0 {
72 return if t >= knots[i] && t < knots[i + 1] { 1.0 } else { 0.0 };
73 }
74 let mut result = 0.0;
75 let d1 = knots[i + degree] - knots[i];
76 if d1.abs() > 1e-10 {
77 result += (t - knots[i]) / d1 * Self::basis(knots, i, degree - 1, t);
78 }
79 let d2 = knots[i + degree + 1] - knots[i + 1];
80 if d2.abs() > 1e-10 {
81 result += (knots[i + degree + 1] - t) / d2 * Self::basis(knots, i + 1, degree - 1, t);
82 }
83 result
84 }
85}
86
87impl ParametricSurface for NurbsSurface {
88 fn evaluate(&self, u: f32, v: f32) -> Vec3 {
89 let rows = self.control_points.len();
90 let cols = if rows > 0 { self.control_points[0].len() } else { 0 };
91
92 let mut numerator = Vec3::ZERO;
93 let mut denominator = 0.0;
94
95 for i in 0..rows {
96 let bu = Self::basis(&self.knots_u, i, self.degree_u, u.clamp(0.001, 0.999));
97 for j in 0..cols {
98 let bv = Self::basis(&self.knots_v, j, self.degree_v, v.clamp(0.001, 0.999));
99 let cp = &self.control_points[i][j];
100 let w = bu * bv * cp.weight;
101 numerator += cp.position * w;
102 denominator += w;
103 }
104 }
105
106 if denominator.abs() > 1e-10 { numerator / denominator } else { Vec3::ZERO }
107 }
108}
109
110#[derive(Debug, Clone)]
112pub struct BezierCurve {
113 pub control_points: Vec<Vec3>,
114}
115
116impl BezierCurve {
117 pub fn new(points: Vec<Vec3>) -> Self { Self { control_points: points } }
118
119 pub fn evaluate(&self, t: f32) -> Vec3 {
121 let n = self.control_points.len();
122 if n == 0 { return Vec3::ZERO; }
123 let mut temp = self.control_points.clone();
124 for k in 1..n {
125 for i in 0..n - k {
126 temp[i] = temp[i] * (1.0 - t) + temp[i + 1] * t;
127 }
128 }
129 temp[0]
130 }
131
132 pub fn tangent(&self, t: f32) -> Vec3 {
134 let eps = 1e-4;
135 (self.evaluate((t + eps).min(1.0)) - self.evaluate((t - eps).max(0.0))).normalize_or_zero()
136 }
137
138 pub fn split(&self, t: f32) -> (BezierCurve, BezierCurve) {
140 let n = self.control_points.len();
141 let mut left = Vec::with_capacity(n);
142 let mut right = Vec::with_capacity(n);
143 let mut temp = self.control_points.clone();
144
145 left.push(temp[0]);
146 right.push(temp[n - 1]);
147
148 for k in 1..n {
149 for i in 0..n - k {
150 temp[i] = temp[i] * (1.0 - t) + temp[i + 1] * t;
151 }
152 left.push(temp[0]);
153 right.push(temp[n - 1 - k]);
154 }
155
156 right.reverse();
157 (BezierCurve::new(left), BezierCurve::new(right))
158 }
159
160 pub fn arc_length(&self, segments: usize) -> f32 {
162 let mut length = 0.0;
163 let mut prev = self.evaluate(0.0);
164 for i in 1..=segments {
165 let t = i as f32 / segments as f32;
166 let p = self.evaluate(t);
167 length += (p - prev).length();
168 prev = p;
169 }
170 length
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn bezier_curve_endpoints() {
180 let curve = BezierCurve::new(vec![Vec3::ZERO, Vec3::X, Vec3::new(1.0, 1.0, 0.0)]);
181 assert!((curve.evaluate(0.0) - Vec3::ZERO).length() < 1e-5);
182 assert!((curve.evaluate(1.0) - Vec3::new(1.0, 1.0, 0.0)).length() < 1e-5);
183 }
184
185 #[test]
186 fn bezier_surface_evaluates() {
187 let points = [[Vec3::ZERO; 4]; 4];
188 let surface = BezierSurface::new(points);
189 let p = surface.evaluate(0.5, 0.5);
190 assert!((p - Vec3::ZERO).length() < 1e-5);
191 }
192
193 #[test]
194 fn bezier_split_preserves_curve() {
195 let curve = BezierCurve::new(vec![Vec3::ZERO, Vec3::new(1.0, 2.0, 0.0), Vec3::X * 2.0]);
196 let (left, right) = curve.split(0.5);
197 let mid_orig = curve.evaluate(0.5);
198 let mid_left = left.evaluate(1.0);
199 assert!((mid_orig - mid_left).length() < 1e-4);
200 }
201}