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

Commit ff4c1fb

Browse files
authored
Merge pull request #294 from daita-technologies/fix/group_drag_bound
add: scroll next and prev in image list at annotation editor
2 parents 512004c + 4d6fb55 commit ff4c1fb

12 files changed

Lines changed: 298 additions & 137 deletions

File tree

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

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
selectorSelectedEllipse,
1414
} from "reduxes/annotation/selector";
1515
import { DrawObject, DrawState, DrawType } from "reduxes/annotation/type";
16+
import { selectorCurrentImageInEditorProps } from "reduxes/annotationmanager/selecetor";
1617
import { CIRCLE_STYLE, CORNER_RADIUS, LINE_STYLE } from "../const";
1718
import { EllipseCompProps, EllipseProps, EllipseSpec } from "../type";
1819
import useCommonShapeEvent from "../useCommonShapeEvent";
@@ -47,7 +48,9 @@ function EllipseComp({
4748
const commonShapeEvent = useCommonShapeEvent({ drawObject: spec });
4849
const currentDrawState = useSelector(selectorCurrentDrawState);
4950
const drawObjectState = useSelector(selectorDrawObjectState(spec.id));
50-
51+
const currentImageInEditorProps = useSelector(
52+
selectorCurrentImageInEditorProps
53+
);
5154
const [strokeWidth, setStrokeWidth] = useState<number>(
5255
spec.cssStyle.strokeWidth
5356
);
@@ -75,28 +78,62 @@ function EllipseComp({
7578
trRef.current.getLayer().batchDraw();
7679
}
7780
}, [isSelected]);
78-
const [stage, setStage] = useState<Konva.Stage | null>(null);
7981

8082
const groupDragBound = (pos: Vector2d) => {
8183
let { x, y } = pos;
82-
const sw = stage ? stage.width() : 0;
83-
const sh = stage ? stage.height() : 0;
84-
if (shapeRef && shapeRef.current) {
84+
if (shapeRef && shapeRef.current && currentImageInEditorProps) {
85+
const { clientRectOfBaseImage } = currentImageInEditorProps;
8586
const box = shapeRef.current.getClientRect();
8687

87-
if (y - box.height / 2 < 0) y = box.height / 2;
88-
if (x - box.width / 2 < 0) x = box.width / 2;
89-
if (y + box.height / 2 > sh) y = sh - box.height / 2;
90-
if (x + box.width / 2 > sw) x = sw - box.width / 2;
91-
return { x, y };
88+
if (clientRectOfBaseImage) {
89+
if (x - box.width / 2 < clientRectOfBaseImage.x) {
90+
x = clientRectOfBaseImage.x + box.width / 2;
91+
}
92+
if (y - box.height / 2 < clientRectOfBaseImage.y) {
93+
y = clientRectOfBaseImage.y + box.height / 2;
94+
}
95+
if (
96+
x + box.width >
97+
clientRectOfBaseImage.x + clientRectOfBaseImage.width
98+
) {
99+
x =
100+
clientRectOfBaseImage.x +
101+
clientRectOfBaseImage.width -
102+
box.width / 2;
103+
}
104+
if (
105+
y + box.height >
106+
clientRectOfBaseImage.y + clientRectOfBaseImage.height
107+
) {
108+
y =
109+
clientRectOfBaseImage.y +
110+
clientRectOfBaseImage.height -
111+
box.height / 2;
112+
}
113+
}
92114
}
93-
return { x: 0, y: 0 };
94-
};
95115

96-
const handleDragStart = (e: KonvaEventObject<DragEvent>) => {
97-
setStage(e.target.getStage());
98-
commonShapeEvent.handleDragStart(e);
116+
return {
117+
x,
118+
y,
119+
};
99120
};
121+
// const groupDragBound = (pos: Vector2d) => {
122+
// let { x, y } = pos;
123+
// const sw = stage ? stage.width() : 0;
124+
// const sh = stage ? stage.height() : 0;
125+
// if (shapeRef && shapeRef.current) {
126+
// const box = shapeRef.current.getClientRect();
127+
128+
// if (y - box.height / 2 < 0) y = box.height / 2;
129+
// if (x - box.width / 2 < 0) x = box.width / 2;
130+
// if (y + box.height / 2 > sh) y = sh - box.height / 2;
131+
// if (x + box.width / 2 > sw) x = sw - box.width / 2;
132+
// return { x, y };
133+
// }
134+
// return { x: 0, y: 0 };
135+
// };
136+
100137
const handleTransformEnd = (e: KonvaEventObject<Event>) => {
101138
const node = shapeRef.current;
102139
if (!node) return;
@@ -150,7 +187,6 @@ function EllipseComp({
150187
onMouseOut={onMouseOut}
151188
draggable={commonShapeEvent.isLock !== true}
152189
onDragEnd={handleDragEnd}
153-
onDragStart={handleDragStart}
154190
onTransformEnd={handleTransformEnd}
155191
strokeScaleEnabled={false}
156192
{...spec}

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

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
selectorSelectedRectangle,
1414
} from "reduxes/annotation/selector";
1515
import { DrawObject, DrawState, DrawType } from "reduxes/annotation/type";
16+
import { selectorCurrentImageInEditorProps } from "reduxes/annotationmanager/selecetor";
1617
import { CIRCLE_STYLE, CORNER_RADIUS, LINE_STYLE } from "../const";
1718
import { RectangleCompProps, RectangleProps, RectangleSpec } from "../type";
1819
import useCommonShapeEvent from "../useCommonShapeEvent";
@@ -46,6 +47,9 @@ function RectangleComp({
4647
const commonShapeEvent = useCommonShapeEvent({ drawObject: spec });
4748
const currentDrawState = useSelector(selectorCurrentDrawState);
4849
const drawObjectState = useSelector(selectorDrawObjectState(spec.id));
50+
const currentImageInEditorProps = useSelector(
51+
selectorCurrentImageInEditorProps
52+
);
4953

5054
const [strokeWidth, setStrokeWidth] = useState<number>(
5155
spec.cssStyle.strokeWidth
@@ -74,29 +78,38 @@ function RectangleComp({
7478
trRef.current.getLayer().batchDraw();
7579
}
7680
}, [isSelected]);
77-
const [stage, setStage] = useState<Konva.Stage | null>(null);
78-
7981
const groupDragBound = (pos: Vector2d) => {
8082
let { x, y } = pos;
81-
const sw = stage ? stage.width() : 0;
82-
const sh = stage ? stage.height() : 0;
83-
if (shapeRef && shapeRef.current) {
84-
const box = shapeRef.current.getClientRect();
85-
const minMaxX = [0, box.width];
86-
const minMaxY = [0, box.height];
87-
88-
if (minMaxY[0] + y < 0) y = -1 * minMaxY[0];
89-
if (minMaxX[0] + x < 0) x = -1 * minMaxX[0];
90-
if (minMaxY[1] + y > sh) y = sh - minMaxY[1];
91-
if (minMaxX[1] + x > sw) x = sw - minMaxX[1];
92-
return { x, y };
83+
if (shapeRef && shapeRef.current && currentImageInEditorProps) {
84+
const { clientRectOfBaseImage } = currentImageInEditorProps;
85+
if (clientRectOfBaseImage) {
86+
if (x < clientRectOfBaseImage.x) {
87+
x = clientRectOfBaseImage.x;
88+
}
89+
if (y < clientRectOfBaseImage.y) {
90+
y = clientRectOfBaseImage.y;
91+
}
92+
const box = shapeRef.current.getClientRect();
93+
if (
94+
x + box.width >
95+
clientRectOfBaseImage.x + clientRectOfBaseImage.width
96+
) {
97+
x = clientRectOfBaseImage.x + clientRectOfBaseImage.width - box.width;
98+
}
99+
if (
100+
y + box.height >
101+
clientRectOfBaseImage.y + clientRectOfBaseImage.height
102+
) {
103+
y =
104+
clientRectOfBaseImage.y + clientRectOfBaseImage.height - box.height;
105+
}
106+
}
93107
}
94-
return { x: 0, y: 0 };
95-
};
96108

97-
const handleDragStart = (e: KonvaEventObject<DragEvent>) => {
98-
setStage(e.target.getStage());
99-
commonShapeEvent.handleDragStart(e);
109+
return {
110+
x,
111+
y,
112+
};
100113
};
101114
const handleDragEnd = (e: KonvaEventObject<DragEvent>) => {
102115
onChange({
@@ -139,7 +152,6 @@ function RectangleComp({
139152
setStrokeWidth(spec.cssStyle.strokeWidth);
140153
onMouseOutHandler(e);
141154
};
142-
143155
return (
144156
<>
145157
<Rect
@@ -152,7 +164,6 @@ function RectangleComp({
152164
onMouseOut={onMouseOut}
153165
draggable={commonShapeEvent.isLock !== true}
154166
onDragEnd={handleDragEnd}
155-
onDragStart={handleDragStart}
156167
onTransformEnd={handleTransformEnd}
157168
strokeScaleEnabled={false}
158169
{...spec}

src/reduxes/annotationmanager/action.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
SAVE_ANNOTATION_STATE_MANAGER,
1010
SAVE_REMOTE_NEW_CLASS_LABEL,
1111
SET_ANNOTATION_STATE_MANAGER,
12+
SET_CLIENT_RECT_OF_BASE_IMAGE,
1213
SET_CLASS_MANAGE_MODAL,
1314
SET_PREVIEW_IMAGE,
1415
} from "./constants";
@@ -21,6 +22,7 @@ import {
2122
EditClassManageModalProps,
2223
FetchingFileAndAnnotaitonProps,
2324
SaveAnnotationStateManagerProps,
25+
SetClientRectOfBaseImageProps,
2426
} from "./type";
2527

2628
export const addImagesToAnnotation = (payload: AddImageToAnnotationProps) => ({
@@ -86,3 +88,9 @@ export const fetchingFileAndAnnotaiton = (
8688
export const resetAnnotationManager = () => ({
8789
type: RESET_ANNOTATION_MANAGER,
8890
});
91+
export const setClientRectOfBaseImage = (
92+
payload: SetClientRectOfBaseImageProps
93+
) => ({
94+
type: SET_CLIENT_RECT_OF_BASE_IMAGE,
95+
payload,
96+
});

src/reduxes/annotationmanager/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ export const FETCH_FILE_AND_ANNOTATION = asyncAction(
1616
);
1717
export const SAVE_REMOTE_NEW_CLASS_LABEL = asyncAction("SAVE_NEW_CLASS_LABEL");
1818
export const RESET_ANNOTATION_MANAGER = "RESET_ANNOTATION_MANAGER";
19+
export const SET_CLIENT_RECT_OF_BASE_IMAGE = "SET_CLIENT_RECT_OF_BASE_IMAGE";

src/reduxes/annotationmanager/reducer.ts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { initialLabelClassPropertiesByLabelClass } from "components/Annotation/Editor/type";
1+
import {
2+
MAX_HEIGHT_EDITOR,
3+
MAX_HEIGHT_IMAGE_IN_EDITOR,
4+
MAX_WIDTH_EDITOR,
5+
MAX_WIDTH_IMAGE_IN_EDITOR,
6+
} from "components/Annotation/Editor/const";
27
import {
38
ADD_IMAGES,
49
ADD_NEW_CLASS_LABEL,
@@ -8,6 +13,7 @@ import {
813
RESET_ANNOTATION_MANAGER,
914
SAVE_ANNOTATION_STATE_MANAGER,
1015
SET_ANNOTATION_STATE_MANAGER,
16+
SET_CLIENT_RECT_OF_BASE_IMAGE,
1117
SET_CLASS_MANAGE_MODAL,
1218
SET_PREVIEW_IMAGE,
1319
} from "./constants";
@@ -20,12 +26,14 @@ import {
2026
EditClassLabelProps,
2127
EditClassManageModalProps,
2228
SaveAnnotationStateManagerProps,
29+
SetClientRectOfBaseImageProps,
2330
} from "./type";
2431

2532
const inititalState: AnnotationManagerReducer = {
2633
idDrawObjectByImageName: {},
2734
images: {},
2835
currentPreviewImageName: null,
36+
currentImageInEditorProps: null,
2937
labelClassPropertiesByLabelClass: {}, // initialLabelClassPropertiesByLabelClass,
3038
dialogClassManageModal: {
3139
isOpen: false,
@@ -62,11 +70,62 @@ const annotationManagerReducer = (
6270
};
6371
}
6472
case CHANGE_PREVIEW_IMAGE.SUCCEEDED: {
73+
if (state.currentPreviewImageName) {
74+
const image = state.images[state.currentPreviewImageName];
75+
if (image) {
76+
const { height, width } = image;
77+
const widthRatio = MAX_WIDTH_IMAGE_IN_EDITOR / width;
78+
const heightRatio = MAX_HEIGHT_IMAGE_IN_EDITOR / height;
79+
let newWidth = width;
80+
let newHeight = height;
81+
if (widthRatio < 1 || heightRatio < 1) {
82+
if (widthRatio < heightRatio) {
83+
newWidth = MAX_WIDTH_IMAGE_IN_EDITOR;
84+
newHeight *= widthRatio;
85+
} else {
86+
newHeight = MAX_HEIGHT_IMAGE_IN_EDITOR;
87+
newWidth *= heightRatio;
88+
}
89+
}
90+
const scaleX = newWidth / width;
91+
const scaleY = newHeight / height;
92+
const paddingLeft = (MAX_WIDTH_EDITOR - newWidth) / scaleX / 2.0;
93+
const paddingTop = (MAX_HEIGHT_EDITOR - newHeight) / scaleY / 2.0;
94+
return {
95+
...state,
96+
currentImageInEditorProps: {
97+
scaleX,
98+
scaleY,
99+
width: newWidth,
100+
height: newHeight,
101+
paddingLeft,
102+
paddingTop,
103+
clientRectOfBaseImage: null,
104+
},
105+
isFetchingImageData: false,
106+
};
107+
}
108+
}
109+
65110
return {
66111
...state,
67112
isFetchingImageData: false,
68113
};
69114
}
115+
case SET_CLIENT_RECT_OF_BASE_IMAGE: {
116+
const { clientRectOfBaseImage } =
117+
payload as SetClientRectOfBaseImageProps;
118+
if (state.currentImageInEditorProps) {
119+
return {
120+
...state,
121+
currentImageInEditorProps: {
122+
...state.currentImageInEditorProps,
123+
clientRectOfBaseImage,
124+
},
125+
};
126+
}
127+
return state;
128+
}
70129
case CHANGE_PREVIEW_IMAGE.FAILED: {
71130
return {
72131
...state,

src/reduxes/annotationmanager/selecetor.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ export const selectorIsSavingAnnotation = (state: RootState) =>
2222
state.annotationManagerReducer.isSavingAnnotation;
2323
export const selectorIsFetchingImageData = (state: RootState) =>
2424
state.annotationManagerReducer.isFetchingImageData;
25+
export const selectorCurrentImageInEditorProps = (state: RootState) =>
26+
state.annotationManagerReducer.currentImageInEditorProps;

src/reduxes/annotationmanager/type.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,28 @@ export interface AnnotationManagerReducer {
55
idDrawObjectByImageName: Record<string, Record<string, DrawObject>>;
66
images: Record<string, AnnotationImagesProperty>;
77
currentPreviewImageName: string | null;
8+
currentImageInEditorProps: ImageInEditorProps | null;
89
labelClassPropertiesByLabelClass: Record<string, LabelClassProperties>;
910
dialogClassManageModal: ClassManageModalProps;
1011
isFetchingImageData: boolean;
1112
isSavingAnnotation: boolean;
1213
}
14+
export interface ImageInEditorProps {
15+
scaleX: number;
16+
scaleY: number;
17+
width: number;
18+
height: number;
19+
paddingLeft: number;
20+
paddingTop: number;
21+
clientRectOfBaseImage: ClientRectType | null;
22+
}
23+
export interface ClientRectType {
24+
width: number;
25+
height: number;
26+
x: number;
27+
y: number;
28+
}
29+
1330
export type ClassManageModalType = "VIEW" | "CREATE" | "EDIT";
1431

1532
export interface ClassManageModalProps {
@@ -51,3 +68,6 @@ export interface FetchingFileAndAnnotaitonProps {
5168
s3keyLabel: string;
5269
s3keyFile: string;
5370
}
71+
export interface SetClientRectOfBaseImageProps {
72+
clientRectOfBaseImage: ClientRectType;
73+
}

src/routes/AnnotationPage/AnnotationEditor.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ const AnnotationEditor = function () {
7777
<Box
7878
display="flex"
7979
gap={2}
80-
sx={{ padding: 1, backgroundColor: "#2a3648" }}
80+
sx={{ backgroundColor: "#2a3648" }}
81+
justifyContent="center"
82+
alignContent="center"
8183
>
8284
<ImagePreview />
8385
</Box>

0 commit comments

Comments
 (0)