@playcanvas/splat-transform 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,7 +1,2311 @@
1
1
  import { open } from 'node:fs/promises';
2
- import { Buffer } from 'node:buffer';
2
+ import { resolve, dirname } from 'node:path';
3
+ import { exit } from 'node:process';
4
+ import { parseArgs } from 'node:util';
5
+ import { Buffer as Buffer$1 } from 'node:buffer';
6
+ import sharp from 'sharp';
3
7
 
4
- var version = "0.1.2";
8
+ const math = {
9
+ DEG_TO_RAD: Math.PI / 180,
10
+ RAD_TO_DEG: 180 / Math.PI};
11
+
12
+ class Vec3 {
13
+ constructor(x = 0, y = 0, z = 0){
14
+ if (x.length === 3) {
15
+ this.x = x[0];
16
+ this.y = x[1];
17
+ this.z = x[2];
18
+ } else {
19
+ this.x = x;
20
+ this.y = y;
21
+ this.z = z;
22
+ }
23
+ }
24
+ add(rhs) {
25
+ this.x += rhs.x;
26
+ this.y += rhs.y;
27
+ this.z += rhs.z;
28
+ return this;
29
+ }
30
+ add2(lhs, rhs) {
31
+ this.x = lhs.x + rhs.x;
32
+ this.y = lhs.y + rhs.y;
33
+ this.z = lhs.z + rhs.z;
34
+ return this;
35
+ }
36
+ addScalar(scalar) {
37
+ this.x += scalar;
38
+ this.y += scalar;
39
+ this.z += scalar;
40
+ return this;
41
+ }
42
+ addScaled(rhs, scalar) {
43
+ this.x += rhs.x * scalar;
44
+ this.y += rhs.y * scalar;
45
+ this.z += rhs.z * scalar;
46
+ return this;
47
+ }
48
+ clone() {
49
+ const cstr = this.constructor;
50
+ return new cstr(this.x, this.y, this.z);
51
+ }
52
+ copy(rhs) {
53
+ this.x = rhs.x;
54
+ this.y = rhs.y;
55
+ this.z = rhs.z;
56
+ return this;
57
+ }
58
+ cross(lhs, rhs) {
59
+ const lx = lhs.x;
60
+ const ly = lhs.y;
61
+ const lz = lhs.z;
62
+ const rx = rhs.x;
63
+ const ry = rhs.y;
64
+ const rz = rhs.z;
65
+ this.x = ly * rz - ry * lz;
66
+ this.y = lz * rx - rz * lx;
67
+ this.z = lx * ry - rx * ly;
68
+ return this;
69
+ }
70
+ distance(rhs) {
71
+ const x = this.x - rhs.x;
72
+ const y = this.y - rhs.y;
73
+ const z = this.z - rhs.z;
74
+ return Math.sqrt(x * x + y * y + z * z);
75
+ }
76
+ div(rhs) {
77
+ this.x /= rhs.x;
78
+ this.y /= rhs.y;
79
+ this.z /= rhs.z;
80
+ return this;
81
+ }
82
+ div2(lhs, rhs) {
83
+ this.x = lhs.x / rhs.x;
84
+ this.y = lhs.y / rhs.y;
85
+ this.z = lhs.z / rhs.z;
86
+ return this;
87
+ }
88
+ divScalar(scalar) {
89
+ this.x /= scalar;
90
+ this.y /= scalar;
91
+ this.z /= scalar;
92
+ return this;
93
+ }
94
+ dot(rhs) {
95
+ return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
96
+ }
97
+ equals(rhs) {
98
+ return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z;
99
+ }
100
+ equalsApprox(rhs, epsilon = 1e-6) {
101
+ return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon;
102
+ }
103
+ length() {
104
+ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
105
+ }
106
+ lengthSq() {
107
+ return this.x * this.x + this.y * this.y + this.z * this.z;
108
+ }
109
+ lerp(lhs, rhs, alpha) {
110
+ this.x = lhs.x + alpha * (rhs.x - lhs.x);
111
+ this.y = lhs.y + alpha * (rhs.y - lhs.y);
112
+ this.z = lhs.z + alpha * (rhs.z - lhs.z);
113
+ return this;
114
+ }
115
+ mul(rhs) {
116
+ this.x *= rhs.x;
117
+ this.y *= rhs.y;
118
+ this.z *= rhs.z;
119
+ return this;
120
+ }
121
+ mul2(lhs, rhs) {
122
+ this.x = lhs.x * rhs.x;
123
+ this.y = lhs.y * rhs.y;
124
+ this.z = lhs.z * rhs.z;
125
+ return this;
126
+ }
127
+ mulScalar(scalar) {
128
+ this.x *= scalar;
129
+ this.y *= scalar;
130
+ this.z *= scalar;
131
+ return this;
132
+ }
133
+ normalize(src = this) {
134
+ const lengthSq = src.x * src.x + src.y * src.y + src.z * src.z;
135
+ if (lengthSq > 0) {
136
+ const invLength = 1 / Math.sqrt(lengthSq);
137
+ this.x = src.x * invLength;
138
+ this.y = src.y * invLength;
139
+ this.z = src.z * invLength;
140
+ }
141
+ return this;
142
+ }
143
+ floor(src = this) {
144
+ this.x = Math.floor(src.x);
145
+ this.y = Math.floor(src.y);
146
+ this.z = Math.floor(src.z);
147
+ return this;
148
+ }
149
+ ceil(src = this) {
150
+ this.x = Math.ceil(src.x);
151
+ this.y = Math.ceil(src.y);
152
+ this.z = Math.ceil(src.z);
153
+ return this;
154
+ }
155
+ round(src = this) {
156
+ this.x = Math.round(src.x);
157
+ this.y = Math.round(src.y);
158
+ this.z = Math.round(src.z);
159
+ return this;
160
+ }
161
+ min(rhs) {
162
+ if (rhs.x < this.x) this.x = rhs.x;
163
+ if (rhs.y < this.y) this.y = rhs.y;
164
+ if (rhs.z < this.z) this.z = rhs.z;
165
+ return this;
166
+ }
167
+ max(rhs) {
168
+ if (rhs.x > this.x) this.x = rhs.x;
169
+ if (rhs.y > this.y) this.y = rhs.y;
170
+ if (rhs.z > this.z) this.z = rhs.z;
171
+ return this;
172
+ }
173
+ project(rhs) {
174
+ const a_dot_b = this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
175
+ const b_dot_b = rhs.x * rhs.x + rhs.y * rhs.y + rhs.z * rhs.z;
176
+ const s = a_dot_b / b_dot_b;
177
+ this.x = rhs.x * s;
178
+ this.y = rhs.y * s;
179
+ this.z = rhs.z * s;
180
+ return this;
181
+ }
182
+ set(x, y, z) {
183
+ this.x = x;
184
+ this.y = y;
185
+ this.z = z;
186
+ return this;
187
+ }
188
+ sub(rhs) {
189
+ this.x -= rhs.x;
190
+ this.y -= rhs.y;
191
+ this.z -= rhs.z;
192
+ return this;
193
+ }
194
+ sub2(lhs, rhs) {
195
+ this.x = lhs.x - rhs.x;
196
+ this.y = lhs.y - rhs.y;
197
+ this.z = lhs.z - rhs.z;
198
+ return this;
199
+ }
200
+ subScalar(scalar) {
201
+ this.x -= scalar;
202
+ this.y -= scalar;
203
+ this.z -= scalar;
204
+ return this;
205
+ }
206
+ fromArray(arr, offset = 0) {
207
+ this.x = arr[offset] ?? this.x;
208
+ this.y = arr[offset + 1] ?? this.y;
209
+ this.z = arr[offset + 2] ?? this.z;
210
+ return this;
211
+ }
212
+ toString() {
213
+ return `[${this.x}, ${this.y}, ${this.z}]`;
214
+ }
215
+ toArray(arr = [], offset = 0) {
216
+ arr[offset] = this.x;
217
+ arr[offset + 1] = this.y;
218
+ arr[offset + 2] = this.z;
219
+ return arr;
220
+ }
221
+ static{
222
+ this.ZERO = Object.freeze(new Vec3(0, 0, 0));
223
+ }
224
+ static{
225
+ this.HALF = Object.freeze(new Vec3(0.5, 0.5, 0.5));
226
+ }
227
+ static{
228
+ this.ONE = Object.freeze(new Vec3(1, 1, 1));
229
+ }
230
+ static{
231
+ this.UP = Object.freeze(new Vec3(0, 1, 0));
232
+ }
233
+ static{
234
+ this.DOWN = Object.freeze(new Vec3(0, -1, 0));
235
+ }
236
+ static{
237
+ this.RIGHT = Object.freeze(new Vec3(1, 0, 0));
238
+ }
239
+ static{
240
+ this.LEFT = Object.freeze(new Vec3(-1, 0, 0));
241
+ }
242
+ static{
243
+ this.FORWARD = Object.freeze(new Vec3(0, 0, -1));
244
+ }
245
+ static{
246
+ this.BACK = Object.freeze(new Vec3(0, 0, 1));
247
+ }
248
+ }
249
+
250
+ class Mat3 {
251
+ constructor(){
252
+ this.data = new Float32Array(9);
253
+ this.data[0] = this.data[4] = this.data[8] = 1;
254
+ }
255
+ clone() {
256
+ const cstr = this.constructor;
257
+ return new cstr().copy(this);
258
+ }
259
+ copy(rhs) {
260
+ const src = rhs.data;
261
+ const dst = this.data;
262
+ dst[0] = src[0];
263
+ dst[1] = src[1];
264
+ dst[2] = src[2];
265
+ dst[3] = src[3];
266
+ dst[4] = src[4];
267
+ dst[5] = src[5];
268
+ dst[6] = src[6];
269
+ dst[7] = src[7];
270
+ dst[8] = src[8];
271
+ return this;
272
+ }
273
+ set(src) {
274
+ const dst = this.data;
275
+ dst[0] = src[0];
276
+ dst[1] = src[1];
277
+ dst[2] = src[2];
278
+ dst[3] = src[3];
279
+ dst[4] = src[4];
280
+ dst[5] = src[5];
281
+ dst[6] = src[6];
282
+ dst[7] = src[7];
283
+ dst[8] = src[8];
284
+ return this;
285
+ }
286
+ getX(x = new Vec3()) {
287
+ return x.set(this.data[0], this.data[1], this.data[2]);
288
+ }
289
+ getY(y = new Vec3()) {
290
+ return y.set(this.data[3], this.data[4], this.data[5]);
291
+ }
292
+ getZ(z = new Vec3()) {
293
+ return z.set(this.data[6], this.data[7], this.data[8]);
294
+ }
295
+ equals(rhs) {
296
+ const l = this.data;
297
+ const r = rhs.data;
298
+ return l[0] === r[0] && l[1] === r[1] && l[2] === r[2] && l[3] === r[3] && l[4] === r[4] && l[5] === r[5] && l[6] === r[6] && l[7] === r[7] && l[8] === r[8];
299
+ }
300
+ isIdentity() {
301
+ const m = this.data;
302
+ return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 0 && m[4] === 1 && m[5] === 0 && m[6] === 0 && m[7] === 0 && m[8] === 1;
303
+ }
304
+ setIdentity() {
305
+ const m = this.data;
306
+ m[0] = 1;
307
+ m[1] = 0;
308
+ m[2] = 0;
309
+ m[3] = 0;
310
+ m[4] = 1;
311
+ m[5] = 0;
312
+ m[6] = 0;
313
+ m[7] = 0;
314
+ m[8] = 1;
315
+ return this;
316
+ }
317
+ toString() {
318
+ return `[${this.data.join(', ')}]`;
319
+ }
320
+ transpose(src = this) {
321
+ const s = src.data;
322
+ const t = this.data;
323
+ if (s === t) {
324
+ let tmp;
325
+ tmp = s[1];
326
+ t[1] = s[3];
327
+ t[3] = tmp;
328
+ tmp = s[2];
329
+ t[2] = s[6];
330
+ t[6] = tmp;
331
+ tmp = s[5];
332
+ t[5] = s[7];
333
+ t[7] = tmp;
334
+ } else {
335
+ t[0] = s[0];
336
+ t[1] = s[3];
337
+ t[2] = s[6];
338
+ t[3] = s[1];
339
+ t[4] = s[4];
340
+ t[5] = s[7];
341
+ t[6] = s[2];
342
+ t[7] = s[5];
343
+ t[8] = s[8];
344
+ }
345
+ return this;
346
+ }
347
+ setFromMat4(m) {
348
+ const src = m.data;
349
+ const dst = this.data;
350
+ dst[0] = src[0];
351
+ dst[1] = src[1];
352
+ dst[2] = src[2];
353
+ dst[3] = src[4];
354
+ dst[4] = src[5];
355
+ dst[5] = src[6];
356
+ dst[6] = src[8];
357
+ dst[7] = src[9];
358
+ dst[8] = src[10];
359
+ return this;
360
+ }
361
+ setFromQuat(r) {
362
+ const qx = r.x;
363
+ const qy = r.y;
364
+ const qz = r.z;
365
+ const qw = r.w;
366
+ const x2 = qx + qx;
367
+ const y2 = qy + qy;
368
+ const z2 = qz + qz;
369
+ const xx = qx * x2;
370
+ const xy = qx * y2;
371
+ const xz = qx * z2;
372
+ const yy = qy * y2;
373
+ const yz = qy * z2;
374
+ const zz = qz * z2;
375
+ const wx = qw * x2;
376
+ const wy = qw * y2;
377
+ const wz = qw * z2;
378
+ const m = this.data;
379
+ m[0] = 1 - (yy + zz);
380
+ m[1] = xy + wz;
381
+ m[2] = xz - wy;
382
+ m[3] = xy - wz;
383
+ m[4] = 1 - (xx + zz);
384
+ m[5] = yz + wx;
385
+ m[6] = xz + wy;
386
+ m[7] = yz - wx;
387
+ m[8] = 1 - (xx + yy);
388
+ return this;
389
+ }
390
+ invertMat4(src) {
391
+ const s = src.data;
392
+ const a0 = s[0];
393
+ const a1 = s[1];
394
+ const a2 = s[2];
395
+ const a4 = s[4];
396
+ const a5 = s[5];
397
+ const a6 = s[6];
398
+ const a8 = s[8];
399
+ const a9 = s[9];
400
+ const a10 = s[10];
401
+ const b11 = a10 * a5 - a6 * a9;
402
+ const b21 = -a10 * a1 + a2 * a9;
403
+ const b31 = a6 * a1 - a2 * a5;
404
+ const b12 = -a10 * a4 + a6 * a8;
405
+ const b22 = a10 * a0 - a2 * a8;
406
+ const b32 = -a6 * a0 + a2 * a4;
407
+ const b13 = a9 * a4 - a5 * a8;
408
+ const b23 = -a9 * a0 + a1 * a8;
409
+ const b33 = a5 * a0 - a1 * a4;
410
+ const det = a0 * b11 + a1 * b12 + a2 * b13;
411
+ if (det === 0) {
412
+ this.setIdentity();
413
+ } else {
414
+ const invDet = 1 / det;
415
+ const t = this.data;
416
+ t[0] = b11 * invDet;
417
+ t[1] = b21 * invDet;
418
+ t[2] = b31 * invDet;
419
+ t[3] = b12 * invDet;
420
+ t[4] = b22 * invDet;
421
+ t[5] = b32 * invDet;
422
+ t[6] = b13 * invDet;
423
+ t[7] = b23 * invDet;
424
+ t[8] = b33 * invDet;
425
+ }
426
+ return this;
427
+ }
428
+ transformVector(vec, res = new Vec3()) {
429
+ const m = this.data;
430
+ const { x, y, z } = vec;
431
+ res.x = x * m[0] + y * m[3] + z * m[6];
432
+ res.y = x * m[1] + y * m[4] + z * m[7];
433
+ res.z = x * m[2] + y * m[5] + z * m[8];
434
+ return res;
435
+ }
436
+ static{
437
+ this.IDENTITY = Object.freeze(new Mat3());
438
+ }
439
+ static{
440
+ this.ZERO = Object.freeze(new Mat3().set([
441
+ 0,
442
+ 0,
443
+ 0,
444
+ 0,
445
+ 0,
446
+ 0,
447
+ 0,
448
+ 0,
449
+ 0
450
+ ]));
451
+ }
452
+ }
453
+
454
+ class Vec2 {
455
+ constructor(x = 0, y = 0){
456
+ if (x.length === 2) {
457
+ this.x = x[0];
458
+ this.y = x[1];
459
+ } else {
460
+ this.x = x;
461
+ this.y = y;
462
+ }
463
+ }
464
+ add(rhs) {
465
+ this.x += rhs.x;
466
+ this.y += rhs.y;
467
+ return this;
468
+ }
469
+ add2(lhs, rhs) {
470
+ this.x = lhs.x + rhs.x;
471
+ this.y = lhs.y + rhs.y;
472
+ return this;
473
+ }
474
+ addScalar(scalar) {
475
+ this.x += scalar;
476
+ this.y += scalar;
477
+ return this;
478
+ }
479
+ addScaled(rhs, scalar) {
480
+ this.x += rhs.x * scalar;
481
+ this.y += rhs.y * scalar;
482
+ return this;
483
+ }
484
+ clone() {
485
+ const cstr = this.constructor;
486
+ return new cstr(this.x, this.y);
487
+ }
488
+ copy(rhs) {
489
+ this.x = rhs.x;
490
+ this.y = rhs.y;
491
+ return this;
492
+ }
493
+ cross(rhs) {
494
+ return this.x * rhs.y - this.y * rhs.x;
495
+ }
496
+ distance(rhs) {
497
+ const x = this.x - rhs.x;
498
+ const y = this.y - rhs.y;
499
+ return Math.sqrt(x * x + y * y);
500
+ }
501
+ div(rhs) {
502
+ this.x /= rhs.x;
503
+ this.y /= rhs.y;
504
+ return this;
505
+ }
506
+ div2(lhs, rhs) {
507
+ this.x = lhs.x / rhs.x;
508
+ this.y = lhs.y / rhs.y;
509
+ return this;
510
+ }
511
+ divScalar(scalar) {
512
+ this.x /= scalar;
513
+ this.y /= scalar;
514
+ return this;
515
+ }
516
+ dot(rhs) {
517
+ return this.x * rhs.x + this.y * rhs.y;
518
+ }
519
+ equals(rhs) {
520
+ return this.x === rhs.x && this.y === rhs.y;
521
+ }
522
+ equalsApprox(rhs, epsilon = 1e-6) {
523
+ return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon;
524
+ }
525
+ length() {
526
+ return Math.sqrt(this.x * this.x + this.y * this.y);
527
+ }
528
+ lengthSq() {
529
+ return this.x * this.x + this.y * this.y;
530
+ }
531
+ lerp(lhs, rhs, alpha) {
532
+ this.x = lhs.x + alpha * (rhs.x - lhs.x);
533
+ this.y = lhs.y + alpha * (rhs.y - lhs.y);
534
+ return this;
535
+ }
536
+ mul(rhs) {
537
+ this.x *= rhs.x;
538
+ this.y *= rhs.y;
539
+ return this;
540
+ }
541
+ mul2(lhs, rhs) {
542
+ this.x = lhs.x * rhs.x;
543
+ this.y = lhs.y * rhs.y;
544
+ return this;
545
+ }
546
+ mulScalar(scalar) {
547
+ this.x *= scalar;
548
+ this.y *= scalar;
549
+ return this;
550
+ }
551
+ normalize(src = this) {
552
+ const lengthSq = src.x * src.x + src.y * src.y;
553
+ if (lengthSq > 0) {
554
+ const invLength = 1 / Math.sqrt(lengthSq);
555
+ this.x = src.x * invLength;
556
+ this.y = src.y * invLength;
557
+ }
558
+ return this;
559
+ }
560
+ rotate(degrees) {
561
+ const angle = Math.atan2(this.x, this.y) + degrees * math.DEG_TO_RAD;
562
+ const len = Math.sqrt(this.x * this.x + this.y * this.y);
563
+ this.x = Math.sin(angle) * len;
564
+ this.y = Math.cos(angle) * len;
565
+ return this;
566
+ }
567
+ angle() {
568
+ return Math.atan2(this.x, this.y) * math.RAD_TO_DEG;
569
+ }
570
+ angleTo(rhs) {
571
+ return Math.atan2(this.x * rhs.y + this.y * rhs.x, this.x * rhs.x + this.y * rhs.y) * math.RAD_TO_DEG;
572
+ }
573
+ floor(src = this) {
574
+ this.x = Math.floor(src.x);
575
+ this.y = Math.floor(src.y);
576
+ return this;
577
+ }
578
+ ceil(src = this) {
579
+ this.x = Math.ceil(src.x);
580
+ this.y = Math.ceil(src.y);
581
+ return this;
582
+ }
583
+ round(src = this) {
584
+ this.x = Math.round(src.x);
585
+ this.y = Math.round(src.y);
586
+ return this;
587
+ }
588
+ min(rhs) {
589
+ if (rhs.x < this.x) this.x = rhs.x;
590
+ if (rhs.y < this.y) this.y = rhs.y;
591
+ return this;
592
+ }
593
+ max(rhs) {
594
+ if (rhs.x > this.x) this.x = rhs.x;
595
+ if (rhs.y > this.y) this.y = rhs.y;
596
+ return this;
597
+ }
598
+ set(x, y) {
599
+ this.x = x;
600
+ this.y = y;
601
+ return this;
602
+ }
603
+ sub(rhs) {
604
+ this.x -= rhs.x;
605
+ this.y -= rhs.y;
606
+ return this;
607
+ }
608
+ sub2(lhs, rhs) {
609
+ this.x = lhs.x - rhs.x;
610
+ this.y = lhs.y - rhs.y;
611
+ return this;
612
+ }
613
+ subScalar(scalar) {
614
+ this.x -= scalar;
615
+ this.y -= scalar;
616
+ return this;
617
+ }
618
+ fromArray(arr, offset = 0) {
619
+ this.x = arr[offset] ?? this.x;
620
+ this.y = arr[offset + 1] ?? this.y;
621
+ return this;
622
+ }
623
+ toString() {
624
+ return `[${this.x}, ${this.y}]`;
625
+ }
626
+ toArray(arr = [], offset = 0) {
627
+ arr[offset] = this.x;
628
+ arr[offset + 1] = this.y;
629
+ return arr;
630
+ }
631
+ static angleRad(lhs, rhs) {
632
+ return Math.atan2(lhs.x * rhs.y - lhs.y * rhs.x, lhs.x * rhs.x + lhs.y * rhs.y);
633
+ }
634
+ static{
635
+ this.ZERO = Object.freeze(new Vec2(0, 0));
636
+ }
637
+ static{
638
+ this.HALF = Object.freeze(new Vec2(0.5, 0.5));
639
+ }
640
+ static{
641
+ this.ONE = Object.freeze(new Vec2(1, 1));
642
+ }
643
+ static{
644
+ this.UP = Object.freeze(new Vec2(0, 1));
645
+ }
646
+ static{
647
+ this.DOWN = Object.freeze(new Vec2(0, -1));
648
+ }
649
+ static{
650
+ this.RIGHT = Object.freeze(new Vec2(1, 0));
651
+ }
652
+ static{
653
+ this.LEFT = Object.freeze(new Vec2(-1, 0));
654
+ }
655
+ }
656
+
657
+ class Vec4 {
658
+ constructor(x = 0, y = 0, z = 0, w = 0){
659
+ if (x.length === 4) {
660
+ this.x = x[0];
661
+ this.y = x[1];
662
+ this.z = x[2];
663
+ this.w = x[3];
664
+ } else {
665
+ this.x = x;
666
+ this.y = y;
667
+ this.z = z;
668
+ this.w = w;
669
+ }
670
+ }
671
+ add(rhs) {
672
+ this.x += rhs.x;
673
+ this.y += rhs.y;
674
+ this.z += rhs.z;
675
+ this.w += rhs.w;
676
+ return this;
677
+ }
678
+ add2(lhs, rhs) {
679
+ this.x = lhs.x + rhs.x;
680
+ this.y = lhs.y + rhs.y;
681
+ this.z = lhs.z + rhs.z;
682
+ this.w = lhs.w + rhs.w;
683
+ return this;
684
+ }
685
+ addScalar(scalar) {
686
+ this.x += scalar;
687
+ this.y += scalar;
688
+ this.z += scalar;
689
+ this.w += scalar;
690
+ return this;
691
+ }
692
+ addScaled(rhs, scalar) {
693
+ this.x += rhs.x * scalar;
694
+ this.y += rhs.y * scalar;
695
+ this.z += rhs.z * scalar;
696
+ this.w += rhs.w * scalar;
697
+ return this;
698
+ }
699
+ clone() {
700
+ const cstr = this.constructor;
701
+ return new cstr(this.x, this.y, this.z, this.w);
702
+ }
703
+ copy(rhs) {
704
+ this.x = rhs.x;
705
+ this.y = rhs.y;
706
+ this.z = rhs.z;
707
+ this.w = rhs.w;
708
+ return this;
709
+ }
710
+ div(rhs) {
711
+ this.x /= rhs.x;
712
+ this.y /= rhs.y;
713
+ this.z /= rhs.z;
714
+ this.w /= rhs.w;
715
+ return this;
716
+ }
717
+ div2(lhs, rhs) {
718
+ this.x = lhs.x / rhs.x;
719
+ this.y = lhs.y / rhs.y;
720
+ this.z = lhs.z / rhs.z;
721
+ this.w = lhs.w / rhs.w;
722
+ return this;
723
+ }
724
+ divScalar(scalar) {
725
+ this.x /= scalar;
726
+ this.y /= scalar;
727
+ this.z /= scalar;
728
+ this.w /= scalar;
729
+ return this;
730
+ }
731
+ dot(rhs) {
732
+ return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z + this.w * rhs.w;
733
+ }
734
+ equals(rhs) {
735
+ return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z && this.w === rhs.w;
736
+ }
737
+ equalsApprox(rhs, epsilon = 1e-6) {
738
+ return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon && Math.abs(this.w - rhs.w) < epsilon;
739
+ }
740
+ length() {
741
+ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
742
+ }
743
+ lengthSq() {
744
+ return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
745
+ }
746
+ lerp(lhs, rhs, alpha) {
747
+ this.x = lhs.x + alpha * (rhs.x - lhs.x);
748
+ this.y = lhs.y + alpha * (rhs.y - lhs.y);
749
+ this.z = lhs.z + alpha * (rhs.z - lhs.z);
750
+ this.w = lhs.w + alpha * (rhs.w - lhs.w);
751
+ return this;
752
+ }
753
+ mul(rhs) {
754
+ this.x *= rhs.x;
755
+ this.y *= rhs.y;
756
+ this.z *= rhs.z;
757
+ this.w *= rhs.w;
758
+ return this;
759
+ }
760
+ mul2(lhs, rhs) {
761
+ this.x = lhs.x * rhs.x;
762
+ this.y = lhs.y * rhs.y;
763
+ this.z = lhs.z * rhs.z;
764
+ this.w = lhs.w * rhs.w;
765
+ return this;
766
+ }
767
+ mulScalar(scalar) {
768
+ this.x *= scalar;
769
+ this.y *= scalar;
770
+ this.z *= scalar;
771
+ this.w *= scalar;
772
+ return this;
773
+ }
774
+ normalize(src = this) {
775
+ const lengthSq = src.x * src.x + src.y * src.y + src.z * src.z + src.w * src.w;
776
+ if (lengthSq > 0) {
777
+ const invLength = 1 / Math.sqrt(lengthSq);
778
+ this.x = src.x * invLength;
779
+ this.y = src.y * invLength;
780
+ this.z = src.z * invLength;
781
+ this.w = src.w * invLength;
782
+ }
783
+ return this;
784
+ }
785
+ floor(src = this) {
786
+ this.x = Math.floor(src.x);
787
+ this.y = Math.floor(src.y);
788
+ this.z = Math.floor(src.z);
789
+ this.w = Math.floor(src.w);
790
+ return this;
791
+ }
792
+ ceil(src = this) {
793
+ this.x = Math.ceil(src.x);
794
+ this.y = Math.ceil(src.y);
795
+ this.z = Math.ceil(src.z);
796
+ this.w = Math.ceil(src.w);
797
+ return this;
798
+ }
799
+ round(src = this) {
800
+ this.x = Math.round(src.x);
801
+ this.y = Math.round(src.y);
802
+ this.z = Math.round(src.z);
803
+ this.w = Math.round(src.w);
804
+ return this;
805
+ }
806
+ min(rhs) {
807
+ if (rhs.x < this.x) this.x = rhs.x;
808
+ if (rhs.y < this.y) this.y = rhs.y;
809
+ if (rhs.z < this.z) this.z = rhs.z;
810
+ if (rhs.w < this.w) this.w = rhs.w;
811
+ return this;
812
+ }
813
+ max(rhs) {
814
+ if (rhs.x > this.x) this.x = rhs.x;
815
+ if (rhs.y > this.y) this.y = rhs.y;
816
+ if (rhs.z > this.z) this.z = rhs.z;
817
+ if (rhs.w > this.w) this.w = rhs.w;
818
+ return this;
819
+ }
820
+ set(x, y, z, w) {
821
+ this.x = x;
822
+ this.y = y;
823
+ this.z = z;
824
+ this.w = w;
825
+ return this;
826
+ }
827
+ sub(rhs) {
828
+ this.x -= rhs.x;
829
+ this.y -= rhs.y;
830
+ this.z -= rhs.z;
831
+ this.w -= rhs.w;
832
+ return this;
833
+ }
834
+ sub2(lhs, rhs) {
835
+ this.x = lhs.x - rhs.x;
836
+ this.y = lhs.y - rhs.y;
837
+ this.z = lhs.z - rhs.z;
838
+ this.w = lhs.w - rhs.w;
839
+ return this;
840
+ }
841
+ subScalar(scalar) {
842
+ this.x -= scalar;
843
+ this.y -= scalar;
844
+ this.z -= scalar;
845
+ this.w -= scalar;
846
+ return this;
847
+ }
848
+ fromArray(arr, offset = 0) {
849
+ this.x = arr[offset] ?? this.x;
850
+ this.y = arr[offset + 1] ?? this.y;
851
+ this.z = arr[offset + 2] ?? this.z;
852
+ this.w = arr[offset + 3] ?? this.w;
853
+ return this;
854
+ }
855
+ toString() {
856
+ return `[${this.x}, ${this.y}, ${this.z}, ${this.w}]`;
857
+ }
858
+ toArray(arr = [], offset = 0) {
859
+ arr[offset] = this.x;
860
+ arr[offset + 1] = this.y;
861
+ arr[offset + 2] = this.z;
862
+ arr[offset + 3] = this.w;
863
+ return arr;
864
+ }
865
+ static{
866
+ this.ZERO = Object.freeze(new Vec4(0, 0, 0, 0));
867
+ }
868
+ static{
869
+ this.HALF = Object.freeze(new Vec4(0.5, 0.5, 0.5, 0.5));
870
+ }
871
+ static{
872
+ this.ONE = Object.freeze(new Vec4(1, 1, 1, 1));
873
+ }
874
+ }
875
+
876
+ const _halfSize = new Vec2();
877
+ const x = new Vec3();
878
+ const y = new Vec3();
879
+ const z = new Vec3();
880
+ const scale = new Vec3();
881
+ class Mat4 {
882
+ constructor(){
883
+ this.data = new Float32Array(16);
884
+ this.data[0] = this.data[5] = this.data[10] = this.data[15] = 1;
885
+ }
886
+ static _getPerspectiveHalfSize(halfSize, fov, aspect, znear, fovIsHorizontal) {
887
+ if (fovIsHorizontal) {
888
+ halfSize.x = znear * Math.tan(fov * Math.PI / 360);
889
+ halfSize.y = halfSize.x / aspect;
890
+ } else {
891
+ halfSize.y = znear * Math.tan(fov * Math.PI / 360);
892
+ halfSize.x = halfSize.y * aspect;
893
+ }
894
+ }
895
+ add2(lhs, rhs) {
896
+ const a = lhs.data, b = rhs.data, r = this.data;
897
+ r[0] = a[0] + b[0];
898
+ r[1] = a[1] + b[1];
899
+ r[2] = a[2] + b[2];
900
+ r[3] = a[3] + b[3];
901
+ r[4] = a[4] + b[4];
902
+ r[5] = a[5] + b[5];
903
+ r[6] = a[6] + b[6];
904
+ r[7] = a[7] + b[7];
905
+ r[8] = a[8] + b[8];
906
+ r[9] = a[9] + b[9];
907
+ r[10] = a[10] + b[10];
908
+ r[11] = a[11] + b[11];
909
+ r[12] = a[12] + b[12];
910
+ r[13] = a[13] + b[13];
911
+ r[14] = a[14] + b[14];
912
+ r[15] = a[15] + b[15];
913
+ return this;
914
+ }
915
+ add(rhs) {
916
+ return this.add2(this, rhs);
917
+ }
918
+ clone() {
919
+ const cstr = this.constructor;
920
+ return new cstr().copy(this);
921
+ }
922
+ copy(rhs) {
923
+ const src = rhs.data, dst = this.data;
924
+ dst[0] = src[0];
925
+ dst[1] = src[1];
926
+ dst[2] = src[2];
927
+ dst[3] = src[3];
928
+ dst[4] = src[4];
929
+ dst[5] = src[5];
930
+ dst[6] = src[6];
931
+ dst[7] = src[7];
932
+ dst[8] = src[8];
933
+ dst[9] = src[9];
934
+ dst[10] = src[10];
935
+ dst[11] = src[11];
936
+ dst[12] = src[12];
937
+ dst[13] = src[13];
938
+ dst[14] = src[14];
939
+ dst[15] = src[15];
940
+ return this;
941
+ }
942
+ equals(rhs) {
943
+ const l = this.data, r = rhs.data;
944
+ return l[0] === r[0] && l[1] === r[1] && l[2] === r[2] && l[3] === r[3] && l[4] === r[4] && l[5] === r[5] && l[6] === r[6] && l[7] === r[7] && l[8] === r[8] && l[9] === r[9] && l[10] === r[10] && l[11] === r[11] && l[12] === r[12] && l[13] === r[13] && l[14] === r[14] && l[15] === r[15];
945
+ }
946
+ isIdentity() {
947
+ const m = this.data;
948
+ return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 0 && m[4] === 0 && m[5] === 1 && m[6] === 0 && m[7] === 0 && m[8] === 0 && m[9] === 0 && m[10] === 1 && m[11] === 0 && m[12] === 0 && m[13] === 0 && m[14] === 0 && m[15] === 1;
949
+ }
950
+ mul2(lhs, rhs) {
951
+ const a = lhs.data;
952
+ const b = rhs.data;
953
+ const r = this.data;
954
+ const a00 = a[0];
955
+ const a01 = a[1];
956
+ const a02 = a[2];
957
+ const a03 = a[3];
958
+ const a10 = a[4];
959
+ const a11 = a[5];
960
+ const a12 = a[6];
961
+ const a13 = a[7];
962
+ const a20 = a[8];
963
+ const a21 = a[9];
964
+ const a22 = a[10];
965
+ const a23 = a[11];
966
+ const a30 = a[12];
967
+ const a31 = a[13];
968
+ const a32 = a[14];
969
+ const a33 = a[15];
970
+ let b0, b1, b2, b3;
971
+ b0 = b[0];
972
+ b1 = b[1];
973
+ b2 = b[2];
974
+ b3 = b[3];
975
+ r[0] = a00 * b0 + a10 * b1 + a20 * b2 + a30 * b3;
976
+ r[1] = a01 * b0 + a11 * b1 + a21 * b2 + a31 * b3;
977
+ r[2] = a02 * b0 + a12 * b1 + a22 * b2 + a32 * b3;
978
+ r[3] = a03 * b0 + a13 * b1 + a23 * b2 + a33 * b3;
979
+ b0 = b[4];
980
+ b1 = b[5];
981
+ b2 = b[6];
982
+ b3 = b[7];
983
+ r[4] = a00 * b0 + a10 * b1 + a20 * b2 + a30 * b3;
984
+ r[5] = a01 * b0 + a11 * b1 + a21 * b2 + a31 * b3;
985
+ r[6] = a02 * b0 + a12 * b1 + a22 * b2 + a32 * b3;
986
+ r[7] = a03 * b0 + a13 * b1 + a23 * b2 + a33 * b3;
987
+ b0 = b[8];
988
+ b1 = b[9];
989
+ b2 = b[10];
990
+ b3 = b[11];
991
+ r[8] = a00 * b0 + a10 * b1 + a20 * b2 + a30 * b3;
992
+ r[9] = a01 * b0 + a11 * b1 + a21 * b2 + a31 * b3;
993
+ r[10] = a02 * b0 + a12 * b1 + a22 * b2 + a32 * b3;
994
+ r[11] = a03 * b0 + a13 * b1 + a23 * b2 + a33 * b3;
995
+ b0 = b[12];
996
+ b1 = b[13];
997
+ b2 = b[14];
998
+ b3 = b[15];
999
+ r[12] = a00 * b0 + a10 * b1 + a20 * b2 + a30 * b3;
1000
+ r[13] = a01 * b0 + a11 * b1 + a21 * b2 + a31 * b3;
1001
+ r[14] = a02 * b0 + a12 * b1 + a22 * b2 + a32 * b3;
1002
+ r[15] = a03 * b0 + a13 * b1 + a23 * b2 + a33 * b3;
1003
+ return this;
1004
+ }
1005
+ mulAffine2(lhs, rhs) {
1006
+ const a = lhs.data;
1007
+ const b = rhs.data;
1008
+ const r = this.data;
1009
+ const a00 = a[0];
1010
+ const a01 = a[1];
1011
+ const a02 = a[2];
1012
+ const a10 = a[4];
1013
+ const a11 = a[5];
1014
+ const a12 = a[6];
1015
+ const a20 = a[8];
1016
+ const a21 = a[9];
1017
+ const a22 = a[10];
1018
+ const a30 = a[12];
1019
+ const a31 = a[13];
1020
+ const a32 = a[14];
1021
+ let b0, b1, b2;
1022
+ b0 = b[0];
1023
+ b1 = b[1];
1024
+ b2 = b[2];
1025
+ r[0] = a00 * b0 + a10 * b1 + a20 * b2;
1026
+ r[1] = a01 * b0 + a11 * b1 + a21 * b2;
1027
+ r[2] = a02 * b0 + a12 * b1 + a22 * b2;
1028
+ r[3] = 0;
1029
+ b0 = b[4];
1030
+ b1 = b[5];
1031
+ b2 = b[6];
1032
+ r[4] = a00 * b0 + a10 * b1 + a20 * b2;
1033
+ r[5] = a01 * b0 + a11 * b1 + a21 * b2;
1034
+ r[6] = a02 * b0 + a12 * b1 + a22 * b2;
1035
+ r[7] = 0;
1036
+ b0 = b[8];
1037
+ b1 = b[9];
1038
+ b2 = b[10];
1039
+ r[8] = a00 * b0 + a10 * b1 + a20 * b2;
1040
+ r[9] = a01 * b0 + a11 * b1 + a21 * b2;
1041
+ r[10] = a02 * b0 + a12 * b1 + a22 * b2;
1042
+ r[11] = 0;
1043
+ b0 = b[12];
1044
+ b1 = b[13];
1045
+ b2 = b[14];
1046
+ r[12] = a00 * b0 + a10 * b1 + a20 * b2 + a30;
1047
+ r[13] = a01 * b0 + a11 * b1 + a21 * b2 + a31;
1048
+ r[14] = a02 * b0 + a12 * b1 + a22 * b2 + a32;
1049
+ r[15] = 1;
1050
+ return this;
1051
+ }
1052
+ mul(rhs) {
1053
+ return this.mul2(this, rhs);
1054
+ }
1055
+ transformPoint(vec, res = new Vec3()) {
1056
+ const m = this.data;
1057
+ const { x, y, z } = vec;
1058
+ res.x = x * m[0] + y * m[4] + z * m[8] + m[12];
1059
+ res.y = x * m[1] + y * m[5] + z * m[9] + m[13];
1060
+ res.z = x * m[2] + y * m[6] + z * m[10] + m[14];
1061
+ return res;
1062
+ }
1063
+ transformVector(vec, res = new Vec3()) {
1064
+ const m = this.data;
1065
+ const { x, y, z } = vec;
1066
+ res.x = x * m[0] + y * m[4] + z * m[8];
1067
+ res.y = x * m[1] + y * m[5] + z * m[9];
1068
+ res.z = x * m[2] + y * m[6] + z * m[10];
1069
+ return res;
1070
+ }
1071
+ transformVec4(vec, res = new Vec4()) {
1072
+ const m = this.data;
1073
+ const { x, y, z, w } = vec;
1074
+ res.x = x * m[0] + y * m[4] + z * m[8] + w * m[12];
1075
+ res.y = x * m[1] + y * m[5] + z * m[9] + w * m[13];
1076
+ res.z = x * m[2] + y * m[6] + z * m[10] + w * m[14];
1077
+ res.w = x * m[3] + y * m[7] + z * m[11] + w * m[15];
1078
+ return res;
1079
+ }
1080
+ setLookAt(position, target, up) {
1081
+ z.sub2(position, target).normalize();
1082
+ y.copy(up).normalize();
1083
+ x.cross(y, z).normalize();
1084
+ y.cross(z, x);
1085
+ const r = this.data;
1086
+ r[0] = x.x;
1087
+ r[1] = x.y;
1088
+ r[2] = x.z;
1089
+ r[3] = 0;
1090
+ r[4] = y.x;
1091
+ r[5] = y.y;
1092
+ r[6] = y.z;
1093
+ r[7] = 0;
1094
+ r[8] = z.x;
1095
+ r[9] = z.y;
1096
+ r[10] = z.z;
1097
+ r[11] = 0;
1098
+ r[12] = position.x;
1099
+ r[13] = position.y;
1100
+ r[14] = position.z;
1101
+ r[15] = 1;
1102
+ return this;
1103
+ }
1104
+ setFrustum(left, right, bottom, top, znear, zfar) {
1105
+ const temp1 = 2 * znear;
1106
+ const temp2 = right - left;
1107
+ const temp3 = top - bottom;
1108
+ const temp4 = zfar - znear;
1109
+ const r = this.data;
1110
+ r[0] = temp1 / temp2;
1111
+ r[1] = 0;
1112
+ r[2] = 0;
1113
+ r[3] = 0;
1114
+ r[4] = 0;
1115
+ r[5] = temp1 / temp3;
1116
+ r[6] = 0;
1117
+ r[7] = 0;
1118
+ r[8] = (right + left) / temp2;
1119
+ r[9] = (top + bottom) / temp3;
1120
+ r[10] = (-zfar - znear) / temp4;
1121
+ r[11] = -1;
1122
+ r[12] = 0;
1123
+ r[13] = 0;
1124
+ r[14] = -temp1 * zfar / temp4;
1125
+ r[15] = 0;
1126
+ return this;
1127
+ }
1128
+ setPerspective(fov, aspect, znear, zfar, fovIsHorizontal) {
1129
+ Mat4._getPerspectiveHalfSize(_halfSize, fov, aspect, znear, fovIsHorizontal);
1130
+ return this.setFrustum(-_halfSize.x, _halfSize.x, -_halfSize.y, _halfSize.y, znear, zfar);
1131
+ }
1132
+ setOrtho(left, right, bottom, top, near, far) {
1133
+ const r = this.data;
1134
+ r[0] = 2 / (right - left);
1135
+ r[1] = 0;
1136
+ r[2] = 0;
1137
+ r[3] = 0;
1138
+ r[4] = 0;
1139
+ r[5] = 2 / (top - bottom);
1140
+ r[6] = 0;
1141
+ r[7] = 0;
1142
+ r[8] = 0;
1143
+ r[9] = 0;
1144
+ r[10] = -2 / (far - near);
1145
+ r[11] = 0;
1146
+ r[12] = -(right + left) / (right - left);
1147
+ r[13] = -(top + bottom) / (top - bottom);
1148
+ r[14] = -(far + near) / (far - near);
1149
+ r[15] = 1;
1150
+ return this;
1151
+ }
1152
+ setFromAxisAngle(axis, angle) {
1153
+ angle *= math.DEG_TO_RAD;
1154
+ const { x, y, z } = axis;
1155
+ const c = Math.cos(angle);
1156
+ const s = Math.sin(angle);
1157
+ const t = 1 - c;
1158
+ const tx = t * x;
1159
+ const ty = t * y;
1160
+ const m = this.data;
1161
+ m[0] = tx * x + c;
1162
+ m[1] = tx * y + s * z;
1163
+ m[2] = tx * z - s * y;
1164
+ m[3] = 0;
1165
+ m[4] = tx * y - s * z;
1166
+ m[5] = ty * y + c;
1167
+ m[6] = ty * z + s * x;
1168
+ m[7] = 0;
1169
+ m[8] = tx * z + s * y;
1170
+ m[9] = ty * z - x * s;
1171
+ m[10] = t * z * z + c;
1172
+ m[11] = 0;
1173
+ m[12] = 0;
1174
+ m[13] = 0;
1175
+ m[14] = 0;
1176
+ m[15] = 1;
1177
+ return this;
1178
+ }
1179
+ setTranslate(x, y, z) {
1180
+ const m = this.data;
1181
+ m[0] = 1;
1182
+ m[1] = 0;
1183
+ m[2] = 0;
1184
+ m[3] = 0;
1185
+ m[4] = 0;
1186
+ m[5] = 1;
1187
+ m[6] = 0;
1188
+ m[7] = 0;
1189
+ m[8] = 0;
1190
+ m[9] = 0;
1191
+ m[10] = 1;
1192
+ m[11] = 0;
1193
+ m[12] = x;
1194
+ m[13] = y;
1195
+ m[14] = z;
1196
+ m[15] = 1;
1197
+ return this;
1198
+ }
1199
+ setScale(x, y, z) {
1200
+ const m = this.data;
1201
+ m[0] = x;
1202
+ m[1] = 0;
1203
+ m[2] = 0;
1204
+ m[3] = 0;
1205
+ m[4] = 0;
1206
+ m[5] = y;
1207
+ m[6] = 0;
1208
+ m[7] = 0;
1209
+ m[8] = 0;
1210
+ m[9] = 0;
1211
+ m[10] = z;
1212
+ m[11] = 0;
1213
+ m[12] = 0;
1214
+ m[13] = 0;
1215
+ m[14] = 0;
1216
+ m[15] = 1;
1217
+ return this;
1218
+ }
1219
+ setViewport(x, y, width, height) {
1220
+ const m = this.data;
1221
+ m[0] = width * 0.5;
1222
+ m[1] = 0;
1223
+ m[2] = 0;
1224
+ m[3] = 0;
1225
+ m[4] = 0;
1226
+ m[5] = height * 0.5;
1227
+ m[6] = 0;
1228
+ m[7] = 0;
1229
+ m[8] = 0;
1230
+ m[9] = 0;
1231
+ m[10] = 0.5;
1232
+ m[11] = 0;
1233
+ m[12] = x + width * 0.5;
1234
+ m[13] = y + height * 0.5;
1235
+ m[14] = 0.5;
1236
+ m[15] = 1;
1237
+ return this;
1238
+ }
1239
+ setReflection(normal, distance) {
1240
+ const a = normal.x;
1241
+ const b = normal.y;
1242
+ const c = normal.z;
1243
+ const data = this.data;
1244
+ data[0] = 1.0 - 2 * a * a;
1245
+ data[1] = -2 * a * b;
1246
+ data[2] = -2 * a * c;
1247
+ data[3] = 0;
1248
+ data[4] = -2 * a * b;
1249
+ data[5] = 1.0 - 2 * b * b;
1250
+ data[6] = -2 * b * c;
1251
+ data[7] = 0;
1252
+ data[8] = -2 * a * c;
1253
+ data[9] = -2 * b * c;
1254
+ data[10] = 1.0 - 2 * c * c;
1255
+ data[11] = 0;
1256
+ data[12] = -2 * a * distance;
1257
+ data[13] = -2 * b * distance;
1258
+ data[14] = -2 * c * distance;
1259
+ data[15] = 1;
1260
+ return this;
1261
+ }
1262
+ invert(src = this) {
1263
+ const s = src.data;
1264
+ const a00 = s[0];
1265
+ const a01 = s[1];
1266
+ const a02 = s[2];
1267
+ const a03 = s[3];
1268
+ const a10 = s[4];
1269
+ const a11 = s[5];
1270
+ const a12 = s[6];
1271
+ const a13 = s[7];
1272
+ const a20 = s[8];
1273
+ const a21 = s[9];
1274
+ const a22 = s[10];
1275
+ const a23 = s[11];
1276
+ const a30 = s[12];
1277
+ const a31 = s[13];
1278
+ const a32 = s[14];
1279
+ const a33 = s[15];
1280
+ const b00 = a00 * a11 - a01 * a10;
1281
+ const b01 = a00 * a12 - a02 * a10;
1282
+ const b02 = a00 * a13 - a03 * a10;
1283
+ const b03 = a01 * a12 - a02 * a11;
1284
+ const b04 = a01 * a13 - a03 * a11;
1285
+ const b05 = a02 * a13 - a03 * a12;
1286
+ const b06 = a20 * a31 - a21 * a30;
1287
+ const b07 = a20 * a32 - a22 * a30;
1288
+ const b08 = a20 * a33 - a23 * a30;
1289
+ const b09 = a21 * a32 - a22 * a31;
1290
+ const b10 = a21 * a33 - a23 * a31;
1291
+ const b11 = a22 * a33 - a23 * a32;
1292
+ const det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
1293
+ if (det === 0) {
1294
+ this.setIdentity();
1295
+ } else {
1296
+ const invDet = 1 / det;
1297
+ const t = this.data;
1298
+ t[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet;
1299
+ t[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet;
1300
+ t[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet;
1301
+ t[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet;
1302
+ t[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet;
1303
+ t[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet;
1304
+ t[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet;
1305
+ t[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet;
1306
+ t[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet;
1307
+ t[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet;
1308
+ t[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet;
1309
+ t[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet;
1310
+ t[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet;
1311
+ t[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet;
1312
+ t[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet;
1313
+ t[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet;
1314
+ }
1315
+ return this;
1316
+ }
1317
+ set(src) {
1318
+ const dst = this.data;
1319
+ dst[0] = src[0];
1320
+ dst[1] = src[1];
1321
+ dst[2] = src[2];
1322
+ dst[3] = src[3];
1323
+ dst[4] = src[4];
1324
+ dst[5] = src[5];
1325
+ dst[6] = src[6];
1326
+ dst[7] = src[7];
1327
+ dst[8] = src[8];
1328
+ dst[9] = src[9];
1329
+ dst[10] = src[10];
1330
+ dst[11] = src[11];
1331
+ dst[12] = src[12];
1332
+ dst[13] = src[13];
1333
+ dst[14] = src[14];
1334
+ dst[15] = src[15];
1335
+ return this;
1336
+ }
1337
+ setIdentity() {
1338
+ const m = this.data;
1339
+ m[0] = 1;
1340
+ m[1] = 0;
1341
+ m[2] = 0;
1342
+ m[3] = 0;
1343
+ m[4] = 0;
1344
+ m[5] = 1;
1345
+ m[6] = 0;
1346
+ m[7] = 0;
1347
+ m[8] = 0;
1348
+ m[9] = 0;
1349
+ m[10] = 1;
1350
+ m[11] = 0;
1351
+ m[12] = 0;
1352
+ m[13] = 0;
1353
+ m[14] = 0;
1354
+ m[15] = 1;
1355
+ return this;
1356
+ }
1357
+ setTRS(t, r, s) {
1358
+ const qx = r.x;
1359
+ const qy = r.y;
1360
+ const qz = r.z;
1361
+ const qw = r.w;
1362
+ const sx = s.x;
1363
+ const sy = s.y;
1364
+ const sz = s.z;
1365
+ const x2 = qx + qx;
1366
+ const y2 = qy + qy;
1367
+ const z2 = qz + qz;
1368
+ const xx = qx * x2;
1369
+ const xy = qx * y2;
1370
+ const xz = qx * z2;
1371
+ const yy = qy * y2;
1372
+ const yz = qy * z2;
1373
+ const zz = qz * z2;
1374
+ const wx = qw * x2;
1375
+ const wy = qw * y2;
1376
+ const wz = qw * z2;
1377
+ const m = this.data;
1378
+ m[0] = (1 - (yy + zz)) * sx;
1379
+ m[1] = (xy + wz) * sx;
1380
+ m[2] = (xz - wy) * sx;
1381
+ m[3] = 0;
1382
+ m[4] = (xy - wz) * sy;
1383
+ m[5] = (1 - (xx + zz)) * sy;
1384
+ m[6] = (yz + wx) * sy;
1385
+ m[7] = 0;
1386
+ m[8] = (xz + wy) * sz;
1387
+ m[9] = (yz - wx) * sz;
1388
+ m[10] = (1 - (xx + yy)) * sz;
1389
+ m[11] = 0;
1390
+ m[12] = t.x;
1391
+ m[13] = t.y;
1392
+ m[14] = t.z;
1393
+ m[15] = 1;
1394
+ return this;
1395
+ }
1396
+ transpose(src = this) {
1397
+ const s = src.data;
1398
+ const t = this.data;
1399
+ if (s === t) {
1400
+ let tmp;
1401
+ tmp = s[1];
1402
+ t[1] = s[4];
1403
+ t[4] = tmp;
1404
+ tmp = s[2];
1405
+ t[2] = s[8];
1406
+ t[8] = tmp;
1407
+ tmp = s[3];
1408
+ t[3] = s[12];
1409
+ t[12] = tmp;
1410
+ tmp = s[6];
1411
+ t[6] = s[9];
1412
+ t[9] = tmp;
1413
+ tmp = s[7];
1414
+ t[7] = s[13];
1415
+ t[13] = tmp;
1416
+ tmp = s[11];
1417
+ t[11] = s[14];
1418
+ t[14] = tmp;
1419
+ } else {
1420
+ t[0] = s[0];
1421
+ t[1] = s[4];
1422
+ t[2] = s[8];
1423
+ t[3] = s[12];
1424
+ t[4] = s[1];
1425
+ t[5] = s[5];
1426
+ t[6] = s[9];
1427
+ t[7] = s[13];
1428
+ t[8] = s[2];
1429
+ t[9] = s[6];
1430
+ t[10] = s[10];
1431
+ t[11] = s[14];
1432
+ t[12] = s[3];
1433
+ t[13] = s[7];
1434
+ t[14] = s[11];
1435
+ t[15] = s[15];
1436
+ }
1437
+ return this;
1438
+ }
1439
+ getTranslation(t = new Vec3()) {
1440
+ return t.set(this.data[12], this.data[13], this.data[14]);
1441
+ }
1442
+ getX(x = new Vec3()) {
1443
+ return x.set(this.data[0], this.data[1], this.data[2]);
1444
+ }
1445
+ getY(y = new Vec3()) {
1446
+ return y.set(this.data[4], this.data[5], this.data[6]);
1447
+ }
1448
+ getZ(z = new Vec3()) {
1449
+ return z.set(this.data[8], this.data[9], this.data[10]);
1450
+ }
1451
+ getScale(scale = new Vec3()) {
1452
+ this.getX(x);
1453
+ this.getY(y);
1454
+ this.getZ(z);
1455
+ scale.set(x.length(), y.length(), z.length());
1456
+ return scale;
1457
+ }
1458
+ get scaleSign() {
1459
+ this.getX(x);
1460
+ this.getY(y);
1461
+ this.getZ(z);
1462
+ x.cross(x, y);
1463
+ return x.dot(z) < 0 ? -1 : 1;
1464
+ }
1465
+ setFromEulerAngles(ex, ey, ez) {
1466
+ ex *= math.DEG_TO_RAD;
1467
+ ey *= math.DEG_TO_RAD;
1468
+ ez *= math.DEG_TO_RAD;
1469
+ const s1 = Math.sin(-ex);
1470
+ const c1 = Math.cos(-ex);
1471
+ const s2 = Math.sin(-ey);
1472
+ const c2 = Math.cos(-ey);
1473
+ const s3 = Math.sin(-ez);
1474
+ const c3 = Math.cos(-ez);
1475
+ const m = this.data;
1476
+ m[0] = c2 * c3;
1477
+ m[1] = -c2 * s3;
1478
+ m[2] = s2;
1479
+ m[3] = 0;
1480
+ m[4] = c1 * s3 + c3 * s1 * s2;
1481
+ m[5] = c1 * c3 - s1 * s2 * s3;
1482
+ m[6] = -c2 * s1;
1483
+ m[7] = 0;
1484
+ m[8] = s1 * s3 - c1 * c3 * s2;
1485
+ m[9] = c3 * s1 + c1 * s2 * s3;
1486
+ m[10] = c1 * c2;
1487
+ m[11] = 0;
1488
+ m[12] = 0;
1489
+ m[13] = 0;
1490
+ m[14] = 0;
1491
+ m[15] = 1;
1492
+ return this;
1493
+ }
1494
+ getEulerAngles(eulers = new Vec3()) {
1495
+ this.getScale(scale);
1496
+ const sx = scale.x;
1497
+ const sy = scale.y;
1498
+ const sz = scale.z;
1499
+ if (sx === 0 || sy === 0 || sz === 0) {
1500
+ return eulers.set(0, 0, 0);
1501
+ }
1502
+ const m = this.data;
1503
+ const y = Math.asin(-m[2] / sx);
1504
+ const halfPi = Math.PI * 0.5;
1505
+ let x, z;
1506
+ if (y < halfPi) {
1507
+ if (y > -halfPi) {
1508
+ x = Math.atan2(m[6] / sy, m[10] / sz);
1509
+ z = Math.atan2(m[1] / sx, m[0] / sx);
1510
+ } else {
1511
+ z = 0;
1512
+ x = -Math.atan2(m[4] / sy, m[5] / sy);
1513
+ }
1514
+ } else {
1515
+ z = 0;
1516
+ x = Math.atan2(m[4] / sy, m[5] / sy);
1517
+ }
1518
+ return eulers.set(x, y, z).mulScalar(math.RAD_TO_DEG);
1519
+ }
1520
+ toString() {
1521
+ return `[${this.data.join(', ')}]`;
1522
+ }
1523
+ static{
1524
+ this.IDENTITY = Object.freeze(new Mat4());
1525
+ }
1526
+ static{
1527
+ this.ZERO = Object.freeze(new Mat4().set([
1528
+ 0,
1529
+ 0,
1530
+ 0,
1531
+ 0,
1532
+ 0,
1533
+ 0,
1534
+ 0,
1535
+ 0,
1536
+ 0,
1537
+ 0,
1538
+ 0,
1539
+ 0,
1540
+ 0,
1541
+ 0,
1542
+ 0,
1543
+ 0
1544
+ ]));
1545
+ }
1546
+ }
1547
+
1548
+ class Quat {
1549
+ constructor(x = 0, y = 0, z = 0, w = 1){
1550
+ if (x.length === 4) {
1551
+ this.x = x[0];
1552
+ this.y = x[1];
1553
+ this.z = x[2];
1554
+ this.w = x[3];
1555
+ } else {
1556
+ this.x = x;
1557
+ this.y = y;
1558
+ this.z = z;
1559
+ this.w = w;
1560
+ }
1561
+ }
1562
+ clone() {
1563
+ const cstr = this.constructor;
1564
+ return new cstr(this.x, this.y, this.z, this.w);
1565
+ }
1566
+ conjugate(src = this) {
1567
+ this.x = src.x * -1;
1568
+ this.y = src.y * -1;
1569
+ this.z = src.z * -1;
1570
+ this.w = src.w;
1571
+ return this;
1572
+ }
1573
+ copy(rhs) {
1574
+ this.x = rhs.x;
1575
+ this.y = rhs.y;
1576
+ this.z = rhs.z;
1577
+ this.w = rhs.w;
1578
+ return this;
1579
+ }
1580
+ dot(other) {
1581
+ return this.x * other.x + this.y * other.y + this.z * other.z + this.w * other.w;
1582
+ }
1583
+ equals(rhs) {
1584
+ return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z && this.w === rhs.w;
1585
+ }
1586
+ equalsApprox(rhs, epsilon = 1e-6) {
1587
+ return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon && Math.abs(this.w - rhs.w) < epsilon;
1588
+ }
1589
+ getAxisAngle(axis) {
1590
+ let rad = Math.acos(this.w) * 2;
1591
+ const s = Math.sin(rad / 2);
1592
+ if (s !== 0) {
1593
+ axis.x = this.x / s;
1594
+ axis.y = this.y / s;
1595
+ axis.z = this.z / s;
1596
+ if (axis.x < 0 || axis.y < 0 || axis.z < 0) {
1597
+ axis.x *= -1;
1598
+ axis.y *= -1;
1599
+ axis.z *= -1;
1600
+ rad *= -1;
1601
+ }
1602
+ } else {
1603
+ axis.x = 1;
1604
+ axis.y = 0;
1605
+ axis.z = 0;
1606
+ }
1607
+ return rad * math.RAD_TO_DEG;
1608
+ }
1609
+ getEulerAngles(eulers = new Vec3()) {
1610
+ let x, y, z;
1611
+ const qx = this.x;
1612
+ const qy = this.y;
1613
+ const qz = this.z;
1614
+ const qw = this.w;
1615
+ const a2 = 2 * (qw * qy - qx * qz);
1616
+ if (a2 <= -0.99999) {
1617
+ x = 2 * Math.atan2(qx, qw);
1618
+ y = -Math.PI / 2;
1619
+ z = 0;
1620
+ } else if (a2 >= 0.99999) {
1621
+ x = 2 * Math.atan2(qx, qw);
1622
+ y = Math.PI / 2;
1623
+ z = 0;
1624
+ } else {
1625
+ x = Math.atan2(2 * (qw * qx + qy * qz), 1 - 2 * (qx * qx + qy * qy));
1626
+ y = Math.asin(a2);
1627
+ z = Math.atan2(2 * (qw * qz + qx * qy), 1 - 2 * (qy * qy + qz * qz));
1628
+ }
1629
+ return eulers.set(x, y, z).mulScalar(math.RAD_TO_DEG);
1630
+ }
1631
+ invert(src = this) {
1632
+ return this.conjugate(src).normalize();
1633
+ }
1634
+ length() {
1635
+ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
1636
+ }
1637
+ lengthSq() {
1638
+ return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
1639
+ }
1640
+ lerp(lhs, rhs, alpha) {
1641
+ const omt = (1 - alpha) * (lhs.dot(rhs) < 0 ? -1 : 1);
1642
+ this.x = lhs.x * omt + rhs.x * alpha;
1643
+ this.y = lhs.y * omt + rhs.y * alpha;
1644
+ this.z = lhs.z * omt + rhs.z * alpha;
1645
+ this.w = lhs.w * omt + rhs.w * alpha;
1646
+ return this.normalize();
1647
+ }
1648
+ mul(rhs) {
1649
+ const q1x = this.x;
1650
+ const q1y = this.y;
1651
+ const q1z = this.z;
1652
+ const q1w = this.w;
1653
+ const q2x = rhs.x;
1654
+ const q2y = rhs.y;
1655
+ const q2z = rhs.z;
1656
+ const q2w = rhs.w;
1657
+ this.x = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y;
1658
+ this.y = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z;
1659
+ this.z = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x;
1660
+ this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z;
1661
+ return this;
1662
+ }
1663
+ mulScalar(scalar, src = this) {
1664
+ this.x = src.x * scalar;
1665
+ this.y = src.y * scalar;
1666
+ this.z = src.z * scalar;
1667
+ this.w = src.w * scalar;
1668
+ return this;
1669
+ }
1670
+ mul2(lhs, rhs) {
1671
+ const q1x = lhs.x;
1672
+ const q1y = lhs.y;
1673
+ const q1z = lhs.z;
1674
+ const q1w = lhs.w;
1675
+ const q2x = rhs.x;
1676
+ const q2y = rhs.y;
1677
+ const q2z = rhs.z;
1678
+ const q2w = rhs.w;
1679
+ this.x = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y;
1680
+ this.y = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z;
1681
+ this.z = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x;
1682
+ this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z;
1683
+ return this;
1684
+ }
1685
+ normalize(src = this) {
1686
+ let len = src.length();
1687
+ if (len === 0) {
1688
+ this.x = this.y = this.z = 0;
1689
+ this.w = 1;
1690
+ } else {
1691
+ len = 1 / len;
1692
+ this.x = src.x * len;
1693
+ this.y = src.y * len;
1694
+ this.z = src.z * len;
1695
+ this.w = src.w * len;
1696
+ }
1697
+ return this;
1698
+ }
1699
+ set(x, y, z, w) {
1700
+ this.x = x;
1701
+ this.y = y;
1702
+ this.z = z;
1703
+ this.w = w;
1704
+ return this;
1705
+ }
1706
+ setFromAxisAngle(axis, angle) {
1707
+ angle *= 0.5 * math.DEG_TO_RAD;
1708
+ const sa = Math.sin(angle);
1709
+ const ca = Math.cos(angle);
1710
+ this.x = sa * axis.x;
1711
+ this.y = sa * axis.y;
1712
+ this.z = sa * axis.z;
1713
+ this.w = ca;
1714
+ return this;
1715
+ }
1716
+ setFromEulerAngles(ex, ey, ez) {
1717
+ if (ex instanceof Vec3) {
1718
+ const vec = ex;
1719
+ ex = vec.x;
1720
+ ey = vec.y;
1721
+ ez = vec.z;
1722
+ }
1723
+ const halfToRad = 0.5 * math.DEG_TO_RAD;
1724
+ ex *= halfToRad;
1725
+ ey *= halfToRad;
1726
+ ez *= halfToRad;
1727
+ const sx = Math.sin(ex);
1728
+ const cx = Math.cos(ex);
1729
+ const sy = Math.sin(ey);
1730
+ const cy = Math.cos(ey);
1731
+ const sz = Math.sin(ez);
1732
+ const cz = Math.cos(ez);
1733
+ this.x = sx * cy * cz - cx * sy * sz;
1734
+ this.y = cx * sy * cz + sx * cy * sz;
1735
+ this.z = cx * cy * sz - sx * sy * cz;
1736
+ this.w = cx * cy * cz + sx * sy * sz;
1737
+ return this;
1738
+ }
1739
+ setFromMat4(m) {
1740
+ const d = m.data;
1741
+ let m00 = d[0];
1742
+ let m01 = d[1];
1743
+ let m02 = d[2];
1744
+ let m10 = d[4];
1745
+ let m11 = d[5];
1746
+ let m12 = d[6];
1747
+ let m20 = d[8];
1748
+ let m21 = d[9];
1749
+ let m22 = d[10];
1750
+ let l;
1751
+ l = m00 * m00 + m01 * m01 + m02 * m02;
1752
+ if (l === 0) return this.set(0, 0, 0, 1);
1753
+ l = 1 / Math.sqrt(l);
1754
+ m00 *= l;
1755
+ m01 *= l;
1756
+ m02 *= l;
1757
+ l = m10 * m10 + m11 * m11 + m12 * m12;
1758
+ if (l === 0) return this.set(0, 0, 0, 1);
1759
+ l = 1 / Math.sqrt(l);
1760
+ m10 *= l;
1761
+ m11 *= l;
1762
+ m12 *= l;
1763
+ l = m20 * m20 + m21 * m21 + m22 * m22;
1764
+ if (l === 0) return this.set(0, 0, 0, 1);
1765
+ l = 1 / Math.sqrt(l);
1766
+ m20 *= l;
1767
+ m21 *= l;
1768
+ m22 *= l;
1769
+ if (m22 < 0) {
1770
+ if (m00 > m11) {
1771
+ this.set(1 + m00 - m11 - m22, m01 + m10, m20 + m02, m12 - m21);
1772
+ } else {
1773
+ this.set(m01 + m10, 1 - m00 + m11 - m22, m12 + m21, m20 - m02);
1774
+ }
1775
+ } else {
1776
+ if (m00 < -m11) {
1777
+ this.set(m20 + m02, m12 + m21, 1 - m00 - m11 + m22, m01 - m10);
1778
+ } else {
1779
+ this.set(m12 - m21, m20 - m02, m01 - m10, 1 + m00 + m11 + m22);
1780
+ }
1781
+ }
1782
+ return this.mulScalar(1.0 / this.length());
1783
+ }
1784
+ setFromDirections(from, to) {
1785
+ const dotProduct = 1 + from.dot(to);
1786
+ if (dotProduct < Number.EPSILON) {
1787
+ if (Math.abs(from.x) > Math.abs(from.y)) {
1788
+ this.x = -from.z;
1789
+ this.y = 0;
1790
+ this.z = from.x;
1791
+ this.w = 0;
1792
+ } else {
1793
+ this.x = 0;
1794
+ this.y = -from.z;
1795
+ this.z = from.y;
1796
+ this.w = 0;
1797
+ }
1798
+ } else {
1799
+ this.x = from.y * to.z - from.z * to.y;
1800
+ this.y = from.z * to.x - from.x * to.z;
1801
+ this.z = from.x * to.y - from.y * to.x;
1802
+ this.w = dotProduct;
1803
+ }
1804
+ return this.normalize();
1805
+ }
1806
+ slerp(lhs, rhs, alpha) {
1807
+ const lx = lhs.x;
1808
+ const ly = lhs.y;
1809
+ const lz = lhs.z;
1810
+ const lw = lhs.w;
1811
+ let rx = rhs.x;
1812
+ let ry = rhs.y;
1813
+ let rz = rhs.z;
1814
+ let rw = rhs.w;
1815
+ let cosHalfTheta = lw * rw + lx * rx + ly * ry + lz * rz;
1816
+ if (cosHalfTheta < 0) {
1817
+ rw = -rw;
1818
+ rx = -rx;
1819
+ ry = -ry;
1820
+ rz = -rz;
1821
+ cosHalfTheta = -cosHalfTheta;
1822
+ }
1823
+ if (Math.abs(cosHalfTheta) >= 1) {
1824
+ this.w = lw;
1825
+ this.x = lx;
1826
+ this.y = ly;
1827
+ this.z = lz;
1828
+ return this;
1829
+ }
1830
+ const halfTheta = Math.acos(cosHalfTheta);
1831
+ const sinHalfTheta = Math.sqrt(1 - cosHalfTheta * cosHalfTheta);
1832
+ if (Math.abs(sinHalfTheta) < 0.001) {
1833
+ this.w = lw * 0.5 + rw * 0.5;
1834
+ this.x = lx * 0.5 + rx * 0.5;
1835
+ this.y = ly * 0.5 + ry * 0.5;
1836
+ this.z = lz * 0.5 + rz * 0.5;
1837
+ return this;
1838
+ }
1839
+ const ratioA = Math.sin((1 - alpha) * halfTheta) / sinHalfTheta;
1840
+ const ratioB = Math.sin(alpha * halfTheta) / sinHalfTheta;
1841
+ this.w = lw * ratioA + rw * ratioB;
1842
+ this.x = lx * ratioA + rx * ratioB;
1843
+ this.y = ly * ratioA + ry * ratioB;
1844
+ this.z = lz * ratioA + rz * ratioB;
1845
+ return this;
1846
+ }
1847
+ transformVector(vec, res = new Vec3()) {
1848
+ const x = vec.x, y = vec.y, z = vec.z;
1849
+ const qx = this.x, qy = this.y, qz = this.z, qw = this.w;
1850
+ const ix = qw * x + qy * z - qz * y;
1851
+ const iy = qw * y + qz * x - qx * z;
1852
+ const iz = qw * z + qx * y - qy * x;
1853
+ const iw = -qx * x - qy * y - qz * z;
1854
+ res.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
1855
+ res.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
1856
+ res.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
1857
+ return res;
1858
+ }
1859
+ fromArray(arr, offset = 0) {
1860
+ this.x = arr[offset] ?? this.x;
1861
+ this.y = arr[offset + 1] ?? this.y;
1862
+ this.z = arr[offset + 2] ?? this.z;
1863
+ this.w = arr[offset + 3] ?? this.w;
1864
+ return this;
1865
+ }
1866
+ toString() {
1867
+ return `[${this.x}, ${this.y}, ${this.z}, ${this.w}]`;
1868
+ }
1869
+ toArray(arr = [], offset = 0) {
1870
+ arr[offset] = this.x;
1871
+ arr[offset + 1] = this.y;
1872
+ arr[offset + 2] = this.z;
1873
+ arr[offset + 3] = this.w;
1874
+ return arr;
1875
+ }
1876
+ static{
1877
+ this.IDENTITY = Object.freeze(new Quat(0, 0, 0, 1));
1878
+ }
1879
+ static{
1880
+ this.ZERO = Object.freeze(new Quat(0, 0, 0, 0));
1881
+ }
1882
+ }
1883
+
1884
+ var version = "0.2.0";
1885
+
1886
+ class Column {
1887
+ name;
1888
+ data;
1889
+ constructor(name, data) {
1890
+ this.name = name;
1891
+ this.data = data;
1892
+ }
1893
+ get dataType() {
1894
+ switch (this.data.constructor) {
1895
+ case Int8Array: return 'int8';
1896
+ case Uint8Array: return 'uint8';
1897
+ case Int16Array: return 'int16';
1898
+ case Uint16Array: return 'uint16';
1899
+ case Int32Array: return 'int32';
1900
+ case Uint32Array: return 'uint32';
1901
+ case Float32Array: return 'float32';
1902
+ case Float64Array: return 'float64';
1903
+ }
1904
+ return null;
1905
+ }
1906
+ clone() {
1907
+ return new Column(this.name, this.data.slice());
1908
+ }
1909
+ filter(length, filter) {
1910
+ const constructor = this.data.constructor;
1911
+ const data = new constructor(length);
1912
+ let j = 0;
1913
+ for (let i = 0; i < this.data.length; i++) {
1914
+ if (filter[i]) {
1915
+ data[j++] = this.data[i];
1916
+ }
1917
+ }
1918
+ return new Column(this.name, data);
1919
+ }
1920
+ }
1921
+ class DataTable {
1922
+ columns;
1923
+ constructor(columns) {
1924
+ if (columns.length === 0) {
1925
+ throw new Error('DataTable must have at least one column');
1926
+ }
1927
+ // check all columns have the same lengths
1928
+ for (let i = 1; i < columns.length; i++) {
1929
+ if (columns[i].data.length !== columns[0].data.length) {
1930
+ throw new Error(`Column '${columns[i].name}' has inconsistent number of rows: expected ${columns[0].data.length}, got ${columns[i].data.length}`);
1931
+ }
1932
+ }
1933
+ this.columns = columns;
1934
+ }
1935
+ // rows
1936
+ get numRows() {
1937
+ return this.columns[0].data.length;
1938
+ }
1939
+ getRow(index, row = {}, columns = this.columns) {
1940
+ for (const column of columns) {
1941
+ row[column.name] = column.data[index];
1942
+ }
1943
+ return row;
1944
+ }
1945
+ setRow(index, row, columns = this.columns) {
1946
+ for (const column of columns) {
1947
+ if (row.hasOwnProperty(column.name)) {
1948
+ column.data[index] = row[column.name];
1949
+ }
1950
+ }
1951
+ }
1952
+ // columns
1953
+ get numColumns() {
1954
+ return this.columns.length;
1955
+ }
1956
+ get columnNames() {
1957
+ return this.columns.map(column => column.name);
1958
+ }
1959
+ get columnData() {
1960
+ return this.columns.map(column => column.data);
1961
+ }
1962
+ get columnTypes() {
1963
+ return this.columns.map(column => column.dataType);
1964
+ }
1965
+ getColumn(index) {
1966
+ return this.columns[index];
1967
+ }
1968
+ getColumnIndex(name) {
1969
+ return this.columns.findIndex(column => column.name === name);
1970
+ }
1971
+ getColumnByName(name) {
1972
+ return this.columns.find(column => column.name === name);
1973
+ }
1974
+ hasColumn(name) {
1975
+ return this.columns.some(column => column.name === name);
1976
+ }
1977
+ addColumn(column) {
1978
+ if (column.data.length !== this.numRows) {
1979
+ throw new Error(`Column '${column.name}' has inconsistent number of rows: expected ${this.numRows}, got ${column.data.length}`);
1980
+ }
1981
+ this.columns.push(column);
1982
+ }
1983
+ removeColumn(name) {
1984
+ const index = this.columns.findIndex(column => column.name === name);
1985
+ if (index === -1) {
1986
+ return false;
1987
+ }
1988
+ this.columns.splice(index, 1);
1989
+ return true;
1990
+ }
1991
+ // general
1992
+ clone() {
1993
+ return new DataTable(this.columns.map(c => c.clone()));
1994
+ }
1995
+ filter(predicate) {
1996
+ const flags = new Uint8Array(this.numRows);
1997
+ const row = {};
1998
+ let numRows = 0;
1999
+ for (let i = 0; i < this.numRows; i++) {
2000
+ this.getRow(i, row);
2001
+ flags[i] = predicate(i, row) ? 1 : 0;
2002
+ numRows += flags[i];
2003
+ }
2004
+ if (numRows === 0) {
2005
+ return null;
2006
+ }
2007
+ if (numRows === this.numRows) {
2008
+ return this;
2009
+ }
2010
+ return new DataTable(this.columns.map(c => c.filter(numRows, flags)));
2011
+ }
2012
+ }
2013
+
2014
+ /* eslint-disable indent */
2015
+ const kSqrt03_02 = Math.sqrt(3.0 / 2.0);
2016
+ const kSqrt01_03 = Math.sqrt(1.0 / 3.0);
2017
+ const kSqrt02_03 = Math.sqrt(2.0 / 3.0);
2018
+ const kSqrt04_03 = Math.sqrt(4.0 / 3.0);
2019
+ const kSqrt01_04 = Math.sqrt(1.0 / 4.0);
2020
+ const kSqrt03_04 = Math.sqrt(3.0 / 4.0);
2021
+ const kSqrt01_05 = Math.sqrt(1.0 / 5.0);
2022
+ const kSqrt03_05 = Math.sqrt(3.0 / 5.0);
2023
+ const kSqrt06_05 = Math.sqrt(6.0 / 5.0);
2024
+ const kSqrt08_05 = Math.sqrt(8.0 / 5.0);
2025
+ const kSqrt09_05 = Math.sqrt(9.0 / 5.0);
2026
+ const kSqrt01_06 = Math.sqrt(1.0 / 6.0);
2027
+ const kSqrt05_06 = Math.sqrt(5.0 / 6.0);
2028
+ const kSqrt03_08 = Math.sqrt(3.0 / 8.0);
2029
+ const kSqrt05_08 = Math.sqrt(5.0 / 8.0);
2030
+ const kSqrt09_08 = Math.sqrt(9.0 / 8.0);
2031
+ const kSqrt05_09 = Math.sqrt(5.0 / 9.0);
2032
+ const kSqrt08_09 = Math.sqrt(8.0 / 9.0);
2033
+ const kSqrt01_10 = Math.sqrt(1.0 / 10.0);
2034
+ const kSqrt03_10 = Math.sqrt(3.0 / 10.0);
2035
+ const kSqrt01_12 = Math.sqrt(1.0 / 12.0);
2036
+ const kSqrt04_15 = Math.sqrt(4.0 / 15.0);
2037
+ const kSqrt01_16 = Math.sqrt(1.0 / 16.0);
2038
+ const kSqrt15_16 = Math.sqrt(15.0 / 16.0);
2039
+ const kSqrt01_18 = Math.sqrt(1.0 / 18.0);
2040
+ const kSqrt01_60 = Math.sqrt(1.0 / 60.0);
2041
+ const dp = (n, start, a, b) => {
2042
+ let sum = 0;
2043
+ for (let i = 0; i < n; i++) {
2044
+ sum += a[start + i] * b[i];
2045
+ }
2046
+ return sum;
2047
+ };
2048
+ const coeffsIn = new Float32Array(15);
2049
+ // Rotate spherical harmonics up to band 3 based on https://github.com/andrewwillmott/sh-lib
2050
+ //
2051
+ // This implementation calculates the rotation factors during construction which can then
2052
+ // be used to rotate multiple spherical harmonics cheaply.
2053
+ class RotateSH {
2054
+ apply;
2055
+ constructor(mat) {
2056
+ const rot = mat.data;
2057
+ // band 1
2058
+ const sh1 = [
2059
+ [rot[4], -rot[7], rot[1]],
2060
+ [-rot[5], rot[8], -rot[2]],
2061
+ [rot[3], -rot[6], rot[0]]
2062
+ ];
2063
+ // band 2
2064
+ const sh2 = [[
2065
+ kSqrt01_04 * ((sh1[2][2] * sh1[0][0] + sh1[2][0] * sh1[0][2]) + (sh1[0][2] * sh1[2][0] + sh1[0][0] * sh1[2][2])),
2066
+ (sh1[2][1] * sh1[0][0] + sh1[0][1] * sh1[2][0]),
2067
+ kSqrt03_04 * (sh1[2][1] * sh1[0][1] + sh1[0][1] * sh1[2][1]),
2068
+ (sh1[2][1] * sh1[0][2] + sh1[0][1] * sh1[2][2]),
2069
+ kSqrt01_04 * ((sh1[2][2] * sh1[0][2] - sh1[2][0] * sh1[0][0]) + (sh1[0][2] * sh1[2][2] - sh1[0][0] * sh1[2][0]))
2070
+ ], [
2071
+ kSqrt01_04 * ((sh1[1][2] * sh1[0][0] + sh1[1][0] * sh1[0][2]) + (sh1[0][2] * sh1[1][0] + sh1[0][0] * sh1[1][2])),
2072
+ sh1[1][1] * sh1[0][0] + sh1[0][1] * sh1[1][0],
2073
+ kSqrt03_04 * (sh1[1][1] * sh1[0][1] + sh1[0][1] * sh1[1][1]),
2074
+ sh1[1][1] * sh1[0][2] + sh1[0][1] * sh1[1][2],
2075
+ kSqrt01_04 * ((sh1[1][2] * sh1[0][2] - sh1[1][0] * sh1[0][0]) + (sh1[0][2] * sh1[1][2] - sh1[0][0] * sh1[1][0]))
2076
+ ], [
2077
+ kSqrt01_03 * (sh1[1][2] * sh1[1][0] + sh1[1][0] * sh1[1][2]) - kSqrt01_12 * ((sh1[2][2] * sh1[2][0] + sh1[2][0] * sh1[2][2]) + (sh1[0][2] * sh1[0][0] + sh1[0][0] * sh1[0][2])),
2078
+ kSqrt04_03 * sh1[1][1] * sh1[1][0] - kSqrt01_03 * (sh1[2][1] * sh1[2][0] + sh1[0][1] * sh1[0][0]),
2079
+ sh1[1][1] * sh1[1][1] - kSqrt01_04 * (sh1[2][1] * sh1[2][1] + sh1[0][1] * sh1[0][1]),
2080
+ kSqrt04_03 * sh1[1][1] * sh1[1][2] - kSqrt01_03 * (sh1[2][1] * sh1[2][2] + sh1[0][1] * sh1[0][2]),
2081
+ kSqrt01_03 * (sh1[1][2] * sh1[1][2] - sh1[1][0] * sh1[1][0]) - kSqrt01_12 * ((sh1[2][2] * sh1[2][2] - sh1[2][0] * sh1[2][0]) + (sh1[0][2] * sh1[0][2] - sh1[0][0] * sh1[0][0]))
2082
+ ], [
2083
+ kSqrt01_04 * ((sh1[1][2] * sh1[2][0] + sh1[1][0] * sh1[2][2]) + (sh1[2][2] * sh1[1][0] + sh1[2][0] * sh1[1][2])),
2084
+ sh1[1][1] * sh1[2][0] + sh1[2][1] * sh1[1][0],
2085
+ kSqrt03_04 * (sh1[1][1] * sh1[2][1] + sh1[2][1] * sh1[1][1]),
2086
+ sh1[1][1] * sh1[2][2] + sh1[2][1] * sh1[1][2],
2087
+ kSqrt01_04 * ((sh1[1][2] * sh1[2][2] - sh1[1][0] * sh1[2][0]) + (sh1[2][2] * sh1[1][2] - sh1[2][0] * sh1[1][0]))
2088
+ ], [
2089
+ kSqrt01_04 * ((sh1[2][2] * sh1[2][0] + sh1[2][0] * sh1[2][2]) - (sh1[0][2] * sh1[0][0] + sh1[0][0] * sh1[0][2])),
2090
+ (sh1[2][1] * sh1[2][0] - sh1[0][1] * sh1[0][0]),
2091
+ kSqrt03_04 * (sh1[2][1] * sh1[2][1] - sh1[0][1] * sh1[0][1]),
2092
+ (sh1[2][1] * sh1[2][2] - sh1[0][1] * sh1[0][2]),
2093
+ kSqrt01_04 * ((sh1[2][2] * sh1[2][2] - sh1[2][0] * sh1[2][0]) - (sh1[0][2] * sh1[0][2] - sh1[0][0] * sh1[0][0]))
2094
+ ]];
2095
+ // band 3
2096
+ const sh3 = [[
2097
+ kSqrt01_04 * ((sh1[2][2] * sh2[0][0] + sh1[2][0] * sh2[0][4]) + (sh1[0][2] * sh2[4][0] + sh1[0][0] * sh2[4][4])),
2098
+ kSqrt03_02 * (sh1[2][1] * sh2[0][0] + sh1[0][1] * sh2[4][0]),
2099
+ kSqrt15_16 * (sh1[2][1] * sh2[0][1] + sh1[0][1] * sh2[4][1]),
2100
+ kSqrt05_06 * (sh1[2][1] * sh2[0][2] + sh1[0][1] * sh2[4][2]),
2101
+ kSqrt15_16 * (sh1[2][1] * sh2[0][3] + sh1[0][1] * sh2[4][3]),
2102
+ kSqrt03_02 * (sh1[2][1] * sh2[0][4] + sh1[0][1] * sh2[4][4]),
2103
+ kSqrt01_04 * ((sh1[2][2] * sh2[0][4] - sh1[2][0] * sh2[0][0]) + (sh1[0][2] * sh2[4][4] - sh1[0][0] * sh2[4][0]))
2104
+ ], [
2105
+ kSqrt01_06 * (sh1[1][2] * sh2[0][0] + sh1[1][0] * sh2[0][4]) + kSqrt01_06 * ((sh1[2][2] * sh2[1][0] + sh1[2][0] * sh2[1][4]) + (sh1[0][2] * sh2[3][0] + sh1[0][0] * sh2[3][4])),
2106
+ sh1[1][1] * sh2[0][0] + (sh1[2][1] * sh2[1][0] + sh1[0][1] * sh2[3][0]),
2107
+ kSqrt05_08 * sh1[1][1] * sh2[0][1] + kSqrt05_08 * (sh1[2][1] * sh2[1][1] + sh1[0][1] * sh2[3][1]),
2108
+ kSqrt05_09 * sh1[1][1] * sh2[0][2] + kSqrt05_09 * (sh1[2][1] * sh2[1][2] + sh1[0][1] * sh2[3][2]),
2109
+ kSqrt05_08 * sh1[1][1] * sh2[0][3] + kSqrt05_08 * (sh1[2][1] * sh2[1][3] + sh1[0][1] * sh2[3][3]),
2110
+ sh1[1][1] * sh2[0][4] + (sh1[2][1] * sh2[1][4] + sh1[0][1] * sh2[3][4]),
2111
+ kSqrt01_06 * (sh1[1][2] * sh2[0][4] - sh1[1][0] * sh2[0][0]) + kSqrt01_06 * ((sh1[2][2] * sh2[1][4] - sh1[2][0] * sh2[1][0]) + (sh1[0][2] * sh2[3][4] - sh1[0][0] * sh2[3][0]))
2112
+ ], [
2113
+ kSqrt04_15 * (sh1[1][2] * sh2[1][0] + sh1[1][0] * sh2[1][4]) + kSqrt01_05 * (sh1[0][2] * sh2[2][0] + sh1[0][0] * sh2[2][4]) - kSqrt01_60 * ((sh1[2][2] * sh2[0][0] + sh1[2][0] * sh2[0][4]) - (sh1[0][2] * sh2[4][0] + sh1[0][0] * sh2[4][4])),
2114
+ kSqrt08_05 * sh1[1][1] * sh2[1][0] + kSqrt06_05 * sh1[0][1] * sh2[2][0] - kSqrt01_10 * (sh1[2][1] * sh2[0][0] - sh1[0][1] * sh2[4][0]),
2115
+ sh1[1][1] * sh2[1][1] + kSqrt03_04 * sh1[0][1] * sh2[2][1] - kSqrt01_16 * (sh1[2][1] * sh2[0][1] - sh1[0][1] * sh2[4][1]),
2116
+ kSqrt08_09 * sh1[1][1] * sh2[1][2] + kSqrt02_03 * sh1[0][1] * sh2[2][2] - kSqrt01_18 * (sh1[2][1] * sh2[0][2] - sh1[0][1] * sh2[4][2]),
2117
+ sh1[1][1] * sh2[1][3] + kSqrt03_04 * sh1[0][1] * sh2[2][3] - kSqrt01_16 * (sh1[2][1] * sh2[0][3] - sh1[0][1] * sh2[4][3]),
2118
+ kSqrt08_05 * sh1[1][1] * sh2[1][4] + kSqrt06_05 * sh1[0][1] * sh2[2][4] - kSqrt01_10 * (sh1[2][1] * sh2[0][4] - sh1[0][1] * sh2[4][4]),
2119
+ kSqrt04_15 * (sh1[1][2] * sh2[1][4] - sh1[1][0] * sh2[1][0]) + kSqrt01_05 * (sh1[0][2] * sh2[2][4] - sh1[0][0] * sh2[2][0]) - kSqrt01_60 * ((sh1[2][2] * sh2[0][4] - sh1[2][0] * sh2[0][0]) - (sh1[0][2] * sh2[4][4] - sh1[0][0] * sh2[4][0]))
2120
+ ], [
2121
+ kSqrt03_10 * (sh1[1][2] * sh2[2][0] + sh1[1][0] * sh2[2][4]) - kSqrt01_10 * ((sh1[2][2] * sh2[3][0] + sh1[2][0] * sh2[3][4]) + (sh1[0][2] * sh2[1][0] + sh1[0][0] * sh2[1][4])),
2122
+ kSqrt09_05 * sh1[1][1] * sh2[2][0] - kSqrt03_05 * (sh1[2][1] * sh2[3][0] + sh1[0][1] * sh2[1][0]),
2123
+ kSqrt09_08 * sh1[1][1] * sh2[2][1] - kSqrt03_08 * (sh1[2][1] * sh2[3][1] + sh1[0][1] * sh2[1][1]),
2124
+ sh1[1][1] * sh2[2][2] - kSqrt01_03 * (sh1[2][1] * sh2[3][2] + sh1[0][1] * sh2[1][2]),
2125
+ kSqrt09_08 * sh1[1][1] * sh2[2][3] - kSqrt03_08 * (sh1[2][1] * sh2[3][3] + sh1[0][1] * sh2[1][3]),
2126
+ kSqrt09_05 * sh1[1][1] * sh2[2][4] - kSqrt03_05 * (sh1[2][1] * sh2[3][4] + sh1[0][1] * sh2[1][4]),
2127
+ kSqrt03_10 * (sh1[1][2] * sh2[2][4] - sh1[1][0] * sh2[2][0]) - kSqrt01_10 * ((sh1[2][2] * sh2[3][4] - sh1[2][0] * sh2[3][0]) + (sh1[0][2] * sh2[1][4] - sh1[0][0] * sh2[1][0]))
2128
+ ], [
2129
+ kSqrt04_15 * (sh1[1][2] * sh2[3][0] + sh1[1][0] * sh2[3][4]) + kSqrt01_05 * (sh1[2][2] * sh2[2][0] + sh1[2][0] * sh2[2][4]) - kSqrt01_60 * ((sh1[2][2] * sh2[4][0] + sh1[2][0] * sh2[4][4]) + (sh1[0][2] * sh2[0][0] + sh1[0][0] * sh2[0][4])),
2130
+ kSqrt08_05 * sh1[1][1] * sh2[3][0] + kSqrt06_05 * sh1[2][1] * sh2[2][0] - kSqrt01_10 * (sh1[2][1] * sh2[4][0] + sh1[0][1] * sh2[0][0]),
2131
+ sh1[1][1] * sh2[3][1] + kSqrt03_04 * sh1[2][1] * sh2[2][1] - kSqrt01_16 * (sh1[2][1] * sh2[4][1] + sh1[0][1] * sh2[0][1]),
2132
+ kSqrt08_09 * sh1[1][1] * sh2[3][2] + kSqrt02_03 * sh1[2][1] * sh2[2][2] - kSqrt01_18 * (sh1[2][1] * sh2[4][2] + sh1[0][1] * sh2[0][2]),
2133
+ sh1[1][1] * sh2[3][3] + kSqrt03_04 * sh1[2][1] * sh2[2][3] - kSqrt01_16 * (sh1[2][1] * sh2[4][3] + sh1[0][1] * sh2[0][3]),
2134
+ kSqrt08_05 * sh1[1][1] * sh2[3][4] + kSqrt06_05 * sh1[2][1] * sh2[2][4] - kSqrt01_10 * (sh1[2][1] * sh2[4][4] + sh1[0][1] * sh2[0][4]),
2135
+ kSqrt04_15 * (sh1[1][2] * sh2[3][4] - sh1[1][0] * sh2[3][0]) + kSqrt01_05 * (sh1[2][2] * sh2[2][4] - sh1[2][0] * sh2[2][0]) - kSqrt01_60 * ((sh1[2][2] * sh2[4][4] - sh1[2][0] * sh2[4][0]) + (sh1[0][2] * sh2[0][4] - sh1[0][0] * sh2[0][0]))
2136
+ ], [
2137
+ kSqrt01_06 * (sh1[1][2] * sh2[4][0] + sh1[1][0] * sh2[4][4]) + kSqrt01_06 * ((sh1[2][2] * sh2[3][0] + sh1[2][0] * sh2[3][4]) - (sh1[0][2] * sh2[1][0] + sh1[0][0] * sh2[1][4])),
2138
+ sh1[1][1] * sh2[4][0] + (sh1[2][1] * sh2[3][0] - sh1[0][1] * sh2[1][0]),
2139
+ kSqrt05_08 * sh1[1][1] * sh2[4][1] + kSqrt05_08 * (sh1[2][1] * sh2[3][1] - sh1[0][1] * sh2[1][1]),
2140
+ kSqrt05_09 * sh1[1][1] * sh2[4][2] + kSqrt05_09 * (sh1[2][1] * sh2[3][2] - sh1[0][1] * sh2[1][2]),
2141
+ kSqrt05_08 * sh1[1][1] * sh2[4][3] + kSqrt05_08 * (sh1[2][1] * sh2[3][3] - sh1[0][1] * sh2[1][3]),
2142
+ sh1[1][1] * sh2[4][4] + (sh1[2][1] * sh2[3][4] - sh1[0][1] * sh2[1][4]),
2143
+ kSqrt01_06 * (sh1[1][2] * sh2[4][4] - sh1[1][0] * sh2[4][0]) + kSqrt01_06 * ((sh1[2][2] * sh2[3][4] - sh1[2][0] * sh2[3][0]) - (sh1[0][2] * sh2[1][4] - sh1[0][0] * sh2[1][0]))
2144
+ ], [
2145
+ kSqrt01_04 * ((sh1[2][2] * sh2[4][0] + sh1[2][0] * sh2[4][4]) - (sh1[0][2] * sh2[0][0] + sh1[0][0] * sh2[0][4])),
2146
+ kSqrt03_02 * (sh1[2][1] * sh2[4][0] - sh1[0][1] * sh2[0][0]),
2147
+ kSqrt15_16 * (sh1[2][1] * sh2[4][1] - sh1[0][1] * sh2[0][1]),
2148
+ kSqrt05_06 * (sh1[2][1] * sh2[4][2] - sh1[0][1] * sh2[0][2]),
2149
+ kSqrt15_16 * (sh1[2][1] * sh2[4][3] - sh1[0][1] * sh2[0][3]),
2150
+ kSqrt03_02 * (sh1[2][1] * sh2[4][4] - sh1[0][1] * sh2[0][4]),
2151
+ kSqrt01_04 * ((sh1[2][2] * sh2[4][4] - sh1[2][0] * sh2[4][0]) - (sh1[0][2] * sh2[0][4] - sh1[0][0] * sh2[0][0]))
2152
+ ]];
2153
+ // rotate spherical harmonic coefficients, up to band 3
2154
+ this.apply = (result, src) => {
2155
+ if (!src || src === result) {
2156
+ coeffsIn.set(result);
2157
+ src = coeffsIn;
2158
+ }
2159
+ // band 1
2160
+ if (result.length < 3) {
2161
+ return;
2162
+ }
2163
+ result[0] = dp(3, 0, src, sh1[0]);
2164
+ result[1] = dp(3, 0, src, sh1[1]);
2165
+ result[2] = dp(3, 0, src, sh1[2]);
2166
+ // band 2
2167
+ if (result.length < 8) {
2168
+ return;
2169
+ }
2170
+ result[3] = dp(5, 3, src, sh2[0]);
2171
+ result[4] = dp(5, 3, src, sh2[1]);
2172
+ result[5] = dp(5, 3, src, sh2[2]);
2173
+ result[6] = dp(5, 3, src, sh2[3]);
2174
+ result[7] = dp(5, 3, src, sh2[4]);
2175
+ // band 3
2176
+ if (result.length < 15) {
2177
+ return;
2178
+ }
2179
+ result[8] = dp(7, 8, src, sh3[0]);
2180
+ result[9] = dp(7, 8, src, sh3[1]);
2181
+ result[10] = dp(7, 8, src, sh3[2]);
2182
+ result[11] = dp(7, 8, src, sh3[3]);
2183
+ result[12] = dp(7, 8, src, sh3[4]);
2184
+ result[13] = dp(7, 8, src, sh3[5]);
2185
+ result[14] = dp(7, 8, src, sh3[6]);
2186
+ };
2187
+ }
2188
+ }
2189
+
2190
+ const shNames$2 = new Array(45).fill('').map((_, i) => `f_rest_${i}`);
2191
+ const v = new Vec3();
2192
+ const q$1 = new Quat();
2193
+ // apply translation, rotation and scale to a data table
2194
+ const transform = (dataTable, t, r, s) => {
2195
+ const mat = new Mat4().setTRS(t, r, new Vec3(s, s, s));
2196
+ const mat3 = new Mat3().setFromQuat(r);
2197
+ const rotateSH = new RotateSH(mat3);
2198
+ const hasTranslation = ['x', 'y', 'z'].every(c => dataTable.hasColumn(c));
2199
+ const hasRotation = ['rot_0', 'rot_1', 'rot_2', 'rot_3'].every(c => dataTable.hasColumn(c));
2200
+ const hasScale = ['scale_0', 'scale_1', 'scale_2'].every(c => dataTable.hasColumn(c));
2201
+ const shBands = { '9': 1, '24': 2, '-1': 3 }[shNames$2.findIndex(v => !dataTable.hasColumn(v))] ?? 0;
2202
+ const shCoeffs = new Float32Array([0, 3, 8, 15][shBands]);
2203
+ const row = {};
2204
+ for (let i = 0; i < dataTable.numRows; ++i) {
2205
+ dataTable.getRow(i, row);
2206
+ if (hasTranslation) {
2207
+ v.set(row.x, row.y, row.z);
2208
+ mat.transformPoint(v, v);
2209
+ row.x = v.x;
2210
+ row.y = v.y;
2211
+ row.z = v.z;
2212
+ }
2213
+ if (hasRotation) {
2214
+ q$1.set(row.rot_1, row.rot_2, row.rot_3, row.rot_0).mul2(r, q$1);
2215
+ row.rot_0 = q$1.w;
2216
+ row.rot_1 = q$1.x;
2217
+ row.rot_2 = q$1.y;
2218
+ row.rot_3 = q$1.z;
2219
+ }
2220
+ if (hasScale) {
2221
+ row.scale_0 = Math.log(Math.exp(row.scale_0) * s);
2222
+ row.scale_1 = Math.log(Math.exp(row.scale_1) * s);
2223
+ row.scale_2 = Math.log(Math.exp(row.scale_2) * s);
2224
+ }
2225
+ if (shBands > 0) {
2226
+ for (let j = 0; j < 3; ++j) {
2227
+ for (let k = 0; k < shCoeffs.length; ++k) {
2228
+ shCoeffs[k] = row[shNames$2[k + j * shCoeffs.length]];
2229
+ }
2230
+ rotateSH.apply(shCoeffs);
2231
+ for (let k = 0; k < shCoeffs.length; ++k) {
2232
+ row[shNames$2[k + j * shCoeffs.length]] = shCoeffs[k];
2233
+ }
2234
+ }
2235
+ }
2236
+ dataTable.setRow(i, row);
2237
+ }
2238
+ };
2239
+
2240
+ const shNames$1 = new Array(45).fill('').map((_, i) => `f_rest_${i}`);
2241
+ // process a data table with standard options
2242
+ const process = (dataTable, processActions) => {
2243
+ let result = dataTable;
2244
+ for (let i = 0; i < processActions.length; i++) {
2245
+ const processAction = processActions[i];
2246
+ switch (processAction.kind) {
2247
+ case 'translate':
2248
+ transform(result, processAction.value, Quat.IDENTITY, 1);
2249
+ break;
2250
+ case 'rotate':
2251
+ transform(result, Vec3.ZERO, new Quat().setFromEulerAngles(processAction.value.x, processAction.value.y, processAction.value.z), 1);
2252
+ break;
2253
+ case 'scale':
2254
+ transform(result, Vec3.ZERO, Quat.IDENTITY, processAction.value);
2255
+ break;
2256
+ case 'filterNaN': {
2257
+ const predicate = (rowIndex, row) => {
2258
+ for (const key in row) {
2259
+ if (!isFinite(row[key])) {
2260
+ return false;
2261
+ }
2262
+ }
2263
+ return true;
2264
+ };
2265
+ result = result.filter(predicate);
2266
+ break;
2267
+ }
2268
+ case 'filterByValue': {
2269
+ const { columnName, comparator, value } = processAction;
2270
+ const Predicates = {
2271
+ 'lt': (rowIndex, row) => row[columnName] < value,
2272
+ 'lte': (rowIndex, row) => row[columnName] <= value,
2273
+ 'gt': (rowIndex, row) => row[columnName] > value,
2274
+ 'gte': (rowIndex, row) => row[columnName] >= value,
2275
+ 'eq': (rowIndex, row) => row[columnName] === value,
2276
+ 'neq': (rowIndex, row) => row[columnName] !== value
2277
+ };
2278
+ const predicate = Predicates[comparator] ?? ((rowIndex, row) => true);
2279
+ result = result.filter(predicate);
2280
+ break;
2281
+ }
2282
+ case 'filterBands': {
2283
+ const inputBands = { '9': 1, '24': 2, '-1': 3 }[shNames$1.findIndex(v => !dataTable.hasColumn(v))] ?? 0;
2284
+ const outputBands = processAction.value;
2285
+ if (outputBands < inputBands) {
2286
+ const inputCoeffs = [0, 3, 8, 15][inputBands];
2287
+ const outputCoeffs = [0, 3, 8, 15][outputBands];
2288
+ const map = {};
2289
+ for (let i = 0; i < inputCoeffs; ++i) {
2290
+ for (let j = 0; j < 3; ++j) {
2291
+ const inputName = `f_rest_${i + j * inputCoeffs}`;
2292
+ map[inputName] = i < outputCoeffs ? `f_rest_${i + j * outputCoeffs}` : null;
2293
+ }
2294
+ }
2295
+ result = new DataTable(result.columns.map((column) => {
2296
+ if (map.hasOwnProperty(column.name)) {
2297
+ const name = map[column.name];
2298
+ return name ? new Column(name, column.data) : null;
2299
+ }
2300
+ return column;
2301
+ }).filter(c => c !== null));
2302
+ }
2303
+ break;
2304
+ }
2305
+ }
2306
+ }
2307
+ return result;
2308
+ };
5
2309
 
6
2310
  const getDataType = (type) => {
7
2311
  switch (type) {
@@ -16,36 +2320,28 @@ const getDataType = (type) => {
16
2320
  default: return null;
17
2321
  }
18
2322
  };
19
- const calcDataSize = (plyFile) => {
20
- let result = 0;
21
- for (const element of plyFile.elements) {
22
- for (const property of element.properties) {
23
- result += getDataType(property.type).BYTES_PER_ELEMENT * element.count;
24
- }
25
- }
26
- return result;
27
- };
28
- const shNames = new Array(45).fill('').map((_, i) => `f_rest_${i}`);
29
-
30
2323
  // parse the ply header text and return an array of Element structures and a
31
2324
  // string containing the ply format
32
- const parsePlyHeader = (data) => {
2325
+ const parseHeader = (data) => {
33
2326
  // decode header and split into lines
34
2327
  const strings = new TextDecoder('ascii')
35
2328
  .decode(data)
36
2329
  .split('\n')
37
2330
  .filter(line => line);
38
2331
  const elements = [];
2332
+ const comments = [];
39
2333
  let element;
40
2334
  for (let i = 1; i < strings.length; ++i) {
41
2335
  const words = strings[i].split(' ');
42
2336
  switch (words[0]) {
43
2337
  case 'ply':
44
2338
  case 'format':
45
- case 'comment':
46
2339
  case 'end_header':
47
2340
  // skip
48
2341
  break;
2342
+ case 'comment':
2343
+ comments.push(strings[i].substring(8)); // skip 'comment '
2344
+ break;
49
2345
  case 'element': {
50
2346
  if (words.length !== 3) {
51
2347
  throw new Error('invalid ply header');
@@ -73,7 +2369,7 @@ const parsePlyHeader = (data) => {
73
2369
  }
74
2370
  }
75
2371
  }
76
- return { strings, elements };
2372
+ return { comments, elements };
77
2373
  };
78
2374
  const cmp = (a, b, aOffset = 0) => {
79
2375
  for (let i = 0; i < b.length; ++i) {
@@ -87,7 +2383,7 @@ const magicBytes = new Uint8Array([112, 108, 121, 10]); // ply\n
87
2383
  const endHeaderBytes = new Uint8Array([10, 101, 110, 100, 95, 104, 101, 97, 100, 101, 114, 10]); // \nend_header\n
88
2384
  const readPly = async (fileHandle) => {
89
2385
  // we don't support ply text header larger than 128k
90
- const headerBuf = Buffer.alloc(128 * 1024);
2386
+ const headerBuf = Buffer$1.alloc(128 * 1024);
91
2387
  // smallest possible header size
92
2388
  let headerSize = magicBytes.length + endHeaderBytes.length;
93
2389
  if ((await fileHandle.read(headerBuf, 0, headerSize)).bytesRead !== headerSize) {
@@ -108,693 +2404,52 @@ const readPly = async (fileHandle) => {
108
2404
  }
109
2405
  }
110
2406
  // parse the header
111
- const header = parsePlyHeader(headerBuf.subarray(0, headerSize));
112
- const dataSize = calcDataSize(header);
113
- const data = Buffer.alloc(dataSize);
114
- if ((await fileHandle.read(data, 0, dataSize)).bytesRead !== dataSize) {
115
- throw new Error('failed reading ply data');
116
- }
117
- return { header, data };
118
- };
119
-
120
- // wraps ply file data and adds helpers accessors
121
- class Splat {
122
- plyFile;
123
- vertex;
124
- properties = {};
125
- constructor(plyFile) {
126
- this.plyFile = plyFile;
127
- // find vertex element and populate property offsets
128
- let offset = 0;
129
- for (let i = 0; i < plyFile.header.elements.length; ++i) {
130
- const element = plyFile.header.elements[i];
131
- if (element.name === 'vertex') {
132
- this.vertex = element;
133
- }
134
- for (let j = 0; j < element.properties.length; ++j) {
135
- const property = element.properties[j];
136
- if (element === this.vertex) {
137
- this.properties[property.name] = {
138
- type: property.type,
139
- offset
140
- };
2407
+ const header = parseHeader(headerBuf.subarray(0, headerSize));
2408
+ // create a data table for each ply element
2409
+ const elements = [];
2410
+ for (let i = 0; i < header.elements.length; ++i) {
2411
+ const element = header.elements[i];
2412
+ const columns = element.properties.map((property) => {
2413
+ return new Column(property.name, new (getDataType(property.type))(element.count));
2414
+ });
2415
+ const buffers = columns.map(column => new Uint8Array(column.data.buffer));
2416
+ const sizes = columns.map(column => column.data.BYTES_PER_ELEMENT);
2417
+ const rowSize = sizes.reduce((total, size) => total + size, 0);
2418
+ // read data in chunks of 1024 rows at a time
2419
+ const chunkSize = 1024;
2420
+ const numChunks = Math.ceil(element.count / chunkSize);
2421
+ const chunkData = Buffer$1.alloc(chunkSize * rowSize);
2422
+ for (let c = 0; c < numChunks; ++c) {
2423
+ const numRows = Math.min(chunkSize, element.count - c * chunkSize);
2424
+ await fileHandle.read(chunkData, 0, rowSize * numRows);
2425
+ let offset = 0;
2426
+ // read data row at a time
2427
+ for (let r = 0; r < numRows; ++r) {
2428
+ const rowOffset = c * chunkSize + r;
2429
+ // copy into column data
2430
+ for (let p = 0; p < columns.length; ++p) {
2431
+ const s = sizes[p];
2432
+ chunkData.copy(buffers[p], rowOffset * s, offset, offset + s);
2433
+ offset += s;
141
2434
  }
142
- offset += getDataType(property.type).BYTES_PER_ELEMENT;
143
2435
  }
144
2436
  }
2437
+ elements.push({
2438
+ name: element.name,
2439
+ dataTable: new DataTable(columns)
2440
+ });
145
2441
  }
146
- // return the total number of splats
147
- get numSplats() {
148
- return this.vertex?.count;
149
- }
150
- // return the number of spherical harmonic bands present in the data
151
- get numSHBands() {
152
- return { '9': 1, '24': 2, '-1': 3 }[shNames.findIndex(v => !this.properties.hasOwnProperty(v))] ?? 0;
153
- }
154
- // simple iterator that assumes input data is float32
155
- createIterator(fields, result) {
156
- const offsets = fields.map(f => this.properties[f].offset / 4);
157
- const float32 = new Float32Array(this.plyFile.data.buffer);
158
- return (index) => {
159
- const base = index * this.vertex.properties.length;
160
- for (let i = 0; i < fields.length; ++i) {
161
- result[i] = float32[base + offsets[i]];
162
- }
163
- };
164
- }
165
- }
166
-
167
- const math = {
168
- DEG_TO_RAD: Math.PI / 180,
169
- RAD_TO_DEG: 180 / Math.PI,
170
- clamp(value, min, max) {
171
- if (value >= max) return max;
172
- if (value <= min) return min;
173
- return value;
174
- },
175
- intToBytes24(i) {
176
- const r = i >> 16 & 0xff;
177
- const g = i >> 8 & 0xff;
178
- const b = i & 0xff;
179
- return [r, g, b];
180
- },
181
- intToBytes32(i) {
182
- const r = i >> 24 & 0xff;
183
- const g = i >> 16 & 0xff;
184
- const b = i >> 8 & 0xff;
185
- const a = i & 0xff;
186
- return [r, g, b, a];
187
- },
188
- bytesToInt24(r, g, b) {
189
- if (r.length) {
190
- b = r[2];
191
- g = r[1];
192
- r = r[0];
193
- }
194
- return r << 16 | g << 8 | b;
195
- },
196
- bytesToInt32(r, g, b, a) {
197
- if (r.length) {
198
- a = r[3];
199
- b = r[2];
200
- g = r[1];
201
- r = r[0];
202
- }
203
- return (r << 24 | g << 16 | b << 8 | a) >>> 0;
204
- },
205
- lerp(a, b, alpha) {
206
- return a + (b - a) * math.clamp(alpha, 0, 1);
207
- },
208
- lerpAngle(a, b, alpha) {
209
- if (b - a > 180) {
210
- b -= 360;
211
- }
212
- if (b - a < -180) {
213
- b += 360;
214
- }
215
- return math.lerp(a, b, math.clamp(alpha, 0, 1));
216
- },
217
- powerOfTwo(x) {
218
- return x !== 0 && !(x & x - 1);
219
- },
220
- nextPowerOfTwo(val) {
221
- val--;
222
- val |= val >> 1;
223
- val |= val >> 2;
224
- val |= val >> 4;
225
- val |= val >> 8;
226
- val |= val >> 16;
227
- val++;
228
- return val;
229
- },
230
- nearestPowerOfTwo(val) {
231
- return Math.pow(2, Math.round(Math.log(val) / Math.log(2)));
232
- },
233
- random(min, max) {
234
- const diff = max - min;
235
- return Math.random() * diff + min;
236
- },
237
- smoothstep(min, max, x) {
238
- if (x <= min) return 0;
239
- if (x >= max) return 1;
240
- x = (x - min) / (max - min);
241
- return x * x * (3 - 2 * x);
242
- },
243
- smootherstep(min, max, x) {
244
- if (x <= min) return 0;
245
- if (x >= max) return 1;
246
- x = (x - min) / (max - min);
247
- return x * x * x * (x * (x * 6 - 15) + 10);
248
- },
249
- roundUp(numToRound, multiple) {
250
- if (multiple === 0) {
251
- return numToRound;
252
- }
253
- return Math.ceil(numToRound / multiple) * multiple;
254
- },
255
- between(num, a, b, inclusive) {
256
- const min = Math.min(a, b);
257
- const max = Math.max(a, b);
258
- return inclusive ? num >= min && num <= max : num > min && num < max;
259
- }
2442
+ return {
2443
+ comments: header.comments,
2444
+ elements
2445
+ };
260
2446
  };
261
2447
 
262
- var _Vec;
263
- class Vec3 {
264
- constructor(x = 0, y = 0, z = 0) {
265
- this.x = void 0;
266
- this.y = void 0;
267
- this.z = void 0;
268
- if (x.length === 3) {
269
- this.x = x[0];
270
- this.y = x[1];
271
- this.z = x[2];
272
- } else {
273
- this.x = x;
274
- this.y = y;
275
- this.z = z;
276
- }
277
- }
278
- add(rhs) {
279
- this.x += rhs.x;
280
- this.y += rhs.y;
281
- this.z += rhs.z;
282
- return this;
283
- }
284
- add2(lhs, rhs) {
285
- this.x = lhs.x + rhs.x;
286
- this.y = lhs.y + rhs.y;
287
- this.z = lhs.z + rhs.z;
288
- return this;
289
- }
290
- addScalar(scalar) {
291
- this.x += scalar;
292
- this.y += scalar;
293
- this.z += scalar;
294
- return this;
295
- }
296
- addScaled(rhs, scalar) {
297
- this.x += rhs.x * scalar;
298
- this.y += rhs.y * scalar;
299
- this.z += rhs.z * scalar;
300
- return this;
301
- }
302
- clone() {
303
- const cstr = this.constructor;
304
- return new cstr(this.x, this.y, this.z);
305
- }
306
- copy(rhs) {
307
- this.x = rhs.x;
308
- this.y = rhs.y;
309
- this.z = rhs.z;
310
- return this;
311
- }
312
- cross(lhs, rhs) {
313
- const lx = lhs.x;
314
- const ly = lhs.y;
315
- const lz = lhs.z;
316
- const rx = rhs.x;
317
- const ry = rhs.y;
318
- const rz = rhs.z;
319
- this.x = ly * rz - ry * lz;
320
- this.y = lz * rx - rz * lx;
321
- this.z = lx * ry - rx * ly;
322
- return this;
323
- }
324
- distance(rhs) {
325
- const x = this.x - rhs.x;
326
- const y = this.y - rhs.y;
327
- const z = this.z - rhs.z;
328
- return Math.sqrt(x * x + y * y + z * z);
329
- }
330
- div(rhs) {
331
- this.x /= rhs.x;
332
- this.y /= rhs.y;
333
- this.z /= rhs.z;
334
- return this;
335
- }
336
- div2(lhs, rhs) {
337
- this.x = lhs.x / rhs.x;
338
- this.y = lhs.y / rhs.y;
339
- this.z = lhs.z / rhs.z;
340
- return this;
341
- }
342
- divScalar(scalar) {
343
- this.x /= scalar;
344
- this.y /= scalar;
345
- this.z /= scalar;
346
- return this;
347
- }
348
- dot(rhs) {
349
- return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
350
- }
351
- equals(rhs) {
352
- return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z;
353
- }
354
- equalsApprox(rhs, epsilon = 1e-6) {
355
- return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon;
356
- }
357
- length() {
358
- return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
359
- }
360
- lengthSq() {
361
- return this.x * this.x + this.y * this.y + this.z * this.z;
362
- }
363
- lerp(lhs, rhs, alpha) {
364
- this.x = lhs.x + alpha * (rhs.x - lhs.x);
365
- this.y = lhs.y + alpha * (rhs.y - lhs.y);
366
- this.z = lhs.z + alpha * (rhs.z - lhs.z);
367
- return this;
368
- }
369
- mul(rhs) {
370
- this.x *= rhs.x;
371
- this.y *= rhs.y;
372
- this.z *= rhs.z;
373
- return this;
374
- }
375
- mul2(lhs, rhs) {
376
- this.x = lhs.x * rhs.x;
377
- this.y = lhs.y * rhs.y;
378
- this.z = lhs.z * rhs.z;
379
- return this;
380
- }
381
- mulScalar(scalar) {
382
- this.x *= scalar;
383
- this.y *= scalar;
384
- this.z *= scalar;
385
- return this;
386
- }
387
- normalize(src = this) {
388
- const lengthSq = src.x * src.x + src.y * src.y + src.z * src.z;
389
- if (lengthSq > 0) {
390
- const invLength = 1 / Math.sqrt(lengthSq);
391
- this.x = src.x * invLength;
392
- this.y = src.y * invLength;
393
- this.z = src.z * invLength;
394
- }
395
- return this;
396
- }
397
- floor(src = this) {
398
- this.x = Math.floor(src.x);
399
- this.y = Math.floor(src.y);
400
- this.z = Math.floor(src.z);
401
- return this;
402
- }
403
- ceil(src = this) {
404
- this.x = Math.ceil(src.x);
405
- this.y = Math.ceil(src.y);
406
- this.z = Math.ceil(src.z);
407
- return this;
408
- }
409
- round(src = this) {
410
- this.x = Math.round(src.x);
411
- this.y = Math.round(src.y);
412
- this.z = Math.round(src.z);
413
- return this;
414
- }
415
- min(rhs) {
416
- if (rhs.x < this.x) this.x = rhs.x;
417
- if (rhs.y < this.y) this.y = rhs.y;
418
- if (rhs.z < this.z) this.z = rhs.z;
419
- return this;
420
- }
421
- max(rhs) {
422
- if (rhs.x > this.x) this.x = rhs.x;
423
- if (rhs.y > this.y) this.y = rhs.y;
424
- if (rhs.z > this.z) this.z = rhs.z;
425
- return this;
426
- }
427
- project(rhs) {
428
- const a_dot_b = this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
429
- const b_dot_b = rhs.x * rhs.x + rhs.y * rhs.y + rhs.z * rhs.z;
430
- const s = a_dot_b / b_dot_b;
431
- this.x = rhs.x * s;
432
- this.y = rhs.y * s;
433
- this.z = rhs.z * s;
434
- return this;
435
- }
436
- set(x, y, z) {
437
- this.x = x;
438
- this.y = y;
439
- this.z = z;
440
- return this;
441
- }
442
- sub(rhs) {
443
- this.x -= rhs.x;
444
- this.y -= rhs.y;
445
- this.z -= rhs.z;
446
- return this;
447
- }
448
- sub2(lhs, rhs) {
449
- this.x = lhs.x - rhs.x;
450
- this.y = lhs.y - rhs.y;
451
- this.z = lhs.z - rhs.z;
452
- return this;
453
- }
454
- subScalar(scalar) {
455
- this.x -= scalar;
456
- this.y -= scalar;
457
- this.z -= scalar;
458
- return this;
459
- }
460
- toString() {
461
- return `[${this.x}, ${this.y}, ${this.z}]`;
462
- }
463
- }
464
- _Vec = Vec3;
465
- Vec3.ZERO = Object.freeze(new _Vec(0, 0, 0));
466
- Vec3.HALF = Object.freeze(new _Vec(0.5, 0.5, 0.5));
467
- Vec3.ONE = Object.freeze(new _Vec(1, 1, 1));
468
- Vec3.UP = Object.freeze(new _Vec(0, 1, 0));
469
- Vec3.DOWN = Object.freeze(new _Vec(0, -1, 0));
470
- Vec3.RIGHT = Object.freeze(new _Vec(1, 0, 0));
471
- Vec3.LEFT = Object.freeze(new _Vec(-1, 0, 0));
472
- Vec3.FORWARD = Object.freeze(new _Vec(0, 0, -1));
473
- Vec3.BACK = Object.freeze(new _Vec(0, 0, 1));
474
-
475
- var _Quat;
476
- class Quat {
477
- constructor(x = 0, y = 0, z = 0, w = 1) {
478
- this.x = void 0;
479
- this.y = void 0;
480
- this.z = void 0;
481
- this.w = void 0;
482
- if (x.length === 4) {
483
- this.x = x[0];
484
- this.y = x[1];
485
- this.z = x[2];
486
- this.w = x[3];
487
- } else {
488
- this.x = x;
489
- this.y = y;
490
- this.z = z;
491
- this.w = w;
492
- }
493
- }
494
- clone() {
495
- const cstr = this.constructor;
496
- return new cstr(this.x, this.y, this.z, this.w);
497
- }
498
- conjugate(src = this) {
499
- this.x = src.x * -1;
500
- this.y = src.y * -1;
501
- this.z = src.z * -1;
502
- this.w = src.w;
503
- return this;
504
- }
505
- copy(rhs) {
506
- this.x = rhs.x;
507
- this.y = rhs.y;
508
- this.z = rhs.z;
509
- this.w = rhs.w;
510
- return this;
511
- }
512
- equals(rhs) {
513
- return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z && this.w === rhs.w;
514
- }
515
- equalsApprox(rhs, epsilon = 1e-6) {
516
- return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon && Math.abs(this.w - rhs.w) < epsilon;
517
- }
518
- getAxisAngle(axis) {
519
- let rad = Math.acos(this.w) * 2;
520
- const s = Math.sin(rad / 2);
521
- if (s !== 0) {
522
- axis.x = this.x / s;
523
- axis.y = this.y / s;
524
- axis.z = this.z / s;
525
- if (axis.x < 0 || axis.y < 0 || axis.z < 0) {
526
- axis.x *= -1;
527
- axis.y *= -1;
528
- axis.z *= -1;
529
- rad *= -1;
530
- }
531
- } else {
532
- axis.x = 1;
533
- axis.y = 0;
534
- axis.z = 0;
535
- }
536
- return rad * math.RAD_TO_DEG;
537
- }
538
- getEulerAngles(eulers = new Vec3()) {
539
- let x, y, z;
540
- const qx = this.x;
541
- const qy = this.y;
542
- const qz = this.z;
543
- const qw = this.w;
544
- const a2 = 2 * (qw * qy - qx * qz);
545
- if (a2 <= -0.99999) {
546
- x = 2 * Math.atan2(qx, qw);
547
- y = -Math.PI / 2;
548
- z = 0;
549
- } else if (a2 >= 0.99999) {
550
- x = 2 * Math.atan2(qx, qw);
551
- y = Math.PI / 2;
552
- z = 0;
553
- } else {
554
- x = Math.atan2(2 * (qw * qx + qy * qz), 1 - 2 * (qx * qx + qy * qy));
555
- y = Math.asin(a2);
556
- z = Math.atan2(2 * (qw * qz + qx * qy), 1 - 2 * (qy * qy + qz * qz));
557
- }
558
- return eulers.set(x, y, z).mulScalar(math.RAD_TO_DEG);
559
- }
560
- invert(src = this) {
561
- return this.conjugate(src).normalize();
562
- }
563
- length() {
564
- return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
565
- }
566
- lengthSq() {
567
- return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
568
- }
569
- mul(rhs) {
570
- const q1x = this.x;
571
- const q1y = this.y;
572
- const q1z = this.z;
573
- const q1w = this.w;
574
- const q2x = rhs.x;
575
- const q2y = rhs.y;
576
- const q2z = rhs.z;
577
- const q2w = rhs.w;
578
- this.x = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y;
579
- this.y = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z;
580
- this.z = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x;
581
- this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z;
582
- return this;
583
- }
584
- mulScalar(scalar, src = this) {
585
- this.x = src.x * scalar;
586
- this.y = src.y * scalar;
587
- this.z = src.z * scalar;
588
- this.w = src.w * scalar;
589
- return this;
590
- }
591
- mul2(lhs, rhs) {
592
- const q1x = lhs.x;
593
- const q1y = lhs.y;
594
- const q1z = lhs.z;
595
- const q1w = lhs.w;
596
- const q2x = rhs.x;
597
- const q2y = rhs.y;
598
- const q2z = rhs.z;
599
- const q2w = rhs.w;
600
- this.x = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y;
601
- this.y = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z;
602
- this.z = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x;
603
- this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z;
604
- return this;
605
- }
606
- normalize(src = this) {
607
- let len = src.length();
608
- if (len === 0) {
609
- this.x = this.y = this.z = 0;
610
- this.w = 1;
611
- } else {
612
- len = 1 / len;
613
- this.x = src.x * len;
614
- this.y = src.y * len;
615
- this.z = src.z * len;
616
- this.w = src.w * len;
617
- }
618
- return this;
619
- }
620
- set(x, y, z, w) {
621
- this.x = x;
622
- this.y = y;
623
- this.z = z;
624
- this.w = w;
625
- return this;
626
- }
627
- setFromAxisAngle(axis, angle) {
628
- angle *= 0.5 * math.DEG_TO_RAD;
629
- const sa = Math.sin(angle);
630
- const ca = Math.cos(angle);
631
- this.x = sa * axis.x;
632
- this.y = sa * axis.y;
633
- this.z = sa * axis.z;
634
- this.w = ca;
635
- return this;
636
- }
637
- setFromEulerAngles(ex, ey, ez) {
638
- if (ex instanceof Vec3) {
639
- const vec = ex;
640
- ex = vec.x;
641
- ey = vec.y;
642
- ez = vec.z;
643
- }
644
- const halfToRad = 0.5 * math.DEG_TO_RAD;
645
- ex *= halfToRad;
646
- ey *= halfToRad;
647
- ez *= halfToRad;
648
- const sx = Math.sin(ex);
649
- const cx = Math.cos(ex);
650
- const sy = Math.sin(ey);
651
- const cy = Math.cos(ey);
652
- const sz = Math.sin(ez);
653
- const cz = Math.cos(ez);
654
- this.x = sx * cy * cz - cx * sy * sz;
655
- this.y = cx * sy * cz + sx * cy * sz;
656
- this.z = cx * cy * sz - sx * sy * cz;
657
- this.w = cx * cy * cz + sx * sy * sz;
658
- return this;
659
- }
660
- setFromMat4(m) {
661
- const d = m.data;
662
- let m00 = d[0];
663
- let m01 = d[1];
664
- let m02 = d[2];
665
- let m10 = d[4];
666
- let m11 = d[5];
667
- let m12 = d[6];
668
- let m20 = d[8];
669
- let m21 = d[9];
670
- let m22 = d[10];
671
- let l;
672
- l = m00 * m00 + m01 * m01 + m02 * m02;
673
- if (l === 0) return this.set(0, 0, 0, 1);
674
- l = 1 / Math.sqrt(l);
675
- m00 *= l;
676
- m01 *= l;
677
- m02 *= l;
678
- l = m10 * m10 + m11 * m11 + m12 * m12;
679
- if (l === 0) return this.set(0, 0, 0, 1);
680
- l = 1 / Math.sqrt(l);
681
- m10 *= l;
682
- m11 *= l;
683
- m12 *= l;
684
- l = m20 * m20 + m21 * m21 + m22 * m22;
685
- if (l === 0) return this.set(0, 0, 0, 1);
686
- l = 1 / Math.sqrt(l);
687
- m20 *= l;
688
- m21 *= l;
689
- m22 *= l;
690
- if (m22 < 0) {
691
- if (m00 > m11) {
692
- this.set(1 + m00 - m11 - m22, m01 + m10, m20 + m02, m12 - m21);
693
- } else {
694
- this.set(m01 + m10, 1 - m00 + m11 - m22, m12 + m21, m20 - m02);
695
- }
696
- } else {
697
- if (m00 < -m11) {
698
- this.set(m20 + m02, m12 + m21, 1 - m00 - m11 + m22, m01 - m10);
699
- } else {
700
- this.set(m12 - m21, m20 - m02, m01 - m10, 1 + m00 + m11 + m22);
701
- }
702
- }
703
- return this.mulScalar(1.0 / this.length());
704
- }
705
- setFromDirections(from, to) {
706
- const dotProduct = 1 + from.dot(to);
707
- if (dotProduct < Number.EPSILON) {
708
- if (Math.abs(from.x) > Math.abs(from.y)) {
709
- this.x = -from.z;
710
- this.y = 0;
711
- this.z = from.x;
712
- this.w = 0;
713
- } else {
714
- this.x = 0;
715
- this.y = -from.z;
716
- this.z = from.y;
717
- this.w = 0;
718
- }
719
- } else {
720
- this.x = from.y * to.z - from.z * to.y;
721
- this.y = from.z * to.x - from.x * to.z;
722
- this.z = from.x * to.y - from.y * to.x;
723
- this.w = dotProduct;
724
- }
725
- return this.normalize();
726
- }
727
- slerp(lhs, rhs, alpha) {
728
- const lx = lhs.x;
729
- const ly = lhs.y;
730
- const lz = lhs.z;
731
- const lw = lhs.w;
732
- let rx = rhs.x;
733
- let ry = rhs.y;
734
- let rz = rhs.z;
735
- let rw = rhs.w;
736
- let cosHalfTheta = lw * rw + lx * rx + ly * ry + lz * rz;
737
- if (cosHalfTheta < 0) {
738
- rw = -rw;
739
- rx = -rx;
740
- ry = -ry;
741
- rz = -rz;
742
- cosHalfTheta = -cosHalfTheta;
743
- }
744
- if (Math.abs(cosHalfTheta) >= 1) {
745
- this.w = lw;
746
- this.x = lx;
747
- this.y = ly;
748
- this.z = lz;
749
- return this;
750
- }
751
- const halfTheta = Math.acos(cosHalfTheta);
752
- const sinHalfTheta = Math.sqrt(1 - cosHalfTheta * cosHalfTheta);
753
- if (Math.abs(sinHalfTheta) < 0.001) {
754
- this.w = lw * 0.5 + rw * 0.5;
755
- this.x = lx * 0.5 + rx * 0.5;
756
- this.y = ly * 0.5 + ry * 0.5;
757
- this.z = lz * 0.5 + rz * 0.5;
758
- return this;
759
- }
760
- const ratioA = Math.sin((1 - alpha) * halfTheta) / sinHalfTheta;
761
- const ratioB = Math.sin(alpha * halfTheta) / sinHalfTheta;
762
- this.w = lw * ratioA + rw * ratioB;
763
- this.x = lx * ratioA + rx * ratioB;
764
- this.y = ly * ratioA + ry * ratioB;
765
- this.z = lz * ratioA + rz * ratioB;
766
- return this;
767
- }
768
- transformVector(vec, res = new Vec3()) {
769
- const x = vec.x,
770
- y = vec.y,
771
- z = vec.z;
772
- const qx = this.x,
773
- qy = this.y,
774
- qz = this.z,
775
- qw = this.w;
776
- const ix = qw * x + qy * z - qz * y;
777
- const iy = qw * y + qz * x - qx * z;
778
- const iz = qw * z + qx * y - qy * x;
779
- const iw = -qx * x - qy * y - qz * z;
780
- res.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
781
- res.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
782
- res.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
783
- return res;
784
- }
785
- toString() {
786
- return `[${this.x}, ${this.y}, ${this.z}, ${this.w}]`;
787
- }
788
- }
789
- _Quat = Quat;
790
- Quat.IDENTITY = Object.freeze(new _Quat(0, 0, 0, 1));
791
- Quat.ZERO = Object.freeze(new _Quat(0, 0, 0, 0));
2448
+ const sigmoid = (v) => 1 / (1 + Math.exp(-v));
792
2449
 
793
- const generatedByString = `Generated by splat-transform ${version}`;
794
- const shBandCoeffs = [0, 3, 8, 15];
795
2450
  const q = new Quat();
796
2451
  // process and compress a chunk of 256 splats
797
- class Chunk {
2452
+ class CompressedChunk {
798
2453
  static members = [
799
2454
  'x', 'y', 'z',
800
2455
  'scale_0', 'scale_1', 'scale_2',
@@ -811,7 +2466,7 @@ class Chunk {
811
2466
  color;
812
2467
  constructor(size = 256) {
813
2468
  this.size = size;
814
- Chunk.members.forEach((m) => {
2469
+ CompressedChunk.members.forEach((m) => {
815
2470
  this.data[m] = new Float32Array(size);
816
2471
  });
817
2472
  this.chunkData = new Float32Array(18);
@@ -820,10 +2475,10 @@ class Chunk {
820
2475
  this.scale = new Uint32Array(size);
821
2476
  this.color = new Uint32Array(size);
822
2477
  }
823
- set(index, singleSplat) {
824
- for (let i = 0; i < Chunk.members.length; ++i) {
825
- this.data[Chunk.members[i]][index] = singleSplat[i];
826
- }
2478
+ set(index, data) {
2479
+ CompressedChunk.members.forEach((m) => {
2480
+ this.data[m][index] = data[m];
2481
+ });
827
2482
  }
828
2483
  pack() {
829
2484
  const calcMinMax = (data) => {
@@ -923,7 +2578,7 @@ class Chunk {
923
2578
  this.position[i] = pack111011(normalize(x[i], px.min, px.max), normalize(y[i], py.min, py.max), normalize(z[i], pz.min, pz.max));
924
2579
  this.rotation[i] = packRot(rot_0[i], rot_1[i], rot_2[i], rot_3[i]);
925
2580
  this.scale[i] = pack111011(normalize(scale_0[i], sx.min, sx.max), normalize(scale_1[i], sy.min, sy.max), normalize(scale_2[i], sz.min, sz.max));
926
- this.color[i] = pack8888(normalize(f_dc_0[i], cr.min, cr.max), normalize(f_dc_1[i], cg.min, cg.max), normalize(f_dc_2[i], cb.min, cb.max), 1 / (1 + Math.exp(-opacity[i])));
2581
+ this.color[i] = pack8888(normalize(f_dc_0[i], cr.min, cr.max), normalize(f_dc_1[i], cg.min, cg.max), normalize(f_dc_2[i], cb.min, cb.max), sigmoid(opacity[i]));
927
2582
  }
928
2583
  this.chunkData.set([
929
2584
  px.min, py.min, pz.min, px.max, py.max, pz.max,
@@ -932,72 +2587,108 @@ class Chunk {
932
2587
  ], 0);
933
2588
  }
934
2589
  }
2590
+
935
2591
  // sort the compressed indices into morton order
936
- const sortSplats = (splat, indices) => {
937
- // https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
938
- const encodeMorton3 = (x, y, z) => {
939
- const Part1By2 = (x) => {
940
- x &= 0x000003ff;
941
- x = (x ^ (x << 16)) & 0xff0000ff;
942
- x = (x ^ (x << 8)) & 0x0300f00f;
943
- x = (x ^ (x << 4)) & 0x030c30c3;
944
- x = (x ^ (x << 2)) & 0x09249249;
945
- return x;
2592
+ const generateOrdering = (dataTable) => {
2593
+ const cx = dataTable.getColumnByName('x').data;
2594
+ const cy = dataTable.getColumnByName('y').data;
2595
+ const cz = dataTable.getColumnByName('z').data;
2596
+ const generate = (indices) => {
2597
+ // https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
2598
+ const encodeMorton3 = (x, y, z) => {
2599
+ const Part1By2 = (x) => {
2600
+ x &= 0x000003ff;
2601
+ x = (x ^ (x << 16)) & 0xff0000ff;
2602
+ x = (x ^ (x << 8)) & 0x0300f00f;
2603
+ x = (x ^ (x << 4)) & 0x030c30c3;
2604
+ x = (x ^ (x << 2)) & 0x09249249;
2605
+ return x;
2606
+ };
2607
+ return (Part1By2(z) << 2) + (Part1By2(y) << 1) + Part1By2(x);
946
2608
  };
947
- return (Part1By2(z) << 2) + (Part1By2(y) << 1) + Part1By2(x);
948
- };
949
- let minx;
950
- let miny;
951
- let minz;
952
- let maxx;
953
- let maxy;
954
- let maxz;
955
- const vertex = [0, 0, 0];
956
- const it = splat.createIterator(['x', 'y', 'z'], vertex);
957
- // calculate scene extents across all splats (using sort centers, because they're in world space)
958
- for (let i = 0; i < splat.numSplats; ++i) {
959
- it(i);
960
- const x = vertex[0];
961
- const y = vertex[1];
962
- const z = vertex[2];
963
- if (minx === undefined) {
964
- minx = maxx = x;
965
- miny = maxy = y;
966
- minz = maxz = z;
2609
+ let mx;
2610
+ let my;
2611
+ let mz;
2612
+ let Mx;
2613
+ let My;
2614
+ let Mz;
2615
+ // calculate scene extents across all splats (using sort centers, because they're in world space)
2616
+ for (let i = 0; i < indices.length; ++i) {
2617
+ const ri = indices[i];
2618
+ const x = cx[ri];
2619
+ const y = cy[ri];
2620
+ const z = cz[ri];
2621
+ if (mx === undefined) {
2622
+ mx = Mx = x;
2623
+ my = My = y;
2624
+ mz = Mz = z;
2625
+ }
2626
+ else {
2627
+ if (x < mx)
2628
+ mx = x;
2629
+ else if (x > Mx)
2630
+ Mx = x;
2631
+ if (y < my)
2632
+ my = y;
2633
+ else if (y > My)
2634
+ My = y;
2635
+ if (z < mz)
2636
+ mz = z;
2637
+ else if (z > Mz)
2638
+ Mz = z;
2639
+ }
967
2640
  }
968
- else {
969
- if (x < minx)
970
- minx = x;
971
- else if (x > maxx)
972
- maxx = x;
973
- if (y < miny)
974
- miny = y;
975
- else if (y > maxy)
976
- maxy = y;
977
- if (z < minz)
978
- minz = z;
979
- else if (z > maxz)
980
- maxz = z;
2641
+ const xlen = Mx - mx;
2642
+ const ylen = My - my;
2643
+ const zlen = Mz - mz;
2644
+ if (!isFinite(xlen) || !isFinite(ylen) || !isFinite(zlen)) {
2645
+ console.log('invalid extents', xlen, ylen, zlen);
2646
+ return;
981
2647
  }
2648
+ const xmul = 1024 / xlen;
2649
+ const ymul = 1024 / ylen;
2650
+ const zmul = 1024 / zlen;
2651
+ const morton = new Uint32Array(indices.length);
2652
+ for (let i = 0; i < indices.length; ++i) {
2653
+ const ri = indices[i];
2654
+ const x = cx[ri];
2655
+ const y = cy[ri];
2656
+ const z = cz[ri];
2657
+ const ix = Math.min(1023, (x - mx) * xmul) >>> 0;
2658
+ const iy = Math.min(1023, (y - my) * ymul) >>> 0;
2659
+ const iz = Math.min(1023, (z - mz) * zmul) >>> 0;
2660
+ morton[i] = encodeMorton3(ix, iy, iz);
2661
+ }
2662
+ // sort indices by morton code
2663
+ const order = indices.map((_, i) => i);
2664
+ order.sort((a, b) => morton[a] - morton[b]);
2665
+ const tmpIndices = indices.slice();
2666
+ for (let i = 0; i < indices.length; ++i) {
2667
+ indices[i] = tmpIndices[order[i]];
2668
+ }
2669
+ // sort the largest buckets recursively
2670
+ let start = 0;
2671
+ let end = 1;
2672
+ while (start < indices.length) {
2673
+ while (end < indices.length && morton[order[end]] === morton[order[start]]) {
2674
+ ++end;
2675
+ }
2676
+ if (end - start > 256) {
2677
+ // console.log('sorting', end - start);
2678
+ generate(indices.subarray(start, end));
2679
+ }
2680
+ start = end;
2681
+ }
2682
+ };
2683
+ const indices = new Uint32Array(dataTable.numRows);
2684
+ for (let i = 0; i < indices.length; ++i) {
2685
+ indices[i] = i;
982
2686
  }
983
- const xlen = maxx - minx;
984
- const ylen = maxy - miny;
985
- const zlen = maxz - minz;
986
- const morton = new Uint32Array(indices.length);
987
- let idx = 0;
988
- for (let i = 0; i < splat.numSplats; ++i) {
989
- it(i);
990
- const x = vertex[0];
991
- const y = vertex[1];
992
- const z = vertex[2];
993
- const ix = Math.floor(1024 * (x - minx) / xlen);
994
- const iy = Math.floor(1024 * (y - miny) / ylen);
995
- const iz = Math.floor(1024 * (z - minz) / zlen);
996
- morton[idx++] = encodeMorton3(ix, iy, iz);
997
- }
998
- // order splats by morton code
999
- indices.sort((a, b) => morton[a.globalIndex] - morton[b.globalIndex]);
2687
+ generate(indices);
2688
+ return indices;
1000
2689
  };
2690
+
2691
+ const generatedByString = `Generated by splat-transform ${version}`;
1001
2692
  const chunkProps = [
1002
2693
  'min_x', 'min_y', 'min_z',
1003
2694
  'max_x', 'max_y', 'max_z',
@@ -1012,20 +2703,13 @@ const vertexProps = [
1012
2703
  'packed_scale',
1013
2704
  'packed_color'
1014
2705
  ];
1015
- const writeCompressedPly = async (fileHandle, splat) => {
1016
- // make a list of indices spanning all splats (so we can sort them together)
1017
- const indices = [];
1018
- for (let i = 0; i < splat.numSplats; ++i) {
1019
- indices.push({ splatIndex: 0, i, globalIndex: indices.length });
1020
- }
1021
- if (indices.length === 0) {
1022
- throw new Error('no splats to write');
1023
- }
1024
- const numSplats = indices.length;
2706
+ const shNames = new Array(45).fill('').map((_, i) => `f_rest_${i}`);
2707
+ const writeCompressedPly = async (fileHandle, dataTable) => {
2708
+ const shBands = { '9': 1, '24': 2, '-1': 3 }[shNames.findIndex(v => !dataTable.hasColumn(v))] ?? 0;
2709
+ const outputSHCoeffs = [0, 3, 8, 15][shBands];
2710
+ const numSplats = dataTable.numRows;
1025
2711
  const numChunks = Math.ceil(numSplats / 256);
1026
- const outputSHBands = splat.numSHBands;
1027
- const outputSHCoeffs = shBandCoeffs[outputSHBands];
1028
- const shHeader = outputSHBands ? [
2712
+ const shHeader = shBands ? [
1029
2713
  `element sh ${numSplats}`,
1030
2714
  new Array(outputSHCoeffs * 3).fill('').map((_, i) => `property uchar f_rest_${i}`)
1031
2715
  ].flat() : [];
@@ -1042,32 +2726,31 @@ const writeCompressedPly = async (fileHandle, splat) => {
1042
2726
  ].flat().join('\n');
1043
2727
  const header = (new TextEncoder()).encode(headerText);
1044
2728
  const chunkData = new Float32Array(numChunks * chunkProps.length);
1045
- const splatData = new Uint32Array(numSplats * vertexProps.length);
2729
+ const splatIData = new Uint32Array(numSplats * vertexProps.length);
1046
2730
  const shData = new Uint8Array(numSplats * outputSHCoeffs * 3);
1047
2731
  // sort splats into some kind of order (morton order rn)
1048
- sortSplats(splat, indices);
1049
- const singleSplat = Chunk.members.map(_ => 0);
1050
- const it = splat.createIterator(Chunk.members, singleSplat);
1051
- const shMembers = shNames.slice(0, outputSHCoeffs * 3);
1052
- const singleSH = shMembers.map(_ => 0);
1053
- const shIt = splat.createIterator(shMembers, singleSH);
1054
- const chunk = new Chunk();
2732
+ const sortIndices = generateOrdering(dataTable);
2733
+ const row = {};
2734
+ const chunk = new CompressedChunk();
1055
2735
  for (let i = 0; i < numChunks; ++i) {
1056
2736
  const num = Math.min(numSplats, (i + 1) * 256) - i * 256;
1057
2737
  for (let j = 0; j < num; ++j) {
1058
- const index = indices[i * 256 + j];
2738
+ const index = sortIndices[i * 256 + j];
1059
2739
  // read splat data
1060
- it(index.i);
2740
+ dataTable.getRow(index, row);
1061
2741
  // update chunk
1062
- chunk.set(j, singleSplat);
1063
- shIt(index.i);
2742
+ chunk.set(j, row);
1064
2743
  // quantize and write sh data
1065
2744
  let off = (i * 256 + j) * outputSHCoeffs * 3;
1066
2745
  for (let k = 0; k < outputSHCoeffs * 3; ++k) {
1067
- const nvalue = singleSH[k] / 8 + 0.5;
2746
+ const nvalue = row[shNames[k]] / 8 + 0.5;
1068
2747
  shData[off++] = Math.max(0, Math.min(255, Math.trunc(nvalue * 256)));
1069
2748
  }
1070
2749
  }
2750
+ // repeat the last gaussian to fill the rest of the final chunk
2751
+ for (let j = num; j < 256; ++j) {
2752
+ chunk.set(j, row);
2753
+ }
1071
2754
  // pack the chunk
1072
2755
  chunk.pack();
1073
2756
  // store the float data
@@ -1075,57 +2758,490 @@ const writeCompressedPly = async (fileHandle, splat) => {
1075
2758
  // write packed bits
1076
2759
  const offset = i * 256 * 4;
1077
2760
  for (let j = 0; j < num; ++j) {
1078
- splatData[offset + j * 4 + 0] = chunk.position[j];
1079
- splatData[offset + j * 4 + 1] = chunk.rotation[j];
1080
- splatData[offset + j * 4 + 2] = chunk.scale[j];
1081
- splatData[offset + j * 4 + 3] = chunk.color[j];
2761
+ splatIData[offset + j * 4 + 0] = chunk.position[j];
2762
+ splatIData[offset + j * 4 + 1] = chunk.rotation[j];
2763
+ splatIData[offset + j * 4 + 2] = chunk.scale[j];
2764
+ splatIData[offset + j * 4 + 3] = chunk.color[j];
1082
2765
  }
1083
2766
  }
1084
2767
  await fileHandle.write(header);
1085
2768
  await fileHandle.write(new Uint8Array(chunkData.buffer));
1086
- await fileHandle.write(new Uint8Array(splatData.buffer));
2769
+ await fileHandle.write(new Uint8Array(splatIData.buffer));
1087
2770
  await fileHandle.write(shData);
1088
2771
  };
1089
2772
 
1090
- const readData = async (filename) => {
1091
- // open input
1092
- console.log(`loading '${filename}'...`);
2773
+ const columnTypeToPlyType = (type) => {
2774
+ switch (type) {
2775
+ case 'float32': return 'float';
2776
+ case 'float64': return 'double';
2777
+ case 'int8': return 'char';
2778
+ case 'uint8': return 'uchar';
2779
+ case 'int16': return 'short';
2780
+ case 'uint16': return 'ushort';
2781
+ case 'int32': return 'int';
2782
+ case 'uint32': return 'uint';
2783
+ }
2784
+ };
2785
+ const writePly = async (fileHandle, plyData) => {
2786
+ const header = [
2787
+ 'ply',
2788
+ 'format binary_little_endian 1.0',
2789
+ plyData.comments.map(c => `comment ${c}`),
2790
+ plyData.elements.map((element) => {
2791
+ return [
2792
+ `element ${element.name} ${element.dataTable.numRows}`,
2793
+ element.dataTable.columns.map((column) => {
2794
+ return `property ${columnTypeToPlyType(column.dataType)} ${column.name}`;
2795
+ })
2796
+ ];
2797
+ }),
2798
+ 'end_header'
2799
+ ];
2800
+ // write the header
2801
+ await fileHandle.write((new TextEncoder()).encode(`${header.flat(3).join('\n')}\n`));
2802
+ for (let i = 0; i < plyData.elements.length; ++i) {
2803
+ const table = plyData.elements[i].dataTable;
2804
+ const columns = table.columns;
2805
+ const buffers = columns.map(c => Buffer.from(c.data.buffer));
2806
+ const sizes = columns.map(c => c.data.BYTES_PER_ELEMENT);
2807
+ const rowSize = sizes.reduce((total, size) => total + size, 0);
2808
+ // write to file in chunks of 1024 rows
2809
+ const chunkSize = 1024;
2810
+ const numChunks = Math.ceil(table.numRows / chunkSize);
2811
+ const chunkData = Buffer.alloc(chunkSize * rowSize);
2812
+ for (let c = 0; c < numChunks; ++c) {
2813
+ const numRows = Math.min(chunkSize, table.numRows - c * chunkSize);
2814
+ let offset = 0;
2815
+ for (let r = 0; r < numRows; ++r) {
2816
+ const rowOffset = c * chunkSize + r;
2817
+ for (let p = 0; p < columns.length; ++p) {
2818
+ const s = sizes[p];
2819
+ buffers[p].copy(chunkData, offset, rowOffset * s, rowOffset * s + s);
2820
+ offset += s;
2821
+ }
2822
+ }
2823
+ // write the chunk
2824
+ await fileHandle.write(chunkData.subarray(0, offset));
2825
+ }
2826
+ }
2827
+ };
2828
+
2829
+ new Array(45).fill('').map((_, i) => `f_rest_${i}`);
2830
+ const calcMinMax = (dataTable, columnNames) => {
2831
+ const columns = columnNames.map(name => dataTable.getColumnByName(name));
2832
+ const minMax = columnNames.map(() => [Infinity, -Infinity]);
2833
+ const row = {};
2834
+ for (let i = 0; i < dataTable.numRows; ++i) {
2835
+ const r = dataTable.getRow(i, row, columns);
2836
+ for (let j = 0; j < columnNames.length; ++j) {
2837
+ const value = r[columnNames[j]];
2838
+ if (value < minMax[j][0])
2839
+ minMax[j][0] = value;
2840
+ if (value > minMax[j][1])
2841
+ minMax[j][1] = value;
2842
+ }
2843
+ }
2844
+ return minMax;
2845
+ };
2846
+ const logTransform = (value) => {
2847
+ return Math.sign(value) * Math.log(Math.abs(value) + 1);
2848
+ };
2849
+ // calculate the output index of the gaussian given its index. chunks of
2850
+ // 256 gaussians are packed into 16x16 tiles on the output texture.
2851
+ const target = (index, width) => {
2852
+ const chunkWidth = width / 16;
2853
+ const chunkIndex = Math.floor(index / 256);
2854
+ const chunkX = chunkIndex % chunkWidth;
2855
+ const chunkY = Math.floor(chunkIndex / chunkWidth);
2856
+ const x = chunkX * 16 + (index % 16);
2857
+ const y = chunkY * 16 + Math.floor((index % 256) / 16);
2858
+ return x + y * width;
2859
+ };
2860
+ const writeSogs = async (outputFilename, dataTable) => {
2861
+ // generate an optimal ordering
2862
+ const sortIndices = generateOrdering(dataTable);
2863
+ const numRows = dataTable.numRows;
2864
+ const width = Math.ceil(Math.sqrt(numRows) / 16) * 16;
2865
+ const height = Math.ceil(numRows / width / 16) * 16;
2866
+ const channels = 4;
2867
+ const write = (filename, data, w = width, h = height) => {
2868
+ const pathname = resolve(dirname(outputFilename), filename);
2869
+ console.log(`writing '${pathname}'...`);
2870
+ return sharp(data, { raw: { width: w, height: h, channels } })
2871
+ .webp({ lossless: true })
2872
+ .toFile(pathname);
2873
+ };
2874
+ const row = {};
2875
+ // convert position/means
2876
+ const meansL = new Uint8Array(width * height * channels);
2877
+ const meansU = new Uint8Array(width * height * channels);
2878
+ const meansNames = ['x', 'y', 'z'];
2879
+ const meansMinMax = calcMinMax(dataTable, meansNames).map(v => v.map(logTransform));
2880
+ const meansColumns = meansNames.map(name => dataTable.getColumnByName(name));
2881
+ for (let i = 0; i < dataTable.numRows; ++i) {
2882
+ dataTable.getRow(sortIndices[i], row, meansColumns);
2883
+ const x = 65535 * (logTransform(row.x) - meansMinMax[0][0]) / (meansMinMax[0][1] - meansMinMax[0][0]);
2884
+ const y = 65535 * (logTransform(row.y) - meansMinMax[1][0]) / (meansMinMax[1][1] - meansMinMax[1][0]);
2885
+ const z = 65535 * (logTransform(row.z) - meansMinMax[2][0]) / (meansMinMax[2][1] - meansMinMax[2][0]);
2886
+ const ti = target(i, width);
2887
+ meansL[ti * 4] = x & 0xff;
2888
+ meansL[ti * 4 + 1] = y & 0xff;
2889
+ meansL[ti * 4 + 2] = z & 0xff;
2890
+ meansL[ti * 4 + 3] = 0xff;
2891
+ meansU[ti * 4] = (x >> 8) & 0xff;
2892
+ meansU[ti * 4 + 1] = (y >> 8) & 0xff;
2893
+ meansU[ti * 4 + 2] = (z >> 8) & 0xff;
2894
+ meansU[ti * 4 + 3] = 0xff;
2895
+ }
2896
+ await write('means_l.webp', meansL);
2897
+ await write('means_u.webp', meansU);
2898
+ // convert quaternions
2899
+ const quats = new Uint8Array(width * height * channels);
2900
+ const quatNames = ['rot_0', 'rot_1', 'rot_2', 'rot_3'];
2901
+ const quatColumns = quatNames.map(name => dataTable.getColumnByName(name));
2902
+ const q = [0, 0, 0, 0];
2903
+ for (let i = 0; i < dataTable.numRows; ++i) {
2904
+ dataTable.getRow(sortIndices[i], row, quatColumns);
2905
+ q[0] = row.rot_0;
2906
+ q[1] = row.rot_1;
2907
+ q[2] = row.rot_2;
2908
+ q[3] = row.rot_3;
2909
+ const l = Math.sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
2910
+ // normalize
2911
+ q.forEach((v, j) => {
2912
+ q[j] = v / l;
2913
+ });
2914
+ // find max component
2915
+ const maxComp = q.reduce((v, _, i) => (Math.abs(q[i]) > Math.abs(q[v]) ? i : v), 0);
2916
+ // invert if max component is negative
2917
+ if (q[maxComp] < 0) {
2918
+ q.forEach((v, j) => {
2919
+ q[j] *= -1;
2920
+ });
2921
+ }
2922
+ // scale by sqrt(2) to fit in [-1, 1] range
2923
+ const sqrt2 = Math.sqrt(2);
2924
+ q.forEach((v, j) => {
2925
+ q[j] *= sqrt2;
2926
+ });
2927
+ const idx = [
2928
+ [1, 2, 3],
2929
+ [0, 2, 3],
2930
+ [0, 1, 3],
2931
+ [0, 1, 2]
2932
+ ][maxComp];
2933
+ const ti = target(i, width);
2934
+ quats[ti * 4] = 255 * (q[idx[0]] * 0.5 + 0.5);
2935
+ quats[ti * 4 + 1] = 255 * (q[idx[1]] * 0.5 + 0.5);
2936
+ quats[ti * 4 + 2] = 255 * (q[idx[2]] * 0.5 + 0.5);
2937
+ quats[ti * 4 + 3] = 252 + maxComp;
2938
+ }
2939
+ await write('quats.webp', quats);
2940
+ // scales
2941
+ const scales = new Uint8Array(width * height * channels);
2942
+ const scaleNames = ['scale_0', 'scale_1', 'scale_2'];
2943
+ const scaleColumns = scaleNames.map(name => dataTable.getColumnByName(name));
2944
+ const scaleMinMax = calcMinMax(dataTable, scaleNames);
2945
+ for (let i = 0; i < dataTable.numRows; ++i) {
2946
+ dataTable.getRow(sortIndices[i], row, scaleColumns);
2947
+ const ti = target(i, width);
2948
+ scales[ti * 4] = 255 * (row.scale_0 - scaleMinMax[0][0]) / (scaleMinMax[0][1] - scaleMinMax[0][0]);
2949
+ scales[ti * 4 + 1] = 255 * (row.scale_1 - scaleMinMax[1][0]) / (scaleMinMax[1][1] - scaleMinMax[1][0]);
2950
+ scales[ti * 4 + 2] = 255 * (row.scale_2 - scaleMinMax[2][0]) / (scaleMinMax[2][1] - scaleMinMax[2][0]);
2951
+ scales[ti * 4 + 3] = 0xff;
2952
+ }
2953
+ await write('scales.webp', scales);
2954
+ // colors
2955
+ const sh0 = new Uint8Array(width * height * channels);
2956
+ const sh0Names = ['f_dc_0', 'f_dc_1', 'f_dc_2', 'opacity'];
2957
+ const sh0Columns = sh0Names.map(name => dataTable.getColumnByName(name));
2958
+ const sh0MinMax = calcMinMax(dataTable, sh0Names);
2959
+ for (let i = 0; i < dataTable.numRows; ++i) {
2960
+ dataTable.getRow(sortIndices[i], row, sh0Columns);
2961
+ const ti = target(i, width);
2962
+ sh0[ti * 4] = 255 * (row.f_dc_0 - sh0MinMax[0][0]) / (sh0MinMax[0][1] - sh0MinMax[0][0]);
2963
+ sh0[ti * 4 + 1] = 255 * (row.f_dc_1 - sh0MinMax[1][0]) / (sh0MinMax[1][1] - sh0MinMax[1][0]);
2964
+ sh0[ti * 4 + 2] = 255 * (row.f_dc_2 - sh0MinMax[2][0]) / (sh0MinMax[2][1] - sh0MinMax[2][0]);
2965
+ sh0[ti * 4 + 3] = 255 * (row.opacity - sh0MinMax[3][0]) / (sh0MinMax[3][1] - sh0MinMax[3][0]);
2966
+ }
2967
+ await write('sh0.webp', sh0);
2968
+ // write meta.json
2969
+ const meta = {
2970
+ means: {
2971
+ shape: [numRows, 3],
2972
+ dtype: 'float32',
2973
+ mins: meansMinMax.map(v => v[0]),
2974
+ maxs: meansMinMax.map(v => v[1]),
2975
+ files: [
2976
+ 'means_l.webp',
2977
+ 'means_u.webp'
2978
+ ]
2979
+ },
2980
+ scales: {
2981
+ shape: [numRows, 3],
2982
+ dtype: 'float32',
2983
+ mins: scaleMinMax.map(v => v[0]),
2984
+ maxs: scaleMinMax.map(v => v[1]),
2985
+ files: ['scales.webp']
2986
+ },
2987
+ quats: {
2988
+ shape: [numRows, 4],
2989
+ dtype: 'uint8',
2990
+ encoding: 'quaternion_packed',
2991
+ files: ['quats.webp']
2992
+ },
2993
+ sh0: {
2994
+ shape: [numRows, 1, 4],
2995
+ dtype: 'float32',
2996
+ mins: sh0MinMax.map(v => v[0]),
2997
+ maxs: sh0MinMax.map(v => v[1]),
2998
+ files: ['sh0.webp']
2999
+ }
3000
+ };
3001
+ const outputFile = await open(outputFilename, 'w');
3002
+ await outputFile.write((new TextEncoder()).encode(JSON.stringify(meta, null, 4)));
3003
+ await outputFile.close();
3004
+ };
3005
+
3006
+ const readFile = async (filename) => {
3007
+ console.log(`reading '${filename}'...`);
1093
3008
  const inputFile = await open(filename, 'r');
1094
- // read contents
1095
- console.log(`reading contents...`);
1096
- const plyFile = await readPly(inputFile);
1097
- // close file
3009
+ const plyData = await readPly(inputFile);
1098
3010
  await inputFile.close();
1099
- return plyFile;
3011
+ return plyData;
1100
3012
  };
1101
- const processData = (plyFile) => {
1102
- // check we have the necessary elements for processing
3013
+ const writeFile = async (filename, dataTable) => {
3014
+ if (filename.endsWith('.json')) {
3015
+ await writeSogs(filename, dataTable);
3016
+ }
3017
+ else if (filename.endsWith('.compressed.ply')) {
3018
+ console.log(`writing '${filename}'...`);
3019
+ const outputFile = await open(filename, 'w');
3020
+ await writeCompressedPly(outputFile, dataTable);
3021
+ await outputFile.close();
3022
+ }
3023
+ else {
3024
+ console.log(`writing '${filename}'...`);
3025
+ const outputFile = await open(filename, 'w');
3026
+ await writePly(outputFile, {
3027
+ comments: [],
3028
+ elements: [{
3029
+ name: 'vertex',
3030
+ dataTable: dataTable
3031
+ }]
3032
+ });
3033
+ await outputFile.close();
3034
+ }
1103
3035
  };
1104
- const writeData = async (filename, plyFile) => {
1105
- const outputFile = await open(filename, 'w');
1106
- await writeCompressedPly(outputFile, new Splat(plyFile));
1107
- await outputFile.close();
3036
+ // combine multiple tables into one
3037
+ // columns with matching name and type are combined
3038
+ const combine = (dataTables) => {
3039
+ if (dataTables.length === 1) {
3040
+ // nothing to combine
3041
+ return dataTables[0];
3042
+ }
3043
+ const findMatchingColumn = (columns, column) => {
3044
+ for (let i = 0; i < columns.length; ++i) {
3045
+ if (columns[i].name === column.name &&
3046
+ columns[i].dataType === column.dataType) {
3047
+ return columns[i];
3048
+ }
3049
+ }
3050
+ return null;
3051
+ };
3052
+ // make unique list of columns where name and type much match
3053
+ const columns = dataTables[0].columns.slice();
3054
+ for (let i = 1; i < dataTables.length; ++i) {
3055
+ const dataTable = dataTables[i];
3056
+ for (let j = 0; j < dataTable.columns.length; ++j) {
3057
+ if (!findMatchingColumn(columns, dataTable.columns[j])) {
3058
+ columns.push(dataTable.columns[j]);
3059
+ }
3060
+ }
3061
+ }
3062
+ // count total number of rows
3063
+ const totalRows = dataTables.reduce((sum, dataTable) => sum + dataTable.numRows, 0);
3064
+ // construct output dataTable
3065
+ const resultColumns = columns.map((column) => {
3066
+ const constructor = column.data.constructor;
3067
+ return new Column(column.name, new constructor(totalRows));
3068
+ });
3069
+ const result = new DataTable(resultColumns);
3070
+ // copy data
3071
+ let rowOffset = 0;
3072
+ for (let i = 0; i < dataTables.length; ++i) {
3073
+ const dataTable = dataTables[i];
3074
+ for (let j = 0; j < dataTable.columns.length; ++j) {
3075
+ const column = dataTable.columns[j];
3076
+ const targetColumn = findMatchingColumn(result.columns, column);
3077
+ targetColumn.data.set(column.data, rowOffset);
3078
+ }
3079
+ rowOffset += dataTable.numRows;
3080
+ }
3081
+ return result;
3082
+ };
3083
+ const isGSDataTable = (dataTable) => {
3084
+ if (![
3085
+ 'x', 'y', 'z',
3086
+ 'rot_0', 'rot_1', 'rot_2', 'rot_3',
3087
+ 'scale_0', 'scale_1', 'scale_2',
3088
+ 'f_dc_0', 'f_dc_1', 'f_dc_2',
3089
+ 'opacity'
3090
+ ].every(c => dataTable.hasColumn(c))) {
3091
+ return false;
3092
+ }
3093
+ return true;
3094
+ };
3095
+ const parseArguments = () => {
3096
+ const { values: v, tokens } = parseArgs({
3097
+ tokens: true,
3098
+ strict: true,
3099
+ allowPositionals: true,
3100
+ options: {
3101
+ translate: { type: 'string', short: 't', multiple: true },
3102
+ rotate: { type: 'string', short: 'r', multiple: true },
3103
+ scale: { type: 'string', short: 's', multiple: true },
3104
+ filterNaN: { type: 'boolean', short: 'n', multiple: true },
3105
+ filterByValue: { type: 'string', short: 'c', multiple: true },
3106
+ filterBands: { type: 'string', short: 'h', multiple: true }
3107
+ }
3108
+ });
3109
+ const parseNumber = (value) => {
3110
+ const result = Number(value);
3111
+ if (isNaN(result)) {
3112
+ throw new Error(`Invalid number value: ${value}`);
3113
+ }
3114
+ return result;
3115
+ };
3116
+ const parseVec3 = (value) => {
3117
+ const parts = value.split(',').map(Number);
3118
+ if (parts.length !== 3 || parts.some(isNaN)) {
3119
+ throw new Error(`Invalid Vec3 value: ${value}`);
3120
+ }
3121
+ return new Vec3(parts[0], parts[1], parts[2]);
3122
+ };
3123
+ const parseComparator = (value) => {
3124
+ switch (value) {
3125
+ case 'lt': return 'lt';
3126
+ case 'lte': return 'lte';
3127
+ case 'gt': return 'gt';
3128
+ case 'gte': return 'gte';
3129
+ case 'eq': return 'eq';
3130
+ case 'neq': return 'neq';
3131
+ default:
3132
+ throw new Error(`Invalid comparator value: ${value}`);
3133
+ }
3134
+ };
3135
+ const files = [];
3136
+ for (const t of tokens) {
3137
+ if (t.kind === 'positional') {
3138
+ files.push({
3139
+ filename: t.value,
3140
+ processActions: []
3141
+ });
3142
+ }
3143
+ else if (t.kind === 'option' && files.length > 0) {
3144
+ const current = files[files.length - 1];
3145
+ switch (t.name) {
3146
+ case 'translate':
3147
+ current.processActions.push({
3148
+ kind: 'translate',
3149
+ value: parseVec3(t.value)
3150
+ });
3151
+ break;
3152
+ case 'rotate':
3153
+ current.processActions.push({
3154
+ kind: 'rotate',
3155
+ value: parseVec3(t.value)
3156
+ });
3157
+ break;
3158
+ case 'scale':
3159
+ current.processActions.push({
3160
+ kind: 'scale',
3161
+ value: parseNumber(t.value)
3162
+ });
3163
+ break;
3164
+ case 'filterNaN':
3165
+ current.processActions.push({
3166
+ kind: 'filterNaN'
3167
+ });
3168
+ break;
3169
+ case 'filterByValue': {
3170
+ const parts = t.value.split(',').map(p => p.trim());
3171
+ if (parts.length !== 3) {
3172
+ throw new Error(`Invalid filterByValue value: ${t.value}`);
3173
+ }
3174
+ current.processActions.push({
3175
+ kind: 'filterByValue',
3176
+ columnName: parts[0],
3177
+ comparator: parseComparator(parts[1]),
3178
+ value: parseNumber(parts[2])
3179
+ });
3180
+ break;
3181
+ }
3182
+ case 'filterBands': {
3183
+ const shBands = parseNumber(t.value);
3184
+ if (![0, 1, 2, 3].includes(shBands)) {
3185
+ throw new Error(`Invalid filterBands value: ${t.value}. Must be 0, 1, 2, or 3.`);
3186
+ }
3187
+ current.processActions.push({
3188
+ kind: 'filterBands',
3189
+ value: shBands
3190
+ });
3191
+ break;
3192
+ }
3193
+ }
3194
+ }
3195
+ }
3196
+ return files;
1108
3197
  };
3198
+ const usage = `Usage: splat-transform input.ply [actions] input.ply [actions] ... output.ply [actions]
3199
+ actions:
3200
+ -translate -t x,y,z Translate splats by (x, y, z)
3201
+ -rotate -r x,y,z Rotate splats by euler angles (x, y, z) (in degrees)
3202
+ -scale -s x Scale splats by x (uniform scaling)
3203
+ -filterNaN -n Remove gaussians containing any NaN or Inf value
3204
+ -filterByValue -c name,comparator,value Filter gaussians by a value. Specify the value name, comparator (lt, lte, gt, gte, eq, neq) and value
3205
+ -filterBands -h 1 Filter spherical harmonic band data. Value must be 0, 1, 2 or 3.
3206
+ `;
1109
3207
  const main = async () => {
1110
3208
  console.log(`splat-transform v${version}`);
1111
- if (process.argv.length < 3) {
1112
- console.error('Usage: splat-transform <input-file> <output-file>');
1113
- process.exit(1);
3209
+ // read args
3210
+ const files = parseArguments();
3211
+ if (files.length < 2) {
3212
+ console.error(usage);
3213
+ exit(1);
1114
3214
  }
1115
- const inputFilename = process.argv[2];
1116
- const outputFilename = process.argv[3];
3215
+ const inputArgs = files.slice(0, -1);
3216
+ const outputArg = files[files.length - 1];
1117
3217
  try {
1118
- // open input
1119
- const plyFile = await readData(inputFilename);
1120
- // process
1121
- processData(plyFile);
1122
- // write
1123
- await writeData(outputFilename, plyFile);
3218
+ // read, filter, process input files
3219
+ const inputFiles = await Promise.all(inputArgs.map(async (inputArg) => {
3220
+ const file = await readFile(resolve(inputArg.filename));
3221
+ // filter out non-gs files
3222
+ if (file.elements.length !== 1) {
3223
+ return null;
3224
+ }
3225
+ const element = file.elements[0];
3226
+ if (element.name !== 'vertex') {
3227
+ return null;
3228
+ }
3229
+ const { dataTable } = element;
3230
+ if (dataTable.numRows === 0 || !isGSDataTable(dataTable)) {
3231
+ return null;
3232
+ }
3233
+ file.elements[0].dataTable = process(dataTable, inputArg.processActions);
3234
+ return file;
3235
+ }));
3236
+ // combine inputs into a single output dataTable
3237
+ const dataTable = process(combine(inputFiles.map(file => file.elements[0].dataTable)), outputArg.processActions);
3238
+ // write file
3239
+ await writeFile(resolve(outputArg.filename), dataTable);
1124
3240
  }
1125
3241
  catch (err) {
1126
3242
  // handle errors
1127
3243
  console.error(`error: ${err.message}`);
1128
- process.exit(1);
3244
+ exit(1);
1129
3245
  }
1130
3246
  console.log('done');
1131
3247
  };