rust_3d/
matrix4.rs

1/*
2Copyright 2016 Martin Buck
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"),
6to deal in the Software without restriction, including without limitation the
7rights to use, copy, modify, merge, publish, distribute, sublicense,
8and/or sell copies of the Software, and to permit persons to whom the Software
9is furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall
12be included all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
20OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21*/
22
23//! Matrix4, a matrix with 4 rows and columns
24
25use std::ops::Mul;
26
27use crate::*;
28
29//------------------------------------------------------------------------------
30
31#[derive(Debug, PartialEq, PartialOrd, Clone)]
32/// Matrix4, a matrix with 4 rows and columns
33pub struct Matrix4 {
34    pub data: [[f64; 4]; 4],
35}
36
37impl Matrix4 {
38    /// Creates a new identity matrix
39    pub fn identity() -> Matrix4 {
40        Matrix4 {
41            data: [
42                [1.0, 0.0, 0.0, 0.0],
43                [0.0, 1.0, 0.0, 0.0],
44                [0.0, 0.0, 1.0, 0.0],
45                [0.0, 0.0, 0.0, 1.0],
46            ],
47        }
48    }
49    /// Creates a new matrix which contains only zeroes
50    pub fn zeroes() -> Matrix4 {
51        Matrix4 {
52            data: [
53                [0.0, 0.0, 0.0, 0.0],
54                [0.0, 0.0, 0.0, 0.0],
55                [0.0, 0.0, 0.0, 0.0],
56                [0.0, 0.0, 0.0, 0.0],
57            ],
58        }
59    }
60    /// Creates a new matrix which applies translation
61    pub fn translation(x: f64, y: f64, z: f64) -> Matrix4 {
62        Matrix4 {
63            data: [
64                [1.0, 0.0, 0.0, x],
65                [0.0, 1.0, 0.0, y],
66                [0.0, 0.0, 1.0, z],
67                [0.0, 0.0, 0.0, 1.0],
68            ],
69        }
70    }
71    /// Creates a new matrix which applies scaling
72    pub fn scale(x: f64, y: f64, z: f64) -> Matrix4 {
73        Matrix4 {
74            data: [
75                [x, 0.0, 0.0, 0.0],
76                [0.0, y, 0.0, 0.0],
77                [0.0, 0.0, z, 0.0],
78                [0.0, 0.0, 0.0, 1.0],
79            ],
80        }
81    }
82    /// Creates a new matrix which applies rotation
83    pub fn rotation(x: Rad, y: Rad, z: Rad) -> Matrix4 {
84        let (mut mx, mut my, mut mz) = (Matrix4::default(), Matrix4::default(), Matrix4::default());
85        let (rad_x, rad_y, rad_z) = (x.val, y.val, z.val);
86
87        mx.data[0][0] = 1.0;
88        mx.data[0][1] = 0.0;
89        mx.data[0][2] = 0.0;
90        mx.data[0][3] = 0.0;
91        mx.data[1][0] = 0.0;
92        mx.data[1][1] = rad_x.cos();
93        mx.data[1][2] = -rad_x.sin();
94        mx.data[1][3] = 0.0;
95        mx.data[2][0] = 0.0;
96        mx.data[2][1] = rad_x.sin();
97        mx.data[2][2] = rad_x.cos();
98        mx.data[2][3] = 0.0;
99        mx.data[3][0] = 0.0;
100        mx.data[3][1] = 0.0;
101        mx.data[3][2] = 0.0;
102        mx.data[3][3] = 1.0;
103
104        my.data[0][0] = rad_y.cos();
105        my.data[0][1] = 0.0;
106        my.data[0][2] = rad_y.sin();
107        my.data[0][3] = 0.0;
108        my.data[1][0] = 0.0;
109        my.data[1][1] = 1.0;
110        my.data[1][2] = 0.0;
111        my.data[1][3] = 0.0;
112        my.data[2][0] = -rad_y.sin();
113        my.data[2][1] = 0.0;
114        my.data[2][2] = rad_y.cos();
115        my.data[2][3] = 0.0;
116        my.data[3][0] = 0.0;
117        my.data[3][1] = 0.0;
118        my.data[3][2] = 0.0;
119        my.data[3][3] = 1.0;
120
121        mz.data[0][0] = rad_z.cos();
122        mz.data[0][1] = -rad_z.sin();
123        mz.data[0][2] = 0.0;
124        mz.data[0][3] = 0.0;
125        mz.data[1][0] = rad_z.sin();
126        mz.data[1][1] = rad_z.cos();
127        mz.data[1][2] = 0.0;
128        mz.data[1][3] = 0.0;
129        mz.data[2][0] = 0.0;
130        mz.data[2][1] = 0.0;
131        mz.data[2][2] = 1.0;
132        mz.data[2][3] = 0.0;
133        mz.data[3][0] = 0.0;
134        mz.data[3][1] = 0.0;
135        mz.data[3][2] = 0.0;
136        mz.data[3][3] = 1.0;
137
138        mx * my * mz
139    }
140    /// Creates a new matrix which applies rotation around an axis
141    pub fn rotation_axis<N>(axis: &N, r: Rad) -> Matrix4
142    where
143        N: IsNormalized3D,
144    {
145        let rad = r.val;
146        let ref u = axis;
147        let mut result = Matrix4::default();
148        result.data[0][0] = rad.cos() + u.x() * u.x() * (1.0 - rad.cos());
149        result.data[0][1] = u.x() * u.y() * (1.0 - rad.cos()) - u.z() * rad.sin();
150        result.data[0][2] = u.x() * u.z() * (1.0 - rad.cos()) + u.y() * rad.sin();
151        result.data[0][3] = 0.0;
152        result.data[1][0] = u.y() * u.x() * (1.0 - rad.cos()) + u.z() * rad.sin();
153        result.data[1][1] = rad.cos() + u.y() * u.y() * (1.0 - rad.cos());
154        result.data[1][2] = u.y() * u.z() * (1.0 - rad.cos()) - u.x() * rad.sin();
155        result.data[1][3] = 0.0;
156        result.data[2][0] = u.z() * u.x() * (1.0 - rad.cos()) - u.y() * rad.sin();
157        result.data[2][1] = u.z() * u.y() * (1.0 - rad.cos()) + u.x() * rad.sin();
158        result.data[2][2] = rad.cos() + u.z() * u.z() * (1.0 - rad.cos());
159        result.data[2][3] = 0.0;
160        result.data[3][0] = 0.0;
161        result.data[3][1] = 0.0;
162        result.data[3][2] = 0.0;
163        result.data[3][3] = 1.0;
164        result
165    }
166    /// Creates a new matrix which applies perspective transformation
167    pub fn perspective(close: f64, away: f64, fov: Rad) -> Matrix4 {
168        let fov_rad = fov.val;
169        let range = close - away;
170        let tan_fov_half = (fov_rad / 2.0).tan();
171
172        let mut result = Matrix4::default();
173        result.data[0][0] = 1.0 / (tan_fov_half * away);
174        result.data[0][1] = 0.0;
175        result.data[0][2] = 0.0;
176        result.data[0][3] = 0.0;
177        result.data[1][0] = 0.0;
178        result.data[1][1] = 1.0 / tan_fov_half;
179        result.data[1][2] = 0.0;
180        result.data[1][3] = 0.0;
181        result.data[2][0] = 0.0;
182        result.data[2][1] = 0.0;
183        result.data[2][2] = (-close - away) / range;
184        result.data[2][3] = 2.0 * away * close / range;
185        result.data[3][0] = 0.0;
186        result.data[3][1] = 0.0;
187        result.data[3][2] = 1.0;
188        result.data[3][3] = 1.0;
189        result
190    }
191    /// Creates a new matrix which applies a look at transformation
192    pub fn look_at<P, N>(target: &P, up: &N) -> Result<Matrix4>
193    where
194        P: IsBuildable3D,
195        N: IsNormalized3D,
196    {
197        let n = target.normalized()?;
198        let u = cross(&*up, target);
199        let v = cross(&n, &u);
200
201        let mut result = Matrix4::default();
202        result.data[0][0] = u.x();
203        result.data[0][1] = u.y();
204        result.data[0][2] = u.z();
205        result.data[0][3] = 0.0;
206        result.data[1][0] = v.x();
207        result.data[1][1] = v.y();
208        result.data[1][2] = v.z();
209        result.data[1][3] = 0.0;
210        result.data[2][0] = n.x();
211        result.data[2][1] = n.y();
212        result.data[2][2] = n.z();
213        result.data[2][3] = 0.0;
214        result.data[3][0] = 0.0;
215        result.data[3][1] = 0.0;
216        result.data[3][2] = 0.0;
217        result.data[3][3] = 1.0;
218        Ok(result)
219    }
220}
221
222//------------------------------------------------------------------------------
223
224impl Default for Matrix4 {
225    fn default() -> Self {
226        Self::identity()
227    }
228}
229
230impl Mul for Matrix4 {
231    type Output = Self;
232
233    fn mul(self, other: Self) -> Self {
234        let mut result = Matrix4::default();
235        for i in 0..4 {
236            for j in 0..4 {
237                result.data[i][j] = self.data[i][0] * other.data[0][j]
238                    + self.data[i][1] * other.data[1][j]
239                    + self.data[i][2] * other.data[2][j]
240                    + self.data[i][3] * other.data[3][j];
241            }
242        }
243        result
244    }
245}
246
247impl Mul<&Matrix4> for Matrix4 {
248    type Output = Self;
249
250    fn mul(self, other: &Self) -> Self {
251        let mut result = Matrix4::default();
252        for i in 0..4 {
253            for j in 0..4 {
254                result.data[i][j] = self.data[i][0] * other.data[0][j]
255                    + self.data[i][1] * other.data[1][j]
256                    + self.data[i][2] * other.data[2][j]
257                    + self.data[i][3] * other.data[3][j];
258            }
259        }
260        result
261    }
262}
263
264impl Mul for &Matrix4 {
265    type Output = Matrix4;
266
267    fn mul(self, other: Self) -> Matrix4 {
268        let mut result = Matrix4::default();
269        for i in 0..4 {
270            for j in 0..4 {
271                result.data[i][j] = self.data[i][0] * other.data[0][j]
272                    + self.data[i][1] * other.data[1][j]
273                    + self.data[i][2] * other.data[2][j]
274                    + self.data[i][3] * other.data[3][j];
275            }
276        }
277        result
278    }
279}
280
281impl Mul<f64> for Matrix4 {
282    type Output = Self;
283
284    fn mul(self, other: f64) -> Self {
285        let mut result = Matrix4::default();
286        for i in 0..4 {
287            for j in 0..4 {
288                result.data[i][j] = other * self.data[i][j];
289            }
290        }
291        result
292    }
293}
294
295impl Mul<f64> for &Matrix4 {
296    type Output = Matrix4;
297
298    fn mul(self, other: f64) -> Matrix4 {
299        let mut result = Matrix4::default();
300        for i in 0..4 {
301            for j in 0..4 {
302                result.data[i][j] = other * self.data[i][j];
303            }
304        }
305        result
306    }
307}