minidraco 0.3.0 → 0.5.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.js CHANGED
@@ -259,6 +259,90 @@ var Mesh = class extends PointCloud {
259
259
  }
260
260
  };
261
261
 
262
+ // src/decoder/core/ScratchArena.ts
263
+ var MAX_CLASS = 28;
264
+ var freeInt32 = [];
265
+ var freeUint32 = [];
266
+ var freeUint8 = [];
267
+ for (let i = 0; i <= MAX_CLASS; ++i) {
268
+ freeInt32.push([]);
269
+ freeUint32.push([]);
270
+ freeUint8.push([]);
271
+ }
272
+ var borrowedInt32 = [];
273
+ var borrowedUint32 = [];
274
+ var borrowedUint8 = [];
275
+ var sizeClass = (size) => size <= 1 ? 0 : 32 - Math.clz32(size - 1);
276
+ var takeInt32 = (size) => {
277
+ const k = sizeClass(size);
278
+ if (k > MAX_CLASS) return new Int32Array(size);
279
+ const bucket = freeInt32[k];
280
+ let pooled = bucket.length > 0 ? bucket.pop() : new Int32Array(size);
281
+ if (pooled.length < size) pooled = new Int32Array(1 << k);
282
+ borrowedInt32.push(pooled);
283
+ return pooled.length === size ? pooled : pooled.subarray(0, size);
284
+ };
285
+ var takeUint32 = (size) => {
286
+ const k = sizeClass(size);
287
+ if (k > MAX_CLASS) return new Uint32Array(size);
288
+ const bucket = freeUint32[k];
289
+ let pooled = bucket.length > 0 ? bucket.pop() : new Uint32Array(size);
290
+ if (pooled.length < size) pooled = new Uint32Array(1 << k);
291
+ borrowedUint32.push(pooled);
292
+ return pooled.length === size ? pooled : pooled.subarray(0, size);
293
+ };
294
+ var byteCapacity = (size) => size < 8 ? 8 : size + 7 & ~7;
295
+ var takeUint8 = (size) => {
296
+ const capacity = byteCapacity(size);
297
+ const k = sizeClass(capacity);
298
+ if (k > MAX_CLASS) return new Uint8Array(capacity);
299
+ const bucket = freeUint8[k];
300
+ let pooled = bucket.length > 0 ? bucket.pop() : new Uint8Array(capacity);
301
+ if (pooled.length < size) pooled = new Uint8Array(1 << k);
302
+ borrowedUint8.push(pooled);
303
+ return pooled.length === size ? pooled : pooled.subarray(0, size);
304
+ };
305
+ var scratchInt32 = (size) => takeInt32(size);
306
+ var scratchInt32Filled = (size, value) => {
307
+ const view = takeInt32(size);
308
+ view.fill(value);
309
+ return view;
310
+ };
311
+ var scratchUint32 = (size) => takeUint32(size);
312
+ var scratchUint32Zeroed = (size) => {
313
+ const view = takeUint32(size);
314
+ view.fill(0);
315
+ return view;
316
+ };
317
+ var scratchUint8 = (size) => takeUint8(size);
318
+ var scratchUint8Zeroed = (size) => {
319
+ const view = takeUint8(size);
320
+ view.fill(0);
321
+ return view;
322
+ };
323
+ var scratchUint8Filled = (size, value) => {
324
+ const view = takeUint8(size);
325
+ view.fill(value);
326
+ return view;
327
+ };
328
+ var releaseScratch = () => {
329
+ for (let i = 0; i < borrowedInt32.length; ++i) {
330
+ const buffer = borrowedInt32[i];
331
+ freeInt32[sizeClass(buffer.length)].push(buffer);
332
+ }
333
+ for (let i = 0; i < borrowedUint32.length; ++i) {
334
+ const buffer = borrowedUint32[i];
335
+ freeUint32[sizeClass(buffer.length)].push(buffer);
336
+ }
337
+ for (let i = 0; i < borrowedUint8.length; ++i) {
338
+ const buffer = borrowedUint8[i];
339
+ freeUint8[sizeClass(buffer.length)].push(buffer);
340
+ }
341
+ borrowedInt32.length = 0;
342
+ borrowedUint32.length = 0;
343
+ borrowedUint8.length = 0;
344
+ };
345
+
262
346
  // src/decoder/core/DataBuffer.ts
263
347
  var DataBuffer = class {
264
348
  _data;
@@ -283,6 +367,15 @@ var DataBuffer = class {
283
367
  resize(newSize) {
284
368
  this._resize(newSize);
285
369
  }
370
+ // Replaces the contents with a decode-scoped scratch buffer of exactly
371
+ // `size` bytes; the previous contents are dropped and the new ones are
372
+ // arbitrary, so every byte must be written before it is read. Only for
373
+ // buffers that never outlive the decode (see ScratchArena) — the portable
374
+ // attributes the integer decoders build and discard per attribute are the
375
+ // one caller, and their per-primitive allocation showed up in profiles.
376
+ adoptScratch(size) {
377
+ this._data = scratchUint8(size);
378
+ }
286
379
  write(bytePos, inArray, dataSize) {
287
380
  if (inArray instanceof Uint8Array) {
288
381
  this._data.set(inArray.length === dataSize ? inArray : inArray.subarray(0, dataSize), bytePos);
@@ -372,6 +465,19 @@ var PointAttribute = class extends GeometryAttribute {
372
465
  this._numUniqueEntries = numAttributeValues;
373
466
  return true;
374
467
  }
468
+ // Like reset(), but backs the attribute with decode-scoped scratch memory.
469
+ // Only valid for attributes that never escape the decode and whose every
470
+ // byte is written before it is read (see DataBuffer.adoptScratch).
471
+ resetScratch(numAttributeValues) {
472
+ if (this._attributeBuffer === null) {
473
+ this._attributeBuffer = new DataBuffer();
474
+ }
475
+ const entrySize = dataTypeLength(this.dataType) * this.numComponents;
476
+ this._attributeBuffer.adoptScratch(numAttributeValues * entrySize);
477
+ this.resetBuffer(this._attributeBuffer, entrySize, 0);
478
+ this._numUniqueEntries = numAttributeValues;
479
+ return true;
480
+ }
375
481
  get size() {
376
482
  return this._numUniqueEntries;
377
483
  }
@@ -414,6 +520,20 @@ var PointAttribute = class extends GeometryAttribute {
414
520
  this._identityMapping = false;
415
521
  this._indicesMap = new Uint32Array(numPoints);
416
522
  }
523
+ // Adopts an already-computed map, shared with other attributes of the same
524
+ // mesh. The map must be treated as read-only from here on (reads go through
525
+ // mappedIndex/extractTo; copyFrom slices).
526
+ setExplicitMappingShared(map) {
527
+ this._identityMapping = false;
528
+ this._indicesMap = map;
529
+ }
530
+ // Like setExplicitMappingUnfilled, but from decode-scoped scratch. Only for
531
+ // attributes that never escape the decode (see ScratchArena) -- the portable
532
+ // attributes the sequential decoders build and discard.
533
+ setExplicitMappingScratch(numPoints) {
534
+ this._identityMapping = false;
535
+ this._indicesMap = scratchUint32(numPoints);
536
+ }
417
537
  setAttributeTransformData(transformData) {
418
538
  this._attributeTransformData = transformData;
419
539
  }
@@ -909,48 +1029,6 @@ var DecoderBuffer = class {
909
1029
  }
910
1030
  };
911
1031
 
912
- // src/decoder/core/ScratchArena.ts
913
- var freeInt32 = [];
914
- var freeUint8 = [];
915
- var borrowedInt32 = [];
916
- var borrowedUint8 = [];
917
- var acquire = (free, borrowed, size) => {
918
- for (let i = free.length - 1; i >= 0; --i) {
919
- const buffer = free[i];
920
- if (buffer.length >= size) {
921
- free[i] = free[free.length - 1];
922
- free.pop();
923
- borrowed.push(buffer);
924
- return buffer;
925
- }
926
- }
927
- return null;
928
- };
929
- var scratchInt32 = (size) => {
930
- const pooled = acquire(freeInt32, borrowedInt32, size);
931
- if (pooled !== null) return pooled.subarray(0, size);
932
- const fresh = new Int32Array(size);
933
- borrowedInt32.push(fresh);
934
- return fresh;
935
- };
936
- var scratchUint8Zeroed = (size) => {
937
- const pooled = acquire(freeUint8, borrowedUint8, size);
938
- if (pooled !== null) {
939
- const view = pooled.subarray(0, size);
940
- view.fill(0);
941
- return view;
942
- }
943
- const fresh = new Uint8Array(size);
944
- borrowedUint8.push(fresh);
945
- return fresh;
946
- };
947
- var releaseScratch = () => {
948
- for (const buffer of borrowedInt32) freeInt32.push(buffer);
949
- for (const buffer of borrowedUint8) freeUint8.push(buffer);
950
- borrowedInt32.length = 0;
951
- borrowedUint8.length = 0;
952
- };
953
-
954
1032
  // src/decoder/compression/config/CompressionShared.ts
955
1033
  var kDracoPointCloudBitstreamVersionMajor = 2;
956
1034
  var kDracoPointCloudBitstreamVersionMinor = 3;
@@ -1182,82 +1260,705 @@ var MetadataDecoder = class {
1182
1260
  }
1183
1261
  };
1184
1262
 
1185
- // src/decoder/compression/point_cloud/PointCloudDecoder.ts
1186
- var PointCloudDecoder = class _PointCloudDecoder {
1187
- _pointCloud;
1188
- _buffer;
1189
- _versionMajor;
1190
- _versionMinor;
1191
- _options;
1192
- _attributesDecoders;
1193
- _attributeToDecoderMap;
1263
+ // src/decoder/compression/entropy/ANSCoding.ts
1264
+ var ANS_P8_PRECISION = 256;
1265
+ var ANS_L_BASE = 4096;
1266
+ var ANS_IO_BASE = 256;
1267
+ var COARSE_STREAM_FACTOR = 8;
1268
+ var COARSE_BUCKET_BITS = 8;
1269
+ function memGetLe16(buf, offset) {
1270
+ return buf[offset] | buf[offset + 1] << 8;
1271
+ }
1272
+ function memGetLe24(buf, offset) {
1273
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16;
1274
+ }
1275
+ function memGetLe32(buf, offset) {
1276
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0;
1277
+ }
1278
+ var AnsDecoder = class {
1279
+ buf;
1280
+ bufOffset;
1281
+ // First valid byte of this decoder's slice within buf: init is passed
1282
+ // absolute offsets into the source buffer to avoid a subarray allocation.
1283
+ bufStart;
1284
+ state;
1194
1285
  constructor() {
1195
- this._pointCloud = null;
1196
- this._buffer = null;
1197
- this._versionMajor = 0;
1198
- this._versionMinor = 0;
1199
- this._options = null;
1200
- this._attributesDecoders = [];
1201
- this._attributeToDecoderMap = [];
1286
+ this.buf = null;
1287
+ this.bufOffset = 0;
1288
+ this.bufStart = 0;
1289
+ this.state = 0;
1202
1290
  }
1203
- getGeometryType() {
1204
- return EncodedGeometryType.POINT_CLOUD;
1291
+ };
1292
+ function ansReadInit(ans, buf, offset, base = 0) {
1293
+ if (offset - base < 1) {
1294
+ return 1;
1205
1295
  }
1206
- // Returns a Status; on success outHeader is populated.
1207
- static decodeHeader(buffer, outHeader) {
1208
- const kIoErrorMsg = "Failed to parse Draco header.";
1209
- const bytes = buffer.decodeBytes(5);
1210
- if (bytes === void 0) {
1211
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1296
+ ans.buf = buf;
1297
+ ans.bufStart = base;
1298
+ const x = buf[offset - 1] >> 6;
1299
+ if (x === 0) {
1300
+ ans.bufOffset = offset - 1;
1301
+ ans.state = buf[offset - 1] & 63;
1302
+ } else if (x === 1) {
1303
+ if (offset - base < 2) {
1304
+ return 1;
1212
1305
  }
1213
- for (let i = 0; i < 5; i++) {
1214
- outHeader.dracoString[i] = bytes[i];
1306
+ ans.bufOffset = offset - 2;
1307
+ ans.state = memGetLe16(buf, offset - 2) & 16383;
1308
+ } else if (x === 2) {
1309
+ if (offset - base < 3) {
1310
+ return 1;
1215
1311
  }
1216
- const magic = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]);
1217
- if (magic !== "DRACO") {
1218
- return new Status(StatusCode.DRACO_ERROR, "Not a Draco file.");
1312
+ ans.bufOffset = offset - 3;
1313
+ ans.state = memGetLe24(buf, offset - 3) & 4194303;
1314
+ } else {
1315
+ return 1;
1316
+ }
1317
+ ans.state += ANS_L_BASE;
1318
+ if (ans.state >= ANS_L_BASE * ANS_IO_BASE) {
1319
+ return 1;
1320
+ }
1321
+ return 0;
1322
+ }
1323
+ function ansReadEnd(ans) {
1324
+ return ans.state === ANS_L_BASE;
1325
+ }
1326
+ var tablePool = [];
1327
+ var acquirePooled = (Ctor, size) => {
1328
+ for (let i = tablePool.length - 1; i >= 0; --i) {
1329
+ const buf = tablePool[i];
1330
+ if (buf.constructor === Ctor && buf.length >= size) {
1331
+ tablePool[i] = tablePool[tablePool.length - 1];
1332
+ tablePool.pop();
1333
+ return buf;
1219
1334
  }
1220
- const versionMajor = buffer.decodeUint8();
1221
- if (versionMajor === void 0) {
1222
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1335
+ }
1336
+ return new Ctor(size);
1337
+ };
1338
+ var RAnsDecoder = class {
1339
+ ransPrecisionBits;
1340
+ ransPrecision;
1341
+ ransPrecisionMask;
1342
+ lRansBase;
1343
+ lutTable;
1344
+ probTable;
1345
+ cumProbTable;
1346
+ // Short-stream mode (see ransBuildLookUpTable): no full-precision lut; a
1347
+ // 256-entry bucket table narrows each lookup to a symbol range that a short
1348
+ // cumProb scan finishes. cumProbTable then carries one extra trailing entry
1349
+ // (== ransPrecision) so the scan needs no bounds check.
1350
+ coarse;
1351
+ bucketShift;
1352
+ bucketTable;
1353
+ buf;
1354
+ bufOffset;
1355
+ // First valid byte of this decoder's slice within buf (absolute offsets,
1356
+ // see AnsDecoder.bufStart).
1357
+ bufStart;
1358
+ state;
1359
+ constructor(ransPrecisionBits) {
1360
+ this.ransPrecisionBits = ransPrecisionBits;
1361
+ this.ransPrecision = 1 << ransPrecisionBits;
1362
+ this.ransPrecisionMask = this.ransPrecision - 1;
1363
+ this.lRansBase = this.ransPrecision * 4;
1364
+ this.lutTable = null;
1365
+ this.probTable = null;
1366
+ this.cumProbTable = null;
1367
+ this.coarse = false;
1368
+ this.bucketShift = 0;
1369
+ this.bucketTable = null;
1370
+ this.buf = null;
1371
+ this.bufOffset = 0;
1372
+ this.bufStart = 0;
1373
+ this.state = 0;
1374
+ }
1375
+ // offset is the absolute end of the encoded bytes within buf and base the
1376
+ // absolute start (offset - base = encoded length). Passing the source
1377
+ // buffer with absolute offsets avoids a subarray allocation per init.
1378
+ // Returns 0 on success, non-zero on error.
1379
+ readInit(buf, offset, base = 0) {
1380
+ if (offset - base < 1) {
1381
+ return 1;
1223
1382
  }
1224
- outHeader.versionMajor = versionMajor;
1225
- const versionMinor = buffer.decodeUint8();
1226
- if (versionMinor === void 0) {
1227
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1383
+ this.buf = buf;
1384
+ this.bufStart = base;
1385
+ const x = buf[offset - 1] >> 6;
1386
+ if (x === 0) {
1387
+ this.bufOffset = offset - 1;
1388
+ this.state = buf[offset - 1] & 63;
1389
+ } else if (x === 1) {
1390
+ if (offset - base < 2) {
1391
+ return 1;
1392
+ }
1393
+ this.bufOffset = offset - 2;
1394
+ this.state = memGetLe16(buf, offset - 2) & 16383;
1395
+ } else if (x === 2) {
1396
+ if (offset - base < 3) {
1397
+ return 1;
1398
+ }
1399
+ this.bufOffset = offset - 3;
1400
+ this.state = memGetLe24(buf, offset - 3) & 4194303;
1401
+ } else if (x === 3) {
1402
+ if (offset - base < 4) {
1403
+ return 1;
1404
+ }
1405
+ this.bufOffset = offset - 4;
1406
+ this.state = memGetLe32(buf, offset - 4) & 1073741823;
1407
+ } else {
1408
+ return 1;
1228
1409
  }
1229
- outHeader.versionMinor = versionMinor;
1230
- const encoderType = buffer.decodeUint8();
1231
- if (encoderType === void 0) {
1232
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1410
+ this.state += this.lRansBase;
1411
+ if (this.state >= this.lRansBase * ANS_IO_BASE) {
1412
+ return 1;
1233
1413
  }
1234
- outHeader.encoderType = encoderType;
1235
- const encoderMethod = buffer.decodeUint8();
1236
- if (encoderMethod === void 0) {
1237
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1414
+ return 0;
1415
+ }
1416
+ readEnd() {
1417
+ if (this.lutTable !== null) {
1418
+ tablePool.push(this.lutTable);
1419
+ this.lutTable = null;
1238
1420
  }
1239
- outHeader.encoderMethod = encoderMethod;
1240
- const flags = buffer.decodeUint16();
1241
- if (flags === void 0) {
1242
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1421
+ if (this.probTable !== null) {
1422
+ tablePool.push(this.probTable);
1423
+ this.probTable = null;
1243
1424
  }
1244
- outHeader.flags = flags;
1245
- return okStatus();
1246
- }
1247
- // Main entry point for point cloud decoding.
1248
- decode(options, inBuffer, outPointCloud) {
1249
- this._options = options;
1250
- this._buffer = inBuffer;
1251
- this._pointCloud = outPointCloud;
1252
- const header = new DracoHeader();
1253
- const headerStatus = _PointCloudDecoder.decodeHeader(this._buffer, header);
1254
- if (!headerStatus.ok()) {
1255
- return headerStatus;
1425
+ if (this.cumProbTable !== null) {
1426
+ tablePool.push(this.cumProbTable);
1427
+ this.cumProbTable = null;
1256
1428
  }
1257
- if (header.encoderType !== this.getGeometryType()) {
1258
- return new Status(StatusCode.DRACO_ERROR, "Using incompatible decoder for the input geometry.");
1429
+ if (this.bucketTable !== null) {
1430
+ tablePool.push(this.bucketTable);
1431
+ this.bucketTable = null;
1259
1432
  }
1260
- this._versionMajor = header.versionMajor;
1433
+ return this.state === this.lRansBase;
1434
+ }
1435
+ ransRead() {
1436
+ const buf = this.buf;
1437
+ const lRansBase = this.lRansBase;
1438
+ let state = this.state;
1439
+ let bufOffset = this.bufOffset;
1440
+ const bufStart = this.bufStart;
1441
+ while (state < lRansBase && bufOffset > bufStart) {
1442
+ state = state << 8 | buf[--bufOffset];
1443
+ }
1444
+ const quo = state >>> this.ransPrecisionBits;
1445
+ const rem = state & this.ransPrecisionMask;
1446
+ let symbol;
1447
+ if (this.coarse) {
1448
+ const cumProbTable = this.cumProbTable;
1449
+ symbol = this.bucketTable[rem >> this.bucketShift];
1450
+ while (cumProbTable[symbol + 1] <= rem) symbol++;
1451
+ } else {
1452
+ symbol = this.lutTable[rem];
1453
+ }
1454
+ this.state = quo * this.probTable[symbol] + rem - this.cumProbTable[symbol];
1455
+ this.bufOffset = bufOffset;
1456
+ return symbol;
1457
+ }
1458
+ // Batch ransRead() into out[0..count): all fields hoisted to locals, state
1459
+ // written back once. Removes per-symbol property reads and call indirection.
1460
+ // lutTable's element type varies per decoder (Uint8/16/32 by symbol count),
1461
+ // which would make the hot lutTable[rem] access site polymorphic — dispatch
1462
+ // once here so each loop body stays monomorphic on its concrete type. The
1463
+ // three bodies are intentionally identical copies.
1464
+ decodeSymbols(out, count) {
1465
+ if (this.coarse) {
1466
+ this._decodeSymbolsCoarse(out, count);
1467
+ return;
1468
+ }
1469
+ const lutTable = this.lutTable;
1470
+ if (lutTable instanceof Uint8Array) {
1471
+ this._decodeSymbolsU8(out, count, lutTable);
1472
+ } else if (lutTable instanceof Uint16Array) {
1473
+ this._decodeSymbolsU16(out, count, lutTable);
1474
+ } else {
1475
+ this._decodeSymbolsU32(out, count, lutTable);
1476
+ }
1477
+ }
1478
+ _decodeSymbolsU8(out, count, lutTable) {
1479
+ const buf = this.buf;
1480
+ const lRansBase = this.lRansBase;
1481
+ const ransPrecisionBits = this.ransPrecisionBits;
1482
+ const ransPrecisionMask = this.ransPrecisionMask;
1483
+ const probTable = this.probTable;
1484
+ const cumProbTable = this.cumProbTable;
1485
+ let state = this.state;
1486
+ let bufOffset = this.bufOffset;
1487
+ const bufStart = this.bufStart;
1488
+ for (let i = 0; i < count; ++i) {
1489
+ while (state < lRansBase && bufOffset > bufStart) {
1490
+ state = state << 8 | buf[--bufOffset];
1491
+ }
1492
+ const rem = state & ransPrecisionMask;
1493
+ const symbol = lutTable[rem];
1494
+ out[i] = symbol;
1495
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1496
+ }
1497
+ this.state = state;
1498
+ this.bufOffset = bufOffset;
1499
+ }
1500
+ _decodeSymbolsU16(out, count, lutTable) {
1501
+ const buf = this.buf;
1502
+ const lRansBase = this.lRansBase;
1503
+ const ransPrecisionBits = this.ransPrecisionBits;
1504
+ const ransPrecisionMask = this.ransPrecisionMask;
1505
+ const probTable = this.probTable;
1506
+ const cumProbTable = this.cumProbTable;
1507
+ let state = this.state;
1508
+ let bufOffset = this.bufOffset;
1509
+ const bufStart = this.bufStart;
1510
+ for (let i = 0; i < count; ++i) {
1511
+ while (state < lRansBase && bufOffset > bufStart) {
1512
+ state = state << 8 | buf[--bufOffset];
1513
+ }
1514
+ const rem = state & ransPrecisionMask;
1515
+ const symbol = lutTable[rem];
1516
+ out[i] = symbol;
1517
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1518
+ }
1519
+ this.state = state;
1520
+ this.bufOffset = bufOffset;
1521
+ }
1522
+ // Short-stream loop: bucket table + cumProb scan instead of the full lut.
1523
+ // Same state arithmetic as the lut loops, so the output is identical.
1524
+ _decodeSymbolsCoarse(out, count) {
1525
+ const buf = this.buf;
1526
+ const lRansBase = this.lRansBase;
1527
+ const ransPrecisionBits = this.ransPrecisionBits;
1528
+ const ransPrecisionMask = this.ransPrecisionMask;
1529
+ const probTable = this.probTable;
1530
+ const cumProbTable = this.cumProbTable;
1531
+ const bucketTable = this.bucketTable;
1532
+ const bucketShift = this.bucketShift;
1533
+ let state = this.state;
1534
+ let bufOffset = this.bufOffset;
1535
+ const bufStart = this.bufStart;
1536
+ for (let i = 0; i < count; ++i) {
1537
+ while (state < lRansBase && bufOffset > bufStart) {
1538
+ state = state << 8 | buf[--bufOffset];
1539
+ }
1540
+ const rem = state & ransPrecisionMask;
1541
+ let symbol = bucketTable[rem >> bucketShift];
1542
+ while (cumProbTable[symbol + 1] <= rem) symbol++;
1543
+ out[i] = symbol;
1544
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1545
+ }
1546
+ this.state = state;
1547
+ this.bufOffset = bufOffset;
1548
+ }
1549
+ _decodeSymbolsU32(out, count, lutTable) {
1550
+ const buf = this.buf;
1551
+ const lRansBase = this.lRansBase;
1552
+ const ransPrecisionBits = this.ransPrecisionBits;
1553
+ const ransPrecisionMask = this.ransPrecisionMask;
1554
+ const probTable = this.probTable;
1555
+ const cumProbTable = this.cumProbTable;
1556
+ let state = this.state;
1557
+ let bufOffset = this.bufOffset;
1558
+ const bufStart = this.bufStart;
1559
+ for (let i = 0; i < count; ++i) {
1560
+ while (state < lRansBase && bufOffset > bufStart) {
1561
+ state = state << 8 | buf[--bufOffset];
1562
+ }
1563
+ const rem = state & ransPrecisionMask;
1564
+ const symbol = lutTable[rem];
1565
+ out[i] = symbol;
1566
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1567
+ }
1568
+ this.state = state;
1569
+ this.bufOffset = bufOffset;
1570
+ }
1571
+ // Builds the decoding tables. Returns false on bad input data.
1572
+ //
1573
+ // expectedCount is how many symbols the caller will decode from this stream.
1574
+ // The full lut has ransPrecision (>= 4096) entries, and a primitive-heavy
1575
+ // file builds thousands of them to decode a few dozen symbols each -- on such
1576
+ // files the table builds cost more than the decodes. Short streams therefore
1577
+ // get a coarse 256-entry bucket table (symbol at the start of each
1578
+ // precision/256-wide bucket) and finish each lookup with a scan of the
1579
+ // cumulative probabilities; long streams keep the exact lut, whose per-symbol
1580
+ // cost is lower.
1581
+ ransBuildLookUpTable(tokenProbs, numSymbols, expectedCount = 2147483647) {
1582
+ const ransPrecision = this.ransPrecision;
1583
+ const coarse = numSymbols <= 65535 && expectedCount * COARSE_STREAM_FACTOR < ransPrecision;
1584
+ this.coarse = coarse;
1585
+ const probTable = acquirePooled(Uint32Array, numSymbols);
1586
+ const cumProbTable = acquirePooled(Uint32Array, numSymbols + 1);
1587
+ this.probTable = probTable;
1588
+ this.cumProbTable = cumProbTable;
1589
+ let cumProb = 0;
1590
+ if (coarse) {
1591
+ this.lutTable = null;
1592
+ for (let i = 0; i < numSymbols; ++i) {
1593
+ const prob = tokenProbs[i];
1594
+ probTable[i] = prob;
1595
+ cumProbTable[i] = cumProb;
1596
+ cumProb += prob;
1597
+ if (cumProb > ransPrecision) {
1598
+ return false;
1599
+ }
1600
+ }
1601
+ if (cumProb !== ransPrecision) {
1602
+ return false;
1603
+ }
1604
+ cumProbTable[numSymbols] = ransPrecision;
1605
+ const bucketShift = this.ransPrecisionBits - COARSE_BUCKET_BITS;
1606
+ const bucketTable = acquirePooled(Uint16Array, 1 << COARSE_BUCKET_BITS);
1607
+ this.bucketShift = bucketShift;
1608
+ this.bucketTable = bucketTable;
1609
+ let symbol = 0;
1610
+ for (let b = 0; b < 1 << COARSE_BUCKET_BITS; ++b) {
1611
+ const rem = b << bucketShift;
1612
+ while (cumProbTable[symbol + 1] <= rem) symbol++;
1613
+ bucketTable[b] = symbol;
1614
+ }
1615
+ return true;
1616
+ }
1617
+ const LutArray = numSymbols <= 256 ? Uint8Array : numSymbols <= 65536 ? Uint16Array : Uint32Array;
1618
+ const lutTable = acquirePooled(LutArray, ransPrecision);
1619
+ this.lutTable = lutTable;
1620
+ let actProb = 0;
1621
+ for (let i = 0; i < numSymbols; ++i) {
1622
+ const prob = tokenProbs[i];
1623
+ probTable[i] = prob;
1624
+ cumProbTable[i] = cumProb;
1625
+ cumProb += prob;
1626
+ if (cumProb > ransPrecision) {
1627
+ return false;
1628
+ }
1629
+ if (prob < 32) {
1630
+ for (let j = actProb; j < cumProb; ++j) {
1631
+ lutTable[j] = i;
1632
+ }
1633
+ } else {
1634
+ lutTable.fill(i, actProb, cumProb);
1635
+ }
1636
+ actProb = cumProb;
1637
+ }
1638
+ if (cumProb !== ransPrecision) {
1639
+ return false;
1640
+ }
1641
+ cumProbTable[numSymbols] = ransPrecision;
1642
+ return true;
1643
+ }
1644
+ };
1645
+ function ransDecodeSymbolsPairU8(a, outA, countA, b, outB, countB) {
1646
+ const lutA = a.lutTable;
1647
+ const lutB = b.lutTable;
1648
+ const bufA = a.buf;
1649
+ const bufB = b.buf;
1650
+ const probA = a.probTable;
1651
+ const probB = b.probTable;
1652
+ const cumA = a.cumProbTable;
1653
+ const cumB = b.cumProbTable;
1654
+ const lBaseA = a.lRansBase;
1655
+ const lBaseB = b.lRansBase;
1656
+ const bitsA = a.ransPrecisionBits;
1657
+ const bitsB = b.ransPrecisionBits;
1658
+ const maskA = a.ransPrecisionMask;
1659
+ const maskB = b.ransPrecisionMask;
1660
+ const startA = a.bufStart;
1661
+ const startB = b.bufStart;
1662
+ let stateA = a.state;
1663
+ let stateB = b.state;
1664
+ let offA = a.bufOffset;
1665
+ let offB = b.bufOffset;
1666
+ const shared = countA < countB ? countA : countB;
1667
+ for (let i = 0; i < shared; ++i) {
1668
+ while (stateA < lBaseA && offA > startA) {
1669
+ stateA = stateA << 8 | bufA[--offA];
1670
+ }
1671
+ while (stateB < lBaseB && offB > startB) {
1672
+ stateB = stateB << 8 | bufB[--offB];
1673
+ }
1674
+ const remA = stateA & maskA;
1675
+ const remB = stateB & maskB;
1676
+ const symA = lutA[remA];
1677
+ const symB = lutB[remB];
1678
+ outA[i] = symA;
1679
+ outB[i] = symB;
1680
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1681
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1682
+ }
1683
+ a.state = stateA;
1684
+ a.bufOffset = offA;
1685
+ b.state = stateB;
1686
+ b.bufOffset = offB;
1687
+ if (shared < countA) {
1688
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1689
+ }
1690
+ if (shared < countB) {
1691
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1692
+ }
1693
+ }
1694
+ function ransDecodeSymbolsPair(a, outA, countA, b, outB, countB) {
1695
+ const lutA = a.lutTable;
1696
+ const lutB = b.lutTable;
1697
+ if (lutA instanceof Uint8Array && lutB instanceof Uint8Array) {
1698
+ ransDecodeSymbolsPairU8(a, outA, countA, b, outB, countB);
1699
+ } else if (lutA instanceof Uint16Array && lutB instanceof Uint16Array) {
1700
+ ransDecodeSymbolsPairU16(a, outA, countA, b, outB, countB);
1701
+ } else if (lutA instanceof Uint8Array && lutB instanceof Uint16Array) {
1702
+ ransDecodeSymbolsPairU8U16(a, outA, countA, b, outB, countB);
1703
+ } else if (lutA instanceof Uint16Array && lutB instanceof Uint8Array) {
1704
+ ransDecodeSymbolsPairU8U16(b, outB, countB, a, outA, countA);
1705
+ } else {
1706
+ a.decodeSymbols(outA, countA);
1707
+ b.decodeSymbols(outB, countB);
1708
+ }
1709
+ }
1710
+ function ransDecodeSymbolsPairU16(a, outA, countA, b, outB, countB) {
1711
+ const lutA = a.lutTable;
1712
+ const lutB = b.lutTable;
1713
+ const bufA = a.buf;
1714
+ const bufB = b.buf;
1715
+ const probA = a.probTable;
1716
+ const probB = b.probTable;
1717
+ const cumA = a.cumProbTable;
1718
+ const cumB = b.cumProbTable;
1719
+ const lBaseA = a.lRansBase;
1720
+ const lBaseB = b.lRansBase;
1721
+ const bitsA = a.ransPrecisionBits;
1722
+ const bitsB = b.ransPrecisionBits;
1723
+ const maskA = a.ransPrecisionMask;
1724
+ const maskB = b.ransPrecisionMask;
1725
+ const startA = a.bufStart;
1726
+ const startB = b.bufStart;
1727
+ let stateA = a.state;
1728
+ let stateB = b.state;
1729
+ let offA = a.bufOffset;
1730
+ let offB = b.bufOffset;
1731
+ const shared = countA < countB ? countA : countB;
1732
+ for (let i = 0; i < shared; ++i) {
1733
+ while (stateA < lBaseA && offA > startA) {
1734
+ stateA = stateA << 8 | bufA[--offA];
1735
+ }
1736
+ while (stateB < lBaseB && offB > startB) {
1737
+ stateB = stateB << 8 | bufB[--offB];
1738
+ }
1739
+ const remA = stateA & maskA;
1740
+ const remB = stateB & maskB;
1741
+ const symA = lutA[remA];
1742
+ const symB = lutB[remB];
1743
+ outA[i] = symA;
1744
+ outB[i] = symB;
1745
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1746
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1747
+ }
1748
+ a.state = stateA;
1749
+ a.bufOffset = offA;
1750
+ b.state = stateB;
1751
+ b.bufOffset = offB;
1752
+ if (shared < countA) {
1753
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1754
+ }
1755
+ if (shared < countB) {
1756
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1757
+ }
1758
+ }
1759
+ function ransDecodeSymbolsPairU8U16(a, outA, countA, b, outB, countB) {
1760
+ const lutA = a.lutTable;
1761
+ const lutB = b.lutTable;
1762
+ const bufA = a.buf;
1763
+ const bufB = b.buf;
1764
+ const probA = a.probTable;
1765
+ const probB = b.probTable;
1766
+ const cumA = a.cumProbTable;
1767
+ const cumB = b.cumProbTable;
1768
+ const lBaseA = a.lRansBase;
1769
+ const lBaseB = b.lRansBase;
1770
+ const bitsA = a.ransPrecisionBits;
1771
+ const bitsB = b.ransPrecisionBits;
1772
+ const maskA = a.ransPrecisionMask;
1773
+ const maskB = b.ransPrecisionMask;
1774
+ const startA = a.bufStart;
1775
+ const startB = b.bufStart;
1776
+ let stateA = a.state;
1777
+ let stateB = b.state;
1778
+ let offA = a.bufOffset;
1779
+ let offB = b.bufOffset;
1780
+ const shared = countA < countB ? countA : countB;
1781
+ for (let i = 0; i < shared; ++i) {
1782
+ while (stateA < lBaseA && offA > startA) {
1783
+ stateA = stateA << 8 | bufA[--offA];
1784
+ }
1785
+ while (stateB < lBaseB && offB > startB) {
1786
+ stateB = stateB << 8 | bufB[--offB];
1787
+ }
1788
+ const remA = stateA & maskA;
1789
+ const remB = stateB & maskB;
1790
+ const symA = lutA[remA];
1791
+ const symB = lutB[remB];
1792
+ outA[i] = symA;
1793
+ outB[i] = symB;
1794
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1795
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1796
+ }
1797
+ a.state = stateA;
1798
+ a.bufOffset = offA;
1799
+ b.state = stateB;
1800
+ b.bufOffset = offB;
1801
+ if (shared < countA) {
1802
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1803
+ }
1804
+ if (shared < countB) {
1805
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1806
+ }
1807
+ }
1808
+ function ransDecodeSymbolsTrioU8(a, outA, countA, b, outB, countB, c, outC, countC) {
1809
+ const lutA = a.lutTable;
1810
+ const lutB = b.lutTable;
1811
+ const lutC = c.lutTable;
1812
+ const bufA = a.buf;
1813
+ const bufB = b.buf;
1814
+ const bufC = c.buf;
1815
+ const probA = a.probTable;
1816
+ const probB = b.probTable;
1817
+ const probC = c.probTable;
1818
+ const cumA = a.cumProbTable;
1819
+ const cumB = b.cumProbTable;
1820
+ const cumC = c.cumProbTable;
1821
+ const lBaseA = a.lRansBase;
1822
+ const lBaseB = b.lRansBase;
1823
+ const lBaseC = c.lRansBase;
1824
+ const bitsA = a.ransPrecisionBits;
1825
+ const bitsB = b.ransPrecisionBits;
1826
+ const bitsC = c.ransPrecisionBits;
1827
+ const maskA = a.ransPrecisionMask;
1828
+ const maskB = b.ransPrecisionMask;
1829
+ const maskC = c.ransPrecisionMask;
1830
+ const startA = a.bufStart;
1831
+ const startB = b.bufStart;
1832
+ const startC = c.bufStart;
1833
+ let stateA = a.state;
1834
+ let stateB = b.state;
1835
+ let stateC = c.state;
1836
+ let offA = a.bufOffset;
1837
+ let offB = b.bufOffset;
1838
+ let offC = c.bufOffset;
1839
+ let shared = countA < countB ? countA : countB;
1840
+ if (countC < shared) shared = countC;
1841
+ for (let i = 0; i < shared; ++i) {
1842
+ while (stateA < lBaseA && offA > startA) {
1843
+ stateA = stateA << 8 | bufA[--offA];
1844
+ }
1845
+ while (stateB < lBaseB && offB > startB) {
1846
+ stateB = stateB << 8 | bufB[--offB];
1847
+ }
1848
+ while (stateC < lBaseC && offC > startC) {
1849
+ stateC = stateC << 8 | bufC[--offC];
1850
+ }
1851
+ const remA = stateA & maskA;
1852
+ const remB = stateB & maskB;
1853
+ const remC = stateC & maskC;
1854
+ const symA = lutA[remA];
1855
+ const symB = lutB[remB];
1856
+ const symC = lutC[remC];
1857
+ outA[i] = symA;
1858
+ outB[i] = symB;
1859
+ outC[i] = symC;
1860
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1861
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1862
+ stateC = (stateC >>> bitsC) * probC[symC] + remC - cumC[symC];
1863
+ }
1864
+ a.state = stateA;
1865
+ a.bufOffset = offA;
1866
+ b.state = stateB;
1867
+ b.bufOffset = offB;
1868
+ c.state = stateC;
1869
+ c.bufOffset = offC;
1870
+ const restA = countA - shared;
1871
+ const restB = countB - shared;
1872
+ const restC = countC - shared;
1873
+ const tails = [];
1874
+ if (restA > 0) tails.push([a, outA.subarray(shared), restA]);
1875
+ if (restB > 0) tails.push([b, outB.subarray(shared), restB]);
1876
+ if (restC > 0) tails.push([c, outC.subarray(shared), restC]);
1877
+ if (tails.length === 2) {
1878
+ ransDecodeSymbolsPairU8(tails[0][0], tails[0][1], tails[0][2], tails[1][0], tails[1][1], tails[1][2]);
1879
+ } else {
1880
+ for (const [decoder, out, count] of tails) {
1881
+ decoder.decodeSymbols(out, count);
1882
+ }
1883
+ }
1884
+ }
1885
+
1886
+ // src/decoder/compression/point_cloud/PointCloudDecoder.ts
1887
+ var PointCloudDecoder = class _PointCloudDecoder {
1888
+ _pointCloud;
1889
+ _buffer;
1890
+ _versionMajor;
1891
+ _versionMinor;
1892
+ _options;
1893
+ _attributesDecoders;
1894
+ _attributeToDecoderMap;
1895
+ constructor() {
1896
+ this._pointCloud = null;
1897
+ this._buffer = null;
1898
+ this._versionMajor = 0;
1899
+ this._versionMinor = 0;
1900
+ this._options = null;
1901
+ this._attributesDecoders = [];
1902
+ this._attributeToDecoderMap = [];
1903
+ }
1904
+ getGeometryType() {
1905
+ return EncodedGeometryType.POINT_CLOUD;
1906
+ }
1907
+ // Returns a Status; on success outHeader is populated.
1908
+ static decodeHeader(buffer, outHeader) {
1909
+ const kIoErrorMsg = "Failed to parse Draco header.";
1910
+ const bytes = buffer.decodeBytes(5);
1911
+ if (bytes === void 0) {
1912
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1913
+ }
1914
+ for (let i = 0; i < 5; i++) {
1915
+ outHeader.dracoString[i] = bytes[i];
1916
+ }
1917
+ const magic = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]);
1918
+ if (magic !== "DRACO") {
1919
+ return new Status(StatusCode.DRACO_ERROR, "Not a Draco file.");
1920
+ }
1921
+ const versionMajor = buffer.decodeUint8();
1922
+ if (versionMajor === void 0) {
1923
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1924
+ }
1925
+ outHeader.versionMajor = versionMajor;
1926
+ const versionMinor = buffer.decodeUint8();
1927
+ if (versionMinor === void 0) {
1928
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1929
+ }
1930
+ outHeader.versionMinor = versionMinor;
1931
+ const encoderType = buffer.decodeUint8();
1932
+ if (encoderType === void 0) {
1933
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1934
+ }
1935
+ outHeader.encoderType = encoderType;
1936
+ const encoderMethod = buffer.decodeUint8();
1937
+ if (encoderMethod === void 0) {
1938
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1939
+ }
1940
+ outHeader.encoderMethod = encoderMethod;
1941
+ const flags = buffer.decodeUint16();
1942
+ if (flags === void 0) {
1943
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1944
+ }
1945
+ outHeader.flags = flags;
1946
+ return okStatus();
1947
+ }
1948
+ // Main entry point for point cloud decoding.
1949
+ decode(options, inBuffer, outPointCloud) {
1950
+ this._options = options;
1951
+ this._buffer = inBuffer;
1952
+ this._pointCloud = outPointCloud;
1953
+ const header = new DracoHeader();
1954
+ const headerStatus = _PointCloudDecoder.decodeHeader(this._buffer, header);
1955
+ if (!headerStatus.ok()) {
1956
+ return headerStatus;
1957
+ }
1958
+ if (header.encoderType !== this.getGeometryType()) {
1959
+ return new Status(StatusCode.DRACO_ERROR, "Using incompatible decoder for the input geometry.");
1960
+ }
1961
+ this._versionMajor = header.versionMajor;
1261
1962
  this._versionMinor = header.versionMinor;
1262
1963
  const maxSupportedMajorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMajor : kDracoMeshBitstreamVersionMajor;
1263
1964
  const maxSupportedMinorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMinor : kDracoMeshBitstreamVersionMinor;
@@ -1376,17 +2077,40 @@ var PointCloudDecoder = class _PointCloudDecoder {
1376
2077
  return true;
1377
2078
  }
1378
2079
  decodeAllAttributes() {
1379
- for (let i = 0; i < this._attributesDecoders.length; i++) {
1380
- if (!this._attributesDecoders[i].decodeAttributes(this._buffer)) {
2080
+ const decoders = this._attributesDecoders;
2081
+ for (let i2 = 0; i2 < decoders.length; i2++) {
2082
+ if (!decoders[i2].decodeAttributesParse(this._buffer)) {
1381
2083
  return false;
1382
2084
  }
1383
2085
  }
1384
- return true;
1385
- }
1386
- onAttributesDecoded() {
1387
- return true;
1388
- }
1389
- _decodeMetadata() {
2086
+ const pending = [];
2087
+ for (let i2 = 0; i2 < decoders.length; i2++) {
2088
+ decoders[i2].collectPendingSymbolStreams(pending);
2089
+ }
2090
+ const paired = pending.filter((stream) => !stream.ans.coarse);
2091
+ let i = 0;
2092
+ for (; i + 1 < paired.length; i += 2) {
2093
+ const a = paired[i];
2094
+ const b = paired[i + 1];
2095
+ ransDecodeSymbolsPair(a.ans, a.out, a.count, b.ans, b.out, b.count);
2096
+ }
2097
+ if (i < paired.length) {
2098
+ paired[i].ans.decodeSymbols(paired[i].out, paired[i].count);
2099
+ }
2100
+ for (const stream of pending) {
2101
+ if (stream.ans.coarse) stream.ans.decodeSymbols(stream.out, stream.count);
2102
+ }
2103
+ for (let k = 0; k < decoders.length; k++) {
2104
+ if (!decoders[k].decodeAttributesFinish()) {
2105
+ return false;
2106
+ }
2107
+ }
2108
+ return true;
2109
+ }
2110
+ onAttributesDecoded() {
2111
+ return true;
2112
+ }
2113
+ _decodeMetadata() {
1390
2114
  const metadataDecoder = new MetadataDecoder();
1391
2115
  if (!metadataDecoder.skipGeometryMetadata(this._buffer)) {
1392
2116
  return new Status(StatusCode.DRACO_ERROR, "Failed to decode metadata.");
@@ -1445,46 +2169,62 @@ var MeshAttributeCornerTable = class {
1445
2169
  no_interior_seams_;
1446
2170
  corner_to_vertex_map_;
1447
2171
  vertex_to_left_most_corner_map_;
1448
- vertex_to_attribute_entry_id_map_;
2172
+ // Attribute-vertex count. C++ keeps a vertex -> attribute-entry map here, but
2173
+ // the decoder only ever reads its size, so track the count directly instead
2174
+ // of allocating an Int32Array per attribute corner table.
2175
+ num_attribute_vertices_;
1449
2176
  corner_table_;
1450
2177
  // Lazily built; see oppositeCornerArray.
1451
2178
  _effectiveOpposite;
1452
2179
  // Every corner passed to addSeamEdge (may contain duplicates); lets
1453
2180
  // oppositeCornerArray patch seams without scanning every corner's flag.
2181
+ // Preallocated to its exact upper bound (2 per seam edge) by the caller via
2182
+ // reserveSeamEdges -- a plain array grown by push() was measurable on
2183
+ // seam-heavy files.
1454
2184
  _seamCorners;
2185
+ _numSeamCorners;
1455
2186
  constructor() {
1456
2187
  this.is_edge_on_seam_ = [];
1457
2188
  this.is_vertex_on_seam_ = [];
1458
2189
  this.no_interior_seams_ = true;
1459
2190
  this.corner_to_vertex_map_ = [];
1460
2191
  this.vertex_to_left_most_corner_map_ = [];
1461
- this.vertex_to_attribute_entry_id_map_ = [];
2192
+ this.num_attribute_vertices_ = 0;
1462
2193
  this.corner_table_ = null;
1463
2194
  this._effectiveOpposite = null;
1464
2195
  this._seamCorners = [];
2196
+ this._numSeamCorners = 0;
1465
2197
  }
1466
2198
  initEmpty(table) {
1467
2199
  if (table === null) {
1468
2200
  return false;
1469
2201
  }
1470
- this.is_edge_on_seam_ = new Uint8Array(table.numCorners());
1471
- this.is_vertex_on_seam_ = new Uint8Array(table.numVertices());
1472
- this.corner_to_vertex_map_ = new Int32Array(table.numCorners()).fill(kInvalidVertexIndex);
1473
- this.vertex_to_attribute_entry_id_map_ = [];
2202
+ this.is_edge_on_seam_ = scratchUint8Zeroed(table.numCorners());
2203
+ this.is_vertex_on_seam_ = scratchUint8Zeroed(table.numVertices());
2204
+ this.corner_to_vertex_map_ = scratchInt32Filled(table.numCorners(), kInvalidVertexIndex);
2205
+ this.num_attribute_vertices_ = 0;
1474
2206
  this.vertex_to_left_most_corner_map_ = [];
1475
2207
  this._effectiveOpposite = null;
1476
2208
  this._seamCorners = [];
2209
+ this._numSeamCorners = 0;
1477
2210
  this.corner_table_ = table;
1478
2211
  this.no_interior_seams_ = true;
1479
2212
  return true;
1480
2213
  }
2214
+ // Sizes the seam-corner list for numSeamEdges upcoming addSeamEdge calls
2215
+ // (each adds at most two corners). Decode-scoped scratch.
2216
+ reserveSeamEdges(numSeamEdges) {
2217
+ this._seamCorners = scratchInt32(numSeamEdges * 2);
2218
+ this._numSeamCorners = 0;
2219
+ }
1481
2220
  addSeamEdge(c) {
1482
2221
  const cornerToVertex = this.corner_table_.cornerToVertexArray();
1483
2222
  const oppositeCorners = this.corner_table_.oppositeCornerArray();
1484
2223
  const isEdge = this.is_edge_on_seam_;
1485
2224
  const isVert = this.is_vertex_on_seam_;
2225
+ const seamCorners = this._seamCorners;
1486
2226
  isEdge[c] = 1;
1487
- this._seamCorners.push(c);
2227
+ seamCorners[this._numSeamCorners++] = c;
1488
2228
  let rem = c - (c / 3 | 0) * 3;
1489
2229
  isVert[cornerToVertex[rem === 2 ? c - 2 : c + 1]] = 1;
1490
2230
  isVert[cornerToVertex[rem === 0 ? c + 2 : c - 1]] = 1;
@@ -1492,7 +2232,7 @@ var MeshAttributeCornerTable = class {
1492
2232
  if (oppCorner !== kInvalidCornerIndex) {
1493
2233
  this.no_interior_seams_ = false;
1494
2234
  isEdge[oppCorner] = 1;
1495
- this._seamCorners.push(oppCorner);
2235
+ seamCorners[this._numSeamCorners++] = oppCorner;
1496
2236
  rem = oppCorner - (oppCorner / 3 | 0) * 3;
1497
2237
  isVert[cornerToVertex[rem === 2 ? oppCorner - 2 : oppCorner + 1]] = 1;
1498
2238
  isVert[cornerToVertex[rem === 0 ? oppCorner + 2 : oppCorner - 1]] = 1;
@@ -1507,12 +2247,12 @@ var MeshAttributeCornerTable = class {
1507
2247
  const ct = this.corner_table_;
1508
2248
  const numCorners = ct.numCorners();
1509
2249
  const numBaseVertices = ct.numVertices();
1510
- const leftMostMap = new Int32Array(numCorners);
2250
+ const leftMostMap = scratchInt32(numCorners);
1511
2251
  const cornerToVertex = this.corner_to_vertex_map_;
1512
2252
  const isVertexOnSeam = this.is_vertex_on_seam_;
1513
2253
  const isEdgeOnSeam = this.is_edge_on_seam_;
1514
2254
  const seamOpp = this.oppositeCornerArray();
1515
- const baseOpp = ct.oppositeCornerArray();
2255
+ const swingRight = ct.swingRightArray();
1516
2256
  const vertexLeftmost = ct.vertexLeftmostCornerArray();
1517
2257
  let numNewVertices = 0;
1518
2258
  for (let v = 0; v < numBaseVertices; ++v) {
@@ -1522,14 +2262,10 @@ var MeshAttributeCornerTable = class {
1522
2262
  const firstVertId = numNewVertices++;
1523
2263
  leftMostMap[firstVertId] = c;
1524
2264
  cornerToVertex[c] = firstVertId;
1525
- let pv = c % 3 === 0 ? c + 2 : c - 1;
1526
- let bopp = baseOpp[pv];
1527
- let actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2265
+ let actC = swingRight[c];
1528
2266
  while (actC !== kInvalidCornerIndex && actC !== c) {
1529
2267
  cornerToVertex[actC] = firstVertId;
1530
- pv = actC % 3 === 0 ? actC + 2 : actC - 1;
1531
- bopp = baseOpp[pv];
1532
- actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2268
+ actC = swingRight[actC];
1533
2269
  }
1534
2270
  } else {
1535
2271
  let firstVertId = numNewVertices++;
@@ -1547,9 +2283,7 @@ var MeshAttributeCornerTable = class {
1547
2283
  }
1548
2284
  cornerToVertex[firstC] = firstVertId;
1549
2285
  leftMostMap[firstVertId] = firstC;
1550
- let pv = firstC % 3 === 0 ? firstC + 2 : firstC - 1;
1551
- let bopp = baseOpp[pv];
1552
- actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2286
+ actC = swingRight[firstC];
1553
2287
  while (actC !== kInvalidCornerIndex && actC !== firstC) {
1554
2288
  const nAct = actC % 3 === 2 ? actC - 2 : actC + 1;
1555
2289
  if (isEdgeOnSeam[nAct]) {
@@ -1557,13 +2291,11 @@ var MeshAttributeCornerTable = class {
1557
2291
  leftMostMap[firstVertId] = actC;
1558
2292
  }
1559
2293
  cornerToVertex[actC] = firstVertId;
1560
- pv = actC % 3 === 0 ? actC + 2 : actC - 1;
1561
- bopp = baseOpp[pv];
1562
- actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2294
+ actC = swingRight[actC];
1563
2295
  }
1564
2296
  }
1565
2297
  }
1566
- this.vertex_to_attribute_entry_id_map_ = new Int32Array(numNewVertices);
2298
+ this.num_attribute_vertices_ = numNewVertices;
1567
2299
  this.vertex_to_left_most_corner_map_ = leftMostMap.subarray(0, numNewVertices);
1568
2300
  return true;
1569
2301
  }
@@ -1589,7 +2321,7 @@ var MeshAttributeCornerTable = class {
1589
2321
  return this.next(this.opposite(this.next(corner)));
1590
2322
  }
1591
2323
  numVertices() {
1592
- return this.vertex_to_attribute_entry_id_map_.length;
2324
+ return this.num_attribute_vertices_;
1593
2325
  }
1594
2326
  numFaces() {
1595
2327
  return this.corner_table_.numFaces();
@@ -1617,12 +2349,13 @@ var MeshAttributeCornerTable = class {
1617
2349
  const nc = this.corner_table_.numCorners();
1618
2350
  const base = this.corner_table_.oppositeCornerArray();
1619
2351
  const seamCorners = this._seamCorners;
1620
- if (seamCorners.length === 0) {
2352
+ const numSeamCorners = this._numSeamCorners;
2353
+ if (numSeamCorners === 0) {
1621
2354
  this._effectiveOpposite = base;
1622
2355
  } else {
1623
2356
  const eff = scratchInt32(nc);
1624
2357
  eff.set(base.length === nc ? base : base.subarray(0, nc));
1625
- for (let i = 0, l = seamCorners.length; i < l; ++i) {
2358
+ for (let i = 0; i < numSeamCorners; ++i) {
1626
2359
  eff[seamCorners[i]] = kInvalidCornerIndex;
1627
2360
  }
1628
2361
  this._effectiveOpposite = eff;
@@ -1637,23 +2370,20 @@ var MeshAttributeCornerTable = class {
1637
2370
  vertexOnSeamArray() {
1638
2371
  return this.is_vertex_on_seam_;
1639
2372
  }
1640
- hasSameSeams(other) {
1641
- if (other === null || other === void 0) return false;
1642
- const seamA = this.is_edge_on_seam_;
1643
- const seamB = other.is_edge_on_seam_;
1644
- if (seamA.length !== seamB.length) return false;
1645
- for (let i = 0, l = seamA.length; i < l; ++i) {
1646
- if (seamA[i] !== seamB[i]) return false;
1647
- }
1648
- return true;
1649
- }
1650
- adoptVertexRecompute(other) {
2373
+ // Takes over another table's state wholesale. Only valid when both tables
2374
+ // were built from the same corner table and the same seam edges, in which
2375
+ // case every one of these is identical and read-only from here on.
2376
+ adoptFrom(other) {
2377
+ this.corner_table_ = other.corner_table_;
2378
+ this.is_edge_on_seam_ = other.is_edge_on_seam_;
2379
+ this.is_vertex_on_seam_ = other.is_vertex_on_seam_;
1651
2380
  this.corner_to_vertex_map_ = other.corner_to_vertex_map_;
1652
- this.vertex_to_attribute_entry_id_map_ = other.vertex_to_attribute_entry_id_map_;
2381
+ this.num_attribute_vertices_ = other.num_attribute_vertices_;
1653
2382
  this.vertex_to_left_most_corner_map_ = other.vertex_to_left_most_corner_map_;
1654
2383
  this.no_interior_seams_ = other.no_interior_seams_;
1655
2384
  this._effectiveOpposite = other._effectiveOpposite;
1656
2385
  this._seamCorners = other._seamCorners;
2386
+ this._numSeamCorners = other._numSeamCorners;
1657
2387
  }
1658
2388
  };
1659
2389
 
@@ -1667,6 +2397,21 @@ var AttributesDecoderInterface = class {
1667
2397
  decodeAttributesDecoderData(_buffer) {
1668
2398
  return false;
1669
2399
  }
2400
+ // --- Optional two-phase decode across attributes decoders ---
2401
+ // PointCloudDecoder parses every decoder first (all buffer reads are
2402
+ // size-driven, so parsing runs ahead of the deferred rANS symbol decodes),
2403
+ // then decodes the collected streams two at a time, then finishes each
2404
+ // decoder in order (so parent attributes complete before dependents).
2405
+ // Defaults keep the original single-phase behavior for decoders that do not
2406
+ // split.
2407
+ decodeAttributesParse(buffer) {
2408
+ return this.decodeAttributes(buffer);
2409
+ }
2410
+ collectPendingSymbolStreams(_out) {
2411
+ }
2412
+ decodeAttributesFinish() {
2413
+ return true;
2414
+ }
1670
2415
  decodeAttributes(_buffer) {
1671
2416
  return false;
1672
2417
  }
@@ -1813,6 +2558,22 @@ var SequentialAttributeDecoder = class {
1813
2558
  this._attributeId = attributeId;
1814
2559
  return true;
1815
2560
  }
2561
+ // --- Optional two-phase decode ---
2562
+ // The controller calls Parse for every attribute first (headers, schemes,
2563
+ // prediction data -- all size-driven cursor movement), collects the pending
2564
+ // rANS symbol streams, decodes them in pairs (see ransDecodeSymbolsPair),
2565
+ // then calls Finish per attribute in order. Decoders without a deferrable
2566
+ // stream simply do the whole decode in Parse. Defaults preserve the
2567
+ // original single-phase behavior.
2568
+ decodePortableAttributeParse(pointIds, buffer) {
2569
+ return this.decodePortableAttribute(pointIds, buffer);
2570
+ }
2571
+ pendingSymbolStream() {
2572
+ return null;
2573
+ }
2574
+ decodePortableAttributeFinish() {
2575
+ return true;
2576
+ }
1816
2577
  decodePortableAttribute(pointIds, buffer) {
1817
2578
  if (this._attribute.numComponents <= 0) {
1818
2579
  return false;
@@ -1833,7 +2594,7 @@ var SequentialAttributeDecoder = class {
1833
2594
  getPortableAttribute() {
1834
2595
  if (!this._attribute.isMappingIdentity && this._portableAttribute && this._portableAttribute.isMappingIdentity) {
1835
2596
  const size = this._attribute.indicesMapSize;
1836
- this._portableAttribute.setExplicitMappingUnfilled(size);
2597
+ this._portableAttribute.setExplicitMappingScratch(size);
1837
2598
  const src = this._attribute.indicesMap;
1838
2599
  const dst = this._portableAttribute.indicesMap;
1839
2600
  if (src.length === size) {
@@ -1875,304 +2636,15 @@ var SequentialAttributeDecoder = class {
1875
2636
  if (valueData === void 0) {
1876
2637
  return false;
1877
2638
  }
1878
- this._attribute.buffer.write(0, valueData, totalSize);
1879
- return true;
1880
- }
1881
- setPortableAttribute(att) {
1882
- this._portableAttribute = att;
1883
- }
1884
- get portableAttribute() {
1885
- return this._portableAttribute;
1886
- }
1887
- };
1888
-
1889
- // src/decoder/compression/entropy/ANSCoding.ts
1890
- var ANS_P8_PRECISION = 256;
1891
- var ANS_L_BASE = 4096;
1892
- var ANS_IO_BASE = 256;
1893
- function memGetLe16(buf, offset) {
1894
- return buf[offset] | buf[offset + 1] << 8;
1895
- }
1896
- function memGetLe24(buf, offset) {
1897
- return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16;
1898
- }
1899
- function memGetLe32(buf, offset) {
1900
- return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0;
1901
- }
1902
- var AnsDecoder = class {
1903
- buf;
1904
- bufOffset;
1905
- // First valid byte of this decoder's slice within buf: init is passed
1906
- // absolute offsets into the source buffer to avoid a subarray allocation.
1907
- bufStart;
1908
- state;
1909
- constructor() {
1910
- this.buf = null;
1911
- this.bufOffset = 0;
1912
- this.bufStart = 0;
1913
- this.state = 0;
1914
- }
1915
- };
1916
- function ansReadInit(ans, buf, offset, base = 0) {
1917
- if (offset - base < 1) {
1918
- return 1;
1919
- }
1920
- ans.buf = buf;
1921
- ans.bufStart = base;
1922
- const x = buf[offset - 1] >> 6;
1923
- if (x === 0) {
1924
- ans.bufOffset = offset - 1;
1925
- ans.state = buf[offset - 1] & 63;
1926
- } else if (x === 1) {
1927
- if (offset - base < 2) {
1928
- return 1;
1929
- }
1930
- ans.bufOffset = offset - 2;
1931
- ans.state = memGetLe16(buf, offset - 2) & 16383;
1932
- } else if (x === 2) {
1933
- if (offset - base < 3) {
1934
- return 1;
1935
- }
1936
- ans.bufOffset = offset - 3;
1937
- ans.state = memGetLe24(buf, offset - 3) & 4194303;
1938
- } else {
1939
- return 1;
1940
- }
1941
- ans.state += ANS_L_BASE;
1942
- if (ans.state >= ANS_L_BASE * ANS_IO_BASE) {
1943
- return 1;
1944
- }
1945
- return 0;
1946
- }
1947
- function ansReadEnd(ans) {
1948
- return ans.state === ANS_L_BASE;
1949
- }
1950
- var tablePool = [];
1951
- var acquirePooled = (Ctor, size) => {
1952
- for (let i = tablePool.length - 1; i >= 0; --i) {
1953
- const buf = tablePool[i];
1954
- if (buf.constructor === Ctor && buf.length >= size) {
1955
- tablePool[i] = tablePool[tablePool.length - 1];
1956
- tablePool.pop();
1957
- return buf;
1958
- }
1959
- }
1960
- return new Ctor(size);
1961
- };
1962
- var RAnsDecoder = class {
1963
- ransPrecisionBits;
1964
- ransPrecision;
1965
- ransPrecisionMask;
1966
- lRansBase;
1967
- lutTable;
1968
- probTable;
1969
- cumProbTable;
1970
- buf;
1971
- bufOffset;
1972
- // First valid byte of this decoder's slice within buf (absolute offsets,
1973
- // see AnsDecoder.bufStart).
1974
- bufStart;
1975
- state;
1976
- constructor(ransPrecisionBits) {
1977
- this.ransPrecisionBits = ransPrecisionBits;
1978
- this.ransPrecision = 1 << ransPrecisionBits;
1979
- this.ransPrecisionMask = this.ransPrecision - 1;
1980
- this.lRansBase = this.ransPrecision * 4;
1981
- this.lutTable = null;
1982
- this.probTable = null;
1983
- this.cumProbTable = null;
1984
- this.buf = null;
1985
- this.bufOffset = 0;
1986
- this.bufStart = 0;
1987
- this.state = 0;
1988
- }
1989
- // offset is the absolute end of the encoded bytes within buf and base the
1990
- // absolute start (offset - base = encoded length). Passing the source
1991
- // buffer with absolute offsets avoids a subarray allocation per init.
1992
- // Returns 0 on success, non-zero on error.
1993
- readInit(buf, offset, base = 0) {
1994
- if (offset - base < 1) {
1995
- return 1;
1996
- }
1997
- this.buf = buf;
1998
- this.bufStart = base;
1999
- const x = buf[offset - 1] >> 6;
2000
- if (x === 0) {
2001
- this.bufOffset = offset - 1;
2002
- this.state = buf[offset - 1] & 63;
2003
- } else if (x === 1) {
2004
- if (offset - base < 2) {
2005
- return 1;
2006
- }
2007
- this.bufOffset = offset - 2;
2008
- this.state = memGetLe16(buf, offset - 2) & 16383;
2009
- } else if (x === 2) {
2010
- if (offset - base < 3) {
2011
- return 1;
2012
- }
2013
- this.bufOffset = offset - 3;
2014
- this.state = memGetLe24(buf, offset - 3) & 4194303;
2015
- } else if (x === 3) {
2016
- if (offset - base < 4) {
2017
- return 1;
2018
- }
2019
- this.bufOffset = offset - 4;
2020
- this.state = memGetLe32(buf, offset - 4) & 1073741823;
2021
- } else {
2022
- return 1;
2023
- }
2024
- this.state += this.lRansBase;
2025
- if (this.state >= this.lRansBase * ANS_IO_BASE) {
2026
- return 1;
2027
- }
2028
- return 0;
2029
- }
2030
- readEnd() {
2031
- if (this.lutTable !== null) {
2032
- tablePool.push(this.lutTable);
2033
- this.lutTable = null;
2034
- }
2035
- if (this.probTable !== null) {
2036
- tablePool.push(this.probTable);
2037
- this.probTable = null;
2038
- }
2039
- if (this.cumProbTable !== null) {
2040
- tablePool.push(this.cumProbTable);
2041
- this.cumProbTable = null;
2042
- }
2043
- return this.state === this.lRansBase;
2044
- }
2045
- ransRead() {
2046
- const buf = this.buf;
2047
- const lRansBase = this.lRansBase;
2048
- let state = this.state;
2049
- let bufOffset = this.bufOffset;
2050
- const bufStart = this.bufStart;
2051
- while (state < lRansBase && bufOffset > bufStart) {
2052
- state = state << 8 | buf[--bufOffset];
2053
- }
2054
- const quo = state >>> this.ransPrecisionBits;
2055
- const rem = state & this.ransPrecisionMask;
2056
- const symbol = this.lutTable[rem];
2057
- this.state = quo * this.probTable[symbol] + rem - this.cumProbTable[symbol];
2058
- this.bufOffset = bufOffset;
2059
- return symbol;
2060
- }
2061
- // Batch ransRead() into out[0..count): all fields hoisted to locals, state
2062
- // written back once. Removes per-symbol property reads and call indirection.
2063
- // lutTable's element type varies per decoder (Uint8/16/32 by symbol count),
2064
- // which would make the hot lutTable[rem] access site polymorphic — dispatch
2065
- // once here so each loop body stays monomorphic on its concrete type. The
2066
- // three bodies are intentionally identical copies.
2067
- decodeSymbols(out, count) {
2068
- const lutTable = this.lutTable;
2069
- if (lutTable instanceof Uint8Array) {
2070
- this._decodeSymbolsU8(out, count, lutTable);
2071
- } else if (lutTable instanceof Uint16Array) {
2072
- this._decodeSymbolsU16(out, count, lutTable);
2073
- } else {
2074
- this._decodeSymbolsU32(out, count, lutTable);
2075
- }
2076
- }
2077
- _decodeSymbolsU8(out, count, lutTable) {
2078
- const buf = this.buf;
2079
- const lRansBase = this.lRansBase;
2080
- const ransPrecisionBits = this.ransPrecisionBits;
2081
- const ransPrecisionMask = this.ransPrecisionMask;
2082
- const probTable = this.probTable;
2083
- const cumProbTable = this.cumProbTable;
2084
- let state = this.state;
2085
- let bufOffset = this.bufOffset;
2086
- const bufStart = this.bufStart;
2087
- for (let i = 0; i < count; ++i) {
2088
- while (state < lRansBase && bufOffset > bufStart) {
2089
- state = state << 8 | buf[--bufOffset];
2090
- }
2091
- const rem = state & ransPrecisionMask;
2092
- const symbol = lutTable[rem];
2093
- out[i] = symbol;
2094
- state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
2095
- }
2096
- this.state = state;
2097
- this.bufOffset = bufOffset;
2098
- }
2099
- _decodeSymbolsU16(out, count, lutTable) {
2100
- const buf = this.buf;
2101
- const lRansBase = this.lRansBase;
2102
- const ransPrecisionBits = this.ransPrecisionBits;
2103
- const ransPrecisionMask = this.ransPrecisionMask;
2104
- const probTable = this.probTable;
2105
- const cumProbTable = this.cumProbTable;
2106
- let state = this.state;
2107
- let bufOffset = this.bufOffset;
2108
- const bufStart = this.bufStart;
2109
- for (let i = 0; i < count; ++i) {
2110
- while (state < lRansBase && bufOffset > bufStart) {
2111
- state = state << 8 | buf[--bufOffset];
2112
- }
2113
- const rem = state & ransPrecisionMask;
2114
- const symbol = lutTable[rem];
2115
- out[i] = symbol;
2116
- state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
2117
- }
2118
- this.state = state;
2119
- this.bufOffset = bufOffset;
2120
- }
2121
- _decodeSymbolsU32(out, count, lutTable) {
2122
- const buf = this.buf;
2123
- const lRansBase = this.lRansBase;
2124
- const ransPrecisionBits = this.ransPrecisionBits;
2125
- const ransPrecisionMask = this.ransPrecisionMask;
2126
- const probTable = this.probTable;
2127
- const cumProbTable = this.cumProbTable;
2128
- let state = this.state;
2129
- let bufOffset = this.bufOffset;
2130
- const bufStart = this.bufStart;
2131
- for (let i = 0; i < count; ++i) {
2132
- while (state < lRansBase && bufOffset > bufStart) {
2133
- state = state << 8 | buf[--bufOffset];
2134
- }
2135
- const rem = state & ransPrecisionMask;
2136
- const symbol = lutTable[rem];
2137
- out[i] = symbol;
2138
- state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
2139
- }
2140
- this.state = state;
2141
- this.bufOffset = bufOffset;
2142
- }
2143
- // Builds the ransPrecision-entry lookup table. Returns false on bad input data.
2144
- ransBuildLookUpTable(tokenProbs, numSymbols) {
2145
- const LutArray = numSymbols <= 256 ? Uint8Array : numSymbols <= 65536 ? Uint16Array : Uint32Array;
2146
- const lutTable = acquirePooled(LutArray, this.ransPrecision);
2147
- const probTable = acquirePooled(Uint32Array, numSymbols);
2148
- const cumProbTable = acquirePooled(Uint32Array, numSymbols);
2149
- this.lutTable = lutTable;
2150
- this.probTable = probTable;
2151
- this.cumProbTable = cumProbTable;
2152
- let cumProb = 0;
2153
- let actProb = 0;
2154
- for (let i = 0; i < numSymbols; ++i) {
2155
- const prob = tokenProbs[i];
2156
- probTable[i] = prob;
2157
- cumProbTable[i] = cumProb;
2158
- cumProb += prob;
2159
- if (cumProb > this.ransPrecision) {
2160
- return false;
2161
- }
2162
- if (prob < 32) {
2163
- for (let j = actProb; j < cumProb; ++j) {
2164
- lutTable[j] = i;
2165
- }
2166
- } else {
2167
- lutTable.fill(i, actProb, cumProb);
2168
- }
2169
- actProb = cumProb;
2170
- }
2171
- if (cumProb !== this.ransPrecision) {
2172
- return false;
2173
- }
2639
+ this._attribute.buffer.write(0, valueData, totalSize);
2174
2640
  return true;
2175
2641
  }
2642
+ setPortableAttribute(att) {
2643
+ this._portableAttribute = att;
2644
+ }
2645
+ get portableAttribute() {
2646
+ return this._portableAttribute;
2647
+ }
2176
2648
  };
2177
2649
 
2178
2650
  // src/decoder/compression/entropy/RAnsSymbolDecoder.ts
@@ -2196,8 +2668,10 @@ var RAnsSymbolDecoder = class {
2196
2668
  get numSymbols() {
2197
2669
  return this.numSymbols_;
2198
2670
  }
2199
- // Initialize the decoder and decode the probability table.
2200
- create(buffer) {
2671
+ // Initialize the decoder and decode the probability table. expectedCount is
2672
+ // the number of symbols the caller will decode (lets short streams skip the
2673
+ // full lookup table; see RAnsDecoder.ransBuildLookUpTable).
2674
+ create(buffer, expectedCount = 2147483647) {
2201
2675
  if (buffer.bitstreamVersion === 0) {
2202
2676
  return false;
2203
2677
  }
@@ -2208,7 +2682,7 @@ var RAnsSymbolDecoder = class {
2208
2682
  return false;
2209
2683
  }
2210
2684
  const numSymbols = this.numSymbols_;
2211
- const probabilityTable = new Uint32Array(numSymbols);
2685
+ const probabilityTable = scratchUint32Zeroed(numSymbols);
2212
2686
  this.probabilityTable_ = probabilityTable;
2213
2687
  if (numSymbols === 0) {
2214
2688
  return true;
@@ -2238,7 +2712,7 @@ var RAnsSymbolDecoder = class {
2238
2712
  }
2239
2713
  }
2240
2714
  buffer.advance(pos - startPos);
2241
- if (!this.ans_.ransBuildLookUpTable(this.probabilityTable_, this.numSymbols_)) {
2715
+ if (!this.ans_.ransBuildLookUpTable(this.probabilityTable_, this.numSymbols_, expectedCount)) {
2242
2716
  return false;
2243
2717
  }
2244
2718
  return true;
@@ -2280,7 +2754,7 @@ function decodeSymbols(numValues, numComponents, srcBuffer, outValues) {
2280
2754
  }
2281
2755
  function decodeTaggedSymbols(numValues, numComponents, srcBuffer, outValues) {
2282
2756
  const tagDecoder = new RAnsSymbolDecoder(5);
2283
- if (!tagDecoder.create(srcBuffer)) {
2757
+ if (!tagDecoder.create(srcBuffer, Math.ceil(numValues / numComponents))) {
2284
2758
  return false;
2285
2759
  }
2286
2760
  if (!tagDecoder.startDecoding(srcBuffer)) {
@@ -2289,27 +2763,76 @@ function decodeTaggedSymbols(numValues, numComponents, srcBuffer, outValues) {
2289
2763
  if (numValues > 0 && tagDecoder.numSymbols === 0) {
2290
2764
  return false;
2291
2765
  }
2766
+ const tagAns = tagDecoder.ans_;
2292
2767
  srcBuffer.startBitDecoding(false);
2293
2768
  const bd = srcBuffer._bitDecoder;
2294
- const tagAns = tagDecoder.ans_;
2769
+ const buf = bd._bitBuffer;
2770
+ const byteLength = bd._byteLength;
2771
+ let bitOffset = bd._bitOffset;
2295
2772
  let valueId = 0;
2296
2773
  for (let i = 0; i < numValues; i += numComponents) {
2297
2774
  const bitLength = tagAns.ransRead();
2298
- for (let j = 0; j < numComponents; ++j) {
2299
- const val = bd.getBits(bitLength);
2300
- if (val === void 0) {
2301
- return false;
2775
+ if (bitLength < 32) {
2776
+ const mask = (1 << bitLength) - 1;
2777
+ let j = 0;
2778
+ for (; j < numComponents; ++j) {
2779
+ const byteOffset = bitOffset >> 3;
2780
+ if (byteOffset + 4 >= byteLength) break;
2781
+ const bitShift = bitOffset & 7;
2782
+ let value = (buf[byteOffset] | buf[byteOffset + 1] << 8 | buf[byteOffset + 2] << 16 | buf[byteOffset + 3] << 24) >>> bitShift;
2783
+ if (bitLength > 32 - bitShift) {
2784
+ value = (value | buf[byteOffset + 4] << 32 - bitShift) >>> 0;
2785
+ }
2786
+ bitOffset += bitLength;
2787
+ outValues[valueId++] = value & mask;
2788
+ }
2789
+ if (j === numComponents) continue;
2790
+ bd._bitOffset = bitOffset;
2791
+ for (; j < numComponents; ++j) {
2792
+ const val = bd.getBits(bitLength);
2793
+ if (val === void 0) {
2794
+ return false;
2795
+ }
2796
+ outValues[valueId++] = val;
2797
+ }
2798
+ bitOffset = bd._bitOffset;
2799
+ } else {
2800
+ bd._bitOffset = bitOffset;
2801
+ for (let j = 0; j < numComponents; ++j) {
2802
+ const val = bd.getBits(bitLength);
2803
+ if (val === void 0) {
2804
+ return false;
2805
+ }
2806
+ outValues[valueId++] = val;
2302
2807
  }
2303
- outValues[valueId++] = val;
2808
+ bitOffset = bd._bitOffset;
2304
2809
  }
2305
2810
  }
2811
+ bd._bitOffset = bitOffset;
2306
2812
  tagDecoder.endDecoding();
2307
2813
  srcBuffer.endBitDecoding();
2308
2814
  return true;
2309
2815
  }
2816
+ function parseRawSymbolStream(numValues, srcBuffer) {
2817
+ const maxBitLength = srcBuffer.decodeUint8();
2818
+ if (maxBitLength === void 0 || maxBitLength < 1 || maxBitLength > 18) {
2819
+ return null;
2820
+ }
2821
+ const decoder = new RAnsSymbolDecoder(maxBitLength);
2822
+ if (!decoder.create(srcBuffer, numValues)) {
2823
+ return null;
2824
+ }
2825
+ if (numValues > 0 && decoder.numSymbols === 0) {
2826
+ return null;
2827
+ }
2828
+ if (!decoder.startDecoding(srcBuffer)) {
2829
+ return null;
2830
+ }
2831
+ return decoder;
2832
+ }
2310
2833
  function decodeRawSymbolsInternal(uniqueSymbolsBitLength, numValues, srcBuffer, outValues) {
2311
2834
  const decoder = new RAnsSymbolDecoder(uniqueSymbolsBitLength);
2312
- if (!decoder.create(srcBuffer)) {
2835
+ if (!decoder.create(srcBuffer, numValues)) {
2313
2836
  return false;
2314
2837
  }
2315
2838
  if (numValues > 0 && decoder.numSymbols === 0) {
@@ -2409,6 +2932,16 @@ var PredictionSchemeDecoderInterface = class {
2409
2932
  decodePredictionData(_buffer) {
2410
2933
  return true;
2411
2934
  }
2935
+ /**
2936
+ * Like computeOriginalValues, but inCorr still holds unsigned zigzag-coded
2937
+ * corrections; the implementation unpacks each one inline, replacing the
2938
+ * standalone convertSymbolsToSignedInts pass. Returns undefined when the
2939
+ * scheme/transform combination cannot fuse (the caller then falls back to
2940
+ * the two-pass path). Base implementation: never fusable.
2941
+ */
2942
+ computeOriginalValuesZigzag(_inCorr, _outData, _size, _numComponents, _entryToPointIdMap) {
2943
+ return void 0;
2944
+ }
2412
2945
  /** Reverts the prediction applied during encoding, writing original values to outData. */
2413
2946
  computeOriginalValues(_inCorr, _outData, _size, _numComponents, _entryToPointIdMap) {
2414
2947
  return false;
@@ -2744,7 +3277,7 @@ var OctahedronToolBox = class {
2744
3277
  // src/decoder/compression/attributes/prediction_schemes/MeshPredictionSchemeGeometricNormalPredictorArea.ts
2745
3278
  var UPPER_BOUND = 1 << 29;
2746
3279
  function buildInt32PositionCache(att, map, numEntries, tempPos) {
2747
- const cache = new Int32Array(numEntries * 3);
3280
+ const cache = scratchInt32(numEntries * 3);
2748
3281
  const bufData = att.buffer && att.buffer.data;
2749
3282
  if (att.dataType === DataType.INT32 && att.numComponents === 3 && bufData) {
2750
3283
  const src = new Int32Array(bufData.buffer);
@@ -2825,7 +3358,7 @@ var MeshPredictionSchemeGeometricNormalPredictorArea = class {
2825
3358
  const cornerToVertex = this._cornerToVertex;
2826
3359
  const vertexToDataMap = this._meshData.vertexToDataMap;
2827
3360
  const nc = cornerToVertex.length;
2828
- const c2o = new Int32Array(nc);
3361
+ const c2o = scratchInt32(nc);
2829
3362
  for (let c = 0; c < nc; ++c) {
2830
3363
  const v = cornerToVertex[c];
2831
3364
  c2o[c] = v < 0 ? -1 : vertexToDataMap[v] * 3;
@@ -3076,10 +3609,21 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3076
3609
  isInitialized() {
3077
3610
  return this._meshData.isInitialized();
3078
3611
  }
3612
+ // Zigzag-fused variant: decodes corrections that are still in their unsigned
3613
+ // zigzag form, unpacking each one inline. Only offered for the wrap
3614
+ // transform (whose corrections are the only zigzag-coded ones this scheme
3615
+ // sees); callers fall back to the standalone conversion pass otherwise.
3616
+ computeOriginalValuesZigzag(inCorr, outData, _size, numComponents, _entryToPointIdMap) {
3617
+ this._transform.init(numComponents);
3618
+ if (!this._transform.getType || this._transform.getType() !== PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
3619
+ return void 0;
3620
+ }
3621
+ return this._computeOriginalValuesWrap(inCorr, outData, numComponents, true);
3622
+ }
3079
3623
  computeOriginalValues(inCorr, outData, size, numComponents, entryToPointIdMap) {
3080
3624
  this._transform.init(numComponents);
3081
3625
  if (this._transform.getType && this._transform.getType() === PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
3082
- return this._computeOriginalValuesWrap(inCorr, outData, numComponents);
3626
+ return this._computeOriginalValuesWrap(inCorr, outData, numComponents, false);
3083
3627
  }
3084
3628
  const table = this._meshData.cornerTable;
3085
3629
  const vertexToDataMap = this._meshData.vertexToDataMap;
@@ -3120,12 +3664,18 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3120
3664
  }
3121
3665
  return true;
3122
3666
  }
3123
- _computeOriginalValuesWrap(inCorr, outData, numComponents) {
3667
+ // The wrap transform's corrections are zigzag-coded; with `zigzag` set the
3668
+ // decode folds the (val >>> 1) ^ -(val & 1) unpacking into each correction
3669
+ // read, replacing the standalone convertSymbolsToSignedInts pass over the
3670
+ // whole array (see computeOriginalValuesZigzag). Every correction is read
3671
+ // exactly once before its slot is overwritten, so the in-place aliasing of
3672
+ // inCorr and outData is preserved.
3673
+ _computeOriginalValuesWrap(inCorr, outData, numComponents, zigzag) {
3124
3674
  if (numComponents === 2) {
3125
- return this._computeOriginalValuesWrap2(inCorr, outData);
3675
+ return this._computeOriginalValuesWrap2(inCorr, outData, zigzag);
3126
3676
  }
3127
3677
  if (numComponents === 3) {
3128
- return this._computeOriginalValuesWrap3(inCorr, outData);
3678
+ return this._computeOriginalValuesWrap3(inCorr, outData, zigzag);
3129
3679
  }
3130
3680
  const table = this._meshData.cornerTable;
3131
3681
  const vertexToDataMap = this._meshData.vertexToDataMap;
@@ -3143,7 +3693,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3143
3693
  } else if (pred < minValue) {
3144
3694
  pred = minValue;
3145
3695
  }
3146
- let orig = pred + inCorr[c] | 0;
3696
+ const raw = inCorr[c];
3697
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3147
3698
  if (orig > maxValue) {
3148
3699
  orig -= maxDif;
3149
3700
  } else if (orig < minValue) {
@@ -3182,7 +3733,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3182
3733
  } else if (pred < minValue) {
3183
3734
  pred = minValue;
3184
3735
  }
3185
- let orig = pred + inCorr[dstOffset + c] | 0;
3736
+ const raw = inCorr[dstOffset + c];
3737
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3186
3738
  if (orig > maxValue) {
3187
3739
  orig -= maxDif;
3188
3740
  } else if (orig < minValue) {
@@ -3199,7 +3751,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3199
3751
  } else if (pred < minValue) {
3200
3752
  pred = minValue;
3201
3753
  }
3202
- let orig = pred + inCorr[dstOffset + c] | 0;
3754
+ const raw = inCorr[dstOffset + c];
3755
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3203
3756
  if (orig > maxValue) {
3204
3757
  orig -= maxDif;
3205
3758
  } else if (orig < minValue) {
@@ -3211,7 +3764,7 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3211
3764
  }
3212
3765
  return true;
3213
3766
  }
3214
- _computeOriginalValuesWrap2(inCorr, outData) {
3767
+ _computeOriginalValuesWrap2(inCorr, outData, zigzag) {
3215
3768
  const table = this._meshData.cornerTable;
3216
3769
  const vertexToDataMap = this._meshData.vertexToDataMap;
3217
3770
  const oppositeCorners = table.oppositeCornerArray();
@@ -3233,8 +3786,10 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3233
3786
  } else if (pred1 < minValue) {
3234
3787
  pred1 = minValue;
3235
3788
  }
3236
- let orig0 = pred0 + inCorr[0] | 0;
3237
- let orig1 = pred1 + inCorr[1] | 0;
3789
+ const raw0 = inCorr[0];
3790
+ const raw1 = inCorr[1];
3791
+ let orig0 = pred0 + (zigzag ? raw0 >>> 1 ^ -(raw0 & 1) : raw0) | 0;
3792
+ let orig1 = pred1 + (zigzag ? raw1 >>> 1 ^ -(raw1 & 1) : raw1) | 0;
3238
3793
  if (orig0 > maxValue) {
3239
3794
  orig0 -= maxDif;
3240
3795
  } else if (orig0 < minValue) {
@@ -3288,8 +3843,10 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3288
3843
  } else if (pred1 < minValue) {
3289
3844
  pred1 = minValue;
3290
3845
  }
3291
- orig0 = pred0 + inCorr[dstOffset] | 0;
3292
- orig1 = pred1 + inCorr[dstOffset + 1] | 0;
3846
+ const rawA = inCorr[dstOffset];
3847
+ const rawB = inCorr[dstOffset + 1];
3848
+ orig0 = pred0 + (zigzag ? rawA >>> 1 ^ -(rawA & 1) : rawA) | 0;
3849
+ orig1 = pred1 + (zigzag ? rawB >>> 1 ^ -(rawB & 1) : rawB) | 0;
3293
3850
  if (orig0 > maxValue) {
3294
3851
  orig0 -= maxDif;
3295
3852
  } else if (orig0 < minValue) {
@@ -3305,7 +3862,7 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3305
3862
  }
3306
3863
  return true;
3307
3864
  }
3308
- _computeOriginalValuesWrap3(inCorr, outData) {
3865
+ _computeOriginalValuesWrap3(inCorr, outData, zigzag) {
3309
3866
  const table = this._meshData.cornerTable;
3310
3867
  const vertexToDataMap = this._meshData.vertexToDataMap;
3311
3868
  const oppositeCorners = table.oppositeCornerArray();
@@ -3333,9 +3890,12 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3333
3890
  } else if (pred2 < minValue) {
3334
3891
  pred2 = minValue;
3335
3892
  }
3336
- let orig0 = pred0 + inCorr[0] | 0;
3337
- let orig1 = pred1 + inCorr[1] | 0;
3338
- let orig2 = pred2 + inCorr[2] | 0;
3893
+ const raw0 = inCorr[0];
3894
+ const raw1 = inCorr[1];
3895
+ const raw2 = inCorr[2];
3896
+ let orig0 = pred0 + (zigzag ? raw0 >>> 1 ^ -(raw0 & 1) : raw0) | 0;
3897
+ let orig1 = pred1 + (zigzag ? raw1 >>> 1 ^ -(raw1 & 1) : raw1) | 0;
3898
+ let orig2 = pred2 + (zigzag ? raw2 >>> 1 ^ -(raw2 & 1) : raw2) | 0;
3339
3899
  if (orig0 > maxValue) {
3340
3900
  orig0 -= maxDif;
3341
3901
  } else if (orig0 < minValue) {
@@ -3402,9 +3962,12 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3402
3962
  } else if (pred2 < minValue) {
3403
3963
  pred2 = minValue;
3404
3964
  }
3405
- orig0 = pred0 + inCorr[dstOffset] | 0;
3406
- orig1 = pred1 + inCorr[dstOffset + 1] | 0;
3407
- orig2 = pred2 + inCorr[dstOffset + 2] | 0;
3965
+ const rawA = inCorr[dstOffset];
3966
+ const rawB = inCorr[dstOffset + 1];
3967
+ const rawC = inCorr[dstOffset + 2];
3968
+ orig0 = pred0 + (zigzag ? rawA >>> 1 ^ -(rawA & 1) : rawA) | 0;
3969
+ orig1 = pred1 + (zigzag ? rawB >>> 1 ^ -(rawB & 1) : rawB) | 0;
3970
+ orig2 = pred2 + (zigzag ? rawC >>> 1 ^ -(rawC & 1) : rawC) | 0;
3408
3971
  if (orig0 > maxValue) {
3409
3972
  orig0 -= maxDif;
3410
3973
  } else if (orig0 < minValue) {
@@ -3771,11 +4334,16 @@ var PredictionSchemeDeltaDecoder = class extends PredictionSchemeDecoder {
3771
4334
  return true;
3772
4335
  }
3773
4336
  computeOriginalValues(inCorr, outData, size, numComponents, entryToPointIdMap) {
3774
- this._transform.init(numComponents);
4337
+ const transform = this._transform;
4338
+ transform.init(numComponents);
4339
+ if (transform.computeOriginalValuesDelta) {
4340
+ transform.computeOriginalValuesDelta(inCorr, outData, size, numComponents);
4341
+ return true;
4342
+ }
3775
4343
  const zeroVals = new Int32Array(numComponents);
3776
- this._transform.computeOriginalValue(zeroVals, 0, inCorr, 0, outData, 0);
4344
+ transform.computeOriginalValue(zeroVals, 0, inCorr, 0, outData, 0);
3777
4345
  for (let i = numComponents; i < size; i += numComponents) {
3778
- this._transform.computeOriginalValue(outData, i - numComponents, inCorr, i, outData, i);
4346
+ transform.computeOriginalValue(outData, i - numComponents, inCorr, i, outData, i);
3779
4347
  }
3780
4348
  return true;
3781
4349
  }
@@ -3911,14 +4479,49 @@ var PredictionSchemeWrapDecodingTransform = class {
3911
4479
  // src/decoder/compression/attributes/SequentialIntegerAttributeDecoder.ts
3912
4480
  var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder {
3913
4481
  _predictionScheme;
4482
+ // Two-phase decode state (see SequentialAttributeDecoder): the parse phase
4483
+ // stashes the primed raw-symbol stream here so the controller can batch and
4484
+ // pair several attributes' decodes; the finish phase consumes it.
4485
+ _pendingSymbolDecoder;
4486
+ _pendingNumValues;
4487
+ _finishPointIds;
3914
4488
  constructor() {
3915
4489
  super();
3916
4490
  this._predictionScheme = null;
4491
+ this._pendingSymbolDecoder = null;
4492
+ this._pendingNumValues = 0;
4493
+ this._finishPointIds = null;
3917
4494
  }
3918
- transformAttributeToOriginalFormat(pointIds) {
3919
- return this._storeValues(pointIds.length);
4495
+ // --- Two-phase decode (parse headers / batch symbol decode / finish) ---
4496
+ decodePortableAttributeParse(pointIds, buffer) {
4497
+ if (this.attribute.numComponents <= 0) {
4498
+ return false;
4499
+ }
4500
+ if (!this.attribute.reset(pointIds.length)) {
4501
+ return false;
4502
+ }
4503
+ this._finishPointIds = pointIds;
4504
+ return this._decodeValuesParse(pointIds, buffer);
3920
4505
  }
3921
- decodeValues(pointIds, buffer) {
4506
+ pendingSymbolStream() {
4507
+ if (this._pendingSymbolDecoder === null) {
4508
+ return null;
4509
+ }
4510
+ const portableAttributeData = this.getPortableAttributeData();
4511
+ return {
4512
+ ans: this._pendingSymbolDecoder.ans_,
4513
+ out: new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, this._pendingNumValues),
4514
+ count: this._pendingNumValues
4515
+ };
4516
+ }
4517
+ decodePortableAttributeFinish() {
4518
+ if (this._pendingSymbolDecoder !== null) {
4519
+ this._pendingSymbolDecoder.endDecoding();
4520
+ this._pendingSymbolDecoder = null;
4521
+ }
4522
+ return this._finishIntegerValues(this._finishPointIds);
4523
+ }
4524
+ _decodeValuesParse(pointIds, buffer) {
3922
4525
  const predictionSchemeMethod = buffer.decodeInt8();
3923
4526
  if (predictionSchemeMethod === void 0) return false;
3924
4527
  if (predictionSchemeMethod < PredictionSchemeMethod.PREDICTION_NONE || predictionSchemeMethod >= PredictionSchemeMethod.NUM_PREDICTION_SCHEMES) {
@@ -3937,12 +4540,6 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
3937
4540
  return false;
3938
4541
  }
3939
4542
  }
3940
- if (!this.decodeIntegerValues(pointIds, buffer)) {
3941
- return false;
3942
- }
3943
- return true;
3944
- }
3945
- decodeIntegerValues(pointIds, buffer) {
3946
4543
  const numComponents = this.getNumValueComponents();
3947
4544
  if (numComponents <= 0) {
3948
4545
  return false;
@@ -3957,9 +4554,23 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
3957
4554
  const compressed = buffer.decodeUint8();
3958
4555
  if (compressed === void 0) return false;
3959
4556
  if (compressed > 0) {
3960
- const outUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
3961
- if (!decodeSymbols(numValues, numComponents, buffer, outUint32)) {
3962
- return false;
4557
+ if (numValues > 0) {
4558
+ const scheme = buffer.decodeUint8();
4559
+ if (scheme === SymbolCodingMethod.SYMBOL_CODING_RAW) {
4560
+ const decoder = parseRawSymbolStream(numValues, buffer);
4561
+ if (decoder === null) {
4562
+ return false;
4563
+ }
4564
+ this._pendingSymbolDecoder = decoder;
4565
+ this._pendingNumValues = numValues;
4566
+ } else if (scheme === SymbolCodingMethod.SYMBOL_CODING_TAGGED) {
4567
+ const outUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4568
+ if (!decodeTaggedSymbols(numValues, numComponents, buffer, outUint32)) {
4569
+ return false;
4570
+ }
4571
+ } else {
4572
+ return false;
4573
+ }
3963
4574
  }
3964
4575
  } else {
3965
4576
  const numBytes = buffer.decodeUint8();
@@ -3990,15 +4601,40 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
3990
4601
  }
3991
4602
  }
3992
4603
  }
3993
- if (numValues > 0 && (this._predictionScheme === null || !this._predictionScheme.areCorrectionsPositive())) {
3994
- const asUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
3995
- convertSymbolsToSignedInts(asUint32, numValues, portableAttributeData);
3996
- }
3997
4604
  if (this._predictionScheme) {
3998
4605
  if (!this._predictionScheme.decodePredictionData(buffer)) {
3999
4606
  return false;
4000
4607
  }
4608
+ }
4609
+ return true;
4610
+ }
4611
+ // The post-symbol tail of the decode: zigzag unpacking and prediction.
4612
+ _finishIntegerValues(pointIds) {
4613
+ const numComponents = this.getNumValueComponents();
4614
+ const numValues = pointIds.length * numComponents;
4615
+ const portableAttributeData = this.getPortableAttributeData();
4616
+ if (portableAttributeData === null) {
4617
+ return false;
4618
+ }
4619
+ const needsZigzag = numValues > 0 && (this._predictionScheme === null || !this._predictionScheme.areCorrectionsPositive());
4620
+ if (this._predictionScheme) {
4001
4621
  if (numValues > 0) {
4622
+ if (needsZigzag) {
4623
+ const fused = this._predictionScheme.computeOriginalValuesZigzag(
4624
+ portableAttributeData,
4625
+ portableAttributeData,
4626
+ numValues,
4627
+ numComponents,
4628
+ pointIds
4629
+ );
4630
+ if (fused !== void 0) {
4631
+ return fused;
4632
+ }
4633
+ }
4634
+ if (needsZigzag) {
4635
+ const asUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4636
+ convertSymbolsToSignedInts(asUint32, numValues, portableAttributeData);
4637
+ }
4002
4638
  if (!this._predictionScheme.computeOriginalValues(
4003
4639
  portableAttributeData,
4004
4640
  portableAttributeData,
@@ -4009,9 +4645,34 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
4009
4645
  return false;
4010
4646
  }
4011
4647
  }
4648
+ } else if (needsZigzag) {
4649
+ const asUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4650
+ convertSymbolsToSignedInts(asUint32, numValues, portableAttributeData);
4012
4651
  }
4013
4652
  return true;
4014
4653
  }
4654
+ transformAttributeToOriginalFormat(pointIds) {
4655
+ return this._storeValues(pointIds.length);
4656
+ }
4657
+ // Single-phase entry (base-class decodePortableAttribute path): parse,
4658
+ // decode any deferred symbol stream immediately, finish.
4659
+ decodeValues(pointIds, buffer) {
4660
+ this._finishPointIds = pointIds;
4661
+ if (!this._decodeValuesParse(pointIds, buffer)) {
4662
+ return false;
4663
+ }
4664
+ const pending = this._pendingSymbolDecoder;
4665
+ if (pending !== null) {
4666
+ const portableAttributeData = this.getPortableAttributeData();
4667
+ const outUint32 = new Uint32Array(
4668
+ portableAttributeData.buffer,
4669
+ portableAttributeData.byteOffset,
4670
+ this._pendingNumValues
4671
+ );
4672
+ pending.ans_.decodeSymbols(outUint32, this._pendingNumValues);
4673
+ }
4674
+ return this.decodePortableAttributeFinish();
4675
+ }
4015
4676
  // Prediction scheme for decoding integer values; subclasses override for others.
4016
4677
  createIntPredictionScheme(method, transformType) {
4017
4678
  if (transformType !== PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
@@ -4074,7 +4735,7 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
4074
4735
  );
4075
4736
  const portAtt = new PointAttribute(ga);
4076
4737
  portAtt.setIdentityMapping();
4077
- portAtt.reset(numEntries);
4738
+ portAtt.resetScratch(numEntries);
4078
4739
  portAtt.uniqueId = this.attribute.uniqueId;
4079
4740
  this.setPortableAttribute(portAtt);
4080
4741
  }
@@ -4100,12 +4761,22 @@ var AttributeTransformType = {
4100
4761
  };
4101
4762
 
4102
4763
  // src/decoder/attributes/AttributeTransformData.ts
4764
+ var INITIAL_CAPACITY = 32;
4103
4765
  var AttributeTransformData = class {
4104
4766
  _transformType;
4105
- _buffer;
4767
+ // Parameter bytes, little-endian, appended in transform-defined order.
4768
+ // Capacity grows geometrically and the DataView is cached alongside it: the
4769
+ // previous DataBuffer-backed version reallocated the buffer and built a
4770
+ // fresh DataView on *every* appended value, which on primitive-heavy files
4771
+ // cost more than the dequantization it describes.
4772
+ _bytes;
4773
+ _view;
4774
+ _size;
4106
4775
  constructor() {
4107
4776
  this._transformType = AttributeTransformType.INVALID;
4108
- this._buffer = new DataBuffer();
4777
+ this._bytes = new Uint8Array(INITIAL_CAPACITY);
4778
+ this._view = new DataView(this._bytes.buffer);
4779
+ this._size = 0;
4109
4780
  }
4110
4781
  get transformType() {
4111
4782
  return this._transformType;
@@ -4113,13 +4784,29 @@ var AttributeTransformData = class {
4113
4784
  set transformType(type) {
4114
4785
  this._transformType = type;
4115
4786
  }
4787
+ // Number of parameter bytes written so far (the next append offset).
4788
+ get dataSize() {
4789
+ return this._size;
4790
+ }
4791
+ get data() {
4792
+ return this._bytes.subarray(0, this._size);
4793
+ }
4794
+ _reserve(sizeNeeded) {
4795
+ if (sizeNeeded <= this._bytes.length) return;
4796
+ let capacity = this._bytes.length * 2;
4797
+ if (capacity < sizeNeeded) capacity = sizeNeeded;
4798
+ const grown = new Uint8Array(capacity);
4799
+ grown.set(this._bytes);
4800
+ this._bytes = grown;
4801
+ this._view = new DataView(grown.buffer);
4802
+ }
4116
4803
  setParameterValue(byteOffset, value, type) {
4117
4804
  const sizeNeeded = byteOffset + this._typeSize(type);
4118
- if (sizeNeeded > this._buffer.dataSize) {
4119
- this._buffer.resize(sizeNeeded);
4805
+ this._reserve(sizeNeeded);
4806
+ if (sizeNeeded > this._size) {
4807
+ this._size = sizeNeeded;
4120
4808
  }
4121
- const data = this._buffer.data;
4122
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
4809
+ const view = this._view;
4123
4810
  switch (type) {
4124
4811
  case "int32":
4125
4812
  view.setInt32(byteOffset, value, true);
@@ -4151,7 +4838,7 @@ var AttributeTransformData = class {
4151
4838
  }
4152
4839
  }
4153
4840
  appendParameterValue(value, type) {
4154
- this.setParameterValue(this._buffer.dataSize, value, type);
4841
+ this.setParameterValue(this._size, value, type);
4155
4842
  }
4156
4843
  _typeSize(type) {
4157
4844
  switch (type) {
@@ -4197,7 +4884,6 @@ var AttributeTransform = class {
4197
4884
  // src/decoder/attributes/AttributeOctahedronTransform.ts
4198
4885
  var AttributeOctahedronTransform = class extends AttributeTransform {
4199
4886
  _quantizationBits;
4200
- _tmpVec;
4201
4887
  constructor() {
4202
4888
  super();
4203
4889
  this._quantizationBits = -1;
@@ -4229,15 +4915,30 @@ var AttributeOctahedronTransform = class extends AttributeTransform {
4229
4915
  const srcI32 = new Int32Array(srcAddr.buffer, srcAddr.byteOffset, numPoints * 2);
4230
4916
  const dstAddr = targetAttribute.getAddress(0);
4231
4917
  const dstF32 = new Float32Array(dstAddr.buffer, dstAddr.byteOffset, numPoints * 3);
4232
- const outVec = this._tmpVec || (this._tmpVec = new Float32Array(3));
4918
+ const fround = Math.fround;
4919
+ const scale = toolBox._dequantizationScale;
4233
4920
  let si = 0;
4234
4921
  let di = 0;
4235
4922
  for (let i = 0; i < numPoints; i++) {
4236
- toolBox.quantizedOctahedralCoordsToUnitVector(srcI32[si], srcI32[si + 1], outVec);
4923
+ let y = fround(fround(fround(srcI32[si]) * scale) - 1);
4924
+ let z = fround(fround(fround(srcI32[si + 1]) * scale) - 1);
4237
4925
  si += 2;
4238
- dstF32[di] = outVec[0];
4239
- dstF32[di + 1] = outVec[1];
4240
- dstF32[di + 2] = outVec[2];
4926
+ const x = fround(fround(1 - Math.abs(y)) - Math.abs(z));
4927
+ let xOffset = -x;
4928
+ if (xOffset < 0) xOffset = 0;
4929
+ y = fround(y + (y < 0 ? xOffset : -xOffset));
4930
+ z = fround(z + (z < 0 ? xOffset : -xOffset));
4931
+ const normSquared = fround(fround(fround(x * x) + fround(y * y)) + fround(z * z));
4932
+ if (normSquared < 1e-6) {
4933
+ dstF32[di] = 0;
4934
+ dstF32[di + 1] = 0;
4935
+ dstF32[di + 2] = 0;
4936
+ } else {
4937
+ const d = fround(1 / fround(Math.sqrt(normSquared)));
4938
+ dstF32[di] = fround(x * d);
4939
+ dstF32[di + 1] = fround(y * d);
4940
+ dstF32[di + 2] = fround(z * d);
4941
+ }
4241
4942
  di += 3;
4242
4943
  }
4243
4944
  return true;
@@ -4407,6 +5108,140 @@ var PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform = class exten
4407
5108
  outOrigVals[outOffset] = origS + center;
4408
5109
  outOrigVals[outOffset + 1] = origT + center;
4409
5110
  }
5111
+ // Fused delta loop for PredictionSchemeDeltaDecoder: the whole
5112
+ // value[i] = original(value[i-1], corr[i]) chain in one call, with the
5113
+ // toolbox fields hoisted to locals and the previous output carried in
5114
+ // locals. This is the hot path for PREDICTION_DIFFERENCE normals (one call
5115
+ // per value through the interface was ~8% of decode time on the bundles).
5116
+ // The arithmetic is computeOriginalValue above verbatim -- keep them in
5117
+ // sync; only the plumbing differs.
5118
+ computeOriginalValuesDelta(inCorr, outData, size, numComponents) {
5119
+ const toolBox = this._octahedronToolBox;
5120
+ const center = toolBox._centerValue;
5121
+ const maxQuantizedValue = toolBox._maxQuantizedValue;
5122
+ let prevS = 0;
5123
+ let prevT = 0;
5124
+ for (let i = 0; i < size; i += numComponents) {
5125
+ const corrS = inCorr[i];
5126
+ const corrT = inCorr[i + 1];
5127
+ let predS = prevS - center;
5128
+ let predT = prevT - center;
5129
+ const predIsInDiamond = Math.abs(predS) + Math.abs(predT) <= center;
5130
+ if (!predIsInDiamond) {
5131
+ let signS = 0;
5132
+ let signT = 0;
5133
+ if (predS >= 0 && predT >= 0) {
5134
+ signS = 1;
5135
+ signT = 1;
5136
+ } else if (predS <= 0 && predT <= 0) {
5137
+ signS = -1;
5138
+ signT = -1;
5139
+ } else {
5140
+ signS = predS > 0 ? 1 : -1;
5141
+ signT = predT > 0 ? 1 : -1;
5142
+ }
5143
+ const cornerPointS = signS * center;
5144
+ const cornerPointT = signT * center;
5145
+ let us = predS * 2 - cornerPointS | 0;
5146
+ let ut = predT * 2 - cornerPointT | 0;
5147
+ if (signS * signT >= 0) {
5148
+ const temp = us;
5149
+ us = -ut;
5150
+ ut = -temp;
5151
+ } else {
5152
+ const temp = us;
5153
+ us = ut;
5154
+ ut = temp;
5155
+ }
5156
+ predS = (us + cornerPointS) / 2 | 0;
5157
+ predT = (ut + cornerPointT) / 2 | 0;
5158
+ }
5159
+ const predIsInBottomLeft = predS === 0 && predT === 0 || predS < 0 && predT <= 0;
5160
+ let rotationCount = 0;
5161
+ if (predS === 0) {
5162
+ if (predT > 0) rotationCount = 3;
5163
+ else if (predT < 0) rotationCount = 1;
5164
+ } else if (predS > 0) {
5165
+ if (predT >= 0) rotationCount = 2;
5166
+ else rotationCount = 1;
5167
+ } else {
5168
+ if (predT > 0) rotationCount = 3;
5169
+ }
5170
+ if (!predIsInBottomLeft) {
5171
+ const s = predS, t = predT;
5172
+ switch (rotationCount) {
5173
+ case 1:
5174
+ predS = t;
5175
+ predT = -s | 0;
5176
+ break;
5177
+ case 2:
5178
+ predS = -s | 0;
5179
+ predT = -t | 0;
5180
+ break;
5181
+ case 3:
5182
+ predS = -t | 0;
5183
+ predT = s;
5184
+ break;
5185
+ }
5186
+ }
5187
+ let origS = predS + corrS | 0;
5188
+ if (origS > center) origS -= maxQuantizedValue;
5189
+ else if (origS < -center) origS += maxQuantizedValue;
5190
+ let origT = predT + corrT | 0;
5191
+ if (origT > center) origT -= maxQuantizedValue;
5192
+ else if (origT < -center) origT += maxQuantizedValue;
5193
+ if (!predIsInBottomLeft) {
5194
+ const s = origS, t = origT;
5195
+ switch (4 - rotationCount & 3) {
5196
+ case 1:
5197
+ origS = t;
5198
+ origT = -s | 0;
5199
+ break;
5200
+ case 2:
5201
+ origS = -s | 0;
5202
+ origT = -t | 0;
5203
+ break;
5204
+ case 3:
5205
+ origS = -t | 0;
5206
+ origT = s;
5207
+ break;
5208
+ }
5209
+ }
5210
+ if (!predIsInDiamond) {
5211
+ let signS = 0;
5212
+ let signT = 0;
5213
+ if (origS >= 0 && origT >= 0) {
5214
+ signS = 1;
5215
+ signT = 1;
5216
+ } else if (origS <= 0 && origT <= 0) {
5217
+ signS = -1;
5218
+ signT = -1;
5219
+ } else {
5220
+ signS = origS > 0 ? 1 : -1;
5221
+ signT = origT > 0 ? 1 : -1;
5222
+ }
5223
+ const cornerPointS = signS * center;
5224
+ const cornerPointT = signT * center;
5225
+ let us = origS * 2 - cornerPointS | 0;
5226
+ let ut = origT * 2 - cornerPointT | 0;
5227
+ if (signS * signT >= 0) {
5228
+ const temp = us;
5229
+ us = -ut;
5230
+ ut = -temp;
5231
+ } else {
5232
+ const temp = us;
5233
+ us = ut;
5234
+ ut = temp;
5235
+ }
5236
+ origS = (us + cornerPointS) / 2 | 0;
5237
+ origT = (ut + cornerPointT) / 2 | 0;
5238
+ }
5239
+ prevS = origS + center | 0;
5240
+ prevT = origT + center | 0;
5241
+ outData[i] = prevS;
5242
+ outData[i + 1] = prevT;
5243
+ }
5244
+ }
4410
5245
  };
4411
5246
 
4412
5247
  // src/decoder/compression/attributes/prediction_schemes/PredictionSchemeNormalOctahedronDecodingTransform.ts
@@ -4721,6 +5556,12 @@ var SequentialAttributeDecodersController = class extends AttributesDecoder {
4721
5556
  return true;
4722
5557
  }
4723
5558
  decodeAttributes(buffer) {
5559
+ if (!this._prepareSequence()) {
5560
+ return false;
5561
+ }
5562
+ return super.decodeAttributes(buffer);
5563
+ }
5564
+ _prepareSequence() {
4724
5565
  if (!this._sequencer) {
4725
5566
  return false;
4726
5567
  }
@@ -4735,7 +5576,44 @@ var SequentialAttributeDecodersController = class extends AttributesDecoder {
4735
5576
  return false;
4736
5577
  }
4737
5578
  }
4738
- return super.decodeAttributes(buffer);
5579
+ return true;
5580
+ }
5581
+ // Two-phase decode (see AttributesDecoder): parse everything that reads the
5582
+ // buffer -- sequence, portable headers (deferring raw rANS symbol decodes),
5583
+ // and the transform parameters -- so the deferred streams of ALL attributes
5584
+ // decoders can then be paired, and finish (zigzag, prediction, inverse
5585
+ // transform) runs per decoder in the original order afterwards. None of the
5586
+ // finish work reads the buffer, and dependents only read parent portable
5587
+ // VALUES in finish, so ordering and output stay identical.
5588
+ decodeAttributesParse(buffer) {
5589
+ if (!this._prepareSequence()) {
5590
+ return false;
5591
+ }
5592
+ const numAttributes = this.getNumAttributes();
5593
+ for (let i = 0; i < numAttributes; i++) {
5594
+ if (!this._sequentialDecoders[i].decodePortableAttributeParse(this._pointIds, buffer)) {
5595
+ return false;
5596
+ }
5597
+ }
5598
+ return this.decodeDataNeededByPortableTransforms(buffer);
5599
+ }
5600
+ collectPendingSymbolStreams(out) {
5601
+ const numAttributes = this.getNumAttributes();
5602
+ for (let i = 0; i < numAttributes; i++) {
5603
+ const pending = this._sequentialDecoders[i].pendingSymbolStream();
5604
+ if (pending !== null) {
5605
+ out.push(pending);
5606
+ }
5607
+ }
5608
+ }
5609
+ decodeAttributesFinish() {
5610
+ const numAttributes = this.getNumAttributes();
5611
+ for (let i = 0; i < numAttributes; i++) {
5612
+ if (!this._sequentialDecoders[i].decodePortableAttributeFinish()) {
5613
+ return false;
5614
+ }
5615
+ }
5616
+ return this.transformAttributesToOriginalFormat();
4739
5617
  }
4740
5618
  getPortableAttribute(pointAttributeId) {
4741
5619
  const locId = this.getLocalIdForPointAttribute(pointAttributeId);
@@ -4862,14 +5740,13 @@ var DepthFirstTraverser = class {
4862
5740
  // Scratch buffers are set up here rather than in init() so a shared-traversal
4863
5741
  // -cache hit — where generateSequence returns before any traversal — skips
4864
5742
  // this entirely, including the visited-flag zero-fill (worth ~0.5% on the
4865
- // 488-primitive manablade-static bundle, which shares attribute corner tables across
5743
+ // ~500-primitive manablade bundle, which shares attribute corner tables across
4866
5744
  // primitives). Uint8Array (0/1) instead of Array(bool): these flags are read
4867
5745
  // and written on every corner of the hottest decode loop (traverseFromCorner).
4868
5746
  // Decode-scoped scratch: released in bulk at the end of the decode.
4869
5747
  onTraversalStart() {
4870
5748
  const cornerTable = this._cornerTable;
4871
5749
  this._isFaceVisited = scratchUint8Zeroed(cornerTable.numFaces());
4872
- this._isVertexVisited = scratchUint8Zeroed(cornerTable.numVertices());
4873
5750
  this._cornerTraversalStack = scratchInt32(this._numCorners);
4874
5751
  }
4875
5752
  onTraversalEnd() {
@@ -4879,7 +5756,6 @@ var DepthFirstTraverser = class {
4879
5756
  return true;
4880
5757
  }
4881
5758
  const isFaceVisited = this._isFaceVisited;
4882
- const isVertexVisited = this._isVertexVisited;
4883
5759
  const observer = this._observer;
4884
5760
  const cornerToVertex = this._cornerToVertex;
4885
5761
  const oppositeCorners = this._oppositeCorners;
@@ -4903,14 +5779,12 @@ var DepthFirstTraverser = class {
4903
5779
  if (nextVert === kInvalidVertexIndex2 || prevVert === kInvalidVertexIndex2) {
4904
5780
  return false;
4905
5781
  }
4906
- if (!isVertexVisited[nextVert]) {
4907
- isVertexVisited[nextVert] = 1;
5782
+ if (vertexToEncodedMap[nextVert] < 0) {
4908
5783
  outPointIds[numOutPoints++] = obsFaces[nextCorner];
4909
5784
  encodedToCornerMap[numValues] = nextCorner;
4910
5785
  vertexToEncodedMap[nextVert] = numValues++;
4911
5786
  }
4912
- if (!isVertexVisited[prevVert]) {
4913
- isVertexVisited[prevVert] = 1;
5787
+ if (vertexToEncodedMap[prevVert] < 0) {
4914
5788
  outPointIds[numOutPoints++] = obsFaces[prevCorner];
4915
5789
  encodedToCornerMap[numValues] = prevCorner;
4916
5790
  vertexToEncodedMap[prevVert] = numValues++;
@@ -4931,27 +5805,26 @@ var DepthFirstTraverser = class {
4931
5805
  encodingData.numValues = numValues;
4932
5806
  return false;
4933
5807
  }
4934
- if (!isVertexVisited[vertId]) {
5808
+ const faceBase = faceId * 3;
5809
+ const nextCornerId = cornerId === faceBase + 2 ? faceBase : cornerId + 1;
5810
+ if (vertexToEncodedMap[vertId] < 0) {
4935
5811
  const lc = vertexLeftmost[vertId];
4936
5812
  let onBoundary = true;
4937
- if (lc !== void 0 && lc >= 0) {
5813
+ if (lc >= 0) {
4938
5814
  const nextLc = lc % 3 === 2 ? lc - 2 : lc + 1;
4939
5815
  onBoundary = oppositeCorners[nextLc] < 0;
4940
5816
  }
4941
- isVertexVisited[vertId] = 1;
4942
5817
  outPointIds[numOutPoints++] = obsFaces[cornerId];
4943
5818
  encodedToCornerMap[numValues] = cornerId;
4944
5819
  vertexToEncodedMap[vertId] = numValues++;
4945
5820
  if (!onBoundary) {
4946
- const nextCornerId2 = cornerId % 3 === 2 ? cornerId - 2 : cornerId + 1;
4947
- cornerId = oppositeCorners[nextCornerId2];
5821
+ cornerId = oppositeCorners[nextCornerId];
4948
5822
  faceId = cornerId / 3 | 0;
4949
5823
  continue;
4950
5824
  }
4951
5825
  }
4952
- const nextCornerId = cornerId % 3 === 2 ? cornerId - 2 : cornerId + 1;
4953
5826
  const rightCornerId = oppositeCorners[nextCornerId];
4954
- const prevCornerId = cornerId % 3 === 0 ? cornerId + 2 : cornerId - 1;
5827
+ const prevCornerId = cornerId === faceBase ? faceBase + 2 : cornerId - 1;
4955
5828
  const leftCornerId = oppositeCorners[prevCornerId];
4956
5829
  const rightFaceId = rightCornerId === kInvalidCornerIndex4 ? kInvalidFaceIndex : rightCornerId / 3 | 0;
4957
5830
  const leftFaceId = leftCornerId === kInvalidCornerIndex4 ? kInvalidFaceIndex : leftCornerId / 3 | 0;
@@ -5181,6 +6054,9 @@ var MeshTraversalSequencer = class {
5181
6054
  _outPointIds;
5182
6055
  _numOutPoints;
5183
6056
  _traversalCache;
6057
+ // The cache entry the last generateSequence resolved to (hit or store);
6058
+ // carries the shared indicesMap.
6059
+ _cacheEntry;
5184
6060
  constructor(mesh, encodingData, traversalCache = null) {
5185
6061
  this._mesh = mesh;
5186
6062
  this._encodingData = encodingData;
@@ -5188,6 +6064,7 @@ var MeshTraversalSequencer = class {
5188
6064
  this._outPointIds = new Int32Array(0);
5189
6065
  this._numOutPoints = 0;
5190
6066
  this._traversalCache = traversalCache;
6067
+ this._cacheEntry = null;
5191
6068
  }
5192
6069
  setTraverser(traverser) {
5193
6070
  this._traverser = traverser;
@@ -5202,6 +6079,7 @@ var MeshTraversalSequencer = class {
5202
6079
  if (cached !== void 0) {
5203
6080
  this._outPointIds = cached.pointIds;
5204
6081
  this._encodingData.adoptTraversalResult(cached.vertexMap, cached.cornerMap, cached.numValues);
6082
+ this._cacheEntry = cached;
5205
6083
  return true;
5206
6084
  }
5207
6085
  }
@@ -5217,12 +6095,15 @@ var MeshTraversalSequencer = class {
5217
6095
  byMethod = /* @__PURE__ */ new Map();
5218
6096
  this._traversalCache.set(cacheKey, byMethod);
5219
6097
  }
5220
- byMethod.set(methodId, {
6098
+ const entry = {
5221
6099
  pointIds: this._outPointIds,
5222
6100
  vertexMap: this._encodingData.vertexToEncodedAttributeValueIndexMap,
5223
6101
  cornerMap: this._encodingData.encodedAttributeValueIndexToCornerMap,
5224
- numValues: this._encodingData.numValues
5225
- });
6102
+ numValues: this._encodingData.numValues,
6103
+ indicesMap: null
6104
+ };
6105
+ byMethod.set(methodId, entry);
6106
+ this._cacheEntry = entry;
5226
6107
  }
5227
6108
  return true;
5228
6109
  }
@@ -5233,6 +6114,11 @@ var MeshTraversalSequencer = class {
5233
6114
  this._outPointIds[this._numOutPoints++] = pointId;
5234
6115
  }
5235
6116
  updatePointToAttributeIndexMapping(attribute) {
6117
+ const entry = this._cacheEntry;
6118
+ if (entry !== null && entry.indicesMap !== null) {
6119
+ attribute.setExplicitMappingShared(entry.indicesMap);
6120
+ return true;
6121
+ }
5236
6122
  const cornerTable = this._traverser.cornerTable();
5237
6123
  const numFaces = this._mesh.numFaces();
5238
6124
  const numPoints = this._mesh.numPoints();
@@ -5254,11 +6140,14 @@ var MeshTraversalSequencer = class {
5254
6140
  }
5255
6141
  indicesMap[pointId] = attEntryId;
5256
6142
  }
6143
+ if (entry !== null) {
6144
+ entry.indicesMap = attribute.indicesMap;
6145
+ }
5257
6146
  return true;
5258
6147
  }
5259
6148
  _generateSequenceInternal() {
5260
6149
  this._numOutPoints = 0;
5261
- this._outPointIds = new Int32Array(this._mesh.numPoints());
6150
+ this._outPointIds = scratchInt32(this._mesh.numPoints());
5262
6151
  this._traverser.onTraversalStart();
5263
6152
  const numFaces = this._traverser.cornerTable().numFaces();
5264
6153
  for (let i = 0; i < numFaces && this._traverser._numVisitedFaces < numFaces; ++i) {
@@ -5449,14 +6338,14 @@ var MeshEdgebreakerDecoderImpl = class {
5449
6338
  this._attributeData = [];
5450
6339
  for (let i = 0; i < numAttributeData; ++i) {
5451
6340
  const ad = new AttributeData();
5452
- ad.attributeSeamCorners = new Int32Array(numFaces * 3);
6341
+ ad.attributeSeamCorners = scratchInt32(numFaces * 3);
5453
6342
  ad.numSeamCorners = 0;
5454
6343
  this._attributeData.push(ad);
5455
6344
  }
5456
6345
  if (!this._cornerTable.reset(numFaces, this._numEncodedVertices + numEncodedSplitSymbols)) {
5457
6346
  return false;
5458
6347
  }
5459
- this._isVertHole = new Uint8Array(this._numEncodedVertices + numEncodedSplitSymbols).fill(1);
6348
+ this._isVertHole = scratchUint8Filled(this._numEncodedVertices + numEncodedSplitSymbols, 1);
5460
6349
  if (this._decodeHoleAndTopologySplitEvents(this._decoder.buffer()) === -1) {
5461
6350
  return false;
5462
6351
  }
@@ -5481,20 +6370,37 @@ var MeshEdgebreakerDecoderImpl = class {
5481
6370
  }
5482
6371
  this._traversalDecoder.done();
5483
6372
  let previousConnectivityData = null;
6373
+ let previousSeamCorners = null;
6374
+ let previousSeamCount = 0;
5484
6375
  for (let i = 0; i < this._attributeData.length; ++i) {
5485
6376
  const connectivityData = this._attributeData[i].connectivityData;
5486
- connectivityData.initEmpty(this._cornerTable);
5487
6377
  const seamCorners = this._attributeData[i].attributeSeamCorners;
5488
6378
  const seamCount = this._attributeData[i].numSeamCorners;
5489
- for (let s = 0; s < seamCount; ++s) {
5490
- connectivityData.addSeamEdge(seamCorners[s]);
6379
+ let sameAsPrevious = previousConnectivityData !== null && seamCount === previousSeamCount;
6380
+ if (sameAsPrevious) {
6381
+ const previous = previousSeamCorners;
6382
+ for (let s = 0; s < seamCount; ++s) {
6383
+ if (seamCorners[s] !== previous[s]) {
6384
+ sameAsPrevious = false;
6385
+ break;
6386
+ }
6387
+ }
5491
6388
  }
5492
- if (connectivityData.hasSameSeams(previousConnectivityData)) {
5493
- connectivityData.adoptVertexRecompute(previousConnectivityData);
5494
- } else if (!connectivityData.recomputeVertices(null, null)) {
5495
- return false;
6389
+ if (sameAsPrevious) {
6390
+ connectivityData.adoptFrom(previousConnectivityData);
6391
+ } else {
6392
+ connectivityData.initEmpty(this._cornerTable);
6393
+ connectivityData.reserveSeamEdges(seamCount);
6394
+ for (let s = 0; s < seamCount; ++s) {
6395
+ connectivityData.addSeamEdge(seamCorners[s]);
6396
+ }
6397
+ if (!connectivityData.recomputeVertices(null, null)) {
6398
+ return false;
6399
+ }
5496
6400
  }
5497
6401
  previousConnectivityData = connectivityData;
6402
+ previousSeamCorners = seamCorners;
6403
+ previousSeamCount = seamCount;
5498
6404
  }
5499
6405
  this._posEncodingData.init(this._cornerTable.numVertices());
5500
6406
  for (let i = 0; i < this._attributeData.length; ++i) {
@@ -5533,39 +6439,27 @@ var MeshEdgebreakerDecoderImpl = class {
5533
6439
  const activeCornerStack = scratchInt32(numSymbols + this._topologySplitData.length + 16);
5534
6440
  let activeCornerStackSize = 0;
5535
6441
  const topologySplitActiveCorners = /* @__PURE__ */ new Map();
6442
+ const splitResult = { faceEdge: 0, encoderSplitSymbolId: 0 };
5536
6443
  const invalidVertices = [];
5537
6444
  const removeInvalidVertices = this._attributeData.length === 0;
5538
6445
  let maxNumVertices = this._isVertHole.length;
5539
6446
  let numFacesDecoded = 0;
5540
6447
  const cornerToVertex = this._cornerTable._cornerToVertex;
5541
6448
  const oppositeCorners = this._cornerTable._oppositeCorners;
5542
- const numCorners = this._cornerTable.numCorners();
5543
- const next = (c) => c < 0 ? -1 : c % 3 === 2 ? c - 2 : c + 1;
5544
- const prev = (c) => c < 0 ? -1 : c % 3 === 0 ? c + 2 : c - 1;
5545
- const vertex = (c) => c < 0 || c >= numCorners ? -1 : cornerToVertex[c];
5546
- const opposite = (c) => c < 0 || c >= numCorners ? -1 : oppositeCorners[c];
5547
- const leftMostCorner = (v) => v < 0 || v >= this._cornerTable._vertexCorners.length ? -1 : this._cornerTable._vertexCorners[v];
5548
- const swingLeft = (c) => {
5549
- const n = next(c);
5550
- const o = opposite(n);
5551
- return o < 0 ? -1 : next(o);
5552
- };
5553
- const swingRight = (c) => {
5554
- const p = prev(c);
5555
- const o = opposite(p);
5556
- return o < 0 ? -1 : prev(o);
5557
- };
5558
6449
  const vc = this._cornerTable;
6450
+ let vertexCorners = vc._vertexCorners;
6451
+ const isVertHole = this._isVertHole;
6452
+ const traversalDecoder = this._traversalDecoder;
5559
6453
  for (let symbolId = 0; symbolId < numSymbols; ++symbolId) {
5560
6454
  const faceIndex = numFacesDecoded++;
5561
6455
  let checkTopologySplit = false;
5562
- const symbol = this._traversalDecoder.decodeSymbol();
6456
+ const symbol = traversalDecoder.decodeSymbol();
5563
6457
  if (symbol === TOPOLOGY_C) {
5564
6458
  if (activeCornerStackSize === 0) return -1;
5565
6459
  const cornerA = activeCornerStack[activeCornerStackSize - 1];
5566
6460
  const nA = cornerA % 3 === 2 ? cornerA - 2 : cornerA + 1;
5567
6461
  const vertexX = cornerToVertex[nA];
5568
- const lmcX = vc._vertexCorners[vertexX];
6462
+ const lmcX = vertexCorners[vertexX];
5569
6463
  const cornerB = lmcX % 3 === 2 ? lmcX - 2 : lmcX + 1;
5570
6464
  if (cornerA === cornerB) return -1;
5571
6465
  if (oppositeCorners[cornerA] !== kInvalidCornerIndex6 || oppositeCorners[cornerB] !== kInvalidCornerIndex6) {
@@ -5584,8 +6478,8 @@ var MeshEdgebreakerDecoderImpl = class {
5584
6478
  cornerToVertex[corner] = vertexX;
5585
6479
  cornerToVertex[corner + 1] = vertBNext;
5586
6480
  cornerToVertex[corner + 2] = vertAPrev;
5587
- vc._vertexCorners[vertAPrev] = corner + 2;
5588
- this._isVertHole[vertexX] = 0;
6481
+ vertexCorners[vertAPrev] = corner + 2;
6482
+ isVertHole[vertexX] = 0;
5589
6483
  activeCornerStack[activeCornerStackSize - 1] = corner;
5590
6484
  } else if (symbol === TOPOLOGY_R || symbol === TOPOLOGY_L) {
5591
6485
  if (activeCornerStackSize === 0) return -1;
@@ -5606,14 +6500,20 @@ var MeshEdgebreakerDecoderImpl = class {
5606
6500
  }
5607
6501
  oppositeCorners[oppCorner] = cornerA;
5608
6502
  oppositeCorners[cornerA] = oppCorner;
5609
- const newVertIndex = this._cornerTable.addNewVertex();
5610
- if (this._cornerTable.numVertices() > maxNumVertices) return -1;
6503
+ let newVertIndex;
6504
+ if (vc._numVertices < vertexCorners.length) {
6505
+ newVertIndex = vc._numVertices++;
6506
+ } else {
6507
+ newVertIndex = vc.addNewVertex();
6508
+ vertexCorners = vc._vertexCorners;
6509
+ }
6510
+ if (vc._numVertices > maxNumVertices) return -1;
5611
6511
  cornerToVertex[oppCorner] = newVertIndex;
5612
- vc._vertexCorners[newVertIndex] = oppCorner;
6512
+ vertexCorners[newVertIndex] = oppCorner;
5613
6513
  const pA = cornerA % 3 === 0 ? cornerA + 2 : cornerA - 1;
5614
6514
  const vertexR = cornerToVertex[pA];
5615
6515
  cornerToVertex[cornerR] = vertexR;
5616
- vc._vertexCorners[vertexR] = cornerR;
6516
+ vertexCorners[vertexR] = cornerR;
5617
6517
  const nA = cornerA % 3 === 2 ? cornerA - 2 : cornerA + 1;
5618
6518
  cornerToVertex[cornerL] = cornerToVertex[nA];
5619
6519
  activeCornerStack[activeCornerStackSize - 1] = corner;
@@ -5645,11 +6545,11 @@ var MeshEdgebreakerDecoderImpl = class {
5645
6545
  const pB = cornerB % 3 === 0 ? cornerB + 2 : cornerB - 1;
5646
6546
  const vertBPrev = cornerToVertex[pB];
5647
6547
  cornerToVertex[corner + 2] = vertBPrev;
5648
- vc._vertexCorners[vertBPrev] = corner + 2;
6548
+ vertexCorners[vertBPrev] = corner + 2;
5649
6549
  let cornerN = cornerB % 3 === 2 ? cornerB - 2 : cornerB + 1;
5650
6550
  const vertexN = cornerToVertex[cornerN];
5651
- this._traversalDecoder.mergeVertices(vertexP, vertexN);
5652
- vc._vertexCorners[vertexP] = vc._vertexCorners[vertexN];
6551
+ traversalDecoder.mergeVertices(vertexP, vertexN);
6552
+ vertexCorners[vertexP] = vertexCorners[vertexN];
5653
6553
  const firstCorner = cornerN;
5654
6554
  while (cornerN !== kInvalidCornerIndex6) {
5655
6555
  cornerToVertex[cornerN] = vertexP;
@@ -5660,32 +6560,38 @@ var MeshEdgebreakerDecoderImpl = class {
5660
6560
  return -1;
5661
6561
  }
5662
6562
  }
5663
- vc._vertexCorners[vertexN] = -1;
6563
+ vertexCorners[vertexN] = -1;
5664
6564
  if (removeInvalidVertices) {
5665
6565
  invalidVertices.push(vertexN);
5666
6566
  }
5667
6567
  activeCornerStack[activeCornerStackSize - 1] = corner;
5668
6568
  } else if (symbol === TOPOLOGY_E) {
5669
6569
  const corner = 3 * faceIndex;
5670
- const firstVertIndex = this._cornerTable.addNewVertex();
5671
- this._cornerTable.addNewVertex();
5672
- this._cornerTable.addNewVertex();
5673
- if (this._cornerTable.numVertices() > maxNumVertices) return -1;
6570
+ let firstVertIndex;
6571
+ if (vc._numVertices + 3 <= vertexCorners.length) {
6572
+ firstVertIndex = vc._numVertices;
6573
+ vc._numVertices += 3;
6574
+ } else {
6575
+ firstVertIndex = vc.addNewVertex();
6576
+ vc.addNewVertex();
6577
+ vc.addNewVertex();
6578
+ vertexCorners = vc._vertexCorners;
6579
+ }
6580
+ if (vc._numVertices > maxNumVertices) return -1;
5674
6581
  cornerToVertex[corner] = firstVertIndex;
5675
6582
  cornerToVertex[corner + 1] = firstVertIndex + 1;
5676
6583
  cornerToVertex[corner + 2] = firstVertIndex + 2;
5677
- vc._vertexCorners[firstVertIndex] = corner;
5678
- vc._vertexCorners[firstVertIndex + 1] = corner + 1;
5679
- vc._vertexCorners[firstVertIndex + 2] = corner + 2;
6584
+ vertexCorners[firstVertIndex] = corner;
6585
+ vertexCorners[firstVertIndex + 1] = corner + 1;
6586
+ vertexCorners[firstVertIndex + 2] = corner + 2;
5680
6587
  activeCornerStack[activeCornerStackSize++] = corner;
5681
6588
  checkTopologySplit = true;
5682
6589
  } else {
5683
6590
  return -1;
5684
6591
  }
5685
- this._traversalDecoder.newActiveCornerReached(activeCornerStack[activeCornerStackSize - 1]);
5686
- if (checkTopologySplit) {
6592
+ traversalDecoder.newActiveCornerReached(activeCornerStack[activeCornerStackSize - 1]);
6593
+ if (checkTopologySplit && this._topologySplitData.length > 0) {
5687
6594
  const encoderSymbolId = numSymbols - symbolId - 1;
5688
- const splitResult = { faceEdge: 0, encoderSplitSymbolId: 0 };
5689
6595
  while (this._isTopologySplit(encoderSymbolId, splitResult)) {
5690
6596
  if (splitResult.encoderSplitSymbolId < 0) return -1;
5691
6597
  const actTopCorner = activeCornerStack[activeCornerStackSize - 1];
@@ -5700,15 +6606,39 @@ var MeshEdgebreakerDecoderImpl = class {
5700
6606
  }
5701
6607
  }
5702
6608
  }
5703
- if (this._cornerTable.numVertices() > maxNumVertices) {
6609
+ if (vc._numVertices > maxNumVertices) {
6610
+ return -1;
6611
+ }
6612
+ numFacesDecoded = this._decodeStartFaces(activeCornerStack, activeCornerStackSize, numFacesDecoded);
6613
+ if (numFacesDecoded === -1) {
6614
+ return -1;
6615
+ }
6616
+ if (numFacesDecoded !== this._cornerTable.numFaces()) {
5704
6617
  return -1;
5705
6618
  }
6619
+ return this._removeInvalidVertices(invalidVertices);
6620
+ }
6621
+ // Connects the remaining active-stack corners to newly decoded start faces.
6622
+ // Returns the updated decoded-face count, or -1 on malformed input.
6623
+ _decodeStartFaces(activeCornerStack, activeCornerStackSize, numFacesDecoded) {
6624
+ const ct = this._cornerTable;
6625
+ const cornerToVertex = ct._cornerToVertex;
6626
+ const oppositeCorners = ct._oppositeCorners;
6627
+ const vertexCorners = ct._vertexCorners;
6628
+ const isVertHole = this._isVertHole;
6629
+ const traversalDecoder = this._traversalDecoder;
6630
+ const numCorners = ct.numCorners();
6631
+ const numFaces = ct.numFaces();
6632
+ const next = (c) => c < 0 ? -1 : c % 3 === 2 ? c - 2 : c + 1;
6633
+ const vertex = (c) => c < 0 || c >= numCorners ? -1 : cornerToVertex[c];
6634
+ const opposite = (c) => c < 0 || c >= numCorners ? -1 : oppositeCorners[c];
6635
+ const leftMostCorner = (v) => v < 0 || v >= vertexCorners.length ? -1 : vertexCorners[v];
5706
6636
  while (activeCornerStackSize > 0) {
5707
6637
  const corner = activeCornerStack[activeCornerStackSize - 1];
5708
6638
  activeCornerStackSize--;
5709
- const interiorFace = this._traversalDecoder.decodeStartFaceConfiguration();
6639
+ const interiorFace = traversalDecoder.decodeStartFaceConfiguration();
5710
6640
  if (interiorFace) {
5711
- if (numFacesDecoded >= this._cornerTable.numFaces()) {
6641
+ if (numFacesDecoded >= numFaces) {
5712
6642
  return -1;
5713
6643
  }
5714
6644
  const cornerA = corner;
@@ -5734,9 +6664,9 @@ var MeshEdgebreakerDecoderImpl = class {
5734
6664
  cornerToVertex[newCorner] = vertX;
5735
6665
  cornerToVertex[newCorner + 1] = vertP;
5736
6666
  cornerToVertex[newCorner + 2] = vertN;
5737
- this._isVertHole[vertX] = 0;
5738
- this._isVertHole[vertP] = 0;
5739
- this._isVertHole[vertN] = 0;
6667
+ isVertHole[vertX] = 0;
6668
+ isVertHole[vertP] = 0;
6669
+ isVertHole[vertN] = 0;
5740
6670
  this._initFaceConfigurations.push(true);
5741
6671
  this._initCorners.push(newCorner);
5742
6672
  } else {
@@ -5744,10 +6674,34 @@ var MeshEdgebreakerDecoderImpl = class {
5744
6674
  this._initCorners.push(corner);
5745
6675
  }
5746
6676
  }
5747
- if (numFacesDecoded !== this._cornerTable.numFaces()) {
5748
- return -1;
5749
- }
5750
- let numVertices = this._cornerTable.numVertices();
6677
+ return numFacesDecoded;
6678
+ }
6679
+ // Removes invalid (isolated) vertices by swapping them with the last valid
6680
+ // vertex in the table, matching C++ mesh_edgebreaker_decoder_impl.cc (the
6681
+ // forward iteration order matters). Returns the final vertex count, or -1.
6682
+ _removeInvalidVertices(invalidVertices) {
6683
+ const ct = this._cornerTable;
6684
+ const cornerToVertex = ct._cornerToVertex;
6685
+ const oppositeCorners = ct._oppositeCorners;
6686
+ const vertexCorners = ct._vertexCorners;
6687
+ const isVertHole = this._isVertHole;
6688
+ const numCorners = ct.numCorners();
6689
+ const next = (c) => c < 0 ? -1 : c % 3 === 2 ? c - 2 : c + 1;
6690
+ const prev = (c) => c < 0 ? -1 : c % 3 === 0 ? c + 2 : c - 1;
6691
+ const vertex = (c) => c < 0 || c >= numCorners ? -1 : cornerToVertex[c];
6692
+ const opposite = (c) => c < 0 || c >= numCorners ? -1 : oppositeCorners[c];
6693
+ const leftMostCorner = (v) => v < 0 || v >= vertexCorners.length ? -1 : vertexCorners[v];
6694
+ const swingLeft = (c) => {
6695
+ const n = next(c);
6696
+ const o = opposite(n);
6697
+ return o < 0 ? -1 : next(o);
6698
+ };
6699
+ const swingRight = (c) => {
6700
+ const p = prev(c);
6701
+ const o = opposite(p);
6702
+ return o < 0 ? -1 : prev(o);
6703
+ };
6704
+ let numVertices = ct.numVertices();
5751
6705
  for (let ivIdx = 0; ivIdx < invalidVertices.length; ++ivIdx) {
5752
6706
  const invalidVert = invalidVertices[ivIdx];
5753
6707
  let srcVert = numVertices - 1;
@@ -5777,10 +6731,10 @@ var MeshEdgebreakerDecoderImpl = class {
5777
6731
  cid = swingRight(cid);
5778
6732
  }
5779
6733
  }
5780
- this._cornerTable._vertexCorners[invalidVert] = leftMostCorner(srcVert);
5781
- this._cornerTable._vertexCorners[srcVert] = -1;
5782
- this._isVertHole[invalidVert] = this._isVertHole[srcVert];
5783
- this._isVertHole[srcVert] = 0;
6734
+ vertexCorners[invalidVert] = leftMostCorner(srcVert);
6735
+ vertexCorners[srcVert] = -1;
6736
+ isVertHole[invalidVert] = isVertHole[srcVert];
6737
+ isVertHole[srcVert] = 0;
5784
6738
  numVertices--;
5785
6739
  }
5786
6740
  return numVertices;
@@ -5823,14 +6777,55 @@ var MeshEdgebreakerDecoderImpl = class {
5823
6777
  // per-corner decodeNextBit work. Within each face the three corners are
5824
6778
  // visited in encoder edge order [base, next, prev] = [c, c+1, c+2] (the
5825
6779
  // caller always starts a face at its base corner, so next/prev need no wrap).
6780
+ //
6781
+ // The face comparison the C++ makes -- floor(oppCorner/3) >= floor(cc/3) for
6782
+ // the face's base corner -- is just `oppCorner >= faceBaseCorner`, since the
6783
+ // base corner is a multiple of 3. That removes the per-corner division; the
6784
+ // invalid-corner case (-1) is still handled by the branch above it.
5826
6785
  _decodeAttributeConnectivities() {
5827
6786
  const oppositeCorners = this._cornerTable.oppositeCornerArray();
5828
6787
  const attributeData = this._attributeData;
5829
6788
  const numAttrData = attributeData.length;
5830
6789
  const connectivityDecoders = this._traversalDecoder._attributeConnectivityDecoders;
5831
6790
  const numCorners = this._cornerTable.numCorners();
6791
+ if (numAttrData === 1) {
6792
+ const ad = attributeData[0];
6793
+ const seamCorners = ad.attributeSeamCorners;
6794
+ let numSeamCorners = ad.numSeamCorners;
6795
+ const decoder = connectivityDecoders[0];
6796
+ const ans = decoder.ansDecoder_;
6797
+ const p = decoder.p_;
6798
+ const buf = ans.buf;
6799
+ const bufStart = ans.bufStart;
6800
+ let state = ans.state;
6801
+ let bufOffset = ans.bufOffset;
6802
+ for (let corner = 0; corner < numCorners; corner += 3) {
6803
+ for (let k = 0; k < 3; ++k) {
6804
+ const cc = corner + k;
6805
+ const oppCorner = oppositeCorners[cc];
6806
+ if (oppCorner === kInvalidCornerIndex6) {
6807
+ seamCorners[numSeamCorners++] = cc;
6808
+ } else if (oppCorner >= corner) {
6809
+ if (state < ANS_L_BASE && bufOffset > bufStart) {
6810
+ state = state << 8 | buf[--bufOffset];
6811
+ }
6812
+ const rem = state & 255;
6813
+ const xn = (state >>> 8) * p;
6814
+ if (rem < p) {
6815
+ state = xn + rem;
6816
+ seamCorners[numSeamCorners++] = cc;
6817
+ } else {
6818
+ state = state - xn - p;
6819
+ }
6820
+ }
6821
+ }
6822
+ }
6823
+ ans.state = state;
6824
+ ans.bufOffset = bufOffset;
6825
+ ad.numSeamCorners = numSeamCorners;
6826
+ return;
6827
+ }
5832
6828
  for (let corner = 0; corner < numCorners; corner += 3) {
5833
- const srcFaceId = corner / 3 | 0;
5834
6829
  for (let k = 0; k < 3; ++k) {
5835
6830
  const cc = corner + k;
5836
6831
  const oppCorner = oppositeCorners[cc];
@@ -5839,7 +6834,7 @@ var MeshEdgebreakerDecoderImpl = class {
5839
6834
  const ad = attributeData[i];
5840
6835
  ad.attributeSeamCorners[ad.numSeamCorners++] = cc;
5841
6836
  }
5842
- } else if ((oppCorner / 3 | 0) >= srcFaceId) {
6837
+ } else if (oppCorner >= corner) {
5843
6838
  for (let i = 0; i < numAttrData; ++i) {
5844
6839
  if (connectivityDecoders[i].decodeNextBit()) {
5845
6840
  const ad = attributeData[i];
@@ -5870,10 +6865,10 @@ var MeshEdgebreakerDecoderImpl = class {
5870
6865
  const attributeData = this._attributeData;
5871
6866
  const numAttrData = attributeData.length;
5872
6867
  let numPoints = 0;
5873
- const cornerToPointMap = new Int32Array(ct.numCorners());
6868
+ const cornerToPointMap = scratchInt32(ct.numCorners());
5874
6869
  const numVertices = ct.numVertices();
5875
6870
  const vertexLeftmost = ct.vertexLeftmostCornerArray();
5876
- const baseOpp = ct.oppositeCornerArray();
6871
+ const swingRight = ct.swingRightArray();
5877
6872
  const _baseCornerToVertex = ct.cornerToVertexArray();
5878
6873
  const isVertHole = this._isVertHole;
5879
6874
  const attCornerToVertex = new Array(numAttrData);
@@ -5887,7 +6882,7 @@ var MeshEdgebreakerDecoderImpl = class {
5887
6882
  if (numAttrData === 1) {
5888
6883
  anyAttVertexOnSeam = attVertexOnSeam[0];
5889
6884
  } else {
5890
- anyAttVertexOnSeam = new Uint8Array(numVertices);
6885
+ anyAttVertexOnSeam = scratchUint8Zeroed(numVertices);
5891
6886
  for (let i = 0; i < numAttrData; ++i) {
5892
6887
  const attSeam = attVertexOnSeam[i];
5893
6888
  for (let v = 0; v < numVertices; ++v) {
@@ -5905,37 +6900,24 @@ var MeshEdgebreakerDecoderImpl = class {
5905
6900
  const initialC = c;
5906
6901
  const pointId = numPoints++;
5907
6902
  cornerToPointMap[initialC] = pointId;
5908
- let rem = initialC % 3;
5909
- let pv = rem === 0 ? initialC + 2 : initialC - 1;
5910
- let opp = baseOpp[pv];
5911
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6903
+ c = swingRight[initialC];
5912
6904
  while (c !== kInvalidCornerIndex6 && c !== initialC) {
5913
6905
  cornerToPointMap[c] = pointId;
5914
- rem = c % 3;
5915
- pv = rem === 0 ? c + 2 : c - 1;
5916
- opp = baseOpp[pv];
5917
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6906
+ c = swingRight[c];
5918
6907
  }
5919
6908
  } else {
5920
6909
  let deduplicationFirstCorner = c;
5921
- let rem, pv, opp;
5922
6910
  if (!isVertHole[v]) {
5923
6911
  if (numAttrData === 1) {
5924
6912
  const vertId = singleAttC2V[c];
5925
- rem = c % 3;
5926
- pv = rem === 0 ? c + 2 : c - 1;
5927
- opp = baseOpp[pv];
5928
- let actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6913
+ let actC = swingRight[c];
5929
6914
  while (actC !== c) {
5930
6915
  if (actC === kInvalidCornerIndex6) return false;
5931
6916
  if (singleAttC2V[actC] !== vertId) {
5932
6917
  deduplicationFirstCorner = actC;
5933
6918
  break;
5934
6919
  }
5935
- rem = actC % 3;
5936
- pv = rem === 0 ? actC + 2 : actC - 1;
5937
- opp = baseOpp[pv];
5938
- actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6920
+ actC = swingRight[actC];
5939
6921
  }
5940
6922
  } else {
5941
6923
  for (let i = 0; i < numAttrData; ++i) {
@@ -5944,10 +6926,7 @@ var MeshEdgebreakerDecoderImpl = class {
5944
6926
  }
5945
6927
  const attC2V = attCornerToVertex[i];
5946
6928
  const vertId = attC2V[c];
5947
- rem = c % 3;
5948
- pv = rem === 0 ? c + 2 : c - 1;
5949
- opp = baseOpp[pv];
5950
- let actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6929
+ let actC = swingRight[c];
5951
6930
  let seamFound = false;
5952
6931
  while (actC !== c) {
5953
6932
  if (actC === kInvalidCornerIndex6) return false;
@@ -5956,10 +6935,7 @@ var MeshEdgebreakerDecoderImpl = class {
5956
6935
  seamFound = true;
5957
6936
  break;
5958
6937
  }
5959
- rem = actC % 3;
5960
- pv = rem === 0 ? actC + 2 : actC - 1;
5961
- opp = baseOpp[pv];
5962
- actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6938
+ actC = swingRight[actC];
5963
6939
  }
5964
6940
  if (seamFound) break;
5965
6941
  }
@@ -5968,10 +6944,7 @@ var MeshEdgebreakerDecoderImpl = class {
5968
6944
  c = deduplicationFirstCorner;
5969
6945
  cornerToPointMap[c] = numPoints++;
5970
6946
  let prevC = c;
5971
- rem = c % 3;
5972
- pv = rem === 0 ? c + 2 : c - 1;
5973
- opp = baseOpp[pv];
5974
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6947
+ c = swingRight[c];
5975
6948
  while (c !== kInvalidCornerIndex6 && c !== deduplicationFirstCorner) {
5976
6949
  let attributeSeam;
5977
6950
  if (numAttrData === 1) {
@@ -5992,10 +6965,7 @@ var MeshEdgebreakerDecoderImpl = class {
5992
6965
  cornerToPointMap[c] = cornerToPointMap[prevC];
5993
6966
  }
5994
6967
  prevC = c;
5995
- rem = c % 3;
5996
- pv = rem === 0 ? c + 2 : c - 1;
5997
- opp = baseOpp[pv];
5998
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6968
+ c = swingRight[c];
5999
6969
  }
6000
6970
  }
6001
6971
  }
@@ -6021,8 +6991,8 @@ var MeshAttributeIndicesEncodingData = class {
6021
6991
  this._numValues = 0;
6022
6992
  }
6023
6993
  init(numVertices) {
6024
- this._vertexToEncodedAttributeValueIndexMap = new Int32Array(numVertices);
6025
- this._encodedAttributeValueIndexToCornerMap = new Int32Array(numVertices);
6994
+ this._vertexToEncodedAttributeValueIndexMap = scratchInt32Filled(numVertices, -1);
6995
+ this._encodedAttributeValueIndexToCornerMap = scratchInt32(numVertices);
6026
6996
  this._numValues = 0;
6027
6997
  }
6028
6998
  // Adopts a traversal result from an identical corner table, avoiding a
@@ -6072,6 +7042,8 @@ var CornerTable = class {
6072
7042
  // corner -> opposite corner
6073
7043
  _vertexCorners;
6074
7044
  // vertex -> left-most corner
7045
+ _swingRight;
7046
+ // corner -> next corner around its vertex, CW
6075
7047
  constructor() {
6076
7048
  this._numFaces = 0;
6077
7049
  this._numCorners = 0;
@@ -6079,16 +7051,42 @@ var CornerTable = class {
6079
7051
  this._cornerToVertex = null;
6080
7052
  this._oppositeCorners = null;
6081
7053
  this._vertexCorners = null;
7054
+ this._swingRight = null;
6082
7055
  }
6083
7056
  reset(numFaces, numVertices) {
6084
7057
  this._numFaces = numFaces;
6085
7058
  this._numCorners = numFaces * 3;
6086
7059
  this._numVertices = 0;
6087
- this._cornerToVertex = new Int32Array(this._numCorners).fill(-1);
6088
- this._oppositeCorners = new Int32Array(this._numCorners).fill(-1);
6089
- this._vertexCorners = new Int32Array(numVertices).fill(-1);
7060
+ this._cornerToVertex = scratchInt32Filled(this._numCorners, -1);
7061
+ this._oppositeCorners = scratchInt32Filled(this._numCorners, -1);
7062
+ this._vertexCorners = scratchInt32Filled(numVertices, -1);
7063
+ this._swingRight = null;
6090
7064
  return true;
6091
7065
  }
7066
+ // swingRight(c) = previous(opposite(previous(c))) for every corner. Walking
7067
+ // the corner ring of a vertex is the inner loop of both the attribute-vertex
7068
+ // recompute and the point assignment, and each of those passes otherwise
7069
+ // pays two modulus-by-3 chains per step on top of the opposite lookup. Built
7070
+ // lazily -- callers only reach it once connectivity is final -- from
7071
+ // decode-scoped scratch, and dropped by reset().
7072
+ swingRightArray() {
7073
+ let table = this._swingRight;
7074
+ if (table === null) {
7075
+ const numCorners = this._numCorners;
7076
+ const opposite = this._oppositeCorners;
7077
+ table = scratchInt32(numCorners);
7078
+ for (let c = 0; c < numCorners; c += 3) {
7079
+ let o = opposite[c + 2];
7080
+ table[c] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
7081
+ o = opposite[c];
7082
+ table[c + 1] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
7083
+ o = opposite[c + 1];
7084
+ table[c + 2] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
7085
+ }
7086
+ this._swingRight = table;
7087
+ }
7088
+ return table;
7089
+ }
6092
7090
  numFaces() {
6093
7091
  return this._numFaces;
6094
7092
  }
@@ -6132,8 +7130,7 @@ var CornerTable = class {
6132
7130
  this._numVertices++;
6133
7131
  if (newVertex >= this._vertexCorners.length) {
6134
7132
  const newCapacity = Math.max(newVertex + 1, this._vertexCorners.length * 2, 64);
6135
- const newArr = new Int32Array(newCapacity);
6136
- newArr.fill(-1);
7133
+ const newArr = scratchInt32Filled(newCapacity, -1);
6137
7134
  newArr.set(this._vertexCorners);
6138
7135
  this._vertexCorners = newArr;
6139
7136
  }
@@ -6164,6 +7161,10 @@ var MeshEdgebreakerTraversalDecoder = class {
6164
7161
  _attributeConnectivityDecoders;
6165
7162
  _numAttributeData;
6166
7163
  _decoderImpl;
7164
+ // _symbolBuffer's bit cursor, captured once bit decoding starts: decodeSymbol
7165
+ // runs per decoded face and would otherwise reach it through two property
7166
+ // loads and a method call per read.
7167
+ _symbolBits;
6167
7168
  constructor() {
6168
7169
  this._buffer = new DecoderBuffer();
6169
7170
  this._symbolBuffer = new DecoderBuffer();
@@ -6171,6 +7172,7 @@ var MeshEdgebreakerTraversalDecoder = class {
6171
7172
  this._attributeConnectivityDecoders = null;
6172
7173
  this._numAttributeData = 0;
6173
7174
  this._decoderImpl = null;
7175
+ this._symbolBits = null;
6174
7176
  }
6175
7177
  init(decoder) {
6176
7178
  this._decoderImpl = decoder;
@@ -6205,6 +7207,21 @@ var MeshEdgebreakerTraversalDecoder = class {
6205
7207
  return this._startFaceDecoder.decodeNextBit() ? true : false;
6206
7208
  }
6207
7209
  decodeSymbol() {
7210
+ const bd = this._symbolBits;
7211
+ if (bd !== null) {
7212
+ const buf = bd._bitBuffer;
7213
+ const off = bd._bitOffset;
7214
+ const byteOffset = off >> 3;
7215
+ if (byteOffset + 4 < bd._byteLength) {
7216
+ const bits = (buf[byteOffset] | buf[byteOffset + 1] << 8 | buf[byteOffset + 2] << 16 | buf[byteOffset + 3] << 24) >>> (off & 7) & 7;
7217
+ if ((bits & 1) === TOPOLOGY_C) {
7218
+ bd._bitOffset = off + 1;
7219
+ return TOPOLOGY_C;
7220
+ }
7221
+ bd._bitOffset = off + 3;
7222
+ return bits;
7223
+ }
7224
+ }
6208
7225
  let symbol = this._symbolBuffer.decodeLeastSignificantBits32(1);
6209
7226
  if (symbol === TOPOLOGY_C) {
6210
7227
  return symbol;
@@ -6234,6 +7251,7 @@ var MeshEdgebreakerTraversalDecoder = class {
6234
7251
  if (traversalSize === void 0) {
6235
7252
  return false;
6236
7253
  }
7254
+ this._symbolBits = this._symbolBuffer._bitDecoder;
6237
7255
  this._buffer.init(
6238
7256
  this._symbolBuffer.dataHead,
6239
7257
  this._symbolBuffer.remainingSize,
@@ -6386,7 +7404,13 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6386
7404
  _maxValence;
6387
7405
  _vertexValences;
6388
7406
  _contextSymbols;
7407
+ // Int32Array, not number[]: read and written once per decoded symbol.
6389
7408
  _contextCounters;
7409
+ // corner -> vertex of _cornerTable, cached at init(); the array is created
7410
+ // once by CornerTable.reset() before the traversal decoder is initialized and
7411
+ // never replaced, so the per-symbol hot path can read it without two property
7412
+ // loads.
7413
+ _cornerToVertex;
6390
7414
  constructor() {
6391
7415
  super();
6392
7416
  this._cornerTable = null;
@@ -6397,11 +7421,13 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6397
7421
  this._maxValence = 7;
6398
7422
  this._vertexValences = new Int32Array(0);
6399
7423
  this._contextSymbols = [];
6400
- this._contextCounters = [];
7424
+ this._contextCounters = new Int32Array(0);
7425
+ this._cornerToVertex = new Int32Array(0);
6401
7426
  }
6402
7427
  init(decoder) {
6403
7428
  super.init(decoder);
6404
7429
  this._cornerTable = decoder.getCornerTable();
7430
+ this._cornerToVertex = this._cornerTable._cornerToVertex;
6405
7431
  }
6406
7432
  setNumEncodedVertices(numVertices) {
6407
7433
  this._numVertices = numVertices;
@@ -6419,10 +7445,11 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6419
7445
  if (this._numVertices < 0) {
6420
7446
  return false;
6421
7447
  }
6422
- this._vertexValences = new Int32Array(this._numVertices);
7448
+ this._vertexValences = scratchInt32Filled(this._numVertices, 0);
6423
7449
  const numUniqueValences = this._maxValence - this._minValence + 1;
6424
7450
  this._contextSymbols = new Array(numUniqueValences);
6425
- this._contextCounters = new Array(numUniqueValences);
7451
+ this._contextCounters = new Int32Array(numUniqueValences);
7452
+ const pending = [];
6426
7453
  for (let i = 0; i < numUniqueValences; ++i) {
6427
7454
  const numSymbols = decodeVarint(outBuffer);
6428
7455
  if (numSymbols === void 0) {
@@ -6432,16 +7459,73 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6432
7459
  return false;
6433
7460
  }
6434
7461
  if (numSymbols > 0) {
6435
- this._contextSymbols[i] = new Uint32Array(numSymbols);
6436
- if (!decodeSymbols(numSymbols, 1, outBuffer, this._contextSymbols[i])) {
7462
+ this._contextSymbols[i] = scratchUint32(numSymbols);
7463
+ const scheme = outBuffer.decodeUint8();
7464
+ if (scheme === SymbolCodingMethod.SYMBOL_CODING_TAGGED) {
7465
+ if (!decodeTaggedSymbols(numSymbols, 1, outBuffer, this._contextSymbols[i])) {
7466
+ return false;
7467
+ }
7468
+ this._contextCounters[i] = numSymbols;
7469
+ continue;
7470
+ }
7471
+ if (scheme !== SymbolCodingMethod.SYMBOL_CODING_RAW) {
6437
7472
  return false;
6438
7473
  }
7474
+ const maxBitLength = outBuffer.decodeUint8();
7475
+ if (maxBitLength === void 0 || maxBitLength < 1 || maxBitLength > 18) {
7476
+ return false;
7477
+ }
7478
+ const decoder = new RAnsSymbolDecoder(maxBitLength);
7479
+ if (!decoder.create(outBuffer, numSymbols)) {
7480
+ return false;
7481
+ }
7482
+ if (decoder.numSymbols === 0) {
7483
+ return false;
7484
+ }
7485
+ if (!decoder.startDecoding(outBuffer)) {
7486
+ return false;
7487
+ }
7488
+ pending.push({ decoder, out: this._contextSymbols[i], count: numSymbols });
6439
7489
  this._contextCounters[i] = numSymbols;
6440
7490
  } else {
6441
7491
  this._contextSymbols[i] = new Uint32Array(0);
6442
7492
  this._contextCounters[i] = 0;
6443
7493
  }
6444
7494
  }
7495
+ const lockstep = pending.filter((entry) => entry.decoder.ans_.lutTable instanceof Uint8Array);
7496
+ let p = 0;
7497
+ while (lockstep.length - p >= 3) {
7498
+ const a = lockstep[p];
7499
+ const b = lockstep[p + 1];
7500
+ const c = lockstep[p + 2];
7501
+ ransDecodeSymbolsTrioU8(
7502
+ a.decoder.ans_,
7503
+ a.out,
7504
+ a.count,
7505
+ b.decoder.ans_,
7506
+ b.out,
7507
+ b.count,
7508
+ c.decoder.ans_,
7509
+ c.out,
7510
+ c.count
7511
+ );
7512
+ p += 3;
7513
+ }
7514
+ if (lockstep.length - p === 2) {
7515
+ const a = lockstep[p];
7516
+ const b = lockstep[p + 1];
7517
+ ransDecodeSymbolsPairU8(a.decoder.ans_, a.out, a.count, b.decoder.ans_, b.out, b.count);
7518
+ p += 2;
7519
+ }
7520
+ for (; p < lockstep.length; ++p) {
7521
+ lockstep[p].decoder.ans_.decodeSymbols(lockstep[p].out, lockstep[p].count);
7522
+ }
7523
+ for (const entry of pending) {
7524
+ if (!(entry.decoder.ans_.lutTable instanceof Uint8Array)) {
7525
+ entry.decoder.ans_.decodeSymbols(entry.out, entry.count);
7526
+ }
7527
+ entry.decoder.endDecoding();
7528
+ }
6445
7529
  return true;
6446
7530
  }
6447
7531
  decodeSymbol() {
@@ -6461,7 +7545,7 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6461
7545
  return this._lastSymbol;
6462
7546
  }
6463
7547
  newActiveCornerReached(corner) {
6464
- const cornerToVertex = this._cornerTable._cornerToVertex;
7548
+ const cornerToVertex = this._cornerToVertex;
6465
7549
  const valences = this._vertexValences;
6466
7550
  const next = corner % 3 === 2 ? corner - 2 : corner + 1;
6467
7551
  const prev = corner % 3 === 0 ? corner + 2 : corner - 1;