Skip to content
This repository was archived by the owner on May 30, 2023. It is now read-only.

Commit 65af709

Browse files
authored
Merge pull request #271 from daita-technologies/update/optimize_split_layer_editor
refactor: improve performance when drawing shapes
2 parents b74dc6c + 9c7210a commit 65af709

11 files changed

Lines changed: 603 additions & 153 deletions

File tree

src/components/Annotation/Editor/Shape/Polygon.tsx

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { useDispatch, useSelector } from "react-redux";
88
import {
99
changeCurrentStatus,
1010
removeDrawObjectStateIdByAI,
11-
setHiddenDrawObject,
1211
setSelectedShape,
1312
updateDrawObject,
1413
} from "reduxes/annotation/action";
@@ -286,6 +285,24 @@ const PolygonComp = ({
286285
);
287286
commonShapeEvent.handleDragEnd(e);
288287
};
288+
const converFlattenPointToPoint = (flatPoints: number[]) => {
289+
const newPoints: Vector2d[] = [];
290+
for (let index = 0; index < flatPoints.length; index = index + 2) {
291+
newPoints.push({ x: flatPoints[index], y: flatPoints[index + 1] });
292+
}
293+
return newPoints;
294+
};
295+
const handleTransformEnd = (e: KonvaEventObject<DragEvent>) => {
296+
dispatch(
297+
updateDrawObject({
298+
data: {
299+
...spec,
300+
points: converFlattenPointToPoint(flattenedPoints as number[]),
301+
},
302+
})
303+
);
304+
e.cancelBubble = true;
305+
};
289306
const handlePointDragMove = (e: KonvaEventObject<DragEvent>) => {
290307
const { points } = spec;
291308
if (groupRef && groupRef.current) {
@@ -294,17 +311,13 @@ const PolygonComp = ({
294311
x: groupRef.current.getRelativePointerPosition().x,
295312
y: groupRef.current.getRelativePointerPosition().y,
296313
};
297-
dispatch(
298-
updateDrawObject({
299-
data: {
300-
...spec,
301-
points: [
302-
...points.slice(0, index),
303-
pos,
304-
...points.slice(index + 1),
305-
],
306-
},
307-
})
314+
const newPoints = [
315+
...points.slice(0, index),
316+
pos,
317+
...points.slice(index + 1),
318+
];
319+
setFlattenedPoints(
320+
newPoints.reduce((a, b) => a.concat([b.x, b.y]), [] as number[])
308321
);
309322
}
310323
};
@@ -361,7 +374,7 @@ const PolygonComp = ({
361374
draggable
362375
onDragMove={handlePointDragMove}
363376
onDragStart={commonShapeEvent.handleTransformStart}
364-
onDragEnd={commonShapeEvent.handleTransformEnd}
377+
onDragEnd={handleTransformEnd}
365378
dragBoundFunc={dragBound}
366379
{...CIRCLE_STYLE}
367380
{...startPointAttr}

src/components/Annotation/Editor/utils/event.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import { Layer } from "konva/lib/Layer";
22
import { KonvaEventObject } from "konva/lib/Node";
3+
import { Stage } from "konva/lib/Stage";
34
import { Vector2d } from "konva/lib/types";
5+
import {
6+
MAX_HEIGHT_IMAGE_IN_EDITOR,
7+
MAX_WIDTH_IMAGE_IN_EDITOR,
8+
} from "../const";
49
import { ScaleResult } from "../type";
510
const scaleBy = 1.2;
611

712
export function getNewPositionOnWheel(
813
e: KonvaEventObject<WheelEvent>,
9-
obj: Layer,
14+
obj: Layer | Stage,
1015
pointer: Vector2d
1116
): ScaleResult {
1217
e.evt.preventDefault();
@@ -29,3 +34,19 @@ export function getNewPositionOnWheel(
2934
};
3035
return { newPosition, newScale };
3136
}
37+
export function getFitScaleEditor(width: number, height: number) {
38+
const widthRatio = MAX_WIDTH_IMAGE_IN_EDITOR / width;
39+
const heightRatio = MAX_HEIGHT_IMAGE_IN_EDITOR / height;
40+
let newWidth = width;
41+
let newHeight = height;
42+
if (widthRatio < 1 || heightRatio < 1) {
43+
if (widthRatio < heightRatio) {
44+
newWidth = MAX_WIDTH_IMAGE_IN_EDITOR;
45+
newHeight = newHeight * widthRatio;
46+
} else {
47+
newHeight = MAX_HEIGHT_IMAGE_IN_EDITOR;
48+
newWidth = newWidth * heightRatio;
49+
}
50+
}
51+
return newWidth / width;
52+
}

src/routes/AnnotationPage/ControlPanel/index.tsx

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { useDropzone } from "react-dropzone";
3636
import { useDispatch, useSelector } from "react-redux";
3737
import {
3838
changeCurrentDrawType,
39+
changeCurrentStatus,
3940
changeZoom,
4041
createDrawObject,
4142
redoDrawObject,
@@ -44,11 +45,12 @@ import {
4445
} from "reduxes/annotation/action";
4546
import {
4647
selectorAnnotationStatehHistory,
48+
selectorCurrentDrawState,
4749
selectorcurrentDrawType,
4850
selectorDrawObjectById,
4951
selectorDrawObjectStateIdByAI,
5052
} from "reduxes/annotation/selector";
51-
import { DrawObject, DrawType } from "reduxes/annotation/type";
53+
import { DrawObject, DrawState, DrawType } from "reduxes/annotation/type";
5254
import {
5355
addImagesToAnnotation,
5456
addNewClassLabel,
@@ -64,10 +66,21 @@ import {
6466
} from "reduxes/annotationmanager/selecetor";
6567
import { hashCode, intToRGB } from "../LabelAnnotation";
6668
import { convertStrokeColorToFillColor } from "../LabelAnnotation/ClassLabel";
69+
import NearMeIcon from "@mui/icons-material/NearMe";
70+
import {
71+
MAX_HEIGHT_IMAGE_IN_EDITOR,
72+
MAX_WIDTH_IMAGE_IN_EDITOR,
73+
} from "components/Annotation/Editor/const";
74+
import { getFitScaleEditor } from "components/Annotation/Editor/utils";
6775

6876
const ControlPanel = () => {
6977
const dispatch = useDispatch();
70-
const currentDrawType = useSelector(selectorcurrentDrawType);
78+
const [drawType, setDrawType] = React.useState<DrawType | null>(
79+
useSelector(selectorcurrentDrawType)
80+
);
81+
const [drawState, setDrawState] = React.useState<DrawState | null>(
82+
useSelector(selectorCurrentDrawState)
83+
);
7184
const drawObjectById = useSelector(selectorDrawObjectById);
7285
const currentPreviewImageName = useSelector(selectorCurrentPreviewImageName);
7386
const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile);
@@ -88,14 +101,32 @@ const ControlPanel = () => {
88101
);
89102

90103
const resetScaleHandler = () => {
91-
dispatch(changeZoom({ zoom: { zoom: 1, position: { x: 0, y: 0 } } }));
104+
if (currentAnnotationFile) {
105+
const { width, height } = currentAnnotationFile;
106+
const zoom = getFitScaleEditor(width, height);
107+
dispatch(
108+
changeZoom({
109+
zoom: { zoom, position: { x: 0, y: 0 } },
110+
})
111+
);
112+
}
92113
};
93114

94115
const selectModeHandle = (
95116
event: React.MouseEvent<HTMLElement>,
96-
drawType: DrawType
117+
type: DrawType
97118
) => {
98-
dispatch(changeCurrentDrawType({ currentDrawType: drawType }));
119+
dispatch(changeCurrentDrawType({ currentDrawType: type }));
120+
setDrawType(type);
121+
setDrawState(null);
122+
};
123+
const handleSelectDrawState = (
124+
event: React.MouseEvent<HTMLElement>,
125+
state: DrawState
126+
) => {
127+
dispatch(changeCurrentStatus({ drawState: state }));
128+
setDrawState(state);
129+
setDrawType(null);
99130
};
100131
const handleExportLabelMe = () => {
101132
if (currentAnnotationFile && drawObjectById) {
@@ -350,7 +381,7 @@ const ControlPanel = () => {
350381
<>
351382
<Box sx={{ minWidth: 100 }} display="flex" flexDirection="column" gap={1}>
352383
<ToggleButtonGroup
353-
value={currentDrawType}
384+
value={drawType}
354385
exclusive
355386
onChange={selectModeHandle}
356387
aria-label="mode"
@@ -387,6 +418,23 @@ const ControlPanel = () => {
387418
<PolylineIcon />
388419
</ToggleButton>
389420
</ToggleButtonGroup>
421+
<ToggleButtonGroup
422+
value={drawState}
423+
exclusive
424+
onChange={handleSelectDrawState}
425+
aria-label="mode"
426+
className="annotationControlPanel"
427+
size="large"
428+
sx={{ border: "1px dashed grey" }}
429+
>
430+
<ToggleButton
431+
className="annotationBtn"
432+
value={DrawState.SELECTING}
433+
aria-label="selecting"
434+
>
435+
<NearMeIcon />
436+
</ToggleButton>
437+
</ToggleButtonGroup>
390438
<Box
391439
display="flex"
392440
mt={3}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { KonvaEventObject } from "konva/lib/Node";
2+
import { useEffect, useRef, useState } from "react";
3+
import { Layer, Rect } from "react-konva";
4+
5+
import Konva from "konva";
6+
import { useDispatch } from "react-redux";
7+
import { setDetectedArea } from "reduxes/annotation/action";
8+
import { DetectedAreaType } from "reduxes/annotation/type";
9+
import { convertStrokeColorToFillColor } from "routes/AnnotationPage/LabelAnnotation/ClassLabel";
10+
import DummyRect from "./DummyRect";
11+
12+
const DetectedRectangleDrawLayer = () => {
13+
const dispatch = useDispatch();
14+
const refDetectedArea = useRef<Konva.Rect | null>(null);
15+
const [localDetectedArea, setLocalDetectedArea] =
16+
useState<DetectedAreaType | null>(null);
17+
18+
const mousedownHandler = (e: KonvaEventObject<MouseEvent>) => {
19+
const position = e.currentTarget.getRelativePointerPosition();
20+
if (position) {
21+
setLocalDetectedArea({
22+
x: position.x,
23+
y: position.y,
24+
width: 3,
25+
height: 3,
26+
});
27+
}
28+
};
29+
const mousemoveHandler = (e: KonvaEventObject<MouseEvent>) => {
30+
const position = e.currentTarget.getRelativePointerPosition();
31+
if (position) {
32+
if (localDetectedArea)
33+
setLocalDetectedArea({
34+
...localDetectedArea,
35+
width: position.x - localDetectedArea.x,
36+
height: position.y - localDetectedArea.y,
37+
});
38+
}
39+
};
40+
const mouseupHandler = (e: KonvaEventObject<MouseEvent>) => {
41+
if (localDetectedArea && refDetectedArea.current) {
42+
dispatch(
43+
setDetectedArea({
44+
detectedArea: { ...refDetectedArea.current.getClientRect() },
45+
})
46+
);
47+
}
48+
setLocalDetectedArea(null);
49+
};
50+
const layer = useRef<Konva.Layer | null>(null);
51+
useEffect(() => {
52+
layer.current?.moveToTop();
53+
}, []);
54+
return (
55+
<Layer
56+
ref={layer}
57+
onMouseMove={mousemoveHandler}
58+
onMouseDown={mousedownHandler}
59+
onMouseUp={mouseupHandler}
60+
>
61+
<DummyRect />
62+
{localDetectedArea && (
63+
<Rect
64+
ref={refDetectedArea}
65+
{...localDetectedArea}
66+
fill={convertStrokeColorToFillColor("#000000")}
67+
strokeWidth={4}
68+
stroke="#000000"
69+
/>
70+
)}
71+
</Layer>
72+
);
73+
};
74+
export default DetectedRectangleDrawLayer;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Layer } from "react-konva";
2+
import { useSelector } from "react-redux";
3+
import {
4+
selectorCurrentDrawState,
5+
selectorcurrentDrawType,
6+
} from "reduxes/annotation/selector";
7+
import { DrawState, DrawType } from "reduxes/annotation/type";
8+
import DetectedRectangleDrawLayer from "./DetectedRectangleDrawLayer";
9+
import EllipseDrawLayer from "./EllipseDrawLayer";
10+
import PolygonDrawLayer from "./PolygonDrawLayer";
11+
import RectangleDrawLayer from "./RectangleDrawLayer";
12+
13+
const DrawLayer = () => {
14+
const currentDrawState = useSelector(selectorCurrentDrawState);
15+
const drawType = useSelector(selectorcurrentDrawType);
16+
17+
const render = () => {
18+
if (
19+
currentDrawState === DrawState.FREE ||
20+
currentDrawState === DrawState.DRAWING
21+
) {
22+
if (drawType == DrawType.POLYGON || drawType == DrawType.LINE_STRIP) {
23+
return <PolygonDrawLayer />;
24+
} else if (drawType === DrawType.RECTANGLE) {
25+
return <RectangleDrawLayer />;
26+
} else if (drawType === DrawType.ELLIPSE) {
27+
return <EllipseDrawLayer />;
28+
} else if (drawType === DrawType.DETECTED_RECTANGLE) {
29+
return <DetectedRectangleDrawLayer />;
30+
}
31+
}
32+
return <Layer></Layer>;
33+
};
34+
return render();
35+
};
36+
export default DrawLayer;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Rect } from "react-konva";
2+
import { useSelector } from "react-redux";
3+
import { selectorCurrentAnnotationFile } from "reduxes/annotationmanager/selecetor";
4+
5+
const DummyRect = () => {
6+
const currentAnnotationFile = useSelector(selectorCurrentAnnotationFile);
7+
if (currentAnnotationFile) {
8+
const { width, height } = currentAnnotationFile;
9+
return <Rect width={width} height={height} />;
10+
}
11+
return <></>;
12+
};
13+
export default DummyRect;

0 commit comments

Comments
 (0)