Skip to content

Commit 04b6b0c

Browse files
author
DavidQ
committed
Improve Collision Inspector V2 results layout and clarify orientation guide line - PR_26139_007-collision-inspector-results-layout
1 parent e9bf668 commit 04b6b0c

8 files changed

Lines changed: 134 additions & 29 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# PR_26139_007-collision-inspector-results-layout Report
2+
3+
## Summary
4+
5+
Adjusted Collision Inspector V2 output layout so Live Result, Collision Summary, and Collision Logs share the output column evenly, each scrolls where needed, and Collision Logs is a working accordion. The origin guide line is retained as an orientation/heading vector and is now labeled.
6+
7+
Playwright impacted: Yes.
8+
9+
## Scope Completed
10+
11+
- Made Collision Logs a real collapsible accordion with a button header.
12+
- Kept the Clear control inside the log accordion body.
13+
- Removed Mode from Live Result output.
14+
- Added vertical scrolling for Live Result.
15+
- Kept Collision Summary in its own accordion and forced vertical scrollbar behavior.
16+
- Split the right output column evenly across Live Result, Collision Summary, and Collision Logs when open.
17+
- Increased Zoom range to `5x` and clamped runtime zoom to `0.5x` through `5x`.
18+
- Labeled the visible right-pointing origin guide as a heading/orientation vector in the canvas and legend.
19+
20+
## Guardrails
21+
22+
- Collision Inspector V2 still uses the shared engine collision path.
23+
- No hardcoded Asteroids geometry was added.
24+
- No fallback/default vector maps were added.
25+
- Workspace-launched manifest loading remains automatic.
26+
27+
## Validation
28+
29+
PASS:
30+
31+
- `npm run build:manifest`
32+
- Passed. This repo does not define a plain `npm run build` script.
33+
- `node --check` on touched Collision Inspector V2 modules and touched Playwright spec.
34+
- `npx playwright test tests/playwright/tools/CollisionInspectorV2.spec.mjs --project=playwright --workers=1 --reporter=list`
35+
- 3 passed.
36+
- Validated output accordion layout, Live Result/Collision Summary scroll behavior, collapsible Collision Logs, removal of Mode from Live Result, 5x zoom behavior, heading legend, and workspace-launched manifest behavior.
37+
- `git diff --check`
38+
- Passed with line-ending warnings only.
39+
- Advisory Playwright V8 coverage report was regenerated by the Collision Inspector V2 Playwright run.
40+
41+
Skipped:
42+
43+
- Full regression was not run; validation scope was targeted to Collision Inspector V2 per request.
44+
- Full samples smoke test was not run; this PR does not broadly affect samples.
45+
46+
## Manual Validation
47+
48+
1. Open `tools/collision-inspector-v2/index.html?manifestPath=/games/Asteroids/game.manifest.json`.
49+
2. Confirm Live Result, Collision Summary, and Collision Logs share the right output column evenly.
50+
3. Confirm Live Result and Collision Summary scroll vertically when content exceeds the pane.
51+
4. Collapse and expand Collision Logs; expected result is a working accordion and preserved Clear control.
52+
5. Set Zoom to `5x`; expected result is `5x` shown and `zoom: 5` in the summary.
53+
6. Confirm the origin guide line is labeled as Heading in the legend and visible canvas output.

tests/playwright/tools/CollisionInspectorV2.spec.mjs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,32 @@ test.describe("Collision Inspector V2", () => {
8787
await expect(page.locator("#collisionSummary")).toContainText('"transformedPoints"');
8888
await expect(page.locator("#resultContent #collisionSummary")).toHaveCount(0);
8989
await expect(page.locator("#collisionSummaryContent #collisionSummary")).toBeVisible();
90+
await expect(page.locator("#resultContent")).not.toContainText("Mode");
91+
await expect(page.locator(".collision-inspector-v2__legend")).toContainText("Heading");
9092
const summaryOverflow = await page.locator("#collisionSummaryContent").evaluate((element) => getComputedStyle(element).overflowY);
9193
expect(["auto", "scroll"]).toContain(summaryOverflow);
94+
const resultOverflow = await page.locator("#resultContent").evaluate((element) => getComputedStyle(element).overflowY);
95+
expect(["auto", "scroll"]).toContain(resultOverflow);
96+
const outputLayout = await page.evaluate(() => {
97+
const result = document.querySelector(".collision-inspector-v2__accordion--result").getBoundingClientRect();
98+
const summary = document.querySelector(".collision-inspector-v2__accordion--summary").getBoundingClientRect();
99+
const logs = document.querySelector(".collision-inspector-v2__accordion--logs").getBoundingClientRect();
100+
const heights = [result.height, summary.height, logs.height];
101+
return {
102+
maxDelta: Math.max(...heights) - Math.min(...heights),
103+
resultWidth: result.width,
104+
summaryWidth: summary.width,
105+
logsWidth: logs.width
106+
};
107+
});
108+
expect(outputLayout.maxDelta).toBeLessThanOrEqual(6);
109+
expect(Math.abs(outputLayout.resultWidth - outputLayout.summaryWidth)).toBeLessThanOrEqual(1);
110+
expect(Math.abs(outputLayout.summaryWidth - outputLayout.logsWidth)).toBeLessThanOrEqual(1);
111+
await expect(page.locator("button[aria-controls='collisionLogContent']")).toBeVisible();
112+
await page.locator("button[aria-controls='collisionLogContent']").click();
113+
await expect(page.locator("#collisionLogContent")).toBeHidden();
114+
await page.locator("button[aria-controls='collisionLogContent']").click();
115+
await expect(page.locator("#collisionLogContent")).toBeVisible();
92116

93117
await page.locator("#objectBRotationInput").fill("180");
94118
await expect(page.locator("#rotationState")).toHaveText("A 0 / B 180");
@@ -115,6 +139,13 @@ test.describe("Collision Inspector V2", () => {
115139
await page.locator("#collisionZoomInput").fill("1.5");
116140
await expect(page.locator("#zoomState")).toHaveText("1.5x");
117141
await expect(page.locator("#collisionSummary")).toContainText('"zoom": 1.5');
142+
await expect(page.locator("#collisionZoomInput")).toHaveAttribute("max", "5");
143+
await page.locator("#collisionZoomInput").fill("5");
144+
await expect(page.locator("#zoomState")).toHaveText("5x");
145+
await expect(page.locator("#collisionSummary")).toContainText('"zoom": 5');
146+
await page.evaluate(() => window.__collisionInspectorV2App.setZoom(8));
147+
await expect(page.locator("#zoomState")).toHaveText("5x");
148+
await expect(page.locator("#collisionSummary")).toContainText('"zoom": 5');
118149

119150
await page.locator("#resetSimulationButton").click();
120151
await expect(page.locator("#collisionResultBadge")).toHaveText("No Collision");

tools/collision-inspector-v2/index.html

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ <h2 class="tools-platform-frame__eyebrow">Shared Manifest Collision Tool</h2>
107107
<div class="collision-inspector-v2__zoom-row">
108108
<label class="collision-inspector-v2__field collision-inspector-v2__field--inline" for="collisionZoomInput">
109109
<span>Zoom</span>
110-
<input id="collisionZoomInput" type="range" min="0.5" max="2" step="0.1" value="1">
110+
<input id="collisionZoomInput" type="range" min="0.5" max="5" step="0.1" value="1">
111111
</label>
112112
<output id="zoomState" for="collisionZoomInput">1x</output>
113113
</div>
@@ -118,28 +118,25 @@ <h2 class="tools-platform-frame__eyebrow">Shared Manifest Collision Tool</h2>
118118
<span>Object A</span>
119119
<span>Object B</span>
120120
<span>Overlap</span>
121+
<span>Heading</span>
121122
</div>
122123
</div>
123124
</section>
124125
</section>
125126

126127
<aside class="collision-inspector-v2__panel collision-inspector-v2__panel--right" aria-label="Collision output">
127-
<section class="accordion-v2 collision-inspector-v2__accordion is-open" data-accordion-v2-open="true">
128+
<section class="accordion-v2 collision-inspector-v2__accordion collision-inspector-v2__accordion--result is-open" data-accordion-v2-open="true">
128129
<button class="accordion-v2__header" type="button" aria-expanded="true" aria-controls="resultContent">
129130
<span>Live Result</span>
130131
<span class="accordion-v2__icon" aria-hidden="true">+</span>
131132
</button>
132-
<div id="resultContent" class="accordion-v2__content">
133+
<div id="resultContent" class="accordion-v2__content collision-inspector-v2__result-content">
133134
<div id="collisionResultBadge" class="collision-inspector-v2__result" data-collision-state="idle">No Collision</div>
134135
<dl class="collision-inspector-v2__metrics">
135136
<div>
136137
<dt>Overlap</dt>
137138
<dd id="overlapState">false</dd>
138139
</div>
139-
<div>
140-
<dt>Mode</dt>
141-
<dd id="modeState">Vector</dd>
142-
</div>
143140
<div>
144141
<dt>Bounds</dt>
145142
<dd id="boundsState">waiting</dd>
@@ -171,14 +168,12 @@ <h2 class="tools-platform-frame__eyebrow">Shared Manifest Collision Tool</h2>
171168
</section>
172169

173170
<section class="accordion-v2 collision-inspector-v2__accordion collision-inspector-v2__accordion--logs is-open" data-accordion-v2-open="true">
174-
<div class="accordion-v2__header collision-inspector-v2__status-header" aria-expanded="true" aria-controls="collisionLogContent">
171+
<button class="accordion-v2__header collision-inspector-v2__status-header" type="button" aria-expanded="true" aria-controls="collisionLogContent">
175172
<span>Collision Logs</span>
176-
<div class="collision-inspector-v2__status-actions">
177-
<button id="clearCollisionLogButton" type="button">Clear</button>
178-
<span class="accordion-v2__icon" aria-hidden="true">+</span>
179-
</div>
180-
</div>
173+
<span class="accordion-v2__icon" aria-hidden="true">+</span>
174+
</button>
181175
<div id="collisionLogContent" class="accordion-v2__content">
176+
<button id="clearCollisionLogButton" class="collision-inspector-v2__log-clear" type="button">Clear</button>
182177
<textarea id="collisionLog" readonly rows="12" aria-label="Collision logs"></textarea>
183178
</div>
184179
</section>

tools/collision-inspector-v2/js/CollisionInspectorV2App.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ export class CollisionInspectorV2App {
191191
}
192192

193193
setZoom(zoom) {
194-
this.zoom = Math.max(0.5, Math.min(2, numberValue(zoom, 1)));
194+
this.zoom = Math.max(0.5, Math.min(5, numberValue(zoom, 1)));
195195
this.renderer.setZoom(this.zoom);
196196
this.controls.setZoom(this.zoom);
197197
this.evaluateAndRender();

tools/collision-inspector-v2/js/CollisionInspectorV2Controls.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ export class CollisionInspectorV2Controls {
6969
this.elements.resultBadge.dataset.collisionState = "fail";
7070
this.elements.resultBadge.textContent = "Manifest Error";
7171
this.elements.overlapState.textContent = "unavailable";
72-
this.elements.modeState.textContent = "unavailable";
7372
this.elements.boundsState.textContent = "unavailable";
7473
this.elements.originState.textContent = "unavailable";
7574
this.elements.rotationState.textContent = "unavailable";
@@ -133,7 +132,6 @@ export class CollisionInspectorV2Controls {
133132
this.elements.resultBadge.dataset.collisionState = result.collided ? "hit" : "clear";
134133
this.elements.resultBadge.textContent = result.collided ? "Collision" : "No Collision";
135134
this.elements.overlapState.textContent = String(result.boundsOverlap === true);
136-
this.elements.modeState.textContent = result.modeLabel || "Vector";
137135
this.elements.boundsState.textContent = result.boundsOverlap ? "overlap" : "clear";
138136
this.elements.originState.textContent = `A ${roundNumber(geometryA.originWorld?.x)},${roundNumber(geometryA.originWorld?.y)} / B ${roundNumber(geometryB.originWorld?.x)},${roundNumber(geometryB.originWorld?.y)}`;
139137
this.elements.rotationState.textContent = `A ${roundNumber(geometryA.instance?.rotation)} / B ${roundNumber(geometryB.instance?.rotation)}`;

tools/collision-inspector-v2/js/CollisionInspectorV2Renderer.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class CollisionInspectorV2Renderer {
1414
}
1515

1616
setZoom(zoom) {
17-
this.zoom = Math.max(0.5, Math.min(2, numberValue(zoom, 1)));
17+
this.zoom = Math.max(0.5, Math.min(5, numberValue(zoom, 1)));
1818
}
1919

2020
clear() {
@@ -135,6 +135,8 @@ export class CollisionInspectorV2Renderer {
135135
ctx.moveTo(origin.x, origin.y);
136136
ctx.lineTo(origin.x + Math.cos(rotationRadians) * 34, origin.y + Math.sin(rotationRadians) * 34);
137137
ctx.stroke();
138+
ctx.font = `${Math.max(9, 11 / this.zoom)}px ui-monospace, monospace`;
139+
ctx.fillText("heading", origin.x + Math.cos(rotationRadians) * 40, origin.y + Math.sin(rotationRadians) * 40);
138140
ctx.restore();
139141
}
140142

tools/collision-inspector-v2/js/bootstrap.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ window.addEventListener("DOMContentLoaded", () => {
2323
log: requireElement("#collisionLog"),
2424
manifestSummary: requireElement("#manifestSummary"),
2525
modeSelect: requireElement("#collisionModeSelect"),
26-
modeState: requireElement("#modeState"),
2726
objectASelect: requireElement("#objectASelect"),
2827
objectBSelect: requireElement("#objectBSelect"),
2928
originState: requireElement("#originState"),

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

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ body[data-tool-id="collision-inspector-v2"] {
103103
.collision-inspector-v2__field input,
104104
.collision-inspector-v2__field select,
105105
.collision-inspector-v2__zoom-row output,
106-
.collision-inspector-v2__status-actions button {
106+
.collision-inspector-v2__log-clear {
107107
border: 1px solid var(--collision-inspector-line);
108108
border-radius: 6px;
109109
background: #ffffff;
@@ -113,7 +113,7 @@ body[data-tool-id="collision-inspector-v2"] {
113113

114114
.tool-starter__workspace__menu button,
115115
.collision-inspector-v2__wide-button,
116-
.collision-inspector-v2__status-actions button {
116+
.collision-inspector-v2__log-clear {
117117
min-height: 36px;
118118
padding: 0 12px;
119119
cursor: pointer;
@@ -164,18 +164,32 @@ body[data-tool-id="collision-inspector-v2"] {
164164
}
165165

166166
.collision-inspector-v2__accordion--fill,
167+
.collision-inspector-v2__accordion--result,
167168
.collision-inspector-v2__accordion--summary,
168169
.collision-inspector-v2__accordion--logs {
169170
flex: 1 1 auto;
170171
}
171172

172173
.collision-inspector-v2__accordion--fill .accordion-v2__content,
174+
.collision-inspector-v2__accordion--result .accordion-v2__content,
173175
.collision-inspector-v2__accordion--summary .accordion-v2__content,
174176
.collision-inspector-v2__accordion--logs .accordion-v2__content {
175177
min-height: 0;
176178
flex: 1 1 auto;
177179
}
178180

181+
.collision-inspector-v2__panel--right .collision-inspector-v2__accordion--result,
182+
.collision-inspector-v2__panel--right .collision-inspector-v2__accordion--summary,
183+
.collision-inspector-v2__panel--right .collision-inspector-v2__accordion--logs {
184+
flex: 1 1 0;
185+
display: flex;
186+
flex-direction: column;
187+
}
188+
189+
.collision-inspector-v2__panel--right .collision-inspector-v2__accordion:not(.is-open) {
190+
flex: 0 0 auto;
191+
}
192+
179193
.collision-inspector-v2__field {
180194
display: grid;
181195
gap: 6px;
@@ -265,7 +279,7 @@ body[data-tool-id="collision-inspector-v2"] {
265279

266280
.collision-inspector-v2__legend {
267281
display: grid;
268-
grid-template-columns: repeat(3, 1fr);
282+
grid-template-columns: repeat(4, 1fr);
269283
gap: 8px;
270284
font-size: 0.83rem;
271285
color: var(--collision-inspector-muted);
@@ -294,6 +308,13 @@ body[data-tool-id="collision-inspector-v2"] {
294308
background: var(--collision-inspector-hit);
295309
}
296310

311+
.collision-inspector-v2__legend span:nth-child(4)::before {
312+
width: 18px;
313+
height: 2px;
314+
border-radius: 0;
315+
background: #f8fafc;
316+
}
317+
297318
.collision-inspector-v2__result {
298319
display: grid;
299320
place-items: center;
@@ -324,7 +345,7 @@ body[data-tool-id="collision-inspector-v2"] {
324345
display: grid;
325346
grid-template-columns: 1fr;
326347
gap: 8px;
327-
margin: 0 0 12px;
348+
margin: 0;
328349
}
329350

330351
.collision-inspector-v2__metrics div {
@@ -361,7 +382,14 @@ body[data-tool-id="collision-inspector-v2"] {
361382

362383
.collision-inspector-v2__summary-content {
363384
min-height: 0;
364-
overflow: auto;
385+
overflow-x: auto;
386+
overflow-y: scroll;
387+
}
388+
389+
.collision-inspector-v2__result-content {
390+
min-height: 0;
391+
overflow-x: hidden;
392+
overflow-y: auto;
365393
}
366394

367395
.collision-inspector-v2__summary-content .collision-inspector-v2__output {
@@ -370,6 +398,11 @@ body[data-tool-id="collision-inspector-v2"] {
370398
overflow: visible;
371399
}
372400

401+
.collision-inspector-v2__log-clear {
402+
flex: 0 0 auto;
403+
align-self: end;
404+
}
405+
373406
#collisionLog {
374407
min-height: 0;
375408
flex: 1 1 auto;
@@ -383,12 +416,6 @@ body[data-tool-id="collision-inspector-v2"] {
383416
gap: 8px;
384417
}
385418

386-
.collision-inspector-v2__status-actions {
387-
display: inline-flex;
388-
align-items: center;
389-
gap: 8px;
390-
}
391-
392419
@media (max-width: 1060px) {
393420
.collision-inspector-v2.app-shell {
394421
grid-template-columns: 1fr;

0 commit comments

Comments
 (0)