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(
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<
211        T: Scalar + Zero + One + ClosedMulAssign + ClosedAddAssign,
212        D: DimName,
213        S: Storage<T, D, D>,
214    > SquareMatrix<T, D, S>
215{
216    /// Computes the transformation equal to `self` followed by an uniform scaling factor.
217    #[inline]
218    #[must_use = "Did you mean to use append_scaling_mut()?"]
219    pub fn append_scaling(&self, scaling: T) -> OMatrix<T, D, D>
220    where
221        D: DimNameSub<U1>,
222        DefaultAllocator: Allocator<D, D>,
223    {
224        let mut res = self.clone_owned();
225        res.append_scaling_mut(scaling);
226        res
227    }
228
229    /// Computes the transformation equal to an uniform scaling factor followed by `self`.
230    #[inline]
231    #[must_use = "Did you mean to use prepend_scaling_mut()?"]
232    pub fn prepend_scaling(&self, scaling: T) -> OMatrix<T, D, D>
233    where
234        D: DimNameSub<U1>,
235        DefaultAllocator: Allocator<D, D>,
236    {
237        let mut res = self.clone_owned();
238        res.prepend_scaling_mut(scaling);
239        res
240    }
241
242    /// Computes the transformation equal to `self` followed by a non-uniform scaling factor.
243    #[inline]
244    #[must_use = "Did you mean to use append_nonuniform_scaling_mut()?"]
245    pub fn append_nonuniform_scaling<SB>(
246        &self,
247        scaling: &Vector<T, DimNameDiff<D, U1>, SB>,
248    ) -> OMatrix<T, D, D>
249    where
250        D: DimNameSub<U1>,
251        SB: Storage<T, DimNameDiff<D, U1>>,
252        DefaultAllocator: Allocator<D, D>,
253    {
254        let mut res = self.clone_owned();
255        res.append_nonuniform_scaling_mut(scaling);
256        res
257    }
258
259    /// Computes the transformation equal to a non-uniform scaling factor followed by `self`.
260    #[inline]
261    #[must_use = "Did you mean to use prepend_nonuniform_scaling_mut()?"]
262    pub fn prepend_nonuniform_scaling<SB>(
263        &self,
264        scaling: &Vector<T, DimNameDiff<D, U1>, SB>,
265    ) -> OMatrix<T, D, D>
266    where
267        D: DimNameSub<U1>,
268        SB: Storage<T, DimNameDiff<D, U1>>,
269        DefaultAllocator: Allocator<D, D>,
270    {
271        let mut res = self.clone_owned();
272        res.prepend_nonuniform_scaling_mut(scaling);
273        res
274    }
275
276    /// Computes the transformation equal to `self` followed by a translation.
277    #[inline]
278    #[must_use = "Did you mean to use append_translation_mut()?"]
279    pub fn append_translation<SB>(
280        &self,
281        shift: &Vector<T, DimNameDiff<D, U1>, SB>,
282    ) -> OMatrix<T, D, D>
283    where
284        D: DimNameSub<U1>,
285        SB: Storage<T, DimNameDiff<D, U1>>,
286        DefaultAllocator: Allocator<D, D>,
287    {
288        let mut res = self.clone_owned();
289        res.append_translation_mut(shift);
290        res
291    }
292
293    /// Computes the transformation equal to a translation followed by `self`.
294    #[inline]
295    #[must_use = "Did you mean to use prepend_translation_mut()?"]
296    pub fn prepend_translation<SB>(
297        &self,
298        shift: &Vector<T, DimNameDiff<D, U1>, SB>,
299    ) -> OMatrix<T, D, D>
300    where
301        D: DimNameSub<U1>,
302        SB: Storage<T, DimNameDiff<D, U1>>,
303        DefaultAllocator: Allocator<D, D> + Allocator<DimNameDiff<D, U1>>,
304    {
305        let mut res = self.clone_owned();
306        res.prepend_translation_mut(shift);
307        res
308    }
309
310    /// Computes in-place the transformation equal to `self` followed by an uniform scaling factor.
311    #[inline]
312    pub fn append_scaling_mut(&mut self, scaling: T)
313    where
314        S: StorageMut<T, D, D>,
315        D: DimNameSub<U1>,
316    {
317        let mut to_scale = self.rows_generic_mut(0, DimNameDiff::<D, U1>::name());
318        to_scale *= scaling;
319    }
320
321    /// Computes in-place the transformation equal to an uniform scaling factor followed by `self`.
322    #[inline]
323    pub fn prepend_scaling_mut(&mut self, scaling: T)
324    where
325        S: StorageMut<T, D, D>,
326        D: DimNameSub<U1>,
327    {
328        let mut to_scale = self.columns_generic_mut(0, DimNameDiff::<D, U1>::name());
329        to_scale *= scaling;
330    }
331
332    /// Computes in-place the transformation equal to `self` followed by a non-uniform scaling factor.
333    #[inline]
334    pub fn append_nonuniform_scaling_mut<SB>(&mut self, scaling: &Vector<T, DimNameDiff<D, U1>, SB>)
335    where
336        S: StorageMut<T, D, D>,
337        D: DimNameSub<U1>,
338        SB: Storage<T, DimNameDiff<D, U1>>,
339    {
340        for i in 0..scaling.len() {
341            let mut to_scale = self.fixed_rows_mut::<1>(i);
342            to_scale *= scaling[i].clone();
343        }
344    }
345
346    /// Computes in-place the transformation equal to a non-uniform scaling factor followed by `self`.
347    #[inline]
348    pub fn prepend_nonuniform_scaling_mut<SB>(
349        &mut self,
350        scaling: &Vector<T, DimNameDiff<D, U1>, SB>,
351    ) where
352        S: StorageMut<T, D, D>,
353        D: DimNameSub<U1>,
354        SB: Storage<T, DimNameDiff<D, U1>>,
355    {
356        for i in 0..scaling.len() {
357            let mut to_scale = self.fixed_columns_mut::<1>(i);
358            to_scale *= scaling[i].clone();
359        }
360    }
361
362    /// Computes the transformation equal to `self` followed by a translation.
363    #[inline]
364    pub fn append_translation_mut<SB>(&mut self, shift: &Vector<T, DimNameDiff<D, U1>, SB>)
365    where
366        S: StorageMut<T, D, D>,
367        D: DimNameSub<U1>,
368        SB: Storage<T, DimNameDiff<D, U1>>,
369    {
370        for i in 0..D::dim() {
371            for j in 0..D::dim() - 1 {
372                let add = shift[j].clone() * self[(D::dim() - 1, i)].clone();
373                self[(j, i)] += add;
374            }
375        }
376    }
377
378    /// Computes the transformation equal to a translation followed by `self`.
379    #[inline]
380    pub fn prepend_translation_mut<SB>(&mut self, shift: &Vector<T, DimNameDiff<D, U1>, SB>)
381    where
382        D: DimNameSub<U1>,
383        S: StorageMut<T, D, D>,
384        SB: Storage<T, DimNameDiff<D, U1>>,
385        DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
386    {
387        let scale = self
388            .generic_view(
389                (D::dim() - 1, 0),
390                (Const::<1>, DimNameDiff::<D, U1>::name()),
391            )
392            .tr_dot(shift);
393        let post_translation = self.generic_view(
394            (0, 0),
395            (DimNameDiff::<D, U1>::name(), DimNameDiff::<D, U1>::name()),
396        ) * shift;
397
398        self[(D::dim() - 1, D::dim() - 1)] += scale;
399
400        let mut translation = self.generic_view_mut(
401            (0, D::dim() - 1),
402            (DimNameDiff::<D, U1>::name(), Const::<1>),
403        );
404        translation += post_translation;
405    }
406}
407
408/// # Transformation of vectors and points
409impl<T: RealField, D: DimNameSub<U1>, S: Storage<T, D, D>> SquareMatrix<T, D, S>
410where
411    DefaultAllocator: Allocator<D, D>
412        + Allocator<DimNameDiff<D, U1>>
413        + Allocator<DimNameDiff<D, U1>, DimNameDiff<D, U1>>,
414{
415    /// Transforms the given vector, assuming the matrix `self` uses homogeneous coordinates.
416    #[inline]
417    pub fn transform_vector(
418        &self,
419        v: &OVector<T, DimNameDiff<D, U1>>,
420    ) -> OVector<T, DimNameDiff<D, U1>> {
421        let transform = self.generic_view(
422            (0, 0),
423            (DimNameDiff::<D, U1>::name(), DimNameDiff::<D, U1>::name()),
424        );
425        let normalizer = self.generic_view(
426            (D::dim() - 1, 0),
427            (Const::<1>, DimNameDiff::<D, U1>::name()),
428        );
429        let n = normalizer.tr_dot(v);
430
431        if !n.is_zero() {
432            return transform * (v / n);
433        }
434
435        transform * v
436    }
437}
438
439impl<T: RealField, S: Storage<T, Const<3>, Const<3>>> SquareMatrix<T, Const<3>, S> {
440    /// Transforms the given point, assuming the matrix `self` uses homogeneous coordinates.
441    #[inline]
442    pub fn transform_point(&self, pt: &Point<T, 2>) -> Point<T, 2> {
443        let transform = self.fixed_view::<2, 2>(0, 0);
444        let translation = self.fixed_view::<2, 1>(0, 2);
445        let normalizer = self.fixed_view::<1, 2>(2, 0);
446        let n = normalizer.tr_dot(&pt.coords) + unsafe { self.get_unchecked((2, 2)).clone() };
447
448        if !n.is_zero() {
449            (transform * pt + translation) / n
450        } else {
451            transform * pt + translation
452        }
453    }
454}
455
456impl<T: RealField, S: Storage<T, Const<4>, Const<4>>> SquareMatrix<T, Const<4>, S> {
457    /// Transforms the given point, assuming the matrix `self` uses homogeneous coordinates.
458    #[inline]
459    pub fn transform_point(&self, pt: &Point<T, 3>) -> Point<T, 3> {
460        let transform = self.fixed_view::<3, 3>(0, 0);
461        let translation = self.fixed_view::<3, 1>(0, 3);
462        let normalizer = self.fixed_view::<1, 3>(3, 0);
463        let n = normalizer.tr_dot(&pt.coords) + unsafe { self.get_unchecked((3, 3)).clone() };
464
465        if !n.is_zero() {
466            (transform * pt + translation) / n
467        } else {
468            transform * pt + translation
469        }
470    }
471}