geop_core_geometry/nurb_curve/
translate.rs1use geop_core_math::{
2 geop_error::GeopResult,
3 primitives::Motion,
4 scalars::Scalar,
5 vector::{Vector2, Vector3, Vector4},
6};
7
8use super::NurbCurve;
9use crate::aabb::compute_aabb;
10
11impl NurbCurve<4> {
12 pub fn translate(&self, offset: Vector3) -> Self {
17 let control_points: Vec<_> = self
18 .control_points
19 .iter()
20 .map(|cp| {
21 let w = cp[3];
22 geop_core_math::vector::Vector4::from_array([
23 cp[0].add(offset[0].mul(w)),
24 cp[1].add(offset[1].mul(w)),
25 cp[2].add(offset[2].mul(w)),
26 w,
27 ])
28 })
29 .collect();
30 let aabb = compute_aabb(&control_points);
31 Self {
32 degree: self.degree,
33 control_points,
34 knot_vector: self.knot_vector.clone(),
35 aabb,
37 }
38 }
39}
40
41impl NurbCurve<3> {
42 pub fn translate(&self, offset: Vector2) -> Self {
46 let zero = |x: Scalar| x.is_sharp() && x.could_be_equal(Scalar::ZERO);
47 if zero(offset[0]) && zero(offset[1]) {
48 return self.clone();
49 }
50 let control_points: Vec<_> = self
51 .control_points
52 .iter()
53 .map(|cp| {
54 let w = cp[2];
55 Vector3::from_array([cp[0].add(offset[0].mul(w)), cp[1].add(offset[1].mul(w)), w])
56 })
57 .collect();
58 let aabb = compute_aabb(&control_points);
59 Self {
60 degree: self.degree,
61 control_points,
62 knot_vector: self.knot_vector.clone(),
63 aabb,
64 }
65 }
66
67 pub fn lifted(&self) -> GeopResult<NurbCurve<4>> {
69 NurbCurve::try_new(
70 self.degree,
71 self.control_points
72 .iter()
73 .map(|q| Vector4::from_array([q[0], q[1], Scalar::ZERO, q[2]]))
74 .collect(),
75 self.knot_vector.clone(),
76 )
77 }
78
79 pub fn embed(&self, origin: &Vector3, e1: &Vector3, e2: &Vector3) -> GeopResult<NurbCurve<4>> {
85 let control_points: Vec<_> = self
86 .control_points
87 .iter()
88 .map(|cp| embed_point(cp, origin, e1, e2))
89 .collect();
90 NurbCurve::try_new(self.degree, control_points, self.knot_vector.clone())
91 }
92}
93
94pub fn embed_point(cp: &Vector3, origin: &Vector3, e1: &Vector3, e2: &Vector3) -> Vector4 {
98 let p = origin
99 .prod_scalar(cp[2])
100 .add(&e1.prod_scalar(cp[0]))
101 .add(&e2.prod_scalar(cp[1]));
102 Vector4::from_array([p[0], p[1], p[2], cp[2]])
103}
104
105pub fn flatten_point(cp: &Vector4, origin: &Vector3, e1: &Vector3, e2: &Vector3) -> Vector3 {
110 let w = cp[3];
111 let d = Vector3::from_array([cp[0], cp[1], cp[2]]).sub(&origin.prod_scalar(w));
112 Vector3::from_array([d.prod_dot(e1), d.prod_dot(e2), w])
113}
114
115pub(crate) fn transform_control_point(cp: &Vector4, motion: &Motion) -> Vector4 {
123 let w = cp[3];
124 let p = motion.rotate(&Vector3::from_array([cp[0], cp[1], cp[2]]));
125 let mut t = motion.position();
126 if !(w.is_sharp() && w.to_f64() == 1.0) {
127 t = t.map(|x| x.mul(w));
128 }
129 Vector4::from_array([p[0].add(t[0]), p[1].add(t[1]), p[2].add(t[2]), w])
130}
131
132impl NurbCurve<4> {
133 pub fn transform(&self, motion: &Motion) -> Self {
137 let mut curve = Self {
138 degree: self.degree,
139 control_points: self
140 .control_points
141 .iter()
142 .map(|cp| transform_control_point(cp, motion))
143 .collect(),
144 knot_vector: self.knot_vector.clone(),
145 aabb: self.aabb,
146 };
147 curve.recompute_aabb();
148 curve
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use geop_core_math::{
155 primitives::Pose,
156 scalars::Scalar,
157 vector::{Vector3, Vector4},
158 };
159
160 use super::super::NurbCurve3D;
161
162 #[test]
163 fn translate_shifts_evaluated_points() {
164 let curve = NurbCurve3D::try_new(
165 1,
166 vec![
167 geop_core_math::vector::Vector4::from_array([
168 Scalar::ZERO,
169 Scalar::ZERO,
170 Scalar::ZERO,
171 Scalar::ONE,
172 ]),
173 geop_core_math::vector::Vector4::from_array([
174 Scalar::ONE,
175 Scalar::ZERO,
176 Scalar::ZERO,
177 Scalar::ONE,
178 ]),
179 ],
180 vec![Scalar::ZERO, Scalar::ZERO, Scalar::ONE, Scalar::ONE],
181 )
182 .unwrap();
183 let offset = Vector3::from_array([
184 Scalar::from_f64(2.0),
185 Scalar::from_f64(3.0),
186 Scalar::from_f64(-1.0),
187 ]);
188 let shifted = curve.translate(offset);
189 for t in [0.0, 0.3, 1.0] {
190 let a = shifted.evaluate(Scalar::from_f64(t)).unwrap();
191 let b = curve.evaluate(Scalar::from_f64(t)).unwrap().add(&offset);
192 assert!(a.could_be_equal(&b));
193 }
194 }
195
196 #[test]
199 fn transform_moves_evaluated_points() {
200 let curve = NurbCurve3D::try_new(
201 2,
202 vec![
203 Vector4::from_array([Scalar::ONE, Scalar::ZERO, Scalar::ZERO, Scalar::ONE]),
204 Vector4::from_array([
205 Scalar::from_f64(0.5),
206 Scalar::from_f64(0.5),
207 Scalar::ZERO,
208 Scalar::from_f64(0.5),
209 ]),
210 Vector4::from_array([Scalar::ZERO, Scalar::ONE, Scalar::ZERO, Scalar::ONE]),
211 ],
212 vec![
213 Scalar::ZERO,
214 Scalar::ZERO,
215 Scalar::ZERO,
216 Scalar::ONE,
217 Scalar::ONE,
218 Scalar::ONE,
219 ],
220 )
221 .unwrap();
222 let pose = Pose::from_euler(
223 Vector3::from_array([1.0, -2.0, 0.5].map(Scalar::from_f64)),
224 [30.0, -10.0, 75.0].map(Scalar::from_f64),
225 )
226 .unwrap();
227 let placed = curve.transform(&pose.motion());
228 for t in [0.0, 0.3, 0.8, 1.0] {
229 let a = placed.evaluate(Scalar::from_f64(t)).unwrap();
230 let b = pose
231 .motion()
232 .apply(&curve.evaluate(Scalar::from_f64(t)).unwrap());
233 assert!(a.could_be_equal(&b));
234 }
235 }
236}