@abidibo/react-cam-roi 0.0.7 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +588 -29
- package/dist/Components/BoolField/BoolField.module.css +60 -0
- package/dist/Components/BoolField/index.d.ts +5 -0
- package/dist/Components/BoolField/index.js +13 -0
- package/dist/Components/Button/Button.module.css +27 -0
- package/dist/Components/Button/index.d.ts +8 -0
- package/dist/Components/Button/index.js +15 -0
- package/dist/Components/EnumField/EnumField.module.css +61 -0
- package/dist/Components/EnumField/index.d.ts +10 -0
- package/dist/Components/EnumField/index.js +16 -0
- package/dist/Components/IconButton/IconButton.module.css +8 -3
- package/dist/Components/IconButton/index.d.ts +1 -0
- package/dist/Components/IconButton/index.js +3 -3
- package/dist/Components/Modal/Modal.module.css +92 -0
- package/dist/Components/Modal/index.d.ts +10 -0
- package/dist/Components/Modal/index.js +16 -0
- package/dist/Components/NumberField/NumberField.module.css +60 -0
- package/dist/Components/NumberField/index.d.ts +3 -0
- package/dist/Components/NumberField/index.js +13 -0
- package/dist/Components/RoiEditor/Canvas.d.ts +2 -0
- package/dist/Components/RoiEditor/Canvas.js +21 -11
- package/dist/Components/RoiEditor/Hooks.d.ts +16 -4
- package/dist/Components/RoiEditor/Hooks.js +148 -21
- package/dist/Components/RoiEditor/ParameterField.d.ts +9 -0
- package/dist/Components/RoiEditor/ParameterField.js +27 -0
- package/dist/Components/RoiEditor/ParametersModalForm/ParametersModalForm.module.css +5 -0
- package/dist/Components/RoiEditor/ParametersModalForm/index.d.ts +10 -0
- package/dist/Components/RoiEditor/ParametersModalForm/index.js +31 -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 +47 -0
- package/dist/Components/RoiEditor/ShapesList.module.css +36 -0
- package/dist/Components/RoiEditor/Toolbar.js +20 -6
- package/dist/Components/RoiEditor/Toolbar.module.css +40 -0
- package/dist/Components/RoiEditor/Types.d.ts +97 -1
- package/dist/Components/RoiEditor/Types.js +15 -1
- package/dist/Components/RoiEditor/Utils.d.ts +22 -0
- package/dist/Components/RoiEditor/Utils.js +143 -0
- package/dist/Components/RoiEditor/index.d.ts +4 -0
- package/dist/Components/RoiEditor/index.js +44 -7
- package/dist/Components/TextField/TextField.module.css +61 -0
- package/dist/Components/TextField/index.d.ts +6 -0
- package/dist/Components/TextField/index.js +13 -0
- package/dist/Components/Typography/index.d.ts +4 -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/Icons/SaveIcon.d.ts +6 -0
- package/dist/Icons/SaveIcon.js +5 -0
- package/dist/Providers/EditorProvider.d.ts +12 -2
- package/dist/Providers/EditorProvider.js +16 -2
- package/dist/Providers/UiProvider.d.ts +44 -2
- package/dist/Providers/UiProvider.js +55 -2
- package/dist/Types.d.ts +10 -0
- package/dist/Types.js +1 -0
- package/dist/Utils/index.d.ts +1 -1
- package/dist/Utils/index.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/package.json +13 -4
- 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,14 @@
|
|
1
|
-
import
|
1
|
+
import * as fabric from 'fabric';
|
2
|
+
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
3
|
+
import { v4 as uuidv4 } from 'uuid';
|
4
|
+
import { useEditorContext } from '../../Providers/EditorProvider';
|
5
|
+
import { UiContext } from '../../Providers/UiProvider';
|
6
|
+
import { log } from '../../Utils';
|
2
7
|
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';
|
8
|
+
import { copyPolygon, handleDoubleClickPolygon, handleMouseDownPolygon, handleMouseMovePolygon } from './Polygon';
|
9
|
+
import { copyPolyline, handleDoubleClickPolyline, handleMouseDownPolyline, handleMouseMovePolyline } from './Polyline';
|
10
|
+
import { copyRectangle, handleMouseDownRect, handleMouseMoveRect, handleMouseUpRect } from './Rectangle';
|
11
|
+
import { canDrawShape } from './Utils';
|
6
12
|
export const useImageSize = (imageUrl) => {
|
7
13
|
const [imageSize, setImageSize] = useState({ width: 0, height: 0 });
|
8
14
|
useEffect(() => {
|
@@ -37,17 +43,81 @@ export const useCanvasSize = (imageUrl) => {
|
|
37
43
|
};
|
38
44
|
if (imageSize.width > 0 && wrapperRef.current) {
|
39
45
|
update();
|
40
|
-
// observe ref for resizing event and in case update canvas dimensions
|
41
|
-
const resizeObserver = new ResizeObserver(() => {
|
42
|
-
update();
|
43
|
-
});
|
44
|
-
resizeObserver.observe(wrapperRef.current);
|
45
46
|
}
|
46
47
|
}
|
47
48
|
}, [imageSize, wrapperRef.current]); // eslint-disable-line
|
48
49
|
return { imageSize, canvasSize, wrapperRef, isReady };
|
49
50
|
};
|
50
|
-
export const
|
51
|
+
export const initCanvasData = (canvasRef, addShapes, metadata, setMetadata, initialData, enableLogs) => {
|
52
|
+
log('info', enableLogs !== null && enableLogs !== void 0 ? enableLogs : false, 'Loading initial shapes data', initialData, canvasRef.current);
|
53
|
+
if (initialData === null || initialData === void 0 ? void 0 : initialData.rois) {
|
54
|
+
const m = [];
|
55
|
+
const s = [];
|
56
|
+
initialData.rois.forEach((r) => {
|
57
|
+
var _a, _b, _c;
|
58
|
+
log('info', enableLogs !== null && enableLogs !== void 0 ? enableLogs : false, 'Loading initial shape', r);
|
59
|
+
const id = uuidv4();
|
60
|
+
let shape;
|
61
|
+
switch (r.type) {
|
62
|
+
case "rect" /* ToolEnum.Rectangle */:
|
63
|
+
shape = new fabric.Rect({
|
64
|
+
left: r.shape.left,
|
65
|
+
top: r.shape.top,
|
66
|
+
originX: 'left',
|
67
|
+
originY: 'top',
|
68
|
+
width: r.shape.width,
|
69
|
+
height: r.shape.height,
|
70
|
+
fill: 'transparent',
|
71
|
+
stroke: r.shape.color,
|
72
|
+
strokeWidth: 2,
|
73
|
+
strokeUniform: true,
|
74
|
+
selectable: false,
|
75
|
+
hasControls: true,
|
76
|
+
hoverCursor: 'default',
|
77
|
+
id,
|
78
|
+
});
|
79
|
+
(_a = canvasRef.current) === null || _a === void 0 ? void 0 : _a.add(shape);
|
80
|
+
break;
|
81
|
+
case "polygon" /* ToolEnum.Polygon */:
|
82
|
+
shape = new fabric.Polygon(r.shape.points, {
|
83
|
+
top: r.shape.top + 10,
|
84
|
+
left: r.shape.left + 10,
|
85
|
+
fill: 'transparent',
|
86
|
+
stroke: r.shape.color,
|
87
|
+
strokeWidth: 2,
|
88
|
+
selectable: false,
|
89
|
+
hasControls: true,
|
90
|
+
hoverCursor: 'default',
|
91
|
+
// @ts-expect-error id is not included in types but the property is added and it works
|
92
|
+
id,
|
93
|
+
});
|
94
|
+
(_b = canvasRef.current) === null || _b === void 0 ? void 0 : _b.add(shape);
|
95
|
+
break;
|
96
|
+
case "polyline" /* ToolEnum.Polyline */:
|
97
|
+
shape = new fabric.Polyline(r.shape.points, {
|
98
|
+
top: r.shape.top + 10,
|
99
|
+
left: r.shape.left + 10,
|
100
|
+
fill: 'transparent',
|
101
|
+
stroke: r.shape.color,
|
102
|
+
strokeWidth: 2,
|
103
|
+
selectable: false,
|
104
|
+
hasControls: true,
|
105
|
+
hoverCursor: 'default',
|
106
|
+
id,
|
107
|
+
});
|
108
|
+
(_c = canvasRef.current) === null || _c === void 0 ? void 0 : _c.add(shape);
|
109
|
+
break;
|
110
|
+
}
|
111
|
+
m.push({ id, parameters: r.parameters });
|
112
|
+
s.push({ id, type: r.type, shape });
|
113
|
+
});
|
114
|
+
addShapes(s);
|
115
|
+
setMetadata(Object.assign(Object.assign({}, metadata), { rois: m }));
|
116
|
+
}
|
117
|
+
};
|
118
|
+
export const useTool = (canvas) => {
|
119
|
+
const { configuration, activeTool, activeColor, shapes, addShape } = useEditorContext();
|
120
|
+
const { notify, strings } = useContext(UiContext);
|
51
121
|
const [isDrawing, setIsDrawing] = useState(false);
|
52
122
|
const [shape, setShape] = useState(null);
|
53
123
|
const [originX, setOriginX] = useState(0);
|
@@ -68,7 +138,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
68
138
|
if (!canvas) {
|
69
139
|
return;
|
70
140
|
}
|
71
|
-
if (
|
141
|
+
if (activeTool === "pointer" /* ToolEnum.Pointer */) {
|
72
142
|
// enable selection
|
73
143
|
canvas.selection = true;
|
74
144
|
canvas.getObjects().forEach((object) => {
|
@@ -84,14 +154,20 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
84
154
|
canvas.renderAll();
|
85
155
|
}
|
86
156
|
const handleMouseDown = (event) => {
|
87
|
-
switch (
|
88
|
-
case "
|
157
|
+
switch (activeTool) {
|
158
|
+
case "rect" /* ToolEnum.Rectangle */:
|
159
|
+
if (!canDrawShape(configuration, "rect" /* ToolEnum.Rectangle */, shapes, notify, strings.cannotDrawMoreRectangles))
|
160
|
+
return;
|
89
161
|
handleMouseDownRect(event, canvas, activeColor, setOriginX, setOriginY, setShape, setIsDrawing);
|
90
162
|
break;
|
91
163
|
case "polygon" /* ToolEnum.Polygon */:
|
164
|
+
if (!canDrawShape(configuration, "polygon" /* ToolEnum.Polygon */, shapes, notify, strings.cannotDrawMorePolygons))
|
165
|
+
return;
|
92
166
|
handleMouseDownPolygon(event, canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines);
|
93
167
|
break;
|
94
168
|
case "polyline" /* ToolEnum.Polyline */:
|
169
|
+
if (!canDrawShape(configuration, "polyline" /* ToolEnum.Polyline */, shapes, notify, strings.cannotDrawMorePolylines))
|
170
|
+
return;
|
95
171
|
handleMouseDownPolyline(event, canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines);
|
96
172
|
break;
|
97
173
|
default:
|
@@ -99,8 +175,8 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
99
175
|
}
|
100
176
|
};
|
101
177
|
const handleMouseMove = (event) => {
|
102
|
-
switch (
|
103
|
-
case "
|
178
|
+
switch (activeTool) {
|
179
|
+
case "rect" /* ToolEnum.Rectangle */:
|
104
180
|
handleMouseMoveRect(event, canvas, originX, originY, shape, isDrawing);
|
105
181
|
break;
|
106
182
|
case "polygon" /* ToolEnum.Polygon */:
|
@@ -114,8 +190,8 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
114
190
|
}
|
115
191
|
};
|
116
192
|
const handleMouseUp = () => {
|
117
|
-
switch (
|
118
|
-
case "
|
193
|
+
switch (activeTool) {
|
194
|
+
case "rect" /* ToolEnum.Rectangle */:
|
119
195
|
handleMouseUpRect(canvas, setIsDrawing, shape, setShape, addShape);
|
120
196
|
break;
|
121
197
|
default:
|
@@ -123,7 +199,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
123
199
|
}
|
124
200
|
};
|
125
201
|
const handleDoubleClick = () => {
|
126
|
-
switch (
|
202
|
+
switch (activeTool) {
|
127
203
|
case "polygon" /* ToolEnum.Polygon */:
|
128
204
|
handleDoubleClickPolygon(canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines, addShape);
|
129
205
|
break;
|
@@ -151,7 +227,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
151
227
|
canvas.off('selection:cleared', handleSelectionCleared);
|
152
228
|
};
|
153
229
|
}, [
|
154
|
-
|
230
|
+
activeTool,
|
155
231
|
activeColor,
|
156
232
|
isDrawing,
|
157
233
|
shape,
|
@@ -164,9 +240,15 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
164
240
|
addShape,
|
165
241
|
handleObjectSelected,
|
166
242
|
handleSelectionCleared,
|
243
|
+
configuration,
|
244
|
+
notify,
|
245
|
+
strings,
|
246
|
+
shapes,
|
167
247
|
]);
|
168
248
|
};
|
169
|
-
export const useDispatcherEvents = (canvas
|
249
|
+
export const useDispatcherEvents = (canvas) => {
|
250
|
+
const { configuration, shapes, addShape, setActiveTool } = useEditorContext();
|
251
|
+
const { notify, strings } = useContext(UiContext);
|
170
252
|
useEffect(() => {
|
171
253
|
const removeShape = (_, id) => {
|
172
254
|
const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
|
@@ -174,6 +256,35 @@ export const useDispatcherEvents = (canvas, setActiveTool) => {
|
|
174
256
|
canvas === null || canvas === void 0 ? void 0 : canvas.remove(obj);
|
175
257
|
}
|
176
258
|
};
|
259
|
+
const copyShape = (_, id) => {
|
260
|
+
const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
|
261
|
+
let copy;
|
262
|
+
switch (obj === null || obj === void 0 ? void 0 : obj.type) {
|
263
|
+
case "polygon" /* ToolEnum.Polygon */:
|
264
|
+
if (!canDrawShape(configuration, "polygon" /* ToolEnum.Polygon */, shapes, notify, strings.cannotDrawMorePolygons))
|
265
|
+
return;
|
266
|
+
copy = copyPolygon(canvas, obj, addShape);
|
267
|
+
// @ts-expect-error id exists but his stupid ts does not know
|
268
|
+
Dispatcher.emit('canvas:selectShape', copy.id);
|
269
|
+
break;
|
270
|
+
case "polyline" /* ToolEnum.Polyline */:
|
271
|
+
if (!canDrawShape(configuration, "polyline" /* ToolEnum.Polyline */, shapes, notify, strings.cannotDrawMorePolylines))
|
272
|
+
return;
|
273
|
+
copy = copyPolyline(canvas, obj, addShape);
|
274
|
+
// @ts-expect-error id exists but his stupid ts does not know
|
275
|
+
Dispatcher.emit('canvas:selectShape', copy.id);
|
276
|
+
break;
|
277
|
+
case "rect" /* ToolEnum.Rectangle */:
|
278
|
+
if (!canDrawShape(configuration, "rect" /* ToolEnum.Rectangle */, shapes, notify, strings.cannotDrawMoreRectangles))
|
279
|
+
return;
|
280
|
+
copy = copyRectangle(canvas, obj, addShape);
|
281
|
+
// @ts-expect-error id exists but his stupid ts does not know
|
282
|
+
Dispatcher.emit('canvas:selectShape', copy.id);
|
283
|
+
break;
|
284
|
+
default:
|
285
|
+
break;
|
286
|
+
}
|
287
|
+
};
|
177
288
|
const selectShape = (_, id) => {
|
178
289
|
const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
|
179
290
|
if (obj) {
|
@@ -184,10 +295,26 @@ export const useDispatcherEvents = (canvas, setActiveTool) => {
|
|
184
295
|
}
|
185
296
|
};
|
186
297
|
Dispatcher.register('canvas:removeShape', removeShape);
|
298
|
+
Dispatcher.register('canvas:copyShape', copyShape);
|
187
299
|
Dispatcher.register('canvas:selectShape', selectShape);
|
188
300
|
return () => {
|
189
301
|
Dispatcher.unregister('canvas:removeShape', removeShape);
|
302
|
+
Dispatcher.unregister('canvas:copyShape', copyShape);
|
190
303
|
Dispatcher.unregister('canvas:selectShape', selectShape);
|
191
304
|
};
|
192
|
-
}, [setActiveTool, canvas]);
|
305
|
+
}, [setActiveTool, canvas, addShape, configuration, shapes, notify, strings]);
|
306
|
+
};
|
307
|
+
export const useParametersForm = (parameters) => {
|
308
|
+
const [errors, setErrors] = useState({});
|
309
|
+
const [fields, setFields] = useState(parameters.reduce((acc, p) => (Object.assign(Object.assign({}, acc), { [p.codename]: p.value })), {}));
|
310
|
+
const setField = (key) => (value) => {
|
311
|
+
setFields(Object.assign(Object.assign({}, fields), { [key]: value }));
|
312
|
+
};
|
313
|
+
return {
|
314
|
+
fields,
|
315
|
+
setField,
|
316
|
+
setFields,
|
317
|
+
errors,
|
318
|
+
setErrors,
|
319
|
+
};
|
193
320
|
};
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import { ConfigurationParameter } from './Types';
|
2
|
+
export type ParameterFieldProps<T> = {
|
3
|
+
value: T;
|
4
|
+
onChange: (value: T) => void;
|
5
|
+
parameter: ConfigurationParameter;
|
6
|
+
errors: Record<string, string>;
|
7
|
+
};
|
8
|
+
declare const ParameterField: <T>({ value, onChange, parameter, errors }: ParameterFieldProps<T>) => import("react/jsx-runtime").JSX.Element | null;
|
9
|
+
export default ParameterField;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { useContext } from 'react';
|
3
|
+
import { UiContext } from '../../Providers/UiProvider';
|
4
|
+
const ParameterField = ({ value, onChange, parameter, errors }) => {
|
5
|
+
var _a, _b;
|
6
|
+
const { TextField, NumberField, BoolField, EnumField, strings } = useContext(UiContext);
|
7
|
+
const props = {
|
8
|
+
required: parameter.required,
|
9
|
+
label: `${parameter.label}${parameter.unit ? ` (${parameter.unit})` : ''}`,
|
10
|
+
error: !!errors[parameter.codename],
|
11
|
+
helperText: errors[parameter.codename]
|
12
|
+
? strings[errors[parameter.codename]]
|
13
|
+
: parameter.description,
|
14
|
+
};
|
15
|
+
switch (parameter.type) {
|
16
|
+
case 'string':
|
17
|
+
return ((_a = parameter.options) === null || _a === void 0 ? void 0 : _a.length) ? (_jsx(EnumField, Object.assign({ value: value, onChange: (v) => onChange(v), options: parameter.options, multiple: parameter.multiple }, props))) : (_jsx(TextField, Object.assign({ type: "text", value: value, onChange: (v) => onChange(v) }, props)));
|
18
|
+
case 'int':
|
19
|
+
case 'float':
|
20
|
+
return ((_b = parameter.options) === null || _b === void 0 ? void 0 : _b.length) ? (_jsx(EnumField, Object.assign({ value: value, onChange: (v) => onChange(v), options: parameter.options, multiple: parameter.multiple }, props))) : (_jsx(NumberField, Object.assign({ value: value, onChange: (v) => onChange(v) }, props)));
|
21
|
+
case 'bool':
|
22
|
+
return _jsx(BoolField, Object.assign({ value: value, onChange: (v) => onChange(v) }, props));
|
23
|
+
default:
|
24
|
+
return null;
|
25
|
+
}
|
26
|
+
};
|
27
|
+
export default ParameterField;
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { ConfigurationParameter, OutputParameter } from '../Types';
|
2
|
+
export type ParametersModalFormProps = {
|
3
|
+
onClose: () => void;
|
4
|
+
title: string;
|
5
|
+
parameters: ConfigurationParameter[];
|
6
|
+
data: OutputParameter[];
|
7
|
+
onSubmit: (data: OutputParameter[]) => void;
|
8
|
+
};
|
9
|
+
declare const ParametersModalForm: React.FC<ParametersModalFormProps>;
|
10
|
+
export default ParametersModalForm;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { useContext } from 'react';
|
3
|
+
import { UiContext } from '../../../Providers/UiProvider';
|
4
|
+
import { css } from '../../../Utils';
|
5
|
+
import { useParametersForm } from '../Hooks';
|
6
|
+
import ParameterField from '../ParameterField';
|
7
|
+
import styles from './ParametersModalForm.module.css';
|
8
|
+
import { validateParametersForm } from '../Utils';
|
9
|
+
const ParametersModalForm = ({ title, onClose, parameters, data, onSubmit }) => {
|
10
|
+
const { Modal } = useContext(UiContext);
|
11
|
+
const { fields, setField, errors, setErrors } = useParametersForm(data);
|
12
|
+
const handleSubmit = () => {
|
13
|
+
if (validateParametersForm(parameters, fields, setErrors)) {
|
14
|
+
onSubmit([...parameters.map((p) => ({ codename: p.codename, value: fields[p.codename] }))]);
|
15
|
+
}
|
16
|
+
};
|
17
|
+
return (_jsx(Modal, { onClose: onClose, title: title, isOpen: true, maxWidth: "sm", onSubmit: handleSubmit, children: _jsx("div", { className: css('form', styles, null), children: parameters.map((parameter) => {
|
18
|
+
switch (parameter.type) {
|
19
|
+
case 'string':
|
20
|
+
return (_jsx(ParameterField, { value: String(fields[parameter.codename]), onChange: setField(parameter.codename), parameter: parameter, errors: errors }, parameter.codename));
|
21
|
+
case 'int':
|
22
|
+
case 'float':
|
23
|
+
return (_jsx(ParameterField, { value: fields[parameter.codename], onChange: setField(parameter.codename), parameter: parameter, errors: errors }, parameter.codename));
|
24
|
+
case 'bool':
|
25
|
+
return (_jsx(ParameterField, { value: fields[parameter.codename], onChange: setField(parameter.codename), parameter: parameter, errors: errors }, parameter.codename));
|
26
|
+
default:
|
27
|
+
return null;
|
28
|
+
}
|
29
|
+
}) }) }));
|
30
|
+
};
|
31
|
+
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,47 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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 ParametersModalForm from './ParametersModalForm';
|
8
|
+
import styles from './ShapesList.module.css';
|
9
|
+
const ShapesList = () => {
|
10
|
+
var _a, _b, _c, _d, _e, _f;
|
11
|
+
const { strings, Typography, IconButton, DeleteIcon, AnnotateIcon, SelectIcon, CopyIcon, themeMode } = useContext(UiContext);
|
12
|
+
const { shapes, removeShape, configuration, metadata, setMetadata } = useEditorContext();
|
13
|
+
const [selected, setSelected] = useState([]);
|
14
|
+
const [form, setForm] = useState({ isOpen: false, shapeId: '' });
|
15
|
+
useEffect(() => {
|
16
|
+
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 : []); };
|
17
|
+
Dispatcher.register('canvas:shapeSelected', setSelectedShapes);
|
18
|
+
return () => {
|
19
|
+
Dispatcher.unregister('canvas:shapeSelected', setSelectedShapes);
|
20
|
+
};
|
21
|
+
}, [shapes]);
|
22
|
+
const handleCopyShape = (id) => () => {
|
23
|
+
Dispatcher.emit('canvas:copyShape', id);
|
24
|
+
};
|
25
|
+
const handleRemoveShape = (id) => () => {
|
26
|
+
Dispatcher.emit('canvas:removeShape', id);
|
27
|
+
removeShape(id);
|
28
|
+
};
|
29
|
+
const handleSelectShape = (id) => () => {
|
30
|
+
Dispatcher.emit('canvas:selectShape', id);
|
31
|
+
};
|
32
|
+
const handleEditShapeMetadata = (id) => () => {
|
33
|
+
setForm({ isOpen: true, shapeId: id });
|
34
|
+
};
|
35
|
+
const handleSubmitMetadata = (shapeId) => (data) => {
|
36
|
+
setMetadata(Object.assign(Object.assign({}, metadata), { rois: [
|
37
|
+
...metadata.rois.filter((r) => r.id !== shapeId),
|
38
|
+
{ id: shapeId, parameters: data },
|
39
|
+
] }));
|
40
|
+
setForm({ isOpen: false, shapeId: '' });
|
41
|
+
};
|
42
|
+
const iconColor = themeMode === 'light' ? 'black' : 'white';
|
43
|
+
return (_jsxs(_Fragment, { children: [_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, { style: { fontWeight: 'bold' }, children: strings.id }) }), _jsx("th", { children: _jsx(Typography, { style: { fontWeight: 'bold' }, children: strings.type }) }), _jsx("th", {})] }) })), _jsx("tbody", { children: Object.keys(shapes).map((id) => {
|
44
|
+
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: handleEditShapeMetadata(id), disabled: !configuration.rois.find((roi) => roi.type === shapes[id].type), children: _jsx(AnnotateIcon, { color: iconColor }) }), _jsx(IconButton, { onClick: handleRemoveShape(id), children: _jsx(DeleteIcon, { color: iconColor }) })] })] }, id));
|
45
|
+
}) })] }), form.isOpen && (_jsx(ParametersModalForm, { parameters: (_b = (_a = configuration.rois.find((roi) => roi.type === shapes[form.shapeId].type)) === null || _a === void 0 ? void 0 : _a.parameters) !== null && _b !== void 0 ? _b : [], data: (_f = (_d = (_c = metadata.rois.find((roi) => roi.id === form.shapeId)) === null || _c === void 0 ? void 0 : _c.parameters) !== null && _d !== void 0 ? _d : (_e = configuration.rois.find((roi) => roi.type === shapes[form.shapeId].type)) === null || _e === void 0 ? void 0 : _e.parameters) !== null && _f !== void 0 ? _f : [], title: strings.mainParametersMetadata, onClose: () => setForm({ isOpen: false, shapeId: '' }), onSubmit: handleSubmitMetadata(form.shapeId) }))] }));
|
46
|
+
};
|
47
|
+
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,19 +1,33 @@
|
|
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';
|
6
7
|
import RectangleIcon from '../../Icons/RectangleIcon';
|
8
|
+
import SaveIcon from '../../Icons/SaveIcon';
|
7
9
|
import { useEditorContext } from '../../Providers/EditorProvider';
|
8
10
|
import { UiContext } from '../../Providers/UiProvider';
|
9
11
|
import { css } from '../../Utils';
|
10
|
-
import styles from './Toolbar.module.css';
|
11
12
|
import ColorPicker from './ColorPicker';
|
13
|
+
import ParametersModalForm from './ParametersModalForm';
|
14
|
+
import styles from './Toolbar.module.css';
|
15
|
+
import { enableMainMetadata, enableRois } from './Utils';
|
12
16
|
const Toolbar = () => {
|
13
|
-
|
14
|
-
const {
|
17
|
+
var _a, _b;
|
18
|
+
const { IconButton, themeMode, primaryColor, Typography, strings } = useContext(UiContext);
|
19
|
+
const { activeTool, setActiveTool, configuration, metadata, setMetadata, onSubmit } = useEditorContext();
|
20
|
+
const [form, setForm] = useState({ isOpen: false });
|
15
21
|
const iconColor = (tool) => (tool === activeTool ? primaryColor : themeMode === 'light' ? 'black' : 'white');
|
16
22
|
const setTool = (tool) => () => setActiveTool(tool);
|
17
|
-
|
23
|
+
const handleSubmitMetadata = (data) => {
|
24
|
+
setMetadata(Object.assign(Object.assign({}, metadata), { parameters: data }));
|
25
|
+
setForm({ isOpen: false });
|
26
|
+
};
|
27
|
+
const hideForbiddenTools = (_a = configuration.options) === null || _a === void 0 ? void 0 : _a.hideForbiddenTools;
|
28
|
+
const polylineEnabled = configuration.rois.find((r) => r.type === "polyline" /* ToolEnum.Polyline */);
|
29
|
+
const polygonEnabled = configuration.rois.find((r) => r.type === "polygon" /* ToolEnum.Polygon */);
|
30
|
+
const rectangleEnabled = configuration.rois.find((r) => r.type === "rect" /* ToolEnum.Rectangle */);
|
31
|
+
return (_jsxs(_Fragment, { children: [((_b = configuration.options) === null || _b === void 0 ? void 0 : _b.description) && (_jsx("div", { className: css('toolbar-info', styles, themeMode), children: _jsx(Typography, { children: configuration.options.description }) })), _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 */) }) }), (!hideForbiddenTools || polylineEnabled) && (_jsx(IconButton, { onClick: setTool("polyline" /* ToolEnum.Polyline */), disabled: !polylineEnabled, children: _jsx(PolylineIcon, { color: iconColor("polyline" /* ToolEnum.Polyline */) }) })), (!hideForbiddenTools || polygonEnabled) && (_jsx(IconButton, { onClick: setTool("polygon" /* ToolEnum.Polygon */), disabled: !polygonEnabled, children: _jsx(PolygonIcon, { color: iconColor("polygon" /* ToolEnum.Polygon */) }) })), (!hideForbiddenTools || rectangleEnabled) && (_jsx(IconButton, { onClick: setTool("rect" /* ToolEnum.Rectangle */), disabled: !rectangleEnabled, children: _jsx(RectangleIcon, { color: iconColor("rect" /* ToolEnum.Rectangle */) }) })), _jsx(ColorPicker, { style: { marginLeft: 'auto', marginRight: '.5rem' } })] })), enableRois(configuration) && enableMainMetadata(configuration) && (_jsx("div", { className: css('toolbar-spacer', styles, themeMode) })), enableMainMetadata(configuration) && (_jsx(IconButton, { onClick: () => setForm({ isOpen: true }), children: _jsx(AnnotateIcon, { color: iconColor("rect" /* ToolEnum.Rectangle */) }) })), _jsx("div", { className: css('toolbar-spacer', styles, themeMode) }), _jsx(IconButton, { onClick: onSubmit, children: _jsx(SaveIcon, { color: iconColor("rect" /* ToolEnum.Rectangle */) }) })] }), enableRois(configuration) && (_jsx("div", { className: css('toolbar-helper', styles, themeMode), children: _jsxs(Typography, { children: [strings[activeTool], ": ", strings[`${activeTool}HelpText`]] }) })), form.isOpen && (_jsx(ParametersModalForm, { parameters: configuration.parameters, data: metadata.parameters, title: strings.mainParametersMetadata, onClose: () => setForm({ isOpen: false }), onSubmit: handleSubmitMetadata }))] }));
|
18
32
|
};
|
19
33
|
export default Toolbar;
|