zx-kit 0.32.0 → 0.33.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,98 @@
1
+ import { type SpectrumColor } from './palette.js';
2
+ /**
3
+ * A snapshot of debug/telemetry numbers for one frame, produced by {@link sampleDebug}.
4
+ *
5
+ * Only what a 2D-canvas browser game can honestly measure: frame pacing and JS main-thread work.
6
+ * There is **no VRAM or GPU figure** — neither is observable from a Canvas2D context (that would
7
+ * need WebGL + driver hooks the browser does not expose). `cpuLoad` is the JS update+render cost
8
+ * against the frame budget, not the GPU's blit time. Resolution, JS heap, draw-call counters and
9
+ * estimated bitmap memory are planned for later slices.
10
+ */
11
+ export interface DebugInfo {
12
+ /** Smoothed frames per second (exponential moving average). */
13
+ fps: number;
14
+ /** Duration of the last frame in milliseconds (delta between consecutive `beginFrame` calls). */
15
+ frameMs: number;
16
+ /** Measured update+render time in ms (between `beginFrame` and `endFrame`); `0` if `endFrame` was not called. */
17
+ workMs: number;
18
+ /** Target per-frame budget in ms (`1000 / targetFps`, e.g. `16.67` at 60 fps). */
19
+ budgetMs: number;
20
+ /** `workMs / budgetMs` (0..1+): JS main-thread load against the budget. Above `1` means the frame overran. */
21
+ cpuLoad: number;
22
+ /** Game-supplied fields merged into the snapshot — e.g. `{ mines: 12, level: 3 }`. */
23
+ custom: Record<string, string | number>;
24
+ }
25
+ /**
26
+ * Per-frame timing state. Create with {@link createDebugMonitor}, bracket each frame with
27
+ * {@link beginFrame} / {@link endFrame}, then read with {@link sampleDebug}.
28
+ * Holds no DOM references — safe to keep at module scope.
29
+ */
30
+ export interface DebugMonitor {
31
+ /** Target frame rate used for `budgetMs` / `cpuLoad` (default `60`). */
32
+ targetFps: number;
33
+ /** EMA smoothing factor 0..1 applied to `fps` (default `0.1`; higher = snappier, noisier). */
34
+ smoothing: number;
35
+ /** Timestamp of the last `beginFrame`, or `null` before the first frame. */
36
+ lastTime: number | null;
37
+ /** Timestamp captured at `beginFrame`, used by `endFrame` to compute `workMs`. */
38
+ workStart: number;
39
+ /** Current smoothed fps. */
40
+ fps: number;
41
+ /** Last frame delta in ms. */
42
+ frameMs: number;
43
+ /** Last measured work time in ms. */
44
+ workMs: number;
45
+ }
46
+ /**
47
+ * Creates a frame-timing monitor.
48
+ *
49
+ * @param opts.targetFps - Frame rate the budget/load are measured against (default `60`)
50
+ * @param opts.smoothing - EMA factor 0..1 for the fps readout (default `0.1`)
51
+ *
52
+ * @example
53
+ * const dbg = createDebugMonitor() // 60 fps budget
54
+ * const dbg = createDebugMonitor({ targetFps: 50 }) // PAL
55
+ */
56
+ export declare function createDebugMonitor(opts?: {
57
+ targetFps?: number;
58
+ smoothing?: number;
59
+ }): DebugMonitor;
60
+ /**
61
+ * Marks the start of a frame. Updates `frameMs` (delta since the previous `beginFrame`) and the
62
+ * smoothed `fps`, and starts the work-time clock for {@link endFrame}.
63
+ * Call once at the top of your `requestAnimationFrame` loop.
64
+ *
65
+ * @param now - Timestamp in ms (default `performance.now()`); pass the rAF timestamp for accuracy.
66
+ */
67
+ export declare function beginFrame(m: DebugMonitor, now?: number): void;
68
+ /**
69
+ * Marks the end of the frame's JS work (after update + render). Sets `workMs` to the time since
70
+ * {@link beginFrame}. Optional — skip it and `workMs` / `cpuLoad` stay `0`.
71
+ *
72
+ * @param now - Timestamp in ms (default `performance.now()`).
73
+ */
74
+ export declare function endFrame(m: DebugMonitor, now?: number): void;
75
+ /**
76
+ * Reads the current numbers as a fresh {@link DebugInfo}. Merge game-specific fields via `custom`.
77
+ *
78
+ * @example
79
+ * const info = sampleDebug(dbg, { mines: mineCount, level })
80
+ */
81
+ export declare function sampleDebug(m: DebugMonitor, custom?: Record<string, string | number>): DebugInfo;
82
+ /**
83
+ * Draws a small Spectrum-style debug overlay: `FPS` + frame ms, then `CPU %` (only when work time
84
+ * was measured), then one line per `custom` field. Lines are `CELL`-spaced down from `(x, y)`.
85
+ *
86
+ * The game owns the toggle key and placement — this is just a convenience renderer over
87
+ * {@link drawText}.
88
+ *
89
+ * @param x - Left edge in game pixels (default `1`)
90
+ * @param y - Top edge in game pixels (default `1`)
91
+ * @param ink - Text colour (default `C.B_YELLOW`)
92
+ * @param paper - Background colour for readability (default `C.BLACK`)
93
+ *
94
+ * @example
95
+ * if (showDebug) drawDebugOverlay(ctx, sampleDebug(dbg, { mines }))
96
+ */
97
+ export declare function drawDebugOverlay(ctx: CanvasRenderingContext2D, info: DebugInfo, x?: number, y?: number, ink?: SpectrumColor, paper?: SpectrumColor): void;
98
+ //# sourceMappingURL=debug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAG1D;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAA;IACX,iGAAiG;IACjG,OAAO,EAAE,MAAM,CAAA;IACf,iHAAiH;IACjH,MAAM,EAAE,MAAM,CAAA;IACd,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAA;IAChB,8GAA8G;IAC9G,OAAO,EAAE,MAAM,CAAA;IACf,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;CACxC;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAA;IACjB,8FAA8F;IAC9F,SAAS,EAAE,MAAM,CAAA;IACjB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAA;IACjB,4BAA4B;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,YAAY,CAUd;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,GAAE,MAA0B,GAAG,IAAI,CASjF;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,GAAE,MAA0B,GAAG,IAAI,CAE/E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE,YAAY,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAM,GAC3C,SAAS,CAUX;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,SAAS,EACf,CAAC,GAAE,MAAU,EACb,CAAC,GAAE,MAAU,EACb,GAAG,GAAE,aAA0B,EAC/B,KAAK,GAAE,aAAuB,GAC7B,IAAI,CAON"}
package/dist/debug.js ADDED
@@ -0,0 +1,92 @@
1
+ import { C, CELL } from './palette.js';
2
+ import { drawText } from './renderer.js';
3
+ /**
4
+ * Creates a frame-timing monitor.
5
+ *
6
+ * @param opts.targetFps - Frame rate the budget/load are measured against (default `60`)
7
+ * @param opts.smoothing - EMA factor 0..1 for the fps readout (default `0.1`)
8
+ *
9
+ * @example
10
+ * const dbg = createDebugMonitor() // 60 fps budget
11
+ * const dbg = createDebugMonitor({ targetFps: 50 }) // PAL
12
+ */
13
+ export function createDebugMonitor(opts = {}) {
14
+ return {
15
+ targetFps: opts.targetFps ?? 60,
16
+ smoothing: opts.smoothing ?? 0.1,
17
+ lastTime: null,
18
+ workStart: 0,
19
+ fps: 0,
20
+ frameMs: 0,
21
+ workMs: 0,
22
+ };
23
+ }
24
+ /**
25
+ * Marks the start of a frame. Updates `frameMs` (delta since the previous `beginFrame`) and the
26
+ * smoothed `fps`, and starts the work-time clock for {@link endFrame}.
27
+ * Call once at the top of your `requestAnimationFrame` loop.
28
+ *
29
+ * @param now - Timestamp in ms (default `performance.now()`); pass the rAF timestamp for accuracy.
30
+ */
31
+ export function beginFrame(m, now = performance.now()) {
32
+ if (m.lastTime !== null) {
33
+ m.frameMs = now - m.lastTime;
34
+ const inst = m.frameMs > 0 ? 1000 / m.frameMs : 0;
35
+ // First real sample primes directly; afterwards apply EMA smoothing.
36
+ m.fps = m.fps === 0 ? inst : m.fps + m.smoothing * (inst - m.fps);
37
+ }
38
+ m.lastTime = now;
39
+ m.workStart = now;
40
+ }
41
+ /**
42
+ * Marks the end of the frame's JS work (after update + render). Sets `workMs` to the time since
43
+ * {@link beginFrame}. Optional — skip it and `workMs` / `cpuLoad` stay `0`.
44
+ *
45
+ * @param now - Timestamp in ms (default `performance.now()`).
46
+ */
47
+ export function endFrame(m, now = performance.now()) {
48
+ m.workMs = Math.max(0, now - m.workStart);
49
+ }
50
+ /**
51
+ * Reads the current numbers as a fresh {@link DebugInfo}. Merge game-specific fields via `custom`.
52
+ *
53
+ * @example
54
+ * const info = sampleDebug(dbg, { mines: mineCount, level })
55
+ */
56
+ export function sampleDebug(m, custom = {}) {
57
+ const budgetMs = m.targetFps > 0 ? 1000 / m.targetFps : 0;
58
+ return {
59
+ fps: m.fps,
60
+ frameMs: m.frameMs,
61
+ workMs: m.workMs,
62
+ budgetMs,
63
+ cpuLoad: budgetMs > 0 ? m.workMs / budgetMs : 0,
64
+ custom,
65
+ };
66
+ }
67
+ /**
68
+ * Draws a small Spectrum-style debug overlay: `FPS` + frame ms, then `CPU %` (only when work time
69
+ * was measured), then one line per `custom` field. Lines are `CELL`-spaced down from `(x, y)`.
70
+ *
71
+ * The game owns the toggle key and placement — this is just a convenience renderer over
72
+ * {@link drawText}.
73
+ *
74
+ * @param x - Left edge in game pixels (default `1`)
75
+ * @param y - Top edge in game pixels (default `1`)
76
+ * @param ink - Text colour (default `C.B_YELLOW`)
77
+ * @param paper - Background colour for readability (default `C.BLACK`)
78
+ *
79
+ * @example
80
+ * if (showDebug) drawDebugOverlay(ctx, sampleDebug(dbg, { mines }))
81
+ */
82
+ export function drawDebugOverlay(ctx, info, x = 1, y = 1, ink = C.B_YELLOW, paper = C.BLACK) {
83
+ const lines = [`${Math.round(info.fps)} FPS ${info.frameMs.toFixed(1)}ms`];
84
+ if (info.workMs > 0)
85
+ lines.push(`CPU ${Math.round(info.cpuLoad * 100)}%`);
86
+ for (const key of Object.keys(info.custom))
87
+ lines.push(`${key} ${info.custom[key]}`);
88
+ for (let i = 0; i < lines.length; i++) {
89
+ drawText(ctx, lines[i], x, y + i * CELL, ink, paper);
90
+ }
91
+ }
92
+ //# sourceMappingURL=debug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -22,4 +22,5 @@ export * from './attrscreen.js';
22
22
  export * from './monoscreen.js';
23
23
  export * from './music.js';
24
24
  export * from './presentation.js';
25
+ export * from './debug.js';
25
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -22,4 +22,5 @@ export * from './attrscreen.js';
22
22
  export * from './monoscreen.js';
23
23
  export * from './music.js';
24
24
  export * from './presentation.js';
25
+ export * from './debug.js';
25
26
  //# 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,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zx-kit",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/zrebec/zx-kit.git"
@@ -1,25 +0,0 @@
1
- /**
2
- * Pixel-perfect collision — visual ground truth test.
3
- *
4
- * Two bitmaps: 8×8 px each, but only a 2×2 px region is lit
5
- * (rows 0–1, cols 0–1). The effective sprite shape is 2×2 — a size that
6
- * is intentionally NOT a multiple of the ZX tile size (8 px).
7
- *
8
- * col: 01234567
9
- * row 0: ██...... (0xC0)
10
- * row 1: ██...... (0xC0)
11
- * rows 2–7: empty
12
- *
13
- * Ground truth: a brute-force Set-of-pixel-coords intersection.
14
- * Both sprites are projected to absolute (x,y) coordinates, all lit pixels
15
- * are collected into a flat Set, and any shared coordinate counts as overlap.
16
- * No formulas, no assumptions — just "which pixels are painted and do any
17
- * coordinates match?"
18
- *
19
- * Every assertion compares masksOverlap against this ground truth, sweeping
20
- * sprite B toward sprite A one pixel at a time in all four directions and
21
- * diagonally. A separate block shows that AABB (8×8 bounding-box) fires
22
- * 6 pixels earlier than pixel-perfect — the gap the ground truth exposes.
23
- */
24
- export {};
25
- //# sourceMappingURL=collision.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"collision.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/collision.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
@@ -1,198 +0,0 @@
1
- /**
2
- * Pixel-perfect collision — visual ground truth test.
3
- *
4
- * Two bitmaps: 8×8 px each, but only a 2×2 px region is lit
5
- * (rows 0–1, cols 0–1). The effective sprite shape is 2×2 — a size that
6
- * is intentionally NOT a multiple of the ZX tile size (8 px).
7
- *
8
- * col: 01234567
9
- * row 0: ██...... (0xC0)
10
- * row 1: ██...... (0xC0)
11
- * rows 2–7: empty
12
- *
13
- * Ground truth: a brute-force Set-of-pixel-coords intersection.
14
- * Both sprites are projected to absolute (x,y) coordinates, all lit pixels
15
- * are collected into a flat Set, and any shared coordinate counts as overlap.
16
- * No formulas, no assumptions — just "which pixels are painted and do any
17
- * coordinates match?"
18
- *
19
- * Every assertion compares masksOverlap against this ground truth, sweeping
20
- * sprite B toward sprite A one pixel at a time in all four directions and
21
- * diagonally. A separate block shows that AABB (8×8 bounding-box) fires
22
- * 6 pixels earlier than pixel-perfect — the gap the ground truth exposes.
23
- */
24
- import { describe, it, expect } from 'vitest';
25
- import { bitmapPixelMask, masksOverlap, rectsOverlap } from '../collision.js';
26
- import { createBitmap } from '../renderer.js';
27
- // ── Fixture ───────────────────────────────────────────────────────────────────
28
- // width=8 (minimum for Bitmap: 1 byte per row). height=8.
29
- // Lit region: rows 0–1, cols 0–1. All other bytes 0x00.
30
- const BMP = createBitmap(new Uint8Array([
31
- 0xC0, // row 0: bits 7,6 set → cols 0,1
32
- 0xC0, // row 1: bits 7,6 set → cols 0,1
33
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // rows 2–7: empty
34
- ]), 8, 8);
35
- const MASK = bitmapPixelMask(BMP);
36
- // ── Brute-force ground truth ──────────────────────────────────────────────────
37
- // Project every lit pixel of a mask to world (x,y). Put them in a Set.
38
- // Check if any pixel of B appears in the Set of A. Completely explicit —
39
- // no clever algorithm, just coordinate comparison.
40
- function litPixels(mask, ox, oy) {
41
- const s = new Set();
42
- for (let row = 0; row < mask.height; row++) {
43
- for (const col of mask.rows[row]) {
44
- s.add(`${ox + col},${oy + row}`);
45
- }
46
- }
47
- return s;
48
- }
49
- function bruteForce(maskA, ax, ay, maskB, bx, by) {
50
- const setA = litPixels(maskA, ax, ay);
51
- for (let row = 0; row < maskB.height; row++) {
52
- for (const col of maskB.rows[row]) {
53
- if (setA.has(`${bx + col},${by + row}`))
54
- return true;
55
- }
56
- }
57
- return false;
58
- }
59
- // ─────────────────────────────────────────────────────────────────────────────
60
- describe('masksOverlap — 2×2 lit region, visual ground truth', () => {
61
- // ── Sanity-check the mask ─────────────────────────────────────────────────
62
- describe('mask shape', () => {
63
- it('has exactly 4 lit pixels (the 2×2 region)', () => {
64
- expect(MASK.totalPixels).toBe(4);
65
- });
66
- it('rows 0–1 contain columns [0, 1]; rows 2–7 are empty', () => {
67
- expect(MASK.rows[0]).toEqual([0, 1]);
68
- expect(MASK.rows[1]).toEqual([0, 1]);
69
- for (let r = 2; r < MASK.height; r++) {
70
- expect(MASK.rows[r]).toEqual([]);
71
- }
72
- });
73
- });
74
- // ── Horizontal sweep ──────────────────────────────────────────────────────
75
- // A is fixed at (20,20). B sweeps from right (x=30) to left (x=17), same y.
76
- // A's lit pixels: x∈[20,21], y∈[20,21].
77
- // First contact: bx=21 — B column 0 (x=21) meets A column 1 (x=21).
78
- // AABB first contact: bx=27 — 8×8 boxes share one column. Gap = 6 px.
79
- describe('horizontal sweep: B approaches A from the right', () => {
80
- const AX = 20, AY = 20;
81
- it('masksOverlap matches brute-force at every pixel step', () => {
82
- for (let bx = AX + 10; bx >= AX - 3; bx--) {
83
- const expected = bruteForce(MASK, AX, AY, MASK, bx, AY);
84
- const actual = masksOverlap(MASK, AX, AY, MASK, bx, AY) > 0;
85
- if (actual !== expected) {
86
- throw new Error(`bx=${bx}: masksOverlap=${actual} but brute-force says ${expected}`);
87
- }
88
- }
89
- });
90
- it('no collision at bx = AX+2 — 1-pixel gap between lit edges', () => {
91
- // A's rightmost lit column is AX+1=21. B's leftmost at bx=AX+2=22. Gap 1 px.
92
- expect(masksOverlap(MASK, AX, AY, MASK, AX + 2, AY)).toBe(0);
93
- expect(bruteForce(MASK, AX, AY, MASK, AX + 2, AY)).toBe(false);
94
- });
95
- it('collision fires at bx = AX+1 — lit edges share column AX+1', () => {
96
- expect(masksOverlap(MASK, AX, AY, MASK, AX + 1, AY)).toBeGreaterThan(0);
97
- expect(bruteForce(MASK, AX, AY, MASK, AX + 1, AY)).toBe(true);
98
- });
99
- it('AABB (8×8 box) fires 6 pixels before pixel-perfect', () => {
100
- const boxA = { x: AX, y: AY, w: 8, h: 8 };
101
- // AABB says: first overlap at bx=AX+7 (right edge of A box = left edge of B box)
102
- expect(rectsOverlap(boxA, { x: AX + 8, y: AY, w: 8, h: 8 })).toBe(false);
103
- expect(rectsOverlap(boxA, { x: AX + 7, y: AY, w: 8, h: 8 })).toBe(true);
104
- // Pixel-perfect: no lit pixels near each other at bx=AX+7 — false positive
105
- expect(masksOverlap(MASK, AX, AY, MASK, AX + 7, AY)).toBe(0);
106
- expect(bruteForce(MASK, AX, AY, MASK, AX + 7, AY)).toBe(false);
107
- });
108
- });
109
- // ── Vertical sweep ────────────────────────────────────────────────────────
110
- // A fixed at (20,20). B sweeps from below (y=30) upward (y=17), same x.
111
- // First contact: by=21 — B row 0 (y=21) meets A row 1 (y=21).
112
- // AABB first contact: by=27. Gap = 6 px.
113
- describe('vertical sweep: B approaches A from below', () => {
114
- const AX = 20, AY = 20;
115
- it('masksOverlap matches brute-force at every pixel step', () => {
116
- for (let by = AY + 10; by >= AY - 3; by--) {
117
- const expected = bruteForce(MASK, AX, AY, MASK, AX, by);
118
- const actual = masksOverlap(MASK, AX, AY, MASK, AX, by) > 0;
119
- if (actual !== expected) {
120
- throw new Error(`by=${by}: masksOverlap=${actual} but brute-force says ${expected}`);
121
- }
122
- }
123
- });
124
- it('no collision at by = AY+2 — 1-pixel gap between lit rows', () => {
125
- expect(masksOverlap(MASK, AX, AY, MASK, AX, AY + 2)).toBe(0);
126
- expect(bruteForce(MASK, AX, AY, MASK, AX, AY + 2)).toBe(false);
127
- });
128
- it('collision fires at by = AY+1 — lit rows share row AY+1', () => {
129
- expect(masksOverlap(MASK, AX, AY, MASK, AX, AY + 1)).toBeGreaterThan(0);
130
- expect(bruteForce(MASK, AX, AY, MASK, AX, AY + 1)).toBe(true);
131
- });
132
- it('AABB fires 6 pixels before pixel-perfect', () => {
133
- const boxA = { x: AX, y: AY, w: 8, h: 8 };
134
- expect(rectsOverlap(boxA, { x: AX, y: AY + 8, w: 8, h: 8 })).toBe(false);
135
- expect(rectsOverlap(boxA, { x: AX, y: AY + 7, w: 8, h: 8 })).toBe(true);
136
- expect(masksOverlap(MASK, AX, AY, MASK, AX, AY + 7)).toBe(0);
137
- expect(bruteForce(MASK, AX, AY, MASK, AX, AY + 7)).toBe(false);
138
- });
139
- });
140
- // ── Diagonal sweep ────────────────────────────────────────────────────────
141
- // A fixed at (20,20). B sweeps along the main diagonal: bx=by=AX+d, d=10→-3.
142
- // First contact: d=1 — corner pixel (AX+1, AY+1) is shared by both sprites.
143
- // At d=2: B's nearest pixel is (AX+2, AY+2) — corner of A is (AX+1, AY+1). Gap.
144
- describe('diagonal sweep: B approaches A from bottom-right', () => {
145
- const AX = 20, AY = 20;
146
- it('masksOverlap matches brute-force at every diagonal step', () => {
147
- for (let d = 10; d >= -3; d--) {
148
- const bx = AX + d, by = AY + d;
149
- const expected = bruteForce(MASK, AX, AY, MASK, bx, by);
150
- const actual = masksOverlap(MASK, AX, AY, MASK, bx, by) > 0;
151
- if (actual !== expected) {
152
- throw new Error(`d=${d} (bx=${bx}, by=${by}): masksOverlap=${actual} but brute-force says ${expected}`);
153
- }
154
- }
155
- });
156
- it('no collision at d=2 — corner pixels are 1 diagonal step apart', () => {
157
- expect(masksOverlap(MASK, AX, AY, MASK, AX + 2, AY + 2)).toBe(0);
158
- expect(bruteForce(MASK, AX, AY, MASK, AX + 2, AY + 2)).toBe(false);
159
- });
160
- it('collision fires at d=1 — single corner pixel (AX+1, AY+1) coincides', () => {
161
- expect(masksOverlap(MASK, AX, AY, MASK, AX + 1, AY + 1)).toBeGreaterThan(0);
162
- expect(bruteForce(MASK, AX, AY, MASK, AX + 1, AY + 1)).toBe(true);
163
- });
164
- });
165
- // ── Overlap count ─────────────────────────────────────────────────────────
166
- // masksOverlap returns a count, not just a boolean. Verify it grows as the
167
- // sprites move deeper into each other.
168
- describe('overlap pixel count increases with penetration depth', () => {
169
- const AX = 20, AY = 20;
170
- it('1 overlapping pixel at first horizontal contact (bx = AX+1)', () => {
171
- // Only column AX+1 is shared (both rows): 2 pixels
172
- expect(masksOverlap(MASK, AX, AY, MASK, AX + 1, AY)).toBe(2);
173
- });
174
- it('4 overlapping pixels when fully aligned (bx = AX)', () => {
175
- // Complete overlap of the 2×2 regions
176
- expect(masksOverlap(MASK, AX, AY, MASK, AX, AY)).toBe(4);
177
- });
178
- it('single corner pixel at diagonal first contact (d=1)', () => {
179
- // Only (AX+1, AY+1) is shared
180
- expect(masksOverlap(MASK, AX, AY, MASK, AX + 1, AY + 1)).toBe(1);
181
- });
182
- });
183
- // ── Symmetry ──────────────────────────────────────────────────────────────
184
- // masksOverlap(A, B) must equal masksOverlap(B, A) — no directional bias.
185
- describe('symmetry: collision count is order-independent', () => {
186
- it('A→B count equals B→A count for all horizontal positions', () => {
187
- const AX = 20, AY = 20;
188
- for (let bx = AX + 5; bx >= AX - 3; bx--) {
189
- const ab = masksOverlap(MASK, AX, AY, MASK, bx, AY);
190
- const ba = masksOverlap(MASK, bx, AY, MASK, AX, AY);
191
- if (ab !== ba) {
192
- throw new Error(`Asymmetry at bx=${bx}: A→B=${ab}, B→A=${ba}`);
193
- }
194
- }
195
- });
196
- });
197
- });
198
- //# sourceMappingURL=collision.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"collision.test.js","sourceRoot":"","sources":["../../src/__tests__/collision.test.ts"],"names":[],"mappings":"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"}