diff --git a/frontend/src/components/ImageCanvas/LabelPolygon.tsx b/frontend/src/components/ImageCanvas/LabelPolygon.tsx new file mode 100644 index 0000000..de9831b --- /dev/null +++ b/frontend/src/components/ImageCanvas/LabelPolygon.tsx @@ -0,0 +1,41 @@ +import { Label } from '@/types'; +import Konva from 'konva'; +import { useRef, useState } from 'react'; +import { Group, Line } from 'react-konva'; +import PolygonTransformer from './PolygonTransformer'; + +export default function LabelPolygon({ + isSelected, + onSelect, + info, +}: { + isSelected: boolean; + onSelect: () => void; + info: Label; +}) { + const polyRef = useRef(null); + const [coordinates, setCoordinates] = useState>(info.coordinates); + + return ( + + + {isSelected && ( + + )} + + ); +} diff --git a/frontend/src/components/ImageCanvas/LabelRect.tsx b/frontend/src/components/ImageCanvas/LabelRect.tsx index 79ea4a2..2c83f82 100644 --- a/frontend/src/components/ImageCanvas/LabelRect.tsx +++ b/frontend/src/components/ImageCanvas/LabelRect.tsx @@ -1,7 +1,7 @@ import { Label } from '@/types'; import Konva from 'konva'; import { useEffect, useRef } from 'react'; -import { Line, Transformer } from 'react-konva'; +import { Group, Line, Transformer } from 'react-konva'; export default function LabelRect({ isSelected, @@ -14,12 +14,16 @@ export default function LabelRect({ }) { const rectRef = useRef(null); const trRef = useRef(null); + const coordinates = [ + info.coordinates[0], + [info.coordinates[0][0], info.coordinates[1][1]], + info.coordinates[1], + [info.coordinates[1][0], info.coordinates[0][1]], + ].flat(); const handleTransformEnd = () => { const points = rectRef.current?.points(); - if (points) { - console.log(points); - console.log(trRef.current?.getAbsoluteScale()); - } + + console.log(points); }; useEffect(() => { @@ -30,9 +34,9 @@ export default function LabelRect({ }, [isSelected]); return ( - <> + )} - + ); } diff --git a/frontend/src/components/ImageCanvas/PolygonTransformer.tsx b/frontend/src/components/ImageCanvas/PolygonTransformer.tsx new file mode 100644 index 0000000..c619826 --- /dev/null +++ b/frontend/src/components/ImageCanvas/PolygonTransformer.tsx @@ -0,0 +1,64 @@ +import Konva from 'konva'; +import { Circle, Line } from 'react-konva'; + +interface PolygonTransformerProps { + coordinates: Array<[number, number]>; + setCoordinates: (coordinates: Array<[number, number]>) => void; +} + +// TODO: scale 상관 없이 고정된 크기로 표시되도록 수정 +export default function PolygonTransformer({ coordinates, setCoordinates }: PolygonTransformerProps) { + const handleDragMove = (index: number) => (e: Konva.KonvaEventObject) => { + const circle = e.target as Konva.Circle; + const stage = circle.getStage(); + const pos = circle.position(); + const newCoordinates = [...coordinates]; + + newCoordinates[index] = [pos.x, pos.y]; + setCoordinates(newCoordinates); + stage?.batchDraw(); + }; + const handleMouseOver = (e: Konva.KonvaEventObject) => { + const circle = e.target as Konva.Circle; + const stage = circle.getStage(); + circle.strokeWidth(2); + circle.radius(15); + stage?.batchDraw(); + }; + const handleMouseOut = (e: Konva.KonvaEventObject) => { + const circle = e.target as Konva.Circle; + const stage = circle.getStage(); + circle.strokeWidth(1); + circle.radius(10); + stage?.batchDraw(); + }; + + return ( + <> + + {coordinates.map((point, index) => { + return ( + + ); + })} + + ); +} diff --git a/frontend/src/components/ImageCanvas/index.tsx b/frontend/src/components/ImageCanvas/index.tsx index 9aa593f..e34f081 100644 --- a/frontend/src/components/ImageCanvas/index.tsx +++ b/frontend/src/components/ImageCanvas/index.tsx @@ -6,51 +6,34 @@ import useImage from 'use-image'; import { Label } from '@/types'; import LabelRect from './LabelRect'; import { Vector2d } from 'konva/lib/types'; +import LabelPolygon from './LabelPolygon'; -const mockLabels: Label[] = [ - { - id: 1, - name: 'Label 1', - color: '#FFaa33', - type: 'rect', - coordinates: [ - [100, 100], - [200, 100], - [200, 200], - [100, 200], - ], - }, - { - id: 2, - name: 'Label 3', - color: '#aa33ff', - type: 'rect', - coordinates: [ - [1200, 200], - [1200, 400], - [1400, 400], - [1400, 200], - ], - }, - { - id: 3, - name: 'Label 3', - color: '#aaff33', - type: 'polygon', - coordinates: [ - [500, 375], - [523, 232], - [600, 232], - [535, 175], - [560, 100], - [500, 150], - [440, 100], - [465, 175], - [400, 232], - [477, 232], - ], - }, -]; +const mockLabels: Label[] = Array.from({ length: 10 }, (_, i) => { + const startX = Math.random() * 1200 + 300; + const startY = Math.random() * 2000 + 300; + const color = Math.floor(Math.random() * 65535) + .toString(16) + .padStart(4, '0'); + + return { + id: i, + name: `label-${i}`, + type: i % 2 === 0 ? 'polygon' : 'rect', + color: i % 2 === 0 ? `#ff${color}` : `#${color}ff`, + coordinates: + i % 2 === 0 + ? [ + [startX, startY], + [startX + 200, startY + 50], + [startX + 300, startY + 300], + [startX + 100, startY + 250], + ] + : [ + [startX, startY], + [startX + 300, startY + 300], + ], + }; +}); export default function ImageCanvas() { const stageWidth = window.innerWidth; @@ -106,8 +89,6 @@ export default function ImageCanvas() { const heightRatio = stageHeight / image!.height; scale.current = Math.min(widthRatio, heightRatio); - console.log(scale); - return { x: scale.current, y: scale.current }; }; @@ -118,11 +99,11 @@ export default function ImageCanvas() { return imageStatus === 'loaded' ? ( - {labels.map((label) => ( - - {label.type === 'rect' ? ( + + {labels.map((label) => + label.type === 'rect' ? ( setSelectedId(label.id)} info={label} /> ) : ( - <> - )} - - ))} + setSelectedId(label.id)} + info={label} + /> + ) + )} + ) : (