@abidibo/react-cam-roi 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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;
|