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