From f00c4a746069bb25906f9b58469443dcdcd1c921 Mon Sep 17 00:00:00 2001 From: Ty Date: Mon, 14 Sep 2026 11:23:00 -0700 Subject: [PATCH] UAT-8a: rotate the rect, not a separate axis vector reduceAxisState 'rotate' now changes state.rect.angle and re-derives state.axis via axisFromRect(newRect). The rectangle rotates as a whole; the axis for Measure is derived from the rotated rect's long side. 2 new regression tests: rect.angle changes on rotate, axis is re-derived from rotated rect. 109/109 tests green. --- frontend/src/lib/core/axis.js | 6 ++++-- frontend/tests/unit/axis.test.js | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/core/axis.js b/frontend/src/lib/core/axis.js index 97f0dc2..bbd5c15 100644 --- a/frontend/src/lib/core/axis.js +++ b/frontend/src/lib/core/axis.js @@ -274,8 +274,10 @@ export function reduceAxisState(state, action) { case 'rotate': { const angle = action.angle; if (angle === 0) return state; - const newAxis = rotateAxis(state.axis, angle); - return { ...state, axis: newAxis, approved: false }; + // Rotate the RECTANGLE, not a separate axis vector. The rect's angle + // changes, and the axis is re-derived from the rotated rect. + const newRect = { ...state.rect, angle: state.rect.angle + angle }; + return { ...state, rect: newRect, axis: axisFromRect(newRect), approved: false }; } case 'approve': { return { ...state, approved: true }; diff --git a/frontend/tests/unit/axis.test.js b/frontend/tests/unit/axis.test.js index 8ac01af..467f738 100644 --- a/frontend/tests/unit/axis.test.js +++ b/frontend/tests/unit/axis.test.js @@ -319,6 +319,25 @@ describe('reduceAxisState', () => { expect(s.axis.y).toBeCloseTo(1, 6); }); + it('rotate changes the rect angle (not just a separate axis vector)', () => { + let s = createAxisState(rect); + const originalAngle = s.rect.angle; + s = reduceAxisState(s, { type: 'rotate', angle: Math.PI / 4 }); + // The rect itself must have rotated + expect(s.rect.angle).toBeCloseTo(originalAngle + Math.PI / 4, 6); + // And the axis is re-derived from the rotated rect + expect(s.axis).toEqual(axisFromRect(s.rect)); + }); + + it('rotate by 90 deg: rect angle changes, axis is perpendicular to original', () => { + let s = createAxisState(rect); + s = reduceAxisState(s, { type: 'rotate', angle: Math.PI / 2 }); + expect(s.rect.angle).toBeCloseTo(Math.PI / 2, 6); + // Original axis was +x (1,0). After 90 deg rect rotation, axis is +y (0,1) + expect(s.axis.x).toBeCloseTo(0, 6); + expect(s.axis.y).toBeCloseTo(1, 6); + }); + it('rotate composes (rotates from current axis)', () => { let s = createAxisState(rect); s = reduceAxisState(s, { type: 'rotate', angle: Math.PI / 2 });