lost-sia 2.0.1-alpha13 → 2.0.1-alpha14
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/Annotation/ui/AnnotationComponent.js +1 -1
- package/dist/Annotation/ui/atoms/DaviIcon.d.ts +1 -1
- package/dist/Canvas/Canvas.js +1 -1
- package/dist/IconButton.d.ts +25 -0
- package/dist/IconButton.js +1 -0
- package/dist/Sia.js +1 -1
- package/dist/Toolbar/ToolbarItems/AccessibilityTools.js +1 -1
- package/dist/Toolbar/ToolbarItems/AnnoToolSelector.js +1 -1
- package/dist/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.js +1 -1
- package/dist/Toolbar/ToolbarItems/ImageTools.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/models/index.d.ts +1 -1
- package/package.json +3 -2
- package/src/AnnoExampleViewer.jsx +18 -18
- package/src/Annotation/logic/Annotation.ts +24 -24
- package/src/Annotation/ui/AnnotationComponent.tsx +89 -96
- package/src/Annotation/ui/atoms/DaviIcon.tsx +12 -22
- package/src/Annotation/ui/atoms/PolygonArea.tsx +35 -35
- package/src/Annotation/ui/tools/Point.tsx +17 -17
- package/src/Canvas/Canvas.tsx +36 -13
- package/src/Canvas/LabelInput.tsx +27 -34
- package/src/IconButton.tsx +119 -0
- package/src/InfoBoxes/AnnoDetails.jsx +53 -53
- package/src/InfoBoxes/AnnoStats.jsx +41 -41
- package/src/InfoBoxes/InfoBox.jsx +16 -16
- package/src/InfoBoxes/LabelInfo.jsx +30 -30
- package/src/SIASettingButton.jsx +25 -25
- package/src/Sia.tsx +29 -12
- package/src/Toolbar/Toolbar.tsx +25 -25
- package/src/Toolbar/ToolbarItems/AccessibilityTools.tsx +23 -43
- package/src/Toolbar/ToolbarItems/AnnoToolSelector.tsx +53 -43
- package/src/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.tsx +71 -71
- package/src/Toolbar/ToolbarItems/ImageToolItems/TagLabel.tsx +24 -24
- package/src/Toolbar/ToolbarItems/ImageTools.tsx +28 -30
- package/src/Toolbar/ToolbarItems/Instructions.tsx +47 -50
- package/src/Toolbar/ToolbarItems/InstructionsModal.tsx +8 -8
- package/src/index.ts +1 -0
- package/src/models/AnnotationMode.ts +1 -1
- package/src/models/AnnotationStatus.ts +1 -1
- package/src/models/AnnotationTool.ts +1 -1
- package/src/models/CanvasAction.ts +1 -1
- package/src/models/Direction.ts +1 -1
- package/src/models/EditorModes.ts +1 -1
- package/src/models/KeyAction.ts +1 -1
- package/src/models/NotificationType.ts +1 -1
- package/src/models/index.ts +6 -12
- package/src/siaDummyData.js +71 -71
- package/src/stories/Button.jsx +11 -15
- package/src/stories/Button.stories.js +17 -17
- package/src/stories/Canvas/CanvasWithOffset.stories.tsx +25 -25
- package/src/stories/FilterDropdown.stories.ts +10 -10
- package/src/stories/Header.jsx +8 -13
- package/src/stories/Header.stories.js +9 -9
- package/src/stories/Page.jsx +21 -26
- package/src/stories/Page.stories.js +14 -14
- package/src/stories/SIA2/DemoWrapper.stories.tsx +22 -22
- package/src/stories/Toolbar/ImageTools/TagLabel.stories.tsx +11 -11
- package/src/stories/Toolbar/Instructions.stories.tsx +11 -11
- package/src/stories/Toolbar/Toolbar.stories.tsx +20 -20
- package/src/stories/siaDummyData.js +71 -71
- package/src/stories/siaDummyData2.ts +72 -72
- package/src/types.ts +47 -47
- package/src/utils/KeyMapper.ts +56 -61
- package/src/utils/TimeUtils.ts +7 -10
- package/src/utils/color.ts +25 -25
- package/src/utils/hist.js +22 -22
- package/src/utils/index.ts +3 -3
- package/src/utils/keyActions.js +81 -81
- package/src/utils/mouse.ts +9 -9
- package/src/utils/transform.ts +66 -72
- package/src/utils/uiConfig.js +19 -22
- package/src/utils/windowViewport.ts +10 -10
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import AnnotationTool from
|
|
2
|
-
import Annotation from
|
|
3
|
-
import * as colorUtils from
|
|
4
|
-
import PointTool from
|
|
5
|
-
import Line from
|
|
6
|
-
import AnnoBar from
|
|
7
|
-
import CanvasAction from
|
|
8
|
-
import BBox from
|
|
9
|
-
import Polygon from
|
|
10
|
-
import { useEffect, useRef, useState } from
|
|
11
|
-
import { AnnotationSettings, Label, Point, SIANotification } from
|
|
12
|
-
import AnnotationMode from
|
|
13
|
-
import TimeUtils from
|
|
1
|
+
import AnnotationTool from '../../models/AnnotationTool'
|
|
2
|
+
import Annotation from '../logic/Annotation'
|
|
3
|
+
import * as colorUtils from '../../utils/color'
|
|
4
|
+
import PointTool from './tools/Point'
|
|
5
|
+
import Line from './tools/Line'
|
|
6
|
+
import AnnoBar from './atoms/AnnoBar'
|
|
7
|
+
import CanvasAction from '../../models/CanvasAction'
|
|
8
|
+
import BBox from './tools/BBox'
|
|
9
|
+
import Polygon from './tools/Polygon'
|
|
10
|
+
import { useEffect, useRef, useState } from 'react'
|
|
11
|
+
import { AnnotationSettings, Label, Point, SIANotification } from '../../types'
|
|
12
|
+
import AnnotationMode from '../../models/AnnotationMode'
|
|
13
|
+
import TimeUtils from '../../utils/TimeUtils'
|
|
14
14
|
|
|
15
15
|
type AnnotationComponentProps = {
|
|
16
|
-
scaledAnnotation: Annotation
|
|
17
|
-
annotationSettings: AnnotationSettings
|
|
18
|
-
possibleLabels: Label[]
|
|
19
|
-
svgScale: number
|
|
20
|
-
svgTranslation: Point
|
|
21
|
-
pageToStageOffset: Point
|
|
22
|
-
strokeWidth: number
|
|
23
|
-
nodeRadius: number
|
|
24
|
-
isSelected: boolean
|
|
25
|
-
isDisabled?: boolean
|
|
26
|
-
onFinishAnnoCreate: (fullyCreatedAnnotation: Annotation) => void
|
|
27
|
-
onLabelIconClicked: (markerPosition: Point) => void
|
|
28
|
-
onAction?: (annotation: Annotation, canvasAction: CanvasAction) => void
|
|
29
|
-
onAnnoChanged?: (annotation: Annotation) => void
|
|
30
|
-
onAnnotationModeChange?: (annotationMode: AnnotationMode) => void
|
|
31
|
-
onNotification?: (notification: SIANotification) => void
|
|
32
|
-
}
|
|
16
|
+
scaledAnnotation: Annotation
|
|
17
|
+
annotationSettings: AnnotationSettings
|
|
18
|
+
possibleLabels: Label[]
|
|
19
|
+
svgScale: number
|
|
20
|
+
svgTranslation: Point
|
|
21
|
+
pageToStageOffset: Point
|
|
22
|
+
strokeWidth: number
|
|
23
|
+
nodeRadius: number
|
|
24
|
+
isSelected: boolean
|
|
25
|
+
isDisabled?: boolean
|
|
26
|
+
onFinishAnnoCreate: (fullyCreatedAnnotation: Annotation) => void
|
|
27
|
+
onLabelIconClicked: (markerPosition: Point) => void
|
|
28
|
+
onAction?: (annotation: Annotation, canvasAction: CanvasAction) => void
|
|
29
|
+
onAnnoChanged?: (annotation: Annotation) => void
|
|
30
|
+
onAnnotationModeChange?: (annotationMode: AnnotationMode) => void
|
|
31
|
+
onNotification?: (notification: SIANotification) => void
|
|
32
|
+
}
|
|
33
33
|
|
|
34
34
|
const AnnotationComponent = ({
|
|
35
35
|
scaledAnnotation,
|
|
@@ -49,21 +49,19 @@ const AnnotationComponent = ({
|
|
|
49
49
|
onAnnotationModeChange = (_) => {},
|
|
50
50
|
onNotification = (_) => {},
|
|
51
51
|
}: AnnotationComponentProps) => {
|
|
52
|
-
const [coordinates, setCoordinates] = useState<Point[]>(
|
|
53
|
-
scaledAnnotation.coordinates,
|
|
54
|
-
);
|
|
52
|
+
const [coordinates, setCoordinates] = useState<Point[]>(scaledAnnotation.coordinates)
|
|
55
53
|
|
|
56
54
|
const [annotationMode, setAnnotationMode] = useState<AnnotationMode>(
|
|
57
55
|
scaledAnnotation.mode,
|
|
58
|
-
)
|
|
59
|
-
const [isDragging, setIsDragging] = useState<boolean>(false)
|
|
56
|
+
)
|
|
57
|
+
const [isDragging, setIsDragging] = useState<boolean>(false)
|
|
60
58
|
|
|
61
|
-
const annoTimestampRef = useRef<number | undefined>(undefined)
|
|
62
|
-
const [annoTimestamp, setAnnoTimestamp] = useState<number | undefined>()
|
|
59
|
+
const annoTimestampRef = useRef<number | undefined>(undefined)
|
|
60
|
+
const [annoTimestamp, setAnnoTimestamp] = useState<number | undefined>()
|
|
63
61
|
|
|
64
62
|
useEffect(() => {
|
|
65
|
-
annoTimestampRef.current = annoTimestamp
|
|
66
|
-
}, [annoTimestamp])
|
|
63
|
+
annoTimestampRef.current = annoTimestamp
|
|
64
|
+
}, [annoTimestamp])
|
|
67
65
|
|
|
68
66
|
/**
|
|
69
67
|
* during user editing of the annotation, multiple events are fired by the children
|
|
@@ -73,120 +71,115 @@ const AnnotationComponent = ({
|
|
|
73
71
|
* since both events are fired during the same render, onMoved would give away old coorinates
|
|
74
72
|
* use this reference as a workaround to get the up-to-date coordinates even in this edge-case
|
|
75
73
|
*/
|
|
76
|
-
const coordinatesRef = useRef<Point[]>(coordinates)
|
|
74
|
+
const coordinatesRef = useRef<Point[]>(coordinates)
|
|
77
75
|
|
|
78
76
|
useEffect(() => {
|
|
79
|
-
coordinatesRef.current = coordinates
|
|
80
|
-
}, [coordinates])
|
|
77
|
+
coordinatesRef.current = coordinates
|
|
78
|
+
}, [coordinates])
|
|
81
79
|
|
|
82
80
|
const finishAnnoCreate = () => {
|
|
83
|
-
setAnnotationMode(AnnotationMode.VIEW)
|
|
81
|
+
setAnnotationMode(AnnotationMode.VIEW)
|
|
84
82
|
|
|
85
83
|
const newAnnotation = {
|
|
86
84
|
...scaledAnnotation,
|
|
87
85
|
coordinates: coordinatesRef.current,
|
|
88
|
-
}
|
|
86
|
+
}
|
|
89
87
|
|
|
90
|
-
onFinishAnnoCreate(newAnnotation)
|
|
91
|
-
}
|
|
88
|
+
onFinishAnnoCreate(newAnnotation)
|
|
89
|
+
}
|
|
92
90
|
|
|
93
91
|
const getLabel = (labelId: number): Label | undefined => {
|
|
94
92
|
return possibleLabels.find((label: Label) => {
|
|
95
|
-
return label.id === labelId
|
|
96
|
-
})
|
|
97
|
-
}
|
|
93
|
+
return label.id === labelId
|
|
94
|
+
})
|
|
95
|
+
}
|
|
98
96
|
|
|
99
97
|
const getColor = () => {
|
|
100
98
|
if (!scaledAnnotation.labelIds || scaledAnnotation.labelIds.length == 0)
|
|
101
|
-
return colorUtils.getDefaultColor()
|
|
99
|
+
return colorUtils.getDefaultColor()
|
|
102
100
|
|
|
103
|
-
const label = getLabel(scaledAnnotation.labelIds[0])
|
|
101
|
+
const label = getLabel(scaledAnnotation.labelIds[0])
|
|
104
102
|
|
|
105
|
-
if (label === undefined || label.color === undefined)
|
|
106
|
-
return colorUtils.getDefaultColor()
|
|
103
|
+
if (label === undefined || label.color === undefined || label.color === null)
|
|
104
|
+
return colorUtils.getDefaultColor()
|
|
107
105
|
|
|
108
|
-
return label.color
|
|
109
|
-
}
|
|
106
|
+
return label.color
|
|
107
|
+
}
|
|
110
108
|
|
|
111
|
-
const color = getColor()
|
|
109
|
+
const color = getColor()
|
|
112
110
|
const annotationStyle = {
|
|
113
111
|
stroke: color,
|
|
114
112
|
fill: color,
|
|
115
113
|
strokeWidth: strokeWidth / svgScale,
|
|
116
114
|
r: nodeRadius / svgScale,
|
|
117
|
-
}
|
|
115
|
+
}
|
|
118
116
|
|
|
119
117
|
const changeAnnoCoords = (newCoordinates: Point[]) => {
|
|
120
|
-
setCoordinates(newCoordinates)
|
|
118
|
+
setCoordinates(newCoordinates)
|
|
121
119
|
|
|
122
|
-
let newCoordinatesWithoutMouse = newCoordinates
|
|
120
|
+
let newCoordinatesWithoutMouse = newCoordinates
|
|
123
121
|
|
|
124
122
|
// while adding/moving the annotation, the mouse cursor is the last point of the coordinates
|
|
125
123
|
// remove it before saving the annotation
|
|
126
124
|
if ([AnnotationMode.ADD, AnnotationMode.MOVE].includes(annotationMode)) {
|
|
127
125
|
// last point is mouse - remove it before export
|
|
128
|
-
newCoordinatesWithoutMouse = newCoordinates.slice(0, -1)
|
|
126
|
+
newCoordinatesWithoutMouse = newCoordinates.slice(0, -1)
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
onAnnoChanged({
|
|
132
130
|
...scaledAnnotation,
|
|
133
131
|
coordinates: newCoordinatesWithoutMouse,
|
|
134
|
-
})
|
|
135
|
-
}
|
|
132
|
+
})
|
|
133
|
+
}
|
|
136
134
|
|
|
137
135
|
const onMoving = (newCoords: Point[]) => {
|
|
138
136
|
if (
|
|
139
|
-
![
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
AnnotationMode.MOVE,
|
|
143
|
-
].includes(annotationMode)
|
|
137
|
+
![AnnotationMode.ADD, AnnotationMode.CREATE, AnnotationMode.MOVE].includes(
|
|
138
|
+
annotationMode,
|
|
139
|
+
)
|
|
144
140
|
) {
|
|
145
|
-
setAnnotationMode(AnnotationMode.MOVE)
|
|
141
|
+
setAnnotationMode(AnnotationMode.MOVE)
|
|
146
142
|
|
|
147
|
-
setAnnoTimestamp(performance.now())
|
|
143
|
+
setAnnoTimestamp(performance.now())
|
|
148
144
|
}
|
|
149
145
|
|
|
150
|
-
setCoordinates(newCoords)
|
|
151
|
-
}
|
|
146
|
+
setCoordinates(newCoords)
|
|
147
|
+
}
|
|
152
148
|
|
|
153
149
|
const onMoved = () => {
|
|
154
|
-
setAnnotationMode(AnnotationMode.VIEW)
|
|
150
|
+
setAnnotationMode(AnnotationMode.VIEW)
|
|
155
151
|
|
|
156
152
|
const annoEditDuration: number = TimeUtils.getRoundedDuration(
|
|
157
153
|
annoTimestampRef.current,
|
|
158
154
|
performance.now(),
|
|
159
|
-
)
|
|
155
|
+
)
|
|
160
156
|
|
|
161
157
|
// add annotation time (or set it if there was no time before)
|
|
162
158
|
// null seems to be a number in the JS world
|
|
163
159
|
const newAnnoTime: number =
|
|
164
160
|
isNaN(scaledAnnotation.annoTime) || scaledAnnotation.annoTime === null
|
|
165
161
|
? annoEditDuration
|
|
166
|
-
: scaledAnnotation.annoTime + annoEditDuration
|
|
162
|
+
: scaledAnnotation.annoTime + annoEditDuration
|
|
167
163
|
|
|
168
164
|
// moving finished - send event to canvas
|
|
169
165
|
onAnnoChanged({
|
|
170
166
|
...scaledAnnotation,
|
|
171
167
|
coordinates: coordinatesRef.current,
|
|
172
168
|
annoTime: newAnnoTime,
|
|
173
|
-
})
|
|
174
|
-
}
|
|
169
|
+
})
|
|
170
|
+
}
|
|
175
171
|
|
|
176
172
|
useEffect(() => {
|
|
177
|
-
onAnnotationModeChange(annotationMode)
|
|
178
|
-
}, [annotationMode])
|
|
173
|
+
onAnnotationModeChange(annotationMode)
|
|
174
|
+
}, [annotationMode])
|
|
179
175
|
|
|
180
176
|
// apply coordinate changes from sia (e.g. out of image fixes)
|
|
181
177
|
// ignore outside changes while creating annotation
|
|
182
178
|
useEffect(() => {
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return;
|
|
188
|
-
setCoordinates(scaledAnnotation.coordinates);
|
|
189
|
-
}, [scaledAnnotation]);
|
|
179
|
+
if (annotationMode === AnnotationMode.CREATE || annotationMode === AnnotationMode.ADD)
|
|
180
|
+
return
|
|
181
|
+
setCoordinates(scaledAnnotation.coordinates)
|
|
182
|
+
}, [scaledAnnotation])
|
|
190
183
|
|
|
191
184
|
const renderAnno = () => {
|
|
192
185
|
switch (scaledAnnotation.type) {
|
|
@@ -204,7 +197,7 @@ const AnnotationComponent = ({
|
|
|
204
197
|
onMoved={onMoved}
|
|
205
198
|
onIsDraggingStateChanged={setIsDragging}
|
|
206
199
|
/>
|
|
207
|
-
)
|
|
200
|
+
)
|
|
208
201
|
case AnnotationTool.Line:
|
|
209
202
|
return (
|
|
210
203
|
<Line
|
|
@@ -224,7 +217,7 @@ const AnnotationComponent = ({
|
|
|
224
217
|
onIsDraggingStateChanged={setIsDragging}
|
|
225
218
|
onFinishAnnoCreate={finishAnnoCreate}
|
|
226
219
|
/>
|
|
227
|
-
)
|
|
220
|
+
)
|
|
228
221
|
case AnnotationTool.BBox:
|
|
229
222
|
return (
|
|
230
223
|
<BBox
|
|
@@ -238,14 +231,14 @@ const AnnotationComponent = ({
|
|
|
238
231
|
svgScale={svgScale}
|
|
239
232
|
svgTranslation={svgTranslation}
|
|
240
233
|
onDeleteNode={() => {
|
|
241
|
-
console.log(
|
|
234
|
+
console.log('TODO')
|
|
242
235
|
}}
|
|
243
236
|
onIsDraggingStateChanged={setIsDragging}
|
|
244
237
|
onFinishAnnoCreate={finishAnnoCreate}
|
|
245
238
|
onMoving={onMoving}
|
|
246
239
|
onMoved={onMoved}
|
|
247
240
|
/>
|
|
248
|
-
)
|
|
241
|
+
)
|
|
249
242
|
case AnnotationTool.Polygon:
|
|
250
243
|
return (
|
|
251
244
|
<Polygon
|
|
@@ -267,9 +260,9 @@ const AnnotationComponent = ({
|
|
|
267
260
|
onIsDraggingStateChanged={setIsDragging}
|
|
268
261
|
onFinishAnnoCreate={finishAnnoCreate}
|
|
269
262
|
/>
|
|
270
|
-
)
|
|
263
|
+
)
|
|
271
264
|
}
|
|
272
|
-
}
|
|
265
|
+
}
|
|
273
266
|
|
|
274
267
|
return (
|
|
275
268
|
<g
|
|
@@ -278,8 +271,8 @@ const AnnotationComponent = ({
|
|
|
278
271
|
// onMouseDown={(e) => this.onMouseDown(e)}
|
|
279
272
|
// onContextMenu={(e) => this.onContextMenu(e)}
|
|
280
273
|
onClick={(e) => {
|
|
281
|
-
e.stopPropagation()
|
|
282
|
-
onAction(scaledAnnotation, CanvasAction.ANNO_SELECTED)
|
|
274
|
+
e.stopPropagation()
|
|
275
|
+
onAction(scaledAnnotation, CanvasAction.ANNO_SELECTED)
|
|
283
276
|
}}
|
|
284
277
|
>
|
|
285
278
|
{!isDragging && annotationMode !== AnnotationMode.CREATE && (
|
|
@@ -297,7 +290,7 @@ const AnnotationComponent = ({
|
|
|
297
290
|
)}
|
|
298
291
|
{renderAnno()}
|
|
299
292
|
</g>
|
|
300
|
-
)
|
|
301
|
-
}
|
|
293
|
+
)
|
|
294
|
+
}
|
|
302
295
|
|
|
303
|
-
export default AnnotationComponent
|
|
296
|
+
export default AnnotationComponent
|
|
@@ -1,26 +1,16 @@
|
|
|
1
1
|
type DaviIconProps = {
|
|
2
|
-
x: number
|
|
3
|
-
y: number
|
|
4
|
-
color: string
|
|
5
|
-
size: number
|
|
6
|
-
onClick?: () => void
|
|
7
|
-
}
|
|
2
|
+
x: number
|
|
3
|
+
y: number
|
|
4
|
+
color: string
|
|
5
|
+
size: number
|
|
6
|
+
onClick?: () => void
|
|
7
|
+
}
|
|
8
8
|
|
|
9
|
-
const DaviIcon = ({
|
|
10
|
-
|
|
11
|
-
y,
|
|
12
|
-
color,
|
|
13
|
-
size = 60,
|
|
14
|
-
onClick = () => {},
|
|
15
|
-
}: DaviIconProps) => {
|
|
16
|
-
const scale: number = size / 1400;
|
|
9
|
+
const DaviIcon = ({ x, y, color, size = 60, onClick = () => {} }: DaviIconProps) => {
|
|
10
|
+
const scale: number = size / 1400
|
|
17
11
|
|
|
18
12
|
return (
|
|
19
|
-
<g
|
|
20
|
-
transform={`translate(${x} ${y}) scale(${scale})`}
|
|
21
|
-
fill={color}
|
|
22
|
-
onClick={onClick}
|
|
23
|
-
>
|
|
13
|
+
<g transform={`translate(${x} ${y}) scale(${scale})`} fill={color} onClick={onClick}>
|
|
24
14
|
<path
|
|
25
15
|
id="Maps"
|
|
26
16
|
d="M620.561,817.217c-1.568-3.62-3.771-7.101-4.611-10.885
|
|
@@ -51,7 +41,7 @@ const DaviIcon = ({
|
|
|
51
41
|
c-0.074,8.051-6.938,12.819-13.703,12.847C731.482,263.519,724.956,259.059,724.709,250.898z"
|
|
52
42
|
/>
|
|
53
43
|
</g>
|
|
54
|
-
)
|
|
55
|
-
}
|
|
44
|
+
)
|
|
45
|
+
}
|
|
56
46
|
|
|
57
|
-
export default DaviIcon
|
|
47
|
+
export default DaviIcon
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { CSSProperties, useEffect, useState } from
|
|
2
|
-
import { Point } from
|
|
3
|
-
import AnnotationMode from
|
|
4
|
-
import { AnnotationSettings } from
|
|
1
|
+
import { CSSProperties, useEffect, useState } from 'react'
|
|
2
|
+
import { Point } from '../../../types'
|
|
3
|
+
import AnnotationMode from '../../../models/AnnotationMode'
|
|
4
|
+
import { AnnotationSettings } from '../../../types'
|
|
5
5
|
|
|
6
6
|
type PolygonAreaProps = {
|
|
7
|
-
coordinates: Point[]
|
|
8
|
-
isSelected: boolean
|
|
9
|
-
isDisabled?: boolean
|
|
10
|
-
annotationMode: AnnotationMode
|
|
11
|
-
annotationSettings: AnnotationSettings
|
|
12
|
-
pageToStageOffset: Point
|
|
13
|
-
svgScale: number
|
|
14
|
-
style: CSSProperties
|
|
15
|
-
onFinishAnnoCreate?: () => void
|
|
16
|
-
onMouseDown: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void
|
|
17
|
-
onMouseUp?: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void
|
|
18
|
-
onMouseMove: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void
|
|
19
|
-
onIsDraggingStateChanged: (bool) => void
|
|
20
|
-
}
|
|
7
|
+
coordinates: Point[]
|
|
8
|
+
isSelected: boolean
|
|
9
|
+
isDisabled?: boolean
|
|
10
|
+
annotationMode: AnnotationMode
|
|
11
|
+
annotationSettings: AnnotationSettings
|
|
12
|
+
pageToStageOffset: Point
|
|
13
|
+
svgScale: number
|
|
14
|
+
style: CSSProperties
|
|
15
|
+
onFinishAnnoCreate?: () => void
|
|
16
|
+
onMouseDown: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void
|
|
17
|
+
onMouseUp?: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void
|
|
18
|
+
onMouseMove: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void
|
|
19
|
+
onIsDraggingStateChanged: (bool) => void
|
|
20
|
+
}
|
|
21
21
|
|
|
22
22
|
const PolygonArea = ({
|
|
23
23
|
coordinates,
|
|
@@ -33,35 +33,35 @@ const PolygonArea = ({
|
|
|
33
33
|
// draw line between nodes
|
|
34
34
|
const svgLineCoords: string = coordinates
|
|
35
35
|
.map((point: Point) => `${point.x},${point.y}`)
|
|
36
|
-
.join(
|
|
36
|
+
.join(' ')
|
|
37
37
|
|
|
38
|
-
const [cursorStyle, setCursorStyle] = useState<string>(
|
|
38
|
+
const [cursorStyle, setCursorStyle] = useState<string>('pointer')
|
|
39
39
|
|
|
40
40
|
useEffect(() => {
|
|
41
|
-
if (isDisabled) return setCursorStyle(
|
|
42
|
-
if (isSelected) setCursorStyle(
|
|
43
|
-
else setCursorStyle(
|
|
44
|
-
}, [isSelected, isDisabled])
|
|
41
|
+
if (isDisabled) return setCursorStyle('not-allowed')
|
|
42
|
+
if (isSelected) setCursorStyle('grab')
|
|
43
|
+
else setCursorStyle('pointer')
|
|
44
|
+
}, [isSelected, isDisabled])
|
|
45
45
|
|
|
46
46
|
// adjust style for polyline
|
|
47
|
-
const polyLineStyle = { ...style }
|
|
48
|
-
polyLineStyle.cursor = cursorStyle
|
|
49
|
-
polyLineStyle.fillOpacity = isSelected ? 0 : 0.3
|
|
47
|
+
const polyLineStyle = { ...style }
|
|
48
|
+
polyLineStyle.cursor = cursorStyle
|
|
49
|
+
polyLineStyle.fillOpacity = isSelected ? 0 : 0.3
|
|
50
50
|
|
|
51
51
|
// dont show the polygon edges (the line does what as a stripe if enabled)
|
|
52
|
-
if (isSelected && isDisabled) polyLineStyle.stroke =
|
|
52
|
+
if (isSelected && isDisabled) polyLineStyle.stroke = 'none'
|
|
53
53
|
|
|
54
54
|
return (
|
|
55
55
|
<polygon
|
|
56
56
|
points={svgLineCoords}
|
|
57
57
|
style={polyLineStyle}
|
|
58
58
|
onMouseDown={(e) => {
|
|
59
|
-
if (isSelected) setCursorStyle(
|
|
60
|
-
onMouseDown(e)
|
|
59
|
+
if (isSelected) setCursorStyle('grabbing')
|
|
60
|
+
onMouseDown(e)
|
|
61
61
|
}}
|
|
62
62
|
onMouseUp={(e) => {
|
|
63
|
-
setCursorStyle(
|
|
64
|
-
onMouseUp(e)
|
|
63
|
+
setCursorStyle('grab')
|
|
64
|
+
onMouseUp(e)
|
|
65
65
|
}}
|
|
66
66
|
onDoubleClick={() =>
|
|
67
67
|
annotationMode === AnnotationMode.CREATE && onFinishAnnoCreate()
|
|
@@ -69,7 +69,7 @@ const PolygonArea = ({
|
|
|
69
69
|
onMouseMove={onMouseMove}
|
|
70
70
|
onContextMenu={(e) => e.preventDefault()}
|
|
71
71
|
/>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
74
|
|
|
75
|
-
export default PolygonArea
|
|
75
|
+
export default PolygonArea
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { CSSProperties } from
|
|
1
|
+
import { CSSProperties } from 'react'
|
|
2
2
|
|
|
3
3
|
// rename type to avoid naming conflict
|
|
4
|
-
import { AnnotationSettings, Point as TPoint } from
|
|
5
|
-
import Node from
|
|
4
|
+
import { AnnotationSettings, Point as TPoint } from '../../../types'
|
|
5
|
+
import Node from '../atoms/Node'
|
|
6
6
|
|
|
7
7
|
type PointProps = {
|
|
8
|
-
annotationSettings: AnnotationSettings
|
|
9
|
-
coordinates: TPoint
|
|
10
|
-
isSelected: boolean
|
|
11
|
-
pageToStageOffset: TPoint
|
|
12
|
-
svgScale: number
|
|
13
|
-
svgTranslation: TPoint
|
|
14
|
-
style: CSSProperties
|
|
15
|
-
onIsDraggingStateChanged: (bool) => void
|
|
16
|
-
onMoving: (coordinates: TPoint) => void
|
|
17
|
-
onMoved: (coordinates: TPoint[]) => void
|
|
18
|
-
}
|
|
8
|
+
annotationSettings: AnnotationSettings
|
|
9
|
+
coordinates: TPoint
|
|
10
|
+
isSelected: boolean
|
|
11
|
+
pageToStageOffset: TPoint
|
|
12
|
+
svgScale: number
|
|
13
|
+
svgTranslation: TPoint
|
|
14
|
+
style: CSSProperties
|
|
15
|
+
onIsDraggingStateChanged: (bool) => void
|
|
16
|
+
onMoving: (coordinates: TPoint) => void // during moving - update coordinates in parent
|
|
17
|
+
onMoved: (coordinates: TPoint[]) => void // moving finished - send annotation changed event
|
|
18
|
+
}
|
|
19
19
|
|
|
20
20
|
const Point = ({
|
|
21
21
|
annotationSettings,
|
|
@@ -46,7 +46,7 @@ const Point = ({
|
|
|
46
46
|
onMoved={() => onMoved([coordinates])}
|
|
47
47
|
onIsDraggingStateChanged={onIsDraggingStateChanged}
|
|
48
48
|
/>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
51
|
|
|
52
|
-
export default Point
|
|
52
|
+
export default Point
|
package/src/Canvas/Canvas.tsx
CHANGED
|
@@ -133,6 +133,9 @@ const Canvas = ({
|
|
|
133
133
|
const [labelInputPosition, setLabelInputPosition] = useState<Point>()
|
|
134
134
|
const [isLabelInputVisible, setIsLabelInputVisible] = useState<boolean>(false)
|
|
135
135
|
|
|
136
|
+
// svg element - used to trap focus
|
|
137
|
+
const svgRef = useRef(null)
|
|
138
|
+
|
|
136
139
|
// available canvas area - all possible space for creating a canvas
|
|
137
140
|
const canvasRef = useRef(null)
|
|
138
141
|
|
|
@@ -234,7 +237,6 @@ const Canvas = ({
|
|
|
234
237
|
}
|
|
235
238
|
}
|
|
236
239
|
|
|
237
|
-
// @TODO
|
|
238
240
|
const editSelectedAnnotation = () => {
|
|
239
241
|
if (
|
|
240
242
|
selectedAnnotation &&
|
|
@@ -242,8 +244,6 @@ const Canvas = ({
|
|
|
242
244
|
)
|
|
243
245
|
return
|
|
244
246
|
|
|
245
|
-
// selectedAnnotation!.mode = AnnotationMode.CREATE;
|
|
246
|
-
|
|
247
247
|
const newSelectedAnnotation: Annotation | undefined = annotations.find(
|
|
248
248
|
(annotation: Annotation) =>
|
|
249
249
|
annotation.internalId === selectedAnnotation?.internalId,
|
|
@@ -251,18 +251,20 @@ const Canvas = ({
|
|
|
251
251
|
|
|
252
252
|
if (newSelectedAnnotation === undefined) return
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
newSelectedAnnotation.status = AnnotationStatus.CREATING
|
|
256
|
-
newSelectedAnnotation.selectedNode = newSelectedAnnotation.coordinates.length - 1
|
|
257
|
-
setEditorMode(EditorModes.ADD)
|
|
254
|
+
setEditorMode(EditorModes.CREATE)
|
|
258
255
|
onSetSelectedTool(newSelectedAnnotation.type)
|
|
259
256
|
|
|
260
|
-
//
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
257
|
+
// recreate annotation with new internal id
|
|
258
|
+
const _newAnnotation: Annotation = {
|
|
259
|
+
...newSelectedAnnotation,
|
|
260
|
+
mode: AnnotationMode.CREATE,
|
|
261
|
+
status: AnnotationStatus.CREATING,
|
|
262
|
+
internalId: onRequestNewAnnoId(),
|
|
263
|
+
selectedNode: newSelectedAnnotation.coordinates.length - 1,
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// "copy" annotation by recreating it with a new internal it and deleting the old one
|
|
267
|
+
onAnnoEditing(_newAnnotation)
|
|
266
268
|
}
|
|
267
269
|
|
|
268
270
|
const traverseAnnos = () => {
|
|
@@ -473,6 +475,11 @@ const Canvas = ({
|
|
|
473
475
|
setIsLabelInputVisible(false)
|
|
474
476
|
}
|
|
475
477
|
|
|
478
|
+
useEffect(() => {
|
|
479
|
+
// set initial focus to make keybinds work
|
|
480
|
+
svgRef.current?.focus()
|
|
481
|
+
}, [])
|
|
482
|
+
|
|
476
483
|
// image changed after init -> reset everything
|
|
477
484
|
useEffect(() => {
|
|
478
485
|
if (canvasRef?.current !== undefined) {
|
|
@@ -832,6 +839,21 @@ const Canvas = ({
|
|
|
832
839
|
},
|
|
833
840
|
)
|
|
834
841
|
|
|
842
|
+
// move the selected annotation to the last spot
|
|
843
|
+
// this way, it's always on top of the others so that its nodes can be reached
|
|
844
|
+
if (selectedAnnotation) {
|
|
845
|
+
const _selectedAnnotation: Annotation = scaledAnnotations.find(
|
|
846
|
+
(a) => a.internalId === selectedAnnotation?.internalId,
|
|
847
|
+
)
|
|
848
|
+
|
|
849
|
+
// since annos was generated by iterating through scaledAnnotations, the index is the same
|
|
850
|
+
const selectedAnnotationIndex: number =
|
|
851
|
+
scaledAnnotations.indexOf(_selectedAnnotation)
|
|
852
|
+
|
|
853
|
+
// move the selected annotation to the last spot
|
|
854
|
+
annos.push(annos.splice(selectedAnnotationIndex, 1)[0])
|
|
855
|
+
}
|
|
856
|
+
|
|
835
857
|
return <g>{annos}</g>
|
|
836
858
|
}
|
|
837
859
|
|
|
@@ -941,6 +963,7 @@ const Canvas = ({
|
|
|
941
963
|
)}
|
|
942
964
|
|
|
943
965
|
<svg
|
|
966
|
+
ref={svgRef}
|
|
944
967
|
width="100%"
|
|
945
968
|
height="100%"
|
|
946
969
|
onKeyDown={onKeyDown}
|