Skip to content

Commit e7bf169

Browse files
author
DavidQ
committed
Normalize vector tool zoom controls to percent defaults - PR_26140_049-normalize-vector-tool-zoom-percent
1 parent 3d090ed commit e7bf169

3 files changed

Lines changed: 65 additions & 35 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# PR_26140_049-normalize-vector-tool-zoom-percent
2+
3+
## Summary
4+
- Normalized Object Vector Studio V2 zoom semantics to 100% default with a 10% to 1000% range.
5+
- Preserved existing Object Vector Studio viewport/viewBox behavior with an internal viewBox scale while keeping the stored zoom default at 1.0.
6+
- Updated Collision Inspector V2 zoom controls, display, clamping, and live summary output to use percent semantics.
7+
- Updated targeted browser assertions and logical-point helpers so tool interactions validate the normalized zoom contract.
8+
9+
## Validation
10+
- PASS: node --check for changed tool/test JavaScript files.
11+
- PASS: targeted ES module import validation for changed tool modules.
12+
- PASS: targeted static zoom validation for Object Vector Studio V2 and Collision Inspector V2.
13+
- PASS: npx playwright test tests/playwright/tools/CollisionInspectorV2.spec.mjs --project=playwright --workers=1 --reporter=list.
14+
- PASS: npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list --grep "shows Object Vector Studio V2 layout shell".
15+
- PASS: npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list --grep "creates Object Vector Studio V2 shapes with canvas drawing|edits Object Vector Studio V2 preview shapes|aligns Object Vector Studio V2 selection bounds|expands Object Vector Studio V2 asset authoring controls|drags selected Object Vector Studio V2 shapes|tracks Object Vector Studio V2 dirty state".
16+
- PASS: npm run test:workspace-v2, 58 passed.
17+
- SKIPPED: full samples smoke test, per request.
18+
19+
## Notes
20+
- An initial workspace run failed after raw 1.0 zoom changed the physical editor viewBox. The final implementation keeps 100% semantic zoom while preserving previous viewport scale, then the full workspace suite passed.
21+
- No sample JSON files are included in the final delta.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,9 @@ async function objectVectorLogicalClientPoint(page, x, y) {
107107
return page.locator("#objectVectorStudioV2RenderSurface").evaluate((surface, point) => {
108108
const app = window.__objectVectorStudioV2App;
109109
const bounds = surface.getBoundingClientRect();
110-
const viewWidth = 320 / app.viewport.zoom;
111-
const viewHeight = 220 / app.viewport.zoom;
110+
const viewBoxZoom = app.viewBoxZoom();
111+
const viewWidth = 320 / viewBoxZoom;
112+
const viewHeight = 220 / viewBoxZoom;
112113
const drawingX = point.x * 10;
113114
const drawingY = point.y * 10;
114115
return {
@@ -2308,25 +2309,25 @@ test.describe("Workspace Manager V2 bootstrap", () => {
23082309
chipsSameRow: true,
23092310
inputAndAddInline: true
23102311
});
2311-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-160 -110 320 220");
2312+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1600 -1100 3200 2200");
23122313
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-center-origin='0,0']")).toHaveCount(1);
23132314
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-center-origin='0,0']")).toHaveAttribute("r", "9");
23142315
await expect(page.locator("#objectVectorStudioV2ViewportControls button")).toHaveText(["Out", "In", "Up", "Down", "Left", "Right", "View", "Center"]);
23152316
await expect(page.locator("#objectVectorStudioV2CenterDotButton")).toHaveAttribute("aria-pressed", "true");
23162317
await page.locator("#objectVectorStudioV2PanRightButton").click();
23172318
await page.locator("#objectVectorStudioV2PanDownButton").click();
23182319
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 2, 2 | Canvas origin -2,-2 from center | Zoom 100%");
2319-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-140 -90 320 220");
2320+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1580 -1080 3200 2200");
23202321
await page.locator("#objectVectorStudioV2RenderSurface").hover({ position: { x: 12, y: 12 } });
23212322
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toContainText("Pointer");
23222323
await page.locator("#objectVectorStudioV2CenterDotButton").click();
2323-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-140 -90 320 220");
2324+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1580 -1080 3200 2200");
23242325
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toContainText("Canvas origin -2,-2 from center | Zoom 100%");
23252326
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-center-origin='0,0']")).toHaveCount(0);
23262327
await expect(page.locator("#objectVectorStudioV2CenterDotButton")).toHaveAttribute("aria-pressed", "false");
23272328
await expect(page.locator("#statusLog")).toHaveValue(/OK Center dot hidden\./);
23282329
await page.locator("#objectVectorStudioV2ResetViewButton").click();
2329-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-160 -110 320 220");
2330+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1600 -1100 3200 2200");
23302331
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 0, 0 | Canvas origin 0,0 centered | Zoom 100%");
23312332
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-center-origin='0,0']")).toHaveCount(0);
23322333
await page.locator("#objectVectorStudioV2CenterDotButton").click();
@@ -3436,6 +3437,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
34363437

34373438
const zoomSource = await readFile("tools/object-vector-studio-v2/js/ToolStarterApp.js", "utf8");
34383439
expect(zoomSource).toContain("const DEFAULT_VIEWPORT = Object.freeze({ height: 220, width: 320, x: 0, y: 0, zoom: 1.0 });");
3440+
expect(zoomSource).toContain("const VIEWPORT_ZOOM_VIEWBOX_SCALE = 0.1;");
34393441
expect(zoomSource).toContain("const MAX_ZOOM = 10.0;");
34403442
expect(zoomSource).toContain("const MIN_ZOOM = 0.1;");
34413443
expect(zoomSource).toContain("const ZOOM_STEP = 0.1;");
@@ -3448,12 +3450,12 @@ test.describe("Workspace Manager V2 bootstrap", () => {
34483450

34493451
await page.locator("#objectVectorStudioV2ZoomInButton").click();
34503452
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toContainText("Zoom 110%");
3451-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-145.455 -100 290.909 200");
3453+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1454.545 -1000 2909.091 2000");
34523454
await page.locator("#objectVectorStudioV2PanRightButton").click();
3453-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-125.455 -100 290.909 200");
3455+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1434.545 -1000 2909.091 2000");
34543456
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 2, 0 | Canvas origin -2,0 from center | Zoom 110%");
34553457
await page.locator("#objectVectorStudioV2ResetViewButton").click();
3456-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-160 -110 320 220");
3458+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1600 -1100 3200 2200");
34573459
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 0, 0 | Canvas origin 0,0 centered | Zoom 100%");
34583460
await expect(page.locator("#statusLog")).toHaveValue(/OK Viewport reset to 100% at origin 0,0\./);
34593461
await page.evaluate(() => {
@@ -3465,14 +3467,14 @@ test.describe("Workspace Manager V2 bootstrap", () => {
34653467
window.__objectVectorStudioV2App.zoomViewport(2.5);
34663468
});
34673469
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 0, 0 | Canvas origin 0,0 centered | Zoom 250%");
3468-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-64 -44 128 88");
3470+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-640 -440 1280 880");
34693471
await page.evaluate(() => {
34703472
window.__objectVectorStudioV2App.zoomViewport(0);
34713473
});
34723474
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 0, 0 | Canvas origin 0,0 centered | Zoom 10%");
3473-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1600 -1100 3200 2200");
3475+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-16000 -11000 32000 22000");
34743476
await page.locator("#objectVectorStudioV2ResetViewButton").click();
3475-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-160 -110 320 220");
3477+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1600 -1100 3200 2200");
34763478
const wheelAnchor = await page.locator("#objectVectorStudioV2RenderSurface").evaluate((surface) => {
34773479
const rect = surface.getBoundingClientRect();
34783480
const clientX = Math.round(rect.left + rect.width * 0.75);
@@ -3508,14 +3510,14 @@ test.describe("Workspace Manager V2 bootstrap", () => {
35083510
expect(zoomedGridState.pointerDelta.y).toBeLessThan(0.001);
35093511
expect(Math.abs(zoomedGridState.viewport.x)).toBeGreaterThan(1);
35103512
expect(Math.abs(zoomedGridState.viewport.y)).toBeGreaterThan(1);
3511-
expect(zoomedGridState.viewBox).not.toBe("-145.455 -100 290.909 200");
3513+
expect(zoomedGridState.viewBox).not.toBe("-1454.545 -1000 2909.091 2000");
35123514
await page.mouse.wheel(0, 240);
35133515
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toContainText("Zoom 100%");
3514-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-160 -110 320 220");
3516+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1600 -1100 3200 2200");
35153517
await page.evaluate(() => {
3516-
window.__objectVectorStudioV2App.zoomViewport(0.25);
3518+
window.__objectVectorStudioV2App.zoomViewport(2.5);
35173519
});
3518-
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toContainText("Zoom 25%");
3520+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toContainText("Zoom 250%");
35193521
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-640 -440 1280 880");
35203522
await page.locator("#objectVectorStudioV2ResetViewButton").click();
35213523

@@ -4724,11 +4726,11 @@ test.describe("Workspace Manager V2 bootstrap", () => {
47244726
await page.locator("#objectVectorStudioV2ShapeGeometryDetails [data-polygon-point-index='1'][data-polygon-point-axis='y']").fill("16");
47254727
await page.locator("#objectVectorStudioV2ShapeGeometryDetails [data-polygon-point-index='1'][data-polygon-point-axis='y']").dispatchEvent("change");
47264728
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-index='0']")).toHaveAttribute("points", "0,-180 140,160 0,80 -140,160");
4727-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-160 -110 320 220");
4729+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1600 -1100 3200 2200");
47284730
await page.evaluate(() => {
4729-
window.__objectVectorStudioV2App.zoomViewport(0.25);
4731+
window.__objectVectorStudioV2App.zoomViewport(2.5);
47304732
});
4731-
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toContainText("Zoom 25%");
4733+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toContainText("Zoom 250%");
47324734
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-640 -440 1280 880");
47334735

47344736
const readPreviewScale = async () => page.locator("#objectVectorStudioV2RenderSurface").evaluate((surface) => {
@@ -4815,16 +4817,16 @@ test.describe("Workspace Manager V2 bootstrap", () => {
48154817
});
48164818
await page.locator("#objectVectorStudioV2PanRightButton").click();
48174819
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-620 -440 1280 880");
4818-
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 2, 0 | Canvas origin -2,0 from center | Zoom 25%");
4820+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 2, 0 | Canvas origin -2,0 from center | Zoom 250%");
48194821
await page.locator("#objectVectorStudioV2PanLeftButton").click();
48204822
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-640 -440 1280 880");
4821-
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 0, 0 | Canvas origin 0,0 centered | Zoom 25%");
4823+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 0, 0 | Canvas origin 0,0 centered | Zoom 250%");
48224824
await page.locator("#objectVectorStudioV2PanUpButton").click();
48234825
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-640 -460 1280 880");
4824-
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 0, -2 | Canvas origin 0,2 from center | Zoom 25%");
4826+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 0, -2 | Canvas origin 0,2 from center | Zoom 250%");
48254827
await page.locator("#objectVectorStudioV2PanDownButton").click();
48264828
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-640 -440 1280 880");
4827-
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 0, 0 | Canvas origin 0,0 centered | Zoom 25%");
4829+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Origin: 0, 0 | Canvas origin 0,0 centered | Zoom 250%");
48284830
const afterPanShapeState = await page.evaluate(() => {
48294831
const shape = window.__objectVectorStudioV2App.selectedShape();
48304832
return {
@@ -4845,7 +4847,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
48454847
clientX: examplePointerScreen.x,
48464848
clientY: examplePointerScreen.y
48474849
});
4848-
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Pointer -14, -16 | Canvas origin 0,0 centered | Zoom 25%");
4850+
await expect(page.locator("#objectVectorStudioV2CoordinateDisplay")).toHaveText("Pointer -14, -16 | Canvas origin 0,0 centered | Zoom 250%");
48494851
await page.setViewportSize({ width: 1040, height: 720 });
48504852
const resizedPreviewScale = await readPreviewScale();
48514853
expect(resizedPreviewScale.aspectRatioStable).toBe(true);
@@ -4855,7 +4857,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
48554857
expect(resizedPreviewScale.gridStepRestored).toBe(true);
48564858
expect(resizedPreviewScale.pointsOnVisibleGridLines).toBe(true);
48574859
await page.locator("#objectVectorStudioV2ResetViewButton").click();
4858-
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-160 -110 320 220");
4860+
await expect(page.locator("#objectVectorStudioV2RenderSurface")).toHaveAttribute("viewBox", "-1600 -1100 3200 2200");
48594861
await page.locator("#objectVectorStudioV2ShapeGeometryDetails [data-polygon-point-delete='true'][data-polygon-point-index='0']").click();
48604862
await expect(page.locator("#objectVectorStudioV2ShapeGeometryDetails .object-vector-studio-v2__polygon-point-field")).toHaveCount(4);
48614863
await expect(page.locator("#objectVectorStudioV2ShapeGeometryDetails [data-polygon-point-delete='true'][data-polygon-point-index='0']")).toHaveAttribute("aria-invalid", "true");

tools/object-vector-studio-v2/js/ToolStarterApp.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const GRID_RENDER_SESSION_KEY = "object-vector-studio-v2.gridRender";
2828
const CENTER_ORIGIN_SESSION_KEY = "object-vector-studio-v2.centerOrigin";
2929
const SVG_NS = "http://www.w3.org/2000/svg";
3030
const DEFAULT_VIEWPORT = Object.freeze({ height: 220, width: 320, x: 0, y: 0, zoom: 1.0 });
31+
const VIEWPORT_ZOOM_VIEWBOX_SCALE = 0.1;
3132
const GRID_STEP = 10;
3233
const OBJECT_PREVIEW_DRAWING_SCALE = GRID_STEP;
3334
const MAX_ZOOM = 10.0;
@@ -3347,8 +3348,8 @@ export class ToolStarterApp {
33473348
if (!this.gridRenderEnabled) {
33483349
return;
33493350
}
3350-
const width = DEFAULT_VIEWPORT.width / this.viewport.zoom;
3351-
const height = DEFAULT_VIEWPORT.height / this.viewport.zoom;
3351+
const width = DEFAULT_VIEWPORT.width / this.viewBoxZoom();
3352+
const height = DEFAULT_VIEWPORT.height / this.viewBoxZoom();
33523353
const minX = this.viewport.x - width / 2;
33533354
const maxX = this.viewport.x + width / 2;
33543355
const minY = this.viewport.y - height / 2;
@@ -3598,8 +3599,8 @@ export class ToolStarterApp {
35983599
if (!bounds.width || !bounds.height) {
35993600
return null;
36003601
}
3601-
const viewWidth = DEFAULT_VIEWPORT.width / this.viewport.zoom;
3602-
const viewHeight = DEFAULT_VIEWPORT.height / this.viewport.zoom;
3602+
const viewWidth = DEFAULT_VIEWPORT.width / this.viewBoxZoom();
3603+
const viewHeight = DEFAULT_VIEWPORT.height / this.viewBoxZoom();
36033604
const drawingX = point.x * OBJECT_PREVIEW_DRAWING_SCALE;
36043605
const drawingY = point.y * OBJECT_PREVIEW_DRAWING_SCALE;
36053606
return {
@@ -4541,8 +4542,8 @@ export class ToolStarterApp {
45414542
}
45424543

45434544
updateViewport() {
4544-
const width = DEFAULT_VIEWPORT.width / this.viewport.zoom;
4545-
const height = DEFAULT_VIEWPORT.height / this.viewport.zoom;
4545+
const width = DEFAULT_VIEWPORT.width / this.viewBoxZoom();
4546+
const height = DEFAULT_VIEWPORT.height / this.viewBoxZoom();
45464547
const viewX = this.formatViewportNumber(this.viewport.x - width / 2);
45474548
const viewY = this.formatViewportNumber(this.viewport.y - height / 2);
45484549
this.elements.renderSurface.setAttribute("viewBox", `${viewX} ${viewY} ${this.formatViewportNumber(width)} ${this.formatViewportNumber(height)}`);
@@ -4579,6 +4580,10 @@ export class ToolStarterApp {
45794580
return Math.round(this.viewport.zoom * 100);
45804581
}
45814582

4583+
viewBoxZoom(zoom = this.viewport.zoom) {
4584+
return zoom * VIEWPORT_ZOOM_VIEWBOX_SCALE;
4585+
}
4586+
45824587
zoomViewportByStep(step) {
45834588
const nextZoom = Number((this.viewport.zoom + step).toFixed(3));
45844589
this.zoomViewport(nextZoom);
@@ -4611,8 +4616,9 @@ export class ToolStarterApp {
46114616
return;
46124617
}
46134618
const zoom = this.clampedViewportZoom(nextZoom);
4614-
const viewWidth = DEFAULT_VIEWPORT.width / zoom;
4615-
const viewHeight = DEFAULT_VIEWPORT.height / zoom;
4619+
const viewBoxZoom = this.viewBoxZoom(zoom);
4620+
const viewWidth = DEFAULT_VIEWPORT.width / viewBoxZoom;
4621+
const viewHeight = DEFAULT_VIEWPORT.height / viewBoxZoom;
46164622
this.viewport.zoom = zoom;
46174623
this.viewport.x = Number((anchor.x - (anchor.ratioX - 0.5) * viewWidth).toFixed(6));
46184624
this.viewport.y = Number((anchor.y - (anchor.ratioY - 0.5) * viewHeight).toFixed(6));
@@ -4666,8 +4672,9 @@ export class ToolStarterApp {
46664672
}
46674673
const ratioX = (clientX - bounds.left) / bounds.width;
46684674
const ratioY = (clientY - bounds.top) / bounds.height;
4669-
const viewWidth = DEFAULT_VIEWPORT.width / zoom;
4670-
const viewHeight = DEFAULT_VIEWPORT.height / zoom;
4675+
const viewBoxZoom = this.viewBoxZoom(zoom);
4676+
const viewWidth = DEFAULT_VIEWPORT.width / viewBoxZoom;
4677+
const viewHeight = DEFAULT_VIEWPORT.height / viewBoxZoom;
46714678
return {
46724679
ratioX,
46734680
ratioY,

0 commit comments

Comments
 (0)