geop_core_geometry/nurb_surface/
gordon.rs1use geop_core_math::{
15 geop_error::{GeopError, GeopResult},
16 scalars::Scalar,
17 vector::{Vector3, Vector4},
18};
19
20use super::NurbSurface3D;
21use crate::nurb_curve::{NurbCurve, NurbCurve3D};
22
23const SKIN_DEGREE: usize = 3;
26
27fn common_parameters(crossings: &[Vec<Scalar>]) -> GeopResult<Vec<Scalar>> {
35 let n = crossings[0].len();
36 let count = Scalar::from_i64(crossings.len() as i64);
37 let mut params = vec![Scalar::ZERO; n];
38 params[n - 1] = Scalar::ONE;
39 for (i, param) in params.iter_mut().enumerate().take(n - 1).skip(1) {
40 let mut sum = Scalar::ZERO;
41 for c in crossings {
42 sum = sum.add(c[i].sub(c[0]).div(c[n - 1].sub(c[0]))?);
43 }
44 *param = sum.div(count)?.sharpen();
45 }
46 if !params.windows(2).all(|w| w[0].definitely_less(w[1])) {
47 return Err(GeopError::new(format!(
48 "NurbSurface::gordon: the crossings {crossings:?} do not each run in one order"
49 )));
50 }
51 Ok(params)
52}
53
54fn skin(rows: &[NurbCurve3D], params: &[Scalar]) -> GeopResult<NurbSurface3D> {
62 let first = &rows[0];
63 let nu = first.control_points.len();
64 let mut columns = Vec::with_capacity(nu);
65 for k in 0..nu {
66 let values: Vec<Vector4> = rows.iter().map(|r| r.control_points[k]).collect();
67 let mut column = NurbCurve::interpolate_homogeneous(&values, params, SKIN_DEGREE)?;
68 let last = column.control_points.len() - 1;
69 column.control_points[0] = values[0];
70 column.control_points[last] = values[values.len() - 1];
71 columns.push(column);
72 }
73 let nv = columns[0].control_points.len();
74 let control_points = (0..nu)
75 .flat_map(|i| (0..nv).map(move |j| (i, j)))
76 .map(|(i, j)| columns[i].control_points[j])
77 .collect();
78 NurbSurface3D::try_new(
79 first.degree,
80 columns[0].degree,
81 control_points,
82 first.knot_vector.clone(),
83 columns[0].knot_vector.clone(),
84 )
85}
86
87impl NurbSurface3D {
88 pub(crate) fn transposed(&self) -> Self {
90 let (nu, nv) = (self.num_u, self.num_v);
91 let control_points = (0..nv)
92 .flat_map(|j| (0..nu).map(move |i| (i, j)))
93 .map(|(i, j)| self.control_points[i * nv + j])
94 .collect();
95 Self {
96 degree_u: self.degree_v,
97 degree_v: self.degree_u,
98 num_u: nv,
99 num_v: nu,
100 control_points,
101 knot_vector_u: self.knot_vector_v.clone(),
102 knot_vector_v: self.knot_vector_u.clone(),
103 aabb: self.aabb,
104 }
105 }
106
107 fn rows_along_u(&self) -> GeopResult<Vec<NurbCurve3D>> {
109 (0..self.num_v)
110 .map(|j| {
111 let points = (0..self.num_u)
112 .map(|i| self.control_points[i * self.num_v + j])
113 .collect();
114 NurbCurve::try_new(self.degree_u, points, self.knot_vector_u.clone())
115 })
116 .collect()
117 }
118
119 fn compatible_along_u(surfaces: &[Self]) -> GeopResult<Vec<Self>> {
123 let mut rows = Vec::new();
124 for s in surfaces {
125 rows.extend(s.rows_along_u()?);
126 }
127 let mut rows = NurbCurve::compatible(&rows)?.into_iter();
128 surfaces
129 .iter()
130 .map(|s| {
131 let curves: Vec<_> = rows.by_ref().take(s.num_v).collect();
132 let num_u = curves[0].control_points.len();
133 let control_points = (0..num_u)
134 .flat_map(|i| (0..s.num_v).map(move |j| (i, j)))
135 .map(|(i, j)| curves[j].control_points[i])
136 .collect();
137 NurbSurface3D::try_new(
138 curves[0].degree,
139 s.degree_v,
140 control_points,
141 curves[0].knot_vector.clone(),
142 s.knot_vector_v.clone(),
143 )
144 })
145 .collect()
146 }
147
148 pub fn gordon(
173 u_curves: &[NurbCurve3D],
174 v_curves: &[NurbCurve3D],
175 u_crossings: &[Vec<Scalar>],
176 v_crossings: &[Vec<Scalar>],
177 ) -> GeopResult<Self> {
178 let (m, n) = (u_curves.len(), v_curves.len());
179 let shaped = |crossings: &[Vec<Scalar>], curves: usize, each: usize| {
180 crossings.len() == curves && crossings.iter().all(|c| c.len() == each)
181 };
182 if m < 2 || n < 2 || !shaped(u_crossings, m, n) || !shaped(v_crossings, n, m) {
183 return Err(GeopError::new(format!(
184 "NurbSurface::gordon: {m} u curves and {n} v curves, two at least each way, need the crossings of each with every other, not {u_crossings:?} and {v_crossings:?}"
185 )));
186 }
187 let u_params = common_parameters(u_crossings)?;
188 let v_params = common_parameters(v_crossings)?;
189 let reparametrized =
190 |curves: &[NurbCurve3D], crossings: &[Vec<Scalar>], params: &[Scalar]| {
191 curves
192 .iter()
193 .zip(crossings)
194 .map(|(c, x)| c.reparametrized(x, params))
195 .collect::<GeopResult<Vec<_>>>()
196 };
197 let u = reparametrized(u_curves, u_crossings, &u_params)?;
198 let v = reparametrized(v_curves, v_crossings, &v_params)?;
199
200 let mut points = Vec::with_capacity(m);
203 for (j, uj) in u.iter().enumerate() {
204 let mut row = Vec::with_capacity(n);
205 for (i, vi) in v.iter().enumerate() {
206 let a = uj.evaluate(u_params[i])?;
207 let b = vi.evaluate(v_params[j])?;
208 if !a.could_be_equal(&b) {
209 return Err(GeopError::new(format!(
210 "NurbSurface::gordon: u curve {j} is at {a:?} where it crosses v curve {i}, which is at {b:?}"
211 )));
212 }
213 let p: Vector3 = a.union(&b);
214 row.push(p.homogeneous(Scalar::ONE));
215 }
216 points.push(row);
217 }
218 let rational = u.iter().chain(&v).any(|c| {
219 c.control_points
220 .iter()
221 .any(|p| !p[3].is_exactly(Scalar::ONE))
222 });
223
224 let along_u = skin(&NurbCurve::compatible(&u)?, &v_params)?;
225 let along_v = skin(&NurbCurve::compatible(&v)?, &u_params)?.transposed();
226 let through_points = {
227 let rows = points
228 .iter()
229 .map(|row| NurbCurve::interpolate_homogeneous(row, &u_params, SKIN_DEGREE))
230 .collect::<GeopResult<Vec<_>>>()?;
231 skin(&rows, &v_params)?
232 };
233 let surfaces = Self::compatible_along_u(&[along_u, along_v, through_points])?;
234 let transposed: Vec<Self> = surfaces.iter().map(Self::transposed).collect();
235 let surfaces: Vec<Self> = Self::compatible_along_u(&transposed)?
236 .iter()
237 .map(Self::transposed)
238 .collect();
239 let [along_u, along_v, through_points] = &surfaces[..] else {
240 unreachable!("three surfaces");
241 };
242
243 let (nu, nv) = (along_u.num_u, along_u.num_v);
244 let mut control_points = Vec::with_capacity(nu * nv);
245 for i in 0..nu {
246 for j in 0..nv {
247 let k = i * nv + j;
248 let (on_u_side, on_v_side) = (j == 0 || j == nv - 1, i == 0 || i == nu - 1);
249 let mut p = match (on_u_side, on_v_side) {
250 (true, true) => along_u.control_points[k].union(&along_v.control_points[k]),
251 (true, false) => along_u.control_points[k],
252 (false, true) => along_v.control_points[k],
253 (false, false) => along_u.control_points[k]
254 .add(&along_v.control_points[k])
255 .sub(&through_points.control_points[k]),
256 };
257 if !rational {
258 p[3] = Scalar::ONE;
261 }
262 control_points.push(p);
263 }
264 }
265 NurbSurface3D::try_new(
266 along_u.degree_u,
267 along_u.degree_v,
268 control_points,
269 along_u.knot_vector_u.clone(),
270 along_u.knot_vector_v.clone(),
271 )
272 }
273}
274
275#[cfg(test)]
276mod tests;