@abidibo/react-cam-roi 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +279 -16
- package/dist/Components/Modal/Modal.module.css +85 -0
- package/dist/Components/Modal/index.d.ts +9 -0
- package/dist/Components/Modal/index.js +16 -0
- package/dist/Components/RoiEditor/Canvas.js +2 -4
- package/dist/Components/RoiEditor/Hooks.d.ts +3 -4
- package/dist/Components/RoiEditor/Hooks.js +64 -16
- package/dist/Components/RoiEditor/ParametersModalForm.d.ts +5 -0
- package/dist/Components/RoiEditor/ParametersModalForm.js +8 -0
- package/dist/Components/RoiEditor/Polygon.d.ts +3 -2
- package/dist/Components/RoiEditor/Polygon.js +18 -0
- package/dist/Components/RoiEditor/Polyline.d.ts +12 -1
- package/dist/Components/RoiEditor/Polyline.js +18 -0
- package/dist/Components/RoiEditor/Rectangle.d.ts +17 -1
- package/dist/Components/RoiEditor/Rectangle.js +23 -1
- package/dist/Components/RoiEditor/ShapesList.d.ts +2 -0
- package/dist/Components/RoiEditor/ShapesList.js +34 -0
- package/dist/Components/RoiEditor/ShapesList.module.css +36 -0
- package/dist/Components/RoiEditor/Toolbar.js +10 -6
- package/dist/Components/RoiEditor/Toolbar.module.css +28 -0
- package/dist/Components/RoiEditor/Types.d.ts +47 -1
- package/dist/Components/RoiEditor/Types.js +15 -1
- package/dist/Components/RoiEditor/Utils.d.ts +5 -0
- package/dist/Components/RoiEditor/Utils.js +37 -0
- package/dist/Components/RoiEditor/index.d.ts +2 -0
- package/dist/Components/RoiEditor/index.js +4 -5
- package/dist/Components/Typography/index.d.ts +3 -0
- package/dist/Components/Typography/index.js +3 -2
- package/dist/Icons/AnnotateIcon.d.ts +6 -0
- package/dist/Icons/AnnotateIcon.js +5 -0
- package/dist/Icons/CloseIcon.d.ts +6 -0
- package/dist/Icons/CloseIcon.js +5 -0
- package/dist/Icons/CopyIcon.d.ts +6 -0
- package/dist/Icons/CopyIcon.js +5 -0
- package/dist/Providers/EditorProvider.d.ts +3 -2
- package/dist/Providers/EditorProvider.js +2 -2
- package/dist/Providers/UiProvider.d.ts +21 -2
- package/dist/Providers/UiProvider.js +26 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/package.json +7 -3
- package/dist/Components/RoiEditor/Metadata.d.ts +0 -2
- package/dist/Components/RoiEditor/Metadata.js +0 -31
- package/dist/Components/RoiEditor/Metadata.module.css +0 -34
@@ -1,8 +1,11 @@
|
|
1
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
1
|
+
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
2
|
+
import { useEditorContext } from '../../Providers/EditorProvider';
|
3
|
+
import { UiContext } from '../../Providers/UiProvider';
|
2
4
|
import Dispatcher from '../../Utils/Dispatcher';
|
3
|
-
import { handleDoubleClickPolygon, handleMouseDownPolygon, handleMouseMovePolygon } from './Polygon';
|
4
|
-
import { handleDoubleClickPolyline, handleMouseDownPolyline, handleMouseMovePolyline } from './Polyline';
|
5
|
-
import { handleMouseDownRect, handleMouseMoveRect, handleMouseUpRect } from './Rectangle';
|
5
|
+
import { copyPolygon, handleDoubleClickPolygon, handleMouseDownPolygon, handleMouseMovePolygon } from './Polygon';
|
6
|
+
import { copyPolyline, handleDoubleClickPolyline, handleMouseDownPolyline, handleMouseMovePolyline } from './Polyline';
|
7
|
+
import { copyRectangle, handleMouseDownRect, handleMouseMoveRect, handleMouseUpRect } from './Rectangle';
|
8
|
+
import { canDrawShape } from './Utils';
|
6
9
|
export const useImageSize = (imageUrl) => {
|
7
10
|
const [imageSize, setImageSize] = useState({ width: 0, height: 0 });
|
8
11
|
useEffect(() => {
|
@@ -47,7 +50,9 @@ export const useCanvasSize = (imageUrl) => {
|
|
47
50
|
}, [imageSize, wrapperRef.current]); // eslint-disable-line
|
48
51
|
return { imageSize, canvasSize, wrapperRef, isReady };
|
49
52
|
};
|
50
|
-
export const useTool = (
|
53
|
+
export const useTool = (canvas) => {
|
54
|
+
const { configuration, activeTool, activeColor, shapes, addShape } = useEditorContext();
|
55
|
+
const { notify, strings } = useContext(UiContext);
|
51
56
|
const [isDrawing, setIsDrawing] = useState(false);
|
52
57
|
const [shape, setShape] = useState(null);
|
53
58
|
const [originX, setOriginX] = useState(0);
|
@@ -68,7 +73,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
68
73
|
if (!canvas) {
|
69
74
|
return;
|
70
75
|
}
|
71
|
-
if (
|
76
|
+
if (activeTool === "pointer" /* ToolEnum.Pointer */) {
|
72
77
|
// enable selection
|
73
78
|
canvas.selection = true;
|
74
79
|
canvas.getObjects().forEach((object) => {
|
@@ -84,14 +89,20 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
84
89
|
canvas.renderAll();
|
85
90
|
}
|
86
91
|
const handleMouseDown = (event) => {
|
87
|
-
switch (
|
88
|
-
case "
|
92
|
+
switch (activeTool) {
|
93
|
+
case "rect" /* ToolEnum.Rectangle */:
|
94
|
+
if (!canDrawShape(configuration, "rect" /* ToolEnum.Rectangle */, shapes, notify, strings.cannotDrawMoreRectangles))
|
95
|
+
return;
|
89
96
|
handleMouseDownRect(event, canvas, activeColor, setOriginX, setOriginY, setShape, setIsDrawing);
|
90
97
|
break;
|
91
98
|
case "polygon" /* ToolEnum.Polygon */:
|
99
|
+
if (!canDrawShape(configuration, "polygon" /* ToolEnum.Polygon */, shapes, notify, strings.cannotDrawMorePolygons))
|
100
|
+
return;
|
92
101
|
handleMouseDownPolygon(event, canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines);
|
93
102
|
break;
|
94
103
|
case "polyline" /* ToolEnum.Polyline */:
|
104
|
+
if (!canDrawShape(configuration, "polyline" /* ToolEnum.Polyline */, shapes, notify, strings.cannotDrawMorePolylines))
|
105
|
+
return;
|
95
106
|
handleMouseDownPolyline(event, canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines);
|
96
107
|
break;
|
97
108
|
default:
|
@@ -99,8 +110,8 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
99
110
|
}
|
100
111
|
};
|
101
112
|
const handleMouseMove = (event) => {
|
102
|
-
switch (
|
103
|
-
case "
|
113
|
+
switch (activeTool) {
|
114
|
+
case "rect" /* ToolEnum.Rectangle */:
|
104
115
|
handleMouseMoveRect(event, canvas, originX, originY, shape, isDrawing);
|
105
116
|
break;
|
106
117
|
case "polygon" /* ToolEnum.Polygon */:
|
@@ -114,8 +125,8 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
114
125
|
}
|
115
126
|
};
|
116
127
|
const handleMouseUp = () => {
|
117
|
-
switch (
|
118
|
-
case "
|
128
|
+
switch (activeTool) {
|
129
|
+
case "rect" /* ToolEnum.Rectangle */:
|
119
130
|
handleMouseUpRect(canvas, setIsDrawing, shape, setShape, addShape);
|
120
131
|
break;
|
121
132
|
default:
|
@@ -123,7 +134,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
123
134
|
}
|
124
135
|
};
|
125
136
|
const handleDoubleClick = () => {
|
126
|
-
switch (
|
137
|
+
switch (activeTool) {
|
127
138
|
case "polygon" /* ToolEnum.Polygon */:
|
128
139
|
handleDoubleClickPolygon(canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines, addShape);
|
129
140
|
break;
|
@@ -151,7 +162,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
151
162
|
canvas.off('selection:cleared', handleSelectionCleared);
|
152
163
|
};
|
153
164
|
}, [
|
154
|
-
|
165
|
+
activeTool,
|
155
166
|
activeColor,
|
156
167
|
isDrawing,
|
157
168
|
shape,
|
@@ -164,9 +175,15 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
164
175
|
addShape,
|
165
176
|
handleObjectSelected,
|
166
177
|
handleSelectionCleared,
|
178
|
+
configuration,
|
179
|
+
notify,
|
180
|
+
strings,
|
181
|
+
shapes,
|
167
182
|
]);
|
168
183
|
};
|
169
|
-
export const useDispatcherEvents = (canvas
|
184
|
+
export const useDispatcherEvents = (canvas) => {
|
185
|
+
const { configuration, shapes, addShape, setActiveTool } = useEditorContext();
|
186
|
+
const { notify, strings } = useContext(UiContext);
|
170
187
|
useEffect(() => {
|
171
188
|
const removeShape = (_, id) => {
|
172
189
|
const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
|
@@ -174,6 +191,35 @@ export const useDispatcherEvents = (canvas, setActiveTool) => {
|
|
174
191
|
canvas === null || canvas === void 0 ? void 0 : canvas.remove(obj);
|
175
192
|
}
|
176
193
|
};
|
194
|
+
const copyShape = (_, id) => {
|
195
|
+
const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
|
196
|
+
let copy;
|
197
|
+
switch (obj === null || obj === void 0 ? void 0 : obj.type) {
|
198
|
+
case "polygon" /* ToolEnum.Polygon */:
|
199
|
+
if (!canDrawShape(configuration, "polygon" /* ToolEnum.Polygon */, shapes, notify, strings.cannotDrawMorePolygons))
|
200
|
+
return;
|
201
|
+
copy = copyPolygon(canvas, obj, addShape);
|
202
|
+
// @ts-expect-error id exists but his stupid ts does not know
|
203
|
+
Dispatcher.emit('canvas:selectShape', copy.id);
|
204
|
+
break;
|
205
|
+
case "polyline" /* ToolEnum.Polyline */:
|
206
|
+
if (!canDrawShape(configuration, "polyline" /* ToolEnum.Polyline */, shapes, notify, strings.cannotDrawMorePolylines))
|
207
|
+
return;
|
208
|
+
copy = copyPolyline(canvas, obj, addShape);
|
209
|
+
// @ts-expect-error id exists but his stupid ts does not know
|
210
|
+
Dispatcher.emit('canvas:selectShape', copy.id);
|
211
|
+
break;
|
212
|
+
case "rect" /* ToolEnum.Rectangle */:
|
213
|
+
if (!canDrawShape(configuration, "rect" /* ToolEnum.Rectangle */, shapes, notify, strings.cannotDrawMoreRectangles))
|
214
|
+
return;
|
215
|
+
copy = copyRectangle(canvas, obj, addShape);
|
216
|
+
// @ts-expect-error id exists but his stupid ts does not know
|
217
|
+
Dispatcher.emit('canvas:selectShape', copy.id);
|
218
|
+
break;
|
219
|
+
default:
|
220
|
+
break;
|
221
|
+
}
|
222
|
+
};
|
177
223
|
const selectShape = (_, id) => {
|
178
224
|
const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
|
179
225
|
if (obj) {
|
@@ -184,10 +230,12 @@ export const useDispatcherEvents = (canvas, setActiveTool) => {
|
|
184
230
|
}
|
185
231
|
};
|
186
232
|
Dispatcher.register('canvas:removeShape', removeShape);
|
233
|
+
Dispatcher.register('canvas:copyShape', copyShape);
|
187
234
|
Dispatcher.register('canvas:selectShape', selectShape);
|
188
235
|
return () => {
|
189
236
|
Dispatcher.unregister('canvas:removeShape', removeShape);
|
237
|
+
Dispatcher.unregister('canvas:copyShape', copyShape);
|
190
238
|
Dispatcher.unregister('canvas:selectShape', selectShape);
|
191
239
|
};
|
192
|
-
}, [setActiveTool, canvas]);
|
240
|
+
}, [setActiveTool, canvas, addShape, configuration, shapes, notify, strings]);
|
193
241
|
};
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { useContext } from 'react';
|
3
|
+
import { UiContext } from '../../Providers/UiProvider';
|
4
|
+
const ParametersModalForm = ({ onClose }) => {
|
5
|
+
const { Modal, strings, Typography } = useContext(UiContext);
|
6
|
+
return (_jsx(Modal, { onClose: onClose, title: strings.mainParametersMetadata, isOpen: true, size: "lg", children: _jsx(Typography, { children: "Here comes the dynamic parameters form" }) }));
|
7
|
+
};
|
8
|
+
export default ParametersModalForm;
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import * as fabric from 'fabric';
|
2
|
-
import { FabricEvent,
|
2
|
+
import { FabricEvent, IAddShape } from './Types';
|
3
3
|
export declare const handleMouseDownPolygon: (event: FabricEvent, canvas: fabric.Canvas, activeColor: string, setIsDrawing: (v: boolean) => void, points: {
|
4
4
|
x: number;
|
5
5
|
y: number;
|
@@ -14,4 +14,5 @@ export declare const handleDoubleClickPolygon: (canvas: fabric.Canvas, activeCol
|
|
14
14
|
}[], setPoints: (v: {
|
15
15
|
x: number;
|
16
16
|
y: number;
|
17
|
-
}[]) => void, lines: fabric.Line[], setLines: (v: fabric.Line[]) => void, addShape:
|
17
|
+
}[]) => void, lines: fabric.Line[], setLines: (v: fabric.Line[]) => void, addShape: IAddShape) => void;
|
18
|
+
export declare const copyPolygon: (canvas: fabric.Canvas, polygon: fabric.Polygon, addShape: IAddShape) => fabric.Polygon;
|
@@ -56,3 +56,21 @@ export const handleDoubleClickPolygon = (canvas, activeColor, setIsDrawing, poin
|
|
56
56
|
setIsDrawing(false);
|
57
57
|
}
|
58
58
|
};
|
59
|
+
export const copyPolygon = (canvas, polygon, addShape) => {
|
60
|
+
const id = uuidv4();
|
61
|
+
const copy = new fabric.Polygon(polygon.points, {
|
62
|
+
top: polygon.top + 10,
|
63
|
+
left: polygon.left + 10,
|
64
|
+
fill: 'transparent',
|
65
|
+
stroke: polygon.stroke,
|
66
|
+
strokeWidth: polygon.strokeWidth,
|
67
|
+
selectable: false,
|
68
|
+
hasControls: true,
|
69
|
+
hoverCursor: 'default',
|
70
|
+
// @ts-expect-error id is not included in types but the property is added and it works
|
71
|
+
id,
|
72
|
+
});
|
73
|
+
canvas.add(copy);
|
74
|
+
addShape(id, "polygon" /* ToolEnum.Polygon */, copy);
|
75
|
+
return copy;
|
76
|
+
};
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import * as fabric from 'fabric';
|
2
|
-
import { FabricEvent, ShapeType } from './Types';
|
2
|
+
import { FabricEvent, IAddShape, ShapeType } from './Types';
|
3
3
|
export declare const handleMouseDownPolyline: (event: FabricEvent, canvas: fabric.Canvas, activeColor: string, setIsDrawing: (v: boolean) => void, points: {
|
4
4
|
x: number;
|
5
5
|
y: number;
|
@@ -15,3 +15,14 @@ export declare const handleDoubleClickPolyline: (canvas: fabric.Canvas, activeCo
|
|
15
15
|
x: number;
|
16
16
|
y: number;
|
17
17
|
}[]) => void, lines: fabric.Line[], setLines: (v: fabric.Line[]) => void, addShape: (id: string, type: ShapeType, shape: fabric.Polyline) => void) => void;
|
18
|
+
export declare const copyPolyline: (canvas: fabric.Canvas, polyline: fabric.Polyline, addShape: IAddShape) => fabric.Polyline<{
|
19
|
+
top: number;
|
20
|
+
left: number;
|
21
|
+
fill: string;
|
22
|
+
stroke: string | fabric.TFiller | null;
|
23
|
+
strokeWidth: number;
|
24
|
+
selectable: false;
|
25
|
+
hasControls: true;
|
26
|
+
hoverCursor: string;
|
27
|
+
id: string;
|
28
|
+
}, fabric.SerializedPolylineProps, fabric.ObjectEvents>;
|
@@ -37,6 +37,7 @@ export const handleDoubleClickPolyline = (canvas, activeColor, setIsDrawing, poi
|
|
37
37
|
if (points.length > 2) {
|
38
38
|
const id = uuidv4();
|
39
39
|
const polyline = new fabric.Polyline(points, {
|
40
|
+
fill: 'transparent',
|
40
41
|
stroke: activeColor,
|
41
42
|
strokeWidth: 2,
|
42
43
|
selectable: false,
|
@@ -54,3 +55,20 @@ export const handleDoubleClickPolyline = (canvas, activeColor, setIsDrawing, poi
|
|
54
55
|
setIsDrawing(false);
|
55
56
|
}
|
56
57
|
};
|
58
|
+
export const copyPolyline = (canvas, polyline, addShape) => {
|
59
|
+
const id = uuidv4();
|
60
|
+
const copy = new fabric.Polyline(polyline.points, {
|
61
|
+
top: polyline.top + 10,
|
62
|
+
left: polyline.left + 10,
|
63
|
+
fill: 'transparent',
|
64
|
+
stroke: polyline.stroke,
|
65
|
+
strokeWidth: polyline.strokeWidth,
|
66
|
+
selectable: false,
|
67
|
+
hasControls: true,
|
68
|
+
hoverCursor: 'default',
|
69
|
+
id,
|
70
|
+
});
|
71
|
+
canvas.add(copy);
|
72
|
+
addShape(id, "polyline" /* ToolEnum.Polyline */, copy);
|
73
|
+
return copy;
|
74
|
+
};
|
@@ -1,5 +1,21 @@
|
|
1
1
|
import * as fabric from 'fabric';
|
2
|
-
import { FabricEvent, Shape, ShapeType } from './Types';
|
2
|
+
import { FabricEvent, IAddShape, Shape, ShapeType } from './Types';
|
3
3
|
export declare const handleMouseDownRect: (event: FabricEvent, canvas: fabric.Canvas, activeColor: string, setOriginX: (v: number) => void, setOriginY: (v: number) => void, setShape: (v: Shape) => void, setIsDrawing: (v: boolean) => void) => void;
|
4
4
|
export declare const handleMouseMoveRect: (event: FabricEvent, canvas: fabric.Canvas, originX: number, originY: number, shape: Shape, isDrawing: boolean) => void;
|
5
5
|
export declare const handleMouseUpRect: (canvas: fabric.Canvas, setIsDrawing: (v: boolean) => void, shape: Shape, setShape: (v: Shape | null) => void, addShape: (id: string, type: ShapeType, shape: Shape) => void) => void;
|
6
|
+
export declare const copyRectangle: (canvas: fabric.Canvas, rectangle: fabric.Rect, addShape: IAddShape) => fabric.Rect<{
|
7
|
+
left: number;
|
8
|
+
top: number;
|
9
|
+
originX: "left";
|
10
|
+
originY: "top";
|
11
|
+
width: number;
|
12
|
+
height: number;
|
13
|
+
fill: string;
|
14
|
+
stroke: string | fabric.TFiller | null;
|
15
|
+
strokeWidth: number;
|
16
|
+
strokeUniform: true;
|
17
|
+
selectable: false;
|
18
|
+
hasControls: true;
|
19
|
+
hoverCursor: string;
|
20
|
+
id: string;
|
21
|
+
}, fabric.SerializedRectProps, fabric.ObjectEvents>;
|
@@ -44,7 +44,29 @@ export const handleMouseMoveRect = (event, canvas, originX, originY, shape, isDr
|
|
44
44
|
export const handleMouseUpRect = (canvas, setIsDrawing, shape, setShape, addShape) => {
|
45
45
|
setIsDrawing(false);
|
46
46
|
shape.setCoords();
|
47
|
-
addShape(shape.id, "
|
47
|
+
addShape(shape.id, "rect" /* ToolEnum.Rectangle */, shape);
|
48
48
|
setShape(null);
|
49
49
|
canvas.defaultCursor = 'default';
|
50
50
|
};
|
51
|
+
export const copyRectangle = (canvas, rectangle, addShape) => {
|
52
|
+
const id = uuidv4();
|
53
|
+
const copy = new fabric.Rect({
|
54
|
+
left: rectangle.left + 10,
|
55
|
+
top: rectangle.top + 10,
|
56
|
+
originX: 'left',
|
57
|
+
originY: 'top',
|
58
|
+
width: rectangle.width,
|
59
|
+
height: rectangle.height,
|
60
|
+
fill: 'transparent',
|
61
|
+
stroke: rectangle.stroke,
|
62
|
+
strokeWidth: rectangle.strokeWidth,
|
63
|
+
strokeUniform: true,
|
64
|
+
selectable: false,
|
65
|
+
hasControls: true,
|
66
|
+
hoverCursor: 'default',
|
67
|
+
id,
|
68
|
+
});
|
69
|
+
canvas.add(copy);
|
70
|
+
addShape(id, "rect" /* ToolEnum.Rectangle */, copy);
|
71
|
+
return copy;
|
72
|
+
};
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import { useContext, useEffect, useState } from 'react';
|
3
|
+
import { useEditorContext } from '../../Providers/EditorProvider';
|
4
|
+
import { UiContext } from '../../Providers/UiProvider';
|
5
|
+
import { css } from '../../Utils';
|
6
|
+
import Dispatcher from '../../Utils/Dispatcher';
|
7
|
+
import styles from './ShapesList.module.css';
|
8
|
+
const ShapesList = () => {
|
9
|
+
const { strings, Typography, IconButton, DeleteIcon, EditIcon, SelectIcon, CopyIcon, themeMode } = useContext(UiContext);
|
10
|
+
const { shapes, removeShape } = useEditorContext();
|
11
|
+
const [selected, setSelected] = useState([]);
|
12
|
+
useEffect(() => {
|
13
|
+
const setSelectedShapes = (_, event) => { var _a; return setSelected((_a = event === null || event === void 0 ? void 0 : event.map((s) => s.id)) !== null && _a !== void 0 ? _a : []); };
|
14
|
+
Dispatcher.register('canvas:shapeSelected', setSelectedShapes);
|
15
|
+
return () => {
|
16
|
+
Dispatcher.unregister('canvas:shapeSelected', setSelectedShapes);
|
17
|
+
};
|
18
|
+
}, [shapes]);
|
19
|
+
const handleCopyShape = (id) => () => {
|
20
|
+
Dispatcher.emit('canvas:copyShape', id);
|
21
|
+
};
|
22
|
+
const handleRemoveShape = (id) => () => {
|
23
|
+
Dispatcher.emit('canvas:removeShape', id);
|
24
|
+
removeShape(id);
|
25
|
+
};
|
26
|
+
const handleSelectShape = (id) => () => {
|
27
|
+
Dispatcher.emit('canvas:selectShape', id);
|
28
|
+
};
|
29
|
+
const iconColor = themeMode === 'light' ? 'black' : 'white';
|
30
|
+
return (_jsxs("table", { className: css('shapes-table', styles, themeMode), children: [Object.keys(shapes).length > 0 && (_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: _jsx(Typography, { children: strings.id }) }), _jsx("th", { children: _jsx(Typography, { children: strings.type }) }), _jsx("th", {})] }) })), _jsx("tbody", { children: Object.keys(shapes).map((id) => {
|
31
|
+
return (_jsxs("tr", { className: selected.indexOf(id) > -1 ? css('shapes-row-selected', styles, themeMode) : '', children: [_jsx("td", { children: _jsx("div", { children: _jsx(Typography, { children: id.substring(0, 6) }) }) }), _jsx("td", { children: _jsx(Typography, { children: strings[shapes[id].type] }) }), _jsxs("td", { children: [_jsx(IconButton, { onClick: handleSelectShape(id), children: _jsx(SelectIcon, { color: iconColor }) }), _jsx(IconButton, { onClick: handleCopyShape(id), children: _jsx(CopyIcon, { color: iconColor }) }), _jsx(IconButton, { onClick: handleRemoveShape(id), children: _jsx(EditIcon, { color: iconColor }) }), _jsx(IconButton, { onClick: handleRemoveShape(id), children: _jsx(DeleteIcon, { color: iconColor }) })] })] }, id));
|
32
|
+
}) })] }));
|
33
|
+
};
|
34
|
+
export default ShapesList;
|
@@ -0,0 +1,36 @@
|
|
1
|
+
.shapes-table {
|
2
|
+
border-collapse: collapse;
|
3
|
+
width: 100%;
|
4
|
+
}
|
5
|
+
|
6
|
+
.shapes-table th {
|
7
|
+
padding: .8rem .3rem .3rem;
|
8
|
+
text-align: left;
|
9
|
+
}
|
10
|
+
|
11
|
+
.shapes-table td {
|
12
|
+
padding: 0 .3rem;
|
13
|
+
}
|
14
|
+
|
15
|
+
.shapes-table tr td:last-child,
|
16
|
+
.shapes-table tr th:last-child {
|
17
|
+
text-align: right;
|
18
|
+
}
|
19
|
+
|
20
|
+
.shapes-table-light {
|
21
|
+
background-color: #eee;
|
22
|
+
color: #000;
|
23
|
+
}
|
24
|
+
|
25
|
+
.shapes-table-dark {
|
26
|
+
background-color: #333;
|
27
|
+
color: #fff;
|
28
|
+
}
|
29
|
+
|
30
|
+
.shapes-row-selected-light {
|
31
|
+
background-color: #ccc;
|
32
|
+
}
|
33
|
+
|
34
|
+
.shapes-row-selected-dark {
|
35
|
+
background-color: #666;
|
36
|
+
}
|
@@ -1,5 +1,6 @@
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
-
import { useContext } from 'react';
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import { useContext, useState } from 'react';
|
3
|
+
import AnnotateIcon from '../../Icons/AnnotateIcon';
|
3
4
|
import PointerIcon from '../../Icons/PointerIcon';
|
4
5
|
import PolygonIcon from '../../Icons/PolygonIcon';
|
5
6
|
import PolylineIcon from '../../Icons/PolylineIcon';
|
@@ -7,13 +8,16 @@ import RectangleIcon from '../../Icons/RectangleIcon';
|
|
7
8
|
import { useEditorContext } from '../../Providers/EditorProvider';
|
8
9
|
import { UiContext } from '../../Providers/UiProvider';
|
9
10
|
import { css } from '../../Utils';
|
10
|
-
import styles from './Toolbar.module.css';
|
11
11
|
import ColorPicker from './ColorPicker';
|
12
|
+
import ParametersModalForm from './ParametersModalForm';
|
13
|
+
import styles from './Toolbar.module.css';
|
14
|
+
import { enableMainMetadata, enableRois } from './Utils';
|
12
15
|
const Toolbar = () => {
|
13
|
-
const { IconButton, themeMode, primaryColor } = useContext(UiContext);
|
14
|
-
const { activeTool, setActiveTool } = useEditorContext();
|
16
|
+
const { IconButton, themeMode, primaryColor, Typography, strings } = useContext(UiContext);
|
17
|
+
const { activeTool, setActiveTool, configuration } = useEditorContext();
|
18
|
+
const [form, setForm] = useState({ isOpen: false, data: [] });
|
15
19
|
const iconColor = (tool) => (tool === activeTool ? primaryColor : themeMode === 'light' ? 'black' : 'white');
|
16
20
|
const setTool = (tool) => () => setActiveTool(tool);
|
17
|
-
return (_jsxs("div", { className: css('toolbar', styles, themeMode), children: [_jsx(IconButton, { onClick: setTool("pointer" /* ToolEnum.Pointer */), children: _jsx(PointerIcon, { color: iconColor("pointer" /* ToolEnum.Pointer */) }) }), _jsx(IconButton, { onClick: setTool("polyline" /* ToolEnum.Polyline */), children: _jsx(PolylineIcon, { color: iconColor("polyline" /* ToolEnum.Polyline */) }) }), _jsx(IconButton, { onClick: setTool("polygon" /* ToolEnum.Polygon */), children: _jsx(PolygonIcon, { color: iconColor("polygon" /* ToolEnum.Polygon */) }) }), _jsx(IconButton, { onClick: setTool("
|
21
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: css('toolbar', styles, themeMode), children: [enableRois(configuration) && (_jsxs(_Fragment, { children: [_jsx(IconButton, { onClick: setTool("pointer" /* ToolEnum.Pointer */), children: _jsx(PointerIcon, { color: iconColor("pointer" /* ToolEnum.Pointer */) }) }), _jsx(IconButton, { onClick: setTool("polyline" /* ToolEnum.Polyline */), children: _jsx(PolylineIcon, { color: iconColor("polyline" /* ToolEnum.Polyline */) }) }), _jsx(IconButton, { onClick: setTool("polygon" /* ToolEnum.Polygon */), children: _jsx(PolygonIcon, { color: iconColor("polygon" /* ToolEnum.Polygon */) }) }), _jsx(IconButton, { onClick: setTool("rect" /* ToolEnum.Rectangle */), children: _jsx(RectangleIcon, { color: iconColor("rect" /* ToolEnum.Rectangle */) }) }), _jsx(ColorPicker, { style: { marginLeft: 'auto' } })] })), enableRois(configuration) && enableMainMetadata(configuration) && (_jsx("div", { className: css('toolbar-spacer', styles, themeMode) })), enableMainMetadata(configuration) && (_jsx(IconButton, { onClick: () => setForm({ isOpen: true, data: configuration.parameters }), children: _jsx(AnnotateIcon, { color: iconColor("rect" /* ToolEnum.Rectangle */) }) }))] }), _jsx("div", { className: css('toolbar-helper', styles, themeMode), children: _jsxs(Typography, { children: [strings[activeTool], ": ", strings[`${activeTool}HelpText`]] }) }), form.isOpen && _jsx(ParametersModalForm, { onClose: () => setForm({ isOpen: false, data: [] }) })] }));
|
18
22
|
};
|
19
23
|
export default Toolbar;
|
@@ -11,3 +11,31 @@
|
|
11
11
|
.toolbar-dark {
|
12
12
|
background-color: #333;
|
13
13
|
}
|
14
|
+
|
15
|
+
.toolbar-helper {
|
16
|
+
padding: .5rem;
|
17
|
+
}
|
18
|
+
|
19
|
+
.toolbar-helper-light {
|
20
|
+
background-color: #f7f7f7;;
|
21
|
+
color: #000;
|
22
|
+
}
|
23
|
+
|
24
|
+
.toolbar-helper-dark {
|
25
|
+
background-color: #222;
|
26
|
+
color: #fff;
|
27
|
+
}
|
28
|
+
|
29
|
+
.toolbar-spacer {
|
30
|
+
height: 20px;
|
31
|
+
margin: 0 .5rem 0 1rem;
|
32
|
+
width: 1px;
|
33
|
+
}
|
34
|
+
|
35
|
+
.toolbar-spacer-light {
|
36
|
+
background-color: #ccc;
|
37
|
+
}
|
38
|
+
|
39
|
+
.toolbar-spacer-dark {
|
40
|
+
background-color: #555;
|
41
|
+
}
|
@@ -3,7 +3,7 @@ export declare const enum ToolEnum {
|
|
3
3
|
Pointer = "pointer",
|
4
4
|
Polyline = "polyline",
|
5
5
|
Polygon = "polygon",
|
6
|
-
Rectangle = "
|
6
|
+
Rectangle = "rect"
|
7
7
|
}
|
8
8
|
export type ShapeType = ToolEnum.Polyline | ToolEnum.Polygon | ToolEnum.Rectangle;
|
9
9
|
export type Shape = (fabric.Rect | fabric.Polygon | fabric.Polyline) & {
|
@@ -17,3 +17,49 @@ export type FabricEvent = fabric.TPointerEventInfo<fabric.TPointerEvent>;
|
|
17
17
|
export type FabricSelectionEvent = Partial<fabric.TEvent> & {
|
18
18
|
selected: fabric.Object[];
|
19
19
|
};
|
20
|
+
export type IAddShape = (id: string, type: ShapeType, shape: Shape) => void;
|
21
|
+
export declare enum DataTypeEnum {
|
22
|
+
Integer = "int",
|
23
|
+
Float = "float",
|
24
|
+
String = "string",
|
25
|
+
Boolean = "bool"
|
26
|
+
}
|
27
|
+
export declare enum OperatorEnum {
|
28
|
+
Lt = "lt",
|
29
|
+
Lte = "lte",
|
30
|
+
Gt = "gt",
|
31
|
+
Gte = "gte",
|
32
|
+
Eq = "eq"
|
33
|
+
}
|
34
|
+
export type ConfigurationParameter = {
|
35
|
+
codename: string;
|
36
|
+
label: string;
|
37
|
+
description: string;
|
38
|
+
unit: string;
|
39
|
+
type: DataTypeEnum;
|
40
|
+
options: {
|
41
|
+
value: number | string | boolean;
|
42
|
+
label: string;
|
43
|
+
}[];
|
44
|
+
required: boolean;
|
45
|
+
value: number | string | boolean | null;
|
46
|
+
};
|
47
|
+
export type RoiConfiguration = {
|
48
|
+
role: string;
|
49
|
+
type: Omit<ShapeType, 'pointer'>;
|
50
|
+
multiplicity: {
|
51
|
+
operator: OperatorEnum;
|
52
|
+
threshold: number;
|
53
|
+
};
|
54
|
+
parameters: ConfigurationParameter[];
|
55
|
+
};
|
56
|
+
export type Configuration = {
|
57
|
+
parameters: ConfigurationParameter[];
|
58
|
+
rois: RoiConfiguration[];
|
59
|
+
};
|
60
|
+
export interface INotify {
|
61
|
+
info: (message: string) => void;
|
62
|
+
warn: (message: string) => void;
|
63
|
+
error: (message: string) => void;
|
64
|
+
success: (message: string) => void;
|
65
|
+
}
|
@@ -1 +1,15 @@
|
|
1
|
-
export
|
1
|
+
export var DataTypeEnum;
|
2
|
+
(function (DataTypeEnum) {
|
3
|
+
DataTypeEnum["Integer"] = "int";
|
4
|
+
DataTypeEnum["Float"] = "float";
|
5
|
+
DataTypeEnum["String"] = "string";
|
6
|
+
DataTypeEnum["Boolean"] = "bool";
|
7
|
+
})(DataTypeEnum || (DataTypeEnum = {}));
|
8
|
+
export var OperatorEnum;
|
9
|
+
(function (OperatorEnum) {
|
10
|
+
OperatorEnum["Lt"] = "lt";
|
11
|
+
OperatorEnum["Lte"] = "lte";
|
12
|
+
OperatorEnum["Gt"] = "gt";
|
13
|
+
OperatorEnum["Gte"] = "gte";
|
14
|
+
OperatorEnum["Eq"] = "eq";
|
15
|
+
})(OperatorEnum || (OperatorEnum = {}));
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import { Configuration, INotify, Shapes, ToolEnum } from './Types';
|
2
|
+
export declare const notify: INotify;
|
3
|
+
export declare const enableRois: (configuration: Configuration) => boolean;
|
4
|
+
export declare const enableMainMetadata: (configuration: Configuration) => boolean;
|
5
|
+
export declare const canDrawShape: (configuration: Configuration, shapeType: Omit<ToolEnum, ToolEnum.Pointer>, shapes: Shapes, notify: INotify, message: string) => boolean;
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import { OperatorEnum } from './Types';
|
2
|
+
export const notify = {
|
3
|
+
info: (message) => alert(`Info: ${message}`),
|
4
|
+
warn: (message) => alert(`Warning: ${message}`),
|
5
|
+
error: (message) => alert(`Error: ${message}`),
|
6
|
+
success: (message) => alert(`Success: ${message}`),
|
7
|
+
};
|
8
|
+
export const enableRois = (configuration) => {
|
9
|
+
return configuration.rois.length > 0;
|
10
|
+
};
|
11
|
+
export const enableMainMetadata = (configuration) => {
|
12
|
+
return configuration.parameters.length > 0;
|
13
|
+
};
|
14
|
+
export const canDrawShape = (configuration, shapeType, shapes, notify, message) => {
|
15
|
+
const rule = configuration.rois.find((roi) => roi.type === shapeType);
|
16
|
+
// no rule
|
17
|
+
if (!rule || !rule.multiplicity || Object.keys(rule.multiplicity).length === 0) {
|
18
|
+
return true;
|
19
|
+
}
|
20
|
+
const currentShapeCount = Object.values(shapes).filter((s) => s.type === shapeType).length;
|
21
|
+
let res = true;
|
22
|
+
switch (rule.multiplicity.operator) {
|
23
|
+
case OperatorEnum.Eq:
|
24
|
+
case OperatorEnum.Lte:
|
25
|
+
res = currentShapeCount + 1 <= rule.multiplicity.threshold;
|
26
|
+
break;
|
27
|
+
case OperatorEnum.Lt:
|
28
|
+
res = currentShapeCount + 1 < rule.multiplicity.threshold;
|
29
|
+
break;
|
30
|
+
default:
|
31
|
+
return true;
|
32
|
+
}
|
33
|
+
if (!res && notify) {
|
34
|
+
notify.warn(message);
|
35
|
+
}
|
36
|
+
return res;
|
37
|
+
};
|
@@ -6,12 +6,11 @@ import { css, log } from '../../Utils';
|
|
6
6
|
import { Loader } from '../Loader';
|
7
7
|
import Canvas from './Canvas';
|
8
8
|
import { useCanvasSize } from './Hooks';
|
9
|
-
import
|
9
|
+
import ShapesList from './ShapesList';
|
10
10
|
import styles from './RoiEditor.module.css';
|
11
11
|
import Toolbar from './Toolbar';
|
12
|
-
// https://medium.com/@na.mazaheri/dynamically-drawing-shapes-on-canvas-with-fabric-js-in-react-js-8b9c42791903
|
13
12
|
// https://github.com/n-mazaheri/image-editor
|
14
|
-
const RoiEditor = ({ imageUrl }) => {
|
13
|
+
const RoiEditor = ({ imageUrl, configuration }) => {
|
15
14
|
const { themeMode, enableLogs, pickerColors } = useContext(UiContext);
|
16
15
|
const { imageSize, canvasSize, wrapperRef, isReady } = useCanvasSize(imageUrl);
|
17
16
|
const [activeTool, setActiveTool] = useState("pointer" /* ToolEnum.Pointer */);
|
@@ -28,10 +27,10 @@ const RoiEditor = ({ imageUrl }) => {
|
|
28
27
|
if (!isReady) {
|
29
28
|
return _jsx(Loader, {});
|
30
29
|
}
|
31
|
-
return (_jsx(EditorProvider, { activeTool: activeTool, setActiveTool: setActiveTool, activeColor: activeColor, setActiveColor: setActiveColor, shapes: shapes, addShape: addShape, removeShape: removeShape, children: _jsxs("div", { style: { maxWidth: '100%', width: `${imageSize.width}px` }, ref: wrapperRef, children: [_jsx(Toolbar, {}), _jsx("div", { className: css('canvasWrapper', styles, themeMode), style: {
|
30
|
+
return (_jsx(EditorProvider, { activeTool: activeTool, setActiveTool: setActiveTool, activeColor: activeColor, setActiveColor: setActiveColor, shapes: shapes, addShape: addShape, removeShape: removeShape, configuration: configuration, children: _jsxs("div", { style: { maxWidth: '100%', width: `${imageSize.width}px` }, ref: wrapperRef, children: [_jsx(Toolbar, {}), _jsx("div", { className: css('canvasWrapper', styles, themeMode), style: {
|
32
31
|
width: `${canvasSize.width}px`,
|
33
32
|
height: `${canvasSize.height}px`,
|
34
33
|
backgroundImage: `url(${imageUrl})`,
|
35
|
-
}, children: _jsx(Canvas, { canvasSize: canvasSize }) }), _jsx(
|
34
|
+
}, children: _jsx(Canvas, { canvasSize: canvasSize }) }), _jsx(ShapesList, {})] }) }));
|
36
35
|
};
|
37
36
|
export default RoiEditor;
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
-
const Typography = ({ children }) => {
|
3
|
-
|
2
|
+
const Typography = ({ children, className = '', component = 'span' }) => {
|
3
|
+
const Tag = component;
|
4
|
+
return _jsx(Tag, { className: className, children: children });
|
4
5
|
};
|
5
6
|
export default Typography;
|