@manycore/aholo-splat-transform 1.2.6
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/CHANGELOG.md +102 -0
- package/README.md +33 -0
- package/bin/cli.js +118 -0
- package/dist/SplatData.d.ts +67 -0
- package/dist/SplatData.js +156 -0
- package/dist/constant.d.ts +3 -0
- package/dist/constant.js +13 -0
- package/dist/file/IFile.d.ts +5 -0
- package/dist/file/IFile.js +1 -0
- package/dist/file/index.d.ts +7 -0
- package/dist/file/index.js +6 -0
- package/dist/file/ksplat.d.ts +12 -0
- package/dist/file/ksplat.js +232 -0
- package/dist/file/lcc.d.ts +11 -0
- package/dist/file/lcc.js +157 -0
- package/dist/file/ply.d.ts +13 -0
- package/dist/file/ply.js +388 -0
- package/dist/file/sog.d.ts +80 -0
- package/dist/file/sog.js +504 -0
- package/dist/file/splat.d.ts +6 -0
- package/dist/file/splat.js +99 -0
- package/dist/file/spz.d.ts +8 -0
- package/dist/file/spz.js +400 -0
- package/dist/file/voxel.d.ts +37 -0
- package/dist/file/voxel.js +280 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +54 -0
- package/dist/native/cpp/bin/linux/binding.node +0 -0
- package/dist/native/cpp/bin/windows/binding.node +0 -0
- package/dist/native/index.d.ts +54 -0
- package/dist/native/index.js +128 -0
- package/dist/tasks/AutoChunkLodTask.d.ts +13 -0
- package/dist/tasks/AutoChunkLodTask.js +117 -0
- package/dist/tasks/AutoLodTask.d.ts +10 -0
- package/dist/tasks/AutoLodTask.js +20 -0
- package/dist/tasks/BaseTask.d.ts +15 -0
- package/dist/tasks/BaseTask.js +5 -0
- package/dist/tasks/FlexLodTask.d.ts +12 -0
- package/dist/tasks/FlexLodTask.js +44 -0
- package/dist/tasks/ModifyTask.d.ts +9 -0
- package/dist/tasks/ModifyTask.js +156 -0
- package/dist/tasks/ReadTask.d.ts +8 -0
- package/dist/tasks/ReadTask.js +29 -0
- package/dist/tasks/SkeletonLodTask.d.ts +10 -0
- package/dist/tasks/SkeletonLodTask.js +156 -0
- package/dist/tasks/VoxelTask.d.ts +30 -0
- package/dist/tasks/VoxelTask.js +37 -0
- package/dist/tasks/WriteTask.d.ts +11 -0
- package/dist/tasks/WriteTask.js +70 -0
- package/dist/utils/BufferReader.d.ts +12 -0
- package/dist/utils/BufferReader.js +47 -0
- package/dist/utils/Logger.d.ts +11 -0
- package/dist/utils/Logger.js +38 -0
- package/dist/utils/StreamChunkDecoder.d.ts +16 -0
- package/dist/utils/StreamChunkDecoder.js +36 -0
- package/dist/utils/index.d.ts +27 -0
- package/dist/utils/index.js +101 -0
- package/dist/utils/k-means.d.ts +4 -0
- package/dist/utils/k-means.js +350 -0
- package/dist/utils/math.d.ts +46 -0
- package/dist/utils/math.js +351 -0
- package/dist/utils/quantize-1d.d.ts +4 -0
- package/dist/utils/quantize-1d.js +164 -0
- package/dist/utils/sh-rotate.d.ts +2 -0
- package/dist/utils/sh-rotate.js +175 -0
- package/dist/utils/splat.d.ts +20 -0
- package/dist/utils/splat.js +378 -0
- package/dist/utils/voxel/common.d.ts +162 -0
- package/dist/utils/voxel/common.js +1700 -0
- package/dist/utils/voxel/coplanar-merge.d.ts +63 -0
- package/dist/utils/voxel/coplanar-merge.js +819 -0
- package/dist/utils/voxel/gpu-dilation.d.ts +2 -0
- package/dist/utils/voxel/gpu-dilation.js +665 -0
- package/dist/utils/voxel/marching-cubes.d.ts +42 -0
- package/dist/utils/voxel/marching-cubes.js +1657 -0
- package/dist/utils/voxel/mesh.d.ts +3 -0
- package/dist/utils/voxel/mesh.js +130 -0
- package/dist/utils/voxel/nav.d.ts +29 -0
- package/dist/utils/voxel/nav.js +1043 -0
- package/dist/utils/voxel/postprocess.d.ts +23 -0
- package/dist/utils/voxel/postprocess.js +375 -0
- package/dist/utils/voxel/voxel-faces.d.ts +18 -0
- package/dist/utils/voxel/voxel-faces.js +663 -0
- package/dist/utils/voxel/voxelize.d.ts +33 -0
- package/dist/utils/voxel/voxelize.js +1193 -0
- package/dist/utils/webgpu.d.ts +8 -0
- package/dist/utils/webgpu.js +122 -0
- package/package.json +32 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
export class Quaternion {
|
|
2
|
+
x;
|
|
3
|
+
y;
|
|
4
|
+
z;
|
|
5
|
+
w;
|
|
6
|
+
static ONE = new Quaternion(0, 0, 0, 1);
|
|
7
|
+
constructor(x, y, z, w) {
|
|
8
|
+
this.x = x;
|
|
9
|
+
this.y = y;
|
|
10
|
+
this.z = z;
|
|
11
|
+
this.w = w;
|
|
12
|
+
}
|
|
13
|
+
set(x, y, z, w) {
|
|
14
|
+
this.x = x;
|
|
15
|
+
this.y = y;
|
|
16
|
+
this.z = z;
|
|
17
|
+
this.w = w;
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
equals(q) {
|
|
21
|
+
return (q.x === this.x) && (q.y === this.y) && (q.z === this.z) && (q.w === this.w);
|
|
22
|
+
}
|
|
23
|
+
normalize() {
|
|
24
|
+
const length = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
|
|
25
|
+
if (length === 0) {
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
const invLength = (1 / length) * (this.w < 0 ? -1 : 1);
|
|
29
|
+
this.x *= invLength;
|
|
30
|
+
this.y *= invLength;
|
|
31
|
+
this.z *= invLength;
|
|
32
|
+
this.w *= invLength;
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
multiply(q) {
|
|
36
|
+
return this.multiplyQuaternions(this, q);
|
|
37
|
+
}
|
|
38
|
+
premultiply(q) {
|
|
39
|
+
return this.multiplyQuaternions(q, this);
|
|
40
|
+
}
|
|
41
|
+
multiplyQuaternions(a, b) {
|
|
42
|
+
// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
|
|
43
|
+
const qax = a.x;
|
|
44
|
+
const qay = a.y;
|
|
45
|
+
const qaz = a.z;
|
|
46
|
+
const qaw = a.w;
|
|
47
|
+
const qbx = b.x;
|
|
48
|
+
const qby = b.y;
|
|
49
|
+
const qbz = b.z;
|
|
50
|
+
const qbw = b.w;
|
|
51
|
+
this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
|
|
52
|
+
this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
|
|
53
|
+
this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
|
|
54
|
+
this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
setRotationMatrix(m) {
|
|
58
|
+
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
|
|
59
|
+
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
|
|
60
|
+
const te = m.elements;
|
|
61
|
+
const m11 = te[0];
|
|
62
|
+
const m12 = te[4];
|
|
63
|
+
const m13 = te[8];
|
|
64
|
+
const m21 = te[1];
|
|
65
|
+
const m22 = te[5];
|
|
66
|
+
const m23 = te[9];
|
|
67
|
+
const m31 = te[2];
|
|
68
|
+
const m32 = te[6];
|
|
69
|
+
const m33 = te[10];
|
|
70
|
+
const trace = m11 + m22 + m33;
|
|
71
|
+
let s;
|
|
72
|
+
if (trace > 0) {
|
|
73
|
+
s = 0.5 / Math.sqrt(trace + 1.0);
|
|
74
|
+
this.w = 0.25 / s;
|
|
75
|
+
this.x = (m32 - m23) * s;
|
|
76
|
+
this.y = (m13 - m31) * s;
|
|
77
|
+
this.z = (m21 - m12) * s;
|
|
78
|
+
}
|
|
79
|
+
else if (m11 > m22 && m11 > m33) {
|
|
80
|
+
s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33);
|
|
81
|
+
this.w = (m32 - m23) / s;
|
|
82
|
+
this.x = 0.25 * s;
|
|
83
|
+
this.y = (m12 + m21) / s;
|
|
84
|
+
this.z = (m13 + m31) / s;
|
|
85
|
+
}
|
|
86
|
+
else if (m22 > m33) {
|
|
87
|
+
s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33);
|
|
88
|
+
this.w = (m13 - m31) / s;
|
|
89
|
+
this.x = (m12 + m21) / s;
|
|
90
|
+
this.y = 0.25 * s;
|
|
91
|
+
this.z = (m23 + m32) / s;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22);
|
|
95
|
+
this.w = (m21 - m12) / s;
|
|
96
|
+
this.x = (m13 + m31) / s;
|
|
97
|
+
this.y = (m23 + m32) / s;
|
|
98
|
+
this.z = 0.25 * s;
|
|
99
|
+
}
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export class Vector3 {
|
|
104
|
+
x;
|
|
105
|
+
y;
|
|
106
|
+
z;
|
|
107
|
+
static ONE = new Vector3(1, 1, 1);
|
|
108
|
+
constructor(x, y, z) {
|
|
109
|
+
this.x = x;
|
|
110
|
+
this.y = y;
|
|
111
|
+
this.z = z;
|
|
112
|
+
}
|
|
113
|
+
set(x, y, z) {
|
|
114
|
+
this.x = x;
|
|
115
|
+
this.y = y;
|
|
116
|
+
this.z = z;
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
length() {
|
|
120
|
+
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
|
121
|
+
}
|
|
122
|
+
equals(v) {
|
|
123
|
+
return (v.x === this.x) && (v.y === this.y) && (v.z === this.z);
|
|
124
|
+
}
|
|
125
|
+
mul(v) {
|
|
126
|
+
this.x *= v.x;
|
|
127
|
+
this.y *= v.y;
|
|
128
|
+
this.z *= v.z;
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
applyMatrix4(m) {
|
|
132
|
+
const { x, y, z } = this;
|
|
133
|
+
const e = m.elements;
|
|
134
|
+
const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]);
|
|
135
|
+
this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w;
|
|
136
|
+
this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w;
|
|
137
|
+
this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w;
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
clone() {
|
|
141
|
+
return new Vector3(this.x, this.y, this.z);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export class Matrix4 {
|
|
145
|
+
static ONE = new Matrix4();
|
|
146
|
+
elements = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
147
|
+
constructor(elements, isRow = false) {
|
|
148
|
+
if (elements) {
|
|
149
|
+
this.set(elements);
|
|
150
|
+
}
|
|
151
|
+
if (isRow) {
|
|
152
|
+
this.transpose();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
set(elements) {
|
|
156
|
+
for (let i = 0; i < 16; i++) {
|
|
157
|
+
this.elements[i] = elements[i];
|
|
158
|
+
}
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
equals(matrix) {
|
|
162
|
+
const te = this.elements;
|
|
163
|
+
const me = matrix.elements;
|
|
164
|
+
for (let i = 0; i < 16; i++) {
|
|
165
|
+
if (te[i] !== me[i]) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
determinant() {
|
|
172
|
+
const te = this.elements;
|
|
173
|
+
const n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12], n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13], n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14], n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15];
|
|
174
|
+
// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
|
|
175
|
+
return (n41 * (+n14 * n23 * n32
|
|
176
|
+
- n13 * n24 * n32
|
|
177
|
+
- n14 * n22 * n33
|
|
178
|
+
+ n12 * n24 * n33
|
|
179
|
+
+ n13 * n22 * n34
|
|
180
|
+
- n12 * n23 * n34) +
|
|
181
|
+
n42 * (+n11 * n23 * n34
|
|
182
|
+
- n11 * n24 * n33
|
|
183
|
+
+ n14 * n21 * n33
|
|
184
|
+
- n13 * n21 * n34
|
|
185
|
+
+ n13 * n24 * n31
|
|
186
|
+
- n14 * n23 * n31) +
|
|
187
|
+
n43 * (+n11 * n24 * n32
|
|
188
|
+
- n11 * n22 * n34
|
|
189
|
+
- n14 * n21 * n32
|
|
190
|
+
+ n12 * n21 * n34
|
|
191
|
+
+ n14 * n22 * n31
|
|
192
|
+
- n12 * n24 * n31) +
|
|
193
|
+
n44 * (-n13 * n22 * n31
|
|
194
|
+
- n11 * n23 * n32
|
|
195
|
+
+ n11 * n22 * n33
|
|
196
|
+
+ n13 * n21 * n32
|
|
197
|
+
- n12 * n21 * n33
|
|
198
|
+
+ n12 * n23 * n31));
|
|
199
|
+
}
|
|
200
|
+
multiply(m) {
|
|
201
|
+
return this.multiplyMatrices(this, m);
|
|
202
|
+
}
|
|
203
|
+
multiplyMatrices(a, b) {
|
|
204
|
+
const te = this.elements;
|
|
205
|
+
const ae = a.elements;
|
|
206
|
+
const be = b.elements;
|
|
207
|
+
const a11 = ae[0];
|
|
208
|
+
const a12 = ae[4];
|
|
209
|
+
const a13 = ae[8];
|
|
210
|
+
const a14 = ae[12];
|
|
211
|
+
const a21 = ae[1];
|
|
212
|
+
const a22 = ae[5];
|
|
213
|
+
const a23 = ae[9];
|
|
214
|
+
const a24 = ae[13];
|
|
215
|
+
const a31 = ae[2];
|
|
216
|
+
const a32 = ae[6];
|
|
217
|
+
const a33 = ae[10];
|
|
218
|
+
const a34 = ae[14];
|
|
219
|
+
const a41 = ae[3];
|
|
220
|
+
const a42 = ae[7];
|
|
221
|
+
const a43 = ae[11];
|
|
222
|
+
const a44 = ae[15];
|
|
223
|
+
const b11 = be[0];
|
|
224
|
+
const b12 = be[4];
|
|
225
|
+
const b13 = be[8];
|
|
226
|
+
const b14 = be[12];
|
|
227
|
+
const b21 = be[1];
|
|
228
|
+
const b22 = be[5];
|
|
229
|
+
const b23 = be[9];
|
|
230
|
+
const b24 = be[13];
|
|
231
|
+
const b31 = be[2];
|
|
232
|
+
const b32 = be[6];
|
|
233
|
+
const b33 = be[10];
|
|
234
|
+
const b34 = be[14];
|
|
235
|
+
const b41 = be[3];
|
|
236
|
+
const b42 = be[7];
|
|
237
|
+
const b43 = be[11];
|
|
238
|
+
const b44 = be[15];
|
|
239
|
+
te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
|
|
240
|
+
te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
|
|
241
|
+
te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
|
|
242
|
+
te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
|
|
243
|
+
te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
|
|
244
|
+
te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
|
|
245
|
+
te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
|
|
246
|
+
te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
|
|
247
|
+
te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
|
|
248
|
+
te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
|
|
249
|
+
te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
|
|
250
|
+
te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
|
|
251
|
+
te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
|
|
252
|
+
te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
|
|
253
|
+
te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
|
|
254
|
+
te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
|
|
255
|
+
return this;
|
|
256
|
+
}
|
|
257
|
+
compose(position, quaternion, scale) {
|
|
258
|
+
const te = this.elements;
|
|
259
|
+
const x = quaternion.x, y = quaternion.y, z = quaternion.z, w = quaternion.w, x2 = x + x, y2 = y + y, z2 = z + z, xx = x * x2, xy = x * y2, xz = x * z2, yy = y * y2, yz = y * z2, zz = z * z2, wx = w * x2, wy = w * y2, wz = w * z2, sx = scale.x, sy = scale.y, sz = scale.z;
|
|
260
|
+
te[0] = (1 - (yy + zz)) * sx;
|
|
261
|
+
te[1] = (xy + wz) * sx;
|
|
262
|
+
te[2] = (xz - wy) * sx;
|
|
263
|
+
te[3] = 0;
|
|
264
|
+
te[4] = (xy - wz) * sy;
|
|
265
|
+
te[5] = (1 - (xx + zz)) * sy;
|
|
266
|
+
te[6] = (yz + wx) * sy;
|
|
267
|
+
te[7] = 0;
|
|
268
|
+
te[8] = (xz + wy) * sz;
|
|
269
|
+
te[9] = (yz - wx) * sz;
|
|
270
|
+
te[10] = (1 - (xx + yy)) * sz;
|
|
271
|
+
te[11] = 0;
|
|
272
|
+
te[12] = position.x;
|
|
273
|
+
te[13] = position.y;
|
|
274
|
+
te[14] = position.z;
|
|
275
|
+
te[15] = 1;
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
278
|
+
decompose(position, quaternion, scale) {
|
|
279
|
+
const te = this.elements;
|
|
280
|
+
let sx = tmpVec.set(te[0], te[1], te[2]).length();
|
|
281
|
+
const sy = tmpVec.set(te[4], te[5], te[6]).length();
|
|
282
|
+
const sz = tmpVec.set(te[8], te[9], te[10]).length();
|
|
283
|
+
const det = this.determinant();
|
|
284
|
+
if (det < 0) {
|
|
285
|
+
sx *= -1;
|
|
286
|
+
}
|
|
287
|
+
position.set(te[12], te[13], te[14]);
|
|
288
|
+
const invSX = 1 / sx;
|
|
289
|
+
const invSY = 1 / sy;
|
|
290
|
+
const invSZ = 1 / sz;
|
|
291
|
+
tmpMat4.set(this.elements);
|
|
292
|
+
tmpMat4.elements[0] *= invSX;
|
|
293
|
+
tmpMat4.elements[1] *= invSX;
|
|
294
|
+
tmpMat4.elements[2] *= invSX;
|
|
295
|
+
tmpMat4.elements[4] *= invSY;
|
|
296
|
+
tmpMat4.elements[5] *= invSY;
|
|
297
|
+
tmpMat4.elements[6] *= invSY;
|
|
298
|
+
tmpMat4.elements[8] *= invSZ;
|
|
299
|
+
tmpMat4.elements[9] *= invSZ;
|
|
300
|
+
tmpMat4.elements[10] *= invSZ;
|
|
301
|
+
quaternion.setRotationMatrix(tmpMat4);
|
|
302
|
+
scale.set(sx, sy, sz);
|
|
303
|
+
return this;
|
|
304
|
+
}
|
|
305
|
+
transpose() {
|
|
306
|
+
const te = this.elements;
|
|
307
|
+
let tmp;
|
|
308
|
+
tmp = te[1];
|
|
309
|
+
te[1] = te[4];
|
|
310
|
+
te[4] = tmp;
|
|
311
|
+
tmp = te[2];
|
|
312
|
+
te[2] = te[8];
|
|
313
|
+
te[8] = tmp;
|
|
314
|
+
tmp = te[6];
|
|
315
|
+
te[6] = te[9];
|
|
316
|
+
te[9] = tmp;
|
|
317
|
+
tmp = te[3];
|
|
318
|
+
te[3] = te[12];
|
|
319
|
+
te[12] = tmp;
|
|
320
|
+
tmp = te[7];
|
|
321
|
+
te[7] = te[13];
|
|
322
|
+
te[13] = tmp;
|
|
323
|
+
tmp = te[11];
|
|
324
|
+
te[11] = te[14];
|
|
325
|
+
te[14] = tmp;
|
|
326
|
+
return this;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
export class Matrix3 {
|
|
330
|
+
elements = new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]);
|
|
331
|
+
set(n11, n12, n13, n21, n22, n23, n31, n32, n33) {
|
|
332
|
+
const te = this.elements;
|
|
333
|
+
te[0] = n11;
|
|
334
|
+
te[1] = n21;
|
|
335
|
+
te[2] = n31;
|
|
336
|
+
te[3] = n12;
|
|
337
|
+
te[4] = n22;
|
|
338
|
+
te[5] = n32;
|
|
339
|
+
te[6] = n13;
|
|
340
|
+
te[7] = n23;
|
|
341
|
+
te[8] = n33;
|
|
342
|
+
return this;
|
|
343
|
+
}
|
|
344
|
+
setFromMatrix4(m) {
|
|
345
|
+
const me = m.elements;
|
|
346
|
+
this.set(me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]);
|
|
347
|
+
return this;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const tmpVec = new Vector3(0, 0, 0);
|
|
351
|
+
const tmpMat4 = new Matrix4();
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// https://github.com/playcanvas/splat-transform/blob/main/src/lib/spatial/quantize-1d.ts
|
|
2
|
+
export function quantize1d(fields, k = 256, alpha = 0.5, transform) {
|
|
3
|
+
const numColumns = fields.length;
|
|
4
|
+
const numRows = fields.length > 0 ? fields[0].length : 0;
|
|
5
|
+
// pool all columns into a flat 1D array
|
|
6
|
+
const N = numRows * numColumns;
|
|
7
|
+
if (N === 0) {
|
|
8
|
+
const centroids = new Float32Array(k);
|
|
9
|
+
const labels = fields.map(_ => new Uint8Array(numRows));
|
|
10
|
+
return { centroids, labels };
|
|
11
|
+
}
|
|
12
|
+
const data = new Float32Array(N);
|
|
13
|
+
for (let i = 0; i < numColumns; ++i) {
|
|
14
|
+
data.set(fields[i], i * numRows);
|
|
15
|
+
}
|
|
16
|
+
if (transform) {
|
|
17
|
+
for (let i = 0; i < data.length; i++) {
|
|
18
|
+
data[i] = transform(data[i]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// sort a copy for histogram binning (keep original for label assignment)
|
|
22
|
+
const sortedData = new Float32Array(data);
|
|
23
|
+
sortedData.sort();
|
|
24
|
+
const vMin = sortedData[0];
|
|
25
|
+
const vMax = sortedData[N - 1];
|
|
26
|
+
// handle degenerate case where all values are identical
|
|
27
|
+
if (vMax - vMin < 1e-20) {
|
|
28
|
+
const centroids = new Float32Array(k);
|
|
29
|
+
centroids.fill(vMin);
|
|
30
|
+
const labels = fields.map(_ => new Uint8Array(numRows));
|
|
31
|
+
return { centroids, labels };
|
|
32
|
+
}
|
|
33
|
+
// build histogram using blended uniform/quantile bin positions
|
|
34
|
+
const H = Math.min(1024, N);
|
|
35
|
+
const vRange = vMax - vMin;
|
|
36
|
+
// adaptive blend ratio: when outliers are extreme (IQR << range), lean
|
|
37
|
+
// strongly toward quantile to give the dense center adequate bins; when
|
|
38
|
+
// the distribution has moderate tails (IQR ~ range), reduce quantile
|
|
39
|
+
// bias somewhat, but keep at least 50% quantile to preserve density
|
|
40
|
+
const iqr = sortedData[Math.floor(N * 0.75)] - sortedData[Math.floor(N * 0.25)];
|
|
41
|
+
const beta = Math.max(0.5, Math.min(0.999, 1 - iqr / vRange));
|
|
42
|
+
const counts = new Float64Array(H);
|
|
43
|
+
const sums = new Float64Array(H);
|
|
44
|
+
for (let i = 0; i < N; ++i) {
|
|
45
|
+
const uniformPos = (sortedData[i] - vMin) / vRange;
|
|
46
|
+
const quantilePos = i / N;
|
|
47
|
+
const bin = Math.min(H - 1, Math.floor(H * (beta * quantilePos + (1 - beta) * uniformPos)));
|
|
48
|
+
counts[bin]++;
|
|
49
|
+
sums[bin] += sortedData[i];
|
|
50
|
+
}
|
|
51
|
+
const centers = new Float64Array(H);
|
|
52
|
+
for (let i = 0; i < H; ++i) {
|
|
53
|
+
centers[i] = counts[i] > 0 ? sums[i] / counts[i] : vMin + (i + 0.5) / H * vRange;
|
|
54
|
+
}
|
|
55
|
+
// compute weights: w = count^alpha (sub-linear density weighting)
|
|
56
|
+
const weights = new Float64Array(H);
|
|
57
|
+
for (let i = 0; i < H; ++i) {
|
|
58
|
+
weights[i] = counts[i] > 0 ? Math.pow(counts[i], alpha) : 0;
|
|
59
|
+
}
|
|
60
|
+
// prefix sums for O(1) range cost queries
|
|
61
|
+
// cost(a,b) = sum_wxx - sum_wx^2 / sum_w
|
|
62
|
+
// centroid(a,b) = sum_wx / sum_w
|
|
63
|
+
const prefW = new Float64Array(H + 1);
|
|
64
|
+
const prefWX = new Float64Array(H + 1);
|
|
65
|
+
const prefWXX = new Float64Array(H + 1);
|
|
66
|
+
for (let i = 0; i < H; ++i) {
|
|
67
|
+
prefW[i + 1] = prefW[i] + weights[i];
|
|
68
|
+
prefWX[i + 1] = prefWX[i] + weights[i] * centers[i];
|
|
69
|
+
prefWXX[i + 1] = prefWXX[i] + weights[i] * centers[i] * centers[i];
|
|
70
|
+
}
|
|
71
|
+
const rangeCost = (a, b) => {
|
|
72
|
+
const w = prefW[b + 1] - prefW[a];
|
|
73
|
+
if (w <= 0) {
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
const wx = prefWX[b + 1] - prefWX[a];
|
|
77
|
+
const wxx = prefWXX[b + 1] - prefWXX[a];
|
|
78
|
+
return wxx - (wx * wx) / w;
|
|
79
|
+
};
|
|
80
|
+
const rangeMean = (a, b) => {
|
|
81
|
+
const w = prefW[b + 1] - prefW[a];
|
|
82
|
+
if (w <= 0) {
|
|
83
|
+
return (centers[a] + centers[b]) * 0.5;
|
|
84
|
+
}
|
|
85
|
+
return (prefWX[b + 1] - prefWX[a]) / w;
|
|
86
|
+
};
|
|
87
|
+
const nonEmpty = counts.reduce((n, c) => n + (c > 0 ? 1 : 0), 0);
|
|
88
|
+
const effectiveK = Math.min(k, nonEmpty);
|
|
89
|
+
// DP: dp[m][j] = min weighted SSE of quantizing bins 0..j into m centroids
|
|
90
|
+
// Use two rows to save memory (only need previous row)
|
|
91
|
+
const INF = 1e30;
|
|
92
|
+
let dpPrev = new Float64Array(H).fill(INF);
|
|
93
|
+
let dpCurr = new Float64Array(H).fill(INF);
|
|
94
|
+
const splitTable = new Array(effectiveK + 1);
|
|
95
|
+
// base case: m = 1
|
|
96
|
+
const split1 = new Int32Array(H);
|
|
97
|
+
for (let j = 0; j < H; ++j) {
|
|
98
|
+
dpPrev[j] = rangeCost(0, j);
|
|
99
|
+
split1[j] = -1;
|
|
100
|
+
}
|
|
101
|
+
splitTable[1] = split1;
|
|
102
|
+
// fill DP for m = 2..effectiveK
|
|
103
|
+
for (let m = 2; m <= effectiveK; ++m) {
|
|
104
|
+
dpCurr.fill(INF);
|
|
105
|
+
const splitM = new Int32Array(H);
|
|
106
|
+
for (let j = m - 1; j < H; ++j) {
|
|
107
|
+
let bestCost = INF;
|
|
108
|
+
let bestS = m - 2;
|
|
109
|
+
for (let s = m - 2; s < j; ++s) {
|
|
110
|
+
const cost = dpPrev[s] + rangeCost(s + 1, j);
|
|
111
|
+
if (cost < bestCost) {
|
|
112
|
+
bestCost = cost;
|
|
113
|
+
bestS = s;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
dpCurr[j] = bestCost;
|
|
117
|
+
splitM[j] = bestS;
|
|
118
|
+
}
|
|
119
|
+
splitTable[m] = splitM;
|
|
120
|
+
// swap rows
|
|
121
|
+
const tmp = dpPrev;
|
|
122
|
+
dpPrev = dpCurr;
|
|
123
|
+
dpCurr = tmp;
|
|
124
|
+
}
|
|
125
|
+
// backtrack to find centroid values
|
|
126
|
+
const centroidValues = new Float32Array(effectiveK);
|
|
127
|
+
let j = H - 1;
|
|
128
|
+
for (let m = effectiveK; m >= 1; --m) {
|
|
129
|
+
const s = m > 1 ? splitTable[m][j] : -1;
|
|
130
|
+
centroidValues[m - 1] = rangeMean(s + 1, j);
|
|
131
|
+
j = s;
|
|
132
|
+
}
|
|
133
|
+
// sort centroids (should already be sorted, but ensure)
|
|
134
|
+
centroidValues.sort();
|
|
135
|
+
// pad to k entries if effectiveK < k (duplicate last centroid)
|
|
136
|
+
const finalCentroids = new Float32Array(k);
|
|
137
|
+
finalCentroids.set(centroidValues);
|
|
138
|
+
for (let i = effectiveK; i < k; ++i) {
|
|
139
|
+
finalCentroids[i] = centroidValues[effectiveK - 1];
|
|
140
|
+
}
|
|
141
|
+
// assign each data point to nearest centroid via binary search
|
|
142
|
+
const labels = new Uint8Array(N);
|
|
143
|
+
for (let i = 0; i < N; ++i) {
|
|
144
|
+
const v = data[i];
|
|
145
|
+
// binary search for nearest centroid
|
|
146
|
+
let lo = 0;
|
|
147
|
+
let hi = k - 1;
|
|
148
|
+
while (lo < hi) {
|
|
149
|
+
const mid = (lo + hi) >> 1;
|
|
150
|
+
// compare against midpoint between centroids mid and mid+1
|
|
151
|
+
if (v < (finalCentroids[mid] + finalCentroids[mid + 1]) * 0.5) {
|
|
152
|
+
hi = mid;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
lo = mid + 1;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
labels[i] = lo;
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
labels: fields.map((_, i) => labels.subarray(i * numRows, (i + 1) * numRows)),
|
|
162
|
+
centroids: finalCentroids,
|
|
163
|
+
};
|
|
164
|
+
}
|