minidraco 0.4.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/README.md CHANGED
@@ -43,16 +43,17 @@ Median across an 18-model corpus vs [draco.js](https://github.com/mrdoob/draco.j
43
43
  [draco3d](https://www.npmjs.com/package/draco3d) wasm decoder (full results in
44
44
  [BENCH.md](https://github.com/verekia/minidraco/blob/main/BENCH.md)):
45
45
 
46
- | benchmark | vs draco.js | vs draco3d wasm |
47
- | --------------------------------------------- | --------------- | --------------- |
48
- | single-threaded decode — bun (JSC) | 🟢 1.23× faster | 🟢 1.32× faster |
49
- | single-threaded decode — Chrome (V8) | 🟢 1.26× faster | 🟢 1.06× faster |
50
- | `GLTFLoader.parse`, worker pool — Chrome (V8) | 🟢 1.46× faster | 🔴 1.05× slower |
51
-
52
- Faster than draco.js across the corpus, ahead of the wasm decoder single-threaded, and
53
- competitive in a real `GLTFLoader.parse` with the main thread left free.
54
-
55
- 🟢 There's no `draco_decoder.wasm` to fetch and compile before the first decode, so minidraco (and draco.js) often beat the wasm decoder on first load (not reflected on the benchmark).
46
+ | benchmark | vs draco.js | vs draco3d wasm |
47
+ | -------------------------------------------------- | --------------- | --------------- |
48
+ | single-threaded decode — bun (JSC) | 🟢 1.27× faster | 🟢 1.38× faster |
49
+ | single-threaded decode — Chrome (V8) | 🟢 1.29× faster | 🟢 1.11× faster |
50
+ | `GLTFLoader.parse`, warm worker pool — Chrome (V8) | 🟢 1.62× faster | even |
51
+ | `GLTFLoader.parse`, cold first load — Chrome (V8) | 🟢 1.31× faster | ⚪ even |
52
+
53
+ Faster than draco.js across the corpus, ahead of the wasm decoder single-threaded, and even with
54
+ it in a real `GLTFLoader.parse` with the main thread left free — both warm and on the first load
55
+ of a session, where minidraco's worker pool is still JIT-warming while the wasm decoder is
56
+ fetching and compiling its module (no `draco_decoder.wasm` to host or download here).
56
57
 
57
58
  ## Download size
58
59
 
package/dist/index.js CHANGED
@@ -1264,6 +1264,8 @@ var MetadataDecoder = class {
1264
1264
  var ANS_P8_PRECISION = 256;
1265
1265
  var ANS_L_BASE = 4096;
1266
1266
  var ANS_IO_BASE = 256;
1267
+ var COARSE_STREAM_FACTOR = 8;
1268
+ var COARSE_BUCKET_BITS = 8;
1267
1269
  function memGetLe16(buf, offset) {
1268
1270
  return buf[offset] | buf[offset + 1] << 8;
1269
1271
  }
@@ -1341,6 +1343,13 @@ var RAnsDecoder = class {
1341
1343
  lutTable;
1342
1344
  probTable;
1343
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;
1344
1353
  buf;
1345
1354
  bufOffset;
1346
1355
  // First valid byte of this decoder's slice within buf (absolute offsets,
@@ -1355,6 +1364,9 @@ var RAnsDecoder = class {
1355
1364
  this.lutTable = null;
1356
1365
  this.probTable = null;
1357
1366
  this.cumProbTable = null;
1367
+ this.coarse = false;
1368
+ this.bucketShift = 0;
1369
+ this.bucketTable = null;
1358
1370
  this.buf = null;
1359
1371
  this.bufOffset = 0;
1360
1372
  this.bufStart = 0;
@@ -1414,6 +1426,10 @@ var RAnsDecoder = class {
1414
1426
  tablePool.push(this.cumProbTable);
1415
1427
  this.cumProbTable = null;
1416
1428
  }
1429
+ if (this.bucketTable !== null) {
1430
+ tablePool.push(this.bucketTable);
1431
+ this.bucketTable = null;
1432
+ }
1417
1433
  return this.state === this.lRansBase;
1418
1434
  }
1419
1435
  ransRead() {
@@ -1427,7 +1443,14 @@ var RAnsDecoder = class {
1427
1443
  }
1428
1444
  const quo = state >>> this.ransPrecisionBits;
1429
1445
  const rem = state & this.ransPrecisionMask;
1430
- const symbol = this.lutTable[rem];
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
+ }
1431
1454
  this.state = quo * this.probTable[symbol] + rem - this.cumProbTable[symbol];
1432
1455
  this.bufOffset = bufOffset;
1433
1456
  return symbol;
@@ -1439,6 +1462,10 @@ var RAnsDecoder = class {
1439
1462
  // once here so each loop body stays monomorphic on its concrete type. The
1440
1463
  // three bodies are intentionally identical copies.
1441
1464
  decodeSymbols(out, count) {
1465
+ if (this.coarse) {
1466
+ this._decodeSymbolsCoarse(out, count);
1467
+ return;
1468
+ }
1442
1469
  const lutTable = this.lutTable;
1443
1470
  if (lutTable instanceof Uint8Array) {
1444
1471
  this._decodeSymbolsU8(out, count, lutTable);
@@ -1492,6 +1519,33 @@ var RAnsDecoder = class {
1492
1519
  this.state = state;
1493
1520
  this.bufOffset = bufOffset;
1494
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
+ }
1495
1549
  _decodeSymbolsU32(out, count, lutTable) {
1496
1550
  const buf = this.buf;
1497
1551
  const lRansBase = this.lRansBase;
@@ -1514,23 +1568,62 @@ var RAnsDecoder = class {
1514
1568
  this.state = state;
1515
1569
  this.bufOffset = bufOffset;
1516
1570
  }
1517
- // Builds the ransPrecision-entry lookup table. Returns false on bad input data.
1518
- ransBuildLookUpTable(tokenProbs, numSymbols) {
1519
- const LutArray = numSymbols <= 256 ? Uint8Array : numSymbols <= 65536 ? Uint16Array : Uint32Array;
1520
- const lutTable = acquirePooled(LutArray, this.ransPrecision);
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;
1521
1585
  const probTable = acquirePooled(Uint32Array, numSymbols);
1522
- const cumProbTable = acquirePooled(Uint32Array, numSymbols);
1523
- this.lutTable = lutTable;
1586
+ const cumProbTable = acquirePooled(Uint32Array, numSymbols + 1);
1524
1587
  this.probTable = probTable;
1525
1588
  this.cumProbTable = cumProbTable;
1526
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;
1527
1620
  let actProb = 0;
1528
1621
  for (let i = 0; i < numSymbols; ++i) {
1529
1622
  const prob = tokenProbs[i];
1530
1623
  probTable[i] = prob;
1531
1624
  cumProbTable[i] = cumProb;
1532
1625
  cumProb += prob;
1533
- if (cumProb > this.ransPrecision) {
1626
+ if (cumProb > ransPrecision) {
1534
1627
  return false;
1535
1628
  }
1536
1629
  if (prob < 32) {
@@ -1542,9 +1635,10 @@ var RAnsDecoder = class {
1542
1635
  }
1543
1636
  actProb = cumProb;
1544
1637
  }
1545
- if (cumProb !== this.ransPrecision) {
1638
+ if (cumProb !== ransPrecision) {
1546
1639
  return false;
1547
1640
  }
1641
+ cumProbTable[numSymbols] = ransPrecision;
1548
1642
  return true;
1549
1643
  }
1550
1644
  };
@@ -1993,14 +2087,18 @@ var PointCloudDecoder = class _PointCloudDecoder {
1993
2087
  for (let i2 = 0; i2 < decoders.length; i2++) {
1994
2088
  decoders[i2].collectPendingSymbolStreams(pending);
1995
2089
  }
2090
+ const paired = pending.filter((stream) => !stream.ans.coarse);
1996
2091
  let i = 0;
1997
- for (; i + 1 < pending.length; i += 2) {
1998
- const a = pending[i];
1999
- const b = pending[i + 1];
2092
+ for (; i + 1 < paired.length; i += 2) {
2093
+ const a = paired[i];
2094
+ const b = paired[i + 1];
2000
2095
  ransDecodeSymbolsPair(a.ans, a.out, a.count, b.ans, b.out, b.count);
2001
2096
  }
2002
- if (i < pending.length) {
2003
- pending[i].ans.decodeSymbols(pending[i].out, pending[i].count);
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);
2004
2102
  }
2005
2103
  for (let k = 0; k < decoders.length; k++) {
2006
2104
  if (!decoders[k].decodeAttributesFinish()) {
@@ -2570,8 +2668,10 @@ var RAnsSymbolDecoder = class {
2570
2668
  get numSymbols() {
2571
2669
  return this.numSymbols_;
2572
2670
  }
2573
- // Initialize the decoder and decode the probability table.
2574
- 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) {
2575
2675
  if (buffer.bitstreamVersion === 0) {
2576
2676
  return false;
2577
2677
  }
@@ -2612,7 +2712,7 @@ var RAnsSymbolDecoder = class {
2612
2712
  }
2613
2713
  }
2614
2714
  buffer.advance(pos - startPos);
2615
- if (!this.ans_.ransBuildLookUpTable(this.probabilityTable_, this.numSymbols_)) {
2715
+ if (!this.ans_.ransBuildLookUpTable(this.probabilityTable_, this.numSymbols_, expectedCount)) {
2616
2716
  return false;
2617
2717
  }
2618
2718
  return true;
@@ -2654,7 +2754,7 @@ function decodeSymbols(numValues, numComponents, srcBuffer, outValues) {
2654
2754
  }
2655
2755
  function decodeTaggedSymbols(numValues, numComponents, srcBuffer, outValues) {
2656
2756
  const tagDecoder = new RAnsSymbolDecoder(5);
2657
- if (!tagDecoder.create(srcBuffer)) {
2757
+ if (!tagDecoder.create(srcBuffer, Math.ceil(numValues / numComponents))) {
2658
2758
  return false;
2659
2759
  }
2660
2760
  if (!tagDecoder.startDecoding(srcBuffer)) {
@@ -2719,7 +2819,7 @@ function parseRawSymbolStream(numValues, srcBuffer) {
2719
2819
  return null;
2720
2820
  }
2721
2821
  const decoder = new RAnsSymbolDecoder(maxBitLength);
2722
- if (!decoder.create(srcBuffer)) {
2822
+ if (!decoder.create(srcBuffer, numValues)) {
2723
2823
  return null;
2724
2824
  }
2725
2825
  if (numValues > 0 && decoder.numSymbols === 0) {
@@ -2732,7 +2832,7 @@ function parseRawSymbolStream(numValues, srcBuffer) {
2732
2832
  }
2733
2833
  function decodeRawSymbolsInternal(uniqueSymbolsBitLength, numValues, srcBuffer, outValues) {
2734
2834
  const decoder = new RAnsSymbolDecoder(uniqueSymbolsBitLength);
2735
- if (!decoder.create(srcBuffer)) {
2835
+ if (!decoder.create(srcBuffer, numValues)) {
2736
2836
  return false;
2737
2837
  }
2738
2838
  if (numValues > 0 && decoder.numSymbols === 0) {
@@ -4234,11 +4334,16 @@ var PredictionSchemeDeltaDecoder = class extends PredictionSchemeDecoder {
4234
4334
  return true;
4235
4335
  }
4236
4336
  computeOriginalValues(inCorr, outData, size, numComponents, entryToPointIdMap) {
4237
- 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
+ }
4238
4343
  const zeroVals = new Int32Array(numComponents);
4239
- this._transform.computeOriginalValue(zeroVals, 0, inCorr, 0, outData, 0);
4344
+ transform.computeOriginalValue(zeroVals, 0, inCorr, 0, outData, 0);
4240
4345
  for (let i = numComponents; i < size; i += numComponents) {
4241
- this._transform.computeOriginalValue(outData, i - numComponents, inCorr, i, outData, i);
4346
+ transform.computeOriginalValue(outData, i - numComponents, inCorr, i, outData, i);
4242
4347
  }
4243
4348
  return true;
4244
4349
  }
@@ -4779,7 +4884,6 @@ var AttributeTransform = class {
4779
4884
  // src/decoder/attributes/AttributeOctahedronTransform.ts
4780
4885
  var AttributeOctahedronTransform = class extends AttributeTransform {
4781
4886
  _quantizationBits;
4782
- _tmpVec;
4783
4887
  constructor() {
4784
4888
  super();
4785
4889
  this._quantizationBits = -1;
@@ -4811,15 +4915,30 @@ var AttributeOctahedronTransform = class extends AttributeTransform {
4811
4915
  const srcI32 = new Int32Array(srcAddr.buffer, srcAddr.byteOffset, numPoints * 2);
4812
4916
  const dstAddr = targetAttribute.getAddress(0);
4813
4917
  const dstF32 = new Float32Array(dstAddr.buffer, dstAddr.byteOffset, numPoints * 3);
4814
- const outVec = this._tmpVec || (this._tmpVec = new Float32Array(3));
4918
+ const fround = Math.fround;
4919
+ const scale = toolBox._dequantizationScale;
4815
4920
  let si = 0;
4816
4921
  let di = 0;
4817
4922
  for (let i = 0; i < numPoints; i++) {
4818
- 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);
4819
4925
  si += 2;
4820
- dstF32[di] = outVec[0];
4821
- dstF32[di + 1] = outVec[1];
4822
- 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
+ }
4823
4942
  di += 3;
4824
4943
  }
4825
4944
  return true;
@@ -4989,6 +5108,140 @@ var PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform = class exten
4989
5108
  outOrigVals[outOffset] = origS + center;
4990
5109
  outOrigVals[outOffset + 1] = origT + center;
4991
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
+ }
4992
5245
  };
4993
5246
 
4994
5247
  // src/decoder/compression/attributes/prediction_schemes/PredictionSchemeNormalOctahedronDecodingTransform.ts
@@ -7223,7 +7476,7 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
7223
7476
  return false;
7224
7477
  }
7225
7478
  const decoder = new RAnsSymbolDecoder(maxBitLength);
7226
- if (!decoder.create(outBuffer)) {
7479
+ if (!decoder.create(outBuffer, numSymbols)) {
7227
7480
  return false;
7228
7481
  }
7229
7482
  if (decoder.numSymbols === 0) {
@@ -7239,37 +7492,38 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
7239
7492
  this._contextCounters[i] = 0;
7240
7493
  }
7241
7494
  }
7242
- const allU8 = pending.every((entry) => entry.decoder.ans_.lutTable instanceof Uint8Array);
7495
+ const lockstep = pending.filter((entry) => entry.decoder.ans_.lutTable instanceof Uint8Array);
7243
7496
  let p = 0;
7244
- if (allU8) {
7245
- while (pending.length - p >= 3) {
7246
- const a = pending[p];
7247
- const b = pending[p + 1];
7248
- const c = pending[p + 2];
7249
- ransDecodeSymbolsTrioU8(
7250
- a.decoder.ans_,
7251
- a.out,
7252
- a.count,
7253
- b.decoder.ans_,
7254
- b.out,
7255
- b.count,
7256
- c.decoder.ans_,
7257
- c.out,
7258
- c.count
7259
- );
7260
- p += 3;
7261
- }
7262
- if (pending.length - p === 2) {
7263
- const a = pending[p];
7264
- const b = pending[p + 1];
7265
- ransDecodeSymbolsPairU8(a.decoder.ans_, a.out, a.count, b.decoder.ans_, b.out, b.count);
7266
- p += 2;
7267
- }
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;
7268
7513
  }
7269
- for (; p < pending.length; ++p) {
7270
- pending[p].decoder.ans_.decodeSymbols(pending[p].out, pending[p].count);
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);
7271
7522
  }
7272
7523
  for (const entry of pending) {
7524
+ if (!(entry.decoder.ans_.lutTable instanceof Uint8Array)) {
7525
+ entry.decoder.ans_.decodeSymbols(entry.out, entry.count);
7526
+ }
7273
7527
  entry.decoder.endDecoding();
7274
7528
  }
7275
7529
  return true;