diff --git a/frontend/src/views/CaptureOverlay.vue b/frontend/src/views/CaptureOverlay.vue index 633b75a..1408449 100644 --- a/frontend/src/views/CaptureOverlay.vue +++ b/frontend/src/views/CaptureOverlay.vue @@ -24,7 +24,7 @@ interface Rect { h: number } -type Tool = 'pen' | 'rect' | 'ellipse' | 'text' | 'mosaic-brush' | 'mosaic-rect' +type Tool = 'pen' | 'line' | 'arrow' | 'rect' | 'ellipse' | 'text' | 'mosaic-brush' | 'mosaic-rect' type MosaicMode = 'brush' | 'rect' interface Point { x: number @@ -150,18 +150,18 @@ const sizeLabel = computed(() => { return `${Math.round(rect.value.w)} × ${Math.round(rect.value.h)}` }) -const MARK_TOOLBAR_W = 212 +const MARK_TOOLBAR_W = 280 const ACTION_TOOLBAR_W = 288 const TOOLBAR_GROUP_GAP = 8 -const toolOrder: Tool[] = ['pen', 'rect', 'ellipse', 'text', 'mosaic-brush', 'mosaic-rect'] +const toolOrder: Tool[] = ['pen', 'line', 'arrow', 'rect', 'ellipse', 'text', 'mosaic-brush', 'mosaic-rect'] const toolSettingsPos = computed(() => { if (!leftToolbarPos.value || !rect.value || !activeTool.value) return null const isMosaic = activeTool.value.startsWith('mosaic-') const menuW = isMosaic ? (mosaicMode.value === 'brush' ? 328 : 112) : activeTool.value === 'text' ? 462 : 398 const menuH = 58 - const buttonIndex = isMosaic ? 4 : toolOrder.indexOf(activeTool.value) + const buttonIndex = isMosaic ? 6 : toolOrder.indexOf(activeTool.value) const buttonCenter = 4 + buttonIndex * 34 + 14 let x = leftToolbarPos.value.x let y = leftToolbarPos.value.y + 44 @@ -315,6 +315,28 @@ function textRenderBox(annotation: Annotation) { } } +function arrowHeadPoints(annotation: Annotation) { + if (annotation.points.length < 2) return '' + const start = annotation.points[0] + const end = annotation.points[annotation.points.length - 1] + const dx = end.x - start.x + const dy = end.y - start.y + const length = Math.hypot(dx, dy) + if (length < 1) return '' + const headLength = Math.min(length * 0.45, Math.max(10, (annotation.strokeWidth || 3) * 4)) + const angle = Math.atan2(dy, dx) + const spread = Math.PI / 6 + const left = { + x: end.x - headLength * Math.cos(angle - spread), + y: end.y - headLength * Math.sin(angle - spread), + } + const right = { + x: end.x - headLength * Math.cos(angle + spread), + y: end.y - headLength * Math.sin(angle + spread), + } + return `${left.x},${left.y} ${end.x},${end.y} ${right.x},${right.y}` +} + function textAnnotationIndexAtPoint(p: Point) { if (!rect.value) return null for (let i = annotations.value.length - 1; i >= 0; i--) { @@ -795,6 +817,35 @@ onUnmounted(() => { stroke-linejoin="round" fill="none" /> + + + + + { + +