nalgebra/base/
cg.rs

1/*
2 *
3 * Computer-graphics specific implementations.
4 * Currently, it is mostly implemented for homogeneous matrices in 2- and 3-space.
5 *
6 */
7
8use num::{One, Zero};
9
10use crate::base::allocator::Allocator;
11use crate::base::dimension::{DimName, DimNameDiff, DimNameSub, U1};
12use crate::base::storage::{Storage, StorageMut};
13use crate::base::{
14    Const, DefaultAllocator, Matrix3, Matrix4, OMatrix, OVector, Scalar, SquareMatrix, Unit,
15    Vector, Vector2, Vector3,
16};
17use crate::geometry::{
18    Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point2, Point3, Rotation2,
19    Rotation3,
20};
21
22use simba::scalar::{ClosedAdd, ClosedMul, RealField};
23
24/// # Translation and scaling in any dimension
25impl<T, D: DimName> OMatrix<T, D, D>
26where
27    T: Scalar + Zero + One,
28    DefaultAllocator: Allocator<T, D, D>,
29{
30    /// Creates a new homogeneous matrix that applies the same scaling factor on each dimension.
31    #[inline]
32    pub fn new_scaling(scaling: T) -> Self {
33        let mut res = Self::from_diagonal_element(scaling);
34        res[(D::dim() - 1, D::dim() - 1)] = T::one();
35
36        res
37    }
38
39    /// Creates a new homogeneous matrix that applies a distinct scaling factor for each dimension.
40    #[inline]
41    pub fn new_nonuniform_scaling<SB>(scaling: &Vector<T, DimNameDiff<D, U1>, SB>) -> Self
42    where
43        D: DimNameSub<U1>,
44        SB: Storage<T, DimNameDiff<D, U1>>,
45    {
46        let mut res = Self::identity();
47        for i in 0..scaling.len() {
48            res[(i, i)] = scaling[i].clone();
49        }
50
51        res
52    }
53
54    /// Creates a new homogeneous matrix that applies a pure translation.
55    #[inline]
56    pub fn new_translation<SB>(translation: &Vector<T, DimNameDiff<D, U1>, SB>) -> Self
57    where
58        D: DimNameSub<U1>,
59        SB: Storage<T, DimNameDiff<D, U1>>,
60    {
61        let mut res = Self::identity();
62        res.generic_slice_mut(
63            (0, D::dim() - 1),
64            (DimNameDiff::<D, U1>::name(), Const::<1>),
65        )
66        .copy_from(translation);
67
68        res
69    }
70}
71
72/// # 2D transformations as a Matrix3
73impl<T: RealField> Matrix3<T> {
74    /// Builds a 2 dimensional homogeneous rotation matrix from an angle in radian.
75    #[inline]
76    pub fn new_rotation(angle: T) -> Self {
77        Rotation2::new(angle).to_homogeneous()
78    }
79
80    /// Creates a new homogeneous matrix that applies a scaling factor for each dimension with respect to point.
81    ///
82    /// Can be used to implement `zoom_to` functionality.
83    #[inline]
84    pub fn new_nonuniform_scaling_wrt_point(scaling: &Vector2<T>, pt: &Point2<T>) -> Self {
85        let zero = T::zero();
86        let one = T::one();
87        Matrix3::new(
88            scaling.x.clone(),
89            zero.clone(),
90            pt.x.clone() - pt.x.clone() * scaling.x.clone(),
91            zero.clone(),
92            scaling.y.clone(),
93            pt.y.clone() - pt.y.clone() * scaling.y.clone(),
94            zero.clone(),
95            zero,
96            one,
97        )
98    }
99}
100
101/// # 3D transformations as a Matrix4
102impl<T: RealField> Matrix4<T> {
103    /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
104    ///
105    /// Returns the identity matrix if the given argument is zero.
106    #[inline]
107    pub fn new_rotation(axisangle: Vector3<T>) -> Self {
108        Rotation3::new(axisangle).to_homogeneous()
109    }
110
111    /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
112    ///
113    /// Returns the identity matrix if the given argument is zero.
114    #[inline]
115    pub fn new_rotation_wrt_point(axisangle: Vector3<T>, pt: Point3<T>) -> Self {
116        let rot = Rotation3::from_scaled_axis(axisangle);
117        Isometry::rotation_wrt_point(rot, pt).to_homogeneous()
118    }
119
120    /// Creates a new homogeneous matrix that applies a scaling factor for each dimension with respect to point.
121    ///
122    /// Can be used to implement `zoom_to` functionality.
123    #[inline]
124    pub fn new_nonuniform_scaling_wrt_point(scaling: &Vector3<T>, pt: &Point3<T>) -> Self {
125        let zero = T::zero();
126        let one = T::one();
127        Matrix4::new(
128            scaling.x.clone(),
129            zero.clone(),
130            zero.clone(),
131            pt.x.clone() - pt.x.clone() * scaling.x.clone(),
132            zero.clone(),
133            scaling.y.clone(),
134            zero.clone(),
135            pt.y.clone() - pt.y.clone() * scaling.y.clone(),
136            zero.clone(),
137            zero.clone(),
138            scaling.z.clone(),
139            pt.z.clone() - pt.z.clone() * scaling.z.clone(),
140            zero.clone(),
141            zero.clone(),
142            zero,
143            one,
144        )
145    }
146
147    /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
148    ///
149    /// Returns the identity matrix if the given argument is zero.
150    /// This is identical to `Self::new_rotation`.
151    #[inline]
152    pub fn from_scaled_axis(axisangle: Vector3<T>) -> Self {
153        Rotation3::from_scaled_axis(axisangle).to_homogeneous()
154    }
155
156    /// Creates a new rotation from Euler angles.
157    ///
158    /// The primitive rotations are applied in order: 1 roll − 2 pitch − 3 yaw.
159    pub fn from_euler_angles(roll: T, pitch: T, yaw: T) -> Self {
160        Rotation3::from_euler_angles(roll, pitch, yaw).to_homogeneous()
161    }
162
163    /// Builds a 3D homogeneous rotation matrix from an axis and a rotation angle.
164    pub fn from_axis_angle(axis: &Unit<Vector3<T>>, angle: T) -> Self {
165        Rotation3::from_axis_angle(axis, angle).to_homogeneous()
166    }
167
168    /// Creates a new homogeneous matrix for an orthographic projection.
169    #[inline]
170    pub fn new_orthographic(left: T, right: T, bottom: T, top: T, znear: T, zfar: T) -> Self {
171        Orthographic3::new(left, right, bottom, top, znear, zfar).into_inner()
172    }
173
174    /// Creates a new homogeneous matrix for a perspective projection.
175    #[inline]
176    pub fn new_perspective(aspect: T, fovy: T, znear: T, zfar: T) -> Self {
177        Perspective3::new(aspect, fovy, znear, zfar).into_inner()
178    }
179
180    /// Creates an isometry that corresponds to the local frame of an observer standing at the
181    /// point `eye` and looking toward `target`.
182    ///
183    /// It maps the view direction `target - eye` to the positive `z` axis and the origin to the
184    /// `eye`.
185    #[inline]
186    pub fn face_towards(eye: &Point3<T>, target: &Point3<T>, up: &Vector3<T>) -> Self {
187        IsometryMatrix3::face_towards(eye, target, up).to_homogeneous()
188    }
189
190    /// Deprecated: Use [`Matrix4::face_towards`] instead.
191    #[deprecated(note = "renamed to `face_towards`")]
192    pub fn new_observer_frame(eye: &Point3<T>, target: &Point3<T>, up: &Vector3<T>) -> Self {
193        Matrix4::face_towards(eye, target, up)
194    }
195
196    /// Builds a right-handed look-at view matrix.
197    #[inline]
198    pub fn look_at_rh(eye: &Point3<T>, target: &Point3<T>, up: &Vector3<T>) -> Self {
199        IsometryMatrix3::look_at_rh(eye, target, up).to_homogeneous()
200    }
201
202    /// Builds a left-handed look-at view matrix.
203    #[inline]
204    pub fn look_at_lh(eye: &Point3<T>, target: &Point3<T>, up: &Vector3<T>) -> Self {
205        IsometryMatrix3::look_at_lh(eye, target, up).to_homogeneous()
206    }
207}
208
209/// # Append/prepend translation and scaling
210impl<T: Scalar + Zero + One + ClosedMul + ClosedAdd, D: DimName, S: Storage<T, D, D>>
211    SquareMatrix<T, D, S>
212{
213    /// Computes the transformation equal to `self` followed by an uniform scaling factor.
214    #[inline]
215    #[must_use = "Did you mean to use append_scaling_mut()?"]
216    pub fn append_scaling(&self, scaling: T) -> OMatrix<T, D, D>
217    where
218        D: DimNameSub<U1>,
219        DefaultAllocator: Allocator<T, D, D>,
220    {
221        let mut res = self.clone_owned();
222        res.append_scaling_mut(scaling);
223        res
224    }
225
226    /// Computes the transformation equal to an uniform scaling factor followed by `self`.
227    #[inline]
228    #[must_use = "Did you mean to use prepend_scaling_mut()?"]
229    pub fn prepend_scaling(&self, scaling: T) -> OMatrix<T, D, D>
230    where
231        D: DimNameSub<U1>,
232        DefaultAllocator: Allocator<T, D, D>,
233    {
234        let mut res = self.clone_owned();
235        res.prepend_scaling_mut(scaling);
236        res
237    }
238
239    /// Computes the transformation equal to `self` followed by a non-uniform scaling factor.
240    #[inline]
241    #[must_use = "Did you mean to use append_nonuniform_scaling_mut()?"]
242    pub fn append_nonuniform_scaling<SB>(
243        &self,
244        scaling: &Vector<T, DimNameDiff<D, U1>, SB>,
245    ) -> OMatrix<T, D, D>
246    where
247        D: DimNameSub<U1>,
248        SB: Storage<T, DimNameDiff<D, U1>>,
249        DefaultAllocator: Allocator<T, D, D>,
250    {
251        let mut res = self.clone_owned();
252        res.append_nonuniform_scaling_mut(scaling);
253        res
254    }
255
256    /// Computes the transformation equal to a non-uniform scaling factor followed by `self`.
257    #[inline]
258    #[must_use = "Did you mean to use prepend_nonuniform_scaling_mut()?"]
259    pub fn prepend_nonuniform_scaling<SB>(
260        &self,
261        scaling: &Vector<T, DimNameDiff<D, U1>, SB>,
262    ) -> OMatrix<T, D, D>
263    where
264        D: DimNameSub<U1>,
265        SB: Storage<T, DimNameDiff<D, U1>>,
266        DefaultAllocator: Allocator<T, D, D>,
267    {
268        let mut res = self.clone_owned();
269        res.prepend_nonuniform_scaling_mut(scaling);
270        res
271    }
272
273    /// Computes the transformation equal to `self` followed by a translation.
274    #[inline]
275    #[must_use = "Did you mean to use append_translation_mut()?"]
276    pub fn append_translation<SB>(
277        &self,
278        shift: &Vector<T, DimNameDiff<D, U1>, SB>,
279    ) -> OMatrix<T, D, D>
280    where
281        D: DimNameSub<U1>,
282        SB: Storage<T, DimNameDiff<D, U1>>,
283        DefaultAllocator: Allocator<T, D, D>,
284    {
285        let mut res = self.clone_owned();
286        res.append_translation_mut(shift);
287        res
288    }
289
290    /// Computes the transformation equal to a translation followed by `self`.
291    #[inline]
292    #[must_use = "Did you mean to use prepend_translation_mut()?"]
293    pub fn prepend_translation<SB>(
294        &self,
295        shift: &Vector<T, DimNameDiff<D, U1>, SB>,
296    ) -> OMatrix<T, D, D>
297    where
298        D: DimNameSub<U1>,
299        SB: Storage<T, DimNameDiff<D, U1>>,
300        DefaultAllocator: Allocator<T, D, D> + Allocator<T, DimNameDiff<D, U1>>,
301    {
302        let mut res = self.clone_owned();
303        res.prepend_translation_mut(shift);
304        res
305    }
306
307    /// Computes in-place the transformation equal to `self` followed by an uniform scaling factor.
308    #[inline]
309    pub fn append_scaling_mut(&mut self, scaling: T)
310    where
311        S: StorageMut<T, D, D>,
312        D: DimNameSub<U1>,
313    {
314        let mut to_scale = self.rows_generic_mut(0, DimNameDiff::<D, U1>::name());
315        to_scale *= scaling;
316    }
317
318    /// Computes in-place the transformation equal to an uniform scaling factor followed by `self`.
319    #[inline]
320    pub fn prepend_scaling_mut(&mut self, scaling: T)
321    where
322        S: StorageMut<T, D, D>,
323        D: DimNameSub<U1>,
324    {
325        let mut to_scale = self.columns_generic_mut(0, DimNameDiff::<D, U1>::name());
326        to_scale *= scaling;
327    }
328
329    /// Computes in-place the transformation equal to `self` followed by a non-uniform scaling factor.
330    #[inline]
331    pub fn append_nonuniform_scaling_mut<SB>(&mut self, scaling: &Vector<T, DimNameDiff<D, U1>, SB>)
332    where
333        S: StorageMut<T, D, D>,
334        D: DimNameSub<U1>,
335        SB: Storage<T, DimNameDiff<D, U1>>,
336    {
337        for i in 0..scaling.len() {
338            let mut to_scale = self.fixed_rows_mut::<1>(i);
339            to_scale *= scaling[i].clone();
340        }
341    }
342
343    /// Computes in-place the transformation equal to a non-uniform scaling factor followed by `self`.
344    #[inline]
345    pub fn prepend_nonuniform_scaling_mut<SB>(
346        &mut self,
347        scaling: &Vector<T, DimNameDiff<D, U1>, SB>,
348    ) where
349        S: StorageMut<T, D, D>,
350        D: DimNameSub<U1>,
351        SB: Storage<T, DimNameDiff<D, U1>>,
352    {
353        for i in 0..scaling.len() {
354            let mut to_scale = self.fixed_columns_mut::<1>(i);
355            to_scale *= scaling[i].clone();
356        }
357    }
358
359    /// Computes the transformation equal to `self` followed by a translation.
360    #[inline]
361    pub fn append_translation_mut<SB>(&mut self, shift: &Vector<T, DimNameDiff<D, U1>, SB>)
362    where
363        S: StorageMut<T, D, D>,
364        D: DimNameSub<U1>,
365        SB: Storage<T, DimNameDiff<D, U1>>,
366    {
367        for i in 0..D::dim() {
368            for j in 0..D::dim() - 1 {
369                let add = shift[j].clone() * self[(D::dim() - 1, i)].clone();
370                self[(j, i)] += add;
371            }
372        }
373    }
374
375    /// Computes the transformation equal to a translation followed by `self`.
376    #[inline]
377    pub fn prepend_translation_mut<SB>(&mut self, shift: &Vector<T, DimNameDiff<D, U1>, SB>)
378    where
379        D: DimNameSub<U1>,
380        S: StorageMut<T, D, D>,
381        SB: Storage<T, DimNameDiff<D, U1>>,
382        DefaultAllocator: Allocator<T, DimNameDiff<D, U1>>,
383    {
384        let scale = self
385            .generic_slice(
386                (D::dim() - 1, 0),
387                (Const::<1>, DimNameDiff::<D, U1>::name()),
388            )
389            .tr_dot(shift);
390        let post_translation = self.generic_slice(
391            (0, 0),
392            (DimNameDiff::<D, U1>::name(), DimNameDiff::<D, U1>::name()),
393        ) * shift;
394
395        self[(D::dim() - 1, D::dim() - 1)] += scale;
396
397        let mut translation = self.generic_slice_mut(
398            (0, D::dim() - 1),
399            (DimNameDiff::<D, U1>::name(), Const::<1>),
400        );
401        translation += post_translation;
402    }
403}
404
405/// # Transformation of vectors and points
406impl<T: RealField, D: DimNameSub<U1>, S: Storage<T, D, D>> SquareMatrix<T, D, S>
407where
408    DefaultAllocator: Allocator<T, D, D>
409        + Allocator<T, DimNameDiff<D, U1>>
410        + Allocator<T, DimNameDiff<D, U1>, DimNameDiff<D, U1>>,
411{
412    /// Transforms the given vector, assuming the matrix `self` uses homogeneous coordinates.
413    #[inline]
414    pub fn transform_vector(
415        &self,
416        v: &OVector<T, DimNameDiff<D, U1>>,
417    ) -> OVector<T, DimNameDiff<D, U1>> {
418        let transform = self.generic_slice(
419            (0, 0),
420            (DimNameDiff::<D, U1>::name(), DimNameDiff::<D, U1>::name()),
421        );
422        let normalizer = self.generic_slice(
423            (D::dim() - 1, 0),
424            (Const::<1>, DimNameDiff::<D, U1>::name()),
425        );
426        let n = normalizer.tr_dot(v);
427
428        if !n.is_zero() {
429            return transform * (v / n);
430        }
431
432        transform * v
433    }
434}
435
436impl<T: RealField, S: Storage<T, Const<3>, Const<3>>> SquareMatrix<T, Const<3>, S> {
437    /// Transforms the given point, assuming the matrix `self` uses homogeneous coordinates.
438    #[inline]
439    pub fn transform_point(&self, pt: &Point<T, 2>) -> Point<T, 2> {
440        let transform = self.fixed_slice::<2, 2>(0, 0);
441        let translation = self.fixed_slice::<2, 1>(0, 2);
442        let normalizer = self.fixed_slice::<1, 2>(2, 0);
443        let n = normalizer.tr_dot(&pt.coords) + unsafe { self.get_unchecked((2, 2)).clone() };
444
445        if !n.is_zero() {
446            (transform * pt + translation) / n
447        } else {
448            transform * pt + translation
449        }
450    }
451}
452
453impl<T: RealField, S: Storage<T, Const<4>, Const<4>>> SquareMatrix<T, Const<4>, S> {
454    /// Transforms the given point, assuming the matrix `self` uses homogeneous coordinates.
455    #[inline]
456    pub fn transform_point(&self, pt: &Point<T, 3>) -> Point<T, 3> {
457        let transform = self.fixed_slice::<3, 3>(0, 0);
458        let translation = self.fixed_slice::<3, 1>(0, 3);
459        let normalizer = self.fixed_slice::<1, 3>(3, 0);
460        let n = normalizer.tr_dot(&pt.coords) + unsafe { self.get_unchecked((3, 3)).clone() };
461
462        if !n.is_zero() {
463            (transform * pt + translation) / n
464        } else {
465            transform * pt + translation
466        }
467    }
468}