ldr 0.0.0-alpha.1 → 0.3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Evan Bacon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,123 @@
1
1
  # ldr
2
2
 
3
- Lego Utilities
3
+ High-performance LEGO® (LDraw) rendering for [three.js](https://threejs.org). Renders 100k-brick models at interactive frame rates by precompiling parts into a compact binary **parts pack** and drawing them with instancing, hidden-stud/tube culling, low-detail studs, single-sided faces and indexed edge lines.
4
+
5
+ - Spec-compliant LDraw reader: `.ldr`, `.mpd` (submodels), `.dat`; BFC, `STEP`, `ROTSTEP`, `BUFEXCHG`, direct colours, `~Moved to` aliases.
6
+ - `createLdrObject()` gives you a `THREE.Group` to drop into any scene, plus a build-steps API.
7
+ - `ldr pack` CLI builds parts packs from your LDraw library (zstd/gzip compressed for serving).
8
+ - ESM, TypeScript types, tree-shakeable (`sideEffects: false`); `three` is a peer dependency.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ bun add ldr three # or: npm i ldr three
14
+ ```
15
+
16
+ ## Quickstart
17
+
18
+ 1. **Build a parts pack** for your model(s) (one-time, at build time):
19
+
20
+ ```bash
21
+ bunx ldr pack --ldraw ./ldraw -o public/car.pack.zst models/car.ldr
22
+ ```
23
+
24
+ 2. **Render it:**
25
+
26
+ ```ts
27
+ import * as THREE from "three";
28
+ import { parseModel, loadPack, createLdrObject } from "ldr";
29
+
30
+ const [text, pack] = await Promise.all([
31
+ fetch("/car.ldr").then((r) => r.text()),
32
+ loadPack("/car.pack.zst"), // .zst / .gz / .br / raw .bin
33
+ ]);
34
+ const model = createLdrObject(parseModel(text, "car.ldr"), pack, {
35
+ edges: true, cullStuds: true, cullUndersides: true, side: "front", material: "plastic",
36
+ });
37
+ scene.add(model.object); // +Y up, 1 unit = 1 LDU (scale option available)
38
+
39
+ // build steps
40
+ model.setStep(0); // show step 0 only … model.stepCount steps total
41
+ model.setStep(null); // show everything
42
+ ```
43
+
44
+ Add lights (or `scene.environment`) as for any `MeshStandardMaterial`/`MeshPhysicalMaterial`.
45
+
46
+ ## Getting parts
47
+
48
+ The LDraw parts library is **not bundled** in this package. Download it from <https://library.ldraw.org> (`complete.zip`, ~90 MB unzipped to an `ldraw/` folder containing `LDConfig.ldr`, `parts/`, `p/`).
49
+ The LDraw library is licensed **CC BY 2.0** — if you ship packs built from it, credit "LDraw.org Parts Library" (CC BY 2.0) in your app.
50
+
51
+ Packs contain only the parts you ask for, so they're small: a 10k-brick Datsville section with 80 unique parts and the 100k section with 290 unique parts are 0.27 MB and 1.1 MB with zstd (1.3 / 3.3 MB gzip).
52
+
53
+ ```bash
54
+ ldr pack --ldraw <dir> -o <out.bin|.zst|.gz> [model.ldr ...] [--part 3001.dat ...] [--all-parts]
55
+ [--studs low|full] [--prims full|low] [--no-weld] [--no-colors] [--cond] [--textures]
56
+ ```
57
+
58
+ Or from code (Node/Bun):
59
+
60
+ ```ts
61
+ import { buildPack } from "ldr/node";
62
+ const { data, parts, missing } = buildPack({ ldrawRoot: "./ldraw", models: ["car.ldr"], studs: "low" });
63
+ await Bun.write("car.pack.bin", data);
64
+ ```
65
+
66
+ The pack embeds the LDConfig colour table by default, so no extra colour file is needed at runtime.
67
+
68
+ ## API
69
+
70
+ ### `ldr` (browser-safe)
71
+
72
+ | export | description |
73
+ |---|---|
74
+ | `parse(text, name?) → Doc` | Parse LDraw text into files/commands (MPD-aware). |
75
+ | `flatten(doc, { resolveModel? }) → PartInstance[]` | Expand submodels into placed parts with `color`, 12-number matrix `m`, `step`, nested `steps`, `path`. |
76
+ | `parseModel(text, name?, opts?)` | `parse` + `flatten`. |
77
+ | `sequenceSteps(instances)` | Global build-step index per instance (nested submodel steps sequenced). |
78
+ | `parseLDConfig(text)` | `!COLOUR` table → `Map<code, {name, hex, edge, alpha}>`. |
79
+ | `loadPack(url) → Promise<Pack>` | Fetch + decompress (by extension) + decode a pack. |
80
+ | `decompressPack(bytes)` / `decodePack(buffer)` | Same, from bytes you already have (zstd/gzip auto-detected). |
81
+ | `createLdrObject(instances, pack, options?) → LdrObject` | High-level three.js object. |
82
+ | `createInstancedEdges(pos, instancedMesh, material?)` | Welded, indexed edge lines sharing an InstancedMesh's matrices (for custom batching). |
83
+ | `conditionalLines(pack, entry, which?)` / `createInstancedConditionalEdges(cond, instancedMesh, material?)` | Type-5 conditional lines from a `--cond` pack (stride 12: p0, p1, c0, c1), drawn by a vertex shader only where the control points fall on the same side of the projected line. |
84
+ | `makeTexturedMaterial(side, texture, wrapU?)` / `packTexture(pack, i)` / `texmapUV` / `texmapTriangleUV` | Printed / sticker parts from a `--textures` pack: a `!TEXMAP` UV material (image alpha blends over the part colour, untextured outside [0,1]) and the decoded embedded PNG. UV conventions follow LDView (v = 0 is the image's top row). |
85
+ | `buildLdrScene`, `computeCulling`, `makeMaterial` | Low-level renderer used by the benchmarks. |
86
+
87
+ `LdrObjectOptions`: `edges` (default `true`), `cullStuds` (`true`), `cullUndersides` (`true`), `side` (`"front"` \| `"double"`, default `"front"`), `material` (`"plastic"` \| `"standard"`), `colors` (Map / record / function override), `fallbackColor`, `ldrawSpace` (keep -Y up), `scale`, `step`, `conditionalLines` (default `true`; only has an effect with packs built with `--cond`), `conditionalStudLines` (default `true`; `false` skips stud silhouettes, ~half the conditional-line GPU cost but most of their visible effect on stud-heavy scenes), `textures` (default `true`; only has an effect with packs built with `--textures`; `object.texturesReady` resolves when images are decoded).
88
+
89
+ `LdrObject`: `object`, `stats` (`instances`, `drawn`, `missing[]`, `studsHidden/Total`, `undersHidden/Total`, `buildMs`), `stepCount`, `setStep(n | null)`, `setEdges(on)`, `setCulling(studs, undersides)`, `setSide(side)`, `getBox()`, `dispose()`.
90
+
91
+ ### `ldr/node` (Node/Bun only)
92
+
93
+ Everything above, plus `buildPack(options)`, `Library` (filesystem LDraw resolver: `where`, `flatten`, `mesh`, `colors`), `Compiler`, `encodePack`, `writePack`.
94
+
95
+ ## Performance
96
+
97
+ Measured with the benchmark harness in [`bench/`](https://github.com/EvanBacon/ldr/tree/main/bench) (Chrome, synced frame time while orbiting). Apple M4 Pro, native GL (Metal via ANGLE):
98
+
99
+ | model | renderer | load | frame | tris/frame | draw calls |
100
+ |---|---|---|---|---|---|
101
+ | Datsville 100k bricks | three.js `LDrawLoader`-geometry instancing | 19.5 s | 105.7 ms (9 fps) | 142.7M | 290 |
102
+ | Datsville 100k bricks | **ldr** (low studs, culling, single-sided) | **2.2 s** | **30.1 ms (33 fps)** | 59.1M | 506 |
103
+ | Datsville 10k bricks | three.js instancing | 13.5 s | 45.9 ms | 60.5M | 80 |
104
+ | Datsville 10k bricks | **ldr** (same) | **1.0 s** | **10.9 ms (92 fps)** | 21.0M | 131 |
105
+ | 100k-brick grid | **ldr** + edges | 0.67 s | 15.7 ms (64 fps) | 4.0M | 24 |
106
+ | 10k-brick grid | `LDrawLoader` | 6.6 s | 61.5 ms | 2.6M | 29,671 |
107
+ | 10k-brick grid | **ldr** + edges | 0.15 s | 2.2 ms | 0.4M | 24 |
108
+
109
+ **Demo app** (`demo/`, rAF fps on the M4 Pro with big-model defaults — shadows/AO/edges off above 50k bricks): Datsville 100k at **43.5 fps** (1.8 s load), full Datsville (392k bricks) at **9.9 fps**.
110
+
111
+ Visual-diff studies show culling and single-sided faces are lossless (≤0.009% pixels); low-detail studs change only stud silhouettes. Stud culling hides ~39–51% of studs on real models.
112
+
113
+ ## Notes & limitations
114
+
115
+ - Transparent colours render opaque. Conditional (type-5) lines are drawn only from packs built with `--cond` (pack v2, opt-in: +8–14% zstd size on Datsville); default packs carry type-2 edges only. Printed parts show their `!TEXMAP` images only from packs built with `--textures` (pack v2, opt-in; the PNGs dominate the size); default packs draw the parts' FALLBACK geometry. Model-level `!TEXMAP` around a part reference (rather than inside the part file) and textured parts embedded in an MPD are not rendered yet.
116
+ - `setStep` rebuilds batches (fast for instruction-sized models; culling is recomputed for the partial build).
117
+ - Coordinates: LDraw units (1 stud = 20 LDU); the object is rotated to +Y up unless `ldrawSpace: true`.
118
+
119
+ LEGO® is a trademark of the LEGO Group, which does not sponsor, authorize or endorse this project.
120
+
121
+ ## License
122
+
123
+ MIT (code). Parts data you build from the LDraw library is CC BY 2.0 (LDraw.org).
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};