digital-boardgame-framework 0.8.2 → 0.9.1

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,150 @@
1
+ export interface Point {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ /** A simple closed ring. First point need not repeat the last. */
6
+ export type Polygon = Point[];
7
+ export interface BBox {
8
+ minX: number;
9
+ minY: number;
10
+ maxX: number;
11
+ maxY: number;
12
+ }
13
+ export declare function boundingBox(polygon: Polygon): BBox;
14
+ /** Signed area (shoelace). Positive for counter-clockwise rings. */
15
+ export declare function signedArea(polygon: Polygon): number;
16
+ export declare function area(polygon: Polygon): number;
17
+ /** Area-weighted polygon centroid. NOTE: for a concave shape this can fall
18
+ * outside the polygon — do not use it to anchor tokens. Use
19
+ * poleOfInaccessibility instead. Kept for comparison / degenerate fallback. */
20
+ export declare function centroid(polygon: Polygon): Point;
21
+ export declare function pointInPolygon(point: Point, polygon: Polygon): boolean;
22
+ /** Signed distance from a point to the polygon boundary: positive inside,
23
+ * negative outside, magnitude = distance to the nearest edge. */
24
+ export declare function signedDistanceToPolygon(point: Point, polygon: Polygon): number;
25
+ export interface PoleResult {
26
+ /** The anchor point. */
27
+ point: Point;
28
+ /** Radius of the largest circle centred at `point` that fits inside the
29
+ * polygon — the natural size budget for a token stack before it bleeds. */
30
+ clearance: number;
31
+ }
32
+ /** The interior point farthest from any edge — the "visual centre" used for map
33
+ * labels — together with its clearance (the largest inscribed-circle radius at
34
+ * that point). Always inside the polygon (for a valid simple polygon) and in
35
+ * its thickest part. This is the correct anchor for a token cluster.
36
+ *
37
+ * `precision` is in COORDINATE UNITS: smaller = more accurate, slower. The
38
+ * default 1.0 assumes pixel-scale coordinates (≈ one pixel). For normalized
39
+ * 0..1 polygons, 1.0 is larger than the whole shape and the search won't
40
+ * subdivide — pass ~1e-3 instead. */
41
+ export declare function poleOfInaccessibilityWithClearance(polygon: Polygon, precision?: number): PoleResult;
42
+ /** The anchor point only. See poleOfInaccessibilityWithClearance for the
43
+ * clearance radius (and the `precision` note — for normalized 0..1 coordinates
44
+ * pass ~1e-3, not the pixel-scale default of 1.0). */
45
+ export declare function poleOfInaccessibility(polygon: Polygon, precision?: number): Point;
46
+ export interface LayoutOptions {
47
+ /** Radius of one token, in coordinate units. */
48
+ tokenRadius: number;
49
+ /** Extra spacing between adjacent tokens (default tokenRadius * 0.3). */
50
+ gap?: number;
51
+ /** Smallest scale to shrink the cluster to before collapsing to a stack
52
+ * (default 0.4). */
53
+ minScale?: number;
54
+ /** Precision passed to poleOfInaccessibility (default 1.0). For normalized
55
+ * 0..1 coordinates pass ~1e-3, not the pixel-scale default. */
56
+ precision?: number;
57
+ /** Precomputed anchor — skip the pole-of-inaccessibility pass. Board geometry
58
+ * is static, so a renderer can compute the anchor once per region and pass it
59
+ * on every re-layout instead of recomputing it each frame. */
60
+ anchor?: Point;
61
+ }
62
+ export interface TokenLayout {
63
+ /** The polygon's visual centre — the cluster anchor / stack location. */
64
+ anchor: Point;
65
+ /** One point per token, all inside the polygon. When `stacked` is true this
66
+ * holds a single point (the anchor) and the renderer should draw a pile. */
67
+ points: Point[];
68
+ /** Scale applied to fit the cluster (1 = full size, smaller = shrunk). */
69
+ scale: number;
70
+ /** True when the cluster couldn't fit even at minScale — render a single
71
+ * anchored pile with a count badge instead of N separate tokens. */
72
+ stacked: boolean;
73
+ }
74
+ /** Lay out `count` tokens inside `polygon`, clustered at its visual centre and
75
+ * kept within the boundary. Shrinks the cluster to fit; if even the shrunk
76
+ * cluster won't fit, returns a single anchored point with `stacked: true` so
77
+ * the renderer can draw a pile + count badge. Deterministic. */
78
+ export declare function layoutTokensInPolygon(polygon: Polygon, count: number, opts: LayoutOptions): TokenLayout;
79
+ export declare function toPolygon(pairs: ReadonlyArray<readonly [number, number]>): Polygon;
80
+ export declare function fromPolygon(polygon: Polygon): [number, number][];
81
+ export declare function distance(a: Point, b: Point): number;
82
+ export interface AuditEntry {
83
+ id: string;
84
+ polygon: Polygon;
85
+ count: number;
86
+ }
87
+ export interface AuditResult {
88
+ regions: number;
89
+ /** Region ids whose anchor fell outside the polygon (should be empty). */
90
+ anchorsOutside: string[];
91
+ /** Total tokens placed outside their polygon (should be 0). */
92
+ tokenBleed: number;
93
+ /** Region ids that collapsed to a stacked pile. */
94
+ stackedPiles: string[];
95
+ /** Smallest anchor clearance across all regions. */
96
+ minClearance: number;
97
+ /** Region id with the smallest clearance. */
98
+ tightestRegion: string | null;
99
+ }
100
+ /** Headless verification of a whole map's token layout — the CI-able core of the
101
+ * per-game audit overlay (the visual page stays per-game; this is the math it
102
+ * and CI both call). Lays out each region and checks containment. */
103
+ export declare function auditLayout(entries: AuditEntry[], opts: LayoutOptions): AuditResult;
104
+ /** Approximate intersection-over-union of two polygons via grid sampling over
105
+ * the combined bounding box. Robust to concavity (no polygon clipping).
106
+ * `samples` is the grid resolution per axis (default 64 → 4096 test points).
107
+ * Deterministic. */
108
+ export declare function polygonIoU(a: Polygon, b: Polygon, samples?: number): number;
109
+ export type Edge = [string, string];
110
+ /** Undirected adjacency derived from polygon geometry: two regions are adjacent
111
+ * when their boundaries come within `tolerance`. Rough/hand-traced polygons
112
+ * rarely share exact edges, so a tolerance is required. O(n²) with a bbox
113
+ * pre-filter — fine for a few hundred regions run occasionally (validation, not
114
+ * a hot path). Returns sorted, de-duplicated, normalized edges. */
115
+ export declare function adjacencyFromPolygons(polygons: Record<string, Polygon>, tolerance: number): Edge[];
116
+ export interface AdjacencyDiff {
117
+ shared: Edge[];
118
+ /** Edges in A but not B. */
119
+ onlyA: Edge[];
120
+ /** Edges in B but not A. */
121
+ onlyB: Edge[];
122
+ }
123
+ /** Set-diff two undirected edge lists (edge endpoint order is normalized). Use
124
+ * to compare polygon-derived adjacency against the engine's known adjacency —
125
+ * the strongest, roughness-immune validation signal. */
126
+ export declare function diffAdjacency(a: Edge[], b: Edge[]): AdjacencyDiff;
127
+ export interface RegionMatch {
128
+ a: string;
129
+ b: string;
130
+ iou: number;
131
+ anchorDistance: number;
132
+ }
133
+ export interface RegionMatchResult {
134
+ matches: RegionMatch[];
135
+ /** Region ids in A with no B match above minIoU (a split, or missing in B). */
136
+ unmatchedA: string[];
137
+ /** Region ids in B never matched (a merge, or extra in B). */
138
+ unmatchedB: string[];
139
+ }
140
+ /** Match two named polygon sets (e.g. extracted vs a human reference) by best
141
+ * IoU. The roughness-tolerant comparison: use it to confirm a 1:1 region
142
+ * correspondence and flag merges/splits — NOT to score vertex precision. Greedy
143
+ * per A-region. `minIoU` is the matching threshold (default 0.5); keep it low,
144
+ * because a rough reference legitimately overlaps a precise extraction at well
145
+ * under 1.0. */
146
+ export declare function matchRegions(a: Record<string, Polygon>, b: Record<string, Polygon>, opts?: {
147
+ minIoU?: number;
148
+ samples?: number;
149
+ }): RegionMatchResult;
150
+ //# sourceMappingURL=geo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo.d.ts","sourceRoot":"","sources":["../../src/core/geo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,419 @@
1
+ // Pure 2D polygon geometry for map games — no DOM, no rendering. Territory
2
+ // polygons live per-game (the data); this module is the shared math that turns a
3
+ // polygon + a token count into where the tokens go, anchored at the polygon's
4
+ // visual centre and kept inside its boundary.
5
+ //
6
+ // Coordinates are in whatever space the caller uses (typically pixels in the map
7
+ // image). Everything here is deterministic — no RNG — so the same state always
8
+ // lays out identically. See docs/territory-polygons-design.md.
9
+ export function boundingBox(polygon) {
10
+ if (polygon.length === 0)
11
+ throw new Error('boundingBox: empty polygon');
12
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
13
+ for (const p of polygon) {
14
+ if (p.x < minX)
15
+ minX = p.x;
16
+ if (p.y < minY)
17
+ minY = p.y;
18
+ if (p.x > maxX)
19
+ maxX = p.x;
20
+ if (p.y > maxY)
21
+ maxY = p.y;
22
+ }
23
+ return { minX, minY, maxX, maxY };
24
+ }
25
+ /** Signed area (shoelace). Positive for counter-clockwise rings. */
26
+ export function signedArea(polygon) {
27
+ let sum = 0;
28
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
29
+ sum += (polygon[j].x + polygon[i].x) * (polygon[j].y - polygon[i].y);
30
+ }
31
+ return sum / 2;
32
+ }
33
+ export function area(polygon) {
34
+ return Math.abs(signedArea(polygon));
35
+ }
36
+ /** Area-weighted polygon centroid. NOTE: for a concave shape this can fall
37
+ * outside the polygon — do not use it to anchor tokens. Use
38
+ * poleOfInaccessibility instead. Kept for comparison / degenerate fallback. */
39
+ export function centroid(polygon) {
40
+ let cx = 0, cy = 0, a = 0;
41
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
42
+ const cross = polygon[j].x * polygon[i].y - polygon[i].x * polygon[j].y;
43
+ cx += (polygon[j].x + polygon[i].x) * cross;
44
+ cy += (polygon[j].y + polygon[i].y) * cross;
45
+ a += cross;
46
+ }
47
+ if (a === 0) {
48
+ // Degenerate (zero area): fall back to the mean of vertices.
49
+ const n = polygon.length;
50
+ return {
51
+ x: polygon.reduce((s, p) => s + p.x, 0) / n,
52
+ y: polygon.reduce((s, p) => s + p.y, 0) / n,
53
+ };
54
+ }
55
+ a *= 3;
56
+ return { x: cx / a, y: cy / a };
57
+ }
58
+ export function pointInPolygon(point, polygon) {
59
+ let inside = false;
60
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
61
+ const a = polygon[i], b = polygon[j];
62
+ if ((a.y > point.y) !== (b.y > point.y) &&
63
+ point.x < ((b.x - a.x) * (point.y - a.y)) / (b.y - a.y) + a.x) {
64
+ inside = !inside;
65
+ }
66
+ }
67
+ return inside;
68
+ }
69
+ function segmentDistSq(p, a, b) {
70
+ let x = a.x, y = a.y;
71
+ let dx = b.x - x, dy = b.y - y;
72
+ if (dx !== 0 || dy !== 0) {
73
+ const t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
74
+ if (t > 1) {
75
+ x = b.x;
76
+ y = b.y;
77
+ }
78
+ else if (t > 0) {
79
+ x += dx * t;
80
+ y += dy * t;
81
+ }
82
+ }
83
+ dx = p.x - x;
84
+ dy = p.y - y;
85
+ return dx * dx + dy * dy;
86
+ }
87
+ /** Signed distance from a point to the polygon boundary: positive inside,
88
+ * negative outside, magnitude = distance to the nearest edge. */
89
+ export function signedDistanceToPolygon(point, polygon) {
90
+ let minSq = Infinity;
91
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
92
+ minSq = Math.min(minSq, segmentDistSq(point, polygon[i], polygon[j]));
93
+ }
94
+ const dist = Math.sqrt(minSq);
95
+ return pointInPolygon(point, polygon) ? dist : -dist;
96
+ }
97
+ function makeCell(x, y, h, polygon) {
98
+ const d = signedDistanceToPolygon({ x, y }, polygon);
99
+ return { x, y, h, d, max: d + h * Math.SQRT2 };
100
+ }
101
+ // Binary max-heap keyed on cell.max.
102
+ class CellHeap {
103
+ items = [];
104
+ get size() { return this.items.length; }
105
+ push(c) {
106
+ const a = this.items;
107
+ a.push(c);
108
+ let i = a.length - 1;
109
+ while (i > 0) {
110
+ const parent = (i - 1) >> 1;
111
+ if (a[parent].max >= a[i].max)
112
+ break;
113
+ [a[parent], a[i]] = [a[i], a[parent]];
114
+ i = parent;
115
+ }
116
+ }
117
+ pop() {
118
+ const a = this.items;
119
+ const top = a[0];
120
+ const last = a.pop();
121
+ if (a.length > 0) {
122
+ a[0] = last;
123
+ let i = 0;
124
+ for (;;) {
125
+ const l = 2 * i + 1, r = 2 * i + 2;
126
+ let largest = i;
127
+ if (l < a.length && a[l].max > a[largest].max)
128
+ largest = l;
129
+ if (r < a.length && a[r].max > a[largest].max)
130
+ largest = r;
131
+ if (largest === i)
132
+ break;
133
+ [a[largest], a[i]] = [a[i], a[largest]];
134
+ i = largest;
135
+ }
136
+ }
137
+ return top;
138
+ }
139
+ }
140
+ /** The interior point farthest from any edge — the "visual centre" used for map
141
+ * labels — together with its clearance (the largest inscribed-circle radius at
142
+ * that point). Always inside the polygon (for a valid simple polygon) and in
143
+ * its thickest part. This is the correct anchor for a token cluster.
144
+ *
145
+ * `precision` is in COORDINATE UNITS: smaller = more accurate, slower. The
146
+ * default 1.0 assumes pixel-scale coordinates (≈ one pixel). For normalized
147
+ * 0..1 polygons, 1.0 is larger than the whole shape and the search won't
148
+ * subdivide — pass ~1e-3 instead. */
149
+ export function poleOfInaccessibilityWithClearance(polygon, precision = 1.0) {
150
+ const bb = boundingBox(polygon);
151
+ const width = bb.maxX - bb.minX;
152
+ const height = bb.maxY - bb.minY;
153
+ const cellSize = Math.min(width, height);
154
+ if (cellSize === 0)
155
+ return { point: { x: bb.minX, y: bb.minY }, clearance: 0 };
156
+ const heap = new CellHeap();
157
+ const h0 = cellSize / 2;
158
+ for (let x = bb.minX; x < bb.maxX; x += cellSize) {
159
+ for (let y = bb.minY; y < bb.maxY; y += cellSize) {
160
+ heap.push(makeCell(x + h0, y + h0, h0, polygon));
161
+ }
162
+ }
163
+ // Seed best with the centroid and the bbox centre.
164
+ const c = centroid(polygon);
165
+ let best = makeCell(c.x, c.y, 0, polygon);
166
+ const bboxCell = makeCell(bb.minX + width / 2, bb.minY + height / 2, 0, polygon);
167
+ if (bboxCell.d > best.d)
168
+ best = bboxCell;
169
+ while (heap.size > 0) {
170
+ const cell = heap.pop();
171
+ if (cell.d > best.d)
172
+ best = cell;
173
+ if (cell.max - best.d <= precision)
174
+ continue;
175
+ const h = cell.h / 2;
176
+ heap.push(makeCell(cell.x - h, cell.y - h, h, polygon));
177
+ heap.push(makeCell(cell.x + h, cell.y - h, h, polygon));
178
+ heap.push(makeCell(cell.x - h, cell.y + h, h, polygon));
179
+ heap.push(makeCell(cell.x + h, cell.y + h, h, polygon));
180
+ }
181
+ return { point: { x: best.x, y: best.y }, clearance: best.d };
182
+ }
183
+ /** The anchor point only. See poleOfInaccessibilityWithClearance for the
184
+ * clearance radius (and the `precision` note — for normalized 0..1 coordinates
185
+ * pass ~1e-3, not the pixel-scale default of 1.0). */
186
+ export function poleOfInaccessibility(polygon, precision = 1.0) {
187
+ return poleOfInaccessibilityWithClearance(polygon, precision).point;
188
+ }
189
+ // Concentric-ring cluster positions around a centre. Ring k holds 6k points, so
190
+ // the cluster grows outward roughly evenly. `spacing` is the ring-radius step.
191
+ function clusterPositions(center, count, spacing) {
192
+ const pts = [{ x: center.x, y: center.y }];
193
+ for (let ring = 1; pts.length < count; ring++) {
194
+ const inRing = 6 * ring;
195
+ for (let i = 0; i < inRing && pts.length < count; i++) {
196
+ const angle = (2 * Math.PI * i) / inRing;
197
+ pts.push({
198
+ x: center.x + ring * spacing * Math.cos(angle),
199
+ y: center.y + ring * spacing * Math.sin(angle),
200
+ });
201
+ }
202
+ }
203
+ return pts;
204
+ }
205
+ function clusterFits(pts, polygon, clearance) {
206
+ for (const p of pts) {
207
+ if (signedDistanceToPolygon(p, polygon) < clearance)
208
+ return false;
209
+ }
210
+ return true;
211
+ }
212
+ /** Lay out `count` tokens inside `polygon`, clustered at its visual centre and
213
+ * kept within the boundary. Shrinks the cluster to fit; if even the shrunk
214
+ * cluster won't fit, returns a single anchored point with `stacked: true` so
215
+ * the renderer can draw a pile + count badge. Deterministic. */
216
+ export function layoutTokensInPolygon(polygon, count, opts) {
217
+ const anchor = opts.anchor ?? poleOfInaccessibility(polygon, opts.precision ?? 1.0);
218
+ const gap = opts.gap ?? opts.tokenRadius * 0.3;
219
+ const minScale = opts.minScale ?? 0.4;
220
+ if (count <= 0)
221
+ return { anchor, points: [], scale: 1, stacked: false };
222
+ if (count === 1) {
223
+ const fits = signedDistanceToPolygon(anchor, polygon) >= opts.tokenRadius;
224
+ return { anchor, points: [anchor], scale: fits ? 1 : minScale, stacked: !fits };
225
+ }
226
+ const step = 2 * opts.tokenRadius + gap;
227
+ // Try full size, then shrink in 0.1 increments down to minScale.
228
+ for (let scale = 1.0; scale >= minScale - 1e-9; scale -= 0.1) {
229
+ const pts = clusterPositions(anchor, count, step * scale);
230
+ if (clusterFits(pts, polygon, opts.tokenRadius * scale)) {
231
+ return { anchor, points: pts, scale: Math.max(scale, minScale), stacked: false };
232
+ }
233
+ }
234
+ // Won't fit even shrunk — collapse to a single anchored pile.
235
+ return { anchor, points: [anchor], scale: minScale, stacked: true };
236
+ }
237
+ // --- conversion helpers ---------------------------------------------------
238
+ // On disk, polygons are [x,y] pairs (clip-path, GeoJSON-ish, VASSAL/SVG exports);
239
+ // in memory the geo functions take {x,y}. These save every consumer re-writing
240
+ // the same adapter.
241
+ export function toPolygon(pairs) {
242
+ return pairs.map(([x, y]) => ({ x, y }));
243
+ }
244
+ export function fromPolygon(polygon) {
245
+ return polygon.map((p) => [p.x, p.y]);
246
+ }
247
+ export function distance(a, b) {
248
+ return Math.hypot(a.x - b.x, a.y - b.y);
249
+ }
250
+ /** Headless verification of a whole map's token layout — the CI-able core of the
251
+ * per-game audit overlay (the visual page stays per-game; this is the math it
252
+ * and CI both call). Lays out each region and checks containment. */
253
+ export function auditLayout(entries, opts) {
254
+ const anchorsOutside = [];
255
+ const stackedPiles = [];
256
+ let tokenBleed = 0;
257
+ let minClearance = Infinity;
258
+ let tightestRegion = null;
259
+ for (const { id, polygon, count } of entries) {
260
+ const { point: anchor, clearance } = poleOfInaccessibilityWithClearance(polygon, opts.precision ?? 1.0);
261
+ if (clearance <= 0)
262
+ anchorsOutside.push(id);
263
+ if (clearance < minClearance) {
264
+ minClearance = clearance;
265
+ tightestRegion = id;
266
+ }
267
+ const layout = layoutTokensInPolygon(polygon, count, { ...opts, anchor });
268
+ if (layout.stacked) {
269
+ stackedPiles.push(id);
270
+ }
271
+ else {
272
+ for (const p of layout.points) {
273
+ if (signedDistanceToPolygon(p, polygon) < 0)
274
+ tokenBleed++;
275
+ }
276
+ }
277
+ }
278
+ return {
279
+ regions: entries.length,
280
+ anchorsOutside,
281
+ tokenBleed,
282
+ stackedPiles,
283
+ minClearance: minClearance === Infinity ? 0 : minClearance,
284
+ tightestRegion,
285
+ };
286
+ }
287
+ // --- comparison / validation (extraction vs a reference) ------------------
288
+ // Roughness-tolerant ways to validate auto-extracted polygons against a human
289
+ // reference (or vice versa). Compare TOPOLOGY (count, 1:1 matching, adjacency,
290
+ // anchor proximity), NOT vertex precision — a rough reference legitimately
291
+ // overlaps a precise extraction at well under IoU 1.0.
292
+ function segSegDistSq(a1, a2, b1, b2) {
293
+ // Min of the four endpoint-to-opposite-segment distances. (For the adjacency
294
+ // use-case we don't need the exact crossing test — touching boundaries give a
295
+ // near-zero distance either way.)
296
+ return Math.min(segmentDistSq(a1, b1, b2), segmentDistSq(a2, b1, b2), segmentDistSq(b1, a1, a2), segmentDistSq(b2, a1, a2));
297
+ }
298
+ function boundaryDistance(a, b) {
299
+ let minSq = Infinity;
300
+ for (let i = 0, ai = a.length - 1; i < a.length; ai = i++) {
301
+ for (let j = 0, bj = b.length - 1; j < b.length; bj = j++) {
302
+ const d = segSegDistSq(a[ai], a[i], b[bj], b[j]);
303
+ if (d < minSq)
304
+ minSq = d;
305
+ if (minSq === 0)
306
+ return 0;
307
+ }
308
+ }
309
+ return Math.sqrt(minSq);
310
+ }
311
+ /** Approximate intersection-over-union of two polygons via grid sampling over
312
+ * the combined bounding box. Robust to concavity (no polygon clipping).
313
+ * `samples` is the grid resolution per axis (default 64 → 4096 test points).
314
+ * Deterministic. */
315
+ export function polygonIoU(a, b, samples = 64) {
316
+ const ba = boundingBox(a), bb = boundingBox(b);
317
+ const minX = Math.min(ba.minX, bb.minX), minY = Math.min(ba.minY, bb.minY);
318
+ const maxX = Math.max(ba.maxX, bb.maxX), maxY = Math.max(ba.maxY, bb.maxY);
319
+ const w = maxX - minX, h = maxY - minY;
320
+ if (w <= 0 || h <= 0)
321
+ return 0;
322
+ let inter = 0, uni = 0;
323
+ for (let i = 0; i < samples; i++) {
324
+ const x = minX + ((i + 0.5) * w) / samples;
325
+ for (let j = 0; j < samples; j++) {
326
+ const y = minY + ((j + 0.5) * h) / samples;
327
+ const inA = pointInPolygon({ x, y }, a);
328
+ const inB = pointInPolygon({ x, y }, b);
329
+ if (inA || inB)
330
+ uni++;
331
+ if (inA && inB)
332
+ inter++;
333
+ }
334
+ }
335
+ return uni === 0 ? 0 : inter / uni;
336
+ }
337
+ /** Undirected adjacency derived from polygon geometry: two regions are adjacent
338
+ * when their boundaries come within `tolerance`. Rough/hand-traced polygons
339
+ * rarely share exact edges, so a tolerance is required. O(n²) with a bbox
340
+ * pre-filter — fine for a few hundred regions run occasionally (validation, not
341
+ * a hot path). Returns sorted, de-duplicated, normalized edges. */
342
+ export function adjacencyFromPolygons(polygons, tolerance) {
343
+ const ids = Object.keys(polygons);
344
+ const bboxes = {};
345
+ for (const id of ids)
346
+ bboxes[id] = boundingBox(polygons[id]);
347
+ const edges = [];
348
+ for (let i = 0; i < ids.length; i++) {
349
+ for (let j = i + 1; j < ids.length; j++) {
350
+ const a = ids[i], b = ids[j];
351
+ const ba = bboxes[a], bb = bboxes[b];
352
+ if (ba.minX - tolerance > bb.maxX || bb.minX - tolerance > ba.maxX ||
353
+ ba.minY - tolerance > bb.maxY || bb.minY - tolerance > ba.maxY)
354
+ continue;
355
+ if (boundaryDistance(polygons[a], polygons[b]) <= tolerance) {
356
+ edges.push(a < b ? [a, b] : [b, a]);
357
+ }
358
+ }
359
+ }
360
+ edges.sort((e, f) => (e[0] === f[0] ? e[1].localeCompare(f[1]) : e[0].localeCompare(f[0])));
361
+ return edges;
362
+ }
363
+ /** Set-diff two undirected edge lists (edge endpoint order is normalized). Use
364
+ * to compare polygon-derived adjacency against the engine's known adjacency —
365
+ * the strongest, roughness-immune validation signal. */
366
+ export function diffAdjacency(a, b) {
367
+ const key = (e) => (e[0] < e[1] ? `${e[0]}|${e[1]}` : `${e[1]}|${e[0]}`);
368
+ const setA = new Set(a.map(key));
369
+ const setB = new Set(b.map(key));
370
+ const shared = [], onlyA = [], onlyB = [];
371
+ for (const e of a)
372
+ (setB.has(key(e)) ? shared : onlyA).push(e);
373
+ for (const e of b)
374
+ if (!setA.has(key(e)))
375
+ onlyB.push(e);
376
+ return { shared, onlyA, onlyB };
377
+ }
378
+ /** Match two named polygon sets (e.g. extracted vs a human reference) by best
379
+ * IoU. The roughness-tolerant comparison: use it to confirm a 1:1 region
380
+ * correspondence and flag merges/splits — NOT to score vertex precision. Greedy
381
+ * per A-region. `minIoU` is the matching threshold (default 0.5); keep it low,
382
+ * because a rough reference legitimately overlaps a precise extraction at well
383
+ * under 1.0. */
384
+ export function matchRegions(a, b, opts = {}) {
385
+ const minIoU = opts.minIoU ?? 0.5;
386
+ const samples = opts.samples ?? 48;
387
+ const idsA = Object.keys(a), idsB = Object.keys(b);
388
+ const bboxesB = {};
389
+ for (const id of idsB)
390
+ bboxesB[id] = boundingBox(b[id]);
391
+ const matches = [];
392
+ const matchedB = new Set();
393
+ const unmatchedA = [];
394
+ for (const ia of idsA) {
395
+ const pa = a[ia];
396
+ const ba = boundingBox(pa);
397
+ let best = null;
398
+ for (const ib of idsB) {
399
+ const bbB = bboxesB[ib];
400
+ if (ba.minX > bbB.maxX || bbB.minX > ba.maxX ||
401
+ ba.minY > bbB.maxY || bbB.minY > ba.maxY)
402
+ continue; // bbox pre-filter
403
+ const iou = polygonIoU(pa, b[ib], samples);
404
+ if (!best || iou > best.iou)
405
+ best = { id: ib, iou };
406
+ }
407
+ if (best && best.iou >= minIoU) {
408
+ const anchorDistance = distance(poleOfInaccessibility(pa), poleOfInaccessibility(b[best.id]));
409
+ matches.push({ a: ia, b: best.id, iou: best.iou, anchorDistance });
410
+ matchedB.add(best.id);
411
+ }
412
+ else {
413
+ unmatchedA.push(ia);
414
+ }
415
+ }
416
+ const unmatchedB = idsB.filter((id) => !matchedB.has(id));
417
+ return { matches, unmatchedA, unmatchedB };
418
+ }
419
+ //# sourceMappingURL=geo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo.js","sourceRoot":"","sources":["../../src/core/geo.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,4 +4,5 @@ export { StructuredLog, type LogEntry } from './core/log.js';
4
4
  export { EffectContext, type ChoiceRequest, type ChoiceResponse } from './core/effects.js';
5
5
  export { type GameAdapter, type GameResult } from './core/adapter.js';
6
6
  export { type PlayerController, type ControllerContext, RandomAI, ScriptedController, } from './core/controller.js';
7
+ export { type Point, type Polygon, type BBox, type LayoutOptions, type TokenLayout, type PoleResult, type AuditEntry, type AuditResult, type Edge, type AdjacencyDiff, type RegionMatch, type RegionMatchResult, boundingBox, signedArea, area, centroid, pointInPolygon, signedDistanceToPolygon, poleOfInaccessibility, poleOfInaccessibilityWithClearance, layoutTokensInPolygon, toPolygon, fromPolygon, distance, auditLayout, polygonIoU, adjacencyFromPolygons, diffAdjacency, matchRegions, } from './core/geo.js';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,QAAQ,EACR,kBAAkB,GACnB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,QAAQ,EACR,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,KAAK,EACV,KAAK,OAAO,EACZ,KAAK,IAAI,EACT,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,IAAI,EACT,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,WAAW,EACX,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,kCAAkC,EAClC,qBAAqB,EACrB,SAAS,EACT,WAAW,EACX,QAAQ,EACR,WAAW,EACX,UAAU,EACV,qBAAqB,EACrB,aAAa,EACb,YAAY,GACb,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -3,4 +3,5 @@ export { jsonCodec } from './core/codec.js';
3
3
  export { StructuredLog } from './core/log.js';
4
4
  export { EffectContext } from './core/effects.js';
5
5
  export { RandomAI, ScriptedController, } from './core/controller.js';
6
+ export { boundingBox, signedArea, area, centroid, pointInPolygon, signedDistanceToPolygon, poleOfInaccessibility, poleOfInaccessibilityWithClearance, layoutTokensInPolygon, toPolygon, fromPolygon, distance, auditLayout, polygonIoU, adjacencyFromPolygons, diffAdjacency, matchRegions, } from './core/geo.js';
6
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,SAAS,EAAc,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAiB,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAA2C,MAAM,mBAAmB,CAAC;AAE3F,OAAO,EAGL,QAAQ,EACR,kBAAkB,GACnB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,SAAS,EAAc,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAiB,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAA2C,MAAM,mBAAmB,CAAC;AAE3F,OAAO,EAGL,QAAQ,EACR,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAaL,WAAW,EACX,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,kCAAkC,EAClC,qBAAqB,EACrB,SAAS,EACT,WAAW,EACX,QAAQ,EACR,WAAW,EACX,UAAU,EACV,qBAAqB,EACrB,aAAa,EACb,YAAY,GACb,MAAM,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "digital-boardgame-framework",
3
- "version": "0.8.2",
3
+ "version": "0.9.1",
4
4
  "description": "Foundation library for turn-based digital boardgames: deterministic engine plumbing, async multiplayer, agent-friendly bug triage.",
5
5
  "keywords": ["boardgame", "board-game", "multiplayer", "async-multiplayer", "turn-based", "game-framework", "supabase", "cloudflare-pages"],
6
6
  "author": "John Champaign",