Merge branch 'fe/develop' of https://lab.ssafy.com/s11-s-project/S11P21S002 into fe/refactor/header

This commit is contained in:
정현조 2024-09-27 16:09:08 +09:00
commit 58fd36c9d2
6 changed files with 97 additions and 78 deletions

View File

@ -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)}

View File

@ -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>

View File

@ -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]}

View File

@ -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>

View File

@ -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;

View File

@ -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>;
}