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.
@@ -0,0 +1,106 @@
1
+ import type { Family } from './core';
2
+ import type { LeafReference, LeafWords } from './leaf';
3
+ export interface NodeIdentity {
4
+ birth: number;
5
+ key: {
6
+ idx: number;
7
+ version: number;
8
+ };
9
+ }
10
+ export interface PlacementIdentity {
11
+ shoot: NodeIdentity;
12
+ station: number;
13
+ }
14
+ /** The three packed words of one leaf; `./leaf` decodes them. */
15
+ export interface Placement {
16
+ identity: PlacementIdentity;
17
+ leaf: LeafWords;
18
+ }
19
+ export interface Run {
20
+ identity: NodeIdentity;
21
+ nodes: {
22
+ identity: NodeIdentity;
23
+ parent: NodeIdentity | null;
24
+ position: {
25
+ x: number;
26
+ y: number;
27
+ z: number;
28
+ };
29
+ radii: number[];
30
+ kind: string;
31
+ stem: boolean;
32
+ }[];
33
+ }
34
+ export interface ChangeRecord {
35
+ born_runs: Run[];
36
+ resized_runs: Run[];
37
+ shed_runs: NodeIdentity[];
38
+ born_placements: Placement[];
39
+ moved_placements: Placement[];
40
+ shed_placements: PlacementIdentity[];
41
+ }
42
+ export interface SpecimenRead {
43
+ age: number;
44
+ envelope: Family['skeleton']['envelope'];
45
+ surfaceHeight: number;
46
+ diagnostics: {
47
+ node_capped: boolean;
48
+ level_capped: boolean;
49
+ attraction_capped: boolean;
50
+ };
51
+ crossover: number;
52
+ shed: NodeIdentity[];
53
+ nodes: NodeIdentity[];
54
+ placements: PlacementIdentity[];
55
+ structure: {
56
+ values: Float64Array;
57
+ topology: Uint32Array;
58
+ };
59
+ /** Three u32 words a leaf, in placement order, and the box they decode
60
+ * against - one box for the family, so every age reads the same one. */
61
+ leaves: Uint32Array;
62
+ foliageReference: LeafReference;
63
+ }
64
+ /** Owned schema-3 little-endian chronicle and writer frontiers, without meshes.
65
+ * Caller mutation never reaches a retained specimen. */
66
+ export interface SpecimenSnapshot {
67
+ schema: 3;
68
+ data: Uint8Array;
69
+ }
70
+ export interface SpecimenHandle {
71
+ readonly frontier: number;
72
+ readonly historyCap: number;
73
+ read(age?: number): SpecimenRead;
74
+ advance(years: number): {
75
+ frontier: number;
76
+ changes: ChangeRecord;
77
+ };
78
+ changes(from: number, to: number): ChangeRecord;
79
+ setNodeCeiling(limit: number): void;
80
+ setHistoryCap(years: number): void;
81
+ snapshot(): SpecimenSnapshot;
82
+ release(): void;
83
+ }
84
+ export interface SpecimenExports {
85
+ memory: WebAssembly.Memory;
86
+ request_alloc(n: number): number;
87
+ request_ptr(): number;
88
+ buffer_ptr(slot: number): number;
89
+ buffer_len(slot: number): number;
90
+ specimen_node_ceiling(handle: number, limit: number): number;
91
+ specimen_history_cap(handle: number, years: number): number;
92
+ specimen_build(cap: number): number;
93
+ specimen_frontier(handle: number): number;
94
+ specimen_read(handle: number, age: number): number;
95
+ specimen_advance(handle: number, years: number): number;
96
+ specimen_changes(handle: number, from: number, to: number): number;
97
+ specimen_snapshot(handle: number): number;
98
+ specimen_snapshot_alloc(length: number): number;
99
+ specimen_snapshot_release(): void;
100
+ specimen_import(): number;
101
+ specimen_release(handle: number): number;
102
+ }
103
+ export declare function specimenBinding(get: () => SpecimenExports, check: (code: number) => void, metadata: () => unknown): {
104
+ build(family: Family | string, cap: number): SpecimenHandle;
105
+ import(snapshot: SpecimenSnapshot): SpecimenHandle;
106
+ };
@@ -0,0 +1,43 @@
1
+ export interface Bounds {
2
+ min: [number, number, number];
3
+ max: [number, number, number];
4
+ }
5
+ /** One batch query's answers, one entry per cell, as the core's field answers
6
+ * them. `flags` bits: wood = 1, foliage = 2. `woodRadius` is the thickest
7
+ * wood sweep reaching the cell, in metres, zero without wood. `leaves`
8
+ * estimates the leaf stations in the cell before the crown cull. `limbs` is
9
+ * the owning limb system, `NO_LIMB` where no foliage reaches. */
10
+ export interface FieldAnswer {
11
+ flags: Uint8Array;
12
+ woodRadius: Float32Array;
13
+ leaves: Float32Array;
14
+ limbs: Uint32Array;
15
+ }
16
+ export declare const NO_LIMB = 4294967295;
17
+ /** A grown tree's field. `query` takes packed cells, four f64 each: centre
18
+ * x, y, z and the half extent of a closed cube; touching counts and a zero
19
+ * half extent is a point. A batch with a length that is not a multiple of
20
+ * four, a non-finite centre or a negative half extent is refused whole.
21
+ * `release` drops the tree; a released tree refuses every query. */
22
+ export interface FieldTree {
23
+ readonly species: string;
24
+ readonly seed: number;
25
+ readonly bounds: Bounds;
26
+ query(cells: Float64Array): FieldAnswer;
27
+ release(): void;
28
+ }
29
+ export type FieldSource = BufferSource | Response | WebAssembly.Module;
30
+ export interface GrowOptions {
31
+ /** The lateral order whose systems name limbs; the family's own when left
32
+ * out. A higher order parts the crown into more and smaller systems. */
33
+ limbOrder?: number;
34
+ /** Where the slim Wasm comes from. Left out, it is fetched from
35
+ * `telperion-field.wasm` beside this module. */
36
+ source?: FieldSource;
37
+ }
38
+ /** Compiles the slim Wasm once; a source given to every `growField` call is
39
+ * compiled each time, so a caller that grows many trees passes a Module. */
40
+ export declare function compileField(source?: FieldSource): Promise<WebAssembly.Module>;
41
+ /** Grows `species` at `seed` and answers its field. An unknown species id,
42
+ * or one the catalogue does not serve, is refused with the core's error. */
43
+ export declare function growField(species: string, seed: number, options?: GrowOptions): Promise<FieldTree>;
@@ -0,0 +1,44 @@
1
+ import type { Bounds, FieldAnswer } from "./index";
2
+ /** A cubic grid of `n` cells a side, `cell` metres each, `n - 2` of them
3
+ * spanning the field's longest axis, centred on the bounds with one empty
4
+ * cell below the lowest point. `cells` is the packed batch the field
5
+ * queries; cell `i + n * (j + n * k)` is at `origin + (i + 0.5, j + 0.5,
6
+ * k + 0.5) * cell`. */
7
+ export interface Grid {
8
+ n: number;
9
+ cell: number;
10
+ origin: [number, number, number];
11
+ cells: Float64Array;
12
+ }
13
+ export declare function grid(bounds: Bounds, n: number): Grid;
14
+ /** The centre of cell `index` in metres. */
15
+ export declare function centre({ n, cell, origin }: Grid, index: number): [number, number, number];
16
+ /** How foliage cells thin to the keep fraction. `coin` drops whole limb
17
+ * systems by a stable coin per limb id. `density` keeps the cells with the
18
+ * most leaf stations. `mass` keeps the cells nearest to drawn wood. `tuft`
19
+ * keeps each limb system's rounded mass about its own centre. `gap` erodes
20
+ * each system where it meets a neighbouring one, so the crown's outer
21
+ * surface stays and a hanging curtain stays a curtain. */
22
+ export type ThinRule = "coin" | "density" | "mass" | "tuft" | "gap";
23
+ export interface Thinning {
24
+ rule: ThinRule;
25
+ keep: number;
26
+ }
27
+ export interface Dials {
28
+ /** Wood draws where the cell's thickest wood radius is at least this, in
29
+ * metres; the owner's reading was about 2 cm for a broadleaf and 1 cm
30
+ * for the spruce. */
31
+ woodCutoff: number;
32
+ /** Left out, no foliage draws: the wood alone. */
33
+ thin?: Thinning;
34
+ }
35
+ /** Cell indices to draw as wood and as foliage, and the number of distinct
36
+ * limb systems the foliage was thinned over. */
37
+ export interface Cubes {
38
+ wood: Uint32Array;
39
+ foliage: Uint32Array;
40
+ clumps: number;
41
+ }
42
+ export declare function voxelize(grid: Grid, answer: FieldAnswer, dials: Dials): Cubes;
43
+ /** A stable coin per limb id: the same limb always answers the same. */
44
+ export declare function coin(limb: number, keep: number): boolean;
package/dist/field.js ADDED
@@ -0,0 +1,62 @@
1
+ var m = Object.defineProperty;
2
+ var w = (r, e, t) => e in r ? m(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
+ var s = (r, e, t) => w(r, typeof e != "symbol" ? e + "" : e, t);
4
+ import { w as y } from "./wasm-source.js";
5
+ const _ = 4294967295, h = 4294967295, f = 2 ** 32;
6
+ let c;
7
+ function b(r) {
8
+ return r instanceof WebAssembly.Module ? Promise.resolve(r) : r !== void 0 ? d(r) : (c ?? (c = d(y(new URL("./telperion-field.wasm", import.meta.url))).catch((e) => {
9
+ throw c = void 0, e;
10
+ })), c);
11
+ }
12
+ async function d(r) {
13
+ const e = await r;
14
+ if (e instanceof Response) {
15
+ if (!e.ok) throw Error(`Field core load failed: HTTP ${e.status}`);
16
+ return WebAssembly.compile(await e.arrayBuffer());
17
+ }
18
+ return WebAssembly.compile(e);
19
+ }
20
+ function u(r, e, t = f) {
21
+ if (!Number.isInteger(r) || r < 0 || r >= t) throw RangeError(`${e} is an unsigned 32-bit integer`);
22
+ return r;
23
+ }
24
+ async function x(r, e, t = {}) {
25
+ u(e, "seed");
26
+ const o = t.limbOrder === void 0 ? h : u(t.limbOrder, "limbOrder", f - 1), n = new TextEncoder().encode(r), l = await b(t.source), { exports: i } = await WebAssembly.instantiate(l, {});
27
+ return new p(i, r, e, n, o);
28
+ }
29
+ class p {
30
+ constructor(e, t, o, n, l) {
31
+ s(this, "species");
32
+ s(this, "seed");
33
+ s(this, "bounds");
34
+ s(this, "exports");
35
+ s(this, "revision");
36
+ this.species = t, this.seed = o, this.exports = e, a(e, e.species_alloc(n.length)), new Uint8Array(e.memory.buffer, e.species_ptr(), n.length).set(n), a(e, e.grow(o, l)), this.revision = e.revision();
37
+ const i = new Float64Array(e.memory.buffer, e.bounds_ptr(), e.bounds_len());
38
+ if (i.length !== 6) throw Error("Field core: the field is empty");
39
+ this.bounds = { min: [i[0], i[1], i[2]], max: [i[3], i[4], i[5]] };
40
+ }
41
+ query(e) {
42
+ const t = this.exports;
43
+ if (!t) throw Error("Field tree is released");
44
+ if (!(e instanceof Float64Array) || e.length % 4) throw Error("Field cells require packed Float64Array x,y,z,halfExtent");
45
+ a(t, t.query_alloc(e.length / 4)), new Float64Array(t.memory.buffer, t.query_ptr(), e.length).set(e), a(t, t.query(this.revision));
46
+ const o = (n) => t.memory.buffer.slice(t.answer_ptr(n), t.answer_ptr(n) + t.answer_len(n) * A[n]);
47
+ return { flags: new Uint8Array(o(0)), woodRadius: new Float32Array(o(1)), leaves: new Float32Array(o(2)), limbs: new Uint32Array(o(3)) };
48
+ }
49
+ release() {
50
+ var e;
51
+ (e = this.exports) == null || e.release(), this.exports = void 0;
52
+ }
53
+ }
54
+ const A = [1, 4, 4, 4];
55
+ function a(r, e) {
56
+ if (e) throw Error(`Field core: ${new TextDecoder().decode(new Uint8Array(r.memory.buffer, r.error_ptr(), r.error_len()))}`);
57
+ }
58
+ export {
59
+ _ as NO_LIMB,
60
+ b as compileField,
61
+ x as growField
62
+ };
@@ -0,0 +1,7 @@
1
+ export { TreeEngine, initializeTreeCore, ORDINARY, OREGON_WHITE_OAK, NORWAY_SPRUCE, SILVER_BIRCH, presetById, TELPERION, LAURELIN, TWO_TREES, PRESETS } from "./browser/core";
2
+ export type { FoliageAnatomy, Family, TreePreset, Outputs, Bounds, Timings, Diagnostics, TreeOutput } from "./browser/core";
3
+ export { LEAF_WORDS, leafWords, leafPosition, leafScale, leafRotation, leafTransform } from "./browser/leaf";
4
+ export type { LeafReference, LeafWords, Vector } from "./browser/leaf";
5
+ export { createRenderer } from "./browser/render";
6
+ export type { FrameStats, Point, Pose, Renderer, Submitted, TimingReport, View } from "./browser/render";
7
+ export type { SpecimenHandle, SpecimenSnapshot, SpecimenRead, ChangeRecord, NodeIdentity, PlacementIdentity, Placement } from "./browser/specimen";
Binary file
Binary file