fix(capture): ±90° tilt slider midpoint + two-finger rotate

Clamp tape tilt to [-90, 90] so the slider knob starts centered and
CCW works. PhotoStage pinch now tracks angle as well as zoom.
This commit is contained in:
grokbot_ty
2026-09-26 07:54:36 -07:00
parent ba30a340e6
commit b841b16cc0
5 changed files with 62 additions and 22 deletions
+7 -6
View File
@@ -12,7 +12,7 @@ import { encodeInventoryPng, type InventoryInput } from '../png/inventory';
import { encodeOverlayPng, type EncodeOverlayInput } from '../png/overlay';
import { rotateCalibration } from '../png/rotateCal';
import { decodeOrientedBitmap } from '../decodeOrientedBitmap';
import { defaultTape, tapeEndpoints, type TapeTransform } from './tapeGeometry';
import { clampTiltDeg, defaultTape, tapeEndpoints, type TapeTransform } from './tapeGeometry';
import { getCropFill } from '../settings/cropFill';
import type { TapeLine } from './capture.types';
@@ -323,7 +323,7 @@ export function createCaptureSession(opts: SessionOpts = {}): CaptureSession {
return { ...d.tape };
}
// Seed tilt so the mask long side sits near vertical under the fixed line.
const seedTilt = 90 - bbox.angleDeg;
const seedTilt = clampTiltDeg(90 - bbox.angleDeg);
const t = defaultTape(getCropFill(), seedTilt);
const ep = w > 0 && h > 0 ? tapeEndpoints(t, w, h) : { spanPx: 0, start: [0, 0] as [number, number], end: [0, 0] as [number, number], ux: 0, uy: 1 };
return { ...t, spanPx: ep.spanPx };
@@ -338,7 +338,7 @@ export function createCaptureSession(opts: SessionOpts = {}): CaptureSession {
: bbox.angleDeg;
const nextBbox = orientedRect(bbox, angleDeg);
const line: TapeLine = {
tiltDeg: tape.tiltDeg,
tiltDeg: clampTiltDeg(tape.tiltDeg),
zoom: tape.zoom,
panX: tape.panX,
panY: tape.panY,
@@ -534,7 +534,7 @@ export function createCaptureSession(opts: SessionOpts = {}): CaptureSession {
};
const w = state.source.widthPx;
const h = state.source.heightPx;
const seedTilt = 90 - bbox.angleDeg;
const seedTilt = clampTiltDeg(90 - bbox.angleDeg);
const tape0 = defaultTape(getCropFill(), seedTilt);
const axis = axisWithTape(bbox, tape0, w, h);
set({ k: 'axis', source: state.source, approvedMask: overlay, axis });
@@ -544,7 +544,7 @@ export function createCaptureSession(opts: SessionOpts = {}): CaptureSession {
// Legacy slider path — maps to photo tilt under the fixed tape line (#99).
setAxisAngle(deg: number) {
if (state.k !== 'axis') return;
const tape = { ...state.axis.tape, tiltDeg: deg };
const tape = { ...state.axis.tape, tiltDeg: clampTiltDeg(deg) };
const axis = axisWithTape(state.axis.bbox, tape, state.source.widthPx, state.source.heightPx);
set({ ...state, axis });
void persist();
@@ -552,7 +552,8 @@ export function createCaptureSession(opts: SessionOpts = {}): CaptureSession {
setTape(tape: TapeTransform) {
if (state.k !== 'axis') return;
const axis = axisWithTape(state.axis.bbox, tape, state.source.widthPx, state.source.heightPx);
const clamped = { ...tape, tiltDeg: clampTiltDeg(tape.tiltDeg) };
const axis = axisWithTape(state.axis.bbox, clamped, state.source.widthPx, state.source.heightPx);
set({ ...state, axis });
void persist();
},