From 1502b213161f00d5d875b6a66e966b803fa95384 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Sun, 6 Nov 2022 23:15:30 +0700 Subject: [PATCH 01/67] update: optimize detect AI area --- .../Annotation/Editor/Shape/Polygon.tsx | 44 +++++++++---------- src/reduxes/annotation/reducer.ts | 38 ++++++++++++---- src/reduxes/annotation/type.ts | 1 + .../Layer/DetectedRectangleDrawLayer.tsx | 1 + 4 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/components/Annotation/Editor/Shape/Polygon.tsx b/src/components/Annotation/Editor/Shape/Polygon.tsx index 10be09b7..28875caa 100644 --- a/src/components/Annotation/Editor/Shape/Polygon.tsx +++ b/src/components/Annotation/Editor/Shape/Polygon.tsx @@ -38,7 +38,7 @@ const PolygonComp = ({ const dispatch = useDispatch(); const commonShapeEvent = useCommonShapeEvent({ drawObject: spec }); const currentpolygon = useSelector(selectorSelectedPolygonOrLineStrip); - const detectedArea = useSelector(selectorDetectedArea); + // const detectedArea = useSelector(selectorDetectedArea); const drawObjectState = useSelector(selectorDrawObjectState(spec.id)); const zoom = useSelector(selectorZoom); @@ -59,27 +59,27 @@ const PolygonComp = ({ } }, [isSelected]); - useEffect(() => { - if (detectedArea) { - const polygonRect = groupRef.current?.getClientRect(); - if (polygonRect) { - if ( - polygonRect.x >= detectedArea.x && - polygonRect.x <= detectedArea.x + detectedArea.width && - polygonRect.y >= detectedArea.y && - polygonRect.y <= detectedArea.y + detectedArea.height && - polygonRect.width <= detectedArea.width && - polygonRect.height <= detectedArea.height - ) { - dispatch( - removeDrawObjectStateIdByAI({ - drawObjectStateIds: [spec.id], - }) - ); - } - } - } - }, [detectedArea]); + // useEffect(() => { + // if (detectedArea) { + // const polygonRect = groupRef.current?.getClientRect(); + // if (polygonRect) { + // if ( + // polygonRect.x >= detectedArea.x && + // polygonRect.x <= detectedArea.x + detectedArea.width && + // polygonRect.y >= detectedArea.y && + // polygonRect.y <= detectedArea.y + detectedArea.height && + // polygonRect.width <= detectedArea.width && + // polygonRect.height <= detectedArea.height + // ) { + // dispatch( + // removeDrawObjectStateIdByAI({ + // drawObjectStateIds: [spec.id], + // }) + // ); + // } + // } + // } + // }, [detectedArea]); const [stage, setStage] = useState(); const [flattenedPoints, setFlattenedPoints] = useState(); diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index 84c8f501..c8321f0a 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -1,9 +1,4 @@ -import { - initialEllipses, - initialLineStrips, - initialPolygons, - initialRectangles, -} from "components/Annotation/Editor/type"; +import { PolygonSpec } from "components/Annotation/Editor/type"; import { ADD_DRAW_OBJECTS_BY_AI, CHANGE_CURRENT_DRAW_STATE, @@ -49,7 +44,7 @@ import { } from "./type"; const inititalState: AnnotationReducer = { - currentDrawType: DrawType.POLYGON, + currentDrawType: DrawType.RECTANGLE, selectedDrawObjectId: null, zoom: { zoom: 1, position: { x: 0, y: 0 } }, // drawObjectById: (() => { @@ -343,10 +338,37 @@ const annotationReducer = ( }; } case SET_DETECTED_AREA: { - const { detectedArea } = payload as SetLockDetectedAreaPayload; + const { detectedArea, scale } = payload as SetLockDetectedAreaPayload; + const newDrawObjectStateIdByAI = new Set(state.drawObjectStateIdByAI); + if (detectedArea) { + const scaleX = 1 / scale.x; + const scaleY = 1 / scale.y; + + for (const drawObjectId of state.drawObjectStateIdByAI) { + const drawObject = state.drawObjectById[drawObjectId]; + if (drawObject) { + const { type, data } = drawObject; + if (type === DrawType.POLYGON) { + const points = (data as PolygonSpec).points; + const isInvalid = points.some((point) => { + return ( + point.x < detectedArea.x * scaleX || + point.y < detectedArea.y * scaleY || + point.x > (detectedArea.x + detectedArea.width) * scaleX || + point.y > (detectedArea.y + detectedArea.height) * scaleY + ); + }); + if (!isInvalid) { + newDrawObjectStateIdByAI.delete(drawObjectId); + } + } + } + } + } return { ...state, detectedArea, + drawObjectStateIdByAI: Array.from(newDrawObjectStateIdByAI), }; } case SET_IS_DRAGGING_VIEW_PORT: { diff --git a/src/reduxes/annotation/type.ts b/src/reduxes/annotation/type.ts index d6b4b628..d59e4c26 100644 --- a/src/reduxes/annotation/type.ts +++ b/src/reduxes/annotation/type.ts @@ -105,6 +105,7 @@ export interface SetLockDrawObecjtPayload { } export interface SetLockDetectedAreaPayload { detectedArea: DetectedAreaType | null; + scale: { x: number; y: number }; } export interface SetIsDraggingViewportPayload { isDraggingViewport: boolean; diff --git a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx index 13f3f8cf..f06b35df 100644 --- a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx @@ -42,6 +42,7 @@ const DetectedRectangleDrawLayer = () => { dispatch( setDetectedArea({ detectedArea: { ...refDetectedArea.current.getClientRect() }, + scale: refDetectedArea.current.getAbsoluteScale(), }) ); } From f184c9523d3c1d541740e5e43c71a4c9d0275f33 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Mon, 7 Nov 2022 21:48:19 +0700 Subject: [PATCH 02/67] fix: unable to select shape after hold Space and Drag --- src/reduxes/annotation/action.ts | 4 ++++ src/reduxes/annotation/constants.ts | 1 + src/reduxes/annotation/reducer.ts | 9 +++++++++ src/reduxes/annotation/selector.ts | 2 ++ src/reduxes/annotation/type.ts | 1 + src/routes/AnnotationPage/Editor/index.tsx | 3 ++- 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/reduxes/annotation/action.ts b/src/reduxes/annotation/action.ts index 1a4a20ac..7d3595b4 100644 --- a/src/reduxes/annotation/action.ts +++ b/src/reduxes/annotation/action.ts @@ -5,6 +5,7 @@ import { CHANGE_ZOOM, CREATE_DRAW_OBJECT, DELETE_DRAW_OBJECT, + RECOVER_PREVIOUS_DRAWSTATE, REDO_DRAW_OBJECT, REMOVE_DRAW_OBJECTS_BY_AI, RESET_ANNOTATION, @@ -120,3 +121,6 @@ export const removeDrawObjectStateIdByAI = ( export const resetAnnotation = () => ({ type: RESET_ANNOTATION, }); +export const recoverPreviousDrawState = () => ({ + type: RECOVER_PREVIOUS_DRAWSTATE, +}); diff --git a/src/reduxes/annotation/constants.ts b/src/reduxes/annotation/constants.ts index b2b96302..39bfc517 100644 --- a/src/reduxes/annotation/constants.ts +++ b/src/reduxes/annotation/constants.ts @@ -17,3 +17,4 @@ export const SET_IS_DRAGGING_VIEW_PORT = "SET_IS_DRAGGING_VIEW_PORT"; export const ADD_DRAW_OBJECTS_BY_AI = "ADD_DRAW_OBJECTS_BY_AI"; export const REMOVE_DRAW_OBJECTS_BY_AI = "REMOVE_DRAW_OBJECTS_BY_AI"; export const RESET_ANNOTATION = "RESET_ANNOTATION"; +export const RECOVER_PREVIOUS_DRAWSTATE = "RECOVER_PREVIOUS_DRAWSTATE"; diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index 84c8f501..9fdffc1a 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -11,6 +11,7 @@ import { CHANGE_ZOOM, CREATE_DRAW_OBJECT, DELETE_DRAW_OBJECT, + RECOVER_PREVIOUS_DRAWSTATE, REDO_DRAW_OBJECT, REMOVE_DRAW_OBJECTS_BY_AI, RESET_ANNOTATION, @@ -86,6 +87,7 @@ const inititalState: AnnotationReducer = { // })(), drawObjectById: {}, currentDrawState: DrawState.FREE, + previousDrawState: DrawState.FREE, statehHistory: { historyStep: 0, stateHistoryItems: [] }, drawObjectStateById: {}, detectedArea: null, @@ -186,6 +188,7 @@ const annotationReducer = ( selectedDrawObjectId: drawState === DrawState.FREE ? null : state.selectedDrawObjectId, currentDrawState: drawState, + previousDrawState: state.currentDrawState, }; } case SET_SELECT_SHAPE: { @@ -387,6 +390,12 @@ const annotationReducer = ( case RESET_ANNOTATION: { return { ...inititalState }; } + case RECOVER_PREVIOUS_DRAWSTATE: { + return { + ...state, + currentDrawState: state.previousDrawState, + }; + } default: return state; } diff --git a/src/reduxes/annotation/selector.ts b/src/reduxes/annotation/selector.ts index 007f57f7..21cac5aa 100644 --- a/src/reduxes/annotation/selector.ts +++ b/src/reduxes/annotation/selector.ts @@ -60,6 +60,8 @@ export const selectorDrawObjectById = (state: RootState) => state.annotationReducer.drawObjectById; export const selectorCurrentDrawState = (state: RootState) => state.annotationReducer.currentDrawState; +export const selectorPreviousDrawState = (state: RootState) => + state.annotationReducer.previousDrawState; export const selectorZoom = (state: RootState) => state.annotationReducer.zoom; export const selectorAnnotationHistoryStep = (state: RootState) => state.annotationReducer.statehHistory.historyStep; diff --git a/src/reduxes/annotation/type.ts b/src/reduxes/annotation/type.ts index d6b4b628..552b037a 100644 --- a/src/reduxes/annotation/type.ts +++ b/src/reduxes/annotation/type.ts @@ -26,6 +26,7 @@ export interface DrawObject { } export interface AnnotationReducer { currentDrawState: DrawState; + previousDrawState: DrawState; currentDrawType: DrawType; selectedDrawObjectId: string | null; zoom: ZoomProps; diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index 6bf8285b..88cf7023 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -26,6 +26,7 @@ import { changeCurrentStatus, changeZoom, deleteDrawObject, + recoverPreviousDrawState, redoDrawObject, setIsDraggingViewpor, undoDrawObject, @@ -167,7 +168,7 @@ const Editor = () => { const keyUpHandler = (): void => { setKeyDown(null); if (currentDrawState === DrawState.ZOOMDRAGGING) { - dispatch(changeCurrentStatus({ drawState: DrawState.FREE })); + dispatch(recoverPreviousDrawState()); } }; const keyDownHandler = (e: KeyboardEvent) => { From f0b63fb65c13f484fe267d66759e474068e20577 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Mon, 7 Nov 2022 22:35:32 +0700 Subject: [PATCH 03/67] fix: segmentation shapes were exported --- .../AnnotationPage/ControlPanel/index.tsx | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index e291ebe3..02cddd0c 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -128,25 +128,39 @@ const ControlPanel = () => { setDrawState(state); setDrawType(null); }; + const getDrawObjectToExport = () => { + if (drawObjectById) { + const filteredDrawObjectById = { ...drawObjectById }; + drawObjectStateIdByAI.forEach((id) => { + delete filteredDrawObjectById[id]; + }); + return filteredDrawObjectById; + } + return null; + }; const handleExportLabelMe = () => { - if (currentAnnotationFile && drawObjectById) { - exportAnnotationLabelMe(currentAnnotationFile, drawObjectById); + const drawObjectToExport = getDrawObjectToExport(); + if (currentAnnotationFile && drawObjectToExport) { + exportAnnotationLabelMe(currentAnnotationFile, drawObjectToExport); } }; const handleExportScaleAI = () => { - if (drawObjectById) { - exportAnnotationScaleAI(drawObjectById); + const drawObjectToExport = getDrawObjectToExport(); + if (drawObjectToExport) { + exportAnnotationScaleAI(drawObjectToExport); } }; const handleExportLabelBox = () => { - if (drawObjectById) { - exportAnnotationLabelBox(drawObjectById); + const drawObjectToExport = getDrawObjectToExport(); + if (drawObjectToExport) { + exportAnnotationLabelBox(drawObjectToExport); } }; const handleExportDaita = () => { - if (drawObjectById) { + const drawObjectToExport = getDrawObjectToExport(); + if (drawObjectToExport) { exportAnnotationDaita( - drawObjectById, + drawObjectToExport, currentPreviewImageName ? currentPreviewImageName : "imagename" ); } From efc1a921ecfb6da4295e4fa9eb95b9d02a45c0b5 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Sun, 6 Nov 2022 23:15:30 +0700 Subject: [PATCH 04/67] update: optimize detect AI area --- .../Annotation/Editor/Shape/Polygon.tsx | 44 +++++++++---------- src/reduxes/annotation/reducer.ts | 38 ++++++++++++---- src/reduxes/annotation/type.ts | 1 + .../Layer/DetectedRectangleDrawLayer.tsx | 1 + 4 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/components/Annotation/Editor/Shape/Polygon.tsx b/src/components/Annotation/Editor/Shape/Polygon.tsx index 10be09b7..28875caa 100644 --- a/src/components/Annotation/Editor/Shape/Polygon.tsx +++ b/src/components/Annotation/Editor/Shape/Polygon.tsx @@ -38,7 +38,7 @@ const PolygonComp = ({ const dispatch = useDispatch(); const commonShapeEvent = useCommonShapeEvent({ drawObject: spec }); const currentpolygon = useSelector(selectorSelectedPolygonOrLineStrip); - const detectedArea = useSelector(selectorDetectedArea); + // const detectedArea = useSelector(selectorDetectedArea); const drawObjectState = useSelector(selectorDrawObjectState(spec.id)); const zoom = useSelector(selectorZoom); @@ -59,27 +59,27 @@ const PolygonComp = ({ } }, [isSelected]); - useEffect(() => { - if (detectedArea) { - const polygonRect = groupRef.current?.getClientRect(); - if (polygonRect) { - if ( - polygonRect.x >= detectedArea.x && - polygonRect.x <= detectedArea.x + detectedArea.width && - polygonRect.y >= detectedArea.y && - polygonRect.y <= detectedArea.y + detectedArea.height && - polygonRect.width <= detectedArea.width && - polygonRect.height <= detectedArea.height - ) { - dispatch( - removeDrawObjectStateIdByAI({ - drawObjectStateIds: [spec.id], - }) - ); - } - } - } - }, [detectedArea]); + // useEffect(() => { + // if (detectedArea) { + // const polygonRect = groupRef.current?.getClientRect(); + // if (polygonRect) { + // if ( + // polygonRect.x >= detectedArea.x && + // polygonRect.x <= detectedArea.x + detectedArea.width && + // polygonRect.y >= detectedArea.y && + // polygonRect.y <= detectedArea.y + detectedArea.height && + // polygonRect.width <= detectedArea.width && + // polygonRect.height <= detectedArea.height + // ) { + // dispatch( + // removeDrawObjectStateIdByAI({ + // drawObjectStateIds: [spec.id], + // }) + // ); + // } + // } + // } + // }, [detectedArea]); const [stage, setStage] = useState(); const [flattenedPoints, setFlattenedPoints] = useState(); diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index 84c8f501..c8321f0a 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -1,9 +1,4 @@ -import { - initialEllipses, - initialLineStrips, - initialPolygons, - initialRectangles, -} from "components/Annotation/Editor/type"; +import { PolygonSpec } from "components/Annotation/Editor/type"; import { ADD_DRAW_OBJECTS_BY_AI, CHANGE_CURRENT_DRAW_STATE, @@ -49,7 +44,7 @@ import { } from "./type"; const inititalState: AnnotationReducer = { - currentDrawType: DrawType.POLYGON, + currentDrawType: DrawType.RECTANGLE, selectedDrawObjectId: null, zoom: { zoom: 1, position: { x: 0, y: 0 } }, // drawObjectById: (() => { @@ -343,10 +338,37 @@ const annotationReducer = ( }; } case SET_DETECTED_AREA: { - const { detectedArea } = payload as SetLockDetectedAreaPayload; + const { detectedArea, scale } = payload as SetLockDetectedAreaPayload; + const newDrawObjectStateIdByAI = new Set(state.drawObjectStateIdByAI); + if (detectedArea) { + const scaleX = 1 / scale.x; + const scaleY = 1 / scale.y; + + for (const drawObjectId of state.drawObjectStateIdByAI) { + const drawObject = state.drawObjectById[drawObjectId]; + if (drawObject) { + const { type, data } = drawObject; + if (type === DrawType.POLYGON) { + const points = (data as PolygonSpec).points; + const isInvalid = points.some((point) => { + return ( + point.x < detectedArea.x * scaleX || + point.y < detectedArea.y * scaleY || + point.x > (detectedArea.x + detectedArea.width) * scaleX || + point.y > (detectedArea.y + detectedArea.height) * scaleY + ); + }); + if (!isInvalid) { + newDrawObjectStateIdByAI.delete(drawObjectId); + } + } + } + } + } return { ...state, detectedArea, + drawObjectStateIdByAI: Array.from(newDrawObjectStateIdByAI), }; } case SET_IS_DRAGGING_VIEW_PORT: { diff --git a/src/reduxes/annotation/type.ts b/src/reduxes/annotation/type.ts index d6b4b628..d59e4c26 100644 --- a/src/reduxes/annotation/type.ts +++ b/src/reduxes/annotation/type.ts @@ -105,6 +105,7 @@ export interface SetLockDrawObecjtPayload { } export interface SetLockDetectedAreaPayload { detectedArea: DetectedAreaType | null; + scale: { x: number; y: number }; } export interface SetIsDraggingViewportPayload { isDraggingViewport: boolean; diff --git a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx index 13f3f8cf..f06b35df 100644 --- a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx @@ -42,6 +42,7 @@ const DetectedRectangleDrawLayer = () => { dispatch( setDetectedArea({ detectedArea: { ...refDetectedArea.current.getClientRect() }, + scale: refDetectedArea.current.getAbsoluteScale(), }) ); } From ec0bdb27013294d4ea766a3aeb039decdfb5552e Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Thu, 10 Nov 2022 11:46:04 +0700 Subject: [PATCH 05/67] fix: Able to move cursor outside of canvas and then the shape will take all the space until the edge and fix draw ellipse from down to top --- .../Editor/Layer/EllipseDrawLayer.tsx | 58 ++++++++++--------- .../Editor/Layer/PolygonDrawLayer.tsx | 35 ++++++++++- .../Editor/Layer/RectangleDrawLayer.tsx | 48 +++++++++++++-- 3 files changed, 105 insertions(+), 36 deletions(-) diff --git a/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx index a9123701..a225d1ae 100644 --- a/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx @@ -4,42 +4,40 @@ import Konva from "konva"; import { KonvaEventObject } from "konva/lib/Node"; import { Vector2d } from "konva/lib/types"; import { useEffect, useRef, useState } from "react"; -import { Ellipse, Layer } from "react-konva"; -import { useDispatch } from "react-redux"; +import { Ellipse, Layer, Rect } from "react-konva"; +import { useDispatch, useSelector } from "react-redux"; import { createDrawObject } from "reduxes/annotation/action"; import { DrawType } from "reduxes/annotation/type"; +import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; import { createEllipse } from "../Hook/useElipseEvent"; -import DummyRect from "./DummyRect"; const EllipseDrawLayer = () => { const dispatch = useDispatch(); - const [centerPoint, setCenterPoint] = useState(null); - const [radiusX, setRadiusX] = useState(0); - const [radiusY, setRadiusY] = useState(0); + const [startPoint, setStartPoint] = useState(null); + const [endPoint, setEndPoint] = useState(null); + const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); - if (!position || !centerPoint) return; - setCenterPoint({ - x: (position.x + (centerPoint.x - radiusX)) / 2, - y: (position.y + (centerPoint.y - radiusY)) / 2, - }); - setRadiusX((position.x - (centerPoint.x - radiusX)) / 2.0); - setRadiusY((position.y - (centerPoint.y - radiusY)) / 2.0); + if (!position || !startPoint) return; + setEndPoint({ ...position }); }; const mousedownHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (!position) return; - setCenterPoint({ ...position }); + setStartPoint({ ...position }); }; const layer = useRef(null); useEffect(() => { layer.current?.moveToTop(); }, []); const handleMouseUp = () => { - if (centerPoint) { - const ellipse = createEllipse(centerPoint); + if (startPoint && endPoint) { + const ellipse = createEllipse({ + x: (startPoint.x + endPoint.x) / 2, + y: (startPoint.y + endPoint.y) / 2, + }); const spec = ellipse.data as EllipseSpec; dispatch( createDrawObject({ @@ -47,18 +45,22 @@ const EllipseDrawLayer = () => { type: DrawType.ELLIPSE, data: { ...spec, - radiusX, - radiusY, + radiusX: Math.abs(endPoint.x - startPoint.x) / 2, + radiusY: Math.abs(endPoint.y - startPoint.y) / 2, } as EllipseSpec, }, }) ); - setCenterPoint(null); - setRadiusX(0); - setRadiusY(0); + } + setStartPoint(null); + setEndPoint(null); + }; + const renderDummyRect = () => { + if (currentAnnotationFile) { + const { width, height } = currentAnnotationFile; + return ; } }; - return ( { onMouseDown={mousedownHandler} onMouseUp={handleMouseUp} > - - {centerPoint && ( + {renderDummyRect()} + {startPoint && endPoint && ( )} diff --git a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx index 8ded9646..1775e3c7 100644 --- a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx @@ -1,6 +1,6 @@ import { KonvaEventObject } from "konva/lib/Node"; import { useEffect, useRef, useState } from "react"; -import { Circle, Group, Layer, Line } from "react-konva"; +import { Circle, Group, Layer, Line, Rect } from "react-konva"; import { CIRCLE_STYLE, @@ -23,6 +23,7 @@ import { DrawState, DrawType } from "reduxes/annotation/type"; import { convertStrokeColorToFillColor } from "routes/AnnotationPage/LabelAnnotation/ClassLabel"; import { createPolygon } from "../Hook/usePolygonEvent"; import DummyRect from "./DummyRect"; +import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; const PolygonDrawLayer = () => { const dispatch = useDispatch(); @@ -39,6 +40,7 @@ const PolygonDrawLayer = () => { const [mousePosition, setMousePosition] = useState(null); const [mouseOverPoint, setMouseOverPoint] = useState(false); const layer = useRef(null); + const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); useEffect(() => { const flatPoints: number[] = points @@ -50,6 +52,14 @@ const PolygonDrawLayer = () => { const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (!position) return; + // console.log("position", position); + if (position.x < 0) { + position.x = 0; + } + if (position.y < 0) { + position.y = 0; + } + // console.log("set position", position); setMousePosition(position); }; const handleMouseDownPoint = (e: KonvaEventObject) => { @@ -166,13 +176,34 @@ const PolygonDrawLayer = () => { useEffect(() => { layer.current?.moveToTop(); }, []); + const handleMouseOut = (e: KonvaEventObject) => { + const position = e.currentTarget.getRelativePointerPosition(); + if (!position) return; + // console.log("position", position); + if (position.x < 0) { + position.x = 0; + } + if (position.y < 0) { + position.y = 0; + } + // console.log("set position", position); + setMousePosition(position); + }; + const renderDummyRect = () => { + if (currentAnnotationFile) { + const { width, height } = currentAnnotationFile; + return ; + } + }; + return ( - + {renderDummyRect()} { const dispatch = useDispatch(); + const [startPoint, setStartPoint] = useState(null); + const [width, setWidth] = useState(0); + const [height, setHeight] = useState(0); + const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); + updateMousePosition(position); + }; + const updateMousePosition = (position: Vector2d) => { if (!position || !startPoint) return; + if (position.x < 0) { + position.x = 0; + } + if ( + currentAnnotationFile?.width && + position.x > currentAnnotationFile?.width + ) { + position.x = currentAnnotationFile?.width; + } + if (position.y < 0) { + position.y = 0; + } + if ( + currentAnnotationFile?.height && + position.y > currentAnnotationFile?.height + ) { + position.y = currentAnnotationFile?.height; + } + const newWidth = position.x - startPoint.x; const newHeight = position.y - startPoint.y; setWidth(newWidth); @@ -53,17 +79,27 @@ const RectangleDrawLayer = () => { setHeight(0); } }; - const [startPoint, setStartPoint] = useState(null); - const [width, setWidth] = useState(0); - const [height, setHeight] = useState(0); + + const handleMouseOut = (e: KonvaEventObject) => { + const position = e.currentTarget.getRelativePointerPosition(); + updateMousePosition(position); + }; + const renderDummyRect = () => { + if (currentAnnotationFile) { + const { width, height } = currentAnnotationFile; + return ; + } + }; + return ( - + {renderDummyRect()} {startPoint && ( Date: Thu, 10 Nov 2022 14:24:20 +0700 Subject: [PATCH 06/67] fix: tooltip is display out of viewport --- src/routes/AnnotationPage/Editor/index.tsx | 42 ++++++++++++---------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index 88cf7023..b0f5504b 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -43,6 +43,9 @@ import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecet import BaseImage from "./BaseImage"; import DrawLayer from "./Layer/DrawLayer"; +const TOOLTIP_WIDTH = 200; +const TOOLTIP_HEIGHT = 40; + const Editor = () => { const dispatch = useDispatch(); const imageRef = useRef(null); @@ -89,17 +92,6 @@ const Editor = () => { return null; }, [currentAnnotationFile]); - // useEffect(() => { - // if (drawType === DrawType.DETECTED_RECTANGLE) { - // if (localDetectedArea && refDetectedArea.current) { - // dispatch( - // setDetectedArea({ - // detectedArea: { ...refDetectedArea.current.getClientRect() }, - // }) - // ); - // } - // } - // }, [localDetectedArea]); const drawObjects = useMemo(() => { const rectanglesById: Record = {}; const polygonsById: Record = {}; @@ -223,11 +215,25 @@ const Editor = () => { return; } const shape = drawObject.data; - if (layer?.current && toolTipLayer.current && toolTip.current) { + if ( + layer?.current && + toolTipLayer.current && + toolTip.current && + currentAnnotationFile + ) { const mousePos = layer.current.getRelativePointerPosition(); + let disX = 5; + let disY = 5; + if (mousePos.x + TOOLTIP_WIDTH > currentAnnotationFile.width) { + disX = -TOOLTIP_WIDTH * 2; + } + if (mousePos.y + 2 * TOOLTIP_HEIGHT > currentAnnotationFile.height) { + disY = -TOOLTIP_HEIGHT * 2; + } + toolTipLayer.current.position({ - x: mousePos.x + 5, - y: mousePos.y + 5, + x: mousePos.x + disX, + y: mousePos.y + disY, }); toolTipLayer.current.setAttrs({ id: shape.id }); toolTip.current.text( @@ -366,13 +372,13 @@ const Editor = () => { stroke={"#555"} strokeWidth={2} fill={"#ddd"} - width={200} - height={40} + width={TOOLTIP_WIDTH} + height={TOOLTIP_HEIGHT} /> Date: Thu, 10 Nov 2022 14:58:33 +0700 Subject: [PATCH 07/67] fix: after change to other image, reset DrawState to default --- .../Annotation/Editor/Shape/Polygon.tsx | 31 +-------- .../Annotation/Editor/useCommonShapeEvent.ts | 10 +-- src/reduxes/annotation/action.ts | 2 +- src/reduxes/annotation/reducer.ts | 4 +- src/reduxes/annotation/selector.ts | 2 +- src/reduxes/annotation/type.ts | 4 +- .../AnnotationPage/ControlPanel/index.tsx | 67 +++++++++---------- .../Editor/Hook/useElipseEvent.ts | 6 +- .../Editor/Hook/usePolygonEvent.ts | 8 +-- .../Editor/Hook/useRectangleEvent.ts | 6 +- .../AnnotationPage/Editor/Layer/DrawLayer.tsx | 4 +- .../Editor/Layer/PolygonDrawLayer.tsx | 10 +-- src/routes/AnnotationPage/Editor/index.tsx | 4 +- 13 files changed, 63 insertions(+), 95 deletions(-) diff --git a/src/components/Annotation/Editor/Shape/Polygon.tsx b/src/components/Annotation/Editor/Shape/Polygon.tsx index 28875caa..9d9db9ad 100644 --- a/src/components/Annotation/Editor/Shape/Polygon.tsx +++ b/src/components/Annotation/Editor/Shape/Polygon.tsx @@ -6,14 +6,12 @@ import React, { useEffect, useMemo, useState } from "react"; import { Circle, Group, Line } from "react-konva"; import { useDispatch, useSelector } from "react-redux"; import { - changeCurrentStatus, - removeDrawObjectStateIdByAI, + changeCurrentDrawState, setSelectedShape, updateDrawObject, } from "reduxes/annotation/action"; import { selectorCurrentDrawState, - selectorDetectedArea, selectorDrawObject, selectorDrawObjectState, selectorDrawObjectStateIdByAI, @@ -58,29 +56,6 @@ const PolygonComp = ({ groupRef.current?.moveToTop(); } }, [isSelected]); - - // useEffect(() => { - // if (detectedArea) { - // const polygonRect = groupRef.current?.getClientRect(); - // if (polygonRect) { - // if ( - // polygonRect.x >= detectedArea.x && - // polygonRect.x <= detectedArea.x + detectedArea.width && - // polygonRect.y >= detectedArea.y && - // polygonRect.y <= detectedArea.y + detectedArea.height && - // polygonRect.width <= detectedArea.width && - // polygonRect.height <= detectedArea.height - // ) { - // dispatch( - // removeDrawObjectStateIdByAI({ - // drawObjectStateIds: [spec.id], - // }) - // ); - // } - // } - // } - // }, [detectedArea]); - const [stage, setStage] = useState(); const [flattenedPoints, setFlattenedPoints] = useState(); const [mouseOverPoint, setMouseOverPoint] = useState(false); @@ -250,7 +225,7 @@ const PolygonComp = ({ }) ); dispatch( - changeCurrentStatus({ + changeCurrentDrawState({ drawState: DrawState.SELECTING, }) ); @@ -301,7 +276,7 @@ const PolygonComp = ({ }, }) ); - e.cancelBubble = true; + commonShapeEvent.handleTransformEnd(e); }; const handlePointDragMove = (e: KonvaEventObject) => { const { points } = spec; diff --git a/src/components/Annotation/Editor/useCommonShapeEvent.ts b/src/components/Annotation/Editor/useCommonShapeEvent.ts index b8f77944..55621a07 100644 --- a/src/components/Annotation/Editor/useCommonShapeEvent.ts +++ b/src/components/Annotation/Editor/useCommonShapeEvent.ts @@ -2,7 +2,7 @@ import { KonvaEventObject } from "konva/lib/Node"; import { useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { - changeCurrentStatus, + changeCurrentDrawState, setSelectedShape, } from "reduxes/annotation/action"; import { @@ -31,7 +31,7 @@ const useCommonShapeEvent = ({ const dispatch = useDispatch(); const handleDragEnd = (e: KonvaEventObject) => { dispatch( - changeCurrentStatus({ + changeCurrentDrawState({ drawState: DrawState.SELECTING, }) ); @@ -39,7 +39,7 @@ const useCommonShapeEvent = ({ }; const handleDragStart = (e: KonvaEventObject) => { dispatch( - changeCurrentStatus({ + changeCurrentDrawState({ drawState: DrawState.DRAGGING, }) ); @@ -50,7 +50,7 @@ const useCommonShapeEvent = ({ }; const handleTransformStart = (e: KonvaEventObject) => { dispatch( - changeCurrentStatus({ + changeCurrentDrawState({ drawState: DrawState.TRANSFORMING, }) ); @@ -58,7 +58,7 @@ const useCommonShapeEvent = ({ }; const handleTransformEnd = (e: KonvaEventObject) => { dispatch( - changeCurrentStatus({ + changeCurrentDrawState({ drawState: DrawState.SELECTING, }) ); diff --git a/src/reduxes/annotation/action.ts b/src/reduxes/annotation/action.ts index 7d3595b4..92e64b07 100644 --- a/src/reduxes/annotation/action.ts +++ b/src/reduxes/annotation/action.ts @@ -56,7 +56,7 @@ export const updateDrawObject = (payload: UpdateDrawObjectPayload) => ({ payload, }); -export const changeCurrentStatus = ( +export const changeCurrentDrawState = ( payload: ChangeCurrentDrawStatePayload ) => ({ type: CHANGE_CURRENT_DRAW_STATE, diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index 06fb2531..8c09c5d9 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -45,7 +45,7 @@ import { } from "./type"; const inititalState: AnnotationReducer = { - currentDrawType: DrawType.RECTANGLE, + currentDrawType: null, selectedDrawObjectId: null, zoom: { zoom: 1, position: { x: 0, y: 0 } }, // drawObjectById: (() => { @@ -81,7 +81,7 @@ const inititalState: AnnotationReducer = { // return ret; // })(), drawObjectById: {}, - currentDrawState: DrawState.FREE, + currentDrawState: DrawState.SELECTING, previousDrawState: DrawState.FREE, statehHistory: { historyStep: 0, stateHistoryItems: [] }, drawObjectStateById: {}, diff --git a/src/reduxes/annotation/selector.ts b/src/reduxes/annotation/selector.ts index 21cac5aa..205eb36f 100644 --- a/src/reduxes/annotation/selector.ts +++ b/src/reduxes/annotation/selector.ts @@ -6,7 +6,7 @@ import { import { RootState } from "reduxes"; import { DrawType } from "./type"; -export const selectorcurrentDrawType = (state: RootState) => +export const selectorCurrentDrawType = (state: RootState) => state.annotationReducer.currentDrawType; export const selectorSelectedDrawObjectId = (state: RootState) => state.annotationReducer.selectedDrawObjectId; diff --git a/src/reduxes/annotation/type.ts b/src/reduxes/annotation/type.ts index b7aecfdf..ec3cde02 100644 --- a/src/reduxes/annotation/type.ts +++ b/src/reduxes/annotation/type.ts @@ -27,7 +27,7 @@ export interface DrawObject { export interface AnnotationReducer { currentDrawState: DrawState; previousDrawState: DrawState; - currentDrawType: DrawType; + currentDrawType: DrawType | null; selectedDrawObjectId: string | null; zoom: ZoomProps; drawObjectById: Record; @@ -61,7 +61,7 @@ export interface ControlPanelProp { onResetScale: () => void; } export interface ChangeCurrentDrawTypePayload { - currentDrawType: DrawType; + currentDrawType: DrawType | null; } export interface ZoomProps { zoom: number; diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index 02cddd0c..d7ea7de3 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -1,6 +1,7 @@ import Crop32Icon from "@mui/icons-material/Crop32"; import HexagonIcon from "@mui/icons-material/Hexagon"; import ImageSearchIcon from "@mui/icons-material/ImageSearch"; +import NearMeIcon from "@mui/icons-material/NearMe"; import PanoramaFishEyeIcon from "@mui/icons-material/PanoramaFishEye"; import PolylineIcon from "@mui/icons-material/Polyline"; import RedoIcon from "@mui/icons-material/Redo"; @@ -22,6 +23,7 @@ import { import ToggleButton from "@mui/material/ToggleButton"; import ToggleButtonGroup from "@mui/material/ToggleButtonGroup"; import { CssStyle } from "components/Annotation/Editor/type"; +import { getFitScaleEditor } from "components/Annotation/Editor/utils"; import { exportAnnotationDaita, exportAnnotationLabelBox, @@ -35,8 +37,8 @@ import * as React from "react"; import { useDropzone } from "react-dropzone"; import { useDispatch, useSelector } from "react-redux"; import { + changeCurrentDrawState, changeCurrentDrawType, - changeCurrentStatus, changeZoom, createDrawObject, redoDrawObject, @@ -46,7 +48,7 @@ import { import { selectorAnnotationStatehHistory, selectorCurrentDrawState, - selectorcurrentDrawType, + selectorCurrentDrawType, selectorDrawObjectById, selectorDrawObjectStateIdByAI, } from "reduxes/annotation/selector"; @@ -66,21 +68,13 @@ import { } from "reduxes/annotationmanager/selecetor"; import { hashCode, intToRGB } from "../LabelAnnotation"; import { convertStrokeColorToFillColor } from "../LabelAnnotation/ClassLabel"; -import NearMeIcon from "@mui/icons-material/NearMe"; -import { - MAX_HEIGHT_IMAGE_IN_EDITOR, - MAX_WIDTH_IMAGE_IN_EDITOR, -} from "components/Annotation/Editor/const"; -import { getFitScaleEditor } from "components/Annotation/Editor/utils"; const ControlPanel = () => { const dispatch = useDispatch(); - const [drawType, setDrawType] = React.useState( - useSelector(selectorcurrentDrawType) - ); - const [drawState, setDrawState] = React.useState( - useSelector(selectorCurrentDrawState) - ); + const currentDrawType = useSelector(selectorCurrentDrawType); + const currentDrawState = useSelector(selectorCurrentDrawState); + // const [drawType, setDrawType] = React.useState(); + // const [drawState, setDrawState] = React.useState(); const drawObjectById = useSelector(selectorDrawObjectById); const currentPreviewImageName = useSelector(selectorCurrentPreviewImageName); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); @@ -117,16 +111,14 @@ const ControlPanel = () => { type: DrawType ) => { dispatch(changeCurrentDrawType({ currentDrawType: type })); - setDrawType(type); - setDrawState(null); + dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); }; const handleSelectDrawState = ( event: React.MouseEvent, state: DrawState ) => { - dispatch(changeCurrentStatus({ drawState: state })); - setDrawState(state); - setDrawType(null); + dispatch(changeCurrentDrawState({ drawState: state })); + dispatch(changeCurrentDrawType({ currentDrawType: null })); }; const getDrawObjectToExport = () => { if (drawObjectById) { @@ -395,7 +387,24 @@ const ControlPanel = () => { <> + + + + + { - - - - - + { let drawObject = createEllipse(position); dispatch(createDrawObject({ drawObject })); dispatch(setSelectedShape({ selectedDrawObjectId: drawObject.data.id })); - dispatch(changeCurrentStatus({ drawState: DrawState.DRAWING })); + dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); } }; @@ -71,7 +71,7 @@ const useEllipseEvent = () => { }; const handleMouseUp = () => { if (currentDrawState === DrawState.DRAWING) { - dispatch(changeCurrentStatus({ drawState: DrawState.SELECTING })); + dispatch(changeCurrentDrawState({ drawState: DrawState.SELECTING })); } }; return { handleMouseDown, handleMouseMove, handleMouseUp }; diff --git a/src/routes/AnnotationPage/Editor/Hook/usePolygonEvent.ts b/src/routes/AnnotationPage/Editor/Hook/usePolygonEvent.ts index 5305c889..c6cddb49 100644 --- a/src/routes/AnnotationPage/Editor/Hook/usePolygonEvent.ts +++ b/src/routes/AnnotationPage/Editor/Hook/usePolygonEvent.ts @@ -3,14 +3,14 @@ import { PolygonSpec } from "components/Annotation/Editor/type"; import { Vector2d } from "konva/lib/types"; import { useDispatch, useSelector } from "react-redux"; import { - changeCurrentStatus, + changeCurrentDrawState, createDrawObject, setSelectedShape, updateDrawObject, } from "reduxes/annotation/action"; import { selectorCurrentDrawState, - selectorcurrentDrawType, + selectorCurrentDrawType, selectorSelectedPolygonOrLineStrip, } from "reduxes/annotation/selector"; import { @@ -46,7 +46,7 @@ const usePolygonEvent = () => { const dispatch = useDispatch(); const polygon = useSelector(selectorSelectedPolygonOrLineStrip); const currentDrawState = useSelector(selectorCurrentDrawState); - const drawType = useSelector(selectorcurrentDrawType); + const drawType = useSelector(selectorCurrentDrawType); const handleMouseDown = (e: EditorEventPayload) => { const position = e.eventObject.currentTarget.getRelativePointerPosition(); @@ -64,7 +64,7 @@ const usePolygonEvent = () => { dispatch( setSelectedShape({ selectedDrawObjectId: drawObject.data.id }) ); - dispatch(changeCurrentStatus({ drawState: DrawState.DRAWING })); + dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); } } else { const polygonAfterBuild = buildPolygon(polygon as PolygonSpec, position); diff --git a/src/routes/AnnotationPage/Editor/Hook/useRectangleEvent.ts b/src/routes/AnnotationPage/Editor/Hook/useRectangleEvent.ts index 64e5834a..32e2c465 100644 --- a/src/routes/AnnotationPage/Editor/Hook/useRectangleEvent.ts +++ b/src/routes/AnnotationPage/Editor/Hook/useRectangleEvent.ts @@ -2,7 +2,7 @@ import { LINE_STYLE } from "components/Annotation/Editor/const"; import { RectangleSpec } from "components/Annotation/Editor/type"; import { useDispatch, useSelector } from "react-redux"; import { - changeCurrentStatus, + changeCurrentDrawState, createDrawObject, setSelectedShape, updateDrawObject, @@ -47,7 +47,7 @@ const useRectangleEvent = () => { let drawObject = createRectangle(position); dispatch(createDrawObject({ drawObject })); dispatch(setSelectedShape({ selectedDrawObjectId: drawObject.data.id })); - dispatch(changeCurrentStatus({ drawState: DrawState.DRAWING })); + dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); } }; @@ -67,7 +67,7 @@ const useRectangleEvent = () => { }; const handleMouseUp = () => { if (currentDrawState === DrawState.DRAWING) { - dispatch(changeCurrentStatus({ drawState: DrawState.SELECTING })); + dispatch(changeCurrentDrawState({ drawState: DrawState.SELECTING })); } }; return { handleMouseDown, handleMouseMove, handleMouseUp }; diff --git a/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx index 41cdf647..acee4e33 100644 --- a/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx @@ -2,7 +2,7 @@ import { Layer } from "react-konva"; import { useSelector } from "react-redux"; import { selectorCurrentDrawState, - selectorcurrentDrawType, + selectorCurrentDrawType, } from "reduxes/annotation/selector"; import { DrawState, DrawType } from "reduxes/annotation/type"; import DetectedRectangleDrawLayer from "./DetectedRectangleDrawLayer"; @@ -12,7 +12,7 @@ import RectangleDrawLayer from "./RectangleDrawLayer"; const DrawLayer = () => { const currentDrawState = useSelector(selectorCurrentDrawState); - const drawType = useSelector(selectorcurrentDrawType); + const drawType = useSelector(selectorCurrentDrawType); const render = () => { if ( diff --git a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx index 1775e3c7..61fde17d 100644 --- a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx @@ -12,12 +12,12 @@ import Konva from "konva"; import { Vector2d } from "konva/lib/types"; import { useDispatch, useSelector } from "react-redux"; import { - changeCurrentStatus, + changeCurrentDrawState, createDrawObject, } from "reduxes/annotation/action"; import { selectorCurrentDrawState, - selectorcurrentDrawType, + selectorCurrentDrawType, } from "reduxes/annotation/selector"; import { DrawState, DrawType } from "reduxes/annotation/type"; import { convertStrokeColorToFillColor } from "routes/AnnotationPage/LabelAnnotation/ClassLabel"; @@ -28,7 +28,7 @@ import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecet const PolygonDrawLayer = () => { const dispatch = useDispatch(); const isLineStrip = - useSelector(selectorcurrentDrawType) === DrawType.LINE_STRIP; + useSelector(selectorCurrentDrawType) === DrawType.LINE_STRIP; const [flattenedPoints, setFlattenedPoints] = useState([]); const [lineStyle, setLineStyle] = useState({ fill: convertStrokeColorToFillColor("#affaaa"), @@ -79,7 +79,7 @@ const PolygonDrawLayer = () => { }, }) ); - dispatch(changeCurrentStatus({ drawState: DrawState.FREE })); + dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); setPoints([]); setIsFinished(false); setMousePosition(null); @@ -170,7 +170,7 @@ const PolygonDrawLayer = () => { if (!position) return; setPoints([...points, position]); if (currentDrawState === DrawState.FREE) { - dispatch(changeCurrentStatus({ drawState: DrawState.DRAWING })); + dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); } }; useEffect(() => { diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index b0f5504b..ea6569ec 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -23,7 +23,7 @@ import { useSelector, } from "react-redux"; import { - changeCurrentStatus, + changeCurrentDrawState, changeZoom, deleteDrawObject, recoverPreviousDrawState, @@ -171,7 +171,7 @@ const Editor = () => { } else if (e.key === " ") { setKeyDown(e.key); if (currentDrawState !== DrawState.ZOOMDRAGGING) { - dispatch(changeCurrentStatus({ drawState: DrawState.ZOOMDRAGGING })); + dispatch(changeCurrentDrawState({ drawState: DrawState.ZOOMDRAGGING })); } } else if (e.key === "Delete") { if (selectedDrawObjectId) { From a4ae20d8da4d77ea60ae0fc67bf9b86be44db4ca Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Thu, 10 Nov 2022 21:51:03 +0700 Subject: [PATCH 08/67] fix: List Label still show, when change annotating image --- src/reduxes/annotationmanager/action.ts | 12 ++++++++++-- src/reduxes/annotationmanager/constants.ts | 2 +- src/reduxes/annotationmanager/reducer.ts | 15 ++++++++++++++- src/reduxes/annotationmanager/selecetor.ts | 2 ++ .../AnnotationPage/ImagePreview/index.tsx | 15 ++++----------- .../AnnotationPage/LabelAnnotation/index.tsx | 17 ++++++++++++++--- src/sagas/annotationEditorSaga.tsx | 6 +++++- 7 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/reduxes/annotationmanager/action.ts b/src/reduxes/annotationmanager/action.ts index 5891aea8..7b1aef9a 100644 --- a/src/reduxes/annotationmanager/action.ts +++ b/src/reduxes/annotationmanager/action.ts @@ -27,10 +27,18 @@ export const addImagesToAnnotation = (payload: AddImageToAnnotationProps) => ({ type: ADD_IMAGES, payload, }); -export const changePreviewImage = (payload: ChangePreviewImageProps) => ({ - type: CHANGE_PREVIEW_IMAGE, +export const requestChangePreviewImage = ( + payload: ChangePreviewImageProps +) => ({ + type: CHANGE_PREVIEW_IMAGE.REQUESTED, payload, }); +export const requestChangePreviewImageSuccess = () => ({ + type: CHANGE_PREVIEW_IMAGE.SUCCEEDED, +}); +export const requestChangePreviewImageFail = () => ({ + type: CHANGE_PREVIEW_IMAGE.FAILED, +}); export const setPreviewImage = (payload: ChangePreviewImageProps) => ({ type: SET_PREVIEW_IMAGE, payload, diff --git a/src/reduxes/annotationmanager/constants.ts b/src/reduxes/annotationmanager/constants.ts index ea388c95..4ba42aa3 100644 --- a/src/reduxes/annotationmanager/constants.ts +++ b/src/reduxes/annotationmanager/constants.ts @@ -1,7 +1,7 @@ import { asyncAction } from "utils/general"; export const ADD_IMAGES = "ADD_IMAGES"; -export const CHANGE_PREVIEW_IMAGE = "CHANGE_PREVIEW_IMAGE"; +export const CHANGE_PREVIEW_IMAGE = asyncAction("CHANGE_PREVIEW_IMAGE"); export const SET_PREVIEW_IMAGE = "SET_PREVIEW_IMAGE"; export const SAVE_ANNOTATION_STATE_MANAGER = asyncAction( "SAVE_ANNOTATION_STATE_MANAGER" diff --git a/src/reduxes/annotationmanager/reducer.ts b/src/reduxes/annotationmanager/reducer.ts index 031ecb37..96934ea0 100644 --- a/src/reduxes/annotationmanager/reducer.ts +++ b/src/reduxes/annotationmanager/reducer.ts @@ -53,11 +53,24 @@ const annotationManagerReducer = ( images: { ...stateImages }, }; } - case CHANGE_PREVIEW_IMAGE: { + case CHANGE_PREVIEW_IMAGE.REQUESTED: { const { imageName } = payload as ChangePreviewImageProps; return { ...state, currentPreviewImageName: imageName, + isFetchingImageData: true, + }; + } + case CHANGE_PREVIEW_IMAGE.SUCCEEDED: { + return { + ...state, + isFetchingImageData: false, + }; + } + case CHANGE_PREVIEW_IMAGE.FAILED: { + return { + ...state, + isFetchingImageData: false, }; } case SET_PREVIEW_IMAGE: { diff --git a/src/reduxes/annotationmanager/selecetor.ts b/src/reduxes/annotationmanager/selecetor.ts index fdb5de3e..fdb13f7a 100644 --- a/src/reduxes/annotationmanager/selecetor.ts +++ b/src/reduxes/annotationmanager/selecetor.ts @@ -20,3 +20,5 @@ export const selectorDialogClassManageModal = (state: RootState) => state.annotationManagerReducer.dialogClassManageModal; export const selectorIsSavingAnnotation = (state: RootState) => state.annotationManagerReducer.isSavingAnnotation; +export const selectorIsFetchingImageData = (state: RootState) => + state.annotationManagerReducer.isFetchingImageData; diff --git a/src/routes/AnnotationPage/ImagePreview/index.tsx b/src/routes/AnnotationPage/ImagePreview/index.tsx index ef64547d..c73d4b92 100644 --- a/src/routes/AnnotationPage/ImagePreview/index.tsx +++ b/src/routes/AnnotationPage/ImagePreview/index.tsx @@ -1,23 +1,16 @@ import { Box, List, ListItem, Skeleton, Typography } from "@mui/material"; -import { loadImage } from "components/UploadFile"; -import { IMAGE_EXTENSIONS } from "constants/defaultValues"; -import { useEffect, useMemo } from "react"; -import { useDropzone } from "react-dropzone"; +import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { resetCurrentStateDrawObject } from "reduxes/annotation/action"; import { selectorDrawObjectById } from "reduxes/annotation/selector"; import { - addImagesToAnnotation, - changePreviewImage, - saveAnnotationStateManager, + requestChangePreviewImage, setAnnotationStateManager, } from "reduxes/annotationmanager/action"; import { - selectorAnnotationManagerImages, selectorCurrentPreviewImageName, selectorIdDrawObjectByImageName, } from "reduxes/annotationmanager/selecetor"; -import { AnnotationImagesProperty } from "reduxes/annotationmanager/type"; import { selectorCurrentAnnotationFiles } from "reduxes/annotationProject/selector"; const createFile = async (imageName: string, url: string) => { @@ -53,7 +46,7 @@ const ImagePreview = function () { useEffect(() => { if (currentAnnotationFiles) { dispatch( - changePreviewImage({ + requestChangePreviewImage({ imageName: currentAnnotationFiles.items[0].filename, }) ); @@ -171,7 +164,7 @@ const ImagePreview = function () { drawObjectById: idDrawObjectByImageName[imageName], }) ); - dispatch(changePreviewImage({ imageName })); + dispatch(requestChangePreviewImage({ imageName })); }; const renderContent = () => { if (!currentAnnotationFiles) { diff --git a/src/routes/AnnotationPage/LabelAnnotation/index.tsx b/src/routes/AnnotationPage/LabelAnnotation/index.tsx index f2097746..3dd10ab6 100644 --- a/src/routes/AnnotationPage/LabelAnnotation/index.tsx +++ b/src/routes/AnnotationPage/LabelAnnotation/index.tsx @@ -1,5 +1,5 @@ import AddIcon from "@mui/icons-material/Add"; -import { Button } from "@mui/material"; +import { Button, CircularProgress } from "@mui/material"; import Box from "@mui/material/Box"; import { LabelClassProperties } from "components/Annotation/Editor/type"; import { CSSProperties, useMemo } from "react"; @@ -11,7 +11,10 @@ import { selectorDrawObjectStateIdByAI, } from "reduxes/annotation/selector"; import { setDialogClassManageModal } from "reduxes/annotationmanager/action"; -import { selectorDialogClassManageModal } from "reduxes/annotationmanager/selecetor"; +import { + selectorDialogClassManageModal, + selectorIsFetchingImageData, +} from "reduxes/annotationmanager/selecetor"; import ClassItem from "./ClassItem"; import { convertStrokeColorToFillColor } from "./ClassLabel"; import ClassManageModel from "./ClassManageModal"; @@ -47,7 +50,7 @@ const LabelAnnotation = function () { const dispatch = useDispatch(); const drawObjectById = useSelector(selectorDrawObjectById); const drawObjectStateIdByAI = useSelector(selectorDrawObjectStateIdByAI); - + const issFetchingImageData = useSelector(selectorIsFetchingImageData); const hanleOpenClassManageModalClick = () => { dispatch( setDialogClassManageModal({ @@ -73,7 +76,15 @@ const LabelAnnotation = function () { } return <>; } + const renderList = () => { + if (issFetchingImageData) { + return ( + + + + ); + } return ( diff --git a/src/sagas/annotationEditorSaga.tsx b/src/sagas/annotationEditorSaga.tsx index d0950c50..42d06f1d 100644 --- a/src/sagas/annotationEditorSaga.tsx +++ b/src/sagas/annotationEditorSaga.tsx @@ -37,6 +37,8 @@ import { import { addImagesToAnnotation, addNewClassLabel, + requestChangePreviewImageFail, + requestChangePreviewImageSuccess, setAnnotationStateManager, } from "reduxes/annotationmanager/action"; import { selectorLabelClassPropertiesByLabelClass } from "reduxes/annotationmanager/selecetor"; @@ -236,8 +238,10 @@ function* handleFetchAnnotationAndFileInfo(action: any): any { ); if (image) { yield put(addImagesToAnnotation({ annotationImagesProperties: [image] })); + yield put(requestChangePreviewImageSuccess()); } else { toast.error("Fail to get image data"); + yield put(requestChangePreviewImageFail()); } } catch (e: any) { toast.error(e.message); @@ -325,7 +329,7 @@ function* handleSaveAnnotationStateManager(action: any): any { } } function* annotationEditorSaga() { - yield takeLatest(CHANGE_PREVIEW_IMAGE, handleChangeImagePreview); + yield takeLatest(CHANGE_PREVIEW_IMAGE.REQUESTED, handleChangeImagePreview); yield takeLatest( SAVE_REMOTE_NEW_CLASS_LABEL.REQUESTED, handleAddNewClassLabel From 235a36305f46fa94740060b568008c0309cec20d Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Thu, 10 Nov 2022 23:52:17 +0700 Subject: [PATCH 09/67] feat: add Show All/Hidden All Segmentation button on annotation tool --- .../Annotation/Editor/Shape/Polygon.tsx | 6 +- src/reduxes/annotation/action.ts | 18 +++-- src/reduxes/annotation/constants.ts | 4 +- src/reduxes/annotation/reducer.ts | 57 +++++++++------ src/reduxes/annotation/type.ts | 8 ++- .../AnnotationPage/ControlPanel/index.tsx | 72 +++++++++++++------ .../AnnotationPage/LabelAnnotation/index.tsx | 5 +- 7 files changed, 116 insertions(+), 54 deletions(-) diff --git a/src/components/Annotation/Editor/Shape/Polygon.tsx b/src/components/Annotation/Editor/Shape/Polygon.tsx index 9d9db9ad..6aa1e973 100644 --- a/src/components/Annotation/Editor/Shape/Polygon.tsx +++ b/src/components/Annotation/Editor/Shape/Polygon.tsx @@ -374,7 +374,11 @@ const PolygonComp = ({ }, [spec.cssStyle]); const isVisible = useMemo(() => { const isVis = drawObjectState ? !drawObjectState.isHidden : true; - return isVis && !drawObjectStateIdByAI.includes(spec.id); + return ( + isVis && + (!drawObjectStateIdByAI[spec.id] || + drawObjectStateIdByAI[spec.id].isShow === true) + ); }, [drawObjectState, drawObjectStateIdByAI]); return ( ({ - type: REMOVE_DRAW_OBJECTS_BY_AI, + type: SHOW_DRAW_OBJECTS_BY_AI, payload, }); export const resetAnnotation = () => ({ @@ -124,3 +126,9 @@ export const resetAnnotation = () => ({ export const recoverPreviousDrawState = () => ({ type: RECOVER_PREVIOUS_DRAWSTATE, }); +export const showAllDrawObjectStateIdByAI = () => ({ + type: SHOW_ALL_DRAW_OBJECTS_BY_AI, +}); +export const hiddenAllDrawObjectStateIdByAI = () => ({ + type: HIDDEN_ALL_DRAW_OBJECTS_BY_AI, +}); diff --git a/src/reduxes/annotation/constants.ts b/src/reduxes/annotation/constants.ts index 39bfc517..6afcd317 100644 --- a/src/reduxes/annotation/constants.ts +++ b/src/reduxes/annotation/constants.ts @@ -15,6 +15,8 @@ export const SET_LOCK_DRAW_OBJECT = "SET_LOCK_DRAW_OBJECT"; export const SET_DETECTED_AREA = "SET_DETECTED_AREA"; export const SET_IS_DRAGGING_VIEW_PORT = "SET_IS_DRAGGING_VIEW_PORT"; export const ADD_DRAW_OBJECTS_BY_AI = "ADD_DRAW_OBJECTS_BY_AI"; -export const REMOVE_DRAW_OBJECTS_BY_AI = "REMOVE_DRAW_OBJECTS_BY_AI"; +export const SHOW_DRAW_OBJECTS_BY_AI = "SHOW_DRAW_OBJECTS_BY_AI"; export const RESET_ANNOTATION = "RESET_ANNOTATION"; export const RECOVER_PREVIOUS_DRAWSTATE = "RECOVER_PREVIOUS_DRAWSTATE"; +export const SHOW_ALL_DRAW_OBJECTS_BY_AI = "SHOW_ALL_DRAW_OBJECTS_BY_AI"; +export const HIDDEN_ALL_DRAW_OBJECTS_BY_AI = "HIDDEN_ALL_DRAW_OBJECTS_BY_AI"; diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index 8c09c5d9..3c8ce3ee 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -6,9 +6,9 @@ import { CHANGE_ZOOM, CREATE_DRAW_OBJECT, DELETE_DRAW_OBJECT, + HIDDEN_ALL_DRAW_OBJECTS_BY_AI, RECOVER_PREVIOUS_DRAWSTATE, REDO_DRAW_OBJECT, - REMOVE_DRAW_OBJECTS_BY_AI, RESET_ANNOTATION, RESET_CURRENT_STATE_DRAW_OBJECT, SET_DETECTED_AREA, @@ -16,6 +16,8 @@ import { SET_IS_DRAGGING_VIEW_PORT, SET_LOCK_DRAW_OBJECT, SET_SELECT_SHAPE, + SHOW_ALL_DRAW_OBJECTS_BY_AI, + SHOW_DRAW_OBJECTS_BY_AI, UNDO_DRAW_OBJECT, UPDATE_DRAW_OBJECT, UPDATE_LABEL_OF_DRAW_OBJECT, @@ -29,16 +31,17 @@ import { CreateDrawObjectPayload, DeleteDrawObjectPayload, DrawObject, + DrawObjectByAIState, DrawObjectState, DrawState, DrawType, - RemoveDrawObjectStateIdByAIPayload, ResetCurrentStateDrawObjectPayload, SetHiddenDrawObjectPayload, SetIsDraggingViewportPayload, SetLockDetectedAreaPayload, SetLockDrawObecjtPayload, SetSelectShapePayload, + ShowDrawObjectStateIdByAIPayload, StateHistory, UpdateDrawObjectPayload, UpdateLabelOfDrawObjectPayload, @@ -87,7 +90,7 @@ const inititalState: AnnotationReducer = { drawObjectStateById: {}, detectedArea: null, isDraggingViewport: false, - drawObjectStateIdByAI: [], + drawObjectStateIdByAI: {}, }; const updateStateHistory = ( drawObjectById: Record, @@ -224,7 +227,7 @@ const annotationReducer = ( ...inititalState, drawObjectById: { ...drawObjectById }, drawObjectStateById, - drawObjectStateIdByAI: [...state.drawObjectStateIdByAI], + drawObjectStateIdByAI: { ...state.drawObjectStateIdByAI }, }; } case UPDATE_LABEL_OF_DRAW_OBJECT: { @@ -342,12 +345,12 @@ const annotationReducer = ( } case SET_DETECTED_AREA: { const { detectedArea, scale } = payload as SetLockDetectedAreaPayload; - const newDrawObjectStateIdByAI = new Set(state.drawObjectStateIdByAI); + const newDrawObjectStateIdByAI = { ...state.drawObjectStateIdByAI }; if (detectedArea) { const scaleX = 1 / scale.x; const scaleY = 1 / scale.y; - for (const drawObjectId of state.drawObjectStateIdByAI) { + for (const drawObjectId of Object.keys(state.drawObjectStateIdByAI)) { const drawObject = state.drawObjectById[drawObjectId]; if (drawObject) { const { type, data } = drawObject; @@ -362,7 +365,10 @@ const annotationReducer = ( ); }); if (!isInvalid) { - newDrawObjectStateIdByAI.delete(drawObjectId); + newDrawObjectStateIdByAI[drawObjectId] = { + ...newDrawObjectStateIdByAI[drawObjectId], + isShow: true, + }; } } } @@ -371,7 +377,7 @@ const annotationReducer = ( return { ...state, detectedArea, - drawObjectStateIdByAI: Array.from(newDrawObjectStateIdByAI), + drawObjectStateIdByAI: newDrawObjectStateIdByAI, }; } case SET_IS_DRAGGING_VIEW_PORT: { @@ -383,30 +389,27 @@ const annotationReducer = ( } case ADD_DRAW_OBJECTS_BY_AI: { const { drawObjectStateIds } = payload as AddDrawObjectStateIdByAIPayload; - const newList = [...state.drawObjectStateIdByAI]; + const newDrawObjectStateIdByAI = { ...state.drawObjectStateIdByAI }; drawObjectStateIds.forEach((id) => { - if (!state.drawObjectStateIdByAI.includes(id)) { - newList.push(id); + if (!state.drawObjectStateIdByAI[id]) { + newDrawObjectStateIdByAI[id] = { isShow: false }; } }); return { ...state, - drawObjectStateIdByAI: newList, + drawObjectStateIdByAI: newDrawObjectStateIdByAI, }; } - case REMOVE_DRAW_OBJECTS_BY_AI: { + case SHOW_DRAW_OBJECTS_BY_AI: { const { drawObjectStateIds } = - payload as RemoveDrawObjectStateIdByAIPayload; - const newList = [...state.drawObjectStateIdByAI]; + payload as ShowDrawObjectStateIdByAIPayload; + const newDrawObjectStateIdByAI = { ...state.drawObjectStateIdByAI }; drawObjectStateIds.forEach((id) => { - const indexOf = newList.indexOf(id); - if (indexOf !== -1) { - newList.splice(indexOf, 1); - } + delete newDrawObjectStateIdByAI[id]; }); return { ...state, - drawObjectStateIdByAI: newList, + drawObjectStateIdByAI: newDrawObjectStateIdByAI, }; } case RESET_ANNOTATION: { @@ -418,6 +421,20 @@ const annotationReducer = ( currentDrawState: state.previousDrawState, }; } + case SHOW_ALL_DRAW_OBJECTS_BY_AI: { + const newDrawObjectStateIdByAI: Record = {}; + Object.entries(state.drawObjectStateIdByAI).map(([key, value]) => { + newDrawObjectStateIdByAI[key] = { ...value, isShow: true }; + }); + return { ...state, drawObjectStateIdByAI: newDrawObjectStateIdByAI }; + } + case HIDDEN_ALL_DRAW_OBJECTS_BY_AI: { + const newDrawObjectStateIdByAI: Record = {}; + Object.entries(state.drawObjectStateIdByAI).map(([key, value]) => { + newDrawObjectStateIdByAI[key] = { ...value, isShow: false }; + }); + return { ...state, drawObjectStateIdByAI: newDrawObjectStateIdByAI }; + } default: return state; } diff --git a/src/reduxes/annotation/type.ts b/src/reduxes/annotation/type.ts index ec3cde02..6362dc77 100644 --- a/src/reduxes/annotation/type.ts +++ b/src/reduxes/annotation/type.ts @@ -32,12 +32,14 @@ export interface AnnotationReducer { zoom: ZoomProps; drawObjectById: Record; drawObjectStateById: Record; - drawObjectStateIdByAI: string[]; + drawObjectStateIdByAI: Record; statehHistory: StateHistory; detectedArea: DetectedAreaType | null; isDraggingViewport: boolean; } - +export interface DrawObjectByAIState { + isShow: boolean; +} export interface DetectedAreaType { x: number; y: number; @@ -114,6 +116,6 @@ export interface SetIsDraggingViewportPayload { export interface AddDrawObjectStateIdByAIPayload { drawObjectStateIds: string[]; } -export interface RemoveDrawObjectStateIdByAIPayload { +export interface ShowDrawObjectStateIdByAIPayload { drawObjectStateIds: string[]; } diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index d7ea7de3..cb825283 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -7,6 +7,8 @@ import PolylineIcon from "@mui/icons-material/Polyline"; import RedoIcon from "@mui/icons-material/Redo"; import SaveIcon from "@mui/icons-material/Save"; import UndoIcon from "@mui/icons-material/Undo"; +import VisibilityIcon from "@mui/icons-material/Visibility"; +import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; import WarningAmberIcon from "@mui/icons-material/WarningAmber"; import { LoadingButton } from "@mui/lab"; import { @@ -41,8 +43,10 @@ import { changeCurrentDrawType, changeZoom, createDrawObject, + hiddenAllDrawObjectStateIdByAI, redoDrawObject, resetCurrentStateDrawObject, + showAllDrawObjectStateIdByAI, undoDrawObject, } from "reduxes/annotation/action"; import { @@ -68,7 +72,6 @@ import { } from "reduxes/annotationmanager/selecetor"; import { hashCode, intToRGB } from "../LabelAnnotation"; import { convertStrokeColorToFillColor } from "../LabelAnnotation/ClassLabel"; - const ControlPanel = () => { const dispatch = useDispatch(); const currentDrawType = useSelector(selectorCurrentDrawType); @@ -123,7 +126,7 @@ const ControlPanel = () => { const getDrawObjectToExport = () => { if (drawObjectById) { const filteredDrawObjectById = { ...drawObjectById }; - drawObjectStateIdByAI.forEach((id) => { + Object.keys(drawObjectStateIdByAI).forEach((id) => { delete filteredDrawObjectById[id]; }); return filteredDrawObjectById; @@ -380,9 +383,19 @@ const ControlPanel = () => { } }; const isAIDetectAvailable = React.useMemo( - () => drawObjectStateIdByAI && drawObjectStateIdByAI.length !== 0, + () => + drawObjectStateIdByAI && Object.keys(drawObjectStateIdByAI).length !== 0, [drawObjectStateIdByAI] ); + const [isShowAllAIDetect, setIsShowAllAIDetect] = React.useState(false); + const handleClickShowAllAIDetect = () => { + setIsShowAllAIDetect(!isShowAllAIDetect); + if (isShowAllAIDetect) { + dispatch(hiddenAllDrawObjectStateIdByAI()); + } else { + dispatch(showAllDrawObjectStateIdByAI()); + } + }; return ( <> @@ -447,29 +460,42 @@ const ControlPanel = () => { mt={3} sx={{ border: "1px dashed grey" }} justifyContent="space-evenly" + flexDirection="column" + gap={2} > - - - - selectModeHandle(e, DrawType.DETECTED_RECTANGLE) - } - > - {isAIDetectAvailable ? ( - - ) : ( - - )} + + + + + selectModeHandle(e, DrawType.DETECTED_RECTANGLE) + } + > + {isAIDetectAvailable ? ( + + ) : ( + + )} + + + + + {isAIDetectAvailable && ( + + Segmentations + + {isShowAllAIDetect ? : } - - + + )} + { const retList: string[] = []; Object.keys(drawObjectById).map((id) => { - if (!drawObjectStateIdByAI.includes(id)) { + if ( + !drawObjectStateIdByAI[id] || + drawObjectStateIdByAI[id].isShow === true + ) { retList.push(id); } }); From 329cd0c3f04b43dbaa9f0300d49a6e8af78717e7 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 11 Nov 2022 11:25:53 +0700 Subject: [PATCH 10/67] fix: drawState is change to Polygon after remove a shape --- src/reduxes/annotation/reducer.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index 3c8ce3ee..4a899a5b 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -85,7 +85,7 @@ const inititalState: AnnotationReducer = { // })(), drawObjectById: {}, currentDrawState: DrawState.SELECTING, - previousDrawState: DrawState.FREE, + previousDrawState: DrawState.SELECTING, statehHistory: { historyStep: 0, stateHistoryItems: [] }, drawObjectStateById: {}, detectedArea: null, @@ -208,7 +208,6 @@ const annotationReducer = ( delete state.drawObjectById[drawObjectId]; return { ...state, - currentDrawState: DrawState.FREE, selectedDrawObjectId: null, drawObjectById: { ...state.drawObjectById }, }; From ddb5fb380a9e4fbe19d138dc1d3b6abf15cd2552 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 11 Nov 2022 11:47:30 +0700 Subject: [PATCH 11/67] fix: do not allow diselect default tool --- src/reduxes/annotation/reducer.ts | 4 +++- src/routes/AnnotationPage/ControlPanel/index.tsx | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index 4a899a5b..5a40797e 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -128,7 +128,9 @@ const annotationReducer = ( ...state, currentDrawType, selectedDrawObjectId: null, - currentDrawState: DrawState.FREE, + currentDrawState: currentDrawType + ? DrawState.FREE + : DrawState.SELECTING, }; } case CHANGE_ZOOM: { diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index cb825283..f98e9dac 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -396,21 +396,27 @@ const ControlPanel = () => { dispatch(showAllDrawObjectStateIdByAI()); } }; + const isSelected = + currentDrawState === DrawState.SELECTING || + currentDrawState === DrawState.DRAGGING || + currentDrawState === DrawState.TRANSFORMING; + console.log("isSelected", isSelected, currentDrawState); return ( <> From cebd5bae74b379265ce837ec7ff6bbdccb418ba0 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 11 Nov 2022 14:20:44 +0700 Subject: [PATCH 12/67] fix: unable to press ESC to cancel drawing and unable to click out side current selected shape to deselect --- src/reduxes/annotation/action.ts | 6 ++++ src/reduxes/annotation/constants.ts | 1 + src/reduxes/annotation/reducer.ts | 7 +++++ src/reduxes/annotation/selector.ts | 2 ++ src/reduxes/annotation/type.ts | 5 ++++ .../AnnotationPage/ControlPanel/index.tsx | 1 - .../Editor/Layer/PolygonDrawLayer.tsx | 30 ++++++++++++++----- src/routes/AnnotationPage/Editor/index.tsx | 17 +++++------ 8 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/reduxes/annotation/action.ts b/src/reduxes/annotation/action.ts index 8fdaf6f1..c5b6f639 100644 --- a/src/reduxes/annotation/action.ts +++ b/src/reduxes/annotation/action.ts @@ -13,6 +13,7 @@ import { SET_DETECTED_AREA, SET_HIDDEN_DRAW_OBJECT, SET_IS_DRAGGING_VIEW_PORT, + SET_KEY_DOWN_IN_EDITOR, SET_LOCK_DRAW_OBJECT, SET_SELECT_SHAPE, SHOW_ALL_DRAW_OBJECTS_BY_AI, @@ -31,6 +32,7 @@ import { ResetCurrentStateDrawObjectPayload, SetHiddenDrawObjectPayload, SetIsDraggingViewportPayload, + SetKeyDownPayload, SetLockDetectedAreaPayload, SetLockDrawObecjtPayload, SetSelectShapePayload, @@ -132,3 +134,7 @@ export const showAllDrawObjectStateIdByAI = () => ({ export const hiddenAllDrawObjectStateIdByAI = () => ({ type: HIDDEN_ALL_DRAW_OBJECTS_BY_AI, }); +export const setKeyDownInEditor = (payload: SetKeyDownPayload) => ({ + type: SET_KEY_DOWN_IN_EDITOR, + payload, +}); diff --git a/src/reduxes/annotation/constants.ts b/src/reduxes/annotation/constants.ts index 6afcd317..96a2f0dd 100644 --- a/src/reduxes/annotation/constants.ts +++ b/src/reduxes/annotation/constants.ts @@ -20,3 +20,4 @@ export const RESET_ANNOTATION = "RESET_ANNOTATION"; export const RECOVER_PREVIOUS_DRAWSTATE = "RECOVER_PREVIOUS_DRAWSTATE"; export const SHOW_ALL_DRAW_OBJECTS_BY_AI = "SHOW_ALL_DRAW_OBJECTS_BY_AI"; export const HIDDEN_ALL_DRAW_OBJECTS_BY_AI = "HIDDEN_ALL_DRAW_OBJECTS_BY_AI"; +export const SET_KEY_DOWN_IN_EDITOR = "SET_KEY_DOWN_IN_EDITOR"; diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index 5a40797e..e457933d 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -14,6 +14,7 @@ import { SET_DETECTED_AREA, SET_HIDDEN_DRAW_OBJECT, SET_IS_DRAGGING_VIEW_PORT, + SET_KEY_DOWN_IN_EDITOR, SET_LOCK_DRAW_OBJECT, SET_SELECT_SHAPE, SHOW_ALL_DRAW_OBJECTS_BY_AI, @@ -38,6 +39,7 @@ import { ResetCurrentStateDrawObjectPayload, SetHiddenDrawObjectPayload, SetIsDraggingViewportPayload, + SetKeyDownPayload, SetLockDetectedAreaPayload, SetLockDrawObecjtPayload, SetSelectShapePayload, @@ -91,6 +93,7 @@ const inititalState: AnnotationReducer = { detectedArea: null, isDraggingViewport: false, drawObjectStateIdByAI: {}, + keyDownInEditor: null, }; const updateStateHistory = ( drawObjectById: Record, @@ -436,6 +439,10 @@ const annotationReducer = ( }); return { ...state, drawObjectStateIdByAI: newDrawObjectStateIdByAI }; } + case SET_KEY_DOWN_IN_EDITOR: { + const { keyDownInEditor } = payload as SetKeyDownPayload; + return { ...state, keyDownInEditor }; + } default: return state; } diff --git a/src/reduxes/annotation/selector.ts b/src/reduxes/annotation/selector.ts index 205eb36f..bfbd697e 100644 --- a/src/reduxes/annotation/selector.ts +++ b/src/reduxes/annotation/selector.ts @@ -92,3 +92,5 @@ export const selectorIsDraggingViewport = (state: RootState) => state.annotationReducer.isDraggingViewport; export const selectorDrawObjectStateIdByAI = (state: RootState) => state.annotationReducer.drawObjectStateIdByAI; +export const selectorKeyDownInEditor = (state: RootState) => + state.annotationReducer.keyDownInEditor; diff --git a/src/reduxes/annotation/type.ts b/src/reduxes/annotation/type.ts index 6362dc77..20870d13 100644 --- a/src/reduxes/annotation/type.ts +++ b/src/reduxes/annotation/type.ts @@ -36,6 +36,7 @@ export interface AnnotationReducer { statehHistory: StateHistory; detectedArea: DetectedAreaType | null; isDraggingViewport: boolean; + keyDownInEditor: string | null; } export interface DrawObjectByAIState { isShow: boolean; @@ -119,3 +120,7 @@ export interface AddDrawObjectStateIdByAIPayload { export interface ShowDrawObjectStateIdByAIPayload { drawObjectStateIds: string[]; } + +export interface SetKeyDownPayload { + keyDownInEditor: string | null; +} diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index f98e9dac..cb52efd9 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -400,7 +400,6 @@ const ControlPanel = () => { currentDrawState === DrawState.SELECTING || currentDrawState === DrawState.DRAGGING || currentDrawState === DrawState.TRANSFORMING; - console.log("isSelected", isSelected, currentDrawState); return ( <> diff --git a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx index 61fde17d..f994e057 100644 --- a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx @@ -1,5 +1,5 @@ import { KonvaEventObject } from "konva/lib/Node"; -import { useEffect, useRef, useState } from "react"; +import { KeyboardEvent, useEffect, useRef, useState } from "react"; import { Circle, Group, Layer, Line, Rect } from "react-konva"; import { @@ -18,6 +18,7 @@ import { import { selectorCurrentDrawState, selectorCurrentDrawType, + selectorKeyDownInEditor, } from "reduxes/annotation/selector"; import { DrawState, DrawType } from "reduxes/annotation/type"; import { convertStrokeColorToFillColor } from "routes/AnnotationPage/LabelAnnotation/ClassLabel"; @@ -41,6 +42,7 @@ const PolygonDrawLayer = () => { const [mouseOverPoint, setMouseOverPoint] = useState(false); const layer = useRef(null); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); + const keyDownInEditor = useSelector(selectorKeyDownInEditor); useEffect(() => { const flatPoints: number[] = points @@ -48,6 +50,11 @@ const PolygonDrawLayer = () => { .reduce((a, b) => a.concat([b.x, b.y]), [] as number[]); setFlattenedPoints(flatPoints); }, [points, isFinished, mousePosition]); + useEffect(() => { + if (keyDownInEditor === "Escape") { + resetDraw(); + } + }, [keyDownInEditor]); const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); @@ -80,14 +87,17 @@ const PolygonDrawLayer = () => { }) ); dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); - setPoints([]); - setIsFinished(false); - setMousePosition(null); - setMouseOverPoint(false); - setFlattenedPoints([]); + resetDraw(); } e.cancelBubble = true; }; + const resetDraw = () => { + setPoints([]); + setIsFinished(false); + setMousePosition(null); + setMouseOverPoint(false); + setFlattenedPoints([]); + }; const handleMouseOverStartPoint = (e: KonvaEventObject) => { if (isFinished || points.length < 3) return; e.target.scale({ x: 2, y: 2 }); @@ -195,13 +205,19 @@ const PolygonDrawLayer = () => { return ; } }; - + const keyDownHandler = (e: KeyboardEvent) => { + console.log("e", e); + if (e.key === "Escape") { + resetDraw(); + } + }; return ( {renderDummyRect()} diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index ea6569ec..80e9ecf3 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -29,6 +29,8 @@ import { recoverPreviousDrawState, redoDrawObject, setIsDraggingViewpor, + setKeyDownInEditor, + setSelectedShape, undoDrawObject, } from "reduxes/annotation/action"; import { @@ -159,6 +161,7 @@ const Editor = () => { const keyUpHandler = (): void => { setKeyDown(null); + dispatch(setKeyDownInEditor({ keyDownInEditor: null })); if (currentDrawState === DrawState.ZOOMDRAGGING) { dispatch(recoverPreviousDrawState()); } @@ -181,15 +184,7 @@ const Editor = () => { } } } else if (e.key === "Escape") { - if (selectedDrawObjectId) { - if (drawObjectById[selectedDrawObjectId].type === DrawType.POLYGON) { - const polygon = drawObjectById[selectedDrawObjectId] - .data as PolygonSpec; - if (!polygon.polygonState.isFinished) { - dispatch(deleteDrawObject({ drawObjectId: selectedDrawObjectId })); - } - } - } + dispatch(setKeyDownInEditor({ keyDownInEditor: e.key })); } }; @@ -279,6 +274,9 @@ const Editor = () => { } return currentAnnotationFile.image === null; }, [currentAnnotationFile]); + const handleMouseDown = () => { + dispatch(setSelectedShape({ selectedDrawObjectId: null })); + }; const renderContent = () => { if (isLoading) { return ( @@ -311,6 +309,7 @@ const Editor = () => { stageProps ? stageProps.height : MAX_HEIGHT_IMAGE_IN_EDITOR } onWheel={wheelHandler} + onMouseDown={handleMouseDown} draggable={isDraggingViewport} > From 86ea08448c38dccb2e331fdddfaff6c17cfc4cb1 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Sat, 12 Nov 2022 00:09:42 +0700 Subject: [PATCH 13/67] update: ui editor and able to move cursor outside of canvas and then the shape will take all the space until the edge --- src/components/Annotation/Editor/const.ts | 4 +- src/reduxes/annotation/action.ts | 15 + src/reduxes/annotation/constants.ts | 4 + src/reduxes/annotation/reducer.ts | 19 ++ src/reduxes/annotation/selector.ts | 4 + src/reduxes/annotation/type.ts | 5 + .../AnnotationPage/AnnotationEditor.tsx | 29 +- .../Editor/Layer/EllipseDrawLayer.tsx | 10 +- .../Editor/Layer/PolygonDrawLayer.tsx | 26 +- .../Editor/Layer/RectangleDrawLayer.tsx | 44 +-- src/routes/AnnotationPage/Editor/index.tsx | 267 ++++++++++-------- .../AnnotationPage/ImagePreview/index.tsx | 12 +- .../AnnotationPage/LabelAnnotation/index.tsx | 4 +- 13 files changed, 273 insertions(+), 170 deletions(-) diff --git a/src/components/Annotation/Editor/const.ts b/src/components/Annotation/Editor/const.ts index 1e41366d..4668000f 100644 --- a/src/components/Annotation/Editor/const.ts +++ b/src/components/Annotation/Editor/const.ts @@ -12,5 +12,7 @@ export const STROKE_WIDTH_LINE = LINE_STYLE.strokeWidth; export const CORNER_RADIUS = 5; -export const MAX_WIDTH_IMAGE_IN_EDITOR = 1200; +export const MAX_WIDTH_IMAGE_IN_EDITOR = 1000; export const MAX_HEIGHT_IMAGE_IN_EDITOR = 700; +export const MAX_WIDTH_EDITOR = 1200; +export const MAX_HEIGHT_EDITOR = 700; diff --git a/src/reduxes/annotation/action.ts b/src/reduxes/annotation/action.ts index c5b6f639..a16e99c2 100644 --- a/src/reduxes/annotation/action.ts +++ b/src/reduxes/annotation/action.ts @@ -15,6 +15,8 @@ import { SET_IS_DRAGGING_VIEW_PORT, SET_KEY_DOWN_IN_EDITOR, SET_LOCK_DRAW_OBJECT, + SET_MOUSE_DOWN_OUT_LAYER_POSITION, + SET_MOUSE_UP_OUT_LAYER_POSITION, SET_SELECT_SHAPE, SHOW_ALL_DRAW_OBJECTS_BY_AI, SHOW_DRAW_OBJECTS_BY_AI, @@ -35,6 +37,7 @@ import { SetKeyDownPayload, SetLockDetectedAreaPayload, SetLockDrawObecjtPayload, + SetMouseOutLayerPosition, SetSelectShapePayload, ShowDrawObjectStateIdByAIPayload, UpdateDrawObjectPayload, @@ -138,3 +141,15 @@ export const setKeyDownInEditor = (payload: SetKeyDownPayload) => ({ type: SET_KEY_DOWN_IN_EDITOR, payload, }); +export const setMouseUpOutLayerPosition = ( + payload: SetMouseOutLayerPosition +) => ({ + type: SET_MOUSE_UP_OUT_LAYER_POSITION, + payload, +}); +export const setMouseDownOutLayerPosition = ( + payload: SetMouseOutLayerPosition +) => ({ + type: SET_MOUSE_DOWN_OUT_LAYER_POSITION, + payload, +}); diff --git a/src/reduxes/annotation/constants.ts b/src/reduxes/annotation/constants.ts index 96a2f0dd..04b49f14 100644 --- a/src/reduxes/annotation/constants.ts +++ b/src/reduxes/annotation/constants.ts @@ -21,3 +21,7 @@ export const RECOVER_PREVIOUS_DRAWSTATE = "RECOVER_PREVIOUS_DRAWSTATE"; export const SHOW_ALL_DRAW_OBJECTS_BY_AI = "SHOW_ALL_DRAW_OBJECTS_BY_AI"; export const HIDDEN_ALL_DRAW_OBJECTS_BY_AI = "HIDDEN_ALL_DRAW_OBJECTS_BY_AI"; export const SET_KEY_DOWN_IN_EDITOR = "SET_KEY_DOWN_IN_EDITOR"; +export const SET_MOUSE_UP_OUT_LAYER_POSITION = + "SET_MOUSE_UP_OUT_LAYER_POSITION"; +export const SET_MOUSE_DOWN_OUT_LAYER_POSITION = + "SET_MOUSE_DOWN_OUT_LAYER_POSITION"; diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index e457933d..e1a35ef8 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -16,6 +16,8 @@ import { SET_IS_DRAGGING_VIEW_PORT, SET_KEY_DOWN_IN_EDITOR, SET_LOCK_DRAW_OBJECT, + SET_MOUSE_DOWN_OUT_LAYER_POSITION, + SET_MOUSE_UP_OUT_LAYER_POSITION, SET_SELECT_SHAPE, SHOW_ALL_DRAW_OBJECTS_BY_AI, SHOW_DRAW_OBJECTS_BY_AI, @@ -42,6 +44,7 @@ import { SetKeyDownPayload, SetLockDetectedAreaPayload, SetLockDrawObecjtPayload, + SetMouseOutLayerPosition, SetSelectShapePayload, ShowDrawObjectStateIdByAIPayload, StateHistory, @@ -94,6 +97,8 @@ const inititalState: AnnotationReducer = { isDraggingViewport: false, drawObjectStateIdByAI: {}, keyDownInEditor: null, + mouseUpOutLayerPosition: null, + mouseDownOutLayerPosition: null, }; const updateStateHistory = ( drawObjectById: Record, @@ -443,6 +448,20 @@ const annotationReducer = ( const { keyDownInEditor } = payload as SetKeyDownPayload; return { ...state, keyDownInEditor }; } + case SET_MOUSE_UP_OUT_LAYER_POSITION: { + const { position } = payload as SetMouseOutLayerPosition; + return { + ...state, + mouseUpOutLayerPosition: position, + }; + } + case SET_MOUSE_DOWN_OUT_LAYER_POSITION: { + const { position } = payload as SetMouseOutLayerPosition; + return { + ...state, + mouseDownOutLayerPosition: position, + }; + } default: return state; } diff --git a/src/reduxes/annotation/selector.ts b/src/reduxes/annotation/selector.ts index bfbd697e..123c4f27 100644 --- a/src/reduxes/annotation/selector.ts +++ b/src/reduxes/annotation/selector.ts @@ -94,3 +94,7 @@ export const selectorDrawObjectStateIdByAI = (state: RootState) => state.annotationReducer.drawObjectStateIdByAI; export const selectorKeyDownInEditor = (state: RootState) => state.annotationReducer.keyDownInEditor; +export const selectorMouseDownOutLayerPosition = (state: RootState) => + state.annotationReducer.mouseDownOutLayerPosition; +export const selectorMouseUpOutLayerPosition = (state: RootState) => + state.annotationReducer.mouseUpOutLayerPosition; diff --git a/src/reduxes/annotation/type.ts b/src/reduxes/annotation/type.ts index 20870d13..ad0dc983 100644 --- a/src/reduxes/annotation/type.ts +++ b/src/reduxes/annotation/type.ts @@ -37,6 +37,8 @@ export interface AnnotationReducer { detectedArea: DetectedAreaType | null; isDraggingViewport: boolean; keyDownInEditor: string | null; + mouseUpOutLayerPosition: Vector2d | null; + mouseDownOutLayerPosition: Vector2d | null; } export interface DrawObjectByAIState { isShow: boolean; @@ -124,3 +126,6 @@ export interface ShowDrawObjectStateIdByAIPayload { export interface SetKeyDownPayload { keyDownInEditor: string | null; } +export interface SetMouseOutLayerPosition { + position: Vector2d | null; +} diff --git a/src/routes/AnnotationPage/AnnotationEditor.tsx b/src/routes/AnnotationPage/AnnotationEditor.tsx index ed90f47d..5cd409b3 100644 --- a/src/routes/AnnotationPage/AnnotationEditor.tsx +++ b/src/routes/AnnotationPage/AnnotationEditor.tsx @@ -50,28 +50,27 @@ const AnnotationEditor = function () { ) { return ( - - - - - - - - - + + + + + + + {/* */} + - + + + ); } else { diff --git a/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx index a225d1ae..1071036e 100644 --- a/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx @@ -7,6 +7,7 @@ import { useEffect, useRef, useState } from "react"; import { Ellipse, Layer, Rect } from "react-konva"; import { useDispatch, useSelector } from "react-redux"; import { createDrawObject } from "reduxes/annotation/action"; +import { selectorMouseUpOutLayerPosition } from "reduxes/annotation/selector"; import { DrawType } from "reduxes/annotation/type"; import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; import { createEllipse } from "../Hook/useElipseEvent"; @@ -16,7 +17,12 @@ const EllipseDrawLayer = () => { const [startPoint, setStartPoint] = useState(null); const [endPoint, setEndPoint] = useState(null); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); - + const mouseUpOutLayerPosition = useSelector(selectorMouseUpOutLayerPosition); + useEffect(() => { + if (mouseUpOutLayerPosition) { + handleMouseUp(); + } + }, [mouseUpOutLayerPosition]); const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (!position || !startPoint) return; @@ -27,6 +33,8 @@ const EllipseDrawLayer = () => { const position = e.currentTarget.getRelativePointerPosition(); if (!position) return; setStartPoint({ ...position }); + e.evt.preventDefault(); + e.evt.stopPropagation(); }; const layer = useRef(null); useEffect(() => { diff --git a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx index f994e057..9f1fceb7 100644 --- a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx @@ -19,6 +19,7 @@ import { selectorCurrentDrawState, selectorCurrentDrawType, selectorKeyDownInEditor, + selectorMouseDownOutLayerPosition, } from "reduxes/annotation/selector"; import { DrawState, DrawType } from "reduxes/annotation/type"; import { convertStrokeColorToFillColor } from "routes/AnnotationPage/LabelAnnotation/ClassLabel"; @@ -43,7 +44,14 @@ const PolygonDrawLayer = () => { const layer = useRef(null); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); const keyDownInEditor = useSelector(selectorKeyDownInEditor); - + const mouseDownOutLayerPosition = useSelector( + selectorMouseDownOutLayerPosition + ); + useEffect(() => { + if (mouseDownOutLayerPosition) { + mousedownHandler(); + } + }, [mouseDownOutLayerPosition]); useEffect(() => { const flatPoints: number[] = points .concat(isFinished || !mousePosition ? [] : mousePosition) @@ -175,12 +183,16 @@ const PolygonDrawLayer = () => { }; const currentDrawState = useSelector(selectorCurrentDrawState); - const mousedownHandler = (e: KonvaEventObject) => { - const position = e.currentTarget.getRelativePointerPosition(); - if (!position) return; - setPoints([...points, position]); - if (currentDrawState === DrawState.FREE) { - dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); + const mousedownHandler = (e?: KonvaEventObject) => { + if (mousePosition) { + setPoints([...points, mousePosition]); + if (currentDrawState === DrawState.FREE) { + dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); + } + if (e) { + e.evt.preventDefault(); + e.evt.stopPropagation(); + } } }; useEffect(() => { diff --git a/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx index 029df7c9..7f1f9d71 100644 --- a/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx @@ -7,6 +7,10 @@ import { useEffect, useRef, useState } from "react"; import { Layer, Rect } from "react-konva"; import { useDispatch, useSelector } from "react-redux"; import { createDrawObject } from "reduxes/annotation/action"; +import { + selectorMouseDownOutLayerPosition, + selectorMouseUpOutLayerPosition, +} from "reduxes/annotation/selector"; import { DrawType } from "reduxes/annotation/type"; import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; import { createRectangle } from "../Hook/useRectangleEvent"; @@ -14,9 +18,14 @@ import { createRectangle } from "../Hook/useRectangleEvent"; const RectangleDrawLayer = () => { const dispatch = useDispatch(); const [startPoint, setStartPoint] = useState(null); - const [width, setWidth] = useState(0); - const [height, setHeight] = useState(0); + const [endPoint, setEndPoint] = useState(null); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); + const mouseUpOutLayerPosition = useSelector(selectorMouseUpOutLayerPosition); + useEffect(() => { + if (mouseUpOutLayerPosition) { + handleMouseUp(); + } + }, [mouseUpOutLayerPosition]); const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); @@ -43,24 +52,28 @@ const RectangleDrawLayer = () => { position.y = currentAnnotationFile?.height; } - const newWidth = position.x - startPoint.x; - const newHeight = position.y - startPoint.y; - setWidth(newWidth); - setHeight(newHeight); + setEndPoint({ ...position }); }; const mousedownHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (!position) return; setStartPoint({ ...position }); + e.evt.preventDefault(); + e.evt.stopPropagation(); }; const layer = useRef(null); useEffect(() => { layer.current?.moveToTop(); }, []); const handleMouseUp = () => { - if (startPoint) { - const rect = createRectangle(startPoint); + if (startPoint && endPoint) { + let rect; + if (startPoint.x < endPoint.x) { + rect = createRectangle(startPoint); + } else { + rect = createRectangle(endPoint); + } const spec = rect.data as RectangleSpec; dispatch( createDrawObject({ @@ -68,16 +81,15 @@ const RectangleDrawLayer = () => { type: DrawType.RECTANGLE, data: { ...spec, - width, - height, + width: Math.abs(startPoint.x - endPoint.x), + height: Math.abs(startPoint.y - endPoint.y), } as RectangleSpec, }, }) ); - setStartPoint(null); - setWidth(0); - setHeight(0); } + setStartPoint(null); + setEndPoint(null); }; const handleMouseOut = (e: KonvaEventObject) => { @@ -100,12 +112,12 @@ const RectangleDrawLayer = () => { onMouseOut={handleMouseOut} > {renderDummyRect()} - {startPoint && ( + {startPoint && endPoint && ( )} diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index 80e9ecf3..bace869f 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -13,7 +13,6 @@ import { MAX_HEIGHT_IMAGE_IN_EDITOR, MAX_WIDTH_IMAGE_IN_EDITOR, } from "components/Annotation/Editor/const"; -import { PolygonSpec } from "components/Annotation/Editor/type"; import Konva from "konva"; import { Vector2d } from "konva/lib/types"; import { @@ -30,11 +29,14 @@ import { redoDrawObject, setIsDraggingViewpor, setKeyDownInEditor, + setMouseDownOutLayerPosition, + setMouseUpOutLayerPosition, setSelectedShape, undoDrawObject, } from "reduxes/annotation/action"; import { selectorCurrentDrawState, + selectorCurrentDrawType, selectorDrawObjectById, selectorIsDraggingViewport, selectorSelectedDrawObjectId, @@ -62,6 +64,8 @@ const Editor = () => { const drawObjectById = useSelector(selectorDrawObjectById); const selectedDrawObjectId = useSelector(selectorSelectedDrawObjectId); const currentDrawState = useSelector(selectorCurrentDrawState); + const currentDrawType = useSelector(selectorCurrentDrawType); + const zoom = useSelector(selectorZoom); const isDraggingViewport = useSelector(selectorIsDraggingViewport); @@ -274,8 +278,30 @@ const Editor = () => { } return currentAnnotationFile.image === null; }, [currentAnnotationFile]); - const handleMouseDown = () => { + const handleMouseDown = (e: KonvaEventObject) => { dispatch(setSelectedShape({ selectedDrawObjectId: null })); + e.evt.preventDefault(); + e.evt.stopPropagation(); + }; + const selectHandleMouseDown = useMemo(() => { + if (currentDrawState !== DrawState.SELECTING && currentDrawType !== null) { + return undefined; + } + return handleMouseDown; + }, [currentDrawState, currentDrawType]); + const handleMouseDownOutLayer = (event: React.MouseEvent) => { + dispatch( + setMouseDownOutLayerPosition({ + position: { x: event.clientX, y: event.clientY }, + }) + ); + }; + const handleMouseUpOutLayer = (event: React.MouseEvent) => { + dispatch( + setMouseUpOutLayerPosition({ + position: { x: event.clientX, y: event.clientY }, + }) + ); }; const renderContent = () => { if (isLoading) { @@ -292,129 +318,136 @@ const Editor = () => { onKeyDown={keyDownHandler} onKeyUp={keyUpHandler} onMouseOver={mouseOverBoundDivHandler} - display="flex" - justifyContent="center" - alignItems="center" + onMouseDown={handleMouseDownOutLayer} + onMouseUp={handleMouseUpOutLayer} + id="testId" + width="100%" + height="100%" > - - {({ store }) => ( - - - - - - + + {({ store }) => ( + + + + + + + {Object.keys(drawObjects.rectangles).map((key) => { + return ( + + onMouseOverToolTipHandler(e, key) + } + onMouseOutHandler={onMouseOutToolTipHandler} + /> + ); + })} + {Object.keys(drawObjects.ellipses).map((key) => { + return ( + + onMouseOverToolTipHandler(e, key) + } + onMouseOutHandler={onMouseOutToolTipHandler} + /> + ); + })} + {Object.keys(polygons).map((key) => { + return ( + { + onMouseOverToolTipHandler(e, key); + }} + onMouseOutHandler={onMouseOutToolTipHandler} + /> + ); + })} + + + - {Object.keys(drawObjects.rectangles).map((key) => { - return ( - - onMouseOverToolTipHandler(e, key) - } - onMouseOutHandler={onMouseOutToolTipHandler} - /> - ); - })} - {Object.keys(drawObjects.ellipses).map((key) => { - return ( - - onMouseOverToolTipHandler(e, key) - } - onMouseOutHandler={onMouseOutToolTipHandler} - /> - ); - })} - {Object.keys(polygons).map((key) => { - return ( - { - onMouseOverToolTipHandler(e, key); - }} - onMouseOutHandler={onMouseOutToolTipHandler} - /> - ); - })} - - - - - - - - - - - - )} - - {/* */} + + + + + + + + + )} + + ); } }; return ( <> - - - {renderContent()} - + + {renderContent()} ); diff --git a/src/routes/AnnotationPage/ImagePreview/index.tsx b/src/routes/AnnotationPage/ImagePreview/index.tsx index c73d4b92..ad62033d 100644 --- a/src/routes/AnnotationPage/ImagePreview/index.tsx +++ b/src/routes/AnnotationPage/ImagePreview/index.tsx @@ -241,16 +241,6 @@ const ImagePreview = function () { ); }; - return ( - - {renderContent()} - - ); + return <>{renderContent()}; }; export default ImagePreview; diff --git a/src/routes/AnnotationPage/LabelAnnotation/index.tsx b/src/routes/AnnotationPage/LabelAnnotation/index.tsx index bc3b66d2..7c39d34b 100644 --- a/src/routes/AnnotationPage/LabelAnnotation/index.tsx +++ b/src/routes/AnnotationPage/LabelAnnotation/index.tsx @@ -83,13 +83,13 @@ const LabelAnnotation = function () { const renderList = () => { if (issFetchingImageData) { return ( - + ); } return ( - + {({ height, width }) => ( Date: Sun, 13 Nov 2022 14:36:59 +0700 Subject: [PATCH 14/67] feat: show confirm dialog before change preview image --- .../Editor/Layer/PolygonDrawLayer.tsx | 3 +- .../Editor/Layer/RectangleDrawLayer.tsx | 5 +- .../AnnotationPage/ImagePreview/index.tsx | 55 +++++++++++++++---- src/sagas/annotationEditorSaga.tsx | 8 +-- 4 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx index 9f1fceb7..96ddf05d 100644 --- a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx @@ -22,10 +22,9 @@ import { selectorMouseDownOutLayerPosition, } from "reduxes/annotation/selector"; import { DrawState, DrawType } from "reduxes/annotation/type"; +import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; import { convertStrokeColorToFillColor } from "routes/AnnotationPage/LabelAnnotation/ClassLabel"; import { createPolygon } from "../Hook/usePolygonEvent"; -import DummyRect from "./DummyRect"; -import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; const PolygonDrawLayer = () => { const dispatch = useDispatch(); diff --git a/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx index 7f1f9d71..3e8cb895 100644 --- a/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx @@ -7,10 +7,7 @@ import { useEffect, useRef, useState } from "react"; import { Layer, Rect } from "react-konva"; import { useDispatch, useSelector } from "react-redux"; import { createDrawObject } from "reduxes/annotation/action"; -import { - selectorMouseDownOutLayerPosition, - selectorMouseUpOutLayerPosition, -} from "reduxes/annotation/selector"; +import { selectorMouseUpOutLayerPosition } from "reduxes/annotation/selector"; import { DrawType } from "reduxes/annotation/type"; import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; import { createRectangle } from "../Hook/useRectangleEvent"; diff --git a/src/routes/AnnotationPage/ImagePreview/index.tsx b/src/routes/AnnotationPage/ImagePreview/index.tsx index ad62033d..30234f4f 100644 --- a/src/routes/AnnotationPage/ImagePreview/index.tsx +++ b/src/routes/AnnotationPage/ImagePreview/index.tsx @@ -1,11 +1,15 @@ import { Box, List, ListItem, Skeleton, Typography } from "@mui/material"; +import useConfirmDialog from "hooks/useConfirmDialog"; import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { resetCurrentStateDrawObject } from "reduxes/annotation/action"; -import { selectorDrawObjectById } from "reduxes/annotation/selector"; +import { + selectorAnnotationHistoryStep, + selectorDrawObjectById, +} from "reduxes/annotation/selector"; import { requestChangePreviewImage, - setAnnotationStateManager, + saveAnnotationStateManager, } from "reduxes/annotationmanager/action"; import { selectorCurrentPreviewImageName, @@ -30,6 +34,8 @@ const ImagePreview = function () { const drawObjectById = useSelector(selectorDrawObjectById); const idDrawObjectByImageName = useSelector(selectorIdDrawObjectByImageName); const currentAnnotationFiles = useSelector(selectorCurrentAnnotationFiles); + const annotationHistoryStep = useSelector(selectorAnnotationHistoryStep); + const { openConfirmDialog, closeConfirmDialog } = useConfirmDialog(); // useEffect(() => { // if (currentAnnotationFiles) { @@ -151,20 +157,45 @@ const ImagePreview = function () { if (imageName === currentPreviewImageName) { return; } - if (currentPreviewImageName) { + if (annotationHistoryStep === 0) { dispatch( - setAnnotationStateManager({ - imageName: currentPreviewImageName, - drawObjectById, + resetCurrentStateDrawObject({ + drawObjectById: idDrawObjectByImageName[imageName], }) ); + dispatch(requestChangePreviewImage({ imageName })); + return; } - dispatch( - resetCurrentStateDrawObject({ - drawObjectById: idDrawObjectByImageName[imageName], - }) - ); - dispatch(requestChangePreviewImage({ imageName })); + openConfirmDialog({ + content: ( + + + If you change the preview image, all annotation already processed + will be lost. Do you still want to CANCEL? + + + ), + negativeText: "Cancel", + positiveText: "Save", + onClickNegative: closeConfirmDialog, + onClickPositive: () => { + if (currentPreviewImageName) { + dispatch( + saveAnnotationStateManager({ + imageName: currentPreviewImageName, + drawObjectById, + }) + ); + } + dispatch( + resetCurrentStateDrawObject({ + drawObjectById: idDrawObjectByImageName[imageName], + }) + ); + dispatch(requestChangePreviewImage({ imageName })); + closeConfirmDialog(); + }, + }); }; const renderContent = () => { if (!currentAnnotationFiles) { diff --git a/src/sagas/annotationEditorSaga.tsx b/src/sagas/annotationEditorSaga.tsx index 42d06f1d..f45a27ba 100644 --- a/src/sagas/annotationEditorSaga.tsx +++ b/src/sagas/annotationEditorSaga.tsx @@ -32,6 +32,7 @@ import { import { selectorDrawObjectStateIdByAI } from "reduxes/annotation/selector"; import { DrawObject, + DrawObjectByAIState, ResetCurrentStateDrawObjectPayload, } from "reduxes/annotation/type"; import { @@ -289,12 +290,11 @@ function* handleSaveAnnotationStateManager(action: any): any { const filename = splitKey.join("") + ".json"; s3key_jsonlabel = annotationCurrentProject.s3_label + "/" + filename; } - const drawObjectStateIdByAI: string[] = yield select( - selectorDrawObjectStateIdByAI - ); + const drawObjectStateIdByAI: Record = + yield select(selectorDrawObjectStateIdByAI); const drawObjectByIdExcluceAIDetect: Record = {}; Object.entries(drawObjectById).forEach(([key, value]) => { - if (!drawObjectStateIdByAI.includes(key)) { + if (!drawObjectStateIdByAI[key]) { drawObjectByIdExcluceAIDetect[key] = value; } }); From 8ee4bffd512d689b2a94b4f6b193aace8ee0b109 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Mon, 14 Nov 2022 00:06:20 +0700 Subject: [PATCH 15/67] feat: add an indicator help user know if there was existed segmentation detected from AI of that image or not~ --- src/reduxes/annotation/reducer.ts | 9 +- .../Layer/DetectedRectangleDrawLayer.tsx | 55 ++++++++++-- .../ImagePreview/ImagePreviewBadge.tsx | 33 +++++++ .../AnnotationPage/ImagePreview/index.tsx | 86 ++++++++++++------- 4 files changed, 142 insertions(+), 41 deletions(-) create mode 100644 src/routes/AnnotationPage/ImagePreview/ImagePreviewBadge.tsx diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index e1a35ef8..a18ab052 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -215,11 +215,16 @@ const annotationReducer = ( } case DELETE_DRAW_OBJECT: { const { drawObjectId } = payload as DeleteDrawObjectPayload; + const newStateHistory = updateStateHistory( + state.drawObjectById, + state.statehHistory + ); delete state.drawObjectById[drawObjectId]; return { ...state, selectedDrawObjectId: null, drawObjectById: { ...state.drawObjectById }, + statehHistory: newStateHistory, }; } case RESET_CURRENT_STATE_DRAW_OBJECT: { @@ -279,8 +284,8 @@ const annotationReducer = ( Object.entries(undoDrawObjectById).map(([key, value]) => { undoDrawObjectById[key].data = { ...undoDrawObjectById[key].data, - label: state.drawObjectById[key].data.label, - cssStyle: state.drawObjectById[key].data.cssStyle, + label: value.data.label, + cssStyle: value.data.cssStyle, }; }); return { diff --git a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx index f06b35df..366a5d20 100644 --- a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx @@ -3,18 +3,27 @@ import { useEffect, useRef, useState } from "react"; import { Layer, Rect } from "react-konva"; import Konva from "konva"; -import { useDispatch } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { setDetectedArea } from "reduxes/annotation/action"; +import { selectorMouseUpOutLayerPosition } from "reduxes/annotation/selector"; import { DetectedAreaType } from "reduxes/annotation/type"; import { convertStrokeColorToFillColor } from "routes/AnnotationPage/LabelAnnotation/ClassLabel"; -import DummyRect from "./DummyRect"; +import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; +import { Vector2d } from "konva/lib/types"; const DetectedRectangleDrawLayer = () => { const dispatch = useDispatch(); const refDetectedArea = useRef(null); const [localDetectedArea, setLocalDetectedArea] = useState(null); + const mouseUpOutLayerPosition = useSelector(selectorMouseUpOutLayerPosition); + const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); + useEffect(() => { + if (mouseUpOutLayerPosition) { + mouseupHandler(); + } + }, [mouseUpOutLayerPosition]); const mousedownHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (position) { @@ -26,18 +35,45 @@ const DetectedRectangleDrawLayer = () => { }); } }; + const handleMouseOut = (e: KonvaEventObject) => { + const position = e.currentTarget.getRelativePointerPosition(); + updateMousePosition(position); + }; const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); - if (position) { - if (localDetectedArea) + updateMousePosition(position); + }; + const updateMousePosition = (position: Vector2d) => { + if (position && currentAnnotationFile) { + if (localDetectedArea) { + if (position.x < 0) { + position.x = 0; + } + console.log("currentAnnotationFile.width", currentAnnotationFile.width); + if ( + currentAnnotationFile.width && + position.x > currentAnnotationFile.width + ) { + position.x = currentAnnotationFile.width; + } + if (position.y < 0) { + position.y = 0; + } + if ( + currentAnnotationFile.height && + position.y > currentAnnotationFile.height + ) { + position.y = currentAnnotationFile.height; + } setLocalDetectedArea({ ...localDetectedArea, width: position.x - localDetectedArea.x, height: position.y - localDetectedArea.y, }); + } } }; - const mouseupHandler = (e: KonvaEventObject) => { + const mouseupHandler = () => { if (localDetectedArea && refDetectedArea.current) { dispatch( setDetectedArea({ @@ -52,14 +88,21 @@ const DetectedRectangleDrawLayer = () => { useEffect(() => { layer.current?.moveToTop(); }, []); + const renderDummyRect = () => { + if (currentAnnotationFile) { + const { width, height } = currentAnnotationFile; + return ; + } + }; return ( - + {renderDummyRect()} {localDetectedArea && ( { + return AI; + }; + if (currentAnnotationFiles) { + const annotationFile = currentAnnotationFiles.items.find( + (t) => t.filename === filename + ); + if (annotationFile) { + if (annotationFile.s3_key_segm) { + return ( + + {children} + + ); + } + } + } + return <> {children}; +}; +export default ImagePreviewBadge; diff --git a/src/routes/AnnotationPage/ImagePreview/index.tsx b/src/routes/AnnotationPage/ImagePreview/index.tsx index 30234f4f..0ae7a551 100644 --- a/src/routes/AnnotationPage/ImagePreview/index.tsx +++ b/src/routes/AnnotationPage/ImagePreview/index.tsx @@ -1,4 +1,11 @@ -import { Box, List, ListItem, Skeleton, Typography } from "@mui/material"; +import { + Badge, + Box, + List, + ListItem, + Skeleton, + Typography, +} from "@mui/material"; import useConfirmDialog from "hooks/useConfirmDialog"; import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -16,6 +23,7 @@ import { selectorIdDrawObjectByImageName, } from "reduxes/annotationmanager/selecetor"; import { selectorCurrentAnnotationFiles } from "reduxes/annotationProject/selector"; +import ImagePreviewBadge from "./ImagePreviewBadge"; const createFile = async (imageName: string, url: string) => { let response = await fetch(url); @@ -175,9 +183,19 @@ const ImagePreview = function () { ), - negativeText: "Cancel", + negativeText: "Skip", positiveText: "Save", - onClickNegative: closeConfirmDialog, + onClickNegative: () => { + if (currentPreviewImageName) { + dispatch( + resetCurrentStateDrawObject({ + drawObjectById: idDrawObjectByImageName[imageName], + }) + ); + dispatch(requestChangePreviewImage({ imageName })); + closeConfirmDialog(); + } + }, onClickPositive: () => { if (currentPreviewImageName) { dispatch( @@ -231,40 +249,42 @@ const ImagePreview = function () { {currentAnnotationFiles.items.map((item) => { return ( - { - handleSelectPreview(item.filename); - }} - > + { + handleSelectPreview(item.filename); + }} > - - {item.filename} - + + {item.filename} + + - + ); })} From 20cadc6dbe8fc9d9f39a098f3268c36cce0a26ff Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Mon, 14 Nov 2022 09:17:59 +0700 Subject: [PATCH 16/67] feat: add short key for every tool in toolbar --- .../AnnotationPage/ControlPanel/index.tsx | 29 ++++++++++++----- .../Layer/DetectedRectangleDrawLayer.tsx | 1 - src/routes/AnnotationPage/Editor/index.tsx | 31 ++++++++++++++++++- src/routes/AnnotationPage/constants.ts | 6 ++++ 4 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 src/routes/AnnotationPage/constants.ts diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index cb52efd9..281b8938 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -70,6 +70,12 @@ import { selectorIsSavingAnnotation, selectorLabelClassPropertiesByLabelClass, } from "reduxes/annotationmanager/selecetor"; +import { + DRAW_ELLIPSE_SHORT_KEY, + DRAW_POLYGON_SHORT_KEY, + DRAW_RECTANGLE_SHORT_KEY, + SELECT_SHORT_KEY, +} from "../constants"; import { hashCode, intToRGB } from "../LabelAnnotation"; import { convertStrokeColorToFillColor } from "../LabelAnnotation/ClassLabel"; const ControlPanel = () => { @@ -418,7 +424,9 @@ const ControlPanel = () => { selected={isSelected} aria-label="selecting" > - + + + { value={DrawType.RECTANGLE} aria-label="Rectangle" > - + + + - + + + - + + + - + + + - { if (position.x < 0) { position.x = 0; } - console.log("currentAnnotationFile.width", currentAnnotationFile.width); if ( currentAnnotationFile.width && position.x > currentAnnotationFile.width diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index bace869f..23258da8 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -23,6 +23,7 @@ import { } from "react-redux"; import { changeCurrentDrawState, + changeCurrentDrawType, changeZoom, deleteDrawObject, recoverPreviousDrawState, @@ -46,6 +47,14 @@ import { DrawObject, DrawState, DrawType } from "reduxes/annotation/type"; import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; import BaseImage from "./BaseImage"; import DrawLayer from "./Layer/DrawLayer"; +import { + DRAW_ELLIPSE_SHORT_KEY, + DRAW_LINE_SHORT_KEY, + DRAW_POLYGON_SHORT_KEY, + DRAW_RECTANGLE_SHORT_KEY, + DRAW_SEGMENTATION_SHORT_KEY, + SELECT_SHORT_KEY, +} from "../constants"; const TOOLTIP_WIDTH = 200; const TOOLTIP_HEIGHT = 40; @@ -189,6 +198,26 @@ const Editor = () => { } } else if (e.key === "Escape") { dispatch(setKeyDownInEditor({ keyDownInEditor: e.key })); + } else if (e.key === SELECT_SHORT_KEY) { + dispatch(changeCurrentDrawState({ drawState: DrawState.SELECTING })); + dispatch(changeCurrentDrawType({ currentDrawType: null })); + } else if (e.key === DRAW_RECTANGLE_SHORT_KEY) { + dispatch(changeCurrentDrawType({ currentDrawType: DrawType.RECTANGLE })); + dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); + } else if (e.key === DRAW_POLYGON_SHORT_KEY) { + dispatch(changeCurrentDrawType({ currentDrawType: DrawType.POLYGON })); + dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); + } else if (e.key === DRAW_ELLIPSE_SHORT_KEY) { + dispatch(changeCurrentDrawType({ currentDrawType: DrawType.ELLIPSE })); + dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); + } else if (e.key === DRAW_LINE_SHORT_KEY) { + dispatch(changeCurrentDrawType({ currentDrawType: DrawType.LINE_STRIP })); + dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); + } else if (e.key === DRAW_SEGMENTATION_SHORT_KEY) { + dispatch( + changeCurrentDrawType({ currentDrawType: DrawType.DETECTED_RECTANGLE }) + ); + dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); } }; @@ -426,7 +455,7 @@ const Editor = () => { fontSize={34} text="" fill="black" - > + /> diff --git a/src/routes/AnnotationPage/constants.ts b/src/routes/AnnotationPage/constants.ts new file mode 100644 index 00000000..35a082bc --- /dev/null +++ b/src/routes/AnnotationPage/constants.ts @@ -0,0 +1,6 @@ +export const SELECT_SHORT_KEY = "v"; +export const DRAW_RECTANGLE_SHORT_KEY = "r"; +export const DRAW_POLYGON_SHORT_KEY = "t"; +export const DRAW_ELLIPSE_SHORT_KEY = "e"; +export const DRAW_LINE_SHORT_KEY = "z"; +export const DRAW_SEGMENTATION_SHORT_KEY = "f"; From 1d46aa6d5bec6021ea7ab07b5bb92186a4b45805 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Mon, 14 Nov 2022 13:59:09 +0700 Subject: [PATCH 17/67] update: refactor --- .../Annotation/Editor/Shape/EllipseShape.tsx | 57 ++- .../Annotation/Editor/Shape/Polygon.tsx | 264 ++++++------ .../Annotation/Editor/Shape/Rectangle.tsx | 49 ++- src/components/Annotation/Editor/type.ts | 10 +- .../Annotation/Editor/utils/canvas.ts | 14 +- .../Annotation/Editor/utils/event.ts | 6 +- .../Annotation/Formart/daita/index.ts | 106 ++--- .../Annotation/Formart/daita/type.ts | 4 +- .../Annotation/Formart/labelbox/index.ts | 35 +- .../Annotation/Formart/labelbox/type.ts | 5 +- .../Annotation/Formart/labelme/index.ts | 81 ++-- .../Annotation/Formart/labelme/type.ts | 9 +- .../Annotation/Formart/scaleai/index.ts | 46 +- .../Annotation/Formart/scaleai/type.ts | 6 +- src/components/Annotation/Formart/type.ts | 1 - src/reduxes/annotation/reducer.ts | 46 +- src/reduxes/annotation/selector.ts | 33 +- src/reduxes/annotationProject/reducer.ts | 18 +- src/reduxes/annotationmanager/reducer.ts | 4 +- .../AnnotationPage/AnnotationEditor.tsx | 11 +- .../AnnotationPage/ControlPanel/index.tsx | 396 +++++++++--------- .../AnnotationPage/Editor/BaseImage/index.tsx | 4 +- .../Editor/Hook/useElipseEvent.ts | 80 ---- .../Editor/Hook/usePolygonEvent.ts | 90 ---- .../Editor/Hook/useRectangleEvent.ts | 76 ---- .../Layer/DetectedRectangleDrawLayer.tsx | 60 ++- .../AnnotationPage/Editor/Layer/DrawLayer.tsx | 13 +- .../AnnotationPage/Editor/Layer/DummyRect.tsx | 13 - .../Editor/Layer/EllipseDrawLayer.tsx | 18 +- .../Editor/Layer/PolygonDrawLayer.tsx | 67 ++- .../Editor/Layer/RectangleDrawLayer.tsx | 46 +- src/routes/AnnotationPage/Editor/index.tsx | 323 +++++++------- .../ImagePreview/ImagePreviewBadge.tsx | 11 +- .../AnnotationPage/ImagePreview/index.tsx | 118 ++---- .../LabelAnnotation/ClassItem.tsx | 74 ++-- .../LabelAnnotation/ClassLabel/index.tsx | 138 +++--- .../LabelAnnotation/ClassLabel/type.ts | 2 +- .../ClassManageModal/FieldArrayAttribute.tsx | 64 ++- .../ClassManageModal/index.tsx | 8 +- .../ClassManageModal/useClassManageEditor.tsx | 40 +- .../ClassManageModal/useListClassView.tsx | 40 +- .../AnnotationPage/LabelAnnotation/index.tsx | 37 +- src/routes/AnnotationProject/index.tsx | 18 +- .../SegmentationProgressModal/index.tsx | 6 +- .../UploadAnnotationImage/index.tsx | 6 +- .../UploadAnnotationImage/type.ts | 1 - src/routes/AnnotationProjectDetail/index.tsx | 32 +- src/sagas/annotationEditorSaga.tsx | 35 +- src/sagas/annotationProjectSaga.tsx | 8 +- 49 files changed, 1165 insertions(+), 1464 deletions(-) delete mode 100644 src/components/Annotation/Formart/type.ts delete mode 100644 src/routes/AnnotationPage/Editor/Hook/useElipseEvent.ts delete mode 100644 src/routes/AnnotationPage/Editor/Hook/usePolygonEvent.ts delete mode 100644 src/routes/AnnotationPage/Editor/Hook/useRectangleEvent.ts delete mode 100644 src/routes/AnnotationPage/Editor/Layer/DummyRect.tsx delete mode 100644 src/routes/AnnotationProjectDetail/UploadAnnotationImage/type.ts diff --git a/src/components/Annotation/Editor/Shape/EllipseShape.tsx b/src/components/Annotation/Editor/Shape/EllipseShape.tsx index ba5c8b3e..b2838c4f 100644 --- a/src/components/Annotation/Editor/Shape/EllipseShape.tsx +++ b/src/components/Annotation/Editor/Shape/EllipseShape.tsx @@ -12,12 +12,30 @@ import { selectorDrawObjectState, selectorSelectedEllipse, } from "reduxes/annotation/selector"; -import { DrawState } from "reduxes/annotation/type"; -import { CIRCLE_STYLE, CORNER_RADIUS } from "../const"; +import { DrawObject, DrawState, DrawType } from "reduxes/annotation/type"; +import { CIRCLE_STYLE, CORNER_RADIUS, LINE_STYLE } from "../const"; import { EllipseCompProps, EllipseProps, EllipseSpec } from "../type"; import useCommonShapeEvent from "../useCommonShapeEvent"; -const EllipseComp = function ({ +export const createEllipse = (position: { + x: number; + y: number; +}): DrawObject => { + const id = `ELLIPSE-${Math.floor(Math.random() * 100000)}`; + const shape: EllipseSpec = { + x: position.x, + y: position.y, + radiusX: 1, + radiusY: 1, + rotation: 0, + id, + label: { label: id }, + cssStyle: { ...LINE_STYLE }, + }; + return { type: DrawType.ELLIPSE, data: shape }; +}; + +function EllipseComp({ spec, onMouseOverHandler, onMouseOutHandler, @@ -34,11 +52,12 @@ const EllipseComp = function ({ spec.cssStyle.strokeWidth ); - const isSelected = useMemo(() => { - return currentShape != null && spec.id === currentShape.id; - }, [currentShape?.id]); + const isSelected = useMemo( + () => currentShape != null && spec.id === currentShape.id, + [currentShape?.id] + ); useEffect(() => { - if (isSelected == true) { + if (isSelected === true) { shapeRef.current?.moveToTop(); } }, [isSelected]); @@ -81,17 +100,15 @@ const EllipseComp = function ({ const handleTransformEnd = (e: KonvaEventObject) => { const node = shapeRef.current; if (!node) return; - - let radiusX = (node.width() * node.scaleX()) / 2.0; - let radiusY = (node.height() * node.scaleY()) / 2.0; - + const radiusX = (node.width() * node.scaleX()) / 2.0; + const radiusY = (node.height() * node.scaleY()) / 2.0; onChange({ ...spec, x: node.x(), y: node.y(), rotation: node.attrs.rotation, - radiusX: radiusX, - radiusY: radiusY, + radiusX, + radiusY, }); node.scale({ x: 1, y: 1 }); commonShapeEvent.handleTransformEnd(e); @@ -122,7 +139,7 @@ const EllipseComp = function ({ }; return ( - + <> )} - + ); -}; -const EllipseShape = function ({ +} +function EllipseShape({ id, onMouseOverHandler, onMouseOutHandler, @@ -171,6 +188,6 @@ const EllipseShape = function ({ /> ); } - return <>; -}; + return null; +} export default EllipseShape; diff --git a/src/components/Annotation/Editor/Shape/Polygon.tsx b/src/components/Annotation/Editor/Shape/Polygon.tsx index 6aa1e973..3fbd7de0 100644 --- a/src/components/Annotation/Editor/Shape/Polygon.tsx +++ b/src/components/Annotation/Editor/Shape/Polygon.tsx @@ -1,7 +1,7 @@ import Konva from "konva"; import { KonvaEventObject } from "konva/lib/Node"; import { Stage } from "konva/lib/Stage"; -import { IRect, Vector2d } from "konva/lib/types"; +import { Vector2d } from "konva/lib/types"; import React, { useEffect, useMemo, useState } from "react"; import { Circle, Group, Line } from "react-konva"; import { useDispatch, useSelector } from "react-redux"; @@ -18,17 +18,37 @@ import { selectorSelectedPolygonOrLineStrip, selectorZoom, } from "reduxes/annotation/selector"; -import { DrawState } from "reduxes/annotation/type"; -import { CIRCLE_STYLE, CORNER_RADIUS, STROKE_WIDTH_LINE } from "../const"; +import { DrawObject, DrawState, DrawType } from "reduxes/annotation/type"; +import { + CIRCLE_STYLE, + CORNER_RADIUS, + LINE_STYLE, + STROKE_WIDTH_LINE, +} from "../const"; import { CssStyle, PolygonCompProps, PolygonProps, PolygonSpec } from "../type"; import useCommonShapeEvent from "../useCommonShapeEvent"; -import { dragBoundFunc, minMax } from "../utils"; +import { dragBoundFunc } from "../utils"; -const PolygonComp = ({ +export const createPolygon = ( + position: Vector2d, + isLineStrip?: boolean +): DrawObject => { + const id = `POLYGON-${Math.floor(Math.random() * 100000)}`; + const polygon: PolygonSpec = { + id, + points: [position], + polygonState: { isFinished: false, isLineStrip }, + label: { label: id }, + cssStyle: { ...LINE_STYLE }, + }; + return { type: DrawType.POLYGON, data: polygon }; +}; + +function PolygonComp({ spec, onMouseOverHandler, onMouseOutHandler, -}: PolygonCompProps) => { +}: PolygonCompProps) { const { points, polygonState: { isFinished, isLineStrip }, @@ -38,21 +58,27 @@ const PolygonComp = ({ const currentpolygon = useSelector(selectorSelectedPolygonOrLineStrip); // const detectedArea = useSelector(selectorDetectedArea); const drawObjectState = useSelector(selectorDrawObjectState(spec.id)); - + const [lineStyle, setLineStyle] = useState({ + ...spec.cssStyle, + strokeWidth: STROKE_WIDTH_LINE, + }); const zoom = useSelector(selectorZoom); const groupRef = React.useRef(null); const currentDrawState = useSelector(selectorCurrentDrawState); const drawObjectStateIdByAI = useSelector(selectorDrawObjectStateIdByAI); - const isSelected = useMemo(() => { - return currentpolygon != null && spec.id === currentpolygon.id; - }, [currentpolygon?.id]); - const position = useMemo(() => { - return currentpolygon != null && spec.id === currentpolygon.id - ? currentpolygon.polygonState.mousePosition - : null; - }, [currentpolygon]); + const isSelected = useMemo( + () => currentpolygon != null && spec.id === currentpolygon.id, + [currentpolygon?.id] + ); + const position = useMemo( + () => + currentpolygon != null && spec.id === currentpolygon.id + ? currentpolygon.polygonState.mousePosition + : null, + [currentpolygon] + ); useEffect(() => { - if (isSelected == true) { + if (isSelected === true) { groupRef.current?.moveToTop(); } }, [isSelected]); @@ -77,18 +103,8 @@ const PolygonComp = ({ const handleGroupMouseOut = (e: KonvaEventObject) => { onMouseOutHandler(e); }; - const [minMaxX, setMinMaxX] = useState([0, 0]); - const [minMaxY, setMinMaxY] = useState([0, 0]); - const [groupPosition, setGroupPosition] = useState({ x: 0, y: 0 }); - const handleGroupDragStart = (e: KonvaEventObject) => { if (groupRef.current) { - const rect = groupRef.current.getClientRect(); - setGroupPosition({ x: rect.x, y: rect.y }); - const arrX = points.map((p) => p.x); - const arrY = points.map((p) => p.y); - setMinMaxX(minMax(arrX)); - setMinMaxY(minMax(arrY)); dispatch( setSelectedShape({ selectedDrawObjectId: spec.id, @@ -97,95 +113,89 @@ const PolygonComp = ({ commonShapeEvent.handleDragStart(e); } }; - const [previousPosition, setPreviousPosition] = useState({ - x: 0, - y: 0, - }); + // const [previousPosition, setPreviousPosition] = useState({ + // x: 0, + // y: 0, + // }); // { // x: 0, // y: 0, // width: 0, // height: 0, // } - const [currentBoudingBox, setCurrentBoudingBox] = useState( - null - ); - useEffect(() => { - // console.log("previousPosition", previousPosition); - }, [previousPosition]); - const groupDragBound = (pos: { x: number; y: number }) => { - let { x, y } = pos; - // console.log("pos", pos); - const sw = stage ? stage.width() : 0; - const sh = stage ? stage.height() : 0; - if (groupRef && groupRef.current) { - const box = groupRef.current.getClientRect(); - const relativePosition = groupRef.current.getRelativePointerPosition(); - console.log("box", box.y); - const minMaxX = [0, box.width]; - const minMaxY = [0, box.height]; - // &&(currentBoudingBox === null || Math.round(box.y) !== Math.round(currentBoudingBox?.y)) - if (box.y <= 0) { - x = previousPosition.x; - y = previousPosition.y; - console.log("assign x,y", x, y); - // if ( - // currentBoudingBox === null || - // Math.round(box.y) <= Math.round(currentBoudingBox?.y) - // ) { - // x = previousPosition.x; - // y = previousPosition.y; - // const tmp = { - // ...box, - // x: Math.round(box.x), - // y: Math.round(box.y), - // }; - // setCurrentBoudingBox(tmp); - // console.log( - // "setCurrentBoudingBox", - // tmp, - // "box", - // box.y, - // "previos", - // previousPosition - // ); - // } else { - // console.log("ignore Dup position"); - // } - } else { - // if ( - // currentBoudingBox && - // Math.round(box.y) !== Math.round(currentBoudingBox?.y) - // ) { - // setPreviousPosition({ x, y }); - // } - // const tmp = { - // ...box, - // x: Math.round(box.x), - // y: Math.round(box.y), - // }; - // setCurrentBoudingBox(tmp); - if (box.y > 10) { - setPreviousPosition({ x, y }); - } - } - if (minMaxX[0] + x < 0) x = -1 * minMaxX[0]; - if (minMaxY[1] + y > sh) y = sh - minMaxY[1]; - if (minMaxX[1] + x > sw) x = sw - minMaxX[1]; - return { x, y }; - // const box = groupRef.current.getClientRect(); - // const maxX = Math.max(...spec.points.map((t) => t.x)); - // const maxY = Math.max(...spec.points.map((t) => t.y)); - // console.log(minMaxX, minMaxY); - // if (minMaxY[0] + y < 0) y = -1 * minMaxY[0]; - // if (minMaxX[0] + x < 0) x = -1 * minMaxX[0]; - // if (minMaxY[1] + y > sh) y = sh - minMaxY[1]; - // if (minMaxX[1] + x > sw) x = sw - minMaxX[1]; - // return { x, y }; - } - return { x: 0, y: 0 }; - }; + // const groupDragBound = (pos: { x: number; y: number }) => { + // let { x, y } = pos; + // // console.log("pos", pos); + // const sw = stage ? stage.width() : 0; + // const sh = stage ? stage.height() : 0; + // if (groupRef && groupRef.current) { + // const box = groupRef.current.getClientRect(); + // const relativePosition = groupRef.current.getRelativePointerPosition(); + // console.log("box", box.y); + // const minMaxX = [0, box.width]; + // const minMaxY = [0, box.height]; + // // &&(currentBoudingBox === null || Math.round(box.y) !== Math.round(currentBoudingBox?.y)) + // if (box.y <= 0) { + // x = previousPosition.x; + // y = previousPosition.y; + // console.log("assign x,y", x, y); + // // if ( + // // currentBoudingBox === null || + // // Math.round(box.y) <= Math.round(currentBoudingBox?.y) + // // ) { + // // x = previousPosition.x; + // // y = previousPosition.y; + // // const tmp = { + // // ...box, + // // x: Math.round(box.x), + // // y: Math.round(box.y), + // // }; + // // setCurrentBoudingBox(tmp); + // // console.log( + // // "setCurrentBoudingBox", + // // tmp, + // // "box", + // // box.y, + // // "previos", + // // previousPosition + // // ); + // // } else { + // // console.log("ignore Dup position"); + // // } + // } else { + // // if ( + // // currentBoudingBox && + // // Math.round(box.y) !== Math.round(currentBoudingBox?.y) + // // ) { + // // setPreviousPosition({ x, y }); + // // } + // // const tmp = { + // // ...box, + // // x: Math.round(box.x), + // // y: Math.round(box.y), + // // }; + // // setCurrentBoudingBox(tmp); + // if (box.y > 10) { + // setPreviousPosition({ x, y }); + // } + // } + // if (minMaxX[0] + x < 0) x = -1 * minMaxX[0]; + // if (minMaxY[1] + y > sh) y = sh - minMaxY[1]; + // if (minMaxX[1] + x > sw) x = sw - minMaxX[1]; + // return { x, y }; + // // const box = groupRef.current.getClientRect(); + // // const maxX = Math.max(...spec.points.map((t) => t.x)); + // // const maxY = Math.max(...spec.points.map((t) => t.y)); + // // console.log(minMaxX, minMaxY); + // // if (minMaxY[0] + y < 0) y = -1 * minMaxY[0]; + // // if (minMaxX[0] + x < 0) x = -1 * minMaxX[0]; + // // if (minMaxY[1] + y > sh) y = sh - minMaxY[1]; + // // if (minMaxX[1] + x > sw) x = sw - minMaxX[1]; + // // return { x, y }; + // } + // return { x: 0, y: 0 }; + // }; const handleMouseOverStartPoint = (e: KonvaEventObject) => { if (isFinished || points.length < 3) return; e.target.scale({ x: 2, y: 2 }); @@ -233,11 +243,8 @@ const PolygonComp = ({ e.cancelBubble = true; }; const mousedownHandler = (e: KonvaEventObject) => { - const position = e.target.getRelativePointerPosition(); - if (!position) return; if (spec.polygonState.isFinished) { commonShapeEvent.handleSelect(e); - return; } }; const handleGroupDragEnd = (e: KonvaEventObject) => { @@ -246,10 +253,6 @@ const PolygonComp = ({ const y: number = e.target.y(); spec.points.map((point) => result.push({ x: point.x + x, y: point.y + y })); e.target.position({ x: 0, y: 0 }); - if (groupRef.current) { - const rect = groupRef.current.getClientRect(); - setGroupPosition({ x: rect.x, y: rect.y }); - } dispatch( updateDrawObject({ data: { @@ -262,7 +265,7 @@ const PolygonComp = ({ }; const converFlattenPointToPoint = (flatPoints: number[]) => { const newPoints: Vector2d[] = []; - for (let index = 0; index < flatPoints.length; index = index + 2) { + for (let index = 0; index < flatPoints.length; index += 2) { newPoints.push({ x: flatPoints[index], y: flatPoints[index + 1] }); } return newPoints; @@ -279,7 +282,7 @@ const PolygonComp = ({ commonShapeEvent.handleTransformEnd(e); }; const handlePointDragMove = (e: KonvaEventObject) => { - const { points } = spec; + const { points: specPoints } = spec; if (groupRef && groupRef.current) { const index = e.target.index - 1; const pos = { @@ -287,9 +290,9 @@ const PolygonComp = ({ y: groupRef.current.getRelativePointerPosition().y, }; const newPoints = [ - ...points.slice(0, index), + ...specPoints.slice(0, index), pos, - ...points.slice(index + 1), + ...specPoints.slice(index + 1), ]; setFlattenedPoints( newPoints.reduce((a, b) => a.concat([b.x, b.y]), [] as number[]) @@ -307,8 +310,8 @@ const PolygonComp = ({ CORNER_RADIUS_AJUST_ZOOM * 2, pos ); - const renderPoints = () => { - return points.map((point, index) => { + const renderPoints = () => + points.map((point, index) => { const x = point.x - CORNER_RADIUS_AJUST_ZOOM / 2; const y = point.y - CORNER_RADIUS_AJUST_ZOOM / 2; let startPointAttr; @@ -342,7 +345,6 @@ const PolygonComp = ({ return ( ); }); - }; const handleMouseDownLine = () => { if (currentDrawState !== DrawState.DRAWING) { setLineStyle({ ...lineStyle, strokeWidth: STROKE_WIDTH_LINE * 2 }); @@ -365,10 +366,7 @@ const PolygonComp = ({ const handleMouseOutLine = () => { setLineStyle({ ...lineStyle, strokeWidth: STROKE_WIDTH_LINE }); }; - const [lineStyle, setLineStyle] = useState({ - ...spec.cssStyle, - strokeWidth: STROKE_WIDTH_LINE, - }); + useEffect(() => { setLineStyle({ ...spec.cssStyle, strokeWidth: STROKE_WIDTH_LINE }); }, [spec.cssStyle]); @@ -407,12 +405,8 @@ const PolygonComp = ({ renderPoints()} ); -}; -const Polygon = ({ - id, - onMouseOverHandler, - onMouseOutHandler, -}: PolygonProps) => { +} +function Polygon({ id, onMouseOverHandler, onMouseOutHandler }: PolygonProps) { const drawObject = useSelector(selectorDrawObject(id)); if (drawObject) { return ( @@ -423,7 +417,7 @@ const Polygon = ({ /> ); } - return <>; -}; + return null; +} export default Polygon; diff --git a/src/components/Annotation/Editor/Shape/Rectangle.tsx b/src/components/Annotation/Editor/Shape/Rectangle.tsx index 3d5293b9..d7b6b03d 100644 --- a/src/components/Annotation/Editor/Shape/Rectangle.tsx +++ b/src/components/Annotation/Editor/Shape/Rectangle.tsx @@ -12,15 +12,33 @@ import { selectorDrawObjectState, selectorSelectedRectangle, } from "reduxes/annotation/selector"; -import { DrawState } from "reduxes/annotation/type"; -import { CIRCLE_STYLE, CORNER_RADIUS } from "../const"; +import { DrawObject, DrawState, DrawType } from "reduxes/annotation/type"; +import { CIRCLE_STYLE, CORNER_RADIUS, LINE_STYLE } from "../const"; import { RectangleCompProps, RectangleProps, RectangleSpec } from "../type"; import useCommonShapeEvent from "../useCommonShapeEvent"; -const RectangleComp = ({ + +export const createRectangle = (position: { + x: number; + y: number; +}): DrawObject => { + const id = `RECTANGLE-${Math.floor(Math.random() * 100000)}`; + const rect: RectangleSpec = { + x: position.x, + y: position.y, + width: 10, + height: 10, + id, + rotation: 0, + label: { label: id }, + cssStyle: { ...LINE_STYLE }, + }; + return { type: DrawType.RECTANGLE, data: rect }; +}; +function RectangleComp({ spec, onMouseOverHandler, onMouseOutHandler, -}: RectangleCompProps) => { +}: RectangleCompProps) { const shapeRef = React.useRef(null); const trRef = React.useRef(null); const dispatch = useDispatch(); @@ -33,11 +51,12 @@ const RectangleComp = ({ spec.cssStyle.strokeWidth ); - const isSelected = useMemo(() => { - return currentRectangle != null && spec.id === currentRectangle.id; - }, [currentRectangle?.id]); + const isSelected = useMemo( + () => currentRectangle != null && spec.id === currentRectangle.id, + [currentRectangle?.id] + ); useEffect(() => { - if (isSelected == true) { + if (isSelected === true) { shapeRef.current?.moveToTop(); } }, [isSelected]); @@ -122,7 +141,7 @@ const RectangleComp = ({ }; return ( - + <> )} - + ); -}; -const Rectangle = function ({ +} +function Rectangle({ id, onMouseOverHandler, onMouseOutHandler, @@ -171,6 +190,6 @@ const Rectangle = function ({ /> ); } - return <>; -}; + return null; +} export default Rectangle; diff --git a/src/components/Annotation/Editor/type.ts b/src/components/Annotation/Editor/type.ts index 9ab1d231..0d434672 100644 --- a/src/components/Annotation/Editor/type.ts +++ b/src/components/Annotation/Editor/type.ts @@ -99,7 +99,7 @@ export const initialRectangles: Record = { width: 100, height: 100, id: "RETANGLE_1", - label: initialLabelClassPropertiesByLabelClass["house"].label, + label: initialLabelClassPropertiesByLabelClass.house.label, cssStyle: { ...LINE_STYLE }, }, RETANGLE_2: { @@ -109,7 +109,7 @@ export const initialRectangles: Record = { width: 100, height: 100, id: "RETANGLE_2", - label: initialLabelClassPropertiesByLabelClass["car"].label, + label: initialLabelClassPropertiesByLabelClass.car.label, cssStyle: { ...LINE_STYLE }, }, }; @@ -124,7 +124,7 @@ export const initialPolygons: Record = { isFinished: true, }, id: "POLYGON_1", - label: initialLabelClassPropertiesByLabelClass["tree"].label, + label: initialLabelClassPropertiesByLabelClass.tree.label, cssStyle: { ...LINE_STYLE }, }, }; @@ -141,7 +141,7 @@ export const initialLineStrips: Record = { isLineStrip: true, }, id: "LINESTRIP_1", - label: initialLabelClassPropertiesByLabelClass["car"].label, + label: initialLabelClassPropertiesByLabelClass.car.label, cssStyle: { ...LINE_STYLE }, }, }; @@ -153,7 +153,7 @@ export const initialEllipses: Record = { y: 300, rotation: 0, id: "ELLIPSE_1", - label: initialLabelClassPropertiesByLabelClass["house"].label, + label: initialLabelClassPropertiesByLabelClass.house.label, cssStyle: { ...LINE_STYLE }, }, }; diff --git a/src/components/Annotation/Editor/utils/canvas.ts b/src/components/Annotation/Editor/utils/canvas.ts index 91b7b068..4dda2fcd 100644 --- a/src/components/Annotation/Editor/utils/canvas.ts +++ b/src/components/Annotation/Editor/utils/canvas.ts @@ -19,7 +19,7 @@ export const getDistance = (node1: number[], node2: number[]) => { const diffX = Math.abs(node1[0] - node2[0]); const diffY = Math.abs(node1[1] - node2[1]); const distaneInPixel = Math.sqrt(diffX * diffX + diffY * diffY); - return Number.parseFloat(distaneInPixel + "").toFixed(2); + return Number.parseFloat(`${distaneInPixel}`).toFixed(2); }; export const dragBoundFunc = ( stageWidth: number, @@ -27,21 +27,19 @@ export const dragBoundFunc = ( vertexRadius: number, pos: { x: number; y: number } ) => { - let x = pos.x; - let y = pos.y; + let { x, y } = pos; if (pos.x + vertexRadius > stageWidth) x = stageWidth; if (pos.x - vertexRadius < 0) x = 0; if (pos.y + vertexRadius > stageHeight) y = stageHeight; if (pos.y - vertexRadius < 0) y = 0; return { x, y }; }; -export const minMax = (points: number[]) => { - return points.reduce((acc: number[], val: number) => { +export const minMax = (points: number[]) => + points.reduce((acc: number[], val: number) => { acc[0] = acc[0] === undefined || val < acc[0] ? val : acc[0]; acc[1] = acc[1] === undefined || val > acc[1] ? val : acc[1]; return acc; }, []); -}; export const getScaleImageDemensionInEditor = ( realWidth: number, @@ -50,11 +48,11 @@ export const getScaleImageDemensionInEditor = ( let newHeight = realHeight; let newWidth = realWidth; if (realWidth > MAX_WIDTH_IMAGE_IN_EDITOR) { - newHeight = newHeight * (MAX_WIDTH_IMAGE_IN_EDITOR / newWidth); + newHeight *= MAX_WIDTH_IMAGE_IN_EDITOR / newWidth; newWidth = MAX_WIDTH_IMAGE_IN_EDITOR; } if (newHeight > MAX_HEIGHT_IMAGE_IN_EDITOR) { - newWidth = newWidth * (MAX_HEIGHT_IMAGE_IN_EDITOR / newHeight); + newWidth *= MAX_HEIGHT_IMAGE_IN_EDITOR / newHeight; newHeight = MAX_HEIGHT_IMAGE_IN_EDITOR; } return { newWidth, newHeight }; diff --git a/src/components/Annotation/Editor/utils/event.ts b/src/components/Annotation/Editor/utils/event.ts index ce0c042f..8011ac98 100644 --- a/src/components/Annotation/Editor/utils/event.ts +++ b/src/components/Annotation/Editor/utils/event.ts @@ -7,6 +7,7 @@ import { MAX_WIDTH_IMAGE_IN_EDITOR, } from "../const"; import { ScaleResult } from "../type"; + const scaleBy = 1.2; export function getNewPositionOnWheel( @@ -38,14 +39,11 @@ export function getFitScaleEditor(width: number, height: number) { const widthRatio = MAX_WIDTH_IMAGE_IN_EDITOR / width; const heightRatio = MAX_HEIGHT_IMAGE_IN_EDITOR / height; let newWidth = width; - let newHeight = height; if (widthRatio < 1 || heightRatio < 1) { if (widthRatio < heightRatio) { newWidth = MAX_WIDTH_IMAGE_IN_EDITOR; - newHeight = newHeight * widthRatio; } else { - newHeight = MAX_HEIGHT_IMAGE_IN_EDITOR; - newWidth = newWidth * heightRatio; + newWidth *= heightRatio; } } return newWidth / width; diff --git a/src/components/Annotation/Formart/daita/index.ts b/src/components/Annotation/Formart/daita/index.ts index 0fc2fe36..9a1e6c33 100644 --- a/src/components/Annotation/Formart/daita/index.ts +++ b/src/components/Annotation/Formart/daita/index.ts @@ -1,3 +1,6 @@ +import { createEllipse } from "components/Annotation/Editor/Shape/EllipseShape"; +import { createPolygon } from "components/Annotation/Editor/Shape/Polygon"; +import { createRectangle } from "components/Annotation/Editor/Shape/Rectangle"; import { EllipseSpec, PolygonSpec, @@ -5,9 +8,7 @@ import { } from "components/Annotation/Editor/type"; import { loadImage } from "components/UploadFile"; import { DrawObject, DrawType } from "reduxes/annotation/type"; -import { createEllipse } from "routes/AnnotationPage/Editor/Hook/useElipseEvent"; -import { createPolygon } from "routes/AnnotationPage/Editor/Hook/usePolygonEvent"; -import { createRectangle } from "routes/AnnotationPage/Editor/Hook/useRectangleEvent"; + import { AnnotationDaitaFormatter, AnnotationFormatter, @@ -19,39 +20,16 @@ import { Shape, ShapeType, } from "./type"; -export const exportAnnotationToJson = ( - drawObjectById: Record -) => { - const shapes: Shape[] = convert(drawObjectById); - const annotationFormatter: AnnotationDaitaFormatter = { - shapes, - }; - return annotationFormatter; -}; -export const exportAnnotation = ( - drawObjectById: Record, - imageName: string -) => { - const json = exportAnnotationToJson(drawObjectById); - const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent( - JSON.stringify(json, null, 2) - )}`; - const link = document.createElement("a"); - link.href = jsonString; - link.download = `${imageName}.json`; - - link.click(); -}; const readImage = async (url: string) => { - let response = await fetch(url); - let data = await response.blob(); - let metadata = { + const response = await fetch(url); + const data = await response.blob(); + const metadata = { type: "image/png", }; const split = url.split("/"); const filename = split[split.length - 1]; - let file = new File([data], filename, metadata); + const file = new File([data], filename, metadata); const image = await loadImage(file); return { height: image.image.height, @@ -61,8 +39,8 @@ const readImage = async (url: string) => { }; export const importFileAndAnnotationFromDaitaAI = ( file: File -): Promise => { - return new Promise((resolve) => { +): Promise => + new Promise((resolve) => { const reader = new FileReader(); reader.readAsText(file); const drawObjectById: Record = {}; @@ -70,11 +48,12 @@ export const importFileAndAnnotationFromDaitaAI = ( const annotationFormatter: AnnotationFormatter = JSON.parse( reader.result as string ); - for (const annotation of annotationFormatter.annotations) { + annotationFormatter.annotations.forEach((annotation) => { const drawObject = createPolygon({ x: 0, y: 0 }); - const formatedPoints = annotation.coordinates.map((arr) => { - return { x: arr[0], y: arr[1] }; - }); + const formatedPoints = annotation.coordinates.map((arr) => ({ + x: arr[0], + y: arr[1], + })); drawObjectById[drawObject.data.id] = { type: drawObject.type, data: { @@ -84,17 +63,16 @@ export const importFileAndAnnotationFromDaitaAI = ( label: { label: ID_2_LABEL_DAITA[annotation.category_id] }, }, }; - } + }); resolve({ drawObjectById, }); }; }); -}; export const importFileAndAnnotation = ( file: File -): Promise => { - return new Promise((resolve) => { +): Promise => + new Promise((resolve) => { const reader = new FileReader(); reader.readAsText(file); const drawObjectById: Record = {}; @@ -104,11 +82,12 @@ export const importFileAndAnnotation = ( ); const property = await readImage(annotationFormatter.image_path); - for (const annotation of annotationFormatter.annotations) { + annotationFormatter.annotations.forEach((annotation) => { const drawObject = createPolygon({ x: 0, y: 0 }); - const formatedPoints = annotation.coordinates.map((arr) => { - return { x: arr[0], y: arr[1] }; - }); + const formatedPoints = annotation.coordinates.map((arr) => ({ + x: arr[0], + y: arr[1], + })); drawObjectById[drawObject.data.id] = { type: drawObject.type, data: { @@ -118,16 +97,15 @@ export const importFileAndAnnotation = ( label: { label: ID_2_LABEL_DAITA[annotation.category_id] }, }, }; - } + }); resolve({ annotationImagesProperty: property, drawObjectById, }); }; }); -}; -export const importAnnotation = (file: File): Promise => { - return new Promise((resolve) => { +export const importAnnotation = (file: File): Promise => + new Promise((resolve) => { const reader = new FileReader(); reader.readAsText(file); const drawObjectById: Record = {}; @@ -135,7 +113,7 @@ export const importAnnotation = (file: File): Promise => { const annotationFormatter: AnnotationDaitaFormatter = JSON.parse( reader.result as string ); - for (const annotation of annotationFormatter.shapes) { + annotationFormatter.shapes.forEach((annotation) => { if ( annotation.shapeType === ShapeType.POLYGON || annotation.shapeType === ShapeType.LINE_STRIP @@ -177,20 +155,19 @@ export const importAnnotation = (file: File): Promise => { } as EllipseSpec, }; } - } + }); resolve({ drawObjectById, }); }; }); -}; export const convert = ( drawObjectById: Record ): Shape[] => { const shapes: Shape[] = []; - for (const [key, value] of Object.entries(drawObjectById)) { + Object.entries(drawObjectById).forEach(([, value]) => { if (value.type === DrawType.POLYGON || value.type === DrawType.LINE_STRIP) { const { points, label } = value.data as PolygonSpec; shapes.push({ @@ -216,6 +193,29 @@ export const convert = ( categoryId: LABEL_2_ID_DAITA[label.label], }); } - } + }); return shapes; }; +export const exportAnnotationToJson = ( + drawObjectById: Record +) => { + const shapes: Shape[] = convert(drawObjectById); + const annotationFormatter: AnnotationDaitaFormatter = { + shapes, + }; + return annotationFormatter; +}; +export const exportAnnotation = ( + drawObjectById: Record, + imageName: string +) => { + const json = exportAnnotationToJson(drawObjectById); + const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent( + JSON.stringify(json, null, 2) + )}`; + const link = document.createElement("a"); + link.href = jsonString; + link.download = `${imageName}.json`; + + link.click(); +}; diff --git a/src/components/Annotation/Formart/daita/type.ts b/src/components/Annotation/Formart/daita/type.ts index 8ec4c825..1ab5e43d 100644 --- a/src/components/Annotation/Formart/daita/type.ts +++ b/src/components/Annotation/Formart/daita/type.ts @@ -66,8 +66,8 @@ export const ID_2_LABEL_DAITA: Record = { }; export const LABEL_2_ID_DAITA: Record = (() => { const temp: Record = {}; - Object.entries(ID_2_LABEL_DAITA).map(([key, value]) => { - temp[value] = parseInt(key); + Object.entries(ID_2_LABEL_DAITA).forEach(([key, value]) => { + temp[value] = parseInt(key, 10); }); return temp; })(); diff --git a/src/components/Annotation/Formart/labelbox/index.ts b/src/components/Annotation/Formart/labelbox/index.ts index 465060e0..9a912c6f 100644 --- a/src/components/Annotation/Formart/labelbox/index.ts +++ b/src/components/Annotation/Formart/labelbox/index.ts @@ -7,27 +7,11 @@ import { Point, } from "./type"; -export const exportAnnotation = ( - drawObjectById: Record -) => { - const shapes: LabelLabelBox = convert(drawObjectById); - const annotationFormatter: AnnotationFormatter = - createAnnotationFormatter(shapes); - const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent( - JSON.stringify(annotationFormatter, null, 2) - )}`; - const link = document.createElement("a"); - link.href = jsonString; - link.download = "data.json"; - - link.click(); -}; - export const convert = ( drawObjectById: Record ): LabelLabelBox => { const shape: LabelLabelBox = {}; - for (const [key, value] of Object.entries(drawObjectById)) { + Object.entries(drawObjectById).forEach(([, value]) => { if (value.type === DrawType.RECTANGLE) { const { x, y, width, height, label } = value.data as RectangleSpec; const points: Point[] = [ @@ -61,6 +45,21 @@ export const convert = ( shape[label.label] = [{ geometry: points }]; } } - } + }); return shape; }; +export const exportAnnotation = ( + drawObjectById: Record +) => { + const shapes: LabelLabelBox = convert(drawObjectById); + const annotationFormatter: AnnotationFormatter = + createAnnotationFormatter(shapes); + const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent( + JSON.stringify(annotationFormatter, null, 2) + )}`; + const link = document.createElement("a"); + link.href = jsonString; + link.download = "data.json"; + + link.click(); +}; diff --git a/src/components/Annotation/Formart/labelbox/type.ts b/src/components/Annotation/Formart/labelbox/type.ts index 4f3cd530..968110dd 100644 --- a/src/components/Annotation/Formart/labelbox/type.ts +++ b/src/components/Annotation/Formart/labelbox/type.ts @@ -18,9 +18,8 @@ export interface AnnotationFormatter { "External ID": string; } -export const convertLabelMeFormatToBase64 = (imageData: string) => { - return "data:image/png;base64," + imageData; -}; +export const convertLabelMeFormatToBase64 = (imageData: string) => + `data:image/png;base64,${imageData}`; export const createAnnotationFormatter = (labelBox: LabelLabelBox) => { const annotationFormatter: AnnotationFormatter = { ID: "a9b7c5d3e1f", diff --git a/src/components/Annotation/Formart/labelme/index.ts b/src/components/Annotation/Formart/labelme/index.ts index 6823179d..c5de9f76 100644 --- a/src/components/Annotation/Formart/labelme/index.ts +++ b/src/components/Annotation/Formart/labelme/index.ts @@ -1,14 +1,10 @@ -import { - EllipseSpec, - PolygonSpec, - RectangleSpec, -} from "components/Annotation/Editor/type"; +import { createEllipse } from "components/Annotation/Editor/Shape/EllipseShape"; +import { createPolygon } from "components/Annotation/Editor/Shape/Polygon"; +import { createRectangle } from "components/Annotation/Editor/Shape/Rectangle"; +import { PolygonSpec, RectangleSpec } from "components/Annotation/Editor/type"; import { loadImage } from "components/UploadFile"; import { DrawObject, DrawType } from "reduxes/annotation/type"; import { AnnotationImagesProperty } from "reduxes/annotationmanager/type"; -import { createEllipse } from "routes/AnnotationPage/Editor/Hook/useElipseEvent"; -import { createPolygon } from "routes/AnnotationPage/Editor/Hook/usePolygonEvent"; -import { createRectangle } from "routes/AnnotationPage/Editor/Hook/useRectangleEvent"; import { AnnotationFormatter, CircleFormatter, @@ -21,34 +17,10 @@ import { Shape, } from "./type"; -export const exportAnnotation = ( - annotationImagesProperty: AnnotationImagesProperty, - drawObjectById: Record -) => { - const shapes: Shape[] = convert(drawObjectById); - const { image } = annotationImagesProperty; - const reader = new FileReader(); - reader.readAsDataURL(image); - reader.onload = () => { - const annotationFormatter: AnnotationFormatter = createAnnotationFormatter( - shapes, - reader.result as string - ); - const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent( - JSON.stringify(annotationFormatter, null, 2) - )}`; - const link = document.createElement("a"); - link.href = jsonString; - link.download = "data.json"; - - link.click(); - }; -}; - export const importAnnotation = ( file: File -): Promise => { - return new Promise((resolve) => { +): Promise => + new Promise((resolve) => { const reader = new FileReader(); reader.readAsText(file); const drawObjectById: Record = {}; @@ -69,13 +41,13 @@ export const importAnnotation = ( type: "image/jpg", }); loadImage(loadedFile) - .then(({ image, fileName }) => { + .then(({ image }) => { const property: AnnotationImagesProperty = { image: loadedFile, width: image.width, height: image.height, }; - for (const shape of annotationFormatter.shapes) { + annotationFormatter.shapes.forEach((shape) => { if (shape.shape_type === "rectangle") { const drawObject = createRectangle({ x: 0, y: 0 }); const points = shape.points as RectangleFormatter; @@ -101,9 +73,10 @@ export const importAnnotation = ( ) { const drawObject = createPolygon({ x: 0, y: 0 }); const points = shape.points as PolygonFormatter; - const formatedPoints = points.map((arr) => { - return { x: arr[0], y: arr[1] }; - }); + const formatedPoints = points.map((arr) => ({ + x: arr[0], + y: arr[1], + })); drawObjectById[drawObject.data.id] = { type: drawObject.type, data: { @@ -148,7 +121,7 @@ export const importAnnotation = ( }, }; } - } + }); resolve({ annotationImagesProperty: property, drawObjectById, @@ -158,13 +131,12 @@ export const importAnnotation = ( }); }; }); -}; export const convert = ( drawObjectById: Record ): Shape[] => { const shape: Shape[] = []; - for (const [key, value] of Object.entries(drawObjectById)) { + Object.entries(drawObjectById).forEach(([, value]) => { if (value.type === DrawType.RECTANGLE) { const { x, y, width, height, label } = value.data as RectangleSpec; shape.push({ @@ -194,6 +166,29 @@ export const convert = ( // label: label.label, // }); // } - } + }); return shape; }; +export const exportAnnotation = ( + annotationImagesProperty: AnnotationImagesProperty, + drawObjectById: Record +) => { + const shapes: Shape[] = convert(drawObjectById); + const { image } = annotationImagesProperty; + const reader = new FileReader(); + reader.readAsDataURL(image); + reader.onload = () => { + const annotationFormatter: AnnotationFormatter = createAnnotationFormatter( + shapes, + reader.result as string + ); + const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent( + JSON.stringify(annotationFormatter, null, 2) + )}`; + const link = document.createElement("a"); + link.href = jsonString; + link.download = "data.json"; + + link.click(); + }; +}; diff --git a/src/components/Annotation/Formart/labelme/type.ts b/src/components/Annotation/Formart/labelme/type.ts index 10d89dd9..c960500e 100644 --- a/src/components/Annotation/Formart/labelme/type.ts +++ b/src/components/Annotation/Formart/labelme/type.ts @@ -7,7 +7,7 @@ export type PolygonFormatter = number[][]; export type CircleFormatter = number[][]; export type EllipseFormatter = EllipseSpec; -export type points = RectangleFormatter | PolygonFormatter | EllipseFormatter; +export type Points = RectangleFormatter | PolygonFormatter | EllipseFormatter; export type ShapeType = | "rectangle" | "polygon" @@ -16,7 +16,7 @@ export type ShapeType = | "line" | "linestrip"; export interface Shape { - points: points; + points: Points; shape_type: ShapeType; label: string; group_id?: null; @@ -38,9 +38,8 @@ const convertBase64ToLabelMeFormat = (base64: string) => { const formatImageData = base64.substring(indexOf + stripKey.length); return formatImageData; }; -export const convertLabelMeFormatToBase64 = (imageData: string) => { - return "data:image/png;base64," + imageData; -}; +export const convertLabelMeFormatToBase64 = (imageData: string) => + `data:image/png;base64,${imageData}`; export const createAnnotationFormatter = ( shapes: Shape[], imageData: string diff --git a/src/components/Annotation/Formart/scaleai/index.ts b/src/components/Annotation/Formart/scaleai/index.ts index a6ab322d..5ee67eaf 100644 --- a/src/components/Annotation/Formart/scaleai/index.ts +++ b/src/components/Annotation/Formart/scaleai/index.ts @@ -1,7 +1,7 @@ +import { createPolygon } from "components/Annotation/Editor/Shape/Polygon"; +import { createRectangle } from "components/Annotation/Editor/Shape/Rectangle"; import { PolygonSpec, RectangleSpec } from "components/Annotation/Editor/type"; import { DrawObject, DrawType } from "reduxes/annotation/type"; -import { createPolygon } from "routes/AnnotationPage/Editor/Hook/usePolygonEvent"; -import { createRectangle } from "routes/AnnotationPage/Editor/Hook/useRectangleEvent"; import { AnnotationFormatter, createAnnotationFormatter, @@ -11,27 +11,11 @@ import { Shape, } from "./type"; -export const exportAnnotation = ( - drawObjectById: Record -) => { - const shapes: Shape[] = convert(drawObjectById); - const annotationFormatter: AnnotationFormatter = - createAnnotationFormatter(shapes); - const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent( - JSON.stringify(annotationFormatter, null, 2) - )}`; - const link = document.createElement("a"); - link.href = jsonString; - link.download = "data.json"; - - link.click(); -}; - export const convert = ( drawObjectById: Record ): Shape[] => { const shapes: Shape[] = []; - for (const [key, value] of Object.entries(drawObjectById)) { + Object.entries(drawObjectById).forEach(([, value]) => { if (value.type === DrawType.RECTANGLE) { const { id, x, y, width, height, label } = value.data as RectangleSpec; const shape: RectangleShape = { @@ -54,14 +38,14 @@ export const convert = ( }; shapes.push(shape); } - } + }); return shapes; }; export const importAnnotation = ( file: File -): Promise => { - return new Promise((resolve) => { +): Promise => + new Promise((resolve) => { const reader = new FileReader(); reader.readAsText(file); const drawObjectById: Record = {}; @@ -69,7 +53,7 @@ export const importAnnotation = ( const annotationFormatter: AnnotationFormatter = JSON.parse( reader.result as string ); - for (const shape of annotationFormatter.response.annotations) { + annotationFormatter.response.annotations.forEach((shape) => { if (shape.type === "box") { const drawObject = createRectangle({ x: 0, y: 0 }); @@ -96,8 +80,22 @@ export const importAnnotation = ( }, }; } - } + }); resolve({ drawObjectById }); }; }); +export const exportAnnotation = ( + drawObjectById: Record +) => { + const shapes: Shape[] = convert(drawObjectById); + const annotationFormatter: AnnotationFormatter = + createAnnotationFormatter(shapes); + const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent( + JSON.stringify(annotationFormatter, null, 2) + )}`; + const link = document.createElement("a"); + link.href = jsonString; + link.download = "data.json"; + + link.click(); }; diff --git a/src/components/Annotation/Formart/scaleai/type.ts b/src/components/Annotation/Formart/scaleai/type.ts index d3336149..59955354 100644 --- a/src/components/Annotation/Formart/scaleai/type.ts +++ b/src/components/Annotation/Formart/scaleai/type.ts @@ -1,5 +1,4 @@ import { DrawObject } from "reduxes/annotation/type"; -import { AnnotationImagesProperty } from "reduxes/annotationmanager/type"; export type Shape = RectangleShape | PolygonShape; export type RectangleShape = { @@ -47,9 +46,8 @@ export interface AnnotationFormatter { scale_internal_attachment: string; workStarted: true; } -export const convertLabelMeFormatToBase64 = (imageData: string) => { - return "data:image/png;base64," + imageData; -}; +export const convertLabelMeFormatToBase64 = (imageData: string) => + `data:image/png;base64,${imageData}`; export const createAnnotationFormatter = (shapes: Shape[]) => { const annotationFormatter: AnnotationFormatter = { task_id: "a9b7c5d3e1f", diff --git a/src/components/Annotation/Formart/type.ts b/src/components/Annotation/Formart/type.ts deleted file mode 100644 index 63d30042..00000000 --- a/src/components/Annotation/Formart/type.ts +++ /dev/null @@ -1 +0,0 @@ -export type CONVERT_ANNOTATION_TYPE = "LABLE_ME" | "SCALE_AI" | "LABEL_BOX"; diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index a18ab052..3cab50d9 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -104,7 +104,7 @@ const updateStateHistory = ( drawObjectById: Record, statehHistory: StateHistory ) => { - let newStateHistory = { ...statehHistory }; + const newStateHistory = { ...statehHistory }; if ( newStateHistory.historyStep >= 0 && newStateHistory.historyStep < newStateHistory.stateHistoryItems.length @@ -124,6 +124,7 @@ const updateStateHistory = ( return newStateHistory; }; const annotationReducer = ( + // eslint-disable-next-line @typescript-eslint/default-param-last state = inititalState, action: any ): AnnotationReducer => { @@ -132,13 +133,15 @@ const annotationReducer = ( switch (actionType) { case CHANGE_CURRENT_DRAW_TYPE: { const { currentDrawType } = payload as ChangeCurrentDrawTypePayload; + const newCurrentDrawState = currentDrawType + ? DrawState.FREE + : DrawState.SELECTING; + return { ...state, - currentDrawType, selectedDrawObjectId: null, - currentDrawState: currentDrawType - ? DrawState.FREE - : DrawState.SELECTING, + currentDrawState: newCurrentDrawState, + currentDrawType, }; } case CHANGE_ZOOM: { @@ -154,7 +157,7 @@ const annotationReducer = ( case CREATE_DRAW_OBJECT: { const { drawObject } = payload as CreateDrawObjectPayload; - let newStateHistory = updateStateHistory( + const newStateHistory = updateStateHistory( state.drawObjectById, state.statehHistory ); @@ -210,6 +213,7 @@ const annotationReducer = ( return { ...state, currentDrawState: DrawState.SELECTING, + currentDrawType: null, selectedDrawObjectId, }; } @@ -219,9 +223,10 @@ const annotationReducer = ( state.drawObjectById, state.statehHistory ); - delete state.drawObjectById[drawObjectId]; + const newState = { ...state }; + delete newState.drawObjectById[drawObjectId]; return { - ...state, + ...newState, selectedDrawObjectId: null, drawObjectById: { ...state.drawObjectById }, statehHistory: newStateHistory, @@ -281,7 +286,7 @@ const annotationReducer = ( const undoDrawObjectById = statehHistory.stateHistoryItems[state.statehHistory.historyStep - 1] .drawObjectById; - Object.entries(undoDrawObjectById).map(([key, value]) => { + Object.entries(undoDrawObjectById).forEach(([key, value]) => { undoDrawObjectById[key].data = { ...undoDrawObjectById[key].data, label: value.data.label, @@ -297,9 +302,8 @@ const annotationReducer = ( historyStep: statehHistory.historyStep - 1, }, }; - } else { - return state; } + return state; } case REDO_DRAW_OBJECT: { const { statehHistory } = state; @@ -317,9 +321,8 @@ const annotationReducer = ( historyStep: statehHistory.historyStep + 1, }, }; - } else { - return state; } + return state; } case SET_HIDDEN_DRAW_OBJECT: { const { drawObjectId, isHidden } = payload as SetHiddenDrawObjectPayload; @@ -364,20 +367,19 @@ const annotationReducer = ( const scaleX = 1 / scale.x; const scaleY = 1 / scale.y; - for (const drawObjectId of Object.keys(state.drawObjectStateIdByAI)) { + Object.keys(state.drawObjectStateIdByAI).forEach((drawObjectId) => { const drawObject = state.drawObjectById[drawObjectId]; if (drawObject) { const { type, data } = drawObject; if (type === DrawType.POLYGON) { - const points = (data as PolygonSpec).points; - const isInvalid = points.some((point) => { - return ( + const { points } = data as PolygonSpec; + const isInvalid = points.some( + (point) => point.x < detectedArea.x * scaleX || point.y < detectedArea.y * scaleY || point.x > (detectedArea.x + detectedArea.width) * scaleX || point.y > (detectedArea.y + detectedArea.height) * scaleY - ); - }); + ); if (!isInvalid) { newDrawObjectStateIdByAI[drawObjectId] = { ...newDrawObjectStateIdByAI[drawObjectId], @@ -386,7 +388,7 @@ const annotationReducer = ( } } } - } + }); } return { ...state, @@ -437,14 +439,14 @@ const annotationReducer = ( } case SHOW_ALL_DRAW_OBJECTS_BY_AI: { const newDrawObjectStateIdByAI: Record = {}; - Object.entries(state.drawObjectStateIdByAI).map(([key, value]) => { + Object.entries(state.drawObjectStateIdByAI).forEach(([key, value]) => { newDrawObjectStateIdByAI[key] = { ...value, isShow: true }; }); return { ...state, drawObjectStateIdByAI: newDrawObjectStateIdByAI }; } case HIDDEN_ALL_DRAW_OBJECTS_BY_AI: { const newDrawObjectStateIdByAI: Record = {}; - Object.entries(state.drawObjectStateIdByAI).map(([key, value]) => { + Object.entries(state.drawObjectStateIdByAI).forEach(([key, value]) => { newDrawObjectStateIdByAI[key] = { ...value, isShow: false }; }); return { ...state, drawObjectStateIdByAI: newDrawObjectStateIdByAI }; diff --git a/src/reduxes/annotation/selector.ts b/src/reduxes/annotation/selector.ts index 123c4f27..b7901355 100644 --- a/src/reduxes/annotation/selector.ts +++ b/src/reduxes/annotation/selector.ts @@ -67,25 +67,20 @@ export const selectorAnnotationHistoryStep = (state: RootState) => state.annotationReducer.statehHistory.historyStep; export const selectorAnnotationStatehHistory = (state: RootState) => state.annotationReducer.statehHistory; -export const selectorListDrawObjectLock = (state: RootState) => { - return Object.entries(state.annotationReducer.drawObjectStateById) - .filter(([key, value]) => value.isLock === true) - .map(([key, value]) => key); -}; -export const selectorListDrawObjectHidden = (state: RootState) => { - return Object.entries(state.annotationReducer.drawObjectStateById) - .filter(([key, value]) => value.isHidden === true) - .map(([key, value]) => key); -}; -export const selectorDrawObjectState = (id: string) => (state: RootState) => { - return state.annotationReducer.drawObjectStateById[id]; -}; -export const selectorDrawObjectStateById = (state: RootState) => { - return state.annotationReducer.drawObjectStateById; -}; -export const selectorDetectedArea = (state: RootState) => { - return state.annotationReducer.detectedArea; -}; +export const selectorListDrawObjectLock = (state: RootState) => + Object.entries(state.annotationReducer.drawObjectStateById) + .filter(([, value]) => value.isLock === true) + .map(([key]) => key); +export const selectorListDrawObjectHidden = (state: RootState) => + Object.entries(state.annotationReducer.drawObjectStateById) + .filter(([, value]) => value.isHidden === true) + .map(([key]) => key); +export const selectorDrawObjectState = (id: string) => (state: RootState) => + state.annotationReducer.drawObjectStateById[id]; +export const selectorDrawObjectStateById = (state: RootState) => + state.annotationReducer.drawObjectStateById; +export const selectorDetectedArea = (state: RootState) => + state.annotationReducer.detectedArea; export const selectorDrawObject = (id: string) => (state: RootState) => state.annotationReducer.drawObjectById[id]; export const selectorIsDraggingViewport = (state: RootState) => diff --git a/src/reduxes/annotationProject/reducer.ts b/src/reduxes/annotationProject/reducer.ts index a204ad50..15a9abbb 100644 --- a/src/reduxes/annotationProject/reducer.ts +++ b/src/reduxes/annotationProject/reducer.ts @@ -97,6 +97,7 @@ const inititalState: AnnotationProjectReducer = { deleteConfirmDialogInfo: null, }; const annotationProjectReducer = ( + // eslint-disable-next-line @typescript-eslint/default-param-last state = inititalState, action: any ): AnnotationProjectReducer => { @@ -114,6 +115,7 @@ const annotationProjectReducer = ( case CLONE_PROJECT_TO_ANNOTATION.FAILED: return { ...state, isCloningProjectToAnnotation: false }; case SHOW_DIALOG_CLONE_PROJECT_TO_ANNOTATION: + // eslint-disable-next-line no-case-declarations const { dialogCloneProjectToAnnotation } = payload as SetDialogCloneProjectToAnnotationProps; if (!dialogCloneProjectToAnnotation.isShow) { @@ -165,7 +167,9 @@ const annotationProjectReducer = ( }; } case FETCH_ANNOTATION_FILES.SUCCEEDED: + // eslint-disable-next-line no-case-declarations, @typescript-eslint/naming-convention const { items, next_token, projectId } = payload as AnnotationFilesApi; + // eslint-disable-next-line no-case-declarations const { currentAnnotationFiles } = state; if ( currentAnnotationFiles && @@ -177,6 +181,7 @@ const annotationProjectReducer = ( ...items, ]; } else { + // eslint-disable-next-line no-param-reassign state.currentAnnotationFiles = { items, next_token, projectId }; } return { @@ -189,10 +194,11 @@ const annotationProjectReducer = ( }; } case DELETE_ANNOTATION_PROJECT.SUCCEEDED: { - const { projectId } = payload as DeleteProjectSucceedPayload; + const { projectId: projectIdPayload } = + payload as DeleteProjectSucceedPayload; const matchProjectIndex = objectIndexOf( state.listProjects, - projectId, + projectIdPayload, "project_id" ); if (matchProjectIndex > -1) { @@ -211,11 +217,11 @@ const annotationProjectReducer = ( }; } case UPDATE_STATISTIC_PROJECT: { - const { projectId, updateInfo } = + const { projectId: projectIdPayload, updateInfo } = payload as UpdateProjectStatisticPayload; if ( state.currentProjectInfo && - state.currentProjectInfo.project_id === projectId + state.currentProjectInfo.project_id === projectIdPayload ) { const { groups } = state.currentProjectInfo; const { fileInfo, typeMethod } = updateInfo; @@ -262,7 +268,7 @@ const annotationProjectReducer = ( const matchProjectIndex = objectIndexOf( state.listProjects, - projectId, + projectIdPayload, "project_id" ); @@ -292,6 +298,8 @@ const annotationProjectReducer = ( } return state; } + default: + return state; } return state; }; diff --git a/src/reduxes/annotationmanager/reducer.ts b/src/reduxes/annotationmanager/reducer.ts index 96934ea0..b787a4b5 100644 --- a/src/reduxes/annotationmanager/reducer.ts +++ b/src/reduxes/annotationmanager/reducer.ts @@ -136,8 +136,8 @@ const annotationManagerReducer = ( ...state, dialogClassManageModal: { isOpen, - className: !!isOpen ? className : "", - classManageModalType: !!isOpen ? classManageModalType : undefined, + className: isOpen ? className : "", + classManageModalType: isOpen ? classManageModalType : undefined, }, }; } diff --git a/src/routes/AnnotationPage/AnnotationEditor.tsx b/src/routes/AnnotationPage/AnnotationEditor.tsx index 5cd409b3..d4e352af 100644 --- a/src/routes/AnnotationPage/AnnotationEditor.tsx +++ b/src/routes/AnnotationPage/AnnotationEditor.tsx @@ -73,13 +73,12 @@ const AnnotationEditor = function () { ); - } else { - return ( - - - - ); } + return ( + + + + ); }; return {renderContent()}; }; diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index 281b8938..1f41ba32 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -76,9 +76,13 @@ import { DRAW_RECTANGLE_SHORT_KEY, SELECT_SHORT_KEY, } from "../constants"; -import { hashCode, intToRGB } from "../LabelAnnotation"; import { convertStrokeColorToFillColor } from "../LabelAnnotation/ClassLabel"; -const ControlPanel = () => { +import { + hashCode, + intToRGB, +} from "../LabelAnnotation/ClassManageModal/useListClassView"; + +function ControlPanel() { const dispatch = useDispatch(); const currentDrawType = useSelector(selectorCurrentDrawType); const currentDrawState = useSelector(selectorCurrentDrawState); @@ -132,8 +136,8 @@ const ControlPanel = () => { const getDrawObjectToExport = () => { if (drawObjectById) { const filteredDrawObjectById = { ...drawObjectById }; - Object.keys(drawObjectStateIdByAI).forEach((id) => { - delete filteredDrawObjectById[id]; + Object.keys(drawObjectStateIdByAI).forEach((drawObjectId) => { + delete filteredDrawObjectById[drawObjectId]; }); return filteredDrawObjectById; } @@ -162,16 +166,16 @@ const ControlPanel = () => { if (drawObjectToExport) { exportAnnotationDaita( drawObjectToExport, - currentPreviewImageName ? currentPreviewImageName : "imagename" + currentPreviewImageName || "imagename" ); } }; const updateDrawObject = (value: DrawObject) => { const drawObjectRet: DrawObject = { ...value }; - const label = value.data.label.label; + const { label } = value.data.label; let classLabel = labelClassPropertiesByLabelClass[label]; if (!classLabel) { - const strokeColor = "#" + intToRGB(hashCode(label)); + const strokeColor = `#${intToRGB(hashCode(label))}`; const fillColor = convertStrokeColorToFillColor(strokeColor); classLabel = { label: { label }, @@ -185,6 +189,7 @@ const ControlPanel = () => { const css = drawObjectRet.data.cssStyle; const newCss = classLabel.cssStyle; + // eslint-disable-next-line no-restricted-syntax, guard-for-in for (const prop in css) { drawObjectRet.data.cssStyle = { ...drawObjectRet.data.cssStyle, @@ -197,10 +202,10 @@ const ControlPanel = () => { return drawObjectRet; }; const importLabelMe = async (acceptedFile: File) => { - const { annotationImagesProperty, drawObjectById } = + const { annotationImagesProperty, drawObjectById: imnportDrawObjectById } = await importAnnotationLabelMe(acceptedFile); - Object.entries(drawObjectById).map(([key, value]) => { - drawObjectById[key] = updateDrawObject(value); + Object.entries(imnportDrawObjectById).forEach(([key, value]) => { + imnportDrawObjectById[key] = updateDrawObject(value); }); dispatch( addImagesToAnnotation({ @@ -210,13 +215,13 @@ const ControlPanel = () => { dispatch( setAnnotationStateManager({ imageName: annotationImagesProperty.image.name, - drawObjectById: drawObjectById, + drawObjectById: imnportDrawObjectById, }) ); dispatch( resetCurrentStateDrawObject({ - drawObjectById: drawObjectById, + drawObjectById, }) ); dispatch( @@ -226,10 +231,10 @@ const ControlPanel = () => { ); }; const importDaita = async (acceptedFile: File) => { - const { annotationImagesProperty, drawObjectById } = + const { annotationImagesProperty, drawObjectById: importDrawObjectById } = await importFileAnnotationDaita(acceptedFile); - Object.entries(drawObjectById).map(([key, value]) => { - drawObjectById[key] = updateDrawObject(value); + Object.entries(importDrawObjectById).forEach(([key, value]) => { + importDrawObjectById[key] = updateDrawObject(value); }); dispatch( addImagesToAnnotation({ @@ -239,13 +244,13 @@ const ControlPanel = () => { dispatch( setAnnotationStateManager({ imageName: annotationImagesProperty.image.name, - drawObjectById: drawObjectById, + drawObjectById: importDrawObjectById, }) ); dispatch( resetCurrentStateDrawObject({ - drawObjectById: drawObjectById, + drawObjectById: importDrawObjectById, }) ); dispatch( @@ -255,17 +260,18 @@ const ControlPanel = () => { ); }; const importScaleAI = async (acceptedFile: File) => { - const { drawObjectById } = await importAnnotationScaleAI(acceptedFile); + const { drawObjectById: importDrawObjectById } = + await importAnnotationScaleAI(acceptedFile); // dispatch( // resetCurrentStateDrawObject({ // drawObjectById: drawObjectById, // }) // ); - Object.entries(drawObjectById).map(([key, value]) => { - drawObjectById[key] = updateDrawObject(value); + Object.entries(importDrawObjectById).forEach(([key, value]) => { + importDrawObjectById[key] = updateDrawObject(value); dispatch( createDrawObject({ - drawObject: drawObjectById[key], + drawObject: importDrawObjectById[key], }) ); }); @@ -273,7 +279,7 @@ const ControlPanel = () => { const onDrop = async (acceptedFiles: File[]) => { if (acceptedFiles && acceptedFiles.length > 0) { const snapImportType = importType; - for (const acceptedFile of acceptedFiles) { + acceptedFiles.forEach((acceptedFile) => { if (snapImportType === "LABEL_ME") { importLabelMe(acceptedFile); } else if (snapImportType === "SCALE_AI") { @@ -281,7 +287,7 @@ const ControlPanel = () => { } else if (snapImportType === "DAITA") { importDaita(acceptedFile); } - } + }); } }; const dropZone = useDropzone({ @@ -289,7 +295,7 @@ const ControlPanel = () => { accept: ".json", noDragEventsBubbling: true, }); - const { getRootProps, isDragActive, getInputProps } = dropZone; + const { getRootProps, getInputProps } = dropZone; const handleUndoDrawObject = () => { dispatch(undoDrawObject()); }; @@ -361,7 +367,8 @@ const ControlPanel = () => { ); - } else if (anchorEl?.id === "export") { + } + if (anchorEl?.id === "export") { return ( @@ -387,6 +394,7 @@ const ControlPanel = () => { ); } + return ; }; const isAIDetectAvailable = React.useMemo( () => @@ -407,192 +415,190 @@ const ControlPanel = () => { currentDrawState === DrawState.DRAGGING || currentDrawState === DrawState.TRANSFORMING; return ( - <> - - + + - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - selectModeHandle(e, DrawType.DETECTED_RECTANGLE) - } - > - {isAIDetectAvailable ? ( - - ) : ( - - )} - - - - - {isAIDetectAvailable && ( - - Segmentations - - {isShowAllAIDetect ? : } - - - )} - - - + + + + - - - - - - + + + + + + + - + + + + + = - annotationStatehHistory.stateHistoryItems.length - 1 + onClick={(e) => + selectModeHandle(e, DrawType.DETECTED_RECTANGLE) } > - + {isAIDetectAvailable ? ( + + ) : ( + + )} + {isAIDetectAvailable && ( + + Segmentations + + {isShowAllAIDetect ? : } + + + )} + + + + + + + + + + + + + = + annotationStatehHistory.stateHistoryItems.length - 1 + } + > + + + + + + + - - - - - {renderPopupContent()} - - - - } - loading={isSavingAnnotation} - loadingPosition="end" - variant="contained" - color="success" - > - Save - - + + + {renderPopupContent()} + + + + } + loading={isSavingAnnotation} + loadingPosition="end" + variant="contained" + color="success" + > + Save + - + ); -}; +} export default ControlPanel; diff --git a/src/routes/AnnotationPage/Editor/BaseImage/index.tsx b/src/routes/AnnotationPage/Editor/BaseImage/index.tsx index da605f64..515a1a53 100644 --- a/src/routes/AnnotationPage/Editor/BaseImage/index.tsx +++ b/src/routes/AnnotationPage/Editor/BaseImage/index.tsx @@ -3,7 +3,7 @@ import { Image } from "react-konva"; import { useSelector } from "react-redux"; import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; -export default () => { +export default function BaseImage() { const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); const [image, setImage] = useState(); @@ -22,4 +22,4 @@ export default () => { height={currentAnnotationFile?.height} /> ); -}; +} diff --git a/src/routes/AnnotationPage/Editor/Hook/useElipseEvent.ts b/src/routes/AnnotationPage/Editor/Hook/useElipseEvent.ts deleted file mode 100644 index 61fc41fa..00000000 --- a/src/routes/AnnotationPage/Editor/Hook/useElipseEvent.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { LINE_STYLE } from "components/Annotation/Editor/const"; -import { EllipseSpec } from "components/Annotation/Editor/type"; -import { useDispatch, useSelector } from "react-redux"; -import { - changeCurrentDrawState, - createDrawObject, - setSelectedShape, - updateDrawObject, -} from "reduxes/annotation/action"; -import { - selectorCurrentDrawState, - selectorSelectedEllipse, -} from "reduxes/annotation/selector"; -import { - DrawObject, - DrawState, - DrawType, - EditorEventPayload, -} from "reduxes/annotation/type"; -export const createEllipse = (position: { - x: number; - y: number; -}): DrawObject => { - const id = `ELLIPSE-${Math.floor(Math.random() * 100000)}`; - const shape: EllipseSpec = { - x: position.x, - y: position.y, - radiusX: 1, - radiusY: 1, - rotation: 0, - id, - label: { label: id }, - cssStyle: { ...LINE_STYLE }, - }; - return { type: DrawType.ELLIPSE, data: shape }; -}; -const useEllipseEvent = () => { - const dispatch = useDispatch(); - const currentDrawState = useSelector(selectorCurrentDrawState); - const selectedShape = useSelector(selectorSelectedEllipse); - const handleMouseDown = (e: EditorEventPayload) => { - if ( - currentDrawState === DrawState.FREE || - currentDrawState === DrawState.SELECTING - ) { - const position = e.eventObject.currentTarget.getRelativePointerPosition(); - let drawObject = createEllipse(position); - dispatch(createDrawObject({ drawObject })); - dispatch(setSelectedShape({ selectedDrawObjectId: drawObject.data.id })); - dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); - } - }; - - const handleMouseMove = (e: EditorEventPayload) => { - if (currentDrawState === DrawState.DRAWING) { - const position = e.eventObject.currentTarget.getRelativePointerPosition(); - - if (selectedShape) { - const newShape: EllipseSpec = { - ...selectedShape, - x: (position.x + (selectedShape.x - selectedShape.radiusX)) / 2, - y: (position.y + (selectedShape.y - selectedShape.radiusY)) / 2, - radiusX: - (position.x - (selectedShape.x - selectedShape.radiusX)) / 2.0, - radiusY: - (position.y - (selectedShape.y - selectedShape.radiusY)) / 2.0, - }; - dispatch(updateDrawObject({ data: newShape })); - } - } - }; - const handleMouseUp = () => { - if (currentDrawState === DrawState.DRAWING) { - dispatch(changeCurrentDrawState({ drawState: DrawState.SELECTING })); - } - }; - return { handleMouseDown, handleMouseMove, handleMouseUp }; -}; - -export default useEllipseEvent; diff --git a/src/routes/AnnotationPage/Editor/Hook/usePolygonEvent.ts b/src/routes/AnnotationPage/Editor/Hook/usePolygonEvent.ts deleted file mode 100644 index c6cddb49..00000000 --- a/src/routes/AnnotationPage/Editor/Hook/usePolygonEvent.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { LINE_STYLE } from "components/Annotation/Editor/const"; -import { PolygonSpec } from "components/Annotation/Editor/type"; -import { Vector2d } from "konva/lib/types"; -import { useDispatch, useSelector } from "react-redux"; -import { - changeCurrentDrawState, - createDrawObject, - setSelectedShape, - updateDrawObject, -} from "reduxes/annotation/action"; -import { - selectorCurrentDrawState, - selectorCurrentDrawType, - selectorSelectedPolygonOrLineStrip, -} from "reduxes/annotation/selector"; -import { - DrawObject, - DrawState, - DrawType, - EditorEventPayload, -} from "reduxes/annotation/type"; - -export const createPolygon = ( - position: Vector2d, - isLineStrip?: boolean -): DrawObject => { - const id = `POLYGON-${Math.floor(Math.random() * 100000)}`; - const polygon: PolygonSpec = { - id: id, - points: [position], - polygonState: { isFinished: false, isLineStrip }, - label: { label: id }, - cssStyle: { ...LINE_STYLE }, - }; - return { type: DrawType.POLYGON, data: polygon }; -}; -const buildPolygon = (polygon: PolygonSpec, position: Vector2d) => { - const { x, y } = polygon.points[0]; - const a = x - position.x; - const b = y - position.y; - - polygon.points = [...polygon.points, position]; - return { data: { ...polygon } }; -}; -const usePolygonEvent = () => { - const dispatch = useDispatch(); - const polygon = useSelector(selectorSelectedPolygonOrLineStrip); - const currentDrawState = useSelector(selectorCurrentDrawState); - const drawType = useSelector(selectorCurrentDrawType); - - const handleMouseDown = (e: EditorEventPayload) => { - const position = e.eventObject.currentTarget.getRelativePointerPosition(); - if (!position) return; - if (polygon === null || polygon.polygonState.isFinished) { - if ( - currentDrawState === DrawState.FREE || - currentDrawState === DrawState.SELECTING - ) { - let drawObject = createPolygon( - position, - drawType === DrawType.LINE_STRIP - ); - dispatch(createDrawObject({ drawObject })); - dispatch( - setSelectedShape({ selectedDrawObjectId: drawObject.data.id }) - ); - dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); - } - } else { - const polygonAfterBuild = buildPolygon(polygon as PolygonSpec, position); - dispatch(updateDrawObject(polygonAfterBuild)); - } - }; - - const handleMouseMove = (e: EditorEventPayload) => { - if (currentDrawState === DrawState.DRAWING) { - const position = e.eventObject.currentTarget.getRelativePointerPosition(); - if (!position) return; - if (polygon) { - const polygonSpec = polygon as PolygonSpec; - polygonSpec.polygonState.mousePosition = position; - dispatch(updateDrawObject({ data: polygonSpec })); - } - } - }; - - return { handleMouseDown, handleMouseMove }; -}; - -export default usePolygonEvent; diff --git a/src/routes/AnnotationPage/Editor/Hook/useRectangleEvent.ts b/src/routes/AnnotationPage/Editor/Hook/useRectangleEvent.ts deleted file mode 100644 index 32e2c465..00000000 --- a/src/routes/AnnotationPage/Editor/Hook/useRectangleEvent.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { LINE_STYLE } from "components/Annotation/Editor/const"; -import { RectangleSpec } from "components/Annotation/Editor/type"; -import { useDispatch, useSelector } from "react-redux"; -import { - changeCurrentDrawState, - createDrawObject, - setSelectedShape, - updateDrawObject, -} from "reduxes/annotation/action"; -import { - selectorCurrentDrawState, - selectorSelectedRectangle, -} from "reduxes/annotation/selector"; -import { - DrawObject, - DrawState, - DrawType, - EditorEventPayload, -} from "reduxes/annotation/type"; -export const createRectangle = (position: { - x: number; - y: number; -}): DrawObject => { - const id = `RECTANGLE-${Math.floor(Math.random() * 100000)}`; - const rect: RectangleSpec = { - x: position.x, - y: position.y, - width: 10, - height: 10, - id: id, - rotation: 0, - label: { label: id }, - cssStyle: { ...LINE_STYLE }, - }; - return { type: DrawType.RECTANGLE, data: rect }; -}; -const useRectangleEvent = () => { - const dispatch = useDispatch(); - const currentDrawState = useSelector(selectorCurrentDrawState); - const selectedRectangle = useSelector(selectorSelectedRectangle); - const handleMouseDown = (e: EditorEventPayload) => { - if ( - currentDrawState === DrawState.FREE || - currentDrawState === DrawState.SELECTING - ) { - const position = e.eventObject.currentTarget.getRelativePointerPosition(); - let drawObject = createRectangle(position); - dispatch(createDrawObject({ drawObject })); - dispatch(setSelectedShape({ selectedDrawObjectId: drawObject.data.id })); - dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); - } - }; - - const handleMouseMove = (e: EditorEventPayload) => { - if (currentDrawState === DrawState.DRAWING) { - const position = e.eventObject.currentTarget.getRelativePointerPosition(); - - if (selectedRectangle) { - const rectangle = selectedRectangle; - const newWidth = position.x - rectangle.x; - const newHeight = position.y - rectangle.y; - rectangle.width = newWidth; - rectangle.height = newHeight; - dispatch(updateDrawObject({ data: rectangle })); - } - } - }; - const handleMouseUp = () => { - if (currentDrawState === DrawState.DRAWING) { - dispatch(changeCurrentDrawState({ drawState: DrawState.SELECTING })); - } - }; - return { handleMouseDown, handleMouseMove, handleMouseUp }; -}; - -export default useRectangleEvent; diff --git a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx index ec617fda..7b54e7fe 100644 --- a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx @@ -1,7 +1,6 @@ import { KonvaEventObject } from "konva/lib/Node"; import { useEffect, useRef, useState } from "react"; import { Layer, Rect } from "react-konva"; - import Konva from "konva"; import { useDispatch, useSelector } from "react-redux"; import { setDetectedArea } from "reduxes/annotation/action"; @@ -11,7 +10,7 @@ import { convertStrokeColorToFillColor } from "routes/AnnotationPage/LabelAnnota import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; import { Vector2d } from "konva/lib/types"; -const DetectedRectangleDrawLayer = () => { +function DetectedRectangleDrawLayer() { const dispatch = useDispatch(); const refDetectedArea = useRef(null); const [localDetectedArea, setLocalDetectedArea] = @@ -19,11 +18,6 @@ const DetectedRectangleDrawLayer = () => { const mouseUpOutLayerPosition = useSelector(selectorMouseUpOutLayerPosition); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); - useEffect(() => { - if (mouseUpOutLayerPosition) { - mouseupHandler(); - } - }, [mouseUpOutLayerPosition]); const mousedownHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (position) { @@ -35,43 +29,39 @@ const DetectedRectangleDrawLayer = () => { }); } }; - const handleMouseOut = (e: KonvaEventObject) => { - const position = e.currentTarget.getRelativePointerPosition(); - updateMousePosition(position); - }; - const mousemoveHandler = (e: KonvaEventObject) => { - const position = e.currentTarget.getRelativePointerPosition(); - updateMousePosition(position); - }; + const updateMousePosition = (position: Vector2d) => { if (position && currentAnnotationFile) { if (localDetectedArea) { - if (position.x < 0) { - position.x = 0; + let { x, y } = position; + if (x < 0) { + x = 0; } - if ( - currentAnnotationFile.width && - position.x > currentAnnotationFile.width - ) { - position.x = currentAnnotationFile.width; + if (currentAnnotationFile.width && x > currentAnnotationFile.width) { + x = currentAnnotationFile.width; } - if (position.y < 0) { - position.y = 0; + if (y < 0) { + y = 0; } - if ( - currentAnnotationFile.height && - position.y > currentAnnotationFile.height - ) { - position.y = currentAnnotationFile.height; + if (currentAnnotationFile.height && y > currentAnnotationFile.height) { + y = currentAnnotationFile.height; } setLocalDetectedArea({ ...localDetectedArea, - width: position.x - localDetectedArea.x, - height: position.y - localDetectedArea.y, + width: x - localDetectedArea.x, + height: y - localDetectedArea.y, }); } } }; + const handleMouseOut = (e: KonvaEventObject) => { + const position = e.currentTarget.getRelativePointerPosition(); + updateMousePosition(position); + }; + const mousemoveHandler = (e: KonvaEventObject) => { + const position = e.currentTarget.getRelativePointerPosition(); + updateMousePosition(position); + }; const mouseupHandler = () => { if (localDetectedArea && refDetectedArea.current) { dispatch( @@ -92,7 +82,13 @@ const DetectedRectangleDrawLayer = () => { const { width, height } = currentAnnotationFile; return ; } + return null; }; + useEffect(() => { + if (mouseUpOutLayerPosition) { + mouseupHandler(); + } + }, [mouseUpOutLayerPosition]); return ( { )} ); -}; +} export default DetectedRectangleDrawLayer; diff --git a/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx index acee4e33..3cecde70 100644 --- a/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx @@ -19,17 +19,20 @@ const DrawLayer = () => { currentDrawState === DrawState.FREE || currentDrawState === DrawState.DRAWING ) { - if (drawType == DrawType.POLYGON || drawType == DrawType.LINE_STRIP) { + if (drawType === DrawType.POLYGON || drawType === DrawType.LINE_STRIP) { return ; - } else if (drawType === DrawType.RECTANGLE) { + } + if (drawType === DrawType.RECTANGLE) { return ; - } else if (drawType === DrawType.ELLIPSE) { + } + if (drawType === DrawType.ELLIPSE) { return ; - } else if (drawType === DrawType.DETECTED_RECTANGLE) { + } + if (drawType === DrawType.DETECTED_RECTANGLE) { return ; } } - return ; + return ; }; return render(); }; diff --git a/src/routes/AnnotationPage/Editor/Layer/DummyRect.tsx b/src/routes/AnnotationPage/Editor/Layer/DummyRect.tsx deleted file mode 100644 index 65b07c9d..00000000 --- a/src/routes/AnnotationPage/Editor/Layer/DummyRect.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Rect } from "react-konva"; -import { useSelector } from "react-redux"; -import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; - -const DummyRect = () => { - const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); - if (currentAnnotationFile) { - const { width, height } = currentAnnotationFile; - return ; - } - return <>; -}; -export default DummyRect; diff --git a/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx index 1071036e..fffd14d8 100644 --- a/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx @@ -1,4 +1,5 @@ import { LINE_STYLE } from "components/Annotation/Editor/const"; +import { createEllipse } from "components/Annotation/Editor/Shape/EllipseShape"; import { EllipseSpec } from "components/Annotation/Editor/type"; import Konva from "konva"; import { KonvaEventObject } from "konva/lib/Node"; @@ -10,19 +11,14 @@ import { createDrawObject } from "reduxes/annotation/action"; import { selectorMouseUpOutLayerPosition } from "reduxes/annotation/selector"; import { DrawType } from "reduxes/annotation/type"; import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; -import { createEllipse } from "../Hook/useElipseEvent"; -const EllipseDrawLayer = () => { +function EllipseDrawLayer() { const dispatch = useDispatch(); const [startPoint, setStartPoint] = useState(null); const [endPoint, setEndPoint] = useState(null); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); const mouseUpOutLayerPosition = useSelector(selectorMouseUpOutLayerPosition); - useEffect(() => { - if (mouseUpOutLayerPosition) { - handleMouseUp(); - } - }, [mouseUpOutLayerPosition]); + const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (!position || !startPoint) return; @@ -68,7 +64,13 @@ const EllipseDrawLayer = () => { const { width, height } = currentAnnotationFile; return ; } + return null; }; + useEffect(() => { + if (mouseUpOutLayerPosition) { + handleMouseUp(); + } + }, [mouseUpOutLayerPosition]); return ( { )} ); -}; +} export default EllipseDrawLayer; diff --git a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx index 96ddf05d..f29a65a9 100644 --- a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx @@ -1,5 +1,5 @@ import { KonvaEventObject } from "konva/lib/Node"; -import { KeyboardEvent, useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { Circle, Group, Layer, Line, Rect } from "react-konva"; import { @@ -24,14 +24,14 @@ import { import { DrawState, DrawType } from "reduxes/annotation/type"; import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; import { convertStrokeColorToFillColor } from "routes/AnnotationPage/LabelAnnotation/ClassLabel"; -import { createPolygon } from "../Hook/usePolygonEvent"; +import { createPolygon } from "components/Annotation/Editor/Shape/Polygon"; -const PolygonDrawLayer = () => { +function PolygonDrawLayer() { const dispatch = useDispatch(); const isLineStrip = useSelector(selectorCurrentDrawType) === DrawType.LINE_STRIP; const [flattenedPoints, setFlattenedPoints] = useState([]); - const [lineStyle, setLineStyle] = useState({ + const [lineStyle] = useState({ fill: convertStrokeColorToFillColor("#affaaa"), stroke: "#affaaa", strokeWidth: STROKE_WIDTH_LINE, @@ -46,23 +46,13 @@ const PolygonDrawLayer = () => { const mouseDownOutLayerPosition = useSelector( selectorMouseDownOutLayerPosition ); - useEffect(() => { - if (mouseDownOutLayerPosition) { - mousedownHandler(); - } - }, [mouseDownOutLayerPosition]); - useEffect(() => { - const flatPoints: number[] = points - .concat(isFinished || !mousePosition ? [] : mousePosition) - .reduce((a, b) => a.concat([b.x, b.y]), [] as number[]); - setFlattenedPoints(flatPoints); - }, [points, isFinished, mousePosition]); - useEffect(() => { - if (keyDownInEditor === "Escape") { - resetDraw(); - } - }, [keyDownInEditor]); - + const resetDraw = () => { + setPoints([]); + setIsFinished(false); + setMousePosition(null); + setMouseOverPoint(false); + setFlattenedPoints([]); + }; const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (!position) return; @@ -98,13 +88,7 @@ const PolygonDrawLayer = () => { } e.cancelBubble = true; }; - const resetDraw = () => { - setPoints([]); - setIsFinished(false); - setMousePosition(null); - setMouseOverPoint(false); - setFlattenedPoints([]); - }; + const handleMouseOverStartPoint = (e: KonvaEventObject) => { if (isFinished || points.length < 3) return; e.target.scale({ x: 2, y: 2 }); @@ -135,8 +119,8 @@ const PolygonDrawLayer = () => { e.target.scale({ x: 2, y: 2 }); } }; - const renderPoints = () => { - return points.map((point, index) => { + const renderPoints = () => + points.map((point, index) => { const x = point.x - CORNER_RADIUS / 2; const y = point.y - CORNER_RADIUS / 2; let startPointAttr; @@ -170,7 +154,6 @@ const PolygonDrawLayer = () => { return ( { /> ); }); - }; const currentDrawState = useSelector(selectorCurrentDrawState); const mousedownHandler = (e?: KonvaEventObject) => { @@ -215,13 +197,24 @@ const PolygonDrawLayer = () => { const { width, height } = currentAnnotationFile; return ; } + return null; }; - const keyDownHandler = (e: KeyboardEvent) => { - console.log("e", e); - if (e.key === "Escape") { + useEffect(() => { + if (mouseDownOutLayerPosition) { + mousedownHandler(); + } + }, [mouseDownOutLayerPosition]); + useEffect(() => { + const flatPoints: number[] = points + .concat(isFinished || !mousePosition ? [] : mousePosition) + .reduce((a, b) => a.concat([b.x, b.y]), [] as number[]); + setFlattenedPoints(flatPoints); + }, [points, isFinished, mousePosition]); + useEffect(() => { + if (keyDownInEditor === "Escape") { resetDraw(); } - }; + }, [keyDownInEditor]); return ( { ); -}; +} export default PolygonDrawLayer; diff --git a/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx index 3e8cb895..49110dfb 100644 --- a/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx @@ -1,4 +1,5 @@ import { LINE_STYLE } from "components/Annotation/Editor/const"; +import { createRectangle } from "components/Annotation/Editor/Shape/Rectangle"; import { RectangleSpec } from "components/Annotation/Editor/type"; import Konva from "konva"; import { KonvaEventObject } from "konva/lib/Node"; @@ -10,46 +11,33 @@ import { createDrawObject } from "reduxes/annotation/action"; import { selectorMouseUpOutLayerPosition } from "reduxes/annotation/selector"; import { DrawType } from "reduxes/annotation/type"; import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; -import { createRectangle } from "../Hook/useRectangleEvent"; -const RectangleDrawLayer = () => { +function RectangleDrawLayer() { const dispatch = useDispatch(); const [startPoint, setStartPoint] = useState(null); const [endPoint, setEndPoint] = useState(null); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); const mouseUpOutLayerPosition = useSelector(selectorMouseUpOutLayerPosition); - useEffect(() => { - if (mouseUpOutLayerPosition) { - handleMouseUp(); - } - }, [mouseUpOutLayerPosition]); - const mousemoveHandler = (e: KonvaEventObject) => { - const position = e.currentTarget.getRelativePointerPosition(); - updateMousePosition(position); - }; const updateMousePosition = (position: Vector2d) => { if (!position || !startPoint) return; - if (position.x < 0) { - position.x = 0; + let { x, y } = position; + if (x < 0) { + x = 0; } if ( currentAnnotationFile?.width && position.x > currentAnnotationFile?.width ) { - position.x = currentAnnotationFile?.width; + x = currentAnnotationFile?.width; } - if (position.y < 0) { - position.y = 0; + if (y < 0) { + y = 0; } - if ( - currentAnnotationFile?.height && - position.y > currentAnnotationFile?.height - ) { - position.y = currentAnnotationFile?.height; + if (currentAnnotationFile?.height && y > currentAnnotationFile?.height) { + y = currentAnnotationFile?.height; } - - setEndPoint({ ...position }); + setEndPoint({ x, y }); }; const mousedownHandler = (e: KonvaEventObject) => { @@ -98,8 +86,18 @@ const RectangleDrawLayer = () => { const { width, height } = currentAnnotationFile; return ; } + return null; }; + useEffect(() => { + if (mouseUpOutLayerPosition) { + handleMouseUp(); + } + }, [mouseUpOutLayerPosition]); + const mousemoveHandler = (e: KonvaEventObject) => { + const position = e.currentTarget.getRelativePointerPosition(); + updateMousePosition(position); + }; return ( { )} ); -}; +} export default RectangleDrawLayer; diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index 23258da8..9ffe536e 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -59,9 +59,8 @@ import { const TOOLTIP_WIDTH = 200; const TOOLTIP_HEIGHT = 40; -const Editor = () => { +function Editor() { const dispatch = useDispatch(); - const imageRef = useRef(null); const layer = useRef(null); const group = useRef(null); const stageRef = useRef(null); @@ -91,10 +90,10 @@ const Editor = () => { if (widthRatio < 1 || heightRatio < 1) { if (widthRatio < heightRatio) { newWidth = MAX_WIDTH_IMAGE_IN_EDITOR; - newHeight = newHeight * widthRatio; + newHeight *= widthRatio; } else { newHeight = MAX_HEIGHT_IMAGE_IN_EDITOR; - newWidth = newWidth * heightRatio; + newWidth *= heightRatio; } } return { @@ -131,12 +130,13 @@ const Editor = () => { linestrips: linestripsById, }; }, [drawObjectById]); - const polygons = useMemo(() => { - return { + const polygons = useMemo( + () => ({ ...drawObjects.polygons, ...drawObjects.linestrips, - }; - }, [drawObjects.polygons]); + }), + [drawObjects.polygons] + ); const refZoom = stageRef; const wheelHandler = (e: KonvaEventObject) => { @@ -159,8 +159,8 @@ const Editor = () => { ); if (currentAnnotationFile) { const { height, width } = currentAnnotationFile; - const zoom = getFitScaleEditor(width, height); - if (newScale <= zoom) return; + const fitScaleEditor = getFitScaleEditor(width, height); + if (newScale <= fitScaleEditor) return; } dispatch(changeZoom({ zoom: { zoom: newScale, position: newPosition } })); } @@ -180,9 +180,9 @@ const Editor = () => { } }; const keyDownHandler = (e: KeyboardEvent) => { - if (e.ctrlKey && e.shiftKey && e.key == "Z") { + if (e.ctrlKey && e.shiftKey && e.key === "Z") { dispatch(redoDrawObject()); - } else if (e.ctrlKey && e.key == "z") { + } else if (e.ctrlKey && e.key === "z") { dispatch(undoDrawObject()); } else if (e.key === " ") { setKeyDown(e.key); @@ -192,7 +192,7 @@ const Editor = () => { } else if (e.key === "Delete") { if (selectedDrawObjectId) { dispatch(deleteDrawObject({ drawObjectId: selectedDrawObjectId })); - if (toolTipLayer.current?.attrs["id"] === selectedDrawObjectId) { + if (toolTipLayer.current?.attrs.id === selectedDrawObjectId) { toolTipLayer.current.hide(); } } @@ -224,10 +224,8 @@ const Editor = () => { useEffect(() => { if (keyDown === " ") { dispatch(setIsDraggingViewpor({ isDraggingViewport: true })); - } else { - if (isDraggingViewport === true) { - dispatch(setIsDraggingViewpor({ isDraggingViewport: false })); - } + } else if (isDraggingViewport === true) { + dispatch(setIsDraggingViewpor({ isDraggingViewport: false })); } }, [keyDown]); const mouseOverBoundDivHandler = () => { @@ -279,23 +277,23 @@ const Editor = () => { toolTipLayer.current.hide(); } }; - const dragBoundFunc = (pos: Vector2d) => { - if (!layer.current || !imageRef.current) { - return { x: 0, y: 0 }; - } - let { x, y } = pos; - const sw = layer.current.width(); - const sh = layer.current.height(); - const box = imageRef.current.getClientRect(); - const minMaxX = [0, box.width]; - const minMaxY = [0, box.height]; + // const dragBoundFunc = (pos: Vector2d) => { + // if (!layer.current || !imageRef.current) { + // return { x: 0, y: 0 }; + // } + // let { x, y } = pos; + // const sw = layer.current.width(); + // const sh = layer.current.height(); + // const box = imageRef.current.getClientRect(); + // const minMaxX = [0, box.width]; + // const minMaxY = [0, box.height]; - if (minMaxY[0] + y < 0) y = -1 * minMaxY[0]; - if (minMaxX[0] + x < 0) x = -1 * minMaxX[0]; - if (minMaxY[1] + y > sh) y = sh - minMaxY[1]; - if (minMaxX[1] + x > sw) x = sw - minMaxX[1]; - return { x, y }; - }; + // if (minMaxY[0] + y < 0) y = -1 * minMaxY[0]; + // if (minMaxX[0] + x < 0) x = -1 * minMaxX[0]; + // if (minMaxY[1] + y > sh) y = sh - minMaxY[1]; + // if (minMaxX[1] + x > sw) x = sw - minMaxX[1]; + // return { x, y }; + // }; useEffect(() => { if (zoom.zoom === 1) { group.current?.setPosition({ x: 0, y: 0 }); @@ -339,147 +337,138 @@ const Editor = () => { ); - } else { - return ( + } + return ( + - - - {({ store }) => ( - - - - - - - {Object.keys(drawObjects.rectangles).map((key) => { - return ( - - onMouseOverToolTipHandler(e, key) - } - onMouseOutHandler={onMouseOutToolTipHandler} - /> - ); - })} - {Object.keys(drawObjects.ellipses).map((key) => { - return ( - - onMouseOverToolTipHandler(e, key) - } - onMouseOutHandler={onMouseOutToolTipHandler} - /> - ); - })} - {Object.keys(polygons).map((key) => { - return ( - { - onMouseOverToolTipHandler(e, key); - }} - onMouseOutHandler={onMouseOutToolTipHandler} - /> - ); - })} - - - + {({ store }) => ( + + + + + + - - - - - - - - - )} - - + {Object.keys(drawObjects.rectangles).map((key) => ( + + onMouseOverToolTipHandler(e, key) + } + onMouseOutHandler={onMouseOutToolTipHandler} + /> + ))} + {Object.keys(drawObjects.ellipses).map((key) => ( + + onMouseOverToolTipHandler(e, key) + } + onMouseOutHandler={onMouseOutToolTipHandler} + /> + ))} + {Object.keys(polygons).map((key) => ( + { + onMouseOverToolTipHandler(e, key); + }} + onMouseOutHandler={onMouseOutToolTipHandler} + /> + ))} + + + + + + + + + + + + )} + - ); - } + + ); }; return ( - <> - - {renderContent()} - - + + {renderContent()} + ); -}; +} export default Editor; diff --git a/src/routes/AnnotationPage/ImagePreview/ImagePreviewBadge.tsx b/src/routes/AnnotationPage/ImagePreview/ImagePreviewBadge.tsx index 2e7fc54c..ae80af9c 100644 --- a/src/routes/AnnotationPage/ImagePreview/ImagePreviewBadge.tsx +++ b/src/routes/AnnotationPage/ImagePreview/ImagePreviewBadge.tsx @@ -1,9 +1,8 @@ -import { Badge } from "@mui/material"; -import { Box } from "@mui/system"; +import { Badge, Box } from "@mui/material"; import { useSelector } from "react-redux"; import { selectorCurrentAnnotationFiles } from "reduxes/annotationProject/selector"; -const ImagePreviewBadge = function ({ +function ImagePreviewBadge({ filename, children, }: { @@ -11,9 +10,7 @@ const ImagePreviewBadge = function ({ children: React.ReactNode | React.ReactNode[]; }) { const currentAnnotationFiles = useSelector(selectorCurrentAnnotationFiles); - const renderCBadgeContent = () => { - return AI; - }; + const renderCBadgeContent = () => AI; if (currentAnnotationFiles) { const annotationFile = currentAnnotationFiles.items.find( (t) => t.filename === filename @@ -29,5 +26,5 @@ const ImagePreviewBadge = function ({ } } return <> {children}; -}; +} export default ImagePreviewBadge; diff --git a/src/routes/AnnotationPage/ImagePreview/index.tsx b/src/routes/AnnotationPage/ImagePreview/index.tsx index 0ae7a551..181959a1 100644 --- a/src/routes/AnnotationPage/ImagePreview/index.tsx +++ b/src/routes/AnnotationPage/ImagePreview/index.tsx @@ -1,11 +1,4 @@ -import { - Badge, - Box, - List, - ListItem, - Skeleton, - Typography, -} from "@mui/material"; +import { Box, List, ListItem, Skeleton, Typography } from "@mui/material"; import useConfirmDialog from "hooks/useConfirmDialog"; import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -25,16 +18,7 @@ import { import { selectorCurrentAnnotationFiles } from "reduxes/annotationProject/selector"; import ImagePreviewBadge from "./ImagePreviewBadge"; -const createFile = async (imageName: string, url: string) => { - let response = await fetch(url); - let data = await response.blob(); - let metadata = { - type: "image/jpeg", - }; - let file = new File([data], imageName, metadata); - return file; -}; -const ImagePreview = function () { +function ImagePreview() { const dispatch = useDispatch(); // const annotationManagerImages = useSelector(selectorAnnotationManagerImages); @@ -183,28 +167,12 @@ const ImagePreview = function () { ), - negativeText: "Skip", - positiveText: "Save", + negativeText: "Cancel", + positiveText: "Dont't Save", onClickNegative: () => { - if (currentPreviewImageName) { - dispatch( - resetCurrentStateDrawObject({ - drawObjectById: idDrawObjectByImageName[imageName], - }) - ); - dispatch(requestChangePreviewImage({ imageName })); - closeConfirmDialog(); - } + closeConfirmDialog(); }, onClickPositive: () => { - if (currentPreviewImageName) { - dispatch( - saveAnnotationStateManager({ - imageName: currentPreviewImageName, - drawObjectById, - }) - ); - } dispatch( resetCurrentStateDrawObject({ drawObjectById: idDrawObjectByImageName[imageName], @@ -246,52 +214,50 @@ const ImagePreview = function () { padding: 0, }} > - {currentAnnotationFiles.items.map((item) => { - return ( - - + {currentAnnotationFiles.items.map((item) => ( + + + { + handleSelectPreview(item.filename); + }} + > { - handleSelectPreview(item.filename); - }} + display="flex" + justifyContent="center" + height="100%" + alignItems="center" > - - - {item.filename} - - + {item.filename} + - - - ); - })} + + + + ))} ); }; return <>{renderContent()}; -}; +} export default ImagePreview; diff --git a/src/routes/AnnotationPage/LabelAnnotation/ClassItem.tsx b/src/routes/AnnotationPage/LabelAnnotation/ClassItem.tsx index 35a1512b..f0854465 100644 --- a/src/routes/AnnotationPage/LabelAnnotation/ClassItem.tsx +++ b/src/routes/AnnotationPage/LabelAnnotation/ClassItem.tsx @@ -29,18 +29,21 @@ import { } from "reduxes/annotation/selector"; import { DrawType } from "reduxes/annotation/type"; import { selectorLabelClassPropertiesByLabelClass } from "reduxes/annotationmanager/selecetor"; -import { getBackgroundColor } from "."; import ClassLabel from "./ClassLabel"; +import { getBackgroundColor } from "./ClassManageModal/useListClassView"; import { ClassItemProps } from "./type"; const renderIcon = (drawType: DrawType) => { if (drawType === DrawType.RECTANGLE) { return ; - } else if (drawType === DrawType.LINE_STRIP) { + } + if (drawType === DrawType.LINE_STRIP) { return ; - } else if (drawType === DrawType.ELLIPSE) { + } + if (drawType === DrawType.ELLIPSE) { return ; - } else if (drawType === DrawType.POLYGON) { + } + if (drawType === DrawType.POLYGON) { return ; } return ; @@ -68,19 +71,19 @@ function ClassItem({ style, id }: ClassItemProps) { const drawObjectState = useSelector(selectorDrawObjectState(id)); const isDrawObjectHidden = useMemo( - () => drawObjectState && drawObjectState.isHidden == true, + () => drawObjectState && drawObjectState.isHidden === true, [drawObjectState] ); const isDrawObjectLock = useMemo( - () => drawObjectState && drawObjectState.isLock == true, + () => drawObjectState && drawObjectState.isLock === true, [drawObjectState] ); const labelClassProperties = labelClassPropertiesByLabelClass[drawObject.data?.label?.label]; - const handleClickLock = (id: string) => { + const handleClickLock = (drawObjectId: string) => { dispatch( setLockDrawObject({ - drawObjectId: id, + drawObjectId, isLock: !isDrawObjectLock, }) ); @@ -93,14 +96,14 @@ function ClassItem({ style, id }: ClassItemProps) { }) ); }; - const renderSecondaryAction = useCallback(() => { - return ( + const renderSecondaryAction = useCallback( + () => ( - {isDrawObjectHidden == true ? ( + {isDrawObjectHidden === true ? ( ) : ( @@ -114,16 +117,18 @@ function ClassItem({ style, id }: ClassItemProps) { {isDrawObjectHidden === true ? : } - ); - }, [isDrawObjectHidden]); - const renderCss = useCallback(() => { - return { + ), + [isDrawObjectHidden] + ); + const renderCss = useCallback( + () => ({ border: isSelect ? "1px solid" : "", cursor: "pointer", - }; - }, [isSelect]); - const renderListAvatar = useCallback(() => { - return ( + }), + [isSelect] + ); + const renderListAvatar = useCallback( + () => ( - ); - }, [labelClassProperties, drawObject]); + ), + [labelClassProperties, drawObject] + ); - const renderContent = () => { - return ( - handleSelect()} - secondaryAction={renderSecondaryAction()} - sx={renderCss()} - > - {renderListAvatar()} - - - - - ); - }; + const renderContent = () => ( + handleSelect()} + secondaryAction={renderSecondaryAction()} + sx={renderCss()} + > + {renderListAvatar()} + + + + + ); return ( (); export const convertStrokeColorToFillColor = (color: string) => { if (color && color.length > 1) { - return color + "33"; + return `${color}33`; } return ""; }; -const ClassLabel = function ({ drawObject }: ClassLabelProps) { +function ClassLabel({ drawObject }: ClassLabelProps) { const dispatch = useDispatch(); const labelClassPropertiesByLabelClass = useSelector( selectorLabelClassPropertiesByLabelClass ); const listLabelClassProperties: LabelClassPropertiesOptionType[] = - React.useMemo(() => { - return Object.entries(labelClassPropertiesByLabelClass).map( - ([key, value]) => { - return { - inputValue: "", - color: value.cssStyle.stroke, - label: value.label, - }; - } - ) as LabelClassPropertiesOptionType[]; - }, [labelClassPropertiesByLabelClass]); + React.useMemo( + () => + Object.entries(labelClassPropertiesByLabelClass).map(([, value]) => ({ + inputValue: "", + color: value.cssStyle.stroke, + label: value.label, + })) as LabelClassPropertiesOptionType[], + [labelClassPropertiesByLabelClass] + ); const handleChangeClassLabel = (label: string) => { const properties = labelClassPropertiesByLabelClass[label]; @@ -60,75 +58,69 @@ const ClassLabel = function ({ drawObject }: ClassLabelProps) { }, [labelClassPropertiesByLabelClass, drawObject]); return ( - - { - if (typeof newValue === "string") { - setTimeout(() => { - dispatch( - setDialogClassManageModal({ - isOpen: true, - className: newValue, - classManageModalType: "CREATE", - }) - ); - }); - } else if (newValue && newValue.inputValue) { + { + if (typeof newValue === "string") { + setTimeout(() => { dispatch( setDialogClassManageModal({ isOpen: true, - className: newValue.inputValue, + className: newValue, classManageModalType: "CREATE", }) ); - } else { - if (newValue) { - handleChangeClassLabel(newValue.label.label); - } - } - }} - filterOptions={(options, params) => { - const filtered = filter(options, params); + }); + } else if (newValue && newValue.inputValue) { + dispatch( + setDialogClassManageModal({ + isOpen: true, + className: newValue.inputValue, + classManageModalType: "CREATE", + }) + ); + } else if (newValue) { + handleChangeClassLabel(newValue.label.label); + } + }} + filterOptions={(options, params) => { + const filtered = filter(options, params); - if (params.inputValue !== "") { - filtered.push({ - inputValue: params.inputValue, - label: { label: `Add "${params.inputValue}"` }, - color: "", - }); - } + if (params.inputValue !== "") { + filtered.push({ + inputValue: params.inputValue, + label: { label: `Add "${params.inputValue}"` }, + color: "", + }); + } - return filtered; - }} - options={listLabelClassProperties} - getOptionLabel={(option) => { - if (!option || !option.label) { - return ""; - } - if (typeof option === "string") { - return option; - } - if (option.inputValue) { - return option.inputValue; - } - return option.label.label; - }} - selectOnFocus - clearOnBlur - handleHomeEndKeys - renderOption={(props, option) => ( -
  • {option.label.label}
  • - )} - sx={{ maxWidth: 180 }} - freeSolo - renderInput={(params) => ( - - )} - /> -
    + return filtered; + }} + options={listLabelClassProperties} + getOptionLabel={(option) => { + if (!option || !option.label) { + return ""; + } + if (typeof option === "string") { + return option; + } + if (option.inputValue) { + return option.inputValue; + } + return option.label.label; + }} + selectOnFocus + clearOnBlur + handleHomeEndKeys + renderOption={(props, option) =>
  • {option.label.label}
  • } + sx={{ maxWidth: 180 }} + freeSolo + renderInput={(params) => ( + + )} + /> ); -}; +} interface LabelClassPropertiesOptionType { inputValue?: string; color: string; diff --git a/src/routes/AnnotationPage/LabelAnnotation/ClassLabel/type.ts b/src/routes/AnnotationPage/LabelAnnotation/ClassLabel/type.ts index 2d59cbdc..efbe5607 100644 --- a/src/routes/AnnotationPage/LabelAnnotation/ClassLabel/type.ts +++ b/src/routes/AnnotationPage/LabelAnnotation/ClassLabel/type.ts @@ -1,4 +1,4 @@ -import { Label, LabelAttribute } from "components/Annotation/Editor/type"; +import { LabelAttribute } from "components/Annotation/Editor/type"; import { DrawObject } from "reduxes/annotation/type"; export interface ClassLabelProps { diff --git a/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/FieldArrayAttribute.tsx b/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/FieldArrayAttribute.tsx index 033ae595..1321da8c 100644 --- a/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/FieldArrayAttribute.tsx +++ b/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/FieldArrayAttribute.tsx @@ -29,39 +29,37 @@ export default function FieldArrayAttribute({ return ( - {fields.map((item, index) => { - return ( - - - - handleRemoveAttribute(index)}> - - - - ); - })} + {fields.map((item, index) => ( + + + + handleRemoveAttribute(index)}> + + + + ))} { - if (!!dialogClassManageModal.isOpen) { + if (dialogClassManageModal.isOpen) { if (dialogClassManageModal.classManageModalType === "VIEW") { return listClassView; - } else if (dialogClassManageModal.classManageModalType === "EDIT") { + } + if (dialogClassManageModal.classManageModalType === "EDIT") { return classManageEditor; - } else if (dialogClassManageModal.classManageModalType === "CREATE") { + } + if (dialogClassManageModal.classManageModalType === "CREATE") { return classManageEditor; } } diff --git a/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useClassManageEditor.tsx b/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useClassManageEditor.tsx index 488b5355..e6a83486 100644 --- a/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useClassManageEditor.tsx +++ b/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useClassManageEditor.tsx @@ -9,7 +9,6 @@ import { useForm } from "react-hook-form"; import { useDispatch, useSelector } from "react-redux"; import { toast } from "react-toastify"; import { - addNewClassLabel, editClassLabel, saveRemoteNewClassLabel, setDialogClassManageModal, @@ -24,12 +23,12 @@ import { ClassManageDialogProps } from "./type"; export const convertStrokeColorToFillColor = (color: string) => { if (color && color.length > 1) { - return color + "33"; + return `${color}33`; } return ""; }; -const useClassManageEditor = function (): ClassManageDialogProps { +function useClassManageEditor(): ClassManageDialogProps { const dispatch = useDispatch(); const labelClassPropertiesByLabelClass = useSelector( selectorLabelClassPropertiesByLabelClass @@ -71,24 +70,19 @@ const useClassManageEditor = function (): ClassManageDialogProps { setValue("color", newColor); }; - const renderPickColor = () => { - return ( - - - - ); - }; + const renderPickColor = () => ( + + + + ); const handleClickShowPickColor = ( event: React.MouseEvent ) => { @@ -156,7 +150,7 @@ const useClassManageEditor = function (): ClassManageDialogProps { }; return { title: - dialogClassManageModal.classManageModalType == "EDIT" + dialogClassManageModal.classManageModalType === "EDIT" ? "Edit class" : "Add new class", content: ( @@ -225,5 +219,5 @@ const useClassManageEditor = function (): ClassManageDialogProps { ), }; -}; +} export default useClassManageEditor; diff --git a/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useListClassView.tsx b/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useListClassView.tsx index b53017f9..9596eda3 100644 --- a/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useListClassView.tsx +++ b/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useListClassView.tsx @@ -26,9 +26,41 @@ import { setDialogClassManageModal, } from "reduxes/annotationmanager/action"; import { selectorLabelClassPropertiesByLabelClass } from "reduxes/annotationmanager/selecetor"; -import { getBackgroundColor } from ".."; +import { convertStrokeColorToFillColor } from "../ClassLabel"; import { ClassManageDialogProps } from "./type"; -const useListClassView = function (): ClassManageDialogProps { + +export const hashCode = (str: string) => { + let hash = 0; + for (let i = 0; i < str.length; i += 1) { + // eslint-disable-next-line no-bitwise + hash = str.charCodeAt(i) + ((hash << 5) - hash); + } + return hash; +}; + +export const intToRGB = (i: number) => { + // eslint-disable-next-line no-bitwise + const c = (i & 0x00ffffff).toString(16).toUpperCase(); + return "00000".substring(0, 6 - c.length) + c; +}; + +export const getBackgroundColor = ( + labelClassProperties: LabelClassProperties +) => { + if (labelClassProperties) { + if (labelClassProperties?.cssStyle?.stroke) { + return labelClassProperties.cssStyle.stroke; + } + if (labelClassProperties.label?.label) { + return convertStrokeColorToFillColor( + `#${intToRGB(hashCode(labelClassProperties.label.label))}` + ); + } + } + return "gray"; +}; + +function useListClassView(): ClassManageDialogProps { const dispatch = useDispatch(); const labelClassPropertieusByLabelClass = useSelector( selectorLabelClassPropertiesByLabelClass @@ -89,7 +121,7 @@ const useListClassView = function (): ClassManageDialogProps { {Object.entries(labelClassPropertieusByLabelClass).map( ([labelName, value]) => { - const { label, cssStyle } = value; + const { label } = value; return ( ), }; -}; +} export default useListClassView; diff --git a/src/routes/AnnotationPage/LabelAnnotation/index.tsx b/src/routes/AnnotationPage/LabelAnnotation/index.tsx index 7c39d34b..7a160ba5 100644 --- a/src/routes/AnnotationPage/LabelAnnotation/index.tsx +++ b/src/routes/AnnotationPage/LabelAnnotation/index.tsx @@ -1,7 +1,6 @@ import AddIcon from "@mui/icons-material/Add"; import { Button, CircularProgress } from "@mui/material"; import Box from "@mui/material/Box"; -import { LabelClassProperties } from "components/Annotation/Editor/type"; import { CSSProperties, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { AutoSizer } from "react-virtualized"; @@ -16,37 +15,9 @@ import { selectorIsFetchingImageData, } from "reduxes/annotationmanager/selecetor"; import ClassItem from "./ClassItem"; -import { convertStrokeColorToFillColor } from "./ClassLabel"; import ClassManageModel from "./ClassManageModal"; -export const hashCode = (str: string) => { - let hash = 0; - for (let i = 0; i < str.length; i++) { - hash = str.charCodeAt(i) + ((hash << 5) - hash); - } - return hash; -}; - -export const intToRGB = (i: number) => { - const c = (i & 0x00ffffff).toString(16).toUpperCase(); - return "00000".substring(0, 6 - c.length) + c; -}; -export const getBackgroundColor = ( - labelClassProperties: LabelClassProperties -) => { - if (labelClassProperties) { - if (labelClassProperties?.cssStyle?.stroke) { - return labelClassProperties.cssStyle.stroke; - } - if (labelClassProperties.label?.label) { - return convertStrokeColorToFillColor( - "#" + intToRGB(hashCode(labelClassProperties.label.label)) - ); - } - } - return "gray"; -}; -const LabelAnnotation = function () { +function LabelAnnotation() { const dispatch = useDispatch(); const drawObjectById = useSelector(selectorDrawObjectById); const drawObjectStateIdByAI = useSelector(selectorDrawObjectStateIdByAI); @@ -62,7 +33,7 @@ const LabelAnnotation = function () { const dialogClassManageModal = useSelector(selectorDialogClassManageModal); const listIdsDrawObjectById = useMemo(() => { const retList: string[] = []; - Object.keys(drawObjectById).map((id) => { + Object.keys(drawObjectById).forEach((id) => { if ( !drawObjectStateIdByAI[id] || drawObjectStateIdByAI[id].isShow === true @@ -77,7 +48,7 @@ const LabelAnnotation = function () { if (listIdsDrawObjectById[index]) { return ; } - return <>; + return null; } const renderList = () => { @@ -134,5 +105,5 @@ const LabelAnnotation = function () { {dialogClassManageModal.isOpen && } ); -}; +} export default LabelAnnotation; diff --git a/src/routes/AnnotationProject/index.tsx b/src/routes/AnnotationProject/index.tsx index 9bf01660..481697e2 100644 --- a/src/routes/AnnotationProject/index.tsx +++ b/src/routes/AnnotationProject/index.tsx @@ -1,9 +1,6 @@ import { Box, Button, Container, Typography } from "@mui/material"; import { ID_TOKEN_NAME } from "constants/defaultValues"; -import { - ANNOTATION_PROJECT_DETAIL_ROUTE_NAME, - ANNOTATION_PROJECT_ROUTE_NAME, -} from "constants/routeName"; +import { ANNOTATION_PROJECT_DETAIL_ROUTE_NAME } from "constants/routeName"; import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useHistory } from "react-router-dom"; @@ -13,9 +10,8 @@ import { ApiListAnnotationProjectsItem } from "reduxes/annotationProject/type"; import { getLocalStorage } from "utils/general"; import { AnnotationProjectItemProps } from "./type"; -const ProjectItem = function ({ projectInfo }: AnnotationProjectItemProps) { +function ProjectItem({ projectInfo }: AnnotationProjectItemProps) { const history = useHistory(); - const dispatch = useDispatch(); const limitTooLongLineStyle = { display: "-webkit-box", @@ -53,7 +49,7 @@ const ProjectItem = function ({ projectInfo }: AnnotationProjectItemProps) { justifyContent="center" minHeight={160} maxHeight={160} - >
    + /> - */} Date: Tue, 15 Nov 2022 16:11:34 +0700 Subject: [PATCH 23/67] chore: open annotation editor in new tab --- src/routes/AnnotationProjectDetail/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/AnnotationProjectDetail/index.tsx b/src/routes/AnnotationProjectDetail/index.tsx index 2ecbe1bf..32e65dda 100644 --- a/src/routes/AnnotationProjectDetail/index.tsx +++ b/src/routes/AnnotationProjectDetail/index.tsx @@ -216,7 +216,7 @@ const annotationProjectDetail = function () { // } }; const handleAnnotateProjectClick = () => { - history.push(`/${ANNOTATION_EDITOR_ROUTE_NAME}/${projectName}`); + window.open(`/${ANNOTATION_EDITOR_ROUTE_NAME}/${projectName}`, "_blank"); }; const renderProject = () => { if ( From 45952c1d15b3a213a86b8e1f2efaf52ab3e1656f Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Tue, 15 Nov 2022 17:52:03 +0700 Subject: [PATCH 24/67] fix: change disabled state of icon in button --- .../AnnotationPage/ControlPanel/index.tsx | 27 +++++++++++++------ src/styles/theme.tsx | 9 +++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index 55934e43..5d9154c7 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -13,6 +13,7 @@ import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; import WarningAmberIcon from "@mui/icons-material/WarningAmber"; import { LoadingButton } from "@mui/lab"; import { + Badge, Box, Button, IconButton, @@ -532,6 +533,7 @@ function ControlPanel() { justifyContent="space-evenly" flexDirection="column" gap={2} + pt={2} > - - selectModeHandle(e, DrawType.DETECTED_RECTANGLE) + + ) : undefined } > - {isAIDetectAvailable ? ( + + selectModeHandle(e, DrawType.DETECTED_RECTANGLE) + } + disabled={!isAIDetectAvailable} + > - ) : ( - - )} - + + diff --git a/src/styles/theme.tsx b/src/styles/theme.tsx index 3c77be6c..addc0c8a 100644 --- a/src/styles/theme.tsx +++ b/src/styles/theme.tsx @@ -16,6 +16,15 @@ export const darkTheme = createTheme({ }, }, }, + MuiButtonBase: { + styleOverrides: { + root: { + "&.Mui-disabled svg": { + opacity: 0.3, + }, + }, + }, + }, MuiSvgIcon: { styleOverrides: { root: { From 03a5db487faba53df9b094e232dfc303abeb57cc Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Tue, 15 Nov 2022 18:15:20 +0700 Subject: [PATCH 25/67] update: clone annotation from project modal --- src/components/CloneProjectModal/index.tsx | 57 +++++++++++++++++-- src/components/Sidebar/index.tsx | 24 +++++--- .../AnnotationPage/ControlPanel/index.tsx | 1 + 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/components/CloneProjectModal/index.tsx b/src/components/CloneProjectModal/index.tsx index 46ee96b8..32667781 100644 --- a/src/components/CloneProjectModal/index.tsx +++ b/src/components/CloneProjectModal/index.tsx @@ -1,5 +1,6 @@ import CancelIcon from "@mui/icons-material/Cancel"; import { + Autocomplete, Box, IconButton, Input, @@ -10,7 +11,6 @@ import { import { useForm } from "react-hook-form"; import { useDispatch, useSelector } from "react-redux"; import { modalCloseStyle, modalStyle } from "styles/generalStyle"; -import { CloneProjectToAnnotationFields } from "./type"; import { MyButton } from "components"; import { @@ -22,12 +22,16 @@ import { selectorIsCloningProjectToAnnotation, } from "reduxes/annotationProject/selector"; import { useEffect } from "react"; +import { selectorListProjects } from "reduxes/project/selector"; +import { CloneProjectToAnnotationFields } from "./type"; const CloneProjectModal = function () { const dispatch = useDispatch(); const dialogCloneProjectToAnnotation = useSelector( selectorDialogCloneProjectToAnnotation ); + const listProjects = useSelector(selectorListProjects); + const { register, handleSubmit, @@ -82,10 +86,46 @@ const CloneProjectModal = function () { Clone Project To Annotation - - - {`From '${dialogCloneProjectToAnnotation.projectName}'`} - + + {dialogCloneProjectToAnnotation.projectName ? ( + + {`From '${dialogCloneProjectToAnnotation.projectName}'`} + + ) : ( + + option.project_name} + onChange={(_, selectedFromProject) => { + if (selectedFromProject) { + setValue( + "fromProjectName", + selectedFromProject.project_name + ); + } + }} + renderOption={(optionProps, option) => ( +
  • + {option.project_name} +
  • + )} + renderInput={(params) => ( + + )} + /> +
    + )} { - dispatch({ - type: SET_IS_OPEN_CREATE_PROJECT_MODAL, - payload: { isOpen: true }, - }); + const handleClickCreateFromProject = () => { + dispatch( + setShowDialogCloneProjectToAnnotation({ + dialogCloneProjectToAnnotation: { + isShow: true, + projectName: "", + }, + }) + ); }; + return ( {renderListProjects()} diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index 5d9154c7..37703186 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -534,6 +534,7 @@ function ControlPanel() { flexDirection="column" gap={2} pt={2} + pb={1} > Date: Tue, 15 Nov 2022 18:46:35 +0700 Subject: [PATCH 26/67] refactor: annotation dashboard UI --- src/components/Sidebar/index.tsx | 4 +- src/reduxes/annotationProject/reducer.ts | 2 +- src/reduxes/annotationProject/selector.ts | 2 + src/reduxes/annotationProject/type.ts | 2 +- src/routes/AnnotationProject/index.tsx | 144 ++++++++++++++++------ 5 files changed, 110 insertions(+), 44 deletions(-) diff --git a/src/components/Sidebar/index.tsx b/src/components/Sidebar/index.tsx index b2d9b9cd..06a53c92 100644 --- a/src/components/Sidebar/index.tsx +++ b/src/components/Sidebar/index.tsx @@ -375,13 +375,13 @@ const Sidebar = function () { /> export const selectorDeleteAnnotationProjectConfirmDialogInfo = ( state: RootState ) => state.annotationProjectReducer.deleteConfirmDialogInfo; +export const selectorIsFetchingListAnnotationProject = (state: RootState) => + state.annotationProjectReducer.isFetchingProjects; diff --git a/src/reduxes/annotationProject/type.ts b/src/reduxes/annotationProject/type.ts index 325d0d76..96af13fd 100644 --- a/src/reduxes/annotationProject/type.ts +++ b/src/reduxes/annotationProject/type.ts @@ -11,7 +11,7 @@ export interface AnnotationProjectReducer { isCloningProjectToAnnotation: boolean; currentProjectInfo: null | AnnotationProjectInfo; currentProjectName: string; - isFetchingProjects: boolean; + isFetchingProjects: null | boolean; isFetchingDetailProject: boolean; currentAnnotationAndFileInfo: null | AnnotationAndFileInfoApi; currentAnnotationFiles: null | AnnotationFilesApi; diff --git a/src/routes/AnnotationProject/index.tsx b/src/routes/AnnotationProject/index.tsx index 481697e2..20cc09a0 100644 --- a/src/routes/AnnotationProject/index.tsx +++ b/src/routes/AnnotationProject/index.tsx @@ -1,26 +1,42 @@ -import { Box, Button, Container, Typography } from "@mui/material"; -import { ID_TOKEN_NAME } from "constants/defaultValues"; +import { + Box, + Button, + CircularProgress, + Container, + Typography, +} from "@mui/material"; +import TodayIcon from "@mui/icons-material/Today"; +import { + ID_TOKEN_NAME, + SYSTEM_DATE_TIME_FORMAT, +} from "constants/defaultValues"; import { ANNOTATION_PROJECT_DETAIL_ROUTE_NAME } from "constants/routeName"; import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { useHistory } from "react-router-dom"; +import { Link, useHistory } from "react-router-dom"; import { fetchListAnnotationProjects } from "reduxes/annotationProject/action"; -import { selectorAnnotationListProjects } from "reduxes/annotationProject/selector"; +import { + selectorAnnotationListProjects, + selectorIsFetchingListAnnotationProject, +} from "reduxes/annotationProject/selector"; import { ApiListAnnotationProjectsItem } from "reduxes/annotationProject/type"; -import { getLocalStorage } from "utils/general"; +import { getLocalStorage, getMomentWithCurrentTimeZone } from "utils/general"; +import moment from "moment"; +import { Empty } from "components"; import { AnnotationProjectItemProps } from "./type"; +const limitTooLongLineStyle = { + display: "-webkit-box", + WebkitBoxOrient: "vertical", + WebkitLineClamp: 2, + overflow: "hidden", + lineHeight: 1.3, + minHeight: 54, +}; + function ProjectItem({ projectInfo }: AnnotationProjectItemProps) { const history = useHistory(); - const limitTooLongLineStyle = { - display: "-webkit-box", - WebkitBoxOrient: "vertical", - WebkitLineClamp: 2, - overflow: "hidden", - maxWidth: 200, - lineHeight: 1.3, - }; const handleOnClickProjectItem = () => { history.push( `/${ANNOTATION_PROJECT_DETAIL_ROUTE_NAME}/${projectInfo.project_name}` @@ -32,50 +48,99 @@ function ProjectItem({ projectInfo }: AnnotationProjectItemProps) { flexBasis="calc(33.33% - 6*8px + 6/3*8px)" bgcolor="primary.dark" borderRadius={2} - py={2} + p={2} onClick={handleOnClickProjectItem} > - - - - {projectInfo.project_name} - - - + + {projectInfo.project_name} + - - + + + {getMomentWithCurrentTimeZone( + moment(projectInfo.created_date) + ).format(SYSTEM_DATE_TIME_FORMAT)} + + ); } function annotationProject() { const dispatch = useDispatch(); + const isFetchingProjects = useSelector( + selectorIsFetchingListAnnotationProject + ); const listProjects = useSelector(selectorAnnotationListProjects); useEffect(() => { dispatch( fetchListAnnotationProjects({ idToken: getLocalStorage(ID_TOKEN_NAME) }) ); }, []); + + const renderPageContent = () => { + if (isFetchingProjects === null || isFetchingProjects === true) { + return ( + + + Fetching project informations... + + ); + } + + if (!listProjects || listProjects.length <= 0) { + return ( + + + No project yet. + + Go to Dashboard and create a new + project. + + + } + /> + + ); + } + + return listProjects.map((project: ApiListAnnotationProjectsItem) => ( + + )); + }; + return ( + + Annotation Dashboard + - {listProjects.map((project: ApiListAnnotationProjectsItem) => ( - - ))} + {renderPageContent()} ); From 9a0bb859b1777139d5437f389825aecbe1500f81 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Tue, 15 Nov 2022 19:09:01 +0700 Subject: [PATCH 27/67] update: annotation editor layout --- src/routes/AnnotationPage/AnnotationEditor.tsx | 9 +++++++-- src/routes/AnnotationPage/Editor/index.tsx | 3 ++- src/routes/AnnotationPage/ImagePreview/index.tsx | 3 +-- src/routes/AnnotationPage/LabelAnnotation/index.tsx | 9 ++++++--- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/routes/AnnotationPage/AnnotationEditor.tsx b/src/routes/AnnotationPage/AnnotationEditor.tsx index d4e352af..ae22bd94 100644 --- a/src/routes/AnnotationPage/AnnotationEditor.tsx +++ b/src/routes/AnnotationPage/AnnotationEditor.tsx @@ -51,7 +51,7 @@ const AnnotationEditor = function () { return ( - + @@ -68,7 +68,12 @@ const AnnotationEditor = function () { - + diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index 2c6baa19..d1e1c5f5 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -359,9 +359,10 @@ function Editor() { onMouseOver={mouseOverBoundDivHandler} onMouseDown={handleMouseDownOutLayer} onMouseUp={handleMouseUpOutLayer} - id="testId" + id="annotation-editor-bound" width="100%" height="100%" + sx={{ outline: "none" }} > { if (issFetchingImageData) { return ( - + ); } return ( - + {({ height, width }) => ( @@ -101,7 +104,7 @@ function LabelAnnotation() { - {renderList()} + {renderList()} {dialogClassManageModal.isOpen && } ); From d3a5ebe871c1f848d20379df93a57ca18c28ea88 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Thu, 17 Nov 2022 17:21:25 +0700 Subject: [PATCH 28/67] update: merge to devlop --- src/components/Annotation/Editor/const.ts | 4 +- src/reduxes/annotation/action.ts | 15 --- src/reduxes/annotation/constants.ts | 4 - src/reduxes/annotation/reducer.ts | 32 +------ src/reduxes/annotation/selector.ts | 4 - src/reduxes/annotation/type.ts | 3 - .../AnnotationPage/AnnotationEditor.tsx | 10 +- .../Layer/DetectedRectangleDrawLayer.tsx | 91 +++++++++--------- .../AnnotationPage/Editor/Layer/DrawLayer.tsx | 13 ++- .../Editor/Layer/EllipseDrawLayer.tsx | 66 +++++++++---- .../Editor/Layer/PolygonDrawLayer.tsx | 93 +++++++++---------- .../Editor/Layer/RectangleDrawLayer.tsx | 91 +++++++++--------- .../AnnotationPage/Editor/Layer/type.ts | 4 +- .../AnnotationPage/Editor/Layer/utils.ts | 26 ++++++ src/routes/AnnotationPage/Editor/index.tsx | 50 +++++----- .../AnnotationPage/LabelAnnotation/index.tsx | 18 +++- src/routes/AnnotationPage/constants.ts | 1 + 17 files changed, 269 insertions(+), 256 deletions(-) create mode 100644 src/routes/AnnotationPage/Editor/Layer/utils.ts diff --git a/src/components/Annotation/Editor/const.ts b/src/components/Annotation/Editor/const.ts index 4668000f..f702603f 100644 --- a/src/components/Annotation/Editor/const.ts +++ b/src/components/Annotation/Editor/const.ts @@ -14,5 +14,5 @@ export const CORNER_RADIUS = 5; export const MAX_WIDTH_IMAGE_IN_EDITOR = 1000; export const MAX_HEIGHT_IMAGE_IN_EDITOR = 700; -export const MAX_WIDTH_EDITOR = 1200; -export const MAX_HEIGHT_EDITOR = 700; +export const MAX_WIDTH_EDITOR = 1270; +export const MAX_HEIGHT_EDITOR = 860; diff --git a/src/reduxes/annotation/action.ts b/src/reduxes/annotation/action.ts index a16e99c2..c5b6f639 100644 --- a/src/reduxes/annotation/action.ts +++ b/src/reduxes/annotation/action.ts @@ -15,8 +15,6 @@ import { SET_IS_DRAGGING_VIEW_PORT, SET_KEY_DOWN_IN_EDITOR, SET_LOCK_DRAW_OBJECT, - SET_MOUSE_DOWN_OUT_LAYER_POSITION, - SET_MOUSE_UP_OUT_LAYER_POSITION, SET_SELECT_SHAPE, SHOW_ALL_DRAW_OBJECTS_BY_AI, SHOW_DRAW_OBJECTS_BY_AI, @@ -37,7 +35,6 @@ import { SetKeyDownPayload, SetLockDetectedAreaPayload, SetLockDrawObecjtPayload, - SetMouseOutLayerPosition, SetSelectShapePayload, ShowDrawObjectStateIdByAIPayload, UpdateDrawObjectPayload, @@ -141,15 +138,3 @@ export const setKeyDownInEditor = (payload: SetKeyDownPayload) => ({ type: SET_KEY_DOWN_IN_EDITOR, payload, }); -export const setMouseUpOutLayerPosition = ( - payload: SetMouseOutLayerPosition -) => ({ - type: SET_MOUSE_UP_OUT_LAYER_POSITION, - payload, -}); -export const setMouseDownOutLayerPosition = ( - payload: SetMouseOutLayerPosition -) => ({ - type: SET_MOUSE_DOWN_OUT_LAYER_POSITION, - payload, -}); diff --git a/src/reduxes/annotation/constants.ts b/src/reduxes/annotation/constants.ts index 04b49f14..96a2f0dd 100644 --- a/src/reduxes/annotation/constants.ts +++ b/src/reduxes/annotation/constants.ts @@ -21,7 +21,3 @@ export const RECOVER_PREVIOUS_DRAWSTATE = "RECOVER_PREVIOUS_DRAWSTATE"; export const SHOW_ALL_DRAW_OBJECTS_BY_AI = "SHOW_ALL_DRAW_OBJECTS_BY_AI"; export const HIDDEN_ALL_DRAW_OBJECTS_BY_AI = "HIDDEN_ALL_DRAW_OBJECTS_BY_AI"; export const SET_KEY_DOWN_IN_EDITOR = "SET_KEY_DOWN_IN_EDITOR"; -export const SET_MOUSE_UP_OUT_LAYER_POSITION = - "SET_MOUSE_UP_OUT_LAYER_POSITION"; -export const SET_MOUSE_DOWN_OUT_LAYER_POSITION = - "SET_MOUSE_DOWN_OUT_LAYER_POSITION"; diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index cf8d9d49..943674d1 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -18,8 +18,6 @@ import { SET_IS_DRAGGING_VIEW_PORT, SET_KEY_DOWN_IN_EDITOR, SET_LOCK_DRAW_OBJECT, - SET_MOUSE_DOWN_OUT_LAYER_POSITION, - SET_MOUSE_UP_OUT_LAYER_POSITION, SET_SELECT_SHAPE, SHOW_ALL_DRAW_OBJECTS_BY_AI, SHOW_DRAW_OBJECTS_BY_AI, @@ -46,7 +44,6 @@ import { SetKeyDownPayload, SetLockDetectedAreaPayload, SetLockDrawObecjtPayload, - SetMouseOutLayerPosition, SetSelectShapePayload, ShowDrawObjectStateIdByAIPayload, StateHistory, @@ -104,8 +101,6 @@ const inititalState: AnnotationReducer = { isDraggingViewport: false, drawObjectStateIdByAI: {}, keyDownInEditor: null, - mouseUpOutLayerPosition: null, - mouseDownOutLayerPosition: null, }; const updateStateHistory = ( drawObjectById: Record, @@ -370,12 +365,9 @@ const annotationReducer = ( }; } case SET_DETECTED_AREA: { - const { detectedArea, scale } = payload as SetLockDetectedAreaPayload; + const { detectedArea } = payload as SetLockDetectedAreaPayload; const newDrawObjectStateIdByAI = { ...state.drawObjectStateIdByAI }; if (detectedArea) { - const scaleX = 1 / scale.x; - const scaleY = 1 / scale.y; - Object.keys(state.drawObjectStateIdByAI).forEach((drawObjectId) => { const drawObject = state.drawObjectById[drawObjectId]; if (drawObject) { @@ -384,10 +376,10 @@ const annotationReducer = ( const { points } = data as PolygonSpec; const isInvalid = points.some( (point) => - point.x < detectedArea.x * scaleX || - point.y < detectedArea.y * scaleY || - point.x > (detectedArea.x + detectedArea.width) * scaleX || - point.y > (detectedArea.y + detectedArea.height) * scaleY + point.x < detectedArea.x || + point.y < detectedArea.y || + point.x > detectedArea.x + detectedArea.width || + point.y > detectedArea.y + detectedArea.height ); if (!isInvalid) { delete newDrawObjectStateIdByAI[drawObjectId]; @@ -461,20 +453,6 @@ const annotationReducer = ( const { keyDownInEditor } = payload as SetKeyDownPayload; return { ...state, keyDownInEditor }; } - case SET_MOUSE_UP_OUT_LAYER_POSITION: { - const { position } = payload as SetMouseOutLayerPosition; - return { - ...state, - mouseUpOutLayerPosition: position, - }; - } - case SET_MOUSE_DOWN_OUT_LAYER_POSITION: { - const { position } = payload as SetMouseOutLayerPosition; - return { - ...state, - mouseDownOutLayerPosition: position, - }; - } case SAVE_REMOTE_NEW_CLASS_LABEL.SUCCEEDED: { const history = state.statehHistory; return { diff --git a/src/reduxes/annotation/selector.ts b/src/reduxes/annotation/selector.ts index b7901355..c60eec6e 100644 --- a/src/reduxes/annotation/selector.ts +++ b/src/reduxes/annotation/selector.ts @@ -89,7 +89,3 @@ export const selectorDrawObjectStateIdByAI = (state: RootState) => state.annotationReducer.drawObjectStateIdByAI; export const selectorKeyDownInEditor = (state: RootState) => state.annotationReducer.keyDownInEditor; -export const selectorMouseDownOutLayerPosition = (state: RootState) => - state.annotationReducer.mouseDownOutLayerPosition; -export const selectorMouseUpOutLayerPosition = (state: RootState) => - state.annotationReducer.mouseUpOutLayerPosition; diff --git a/src/reduxes/annotation/type.ts b/src/reduxes/annotation/type.ts index 20f76ea7..946648d9 100644 --- a/src/reduxes/annotation/type.ts +++ b/src/reduxes/annotation/type.ts @@ -37,8 +37,6 @@ export interface AnnotationReducer { detectedArea: DetectedAreaType | null; isDraggingViewport: boolean; keyDownInEditor: string | null; - mouseUpOutLayerPosition: Vector2d | null; - mouseDownOutLayerPosition: Vector2d | null; } export interface DrawObjectByAIState { isShow: boolean; @@ -113,7 +111,6 @@ export interface SetLockDrawObecjtPayload { } export interface SetLockDetectedAreaPayload { detectedArea: DetectedAreaType | null; - scale: { x: number; y: number }; } export interface SetIsDraggingViewportPayload { isDraggingViewport: boolean; diff --git a/src/routes/AnnotationPage/AnnotationEditor.tsx b/src/routes/AnnotationPage/AnnotationEditor.tsx index ae22bd94..f5ebb2b6 100644 --- a/src/routes/AnnotationPage/AnnotationEditor.tsx +++ b/src/routes/AnnotationPage/AnnotationEditor.tsx @@ -1,4 +1,8 @@ import { Box, CircularProgress } from "@mui/material"; +import { + MAX_HEIGHT_EDITOR, + MAX_WIDTH_EDITOR, +} from "components/Annotation/Editor/const"; import { ID_TOKEN_NAME } from "constants/defaultValues"; import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -58,8 +62,10 @@ const AnnotationEditor = function () { diff --git a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx index fd168122..78dfcffe 100644 --- a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx @@ -8,77 +8,77 @@ import { changeCurrentDrawState, setDetectedArea, } from "reduxes/annotation/action"; -import { selectorMouseUpOutLayerPosition } from "reduxes/annotation/selector"; import { DetectedAreaType, DrawState } from "reduxes/annotation/type"; import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; +import { FULL_PADDING_VALUE } from "routes/AnnotationPage/constants"; import { convertStrokeColorToFillColor } from "routes/AnnotationPage/LabelAnnotation/ClassLabel"; +import { DrawLayerProps } from "./type"; +import { adjustPosition } from "./utils"; -function DetectedRectangleDrawLayer() { +function DetectedRectangleDrawLayer({ + drawLayerProps: { paddingLeft, paddingTop }, +}: { + drawLayerProps: DrawLayerProps; +}) { const dispatch = useDispatch(); const refDetectedArea = useRef(null); const [localDetectedArea, setLocalDetectedArea] = useState(null); - const mouseUpOutLayerPosition = useSelector(selectorMouseUpOutLayerPosition); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); const mousedownHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); - if (position) { - setLocalDetectedArea({ - x: position.x, - y: position.y, - width: 3, - height: 3, - }); - } + const newPosition = adjustPosition( + position, + currentAnnotationFile?.width, + currentAnnotationFile?.height + ); + setLocalDetectedArea({ + x: newPosition.x, + y: newPosition.y, + width: 3, + height: 3, + }); dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); e.evt.preventDefault(); e.evt.stopPropagation(); }; - const updateMousePosition = (position: Vector2d) => { if (position && currentAnnotationFile) { if (localDetectedArea) { - let { x, y } = position; - if (x < 0) { - x = 0; - } - if (currentAnnotationFile.width && x > currentAnnotationFile.width) { - x = currentAnnotationFile.width; - } - if (y < 0) { - y = 0; - } - if (currentAnnotationFile.height && y > currentAnnotationFile.height) { - y = currentAnnotationFile.height; - } setLocalDetectedArea({ ...localDetectedArea, - width: x - localDetectedArea.x, - height: y - localDetectedArea.y, + width: position.x - localDetectedArea.x, + height: position.y - localDetectedArea.y, }); } } }; - const handleMouseOut = (e: KonvaEventObject) => { - const position = e.currentTarget.getRelativePointerPosition(); - updateMousePosition(position); - }; + const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); - updateMousePosition(position); + updateMousePosition( + adjustPosition( + position, + currentAnnotationFile?.width, + currentAnnotationFile?.height + ) + ); + }; + const resetDraw = () => { + console.log("reset"); + setLocalDetectedArea(null); + dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); }; const mouseupHandler = () => { if (localDetectedArea && refDetectedArea.current) { dispatch( setDetectedArea({ - detectedArea: { ...refDetectedArea.current.getClientRect() }, - scale: refDetectedArea.current.getAbsoluteScale(), + detectedArea: { ...localDetectedArea }, }) ); } - setLocalDetectedArea(null); - dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); + resetDraw(); }; const layer = useRef(null); useEffect(() => { @@ -86,23 +86,26 @@ function DetectedRectangleDrawLayer() { }, []); const renderDummyRect = () => { if (currentAnnotationFile) { - const { width, height } = currentAnnotationFile; - return ; + return ( + + ); } return null; }; - useEffect(() => { - if (mouseUpOutLayerPosition) { - mouseupHandler(); - } - }, [mouseUpOutLayerPosition]); + return ( {renderDummyRect()} {localDetectedArea && ( diff --git a/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx index 3cecde70..280d701a 100644 --- a/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/DrawLayer.tsx @@ -1,3 +1,4 @@ +import { useCallback } from "react"; import { Layer } from "react-konva"; import { useSelector } from "react-redux"; import { @@ -9,8 +10,9 @@ import DetectedRectangleDrawLayer from "./DetectedRectangleDrawLayer"; import EllipseDrawLayer from "./EllipseDrawLayer"; import PolygonDrawLayer from "./PolygonDrawLayer"; import RectangleDrawLayer from "./RectangleDrawLayer"; +import { DrawLayerProps } from "./type"; -const DrawLayer = () => { +const DrawLayer = (drawLayerProps: DrawLayerProps) => { const currentDrawState = useSelector(selectorCurrentDrawState); const drawType = useSelector(selectorCurrentDrawType); @@ -20,20 +22,21 @@ const DrawLayer = () => { currentDrawState === DrawState.DRAWING ) { if (drawType === DrawType.POLYGON || drawType === DrawType.LINE_STRIP) { - return ; + return ; } if (drawType === DrawType.RECTANGLE) { - return ; + return ; } if (drawType === DrawType.ELLIPSE) { - return ; + return ; } if (drawType === DrawType.DETECTED_RECTANGLE) { - return ; + return ; } } return ; }; return render(); }; + export default DrawLayer; diff --git a/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx index 62cf74a9..e75349f1 100644 --- a/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx @@ -11,30 +11,55 @@ import { changeCurrentDrawState, createDrawObject, } from "reduxes/annotation/action"; -import { selectorMouseUpOutLayerPosition } from "reduxes/annotation/selector"; import { DrawState, DrawType } from "reduxes/annotation/type"; import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; +import { FULL_PADDING_VALUE } from "routes/AnnotationPage/constants"; +import { DrawLayerProps } from "./type"; +import { adjustPosition } from "./utils"; -function EllipseDrawLayer() { +function EllipseDrawLayer({ + drawLayerProps: { paddingLeft, paddingTop }, +}: { + drawLayerProps: DrawLayerProps; +}) { const dispatch = useDispatch(); const [startPoint, setStartPoint] = useState(null); const [endPoint, setEndPoint] = useState(null); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); - const mouseUpOutLayerPosition = useSelector(selectorMouseUpOutLayerPosition); const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (!position || !startPoint) return; - setEndPoint({ ...position }); + setEndPoint({ + ...adjustPosition( + position, + currentAnnotationFile?.width, + currentAnnotationFile?.height + ), + }); + }; + const resetDraw = () => { + setStartPoint(null); + setEndPoint(null); + dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); }; - const mousedownHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (!position) return; - setStartPoint({ ...position }); - dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); - e.evt.preventDefault(); - e.evt.stopPropagation(); + const newPosition = adjustPosition( + position, + currentAnnotationFile?.width, + currentAnnotationFile?.height + ); + if (!startPoint) { + setStartPoint({ ...newPosition }); + dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); + e.evt.preventDefault(); + e.evt.stopPropagation(); + } else { + setEndPoint(newPosition); + handleMouseUp(); + } }; const layer = useRef(null); useEffect(() => { @@ -60,25 +85,28 @@ function EllipseDrawLayer() { }) ); } - setStartPoint(null); - setEndPoint(null); - dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); + resetDraw(); }; + const renderDummyRect = () => { if (currentAnnotationFile) { - const { width, height } = currentAnnotationFile; - return ; + return ( + + ); } return null; }; - useEffect(() => { - if (mouseUpOutLayerPosition) { - handleMouseUp(); - } - }, [mouseUpOutLayerPosition]); + return ( (null); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); const keyDownInEditor = useSelector(selectorKeyDownInEditor); - const mouseDownOutLayerPosition = useSelector( - selectorMouseDownOutLayerPosition - ); const resetDraw = () => { setPoints([]); setIsFinished(false); @@ -56,15 +58,13 @@ function PolygonDrawLayer() { const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (!position) return; - // console.log("position", position); - if (position.x < 0) { - position.x = 0; - } - if (position.y < 0) { - position.y = 0; - } - // console.log("set position", position); - setMousePosition(position); + setMousePosition( + adjustPosition( + position, + currentAnnotationFile?.width, + currentAnnotationFile?.height + ) + ); }; const handleMouseDownPoint = (e: KonvaEventObject) => { if (mouseOverPoint) { @@ -98,7 +98,7 @@ function PolygonDrawLayer() { e: KonvaEventObject ) => { if (isFinished || points.length < 2) return; - e.target.scale({ x: 2, y: 2 }); + e.target.scale({ x: 3, y: 3 }); setMouseOverPoint(true); }; const handleMouseOutStartPointWhenFinished = ( @@ -124,36 +124,23 @@ function PolygonDrawLayer() { const x = point.x - CORNER_RADIUS / 2; const y = point.y - CORNER_RADIUS / 2; let startPointAttr; - if (isLineStrip === true) { - startPointAttr = - index === points.length - 1 - ? { - hitStrokeWidth: 12, - onMouseOver: handleMouseOverStartPointLineStrip, - onMouseOut: handleMouseOutStartPoint, - onMouseDown: handleMouseDownPoint, - } - : { - onMouseOver: handleMouseOverStartPointWhenFinished, - onMouseOut: handleMouseOutStartPointWhenFinished, - }; - } else { - startPointAttr = - index === 0 && !isFinished - ? { - hitStrokeWidth: 12, - onMouseOver: handleMouseOverStartPoint, - onMouseOut: handleMouseOutStartPoint, - onMouseDown: handleMouseDownPoint, - } - : { - onMouseOver: handleMouseOverStartPointWhenFinished, - onMouseOut: handleMouseOutStartPointWhenFinished, - }; - } + startPointAttr = + (isLineStrip === true && index === points.length - 1) || + (isLineStrip !== true && index === 0 && !isFinished) + ? { + hitStrokeWidth: 12, + onMouseOver: handleMouseOverStartPointLineStrip, + onMouseOut: handleMouseOutStartPoint, + onMouseDown: handleMouseDownPoint, + } + : { + onMouseOver: handleMouseOverStartPointWhenFinished, + onMouseOut: handleMouseOutStartPointWhenFinished, + }; return ( ) => { + console.log("mousedownHandler"); if (mousePosition) { setPoints([...points, mousePosition]); dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); @@ -190,16 +178,17 @@ function PolygonDrawLayer() { }; const renderDummyRect = () => { if (currentAnnotationFile) { - const { width, height } = currentAnnotationFile; - return ; + return ( + + ); } return null; }; - useEffect(() => { - if (mouseDownOutLayerPosition) { - mousedownHandler(); - } - }, [mouseDownOutLayerPosition]); useEffect(() => { const flatPoints: number[] = points .concat(isFinished || !mousePosition ? [] : mousePosition) @@ -214,6 +203,8 @@ function PolygonDrawLayer() { return ( (null); const [endPoint, setEndPoint] = useState(null); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); - const mouseUpOutLayerPosition = useSelector(selectorMouseUpOutLayerPosition); - - const updateMousePosition = (position: Vector2d) => { - if (!position || !startPoint) return; - let { x, y } = position; - if (x < 0) { - x = 0; - } - if ( - currentAnnotationFile?.width && - position.x > currentAnnotationFile?.width - ) { - x = currentAnnotationFile?.width; - } - if (y < 0) { - y = 0; - } - if (currentAnnotationFile?.height && y > currentAnnotationFile?.height) { - y = currentAnnotationFile?.height; - } - setEndPoint({ x, y }); - }; const mousedownHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); if (!position) return; - setStartPoint({ ...position }); + const newPosition = adjustPosition( + position, + currentAnnotationFile?.width, + currentAnnotationFile?.height + ); + setStartPoint({ ...newPosition }); dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); e.evt.preventDefault(); e.evt.stopPropagation(); @@ -55,14 +44,17 @@ function RectangleDrawLayer() { useEffect(() => { layer.current?.moveToTop(); }, []); + const resetDraw = () => { + setStartPoint(null); + setEndPoint(null); + dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); + }; const handleMouseUp = () => { if (startPoint && endPoint) { - let rect; - if (startPoint.x < endPoint.x) { - rect = createRectangle(startPoint); - } else { - rect = createRectangle(endPoint); - } + const rect = createRectangle({ + x: Math.min(startPoint.x, endPoint.x), + y: Math.min(startPoint.y, endPoint.y), + }); const spec = rect.data as RectangleSpec; dispatch( createDrawObject({ @@ -77,43 +69,46 @@ function RectangleDrawLayer() { }) ); } - setStartPoint(null); - setEndPoint(null); - dispatch(changeCurrentDrawState({ drawState: DrawState.FREE })); + resetDraw(); }; - const handleMouseOut = (e: KonvaEventObject) => { - const position = e.currentTarget.getRelativePointerPosition(); - updateMousePosition(position); - }; const renderDummyRect = () => { if (currentAnnotationFile) { - const { width, height } = currentAnnotationFile; - return ; + return ( + + ); } return null; }; - useEffect(() => { - if (mouseUpOutLayerPosition) { - handleMouseUp(); - } - }, [mouseUpOutLayerPosition]); - const mousemoveHandler = (e: KonvaEventObject) => { const position = e.currentTarget.getRelativePointerPosition(); - updateMousePosition(position); + setEndPoint( + adjustPosition( + position, + currentAnnotationFile?.width, + currentAnnotationFile?.height + ) + ); }; + return ( {renderDummyRect()} {startPoint && endPoint && ( { + if (limitWidth && limitHeight) { + const newPosition = { ...position }; + if (position.x < 0) { + newPosition.x = 0; + } + if (position.x > limitWidth) { + newPosition.x = limitWidth; + } + if (position.y < 0) { + newPosition.y = 0; + } + if (position.y > limitHeight) { + newPosition.y = limitHeight; + } + return newPosition; + } + return position; +}; diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index d1e1c5f5..78aead3a 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -10,7 +10,9 @@ import { Group, Layer, Rect, Stage, Text } from "react-konva"; import { CircularProgress } from "@mui/material"; import { Ellipse, Polygon, Rectangle } from "components/Annotation"; import { + MAX_HEIGHT_EDITOR, MAX_HEIGHT_IMAGE_IN_EDITOR, + MAX_WIDTH_EDITOR, MAX_WIDTH_IMAGE_IN_EDITOR, } from "components/Annotation/Editor/const"; import Konva from "konva"; @@ -29,8 +31,6 @@ import { redoDrawObject, setIsDraggingViewpor, setKeyDownInEditor, - setMouseDownOutLayerPosition, - setMouseUpOutLayerPosition, setSelectedShape, undoDrawObject, } from "reduxes/annotation/action"; @@ -54,6 +54,7 @@ import { } from "../constants"; import BaseImage from "./BaseImage"; import DrawLayer from "./Layer/DrawLayer"; +import { Vector2d } from "konva/lib/types"; const TOOLTIP_WIDTH = 200; const TOOLTIP_HEIGHT = 40; @@ -328,20 +329,16 @@ function Editor() { } return handleMouseDown; }, [currentDrawState, currentDrawType]); - const handleMouseDownOutLayer = (event: React.MouseEvent) => { - dispatch( - setMouseDownOutLayerPosition({ - position: { x: event.clientX, y: event.clientY }, - }) - ); - }; - const handleMouseUpOutLayer = (event: React.MouseEvent) => { - dispatch( - setMouseUpOutLayerPosition({ - position: { x: event.clientX, y: event.clientY }, - }) - ); - }; + + const paddingStage = useMemo(() => { + if (stageProps) { + return { + paddingLeft: MAX_WIDTH_EDITOR - stageProps.width, + paddingTop: MAX_HEIGHT_EDITOR - stageProps.height, + }; + } + return { paddingLeft: 0, paddingTop: 0 }; + }, [currentAnnotationFile]); const renderContent = () => { if (isLoading) { return ( @@ -357,8 +354,6 @@ function Editor() { onKeyDown={keyDownHandler} onKeyUp={keyUpHandler} onMouseOver={mouseOverBoundDivHandler} - onMouseDown={handleMouseDownOutLayer} - onMouseUp={handleMouseUpOutLayer} id="annotation-editor-bound" width="100%" height="100%" @@ -376,19 +371,22 @@ function Editor() { ref={stageRef} scaleX={stageProps ? stageProps.scaleX : 1} scaleY={stageProps ? stageProps.scaleY : 1} - width={ - stageProps ? stageProps.width : MAX_WIDTH_IMAGE_IN_EDITOR - } - height={ - stageProps ? stageProps.height : MAX_HEIGHT_IMAGE_IN_EDITOR - } + width={MAX_WIDTH_EDITOR} + height={MAX_HEIGHT_EDITOR} onWheel={wheelHandler} onClick={selectHandleMouseDown} draggable={isDraggingViewport} > - - + + { if (issFetchingImageData) { return ( - + ); } return ( - + {({ height, width }) => ( - +

    List Label

    diff --git a/src/routes/AnnotationPage/constants.ts b/src/routes/AnnotationPage/constants.ts index 91052a33..ed73639b 100644 --- a/src/routes/AnnotationPage/constants.ts +++ b/src/routes/AnnotationPage/constants.ts @@ -8,3 +8,4 @@ export const QUIT_ANNOTATION_EDITOR_ALERT_MESSAGE = "If you change the preview image, all annotation already processed will be lost. Do you still want to CANCEL?"; export const QUIT_ANNOTATION_EDITOR_PROMPT_MESSAGE = "If you change the preview image, all annotation already processed will be lost. Do you still want to LEAVE?"; +export const FULL_PADDING_VALUE = 1000; From 594f14567c410ffcc9f48bb207a58acc62afd960 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Thu, 17 Nov 2022 17:25:35 +0700 Subject: [PATCH 29/67] update: off log (ignore) --- src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx index 5ee65fdb..90602363 100644 --- a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx @@ -150,7 +150,6 @@ function PolygonDrawLayer({ ); }); const mousedownHandler = (e?: KonvaEventObject) => { - console.log("mousedownHandler"); if (mousePosition) { setPoints([...points, mousePosition]); dispatch(changeCurrentDrawState({ drawState: DrawState.DRAWING })); From 093a2f70c539d063bd11825d6304a1978b075399 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Thu, 17 Nov 2022 22:05:46 +0700 Subject: [PATCH 30/67] update: add Tooltip note for every tool in toolbar --- src/components/TooltipToggleButton/index.tsx | 19 ++++++ .../AnnotationPage/ControlPanel/index.tsx | 59 ++++++++++--------- 2 files changed, 51 insertions(+), 27 deletions(-) create mode 100644 src/components/TooltipToggleButton/index.tsx diff --git a/src/components/TooltipToggleButton/index.tsx b/src/components/TooltipToggleButton/index.tsx new file mode 100644 index 00000000..e4c7a8db --- /dev/null +++ b/src/components/TooltipToggleButton/index.tsx @@ -0,0 +1,19 @@ +import ToggleButton, { ToggleButtonProps } from "@mui/material/ToggleButton"; +import Tooltip, { TooltipProps } from "@mui/material/Tooltip"; +import { forwardRef, VFC } from "react"; + +type TooltipToggleButtonProps = ToggleButtonProps & { + TooltipProps: Omit; +}; + +const TooltipToggleButton: VFC = forwardRef( + ({ TooltipProps, ...props }, ref) => { + return ( + + + + ); + } +); + +export default TooltipToggleButton; diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index 37703186..0b6d1c49 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -38,6 +38,7 @@ import { importAnnotationScaleAI, importFileAnnotationDaita, } from "components/Annotation/Formart"; +import TooltipToggleButton from "components/TooltipToggleButton"; import * as React from "react"; import { useDropzone } from "react-dropzone"; import { useDispatch, useSelector } from "react-redux"; @@ -77,6 +78,7 @@ import { import { selectorAnnotationCurrentProjectName } from "reduxes/annotationProject/selector"; import { DRAW_ELLIPSE_SHORT_KEY, + DRAW_SEGMENTATION_SHORT_KEY, DRAW_POLYGON_SHORT_KEY, DRAW_RECTANGLE_SHORT_KEY, SELECT_SHORT_KEY, @@ -469,16 +471,15 @@ function ControlPanel() { sx={{ border: "1px dashed grey" }} onChange={handleSelectDrawState} > - - - - - + + - - - - - - + + - - - - - + + + - - - - - + + - - - - + + @@ -556,7 +554,14 @@ function ControlPanel() { } > selectModeHandle(e, DrawType.DETECTED_RECTANGLE) } From e3e52086531ebaff86a0f8164aafcbe1f6c2c9b3 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Thu, 17 Nov 2022 22:45:07 +0700 Subject: [PATCH 31/67] update: add toast info after trigger clone project to annotation --- src/sagas/annotationProjectSaga.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sagas/annotationProjectSaga.tsx b/src/sagas/annotationProjectSaga.tsx index fe210491..bbdd915b 100644 --- a/src/sagas/annotationProjectSaga.tsx +++ b/src/sagas/annotationProjectSaga.tsx @@ -45,6 +45,9 @@ function* handleCloneProjectToAnnotation(action: { yield history.push( `/${ANNOTATION_PROJECT_DETAIL_ROUTE_NAME}/${action.payload.annotationProjectName}` ); + yield toast.info( + `We are automatically AI-segmenting your image. You will receive an automatic email notification when finished. The segmented images will be green-marked "AI" on the annotation web page.` + ); } else { yield put({ type: CLONE_PROJECT_TO_ANNOTATION.FAILED }); toast.error(updateProjectInfoResponse.message); From 323303457361cc49dfd25a5182ef64fa8ca50b51 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Thu, 17 Nov 2022 22:54:21 +0700 Subject: [PATCH 32/67] update: toast message text content --- src/sagas/annotationEditorSaga.tsx | 6 +++--- src/sagas/annotationProjectSaga.tsx | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sagas/annotationEditorSaga.tsx b/src/sagas/annotationEditorSaga.tsx index 8ed2c953..711be24c 100644 --- a/src/sagas/annotationEditorSaga.tsx +++ b/src/sagas/annotationEditorSaga.tsx @@ -242,7 +242,7 @@ function* handleFetchAnnotationAndFileInfo(action: any): any { yield put(addImagesToAnnotation({ annotationImagesProperties: [image] })); yield put(requestChangePreviewImageSuccess()); } else { - toast.error("Fail to get image data"); + toast.error("Failed to get image data."); yield put(requestChangePreviewImageFail()); } } catch (e: any) { @@ -266,7 +266,7 @@ function* handleAddNewClassLabel(action: any): any { ); if (saveLabelResp.error === false) { yield put(addNewClassLabel(action.payload)); - toast.success("Add new class success"); + toast.success("New class was successfully added."); } else { toast.error(saveLabelResp.message); } @@ -325,7 +325,7 @@ function* handleSaveAnnotationStateManager(action: any): any { }); if (saveLabelResponse.error !== true) { yield put({ type: SAVE_ANNOTATION_STATE_MANAGER.SUCCEEDED }); - toast.success("Save annotation success"); + toast.success("Annotation data is successfully saved."); } else { yield put({ type: SAVE_ANNOTATION_STATE_MANAGER.FAILED }); toast.error(saveLabelResponse.message); diff --git a/src/sagas/annotationProjectSaga.tsx b/src/sagas/annotationProjectSaga.tsx index bbdd915b..70f03743 100644 --- a/src/sagas/annotationProjectSaga.tsx +++ b/src/sagas/annotationProjectSaga.tsx @@ -76,7 +76,8 @@ function* handleFetchListProjects(action: any): any { type: FETCH_LIST_ANNOTATION_PROJECTS.FAILED, }); toast.error( - fetchListProjectsResponse.message || "Can't fetch list projects." + fetchListProjectsResponse.message || + "Can't fetch project list at the moment. Please try again later!" ); } } catch (e: any) { From 722a47eb7e24a6539c4811bb30175568b6e03347 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Thu, 17 Nov 2022 22:55:08 +0700 Subject: [PATCH 33/67] update: export file json name as\ image name --- src/components/Annotation/Formart/daita/index.ts | 2 +- src/components/Annotation/Formart/labelbox/index.ts | 7 +++++-- src/components/Annotation/Formart/labelme/index.ts | 7 +++++-- src/components/Annotation/Formart/scaleai/index.ts | 7 +++++-- src/routes/AnnotationPage/ControlPanel/index.tsx | 10 +++++++--- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/components/Annotation/Formart/daita/index.ts b/src/components/Annotation/Formart/daita/index.ts index 9a1e6c33..af831649 100644 --- a/src/components/Annotation/Formart/daita/index.ts +++ b/src/components/Annotation/Formart/daita/index.ts @@ -215,7 +215,7 @@ export const exportAnnotation = ( )}`; const link = document.createElement("a"); link.href = jsonString; - link.download = `${imageName}.json`; + link.download = `${imageName.replace(/\.[^.]+$/, ".json")}`; link.click(); }; diff --git a/src/components/Annotation/Formart/labelbox/index.ts b/src/components/Annotation/Formart/labelbox/index.ts index 9a912c6f..0a8ba0f3 100644 --- a/src/components/Annotation/Formart/labelbox/index.ts +++ b/src/components/Annotation/Formart/labelbox/index.ts @@ -49,7 +49,8 @@ export const convert = ( return shape; }; export const exportAnnotation = ( - drawObjectById: Record + drawObjectById: Record, + imageName: string | null ) => { const shapes: LabelLabelBox = convert(drawObjectById); const annotationFormatter: AnnotationFormatter = @@ -59,7 +60,9 @@ export const exportAnnotation = ( )}`; const link = document.createElement("a"); link.href = jsonString; - link.download = "data.json"; + link.download = imageName + ? `${imageName.replace(/\.[^.]+$/, ".json")}` + : "data.json"; link.click(); }; diff --git a/src/components/Annotation/Formart/labelme/index.ts b/src/components/Annotation/Formart/labelme/index.ts index c5de9f76..3504e408 100644 --- a/src/components/Annotation/Formart/labelme/index.ts +++ b/src/components/Annotation/Formart/labelme/index.ts @@ -171,7 +171,8 @@ export const convert = ( }; export const exportAnnotation = ( annotationImagesProperty: AnnotationImagesProperty, - drawObjectById: Record + drawObjectById: Record, + imageName: string | null ) => { const shapes: Shape[] = convert(drawObjectById); const { image } = annotationImagesProperty; @@ -187,7 +188,9 @@ export const exportAnnotation = ( )}`; const link = document.createElement("a"); link.href = jsonString; - link.download = "data.json"; + link.download = imageName + ? `${imageName.replace(/\.[^.]+$/, ".json")}` + : "data.json"; link.click(); }; diff --git a/src/components/Annotation/Formart/scaleai/index.ts b/src/components/Annotation/Formart/scaleai/index.ts index 5ee67eaf..3f65c250 100644 --- a/src/components/Annotation/Formart/scaleai/index.ts +++ b/src/components/Annotation/Formart/scaleai/index.ts @@ -85,7 +85,8 @@ export const importAnnotation = ( }; }); export const exportAnnotation = ( - drawObjectById: Record + drawObjectById: Record, + imageName: string | null ) => { const shapes: Shape[] = convert(drawObjectById); const annotationFormatter: AnnotationFormatter = @@ -95,7 +96,9 @@ export const exportAnnotation = ( )}`; const link = document.createElement("a"); link.href = jsonString; - link.download = "data.json"; + link.download = imageName + ? `${imageName.replace(/\.[^.]+$/, ".json")}` + : "data.json"; link.click(); }; diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index 0b6d1c49..9f692d70 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -164,19 +164,23 @@ function ControlPanel() { const handleExportLabelMe = () => { const drawObjectToExport = getDrawObjectToExport(); if (currentAnnotationFile && drawObjectToExport) { - exportAnnotationLabelMe(currentAnnotationFile, drawObjectToExport); + exportAnnotationLabelMe( + currentAnnotationFile, + drawObjectToExport, + currentPreviewImageName + ); } }; const handleExportScaleAI = () => { const drawObjectToExport = getDrawObjectToExport(); if (drawObjectToExport) { - exportAnnotationScaleAI(drawObjectToExport); + exportAnnotationScaleAI(drawObjectToExport, currentPreviewImageName); } }; const handleExportLabelBox = () => { const drawObjectToExport = getDrawObjectToExport(); if (drawObjectToExport) { - exportAnnotationLabelBox(drawObjectToExport); + exportAnnotationLabelBox(drawObjectToExport, currentPreviewImageName); } }; const handleExportDaita = () => { From ca88b278388bb0f2be62703830928162ac9e6e46 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Thu, 17 Nov 2022 23:57:43 +0700 Subject: [PATCH 34/67] update: dialog confirm change preview image --- .../ChangePreviewConfirmDialog.tsx | 103 ++++++++++++++++ .../AnnotationPage/ImagePreview/index.tsx | 113 ++++-------------- 2 files changed, 124 insertions(+), 92 deletions(-) create mode 100644 src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx diff --git a/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx b/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx new file mode 100644 index 00000000..d8196edd --- /dev/null +++ b/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx @@ -0,0 +1,103 @@ +import { LoadingButton } from "@mui/lab"; +import { Box, Typography } from "@mui/material"; +import Button from "@mui/material/Button"; +import Dialog from "@mui/material/Dialog"; +import DialogActions from "@mui/material/DialogActions"; +import DialogContent from "@mui/material/DialogContent"; +import DialogContentText from "@mui/material/DialogContentText"; +import DialogTitle from "@mui/material/DialogTitle"; +import * as React from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { resetCurrentStateDrawObject } from "reduxes/annotation/action"; +import { DrawObject } from "reduxes/annotation/type"; +import { + requestChangePreviewImage, + saveAnnotationStateManager, +} from "reduxes/annotationmanager/action"; +import { selectorIsSavingAnnotation } from "reduxes/annotationmanager/selecetor"; +import { QUIT_ANNOTATION_EDITOR_ALERT_MESSAGE } from "../constants"; + +function ChangePreviewConfirmDialog({ + isOpen, + drawObjectById, + onClose, + imageName, + nextPreviewImageName, +}: { + isOpen: boolean; + imageName: string; + nextPreviewImageName: string; + onClose: () => void; + drawObjectById: Record; +}) { + const dispatch = useDispatch(); + const [markSavedConfirm, setMarkSavedConfirm] = + React.useState(false); + const isSavingAnnotation = useSelector(selectorIsSavingAnnotation); + React.useEffect(() => { + if (isSavingAnnotation === false && markSavedConfirm === true) { + dispatch( + resetCurrentStateDrawObject({ + drawObjectById, + }) + ); + dispatch(requestChangePreviewImage({ imageName: nextPreviewImageName })); + setMarkSavedConfirm(false); + onClose(); + } + }, [isSavingAnnotation]); + + const handleSave = () => { + dispatch( + saveAnnotationStateManager({ + imageName, + drawObjectById, + }) + ); + setMarkSavedConfirm(true); + }; + const handleDontSave = () => { + dispatch( + resetCurrentStateDrawObject({ + drawObjectById, + }) + ); + dispatch(requestChangePreviewImage({ imageName: nextPreviewImageName })); + onClose(); + }; + + return ( + + Confirm + + + + {QUIT_ANNOTATION_EDITOR_ALERT_MESSAGE} + + + + + + Save + + + + + + ); +} +export default ChangePreviewConfirmDialog; diff --git a/src/routes/AnnotationPage/ImagePreview/index.tsx b/src/routes/AnnotationPage/ImagePreview/index.tsx index d0eee291..33a08ed1 100644 --- a/src/routes/AnnotationPage/ImagePreview/index.tsx +++ b/src/routes/AnnotationPage/ImagePreview/index.tsx @@ -1,7 +1,6 @@ import { Box, List, ListItem, Skeleton, Typography } from "@mui/material"; import { BeforeUnload } from "components"; -import useConfirmDialog from "hooks/useConfirmDialog"; -import { useEffect, useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { resetCurrentStateDrawObject } from "reduxes/annotation/action"; import { selectorAnnotationStatehHistory } from "reduxes/annotation/selector"; @@ -11,33 +10,20 @@ import { selectorIdDrawObjectByImageName, } from "reduxes/annotationmanager/selecetor"; import { selectorCurrentAnnotationFiles } from "reduxes/annotationProject/selector"; -import { - QUIT_ANNOTATION_EDITOR_ALERT_MESSAGE, - QUIT_ANNOTATION_EDITOR_PROMPT_MESSAGE, -} from "../constants"; +import { QUIT_ANNOTATION_EDITOR_PROMPT_MESSAGE } from "../constants"; +import ChangePreviewConfirmDialog from "./ChangePreviewConfirmDialog"; import ImagePreviewBadge from "./ImagePreviewBadge"; function ImagePreview() { const dispatch = useDispatch(); - // const annotationManagerImages = useSelector(selectorAnnotationManagerImages); - const currentPreviewImageName = useSelector(selectorCurrentPreviewImageName); const idDrawObjectByImageName = useSelector(selectorIdDrawObjectByImageName); const currentAnnotationFiles = useSelector(selectorCurrentAnnotationFiles); const annotationStatehHistory = useSelector(selectorAnnotationStatehHistory); - const { openConfirmDialog, closeConfirmDialog } = useConfirmDialog(); - - // useEffect(() => { - // if (currentAnnotationFiles) { - // currentAnnotationFiles.items.map((item) => { - // // dispatch( - // // addImagesToAnnotation({ - // // annotationImagesProperties: [{ image, width: 1920, height: 1208 }], - // // }) - // // ); - // }); - // } - // }, [currentAnnotationFiles]); + const [isOpenDiaglog, setIsOpenDiaglog] = useState(false); + const [nextPreviewImageName, setNextPreviewImageName] = useState< + string | null + >(null); useEffect(() => { if (currentAnnotationFiles) { @@ -109,40 +95,6 @@ function ImagePreview() { // }); }, []); - // const onDrop = (acceptedFiles: File[]) => { - // if (acceptedFiles && acceptedFiles.length > 0) { - // for (const file of acceptedFiles) { - // loadImage(file).then(({ image, fileName }) => { - // const property: AnnotationImagesProperty = { - // image: file, - // width: image.width, - // height: image.height, - // }; - // dispatch( - // addImagesToAnnotation({ annotationImagesProperties: [property] }) - // ); - // if (!currentPreviewImageName) { - // dispatch(changePreviewImage({ imageName: acceptedFiles[0].name })); - // } - // }); - // } - // } - // }; - // const dropZone = useDropzone({ - // onDrop, - // accept: IMAGE_EXTENSIONS.join(","), - // noDragEventsBubbling: true, - // }); - // const { getRootProps, isDragActive, getInputProps } = dropZone; - // const fileThumbByImageName = useMemo(() => { - // const thumbs: Record = {}; - // Object.entries(annotationManagerImages).map( - // ([imageName, annotationImagesProperty]) => { - // thumbs[imageName] = URL.createObjectURL(annotationImagesProperty.image); - // } - // ); - // return thumbs; - // }, [annotationManagerImages]); const needConfirmChangePreviewImageDialog = useMemo( () => annotationStatehHistory.stateHistoryItems[ @@ -168,27 +120,11 @@ function ImagePreview() { dispatch(requestChangePreviewImage({ imageName })); return; } - openConfirmDialog({ - content: ( - - {QUIT_ANNOTATION_EDITOR_ALERT_MESSAGE} - - ), - negativeText: "Cancel", - positiveText: "Don't Save", - onClickNegative: () => { - closeConfirmDialog(); - }, - onClickPositive: () => { - dispatch( - resetCurrentStateDrawObject({ - drawObjectById: idDrawObjectByImageName[imageName], - }) - ); - dispatch(requestChangePreviewImage({ imageName })); - closeConfirmDialog(); - }, - }); + setNextPreviewImageName(imageName); + setIsOpenDiaglog(true); + }; + const handleCloseDialog = () => { + setIsOpenDiaglog(false); }; const renderContent = () => { if (!currentAnnotationFiles) { @@ -196,22 +132,6 @@ function ImagePreview() { } return ( <> - {/* - - */} ))}
    + {currentPreviewImageName && nextPreviewImageName && ( + + )} ); }; From 431547065a18b617436bbb447a15b4667f8bb46e Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 00:07:58 +0700 Subject: [PATCH 35/67] update: hidden Attributes UI feature for annotation --- .../ImagePreview/ChangePreviewConfirmDialog.tsx | 2 +- .../ClassManageModal/useClassManageEditor.tsx | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx b/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx index d8196edd..e09ffd8c 100644 --- a/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx +++ b/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx @@ -73,7 +73,7 @@ function ChangePreviewConfirmDialog({ aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" > - Confirm + NOTIFICATION diff --git a/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useClassManageEditor.tsx b/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useClassManageEditor.tsx index e6a83486..864fe198 100644 --- a/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useClassManageEditor.tsx +++ b/src/routes/AnnotationPage/LabelAnnotation/ClassManageModal/useClassManageEditor.tsx @@ -34,7 +34,7 @@ function useClassManageEditor(): ClassManageDialogProps { selectorLabelClassPropertiesByLabelClass ); const dialogClassManageModal = useSelector(selectorDialogClassManageModal); - + const isHiddenAttribute = true; const { register, setValue, @@ -190,10 +190,14 @@ function useClassManageEditor(): ClassManageDialogProps { variant="standard" /> - - - - {renderPickColor()} + {!isHiddenAttribute && ( + <> + + + + {renderPickColor()} + + )}
    ), From bcc7a3455a031a45aa6718c32d0d98523ac6a694 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 14:13:59 +0700 Subject: [PATCH 36/67] fix: position small image in canvas --- src/components/Annotation/Editor/const.ts | 2 +- src/routes/AnnotationPage/AnnotationEditor.tsx | 2 +- src/routes/AnnotationPage/Editor/index.tsx | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/Annotation/Editor/const.ts b/src/components/Annotation/Editor/const.ts index f702603f..c430e072 100644 --- a/src/components/Annotation/Editor/const.ts +++ b/src/components/Annotation/Editor/const.ts @@ -15,4 +15,4 @@ export const CORNER_RADIUS = 5; export const MAX_WIDTH_IMAGE_IN_EDITOR = 1000; export const MAX_HEIGHT_IMAGE_IN_EDITOR = 700; export const MAX_WIDTH_EDITOR = 1270; -export const MAX_HEIGHT_EDITOR = 860; +export const MAX_HEIGHT_EDITOR = 825; diff --git a/src/routes/AnnotationPage/AnnotationEditor.tsx b/src/routes/AnnotationPage/AnnotationEditor.tsx index f5ebb2b6..05555ed9 100644 --- a/src/routes/AnnotationPage/AnnotationEditor.tsx +++ b/src/routes/AnnotationPage/AnnotationEditor.tsx @@ -77,7 +77,7 @@ const AnnotationEditor = function () { diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index 78aead3a..8c7d5aa8 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -333,8 +333,10 @@ function Editor() { const paddingStage = useMemo(() => { if (stageProps) { return { - paddingLeft: MAX_WIDTH_EDITOR - stageProps.width, - paddingTop: MAX_HEIGHT_EDITOR - stageProps.height, + paddingLeft: + (MAX_WIDTH_EDITOR - stageProps.width) / stageProps.scaleX / 2.0, + paddingTop: + (MAX_HEIGHT_EDITOR - stageProps.height) / stageProps.scaleY / 2.0, }; } return { paddingLeft: 0, paddingTop: 0 }; From 33f20dcb5c0833e5599eac6ee2a5a979e93acffa Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 14:38:19 +0700 Subject: [PATCH 37/67] update: file export name --- src/components/Annotation/Formart/daita/index.ts | 2 +- src/components/Annotation/Formart/labelbox/index.ts | 2 +- src/components/Annotation/Formart/labelme/index.ts | 2 +- src/components/Annotation/Formart/scaleai/index.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Annotation/Formart/daita/index.ts b/src/components/Annotation/Formart/daita/index.ts index af831649..00154bcc 100644 --- a/src/components/Annotation/Formart/daita/index.ts +++ b/src/components/Annotation/Formart/daita/index.ts @@ -215,7 +215,7 @@ export const exportAnnotation = ( )}`; const link = document.createElement("a"); link.href = jsonString; - link.download = `${imageName.replace(/\.[^.]+$/, ".json")}`; + link.download = `${imageName.replace(/\.[^.]+$/, "-Daita")}.json`; link.click(); }; diff --git a/src/components/Annotation/Formart/labelbox/index.ts b/src/components/Annotation/Formart/labelbox/index.ts index 0a8ba0f3..b0f6a793 100644 --- a/src/components/Annotation/Formart/labelbox/index.ts +++ b/src/components/Annotation/Formart/labelbox/index.ts @@ -61,7 +61,7 @@ export const exportAnnotation = ( const link = document.createElement("a"); link.href = jsonString; link.download = imageName - ? `${imageName.replace(/\.[^.]+$/, ".json")}` + ? `${imageName.replace(/\.[^.]+$/, "-LabelBox")}.json` : "data.json"; link.click(); diff --git a/src/components/Annotation/Formart/labelme/index.ts b/src/components/Annotation/Formart/labelme/index.ts index 3504e408..3add0380 100644 --- a/src/components/Annotation/Formart/labelme/index.ts +++ b/src/components/Annotation/Formart/labelme/index.ts @@ -189,7 +189,7 @@ export const exportAnnotation = ( const link = document.createElement("a"); link.href = jsonString; link.download = imageName - ? `${imageName.replace(/\.[^.]+$/, ".json")}` + ? `${imageName.replace(/\.[^.]+$/, "-LabelMe")}.json` : "data.json"; link.click(); diff --git a/src/components/Annotation/Formart/scaleai/index.ts b/src/components/Annotation/Formart/scaleai/index.ts index 3f65c250..930c3ab1 100644 --- a/src/components/Annotation/Formart/scaleai/index.ts +++ b/src/components/Annotation/Formart/scaleai/index.ts @@ -97,7 +97,7 @@ export const exportAnnotation = ( const link = document.createElement("a"); link.href = jsonString; link.download = imageName - ? `${imageName.replace(/\.[^.]+$/, ".json")}` + ? `${imageName.replace(/\.[^.]+$/, "-ScaleAI")}.json` : "data.json"; link.click(); From 338f3778740f1fe668203e7c15617db2755c60c9 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 15:18:52 +0700 Subject: [PATCH 38/67] update: expand dummy rectangle draw layer --- src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx | 4 ++-- src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx | 5 +++-- .../AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx | 4 ++-- src/routes/AnnotationPage/constants.ts | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx index e75349f1..0e5234ce 100644 --- a/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/EllipseDrawLayer.tsx @@ -94,8 +94,8 @@ function EllipseDrawLayer({ ); } diff --git a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx index 90602363..a50c61fb 100644 --- a/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/PolygonDrawLayer.tsx @@ -181,13 +181,14 @@ function PolygonDrawLayer({ ); } return null; }; + useEffect(() => { const flatPoints: number[] = points .concat(isFinished || !mousePosition ? [] : mousePosition) diff --git a/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx index 0fedf879..083cf93e 100644 --- a/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/RectangleDrawLayer.tsx @@ -78,8 +78,8 @@ function RectangleDrawLayer({ ); } diff --git a/src/routes/AnnotationPage/constants.ts b/src/routes/AnnotationPage/constants.ts index ed73639b..27b42ff7 100644 --- a/src/routes/AnnotationPage/constants.ts +++ b/src/routes/AnnotationPage/constants.ts @@ -8,4 +8,4 @@ export const QUIT_ANNOTATION_EDITOR_ALERT_MESSAGE = "If you change the preview image, all annotation already processed will be lost. Do you still want to CANCEL?"; export const QUIT_ANNOTATION_EDITOR_PROMPT_MESSAGE = "If you change the preview image, all annotation already processed will be lost. Do you still want to LEAVE?"; -export const FULL_PADDING_VALUE = 1000; +export const FULL_PADDING_VALUE = 10000; From 95e47dfa7bb492b9e4a2b24ec4ee58c721591551 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 15:42:32 +0700 Subject: [PATCH 39/67] add: loading text --- src/components/CheckingApp/index.tsx | 4 +++- src/components/InviteFriendModal/index.tsx | 9 +++++++++ src/routes/AnnotationPage/AnnotationEditor.tsx | 12 ++++++++++-- src/routes/AnnotationPage/Editor/index.tsx | 17 +++++++++++++++-- .../AnnotationPage/LabelAnnotation/index.tsx | 18 ++++++++++++++++-- src/routes/AnnotationPage/index.tsx | 6 ++++-- src/routes/AnnotationProject/index.tsx | 9 ++++++++- src/routes/AnnotationProjectDetail/index.tsx | 9 +++++++++ src/routes/DashboardPage/ProjectList.tsx | 17 ++++++++++++++++- .../HealthCheckMainContent/index.tsx | 9 +++++++-- src/routes/HealthCheckPage/index.tsx | 9 ++++++++- src/routes/ProjectDetail/index.tsx | 16 +++++++++++++++- 12 files changed, 120 insertions(+), 15 deletions(-) diff --git a/src/components/CheckingApp/index.tsx b/src/components/CheckingApp/index.tsx index 8b86a91e..55944ada 100644 --- a/src/components/CheckingApp/index.tsx +++ b/src/components/CheckingApp/index.tsx @@ -1,4 +1,4 @@ -import { Box, CircularProgress } from "@mui/material"; +import { Box, CircularProgress, Typography } from "@mui/material"; import { CREDENTIAL_TOKEN_EXPIRE_NAME, LAST_USED_SYSTEM_STORAGE_KEY_NAME, @@ -154,12 +154,14 @@ const CheckingApp = function ({ children }: TokenCheckingProps) { return ( + Checking application status... ); } diff --git a/src/components/InviteFriendModal/index.tsx b/src/components/InviteFriendModal/index.tsx index ffb3ebf0..0a389c11 100644 --- a/src/components/InviteFriendModal/index.tsx +++ b/src/components/InviteFriendModal/index.tsx @@ -122,11 +122,20 @@ const InviteFriendModal = function () { {isFetchingInviteEmailTemplate ? ( + + Fetching email content... + ) : ( + + Initializing annotation editor... ); }; diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index d1e1c5f5..a291f78b 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -7,7 +7,7 @@ import { KonvaEventObject } from "konva/lib/Node"; import { KeyboardEvent, useEffect, useMemo, useRef, useState } from "react"; import { Group, Layer, Rect, Stage, Text } from "react-konva"; -import { CircularProgress } from "@mui/material"; +import { CircularProgress, Typography } from "@mui/material"; import { Ellipse, Polygon, Rectangle } from "components/Annotation"; import { MAX_HEIGHT_IMAGE_IN_EDITOR, @@ -345,8 +345,21 @@ function Editor() { const renderContent = () => { if (isLoading) { return ( - + + + Fetching image and segmentation information... + ); } diff --git a/src/routes/AnnotationPage/LabelAnnotation/index.tsx b/src/routes/AnnotationPage/LabelAnnotation/index.tsx index 87ba6b94..4b1bcab8 100644 --- a/src/routes/AnnotationPage/LabelAnnotation/index.tsx +++ b/src/routes/AnnotationPage/LabelAnnotation/index.tsx @@ -1,5 +1,5 @@ import AddIcon from "@mui/icons-material/Add"; -import { Button, CircularProgress } from "@mui/material"; +import { Button, CircularProgress, Typography } from "@mui/material"; import Box from "@mui/material/Box"; import { CSSProperties, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -54,8 +54,22 @@ function LabelAnnotation() { const renderList = () => { if (issFetchingImageData) { return ( - + + + Fetching labels information... + ); } diff --git a/src/routes/AnnotationPage/index.tsx b/src/routes/AnnotationPage/index.tsx index 3d0a7240..33a28bc8 100644 --- a/src/routes/AnnotationPage/index.tsx +++ b/src/routes/AnnotationPage/index.tsx @@ -1,4 +1,4 @@ -import { Box, CircularProgress } from "@mui/material"; +import { Box, CircularProgress, Typography } from "@mui/material"; import { ID_TOKEN_NAME } from "constants/defaultValues"; import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -36,12 +36,14 @@ const AnnotationPage = function () { return ( + Fetching project information... ); }; diff --git a/src/routes/AnnotationProject/index.tsx b/src/routes/AnnotationProject/index.tsx index 20cc09a0..95da4b62 100644 --- a/src/routes/AnnotationProject/index.tsx +++ b/src/routes/AnnotationProject/index.tsx @@ -108,7 +108,14 @@ function annotationProject() { py={6} > - Fetching project informations... + + Fetching project information... + ); } diff --git a/src/routes/AnnotationProjectDetail/index.tsx b/src/routes/AnnotationProjectDetail/index.tsx index 32e65dda..75341ece 100644 --- a/src/routes/AnnotationProjectDetail/index.tsx +++ b/src/routes/AnnotationProjectDetail/index.tsx @@ -239,12 +239,21 @@ const annotationProjectDetail = function () { return ( + + Fetching project information... + ); } diff --git a/src/routes/DashboardPage/ProjectList.tsx b/src/routes/DashboardPage/ProjectList.tsx index b4123240..3f00110a 100644 --- a/src/routes/DashboardPage/ProjectList.tsx +++ b/src/routes/DashboardPage/ProjectList.tsx @@ -314,8 +314,23 @@ const ProjectList = function () { if (isFetchingProjects === null || isFetchingProjects === true) { return ( - + + + Fetching projects information... + ); } diff --git a/src/routes/HealthCheckPage/HealthCheckMainContent/index.tsx b/src/routes/HealthCheckPage/HealthCheckMainContent/index.tsx index 2af5af37..93f2f07e 100644 --- a/src/routes/HealthCheckPage/HealthCheckMainContent/index.tsx +++ b/src/routes/HealthCheckPage/HealthCheckMainContent/index.tsx @@ -165,8 +165,13 @@ const HealthCheckMainContent = function ({ flexDirection="column" > - - Fetching Dataset Health Check informations... + + Fetching dataset health check information... ); diff --git a/src/routes/HealthCheckPage/index.tsx b/src/routes/HealthCheckPage/index.tsx index 61f2c757..c7c11111 100644 --- a/src/routes/HealthCheckPage/index.tsx +++ b/src/routes/HealthCheckPage/index.tsx @@ -71,7 +71,14 @@ const HealthCheckPage = function () { py={6} > - Fetching project informations... + + Fetching project information... + ); } diff --git a/src/routes/ProjectDetail/index.tsx b/src/routes/ProjectDetail/index.tsx index 791e5607..d5a95668 100644 --- a/src/routes/ProjectDetail/index.tsx +++ b/src/routes/ProjectDetail/index.tsx @@ -356,8 +356,22 @@ const ProjectDetail = function () { {isFetchingDetailProject === null || isFetchingDetailProject ? ( - + + + Fetching project information... + ) : ( renderUploadFile() From a382ce7eb6ca5fb83e535ed1423c4e4c74ee87a9 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 16:02:41 +0700 Subject: [PATCH 40/67] fix: clear button on class assign input exceed the width --- .../AnnotationPage/LabelAnnotation/ClassItem.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/routes/AnnotationPage/LabelAnnotation/ClassItem.tsx b/src/routes/AnnotationPage/LabelAnnotation/ClassItem.tsx index f0854465..29c5b3ef 100644 --- a/src/routes/AnnotationPage/LabelAnnotation/ClassItem.tsx +++ b/src/routes/AnnotationPage/LabelAnnotation/ClassItem.tsx @@ -145,15 +145,13 @@ function ClassItem({ style, id }: ClassItemProps) { ); const renderContent = () => ( - handleSelect()} - secondaryAction={renderSecondaryAction()} - sx={renderCss()} - > + handleSelect()} sx={renderCss()}> {renderListAvatar()} - + - + + + {renderSecondaryAction()} ); return ( From 42d2b13763d8ce50b507f4f0ff8c531be558266f Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 16:02:56 +0700 Subject: [PATCH 41/67] fix: "Daita" to "DAITA" --- src/routes/AnnotationPage/ControlPanel/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index 37703186..ba734a33 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -378,7 +378,7 @@ function ControlPanel() { setImportType("DAITA"); }} > - +
    @@ -404,7 +404,7 @@ function ControlPanel() { - + From 289441890ac36fa6a76d855abbf5c955621ba412 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 16:16:26 +0700 Subject: [PATCH 42/67] update: auth form spacing and move recaptcha badge inline --- src/components/ReCaptchaInput/index.tsx | 1 + src/routes/ForgotPasswordPage/index.tsx | 4 ++-- src/routes/RegisterPage/UserInfoForm.tsx | 3 +-- src/routes/RegisterPage/index.tsx | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/ReCaptchaInput/index.tsx b/src/components/ReCaptchaInput/index.tsx index 8069b6db..ff8273e5 100644 --- a/src/components/ReCaptchaInput/index.tsx +++ b/src/components/ReCaptchaInput/index.tsx @@ -37,6 +37,7 @@ const ReCaptchaInput = function ({ ref={recaptchaRef} sitekey={RECAPTCHA_SITE_KEY} size="invisible" + badge="inline" /> {/* NOTE: We use invisible recaptcha is auto generate when submitted, therefore no need show required message */} {error && error.message !== REQUIRE_RECAPTCHA_ERROR_MESSAGE && ( diff --git a/src/routes/ForgotPasswordPage/index.tsx b/src/routes/ForgotPasswordPage/index.tsx index 19cd4274..be11a7c5 100644 --- a/src/routes/ForgotPasswordPage/index.tsx +++ b/src/routes/ForgotPasswordPage/index.tsx @@ -149,7 +149,7 @@ const ForgotPasswordPage = function () { )} - + - + From 13324fdf4064e243ae46dc799c17c190055ad2f4 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 16:31:37 +0700 Subject: [PATCH 43/67] update: disable cache annotation file --- src/reduxes/annotation/reducer.ts | 6 +++--- src/reduxes/annotationmanager/type.ts | 3 --- src/routes/AnnotationPage/Editor/index.tsx | 11 +++++++---- .../ImagePreview/ChangePreviewConfirmDialog.tsx | 12 ++++++------ src/routes/AnnotationPage/ImagePreview/index.tsx | 1 - src/sagas/annotationEditorSaga.tsx | 1 + 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/reduxes/annotation/reducer.ts b/src/reduxes/annotation/reducer.ts index 943674d1..34cc2bad 100644 --- a/src/reduxes/annotation/reducer.ts +++ b/src/reduxes/annotation/reducer.ts @@ -1,5 +1,5 @@ import { PolygonSpec } from "components/Annotation/Editor/type"; -import { SAVE_REMOTE_NEW_CLASS_LABEL } from "reduxes/annotationmanager/constants"; +import { SAVE_ANNOTATION_STATE_MANAGER } from "reduxes/annotationmanager/constants"; import { v4 as uuidv4 } from "uuid"; import { ADD_DRAW_OBJECTS_BY_AI, @@ -453,14 +453,14 @@ const annotationReducer = ( const { keyDownInEditor } = payload as SetKeyDownPayload; return { ...state, keyDownInEditor }; } - case SAVE_REMOTE_NEW_CLASS_LABEL.SUCCEEDED: { + case SAVE_ANNOTATION_STATE_MANAGER.SUCCEEDED: { const history = state.statehHistory; return { ...state, statehHistory: { ...history, savedStateHistoryId: - history.stateHistoryItems[history.historyStep].id, + history.stateHistoryItems[history.historyStep - 1].id, }, }; } diff --git a/src/reduxes/annotationmanager/type.ts b/src/reduxes/annotationmanager/type.ts index 0c506776..d1c0f057 100644 --- a/src/reduxes/annotationmanager/type.ts +++ b/src/reduxes/annotationmanager/type.ts @@ -44,9 +44,6 @@ export interface EditClassLabelProps { label: string; labelClassProperties: LabelClassProperties; } -export interface EditClassManageModalProps { - className: string; -} export interface FetchingFileAndAnnotaitonProps { filename: string; fileId: string; diff --git a/src/routes/AnnotationPage/Editor/index.tsx b/src/routes/AnnotationPage/Editor/index.tsx index 8c7d5aa8..52e2d4dc 100644 --- a/src/routes/AnnotationPage/Editor/index.tsx +++ b/src/routes/AnnotationPage/Editor/index.tsx @@ -43,7 +43,10 @@ import { selectorZoom, } from "reduxes/annotation/selector"; import { DrawObject, DrawState, DrawType } from "reduxes/annotation/type"; -import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor"; +import { + selectorCurrentAnnotationFile, + selectorIsFetchingImageData, +} from "reduxes/annotationmanager/selecetor"; import { DRAW_ELLIPSE_SHORT_KEY, DRAW_LINE_SHORT_KEY, @@ -54,7 +57,6 @@ import { } from "../constants"; import BaseImage from "./BaseImage"; import DrawLayer from "./Layer/DrawLayer"; -import { Vector2d } from "konva/lib/types"; const TOOLTIP_WIDTH = 200; const TOOLTIP_HEIGHT = 40; @@ -69,6 +71,7 @@ function Editor() { const refTextPosition = useRef(null); const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile); + const isFetchingImageData = useSelector(selectorIsFetchingImageData); const drawObjectById = useSelector(selectorDrawObjectById); const selectedDrawObjectId = useSelector(selectorSelectedDrawObjectId); const currentDrawState = useSelector(selectorCurrentDrawState); @@ -313,11 +316,11 @@ function Editor() { } }, [zoom]); const isLoading = useMemo(() => { - if (!currentAnnotationFile) { + if (!currentAnnotationFile || isFetchingImageData === true) { return true; } return currentAnnotationFile.image === null; - }, [currentAnnotationFile]); + }, [currentAnnotationFile, isFetchingImageData]); const handleMouseDown = (e: KonvaEventObject) => { dispatch(setSelectedShape({ selectedDrawObjectId: null })); e.evt.preventDefault(); diff --git a/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx b/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx index e09ffd8c..dad2bb9e 100644 --- a/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx +++ b/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx @@ -9,17 +9,17 @@ import DialogTitle from "@mui/material/DialogTitle"; import * as React from "react"; import { useDispatch, useSelector } from "react-redux"; import { resetCurrentStateDrawObject } from "reduxes/annotation/action"; -import { DrawObject } from "reduxes/annotation/type"; +import { selectorDrawObjectById } from "reduxes/annotation/selector"; import { requestChangePreviewImage, saveAnnotationStateManager, } from "reduxes/annotationmanager/action"; import { selectorIsSavingAnnotation } from "reduxes/annotationmanager/selecetor"; import { QUIT_ANNOTATION_EDITOR_ALERT_MESSAGE } from "../constants"; +import SaveIcon from "@mui/icons-material/Save"; function ChangePreviewConfirmDialog({ isOpen, - drawObjectById, onClose, imageName, nextPreviewImageName, @@ -28,11 +28,12 @@ function ChangePreviewConfirmDialog({ imageName: string; nextPreviewImageName: string; onClose: () => void; - drawObjectById: Record; }) { const dispatch = useDispatch(); const [markSavedConfirm, setMarkSavedConfirm] = React.useState(false); + const drawObjectById = useSelector(selectorDrawObjectById); + const isSavingAnnotation = useSelector(selectorIsSavingAnnotation); React.useEffect(() => { if (isSavingAnnotation === false && markSavedConfirm === true) { @@ -76,9 +77,7 @@ function ChangePreviewConfirmDialog({ NOTIFICATION - - {QUIT_ANNOTATION_EDITOR_ALERT_MESSAGE} - + {QUIT_ANNOTATION_EDITOR_ALERT_MESSAGE} @@ -88,6 +87,7 @@ function ChangePreviewConfirmDialog({ loadingPosition="end" variant="contained" color="primary" + endIcon={} sx={{ mr: 2, backgroundColor: "#888c94c4 !important", width: 100 }} > Save diff --git a/src/routes/AnnotationPage/ImagePreview/index.tsx b/src/routes/AnnotationPage/ImagePreview/index.tsx index 33a08ed1..d35bc0f4 100644 --- a/src/routes/AnnotationPage/ImagePreview/index.tsx +++ b/src/routes/AnnotationPage/ImagePreview/index.tsx @@ -188,7 +188,6 @@ function ImagePreview() { {currentPreviewImageName && nextPreviewImageName && ( Date: Fri, 18 Nov 2022 16:36:15 +0700 Subject: [PATCH 44/67] update: change cursor to "not-allowed" when uploading image --- src/components/UploadFile/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/UploadFile/index.tsx b/src/components/UploadFile/index.tsx index 6f3609b9..eabba7c0 100644 --- a/src/components/UploadFile/index.tsx +++ b/src/components/UploadFile/index.tsx @@ -460,7 +460,7 @@ const UploadFile = function (props: UploadFileProps) { border: "1px dashed", borderColor: isDragActive ? "primary.main" : "text.secondary", borderWidth: isDragActive ? 2 : 1, - cursor: "pointer", + cursor: isDisabledUpload ? "not-allowed" : "pointer", borderRadius: "6px", }} display="flex" From 73a1579cf09810df1df0eaf7faaf0f29d15df9c7 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 17:21:21 +0700 Subject: [PATCH 45/67] fix: wrong limit max size text --- src/components/FeedbackComponent/FeedbackForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FeedbackComponent/FeedbackForm.tsx b/src/components/FeedbackComponent/FeedbackForm.tsx index 8cf386c9..80bef77e 100644 --- a/src/components/FeedbackComponent/FeedbackForm.tsx +++ b/src/components/FeedbackComponent/FeedbackForm.tsx @@ -207,7 +207,7 @@ export const FeedbackForm = function ({ color="text.secondary" display="inline-block" > - File types supported: JPG, PNG, Max size: 5 MB + File types supported: JPG, PNG, Max size: 2 MB Date: Fri, 18 Nov 2022 17:29:03 +0700 Subject: [PATCH 46/67] fix: detected rectangle draw layer --- src/components/TooltipToggleButton/index.tsx | 12 +++++------- .../Editor/Layer/DetectedRectangleDrawLayer.tsx | 5 ++--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/TooltipToggleButton/index.tsx b/src/components/TooltipToggleButton/index.tsx index e4c7a8db..c3bd6eea 100644 --- a/src/components/TooltipToggleButton/index.tsx +++ b/src/components/TooltipToggleButton/index.tsx @@ -7,13 +7,11 @@ type TooltipToggleButtonProps = ToggleButtonProps & { }; const TooltipToggleButton: VFC = forwardRef( - ({ TooltipProps, ...props }, ref) => { - return ( - - - - ); - } + ({ TooltipProps, ...props }, ref) => ( + + + + ) ); export default TooltipToggleButton; diff --git a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx index 78dfcffe..26185675 100644 --- a/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx +++ b/src/routes/AnnotationPage/Editor/Layer/DetectedRectangleDrawLayer.tsx @@ -90,14 +90,13 @@ function DetectedRectangleDrawLayer({ ); } return null; }; - return ( Date: Fri, 18 Nov 2022 17:39:42 +0700 Subject: [PATCH 47/67] fix: not limit the height of annotation editor --- src/routes/AnnotationPage/AnnotationEditor.tsx | 5 ++--- src/routes/AnnotationPage/ImagePreview/index.tsx | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/routes/AnnotationPage/AnnotationEditor.tsx b/src/routes/AnnotationPage/AnnotationEditor.tsx index 05555ed9..41ae93ee 100644 --- a/src/routes/AnnotationPage/AnnotationEditor.tsx +++ b/src/routes/AnnotationPage/AnnotationEditor.tsx @@ -53,8 +53,8 @@ const AnnotationEditor = function () { currentAnnotationFiles.projectId === annotationCurrentProject.project_id ) { return ( - - + + @@ -77,7 +77,6 @@ const AnnotationEditor = function () { diff --git a/src/routes/AnnotationPage/ImagePreview/index.tsx b/src/routes/AnnotationPage/ImagePreview/index.tsx index d35bc0f4..08ab8d36 100644 --- a/src/routes/AnnotationPage/ImagePreview/index.tsx +++ b/src/routes/AnnotationPage/ImagePreview/index.tsx @@ -141,7 +141,7 @@ function ImagePreview() { overflow: "auto", display: "flex", flexDirection: "row", - padding: 0, + py: 2, }} > {currentAnnotationFiles.items.map((item) => ( From c62c050f5e490de800f301a0f3c367de3f8d1aa1 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 17:41:10 +0700 Subject: [PATCH 48/67] fix: daita uppercase --- src/components/Annotation/Formart/daita/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Annotation/Formart/daita/index.ts b/src/components/Annotation/Formart/daita/index.ts index 00154bcc..5de9695c 100644 --- a/src/components/Annotation/Formart/daita/index.ts +++ b/src/components/Annotation/Formart/daita/index.ts @@ -215,7 +215,7 @@ export const exportAnnotation = ( )}`; const link = document.createElement("a"); link.href = jsonString; - link.download = `${imageName.replace(/\.[^.]+$/, "-Daita")}.json`; + link.download = `${imageName.replace(/\.[^.]+$/, "-DAITA")}.json`; link.click(); }; From ca8f77a48b66fb84cd1fa3db55174726fb45c059 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 17:45:19 +0700 Subject: [PATCH 49/67] update: change draw line shortkey --- src/routes/AnnotationPage/ControlPanel/index.tsx | 3 ++- src/routes/AnnotationPage/constants.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/routes/AnnotationPage/ControlPanel/index.tsx b/src/routes/AnnotationPage/ControlPanel/index.tsx index 9f692d70..df4aa282 100644 --- a/src/routes/AnnotationPage/ControlPanel/index.tsx +++ b/src/routes/AnnotationPage/ControlPanel/index.tsx @@ -82,6 +82,7 @@ import { DRAW_POLYGON_SHORT_KEY, DRAW_RECTANGLE_SHORT_KEY, SELECT_SHORT_KEY, + DRAW_LINE_SHORT_KEY, } from "../constants"; import { convertStrokeColorToFillColor } from "../LabelAnnotation/ClassLabel"; import { @@ -520,7 +521,7 @@ function ControlPanel() { Date: Fri, 18 Nov 2022 17:47:09 +0700 Subject: [PATCH 50/67] update: change color of save prompt dialog --- .../ImagePreview/ChangePreviewConfirmDialog.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx b/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx index dad2bb9e..22d474c5 100644 --- a/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx +++ b/src/routes/AnnotationPage/ImagePreview/ChangePreviewConfirmDialog.tsx @@ -88,14 +88,20 @@ function ChangePreviewConfirmDialog({ variant="contained" color="primary" endIcon={} - sx={{ mr: 2, backgroundColor: "#888c94c4 !important", width: 100 }} + sx={{ mr: 2, width: 100 }} > Save + - ); From 16aa74a0f06a1db99e73a70be7ca0e074e0eba50 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 21:53:20 +0700 Subject: [PATCH 51/67] update: change the maximum characters of project description --- src/components/CreateProjectModal/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/CreateProjectModal/index.tsx b/src/components/CreateProjectModal/index.tsx index 05be360f..dd503f8f 100644 --- a/src/components/CreateProjectModal/index.tsx +++ b/src/components/CreateProjectModal/index.tsx @@ -312,8 +312,8 @@ const CreateProjectModal = function (props: CreateProjectModalProps) { {...register("description", { required: false, maxLength: { - value: 75, - message: `Your project description cannot exceed 75 characters.`, + value: 300, + message: `Your project description cannot exceed 300 characters.`, }, })} error={!!errors.description} From ae8947773b6265f5ad063b924039aa24ce788182 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 22:49:37 +0700 Subject: [PATCH 52/67] update: change the "Generate the Reference Images" tooltip text --- .../PreprocessingOption/ExpertPreprocessOption.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/ProjectDetail/PreprocessingOption/ExpertPreprocessOption.tsx b/src/routes/ProjectDetail/PreprocessingOption/ExpertPreprocessOption.tsx index bbba7581..6fd90cd6 100644 --- a/src/routes/ProjectDetail/PreprocessingOption/ExpertPreprocessOption.tsx +++ b/src/routes/ProjectDetail/PreprocessingOption/ExpertPreprocessOption.tsx @@ -211,7 +211,7 @@ const ExpertPreprocessingOption = function () { From ae603b20e0eb815dbfe3f87a125fa8f6753aa87a Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 22:55:25 +0700 Subject: [PATCH 53/67] update: change the unsave annotation alert message --- src/routes/AnnotationPage/ImagePreview/index.tsx | 7 ++----- src/routes/AnnotationPage/constants.ts | 4 +--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/routes/AnnotationPage/ImagePreview/index.tsx b/src/routes/AnnotationPage/ImagePreview/index.tsx index d0eee291..7f2d0457 100644 --- a/src/routes/AnnotationPage/ImagePreview/index.tsx +++ b/src/routes/AnnotationPage/ImagePreview/index.tsx @@ -11,10 +11,7 @@ import { selectorIdDrawObjectByImageName, } from "reduxes/annotationmanager/selecetor"; import { selectorCurrentAnnotationFiles } from "reduxes/annotationProject/selector"; -import { - QUIT_ANNOTATION_EDITOR_ALERT_MESSAGE, - QUIT_ANNOTATION_EDITOR_PROMPT_MESSAGE, -} from "../constants"; +import { QUIT_ANNOTATION_EDITOR_ALERT_MESSAGE } from "../constants"; import ImagePreviewBadge from "./ImagePreviewBadge"; function ImagePreview() { @@ -214,7 +211,7 @@ function ImagePreview() { */} Date: Fri, 18 Nov 2022 23:13:10 +0700 Subject: [PATCH 54/67] update: change the package.json version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 81d2a00c..252e26fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "daita-interface", - "version": "0.1.0", + "version": "1.1.0", "private": true, "description": "React-based user interface for the DAITA platform.", "author": "DAITA Technologies", From b07d7187eb170fad1815b566fc1ba2d9714e3856 Mon Sep 17 00:00:00 2001 From: locpnh1995 Date: Fri, 18 Nov 2022 23:58:07 +0700 Subject: [PATCH 55/67] update: increase project description maxium character length up to 300 --- src/components/CloneProjectModal/index.tsx | 5 +++-- src/components/CreateProjectModal/index.tsx | 5 +++-- src/constants/defaultValues.tsx | 2 ++ src/routes/DashboardPage/UpdateProjectInfoDialog.tsx | 11 +++++++---- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/components/CloneProjectModal/index.tsx b/src/components/CloneProjectModal/index.tsx index 32667781..884643c8 100644 --- a/src/components/CloneProjectModal/index.tsx +++ b/src/components/CloneProjectModal/index.tsx @@ -23,6 +23,7 @@ import { } from "reduxes/annotationProject/selector"; import { useEffect } from "react"; import { selectorListProjects } from "reduxes/project/selector"; +import { MAX_PROJECT_DESCRIPTION_CHARACTER_LENGTH } from "constants/defaultValues"; import { CloneProjectToAnnotationFields } from "./type"; const CloneProjectModal = function () { @@ -162,8 +163,8 @@ const CloneProjectModal = function () { {...register("annotationProjectDescription", { required: false, maxLength: { - value: 75, - message: `Your project description cannot exceed 75 characters.`, + value: MAX_PROJECT_DESCRIPTION_CHARACTER_LENGTH, + message: `Your project description cannot exceed ${MAX_PROJECT_DESCRIPTION_CHARACTER_LENGTH} characters.`, }, })} error={!!errors.annotationProjectDescription} diff --git a/src/components/CreateProjectModal/index.tsx b/src/components/CreateProjectModal/index.tsx index dd503f8f..ada795b8 100644 --- a/src/components/CreateProjectModal/index.tsx +++ b/src/components/CreateProjectModal/index.tsx @@ -26,6 +26,7 @@ import { EXISTING_DATASET_CREATE_PROJECT_DATASET_TYPE_VALUE, ID_TOKEN_NAME, MAX_DATASET_IMAGES_CREATE_PROJECT, + MAX_PROJECT_DESCRIPTION_CHARACTER_LENGTH, MIN_DATASET_IMAGES_CREATE_PROJECT, TOKEN_NAME, // MAX_ALLOW_UPLOAD_IMAGES, @@ -312,8 +313,8 @@ const CreateProjectModal = function (props: CreateProjectModalProps) { {...register("description", { required: false, maxLength: { - value: 300, - message: `Your project description cannot exceed 300 characters.`, + value: MAX_PROJECT_DESCRIPTION_CHARACTER_LENGTH, + message: `Your project description cannot exceed ${MAX_PROJECT_DESCRIPTION_CHARACTER_LENGTH} characters.`, }, })} error={!!errors.description} diff --git a/src/constants/defaultValues.tsx b/src/constants/defaultValues.tsx index e9843006..3da91c97 100644 --- a/src/constants/defaultValues.tsx +++ b/src/constants/defaultValues.tsx @@ -164,6 +164,8 @@ export const CREATE_PROJECT_DATASET_TYPE_LIST: CreateProjectDatasetTypeControlTy }, ]; +export const MAX_PROJECT_DESCRIPTION_CHARACTER_LENGTH = 300; + export const MIN_DATASET_IMAGES_CREATE_PROJECT = 1; export const MAX_DATASET_IMAGES_CREATE_PROJECT = 1000; diff --git a/src/routes/DashboardPage/UpdateProjectInfoDialog.tsx b/src/routes/DashboardPage/UpdateProjectInfoDialog.tsx index 0662c671..ca116262 100644 --- a/src/routes/DashboardPage/UpdateProjectInfoDialog.tsx +++ b/src/routes/DashboardPage/UpdateProjectInfoDialog.tsx @@ -8,7 +8,10 @@ import { Typography, } from "@mui/material"; import { MyButton } from "components"; -import { ID_TOKEN_NAME } from "constants/defaultValues"; +import { + ID_TOKEN_NAME, + MAX_PROJECT_DESCRIPTION_CHARACTER_LENGTH, +} from "constants/defaultValues"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { useDispatch, useSelector } from "react-redux"; @@ -109,8 +112,8 @@ const UpdateProjectInfoDialog = function () { {...register("description", { required: false, maxLength: { - value: 75, - message: `Your project description cannot exceed 75 characters.`, + value: MAX_PROJECT_DESCRIPTION_CHARACTER_LENGTH, + message: `Your project description cannot exceed ${MAX_PROJECT_DESCRIPTION_CHARACTER_LENGTH} characters.`, }, })} error={!!errors.description} @@ -133,7 +136,7 @@ const UpdateProjectInfoDialog = function () { color="text.secondary" > * The maximum number of characters for Project name and Description - is 75. + is {MAX_PROJECT_DESCRIPTION_CHARACTER_LENGTH}.