koota-kit 0.8.6 → 0.8.8

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,195 @@
1
+ export class NumericalOracleRangeError extends RangeError {
2
+ constructor() {
3
+ super("proof: numerical oracle range exceeded");
4
+ this.name = "NumericalOracleRangeError";
5
+ }
6
+ }
7
+ const MIN_NORMAL = 2 ** -1022;
8
+ const checked = (value) => {
9
+ if (!Number.isFinite(value) || (value !== 0 && Math.abs(value) < MIN_NORMAL))
10
+ throw new NumericalOracleRangeError();
11
+ return value;
12
+ };
13
+ const sum = (...values) => values.reduce((total, value) => checked(total + value), 0);
14
+ const product = (left, right) => {
15
+ const value = checked(left * right);
16
+ if (value === 0 && left !== 0 && right !== 0) throw new NumericalOracleRangeError();
17
+ return value;
18
+ };
19
+ const quotient = (numerator, denominator) => {
20
+ if (denominator === 0) throw new NumericalOracleRangeError();
21
+ const value = checked(numerator / denominator);
22
+ if (value === 0 && numerator !== 0) throw new NumericalOracleRangeError();
23
+ return value;
24
+ };
25
+ const point = (x, y, z) => Object.freeze({ x: checked(x), y: checked(y), z: checked(z) });
26
+ const sub = (a, b) => [checked(a.x - b.x), checked(a.y - b.y), checked(a.z - b.z)];
27
+ const dot = (a, b) => sum(product(a[0], b[0]), product(a[1], b[1]), product(a[2], b[2]));
28
+
29
+ export const IDENTITY = Object.freeze([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
30
+
31
+ /** Column-major affine transformation, applied to every vertex before ranking. */
32
+ export function worldPositions(positions, matrix) {
33
+ if (
34
+ matrix.length !== 16 ||
35
+ matrix.some((value) => !Number.isFinite(value)) ||
36
+ matrix[3] !== 0 ||
37
+ matrix[7] !== 0 ||
38
+ matrix[11] !== 0 ||
39
+ matrix[15] !== 1
40
+ ) {
41
+ throw new Error("proof: expected a finite column-major affine transform");
42
+ }
43
+ const output = new Float64Array(positions.length);
44
+ for (let i = 0; i < positions.length; i += 3) {
45
+ const [x, y, z] = positions.subarray(i, i + 3);
46
+ output[i] = sum(
47
+ product(matrix[0], x),
48
+ product(matrix[4], y),
49
+ product(matrix[8], z),
50
+ matrix[12],
51
+ );
52
+ output[i + 1] = sum(
53
+ product(matrix[1], x),
54
+ product(matrix[5], y),
55
+ product(matrix[9], z),
56
+ matrix[13],
57
+ );
58
+ output[i + 2] = sum(
59
+ product(matrix[2], x),
60
+ product(matrix[6], y),
61
+ product(matrix[10], z),
62
+ matrix[14],
63
+ );
64
+ }
65
+ if (output.some((value) => !Number.isFinite(value))) throw new Error("proof: transform overflow");
66
+ return output;
67
+ }
68
+
69
+ export function indexedBounds(positions, indices) {
70
+ if (indices.length === 0) throw new Error("proof: empty topology has no certified bounds");
71
+ const min = [Infinity, Infinity, Infinity];
72
+ const max = [-Infinity, -Infinity, -Infinity];
73
+ for (const index of indices) {
74
+ for (let axis = 0; axis < 3; axis += 1) {
75
+ min[axis] = Math.min(min[axis], positions[index * 3 + axis]);
76
+ max[axis] = Math.max(max[axis], positions[index * 3 + axis]);
77
+ }
78
+ }
79
+ return Object.freeze({ min: point(...min), max: point(...max) });
80
+ }
81
+
82
+ // Bounded reference oracle only; deliberately exhaustive, with no cache or BVH.
83
+ function closestTriangle(p, a, b, c) {
84
+ const ab = sub(b, a),
85
+ ac = sub(c, a),
86
+ ap = sub(p, a);
87
+ const d1 = dot(ab, ap),
88
+ d2 = dot(ac, ap);
89
+ if (d1 <= 0 && d2 <= 0) return [1, 0, 0];
90
+ const bp = sub(p, b),
91
+ d3 = dot(ab, bp),
92
+ d4 = dot(ac, bp);
93
+ if (d3 >= 0 && d4 <= d3) return [0, 1, 0];
94
+ const vc = checked(product(d1, d4) - product(d3, d2));
95
+ if (vc <= 0 && d1 >= 0 && d3 <= 0) {
96
+ const v = quotient(d1, checked(d1 - d3));
97
+ return [checked(1 - v), v, 0];
98
+ }
99
+ const cp = sub(p, c),
100
+ d5 = dot(ab, cp),
101
+ d6 = dot(ac, cp);
102
+ if (d6 >= 0 && d5 <= d6) return [0, 0, 1];
103
+ const vb = checked(product(d5, d2) - product(d1, d6));
104
+ if (vb <= 0 && d2 >= 0 && d6 <= 0) {
105
+ const w = quotient(d2, checked(d2 - d6));
106
+ return [checked(1 - w), 0, w];
107
+ }
108
+ const va = checked(product(d3, d6) - product(d5, d4));
109
+ if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) {
110
+ const numerator = checked(d4 - d3);
111
+ const w = quotient(numerator, sum(numerator, checked(d5 - d6)));
112
+ return [0, checked(1 - w), w];
113
+ }
114
+ const denominator = sum(va, vb, vc);
115
+ if (denominator <= 0) throw new NumericalOracleRangeError();
116
+ const inverse = quotient(1, denominator),
117
+ v = product(vb, inverse),
118
+ w = product(vc, inverse);
119
+ return [sum(1, -v, -w), v, w];
120
+ }
121
+
122
+ export function nearestOpenSurface(positions, indices, query, sourceFaceIds) {
123
+ let best;
124
+ for (let offset = 0; offset < indices.length; offset += 3) {
125
+ const vertices = [0, 1, 2].map((corner) => {
126
+ const i = indices[offset + corner] * 3;
127
+ return point(positions[i], positions[i + 1], positions[i + 2]);
128
+ });
129
+ const [a, b, c] = vertices,
130
+ ab = sub(b, a),
131
+ ac = sub(c, a);
132
+ const cross = [
133
+ checked(product(ab[1], ac[2]) - product(ab[2], ac[1])),
134
+ checked(product(ab[2], ac[0]) - product(ab[0], ac[2])),
135
+ checked(product(ab[0], ac[1]) - product(ab[1], ac[0])),
136
+ ];
137
+ const area = checked(Math.hypot(...cross));
138
+ let barycentric;
139
+ if (area === 0) {
140
+ const candidates = [
141
+ [0, 1],
142
+ [1, 2],
143
+ [2, 0],
144
+ ].map(([start, end]) => {
145
+ const edge = sub(vertices[end], vertices[start]);
146
+ const length = dot(edge, edge);
147
+ const t =
148
+ length === 0
149
+ ? 0
150
+ : Math.max(0, Math.min(1, quotient(dot(sub(query, vertices[start]), edge), length)));
151
+ const weights = [0, 0, 0];
152
+ weights[start] = checked(1 - t);
153
+ weights[end] = t;
154
+ const projected = point(
155
+ ...["x", "y", "z"].map((axis) =>
156
+ sum(
157
+ vertices[start][axis],
158
+ product(t, checked(vertices[end][axis] - vertices[start][axis])),
159
+ ),
160
+ ),
161
+ );
162
+ return { weights, distance: checked(Math.hypot(...sub(query, projected))) };
163
+ });
164
+ candidates.sort((left, right) => left.distance - right.distance);
165
+ barycentric = candidates[0].weights;
166
+ } else {
167
+ // Gram-region classification squares the aspect ratio. Below this
168
+ // conservative roundoff margin, this bounded oracle cannot certify it.
169
+ const longestEdge = checked(
170
+ Math.max(Math.hypot(...ab), Math.hypot(...ac), Math.hypot(...sub(c, b))),
171
+ );
172
+ const quality = quotient(quotient(area, longestEdge), longestEdge);
173
+ if (quality <= Math.sqrt(64 * Number.EPSILON)) throw new NumericalOracleRangeError();
174
+ barycentric = closestTriangle(query, a, b, c);
175
+ }
176
+ const hit = point(
177
+ ...["x", "y", "z"].map((axis) =>
178
+ sum(...vertices.map((vertex, corner) => product(vertex[axis], barycentric[corner]))),
179
+ ),
180
+ );
181
+ const distance = checked(Math.hypot(...sub(query, hit)));
182
+ const face = sourceFaceIds?.[offset / 3] ?? offset / 3;
183
+ if (!best || distance < best.distance || (distance === best.distance && face < best.face)) {
184
+ best = Object.freeze({
185
+ face,
186
+ point: hit,
187
+ distance,
188
+ barycentric: Object.freeze(barycentric),
189
+ geometricNormal:
190
+ area === 0 ? undefined : point(...cross.map((value) => quotient(value, area))),
191
+ });
192
+ }
193
+ }
194
+ return best;
195
+ }
@@ -0,0 +1,82 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ import { loadFigureFixture } from "./fixture.mjs";
5
+ import { IDENTITY, indexedBounds, nearestOpenSurface, worldPositions } from "./open-surface.mjs";
6
+
7
+ const directory = process.argv[2];
8
+ if (!directory)
9
+ throw new Error(
10
+ "Usage: node examples/coordination-proof/prepare.mjs <fixture-directory> [receipt.json]",
11
+ );
12
+ const figure = await loadFigureFixture(directory);
13
+ const report = {
14
+ scope: "copied-buffer/open-surface preparation; no live producer or rendered parity claim",
15
+ inputs: figure.inputs,
16
+ parts: [],
17
+ };
18
+ for (const part of figure.parts) {
19
+ assert.equal(part.poses.relaxed.positions.length, part.rest.length);
20
+ assert.notDeepEqual(part.poses.relaxed.positions, part.poses.overhead.positions);
21
+ const before = part.poses.relaxed.positions[0];
22
+ const copied = new Float32Array(part.poses.relaxed.positions);
23
+ copied[0] += 1;
24
+ assert.equal(part.poses.relaxed.positions[0], before);
25
+ const positions = worldPositions(part.poses.relaxed.positions, IDENTITY);
26
+ const bounds = indexedBounds(positions, part.full);
27
+ const face = 0;
28
+ const corners = [0, 1, 2].map((corner) => part.full[face * 3 + corner] * 3);
29
+ const centroid = Object.fromEntries(
30
+ ["x", "y", "z"].map((axis, index) => [
31
+ axis,
32
+ corners.reduce((sum, offset) => sum + positions[offset + index], 0) / 3,
33
+ ]),
34
+ );
35
+ const witness = nearestOpenSurface(positions, part.full, centroid);
36
+ assert.ok(witness.distance < 1e-12);
37
+ assert.ok(Math.abs(witness.barycentric.reduce((sum, value) => sum + value, 0) - 1) < 1e-12);
38
+ let hiddenWitness;
39
+ if (part.full.length !== part.visible.length) {
40
+ const visibleIds = new Set(part.visibleFaceIds);
41
+ const hiddenFace = Array.from({ length: part.full.length / 3 }, (_, face) => face).find(
42
+ (face) => !visibleIds.has(face),
43
+ );
44
+ const offsets = [0, 1, 2].map((corner) => part.full[hiddenFace * 3 + corner] * 3);
45
+ const point = Object.fromEntries(
46
+ ["x", "y", "z"].map((axis, index) => [
47
+ axis,
48
+ offsets.reduce((sum, offset) => sum + positions[offset + index], 0) / 3,
49
+ ]),
50
+ );
51
+ const full = nearestOpenSurface(positions, part.full, point);
52
+ const visible = nearestOpenSurface(positions, part.visible, point, part.visibleFaceIds);
53
+ assert.ok(full.distance < 1e-12);
54
+ assert.ok(visible.distance > 1e-12);
55
+ hiddenWitness = { hiddenFace, point, full, visible };
56
+ }
57
+ report.parts.push({
58
+ id: part.id,
59
+ fullTriangles: part.full.length / 3,
60
+ visibleTriangles: part.visible.length / 3,
61
+ hiddenTriangles: (part.full.length - part.visible.length) / 3,
62
+ bounds,
63
+ witness,
64
+ hiddenWitness,
65
+ grounding: "posed input bytes used unchanged; no additional lift",
66
+ });
67
+ }
68
+ assert.equal(report.parts[0].fullTriangles, 107024);
69
+ assert.equal(report.parts[0].visibleTriangles, 80112);
70
+ assert.equal(report.parts[1].fullTriangles, 66688);
71
+ assert.equal(report.parts[1].visibleTriangles, 66688);
72
+ if (process.argv[3]) {
73
+ await mkdir(dirname(process.argv[3]), { recursive: true });
74
+ await writeFile(process.argv[3], `${JSON.stringify(report, null, 2)}\n`);
75
+ }
76
+ console.log(
77
+ JSON.stringify(
78
+ { scope: report.scope, inputCount: report.inputs.length, parts: report.parts },
79
+ null,
80
+ 2,
81
+ ),
82
+ );
@@ -0,0 +1,69 @@
1
+ import assert from "node:assert/strict";
2
+
3
+ /** A headless mounted observer; neither the component nor its unmount owns the World. */
4
+ export async function observeCommittedUpdate({ registryImport, cohort, world, publish }) {
5
+ const React = await registryImport("react");
6
+ const { createRoot } = await registryImport("react-dom/client");
7
+ const { WorldProvider, useWorld } = await registryImport("koota/react");
8
+ const { JSDOM } = await registryImport("jsdom");
9
+ const dom = new JSDOM("<!doctype html><html><body><div id='proof'></div></body></html>");
10
+ const descriptors = new Map();
11
+ for (const [name, value] of Object.entries({
12
+ window: dom.window,
13
+ document: dom.window.document,
14
+ IS_REACT_ACT_ENVIRONMENT: true,
15
+ })) {
16
+ descriptors.set(name, Object.getOwnPropertyDescriptor(globalThis, name));
17
+ Object.defineProperty(globalThis, name, { configurable: true, writable: true, value });
18
+ }
19
+ const observations = [];
20
+ const container = dom.window.document.getElementById("proof");
21
+ const root = createRoot(container);
22
+ function Inspector() {
23
+ assert.equal(useWorld(), world);
24
+ const snapshot = React.useSyncExternalStore(
25
+ cohort.subscribe,
26
+ cohort.getSnapshot,
27
+ cohort.getSnapshot,
28
+ );
29
+ observations.push(snapshot);
30
+ return React.createElement(
31
+ "output",
32
+ null,
33
+ snapshot.coordination.entries
34
+ .map((entry) => `${entry.id}:${entry.completed?.revisions.pose ?? "pending"}`)
35
+ .join(","),
36
+ );
37
+ }
38
+ let unmounted = false;
39
+ try {
40
+ await React.act(async () =>
41
+ root.render(React.createElement(WorldProvider, { world }, React.createElement(Inspector))),
42
+ );
43
+ const before = container.textContent;
44
+ const previous = cohort.getSnapshot();
45
+ await React.act(async () => publish());
46
+ assert.notEqual(cohort.getSnapshot(), previous);
47
+ assert.equal(observations.at(-1), cohort.getSnapshot());
48
+ const after = container.textContent;
49
+ assert.notEqual(after, before);
50
+ await React.act(async () => root.unmount());
51
+ unmounted = true;
52
+ assert.ok(world.isInitialized);
53
+ return {
54
+ before,
55
+ after,
56
+ renderCount: observations.length,
57
+ sameWorld: true,
58
+ unmountPreservedWorld: true,
59
+ };
60
+ } finally {
61
+ // Clean up an assertion failure before the normal unmount.
62
+ if (!unmounted) await React.act(async () => root.unmount());
63
+ dom.window.close();
64
+ for (const [name, descriptor] of descriptors) {
65
+ if (descriptor) Object.defineProperty(globalThis, name, descriptor);
66
+ else delete globalThis[name];
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,3 @@
1
+ // Copy beside the private consumer package.json so all imports resolve from its registry install.
2
+ export const registryImport = (specifier) => import(specifier);
3
+ export const registryResolve = (specifier) => import.meta.resolve(specifier);