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