@abidibo/react-cam-roi 0.2.1 → 0.2.3
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/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 +29 -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 +20 -0
- package/dist/Components/IconButton/index.d.ts +7 -0
- package/dist/Components/IconButton/index.js +10 -0
- package/dist/Components/Loader/Loader.module.css +25 -0
- package/dist/Components/Loader/index.d.ts +1 -0
- package/dist/Components/Loader/index.js +9 -0
- 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 +14 -0
- package/dist/Components/RoiEditor/Canvas.js +30 -0
- package/dist/Components/RoiEditor/ColorPicker.d.ts +5 -0
- package/dist/Components/RoiEditor/ColorPicker.js +12 -0
- package/dist/Components/RoiEditor/ColorPicker.module.css +17 -0
- package/dist/Components/RoiEditor/Header.d.ts +2 -0
- package/dist/Components/RoiEditor/Header.js +14 -0
- package/dist/Components/RoiEditor/Header.module.css +26 -0
- package/dist/Components/RoiEditor/Hooks.d.ts +38 -0
- package/dist/Components/RoiEditor/Hooks.js +328 -0
- 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 +17 -0
- package/dist/Components/RoiEditor/ParametersModalForm/index.js +42 -0
- package/dist/Components/RoiEditor/Polygon.d.ts +18 -0
- package/dist/Components/RoiEditor/Polygon.js +77 -0
- package/dist/Components/RoiEditor/Polyline.d.ts +28 -0
- package/dist/Components/RoiEditor/Polyline.js +75 -0
- package/dist/Components/RoiEditor/Rectangle.d.ts +21 -0
- package/dist/Components/RoiEditor/Rectangle.js +73 -0
- package/dist/Components/RoiEditor/RoiEditor.module.css +5 -0
- package/dist/Components/RoiEditor/RoisInfo.d.ts +2 -0
- package/dist/Components/RoiEditor/RoisInfo.js +43 -0
- package/dist/Components/RoiEditor/ShapesList.d.ts +2 -0
- package/dist/Components/RoiEditor/ShapesList.js +77 -0
- package/dist/Components/RoiEditor/ShapesList.module.css +71 -0
- package/dist/Components/RoiEditor/Toolbar.d.ts +2 -0
- package/dist/Components/RoiEditor/Toolbar.js +27 -0
- package/dist/Components/RoiEditor/Toolbar.module.css +41 -0
- package/dist/Components/RoiEditor/TopBar.d.ts +2 -0
- package/dist/Components/RoiEditor/TopBar.js +21 -0
- package/dist/Components/RoiEditor/TopBar.module.css +7 -0
- package/dist/Components/RoiEditor/Types.d.ts +128 -0
- package/dist/Components/RoiEditor/Types.js +23 -0
- package/dist/Components/RoiEditor/Utils.d.ts +25 -0
- package/dist/Components/RoiEditor/Utils.js +161 -0
- package/dist/Components/RoiEditor/index.d.ts +12 -0
- package/dist/Components/RoiEditor/index.js +99 -0
- package/dist/Components/RoleField.d.ts +7 -0
- package/dist/Components/RoleField.js +49 -0
- 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 +9 -0
- package/dist/Components/Typography/index.js +6 -0
- 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/DeleteIcon.d.ts +6 -0
- package/dist/Icons/DeleteIcon.js +5 -0
- package/dist/Icons/EditIcon.d.ts +6 -0
- package/dist/Icons/EditIcon.js +5 -0
- package/dist/Icons/PointerIcon.d.ts +6 -0
- package/dist/Icons/PointerIcon.js +5 -0
- package/dist/Icons/PolygonIcon.d.ts +6 -0
- package/dist/Icons/PolygonIcon.js +5 -0
- package/dist/Icons/PolylineIcon.d.ts +6 -0
- package/dist/Icons/PolylineIcon.js +5 -0
- package/dist/Icons/RectangleIcon.d.ts +6 -0
- package/dist/Icons/RectangleIcon.js +5 -0
- package/dist/Icons/SaveIcon.d.ts +6 -0
- package/dist/Icons/SaveIcon.js +5 -0
- package/dist/Providers/EditorProvider.d.ts +26 -0
- package/dist/Providers/EditorProvider.js +29 -0
- package/dist/Providers/UiProvider.d.ts +82 -0
- package/dist/Providers/UiProvider.js +104 -0
- package/dist/Types.d.ts +10 -0
- package/dist/Types.js +1 -0
- package/dist/Utils/Dispatcher.d.ts +16 -0
- package/dist/Utils/Dispatcher.js +65 -0
- package/dist/Utils/index.d.ts +6 -0
- package/dist/Utils/index.js +16 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/package.json +1 -1
@@ -0,0 +1,75 @@
|
|
1
|
+
import * as fabric from 'fabric';
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
3
|
+
import Dispatcher from '../../Utils/Dispatcher';
|
4
|
+
const addPoint = (event, canvas, color, points, setPoints, lines, setLines) => {
|
5
|
+
const pointer = canvas.getScenePoint(event.e);
|
6
|
+
const newPoint = { x: pointer.x, y: pointer.y };
|
7
|
+
const newPolylinePoints = [...points, newPoint];
|
8
|
+
setPoints(newPolylinePoints);
|
9
|
+
if (newPolylinePoints.length > 0) {
|
10
|
+
const line = new fabric.Line([
|
11
|
+
newPolylinePoints[newPolylinePoints.length - 1].x,
|
12
|
+
newPolylinePoints[newPolylinePoints.length - 1].y,
|
13
|
+
pointer.x,
|
14
|
+
pointer.y,
|
15
|
+
], {
|
16
|
+
stroke: color,
|
17
|
+
strokeWidth: 2,
|
18
|
+
strokeUniform: true,
|
19
|
+
selectable: false,
|
20
|
+
hasControls: false,
|
21
|
+
});
|
22
|
+
canvas.add(line);
|
23
|
+
setLines([...lines, line]);
|
24
|
+
}
|
25
|
+
};
|
26
|
+
export const handleMouseDownPolyline = (event, canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines) => {
|
27
|
+
setIsDrawing(true);
|
28
|
+
addPoint(event, canvas, activeColor, points, setPoints, lines, setLines);
|
29
|
+
};
|
30
|
+
export const handleMouseMovePolyline = (event, canvas, isDrawing, lines) => {
|
31
|
+
if (isDrawing && lines.length > 0) {
|
32
|
+
const pointer = canvas.getScenePoint(event.e);
|
33
|
+
lines[lines.length - 1].set({ x2: pointer.x, y2: pointer.y });
|
34
|
+
canvas.renderAll();
|
35
|
+
}
|
36
|
+
};
|
37
|
+
export const handleDoubleClickPolyline = (editorId, canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines) => {
|
38
|
+
if (points.length > 2) {
|
39
|
+
const id = uuidv4();
|
40
|
+
const polyline = new fabric.Polyline(points, {
|
41
|
+
fill: 'transparent',
|
42
|
+
stroke: activeColor,
|
43
|
+
strokeWidth: 2,
|
44
|
+
selectable: false,
|
45
|
+
hasControls: true,
|
46
|
+
lockRotation: false,
|
47
|
+
id,
|
48
|
+
});
|
49
|
+
canvas.add(polyline);
|
50
|
+
Dispatcher.emit(`canvas:${editorId}:shapeAdded`, { id, type: "polyline" /* ToolEnum.Polyline */, shape: polyline });
|
51
|
+
setPoints([]);
|
52
|
+
for (const line of lines) {
|
53
|
+
canvas.remove(line); // Remove temporary lines
|
54
|
+
}
|
55
|
+
setLines([]);
|
56
|
+
setIsDrawing(false);
|
57
|
+
}
|
58
|
+
};
|
59
|
+
export const copyPolyline = (editorId, canvas, polyline) => {
|
60
|
+
const id = uuidv4();
|
61
|
+
const copy = new fabric.Polyline(polyline.points, {
|
62
|
+
top: polyline.top + 10,
|
63
|
+
left: polyline.left + 10,
|
64
|
+
fill: 'transparent',
|
65
|
+
stroke: polyline.stroke,
|
66
|
+
strokeWidth: polyline.strokeWidth,
|
67
|
+
selectable: false,
|
68
|
+
hasControls: true,
|
69
|
+
hoverCursor: 'default',
|
70
|
+
id,
|
71
|
+
});
|
72
|
+
canvas.add(copy);
|
73
|
+
Dispatcher.emit(`canvas:${editorId}:shapeAdded`, { id, type: "polyline" /* ToolEnum.Polyline */, shape: copy });
|
74
|
+
return copy;
|
75
|
+
};
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import * as fabric from 'fabric';
|
2
|
+
import { FabricEvent, Shape } from './Types';
|
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
|
+
export declare const handleMouseMoveRect: (event: FabricEvent, canvas: fabric.Canvas, originX: number, originY: number, shape: Shape, isDrawing: boolean) => void;
|
5
|
+
export declare const handleMouseUpRect: (editorId: string, canvas: fabric.Canvas, setIsDrawing: (v: boolean) => void, shape: Shape, setShape: (v: Shape | null) => void) => void;
|
6
|
+
export declare const copyRectangle: (editorId: string, canvas: fabric.Canvas, rectangle: fabric.Rect) => 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>;
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import * as fabric from 'fabric';
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
3
|
+
import Dispatcher from '../../Utils/Dispatcher';
|
4
|
+
export const handleMouseDownRect = (event, canvas, activeColor, setOriginX, setOriginY, setShape, setIsDrawing) => {
|
5
|
+
const pointer = canvas.getScenePoint(event.e);
|
6
|
+
setOriginX(pointer.x);
|
7
|
+
setOriginY(pointer.y);
|
8
|
+
const id = uuidv4();
|
9
|
+
const newRectangle = new fabric.Rect({
|
10
|
+
left: pointer.x,
|
11
|
+
top: pointer.y,
|
12
|
+
originX: 'left',
|
13
|
+
originY: 'top',
|
14
|
+
width: 0,
|
15
|
+
height: 0,
|
16
|
+
fill: 'transparent',
|
17
|
+
stroke: activeColor,
|
18
|
+
strokeWidth: 2,
|
19
|
+
strokeUniform: true,
|
20
|
+
selectable: false,
|
21
|
+
hasControls: true,
|
22
|
+
hoverCursor: 'default',
|
23
|
+
id,
|
24
|
+
});
|
25
|
+
canvas.add(newRectangle);
|
26
|
+
setShape(newRectangle);
|
27
|
+
setIsDrawing(true);
|
28
|
+
};
|
29
|
+
export const handleMouseMoveRect = (event, canvas, originX, originY, shape, isDrawing) => {
|
30
|
+
if (isDrawing && shape) {
|
31
|
+
const pointer = canvas.getScenePoint(event.e);
|
32
|
+
shape.set({
|
33
|
+
width: Math.abs(originX - pointer.x),
|
34
|
+
height: Math.abs(originY - pointer.y),
|
35
|
+
});
|
36
|
+
if (originX > pointer.x) {
|
37
|
+
shape.set({ left: pointer.x });
|
38
|
+
}
|
39
|
+
if (originY > pointer.y) {
|
40
|
+
shape.set({ top: pointer.y });
|
41
|
+
}
|
42
|
+
canvas.renderAll();
|
43
|
+
}
|
44
|
+
};
|
45
|
+
export const handleMouseUpRect = (editorId, canvas, setIsDrawing, shape, setShape) => {
|
46
|
+
setIsDrawing(false);
|
47
|
+
shape.setCoords();
|
48
|
+
Dispatcher.emit(`canvas:${editorId}:shapeAdded`, { id: shape.id, type: "rect" /* ToolEnum.Rectangle */, shape });
|
49
|
+
setShape(null);
|
50
|
+
canvas.defaultCursor = 'default';
|
51
|
+
};
|
52
|
+
export const copyRectangle = (editorId, canvas, rectangle) => {
|
53
|
+
const id = uuidv4();
|
54
|
+
const copy = new fabric.Rect({
|
55
|
+
left: rectangle.left + 10,
|
56
|
+
top: rectangle.top + 10,
|
57
|
+
originX: 'left',
|
58
|
+
originY: 'top',
|
59
|
+
width: rectangle.width,
|
60
|
+
height: rectangle.height,
|
61
|
+
fill: 'transparent',
|
62
|
+
stroke: rectangle.stroke,
|
63
|
+
strokeWidth: rectangle.strokeWidth,
|
64
|
+
strokeUniform: true,
|
65
|
+
selectable: false,
|
66
|
+
hasControls: true,
|
67
|
+
hoverCursor: 'default',
|
68
|
+
id,
|
69
|
+
});
|
70
|
+
canvas.add(copy);
|
71
|
+
Dispatcher.emit(`canvas:${editorId}:shapeAdded`, { id, type: "rect" /* ToolEnum.Rectangle */, shape: copy });
|
72
|
+
return copy;
|
73
|
+
};
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { useContext } from 'react';
|
3
|
+
import { useEditorContext } from '../../Providers/EditorProvider';
|
4
|
+
import { UiContext } from '../../Providers/UiProvider';
|
5
|
+
import { formatString } from '../../Utils';
|
6
|
+
import { OperatorEnum } from './Types';
|
7
|
+
const RoisInfo = () => {
|
8
|
+
var _a;
|
9
|
+
const { strings, Typography } = useContext(UiContext);
|
10
|
+
const { configuration } = useEditorContext();
|
11
|
+
if (!((_a = configuration.rois) === null || _a === void 0 ? void 0 : _a.length))
|
12
|
+
return null;
|
13
|
+
return (_jsxs("div", { children: [_jsxs(Typography, { component: "div", children: [strings.roisToBeDrawn, ":"] }), _jsx("ul", { children: configuration.rois.map((r) => {
|
14
|
+
var _a, _b;
|
15
|
+
let rule;
|
16
|
+
const data = {
|
17
|
+
role: r.label,
|
18
|
+
type: r.type,
|
19
|
+
threshold: (_a = r.multiplicity) === null || _a === void 0 ? void 0 : _a.threshold,
|
20
|
+
};
|
21
|
+
switch ((_b = r.multiplicity) === null || _b === void 0 ? void 0 : _b.operator) {
|
22
|
+
case OperatorEnum.Eq:
|
23
|
+
rule = formatString(strings.roiMultiplicityEqRule, data);
|
24
|
+
break;
|
25
|
+
case OperatorEnum.Lt:
|
26
|
+
rule = formatString(strings.roiMultiplicityLtRule, data);
|
27
|
+
break;
|
28
|
+
case OperatorEnum.Lte:
|
29
|
+
rule = formatString(strings.roiMultiplicityLteRule, data);
|
30
|
+
break;
|
31
|
+
case OperatorEnum.Gt:
|
32
|
+
rule = formatString(strings.roiMultiplicityGtRule, data);
|
33
|
+
break;
|
34
|
+
case OperatorEnum.Gte:
|
35
|
+
rule = formatString(strings.roiMultiplicityGteRule, data);
|
36
|
+
break;
|
37
|
+
default:
|
38
|
+
rule = formatString(strings.roiMultiplicityNoRule, data);
|
39
|
+
}
|
40
|
+
return (_jsx("li", { children: _jsx(Typography, { children: rule }) }, r.role));
|
41
|
+
}) })] }));
|
42
|
+
};
|
43
|
+
export default RoisInfo;
|
@@ -0,0 +1,77 @@
|
|
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, _g, _h, _j, _k;
|
11
|
+
const { strings, Typography, IconButton, DeleteIcon, AnnotateIcon, CopyIcon, themeMode } = useContext(UiContext);
|
12
|
+
const { shapes, removeShape, configuration, metadata, setMetadata, addShape, editorId } = useEditorContext();
|
13
|
+
const [selected, setSelected] = useState([]);
|
14
|
+
const [form, setForm] = useState({
|
15
|
+
isOpen: false,
|
16
|
+
shapeId: '',
|
17
|
+
type: null,
|
18
|
+
shape: null,
|
19
|
+
});
|
20
|
+
// open metadata form immediately after drawing the shape
|
21
|
+
useEffect(() => {
|
22
|
+
const openForm = (_, { id, type, shape }) => {
|
23
|
+
setForm({ isOpen: true, shapeId: id, type, shape });
|
24
|
+
};
|
25
|
+
Dispatcher.register(`canvas:${editorId}:shapeAdded`, openForm);
|
26
|
+
return () => {
|
27
|
+
Dispatcher.unregister(`canvas:${editorId}:shapeAdded`, openForm);
|
28
|
+
};
|
29
|
+
}, [editorId]);
|
30
|
+
useEffect(() => {
|
31
|
+
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 : []); };
|
32
|
+
Dispatcher.register(`canvas:${editorId}:shapeSelected`, setSelectedShapes);
|
33
|
+
return () => {
|
34
|
+
Dispatcher.unregister(`canvas:${editorId}:shapeSelected`, setSelectedShapes);
|
35
|
+
};
|
36
|
+
}, [shapes, editorId]);
|
37
|
+
const handleCopyShape = (id) => (evt) => {
|
38
|
+
evt.stopPropagation();
|
39
|
+
Dispatcher.emit(`canvas:${editorId}:copyShape`, id);
|
40
|
+
};
|
41
|
+
const handleRemoveShape = (id) => () => {
|
42
|
+
Dispatcher.emit(`canvas:${editorId}:removeShape`, id);
|
43
|
+
removeShape(id);
|
44
|
+
};
|
45
|
+
const handleSelectShape = (id) => () => {
|
46
|
+
Dispatcher.emit(`canvas:${editorId}:selectShape`, id);
|
47
|
+
};
|
48
|
+
const handleEditShapeMetadata = (id) => () => {
|
49
|
+
setForm({ isOpen: true, shapeId: id, type: null, shape: null });
|
50
|
+
};
|
51
|
+
const handleSubmitMetadata = (shapeId) => (data, properties) => {
|
52
|
+
// if in creation mode, add the shape
|
53
|
+
if (form.type !== null) {
|
54
|
+
addShape(shapeId, form.type, form.shape);
|
55
|
+
}
|
56
|
+
setMetadata(Object.assign(Object.assign({}, metadata), { rois: [...metadata.rois.filter((r) => r.id !== shapeId), Object.assign({ id: shapeId, parameters: data }, properties)] }));
|
57
|
+
setForm({ isOpen: false, shapeId: '', type: null, shape: null });
|
58
|
+
};
|
59
|
+
const handleCloseMetadataForm = () => {
|
60
|
+
// if in creation mode do not add shape and delete shape from canvas
|
61
|
+
if (form.type !== null) {
|
62
|
+
Dispatcher.emit(`canvas:${editorId}:removeShape`, form.shapeId);
|
63
|
+
}
|
64
|
+
setForm({ isOpen: false, shapeId: '', type: null, shape: null });
|
65
|
+
};
|
66
|
+
const iconColor = themeMode === 'light' ? 'black' : 'white';
|
67
|
+
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.name }) }), _jsx("th", { children: _jsx(Typography, { style: { fontWeight: 'bold' }, children: strings.role }) }), _jsx("th", { children: _jsx(Typography, { style: { fontWeight: 'bold' }, children: strings.type }) }), _jsx("th", {})] }) })), _jsx("tbody", { children: Object.keys(shapes).map((id, idx) => {
|
68
|
+
var _a;
|
69
|
+
const m = metadata.rois.find((roi) => roi.id === id);
|
70
|
+
return (_jsxs("tr", { onClick: handleSelectShape(id), className: selected.indexOf(id) > -1
|
71
|
+
? css('shapes-row-selected', styles, themeMode)
|
72
|
+
: idx % 2 === 0
|
73
|
+
? css('shapes-row-even', styles, themeMode)
|
74
|
+
: css('shapes-row-odd', styles, themeMode), children: [_jsx("td", { children: _jsxs("div", { className: styles.shapesTableName, children: [_jsx("div", { className: styles.shapesTableColor, style: { backgroundColor: shapes[id].shape.stroke } }), _jsx(Typography, { children: m === null || m === void 0 ? void 0 : m.name })] }) }), _jsx("td", { children: _jsx(Typography, { children: (_a = configuration.rois.find(r => r.role === (m === null || m === void 0 ? void 0 : m.role))) === null || _a === void 0 ? void 0 : _a.label }) }), _jsx("td", { children: _jsx(Typography, { children: strings[shapes[id].type] }) }), _jsxs("td", { children: [_jsx(IconButton, { onClick: handleCopyShape(id), children: _jsx(CopyIcon, { color: iconColor }) }), _jsx(IconButton, { onClick: handleEditShapeMetadata(id), children: _jsx(AnnotateIcon, { color: iconColor }) }), _jsx(IconButton, { onClick: handleRemoveShape(id), children: _jsx(DeleteIcon, { color: iconColor }) })] })] }, id));
|
75
|
+
}) })] }), form.isOpen && (_jsx(ParametersModalForm, { shapeType: form.type || shapes[form.shapeId].type, shapeName: (_b = (_a = metadata.rois.find((roi) => roi.id === form.shapeId)) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : '', shapeRole: (_d = (_c = metadata.rois.find((roi) => roi.id === form.shapeId)) === null || _c === void 0 ? void 0 : _c.role) !== null && _d !== void 0 ? _d : '', shapeId: form.shapeId, parameters: (_f = (_e = configuration.rois.find((roi) => roi.type === (form.type || shapes[form.shapeId].type))) === null || _e === void 0 ? void 0 : _e.parameters) !== null && _f !== void 0 ? _f : [], data: (_k = (_h = (_g = metadata.rois.find((roi) => roi.id === form.shapeId)) === null || _g === void 0 ? void 0 : _g.parameters) !== null && _h !== void 0 ? _h : (_j = configuration.rois.find((roi) => roi.type === (form.type || shapes[form.shapeId].type))) === null || _j === void 0 ? void 0 : _j.parameters) !== null && _k !== void 0 ? _k : [], title: strings.shapeParametersMetadata, onClose: handleCloseMetadataForm, onSubmit: handleSubmitMetadata(form.shapeId) }))] }));
|
76
|
+
};
|
77
|
+
export default ShapesList;
|
@@ -0,0 +1,71 @@
|
|
1
|
+
.shapes-table {
|
2
|
+
border-collapse: collapse;
|
3
|
+
width: 100%;
|
4
|
+
}
|
5
|
+
|
6
|
+
.shapes-table tr {
|
7
|
+
cursor: pointer;
|
8
|
+
}
|
9
|
+
|
10
|
+
.shapes-table th {
|
11
|
+
padding: .8rem .3rem;
|
12
|
+
text-align: left;
|
13
|
+
}
|
14
|
+
|
15
|
+
.shapes-table td {
|
16
|
+
padding: 0 .3rem;
|
17
|
+
}
|
18
|
+
|
19
|
+
.shapes-table tr td:last-child,
|
20
|
+
.shapes-table tr th:last-child {
|
21
|
+
text-align: right;
|
22
|
+
}
|
23
|
+
|
24
|
+
.shapes-table-light {
|
25
|
+
background-color: #eee;
|
26
|
+
color: #000;
|
27
|
+
}
|
28
|
+
|
29
|
+
.shapes-table-dark {
|
30
|
+
background-color: #333;
|
31
|
+
color: #fff;
|
32
|
+
}
|
33
|
+
|
34
|
+
.shapes-row-selected-light {
|
35
|
+
background-color: #ccc;
|
36
|
+
}
|
37
|
+
|
38
|
+
.shapes-row-selected-dark {
|
39
|
+
background-color: #666;
|
40
|
+
}
|
41
|
+
|
42
|
+
.shapes-row-even-light {
|
43
|
+
background-color: #f7f7f7;
|
44
|
+
}
|
45
|
+
|
46
|
+
.shapes-row-even-dark {
|
47
|
+
background-color: #444;
|
48
|
+
}
|
49
|
+
|
50
|
+
.shapes-row-odd-light {
|
51
|
+
background-color: #f0f0f0;
|
52
|
+
}
|
53
|
+
|
54
|
+
.shapes-row-odd-dark {
|
55
|
+
background-color: #555;
|
56
|
+
}
|
57
|
+
|
58
|
+
.shapesTableName {
|
59
|
+
display: flex;
|
60
|
+
align-items: center;
|
61
|
+
}
|
62
|
+
|
63
|
+
.shapesTableColor {
|
64
|
+
border: 1px solid #000;
|
65
|
+
box-sizing: border-box;
|
66
|
+
display: inline-block;
|
67
|
+
width: 24px;
|
68
|
+
height: 24px;
|
69
|
+
border-radius: 50%;
|
70
|
+
margin-right: .5rem;
|
71
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import { useContext } from 'react';
|
3
|
+
import PointerIcon from '../../Icons/PointerIcon';
|
4
|
+
import PolygonIcon from '../../Icons/PolygonIcon';
|
5
|
+
import PolylineIcon from '../../Icons/PolylineIcon';
|
6
|
+
import RectangleIcon from '../../Icons/RectangleIcon';
|
7
|
+
import { useEditorContext } from '../../Providers/EditorProvider';
|
8
|
+
import { UiContext } from '../../Providers/UiProvider';
|
9
|
+
import { css } from '../../Utils';
|
10
|
+
import ColorPicker from './ColorPicker';
|
11
|
+
import styles from './Toolbar.module.css';
|
12
|
+
import { canDrawShape, enableRois } from './Utils';
|
13
|
+
const Toolbar = () => {
|
14
|
+
var _a;
|
15
|
+
const { IconButton, themeMode, primaryColor, Typography, strings } = useContext(UiContext);
|
16
|
+
const { activeTool, setActiveTool, configuration, shapes } = useEditorContext();
|
17
|
+
const iconColor = (tool) => (tool === activeTool ? primaryColor : themeMode === 'light' ? 'black' : 'white');
|
18
|
+
const setTool = (tool) => () => setActiveTool(tool);
|
19
|
+
const hideForbiddenTools = (_a = configuration.options) === null || _a === void 0 ? void 0 : _a.hideForbiddenTools;
|
20
|
+
const polylineEnabled = configuration.rois.find((r) => r.type === "polyline" /* ToolEnum.Polyline */) &&
|
21
|
+
canDrawShape(configuration, "polyline" /* ToolEnum.Polyline */, shapes);
|
22
|
+
const polygonEnabled = configuration.rois.find((r) => r.type === "polygon" /* ToolEnum.Polygon */) && canDrawShape(configuration, "polygon" /* ToolEnum.Polygon */, shapes);
|
23
|
+
const rectangleEnabled = configuration.rois.find((r) => r.type === "rect" /* ToolEnum.Rectangle */) &&
|
24
|
+
canDrawShape(configuration, "rect" /* ToolEnum.Rectangle */, shapes);
|
25
|
+
return (_jsxs(_Fragment, { children: [_jsx("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) && (_jsx("div", { className: css('toolbar-helper', styles, themeMode), children: _jsxs(Typography, { children: [strings[activeTool], ": ", strings[`${activeTool}HelpText`]] }) }))] }));
|
26
|
+
};
|
27
|
+
export default Toolbar;
|
@@ -0,0 +1,41 @@
|
|
1
|
+
.toolbar {
|
2
|
+
align-items: center;
|
3
|
+
display: flex;
|
4
|
+
padding: .5rem;
|
5
|
+
}
|
6
|
+
|
7
|
+
.toolbar-light {
|
8
|
+
background-color: #eee;;
|
9
|
+
}
|
10
|
+
|
11
|
+
.toolbar-dark {
|
12
|
+
background-color: #333;
|
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;
|
32
|
+
width: 1px;
|
33
|
+
}
|
34
|
+
|
35
|
+
.toolbar-spacer-light {
|
36
|
+
background-color: #ccc;
|
37
|
+
}
|
38
|
+
|
39
|
+
.toolbar-spacer-dark {
|
40
|
+
background-color: #555;
|
41
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import { useContext, useState } from 'react';
|
3
|
+
import { useEditorContext } from '../../Providers/EditorProvider';
|
4
|
+
import { UiContext } from '../../Providers/UiProvider';
|
5
|
+
import { css } from '../../Utils';
|
6
|
+
import { enableMainMetadata } from './Utils';
|
7
|
+
import SaveIcon from '../../Icons/SaveIcon';
|
8
|
+
import ParametersModalForm from './ParametersModalForm';
|
9
|
+
import styles from './TopBar.module.css';
|
10
|
+
const TopBar = () => {
|
11
|
+
const { themeMode, AnnotateIcon, Button, primaryFgColor, strings } = useContext(UiContext);
|
12
|
+
const { configuration, onSubmit, metadata, setMetadata } = useEditorContext();
|
13
|
+
const [form, setForm] = useState({ isOpen: false });
|
14
|
+
const iconColor = themeMode === 'light' ? 'black' : 'white';
|
15
|
+
const handleSubmitMetadata = (data) => {
|
16
|
+
setMetadata(Object.assign(Object.assign({}, metadata), { parameters: data }));
|
17
|
+
setForm({ isOpen: false });
|
18
|
+
};
|
19
|
+
return (_jsxs("div", { className: css('top-bar', styles, themeMode), children: [enableMainMetadata(configuration) && (_jsxs(Button, { onClick: () => setForm({ isOpen: true }), children: [_jsx(AnnotateIcon, { color: iconColor }), " ", strings.mainParametersMetadata] })), _jsxs(Button, { primary: true, onClick: onSubmit, children: [_jsx(SaveIcon, { color: primaryFgColor }), " ", strings.save] }), form.isOpen && (_jsx(ParametersModalForm, { parameters: configuration.parameters, data: metadata.parameters, title: strings.mainParametersMetadata, onClose: () => setForm({ isOpen: false }), onSubmit: handleSubmitMetadata }))] }));
|
20
|
+
};
|
21
|
+
export default TopBar;
|
@@ -0,0 +1,128 @@
|
|
1
|
+
import * as fabric from 'fabric';
|
2
|
+
export declare const enum ToolEnum {
|
3
|
+
Pointer = "pointer",
|
4
|
+
Polyline = "polyline",
|
5
|
+
Polygon = "polygon",
|
6
|
+
Rectangle = "rect"
|
7
|
+
}
|
8
|
+
export type ShapeType = ToolEnum.Polyline | ToolEnum.Polygon | ToolEnum.Rectangle;
|
9
|
+
export type Shape = (fabric.Rect | fabric.Polygon | fabric.Polyline) & {
|
10
|
+
id?: string;
|
11
|
+
};
|
12
|
+
export type Shapes = Record<string, {
|
13
|
+
type: ShapeType;
|
14
|
+
shape: Shape;
|
15
|
+
}>;
|
16
|
+
export type FabricEvent = fabric.TPointerEventInfo<fabric.TPointerEvent>;
|
17
|
+
export type FabricSelectionEvent = Partial<fabric.TEvent> & {
|
18
|
+
selected: fabric.Object[];
|
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;
|
42
|
+
label: string;
|
43
|
+
}[];
|
44
|
+
multiple?: boolean;
|
45
|
+
required: boolean;
|
46
|
+
value: number | string | boolean | string[] | number[] | null;
|
47
|
+
};
|
48
|
+
export type RoiConfiguration = {
|
49
|
+
role: string;
|
50
|
+
label: string;
|
51
|
+
type: Omit<ShapeType, 'pointer'>;
|
52
|
+
multiplicity: {
|
53
|
+
operator: OperatorEnum;
|
54
|
+
threshold: number;
|
55
|
+
};
|
56
|
+
parameters: ConfigurationParameter[];
|
57
|
+
};
|
58
|
+
export type Configuration = {
|
59
|
+
parameters: ConfigurationParameter[];
|
60
|
+
rois: RoiConfiguration[];
|
61
|
+
options?: {
|
62
|
+
hideForbiddenTools?: boolean;
|
63
|
+
description?: string;
|
64
|
+
};
|
65
|
+
};
|
66
|
+
export interface INotify {
|
67
|
+
info: (message: string) => void;
|
68
|
+
warn: (message: string) => void;
|
69
|
+
error: (message: string) => void;
|
70
|
+
success: (message: string) => void;
|
71
|
+
}
|
72
|
+
export type Metadata = {
|
73
|
+
parameters: OutputParameter[];
|
74
|
+
rois: {
|
75
|
+
id: string;
|
76
|
+
parameters: OutputParameter[];
|
77
|
+
name: string;
|
78
|
+
role: string;
|
79
|
+
}[];
|
80
|
+
};
|
81
|
+
export type OutputShapeRect = {
|
82
|
+
top: number;
|
83
|
+
left: number;
|
84
|
+
width: number;
|
85
|
+
height: number;
|
86
|
+
color: string;
|
87
|
+
};
|
88
|
+
export type OutputShapePolyline = {
|
89
|
+
points: {
|
90
|
+
x: number;
|
91
|
+
y: number;
|
92
|
+
}[];
|
93
|
+
top: number;
|
94
|
+
left: number;
|
95
|
+
color: string;
|
96
|
+
};
|
97
|
+
export type OutputShapePolygon = {
|
98
|
+
points: {
|
99
|
+
x: number;
|
100
|
+
y: number;
|
101
|
+
}[];
|
102
|
+
top: number;
|
103
|
+
left: number;
|
104
|
+
color: string;
|
105
|
+
};
|
106
|
+
export interface OutputParameter {
|
107
|
+
codename: string;
|
108
|
+
value: number | string | boolean | string[] | number[] | null;
|
109
|
+
}
|
110
|
+
export interface OutputRoi {
|
111
|
+
parameters: OutputParameter[];
|
112
|
+
type: ShapeType;
|
113
|
+
name: string;
|
114
|
+
role: string;
|
115
|
+
id: string;
|
116
|
+
shape: OutputShapeRect | OutputShapePolyline | OutputShapePolygon;
|
117
|
+
}
|
118
|
+
export interface Output {
|
119
|
+
parameters: OutputParameter[];
|
120
|
+
rois: OutputRoi[];
|
121
|
+
}
|
122
|
+
export declare enum UpdateEventType {
|
123
|
+
AddRoi = "AddRoi",
|
124
|
+
RemoveRoi = "RemoveRoi",
|
125
|
+
UpdateRoi = "UpdateRoi",
|
126
|
+
UpdateRoiParameters = "UpdateRoiParameters",
|
127
|
+
UpdateMainParameters = "UpdateMainParameters"
|
128
|
+
}
|
@@ -0,0 +1,23 @@
|
|
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 = {}));
|
16
|
+
export var UpdateEventType;
|
17
|
+
(function (UpdateEventType) {
|
18
|
+
UpdateEventType["AddRoi"] = "AddRoi";
|
19
|
+
UpdateEventType["RemoveRoi"] = "RemoveRoi";
|
20
|
+
UpdateEventType["UpdateRoi"] = "UpdateRoi";
|
21
|
+
UpdateEventType["UpdateRoiParameters"] = "UpdateRoiParameters";
|
22
|
+
UpdateEventType["UpdateMainParameters"] = "UpdateMainParameters";
|
23
|
+
})(UpdateEventType || (UpdateEventType = {}));
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { Configuration, ConfigurationParameter, INotify, Metadata, Shape, Shapes, ShapeType, 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;
|
6
|
+
export declare const validateParametersForm: (parameters: ConfigurationParameter[], fields: Record<string, unknown>, setErrors: (errors: Record<string, string>) => void) => boolean;
|
7
|
+
export declare const validate: (configuration: Configuration, shapes: Shapes, metadata: Metadata, strings: Record<string, string>) => [boolean, string[]];
|
8
|
+
export declare const fabricShapeToOutputShape: (shape: Shape, type: ShapeType, imageSize: {
|
9
|
+
width: number;
|
10
|
+
height: number;
|
11
|
+
}) => {
|
12
|
+
top: number;
|
13
|
+
left: number;
|
14
|
+
width: number;
|
15
|
+
height: number;
|
16
|
+
color: string;
|
17
|
+
points?: undefined;
|
18
|
+
} | {
|
19
|
+
points: any;
|
20
|
+
top: number;
|
21
|
+
left: number;
|
22
|
+
color: string;
|
23
|
+
width?: undefined;
|
24
|
+
height?: undefined;
|
25
|
+
};
|