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