Skip to content

Commit 3e63b40

Browse files
author
DavidQ
committed
Polish Collision Inspector V2 usability after template CSS alignment - PR_26139_010-collision-inspector-usability-polish
1 parent 139a5fd commit 3e63b40

4 files changed

Lines changed: 92 additions & 2 deletions

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# PR_26139_010-collision-inspector-usability-polish
2+
3+
## Scope
4+
- Continued Collision Inspector V2 polish on top of PR_26139_009.
5+
- Kept `../templates-v2/styles/toolStarter.css` as the visual source of truth.
6+
- Added a concise heading/orientation guide label:
7+
- Canvas now references `collisionGuideNote` with `aria-describedby`.
8+
- Heading legend item has a title explaining the guide.
9+
- Visible note: `Heading guides show each object's rotation from its origin.`
10+
- Preserved A/B rotate inputs, fixed three-decimal Origins output, scale normalization, shared engine collision path, manifest-only geometry, and no fallback/default vector maps.
11+
12+
## Usability Validation Added
13+
- Verifies left and right open accordion groups share vertical space evenly.
14+
- Verifies Live Result, Collision Summary, and Collision Logs each retain readable vertical space and scrollable output containers.
15+
- Verifies A and B rotate input changes recompute the shared engine collision summary.
16+
- Verifies A rotation changes transformed Object A points and the canvas preview pixels.
17+
- Verifies B rotation changes transformed Object B points.
18+
- Verifies zoom clamps to `5x` and preserves the manifest screen aspect ratio.
19+
- Verifies the heading/orientation guide is labeled and described.
20+
21+
## Validation
22+
- PASS: `node --check tests/playwright/tools/CollisionInspectorV2.spec.mjs`
23+
- PASS: `npx playwright test tests/playwright/tools/CollisionInspectorV2.spec.mjs --project=playwright --workers=1 --reporter=list`
24+
- `3 passed`
25+
- Covers targeted Collision Inspector V2 usability/layout validation.
26+
- Covers targeted A/B rotate and zoom validation.
27+
- Covers targeted shared collision validation through `src/engine/collision/objectVector.js`.
28+
- PASS: `npm run build:manifest`
29+
- This repo does not define a plain `npm run build`; `build:manifest` is the available build script.
30+
- Removed generated `docs/build` output after validation.
31+
- PASS: `git diff --check`
32+
- Only CRLF working-copy warnings were reported.
33+
34+
## Full Samples Smoke Test
35+
- Skipped. This PR is limited to Collision Inspector V2 usability/layout polish and targeted validation.
36+
37+
## Changed Files
38+
- `tools/collision-inspector-v2/index.html`
39+
- `tools/collision-inspector-v2/styles/collisionInspectorV2.css`
40+
- `tests/playwright/tools/CollisionInspectorV2.spec.mjs`

tests/playwright/tools/CollisionInspectorV2.spec.mjs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ async function dragCanvasPoint(page, from, to) {
3131
}, { from, to });
3232
}
3333

34+
async function readCollisionSummary(page) {
35+
return JSON.parse(await page.locator("#collisionSummary").textContent());
36+
}
37+
38+
async function canvasSignature(page) {
39+
return page.locator("#collisionCanvas").evaluate((canvas) => {
40+
const context = canvas.getContext("2d");
41+
const data = context.getImageData(0, 0, canvas.width, canvas.height).data;
42+
let signature = 0;
43+
for (let index = 0; index < data.length; index += 97) {
44+
signature = (signature * 31 + data[index] * 3 + data[index + 1] * 5 + data[index + 2] * 7 + data[index + 3]) >>> 0;
45+
}
46+
return signature;
47+
});
48+
}
49+
3450
test.describe("Collision Inspector V2", () => {
3551
test("loads a game manifest and reports live vector, pixel, bounds, and hybrid collisions", async ({ page }) => {
3652
const server = await startRepoServer();
@@ -139,6 +155,9 @@ test.describe("Collision Inspector V2", () => {
139155
await expect(page.locator("#collisionSummaryContent #collisionSummary")).toBeVisible();
140156
await expect(page.locator("#resultContent")).not.toContainText("Mode");
141157
await expect(page.locator(".collision-inspector-v2__legend")).toContainText("Heading");
158+
await expect(page.locator(".collision-inspector-v2__legend span").nth(3)).toHaveAttribute("title", "Heading guide shows object rotation from its origin.");
159+
await expect(page.locator("#collisionCanvas")).toHaveAttribute("aria-describedby", "collisionGuideNote");
160+
await expect(page.locator("#collisionGuideNote")).toHaveText("Heading guides show each object's rotation from its origin.");
142161
const summaryOverflow = await page.locator("#collisionSummaryContent").evaluate((element) => getComputedStyle(element).overflowY);
143162
expect(["auto", "scroll"]).toContain(summaryOverflow);
144163
const resultOverflow = await page.locator("#resultContent").evaluate((element) => getComputedStyle(element).overflowY);
@@ -151,15 +170,21 @@ test.describe("Collision Inspector V2", () => {
151170
const logs = document.querySelector(".collision-inspector-v2__accordion--logs").getBoundingClientRect();
152171
const heights = [result.height, summary.height, logs.height];
153172
return {
173+
logContentHeight: document.querySelector("#collisionLogContent").getBoundingClientRect().height,
154174
inputMaxDelta: Math.abs(manifest.height - pair.height),
155175
maxDelta: Math.max(...heights) - Math.min(...heights),
176+
resultContentHeight: document.querySelector("#resultContent").getBoundingClientRect().height,
156177
resultWidth: result.width,
178+
summaryContentHeight: document.querySelector("#collisionSummaryContent").getBoundingClientRect().height,
157179
summaryWidth: summary.width,
158180
logsWidth: logs.width
159181
};
160182
});
161183
expect(outputLayout.inputMaxDelta).toBeLessThanOrEqual(6);
162184
expect(outputLayout.maxDelta).toBeLessThanOrEqual(6);
185+
expect(outputLayout.resultContentHeight).toBeGreaterThan(80);
186+
expect(outputLayout.summaryContentHeight).toBeGreaterThan(80);
187+
expect(outputLayout.logContentHeight).toBeGreaterThan(80);
163188
expect(Math.abs(outputLayout.resultWidth - outputLayout.summaryWidth)).toBeLessThanOrEqual(1);
164189
expect(Math.abs(outputLayout.summaryWidth - outputLayout.logsWidth)).toBeLessThanOrEqual(1);
165190
await expect(page.locator("button[aria-controls='collisionLogContent']")).toBeVisible();
@@ -168,10 +193,23 @@ test.describe("Collision Inspector V2", () => {
168193
await page.locator("button[aria-controls='collisionLogContent']").click();
169194
await expect(page.locator("#collisionLogContent")).toBeVisible();
170195

196+
const initialSummary = await readCollisionSummary(page);
197+
const initialSignature = await canvasSignature(page);
171198
await page.locator("#objectARotationInput").fill("45");
172199
await expect(page.locator("#rotationState")).toHaveText("A 45 / B 0");
200+
const rotatedASummary = await readCollisionSummary(page);
201+
expect(rotatedASummary.enginePath).toBe("src/engine/collision/objectVector.js");
202+
expect(rotatedASummary.rotation.objectA).toBe(45);
203+
expect(rotatedASummary.rotation.objectB).toBe(0);
204+
expect(rotatedASummary.transformedPoints.objectA).not.toEqual(initialSummary.transformedPoints.objectA);
205+
expect(await canvasSignature(page)).not.toBe(initialSignature);
173206
await page.locator("#objectBRotationInput").fill("180");
174207
await expect(page.locator("#rotationState")).toHaveText("A 45 / B 180");
208+
const rotatedBSummary = await readCollisionSummary(page);
209+
expect(rotatedBSummary.enginePath).toBe("src/engine/collision/objectVector.js");
210+
expect(rotatedBSummary.rotation.objectA).toBe(45);
211+
expect(rotatedBSummary.rotation.objectB).toBe(180);
212+
expect(rotatedBSummary.transformedPoints.objectB).not.toEqual(initialSummary.transformedPoints.objectB);
175213
await page.locator("#objectARotationInput").fill("0");
176214
await expect(page.locator("#rotationState")).toHaveText("A 0 / B 180");
177215

@@ -201,6 +239,11 @@ test.describe("Collision Inspector V2", () => {
201239
await page.locator("#collisionZoomInput").fill("5");
202240
await expect(page.locator("#zoomState")).toHaveText("5x");
203241
await expect(page.locator("#collisionSummary")).toContainText('"zoom": 5');
242+
const zoomAspectRatio = await page.locator("#collisionCanvas").evaluate((canvas) => {
243+
const rect = canvas.getBoundingClientRect();
244+
return rect.width / rect.height;
245+
});
246+
expect(Math.abs(zoomAspectRatio - (960 / 720))).toBeLessThan(0.02);
204247
await page.evaluate(() => window.__collisionInspectorV2App.setZoom(8));
205248
await expect(page.locator("#zoomState")).toHaveText("5x");
206249
await expect(page.locator("#collisionSummary")).toContainText('"zoom": 5');

tools/collision-inspector-v2/index.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,15 @@ <h2 class="tools-platform-frame__eyebrow">Shared Manifest Collision Tool</h2>
113113
<output id="zoomState" for="collisionZoomInput">1x</output>
114114
</div>
115115
<div class="collision-inspector-v2__canvas-viewport">
116-
<canvas id="collisionCanvas" width="1" height="1" aria-label="Collision movement simulation"></canvas>
116+
<canvas id="collisionCanvas" width="1" height="1" aria-label="Collision movement simulation" aria-describedby="collisionGuideNote"></canvas>
117117
</div>
118118
<div class="collision-inspector-v2__legend" aria-label="Collision overlay legend">
119119
<span>Object A</span>
120120
<span>Object B</span>
121121
<span>Overlap</span>
122-
<span>Heading</span>
122+
<span title="Heading guide shows object rotation from its origin.">Heading</span>
123123
</div>
124+
<p id="collisionGuideNote" class="collision-inspector-v2__guide-note">Heading guides show each object's rotation from its origin.</p>
124125
</div>
125126
</section>
126127
</section>

tools/collision-inspector-v2/styles/collisionInspectorV2.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@
127127
background: var(--tool-starter-text);
128128
}
129129

130+
.collision-inspector-v2__guide-note {
131+
margin: 0;
132+
color: var(--tool-starter-muted);
133+
font-size: 0.86rem;
134+
}
135+
130136
.collision-inspector-v2__result {
131137
min-height: 48px;
132138
display: grid;

0 commit comments

Comments
 (0)