idlize

Форк
0
381 строка · 12.4 Кб
1
/*
2
 * Copyright (c) 2022-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

16
import { Array_from_number, float32 } from "@koalaui/compat"
17
import { Matrix33 } from "./Matrix33"
18
import { Point3 } from "./Point3"
19

20

21
export interface RotateOptions {
22
    angle?: float32
23
    x?: float32
24
    y?: float32
25
    z?: float32
26
    pivotX?: float32
27
    pivotY?: float32
28
    pivotZ?: float32
29
}
30

31
export interface ScaleOptions {
32
    x?: float32
33
    y?: float32
34
    z?: float32
35
    pivotX?: float32
36
    pivotY?: float32
37
    pivotZ?: float32
38
}
39

40
export interface TranslateOptions {
41
    x?: float32
42
    y?: float32
43
    z?: float32
44
}
45

46
// TODO: this is because ArkTS doesn allow interface literal instances.
47
class TranslateOptionsImpl implements TranslateOptions {
48
    _x: float32 | undefined
49
    _y: float32 | undefined
50
    _z: float32 | undefined
51

52
    get x(): float32 | undefined { return this._x }
53
    get y(): float32 | undefined { return this._y }
54
    get z(): float32 | undefined { return this._z }
55

56
    set x(x: float32 | undefined) { this._x = x }
57
    set y(y: float32 | undefined) { this._y = y }
58
    set z(z: float32 | undefined) { this._z = z }
59

60
    constructor(
61
        x: float32 | undefined,
62
        y: float32 | undefined,
63
        z: float32 | undefined
64
    ) {
65
        this._x = x
66
        this._y = y
67
        this._z = z
68
    }
69
}
70

71
export function mat44(array?: Float32Array): Matrix44 {
72
    return (array == undefined)? new Matrix44() : new Matrix44(array)
73
}
74
/**
75
 * 4x4 matrix with right-handed coordinate system:
76
 * +x goes to the right
77
 * +y goes down
78
 * +z goes into the screen (away from the viewer)
79
 */
80
export class Matrix44 {
81
    public readonly array: Float32Array
82
    constructor (array: Float32Array = new Float32Array(Array_from_number([
83
        1.0, 0.0, 0.0, 0.0,
84
        0.0, 1.0, 0.0, 0.0,
85
        0.0, 0.0, 1.0, 0.0,
86
        0.0, 0.0, 0.0, 1.0
87
    ]))) {
88
        this.array = array.slice()
89
    }
90

91
    public static identity(): Matrix44 {
92
        return mat44()
93
    }
94

95
    static zero(): Matrix44 {
96
        return mat44(new Float32Array(Array_from_number([
97
            0.0, 0.0, 0.0, 0.0,
98
            0.0, 0.0, 0.0, 0.0,
99
            0.0, 0.0, 0.0, 0.0,
100
            0.0, 0.0, 0.0, 0.0,
101
        ])))
102
    }
103

104
    public static lookAt(eye: Point3, center: Point3, up: Point3): Matrix44 {
105
        const f = center.subtract(eye).normalize()
106
        const u = up.normalize()
107
        const s = f.cross(u).normalize()
108
        const sf = s.cross(f)
109
        return new Matrix44(new Float32Array(Array_from_number([
110
            s.x, sf.x, -f.x, eye.x,
111
            s.y, sf.y, -f.y, eye.y,
112
            s.z, sf.z, -f.z, eye.z,
113
            0,   0,     0,   1,
114
        ]))).invert()
115
    }
116

117
    public static perspective(depth: float32): Matrix44 {
118
        return new Matrix44(new Float32Array(Array_from_number([
119
            1.0, 0.0, 0.0, 0.0,
120
            0.0, 1.0, 0.0, 0.0,
121
            0.0, 0.0, 1.0, 0.0,
122
            0.0, 0.0, -1.0 / depth, 1.0,
123
        ])))
124
    }
125

126
    public static perspectiveFov(fov: float32, near: float32, far: float32): Matrix44 {
127
        const denomInv = (far - near)
128
        const halfAngle = fov * 0.5;
129
        const cot = Math.cos(halfAngle) / Math.sin(halfAngle)
130
        return new Matrix44(new Float32Array(Array_from_number([
131
            cot, 0.0, 0.0, 0.0,
132
            0.0, cot, 0.0, 0.0,
133
            0.0, 0.0, (far + near) * denomInv, 2 * far * near * denomInv,
134
            0.0, 0.0, -1.0, 0.0,
135
        ])))
136
    }
137

138
    /**
139
     * Returns new matrix, made from Matrix33.
140
     *
141
     * @param matrix - 3x3 matrix
142
     * @returns the new instance of Matrix44
143
     *
144
     */
145
    public static makeFromMatrix33(matrix: Matrix33): Matrix44{
146
        return new Matrix44(new Float32Array(Array_from_number([
147
            matrix.array[0], matrix.array[1], 0.0, matrix.array[2],
148
            matrix.array[3], matrix.array[4], 0.0, matrix.array[5],
149
            0.0,             0.0,             1.0,           0.0,
150
            matrix.array[6], matrix.array[7], 0.0, matrix.array[8]
151
        ])))
152
    }
153

154
    /**
155
     * Returns new 3x3 matrix, made from this matrix by dropping the third row and the third column.
156
     *
157
     * @returns the new instance of Matrix33
158
     *
159
     */
160
    public asMatrix33(): Matrix33{
161
        return new Matrix33(new Float32Array(Array_from_number([
162
            this.array[0],  this.array[1],  this.array[3],
163
            this.array[4],  this.array[5],  this.array[7],
164
            this.array[12], this.array[13], this.array[15]
165
        ])))
166
    }
167

168
    public copy(): Matrix44 {
169
        return new Matrix44(new Float32Array(Array_from_number([
170
            this.array[0],  this.array[1],  this.array[2],  this.array[3],
171
            this.array[4],  this.array[5],  this.array[6],  this.array[7],
172
            this.array[8],  this.array[9],  this.array[10], this.array[11],
173
            this.array[12], this.array[13], this.array[14], this.array[15]
174
        ])))
175
    }
176

177
    concat(matrix: Matrix44): Matrix44 {
178
        const result: Float32Array = new Float32Array(Array_from_number([
179
            1.0, 0.0, 0.0, 0.0,
180
            0.0, 1.0, 0.0, 0.0,
181
            0.0, 0.0, 1.0, 0.0,
182
            0.0, 0.0, 0.0, 1.0,
183
        ]))
184
        for (let row = 0; row < 4; row++) {
185
            for (let col = 0; col < 4; col++) {
186
                let num: float32 = 0
187
                for (let k = 0; k < 4; k++) {
188
                    num += this.array[row * 4 + k] * matrix.array[col + 4 * k]
189
                }
190
                result[row * 4 + col] = num
191
            }
192
        }
193
        for (let i = 0; i < this.array.length; i++) {
194
            this.array[i] = result[i]
195
        }
196
        return this
197
    }
198

199
    public scale(options: ScaleOptions): Matrix44 {
200
        const scaled = new Matrix44()
201
        scaled.array[0] = options.x ?? 1.0 as float32
202
        scaled.array[5] = options.y ?? 1.0 as float32
203
        scaled.array[10] = options.z ?? 1.0 as float32
204

205
        this.translate(new TranslateOptionsImpl(
206
            -(options.pivotX ?? 0.0 as float32) * (options.x ?? 1.0 as float32) + (options.pivotX ?? 0.0 as float32),
207
            -(options.pivotY ?? 0.0 as float32) * (options.y ?? 1.0 as float32) + (options.pivotY ?? 0.0 as float32),
208
            undefined
209
        )).concat(scaled)
210

211
        return this
212
    }
213

214
    public rotate(options: RotateOptions): Matrix44 {
215
        const translationToPivot = mat44().translate(new TranslateOptionsImpl(
216
             (options.pivotX ?? 0.0 as float32),
217
             (options.pivotY ?? 0.0 as float32),
218
             (options.pivotZ ?? 0.0 as float32),
219
        ))
220
        const translationToBack = mat44().translate(new TranslateOptionsImpl(
221
            -(options.pivotX ?? 0.0 as float32),
222
            -(options.pivotY ?? 0.0 as float32),
223
            -(options.pivotZ ?? 0.0 as float32),
224
        ))
225

226
        const vec = new Point3(options.x ?? 0.0 as float32, options.y ?? 0.0 as float32, options.z ?? 0.0 as float32).normalize()
227
        const rads = (options.angle ?? 0.0 as float32) * Math.PI / 180
228
        let c = Math.cos(rads)
229
        let s = Math.sin(rads)
230
        const tolerance = (1.0 / (1 << 12))
231
        if (Math.abs(s) <= tolerance) s = 0.0
232
        if (Math.abs(c) <= tolerance) c = 0.0
233
        let t = 1 - c
234
        const x = vec.x
235
        const y = vec.y
236
        const z = vec.z
237

238
        const rotation = mat44()
239
        rotation.array[0] = t * x * x + c
240
        rotation.array[1] = t * x * y - s * z
241
        rotation.array[2] = t * x * z + s * y
242
        rotation.array[3] = 0
243
        rotation.array[4] = t * x * y + s * z
244
        rotation.array[5] = t * y * y + c
245
        rotation.array[6] = t * y * z - s * x
246
        rotation.array[7] = 0
247
        rotation.array[8] = t * x * z - s * y
248
        rotation.array[9] = t * y * z + s * x
249
        rotation.array[10] = t * z * z + c
250
        rotation.array[11] = 0
251
        rotation.array[12] = 0
252
        rotation.array[13] = 0
253
        rotation.array[14] = 0
254
        rotation.array[15] = 1
255

256
        this.concat(translationToPivot).concat(rotation).concat(translationToBack)
257

258
        return this
259
    }
260

261
    public translate(options: TranslateOptions): Matrix44 {
262
        this.array[3] = options.x ?? 0.0 as float32
263
        this.array[7] = options.y ?? 0.0 as float32
264
        this.array[11] = options.z ?? 0.0 as float32
265
        return this
266
    }
267

268
    public invert(): Matrix44 {
269
        const result: Float32Array = new Float32Array(16)
270

271
        let a00 = this.array[0]
272
        let a01 = this.array[1]
273
        let a02 = this.array[2]
274
        let a03 = this.array[3]
275
        let a10 = this.array[4]
276
        let a11 = this.array[5]
277
        let a12 = this.array[6]
278
        let a13 = this.array[7]
279
        let a20 = this.array[8]
280
        let a21 = this.array[9]
281
        let a22 = this.array[10]
282
        let a23 = this.array[11]
283
        let a30 = this.array[12]
284
        let a31 = this.array[13]
285
        let a32 = this.array[14]
286
        let a33 = this.array[15]
287

288
        let b00 = a00 * a11 - a01 * a10
289
        let b01 = a00 * a12 - a02 * a10
290
        let b02 = a00 * a13 - a03 * a10
291
        let b03 = a01 * a12 - a02 * a11
292
        let b04 = a01 * a13 - a03 * a11
293
        let b05 = a02 * a13 - a03 * a12
294
        let b06 = a20 * a31 - a21 * a30
295
        let b07 = a20 * a32 - a22 * a30
296
        let b08 = a20 * a33 - a23 * a30
297
        let b09 = a21 * a32 - a22 * a31
298
        let b10 = a21 * a33 - a23 * a31
299
        let b11 = a22 * a33 - a23 * a32
300

301
        let determinant = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
302
        let invdet = 1.0 / determinant
303
        b00 *= invdet
304
        b01 *= invdet
305
        b02 *= invdet
306
        b03 *= invdet
307
        b04 *= invdet
308
        b05 *= invdet
309
        b06 *= invdet
310
        b07 *= invdet
311
        b08 *= invdet
312
        b09 *= invdet
313
        b10 *= invdet
314
        b11 *= invdet
315

316
        result[0]  = a11 * b11 - a12 * b10 + a13 * b09
317
        result[1]  = a02 * b10 - a01 * b11 - a03 * b09
318
        result[2]  = a31 * b05 - a32 * b04 + a33 * b03
319
        result[3]  = a22 * b04 - a21 * b05 - a23 * b03
320
        result[4]  = a12 * b08 - a10 * b11 - a13 * b07
321
        result[5]  = a00 * b11 - a02 * b08 + a03 * b07
322
        result[6]  = a32 * b02 - a30 * b05 - a33 * b01
323
        result[7]  = a20 * b05 - a22 * b02 + a23 * b01
324
        result[8]  = a10 * b10 - a11 * b08 + a13 * b06
325
        result[9]  = a01 * b08 - a00 * b10 - a03 * b06
326
        result[10] = a30 * b04 - a31 * b02 + a33 * b00
327
        result[11] = a21 * b02 - a20 * b04 - a23 * b00
328
        result[12] = a11 * b07 - a10 * b09 - a12 * b06
329
        result[13] = a00 * b09 - a01 * b07 + a02 * b06
330
        result[14] = a31 * b01 - a30 * b03 - a32 * b00
331
        result[15] = a20 * b03 - a21 * b01 + a22 * b00
332

333
        // If 1/det overflows to infinity (i.e. det is denormalized) or any of the inverted matrix
334
        // values is non-finite, return zero to indicate a non-invertible matrix.
335
        let prod = 0
336
        for (let i = 0; i < result.length; ++i) {
337
            prod *= result[i]
338
        }
339
        // At this point, prod will either be NaN or 0
340
        // if prod is NaN, this check will return false
341
        if (prod == 0) {
342
            for (let i = 0; i < this.array.length; i++) {
343
                this.array[i] = result[i]
344
            }
345
        }
346
        return this
347
    }
348

349
    public transpose(): Matrix44 {
350
        const result: Float32Array = new Float32Array(16)
351

352
        result[0] = this.array[0]
353
        result[1] = this.array[4]
354
        result[2] = this.array[8]
355
        result[3] = this.array[12]
356
        result[4] = this.array[1]
357
        result[5] = this.array[5]
358
        result[6] = this.array[9]
359
        result[7] = this.array[13]
360
        result[8] = this.array[2]
361
        result[9] = this.array[6]
362
        result[10] = this.array[10]
363
        result[11] = this.array[14]
364
        result[12] = this.array[3]
365
        result[13] = this.array[7]
366
        result[14] = this.array[11]
367
        result[15] = this.array[15]
368

369
        for (let i = 0; i < this.array.length; i++) {
370
            this.array[i] = result[i]
371
        }
372

373
        return this
374
    }
375

376
    public skew(x?: float32, y?: float32): Matrix44 {
377
        this.array[1] += x ?? 0.0 as float32
378
        this.array[4] += y ?? 0.0 as float32
379
        return this
380
    }
381
}
382

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.