ldr 0.33.0 → 0.33.2
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 +1 -1
- package/dist/cli.js +178 -60
- package/dist/cull.d.ts +2 -0
- package/dist/index.js +17 -16
- package/dist/load-worker.gen.d.ts +1 -1
- package/dist/node/compile.d.ts +24 -0
- package/dist/node/index.js +247 -94
- package/dist/node/library.d.ts +2 -0
- package/dist/spec.d.ts +13 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -209,7 +209,7 @@ Reproduce: `cd bench && bun run setup && bun run perf` (regression gate) or `bun
|
|
|
209
209
|
|
|
210
210
|
## LDraw spec compliance
|
|
211
211
|
|
|
212
|
-
- Line types 0–5. BFC: `CERTIFY
|
|
212
|
+
- Line types 0–5. BFC (ldraw.org article 415): `CERTIFY`/`NOCERTIFY` (decided by the first BFC statement before the file's first type 1-5 line, so `0 BFC CCW` alone certifies and a late `CERTIFY` doesn't), `CCW`/`CW`, `CLIP`/`NOCLIP` (polygons drawn while culling is off are visible from both sides, including in subfiles referenced then, which can't turn it back on), `INVERTNEXT` (the immediately following subfile reference only), inverted matrices, and degenerate (determinant 0) matrices: a subfile that lies flat at y = 0 is repaired the way LDView and L3P do (an all-zero row, or else the all-zero Y column, gets a 1, and the repaired determinant decides the winding); any other singular reference, including a part placed squashed flat in a model, is drawn without culling.
|
|
213
213
|
- MPD: `0 FILE` / `0 NOFILE`, and `0 !DATA` (base64 PNGs). An MPD file is a part or a model by its `!LDRAW_ORG` type, otherwise by whether it has its own geometry. Embedded parts override library parts.
|
|
214
214
|
- Geometry drawn directly in model files (type 2–5 lines outside parts), on by default.
|
|
215
215
|
- `0 STEP`, nested submodel step sequencing, `0 ROTSTEP` (`REL`/`ADD`/`ABS`/`END`), and `0 BUFEXCHG`.
|
package/dist/cli.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// ../../../../../../../tmp/ldr-build-
|
|
3
|
+
// ../../../../../../../tmp/ldr-build-RWoXTz/src/cli.ts
|
|
4
4
|
import fs4 from "node:fs";
|
|
5
5
|
import zlib from "node:zlib";
|
|
6
6
|
|
|
7
|
-
// ../../../../../../../tmp/ldr-build-
|
|
7
|
+
// ../../../../../../../tmp/ldr-build-RWoXTz/src/node/build-pack.ts
|
|
8
8
|
import fs3 from "node:fs";
|
|
9
9
|
import path3 from "node:path";
|
|
10
10
|
|
|
11
|
-
// ../../../../../../../tmp/ldr-build-
|
|
11
|
+
// ../../../../../../../tmp/ldr-build-RWoXTz/src/node/resolver.ts
|
|
12
12
|
import fs from "node:fs";
|
|
13
13
|
import path from "node:path";
|
|
14
14
|
|
|
15
|
-
// ../../../../../../../tmp/ldr-build-
|
|
15
|
+
// ../../../../../../../tmp/ldr-build-RWoXTz/src/spec.ts
|
|
16
16
|
var norm = (s) => s.trim().toLowerCase().replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
17
17
|
var COLOUR_ID_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
18
18
|
var namedCodes = new Map;
|
|
@@ -671,6 +671,29 @@ function invert(m) {
|
|
|
671
671
|
const r = [A * k, -(b * i - c * h) * k, (b * f - c * e) * k, B * k, (a * i - c * g) * k, -(a * f - c * d) * k, C * k, -(a * h - b * g) * k, (a * e - b * d) * k];
|
|
672
672
|
return [-(r[0] * x + r[1] * y + r[2] * z), -(r[3] * x + r[4] * y + r[5] * z), -(r[6] * x + r[7] * y + r[8] * z), ...r];
|
|
673
673
|
}
|
|
674
|
+
function singularFix(m, flatY) {
|
|
675
|
+
const det = (q) => q[3] * (q[7] * q[11] - q[8] * q[10]) - q[4] * (q[6] * q[11] - q[8] * q[9]) + q[5] * (q[6] * q[10] - q[7] * q[9]);
|
|
676
|
+
const rows = Math.hypot(m[3], m[4], m[5]) * Math.hypot(m[6], m[7], m[8]) * Math.hypot(m[9], m[10], m[11]);
|
|
677
|
+
if (!(Math.abs(det(m)) <= 0.000000001 * rows))
|
|
678
|
+
return null;
|
|
679
|
+
if (!flatY())
|
|
680
|
+
return { m, cull: false };
|
|
681
|
+
const q = [...m];
|
|
682
|
+
const ok = () => Math.abs(det(q)) > 0.000000001 * Math.hypot(q[3], q[4], q[5]) * Math.hypot(q[6], q[7], q[8]) * Math.hypot(q[9], q[10], q[11]);
|
|
683
|
+
for (let i = 0;i < 3; i++)
|
|
684
|
+
if (q[3 + 3 * i] === 0 && q[4 + 3 * i] === 0 && q[5 + 3 * i] === 0) {
|
|
685
|
+
q[4 + 3 * i] = 1;
|
|
686
|
+
if (ok())
|
|
687
|
+
return { m: q, cull: true };
|
|
688
|
+
}
|
|
689
|
+
if (q[4] === 0 && q[7] === 0 && q[10] === 0)
|
|
690
|
+
for (let i = 0;i < 3; i++) {
|
|
691
|
+
q[4 + 3 * i] = 1;
|
|
692
|
+
if (ok())
|
|
693
|
+
return { m: q, cull: true };
|
|
694
|
+
}
|
|
695
|
+
return { m, cull: false };
|
|
696
|
+
}
|
|
674
697
|
var num = (x) => String(+x.toFixed(6) || 0);
|
|
675
698
|
var quote = (s) => '"' + s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + '"';
|
|
676
699
|
function texWrapper(part, map) {
|
|
@@ -723,7 +746,7 @@ function fileStepViews(f) {
|
|
|
723
746
|
return out;
|
|
724
747
|
}
|
|
725
748
|
|
|
726
|
-
// ../../../../../../../tmp/ldr-build-
|
|
749
|
+
// ../../../../../../../tmp/ldr-build-RWoXTz/src/model.ts
|
|
727
750
|
var CALLOUT_END = 999999;
|
|
728
751
|
var stepKey = (steps) => steps.map((x) => String(x).padStart(6, "0")).join(".") + "~";
|
|
729
752
|
var defLine = new WeakMap;
|
|
@@ -1138,7 +1161,7 @@ function blendedColourHex(code) {
|
|
|
1138
1161
|
return "#" + [0, 1, 2].map((k) => (a[k] + b[k] >> 1).toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
1139
1162
|
}
|
|
1140
1163
|
|
|
1141
|
-
// ../../../../../../../tmp/ldr-build-
|
|
1164
|
+
// ../../../../../../../tmp/ldr-build-RWoXTz/src/node/resolver.ts
|
|
1142
1165
|
var TOP_STUD_RE = /^(stud|studa|stud2|stud2a|stud6|stud6a|stud10|stud15|stud2s|stud2s2|stud-logo\d*|stud2-logo\d*)\.dat$/i;
|
|
1143
1166
|
var UNDER_STUD_RE = /^(stud3|stud3a|stud4.*|stud12|stud16.*|stud18a)\.dat$/i;
|
|
1144
1167
|
var norm2 = (ref) => ref.trim().replace(/\\/g, "/").toLowerCase();
|
|
@@ -1465,11 +1488,11 @@ function collectDeps(lib, refs) {
|
|
|
1465
1488
|
return out;
|
|
1466
1489
|
}
|
|
1467
1490
|
|
|
1468
|
-
// ../../../../../../../tmp/ldr-build-
|
|
1491
|
+
// ../../../../../../../tmp/ldr-build-RWoXTz/src/node/compile.ts
|
|
1469
1492
|
import fs2 from "node:fs";
|
|
1470
1493
|
import path2 from "node:path";
|
|
1471
1494
|
|
|
1472
|
-
// ../../../../../../../tmp/ldr-build-
|
|
1495
|
+
// ../../../../../../../tmp/ldr-build-RWoXTz/src/node/lod.ts
|
|
1473
1496
|
import { MeshoptSimplifier, MeshoptEncoder } from "meshoptimizer";
|
|
1474
1497
|
await Promise.all([MeshoptSimplifier.ready, MeshoptEncoder.ready]);
|
|
1475
1498
|
function reorderMesh(P, N, C, I) {
|
|
@@ -1661,7 +1684,55 @@ function lodChain(P, C, I) {
|
|
|
1661
1684
|
return out;
|
|
1662
1685
|
}
|
|
1663
1686
|
|
|
1664
|
-
// ../../../../../../../tmp/ldr-build-
|
|
1687
|
+
// ../../../../../../../tmp/ldr-build-RWoXTz/src/node/compile.ts
|
|
1688
|
+
class BfcState {
|
|
1689
|
+
accum;
|
|
1690
|
+
ccw = true;
|
|
1691
|
+
certified = null;
|
|
1692
|
+
local = true;
|
|
1693
|
+
inv = false;
|
|
1694
|
+
constructor(accum = true) {
|
|
1695
|
+
this.accum = accum;
|
|
1696
|
+
}
|
|
1697
|
+
meta(t) {
|
|
1698
|
+
const o = t.split(/\s+/).slice(2);
|
|
1699
|
+
this.inv = false;
|
|
1700
|
+
if (this.certified === null)
|
|
1701
|
+
this.certified = !o.includes("NOCERTIFY");
|
|
1702
|
+
if (!this.certified)
|
|
1703
|
+
return;
|
|
1704
|
+
for (const w of o) {
|
|
1705
|
+
if (w === "CW")
|
|
1706
|
+
this.ccw = false;
|
|
1707
|
+
else if (w === "CCW")
|
|
1708
|
+
this.ccw = true;
|
|
1709
|
+
else if (w === "CLIP")
|
|
1710
|
+
this.local = true;
|
|
1711
|
+
else if (w === "NOCLIP")
|
|
1712
|
+
this.local = false;
|
|
1713
|
+
else if (w === "INVERTNEXT")
|
|
1714
|
+
this.inv = true;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
line(type) {
|
|
1718
|
+
if (type !== "0" && this.certified === null)
|
|
1719
|
+
this.certified = false;
|
|
1720
|
+
if (type !== "1")
|
|
1721
|
+
this.inv = false;
|
|
1722
|
+
}
|
|
1723
|
+
takeInvert() {
|
|
1724
|
+
const v = this.inv;
|
|
1725
|
+
this.inv = false;
|
|
1726
|
+
return v;
|
|
1727
|
+
}
|
|
1728
|
+
get cull() {
|
|
1729
|
+
return this.accum && this.local;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
var windOrder = (n, flip, cull) => {
|
|
1733
|
+
const f = n === 3 ? [0, 1, 2] : [0, 1, 2, 0, 2, 3], b = n === 3 ? [0, 2, 1] : [0, 2, 1, 0, 3, 2];
|
|
1734
|
+
return cull ? flip ? b : f : [...f, ...b];
|
|
1735
|
+
};
|
|
1665
1736
|
function loadColors(root) {
|
|
1666
1737
|
const map = new Map;
|
|
1667
1738
|
for (const l of fs2.readFileSync(path2.join(root, "LDConfig.ldr"), "latin1").split(/\r?\n/)) {
|
|
@@ -1705,6 +1776,40 @@ class Compiler {
|
|
|
1705
1776
|
this.opts = opts;
|
|
1706
1777
|
this.colors = loadColors(lib.root);
|
|
1707
1778
|
}
|
|
1779
|
+
flat = new Map;
|
|
1780
|
+
flatY(key) {
|
|
1781
|
+
const hit = this.flat.get(key);
|
|
1782
|
+
if (hit !== undefined)
|
|
1783
|
+
return hit;
|
|
1784
|
+
const walk = (fk, M, depth) => {
|
|
1785
|
+
if (depth > 40)
|
|
1786
|
+
return true;
|
|
1787
|
+
for (const raw of this.lib.read(fk).split(/\r?\n/)) {
|
|
1788
|
+
const t = raw.trim();
|
|
1789
|
+
const c = t[0];
|
|
1790
|
+
if (c === "1") {
|
|
1791
|
+
const r = parseType1(t);
|
|
1792
|
+
if (!r)
|
|
1793
|
+
continue;
|
|
1794
|
+
const base = r.name.trim().replace(/\\/g, "/").split("/").pop().toLowerCase();
|
|
1795
|
+
const k = this.resolve(r.name, TOP_STUD_RE.test(base));
|
|
1796
|
+
if (k && !walk(k, compose(M, r.m), depth + 1))
|
|
1797
|
+
return false;
|
|
1798
|
+
continue;
|
|
1799
|
+
}
|
|
1800
|
+
if (c !== "2" && c !== "3" && c !== "4" && c !== "5")
|
|
1801
|
+
continue;
|
|
1802
|
+
const v = t.split(/\s+/).slice(2).map(Number), n = c === "2" ? 2 : c === "3" ? 3 : 4;
|
|
1803
|
+
for (let i = 0;i < n * 3; i += 3)
|
|
1804
|
+
if (Math.abs(M[1] + M[6] * v[i] + M[7] * v[i + 1] + M[8] * v[i + 2]) > 0.000001)
|
|
1805
|
+
return false;
|
|
1806
|
+
}
|
|
1807
|
+
return true;
|
|
1808
|
+
};
|
|
1809
|
+
const r = walk(key, IDENT, 0);
|
|
1810
|
+
this.flat.set(key, r);
|
|
1811
|
+
return r;
|
|
1812
|
+
}
|
|
1708
1813
|
resolve(ref, isStud) {
|
|
1709
1814
|
const key = this.lib.resolve(ref);
|
|
1710
1815
|
if (!key)
|
|
@@ -1731,54 +1836,49 @@ class Compiler {
|
|
|
1731
1836
|
return this.cache.get(ck);
|
|
1732
1837
|
const out = { body: { pos: [], col: [] }, under: { pos: [], col: [] }, edges: [], condLines: 0, cond: this.opts.cond ? [] : undefined, condUnder: this.opts.cond ? [] : undefined, studs: [], bbox: [Infinity, Infinity, Infinity, -Infinity, -Infinity, -Infinity], fullBottom: false };
|
|
1733
1838
|
const colorOf = (code) => code === "16" ? [1, 1, 1, 0] : [...this.colors.get(code) || (code.startsWith("0x2") ? [parseInt(code.slice(3, 5), 16) / 255, parseInt(code.slice(5, 7), 16) / 255, parseInt(code.slice(7, 9), 16) / 255] : blendedRgb(code) ?? [1, 0, 1]), 1];
|
|
1734
|
-
const walk = (fk, M, color, invert, target, depth, inh = null, scope0 = null) => {
|
|
1839
|
+
const walk = (fk, M, color, invert, target, depth, inh = null, scope0 = null, cull = true) => {
|
|
1735
1840
|
if (depth > 40)
|
|
1736
1841
|
return;
|
|
1737
|
-
|
|
1842
|
+
const bfc = new BfcState(cull);
|
|
1843
|
+
let scope = scope0;
|
|
1738
1844
|
for (const raw of this.lib.read(fk).split(/\r?\n/)) {
|
|
1739
1845
|
const t = raw.trim();
|
|
1740
1846
|
if (!t)
|
|
1741
1847
|
continue;
|
|
1742
1848
|
const c = t[0];
|
|
1849
|
+
if (c === "0" && /^0\s+BFC(\s|$)/.test(t)) {
|
|
1850
|
+
bfc.meta(t);
|
|
1851
|
+
continue;
|
|
1852
|
+
}
|
|
1853
|
+
bfc.line(c);
|
|
1743
1854
|
if (c === "0") {
|
|
1744
1855
|
if (t.includes("!COLOUR") && COLOUR_RE.test(t)) {
|
|
1745
1856
|
scope = defineColour(t, scope);
|
|
1746
1857
|
continue;
|
|
1747
1858
|
}
|
|
1748
|
-
if (/^0\s+BFC/.test(t)) {
|
|
1749
|
-
if (/CERTIFY/.test(t) && !/NOCERTIFY/.test(t))
|
|
1750
|
-
certified = true;
|
|
1751
|
-
if (/\bCW\b/.test(t))
|
|
1752
|
-
ccw = false;
|
|
1753
|
-
if (/\bCCW\b/.test(t))
|
|
1754
|
-
ccw = true;
|
|
1755
|
-
if (/INVERTNEXT/.test(t))
|
|
1756
|
-
invertNext = true;
|
|
1757
|
-
}
|
|
1758
1859
|
continue;
|
|
1759
1860
|
}
|
|
1760
1861
|
if (c === "1") {
|
|
1862
|
+
const invertNext = bfc.takeInvert();
|
|
1761
1863
|
const r = parseType1(t);
|
|
1762
1864
|
if (!r)
|
|
1763
1865
|
continue;
|
|
1764
1866
|
const base = r.name.trim().replace(/\\/g, "/").split("/").pop().toLowerCase();
|
|
1765
1867
|
const isTop = TOP_STUD_RE.test(base), isUnder = UNDER_STUD_RE.test(base);
|
|
1766
1868
|
const ck2 = this.resolve(r.name, isTop);
|
|
1767
|
-
if (!ck2)
|
|
1768
|
-
invertNext = false;
|
|
1869
|
+
if (!ck2)
|
|
1769
1870
|
continue;
|
|
1770
|
-
}
|
|
1771
1871
|
const childColor = r.color === "16" ? color : r.color, childInh = r.color === "16" ? inh : this.local(r.color, scope);
|
|
1772
|
-
const
|
|
1773
|
-
const
|
|
1774
|
-
|
|
1775
|
-
if (isTop && separateStuds) {
|
|
1872
|
+
const fx = singularFix(r.m, () => this.flatY(ck2)), rm = fx ? fx.m : r.m, subCull = bfc.cull && (!fx || fx.cull);
|
|
1873
|
+
const Mc = compose(M, rm);
|
|
1874
|
+
const inv = invert !== invertNext !== det3(rm) < 0;
|
|
1875
|
+
if (isTop && separateStuds && (!fx || fx.cull)) {
|
|
1776
1876
|
out.studs.push({ kind: ck2, color: childInh ? this.studToken(childColor, childInh) : childColor, m: Mc });
|
|
1777
1877
|
fullKind.set(out.studs.length - 1, this.lib.resolve(r.name));
|
|
1778
1878
|
growBox(out.bbox, Mc, this.compile(ck2, false).bbox);
|
|
1779
1879
|
continue;
|
|
1780
1880
|
}
|
|
1781
|
-
walk(ck2, Mc, childColor, inv, isUnder ? "under" : target, depth + 1, childInh, scope);
|
|
1881
|
+
walk(ck2, Mc, childColor, inv, isUnder ? "under" : target, depth + 1, childInh, scope, subCull);
|
|
1782
1882
|
continue;
|
|
1783
1883
|
}
|
|
1784
1884
|
const tok = t.split(/\s+/);
|
|
@@ -1806,22 +1906,40 @@ class Compiler {
|
|
|
1806
1906
|
pts.push(xf(M, v.slice(i * 3, i * 3 + 3)));
|
|
1807
1907
|
for (const p of pts)
|
|
1808
1908
|
grow(out.bbox, p);
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
order = n === 3 ? [0, 2, 1] : [0, 2, 1, 0, 3, 2];
|
|
1812
|
-
const rgba = this.rgbaAt(tok[1], color, inh, scope, colorOf);
|
|
1813
|
-
const b = out[target];
|
|
1814
|
-
for (const i of order) {
|
|
1909
|
+
const flip = !bfc.ccw !== invert, rgba = this.rgbaAt(tok[1], color, inh, scope, colorOf), b = out[target];
|
|
1910
|
+
for (const i of windOrder(n, flip, true)) {
|
|
1815
1911
|
b.pos.push(...pts[i]);
|
|
1816
1912
|
b.col.push(...rgba);
|
|
1817
1913
|
}
|
|
1914
|
+
if (bfc.cull)
|
|
1915
|
+
culled++;
|
|
1916
|
+
else {
|
|
1917
|
+
const bb = back[target];
|
|
1918
|
+
for (const i of windOrder(n, !flip, true)) {
|
|
1919
|
+
bb.pos.push(...pts[i]);
|
|
1920
|
+
bb.col.push(...rgba);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1818
1923
|
}
|
|
1819
1924
|
}
|
|
1820
|
-
if (!certified)
|
|
1925
|
+
if (!bfc.certified)
|
|
1821
1926
|
out.uncertified = true;
|
|
1822
1927
|
};
|
|
1823
1928
|
const fullKind = new Map;
|
|
1929
|
+
let culled = 0;
|
|
1930
|
+
const back = { body: { pos: [], col: [] }, under: { pos: [], col: [] } };
|
|
1824
1931
|
walk(key, IDENT, "16", false, "body", 0);
|
|
1932
|
+
if (back.body.pos.length || back.under.pos.length) {
|
|
1933
|
+
if (!culled)
|
|
1934
|
+
out.uncertified = true;
|
|
1935
|
+
if (!out.uncertified)
|
|
1936
|
+
for (const t of ["body", "under"]) {
|
|
1937
|
+
for (const x of back[t].pos)
|
|
1938
|
+
out[t].pos.push(x);
|
|
1939
|
+
for (const x of back[t].col)
|
|
1940
|
+
out[t].col.push(x);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1825
1943
|
seatStuds(out, fullKind, (k) => this.compile(k, false).bbox);
|
|
1826
1944
|
if (this.opts.tex && this.hasTex(key))
|
|
1827
1945
|
this.texPass(key, out, separateStuds, colorOf);
|
|
@@ -1906,10 +2024,11 @@ class Compiler {
|
|
|
1906
2024
|
}
|
|
1907
2025
|
texPass(key, out, separateStuds, colorOf) {
|
|
1908
2026
|
const tbody = { pos: [], col: [] }, texb = new Map;
|
|
1909
|
-
const walk = (fk, M, color, invert, target, depth, texIn, inh = null, scope0 = null) => {
|
|
2027
|
+
const walk = (fk, M, color, invert, target, depth, texIn, inh = null, scope0 = null, cull = true) => {
|
|
1910
2028
|
if (depth > 40)
|
|
1911
2029
|
return;
|
|
1912
|
-
|
|
2030
|
+
const bfc = new BfcState(cull);
|
|
2031
|
+
let scope = scope0;
|
|
1913
2032
|
let stack = [];
|
|
1914
2033
|
let next = null;
|
|
1915
2034
|
for (const raw of this.lib.read(fk).split(/\r?\n/)) {
|
|
@@ -1920,7 +2039,13 @@ class Compiler {
|
|
|
1920
2039
|
if (t[0] === "0") {
|
|
1921
2040
|
const m = /^0\s+(\S+)\s*(.*)$/.exec(t);
|
|
1922
2041
|
const kw = (m?.[1] ?? "").toUpperCase(), rest = m?.[2] ?? "";
|
|
2042
|
+
if (m?.[1] === "BFC") {
|
|
2043
|
+
bfc.meta(t);
|
|
2044
|
+
next = null;
|
|
2045
|
+
continue;
|
|
2046
|
+
}
|
|
1923
2047
|
if (kw === "!TEXMAP") {
|
|
2048
|
+
bfc.line("0");
|
|
1924
2049
|
const op = (rest.split(/\s+/)[0] || "").toUpperCase();
|
|
1925
2050
|
next = null;
|
|
1926
2051
|
if (op === "START" || op === "NEXT") {
|
|
@@ -1944,25 +2069,23 @@ class Compiler {
|
|
|
1944
2069
|
next = null;
|
|
1945
2070
|
t = rest.trim();
|
|
1946
2071
|
colon = true;
|
|
1947
|
-
if (!t || t[0] === "0")
|
|
2072
|
+
if (!t || t[0] === "0") {
|
|
2073
|
+
bfc.line("0");
|
|
1948
2074
|
continue;
|
|
2075
|
+
}
|
|
1949
2076
|
} else {
|
|
2077
|
+
bfc.line("0");
|
|
1950
2078
|
if (kw === "!COLOUR")
|
|
1951
2079
|
scope = defineColour(t, scope);
|
|
1952
|
-
if (kw === "BFC") {
|
|
1953
|
-
if (/\bCW\b/.test(t))
|
|
1954
|
-
ccw = false;
|
|
1955
|
-
if (/\bCCW\b/.test(t))
|
|
1956
|
-
ccw = true;
|
|
1957
|
-
if (/INVERTNEXT/.test(t))
|
|
1958
|
-
invertNext = true;
|
|
1959
|
-
}
|
|
1960
2080
|
if (kw === "STEP")
|
|
1961
2081
|
stack = [];
|
|
1962
2082
|
next = null;
|
|
1963
2083
|
continue;
|
|
1964
2084
|
}
|
|
1965
2085
|
}
|
|
2086
|
+
const c = t[0];
|
|
2087
|
+
bfc.line(c);
|
|
2088
|
+
const invertNext = c === "1" && bfc.takeInvert();
|
|
1966
2089
|
let tex = texIn;
|
|
1967
2090
|
if (next) {
|
|
1968
2091
|
tex = next;
|
|
@@ -1977,7 +2100,6 @@ class Compiler {
|
|
|
1977
2100
|
tex = top.map;
|
|
1978
2101
|
}
|
|
1979
2102
|
}
|
|
1980
|
-
const c = t[0];
|
|
1981
2103
|
if (c === "1") {
|
|
1982
2104
|
const r = parseType1(t);
|
|
1983
2105
|
if (!r)
|
|
@@ -1985,15 +2107,13 @@ class Compiler {
|
|
|
1985
2107
|
const base = r.name.trim().replace(/\\/g, "/").split("/").pop().toLowerCase();
|
|
1986
2108
|
const isTop = TOP_STUD_RE.test(base), isUnder = UNDER_STUD_RE.test(base);
|
|
1987
2109
|
const ck2 = this.resolve(r.name, isTop);
|
|
1988
|
-
if (!ck2)
|
|
1989
|
-
invertNext = false;
|
|
2110
|
+
if (!ck2)
|
|
1990
2111
|
continue;
|
|
1991
|
-
|
|
1992
|
-
const inv = invert !== invertNext !== det3(
|
|
1993
|
-
|
|
1994
|
-
if (isTop && separateStuds)
|
|
2112
|
+
const fx = singularFix(r.m, () => this.flatY(ck2)), rm = fx ? fx.m : r.m, subCull = bfc.cull && (!fx || fx.cull);
|
|
2113
|
+
const inv = invert !== invertNext !== det3(rm) < 0;
|
|
2114
|
+
if (isTop && separateStuds && (!fx || fx.cull))
|
|
1995
2115
|
continue;
|
|
1996
|
-
walk(ck2, compose(M,
|
|
2116
|
+
walk(ck2, compose(M, rm), r.color === "16" ? color : r.color, inv, isUnder ? "under" : target, depth + 1, tex, r.color === "16" ? inh : this.local(r.color, scope), scope, subCull);
|
|
1997
2117
|
continue;
|
|
1998
2118
|
}
|
|
1999
2119
|
if (c !== "3" && c !== "4")
|
|
@@ -2004,9 +2124,7 @@ class Compiler {
|
|
|
2004
2124
|
const pts = [];
|
|
2005
2125
|
for (let i = 0;i < n; i++)
|
|
2006
2126
|
pts.push(xf(M, v.slice(i * 3, i * 3 + 3)));
|
|
2007
|
-
|
|
2008
|
-
if (!ccw !== invert)
|
|
2009
|
-
order = n === 3 ? [0, 2, 1] : [0, 2, 1, 0, 3, 2];
|
|
2127
|
+
const order = windOrder(n, !bfc.ccw !== invert, bfc.cull || !!out.uncertified);
|
|
2010
2128
|
const rgba = this.rgbaAt(tok[1], color, inh, scope, colorOf);
|
|
2011
2129
|
if (tex) {
|
|
2012
2130
|
const tk = tex.glossmap ? tex.texture + "\x00" + tex.glossmap : tex.texture;
|
|
@@ -2376,7 +2494,7 @@ function encodePack(parts, studKinds, weld, extra, options = {}) {
|
|
|
2376
2494
|
return Buffer.concat([head, ...chunks.map((c) => new Uint8Array(c.buffer, c.byteOffset, c.byteLength))]);
|
|
2377
2495
|
}
|
|
2378
2496
|
|
|
2379
|
-
// ../../../../../../../tmp/ldr-build-
|
|
2497
|
+
// ../../../../../../../tmp/ldr-build-RWoXTz/src/node/build-pack.ts
|
|
2380
2498
|
function packCompilerOpts(o) {
|
|
2381
2499
|
return { studs: o.studs ?? "low", prims: o.primitives ?? "full", weld: o.weld ?? true, separateStuds: true, cond: !!o.cond, tex: !!o.textures };
|
|
2382
2500
|
}
|
|
@@ -2449,7 +2567,7 @@ function buildPack(o) {
|
|
|
2449
2567
|
return { data: encodePack(parts, kinds, comp.opts.weld, extra, { texture, lod: o.lod !== false, reorder: !!o.reorder }), parts: parts.size, studKinds: kinds.size, missing: [...missing], ...warnings.length ? { warnings } : {} };
|
|
2450
2568
|
}
|
|
2451
2569
|
|
|
2452
|
-
// ../../../../../../../tmp/ldr-build-
|
|
2570
|
+
// ../../../../../../../tmp/ldr-build-RWoXTz/src/cli.ts
|
|
2453
2571
|
var HELP = `ldr pack — build a parts pack for the ldr three.js renderer
|
|
2454
2572
|
|
|
2455
2573
|
Usage:
|
package/dist/cull.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export declare function prepPart(H: any, part: string): PrepPart | null;
|
|
|
19
19
|
/** w = a * b for LDraw 12-number matrices (x y z a b c d e f g h i); same arithmetic as the instance flattener. */
|
|
20
20
|
export declare function mul12To(a: ArrayLike<number>, b: ArrayLike<number>, bo: number, w: Float64Array, ao?: number): void;
|
|
21
21
|
export declare const det12: (m: ArrayLike<number>) => number;
|
|
22
|
+
/** Singular placement (determinant ~0, relative to the row lengths): BFC can't cull it (article 415 "Degenerate Matrices"). */
|
|
23
|
+
export declare const singular12: (m: ArrayLike<number>, d?: number) => boolean;
|
|
22
24
|
/** Open-addressing hash table from int32 triples to int32 values (-1 = absent); grows as needed. */
|
|
23
25
|
export declare class Int3Table {
|
|
24
26
|
a: Int32Array;
|