fix(measure): show masked calibration preview

This commit is contained in:
grokbot_ty
2026-09-26 08:38:17 -07:00
parent 0dd5d255ec
commit 89bc7d4ee9
3 changed files with 116 additions and 10 deletions
+50 -10
View File
@@ -8,6 +8,7 @@
import PhotoStage from './PhotoStage.svelte';
import { getCropFill, setCropFill } from '../settings/cropFill';
import { cropMarginFraction } from '../png/crop';
import { createMaskedPreviewBlob } from '../png/maskedPreview';
import { getTaxonomy } from '../api/slabs';
import { speciesLeavesFromTaxonomy } from '../create/settings/speciesLeaves';
import {
@@ -41,6 +42,40 @@
$: tape = state.axis.tape;
$: tiltShown = Math.round(tape.tiltDeg * 10) / 10;
let maskedPreview: Blob | null = null;
let previewError = '';
let previewGeneration = 0;
let previewSource: Blob | null = null;
let previewMask: Uint8Array | null = null;
let previewW = 0;
let previewH = 0;
// Build the tape photo once from the oriented source plus the approved mask.
// Never fall back to the raw source here: tape alignment must show the same
// background-removed slab footprint that was approved in Mask.
$: {
const sourceBlob = state.source.blob;
const bitset = state.approvedMask.bitset;
const width = state.source.widthPx;
const height = state.source.heightPx;
if (sourceBlob !== previewSource || bitset !== previewMask || width !== previewW || height !== previewH) {
previewSource = sourceBlob;
previewMask = bitset;
previewW = width;
previewH = height;
maskedPreview = null;
previewError = '';
const generation = ++previewGeneration;
void createMaskedPreviewBlob(sourceBlob, bitset)
.then((blob) => {
if (generation === previewGeneration) maskedPreview = blob;
})
.catch(() => {
if (generation === previewGeneration) previewError = 'Could not build the background-removed preview.';
});
}
}
let lineupUrls: string[] = [];
let lineupItems: PhotosReadyLineupItem[] = [];
@@ -84,6 +119,7 @@
});
onDestroy(() => {
previewGeneration += 1;
for (const u of lineupUrls) URL.revokeObjectURL(u);
});
@@ -154,16 +190,19 @@
<p class="hint">Orient the slab on the fixed tape line, then enter length, thickness, and species.</p>
<p class="nudge">The black line is <b>fixed</b>: tilting, dragging and zooming the slab never move it. It stays in the middle of the rectangle.</p>
<PhotoStage
mode="tape"
blob={state.source.blob}
maskBitset={state.approvedMask.bitset}
{maskW}
{maskH}
{tape}
{cropFill}
onTapeChange={onTapeChange}
/>
{#if maskedPreview}
<PhotoStage
mode="tape"
blob={maskedPreview}
{tape}
{cropFill}
onTapeChange={onTapeChange}
/>
{:else}
<div class="preview-loading" role="status">
{previewError || 'Preparing background-removed preview…'}
</div>
{/if}
<div class="controls">
<div>
@@ -344,6 +383,7 @@
h1 { font-size: 1.3rem; margin: 0 0 8px; }
.hint { color: #aaa; font-size: 0.9rem; margin: 0 0 8px; }
.nudge { color: #e8a; font-size: 0.74rem; margin: 6px 0 0; line-height: 1.45; }
.preview-loading { min-height: 420px; border: 1px solid #333; border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #999; text-align: center; padding: 16px; box-sizing: border-box; }
.controls { margin-top: 16px; display: flex; flex-direction: column; gap: 14px; }
.ctl-label { font-size: 0.78rem; color: #aaa; margin: 0 0 6px; }
.ctl-label .sub { font-size: 0.7rem; color: #777; }