diff --git a/frontend/src/components/ImageCanvas/index.tsx b/frontend/src/components/ImageCanvas/index.tsx index 7bf4ffb..e1dc476 100644 --- a/frontend/src/components/ImageCanvas/index.tsx +++ b/frontend/src/components/ImageCanvas/index.tsx @@ -1,7 +1,7 @@ import useCanvasStore from '@/stores/useCanvasStore'; import Konva from 'konva'; import { useEffect, useRef, useState } from 'react'; -import { Image, Layer, Rect, Stage } from 'react-konva'; +import { Circle, Image, Layer, Line, Rect, Stage } from 'react-konva'; import useImage from 'use-image'; import { Label } from '@/types'; import LabelRect from './LabelRect'; @@ -47,7 +47,9 @@ export default function ImageCanvas() { const [selectedId, setSelectedId] = useState(null); const [image, imageStatus] = useImage(imageUrl); const [rectPoints, setRectPoints] = useState<[number, number][]>([]); + const [polygonPoints, setPolygonPoints] = useState<[number, number][]>([]); const drawState = useCanvasStore((state) => state.drawState); + const setDrawState = useCanvasStore((state) => state.setDrawState); const addLabel = useCanvasStore((state) => state.addLabel); const startDrawRect = () => { const { x, y } = stageRef.current!.getRelativePointerPosition()!; @@ -56,6 +58,59 @@ export default function ImageCanvas() { [x, y], ]); }; + const addPointToPolygon = () => { + const { x, y } = stageRef.current!.getRelativePointerPosition()!; + if (polygonPoints.length === 0) { + setPolygonPoints([ + [x, y], + [x, y], + ]); + return; + } + + const diff = Math.max(Math.abs(x - polygonPoints[0][0]), Math.abs(y - polygonPoints[0][1])); + + if (diff === 0) return; + + const scale = stageRef.current!.getAbsoluteScale().x; + const clickedFirstPoint = polygonPoints.length > 1 && diff * scale < 5; + + if (clickedFirstPoint) { + endDrawPolygon(); + return; + } + setPolygonPoints([...polygonPoints, [x, y]]); + }; + const removeLastPointOfPolygon = (e: MouseEvent) => { + e.preventDefault(); + if (polygonPoints.length === 0) return; + setPolygonPoints(polygonPoints.slice(0, -1)); + }; + const moveLastPointOfPolygon = () => { + if (polygonPoints.length < 2) return; + const { x, y } = stageRef.current!.getRelativePointerPosition()!; + setPolygonPoints([...polygonPoints.slice(0, -1), [x, y]]); + }; + const endDrawPolygon = () => { + if (drawState !== 'pen' || polygonPoints.length === 0) return; + setDrawState('pointer'); + setPolygonPoints([]); + if (polygonPoints.length < 4) return; + + const color = Math.floor(Math.random() * 65535) + .toString(16) + .padStart(6, '0'); + const id = labels.length; + addLabel({ + id: id, + name: 'label', + type: 'polygon', + color: `#${color}`, + coordinates: polygonPoints.slice(0, -1), + }); + setDrawState('pointer'); + setSelectedId(id); + }; const updateDrawingRect = () => { if (rectPoints.length === 0) return; @@ -71,23 +126,32 @@ export default function ImageCanvas() { setRectPoints([]); const color = Math.floor(Math.random() * 65535) .toString(16) - .padStart(4, '0'); + .padStart(6, '0'); + const id = labels.length; addLabel({ - id: labels.length, + id: id, name: 'label', type: 'rect', - color: `#${color}ff`, + color: `#${color}`, coordinates: rectPoints, }); + setDrawState('pointer'); + setSelectedId(id); }; const handleClick = (e: Konva.KonvaEventObject) => { - const isLeftMouseClicked = e.evt.type === 'mousedown' && (e.evt as MouseEvent).button === 0; + e.evt.preventDefault(); + e.evt.stopPropagation(); + const isLeftClicked = e.evt.type === 'mousedown' && (e.evt as MouseEvent).button === 0; + const isRightClicked = e.evt.type === 'mousedown' && (e.evt as MouseEvent).button === 2; - if (drawState !== 'pointer' && isLeftMouseClicked) { + if (drawState !== 'pointer' && (isLeftClicked || isRightClicked)) { stageRef.current?.stopDrag(); if (drawState === 'rect') { startDrawRect(); } + if (drawState === 'pen') { + isRightClicked ? removeLastPointOfPolygon(e.evt as MouseEvent) : addPointToPolygon(); + } return; } if (e.target === e.target.getStage() || e.target.getClassName() === 'Image') { @@ -98,6 +162,9 @@ export default function ImageCanvas() { if (drawState === 'rect' && rectPoints.length) { updateDrawingRect(); } + if (drawState === 'pen' && polygonPoints.length) { + moveLastPointOfPolygon(); + } }; const handleZoom = (e: Konva.KonvaEventObject) => { const scaleBy = 1.05; @@ -172,6 +239,7 @@ export default function ImageCanvas() { onMouseUp={endDrawRect} onTouchEnd={endDrawRect} scale={getScale()} + onContextMenu={(e) => e.evt.preventDefault()} > @@ -203,15 +271,47 @@ export default function ImageCanvas() { y={rectPoints[0][1]} width={rectPoints[1][0] - rectPoints[0][0]} height={rectPoints[1][1] - rectPoints[0][1]} - stroke={'#ff0000'} + stroke={'#00a1ff'} strokeWidth={1} strokeScaleEnabled={false} fillAfterStrokeEnabled={false} - fill="#ff000033" + fill="#00a1ff33" shadowForStrokeEnabled={false} listening={false} /> ) : null} + {polygonPoints.length ? ( + <> + + {polygonPoints.map((point, index) => ( + + ))} + + ) : null}