telperion 0.0.1 → 0.1.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.
Binary file
@@ -0,0 +1,98 @@
1
+ function F(o, t) {
2
+ if (!Number.isInteger(t) || t < 3) throw RangeError("a grid needs at least three cells a side");
3
+ const { min: s, max: e } = o, n = Math.max(e[0] - s[0], e[1] - s[1], e[2] - s[2]) / (t - 2), f = [(s[0] + e[0]) / 2 - t * n / 2, s[1] - n, (s[2] + e[2]) / 2 - t * n / 2], u = new Float64Array(t * t * t * 4);
4
+ for (let r = 0; r < t; r++) for (let c = 0; c < t; c++) for (let i = 0; i < t; i++)
5
+ u.set([f[0] + (i + 0.5) * n, f[1] + (c + 0.5) * n, f[2] + (r + 0.5) * n, n / 2], ((r * t + c) * t + i) * 4);
6
+ return { n: t, cell: n, origin: f, cells: u };
7
+ }
8
+ function S({ n: o, cell: t, origin: s }, e) {
9
+ return [s[0] + (e % o + 0.5) * t, s[1] + (Math.floor(e / o) % o + 0.5) * t, s[2] + (Math.floor(e / (o * o)) + 0.5) * t];
10
+ }
11
+ const M = 1;
12
+ function C(o, t, s) {
13
+ const { n: e } = o, n = e * e * e;
14
+ if (t.flags.length !== n) throw RangeError(`the answer has ${t.flags.length} cells, the grid ${n}`);
15
+ if (!(s.woodCutoff >= 0)) throw RangeError("woodCutoff is a radius in metres");
16
+ const { flags: f, limbs: u, leaves: r, woodRadius: c } = t, i = new Uint8Array(n), h = [], l = (a) => (f[a] & 1) !== 0 && c[a] >= s.woodCutoff;
17
+ for (let a = 0; a < n; a++) l(a) && (i[a] = M, h.push(a));
18
+ if (s.thin === void 0) return { wood: Uint32Array.from(h), foliage: new Uint32Array(0), clumps: 0 };
19
+ const { rule: y, keep: g } = s.thin;
20
+ if (!(g >= 0 && g <= 1)) throw RangeError("keep is a fraction");
21
+ const m = /* @__PURE__ */ new Set(), d = [];
22
+ for (let a = 0; a < n; a++) !l(a) && f[a] & 2 && (d.push(a), m.add(u[a]));
23
+ if (y === "coin") {
24
+ const a = d.filter((w) => R(u[w], g));
25
+ return { wood: Uint32Array.from(h), foliage: Uint32Array.from(a), clumps: m.size };
26
+ }
27
+ const A = v[y](o, d, i, u, r), U = d.map((a, w) => ({ i: a, at: w })).sort((a, w) => A[w.at] - A[a.at]), E = Math.round(d.length * g);
28
+ return { wood: Uint32Array.from(h), foliage: Uint32Array.from(U.slice(0, E), (a) => a.i), clumps: m.size };
29
+ }
30
+ function R(o, t) {
31
+ let s = Math.imul(o ^ 2654435769, 2246822507);
32
+ return s = Math.imul(s ^ s >>> 13, 3266489909), ((s ^ s >>> 16) >>> 0) / 2 ** 32 < t;
33
+ }
34
+ const p = (o, t) => [t % o, Math.floor(t / o) % o, Math.floor(t / (o * o))], _ = [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]];
35
+ function j(o, t) {
36
+ const [s, e, n] = p(o, t), f = [];
37
+ for (const [u, r, c] of _) {
38
+ const i = s + u, h = e + r, l = n + c;
39
+ i >= 0 && h >= 0 && l >= 0 && i < o && h < o && l < o && f.push((l * o + h) * o + i);
40
+ }
41
+ return f;
42
+ }
43
+ function k(o, t, s) {
44
+ const e = new Int32Array(o * o * o).fill(-1);
45
+ let n = t;
46
+ for (const f of t) e[f] = 0;
47
+ for (; n.length; ) {
48
+ const f = [];
49
+ for (const u of n) for (const r of j(o, u)) e[r] === -1 && s(u, r) && (e[r] = e[u] + 1, f.push(r));
50
+ n = f;
51
+ }
52
+ return e;
53
+ }
54
+ const v = {
55
+ density: (o, t, s, e, n) => Float64Array.from(t, (f) => n[f]),
56
+ // Nearer drawn wood scores higher; ties broken by density so masses stay full.
57
+ mass: ({ n: o }, t, s, e, n) => {
58
+ const f = [];
59
+ for (let r = 0; r < s.length; r++) s[r] === M && f.push(r);
60
+ const u = k(o, f, () => !0);
61
+ return Float64Array.from(t, (r) => -u[r] + n[r] * 1e-6);
62
+ },
63
+ // A cell scores by how deep it sits in its own system, so the fringe
64
+ // between neighbouring systems goes first and the branch to each mass shows.
65
+ tuft: ({ n: o }, t, s, e) => {
66
+ const n = /* @__PURE__ */ new Map();
67
+ for (const r of t) {
68
+ const [c, i, h] = p(o, r), l = n.get(e[r]) ?? { i: 0, j: 0, k: 0, c: 0 };
69
+ l.i += c, l.j += i, l.k += h, l.c++, n.set(e[r], l);
70
+ }
71
+ const f = (r) => {
72
+ const [c, i, h] = p(o, r), l = n.get(e[r]);
73
+ return Math.hypot(c - l.i / l.c, i - l.j / l.c, h - l.k / l.c);
74
+ }, u = /* @__PURE__ */ new Map();
75
+ for (const r of t) {
76
+ const c = u.get(e[r]) ?? { s: 0, c: 0 };
77
+ c.s += f(r), c.c++, u.set(e[r], c);
78
+ }
79
+ return Float64Array.from(t, (r) => {
80
+ const c = u.get(e[r]);
81
+ return -f(r) / (c.s / c.c + 1e-9);
82
+ });
83
+ },
84
+ // The grid distance to the nearest cell of a different system; a system
85
+ // with no neighbour never erodes and scores as its deepest cell would.
86
+ gap: ({ n: o }, t, s, e, n) => {
87
+ const f = new Uint8Array(o * o * o);
88
+ for (const c of t) f[c] = 1;
89
+ const u = t.filter((c) => j(o, c).some((i) => f[i] && e[i] !== e[c])), r = k(o, u, (c, i) => f[i] === 1 && e[i] === e[c]);
90
+ return Float64Array.from(t, (c) => (r[c] === -1 ? o : r[c]) + n[c] * 1e-6);
91
+ }
92
+ };
93
+ export {
94
+ S as centre,
95
+ R as coin,
96
+ F as grid,
97
+ C as voxelize
98
+ };
@@ -0,0 +1 @@
1
+ export declare function wasmSource(url: URL): Promise<Response | Uint8Array>;
@@ -0,0 +1,12 @@
1
+ async function i(e) {
2
+ if (e.protocol !== "file:") return fetch(e);
3
+ const o = "node:fs/promises", { readFile: t } = await import(
4
+ /* @vite-ignore */
5
+ /* webpackIgnore: true */
6
+ o
7
+ );
8
+ return t(e);
9
+ }
10
+ export {
11
+ i as w
12
+ };
package/package.json CHANGED
@@ -1,13 +1,84 @@
1
1
  {
2
2
  "name": "telperion",
3
- "version": "0.0.1",
4
- "description": "",
5
- "main": "index.js",
3
+ "version": "0.1.0",
4
+ "description": "A procedural tree generator. Authored silhouette, one builder growing every axis from continuous botanical traits with attractor pull among them, local rules down to leaf-bearing twigs, conserved thickness, one continuous plaited surface, and foliage culled to a shell and drawn one indirect draw per level of detail, each leaf at the coarsest level a pixel cannot tell from the finest.",
5
+ "keywords": [
6
+ "procedural",
7
+ "tree",
8
+ "generator",
9
+ "space-colonization",
10
+ "foliage",
11
+ "canopy",
12
+ "geometry",
13
+ "wgpu",
14
+ "webgpu",
15
+ "rust"
16
+ ],
17
+ "license": "MIT",
18
+ "author": "Daniel Killenberger",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/DanielKillenberger/telperion.git"
22
+ },
23
+ "type": "module",
24
+ "sideEffects": false,
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "main": "./dist/telperion.js",
29
+ "module": "./dist/telperion.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/telperion.js"
35
+ },
36
+ "./field": {
37
+ "types": "./dist/field/index.d.ts",
38
+ "import": "./dist/field.js"
39
+ },
40
+ "./field/voxelize": {
41
+ "types": "./dist/field/voxelize.d.ts",
42
+ "import": "./dist/voxelize.js"
43
+ },
44
+ "./telperion.wasm": "./dist/telperion.wasm",
45
+ "./telperion-render.wasm": "./dist/telperion-render.wasm",
46
+ "./telperion-field.wasm": "./dist/telperion-field.wasm"
47
+ },
48
+ "engines": {
49
+ "node": ">=20"
50
+ },
6
51
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
52
+ "dev": "npm run wasm:build && npm run render:build && vite",
53
+ "build": "npm run wasm:build && npm run render:build && tsc -p tsconfig.build.json && vite build",
54
+ "test:dist": "node scripts/test-dist.mjs",
55
+ "pretest": "npm run wasm:build && npm run render:build",
56
+ "test": "npm run catalogue:check && vitest run",
57
+ "catalogue:check": "node scripts/catalogue-check.mjs",
58
+ "catalogue:pages": "node scripts/catalogue-pages.mjs",
59
+ "pretypecheck": "npm run render:build",
60
+ "typecheck": "tsc --noEmit",
61
+ "rust:build": "cargo build --release --workspace && cargo build --release --target wasm32-unknown-unknown -p telperion-wasm",
62
+ "rust:test": "cargo test --release --workspace",
63
+ "rust:test:wasm": "node scripts/test-wasm.mjs",
64
+ "migration:reference": "node scripts/export-reference.mjs",
65
+ "wasm:build": "node scripts/build-wasm.mjs",
66
+ "render:build": "node scripts/build-render.mjs",
67
+ "species:measure": "cargo build --release -p telperion-core --example species_measure && node tests/species.mjs --measure-only",
68
+ "species:qa": "cargo build --release -p telperion-core --example species_measure && cargo build --release -p telperion-render --example headless && node tests/species.mjs",
69
+ "test:render": "node scripts/test-render.mjs",
70
+ "species:compare": "uv run scripts/compare-references.py",
71
+ "species:quick": "cargo build --release -p telperion-render --example headless && node tests/species.mjs"
8
72
  },
9
- "keywords": [],
10
- "author": "",
11
- "license": "ISC",
12
- "type": "commonjs"
73
+ "devDependencies": {
74
+ "@types/react": "19.0.0",
75
+ "@types/react-dom": "19.0.0",
76
+ "@vitejs/plugin-react": "^4.3.4",
77
+ "playwright": "^1.63.0",
78
+ "react": "19.0.0",
79
+ "react-dom": "19.0.0",
80
+ "typescript": "5.7.2",
81
+ "vite": "^6.0.0",
82
+ "vitest": "^4.1.11"
83
+ }
13
84
  }