scad_tree_math/
mt4.rs

1// MIT License
2//
3// Copyright (c) 2023 Michael H. Phillips
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22//
23
24use crate::{dcos, dsin, dtan, Pt3, Pt4};
25
26/// A matrix with column major order.
27#[derive(Clone, Copy, Default, PartialEq)]
28pub struct Mt4 {
29    pub x: Pt4,
30    pub y: Pt4,
31    pub z: Pt4,
32    pub w: Pt4,
33}
34
35impl std::fmt::Display for Mt4 {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        writeln!(f, "| {} {} {} {} |", self.x.x, self.y.x, self.z.x, self.w.x)?;
38        writeln!(f, "| {} {} {} {} |", self.x.y, self.y.y, self.z.y, self.w.y)?;
39        writeln!(f, "| {} {} {} {} |", self.x.z, self.y.z, self.z.z, self.w.z)?;
40        writeln!(f, "| {} {} {} {} |", self.x.w, self.y.w, self.z.w, self.w.w)
41    }
42}
43
44impl Mt4 {
45    pub fn new(x: Pt4, y: Pt4, z: Pt4, w: Pt4) -> Self {
46        Self { x, y, z, w }
47    }
48
49    pub fn transposed(&self) -> Self {
50        Mt4::new(
51            Pt4::new(self.x.x, self.y.x, self.z.x, self.w.x),
52            Pt4::new(self.x.y, self.y.y, self.z.y, self.w.y),
53            Pt4::new(self.x.z, self.y.z, self.z.z, self.w.z),
54            Pt4::new(self.x.w, self.y.w, self.z.w, self.w.w),
55        )
56    }
57
58    pub fn identity() -> Self {
59        Mt4::new(
60            Pt4::new(1.0, 0.0, 0.0, 0.0),
61            Pt4::new(0.0, 1.0, 0.0, 0.0),
62            Pt4::new(0.0, 0.0, 1.0, 0.0),
63            Pt4::new(0.0, 0.0, 0.0, 1.0),
64        )
65    }
66
67    pub fn scale_matrix(x: f64, y: f64, z: f64) -> Self {
68        let mut result = Mt4::identity();
69        result.x.x = x;
70        result.y.y = y;
71        result.z.z = z;
72        result
73    }
74
75    pub fn translate_matrix(x: f64, y: f64, z: f64) -> Self {
76        let mut result = Mt4::identity();
77        result.w.x = x;
78        result.w.y = y;
79        result.w.z = z;
80        result
81    }
82
83    pub fn rot_x_matrix(degrees: f64) -> Self {
84        let c = dcos(degrees);
85        let s = dsin(degrees);
86        Mt4::new(
87            Pt4::new(1.0, 0.0, 0.0, 0.0),
88            Pt4::new(0.0, c, -s, 0.0),
89            Pt4::new(0.0, s, c, 0.0),
90            Pt4::new(0.0, 0.0, 0.0, 1.0),
91        )
92        .transposed()
93    }
94
95    pub fn rot_y_matrix(degrees: f64) -> Self {
96        let c = dcos(degrees);
97        let s = dsin(degrees);
98        Mt4::new(
99            Pt4::new(c, 0.0, s, 0.0),
100            Pt4::new(0.0, 1.0, 0.0, 0.0),
101            Pt4::new(-s, 0.0, c, 0.0),
102            Pt4::new(0.0, 0.0, 0.0, 1.0),
103        )
104        .transposed()
105    }
106
107    pub fn rot_z_matrix(degrees: f64) -> Self {
108        let c = dcos(degrees);
109        let s = dsin(degrees);
110        Mt4::new(
111            Pt4::new(c, -s, 0.0, 0.0),
112            Pt4::new(s, c, 0.0, 0.0),
113            Pt4::new(0.0, 0.0, 1.0, 0.0),
114            Pt4::new(0.0, 0.0, 0.0, 1.0),
115        )
116        .transposed()
117    }
118
119    pub fn rot_vec(x: f64, y: f64, z: f64, degrees: f64) -> Self {
120        let c = dcos(degrees);
121        let s = dsin(degrees);
122        Mt4::new(
123            Pt4::new(
124                c + x * x * (1.0 - c),
125                x * y * (1.0 - c) - z * s,
126                x * z * (1.0 - c) + y * s,
127                0.0,
128            ),
129            Pt4::new(
130                y * x * (1.0 - c) + z * s,
131                c + y * y * (1.0 - c),
132                y * z * (1.0 - c) - x * s,
133                0.0,
134            ),
135            Pt4::new(
136                z * x * (1.0 - c) - z * s,
137                z * y * (1.0 - c) + x * s,
138                c + z * z * (1.0 - c),
139                0.0,
140            ),
141            Pt4::new(0.0, 0.0, 0.0, 1.0),
142        )
143        .transposed()
144    }
145
146    pub fn perspective_matrix(fovy: f64, aspect: f64, near: f64, far: f64) -> Self {
147        let tan_half_fovy = dtan(fovy / 2.0);
148        Mt4::new(
149            Pt4::new(1.0 / (aspect * tan_half_fovy), 0.0, 0.0, 0.0),
150            Pt4::new(0.0, 1.0 / tan_half_fovy, 0.0, 0.0),
151            Pt4::new(
152                0.0,
153                0.0,
154                -(far + near) / (far - near),
155                -(2.0 * far * near) / (far - near),
156            ),
157            Pt4::new(0.0, 0.0, -1.0, 1.0),
158        )
159        .transposed()
160    }
161
162    pub fn inverse(&self) -> Option<Self> {
163        let mut out = Mt4::identity();
164
165        out[0] = self[5] * self[10] * self[15]
166            - self[5] * self[11] * self[14]
167            - self[9] * self[6] * self[15]
168            + self[9] * self[7] * self[14]
169            + self[13] * self[6] * self[11]
170            - self[13] * self[7] * self[10];
171
172        out[4] = -self[4] * self[10] * self[15]
173            + self[4] * self[11] * self[14]
174            + self[8] * self[6] * self[15]
175            - self[8] * self[7] * self[14]
176            - self[12] * self[6] * self[11]
177            + self[12] * self[7] * self[10];
178
179        out[8] = self[4] * self[9] * self[15]
180            - self[4] * self[11] * self[13]
181            - self[8] * self[5] * self[15]
182            + self[8] * self[7] * self[13]
183            + self[12] * self[5] * self[11]
184            - self[12] * self[7] * self[9];
185
186        out[12] = -self[4] * self[9] * self[14]
187            + self[4] * self[10] * self[13]
188            + self[8] * self[5] * self[14]
189            - self[8] * self[6] * self[13]
190            - self[12] * self[5] * self[10]
191            + self[12] * self[6] * self[9];
192
193        out[1] = -self[1] * self[10] * self[15]
194            + self[1] * self[11] * self[14]
195            + self[9] * self[2] * self[15]
196            - self[9] * self[3] * self[14]
197            - self[13] * self[2] * self[11]
198            + self[13] * self[3] * self[10];
199
200        out[5] = self[0] * self[10] * self[15]
201            - self[0] * self[11] * self[14]
202            - self[8] * self[2] * self[15]
203            + self[8] * self[3] * self[14]
204            + self[12] * self[2] * self[11]
205            - self[12] * self[3] * self[10];
206
207        out[9] = -self[0] * self[9] * self[15]
208            + self[0] * self[11] * self[13]
209            + self[8] * self[1] * self[15]
210            - self[8] * self[3] * self[13]
211            - self[12] * self[1] * self[11]
212            + self[12] * self[3] * self[9];
213
214        out[13] = self[0] * self[9] * self[14]
215            - self[0] * self[10] * self[13]
216            - self[8] * self[1] * self[14]
217            + self[8] * self[2] * self[13]
218            + self[12] * self[1] * self[10]
219            - self[12] * self[2] * self[9];
220
221        out[2] = self[1] * self[6] * self[15]
222            - self[1] * self[7] * self[14]
223            - self[5] * self[2] * self[15]
224            + self[5] * self[3] * self[14]
225            + self[13] * self[2] * self[7]
226            - self[13] * self[3] * self[6];
227
228        out[6] = -self[0] * self[6] * self[15]
229            + self[0] * self[7] * self[14]
230            + self[4] * self[2] * self[15]
231            - self[4] * self[3] * self[14]
232            - self[12] * self[2] * self[7]
233            + self[12] * self[3] * self[6];
234
235        out[10] = self[0] * self[5] * self[15]
236            - self[0] * self[7] * self[13]
237            - self[4] * self[1] * self[15]
238            + self[4] * self[3] * self[13]
239            + self[12] * self[1] * self[7]
240            - self[12] * self[3] * self[5];
241
242        out[14] = -self[0] * self[5] * self[14]
243            + self[0] * self[6] * self[13]
244            + self[4] * self[1] * self[14]
245            - self[4] * self[2] * self[13]
246            - self[12] * self[1] * self[6]
247            + self[12] * self[2] * self[5];
248
249        out[3] = -self[1] * self[6] * self[11]
250            + self[1] * self[7] * self[10]
251            + self[5] * self[2] * self[11]
252            - self[5] * self[3] * self[10]
253            - self[9] * self[2] * self[7]
254            + self[9] * self[3] * self[6];
255
256        out[7] = self[0] * self[6] * self[11]
257            - self[0] * self[7] * self[10]
258            - self[4] * self[2] * self[11]
259            + self[4] * self[3] * self[10]
260            + self[8] * self[2] * self[7]
261            - self[8] * self[3] * self[6];
262
263        out[11] = -self[0] * self[5] * self[11]
264            + self[0] * self[7] * self[9]
265            + self[4] * self[1] * self[11]
266            - self[4] * self[3] * self[9]
267            - self[8] * self[1] * self[7]
268            + self[8] * self[3] * self[5];
269
270        out[15] = self[0] * self[5] * self[10]
271            - self[0] * self[6] * self[9]
272            - self[4] * self[1] * self[10]
273            + self[4] * self[2] * self[9]
274            + self[8] * self[1] * self[6]
275            - self[8] * self[2] * self[5];
276
277        let mut det = self[0] * out[0] + self[1] * out[4] + self[2] * out[8] + self[3] * out[12];
278
279        if det == 0.0 {
280            None
281        } else {
282            det = 1.0 / det;
283            for i in 0..16 {
284                out[i] = out[i] * det;
285            }
286            Some(out)
287        }
288    }
289
290    pub fn look_at_matrix_rh(eye: Pt3, center: Pt3, up: Pt3) -> Self {
291        let mut f = center - eye;
292        f = f.normalized();
293        let mut s = f.cross(up);
294        s = s.normalized();
295        let u = s.cross(f);
296        Mt4::new(
297            Pt4::new(s.x, s.y, s.z, -s.dot(eye)),
298            Pt4::new(u.x, u.y, u.z, -u.dot(eye)),
299            Pt4::new(-f.x, -f.y, -f.z, f.dot(eye)),
300            Pt4::new(0.0, 0.0, 0.0, 1.0),
301        )
302    }
303
304    pub fn look_at_matrix_lh(eye: Pt3, center: Pt3, up: Pt3) -> Self {
305        let mut f = center - eye;
306        f = f.normalized();
307        let mut s = up.cross(f);
308        // parallel check
309        if s == Pt3::new(0.0, 0.0, 0.0) {
310            // anti-parallel check
311            if up.dot(f) < 0.0 {
312                return Mt4::rot_x_matrix(180.0);
313            }
314            return Mt4::identity();
315        }
316        s = s.normalized();
317        let u = f.cross(s);
318        Mt4::new(
319            Pt4::new(s.x, s.y, s.z, -s.dot(eye)),
320            Pt4::new(u.x, u.y, u.z, -u.dot(eye)),
321            Pt4::new(f.x, f.y, f.z, -f.dot(eye)),
322            Pt4::new(0.0, 0.0, 0.0, 1.0),
323        )
324    }
325
326    pub fn rotation_from_direction(direction: Pt3, up: Pt3) -> Self {
327        let x_axis: Pt3 = up.cross(direction).normalized();
328        let z_axis: Pt3 = direction.cross(x_axis).normalized();
329        let mut result = Mt4::identity();
330        result.x.x = x_axis.x;
331        result.x.y = direction.x;
332        result.x.z = z_axis.x;
333
334        result.y.x = x_axis.y;
335        result.y.y = direction.y;
336        result.y.z = z_axis.y;
337
338        result.z.x = x_axis.z;
339        result.z.y = direction.z;
340        result.z.z = z_axis.z;
341
342        result
343    }
344}
345
346impl std::ops::Mul<Pt4> for Mt4 {
347    type Output = Pt4;
348
349    fn mul(self, rhs: Pt4) -> Self::Output {
350        let t = self.transposed();
351        Pt4::new(t.x.dot(rhs), t.y.dot(rhs), t.z.dot(rhs), t.w.dot(rhs))
352    }
353}
354
355impl std::ops::Mul<Pt3> for Mt4 {
356    type Output = Pt3;
357
358    fn mul(self, rhs: Pt3) -> Self::Output {
359        let t = self.transposed();
360        Pt3::new(
361            t.x.as_pt3().dot(rhs),
362            t.y.as_pt3().dot(rhs),
363            t.z.as_pt3().dot(rhs),
364        )
365    }
366}
367
368impl std::ops::Mul<Mt4> for Mt4 {
369    type Output = Mt4;
370
371    fn mul(self, rhs: Mt4) -> Self::Output {
372        let t = self.transposed();
373        Mt4::new(
374            Pt4::new(
375                t.x.dot(rhs.x),
376                t.y.dot(rhs.x),
377                t.z.dot(rhs.x),
378                t.w.dot(rhs.x),
379            ),
380            Pt4::new(
381                t.x.dot(rhs.y),
382                t.y.dot(rhs.y),
383                t.z.dot(rhs.y),
384                t.w.dot(rhs.y),
385            ),
386            Pt4::new(
387                t.x.dot(rhs.z),
388                t.y.dot(rhs.z),
389                t.z.dot(rhs.z),
390                t.w.dot(rhs.z),
391            ),
392            Pt4::new(
393                t.x.dot(rhs.w),
394                t.y.dot(rhs.w),
395                t.z.dot(rhs.w),
396                t.w.dot(rhs.w),
397            ),
398        )
399    }
400}
401
402impl std::ops::Index<usize> for Mt4 {
403    type Output = f64;
404
405    fn index(&self, index: usize) -> &Self::Output {
406        match index {
407            0 => &self.x.x,
408            1 => &self.x.y,
409            2 => &self.x.z,
410            3 => &self.x.w,
411            4 => &self.y.x,
412            5 => &self.y.y,
413            6 => &self.y.z,
414            7 => &self.y.w,
415            8 => &self.z.x,
416            9 => &self.z.y,
417            10 => &self.z.z,
418            11 => &self.z.w,
419            12 => &self.w.x,
420            13 => &self.w.y,
421            14 => &self.w.z,
422            15 => &self.w.w,
423            _ => panic!("Index out of bounds"),
424        }
425    }
426}
427
428impl std::ops::IndexMut<usize> for Mt4 {
429    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
430        match index {
431            0 => &mut self.x.x,
432            1 => &mut self.x.y,
433            2 => &mut self.x.z,
434            3 => &mut self.x.w,
435            4 => &mut self.y.x,
436            5 => &mut self.y.y,
437            6 => &mut self.y.z,
438            7 => &mut self.y.w,
439            8 => &mut self.z.x,
440            9 => &mut self.z.y,
441            10 => &mut self.z.z,
442            11 => &mut self.z.w,
443            12 => &mut self.w.x,
444            13 => &mut self.w.y,
445            14 => &mut self.w.z,
446            15 => &mut self.w.w,
447            _ => panic!("Index out of bounds"),
448        }
449    }
450}