@playcanvas/splat-transform 1.9.0 → 1.9.1
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/cli.mjs +214 -103
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +205 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +205 -96
- package/dist/index.mjs.map +1 -1
- package/dist/lib/spatial/kd-tree.d.ts +1 -0
- package/dist/lib/utils/logger.d.ts +6 -0
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -11854,6 +11854,16 @@ class Progress {
|
|
|
11854
11854
|
if (!quiet)
|
|
11855
11855
|
impl.onProgress(this.currentNode);
|
|
11856
11856
|
}
|
|
11857
|
+
/**
|
|
11858
|
+
* Cancel the current progress node, popping it from the stack without
|
|
11859
|
+
* completing remaining steps. Use this before early exits (e.g. break)
|
|
11860
|
+
* to keep the progress stack balanced.
|
|
11861
|
+
*/
|
|
11862
|
+
cancel() {
|
|
11863
|
+
if (!this.currentNode)
|
|
11864
|
+
return;
|
|
11865
|
+
this.currentNode = this.currentNode.parent;
|
|
11866
|
+
}
|
|
11857
11867
|
/**
|
|
11858
11868
|
* Advance to the next step. Auto-increments the step counter.
|
|
11859
11869
|
* Auto-ends when all steps are complete.
|
|
@@ -12041,33 +12051,74 @@ const sortMortonOrder = (dataTable, indices) => {
|
|
|
12041
12051
|
generate(indices);
|
|
12042
12052
|
};
|
|
12043
12053
|
|
|
12054
|
+
const nthElement = (arr, lo, hi, k, values) => {
|
|
12055
|
+
while (lo < hi) {
|
|
12056
|
+
const mid = (lo + hi) >> 1;
|
|
12057
|
+
const va = values[arr[lo]], vb = values[arr[mid]], vc = values[arr[hi]];
|
|
12058
|
+
let pivotIdx;
|
|
12059
|
+
if ((vb - va) * (vc - vb) >= 0)
|
|
12060
|
+
pivotIdx = mid;
|
|
12061
|
+
else if ((va - vb) * (vc - va) >= 0)
|
|
12062
|
+
pivotIdx = lo;
|
|
12063
|
+
else
|
|
12064
|
+
pivotIdx = hi;
|
|
12065
|
+
const pivotVal = values[arr[pivotIdx]];
|
|
12066
|
+
let tmp = arr[pivotIdx];
|
|
12067
|
+
arr[pivotIdx] = arr[hi];
|
|
12068
|
+
arr[hi] = tmp;
|
|
12069
|
+
let store = lo;
|
|
12070
|
+
for (let i = lo; i < hi; i++) {
|
|
12071
|
+
if (values[arr[i]] < pivotVal) {
|
|
12072
|
+
tmp = arr[i];
|
|
12073
|
+
arr[i] = arr[store];
|
|
12074
|
+
arr[store] = tmp;
|
|
12075
|
+
store++;
|
|
12076
|
+
}
|
|
12077
|
+
}
|
|
12078
|
+
tmp = arr[store];
|
|
12079
|
+
arr[store] = arr[hi];
|
|
12080
|
+
arr[hi] = tmp;
|
|
12081
|
+
if (store === k)
|
|
12082
|
+
return;
|
|
12083
|
+
else if (store < k)
|
|
12084
|
+
lo = store + 1;
|
|
12085
|
+
else
|
|
12086
|
+
hi = store - 1;
|
|
12087
|
+
}
|
|
12088
|
+
};
|
|
12044
12089
|
class KdTree {
|
|
12045
12090
|
centroids;
|
|
12046
12091
|
root;
|
|
12092
|
+
colData;
|
|
12047
12093
|
constructor(centroids) {
|
|
12048
|
-
const
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
|
|
12094
|
+
const numCols = centroids.numColumns;
|
|
12095
|
+
const colData = centroids.columns.map(c => c.data);
|
|
12096
|
+
const indices = new Uint32Array(centroids.numRows);
|
|
12097
|
+
for (let i = 0; i < indices.length; ++i) {
|
|
12098
|
+
indices[i] = i;
|
|
12099
|
+
}
|
|
12100
|
+
const build = (lo, hi, depth) => {
|
|
12101
|
+
const count = hi - lo + 1;
|
|
12102
|
+
if (count === 1) {
|
|
12103
|
+
return { index: indices[lo], count: 1 };
|
|
12057
12104
|
}
|
|
12058
|
-
|
|
12105
|
+
const values = colData[depth % numCols];
|
|
12106
|
+
if (count === 2) {
|
|
12107
|
+
if (values[indices[lo]] > values[indices[hi]]) {
|
|
12108
|
+
const tmp = indices[lo];
|
|
12109
|
+
indices[lo] = indices[hi];
|
|
12110
|
+
indices[hi] = tmp;
|
|
12111
|
+
}
|
|
12059
12112
|
return {
|
|
12060
|
-
index: indices[
|
|
12113
|
+
index: indices[lo],
|
|
12061
12114
|
count: 2,
|
|
12062
|
-
right: {
|
|
12063
|
-
index: indices[1],
|
|
12064
|
-
count: 1
|
|
12065
|
-
}
|
|
12115
|
+
right: { index: indices[hi], count: 1 }
|
|
12066
12116
|
};
|
|
12067
12117
|
}
|
|
12068
|
-
const mid =
|
|
12069
|
-
|
|
12070
|
-
const
|
|
12118
|
+
const mid = lo + (count >> 1);
|
|
12119
|
+
nthElement(indices, lo, hi, mid, values);
|
|
12120
|
+
const left = build(lo, mid - 1, depth + 1);
|
|
12121
|
+
const right = build(mid + 1, hi, depth + 1);
|
|
12071
12122
|
return {
|
|
12072
12123
|
index: indices[mid],
|
|
12073
12124
|
count: 1 + left.count + right.count,
|
|
@@ -12075,48 +12126,39 @@ class KdTree {
|
|
|
12075
12126
|
right
|
|
12076
12127
|
};
|
|
12077
12128
|
};
|
|
12078
|
-
const indices = new Uint32Array(centroids.numRows);
|
|
12079
|
-
for (let i = 0; i < indices.length; ++i) {
|
|
12080
|
-
indices[i] = i;
|
|
12081
|
-
}
|
|
12082
12129
|
this.centroids = centroids;
|
|
12083
|
-
this.
|
|
12130
|
+
this.colData = colData;
|
|
12131
|
+
this.root = build(0, indices.length - 1, 0);
|
|
12084
12132
|
}
|
|
12085
12133
|
findNearest(point, filterFunc) {
|
|
12086
|
-
const
|
|
12087
|
-
const
|
|
12088
|
-
const calcDistance = (index) => {
|
|
12089
|
-
let l = 0;
|
|
12090
|
-
for (let i = 0; i < numColumns; ++i) {
|
|
12091
|
-
const v = centroids.columns[i].data[index] - point[i];
|
|
12092
|
-
l += v * v;
|
|
12093
|
-
}
|
|
12094
|
-
return l;
|
|
12095
|
-
};
|
|
12134
|
+
const colData = this.colData;
|
|
12135
|
+
const numCols = colData.length;
|
|
12096
12136
|
let mind = Infinity;
|
|
12097
12137
|
let mini = -1;
|
|
12098
12138
|
let cnt = 0;
|
|
12099
|
-
const recurse = (node,
|
|
12100
|
-
const
|
|
12101
|
-
const distance = point[axis] - centroids.columns[axis].data[node.index];
|
|
12139
|
+
const recurse = (node, axis) => {
|
|
12140
|
+
const distance = point[axis] - colData[axis][node.index];
|
|
12102
12141
|
const next = (distance > 0) ? node.right : node.left;
|
|
12142
|
+
const nextAxis = axis + 1 < numCols ? axis + 1 : 0;
|
|
12103
12143
|
cnt++;
|
|
12104
12144
|
if (next) {
|
|
12105
|
-
recurse(next,
|
|
12145
|
+
recurse(next, nextAxis);
|
|
12106
12146
|
}
|
|
12107
|
-
// check index
|
|
12108
12147
|
if (!filterFunc || filterFunc(node.index)) {
|
|
12109
|
-
|
|
12148
|
+
let thisd = 0;
|
|
12149
|
+
for (let c = 0; c < numCols; c++) {
|
|
12150
|
+
const v = colData[c][node.index] - point[c];
|
|
12151
|
+
thisd += v * v;
|
|
12152
|
+
}
|
|
12110
12153
|
if (thisd < mind) {
|
|
12111
12154
|
mind = thisd;
|
|
12112
12155
|
mini = node.index;
|
|
12113
12156
|
}
|
|
12114
12157
|
}
|
|
12115
|
-
// check the other side
|
|
12116
12158
|
if (distance * distance < mind) {
|
|
12117
12159
|
const other = next === node.right ? node.left : node.right;
|
|
12118
12160
|
if (other) {
|
|
12119
|
-
recurse(other,
|
|
12161
|
+
recurse(other, nextAxis);
|
|
12120
12162
|
}
|
|
12121
12163
|
}
|
|
12122
12164
|
};
|
|
@@ -12128,16 +12170,8 @@ class KdTree {
|
|
|
12128
12170
|
return { indices: new Int32Array(0), distances: new Float32Array(0) };
|
|
12129
12171
|
}
|
|
12130
12172
|
k = Math.min(k, this.centroids.numRows);
|
|
12131
|
-
const
|
|
12132
|
-
const
|
|
12133
|
-
const calcDistance = (index) => {
|
|
12134
|
-
let l = 0;
|
|
12135
|
-
for (let i = 0; i < numColumns; ++i) {
|
|
12136
|
-
const v = centroids.columns[i].data[index] - point[i];
|
|
12137
|
-
l += v * v;
|
|
12138
|
-
}
|
|
12139
|
-
return l;
|
|
12140
|
-
};
|
|
12173
|
+
const colData = this.colData;
|
|
12174
|
+
const numCols = colData.length;
|
|
12141
12175
|
// Bounded max-heap: stores (distance, index) pairs sorted so the
|
|
12142
12176
|
// farthest element is at position 0, enabling O(1) pruning bound.
|
|
12143
12177
|
const heapDist = new Float32Array(k).fill(Infinity);
|
|
@@ -12145,14 +12179,12 @@ class KdTree {
|
|
|
12145
12179
|
let heapSize = 0;
|
|
12146
12180
|
const heapPush = (dist, idx) => {
|
|
12147
12181
|
if (heapSize < k) {
|
|
12148
|
-
// Heap not full yet -- insert via sift-up
|
|
12149
12182
|
let pos = heapSize++;
|
|
12150
12183
|
heapDist[pos] = dist;
|
|
12151
12184
|
heapIdx[pos] = idx;
|
|
12152
12185
|
while (pos > 0) {
|
|
12153
12186
|
const parent = (pos - 1) >> 1;
|
|
12154
12187
|
if (heapDist[parent] < heapDist[pos]) {
|
|
12155
|
-
// swap
|
|
12156
12188
|
const td = heapDist[parent];
|
|
12157
12189
|
heapDist[parent] = heapDist[pos];
|
|
12158
12190
|
heapDist[pos] = td;
|
|
@@ -12167,7 +12199,6 @@ class KdTree {
|
|
|
12167
12199
|
}
|
|
12168
12200
|
}
|
|
12169
12201
|
else if (dist < heapDist[0]) {
|
|
12170
|
-
// Replace root (farthest) and sift-down
|
|
12171
12202
|
heapDist[0] = dist;
|
|
12172
12203
|
heapIdx[0] = idx;
|
|
12173
12204
|
let pos = 0;
|
|
@@ -12191,22 +12222,26 @@ class KdTree {
|
|
|
12191
12222
|
}
|
|
12192
12223
|
}
|
|
12193
12224
|
};
|
|
12194
|
-
const recurse = (node,
|
|
12195
|
-
const
|
|
12196
|
-
const distance = point[axis] - centroids.columns[axis].data[node.index];
|
|
12225
|
+
const recurse = (node, axis) => {
|
|
12226
|
+
const distance = point[axis] - colData[axis][node.index];
|
|
12197
12227
|
const next = (distance > 0) ? node.right : node.left;
|
|
12228
|
+
const nextAxis = axis + 1 < numCols ? axis + 1 : 0;
|
|
12198
12229
|
if (next) {
|
|
12199
|
-
recurse(next,
|
|
12230
|
+
recurse(next, nextAxis);
|
|
12200
12231
|
}
|
|
12201
12232
|
if (!filterFunc || filterFunc(node.index)) {
|
|
12202
|
-
|
|
12233
|
+
let thisd = 0;
|
|
12234
|
+
for (let c = 0; c < numCols; c++) {
|
|
12235
|
+
const v = colData[c][node.index] - point[c];
|
|
12236
|
+
thisd += v * v;
|
|
12237
|
+
}
|
|
12203
12238
|
heapPush(thisd, node.index);
|
|
12204
12239
|
}
|
|
12205
12240
|
const bound = heapSize < k ? Infinity : heapDist[0];
|
|
12206
12241
|
if (distance * distance < bound) {
|
|
12207
12242
|
const other = next === node.right ? node.left : node.right;
|
|
12208
12243
|
if (other) {
|
|
12209
|
-
recurse(other,
|
|
12244
|
+
recurse(other, nextAxis);
|
|
12210
12245
|
}
|
|
12211
12246
|
}
|
|
12212
12247
|
};
|
|
@@ -12241,6 +12276,54 @@ const OPACITY_PRUNE_THRESHOLD = 0.1;
|
|
|
12241
12276
|
const KNN_K = 16;
|
|
12242
12277
|
const MC_SAMPLES = 1;
|
|
12243
12278
|
const EPS_COV = 1e-8;
|
|
12279
|
+
const PROGRESS_TICKS = 100;
|
|
12280
|
+
// Radix sort edge indices by their Float32 costs.
|
|
12281
|
+
// Converts floats to sortable uint32 keys (preserving order), then does
|
|
12282
|
+
// 4-pass LSD radix sort with 8-bit radix. Returns the number of valid
|
|
12283
|
+
// (finite-cost) edges written to `out`.
|
|
12284
|
+
const radixSortIndicesByFloat = (out, count, keys) => {
|
|
12285
|
+
const keyBits = new Uint32Array(keys.buffer, keys.byteOffset, keys.length);
|
|
12286
|
+
const sortKeys = new Uint32Array(count);
|
|
12287
|
+
let validCount = 0;
|
|
12288
|
+
for (let i = 0; i < count; i++) {
|
|
12289
|
+
const bits = keyBits[i];
|
|
12290
|
+
if ((bits & 0x7F800000) === 0x7F800000)
|
|
12291
|
+
continue;
|
|
12292
|
+
sortKeys[validCount] = (bits & 0x80000000) ? ~bits >>> 0 : (bits | 0x80000000) >>> 0;
|
|
12293
|
+
out[validCount] = i;
|
|
12294
|
+
validCount++;
|
|
12295
|
+
}
|
|
12296
|
+
if (validCount <= 1)
|
|
12297
|
+
return validCount;
|
|
12298
|
+
const n = validCount;
|
|
12299
|
+
const temp = new Uint32Array(n);
|
|
12300
|
+
const tempKeys = new Uint32Array(n);
|
|
12301
|
+
const counts = new Uint32Array(256);
|
|
12302
|
+
for (let pass = 0; pass < 4; pass++) {
|
|
12303
|
+
const shift = pass << 3;
|
|
12304
|
+
const srcIdx = (pass & 1) ? temp : out;
|
|
12305
|
+
const dstIdx = (pass & 1) ? out : temp;
|
|
12306
|
+
const srcK = (pass & 1) ? tempKeys : sortKeys;
|
|
12307
|
+
const dstK = (pass & 1) ? sortKeys : tempKeys;
|
|
12308
|
+
counts.fill(0);
|
|
12309
|
+
for (let i = 0; i < n; i++) {
|
|
12310
|
+
counts[(srcK[i] >>> shift) & 0xFF]++;
|
|
12311
|
+
}
|
|
12312
|
+
let sum = 0;
|
|
12313
|
+
for (let b = 0; b < 256; b++) {
|
|
12314
|
+
const c = counts[b];
|
|
12315
|
+
counts[b] = sum;
|
|
12316
|
+
sum += c;
|
|
12317
|
+
}
|
|
12318
|
+
for (let i = 0; i < n; i++) {
|
|
12319
|
+
const bucket = (srcK[i] >>> shift) & 0xFF;
|
|
12320
|
+
const pos = counts[bucket]++;
|
|
12321
|
+
dstIdx[pos] = srcIdx[i];
|
|
12322
|
+
dstK[pos] = srcK[i];
|
|
12323
|
+
}
|
|
12324
|
+
}
|
|
12325
|
+
return validCount;
|
|
12326
|
+
};
|
|
12244
12327
|
// ---------- sigmoid / logit ----------
|
|
12245
12328
|
const sigmoid$1 = (x) => 1 / (1 + Math.exp(-x));
|
|
12246
12329
|
const logit = (p) => {
|
|
@@ -12728,7 +12811,6 @@ const simplifyGaussians = (dataTable, targetCount) => {
|
|
|
12728
12811
|
let current;
|
|
12729
12812
|
if (keptIndices.length < N && keptIndices.length > targetCount) {
|
|
12730
12813
|
current = dataTable.permuteRows(keptIndices);
|
|
12731
|
-
logger.debug(`opacity pruning: ${N} -> ${current.numRows} splats (threshold=${pruneThreshold.toFixed(4)})`);
|
|
12732
12814
|
}
|
|
12733
12815
|
else {
|
|
12734
12816
|
current = dataTable;
|
|
@@ -12739,7 +12821,8 @@ const simplifyGaussians = (dataTable, targetCount) => {
|
|
|
12739
12821
|
while (current.numRows > targetCount) {
|
|
12740
12822
|
const n = current.numRows;
|
|
12741
12823
|
const kEff = Math.min(Math.max(1, KNN_K), Math.max(1, n - 1));
|
|
12742
|
-
logger.
|
|
12824
|
+
logger.progress.begin(5);
|
|
12825
|
+
logger.progress.step('Building KD-tree');
|
|
12743
12826
|
const cx = current.getColumnByName('x').data;
|
|
12744
12827
|
const cy = current.getColumnByName('y').data;
|
|
12745
12828
|
const cz = current.getColumnByName('z').data;
|
|
@@ -12752,16 +12835,21 @@ const simplifyGaussians = (dataTable, targetCount) => {
|
|
|
12752
12835
|
const cr2 = current.getColumnByName('rot_2').data;
|
|
12753
12836
|
const cr3 = current.getColumnByName('rot_3').data;
|
|
12754
12837
|
const cache = buildPerSplatCache(n, cx, cy, cz, cop, cs0, cs1, cs2, cr0, cr1, cr2, cr3);
|
|
12755
|
-
// Build KNN graph
|
|
12756
12838
|
const posTable = new DataTable([
|
|
12757
12839
|
new Column('x', cx instanceof Float32Array ? cx : new Float32Array(cx)),
|
|
12758
12840
|
new Column('y', cy instanceof Float32Array ? cy : new Float32Array(cy)),
|
|
12759
12841
|
new Column('z', cz instanceof Float32Array ? cz : new Float32Array(cz))
|
|
12760
12842
|
]);
|
|
12761
12843
|
const kdTree = new KdTree(posTable);
|
|
12762
|
-
|
|
12763
|
-
|
|
12844
|
+
logger.progress.step('Finding nearest neighbors');
|
|
12845
|
+
let edgeCapacity = Math.ceil(n * kEff / 2);
|
|
12846
|
+
let edgeU = new Uint32Array(edgeCapacity);
|
|
12847
|
+
let edgeV = new Uint32Array(edgeCapacity);
|
|
12848
|
+
let edgeCount = 0;
|
|
12764
12849
|
const queryPoint = new Float32Array(3);
|
|
12850
|
+
const knnInterval = Math.max(1, Math.ceil(n / PROGRESS_TICKS));
|
|
12851
|
+
const knnTicks = Math.ceil(n / knnInterval);
|
|
12852
|
+
logger.progress.begin(knnTicks);
|
|
12765
12853
|
for (let i = 0; i < n; i++) {
|
|
12766
12854
|
queryPoint[0] = cx[i];
|
|
12767
12855
|
queryPoint[1] = cy[i];
|
|
@@ -12769,46 +12857,58 @@ const simplifyGaussians = (dataTable, targetCount) => {
|
|
|
12769
12857
|
const knn = kdTree.findKNearest(queryPoint, kEff + 1);
|
|
12770
12858
|
for (let ki = 0; ki < knn.indices.length; ki++) {
|
|
12771
12859
|
const j = knn.indices[ki];
|
|
12772
|
-
if (j
|
|
12860
|
+
if (j <= i)
|
|
12773
12861
|
continue;
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
|
|
12862
|
+
if (edgeCount === edgeCapacity) {
|
|
12863
|
+
edgeCapacity *= 2;
|
|
12864
|
+
const newU = new Uint32Array(edgeCapacity);
|
|
12865
|
+
const newV = new Uint32Array(edgeCapacity);
|
|
12866
|
+
newU.set(edgeU);
|
|
12867
|
+
newV.set(edgeV);
|
|
12868
|
+
edgeU = newU;
|
|
12869
|
+
edgeV = newV;
|
|
12780
12870
|
}
|
|
12871
|
+
edgeU[edgeCount] = i;
|
|
12872
|
+
edgeV[edgeCount] = j;
|
|
12873
|
+
edgeCount++;
|
|
12781
12874
|
}
|
|
12875
|
+
if ((i + 1) % knnInterval === 0)
|
|
12876
|
+
logger.progress.step();
|
|
12782
12877
|
}
|
|
12783
|
-
if (
|
|
12878
|
+
if (n % knnInterval !== 0)
|
|
12879
|
+
logger.progress.step();
|
|
12880
|
+
if (edgeCount === 0) {
|
|
12881
|
+
logger.progress.cancel();
|
|
12784
12882
|
break;
|
|
12785
|
-
|
|
12883
|
+
}
|
|
12884
|
+
logger.progress.step('Computing edge costs');
|
|
12786
12885
|
const appData = [];
|
|
12787
12886
|
for (let ai = 0; ai < allAppearanceCols.length; ai++) {
|
|
12788
12887
|
const col = current.getColumnByName(allAppearanceCols[ai]);
|
|
12789
12888
|
if (col)
|
|
12790
12889
|
appData.push(col.data);
|
|
12791
12890
|
}
|
|
12792
|
-
const costs = new Float32Array(edges.length);
|
|
12793
|
-
for (let e = 0; e < edges.length; e++) {
|
|
12794
|
-
costs[e] = computeEdgeCost(edges[e][0], edges[e][1], cx, cy, cz, cache, Z, appData, appData.length);
|
|
12795
|
-
}
|
|
12796
|
-
// Greedy disjoint pair selection
|
|
12797
|
-
const valid = [];
|
|
12798
|
-
for (let i = 0; i < edges.length; i++) {
|
|
12799
|
-
if (Number.isFinite(costs[i]))
|
|
12800
|
-
valid.push(i);
|
|
12801
|
-
}
|
|
12802
|
-
valid.sort((a, b) => {
|
|
12803
|
-
const d = costs[a] - costs[b];
|
|
12804
|
-
return d !== 0 ? d : a - b;
|
|
12805
|
-
});
|
|
12806
12891
|
const mergesNeeded = n - targetCount;
|
|
12892
|
+
const costs = new Float32Array(edgeCount);
|
|
12893
|
+
const costInterval = Math.max(1, Math.ceil(edgeCount / PROGRESS_TICKS));
|
|
12894
|
+
const costTicks = Math.ceil(edgeCount / costInterval);
|
|
12895
|
+
logger.progress.begin(costTicks);
|
|
12896
|
+
for (let e = 0; e < edgeCount; e++) {
|
|
12897
|
+
costs[e] = computeEdgeCost(edgeU[e], edgeV[e], cx, cy, cz, cache, Z, appData, appData.length);
|
|
12898
|
+
if ((e + 1) % costInterval === 0)
|
|
12899
|
+
logger.progress.step();
|
|
12900
|
+
}
|
|
12901
|
+
if (edgeCount % costInterval !== 0)
|
|
12902
|
+
logger.progress.step();
|
|
12903
|
+
logger.progress.step('Merging splats');
|
|
12904
|
+
// Sort and greedy disjoint pair selection
|
|
12905
|
+
const sorted = new Uint32Array(edgeCount);
|
|
12906
|
+
const validCount = radixSortIndicesByFloat(sorted, edgeCount, costs);
|
|
12807
12907
|
const used = new Uint8Array(n);
|
|
12808
12908
|
const pairs = [];
|
|
12809
|
-
for (let t = 0; t <
|
|
12810
|
-
const e =
|
|
12811
|
-
const u =
|
|
12909
|
+
for (let t = 0; t < validCount; t++) {
|
|
12910
|
+
const e = sorted[t];
|
|
12911
|
+
const u = edgeU[e], v = edgeV[e];
|
|
12812
12912
|
if (used[u] || used[v])
|
|
12813
12913
|
continue;
|
|
12814
12914
|
used[u] = 1;
|
|
@@ -12817,9 +12917,10 @@ const simplifyGaussians = (dataTable, targetCount) => {
|
|
|
12817
12917
|
if (pairs.length >= mergesNeeded)
|
|
12818
12918
|
break;
|
|
12819
12919
|
}
|
|
12820
|
-
if (pairs.length === 0)
|
|
12920
|
+
if (pairs.length === 0) {
|
|
12921
|
+
logger.progress.cancel();
|
|
12821
12922
|
break;
|
|
12822
|
-
|
|
12923
|
+
}
|
|
12823
12924
|
// Mark which indices are consumed by merging
|
|
12824
12925
|
const usedSet = new Uint8Array(n);
|
|
12825
12926
|
for (let p = 0; p < pairs.length; p++) {
|
|
@@ -12847,7 +12948,7 @@ const simplifyGaussians = (dataTable, targetCount) => {
|
|
|
12847
12948
|
newTable.columns[c].data[dst] = cols[c].data[src];
|
|
12848
12949
|
}
|
|
12849
12950
|
}
|
|
12850
|
-
// Merge pairs
|
|
12951
|
+
// Merge pairs
|
|
12851
12952
|
const mergeOut = {
|
|
12852
12953
|
mu: new Float64Array(3),
|
|
12853
12954
|
sc: new Float64Array(3),
|
|
@@ -12875,6 +12976,9 @@ const simplifyGaussians = (dataTable, targetCount) => {
|
|
|
12875
12976
|
.filter(col => !handledCols.has(col.name))
|
|
12876
12977
|
.map(col => ({ src: col, dst: newTable.getColumnByName(col.name) }))
|
|
12877
12978
|
.filter(pair => pair.dst);
|
|
12979
|
+
const mergeInterval = Math.max(1, Math.ceil(pairs.length / PROGRESS_TICKS));
|
|
12980
|
+
const mergeTicks = Math.ceil(pairs.length / mergeInterval);
|
|
12981
|
+
logger.progress.begin(mergeTicks);
|
|
12878
12982
|
for (let p = 0; p < pairs.length; p++, dst++) {
|
|
12879
12983
|
const pi = pairs[p][0], pj = pairs[p][1];
|
|
12880
12984
|
momentMatch(pi, pj, cx, cy, cz, cop, cs0, cs1, cs2, cr0, cr1, cr2, cr3, mergeOut, appData, appData.length);
|
|
@@ -12897,7 +13001,12 @@ const simplifyGaussians = (dataTable, targetCount) => {
|
|
|
12897
13001
|
for (let u = 0; u < unhandledColPairs.length; u++) {
|
|
12898
13002
|
unhandledColPairs[u].dst.data[dst] = unhandledColPairs[u].src.data[dominant];
|
|
12899
13003
|
}
|
|
13004
|
+
if ((p + 1) % mergeInterval === 0)
|
|
13005
|
+
logger.progress.step();
|
|
12900
13006
|
}
|
|
13007
|
+
if (pairs.length % mergeInterval !== 0)
|
|
13008
|
+
logger.progress.step();
|
|
13009
|
+
logger.progress.step('Finalizing');
|
|
12901
13010
|
current = newTable;
|
|
12902
13011
|
}
|
|
12903
13012
|
return current;
|
|
@@ -16330,7 +16439,7 @@ class CompressedChunk {
|
|
|
16330
16439
|
}
|
|
16331
16440
|
}
|
|
16332
16441
|
|
|
16333
|
-
var version = "1.9.
|
|
16442
|
+
var version = "1.9.1";
|
|
16334
16443
|
|
|
16335
16444
|
const generatedByString = `Generated by splat-transform ${version}`;
|
|
16336
16445
|
const chunkProps = [
|
|
@@ -20720,15 +20829,17 @@ const main = async () => {
|
|
|
20720
20829
|
if (node.stepName) {
|
|
20721
20830
|
console.error(`[${node.step}/${node.totalSteps}] ${node.stepName}`);
|
|
20722
20831
|
}
|
|
20832
|
+
else if (node.step === 0) {
|
|
20833
|
+
start = hrtime();
|
|
20834
|
+
}
|
|
20723
20835
|
else {
|
|
20724
|
-
|
|
20725
|
-
|
|
20726
|
-
|
|
20727
|
-
|
|
20728
|
-
process.stderr.write(
|
|
20729
|
-
|
|
20730
|
-
|
|
20731
|
-
process.stderr.write('#');
|
|
20836
|
+
const displaySteps = 10;
|
|
20837
|
+
const curr = Math.round(displaySteps * node.step / node.totalSteps);
|
|
20838
|
+
const prev = Math.round(displaySteps * (node.step - 1) / node.totalSteps);
|
|
20839
|
+
if (curr > prev)
|
|
20840
|
+
process.stderr.write('#'.repeat(curr - prev));
|
|
20841
|
+
if (node.step === node.totalSteps) {
|
|
20842
|
+
process.stderr.write(` done in ${hrtimeDelta(start, hrtime()).toFixed(3)}s 🎉\n`);
|
|
20732
20843
|
}
|
|
20733
20844
|
}
|
|
20734
20845
|
}
|