Merge branch 'fe/develop' of https://lab.ssafy.com/s11-s-project/S11P21S002 into fe/refactor/header
This commit is contained in:
commit
58fd36c9d2
@ -1,5 +1,5 @@
|
||||
import useCanvasStore from '@/stores/useCanvasStore';
|
||||
import { LucideIcon, MousePointer2, PenTool, Save, Square } from 'lucide-react';
|
||||
import { LucideIcon, MousePointer2, PenTool, Plus, Save, Square } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export default function CanvasControlBar({
|
||||
@ -9,15 +9,14 @@ export default function CanvasControlBar({
|
||||
saveJson: () => void;
|
||||
projectType: 'classification' | 'detection' | 'segmentation';
|
||||
}) {
|
||||
const drawState = useCanvasStore((state) => state.drawState);
|
||||
const setDrawState = useCanvasStore((state) => state.setDrawState);
|
||||
const { drawState, setDrawState, setLabels, labels } = useCanvasStore();
|
||||
const buttonBaseClassName = 'rounded-lg p-2 transition-colors ';
|
||||
const buttonClassName = 'hover:bg-gray-100';
|
||||
const activeButtonClassName = 'bg-primary stroke-white';
|
||||
|
||||
const controls: { [key: string]: LucideIcon } = {
|
||||
pointer: MousePointer2,
|
||||
...(projectType === 'segmentation' ? { pen: PenTool } : { rect: Square }),
|
||||
...(projectType === 'segmentation' ? { pen: PenTool } : projectType === 'detection' ? { rect: Square } : null),
|
||||
};
|
||||
|
||||
return (
|
||||
@ -37,6 +36,27 @@ export default function CanvasControlBar({
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
{projectType === 'classification' && labels.length === 0 && (
|
||||
<button
|
||||
className={cn(buttonClassName, buttonBaseClassName)}
|
||||
onClick={() => {
|
||||
setLabels([
|
||||
{
|
||||
id: 0,
|
||||
categoryId: 0,
|
||||
color: '#1177ff',
|
||||
type: 'point',
|
||||
coordinates: [[0, 0]],
|
||||
},
|
||||
]);
|
||||
}}
|
||||
>
|
||||
<Plus
|
||||
size={20}
|
||||
color="black"
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
<div className="h-5 w-0.5 rounded bg-gray-400" />
|
||||
<button
|
||||
className={cn(buttonClassName, buttonBaseClassName)}
|
||||
|
@ -10,13 +10,12 @@ import CanvasControlBar from '../CanvasControlBar';
|
||||
import { Label } from '@/types';
|
||||
import useLabelJson from '@/hooks/useLabelJson';
|
||||
import useProjectStore from '@/stores/useProjectStore';
|
||||
import { LABEL_CATEGORY } from '@/constants';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import useSaveImageLabelsQuery from '@/queries/projects/useSaveImageLabelsQuery';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
|
||||
export default function ImageCanvas() {
|
||||
const { project, folderId } = useProjectStore();
|
||||
const { project, folderId, categories } = useProjectStore();
|
||||
const { id: imageId, imagePath, dataPath } = useCanvasStore((state) => state.image)!;
|
||||
const { data: labelData } = useLabelJson(dataPath, project!);
|
||||
const { labels, drawState, setDrawState, addLabel, setLabels, selectedLabelId, setSelectedLabelId, sidebarSize } =
|
||||
@ -40,7 +39,7 @@ export default function ImageCanvas() {
|
||||
id: index,
|
||||
categoryId: group_id,
|
||||
color,
|
||||
type: shape_type === 'polygon' ? 'polygon' : 'rect',
|
||||
type: shape_type,
|
||||
coordinates: points,
|
||||
}))
|
||||
);
|
||||
@ -59,7 +58,7 @@ export default function ImageCanvas() {
|
||||
const json = JSON.stringify({
|
||||
...labelData,
|
||||
shapes: labels.map(({ categoryId, color, coordinates, type }) => ({
|
||||
label: LABEL_CATEGORY[categoryId],
|
||||
label: categories.find((category) => category.id === categoryId)!.labelName,
|
||||
color,
|
||||
points: coordinates,
|
||||
group_id: categoryId,
|
||||
@ -167,7 +166,7 @@ export default function ImageCanvas() {
|
||||
addLabel({
|
||||
id: id,
|
||||
categoryId: 0,
|
||||
type: 'rect',
|
||||
type: 'rectangle',
|
||||
color: `#${color}`,
|
||||
coordinates: rectPoints,
|
||||
});
|
||||
@ -286,75 +285,77 @@ export default function ImageCanvas() {
|
||||
<Layer>
|
||||
<Image image={image} />
|
||||
</Layer>
|
||||
<Layer listening={drawState === 'pointer'}>
|
||||
{labels.map((label) =>
|
||||
label.type === 'rect' ? (
|
||||
<LabelRect
|
||||
key={label.id}
|
||||
isSelected={label.id === selectedLabelId}
|
||||
onSelect={() => setSelectedLabelId(label.id)}
|
||||
info={label}
|
||||
setLabel={setLabel(label.id)}
|
||||
dragLayer={dragLayerRef.current as Konva.Layer}
|
||||
/>
|
||||
) : (
|
||||
<LabelPolygon
|
||||
key={label.id}
|
||||
isSelected={label.id === selectedLabelId}
|
||||
onSelect={() => setSelectedLabelId(label.id)}
|
||||
info={label}
|
||||
setLabel={setLabel(label.id)}
|
||||
stage={stageRef.current as Konva.Stage}
|
||||
dragLayer={dragLayerRef.current as Konva.Layer}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{rectPoints.length ? (
|
||||
<Rect
|
||||
x={rectPoints[0][0]}
|
||||
y={rectPoints[0][1]}
|
||||
width={rectPoints[1][0] - rectPoints[0][0]}
|
||||
height={rectPoints[1][1] - rectPoints[0][1]}
|
||||
stroke={'#00a1ff'}
|
||||
strokeWidth={1}
|
||||
strokeScaleEnabled={false}
|
||||
fillAfterStrokeEnabled={false}
|
||||
fill="#00a1ff33"
|
||||
shadowForStrokeEnabled={false}
|
||||
listening={false}
|
||||
/>
|
||||
) : null}
|
||||
{polygonPoints.length ? (
|
||||
<>
|
||||
<Line
|
||||
points={polygonPoints.flat()}
|
||||
{project?.type !== 'classification' && (
|
||||
<Layer listening={drawState === 'pointer'}>
|
||||
{labels.map((label) =>
|
||||
label.type === 'rectangle' ? (
|
||||
<LabelRect
|
||||
key={label.id}
|
||||
isSelected={label.id === selectedLabelId}
|
||||
onSelect={() => setSelectedLabelId(label.id)}
|
||||
info={label}
|
||||
setLabel={setLabel(label.id)}
|
||||
dragLayer={dragLayerRef.current as Konva.Layer}
|
||||
/>
|
||||
) : (
|
||||
<LabelPolygon
|
||||
key={label.id}
|
||||
isSelected={label.id === selectedLabelId}
|
||||
onSelect={() => setSelectedLabelId(label.id)}
|
||||
info={label}
|
||||
setLabel={setLabel(label.id)}
|
||||
stage={stageRef.current as Konva.Stage}
|
||||
dragLayer={dragLayerRef.current as Konva.Layer}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{rectPoints.length ? (
|
||||
<Rect
|
||||
x={rectPoints[0][0]}
|
||||
y={rectPoints[0][1]}
|
||||
width={rectPoints[1][0] - rectPoints[0][0]}
|
||||
height={rectPoints[1][1] - rectPoints[0][1]}
|
||||
stroke={'#00a1ff'}
|
||||
strokeWidth={1}
|
||||
strokeScaleEnabled={false}
|
||||
fillAfterStrokeEnabled={false}
|
||||
fill="#00a1ff33"
|
||||
shadowForStrokeEnabled={false}
|
||||
listening={false}
|
||||
/>
|
||||
{polygonPoints.map((point, index) => (
|
||||
<Circle
|
||||
key={index}
|
||||
x={point[0]}
|
||||
y={point[1]}
|
||||
radius={5}
|
||||
stroke="#00a1ff"
|
||||
) : null}
|
||||
{polygonPoints.length ? (
|
||||
<>
|
||||
<Line
|
||||
points={polygonPoints.flat()}
|
||||
stroke={'#00a1ff'}
|
||||
strokeWidth={1}
|
||||
fill="white"
|
||||
strokeScaleEnabled={false}
|
||||
listening={false}
|
||||
scale={{
|
||||
x: 1 / stageRef.current!.getAbsoluteScale().x,
|
||||
y: 1 / stageRef.current!.getAbsoluteScale().y,
|
||||
}}
|
||||
perfectDrawEnabled={false}
|
||||
shadowForStrokeEnabled={false}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : null}
|
||||
</Layer>
|
||||
{polygonPoints.map((point, index) => (
|
||||
<Circle
|
||||
key={index}
|
||||
x={point[0]}
|
||||
y={point[1]}
|
||||
radius={5}
|
||||
stroke="#00a1ff"
|
||||
strokeWidth={1}
|
||||
fill="white"
|
||||
strokeScaleEnabled={false}
|
||||
listening={false}
|
||||
scale={{
|
||||
x: 1 / stageRef.current!.getAbsoluteScale().x,
|
||||
y: 1 / stageRef.current!.getAbsoluteScale().y,
|
||||
}}
|
||||
perfectDrawEnabled={false}
|
||||
shadowForStrokeEnabled={false}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : null}
|
||||
</Layer>
|
||||
)}
|
||||
|
||||
<Layer ref={dragLayerRef} />
|
||||
</Stage>
|
||||
|
@ -22,7 +22,7 @@ export default function ImageWithLabels({ imagePath, labelData }: ImageWithLabel
|
||||
id: index,
|
||||
categoryId: shape.categoryId,
|
||||
color: shape.color,
|
||||
type: shape.shape_type === 'polygon' ? 'polygon' : 'rect',
|
||||
type: shape.shape_type,
|
||||
coordinates: shape.points,
|
||||
})) as Label[];
|
||||
setLabels(shapes);
|
||||
@ -65,7 +65,7 @@ export default function ImageWithLabels({ imagePath, labelData }: ImageWithLabel
|
||||
<Layer>{image && <Image image={image} />}</Layer>
|
||||
<Layer>
|
||||
{labels.map((label) =>
|
||||
label.type === 'rect' ? (
|
||||
label.type === 'rectangle' ? (
|
||||
<Rect
|
||||
key={label.id}
|
||||
x={label.coordinates[0][0]}
|
||||
|
@ -12,9 +12,9 @@ export default function ProjectCard({ title, description, imageUrl = '', to = ''
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
className="relative flex w-[327px] cursor-pointer items-start gap-4 overflow-hidden rounded-lg border border-gray-200 bg-white p-4 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50"
|
||||
className="relative flex w-[327px] cursor-pointer items-start gap-4 rounded-lg border border-gray-200 bg-white p-4 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50"
|
||||
>
|
||||
<div className="flex h-24 w-24 items-center justify-center rounded-lg bg-gray-100">
|
||||
<div className="flex h-24 w-24 shrink-0 items-center justify-center rounded-lg bg-gray-100">
|
||||
{imageUrl ? (
|
||||
<img
|
||||
src={imageUrl}
|
||||
@ -25,7 +25,7 @@ export default function ProjectCard({ title, description, imageUrl = '', to = ''
|
||||
<Compass className="h-8 w-8 text-gray-500" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col items-start gap-1">
|
||||
<div className="flex flex-col items-start gap-1 overflow-hidden">
|
||||
<div className="font-sans text-lg leading-tight text-black dark:text-white">{title}</div>
|
||||
<div className="text-sm leading-tight text-gray-500 dark:text-gray-400">{description}</div>
|
||||
</div>
|
||||
|
@ -2,13 +2,11 @@ import { ImageResponse, Label } from '@/types';
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface CanvasState {
|
||||
// project: Project | null;
|
||||
sidebarSize: number;
|
||||
image: ImageResponse | null;
|
||||
labels: Label[];
|
||||
drawState: 'pen' | 'rect' | 'pointer';
|
||||
selectedLabelId: number | null;
|
||||
// setProject: (project: Project | null) => void;
|
||||
setSidebarSize: (width: number) => void;
|
||||
setImage: (image: ImageResponse | null) => void;
|
||||
setLabels: (labels: Label[]) => void;
|
||||
|
@ -34,7 +34,7 @@ export type Label = {
|
||||
id: number;
|
||||
categoryId: number;
|
||||
color: string;
|
||||
type: 'polygon' | 'rect';
|
||||
type: 'polygon' | 'rectangle' | 'point';
|
||||
coordinates: Array<[number, number]>;
|
||||
};
|
||||
|
||||
@ -253,7 +253,7 @@ export interface Shape {
|
||||
color: string;
|
||||
points: [number, number][];
|
||||
group_id: number;
|
||||
shape_type: 'polygon' | 'rectangle';
|
||||
shape_type: 'polygon' | 'rectangle' | 'point';
|
||||
flags: Record<string, never>;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user