1 /// Matrix determinant and inverse
2 module gfx.math.inverse;
3 
4 import gfx.math.mat;
5 
6 pure @safe @nogc nothrow:
7 
8 // algos in this module are from GLM
9 
10 /// Compute the determinant of a matrix.
11 @property auto determinant(M)(in M m) if (isMat2!M)
12 {
13     return m.ct!(0, 0) * m.ct!(1, 1) - m.ct!(0, 1) * m.ct!(1, 0);
14 }
15 
16 /// ditto
17 @property auto determinant(M)(in M m) if (isMat3!M)
18 {
19     return
20         + m.ct!(0, 0) * (m.ct!(1, 1) * m.ct!(2, 2) - m.ct!(1, 2) * m.ct!(2, 1))
21         - m.ct!(0, 1) * (m.ct!(1, 0) * m.ct!(2, 2) - m.ct!(1, 2) * m.ct!(2, 0))
22         + m.ct!(0, 2) * (m.ct!(1, 0) * m.ct!(2, 1) - m.ct!(1, 1) * m.ct!(2, 0));
23 }
24 
25 /// ditto
26 @property auto determinant(M)(in M m) if (isMat4!M)
27 {
28     import gfx.math.vec : vec4;
29 
30     const subFactor00 = m.ct!(2, 2) * m.ct!(3, 3) - m.ct!(2, 3) * m.ct!(3, 2);
31     const subFactor01 = m.ct!(1, 2) * m.ct!(3, 3) - m.ct!(1, 3) * m.ct!(3, 2);
32     const subFactor02 = m.ct!(1, 2) * m.ct!(2, 3) - m.ct!(1, 3) * m.ct!(2, 2);
33     const subFactor03 = m.ct!(0, 2) * m.ct!(3, 3) - m.ct!(0, 3) * m.ct!(3, 2);
34     const subFactor04 = m.ct!(0, 2) * m.ct!(2, 3) - m.ct!(0, 3) * m.ct!(2, 2);
35     const subFactor05 = m.ct!(0, 2) * m.ct!(1, 3) - m.ct!(0, 3) * m.ct!(1, 2);
36 
37     const detCof = vec4 (
38         + (m.ct!(1, 1) * subFactor00 - m.ct!(2, 1) * subFactor01 + m.ct!(3, 1) * subFactor02),
39         - (m.ct!(0, 1) * subFactor00 - m.ct!(2, 1) * subFactor03 + m.ct!(3, 1) * subFactor04),
40         + (m.ct!(0, 1) * subFactor01 - m.ct!(1, 1) * subFactor03 + m.ct!(3, 1) * subFactor05),
41         - (m.ct!(0, 1) * subFactor02 - m.ct!(1, 1) * subFactor04 + m.ct!(2, 1) * subFactor05)
42     );
43 
44     return
45         m.ct!(0, 0) * detCof[0] + m.ct!(1, 0) * detCof[1] +
46         m.ct!(2, 0) * detCof[2] + m.ct!(3, 0) * detCof[3];
47 }
48 
49 /// Compute the inverse of a matrix
50 M inverse(M)(in M m) if (isMat2!M)
51 {
52     alias T = M.Component;
53 
54     const oneOverD = T(1) / (
55         + m.ct!(0, 0) * m.ct!(1, 1)
56         - m.ct!(0, 1) * m.ct!(1, 0));
57 
58     return Mat2!T (
59         + m.ct!(1, 1) * oneOverD,
60         - m.ct!(1, 0) * oneOverD,
61         - m.ct!(0, 1) * oneOverD,
62         + m.ct!(0, 0) * oneOverD
63     );
64 }
65 
66 /// ditto
67 M inverse(M)(in M m) if (isMat3!M)
68 {
69     alias T = M.Component;
70 
71     const oneOverD = T(1) / (
72         + m.ct!(0, 0) * (m.ct!(1, 1) * m.ct!(2, 2) - m.ct!(1, 2) * m.ct!(2, 1))
73         - m.ct!(0, 1) * (m.ct!(1, 0) * m.ct!(2, 2) - m.ct!(1, 2) * m.ct!(2, 0))
74         + m.ct!(0, 2) * (m.ct!(1, 0) * m.ct!(2, 1) - m.ct!(1, 1) * m.ct!(2, 0))
75     );
76 
77     return M(
78         + (m.ct!(1, 1) * m.ct!(2, 2) - m.ct!(1, 2) * m.ct!(2, 1)) * oneOverD,
79         - (m.ct!(0, 1) * m.ct!(2, 2) - m.ct!(0, 2) * m.ct!(2, 1)) * oneOverD,
80         + (m.ct!(0, 1) * m.ct!(1, 2) - m.ct!(0, 2) * m.ct!(1, 1)) * oneOverD,
81         - (m.ct!(1, 0) * m.ct!(2, 2) - m.ct!(1, 2) * m.ct!(2, 0)) * oneOverD,
82         + (m.ct!(0, 0) * m.ct!(2, 2) - m.ct!(0, 2) * m.ct!(2, 0)) * oneOverD,
83         - (m.ct!(0, 0) * m.ct!(1, 2) - m.ct!(0, 2) * m.ct!(1, 0)) * oneOverD,
84         + (m.ct!(1, 0) * m.ct!(2, 1) - m.ct!(1, 1) * m.ct!(2, 0)) * oneOverD,
85         - (m.ct!(0, 0) * m.ct!(2, 1) - m.ct!(0, 1) * m.ct!(2, 0)) * oneOverD,
86         + (m.ct!(0, 0) * m.ct!(1, 1) - m.ct!(0, 1) * m.ct!(1, 0)) * oneOverD,
87     );
88 }
89 
90 ///
91 unittest
92 {
93     /// Example from https://en.wikipedia.org/wiki/Gaussian_elimination
94     const m = FMat3(
95         2, -1, 0,
96         -1, 2, -1,
97         0, -1, 2
98     );
99     const invM = inverse(m);
100 
101     import gfx.math.approx : approxUlp;
102     assert(approxUlp(invM, FMat3(
103         0.75f, 0.5f, 0.25f,
104         0.5f,  1f,   0.5f,
105         0.25f, 0.5f, 0.75f
106     )));
107     assert(approxUlp(inverse(invM), m));
108 }
109 
110 
111 /// ditto
112 M inverse(M)(in M m) if (isMat4!M)
113 {
114     import gfx.math.vec : vec;
115 
116     alias T = M.Component;
117 
118     const coef00 = m.ct!(2, 2) * m.ct!(3, 3) - m.ct!(2, 3) * m.ct!(3, 2);
119     const coef02 = m.ct!(2, 1) * m.ct!(3, 3) - m.ct!(2, 3) * m.ct!(3, 1);
120     const coef03 = m.ct!(2, 1) * m.ct!(3, 2) - m.ct!(2, 2) * m.ct!(3, 1);
121 
122     const coef04 = m.ct!(1, 2) * m.ct!(3, 3) - m.ct!(1, 3) * m.ct!(3, 2);
123     const coef06 = m.ct!(1, 1) * m.ct!(3, 3) - m.ct!(1, 3) * m.ct!(3, 1);
124     const coef07 = m.ct!(1, 1) * m.ct!(3, 2) - m.ct!(1, 2) * m.ct!(3, 1);
125 
126     const coef08 = m.ct!(1, 2) * m.ct!(2, 3) - m.ct!(1, 3) * m.ct!(2, 2);
127     const coef10 = m.ct!(1, 1) * m.ct!(2, 3) - m.ct!(1, 3) * m.ct!(2, 1);
128     const coef11 = m.ct!(1, 1) * m.ct!(2, 2) - m.ct!(1, 2) * m.ct!(2, 1);
129 
130     const coef12 = m.ct!(0, 2) * m.ct!(3, 3) - m.ct!(0, 3) * m.ct!(3, 2);
131     const coef14 = m.ct!(0, 1) * m.ct!(3, 3) - m.ct!(0, 3) * m.ct!(3, 1);
132     const coef15 = m.ct!(0, 1) * m.ct!(3, 2) - m.ct!(0, 2) * m.ct!(3, 1);
133 
134     const coef16 = m.ct!(0, 2) * m.ct!(2, 3) - m.ct!(0, 3) * m.ct!(2, 2);
135     const coef18 = m.ct!(0, 1) * m.ct!(2, 3) - m.ct!(0, 3) * m.ct!(2, 1);
136     const coef19 = m.ct!(0, 1) * m.ct!(2, 2) - m.ct!(0, 2) * m.ct!(2, 1);
137 
138     const coef20 = m.ct!(0, 2) * m.ct!(1, 3) - m.ct!(0, 3) * m.ct!(1, 2);
139     const coef22 = m.ct!(0, 1) * m.ct!(1, 3) - m.ct!(0, 3) * m.ct!(1, 1);
140     const coef23 = m.ct!(0, 1) * m.ct!(1, 2) - m.ct!(0, 2) * m.ct!(1, 1);
141 
142     const fac0 = vec(coef00, coef00, coef02, coef03);
143     const fac1 = vec(coef04, coef04, coef06, coef07);
144     const fac2 = vec(coef08, coef08, coef10, coef11);
145     const fac3 = vec(coef12, coef12, coef14, coef15);
146     const fac4 = vec(coef16, coef16, coef18, coef19);
147     const fac5 = vec(coef20, coef20, coef22, coef23);
148 
149     const v0 = vec(m.ct!(0, 1), m.ct!(0, 0), m.ct!(0, 0), m.ct!(0, 0));
150     const v1 = vec(m.ct!(1, 1), m.ct!(1, 0), m.ct!(1, 0), m.ct!(1, 0));
151     const v2 = vec(m.ct!(2, 1), m.ct!(2, 0), m.ct!(2, 0), m.ct!(2, 0));
152     const v3 = vec(m.ct!(3, 1), m.ct!(3, 0), m.ct!(3, 0), m.ct!(3, 0));
153 
154     const inv0 = v1 * fac0 - v2 * fac1 + v3 * fac2;
155     const inv1 = v0 * fac0 - v2 * fac3 + v3 * fac4;
156     const inv2 = v0 * fac1 - v1 * fac3 + v3 * fac5;
157     const inv3 = v0 * fac2 - v1 * fac4 + v2 * fac5;
158 
159     const signA = vec(+1, -1, +1, -1);
160     const signB = vec(-1, +1, -1, +1);
161 
162     // GLM is column major!
163     // We have to transpose or refactor the algorithm
164     // Note: without this transpose gfx:math beats gl3n on inversion benchmark
165     const inverse = transpose(mat(
166         inv0 * signA, inv1 * signB, inv2 * signA, inv3 * signB
167     ));
168 
169     const dot0 = m.column(0) * inverse.row(0);
170     const dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w);
171 
172     const oneOverD = T(1) / dot1;
173 
174     return inverse * oneOverD;
175 }
176 
177 ///
178 unittest {
179     import gfx.math.transform : translation;
180     import gfx.math.approx : approxUlpAndAbs;
181 
182     const trM = translation!float(3, 4, 5);
183     const expected = translation!float(-3, -4, -5);
184     const inv = inverse(trM);
185 
186     assert(approxUlpAndAbs( inv, expected ));
187     assert(approxUlpAndAbs( inverse(inv), trM ));
188     assert(approxUlpAndAbs( inv * trM, FMat4.identity ));
189 }
190 
191 /// Fast matrix inverse for affine matrices
192 M affineInverse(M)(in M m) if(isMat3!M)
193 {
194     import gfx.math.vec : vec;
195     alias T = M.Component;
196 
197     const inv = inverse(m.slice!(0, 2, 0, 2));
198     const col = -inv * vec(m.column(2), T(1));
199 
200     return M (
201         vec!T( inv[0], col[0] ),
202         vec!T( inv[1], col[1] ),
203         vec!T( 0, 0,    1 ),
204     );
205 }
206 
207 /// ditto
208 M affineInverse(M)(in M m) if(isMat4!M)
209 {
210     import gfx.math.vec : vec;
211     alias T = M.Component;
212 
213     const inv = inverse(m.slice!(0, 3, 0, 3));
214     const col = -(inv * m.column(3).xyz);
215 
216     return M (
217         vec!T( inv[0], col[0] ),
218         vec!T( inv[1], col[1] ),
219         vec!T( inv[2], col[2] ),
220         vec!T( 0, 0, 0,    1 ),
221     );
222 }
223 
224 /// Compute the invert transpose of a matrix
225 M inverseTranspose(M)(in M m) if(isMat2!M)
226 {
227     alias T = M.Component;
228 
229     const oneOverD = T(1) / (m.ct!(0, 0) * m.ct!(1, 1) - m.ct!(0, 1) * m.ct!(1, 0));
230 
231     return M (
232         + m.ct!(1, 1) * oneOverD,
233         - m.ct!(0, 1) * oneOverD,
234         - m.ct!(1, 0) * oneOverD,
235         + m.ct!(0, 0) * oneOverD,
236     );
237 }
238 
239 /// ditto
240 M inverseTranspose(M)(in M m) if(isMat3!M)
241 {
242     alias T = M.Component;
243 
244     const oneOverD = T(1) / (
245         + m.ct!(0, 0) * (m.ct!(1, 1) * m.ct!(2, 2) - m.ct!(2, 1) * m.ct!(1, 2))
246         - m.ct!(1, 0) * (m.ct!(0, 1) * m.ct!(2, 2) - m.ct!(2, 1) * m.ct!(0, 2))
247         + m.ct!(2, 0) * (m.ct!(0, 1) * m.ct!(1, 2) - m.ct!(1, 1) * m.ct!(0, 2))
248     );
249 
250     return M (
251         + (m.ct!(1, 1) * m.ct!(2, 2) - m.ct!(1, 2) * m.ct!(2, 1)) * oneOverD,
252         - (m.ct!(0, 1) * m.ct!(2, 2) - m.ct!(0, 2) * m.ct!(2, 1)) * oneOverD,
253         + (m.ct!(0, 1) * m.ct!(1, 2) - m.ct!(0, 2) * m.ct!(1, 1)) * oneOverD,
254         - (m.ct!(1, 0) * m.ct!(2, 2) - m.ct!(1, 2) * m.ct!(2, 0)) * oneOverD,
255         + (m.ct!(0, 0) * m.ct!(2, 2) - m.ct!(0, 2) * m.ct!(2, 0)) * oneOverD,
256         - (m.ct!(0, 0) * m.ct!(1, 2) - m.ct!(0, 2) * m.ct!(1, 0)) * oneOverD,
257         + (m.ct!(1, 0) * m.ct!(2, 1) - m.ct!(1, 1) * m.ct!(2, 0)) * oneOverD,
258         - (m.ct!(0, 0) * m.ct!(2, 1) - m.ct!(0, 1) * m.ct!(2, 0)) * oneOverD,
259         + (m.ct!(0, 0) * m.ct!(1, 1) - m.ct!(0, 1) * m.ct!(1, 0)) * oneOverD,
260     );
261 }
262 
263 /// ditto
264 M inverseTranspose(M)(in M m) if(isMat4!M)
265 {
266     alias T = M.Component;
267 
268     const subFactor00 = m.ct!(2, 2) * m.ct!(3, 3) - m.ct!(2, 3) * m.ct!(3, 2);
269     const subFactor01 = m.ct!(1, 2) * m.ct!(3, 3) - m.ct!(1, 3) * m.ct!(3, 2);
270     const subFactor02 = m.ct!(1, 2) * m.ct!(2, 3) - m.ct!(1, 3) * m.ct!(2, 2);
271     const subFactor03 = m.ct!(0, 2) * m.ct!(3, 3) - m.ct!(0, 3) * m.ct!(3, 2);
272     const subFactor04 = m.ct!(0, 2) * m.ct!(2, 3) - m.ct!(0, 3) * m.ct!(2, 2);
273     const subFactor05 = m.ct!(0, 2) * m.ct!(1, 3) - m.ct!(0, 3) * m.ct!(1, 2);
274     const subFactor06 = m.ct!(2, 1) * m.ct!(3, 3) - m.ct!(2, 3) * m.ct!(3, 1);
275     const subFactor07 = m.ct!(1, 1) * m.ct!(3, 3) - m.ct!(1, 3) * m.ct!(3, 1);
276     const subFactor08 = m.ct!(1, 1) * m.ct!(2, 3) - m.ct!(1, 3) * m.ct!(2, 1);
277     const subFactor09 = m.ct!(0, 1) * m.ct!(3, 3) - m.ct!(0, 3) * m.ct!(3, 1);
278     const subFactor10 = m.ct!(0, 1) * m.ct!(2, 3) - m.ct!(0, 3) * m.ct!(2, 1);
279     const subFactor11 = m.ct!(1, 1) * m.ct!(3, 3) - m.ct!(1, 3) * m.ct!(3, 1);
280     const subFactor12 = m.ct!(0, 1) * m.ct!(1, 3) - m.ct!(0, 3) * m.ct!(1, 1);
281     const subFactor13 = m.ct!(2, 1) * m.ct!(3, 2) - m.ct!(2, 2) * m.ct!(3, 1);
282     const subFactor14 = m.ct!(1, 1) * m.ct!(3, 2) - m.ct!(1, 2) * m.ct!(3, 1);
283     const subFactor15 = m.ct!(1, 1) * m.ct!(2, 2) - m.ct!(1, 2) * m.ct!(2, 1);
284     const subFactor16 = m.ct!(0, 1) * m.ct!(3, 2) - m.ct!(0, 2) * m.ct!(3, 1);
285     const subFactor17 = m.ct!(0, 1) * m.ct!(2, 2) - m.ct!(0, 2) * m.ct!(2, 1);
286     const subFactor18 = m.ct!(0, 1) * m.ct!(1, 2) - m.ct!(0, 2) * m.ct!(1, 1);
287 
288     const inv = M (
289         + (m.ct!(1, 1) * subFactor00 - m.ct!(2, 1) * subFactor01 + m.ct!(3, 1) * subFactor02),
290         - (m.ct!(1, 0) * subFactor00 - m.ct!(2, 0) * subFactor01 + m.ct!(3, 0) * subFactor02),
291         + (m.ct!(1, 0) * subFactor06 - m.ct!(2, 0) * subFactor07 + m.ct!(3, 0) * subFactor08),
292         - (m.ct!(1, 0) * subFactor13 - m.ct!(2, 0) * subFactor14 + m.ct!(3, 0) * subFactor15),
293 
294         - (m.ct!(0, 1) * subFactor00 - m.ct!(2, 1) * subFactor03 + m.ct!(3, 1) * subFactor04),
295         + (m.ct!(0, 0) * subFactor00 - m.ct!(2, 0) * subFactor03 + m.ct!(3, 0) * subFactor04),
296         - (m.ct!(0, 0) * subFactor06 - m.ct!(2, 0) * subFactor09 + m.ct!(3, 0) * subFactor10),
297         + (m.ct!(0, 0) * subFactor13 - m.ct!(2, 0) * subFactor16 + m.ct!(3, 0) * subFactor17),
298 
299         + (m.ct!(0, 1) * subFactor01 - m.ct!(1, 1) * subFactor03 + m.ct!(3, 1) * subFactor05),
300         - (m.ct!(0, 0) * subFactor01 - m.ct!(1, 0) * subFactor03 + m.ct!(3, 0) * subFactor05),
301         + (m.ct!(0, 0) * subFactor11 - m.ct!(1, 0) * subFactor09 + m.ct!(3, 0) * subFactor12),
302         - (m.ct!(0, 0) * subFactor14 - m.ct!(1, 0) * subFactor16 + m.ct!(3, 0) * subFactor18),
303 
304         - (m.ct!(0, 1) * subFactor02 - m.ct!(1, 1) * subFactor04 + m.ct!(2, 1) * subFactor05),
305         + (m.ct!(0, 0) * subFactor02 - m.ct!(1, 0) * subFactor04 + m.ct!(2, 0) * subFactor05),
306         - (m.ct!(0, 0) * subFactor08 - m.ct!(1, 0) * subFactor10 + m.ct!(2, 0) * subFactor12),
307         + (m.ct!(0, 0) * subFactor15 - m.ct!(1, 0) * subFactor17 + m.ct!(2, 0) * subFactor18),
308     );
309 
310     const oneOverD = T(1) / (
311         + m.ct!(0, 0) * inv.ct!(0, 0)
312         + m.ct!(1, 0) * inv.ct!(1, 0)
313         + m.ct!(2, 0) * inv.ct!(2, 0)
314         + m.ct!(3, 0) * inv.ct!(3, 0)
315     );
316 
317     return inv * oneOverD;
318 }