graphene/matrix.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{fmt, ops};
4
5use glib::translate::*;
6
7use crate::{ffi, Matrix, Point, Point3D, Vec3, Vec4};
8
9impl Matrix {
10 /// Initializes a [`Matrix`][crate::Matrix] from the values of an affine
11 /// transformation matrix.
12 ///
13 /// The arguments map to the following matrix layout:
14 ///
15 ///
16 ///
17 /// **⚠️ The following code is in plain ⚠️**
18 ///
19 /// ```plain
20 /// ⎛ xx yx ⎞ ⎛ a b 0 ⎞
21 /// ⎜ xy yy ⎟ = ⎜ c d 0 ⎟
22 /// ⎝ x0 y0 ⎠ ⎝ tx ty 1 ⎠
23 /// ```
24 ///
25 /// This function can be used to convert between an affine matrix type
26 /// from other libraries and a [`Matrix`][crate::Matrix].
27 /// ## `xx`
28 /// the xx member
29 /// ## `yx`
30 /// the yx member
31 /// ## `xy`
32 /// the xy member
33 /// ## `yy`
34 /// the yy member
35 /// ## `x_0`
36 /// the x0 member
37 /// ## `y_0`
38 /// the y0 member
39 ///
40 /// # Returns
41 ///
42 /// the initialized matrix
43 #[doc(alias = "graphene_matrix_init_from_2d")]
44 #[doc(alias = "init_from_2d")]
45 pub fn from_2d(xx: f64, yx: f64, xy: f64, yy: f64, x_0: f64, y_0: f64) -> Self {
46 assert_initialized_main_thread!();
47 unsafe {
48 let mut mat = Self::uninitialized();
49 ffi::graphene_matrix_init_from_2d(mat.to_glib_none_mut().0, xx, yx, xy, yy, x_0, y_0);
50 mat
51 }
52 }
53
54 /// Initializes a [`Matrix`][crate::Matrix] with the given array of floating
55 /// point values.
56 /// ## `v`
57 /// an array of at least 16 floating
58 /// point values
59 ///
60 /// # Returns
61 ///
62 /// the initialized matrix
63 #[doc(alias = "graphene_matrix_init_from_float")]
64 #[doc(alias = "init_from_float")]
65 pub fn from_float(v: [f32; 16]) -> Self {
66 assert_initialized_main_thread!();
67 unsafe {
68 let mut mat = Self::uninitialized();
69 ffi::graphene_matrix_init_from_float(mat.to_glib_none_mut().0, v.as_ptr() as *const _);
70 mat
71 }
72 }
73
74 /// Initializes a [`Matrix`][crate::Matrix] with the given four row
75 /// vectors.
76 /// ## `v0`
77 /// the first row vector
78 /// ## `v1`
79 /// the second row vector
80 /// ## `v2`
81 /// the third row vector
82 /// ## `v3`
83 /// the fourth row vector
84 ///
85 /// # Returns
86 ///
87 /// the initialized matrix
88 #[doc(alias = "graphene_matrix_init_from_vec4")]
89 #[doc(alias = "init_from_vec4")]
90 pub fn from_vec4(v0: &Vec4, v1: &Vec4, v2: &Vec4, v3: &Vec4) -> Self {
91 assert_initialized_main_thread!();
92 unsafe {
93 let mut mat = Self::uninitialized();
94 ffi::graphene_matrix_init_from_vec4(
95 mat.to_glib_none_mut().0,
96 v0.to_glib_none().0,
97 v1.to_glib_none().0,
98 v2.to_glib_none().0,
99 v3.to_glib_none().0,
100 );
101 mat
102 }
103 }
104
105 /// Initializes a [`Matrix`][crate::Matrix] compatible with [`Frustum`][crate::Frustum].
106 ///
107 /// See also: [`Frustum::from_matrix()`][crate::Frustum::from_matrix()]
108 /// ## `left`
109 /// distance of the left clipping plane
110 /// ## `right`
111 /// distance of the right clipping plane
112 /// ## `bottom`
113 /// distance of the bottom clipping plane
114 /// ## `top`
115 /// distance of the top clipping plane
116 /// ## `z_near`
117 /// distance of the near clipping plane
118 /// ## `z_far`
119 /// distance of the far clipping plane
120 ///
121 /// # Returns
122 ///
123 /// the initialized matrix
124 #[doc(alias = "graphene_matrix_init_frustum")]
125 #[doc(alias = "init_frustum")]
126 pub fn new_frustum(
127 left: f32,
128 right: f32,
129 bottom: f32,
130 top: f32,
131 z_near: f32,
132 z_far: f32,
133 ) -> Self {
134 assert_initialized_main_thread!();
135 unsafe {
136 let mut mat = Self::uninitialized();
137 ffi::graphene_matrix_init_frustum(
138 mat.to_glib_none_mut().0,
139 left,
140 right,
141 bottom,
142 top,
143 z_near,
144 z_far,
145 );
146 mat
147 }
148 }
149
150 /// Initializes a [`Matrix`][crate::Matrix] with the identity matrix.
151 ///
152 /// # Returns
153 ///
154 /// the initialized matrix
155 #[doc(alias = "graphene_matrix_init_identity")]
156 #[doc(alias = "init_identity")]
157 pub fn new_identity() -> Self {
158 assert_initialized_main_thread!();
159 unsafe {
160 let mut mat = Self::uninitialized();
161 ffi::graphene_matrix_init_identity(mat.to_glib_none_mut().0);
162 mat
163 }
164 }
165
166 /// Initializes a [`Matrix`][crate::Matrix] so that it positions the "camera"
167 /// at the given `eye` coordinates towards an object at the `center`
168 /// coordinates. The top of the camera is aligned to the direction
169 /// of the `up` vector.
170 ///
171 /// Before the transform, the camera is assumed to be placed at the
172 /// origin, looking towards the negative Z axis, with the top side of
173 /// the camera facing in the direction of the Y axis and the right
174 /// side in the direction of the X axis.
175 ///
176 /// In theory, one could use `self` to transform a model of such a camera
177 /// into world-space. However, it is more common to use the inverse of
178 /// `self` to transform another object from world coordinates to the view
179 /// coordinates of the camera. Typically you would then apply the
180 /// camera projection transform to get from view to screen
181 /// coordinates.
182 /// ## `eye`
183 /// the vector describing the position to look from
184 /// ## `center`
185 /// the vector describing the position to look at
186 /// ## `up`
187 /// the vector describing the world's upward direction; usually,
188 /// this is the [`Vec3::y_axis()`][crate::Vec3::y_axis()] vector
189 ///
190 /// # Returns
191 ///
192 /// the initialized matrix
193 #[doc(alias = "graphene_matrix_init_look_at")]
194 #[doc(alias = "init_look_at")]
195 pub fn new_look_at(eye: &Vec3, center: &Vec3, up: &Vec3) -> Self {
196 assert_initialized_main_thread!();
197 unsafe {
198 let mut mat = Self::uninitialized();
199 ffi::graphene_matrix_init_look_at(
200 mat.to_glib_none_mut().0,
201 eye.to_glib_none().0,
202 center.to_glib_none().0,
203 up.to_glib_none().0,
204 );
205 mat
206 }
207 }
208
209 /// Initializes a [`Matrix`][crate::Matrix] with an orthographic projection.
210 /// ## `left`
211 /// the left edge of the clipping plane
212 /// ## `right`
213 /// the right edge of the clipping plane
214 /// ## `top`
215 /// the top edge of the clipping plane
216 /// ## `bottom`
217 /// the bottom edge of the clipping plane
218 /// ## `z_near`
219 /// the distance of the near clipping plane
220 /// ## `z_far`
221 /// the distance of the far clipping plane
222 ///
223 /// # Returns
224 ///
225 /// the initialized matrix
226 #[doc(alias = "graphene_matrix_init_ortho")]
227 #[doc(alias = "init_ortho")]
228 pub fn new_ortho(
229 left: f32,
230 right: f32,
231 top: f32,
232 bottom: f32,
233 z_near: f32,
234 z_far: f32,
235 ) -> Self {
236 assert_initialized_main_thread!();
237 unsafe {
238 let mut mat = Self::uninitialized();
239 ffi::graphene_matrix_init_ortho(
240 mat.to_glib_none_mut().0,
241 left,
242 right,
243 top,
244 bottom,
245 z_near,
246 z_far,
247 );
248 mat
249 }
250 }
251
252 /// Initializes a [`Matrix`][crate::Matrix] with a perspective projection.
253 /// ## `fovy`
254 /// the field of view angle, in degrees
255 /// ## `aspect`
256 /// the aspect value
257 /// ## `z_near`
258 /// the near Z plane
259 /// ## `z_far`
260 /// the far Z plane
261 ///
262 /// # Returns
263 ///
264 /// the initialized matrix
265 #[doc(alias = "graphene_matrix_init_perspective")]
266 #[doc(alias = "init_perspective")]
267 pub fn new_perspective(fovy: f32, aspect: f32, z_near: f32, z_far: f32) -> Self {
268 assert_initialized_main_thread!();
269 unsafe {
270 let mut mat = Self::uninitialized();
271 ffi::graphene_matrix_init_perspective(
272 mat.to_glib_none_mut().0,
273 fovy,
274 aspect,
275 z_near,
276 z_far,
277 );
278 mat
279 }
280 }
281
282 /// Initializes `self` to represent a rotation of `angle` degrees on
283 /// the axis represented by the `axis` vector.
284 /// ## `angle`
285 /// the rotation angle, in degrees
286 /// ## `axis`
287 /// the axis vector as a [`Vec3`][crate::Vec3]
288 ///
289 /// # Returns
290 ///
291 /// the initialized matrix
292 #[doc(alias = "graphene_matrix_init_rotate")]
293 #[doc(alias = "init_rotate")]
294 pub fn new_rotate(angle: f32, axis: &Vec3) -> Self {
295 assert_initialized_main_thread!();
296 unsafe {
297 let mut mat = Self::uninitialized();
298 ffi::graphene_matrix_init_rotate(
299 mat.to_glib_none_mut().0,
300 angle,
301 axis.to_glib_none().0,
302 );
303 mat
304 }
305 }
306
307 /// Initializes a [`Matrix`][crate::Matrix] with the given scaling factors.
308 /// ## `x`
309 /// the scale factor on the X axis
310 /// ## `y`
311 /// the scale factor on the Y axis
312 /// ## `z`
313 /// the scale factor on the Z axis
314 ///
315 /// # Returns
316 ///
317 /// the initialized matrix
318 #[doc(alias = "graphene_matrix_init_scale")]
319 #[doc(alias = "init_scale")]
320 pub fn new_scale(x: f32, y: f32, z: f32) -> Self {
321 assert_initialized_main_thread!();
322 unsafe {
323 let mut mat = Self::uninitialized();
324 ffi::graphene_matrix_init_scale(mat.to_glib_none_mut().0, x, y, z);
325 mat
326 }
327 }
328
329 /// Initializes a [`Matrix`][crate::Matrix] with a skew transformation
330 /// with the given factors.
331 /// ## `x_skew`
332 /// skew factor, in radians, on the X axis
333 /// ## `y_skew`
334 /// skew factor, in radians, on the Y axis
335 ///
336 /// # Returns
337 ///
338 /// the initialized matrix
339 #[doc(alias = "graphene_matrix_init_skew")]
340 #[doc(alias = "init_skew")]
341 pub fn new_skew(x_skew: f32, y_skew: f32) -> Self {
342 assert_initialized_main_thread!();
343 unsafe {
344 let mut mat = Self::uninitialized();
345 ffi::graphene_matrix_init_skew(mat.to_glib_none_mut().0, x_skew, y_skew);
346 mat
347 }
348 }
349
350 /// Initializes a [`Matrix`][crate::Matrix] with a translation to the
351 /// given coordinates.
352 /// ## `p`
353 /// the translation coordinates
354 ///
355 /// # Returns
356 ///
357 /// the initialized matrix
358 #[doc(alias = "graphene_matrix_init_translate")]
359 #[doc(alias = "init_translate")]
360 pub fn new_translate(p: &Point3D) -> Self {
361 assert_initialized_main_thread!();
362 unsafe {
363 let mut mat = Self::uninitialized();
364 ffi::graphene_matrix_init_translate(mat.to_glib_none_mut().0, p.to_glib_none().0);
365 mat
366 }
367 }
368
369 /// Converts a [`Matrix`][crate::Matrix] to an array of floating point
370 /// values.
371 ///
372 /// # Returns
373 ///
374 ///
375 /// ## `v`
376 /// return location
377 /// for an array of floating point values. The array must be capable
378 /// of holding at least 16 values.
379 #[doc(alias = "graphene_matrix_to_float")]
380 pub fn to_float(&self) -> [f32; 16] {
381 unsafe {
382 let mut out = std::mem::MaybeUninit::uninit();
383 ffi::graphene_matrix_to_float(self.to_glib_none().0, out.as_mut_ptr());
384 out.assume_init()
385 }
386 }
387
388 #[inline]
389 pub fn values(&self) -> &[[f32; 4]; 4] {
390 unsafe { &*(&self.inner.value as *const ffi::graphene_simd4x4f_t as *const [[f32; 4]; 4]) }
391 }
392}
393
394impl fmt::Debug for Matrix {
395 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396 f.debug_struct("Matrix")
397 .field("values", &self.values())
398 .finish()
399 }
400}
401
402impl Default for Matrix {
403 fn default() -> Self {
404 Self::new_identity()
405 }
406}
407
408// Scalar multiplication
409impl ops::Mul<Matrix> for f32 {
410 type Output = Matrix;
411
412 fn mul(self, mut rhs: Matrix) -> Self::Output {
413 rhs.scale(self, self, self);
414 rhs
415 }
416}
417
418// Matrix-matrix/-vector multiplication
419impl ops::Mul<Matrix> for Matrix {
420 type Output = Matrix;
421
422 fn mul(self, rhs: Matrix) -> Self::Output {
423 Matrix::multiply(&self, &rhs)
424 }
425}
426impl ops::MulAssign<Matrix> for Matrix {
427 fn mul_assign(&mut self, rhs: Matrix) {
428 *self = *self * rhs;
429 }
430}
431
432impl ops::Mul<Vec4> for Matrix {
433 type Output = Vec4;
434
435 /// Transforms this `Vec4` using the provided matrix.
436 /// See [Matrix::transform_vec4].
437 fn mul(self, rhs: Vec4) -> Self::Output {
438 Matrix::transform_vec4(&self, &rhs)
439 }
440}
441
442impl ops::Mul<Vec3> for Matrix {
443 type Output = Vec3;
444
445 /// Transforms this `Vec3` using the provided matrix.
446 /// See [Matrix::transform_vec3].
447 fn mul(self, rhs: Vec3) -> Self::Output {
448 Matrix::transform_vec3(&self, &rhs)
449 }
450}
451
452impl ops::Mul<Point> for Matrix {
453 type Output = Point;
454
455 fn mul(self, rhs: Point) -> Self::Output {
456 Matrix::transform_point(&self, &rhs)
457 }
458}
459
460impl ops::Mul<Point3D> for Matrix {
461 type Output = Point3D;
462
463 /// Transforms this point using the provided matrix.
464 /// See [Matrix::transform_point3d].
465 fn mul(self, rhs: Point3D) -> Self::Output {
466 Matrix::transform_point3d(&self, &rhs)
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use super::Matrix;
473 #[test]
474 fn test_matrix_values() {
475 let matrix = Matrix::new_identity();
476 assert_eq!(
477 matrix.values(),
478 &[
479 [1.0, 0.0, 0.0, 0.0],
480 [0.0, 1.0, 0.0, 0.0],
481 [0.0, 0.0, 1.0, 0.0],
482 [0.0, 0.0, 0.0, 1.0]
483 ],
484 );
485 }
486}