@d5techs/3dgs-lib 1.4.18 → 1.4.19

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/3dgs-lib.js CHANGED
@@ -6148,14 +6148,43 @@ const SH_C0 = 0.28209479177387814;
6148
6148
  function symLog(x) {
6149
6149
  return Math.sign(x) * Math.log(1 + Math.abs(x));
6150
6150
  }
6151
- function buildCodebook(values, size = 256, iterations = 8) {
6151
+ function kmeansppInit(values, k) {
6152
+ const n = values.length;
6153
+ const centers = new Float64Array(k);
6154
+ centers[0] = values[Math.floor(Math.random() * n)];
6155
+ const dist = new Float64Array(n);
6156
+ dist.fill(Infinity);
6157
+ for (let c = 1; c < k; c++) {
6158
+ let totalDist = 0;
6159
+ for (let i = 0; i < n; i++) {
6160
+ const d = (values[i] - centers[c - 1]) ** 2;
6161
+ if (d < dist[i]) dist[i] = d;
6162
+ totalDist += dist[i];
6163
+ }
6164
+ let target = Math.random() * totalDist;
6165
+ let chosen = 0;
6166
+ for (let i = 0; i < n; i++) {
6167
+ target -= dist[i];
6168
+ if (target <= 0) {
6169
+ chosen = i;
6170
+ break;
6171
+ }
6172
+ }
6173
+ centers[c] = values[chosen];
6174
+ }
6175
+ return centers;
6176
+ }
6177
+ function buildCodebook(values, size = 256, iterations = 20) {
6152
6178
  if (values.length === 0) return new Array(size).fill(0);
6153
- const sorted = Float32Array.from(values).sort();
6154
- const codebook = new Float64Array(size);
6155
- for (let i = 0; i < size; i++) {
6156
- codebook[i] = sorted[Math.floor(i / (size - 1) * (sorted.length - 1))];
6179
+ if (values.length <= size) {
6180
+ const cb = Array.from(new Set(Array.from(values))).sort((a, b) => a - b);
6181
+ while (cb.length < size) cb.push(cb[cb.length - 1]);
6182
+ return cb.slice(0, size);
6157
6183
  }
6184
+ const codebook = kmeansppInit(values, size);
6158
6185
  const assignments = new Uint8Array(values.length);
6186
+ const sums = new Float64Array(size);
6187
+ const counts = new Uint32Array(size);
6159
6188
  for (let iter = 0; iter < iterations; iter++) {
6160
6189
  for (let i = 0; i < values.length; i++) {
6161
6190
  let bestIdx = 0;
@@ -6169,29 +6198,74 @@ function buildCodebook(values, size = 256, iterations = 8) {
6169
6198
  }
6170
6199
  assignments[i] = bestIdx;
6171
6200
  }
6172
- const sums = new Float64Array(size);
6173
- const counts = new Uint32Array(size);
6201
+ sums.fill(0);
6202
+ counts.fill(0);
6174
6203
  for (let i = 0; i < values.length; i++) {
6175
6204
  sums[assignments[i]] += values[i];
6176
6205
  counts[assignments[i]]++;
6177
6206
  }
6207
+ let changed = false;
6178
6208
  for (let j = 0; j < size; j++) {
6179
- if (counts[j] > 0) codebook[j] = sums[j] / counts[j];
6209
+ if (counts[j] > 0) {
6210
+ const newVal = sums[j] / counts[j];
6211
+ if (newVal !== codebook[j]) {
6212
+ codebook[j] = newVal;
6213
+ changed = true;
6214
+ }
6215
+ }
6180
6216
  }
6217
+ if (!changed) break;
6181
6218
  }
6182
- return Array.from(codebook);
6219
+ const result = Array.from(codebook);
6220
+ result.sort((a, b) => a - b);
6221
+ return result;
6183
6222
  }
6184
- function findNearest(codebook, value) {
6185
- let bestIdx = 0;
6186
- let bestDist = Math.abs(value - codebook[0]);
6187
- for (let j = 1; j < codebook.length; j++) {
6188
- const d = Math.abs(value - codebook[j]);
6189
- if (d < bestDist) {
6190
- bestDist = d;
6191
- bestIdx = j;
6192
- }
6223
+ function findNearest(sortedCodebook, value) {
6224
+ let lo = 0, hi = sortedCodebook.length - 1;
6225
+ while (lo < hi) {
6226
+ const mid = lo + hi >> 1;
6227
+ if (sortedCodebook[mid] < value) lo = mid + 1;
6228
+ else hi = mid;
6229
+ }
6230
+ if (lo > 0 && Math.abs(sortedCodebook[lo - 1] - value) <= Math.abs(sortedCodebook[lo] - value)) {
6231
+ return lo - 1;
6193
6232
  }
6194
- return bestIdx;
6233
+ return lo;
6234
+ }
6235
+ function mortonSort(positions, count) {
6236
+ let minx = Infinity, miny = Infinity, minz = Infinity;
6237
+ let maxx = -Infinity, maxy = -Infinity, maxz = -Infinity;
6238
+ for (let i = 0; i < count; i++) {
6239
+ const x = positions[i * 3], y = positions[i * 3 + 1], z = positions[i * 3 + 2];
6240
+ if (x < minx) minx = x;
6241
+ if (x > maxx) maxx = x;
6242
+ if (y < miny) miny = y;
6243
+ if (y > maxy) maxy = y;
6244
+ if (z < minz) minz = z;
6245
+ if (z > maxz) maxz = z;
6246
+ }
6247
+ const xlen = maxx - minx || 1;
6248
+ const ylen = maxy - miny || 1;
6249
+ const zlen = maxz - minz || 1;
6250
+ const part1By2 = (x) => {
6251
+ x &= 1023;
6252
+ x = (x ^ x << 16) & 4278190335;
6253
+ x = (x ^ x << 8) & 50393103;
6254
+ x = (x ^ x << 4) & 51130563;
6255
+ x = (x ^ x << 2) & 153391689;
6256
+ return x;
6257
+ };
6258
+ const morton = new Uint32Array(count);
6259
+ const indices = new Uint32Array(count);
6260
+ for (let i = 0; i < count; i++) {
6261
+ const ix = Math.min(1023, Math.floor(1024 * (positions[i * 3] - minx) / xlen));
6262
+ const iy = Math.min(1023, Math.floor(1024 * (positions[i * 3 + 1] - miny) / ylen));
6263
+ const iz = Math.min(1023, Math.floor(1024 * (positions[i * 3 + 2] - minz) / zlen));
6264
+ morton[i] = (part1By2(iz) << 2) + (part1By2(iy) << 1) + part1By2(ix);
6265
+ indices[i] = i;
6266
+ }
6267
+ indices.sort((a, b) => morton[a] - morton[b]);
6268
+ return indices;
6195
6269
  }
6196
6270
  function calcImageDimensions(count) {
6197
6271
  const width = Math.ceil(Math.sqrt(count));
@@ -6247,11 +6321,36 @@ async function serializeSOG(data, coordinateSystem = "blender") {
6247
6321
  rotations[i4 + 3] = ry;
6248
6322
  }
6249
6323
  }
6324
+ const sortOrder = mortonSort(positions, count);
6325
+ const sortedPositions = new Float32Array(count * 3);
6326
+ const sortedScales = new Float32Array(count * 3);
6327
+ const sortedRotations = new Float32Array(count * 4);
6328
+ const sortedColors = new Float32Array(count * 3);
6329
+ const sortedOpacities = new Float32Array(count);
6330
+ for (let dst = 0; dst < count; dst++) {
6331
+ const src = sortOrder[dst];
6332
+ const s3 = src * 3, d3 = dst * 3;
6333
+ sortedPositions[d3] = positions[s3];
6334
+ sortedPositions[d3 + 1] = positions[s3 + 1];
6335
+ sortedPositions[d3 + 2] = positions[s3 + 2];
6336
+ sortedScales[d3] = scales[s3];
6337
+ sortedScales[d3 + 1] = scales[s3 + 1];
6338
+ sortedScales[d3 + 2] = scales[s3 + 2];
6339
+ sortedColors[d3] = colors[s3];
6340
+ sortedColors[d3 + 1] = colors[s3 + 1];
6341
+ sortedColors[d3 + 2] = colors[s3 + 2];
6342
+ const s4 = src * 4, d4 = dst * 4;
6343
+ sortedRotations[d4] = rotations[s4];
6344
+ sortedRotations[d4 + 1] = rotations[s4 + 1];
6345
+ sortedRotations[d4 + 2] = rotations[s4 + 2];
6346
+ sortedRotations[d4 + 3] = rotations[s4 + 3];
6347
+ sortedOpacities[dst] = opacities[src];
6348
+ }
6250
6349
  const { width, height } = calcImageDimensions(count);
6251
6350
  const totalPixels = width * height;
6252
6351
  const logPositions = new Float32Array(count * 3);
6253
6352
  for (let i = 0; i < count * 3; i++) {
6254
- logPositions[i] = symLog(positions[i]);
6353
+ logPositions[i] = symLog(sortedPositions[i]);
6255
6354
  }
6256
6355
  const mins = [Infinity, Infinity, Infinity];
6257
6356
  const maxs = [-Infinity, -Infinity, -Infinity];
@@ -6280,7 +6379,7 @@ async function serializeSOG(data, coordinateSystem = "blender") {
6280
6379
  }
6281
6380
  const logScales = new Float32Array(count * 3);
6282
6381
  for (let i = 0; i < count * 3; i++) {
6283
- logScales[i] = Math.log(Math.max(1e-8, scales[i]));
6382
+ logScales[i] = Math.log(Math.max(1e-8, sortedScales[i]));
6284
6383
  }
6285
6384
  const scaleCodebook = buildCodebook(logScales);
6286
6385
  const scalesData = new Uint8ClampedArray(totalPixels * 4);
@@ -6297,10 +6396,18 @@ async function serializeSOG(data, coordinateSystem = "blender") {
6297
6396
  const SQRT2 = Math.SQRT2;
6298
6397
  for (let i = 0; i < count; i++) {
6299
6398
  const off = i * 4;
6300
- let qw = rotations[i * 4 + 0];
6301
- let qx = rotations[i * 4 + 1];
6302
- let qy = rotations[i * 4 + 2];
6303
- let qz = rotations[i * 4 + 3];
6399
+ let qw = sortedRotations[i * 4 + 0];
6400
+ let qx = sortedRotations[i * 4 + 1];
6401
+ let qy = sortedRotations[i * 4 + 2];
6402
+ let qz = sortedRotations[i * 4 + 3];
6403
+ const len = Math.sqrt(qw * qw + qx * qx + qy * qy + qz * qz);
6404
+ if (len > 0) {
6405
+ const inv = 1 / len;
6406
+ qw *= inv;
6407
+ qx *= inv;
6408
+ qy *= inv;
6409
+ qz *= inv;
6410
+ }
6304
6411
  const comps = [qw, qx, qy, qz];
6305
6412
  let largest = 0;
6306
6413
  let largestAbs = Math.abs(comps[0]);
@@ -6342,9 +6449,9 @@ async function serializeSOG(data, coordinateSystem = "blender") {
6342
6449
  }
6343
6450
  const dcValues = new Float32Array(count * 3);
6344
6451
  for (let i = 0; i < count; i++) {
6345
- dcValues[i * 3 + 0] = (colors[i * 3 + 0] - 0.5) / SH_C0;
6346
- dcValues[i * 3 + 1] = (colors[i * 3 + 1] - 0.5) / SH_C0;
6347
- dcValues[i * 3 + 2] = (colors[i * 3 + 2] - 0.5) / SH_C0;
6452
+ dcValues[i * 3 + 0] = (sortedColors[i * 3 + 0] - 0.5) / SH_C0;
6453
+ dcValues[i * 3 + 1] = (sortedColors[i * 3 + 1] - 0.5) / SH_C0;
6454
+ dcValues[i * 3 + 2] = (sortedColors[i * 3 + 2] - 0.5) / SH_C0;
6348
6455
  }
6349
6456
  const dcCodebook = buildCodebook(dcValues);
6350
6457
  const sh0Data = new Uint8ClampedArray(totalPixels * 4);
@@ -6354,7 +6461,7 @@ async function serializeSOG(data, coordinateSystem = "blender") {
6354
6461
  sh0Data[off + 0] = findNearest(dcCodebook, dcValues[i * 3 + 0]);
6355
6462
  sh0Data[off + 1] = findNearest(dcCodebook, dcValues[i * 3 + 1]);
6356
6463
  sh0Data[off + 2] = findNearest(dcCodebook, dcValues[i * 3 + 2]);
6357
- sh0Data[off + 3] = Math.round(Math.max(0, Math.min(255, opacities[i] * 255)));
6464
+ sh0Data[off + 3] = Math.round(Math.max(0, Math.min(255, sortedOpacities[i] * 255)));
6358
6465
  }
6359
6466
  const [meansLWebP, meansUWebP, scalesWebP, quatsWebP, sh0WebP] = await Promise.all([
6360
6467
  encodeWebP(width, height, meansLData),