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