Skip to content

Commit 0bcb424

Browse files
author
DavidQ
committed
Align Collision Inspector V2 with shared engine collision and refactor tool structure - PR_26139_004-collision-inspector-engine-alignment
1 parent 8a3ccce commit 0bcb424

30 files changed

Lines changed: 1261 additions & 858 deletions
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# PR_26139_004-collision-inspector-engine-alignment Report
2+
3+
## Summary
4+
5+
Aligned Collision Inspector V2 with shared engine Object Vector collision logic and kept Asteroids collision consumers on the same manifest geometry path. The inspector now calls `src/engine/collision/objectVector.js` for bounds, vector, pixel/sprite, and hybrid checks instead of maintaining its own competing polygon/mask implementation.
6+
7+
Playwright impacted: Yes.
8+
9+
## Scope Completed
10+
11+
- Added shared Object Vector collision support under `src/engine/collision/objectVector.js` and exported it through `src/engine/collision/index.js`.
12+
- Moved reusable Object Vector geometry normalization, transformed points, bounds, raster-mask, and collision-mode recommendation logic into the engine.
13+
- Updated Collision Inspector V2 to consume `evaluateObjectVectorCollisionPair(...)` and shared engine diagnostics only.
14+
- Refactored `CollisionInspectorV2App.js` into one-class files:
15+
- `CollisionInspectorV2App.js`
16+
- `CollisionInspectorV2Controls.js`
17+
- `CollisionInspectorV2Logger.js`
18+
- `CollisionInspectorV2ManifestLoader.js`
19+
- `CollisionInspectorV2Renderer.js`
20+
- Kept `bootstrap.js` as a thin element/class wiring entry point.
21+
- Added inspector schema support with `tools/schemas/tools/collision-inspector-v2.schema.json` and wired it through `workspace.manifest.schema.json`.
22+
- Moved Collision Inspector V2 into the Workspace Manager V2 `Viewers` group and tools index viewer category.
23+
- Added object-pair collision-mode auto-selection with manual override.
24+
- Moved Collision Mode under Object B, moved Reset into the Collision Pair accordion, removed Copy Report, and added zoom.
25+
- Updated Asteroids entity/object-geometry collision transforms to use shared collision helpers while continuing to load manifest Object Vector Studio V2 objects only.
26+
27+
## Guardrails
28+
29+
- No hardcoded Asteroids object geometry was added.
30+
- No fallback/default vector maps were added.
31+
- Collision Inspector V2 uses manifest `objects[].shapes[]` geometry only.
32+
- Existing intentional ship flame flicker and asteroid scale tuning were preserved.
33+
34+
## Validation
35+
36+
PASS:
37+
38+
- `node --check` on touched Collision Inspector V2 modules and touched Asteroids entity modules.
39+
- `npx playwright test tests/playwright/tools/CollisionInspectorV2.spec.mjs --project=playwright --workers=1 --reporter=list`
40+
- 2 passed.
41+
- `node -e "import('./tests/final/PrecisionCollisionSystems.test.mjs').then(({ run }) => run())"`
42+
- `node -e "import('./tests/games/AsteroidsVectorTransforms.test.mjs').then(({ run }) => run())"`
43+
- `node -e "import('./tests/games/AsteroidsCollisionTimingStress.test.mjs').then(({ run }) => run())"`
44+
- `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list --grep "registers Workspace Manager V2 from the tools index"`
45+
- `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list --grep "uses header lifecycle controls and launches tools from fixed Workspace Manager V2 tiles"`
46+
- `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list --grep "loads Object Vector Studio V2 runtime assets into Asteroids gameplay rendering"`
47+
- `git diff --check`
48+
- Passed with line-ending warnings only.
49+
50+
FAIL, broader existing gate:
51+
52+
- `npm test`
53+
- Fails in `pretest` at `tools/dev/checkSharedExtractionGuard.mjs`.
54+
- Reported `185 unexpected violation(s)`, `baseline_expected=609`, `baseline_resolved=6`, `total_violations=848`.
55+
- The PR_003 Collision Inspector helper hits are removed; remaining failures are broad repository guard drift across existing game, sample, engine, and tool files.
56+
57+
FAIL, broader existing Workspace V2 suite:
58+
59+
- `npm run test:workspace-v2`
60+
- 54 passed, 2 failed.
61+
- Failing test: `validates optional Text to Speech V2 schema contract through Workspace Manager V2 schema`
62+
- Expected `activeContext.tools` to include `text2speech-V2`; received false.
63+
- Failing test: `tracks Object Vector Studio V2 dirty state through persisted edits and save outcomes`
64+
- Expected generated manifest schema validation failure; save succeeded.
65+
- Collision Inspector V2 category, schema, hydration, tile enablement, launch, and storage-session checks passed inside this run.
66+
67+
## Notes
68+
69+
- The targeted Asteroids collision stress fixture was adjusted to place the ship at the current manifest-accurate asteroid edge. The prior point was outside the restored Object Vector geometry and correctly produced no collision under the shared polygon path.
70+
- Full samples smoke test was not run; this PR is scoped to Collision Inspector V2, shared Object Vector collision, Asteroids targeted collision validation, and Workspace Manager launch wiring.

games/Asteroids/entities/Asteroid.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ David Quesenberry
55
Asteroid.js
66
*/
77
import { TAU, randomRange, wrap } from '../utils/math.js';
8-
import { transformPoints } from '../../../src/engine/rendering/index.js';
8+
import { transformCollisionPoints } from '../../../src/engine/collision/index.js';
99

1010
const SIZE_PROFILES = {
1111
SML: { id: 1 },
@@ -57,10 +57,11 @@ export default class Asteroid {
5757
if (!this.collisionPoints.length) {
5858
return [];
5959
}
60-
return transformPoints(this.collisionPoints, {
60+
return transformCollisionPoints(this.collisionPoints, {
6161
x: this.x,
6262
y: this.y,
6363
rotation: this.angle,
64+
rotationUnit: 'radians',
6465
scale: this.scale,
6566
});
6667
}

games/Asteroids/entities/Bullet.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ David Quesenberry
55
Bullet.js
66
*/
77
import { wrap } from '../utils/math.js';
8-
import { transformPoints } from '../../../src/engine/rendering/index.js';
8+
import { transformCollisionPoints } from '../../../src/engine/collision/index.js';
99

1010
function normalizePoints(points) {
1111
return Array.isArray(points)
@@ -41,9 +41,11 @@ export default class Bullet {
4141
}
4242

4343
getCollisionPolygon() {
44-
return transformPoints(this.collisionPoints, {
44+
return transformCollisionPoints(this.collisionPoints, {
4545
x: this.x,
4646
y: this.y,
47+
rotation: this.angle,
48+
rotationUnit: 'radians',
4749
});
4850
}
4951
}

games/Asteroids/entities/Ship.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ David Quesenberry
55
Ship.js
66
*/
77
import { wrap } from '../utils/math.js';
8-
import { transformPoints } from '../../../src/engine/rendering/index.js';
8+
import { transformCollisionPoints } from '../../../src/engine/collision/index.js';
99

1010
function normalizePoints(points) {
1111
return Array.isArray(points)
@@ -56,10 +56,11 @@ export default class Ship {
5656
}
5757

5858
getPoints() {
59-
return transformPoints(this.collisionPoints, {
59+
return transformCollisionPoints(this.collisionPoints, {
6060
x: this.x,
6161
y: this.y,
6262
rotation: this.angle,
63+
rotationUnit: 'radians',
6364
});
6465
}
6566
}

games/Asteroids/entities/Ufo.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Ufo.js
66
*/
77
import Bullet from './Bullet.js';
88
import { distance } from '../../../src/shared/utils/mathUtils.js';
9-
import { transformPoints } from '../../../src/engine/rendering/index.js';
9+
import { transformCollisionPoints } from '../../../src/engine/collision/index.js';
1010
import { randomRange } from '../utils/math.js';
1111

1212
const UFO_PROFILES = {
@@ -110,7 +110,7 @@ export default class Ufo {
110110
}
111111

112112
getCollisionPolygon() {
113-
return transformPoints(this.collisionPoints, {
113+
return transformCollisionPoints(this.collisionPoints, {
114114
x: this.x,
115115
y: this.y,
116116
});

games/Asteroids/game/asteroidObjectGeometry.js

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
ASTEROIDS_ASTEROID_SIZE_OBJECT_IDS,
33
} from './asteroidsObjectGeometryManifest.js';
4+
import { getObjectVectorCollisionOutlinePoints } from '../../../src/engine/collision/index.js';
45

56
const ASTEROID_SIZE_LABELS = Object.freeze({
67
1: 'SML',
@@ -12,18 +13,6 @@ function asArray(value) {
1213
return Array.isArray(value) ? value : [];
1314
}
1415

15-
function toSafeNumber(value, fallback = 0) {
16-
const numberValue = Number(value);
17-
return Number.isNaN(numberValue) || Math.abs(numberValue) === Infinity ? fallback : numberValue;
18-
}
19-
20-
function cleanPoint(point) {
21-
return {
22-
x: toSafeNumber(point?.x, 0),
23-
y: toSafeNumber(point?.y, 0),
24-
};
25-
}
26-
2716
function centerPoints(points) {
2817
if (!points.length) {
2918
return [];
@@ -45,21 +34,6 @@ function maxRadius(points) {
4534
return Math.max(...points.map(({ x, y }) => Math.sqrt(x * x + y * y)));
4635
}
4736

48-
function sortedShapes(object) {
49-
return [...asArray(object?.shapes)].sort((left, right) => (
50-
toSafeNumber(left?.order, 0) - toSafeNumber(right?.order, 0)
51-
));
52-
}
53-
54-
function extractPrimaryPolygonPoints(object) {
55-
const shape = sortedShapes(object).find((candidate) => (
56-
candidate?.visible !== false
57-
&& candidate?.tool === 'polygon'
58-
&& asArray(candidate?.geometry?.points).length >= 3
59-
));
60-
return asArray(shape?.geometry?.points).map(cleanPoint);
61-
}
62-
6337
function logProfileFailure(logger, message, details = {}) {
6438
if (!logger) {
6539
return;
@@ -84,7 +58,7 @@ function createProfilesFromObjects(objects, options = {}) {
8458
});
8559
return;
8660
}
87-
const points = centerPoints(extractPrimaryPolygonPoints(object));
61+
const points = centerPoints(getObjectVectorCollisionOutlinePoints(object));
8862
if (points.length < 3) {
8963
logProfileFailure(options.logger, `Asteroids asteroid geometry profile ${objectId} must contain visible polygon geometry with at least 3 points.`, {
9064
objectId,

games/Asteroids/game/asteroidsObjectGeometryManifest.js

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { getObjectVectorCollisionOutlinePoints } from '../../../src/engine/collision/index.js';
2+
13
const ASTEROIDS_OBJECT_VECTOR_TOOL_KEY = 'object-vector-studio-v2';
24
const OBJECT_VECTOR_PAYLOAD_KEYS = new Set(['version', 'toolId', 'name', 'objects']);
35

@@ -57,47 +59,8 @@ function oldObjectSignal(object) {
5759
return /(^|[.\s_-])(old|legacy|deprecated|archive|archived|renamed|stale)($|[.\s_-])/.test(text);
5860
}
5961

60-
function normalizePoint(point) {
61-
return {
62-
x: Number(point?.x ?? 0),
63-
y: Number(point?.y ?? 0),
64-
};
65-
}
66-
67-
function normalizePoints(points) {
68-
return Array.isArray(points)
69-
? points.map(normalizePoint).filter((point) => Number.isFinite(point.x) && Number.isFinite(point.y))
70-
: [];
71-
}
72-
73-
function shapeTool(shape) {
74-
return normalizeString(shape?.tool).toLowerCase();
75-
}
76-
77-
function sortedShapes(object) {
78-
return Array.isArray(object?.shapes)
79-
? [...object.shapes].sort((left, right) => Number(left?.order ?? 0) - Number(right?.order ?? 0))
80-
: [];
81-
}
82-
83-
function shapeGeometryPoints(shape) {
84-
const tool = shapeTool(shape);
85-
if (tool === 'polygon' || tool === 'polyline') {
86-
return normalizePoints(shape?.geometry?.points);
87-
}
88-
if (tool === 'line') {
89-
return normalizePoints([shape?.geometry?.point1, shape?.geometry?.point2]);
90-
}
91-
return [];
92-
}
93-
9462
function objectVectorGeometryPoints(object) {
95-
const shapes = sortedShapes(object).filter((shape) => shape?.visible !== false);
96-
const shape = shapes.find((candidate) => {
97-
const tool = shapeTool(candidate);
98-
return tool === 'polygon' || tool === 'polyline';
99-
}) || shapes.find((candidate) => shapeTool(candidate) === 'line');
100-
return shapeGeometryPoints(shape);
63+
return getObjectVectorCollisionOutlinePoints(object);
10164
}
10265

10366
function requiredObjectGeometryPointCount(objectId) {

src/engine/collision/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,15 @@ export { isColliding } from './aabb.js';
88
export { arePolygonsColliding, isPointInPolygon, getPolygonBounds } from './polygon.js';
99
export { createRasterMask, areMasksColliding, isPointInMask, getMaskBounds } from './raster.js';
1010
export { runHybridCollision, getCollisionBoundsFromPolygon } from './hybrid.js';
11+
export {
12+
createObjectVectorCollisionGeometry,
13+
evaluateObjectVectorCollisionPair,
14+
getObjectVectorCollisionOutlinePoints,
15+
getObjectVectorOrigin,
16+
normalizeObjectVectorCollisionMode,
17+
OBJECT_VECTOR_COLLISION_ENGINE_PATH,
18+
OBJECT_VECTOR_COLLISION_MODE_LABELS,
19+
OBJECT_VECTOR_COLLISION_MODES,
20+
recommendObjectVectorCollisionMode,
21+
transformCollisionPoints,
22+
} from './objectVector.js';

0 commit comments

Comments
 (0)