ldr 0.0.0-alpha.1 → 0.2.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.
@@ -0,0 +1,35 @@
1
+ import { type Doc, type PartMesh, type Instance } from "../spec.js";
2
+ export declare class Library {
3
+ root: string;
4
+ unofficialRoot?: string | undefined;
5
+ cache: Map<string, Doc | null>;
6
+ meshCache: Map<string, PartMesh>;
7
+ /** Optional second root for unofficial parts (e.g. Unofficial/ next to the official tree). */
8
+ constructor(root: string, unofficialRoot?: string | undefined);
9
+ /**
10
+ * Resolve a part/prim/model by LDraw search order (case-insensitive):
11
+ * parts/, p/, models/, then parts/s (subparts). If `unofficial/` exists under root
12
+ * (or `unofficialRoot` is set), those trees are tried after the official ones.
13
+ * Follows `~Moved to` aliases to the canonical file.
14
+ */
15
+ where(name: string, _seen?: Set<string>): {
16
+ kind: "part" | "prim" | "model";
17
+ doc: Doc;
18
+ aliasOf?: string;
19
+ } | null;
20
+ private lookup;
21
+ /** Case-insensitive path resolve (library files are lowercase; callers may pass any case). */
22
+ private resolveFile;
23
+ colors(): Map<number, {
24
+ name: string;
25
+ hex: string;
26
+ edge: string;
27
+ alpha: number;
28
+ }>;
29
+ /** Flattened triangle mesh: positions + color code per tri (16 = inherit); edges as line segments.
30
+ * `cond` stores type-5 lines as 12 floats each: endpoint0, endpoint1, control0, control1 (for future conditional-line rendering). */
31
+ mesh(name: string): PartMesh;
32
+ private emit;
33
+ /** Flatten a model into part instances. 24 handled at render (edge color). Alias parts resolve to canonical names. */
34
+ flatten(doc: Doc): Instance[];
35
+ }
@@ -0,0 +1,32 @@
1
+ export declare const TOP_STUD_RE: RegExp;
2
+ export declare const UNDER_STUD_RE: RegExp;
3
+ export declare const norm: (ref: any) => any;
4
+ export declare const threeName: (ref: any) => any;
5
+ export declare function parseType1(line: any): {
6
+ color: any;
7
+ m: any;
8
+ name: any;
9
+ } | null;
10
+ export declare function xf(m: any, p: any): any[];
11
+ export declare function compose(a: any, b: any): any[];
12
+ export declare const IDENT: number[];
13
+ export declare class Library {
14
+ #private;
15
+ root: string;
16
+ index: Map<string, string>;
17
+ text: Map<string, string>;
18
+ stats: Map<string, any>;
19
+ constructor(root: string);
20
+ resolve(ref: any): string | null;
21
+ read(key: string): string;
22
+ isPart(key: any): any;
23
+ fileStats(key: any): any;
24
+ }
25
+ export declare function splitModel(text: any, fallbackName?: string): {
26
+ files: Map<string, string>;
27
+ main: string | null;
28
+ };
29
+ export declare function flatten(model: any, lib: any, onInstance: any): {
30
+ missing: Map<any, any>;
31
+ };
32
+ export declare function collectDeps(lib: any, refs: any): Map<any, any>;
@@ -0,0 +1,2 @@
1
+ /** Detect gzip / zstd magic and return raw pack bytes (an ArrayBuffer you can hand to `decodePack`). */
2
+ export declare function decompressPack(data: ArrayBuffer | Uint8Array): Promise<ArrayBuffer>;
@@ -0,0 +1,43 @@
1
+ import * as THREE from 'three';
2
+ export type LdrParams = {
3
+ pack: string;
4
+ cullStuds: boolean;
5
+ cullUnder: boolean;
6
+ edges: boolean | 'idx' | 'ss';
7
+ side: 'double' | 'front';
8
+ uncertDouble?: boolean;
9
+ };
10
+ export type Inst = {
11
+ part: string;
12
+ color: string;
13
+ m: number[];
14
+ };
15
+ export type Pack = {
16
+ header: any;
17
+ arr: (r: any) => any;
18
+ bytes: number;
19
+ };
20
+ export declare function loadPack(url: string): Promise<Pack>;
21
+ /** Decode an uncompressed parts pack (4-byte LE header length, JSON header, then aligned typed-array payload). */
22
+ export declare function decodePack(buf: ArrayBuffer): Pack;
23
+ export declare function makeMaterial(side: THREE.Side): THREE.MeshStandardMaterial;
24
+ /** Edge lines for an InstancedMesh: welds + indexes `pos` (line-segment endpoints, part space) and shares the mesh's instanceMatrix buffer, so edges cost one extra draw and no extra per-instance memory. Keep `geometry.instanceCount` in sync with `im.count`. */
25
+ export declare function createInstancedEdges(pos: ArrayLike<number>, im: THREE.InstancedMesh, material?: THREE.Material): THREE.LineSegments;
26
+ export declare function computeCulling(insts: Inst[], H: any): {
27
+ studs: {
28
+ kind: string;
29
+ color: string;
30
+ w: number[];
31
+ hidden: boolean;
32
+ }[];
33
+ underHidden: boolean[];
34
+ };
35
+ export declare function buildLdrScene(insts: Inst[], pack: Pack, P: LdrParams, color: (c: string) => THREE.Color, mark: (k: string) => void): {
36
+ root: THREE.Group<THREE.Object3DEventMap>;
37
+ stats: any;
38
+ setCull: (studs: boolean, under: boolean) => void;
39
+ setSide: (front: boolean) => void;
40
+ setEdges: (v: boolean) => void;
41
+ swapStuds: (alt: any) => () => void;
42
+ box: THREE.Box3;
43
+ };
@@ -0,0 +1,56 @@
1
+ import * as THREE from "three";
2
+ import { type Pack } from "./render.js";
3
+ import type { PartInstance, LDrawColor } from "./model.js";
4
+ export type ColorSource = Map<number, string | LDrawColor> | Record<number | string, string | LDrawColor> | ((code: number) => THREE.ColorRepresentation | undefined);
5
+ export interface LdrObjectOptions {
6
+ /** Draw LDraw type-2 edge lines (indexed, sharing each part's instance matrices). Default true. */
7
+ edges?: boolean;
8
+ /** Hide studs fully covered by a part above. Default true. */
9
+ cullStuds?: boolean;
10
+ /** Hide undersides (tubes) sitting fully on studs below. Default true. */
11
+ cullUndersides?: boolean;
12
+ /** 'front' = single-sided BFC faces (fast); 'double' = render both sides. Uncertified parts are always double-sided. Default 'front'. */
13
+ side?: "front" | "double";
14
+ /** 'plastic' = MeshPhysicalMaterial with clearcoat; 'standard' = MeshStandardMaterial (cheapest, used by the benchmarks). Default 'plastic'. */
15
+ material?: "plastic" | "standard";
16
+ /** Colour table override. Defaults to the `colors` table embedded in the pack by `ldr pack`. */
17
+ colors?: ColorSource;
18
+ /** Colour for unknown codes. Default #A0A5A9. */
19
+ fallbackColor?: THREE.ColorRepresentation;
20
+ /** Keep native LDraw space (-Y up, 1 unit = 1 LDU). Default false: the object is rotated to +Y up. */
21
+ ldrawSpace?: boolean;
22
+ /** Uniform scale applied to the object (e.g. 0.4 for millimetres, 0.01 for ~metres-ish). Default 1 (LDU). */
23
+ scale?: number;
24
+ /** Initial step (inclusive, 0-based global sequence from `sequenceSteps`). Default: show all. */
25
+ step?: number;
26
+ }
27
+ export interface LdrStats {
28
+ instances: number;
29
+ drawn: number;
30
+ missing: string[];
31
+ studsTotal: number;
32
+ studsHidden: number;
33
+ undersTotal: number;
34
+ undersHidden: number;
35
+ buildMs: number;
36
+ }
37
+ export interface LdrObject {
38
+ /** Add this to your scene. */
39
+ object: THREE.Group;
40
+ stats: LdrStats;
41
+ /** Number of build steps (nested submodel steps are sequenced). */
42
+ stepCount: number;
43
+ /** Show instances up to and including `step` (null = all). Rebuilds batches; culling is recomputed for the partial model. */
44
+ setStep(step: number | null): void;
45
+ setEdges(on: boolean): void;
46
+ setCulling(studs: boolean, undersides: boolean): void;
47
+ setSide(side: "front" | "double"): void;
48
+ /** Bounding box in the object's parent space. */
49
+ getBox(target?: THREE.Box3): THREE.Box3;
50
+ dispose(): void;
51
+ }
52
+ /**
53
+ * Build a three.js object for a flattened model using a parts pack.
54
+ * Instances whose part is not in the pack are skipped and listed in `stats.missing`.
55
+ */
56
+ export declare function createLdrObject(instances: PartInstance[], pack: Pack, options?: LdrObjectOptions): LdrObject;
package/dist/spec.d.ts ADDED
@@ -0,0 +1,73 @@
1
+ export type M = number[];
2
+ export type RotStep = {
3
+ x: number;
4
+ y: number;
5
+ z: number;
6
+ mode: "REL" | "ABS" | "ADD" | "END";
7
+ };
8
+ export type BufExchg = {
9
+ op: "STORE" | "RETRIEVE";
10
+ buf: string;
11
+ };
12
+ export type Cmd = {
13
+ t: 0;
14
+ text: string;
15
+ meta?: string;
16
+ bfc?: string;
17
+ step?: boolean;
18
+ rotstep?: RotStep;
19
+ bufexchg?: BufExchg;
20
+ } | {
21
+ t: 1;
22
+ color: number;
23
+ m: M;
24
+ file: string;
25
+ } | {
26
+ t: 2 | 5;
27
+ color: number;
28
+ p: number[];
29
+ } | {
30
+ t: 3 | 4;
31
+ color: number;
32
+ p: number[];
33
+ };
34
+ export interface LFile {
35
+ name: string;
36
+ cmds: Cmd[];
37
+ movedTo?: string;
38
+ }
39
+ export interface Doc {
40
+ main: string;
41
+ files: Map<string, LFile>;
42
+ }
43
+ export declare const norm: (s: string) => string;
44
+ /** Parse LDraw color token: decimal code, or direct color 0x2RRGGBB (and related 0xN…… forms). */
45
+ export declare function parseColor(tok: string): number;
46
+ export declare function parse(src: string, name?: string): Doc;
47
+ export declare function mul(a: M, b: M): M;
48
+ export declare const I: M;
49
+ /** True if color is an LDraw direct color (0x2RRGGBB etc.), not a palette CODE. */
50
+ export declare const isDirectColor: (c: number) => boolean;
51
+ export interface PartMesh {
52
+ name: string;
53
+ pos: Float32Array;
54
+ col: Uint32Array;
55
+ edges: Float32Array;
56
+ cond: Float32Array;
57
+ nrm: Float32Array;
58
+ }
59
+ export interface Instance {
60
+ part: string;
61
+ color: number;
62
+ m: M;
63
+ step: number;
64
+ localStep: number;
65
+ steps: number[];
66
+ path: string;
67
+ missing: boolean;
68
+ aliasOf?: string;
69
+ }
70
+ /** Smooth normals that respect LDraw semantics: faces sharing an edge are smoothed together unless the
71
+ * edge is a type-2 line (hard). Conditional (type-5) lines force smooth; unmarked edges smooth only below creaseDeg.
72
+ * `cond` may be stride-6 (legacy endpoints only) or stride-12 (endpoints + 2 control points). */
73
+ export declare function smoothNormals(pos: number[], edges: number[], cond: number[], creaseDeg?: number): Float32Array;
package/package.json CHANGED
@@ -1,52 +1,72 @@
1
1
  {
2
2
  "name": "ldr",
3
- "version": "0.0.0-alpha.1",
4
- "description": "LDraw",
5
- "files": [
6
- "lib"
7
- ],
8
- "homepage": "https://github.com/evanbacon/ldr#readme",
3
+ "version": "0.2.0",
4
+ "description": "High-performance LEGO (LDraw) rendering for three.js: instanced parts packs, hidden stud/tube culling, low-detail studs, indexed edges, build steps.",
5
+ "license": "MIT",
6
+ "author": "Evan Bacon",
7
+ "type": "module",
8
+ "sideEffects": false,
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "git+https://github.com/evanbacon/ldr.git"
12
- },
13
- "bugs": {
14
- "url": "https://github.com/evanbacon/ldr/issues"
15
- },
16
- "main": "lib/index.js",
17
- "scripts": {
18
- "test": "echo \"Error: no test specified\" && exit 1",
19
- "lint": "eslint .",
20
- "prettier": "eslint . --fix"
21
- },
22
- "directories": {
23
- "example": "examples",
24
- "lib": "lib"
11
+ "url": "git+https://github.com/EvanBacon/ldr.git",
12
+ "directory": "packages/ldr"
25
13
  },
14
+ "homepage": "https://github.com/EvanBacon/ldr#readme",
15
+ "bugs": "https://github.com/EvanBacon/ldr/issues",
26
16
  "keywords": [
27
17
  "lego",
28
- "three.js",
29
- "graphics",
30
- "opengl",
31
- "gl",
32
- "arkit",
33
- "ldraw"
18
+ "ldraw",
19
+ "three",
20
+ "threejs",
21
+ "webgl",
22
+ "instancing",
23
+ "renderer",
24
+ "mpd",
25
+ "ldr",
26
+ "bricks",
27
+ "3d"
34
28
  ],
35
- "author": "Evan Bacon",
36
- "license": "MIT",
29
+ "main": "./dist/index.js",
30
+ "module": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "import": "./dist/index.js"
36
+ },
37
+ "./node": {
38
+ "types": "./dist/node/index.d.ts",
39
+ "import": "./dist/node/index.js"
40
+ },
41
+ "./package.json": "./package.json"
42
+ },
43
+ "bin": {
44
+ "ldr": "./dist/cli.js"
45
+ },
46
+ "files": [
47
+ "dist",
48
+ "README.md",
49
+ "LICENSE"
50
+ ],
51
+ "scripts": {
52
+ "build": "rm -rf dist && bun build src/index.ts --outdir dist --target browser --format esm --external three --external fzstd && bun build src/node/index.ts src/cli.ts --outdir dist --root src --target node --format esm --external three --external fzstd && tsc -p tsconfig.build.json && bun scripts/postbuild.ts",
53
+ "test": "bun test",
54
+ "prepublishOnly": "bun run build && bun test"
55
+ },
37
56
  "dependencies": {
38
- "three": "0.88.0"
57
+ "fzstd": "^0.1.1"
39
58
  },
40
59
  "peerDependencies": {
41
- "three": "0.88.0"
60
+ "three": ">=0.160.0"
42
61
  },
43
62
  "devDependencies": {
44
- "babel-jest": "^23.0.1",
45
- "eslint": "^4.19.1",
46
- "flow-bin": "^0.26.0",
47
- "jest": "^23.1.0",
48
- "prettier": "^1.13.5"
63
+ "@types/bun": "latest",
64
+ "@types/node": "^26.6.5",
65
+ "@types/three": "^0.180.0",
66
+ "three": "^0.186.1",
67
+ "typescript": "^5.9.0"
49
68
  },
50
- "eslintConfig": {
69
+ "engines": {
70
+ "node": ">=18"
51
71
  }
52
72
  }
package/lib/.DS_Store DELETED
Binary file
package/lib/index.js DELETED
@@ -1 +0,0 @@
1
- export default {};