1/*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16export class Matrix4 {
17    elements;
18
19    constructor() {
20        this.elements = new Float32Array(16);
21        this.setIdentity();
22    }
23
24    setIdentity() {
25        this.elements[0] = 1;
26        this.elements[5] = 1;
27        this.elements[10] = 1;
28        this.elements[15] = 1;
29        return this;
30    }
31
32    set(m) {
33        let elements = m.getElements();
34        for (let i = 0; i < 16; i++) {
35            this.elements[i] = elements[i];
36        }
37        return this;
38    }
39
40    setTranslate(x, y, z) {
41        this.elements[12] = x;
42        this.elements[13] = y;
43        this.elements[14] = z;
44        return this;
45    }
46
47    setScale(x, y, z) {
48        this.elements[0] = x;
49        this.elements[5] = y;
50        this.elements[10] = z;
51        return this;
52    }
53
54    rotateX(e, s, c) {
55        e[0] = 1;
56        e[4] = 0;
57        e[8] = 0;
58        e[12] = 0;
59        e[1] = 0;
60        e[5] = c;
61        e[9] = -s;
62        e[13] = 0;
63        e[2] = 0;
64        e[6] = s;
65        e[10] = c;
66        e[14] = 0;
67        e[3] = 0;
68        e[7] = 0;
69        e[11] = 0;
70        e[15] = 1;
71    }
72
73    rotateY(e, s, c) {
74        e[0] = c;
75        e[4] = 0;
76        e[8] = s;
77        e[12] = 0;
78        e[1] = 0;
79        e[5] = 1;
80        e[9] = 0;
81        e[13] = 0;
82        e[2] = -s;
83        e[6] = 0;
84        e[10] = c;
85        e[14] = 0;
86        e[3] = 0;
87        e[7] = 0;
88        e[11] = 0;
89        e[15] = 1;
90    }
91
92    rotateZ(e, s, c) {
93        e[0] = c;
94        e[4] = -s;
95        e[8] = 0;
96        e[12] = 0;
97        e[1] = s;
98        e[5] = c;
99        e[9] = 0;
100        e[13] = 0;
101        e[2] = 0;
102        e[6] = 0;
103        e[10] = 1;
104        e[14] = 0;
105        e[3] = 0;
106        e[7] = 0;
107        e[11] = 0;
108        e[15] = 1;
109    }
110
111    setRotate(angle, x, y, z) {
112        let e, s, c, len, rlen, nc, xy, yz, zx, xs, ys, zs;
113        angle = Math.PI * angle / 180;
114        e = this.elements;
115        s = Math.sin(angle);
116        c = Math.cos(angle);
117        if (0 !== x && 0 === y && 0 === z) {
118            if (x < 0) {
119                s = -s;
120            }
121            this.rotateX(e, s, c)
122        } else if (0 === x && 0 !== y && 0 === z) {
123            if (y < 0) {
124                s = -s;
125            }
126            this.rotateY(e, s, c)
127        } else if (0 === x && 0 === y && 0 !== z) {
128            if (z < 0) {
129                s = -s;
130            }
131            this.rotateZ(e, s, c)
132        } else {
133            len = Math.sqrt(x * x + y * y + z * z);
134            if (len !== 1) {
135                rlen = 1 / len;
136                x *= rlen;
137                y *= rlen;
138                z *= rlen;
139            }
140            nc = 1 - c;
141            xy = x * y;
142            yz = y * z;
143            zx = z * x;
144            xs = x * s;
145            ys = y * s;
146            zs = z * s;
147            e[0] = x * x * nc + c;
148            e[1] = xy * nc + zs;
149            e[2] = zx * nc - ys;
150            e[3] = e[7] = 0;
151            e[4] = xy * nc - zs;
152            e[5] = y * y * nc + c;
153            e[6] = yz * nc + xs;
154            e[8] = zx * nc + ys;
155            e[9] = yz * nc - xs;
156            e[10] = z * z * nc + c;
157            e[11] = e[12] = e[13] = e[14] = 0;
158            e[15] = 1;
159        }
160        return this;
161    }
162
163    multiply(other) {
164        let i, e, a, b, ai0, ai1, ai2, ai3;
165        e = this.elements;
166        a = this.elements;
167        b = other.elements;
168        if (e === b) {
169            b = new Float32Array(16);
170            for (i = 0; i < 16; ++i) {
171                b[i] = e[i];
172            }
173        }
174        for (i = 0; i < 4; i++) {
175            ai0 = a[i];
176            ai1 = a[i + 4];
177            ai2 = a[i + 8];
178            ai3 = a[i + 12];
179            e[i] = ai0 * b[0] + ai1 * b[1] + ai2 * b[2] + ai3 * b[3];
180            e[i + 4] = ai0 * b[4] + ai1 * b[5] + ai2 * b[6] + ai3 * b[7];
181            e[i + 8] = ai0 * b[8] + ai1 * b[9] + ai2 * b[10] + ai3 * b[11];
182            e[i + 12] = ai0 * b[12] + ai1 * b[13] + ai2 * b[14] + ai3 * b[15];
183        }
184        return this;
185    }
186
187    getElements() {
188        return this.elements;
189    }
190
191    getInverse() {
192        let result = new Matrix4();
193        let a = this.elements;
194        let b = result.elements;
195        let A0 = a[0] * a[5] - a[1] * a[4];
196        let A1 = a[0] * a[6] - a[2] * a[4];
197        let A2 = a[0] * a[7] - a[3] * a[4];
198        let A3 = a[1] * a[6] - a[2] * a[5];
199        let A4 = a[1] * a[7] - a[3] * a[5];
200        let A5 = a[2] * a[7] - a[3] * a[6];
201        let B0 = a[8] * a[13] - a[9] * a[12];
202        let B1 = a[8] * a[14] - a[10] * a[12];
203        let B2 = a[8] * a[15] - a[11] * a[12];
204        let B3 = a[9] * a[14] - a[10] * a[13];
205        let B4 = a[9] * a[15] - a[11] * a[13];
206        let B5 = a[10] * a[15] - a[11] * a[14];
207        let det = A0 * B5 - A1 * B4 + A2 * B3 + A3 * B2 - A4 * B1 + A5 * B0;
208        if (det == 0) {
209            return null;
210        }
211        let invDet = 1 / det;
212        b[0] = (a[5] * B5 - a[6] * B4 + a[7] * B3) * invDet;
213        b[1] = (-a[1] * B5 + a[2] * B4 - a[3] * B3) * invDet;
214        b[2] = (a[13] * A5 - a[14] * A4 + a[15] * A3) * invDet;
215        b[3] = (-a[9] * A5 + a[10] * A4 - a[11] * A3) * invDet;
216        b[4] = (-a[4] * B5 + a[6] * B2 - a[7] * B1) * invDet;
217        b[5] = (a[0] * B5 - a[2] * B2 + a[3] * B1) * invDet;
218        b[6] = (-a[12] * A5 + a[14] * A2 - a[15] * A1) * invDet;
219        b[7] = (a[8] * A5 - a[10] * A2 + a[11] * A1) * invDet;
220        b[8] = (a[4] * B4 - a[5] * B2 + a[7] * B0) * invDet;
221        b[9] = (-a[0] * B4 + a[1] * B2 - a[3] * B0) * invDet;
222        b[10] = (a[12] * A4 - a[13] * A2 + a[15] * A0) * invDet;
223        b[11] = (-a[8] * A4 + a[9] * A2 - a[11] * A0) * invDet;
224        b[12] = (-a[4] * B3 + a[5] * B1 - a[6] * B0) * invDet;
225        b[13] = (a[0] * B3 - a[1] * B1 + a[2] * B0) * invDet;
226        b[14] = (-a[12] * A3 + a[13] * A1 - a[14] * A0) * invDet;
227        b[15] = (a[8] * A3 - a[9] * A1 + a[10] * A0) * invDet;
228        return result;
229    }
230
231    setPerspective(fovy, aspect, near, far) {
232        let e, rd, s, ct;
233        if (near === far || aspect === 0) {
234            throw 'null frustum';
235        }
236        if (near <= 0) {
237            throw 'near <= 0';
238        }
239        if (far <= 0) {
240            throw 'far <= 0';
241        }
242        fovy = Math.PI * fovy / 180 / 2;
243        s = Math.sin(fovy);
244        if (s === 0) {
245            throw 'null frustum';
246        }
247        rd = 1 / (far - near);
248        ct = Math.cos(fovy) / s;
249        e = this.elements;
250        e[0] = ct / aspect;
251        e[1] = 0;
252        e[2] = 0;
253        e[3] = 0;
254        e[4] = 0;
255        e[5] = ct;
256        e[6] = 0;
257        e[7] = 0;
258        e[8] = 0;
259        e[9] = 0;
260        e[10] = -(far + near) * rd;
261        e[11] = -1;
262        e[12] = 0;
263        e[13] = 0;
264        e[14] = -2 * near * far * rd;
265        e[15] = 0;
266        return this;
267    }
268
269    setOrtho(left, right, bottom, top, near, far) {
270        let e = this.elements;
271        let rw = 1 / (right - left);
272        let rh = 1 / (top - bottom);
273        let rd = 1 / (far - near);
274        e[0] = 2 * rw;
275        e[1] = 0;
276        e[2] = 0;
277        e[3] = 0;
278        e[4] = 0;
279        e[5] = 2 * rh;
280        e[6] = 0;
281        e[7] = 0;
282        e[8] = 0;
283        e[9] = 0;
284        e[10] = -2 * rd;
285        e[11] = 0;
286        e[12] = -(right + left) * rw;
287        e[13] = -(top + bottom) * rh;
288        e[14] = -(far + near) * rd;
289        e[15] = 1;
290    }
291
292    lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
293        let lookAt = new Matrix4().setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
294        return this.multiply(lookAt);
295    }
296
297    setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
298        let e, fx, fy, fz, rlf, sx, sy, sz, rls, ux, uy, uz;
299        fx = centerX - eyeX;
300        fy = centerY - eyeY;
301        fz = centerZ - eyeZ;
302        rlf = 1 / Math.sqrt(fx * fx + fy * fy + fz * fz);
303        fx *= rlf;
304        fy *= rlf;
305        fz *= rlf;
306        sx = fy * upZ - fz * upY;
307        sy = fz * upX - fx * upZ;
308        sz = fx * upY - fy * upX;
309        rls = 1 / Math.sqrt(sx * sx + sy * sy + sz * sz);
310        sx *= rls;
311        sy *= rls;
312        sz *= rls;
313        ux = sy * fz - sz * fy;
314        uy = sz * fx - sx * fz;
315        uz = sx * fy - sy * fx;
316        e = this.elements;
317        e[0] = sx;
318        e[1] = ux;
319        e[2] = -fx;
320        e[3] = 0;
321        e[4] = sy;
322        e[5] = uy;
323        e[6] = -fy;
324        e[7] = 0;
325        e[8] = sz;
326        e[9] = uz;
327        e[10] = -fz;
328        e[11] = 0;
329        e[12] = 0;
330        e[13] = 0;
331        e[14] = 0;
332        e[15] = 1;
333        return this.translate(-eyeX, -eyeY, -eyeZ);
334    }
335
336    translate(x, y, z) {
337        let e = this.elements;
338        e[12] += e[0] * x + e[4] * y + e[8] * z;
339        e[13] += e[1] * x + e[5] * y + e[9] * z;
340        e[14] += e[2] * x + e[6] * y + e[10] * z;
341        e[15] += e[3] * x + e[7] * y + e[11] * z;
342        return this;
343    }
344
345    scale(x, y, z) {
346        let e = this.elements;
347        e[0] *= x;
348        e[1] *= x;
349        e[2] *= x;
350        e[3] *= x;
351        e[4] *= y;
352        e[5] *= y;
353        e[6] *= y;
354        e[7] *= y;
355        e[8] *= z;
356        e[9] *= z;
357        e[10] *= z;
358        e[11] *= z;
359    }
360
361    rotate(angle, x, y, z) {
362        let e = this.elements;
363        let radian = angle * Math.PI / 180;
364        let cosB = Math.cos(radian);
365        let sinB = Math.sin(radian);
366        let len = Math.sqrt(x * x + y * y + z * z);
367        if (len != 1) {
368            let rlen = 1 / len;
369            x *= rlen;
370            y *= rlen;
371            z *= rlen;
372        }
373        let nc = 1 - cosB;
374        let xy = x * y;
375        let yz = y * z;
376        let zx = z * x;
377        let xs = x * sinB;
378        let ys = y * sinB;
379        let zs = z * sinB;
380        let f00 = x * x * nc + cosB;
381        let f01 = xy * nc + zs;
382        let f02 = zx * nc - ys;
383        let f10 = xy * nc - zs;
384        let f11 = y * y * nc + cosB;
385        let f12 = yz * nc + xs;
386        let f20 = zx * nc + ys;
387        let f21 = yz * nc - xs;
388        let f22 = z * z * nc + cosB;
389        let t00 = e[0] * f00 + e[4] * f01 + e[8] * f02;
390        let t01 = e[1] * f00 + e[5] * f01 + e[9] * f02;
391        let t02 = e[2] * f00 + e[6] * f01 + e[10] * f02;
392        let t03 = e[3] * f00 + e[7] * f01 + e[11] * f02;
393        let t10 = e[0] * f10 + e[4] * f11 + e[8] * f12;
394        let t11 = e[1] * f10 + e[5] * f11 + e[9] * f12;
395        let t12 = e[2] * f10 + e[6] * f11 + e[10] * f12;
396        let t13 = e[3] * f10 + e[7] * f11 + e[11] * f12;
397        e[8] = e[0] * f20 + e[4] * f21 + e[8] * f22;
398        e[9] = e[1] * f20 + e[5] * f21 + e[9] * f22;
399        e[10] = e[2] * f20 + e[6] * f21 + e[10] * f22;
400        e[11] = e[3] * f20 + e[7] * f21 + e[11] * f22;
401        e[0] = t00;
402        e[1] = t01;
403        e[2] = t02;
404        e[3] = t03;
405        e[4] = t10;
406        e[5] = t11;
407        e[6] = t12;
408        e[7] = t13;
409    }
410
411    setInverseOf(other) {
412        let i, s, d, arr, det;
413        s = other.elements;
414        d = this.elements;
415        arr = new Float32Array(16);
416        arr[0] = s[5] * s[10] * s[15] - s[5] * s[11] * s[14] - s[9] * s[6] * s[15] + s[9] * s[7] * s[14] + s[13] * s[6] * s[11] - s[13] * s[7] * s[10];
417        arr[4] = -s[4] * s[10] * s[15] + s[4] * s[11] * s[14] + s[8] * s[6] * s[15] - s[8] * s[7] * s[14] - s[12] * s[6] * s[11] + s[12] * s[7] * s[10];
418        arr[8] = s[4] * s[9] * s[15] - s[4] * s[11] * s[13] - s[8] * s[5] * s[15] + s[8] * s[7] * s[13] + s[12] * s[5] * s[11] - s[12] * s[7] * s[9];
419        arr[12] = -s[4] * s[9] * s[14] + s[4] * s[10] * s[13] + s[8] * s[5] * s[14] - s[8] * s[6] * s[13] - s[12] * s[5] * s[10] + s[12] * s[6] * s[9];
420        arr[1] = -s[1] * s[10] * s[15] + s[1] * s[11] * s[14] + s[9] * s[2] * s[15] - s[9] * s[3] * s[14] - s[13] * s[2] * s[11] + s[13] * s[3] * s[10];
421        arr[5] = s[0] * s[10] * s[15] - s[0] * s[11] * s[14] - s[8] * s[2] * s[15] + s[8] * s[3] * s[14] + s[12] * s[2] * s[11] - s[12] * s[3] * s[10];
422        arr[9] = -s[0] * s[9] * s[15] + s[0] * s[11] * s[13] + s[8] * s[1] * s[15] - s[8] * s[3] * s[13] - s[12] * s[1] * s[11] + s[12] * s[3] * s[9];
423        arr[13] = s[0] * s[9] * s[14] - s[0] * s[10] * s[13] - s[8] * s[1] * s[14] + s[8] * s[2] * s[13] + s[12] * s[1] * s[10] - s[12] * s[2] * s[9];
424        arr[2] = s[1] * s[6] * s[15] - s[1] * s[7] * s[14] - s[5] * s[2] * s[15] + s[5] * s[3] * s[14] + s[13] * s[2] * s[7] - s[13] * s[3] * s[6];
425        arr[6] = -s[0] * s[6] * s[15] + s[0] * s[7] * s[14] + s[4] * s[2] * s[15] - s[4] * s[3] * s[14] - s[12] * s[2] * s[7] + s[12] * s[3] * s[6];
426        arr[10] = s[0] * s[5] * s[15] - s[0] * s[7] * s[13] - s[4] * s[1] * s[15] + s[4] * s[3] * s[13] + s[12] * s[1] * s[7] - s[12] * s[3] * s[5];
427        arr[14] = -s[0] * s[5] * s[14] + s[0] * s[6] * s[13] + s[4] * s[1] * s[14] - s[4] * s[2] * s[13] - s[12] * s[1] * s[6] + s[12] * s[2] * s[5];
428        arr[3] = -s[1] * s[6] * s[11] + s[1] * s[7] * s[10] + s[5] * s[2] * s[11] - s[5] * s[3] * s[10] - s[9] * s[2] * s[7] + s[9] * s[3] * s[6];
429        arr[7] = s[0] * s[6] * s[11] - s[0] * s[7] * s[10] - s[4] * s[2] * s[11] + s[4] * s[3] * s[10] + s[8] * s[2] * s[7] - s[8] * s[3] * s[6];
430        arr[11] = -s[0] * s[5] * s[11] + s[0] * s[7] * s[9] + s[4] * s[1] * s[11] - s[4] * s[3] * s[9] - s[8] * s[1] * s[7] + s[8] * s[3] * s[5];
431        arr[15] = s[0] * s[5] * s[10] - s[0] * s[6] * s[9] - s[4] * s[1] * s[10] + s[4] * s[2] * s[9] + s[8] * s[1] * s[6] - s[8] * s[2] * s[5];
432        det = s[0] * arr[0] + s[1] * arr[4] + s[2] * arr[8] + s[3] * arr[12];
433        if (det == 0) {
434            return false;
435        }
436        det = 1 / det;
437        for (i = 0; i < 16; i++) {
438            d[i] = arr[i] * det;
439        }
440        return true;
441    }
442
443    invert() {
444        this.setInverseOf(this);
445    }
446
447    setTranslateRotateScale(tx, ty, tz, rx, ry, rz, sx, sy, sz) {
448        let radian = rx * Math.PI / 180;
449        let cosB = Math.cos(radian);
450        let sinB = Math.sin(radian);
451        let cosA = 1, sinA = 0;
452        if (ry != 0) {
453            cosA = this.elements[5];
454            sinA = this.elements[6];
455        }
456        if (rz != 0) {
457            cosA = this.elements[0];
458            sinA = this.elements[1];
459        }
460        this.elements[0] = cosA * cosB;
461        this.elements[1] = sinA * cosB;
462        this.elements[2] = -sinB;
463        this.elements[4] = -sinA;
464        this.elements[5] = cosA;
465        this.elements[6] = 0;
466        this.elements[8] = cosA * sinB;
467        this.elements[9] = sinA * sinB;
468        this.elements[10] = cosB;
469        this.elements[12] = tx;
470        this.elements[13] = ty;
471        this.elements[14] = tz;
472        this.elements[3] = 0;
473        this.elements[7] = 0;
474        this.elements[11] = 0;
475        this.elements[15] = 1;
476        this.scale(sx, sy, sz);
477    }
478
479    setTRS(tx, ty, tz, rx, ry, rz, sx, sy, sz) {
480        let radian = rx * Math.PI / 180;
481        let cosB = Math.cos(radian);
482        let sinB = Math.sin(radian);
483        let cosA = 1, sinA = 0;
484        if (ry != 0) {
485            cosA = this.elements[5];
486            sinA = this.elements[6];
487        }
488        if (rz != 0) {
489            cosA = this.elements[0];
490            sinA = this.elements[1];
491        }
492        this.elements[0] = cosA * cosB;
493        this.elements[1] = sinA * cosB;
494        this.elements[2] = -sinB;
495        this.elements[4] = -sinA;
496        this.elements[5] = cosA;
497        this.elements[6] = 0;
498        this.elements[8] = cosA * sinB;
499        this.elements[9] = sinA * sinB;
500        this.elements[10] = cosB;
501        this.elements[12] = tx;
502        this.elements[13] = ty;
503        this.elements[14] = tz;
504        this.elements[3] = 0;
505        this.elements[7] = 0;
506        this.elements[11] = 0;
507        this.elements[15] = 1;
508        this.scale(sx, sy, sz);
509    }
510
511    transpose() {
512        let e, t;
513        e = this.elements;
514        t = e[1];
515        e[1] = e[4];
516        e[4] = t;
517        t = e[2];
518        e[2] = e[8];
519        e[8] = t;
520        t = e[3];
521        e[3] = e[12];
522        e[12] = t;
523        t = e[6];
524        e[6] = e[9];
525        e[9] = t;
526        t = e[7];
527        e[7] = e[13];
528        e[13] = t;
529        t = e[11];
530        e[11] = e[14];
531        e[14] = t;
532        return this;
533    }
534}
535