fix(fe): show the sheet-removed image + length-axis line on the axis step
The axis step rendered only text and buttons - no image, so the owner had nothing to confirm the length axis against and 'handing to step 4' felt like a dead end. Render the calibration photo with the sheet checkerboarded (using the frozen approvedMask) and draw the length-axis tape line in red from axis.tapeLine.start to .end (source-pixel coords scaled to the canvas), so the BG-removed slab and its axis are visible before approving.
This commit is contained in:
@@ -68,6 +68,70 @@
|
||||
ctx.putImageData(imgData, 0, 0);
|
||||
}
|
||||
|
||||
// Axis preview: the sheet-removed photo with the length-axis tape line
|
||||
// (red) drawn from axis.tapeLine.start to .end in source-pixel coords.
|
||||
let axisCanvas: HTMLCanvasElement;
|
||||
let axisImg: HTMLImageElement;
|
||||
let axisObjUrl = '';
|
||||
|
||||
$: if (kind === 'axis' && cap.source) {
|
||||
if (axisObjUrl) URL.revokeObjectURL(axisObjUrl);
|
||||
axisObjUrl = URL.createObjectURL(cap.source.blob);
|
||||
}
|
||||
|
||||
$: if (kind === 'axis' && axisImg?.complete && axisImg.naturalWidth > 0) {
|
||||
drawAxisPreview();
|
||||
}
|
||||
|
||||
function drawAxisPreview() {
|
||||
const canvas = axisCanvas;
|
||||
if (!canvas || !axisImg || !cap.approvedMask) return;
|
||||
const w = axisImg.naturalWidth;
|
||||
const h = axisImg.naturalHeight;
|
||||
const maxDim = 900;
|
||||
const scale = Math.min(1, maxDim / Math.max(w, h));
|
||||
canvas.width = Math.round(w * scale);
|
||||
canvas.height = Math.round(h * scale);
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
ctx.drawImage(axisImg, 0, 0, canvas.width, canvas.height);
|
||||
// Checkerboard the removed (non-slab) pixels.
|
||||
const bitset = cap.approvedMask.bitset;
|
||||
const cw = canvas.width;
|
||||
const ch = canvas.height;
|
||||
const cell = 6;
|
||||
const imgData = ctx.getImageData(0, 0, cw, ch);
|
||||
const px = imgData.data;
|
||||
for (let y = 0; y < ch; y++) {
|
||||
for (let x = 0; x < cw; x++) {
|
||||
const sx = Math.min(w - 1, Math.round((x / cw) * w));
|
||||
const sy = Math.min(h - 1, Math.round((y / ch) * h));
|
||||
if (bitset[sy * w + sx]) continue;
|
||||
const checker = ((Math.floor(x / cell) + Math.floor(y / cell)) % 2 === 0);
|
||||
const i = (y * cw + x) * 4;
|
||||
const g = checker ? 210 : 235;
|
||||
px[i] = g; px[i + 1] = g; px[i + 2] = g; px[i + 3] = 255;
|
||||
}
|
||||
}
|
||||
ctx.putImageData(imgData, 0, 0);
|
||||
// Draw the tape line (length axis) in red.
|
||||
const [x1, y1] = cap.axis.tapeLine.start;
|
||||
const [x2, y2] = cap.axis.tapeLine.end;
|
||||
ctx.strokeStyle = '#ff3b30';
|
||||
ctx.lineWidth = Math.max(2, canvas.width / 300);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo((x1 / w) * cw, (y1 / h) * ch);
|
||||
ctx.lineTo((x2 / w) * cw, (y2 / h) * ch);
|
||||
ctx.stroke();
|
||||
// Endpoint dots
|
||||
ctx.fillStyle = '#ff3b30';
|
||||
for (const [ex, ey] of [[x1, y1], [x2, y2]] as [number, number][]) {
|
||||
ctx.beginPath();
|
||||
ctx.arc((ex / w) * cw, (ey / h) * ch, Math.max(3, canvas.width / 150), 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
let fileInput: HTMLInputElement;
|
||||
|
||||
// Auto-run the mask so the overlay (and preview) populate without the owner
|
||||
@@ -192,7 +256,13 @@
|
||||
|
||||
{:else if kind === 'axis'}
|
||||
<h2>4. Length axis</h2>
|
||||
<p>Place the tape endpoints at the two ends of the slab's length.</p>
|
||||
<p>The sheet is removed. The red line marks the slab's length axis — confirm it runs end-to-end, then approve.</p>
|
||||
{#if cap.source}
|
||||
<div class="mask-preview">
|
||||
<canvas bind:this={axisCanvas} aria-label="axis preview"></canvas>
|
||||
<img bind:this={axisImg} src={axisObjUrl} alt="calibration photo (hidden, drives axis preview)" class="mask-src" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="actions">
|
||||
<button class="primary" on:click={() => session.approveAxis()}>Approve axis →</button>
|
||||
<button on:click={() => session.back()}>← Back</button>
|
||||
|
||||
Reference in New Issue
Block a user