1use approx::{AbsDiffEq, RelativeEq, UlpsEq};
2use num::Zero;
3use std::fmt;
4use std::hash;
5
6#[cfg(feature = "serde-serialize-no-std")]
7use serde::{Deserialize, Serialize};
8
9use simba::scalar::{RealField, SubsetOf};
10use simba::simd::SimdRealField;
11
12use crate::base::allocator::Allocator;
13use crate::base::dimension::{DimNameAdd, DimNameSum, U1};
14use crate::base::storage::Owned;
15use crate::base::{Const, DefaultAllocator, OMatrix, SVector, Scalar};
16use crate::geometry::{AbstractRotation, Isometry, Point, Translation};
17
18#[cfg(feature = "rkyv-serialize")]
19use rkyv::bytecheck;
20
21#[repr(C)]
23#[derive(Debug, Copy, Clone)]
24#[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))]
25#[cfg_attr(
26 feature = "serde-serialize-no-std",
27 serde(bound(serialize = "T: Scalar + Serialize,
28 R: Serialize,
29 DefaultAllocator: Allocator<Const<D>>,
30 Owned<T, Const<D>>: Serialize"))
31)]
32#[cfg_attr(
33 feature = "serde-serialize-no-std",
34 serde(bound(deserialize = "T: Scalar + Deserialize<'de>,
35 R: Deserialize<'de>,
36 DefaultAllocator: Allocator<Const<D>>,
37 Owned<T, Const<D>>: Deserialize<'de>"))
38)]
39#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
40#[cfg_attr(
41 feature = "rkyv-serialize-no-std",
42 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
43 archive(
44 as = "Similarity<T::Archived, R::Archived, D>",
45 bound(archive = "
46 T: rkyv::Archive,
47 R: rkyv::Archive,
48 Isometry<T, R, D>: rkyv::Archive<Archived = Isometry<T::Archived, R::Archived, D>>
49 ")
50 )
51)]
52pub struct Similarity<T, R, const D: usize> {
53 pub isometry: Isometry<T, R, D>,
55 scaling: T,
56}
57
58impl<T: Scalar + hash::Hash, R: hash::Hash, const D: usize> hash::Hash for Similarity<T, R, D>
59where
60 Owned<T, Const<D>>: hash::Hash,
61{
62 fn hash<H: hash::Hasher>(&self, state: &mut H) {
63 self.isometry.hash(state);
64 self.scaling.hash(state);
65 }
66}
67
68#[cfg(feature = "bytemuck")]
69unsafe impl<T: Scalar, R, const D: usize> bytemuck::Zeroable for Similarity<T, R, D> where
70 Isometry<T, R, D>: bytemuck::Zeroable
71{
72}
73
74#[cfg(feature = "bytemuck")]
75unsafe impl<T: Scalar, R, const D: usize> bytemuck::Pod for Similarity<T, R, D>
76where
77 Isometry<T, R, D>: bytemuck::Pod,
78 R: Copy,
79 T: Copy,
80{
81}
82
83impl<T: Scalar + Zero, R, const D: usize> Similarity<T, R, D>
84where
85 R: AbstractRotation<T, D>,
86{
87 #[inline]
89 pub fn from_parts(translation: Translation<T, D>, rotation: R, scaling: T) -> Self {
90 Self::from_isometry(Isometry::from_parts(translation, rotation), scaling)
91 }
92
93 #[inline]
95 pub fn from_isometry(isometry: Isometry<T, R, D>, scaling: T) -> Self {
96 assert!(!scaling.is_zero(), "The scaling factor must not be zero.");
97
98 Self { isometry, scaling }
99 }
100
101 #[inline]
103 pub fn set_scaling(&mut self, scaling: T) {
104 assert!(
105 !scaling.is_zero(),
106 "The similarity scaling factor must not be zero."
107 );
108
109 self.scaling = scaling;
110 }
111}
112
113impl<T: Scalar, R, const D: usize> Similarity<T, R, D> {
114 #[inline]
116 #[must_use]
117 pub fn scaling(&self) -> T {
118 self.scaling.clone()
119 }
120}
121
122impl<T: SimdRealField, R, const D: usize> Similarity<T, R, D>
123where
124 T::Element: SimdRealField,
125 R: AbstractRotation<T, D>,
126{
127 #[inline]
129 pub fn from_scaling(scaling: T) -> Self {
130 Self::from_isometry(Isometry::identity(), scaling)
131 }
132
133 #[inline]
135 #[must_use = "Did you mean to use inverse_mut()?"]
136 pub fn inverse(&self) -> Self {
137 let mut res = self.clone();
138 res.inverse_mut();
139 res
140 }
141
142 #[inline]
144 pub fn inverse_mut(&mut self) {
145 self.scaling = T::one() / self.scaling.clone();
146 self.isometry.inverse_mut();
147 self.isometry.translation.vector *= self.scaling.clone();
148 }
149
150 #[inline]
152 #[must_use = "Did you mean to use prepend_scaling_mut()?"]
153 pub fn prepend_scaling(&self, scaling: T) -> Self {
154 assert!(
155 !scaling.is_zero(),
156 "The similarity scaling factor must not be zero."
157 );
158
159 Self::from_isometry(self.isometry.clone(), self.scaling.clone() * scaling)
160 }
161
162 #[inline]
164 #[must_use = "Did you mean to use append_scaling_mut()?"]
165 pub fn append_scaling(&self, scaling: T) -> Self {
166 assert!(
167 !scaling.is_zero(),
168 "The similarity scaling factor must not be zero."
169 );
170
171 Self::from_parts(
172 Translation::from(&self.isometry.translation.vector * scaling.clone()),
173 self.isometry.rotation.clone(),
174 self.scaling.clone() * scaling,
175 )
176 }
177
178 #[inline]
180 pub fn prepend_scaling_mut(&mut self, scaling: T) {
181 assert!(
182 !scaling.is_zero(),
183 "The similarity scaling factor must not be zero."
184 );
185
186 self.scaling *= scaling
187 }
188
189 #[inline]
191 pub fn append_scaling_mut(&mut self, scaling: T) {
192 assert!(
193 !scaling.is_zero(),
194 "The similarity scaling factor must not be zero."
195 );
196
197 self.isometry.translation.vector *= scaling.clone();
198 self.scaling *= scaling;
199 }
200
201 #[inline]
203 pub fn append_translation_mut(&mut self, t: &Translation<T, D>) {
204 self.isometry.append_translation_mut(t)
205 }
206
207 #[inline]
209 pub fn append_rotation_mut(&mut self, r: &R) {
210 self.isometry.append_rotation_mut(r)
211 }
212
213 #[inline]
216 pub fn append_rotation_wrt_point_mut(&mut self, r: &R, p: &Point<T, D>) {
217 self.isometry.append_rotation_wrt_point_mut(r, p)
218 }
219
220 #[inline]
223 pub fn append_rotation_wrt_center_mut(&mut self, r: &R) {
224 self.isometry.append_rotation_wrt_center_mut(r)
225 }
226
227 #[inline]
243 #[must_use]
244 pub fn transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
245 self * pt
246 }
247
248 #[inline]
265 #[must_use]
266 pub fn transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
267 self * v
268 }
269
270 #[inline]
286 #[must_use]
287 pub fn inverse_transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
288 self.isometry.inverse_transform_point(pt) / self.scaling()
289 }
290
291 #[inline]
307 #[must_use]
308 pub fn inverse_transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
309 self.isometry.inverse_transform_vector(v) / self.scaling()
310 }
311}
312
313impl<T: SimdRealField, R, const D: usize> Similarity<T, R, D> {
318 #[inline]
320 #[must_use]
321 pub fn to_homogeneous(&self) -> OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>
322 where
323 Const<D>: DimNameAdd<U1>,
324 R: SubsetOf<OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>>,
325 DefaultAllocator: Allocator<DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>,
326 {
327 let mut res = self.isometry.to_homogeneous();
328
329 for e in res.fixed_view_mut::<D, D>(0, 0).iter_mut() {
330 *e *= self.scaling.clone()
331 }
332
333 res
334 }
335}
336
337impl<T: SimdRealField, R, const D: usize> Eq for Similarity<T, R, D> where
338 R: AbstractRotation<T, D> + Eq
339{
340}
341
342impl<T: SimdRealField, R, const D: usize> PartialEq for Similarity<T, R, D>
343where
344 R: AbstractRotation<T, D> + PartialEq,
345{
346 #[inline]
347 fn eq(&self, right: &Self) -> bool {
348 self.isometry == right.isometry && self.scaling == right.scaling
349 }
350}
351
352impl<T: RealField, R, const D: usize> AbsDiffEq for Similarity<T, R, D>
353where
354 R: AbstractRotation<T, D> + AbsDiffEq<Epsilon = T::Epsilon>,
355 T::Epsilon: Clone,
356{
357 type Epsilon = T::Epsilon;
358
359 #[inline]
360 fn default_epsilon() -> Self::Epsilon {
361 T::default_epsilon()
362 }
363
364 #[inline]
365 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
366 self.isometry.abs_diff_eq(&other.isometry, epsilon.clone())
367 && self.scaling.abs_diff_eq(&other.scaling, epsilon)
368 }
369}
370
371impl<T: RealField, R, const D: usize> RelativeEq for Similarity<T, R, D>
372where
373 R: AbstractRotation<T, D> + RelativeEq<Epsilon = T::Epsilon>,
374 T::Epsilon: Clone,
375{
376 #[inline]
377 fn default_max_relative() -> Self::Epsilon {
378 T::default_max_relative()
379 }
380
381 #[inline]
382 fn relative_eq(
383 &self,
384 other: &Self,
385 epsilon: Self::Epsilon,
386 max_relative: Self::Epsilon,
387 ) -> bool {
388 self.isometry
389 .relative_eq(&other.isometry, epsilon.clone(), max_relative.clone())
390 && self
391 .scaling
392 .relative_eq(&other.scaling, epsilon, max_relative)
393 }
394}
395
396impl<T: RealField, R, const D: usize> UlpsEq for Similarity<T, R, D>
397where
398 R: AbstractRotation<T, D> + UlpsEq<Epsilon = T::Epsilon>,
399 T::Epsilon: Clone,
400{
401 #[inline]
402 fn default_max_ulps() -> u32 {
403 T::default_max_ulps()
404 }
405
406 #[inline]
407 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
408 self.isometry
409 .ulps_eq(&other.isometry, epsilon.clone(), max_ulps)
410 && self.scaling.ulps_eq(&other.scaling, epsilon, max_ulps)
411 }
412}
413
414impl<T, R, const D: usize> fmt::Display for Similarity<T, R, D>
420where
421 T: RealField + fmt::Display,
422 R: AbstractRotation<T, D> + fmt::Display,
423{
424 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425 let precision = f.precision().unwrap_or(3);
426
427 writeln!(f, "Similarity {{")?;
428 write!(f, "{:.*}", precision, self.isometry)?;
429 write!(f, "Scaling: {:.*}", precision, self.scaling)?;
430 writeln!(f, "}}")
431 }
432}