1use crate::curves::{Circle3D, Ellipse3D};
7use crate::nurbs::curve::NurbsCurve;
8use crate::nurbs::projection::project_point_to_surface;
9use crate::nurbs::surface::NurbsSurface;
10use crate::surfaces::{ConicalSurface, CylindricalSurface, SphericalSurface, ToroidalSurface};
11use crate::vec::{Point3, Vec3};
12
13pub trait ParametricSurface {
18 fn evaluate(&self, u: f64, v: f64) -> Point3;
20
21 fn normal(&self, u: f64, v: f64) -> Vec3;
26
27 fn project_point(&self, point: Point3) -> (f64, f64);
29
30 fn partial_u(&self, u: f64, v: f64) -> Vec3;
32
33 fn partial_v(&self, u: f64, v: f64) -> Vec3;
35
36 fn closed_u(&self) -> Option<(f64, f64)> {
41 None
42 }
43
44 fn closed_v(&self) -> Option<(f64, f64)> {
47 None
48 }
49}
50
51pub trait ParametricCurve {
55 fn evaluate(&self, t: f64) -> Point3;
57
58 fn tangent(&self, t: f64) -> Vec3;
60
61 fn domain(&self) -> (f64, f64);
63}
64
65impl ParametricSurface for CylindricalSurface {
68 #[inline]
69 fn evaluate(&self, u: f64, v: f64) -> Point3 {
70 self.evaluate(u, v)
71 }
72
73 #[inline]
74 fn normal(&self, u: f64, v: f64) -> Vec3 {
75 self.normal(u, v)
76 }
77
78 #[inline]
79 fn project_point(&self, point: Point3) -> (f64, f64) {
80 self.project_point(point)
81 }
82
83 #[inline]
84 fn partial_u(&self, u: f64, _v: f64) -> Vec3 {
85 let (sin_u, cos_u) = u.sin_cos();
86 self.x_axis() * (-self.radius() * sin_u) + self.y_axis() * (self.radius() * cos_u)
87 }
88
89 #[inline]
90 fn partial_v(&self, _u: f64, _v: f64) -> Vec3 {
91 self.axis()
92 }
93}
94
95impl ParametricSurface for ConicalSurface {
96 #[inline]
97 fn evaluate(&self, u: f64, v: f64) -> Point3 {
98 self.evaluate(u, v)
99 }
100
101 #[inline]
102 fn normal(&self, u: f64, v: f64) -> Vec3 {
103 self.normal(u, v)
104 }
105
106 #[inline]
107 fn project_point(&self, point: Point3) -> (f64, f64) {
108 self.project_point(point)
109 }
110
111 #[inline]
112 fn partial_u(&self, u: f64, v: f64) -> Vec3 {
113 let (sin_u, cos_u) = u.sin_cos();
114 let cos_a = self.half_angle().cos();
115 self.x_axis() * (-v * cos_a * sin_u) + self.y_axis() * (v * cos_a * cos_u)
116 }
117
118 #[inline]
119 fn partial_v(&self, u: f64, _v: f64) -> Vec3 {
120 let (sin_u, cos_u) = u.sin_cos();
121 let (sin_a, cos_a) = self.half_angle().sin_cos();
122 (self.x_axis() * cos_u + self.y_axis() * sin_u) * cos_a + self.axis() * sin_a
123 }
124}
125
126impl ParametricSurface for SphericalSurface {
127 #[inline]
128 fn evaluate(&self, u: f64, v: f64) -> Point3 {
129 self.evaluate(u, v)
130 }
131
132 #[inline]
133 fn normal(&self, u: f64, v: f64) -> Vec3 {
134 self.normal(u, v)
135 }
136
137 #[inline]
138 fn project_point(&self, point: Point3) -> (f64, f64) {
139 self.project_point(point)
140 }
141
142 #[inline]
143 fn partial_u(&self, u: f64, v: f64) -> Vec3 {
144 let (sin_u, cos_u) = u.sin_cos();
145 let cos_v = v.cos();
146 self.x_axis() * (-self.radius() * cos_v * sin_u)
147 + self.y_axis() * (self.radius() * cos_v * cos_u)
148 }
149
150 #[inline]
151 fn partial_v(&self, u: f64, v: f64) -> Vec3 {
152 let (sin_u, cos_u) = u.sin_cos();
153 let (sin_v, cos_v) = v.sin_cos();
154 self.x_axis() * (-self.radius() * sin_v * cos_u)
155 + self.y_axis() * (-self.radius() * sin_v * sin_u)
156 + self.z_axis() * (self.radius() * cos_v)
157 }
158}
159
160impl ParametricSurface for ToroidalSurface {
161 #[inline]
162 fn evaluate(&self, u: f64, v: f64) -> Point3 {
163 self.evaluate(u, v)
164 }
165
166 #[inline]
167 fn normal(&self, u: f64, v: f64) -> Vec3 {
168 self.normal(u, v)
169 }
170
171 #[inline]
172 fn project_point(&self, point: Point3) -> (f64, f64) {
173 self.project_point(point)
174 }
175
176 #[inline]
177 fn partial_u(&self, u: f64, v: f64) -> Vec3 {
178 let (sin_u, cos_u) = u.sin_cos();
179 let cos_v = v.cos();
180 let tube_radius = self.major_radius() + self.minor_radius() * cos_v;
181 self.x_axis() * (-tube_radius * sin_u) + self.y_axis() * (tube_radius * cos_u)
182 }
183
184 #[inline]
185 fn partial_v(&self, u: f64, v: f64) -> Vec3 {
186 let (sin_u, cos_u) = u.sin_cos();
187 let (sin_v, cos_v) = v.sin_cos();
188 (self.x_axis() * cos_u + self.y_axis() * sin_u) * (-self.minor_radius() * sin_v)
189 + self.z_axis() * (self.minor_radius() * cos_v)
190 }
191}
192
193impl ParametricSurface for NurbsSurface {
194 #[inline]
195 fn evaluate(&self, u: f64, v: f64) -> Point3 {
196 self.evaluate(u, v)
197 }
198
199 #[inline]
200 fn normal(&self, u: f64, v: f64) -> Vec3 {
201 self.normal(u, v)
202 .unwrap_or_else(|_| Vec3::new(0.0, 0.0, 1.0))
203 }
204
205 #[inline]
206 fn project_point(&self, point: Point3) -> (f64, f64) {
207 if let Ok(proj) = project_point_to_surface(self, point, 1e-7) {
209 (proj.u, proj.v)
210 } else {
211 let (u0, u1) = self.domain_u();
213 let (v0, v1) = self.domain_v();
214 ((u0 + u1) * 0.5, (v0 + v1) * 0.5)
215 }
216 }
217
218 #[inline]
219 fn partial_u(&self, u: f64, v: f64) -> Vec3 {
220 let d = self.derivatives(u, v, 1);
221 d[1][0]
222 }
223
224 #[inline]
225 fn partial_v(&self, u: f64, v: f64) -> Vec3 {
226 let d = self.derivatives(u, v, 1);
227 d[0][1]
228 }
229
230 fn closed_u(&self) -> Option<(f64, f64)> {
231 self.is_periodic_u().then(|| self.domain_u())
232 }
233
234 fn closed_v(&self) -> Option<(f64, f64)> {
235 self.is_periodic_v().then(|| self.domain_v())
236 }
237}
238
239impl ParametricCurve for Circle3D {
242 #[inline]
243 fn evaluate(&self, t: f64) -> Point3 {
244 self.evaluate(t)
245 }
246
247 #[inline]
248 fn tangent(&self, t: f64) -> Vec3 {
249 self.tangent(t)
250 }
251
252 #[inline]
253 fn domain(&self) -> (f64, f64) {
254 (0.0, std::f64::consts::TAU)
255 }
256}
257
258impl ParametricCurve for Ellipse3D {
259 #[inline]
260 fn evaluate(&self, t: f64) -> Point3 {
261 self.evaluate(t)
262 }
263
264 #[inline]
265 fn tangent(&self, t: f64) -> Vec3 {
266 self.tangent(t)
267 }
268
269 #[inline]
270 fn domain(&self) -> (f64, f64) {
271 (0.0, std::f64::consts::TAU)
272 }
273}
274
275impl ParametricCurve for NurbsCurve {
276 #[inline]
277 fn evaluate(&self, t: f64) -> Point3 {
278 self.evaluate(t)
279 }
280
281 #[inline]
282 fn tangent(&self, t: f64) -> Vec3 {
283 self.tangent(t).unwrap_or_else(|_| Vec3::new(1.0, 0.0, 0.0))
284 }
285
286 #[inline]
287 fn domain(&self) -> (f64, f64) {
288 self.domain()
289 }
290}