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