fix: track 5 frames works for bbox and polygon shapes (REQ-189)
trackForward read geometry.coordinates, a key the backend never emits, and had no bbox branch - the action silently no-op'd for every shape (flagged dead in docs/audit-2026-08-07.md:103). - bbox points used as-is, polygon reduced to its bounding box - guards speak instead of returning silently - a frame SAM3 refuses no longer aborts the remaining frames - a press during an in-flight run is ignored (would double-write) - REQ-189 added; non-goal requirements.md:22 amended to keep exemplar propagation out while admitting this one-shot hand-off
This commit is contained in:
1 parent
2971f9192c
commit
b71f1d4bf2
4 files changed
+79
-24
No files matched your search
@@ -355,15 +355,27 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
|
||||
}, [frames, index, frame, hiddenClasses])
|
||||
|
||||
const trackForward = useCallback(async () => {
|
||||
if (selectedId == null || !frames?.length) return
|
||||
if (busy) return
|
||||
if (!frames?.length) return
|
||||
if (selectedId == null) {
|
||||
setError('Select a shape first — Track 5 Frames follows one shape forward.')
|
||||
return
|
||||
}
|
||||
const current = annotations.find((a) => a.id === selectedId)
|
||||
if (!current) return
|
||||
|
||||
if (!current) {
|
||||
setError('That shape is gone — select another one.')
|
||||
return
|
||||
}
|
||||
|
||||
// SAM3 assist wants one normalized [x0, y0, x1, y1] box (REQ-043). A bbox
|
||||
// shape already is that; a polygon is reduced to its bounding box.
|
||||
const geometry = current.geometry
|
||||
let box = null
|
||||
if (current.geometry?.type === 'polygon' && current.geometry.coordinates?.[0]) {
|
||||
const pts = current.geometry.coordinates[0]
|
||||
if (geometry?.type === 'bbox' && geometry.points?.length === 4) {
|
||||
box = geometry.points
|
||||
} else if (geometry?.type === 'polygon' && geometry.points?.length) {
|
||||
let minX = 1, minY = 1, maxX = 0, maxY = 0
|
||||
for (const [x, y] of pts) {
|
||||
for (const [x, y] of geometry.points) {
|
||||
if (x < minX) minX = x
|
||||
if (x > maxX) maxX = x
|
||||
if (y < minY) minY = y
|
||||
@@ -371,27 +383,38 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
|
||||
}
|
||||
box = [minX, minY, maxX, maxY]
|
||||
}
|
||||
if (!box) return
|
||||
if (!box) {
|
||||
setError('That shape has no usable box — Track 5 Frames needs one.')
|
||||
return
|
||||
}
|
||||
const ahead = frames.slice(index + 1, index + 6)
|
||||
if (!ahead.length) {
|
||||
setError('Last frame — Track 5 Frames has no frames ahead to follow into.')
|
||||
return
|
||||
}
|
||||
|
||||
setBusy(true)
|
||||
setError('')
|
||||
try {
|
||||
let updatedCount = 0
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
const nextIdx = index + i
|
||||
if (nextIdx >= frames.length) break
|
||||
const targetFrame = frames[nextIdx]
|
||||
|
||||
await api.assist(targetFrame.id, { box, class_id: current.class_id })
|
||||
updatedCount++
|
||||
patchFrameLocally(targetFrame.id, { annotation_count: (targetFrame.annotation_count ?? 0) + 1 })
|
||||
let tracked = 0
|
||||
const failures = []
|
||||
for (let i = 0; i < ahead.length; i++) {
|
||||
const targetFrame = ahead[i]
|
||||
try {
|
||||
await api.assist(targetFrame.id, { box, class_id: current.class_id })
|
||||
tracked++
|
||||
patchFrameLocally(targetFrame.id, { annotation_count: (targetFrame.annotation_count ?? 0) + 1 })
|
||||
} catch (exc) {
|
||||
failures.push(`frame ${index + i + 2}: ${exc.message}`)
|
||||
}
|
||||
}
|
||||
if (failures.length) {
|
||||
setError(`Tracked ${tracked} of ${ahead.length} — ${failures.join(' · ')}`)
|
||||
}
|
||||
} catch (exc) {
|
||||
setError(exc.message)
|
||||
} finally {
|
||||
setBusy(false)
|
||||
}
|
||||
}, [annotations, selectedId, index, frames])
|
||||
}, [annotations, selectedId, index, frames, busy])
|
||||
|
||||
const stateRef = useRef({})
|
||||
stateRef.current = { frames, index, project, selectedId, setStatus, removeSelected, reclass, jumpToPending, jumpToNextAnnotated, setAssistMode, copyPrevious, trackForward, mode, setMode, markedIds, removeMarked, reclassMarked, setMarkedIds, toggleHideSelected }
|
||||
|
||||
Reference in new issue
Block a user