geop_core_geometry/nurb_surface/
normal.rs1use crate::nurb_curve::NurbCurve;
2use geop_core_math::{geop_error::GeopResult, scalars::Scalar, vector::Vector3};
3
4use super::{
5 NurbSurface,
6 evaluate::{de_boor, find_span},
7};
8
9impl<S: Scalar> NurbSurface<S, 4> {
10 fn isocurve_u(&self, v: S) -> GeopResult<NurbCurve<S, 4>> {
12 let span_v = find_span(self.degree_v, &self.knot_vector_v, self.num_v - 1, v)?;
13 let mut pts = Vec::with_capacity(self.num_u);
14 for i in 0..self.num_u {
15 let col: Vec<_> = (0..self.num_v)
16 .map(|j| self.control_points[i * self.num_v + j])
17 .collect();
18 pts.push(de_boor(self.degree_v, &self.knot_vector_v, &col, v, span_v));
19 }
20 NurbCurve::try_new(self.degree_u, pts, self.knot_vector_u.clone())
21 }
22
23 fn isocurve_v(&self, u: S) -> GeopResult<NurbCurve<S, 4>> {
25 let span_u = find_span(self.degree_u, &self.knot_vector_u, self.num_u - 1, u)?;
26 let mut pts = Vec::with_capacity(self.num_v);
27 for j in 0..self.num_v {
28 let row: Vec<_> = (0..self.num_u)
29 .map(|i| self.control_points[i * self.num_v + j])
30 .collect();
31 pts.push(de_boor(self.degree_u, &self.knot_vector_u, &row, u, span_u));
32 }
33 NurbCurve::try_new(self.degree_v, pts, self.knot_vector_v.clone())
34 }
35
36 pub fn derivatives(&self, u: S, v: S) -> GeopResult<(Vector3<S>, Vector3<S>)> {
38 let du = self.isocurve_u(v)?.tangent(u)?;
39 let dv = self.isocurve_v(u)?.tangent(v)?;
40 Ok((du, dv))
41 }
42
43 pub(crate) fn second_derivatives(&self, u: S, v: S) -> GeopResult<(Vector3<S>, Vector3<S>)> {
52 let duu = self.isocurve_u(v)?.second_derivative(u)?;
53 let dvv = self.isocurve_v(u)?.second_derivative(v)?;
54 Ok((duu, dvv))
55 }
56
57 pub fn normal(&self, u: S, v: S) -> GeopResult<Vector3<S>> {
69 let (du, dv) = self.derivatives(u, v)?;
70 du.prod_cross(&dv).normalize()
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use crate::nurb_surface::NurbSurface;
77 use geop_core_math::for_all_scalars;
78 use geop_core_math::{scalars::Scalar, vector::Vector4};
79
80 fn pt<S: Scalar>(x: f64, y: f64, z: f64, w: f64) -> Vector4<S> {
81 Vector4::from_array([
82 S::from_f64(x),
83 S::from_f64(y),
84 S::from_f64(z),
85 S::from_f64(w),
86 ])
87 }
88
89 fn flat_xy<S: Scalar>() -> NurbSurface<S, 4> {
91 let f = S::from_f64;
92 NurbSurface::try_new(
93 1,
94 1,
95 vec![
96 pt(0., 0., 0., 1.),
97 pt(0., 1., 0., 1.),
98 pt(1., 0., 0., 1.),
99 pt(1., 1., 0., 1.),
100 ],
101 vec![f(0.), f(0.), f(1.), f(1.)],
102 vec![f(0.), f(0.), f(1.), f(1.)],
103 )
104 .unwrap()
105 }
106
107 fn check_flat_surface_normal_is_constant_z<S: Scalar>() {
108 let s = flat_xy::<S>();
109 for &(u, v) in &[(0.0, 0.0), (0.5, 0.5), (1.0, 0.0), (0.2, 0.8)] {
110 let n = s.normal(S::from_f64(u), S::from_f64(v)).unwrap();
111 assert!(n[0].could_be_equal(S::ZERO));
112 assert!(n[1].could_be_equal(S::ZERO));
113 assert!(n[2].could_be_equal(S::ONE));
114 }
115 }
116 #[test]
117 fn flat_surface_normal_is_constant_z() {
118 for_all_scalars!(check_flat_surface_normal_is_constant_z);
119 }
120
121 fn bent_surface<S: Scalar>() -> NurbSurface<S, 4> {
125 let f = S::from_f64;
126 NurbSurface::try_new(
127 1,
128 1,
129 vec![
130 pt(0., 0., 0., 1.),
131 pt(0., 2., 1., 1.),
132 pt(2., 0., 1., 1.),
133 pt(2., 2., 0., 1.),
134 ],
135 vec![f(0.), f(0.), f(1.), f(1.)],
136 vec![f(0.), f(0.), f(1.), f(1.)],
137 )
138 .unwrap()
139 }
140
141 fn check_bent_surface_center_normal_is_z<S: Scalar>() {
142 let s = bent_surface::<S>();
143 let n = s.normal(S::from_f64(0.5), S::from_f64(0.5)).unwrap();
144 assert!(n[0].could_be_equal(S::ZERO));
145 assert!(n[1].could_be_equal(S::ZERO));
146 assert!(n[2].definitely_greater(S::ZERO));
147 }
148 #[test]
149 fn bent_surface_center_normal_is_z() {
150 for_all_scalars!(check_bent_surface_center_normal_is_z);
151 }
152}