geop_ops_extrude_revolve/
common.rs1use geop_core_geometry::{
8 nurb_curve::{NurbCurve, NurbCurve2D, NurbCurve3D},
9 nurb_surface::{NurbSurface, NurbSurface3D},
10};
11use geop_core_math::{
12 geop_error::{GeopError, GeopResult},
13 scalars::Scalar,
14 vector::{Vector2, Vector3, Vector4},
15};
16
17#[derive(Clone, Debug)]
28pub struct Profile<S: Scalar> {
29 pub curves: Vec<NurbCurve2D<S>>,
30 pub curve_names: Vec<String>,
31 pub joint_names: Vec<String>,
32}
33
34impl<S: Scalar> Profile<S> {
35 pub fn closed(curves: Vec<NurbCurve2D<S>>) -> Self {
38 let n = curves.len();
39 Self::numbered(curves, n)
40 }
41
42 pub fn open(curves: Vec<NurbCurve2D<S>>) -> Self {
44 let n = curves.len();
45 Self::numbered(curves, n + 1)
46 }
47
48 fn numbered(curves: Vec<NurbCurve2D<S>>, joints: usize) -> Self {
49 Self {
50 curve_names: (0..curves.len()).map(|i| format!("c{i}")).collect(),
51 joint_names: (0..joints).map(|i| format!("p{i}")).collect(),
52 curves,
53 }
54 }
55
56 pub fn with_prefix(mut self, prefix: &str) -> Self {
59 for name in self.curve_names.iter_mut().chain(&mut self.joint_names) {
60 *name = format!("{prefix}{name}");
61 }
62 self
63 }
64
65 pub fn is_closed(&self) -> bool {
66 self.joint_names.len() == self.curves.len()
67 }
68
69 pub fn check_names(&self) -> GeopResult<()> {
71 let n = self.curves.len();
72 if self.curve_names.len() != n
73 || !(self.joint_names.len() == n || self.joint_names.len() == n + 1)
74 {
75 return Err(GeopError::new(format!(
76 "profile of {n} curves has {} curve names and {} joint names",
77 self.curve_names.len(),
78 self.joint_names.len()
79 )));
80 }
81 Ok(())
82 }
83
84 pub fn reversed(&self) -> Self {
87 let n = self.curves.len();
88 let joint_names = if self.is_closed() {
89 (0..n)
92 .map(|m| self.joint_names[(n - m) % n].clone())
93 .collect()
94 } else {
95 self.joint_names.iter().rev().cloned().collect()
96 };
97 Self {
98 curves: self.curves.iter().rev().map(|c| c.reverse()).collect(),
99 curve_names: self.curve_names.iter().rev().cloned().collect(),
100 joint_names,
101 }
102 }
103
104 pub fn map_curves(&self, f: impl Fn(&NurbCurve2D<S>) -> NurbCurve2D<S>) -> Self {
106 Self {
107 curves: self.curves.iter().map(f).collect(),
108 ..self.clone()
109 }
110 }
111}
112
113pub fn pt3<S: Scalar>(p: Vector3<S>) -> Vector4<S> {
117 Vector4::from_array([p[0], p[1], p[2], S::ONE])
118}
119
120pub fn pt2<S: Scalar>(p: Vector2<S>) -> Vector3<S> {
122 Vector3::from_array([p[0], p[1], S::ONE])
123}
124
125pub fn line3<S: Scalar>(p0: Vector3<S>, p1: Vector3<S>) -> GeopResult<NurbCurve3D<S>> {
127 NurbCurve::try_new(
128 1,
129 vec![pt3(p0), pt3(p1)],
130 vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
131 )
132}
133
134pub fn line2<S: Scalar>(p0: Vector2<S>, p1: Vector2<S>) -> GeopResult<NurbCurve2D<S>> {
136 NurbCurve::try_new(
137 1,
138 vec![pt2(p0), pt2(p1)],
139 vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
140 )
141}
142
143pub fn polygon<S: Scalar>(points: &[Vector2<S>]) -> GeopResult<Vec<NurbCurve2D<S>>> {
146 (0..points.len())
147 .map(|i| line2(points[i], points[(i + 1) % points.len()]))
148 .collect()
149}
150
151pub fn polyline<S: Scalar>(points: &[Vector2<S>]) -> GeopResult<Vec<NurbCurve2D<S>>> {
154 points.windows(2).map(|w| line2(w[0], w[1])).collect()
155}
156
157pub fn start_point<S: Scalar>(curve: &NurbCurve2D<S>) -> GeopResult<Vector2<S>> {
160 let cp = curve.control_points[0];
161 Ok(Vector2::from_array([cp[0].div(cp[2])?, cp[1].div(cp[2])?]))
162}
163
164pub fn end_point<S: Scalar>(curve: &NurbCurve2D<S>) -> GeopResult<Vector2<S>> {
166 start_point(&curve.reverse())
167}
168
169pub fn embed_point<S: Scalar>(
172 cp: &Vector3<S>,
173 origin: &Vector3<S>,
174 e1: &Vector3<S>,
175 e2: &Vector3<S>,
176) -> Vector4<S> {
177 let p = origin
178 .prod_scalar(cp[2])
179 .add(&e1.prod_scalar(cp[0]))
180 .add(&e2.prod_scalar(cp[1]));
181 Vector4::from_array([p[0], p[1], p[2], cp[2]])
182}
183
184pub fn embed_curve<S: Scalar>(
187 curve: &NurbCurve2D<S>,
188 origin: &Vector3<S>,
189 e1: &Vector3<S>,
190 e2: &Vector3<S>,
191) -> GeopResult<NurbCurve3D<S>> {
192 NurbCurve::try_new(
193 curve.degree,
194 curve
195 .control_points
196 .iter()
197 .map(|cp| embed_point(cp, origin, e1, e2))
198 .collect(),
199 curve.knot_vector.clone(),
200 )
201}
202
203pub fn bilinear<S: Scalar>(
206 p00: Vector3<S>,
207 p10: Vector3<S>,
208 p11: Vector3<S>,
209 p01: Vector3<S>,
210) -> GeopResult<NurbSurface3D<S>> {
211 NurbSurface::try_new(
212 1,
213 1,
214 vec![pt3(p00), pt3(p01), pt3(p10), pt3(p11)],
215 vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
216 vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
217 )
218}
219
220pub fn sqrt2_over_2<S: Scalar>() -> S {
223 S::from_f64(std::f64::consts::SQRT_2 / 2.0)
224}
225
226pub fn arc3<S: Scalar>(
233 p0: Vector3<S>,
234 mid: Vector3<S>,
235 p2: Vector3<S>,
236 w: S,
237) -> GeopResult<NurbCurve3D<S>> {
238 let cp0 = pt3(p0);
239 let cp2 = pt3(p2);
240 let cp1 = Vector4::from_array([mid[0].mul(w), mid[1].mul(w), mid[2].mul(w), w]);
241 NurbCurve::try_new(
242 2,
243 vec![cp0, cp1, cp2],
244 vec![S::ZERO, S::ZERO, S::ZERO, S::ONE, S::ONE, S::ONE],
245 )
246}
247
248pub fn arc2<S: Scalar>(
251 p0: Vector2<S>,
252 mid: Vector2<S>,
253 p2: Vector2<S>,
254 w: S,
255) -> GeopResult<NurbCurve2D<S>> {
256 let cp0 = pt2(p0);
257 let cp2 = pt2(p2);
258 let cp1 = Vector3::from_array([mid[0].mul(w), mid[1].mul(w), w]);
259 NurbCurve::try_new(
260 2,
261 vec![cp0, cp1, cp2],
262 vec![S::ZERO, S::ZERO, S::ZERO, S::ONE, S::ONE, S::ONE],
263 )
264}
265
266#[cfg(test)]
267mod tests {
268 use super::bilinear;
269 use geop_core_math::{for_all_scalars, scalars::Scalar, vector::Vector3};
270
271 fn p<S: Scalar>(x: f64, y: f64, z: f64) -> Vector3<S> {
272 Vector3::from_array([S::from_f64(x), S::from_f64(y), S::from_f64(z)])
273 }
274
275 fn check_bilinear_maps_uv_correctly<S: Scalar>() {
280 let p00 = p::<S>(0.3, -1.2, 2.5);
281 let p10 = p::<S>(4.1, 0.7, -0.3);
282 let p11 = p::<S>(2.2, 3.3, 1.1);
283 let p01 = p::<S>(-1.5, 2.0, 0.6);
284
285 let surface = bilinear(p00, p10, p11, p01).unwrap();
286
287 assert!(
288 surface
289 .evaluate(S::ZERO, S::ZERO)
290 .unwrap()
291 .could_be_equal(&p00)
292 );
293 assert!(
294 surface
295 .evaluate(S::ONE, S::ZERO)
296 .unwrap()
297 .could_be_equal(&p10)
298 );
299 assert!(
300 surface
301 .evaluate(S::ONE, S::ONE)
302 .unwrap()
303 .could_be_equal(&p11)
304 );
305 assert!(
306 surface
307 .evaluate(S::ZERO, S::ONE)
308 .unwrap()
309 .could_be_equal(&p01)
310 );
311 }
312 #[test]
313 fn bilinear_maps_uv_correctly() {
314 for_all_scalars!(check_bilinear_maps_uv_correctly);
315 }
316}