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