@ifc-lite/collab 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.
- package/LICENSE +373 -0
- package/README.md +92 -0
- package/dist/awareness/agent.d.ts +36 -0
- package/dist/awareness/agent.d.ts.map +1 -0
- package/dist/awareness/agent.js +39 -0
- package/dist/awareness/agent.js.map +1 -0
- package/dist/awareness/color.d.ts +31 -0
- package/dist/awareness/color.d.ts.map +1 -0
- package/dist/awareness/color.js +61 -0
- package/dist/awareness/color.js.map +1 -0
- package/dist/awareness/index.d.ts +6 -0
- package/dist/awareness/index.d.ts.map +1 -0
- package/dist/awareness/index.js +9 -0
- package/dist/awareness/index.js.map +1 -0
- package/dist/awareness/overlay.d.ts +59 -0
- package/dist/awareness/overlay.d.ts.map +1 -0
- package/dist/awareness/overlay.js +110 -0
- package/dist/awareness/overlay.js.map +1 -0
- package/dist/awareness/presence.d.ts +95 -0
- package/dist/awareness/presence.d.ts.map +1 -0
- package/dist/awareness/presence.js +114 -0
- package/dist/awareness/presence.js.map +1 -0
- package/dist/awareness/render.d.ts +58 -0
- package/dist/awareness/render.d.ts.map +1 -0
- package/dist/awareness/render.js +66 -0
- package/dist/awareness/render.js.map +1 -0
- package/dist/branch/branch-tree.d.ts +40 -0
- package/dist/branch/branch-tree.d.ts.map +1 -0
- package/dist/branch/branch-tree.js +66 -0
- package/dist/branch/branch-tree.js.map +1 -0
- package/dist/branch/branch.d.ts +44 -0
- package/dist/branch/branch.d.ts.map +1 -0
- package/dist/branch/branch.js +109 -0
- package/dist/branch/branch.js.map +1 -0
- package/dist/branch/history-automerge.d.ts +31 -0
- package/dist/branch/history-automerge.d.ts.map +1 -0
- package/dist/branch/history-automerge.js +237 -0
- package/dist/branch/history-automerge.js.map +1 -0
- package/dist/branch/history.d.ts +147 -0
- package/dist/branch/history.d.ts.map +1 -0
- package/dist/branch/history.js +223 -0
- package/dist/branch/history.js.map +1 -0
- package/dist/branch/index.d.ts +5 -0
- package/dist/branch/index.d.ts.map +1 -0
- package/dist/branch/index.js +8 -0
- package/dist/branch/index.js.map +1 -0
- package/dist/conflicts/detector.d.ts +52 -0
- package/dist/conflicts/detector.d.ts.map +1 -0
- package/dist/conflicts/detector.js +226 -0
- package/dist/conflicts/detector.js.map +1 -0
- package/dist/conflicts/index.d.ts +3 -0
- package/dist/conflicts/index.d.ts.map +1 -0
- package/dist/conflicts/index.js +6 -0
- package/dist/conflicts/index.js.map +1 -0
- package/dist/conflicts/ui-bridge.d.ts +80 -0
- package/dist/conflicts/ui-bridge.d.ts.map +1 -0
- package/dist/conflicts/ui-bridge.js +126 -0
- package/dist/conflicts/ui-bridge.js.map +1 -0
- package/dist/doc/entity.d.ts +84 -0
- package/dist/doc/entity.d.ts.map +1 -0
- package/dist/doc/entity.js +345 -0
- package/dist/doc/entity.js.map +1 -0
- package/dist/doc/geometry.d.ts +39 -0
- package/dist/doc/geometry.d.ts.map +1 -0
- package/dist/doc/geometry.js +99 -0
- package/dist/doc/geometry.js.map +1 -0
- package/dist/doc/index.d.ts +8 -0
- package/dist/doc/index.d.ts.map +1 -0
- package/dist/doc/index.js +11 -0
- package/dist/doc/index.js.map +1 -0
- package/dist/doc/migration-ifc4-to-ifc4x3.d.ts +21 -0
- package/dist/doc/migration-ifc4-to-ifc4x3.d.ts.map +1 -0
- package/dist/doc/migration-ifc4-to-ifc4x3.js +55 -0
- package/dist/doc/migration-ifc4-to-ifc4x3.js.map +1 -0
- package/dist/doc/relationship.d.ts +27 -0
- package/dist/doc/relationship.d.ts.map +1 -0
- package/dist/doc/relationship.js +110 -0
- package/dist/doc/relationship.js.map +1 -0
- package/dist/doc/schema-version.d.ts +47 -0
- package/dist/doc/schema-version.d.ts.map +1 -0
- package/dist/doc/schema-version.js +41 -0
- package/dist/doc/schema-version.js.map +1 -0
- package/dist/doc/schema.d.ts +131 -0
- package/dist/doc/schema.d.ts.map +1 -0
- package/dist/doc/schema.js +117 -0
- package/dist/doc/schema.js.map +1 -0
- package/dist/doc/units.d.ts +45 -0
- package/dist/doc/units.d.ts.map +1 -0
- package/dist/doc/units.js +120 -0
- package/dist/doc/units.js.map +1 -0
- package/dist/federation/bridge.d.ts +34 -0
- package/dist/federation/bridge.d.ts.map +1 -0
- package/dist/federation/bridge.js +52 -0
- package/dist/federation/bridge.js.map +1 -0
- package/dist/federation/index.d.ts +4 -0
- package/dist/federation/index.d.ts.map +1 -0
- package/dist/federation/index.js +7 -0
- package/dist/federation/index.js.map +1 -0
- package/dist/federation/resolver.d.ts +58 -0
- package/dist/federation/resolver.d.ts.map +1 -0
- package/dist/federation/resolver.js +43 -0
- package/dist/federation/resolver.js.map +1 -0
- package/dist/federation/session.d.ts +79 -0
- package/dist/federation/session.d.ts.map +1 -0
- package/dist/federation/session.js +126 -0
- package/dist/federation/session.js.map +1 -0
- package/dist/geometry/blob-store.d.ts +106 -0
- package/dist/geometry/blob-store.d.ts.map +1 -0
- package/dist/geometry/blob-store.js +266 -0
- package/dist/geometry/blob-store.js.map +1 -0
- package/dist/geometry/csg.d.ts +63 -0
- package/dist/geometry/csg.d.ts.map +1 -0
- package/dist/geometry/csg.js +101 -0
- package/dist/geometry/csg.js.map +1 -0
- package/dist/geometry/determinism.d.ts +45 -0
- package/dist/geometry/determinism.d.ts.map +1 -0
- package/dist/geometry/determinism.js +92 -0
- package/dist/geometry/determinism.js.map +1 -0
- package/dist/geometry/gc.d.ts +63 -0
- package/dist/geometry/gc.d.ts.map +1 -0
- package/dist/geometry/gc.js +109 -0
- package/dist/geometry/gc.js.map +1 -0
- package/dist/geometry/index.d.ts +6 -0
- package/dist/geometry/index.d.ts.map +1 -0
- package/dist/geometry/index.js +9 -0
- package/dist/geometry/index.js.map +1 -0
- package/dist/geometry/parametric.d.ts +92 -0
- package/dist/geometry/parametric.d.ts.map +1 -0
- package/dist/geometry/parametric.js +200 -0
- package/dist/geometry/parametric.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/mutations/bind.d.ts +56 -0
- package/dist/mutations/bind.d.ts.map +1 -0
- package/dist/mutations/bind.js +68 -0
- package/dist/mutations/bind.js.map +1 -0
- package/dist/mutations/index.d.ts +2 -0
- package/dist/mutations/index.d.ts.map +1 -0
- package/dist/mutations/index.js +5 -0
- package/dist/mutations/index.js.map +1 -0
- package/dist/perf/benchmark.d.ts +25 -0
- package/dist/perf/benchmark.d.ts.map +1 -0
- package/dist/perf/benchmark.js +97 -0
- package/dist/perf/benchmark.js.map +1 -0
- package/dist/perf/index.d.ts +3 -0
- package/dist/perf/index.d.ts.map +1 -0
- package/dist/perf/index.js +6 -0
- package/dist/perf/index.js.map +1 -0
- package/dist/perf/latency.d.ts +39 -0
- package/dist/perf/latency.d.ts.map +1 -0
- package/dist/perf/latency.js +79 -0
- package/dist/perf/latency.js.map +1 -0
- package/dist/privacy.d.ts +45 -0
- package/dist/privacy.d.ts.map +1 -0
- package/dist/privacy.js +66 -0
- package/dist/privacy.js.map +1 -0
- package/dist/providers/indexeddb.d.ts +37 -0
- package/dist/providers/indexeddb.d.ts.map +1 -0
- package/dist/providers/indexeddb.js +45 -0
- package/dist/providers/indexeddb.js.map +1 -0
- package/dist/providers/webrtc.d.ts +40 -0
- package/dist/providers/webrtc.d.ts.map +1 -0
- package/dist/providers/webrtc.js +81 -0
- package/dist/providers/webrtc.js.map +1 -0
- package/dist/providers/websocket.d.ts +45 -0
- package/dist/providers/websocket.d.ts.map +1 -0
- package/dist/providers/websocket.js +56 -0
- package/dist/providers/websocket.js.map +1 -0
- package/dist/security/e2e.d.ts +54 -0
- package/dist/security/e2e.d.ts.map +1 -0
- package/dist/security/e2e.js +147 -0
- package/dist/security/e2e.js.map +1 -0
- package/dist/security/index.d.ts +2 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +5 -0
- package/dist/security/index.js.map +1 -0
- package/dist/session.d.ts +79 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +112 -0
- package/dist/session.js.map +1 -0
- package/dist/snapshot/from-ifcx.d.ts +24 -0
- package/dist/snapshot/from-ifcx.d.ts.map +1 -0
- package/dist/snapshot/from-ifcx.js +93 -0
- package/dist/snapshot/from-ifcx.js.map +1 -0
- package/dist/snapshot/index.d.ts +6 -0
- package/dist/snapshot/index.d.ts.map +1 -0
- package/dist/snapshot/index.js +9 -0
- package/dist/snapshot/index.js.map +1 -0
- package/dist/snapshot/layers.d.ts +56 -0
- package/dist/snapshot/layers.d.ts.map +1 -0
- package/dist/snapshot/layers.js +82 -0
- package/dist/snapshot/layers.js.map +1 -0
- package/dist/snapshot/minimal-layer.d.ts +40 -0
- package/dist/snapshot/minimal-layer.d.ts.map +1 -0
- package/dist/snapshot/minimal-layer.js +123 -0
- package/dist/snapshot/minimal-layer.js.map +1 -0
- package/dist/snapshot/to-ifcx.d.ts +26 -0
- package/dist/snapshot/to-ifcx.d.ts.map +1 -0
- package/dist/snapshot/to-ifcx.js +54 -0
- package/dist/snapshot/to-ifcx.js.map +1 -0
- package/dist/snapshot/worker.d.ts +45 -0
- package/dist/snapshot/worker.d.ts.map +1 -0
- package/dist/snapshot/worker.js +73 -0
- package/dist/snapshot/worker.js.map +1 -0
- package/dist/sync/room.d.ts +15 -0
- package/dist/sync/room.d.ts.map +1 -0
- package/dist/sync/room.js +20 -0
- package/dist/sync/room.js.map +1 -0
- package/dist/undo.d.ts +25 -0
- package/dist/undo.d.ts.map +1 -0
- package/dist/undo.js +37 -0
- package/dist/undo.js.map +1 -0
- package/dist/viewer-bridge.d.ts +27 -0
- package/dist/viewer-bridge.d.ts.map +1 -0
- package/dist/viewer-bridge.js +82 -0
- package/dist/viewer-bridge.js.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* CSG (constructive solid geometry) operation tree as a Y.Array<CSGOp>.
|
|
6
|
+
*
|
|
7
|
+
* Spec §9.4 / §11 + open problem #4: full CRDT-tree merging of CSG ops is
|
|
8
|
+
* a v1.x topic. v0.1 stores ops as an ordered Y.Array — concurrent
|
|
9
|
+
* additions from two peers interleave preserving each peer's relative
|
|
10
|
+
* order, and a UI can drag-to-reorder via explicit moves. The CSG result
|
|
11
|
+
* is order-dependent; that's exposed honestly in the README.
|
|
12
|
+
*
|
|
13
|
+
* The geometry node referenced here is one of the Y.Doc's
|
|
14
|
+
* `geometry` top-level entries with `type: 'csg-tree'`. The tree's ops
|
|
15
|
+
* live as a Y.Array on `params.ops` so they ride the same CRDT machinery
|
|
16
|
+
* as parametric params.
|
|
17
|
+
*/
|
|
18
|
+
import * as Y from 'yjs';
|
|
19
|
+
import { GEOMETRY_KEY, geometryMap } from '../doc/schema.js';
|
|
20
|
+
import { createGeometry } from '../doc/geometry.js';
|
|
21
|
+
/**
|
|
22
|
+
* Get-or-create a CSG-tree geometry node and return its `ops` Y.Array.
|
|
23
|
+
*
|
|
24
|
+
* Idempotent: if the geometry already exists with `type==='csg-tree'`,
|
|
25
|
+
* returns the existing ops Y.Array.
|
|
26
|
+
*/
|
|
27
|
+
export function ensureCSGTree(doc, geomId, options = {}) {
|
|
28
|
+
let node = geometryMap(doc).get(geomId);
|
|
29
|
+
if (!node) {
|
|
30
|
+
node = createGeometry(doc, geomId, {
|
|
31
|
+
type: 'csg-tree',
|
|
32
|
+
source: 'csg-op',
|
|
33
|
+
bbox: options.bbox,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const params = node.get(GEOMETRY_KEY.PARAMS);
|
|
37
|
+
if (!params)
|
|
38
|
+
throw new Error(`@ifc-lite/collab: csg-tree "${geomId}" missing params map`);
|
|
39
|
+
let ops = params.get('ops');
|
|
40
|
+
if (!ops) {
|
|
41
|
+
ops = new Y.Array();
|
|
42
|
+
params.set('ops', ops);
|
|
43
|
+
}
|
|
44
|
+
if (options.ops && ops.length === 0)
|
|
45
|
+
ops.push(options.ops);
|
|
46
|
+
return ops;
|
|
47
|
+
}
|
|
48
|
+
export function getCSGOps(doc, geomId) {
|
|
49
|
+
const node = geometryMap(doc).get(geomId);
|
|
50
|
+
const params = node?.get(GEOMETRY_KEY.PARAMS);
|
|
51
|
+
const ops = params?.get('ops');
|
|
52
|
+
return ops ? ops.toArray() : [];
|
|
53
|
+
}
|
|
54
|
+
/** Append one CSG op. Concurrent appends interleave per Y.Array semantics. */
|
|
55
|
+
export function appendCSGOp(doc, geomId, op) {
|
|
56
|
+
const ops = ensureCSGTree(doc, geomId);
|
|
57
|
+
ops.push([op]);
|
|
58
|
+
return ops.length - 1;
|
|
59
|
+
}
|
|
60
|
+
/** Insert a CSG op at a specific index. */
|
|
61
|
+
export function insertCSGOp(doc, geomId, index, op) {
|
|
62
|
+
const ops = ensureCSGTree(doc, geomId);
|
|
63
|
+
ops.insert(Math.max(0, Math.min(index, ops.length)), [op]);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Remove a CSG op by `opId`. Returns true if found and removed.
|
|
67
|
+
*
|
|
68
|
+
* We search by opId rather than index so concurrent inserts elsewhere
|
|
69
|
+
* don't shift the target.
|
|
70
|
+
*/
|
|
71
|
+
export function removeCSGOp(doc, geomId, opId) {
|
|
72
|
+
const ops = ensureCSGTree(doc, geomId);
|
|
73
|
+
const arr = ops.toArray();
|
|
74
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
75
|
+
if (arr[i].opId === opId) {
|
|
76
|
+
ops.delete(i, 1);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Reorder a CSG op (move by opId to a new index). Implemented as
|
|
84
|
+
* delete-then-insert; the underlying CRDT semantics keep concurrent
|
|
85
|
+
* reorder + reorder visible as the last-applied wins (ordering is what
|
|
86
|
+
* the user is *for sure* fighting over).
|
|
87
|
+
*/
|
|
88
|
+
export function moveCSGOp(doc, geomId, opId, toIndex) {
|
|
89
|
+
const ops = ensureCSGTree(doc, geomId);
|
|
90
|
+
const arr = ops.toArray();
|
|
91
|
+
const idx = arr.findIndex((o) => o.opId === opId);
|
|
92
|
+
if (idx < 0)
|
|
93
|
+
return false;
|
|
94
|
+
const [op] = arr.splice(idx, 1);
|
|
95
|
+
doc.transact(() => {
|
|
96
|
+
ops.delete(idx, 1);
|
|
97
|
+
ops.insert(Math.max(0, Math.min(toIndex, ops.length)), [op]);
|
|
98
|
+
});
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=csg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csg.js","sourceRoot":"","sources":["../../src/geometry/csg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determinism harness (spec open problem #5).
|
|
3
|
+
*
|
|
4
|
+
* The CRDT pipeline assumes the geometry kernel is bit-identical
|
|
5
|
+
* across machines for the same params. This harness lets a CI matrix
|
|
6
|
+
* run the kernel against a fixed set of param fixtures and compare
|
|
7
|
+
* hashes. Drift is reported, not silently masked.
|
|
8
|
+
*
|
|
9
|
+
* Designed to be runtime-agnostic: pass a `kernel(params) => Mesh`
|
|
10
|
+
* callable and a list of fixtures.
|
|
11
|
+
*/
|
|
12
|
+
import { type Mesh, type ParametricSource } from './parametric.js';
|
|
13
|
+
export interface DeterminismFixture {
|
|
14
|
+
name: string;
|
|
15
|
+
source: ParametricSource;
|
|
16
|
+
params: unknown;
|
|
17
|
+
}
|
|
18
|
+
export interface DeterminismResult {
|
|
19
|
+
name: string;
|
|
20
|
+
hash: string;
|
|
21
|
+
ok: boolean;
|
|
22
|
+
/** Expected hash, when comparing against a known-good baseline. */
|
|
23
|
+
expected?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface DeterminismReport {
|
|
26
|
+
results: DeterminismResult[];
|
|
27
|
+
ok: boolean;
|
|
28
|
+
}
|
|
29
|
+
export type Kernel = (source: ParametricSource, params: unknown) => Mesh;
|
|
30
|
+
/**
|
|
31
|
+
* Run `kernel` against every fixture and return per-fixture hashes.
|
|
32
|
+
*
|
|
33
|
+
* If `expected` is supplied, *every* fixture must have a matching entry
|
|
34
|
+
* — a missing entry counts as a failure, not an implicit pass. This is
|
|
35
|
+
* intentional: a typo in a fixture name or a forgotten baseline update
|
|
36
|
+
* used to silently report `ok=true`, which made drift unobservable. If
|
|
37
|
+
* you genuinely want partial baselines, omit `expected` entirely.
|
|
38
|
+
*/
|
|
39
|
+
export declare function runDeterminismHarness(kernel: Kernel, fixtures: readonly DeterminismFixture[], expected?: Record<string, string>): DeterminismReport;
|
|
40
|
+
/**
|
|
41
|
+
* Default fixture set covering all built-in primitives.
|
|
42
|
+
* Apps extend with their own per-platform-stable parametric library.
|
|
43
|
+
*/
|
|
44
|
+
export declare const DEFAULT_FIXTURES: readonly DeterminismFixture[];
|
|
45
|
+
//# sourceMappingURL=determinism.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"determinism.d.ts","sourceRoot":"","sources":["../../src/geometry/determinism.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAAY,KAAK,IAAI,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE7E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AAEzE;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,SAAS,kBAAkB,EAAE,EACvC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,iBAAiB,CAsBnB;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,kBAAkB,EAoCzD,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Determinism harness (spec open problem #5).
|
|
6
|
+
*
|
|
7
|
+
* The CRDT pipeline assumes the geometry kernel is bit-identical
|
|
8
|
+
* across machines for the same params. This harness lets a CI matrix
|
|
9
|
+
* run the kernel against a fixed set of param fixtures and compare
|
|
10
|
+
* hashes. Drift is reported, not silently masked.
|
|
11
|
+
*
|
|
12
|
+
* Designed to be runtime-agnostic: pass a `kernel(params) => Mesh`
|
|
13
|
+
* callable and a list of fixtures.
|
|
14
|
+
*/
|
|
15
|
+
import { hashMesh } from './parametric.js';
|
|
16
|
+
/**
|
|
17
|
+
* Run `kernel` against every fixture and return per-fixture hashes.
|
|
18
|
+
*
|
|
19
|
+
* If `expected` is supplied, *every* fixture must have a matching entry
|
|
20
|
+
* — a missing entry counts as a failure, not an implicit pass. This is
|
|
21
|
+
* intentional: a typo in a fixture name or a forgotten baseline update
|
|
22
|
+
* used to silently report `ok=true`, which made drift unobservable. If
|
|
23
|
+
* you genuinely want partial baselines, omit `expected` entirely.
|
|
24
|
+
*/
|
|
25
|
+
export function runDeterminismHarness(kernel, fixtures, expected) {
|
|
26
|
+
const results = [];
|
|
27
|
+
let allOk = true;
|
|
28
|
+
const baselineProvided = expected !== undefined;
|
|
29
|
+
for (const fixture of fixtures) {
|
|
30
|
+
const mesh = kernel(fixture.source, fixture.params);
|
|
31
|
+
const hash = hashMesh(mesh);
|
|
32
|
+
const exp = expected?.[fixture.name];
|
|
33
|
+
let ok;
|
|
34
|
+
if (!baselineProvided) {
|
|
35
|
+
ok = true;
|
|
36
|
+
}
|
|
37
|
+
else if (exp == null) {
|
|
38
|
+
// Baseline was provided but doesn't cover this fixture — refuse
|
|
39
|
+
// to silently report success.
|
|
40
|
+
ok = false;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
ok = hash === exp;
|
|
44
|
+
}
|
|
45
|
+
if (!ok)
|
|
46
|
+
allOk = false;
|
|
47
|
+
results.push({ name: fixture.name, hash, ok, expected: exp });
|
|
48
|
+
}
|
|
49
|
+
return { results, ok: allOk };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Default fixture set covering all built-in primitives.
|
|
53
|
+
* Apps extend with their own per-platform-stable parametric library.
|
|
54
|
+
*/
|
|
55
|
+
export const DEFAULT_FIXTURES = [
|
|
56
|
+
{
|
|
57
|
+
name: 'box-1x2x3',
|
|
58
|
+
source: 'box',
|
|
59
|
+
params: { length: 1, width: 2, height: 3 },
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'cylinder-r1-h2',
|
|
63
|
+
source: 'cylinder',
|
|
64
|
+
params: { radius: 1, height: 2, segments: 16 },
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'extruded-rect-2x3-d4',
|
|
68
|
+
source: 'extruded-area-solid',
|
|
69
|
+
params: {
|
|
70
|
+
profile: { type: 'rectangle', width: 2, height: 3 },
|
|
71
|
+
depth: 4,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'extruded-circle-r1-d2',
|
|
76
|
+
source: 'extruded-area-solid',
|
|
77
|
+
params: {
|
|
78
|
+
profile: { type: 'circle', radius: 1, segments: 16 },
|
|
79
|
+
depth: 2,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'revolved-r0.5-full',
|
|
84
|
+
source: 'revolved-area-solid',
|
|
85
|
+
params: {
|
|
86
|
+
profile: { type: 'circle', radius: 0.5, segments: 16 },
|
|
87
|
+
angle: Math.PI * 2,
|
|
88
|
+
segments: 16,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
//# sourceMappingURL=determinism.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"determinism.js","sourceRoot":"","sources":["../../src/geometry/determinism.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAoC,MAAM,iBAAiB,CAAC;AAuB7E;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAc,EACd,QAAuC,EACvC,QAAiC;IAEjC,MAAM,OAAO,GAAwB,EAAE,CAAC;IACxC,IAAI,KAAK,GAAG,IAAI,CAAC;IACjB,MAAM,gBAAgB,GAAG,QAAQ,KAAK,SAAS,CAAC;IAChD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,EAAW,CAAC;QAChB,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,EAAE,GAAG,IAAI,CAAC;QACZ,CAAC;aAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,gEAAgE;YAChE,8BAA8B;YAC9B,EAAE,GAAG,KAAK,CAAC;QACb,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,IAAI,KAAK,GAAG,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,EAAE;YAAE,KAAK,GAAG,KAAK,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAkC;IAC7D;QACE,IAAI,EAAE,WAAW;QACjB,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;KAC3C;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,MAAM,EAAE,UAAU;QAClB,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;KAC/C;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,MAAM,EAAE,qBAAqB;QAC7B,MAAM,EAAE;YACN,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;YACnD,KAAK,EAAE,CAAC;SACT;KACF;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,MAAM,EAAE,qBAAqB;QAC7B,MAAM,EAAE;YACN,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;YACpD,KAAK,EAAE,CAAC;SACT;KACF;IACD;QACE,IAAI,EAAE,oBAAoB;QAC1B,MAAM,EAAE,qBAAqB;QAC7B,MAAM,EAAE;YACN,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE;YACtD,KAAK,EAAE,IAAI,CAAC,EAAE,GAAG,CAAC;YAClB,QAAQ,EAAE,EAAE;SACb;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blob garbage collection (open problem #6).
|
|
3
|
+
*
|
|
4
|
+
* Mesh blobs grow without bound during long sessions. This module
|
|
5
|
+
* implements an epoch + reference-count sweep:
|
|
6
|
+
*
|
|
7
|
+
* 1. `collectReferencedBlobHashes(doc)` walks every entity, collects
|
|
8
|
+
* its `geometryRef.geomId`, then resolves each geometry node to
|
|
9
|
+
* its `blobHash`. Returns the set of currently-referenced hashes.
|
|
10
|
+
*
|
|
11
|
+
* 2. `planBlobSweep(store, referenced, opts)` returns the list of
|
|
12
|
+
* blob hashes that exist in `store` but aren't referenced. With
|
|
13
|
+
* `epochMs` set, only blobs older than that grace window are
|
|
14
|
+
* candidates — that's the safety valve against deleting a blob a
|
|
15
|
+
* peer is about to reference but hasn't synced yet.
|
|
16
|
+
*
|
|
17
|
+
* 3. `sweepBlobs(store, decision)` deletes the candidates and reports
|
|
18
|
+
* bytes freed.
|
|
19
|
+
*
|
|
20
|
+
* GC is correct under sync because: each peer makes blob refs visible
|
|
21
|
+
* via the Y.Doc; once the Y.Doc converges across peers, the
|
|
22
|
+
* referenced-set is the same on every peer. The grace window covers
|
|
23
|
+
* the gap between "blob uploaded" and "Y.Doc reference applied."
|
|
24
|
+
*/
|
|
25
|
+
import * as Y from 'yjs';
|
|
26
|
+
import type { BlobHash, BlobMeta, BlobStore } from './blob-store.js';
|
|
27
|
+
/**
|
|
28
|
+
* Collect every blob hash currently referenced from a Y.Doc.
|
|
29
|
+
*
|
|
30
|
+
* An entity's `geometryRef.geomId` points at a `geometry` top-level
|
|
31
|
+
* entry whose `blobHash` (if any) we want to keep. We also include any
|
|
32
|
+
* `blobHash` that appears directly in `geometry.params.<*>` so apps
|
|
33
|
+
* that store auxiliary blob refs in params (e.g. textures) survive
|
|
34
|
+
* gc.
|
|
35
|
+
*/
|
|
36
|
+
export declare function collectReferencedBlobHashes(doc: Y.Doc): Set<BlobHash>;
|
|
37
|
+
export interface SweepOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Grace window in ms — only blobs older than this are candidates.
|
|
40
|
+
* Default 60_000 (1 minute). Set to 0 to sweep aggressively (tests).
|
|
41
|
+
*/
|
|
42
|
+
epochMs?: number;
|
|
43
|
+
/** Override `Date.now` for deterministic tests. */
|
|
44
|
+
now?: () => number;
|
|
45
|
+
}
|
|
46
|
+
export interface SweepDecision {
|
|
47
|
+
drop: BlobHash[];
|
|
48
|
+
/** Bytes that will be reclaimed once `drop` is processed. May be undefined when the store doesn't expose sizes. */
|
|
49
|
+
reclaimBytes: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Plan a blob sweep against `store`, retaining everything in `referenced`.
|
|
53
|
+
*
|
|
54
|
+
* The store's `list()` returns hashes only; size info comes from a
|
|
55
|
+
* `metaProvider` (default: walks `store.get(hash).byteLength`).
|
|
56
|
+
*/
|
|
57
|
+
export declare function planBlobSweep(store: BlobStore, referenced: Set<BlobHash>, options?: SweepOptions & {
|
|
58
|
+
/** Optional fast-path metadata lookup; default streams via `store.get`. */
|
|
59
|
+
metaProvider?: (hash: BlobHash) => Promise<BlobMeta | null>;
|
|
60
|
+
}): Promise<SweepDecision>;
|
|
61
|
+
/** Apply a sweep decision: delete the candidates from `store`. */
|
|
62
|
+
export declare function sweepBlobs(store: BlobStore, decision: SweepDecision): Promise<number>;
|
|
63
|
+
//# sourceMappingURL=gc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gc.d.ts","sourceRoot":"","sources":["../../src/geometry/gc.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAOzB,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErE;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,CAuBrE;AAgBD,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,mHAAmH;IACnH,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,EAChB,UAAU,EAAE,GAAG,CAAC,QAAQ,CAAC,EACzB,OAAO,GAAE,YAAY,GAAG;IACtB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;CACxD,GACL,OAAO,CAAC,aAAa,CAAC,CAsBxB;AAmBD,kEAAkE;AAClE,wBAAsB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ3F"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { ENTITY_KEY, GEOMETRY_KEY, entitiesMap, geometryMap, } from '../doc/schema.js';
|
|
5
|
+
/**
|
|
6
|
+
* Collect every blob hash currently referenced from a Y.Doc.
|
|
7
|
+
*
|
|
8
|
+
* An entity's `geometryRef.geomId` points at a `geometry` top-level
|
|
9
|
+
* entry whose `blobHash` (if any) we want to keep. We also include any
|
|
10
|
+
* `blobHash` that appears directly in `geometry.params.<*>` so apps
|
|
11
|
+
* that store auxiliary blob refs in params (e.g. textures) survive
|
|
12
|
+
* gc.
|
|
13
|
+
*/
|
|
14
|
+
export function collectReferencedBlobHashes(doc) {
|
|
15
|
+
const referenced = new Set();
|
|
16
|
+
const geom = geometryMap(doc);
|
|
17
|
+
const ents = entitiesMap(doc);
|
|
18
|
+
// 1. From every entity's geometryRef → resolve to a geometry node.
|
|
19
|
+
ents.forEach((entUntyped) => {
|
|
20
|
+
const entity = entUntyped;
|
|
21
|
+
const refMap = entity.get(ENTITY_KEY.GEOMETRY_REF);
|
|
22
|
+
const geomId = refMap?.get('geomId');
|
|
23
|
+
if (typeof geomId !== 'string')
|
|
24
|
+
return;
|
|
25
|
+
addBlobHashesFromGeometry(geom.get(geomId), referenced);
|
|
26
|
+
});
|
|
27
|
+
// 2. Also include unreferenced-by-entity geometry entries' blobs —
|
|
28
|
+
// they are reachable via `geometry` and may get a fresh entity ref
|
|
29
|
+
// in the next transaction. We *only* sweep blobs that aren't even
|
|
30
|
+
// in any geometry entry.
|
|
31
|
+
geom.forEach((nodeUntyped) => {
|
|
32
|
+
addBlobHashesFromGeometry(nodeUntyped, referenced);
|
|
33
|
+
});
|
|
34
|
+
return referenced;
|
|
35
|
+
}
|
|
36
|
+
function addBlobHashesFromGeometry(node, out) {
|
|
37
|
+
if (!node)
|
|
38
|
+
return;
|
|
39
|
+
const blobHash = node.get(GEOMETRY_KEY.BLOB_HASH);
|
|
40
|
+
if (typeof blobHash === 'string')
|
|
41
|
+
out.add(blobHash);
|
|
42
|
+
const params = node.get(GEOMETRY_KEY.PARAMS);
|
|
43
|
+
if (!params)
|
|
44
|
+
return;
|
|
45
|
+
params.forEach((value) => {
|
|
46
|
+
if (typeof value === 'string' && /^[a-f0-9]{32}$/.test(value))
|
|
47
|
+
out.add(value);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Plan a blob sweep against `store`, retaining everything in `referenced`.
|
|
52
|
+
*
|
|
53
|
+
* The store's `list()` returns hashes only; size info comes from a
|
|
54
|
+
* `metaProvider` (default: walks `store.get(hash).byteLength`).
|
|
55
|
+
*/
|
|
56
|
+
export async function planBlobSweep(store, referenced, options = {}) {
|
|
57
|
+
const epochMs = options.epochMs ?? 60_000;
|
|
58
|
+
const now = options.now ? options.now() : Date.now();
|
|
59
|
+
const all = await store.list();
|
|
60
|
+
const drop = [];
|
|
61
|
+
let reclaim = 0;
|
|
62
|
+
for (const hash of all) {
|
|
63
|
+
if (referenced.has(hash))
|
|
64
|
+
continue;
|
|
65
|
+
const meta = options.metaProvider
|
|
66
|
+
? await options.metaProvider(hash)
|
|
67
|
+
: await metaFromGet(store, hash);
|
|
68
|
+
if (!meta)
|
|
69
|
+
continue;
|
|
70
|
+
const ageMs = meta.uploadedAt != null
|
|
71
|
+
? Math.max(0, now - new Date(meta.uploadedAt).getTime())
|
|
72
|
+
: Number.POSITIVE_INFINITY;
|
|
73
|
+
if (ageMs >= epochMs) {
|
|
74
|
+
drop.push(hash);
|
|
75
|
+
reclaim += meta.byteLength;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { drop, reclaimBytes: reclaim };
|
|
79
|
+
}
|
|
80
|
+
async function metaFromGet(store, hash) {
|
|
81
|
+
// Prefer cheap stat() if the backend offers it (size + uploadedAt
|
|
82
|
+
// without paying for the bytes).
|
|
83
|
+
if (typeof store.stat === 'function') {
|
|
84
|
+
const meta = await store.stat(hash);
|
|
85
|
+
if (meta)
|
|
86
|
+
return meta;
|
|
87
|
+
}
|
|
88
|
+
const bytes = await store.get(hash);
|
|
89
|
+
if (!bytes)
|
|
90
|
+
return null;
|
|
91
|
+
return {
|
|
92
|
+
hash,
|
|
93
|
+
byteLength: bytes.byteLength,
|
|
94
|
+
// No uploadedAt available via raw bytes — without it, the blob is
|
|
95
|
+
// treated as unbounded-old (always eligible).
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/** Apply a sweep decision: delete the candidates from `store`. */
|
|
99
|
+
export async function sweepBlobs(store, decision) {
|
|
100
|
+
let freed = 0;
|
|
101
|
+
for (const hash of decision.drop) {
|
|
102
|
+
const ok = await store.delete(hash);
|
|
103
|
+
if (ok)
|
|
104
|
+
freed += 1;
|
|
105
|
+
}
|
|
106
|
+
void freed;
|
|
107
|
+
return decision.reclaimBytes;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=gc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gc.js","sourceRoot":"","sources":["../../src/geometry/gc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/geometry/index.ts"],"names":[],"mappings":"AAIA,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
export * from './blob-store.js';
|
|
5
|
+
export * from './csg.js';
|
|
6
|
+
export * from './gc.js';
|
|
7
|
+
export * from './parametric.js';
|
|
8
|
+
export * from './determinism.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/geometry/index.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parametric mesh primitives (spec §11.2 — v0.3 reference impl).
|
|
3
|
+
*
|
|
4
|
+
* The Rust `ifc-lite-geometry` kernel does the production work.
|
|
5
|
+
* Until the kernel is wired into the collab pipeline, this module
|
|
6
|
+
* ships a pure-TypeScript reference implementation for the most
|
|
7
|
+
* common parametric primitives:
|
|
8
|
+
*
|
|
9
|
+
* - extruded-area-solid (rectangle + circle profiles)
|
|
10
|
+
* - box (length × width × height)
|
|
11
|
+
* - cylinder (radius, height)
|
|
12
|
+
* - revolved-area-solid (circle profile around an axis)
|
|
13
|
+
*
|
|
14
|
+
* Output is deterministic given identical params. Hashing the
|
|
15
|
+
* `Mesh` (positions + indices) gives the same bytes on every machine
|
|
16
|
+
* because we use only deterministic float arithmetic — no Math.random,
|
|
17
|
+
* no transcendentals beyond Math.cos/sin which are IEEE-754 specified
|
|
18
|
+
* but not strictly reproducible across architectures. For tests we
|
|
19
|
+
* accept the small chance of cross-platform drift; the
|
|
20
|
+
* `determinism` harness flags it explicitly so consumers know when to
|
|
21
|
+
* fall back to mesh-blob upload.
|
|
22
|
+
*/
|
|
23
|
+
export type ParametricSource = 'extruded-area-solid' | 'box' | 'cylinder' | 'revolved-area-solid';
|
|
24
|
+
export interface RectProfile {
|
|
25
|
+
type: 'rectangle';
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
}
|
|
29
|
+
export interface CircleProfile {
|
|
30
|
+
type: 'circle';
|
|
31
|
+
radius: number;
|
|
32
|
+
/** Number of segments for circle tessellation. Default 16. */
|
|
33
|
+
segments?: number;
|
|
34
|
+
}
|
|
35
|
+
export type Profile = RectProfile | CircleProfile;
|
|
36
|
+
export interface ExtrudedAreaSolidParams {
|
|
37
|
+
profile: Profile;
|
|
38
|
+
/** Extrusion direction (unit-ish). Default [0, 0, 1]. */
|
|
39
|
+
direction?: [number, number, number];
|
|
40
|
+
/** Extrusion depth. */
|
|
41
|
+
depth: number;
|
|
42
|
+
}
|
|
43
|
+
export interface BoxParams {
|
|
44
|
+
length: number;
|
|
45
|
+
width: number;
|
|
46
|
+
height: number;
|
|
47
|
+
}
|
|
48
|
+
export interface CylinderParams {
|
|
49
|
+
radius: number;
|
|
50
|
+
height: number;
|
|
51
|
+
segments?: number;
|
|
52
|
+
}
|
|
53
|
+
export interface RevolvedAreaSolidParams {
|
|
54
|
+
profile: CircleProfile;
|
|
55
|
+
/** Axis to revolve around. Default [0, 0, 1] (Z axis). */
|
|
56
|
+
axis?: [number, number, number];
|
|
57
|
+
/** Total revolution in radians. Default 2π. */
|
|
58
|
+
angle?: number;
|
|
59
|
+
/** Number of segments around the axis. Default 16. */
|
|
60
|
+
segments?: number;
|
|
61
|
+
}
|
|
62
|
+
export interface Mesh {
|
|
63
|
+
positions: Float32Array;
|
|
64
|
+
indices: Uint32Array;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build a mesh from `(source, params)`. Throws if the source is not
|
|
68
|
+
* recognized — the conflict detector / kernel hookup should fall back
|
|
69
|
+
* to mesh-blob upload in that case.
|
|
70
|
+
*/
|
|
71
|
+
export declare function paramsToMesh(source: ParametricSource, params: unknown): Mesh;
|
|
72
|
+
export declare function extrudedAreaSolid(params: ExtrudedAreaSolidParams): Mesh;
|
|
73
|
+
export declare function box(params: BoxParams): Mesh;
|
|
74
|
+
export declare function cylinder(params: CylinderParams): Mesh;
|
|
75
|
+
export declare function revolvedAreaSolid(params: RevolvedAreaSolidParams): Mesh;
|
|
76
|
+
/**
|
|
77
|
+
* Hash a mesh for determinism / cache-key purposes.
|
|
78
|
+
*
|
|
79
|
+
* 128-bit digest built by running FNV-1a four times in parallel with
|
|
80
|
+
* different IVs over a stable byte ordering of positions+indices.
|
|
81
|
+
* Returned as 32 hex chars. Position floats are quantized to 8 decimal
|
|
82
|
+
* places before hashing so trivial floating-point drift doesn't break
|
|
83
|
+
* cache lookups.
|
|
84
|
+
*
|
|
85
|
+
* Note: this is not a cryptographic hash — collision-resistance is
|
|
86
|
+
* "good enough for cache keys and drift detection," not "good enough
|
|
87
|
+
* for security." The previous implementation returned `.repeat(4)` of
|
|
88
|
+
* a single 32-bit digest, which looked 128-bit but had only 32 bits of
|
|
89
|
+
* entropy; the four-IV variant restores real width.
|
|
90
|
+
*/
|
|
91
|
+
export declare function hashMesh(mesh: Mesh): string;
|
|
92
|
+
//# sourceMappingURL=parametric.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parametric.d.ts","sourceRoot":"","sources":["../../src/geometry/parametric.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,MAAM,gBAAgB,GACxB,qBAAqB,GACrB,KAAK,GACL,UAAU,GACV,qBAAqB,CAAC;AAE1B,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,MAAM,OAAO,GAAG,WAAW,GAAG,aAAa,CAAC;AAElD,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,yDAAyD;IACzD,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,aAAa,CAAC;IACvB,0DAA0D;IAC1D,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,IAAI;IACnB,SAAS,EAAE,YAAY,CAAC;IACxB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAa5E;AAMD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAuCvE;AA0BD,wBAAgB,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAM3C;AAMD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAMrD;AAMD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAwDvE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CA4B3C"}
|