lost-sia 2.0.1-alpha12 → 2.0.1-alpha13

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.
Files changed (146) hide show
  1. package/dist/Annotation/ui/atoms/AnnoBar.js +1 -1
  2. package/dist/Annotation/ui/atoms/Edge.js +1 -1
  3. package/dist/Annotation/ui/atoms/Node.js +1 -1
  4. package/dist/Annotation/ui/tools/BBox.js +1 -1
  5. package/dist/Annotation/ui/tools/Line.js +1 -1
  6. package/dist/Annotation/ui/tools/Polygon.js +1 -1
  7. package/dist/Canvas/Canvas.d.ts +2 -1
  8. package/dist/Canvas/Canvas.js +1 -1
  9. package/dist/Sia.js +1 -1
  10. package/dist/index.d.ts +0 -9
  11. package/dist/index.js +1 -1
  12. package/dist/models/KeyAction.d.ts +2 -1
  13. package/dist/models/KeyAction.js +1 -1
  14. package/dist/stories/Canvas/Canvas.stories.d.ts +2 -1
  15. package/dist/utils/KeyMapper.js +1 -1
  16. package/dist/utils/mouse.js +1 -1
  17. package/dist/utils/siaIcons.js +5 -5
  18. package/dist/utils/transform.js +1 -1
  19. package/dist/utils/windowViewport.d.ts +22 -0
  20. package/dist/utils/windowViewport.js +1 -1
  21. package/package.json +9 -8
  22. package/src/Annotation/ui/atoms/AnnoBar.tsx +46 -46
  23. package/src/Annotation/ui/atoms/Edge.tsx +23 -23
  24. package/src/Annotation/ui/atoms/Node.tsx +51 -51
  25. package/src/Annotation/ui/tools/BBox.tsx +129 -145
  26. package/src/Annotation/ui/tools/Line.tsx +81 -87
  27. package/src/Annotation/ui/tools/Polygon.tsx +92 -95
  28. package/src/Canvas/Canvas.tsx +369 -397
  29. package/src/InfoBoxes/AnnoStats.jsx +1 -1
  30. package/src/InfoBoxes/InfoBoxArea.jsx +32 -34
  31. package/src/{Sia2.tsx → Sia.tsx} +156 -175
  32. package/src/index.ts +6 -17
  33. package/src/models/KeyAction.ts +1 -0
  34. package/src/stories/AnnotationTools.stories.tsx +21 -21
  35. package/src/stories/Canvas/Canvas.stories.tsx +31 -26
  36. package/src/stories/Canvas/CanvasOffset.tsx +23 -25
  37. package/src/stories/MinimalSia.stories.tsx +22 -22
  38. package/src/stories/SIA2/DemoWrapper.tsx +20 -24
  39. package/src/stories/SIA2/Sia2.stories.tsx +24 -24
  40. package/src/utils/KeyMapper.ts +3 -0
  41. package/src/utils/siaIcons.jsx +5 -7
  42. package/src/utils/windowViewport.ts +34 -0
  43. package/dist/AnnoExampleViewer.js +0 -1
  44. package/dist/AnnoLabelInput.js +0 -1
  45. package/dist/AnnoToolBar.js +0 -19
  46. package/dist/Annotation/AnnoBar.js +0 -1
  47. package/dist/Annotation/Annotation.js +0 -1
  48. package/dist/Annotation/BBox.js +0 -1
  49. package/dist/Annotation/Edge.js +0 -1
  50. package/dist/Annotation/InfSelectionArea.js +0 -1
  51. package/dist/Annotation/Line.js +0 -1
  52. package/dist/Annotation/Node.js +0 -1
  53. package/dist/Annotation/Point.js +0 -1
  54. package/dist/Annotation/Polygon.js +0 -1
  55. package/dist/Canvas.js +0 -1
  56. package/dist/ImgBar.js +0 -1
  57. package/dist/InfoBoxes/AnnoDetails.js +0 -1
  58. package/dist/InfoBoxes/AnnoStats.js +0 -1
  59. package/dist/InfoBoxes/InfoBox.js +0 -1
  60. package/dist/InfoBoxes/InfoBoxArea.js +0 -1
  61. package/dist/InfoBoxes/LabelInfo.js +0 -1
  62. package/dist/LabelInput.js +0 -1
  63. package/dist/Prompt.js +0 -1
  64. package/dist/SIAFilterButton.js +0 -1
  65. package/dist/SIASettingButton.js +0 -1
  66. package/dist/Sia2.js +0 -1
  67. package/dist/SiaPopup.js +0 -1
  68. package/dist/ToolBar.js +0 -1
  69. package/dist/ToolbarItem.js +0 -1
  70. package/dist/_virtual/_commonjsHelpers.js +0 -1
  71. package/dist/_virtual/lodash.js +0 -1
  72. package/dist/assets/Annotation/Annotation-Cd5Ua5TG.css +0 -1
  73. package/dist/assets/Toolbar-Cp1xyYeH.css +0 -1
  74. package/dist/filterTools.js +0 -1
  75. package/dist/siaDummyData.js +0 -7
  76. package/dist/stories/Canvas/CanvasOffset.js +0 -1
  77. package/dist/stories/FilterDropdown.stories.js +0 -1
  78. package/dist/stories/SIA/SIA.stories.d.ts +0 -36
  79. package/dist/stories/SIA2/DemoWrapper.js +0 -1
  80. package/dist/stories/Toolbar/ImageTools/ImageLabel.stories.d.ts +0 -13
  81. package/dist/stories/siaDummyData.js +0 -1
  82. package/dist/stories/siaDummyData2.js +0 -7
  83. package/dist/types/annoStatus.js +0 -1
  84. package/dist/types/canvasActions.js +0 -1
  85. package/dist/types/cursorstyles.js +0 -1
  86. package/dist/types/modes.js +0 -1
  87. package/dist/types/notificationType.js +0 -1
  88. package/dist/types/toolbarEvents.js +0 -1
  89. package/dist/types/tools.js +0 -1
  90. package/dist/utils/annoConversion.js +0 -1
  91. package/dist/utils/annoConversion2.d.ts +0 -27
  92. package/dist/utils/annoConversion2.js +0 -1
  93. package/dist/utils/colorlut.js +0 -1
  94. package/dist/utils/constraints.js +0 -1
  95. package/dist/utils/hist.js +0 -1
  96. package/dist/utils/keyActions.js +0 -1
  97. package/dist/utils/mouse2.js +0 -1
  98. package/dist/utils/transform2.js +0 -1
  99. package/src/AnnoLabelInput.jsx +0 -109
  100. package/src/AnnoToolBar.jsx +0 -153
  101. package/src/Annotation/AnnoBar.jsx +0 -154
  102. package/src/Annotation/Annotation.jsx +0 -395
  103. package/src/Annotation/Annotation.scss +0 -47
  104. package/src/Annotation/BBox.jsx +0 -299
  105. package/src/Annotation/Edge.jsx +0 -92
  106. package/src/Annotation/InfSelectionArea.jsx +0 -72
  107. package/src/Annotation/Line.jsx +0 -68
  108. package/src/Annotation/Node.jsx +0 -282
  109. package/src/Annotation/Point.jsx +0 -200
  110. package/src/Annotation/Polygon.jsx +0 -404
  111. package/src/Canvas.jsx +0 -2194
  112. package/src/ImgBar.jsx +0 -131
  113. package/src/LabelInput.jsx +0 -238
  114. package/src/Prompt.jsx +0 -45
  115. package/src/SIAFilterButton.jsx +0 -186
  116. package/src/Sia.jsx +0 -478
  117. package/src/SiaPopup.jsx +0 -15
  118. package/src/ToolBar.jsx +0 -463
  119. package/src/Toolbar/ToolbarItem.jsx +0 -30
  120. package/src/Toolbar.css +0 -13
  121. package/src/ToolbarItem.jsx +0 -31
  122. package/src/filterTools.js +0 -5
  123. package/src/stories/SIA/SIA.stories.tsx +0 -64
  124. package/src/stories/Toolbar/ImageTools/ImageLabel.stories.tsx +0 -31
  125. package/src/stories/lost.js +0 -54
  126. package/src/stories/store.js +0 -18
  127. package/src/test.js +0 -7
  128. package/src/types/annoStatus.js +0 -4
  129. package/src/types/canvasActions.js +0 -58
  130. package/src/types/cursorstyles.js +0 -3
  131. package/src/types/modes.js +0 -9
  132. package/src/types/notificationType.js +0 -11
  133. package/src/types/toolbarEvents.js +0 -35
  134. package/src/types/tools.js +0 -17
  135. package/src/utils/annoConversion.js +0 -145
  136. package/src/utils/annoConversion2.ts +0 -145
  137. package/src/utils/colorlut.js +0 -68
  138. package/src/utils/constraints.js +0 -81
  139. package/src/utils/mouse.js +0 -14
  140. package/src/utils/transform.js +0 -336
  141. package/src/utils/windowViewport.js +0 -34
  142. /package/dist/{Sia2.d.ts → Sia.d.ts} +0 -0
  143. /package/dist/utils/{mouse2.d.ts → mouse.d.ts} +0 -0
  144. /package/dist/utils/{transform2.d.ts → transform.d.ts} +0 -0
  145. /package/src/utils/{mouse2.ts → mouse.ts} +0 -0
  146. /package/src/utils/{transform2.ts → transform.ts} +0 -0
@@ -5,64 +5,63 @@ import React, {
5
5
  useRef,
6
6
  useState,
7
7
  WheelEvent,
8
- } from "react";
9
- import AnnotationTool from "../models/AnnotationTool";
10
- import EditorModes from "../models/EditorModes";
11
- import KeyMapper from "../utils/KeyMapper";
12
- import KeyAction from "../models/KeyAction";
13
- import Annotation from "../Annotation/logic/Annotation";
14
- import CanvasAction from "../models/CanvasAction";
15
- import AnnotationComponent from "../Annotation/ui/AnnotationComponent";
16
- import { AnnotationSettings, Label, UiConfig } from "../types";
8
+ } from 'react'
9
+ import AnnotationTool from '../models/AnnotationTool'
10
+ import EditorModes from '../models/EditorModes'
11
+ import KeyMapper from '../utils/KeyMapper'
12
+ import KeyAction from '../models/KeyAction'
13
+ import Annotation from '../Annotation/logic/Annotation'
14
+ import CanvasAction from '../models/CanvasAction'
15
+ import AnnotationComponent from '../Annotation/ui/AnnotationComponent'
16
+ import { AnnotationSettings, Label, UiConfig } from '../types'
17
17
  import {
18
18
  Point,
19
19
  PolygonOperationResult,
20
20
  SIANotification,
21
21
  ToolCoordinates,
22
22
  Vector2,
23
- } from "../types";
24
- import mouse2 from "../utils/mouse2";
25
- import AnnotationMode from "../models/AnnotationMode";
26
- import LabelInput from "./LabelInput";
27
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
28
- import { faBan } from "@fortawesome/free-solid-svg-icons";
29
- import { IconProp } from "@fortawesome/fontawesome-svg-core";
30
- import AnnotationStatus from "../models/AnnotationStatus";
31
- import transform2 from "../utils/transform2";
32
- import NotificationType from "../models/NotificationType";
33
- import TimeUtils from "../utils/TimeUtils";
23
+ } from '../types'
24
+ import mouse from '../utils/mouse'
25
+ import AnnotationMode from '../models/AnnotationMode'
26
+ import LabelInput from './LabelInput'
27
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
28
+ import { faBan } from '@fortawesome/free-solid-svg-icons'
29
+ import { IconProp } from '@fortawesome/fontawesome-svg-core'
30
+ import AnnotationStatus from '../models/AnnotationStatus'
31
+ import transform from '../utils/transform'
32
+ import NotificationType from '../models/NotificationType'
33
+ import TimeUtils from '../utils/TimeUtils'
34
+ import windowViewport from '../utils/windowViewport'
34
35
 
35
36
  type CanvasProps = {
36
- annotations?: Annotation[];
37
- annotationSettings: AnnotationSettings;
38
- defaultLabelId?: number;
39
- image: string;
40
- isFullscreen?: boolean;
41
- isImageJunk?: boolean;
42
- isPolygonSelectionMode?: boolean;
43
- selectedAnnotation: Annotation | undefined;
44
- selectedAnnoTool: AnnotationTool;
45
- toolbarHeight?: number;
46
- polygonOperationResult?: PolygonOperationResult;
47
- possibleLabels: Label[];
48
- preventScrolling?: boolean;
49
- uiConfig: UiConfig;
37
+ annotations?: Annotation[]
38
+ annotationSettings: AnnotationSettings
39
+ defaultLabelId?: number
40
+ image: string
41
+ isFullscreen?: boolean
42
+ isImageJunk?: boolean
43
+ isPolygonSelectionMode?: boolean
44
+ selectedAnnotation: Annotation | undefined
45
+ selectedAnnoTool: AnnotationTool
46
+ toolbarHeight?: number
47
+ polygonOperationResult?: PolygonOperationResult
48
+ possibleLabels: Label[]
49
+ preventScrolling?: boolean
50
+ uiConfig: UiConfig
50
51
  // onKeyDown?: (e) => void | undefined;
51
52
  // onKeyUp?: (e) => void | undefined;
52
- onAnnoCreated: (createdAnno: Annotation) => void;
53
- onAnnoCreationFinished: (
54
- createdAnno: Annotation,
55
- hasAnnotationExisted: boolean,
56
- ) => void;
57
- onAnnoChanged: (changedAnno: Annotation) => void;
58
- onAnnoEditing: (annoToEdit: Annotation) => void;
53
+ onAnnoCreated: (createdAnno: Annotation) => void
54
+ onAnnoCreationFinished: (createdAnno: Annotation, hasAnnotationExisted: boolean) => void
55
+ onAnnoChanged: (changedAnno: Annotation) => void
56
+ onAnnoEditing: (annoToEdit: Annotation) => void
59
57
  // onAnnoDeleted: (deletedAnno: Annotation, allAnnos: Annotation[]) => void;
60
- onNotification?: (notification: SIANotification) => void;
61
- onRequestNewAnnoId: () => number;
62
- onSelectAnnotation: (annotation?: Annotation) => void;
63
- onSetSelectedTool: (tool: AnnotationTool) => void;
64
- onShouldDeleteAnno: (internalAnnoId: number) => void;
65
- };
58
+ onNotification?: (notification: SIANotification) => void
59
+ onRequestNewAnnoId: () => number
60
+ onSelectAnnotation: (annotation?: Annotation) => void
61
+ onSetIsImageJunk: (newJunkState: boolean) => void
62
+ onSetSelectedTool: (tool: AnnotationTool) => void
63
+ onShouldDeleteAnno: (internalAnnoId: number) => void
64
+ }
66
65
 
67
66
  const Canvas = ({
68
67
  annotations = [],
@@ -88,148 +87,137 @@ const Canvas = ({
88
87
  onNotification = (_) => {},
89
88
  onRequestNewAnnoId,
90
89
  onSelectAnnotation,
90
+ onSetIsImageJunk,
91
91
  onSetSelectedTool = (_) => {},
92
92
  onShouldDeleteAnno,
93
93
  }: CanvasProps) => {
94
- const [editorMode, setEditorMode] = useState<EditorModes>(EditorModes.VIEW);
95
- const [annoTimestamp, setAnnoTimestamp] = useState<number | undefined>();
94
+ const [editorMode, setEditorMode] = useState<EditorModes>(EditorModes.VIEW)
95
+ const [annoTimestamp, setAnnoTimestamp] = useState<number | undefined>()
96
96
 
97
97
  // remember which label was added last
98
- const [currentLabelId, setCurrentLabelId] = useState<number | undefined>(
99
- defaultLabelId,
100
- );
98
+ const [currentLabelId, setCurrentLabelId] = useState<number | undefined>(defaultLabelId)
101
99
 
102
100
  // vector from the top left of the DOM document to the top left of the stage
103
101
  // (events are emitting page coordinates, so we need this to convert them)
104
102
  const [pageToCanvasOffset, setPageToCanvasOffset] = useState<Point>({
105
103
  x: -1,
106
104
  y: -1,
107
- });
105
+ })
108
106
 
109
107
  // space to add to the x translation to center the image
110
- const [imageCenteringSpace, setImageCenteringSpace] = useState<number>(0);
108
+ const [imageCenteringSpace, setImageCenteringSpace] = useState<number>(0)
111
109
 
112
110
  // the stage can be horizontally centered - apply offset on x axis
113
111
  const pageToStageOffset: Vector2 = {
114
112
  // x: pageToCanvasOffset.x + imageCenteringSpace,
115
113
  x: pageToCanvasOffset.x,
116
114
  y: pageToCanvasOffset.y,
117
- };
115
+ }
118
116
 
119
117
  // default (unscaled) image and canvas sizes (for stage calculation)
120
118
  // invalid default value, so that the image uses its default values at first
121
- const [imgSize, setImgSize] = useState<Vector2>({ x: -1, y: -1 });
122
- const [canvasSize, setCanvasSize] = useState<Vector2>({ x: -1, y: -1 });
119
+ const [imgSize, setImgSize] = useState<Vector2>({ x: -1, y: -1 })
120
+ const [canvasSize, setCanvasSize] = useState<Vector2>({ x: -1, y: -1 })
123
121
 
124
122
  // largest possible annotation size fitting the whole image
125
- const [stageSize, setStageSize] = useState<Vector2>({ x: -1, y: -1 });
123
+ const [stageSize, setStageSize] = useState<Vector2>({ x: -1, y: -1 })
126
124
 
127
- const [svgScale, setSvgScale] = useState<number>(1.0);
128
- const [svgTranslation, setSvgTranslation] = useState<Vector2>({ x: 0, y: 0 });
125
+ const [svgScale, setSvgScale] = useState<number>(1.0)
126
+ const [svgTranslation, setSvgTranslation] = useState<Vector2>({ x: 0, y: 0 })
129
127
  const centeredSvgTranslation: Vector2 = {
130
128
  x: svgTranslation.x + imageCenteringSpace,
131
129
  y: svgTranslation.y,
132
- };
130
+ }
133
131
 
134
132
  // label input will be opened if a position is set here
135
- const [labelInputPosition, setLabelInputPosition] = useState<Point>();
136
- const [isLabelInputVisible, setIsLabelInputVisible] =
137
- useState<boolean>(false);
133
+ const [labelInputPosition, setLabelInputPosition] = useState<Point>()
134
+ const [isLabelInputVisible, setIsLabelInputVisible] = useState<boolean>(false)
138
135
 
139
136
  // available canvas area - all possible space for creating a canvas
140
- const canvasRef = useRef(null);
137
+ const canvasRef = useRef(null)
141
138
 
142
139
  // used to get the size of the dynamically loaded image
143
- const imageRef = useRef(null);
140
+ const imageRef = useRef(null)
144
141
 
145
142
  // returns the aspect ratio of the largest image size fitting onto the canvas without changing the aspect ratio
146
143
  const getFittedImageScale = (imgSize: Vector2, svgSize: Vector2): number => {
147
- if (
148
- imgSize.x === 0 ||
149
- imgSize.y === 0 ||
150
- svgSize.x === 0 ||
151
- svgSize.y === 0
152
- )
153
- return 0;
144
+ if (imgSize.x === 0 || imgSize.y === 0 || svgSize.x === 0 || svgSize.y === 0) return 0
154
145
 
155
- const scaleX = svgSize.x / imgSize.x;
156
- const scaleY = svgSize.y / imgSize.y;
146
+ const scaleX = svgSize.x / imgSize.x
147
+ const scaleY = svgSize.y / imgSize.y
157
148
 
158
- return Math.min(scaleX, scaleY);
159
- };
149
+ return Math.min(scaleX, scaleY)
150
+ }
160
151
 
161
152
  // factor to convert coordinates from an (untransformed) image into the stage
162
- const imageToStageFactor = getFittedImageScale(imgSize, canvasSize);
153
+ const imageToStageFactor = getFittedImageScale(imgSize, canvasSize)
163
154
 
164
155
  // store + update the vector between start of the page and start of the (translated) image to be able to do transformations
165
156
  const calculatePageToCanvasOffset = () => {
166
- if (imageRef?.current === null) return { x: 0, y: 0 };
157
+ if (imageRef?.current === null) return { x: 0, y: 0 }
167
158
 
168
159
  // if image can and should be centered
169
- const resizedImageWidth: number = imgSize.x * imageToStageFactor;
160
+ const resizedImageWidth: number = imgSize.x * imageToStageFactor
170
161
  if (uiConfig.imageCentered && canvasSize.x > resizedImageWidth) {
171
162
  // image is at (0,0) - get the blanc space at the right side of the image
172
- const remainingSpace: number = canvasSize.x - resizedImageWidth;
163
+ const remainingSpace: number = canvasSize.x - resizedImageWidth
173
164
 
174
165
  // divide remaining space to be equal to the left and right side
175
- const spaceToLeft: number = remainingSpace / 2;
166
+ const spaceToLeft: number = remainingSpace / 2
176
167
 
177
168
  // page to stage offset marks the top left point of the stage
178
169
  // add the space to the translation to center the image
179
- setImageCenteringSpace(spaceToLeft);
170
+ setImageCenteringSpace(spaceToLeft)
180
171
  } else {
181
- setImageCenteringSpace(0);
172
+ setImageCenteringSpace(0)
182
173
  }
183
174
 
184
175
  // get image starting position in window coordinates
185
- const { top, left } = canvasRef.current.getBoundingClientRect();
176
+ const { top, left } = canvasRef.current.getBoundingClientRect()
186
177
 
187
178
  // top and left are in window coordinates
188
179
  // we need to convert them to page coordinates
189
180
  const pageOffset: Point = {
190
181
  x: left + window.scrollX,
191
182
  y: top + window.scrollY,
192
- };
183
+ }
193
184
 
194
- setPageToCanvasOffset(pageOffset);
195
- };
185
+ setPageToCanvasOffset(pageOffset)
186
+ }
196
187
 
197
188
  // const pageToStageOffset = calculatePageToStageOffset();
198
189
 
199
- const keyMapper = new KeyMapper((keyAction: KeyAction) =>
200
- handleKeyAction(keyAction),
201
- );
190
+ const keyMapper = new KeyMapper((keyAction: KeyAction) => handleKeyAction(keyAction))
202
191
 
203
192
  const createNewAnnotation = (antiScaledMouseStagePosition: Point) => {
204
193
  // switch to editing mode
205
- setEditorMode(EditorModes.CREATE);
194
+ setEditorMode(EditorModes.CREATE)
206
195
 
207
196
  // we get the mouse position in the stage coordinate system
208
197
  // to use it as annotation coordinates, convert it into a list in the percentage coordinates
209
- const percentagedInitialCoords =
210
- transform2.convertStageCoordinatesToPercentaged(
211
- [antiScaledMouseStagePosition],
212
- imageToStageFactor,
213
- imgSize,
214
- );
198
+ const percentagedInitialCoords = transform.convertStageCoordinatesToPercentaged(
199
+ [antiScaledMouseStagePosition],
200
+ imageToStageFactor,
201
+ imgSize,
202
+ )
215
203
 
216
204
  // bbox always require 2 points - add first coordinate again
217
205
  if (selectedAnnoTool === AnnotationTool.BBox)
218
- percentagedInitialCoords.push(percentagedInitialCoords[0]);
206
+ percentagedInitialCoords.push(percentagedInitialCoords[0])
219
207
 
220
- const newAnnotationInternalId: number = onRequestNewAnnoId();
208
+ const newAnnotationInternalId: number = onRequestNewAnnoId()
221
209
  const newAnnotation = new Annotation(
222
210
  newAnnotationInternalId,
223
211
  selectedAnnoTool,
224
212
  percentagedInitialCoords,
225
- );
213
+ )
226
214
 
227
- setAnnoTimestamp(performance.now());
215
+ setAnnoTimestamp(performance.now())
228
216
 
229
217
  // automatically select the last used label
230
- if (currentLabelId !== undefined) newAnnotation.labelIds = [currentLabelId];
218
+ if (currentLabelId !== undefined) newAnnotation.labelIds = [currentLabelId]
231
219
 
232
- onAnnoCreated(newAnnotation);
220
+ onAnnoCreated(newAnnotation)
233
221
 
234
222
  // points are created in only one frame
235
223
  // (no size / shape has to be defined)
@@ -241,468 +229,474 @@ const Canvas = ({
241
229
  ...newAnnotation,
242
230
  coordinates: [antiScaledMouseStagePosition],
243
231
  annoTime: 0, // its literally one frame
244
- };
245
- onFinishCreateAnno(newPointAnnotation);
232
+ }
233
+ onFinishCreateAnno(newPointAnnotation)
246
234
  }
247
- };
235
+ }
248
236
 
249
237
  // @TODO
250
238
  const editSelectedAnnotation = () => {
251
239
  if (
252
240
  selectedAnnotation &&
253
- ![AnnotationTool.Line, AnnotationTool.Polygon].includes(
254
- selectedAnnotation!.type,
255
- )
241
+ ![AnnotationTool.Line, AnnotationTool.Polygon].includes(selectedAnnotation!.type)
256
242
  )
257
- return;
243
+ return
258
244
 
259
245
  // selectedAnnotation!.mode = AnnotationMode.CREATE;
260
246
 
261
247
  const newSelectedAnnotation: Annotation | undefined = annotations.find(
262
248
  (annotation: Annotation) =>
263
249
  annotation.internalId === selectedAnnotation?.internalId,
264
- );
250
+ )
265
251
 
266
- if (newSelectedAnnotation === undefined) return;
252
+ if (newSelectedAnnotation === undefined) return
267
253
 
268
- newSelectedAnnotation.mode = AnnotationMode.CREATE;
269
- newSelectedAnnotation.status = AnnotationStatus.CREATING;
270
- newSelectedAnnotation.selectedNode =
271
- newSelectedAnnotation.coordinates.length - 1;
272
- setEditorMode(EditorModes.ADD);
273
- onSetSelectedTool(newSelectedAnnotation.type);
254
+ newSelectedAnnotation.mode = AnnotationMode.CREATE
255
+ newSelectedAnnotation.status = AnnotationStatus.CREATING
256
+ newSelectedAnnotation.selectedNode = newSelectedAnnotation.coordinates.length - 1
257
+ setEditorMode(EditorModes.ADD)
258
+ onSetSelectedTool(newSelectedAnnotation.type)
274
259
 
275
260
  // replace annotation in list
276
261
  // const selectedAnnotationId: number = annotations.findIndex((annotation: Annotation) => annotation.internalId === selectedAnnotation?.internalId)
277
262
  // const newAnnotations = [...annotations]
278
263
  // newAnnotations[selectedAnnotationId] = newSelectedAnnotation
279
- onAnnoEditing(newSelectedAnnotation);
264
+ onAnnoEditing(newSelectedAnnotation)
280
265
  // onShouldDeleteAnno(selectedAnnotation!.internalId);
281
- };
266
+ }
282
267
 
283
268
  const traverseAnnos = () => {
284
- const currentId = selectedAnnotation ? selectedAnnotation.internalId : 0;
269
+ const currentId = selectedAnnotation ? selectedAnnotation.internalId : 0
285
270
 
286
271
  // get the first annotation with an higher internal id
287
272
  const nextAnno: Annotation | undefined = annotations.find(
288
273
  (annotation: Annotation) => annotation.internalId > currentId,
289
- );
274
+ )
290
275
 
291
- if (nextAnno) return onSelectAnnotation(nextAnno);
276
+ if (nextAnno) return onSelectAnnotation(nextAnno)
292
277
 
293
278
  // fallback: there was no anno with an higher id -> just use first anno
294
- if (annotations.length > 0) return onSelectAnnotation(annotations[0]);
295
- };
279
+ if (annotations.length > 0) return onSelectAnnotation(annotations[0])
280
+ }
296
281
 
297
282
  const traverseAnnosBackwards = () => {
298
- const currentId = selectedAnnotation ? selectedAnnotation.internalId : 0;
283
+ const currentId = selectedAnnotation ? selectedAnnotation.internalId : 0
299
284
 
300
285
  // copy list (dont mutate original annotations)
301
- const newAnnotations: Annotation[] = [...annotations];
286
+ const newAnnotations: Annotation[] = [...annotations]
302
287
 
303
288
  // sort by internalId descending
304
- newAnnotations.sort((a, b) => b.internalId - a.internalId);
289
+ newAnnotations.sort((a, b) => b.internalId - a.internalId)
305
290
 
306
291
  // get the first annotation with an lower internal id than current
307
292
  const nextAnno: Annotation | undefined = newAnnotations.find(
308
293
  (annotation: Annotation) => annotation.internalId < currentId,
309
- );
294
+ )
310
295
 
311
- if (nextAnno) return onSelectAnnotation(nextAnno);
296
+ if (nextAnno) return onSelectAnnotation(nextAnno)
312
297
 
313
298
  // fallback: there was no anno with an higher id -> just use first anno
314
299
  if (annotations.length > 0)
315
- return onSelectAnnotation(annotations[annotations.length - 1]);
316
- };
300
+ return onSelectAnnotation(annotations[annotations.length - 1])
301
+ }
317
302
 
318
303
  const copyCurrentAnnotation = () => {
319
304
  // set a copy of the annotation object to the clipboard in localstorage
320
305
  // if (selectedAnnotation) setAnnotationClipboard({ ...selectedAnnotation });
321
306
  if (selectedAnnotation) {
322
- const serializedAnnotation: string = JSON.stringify(selectedAnnotation);
323
- localStorage.setItem("lostAnnotationClipboard", serializedAnnotation);
307
+ const serializedAnnotation: string = JSON.stringify(selectedAnnotation)
308
+ localStorage.setItem('lostAnnotationClipboard', serializedAnnotation)
324
309
 
325
310
  const copySuccessNotification: SIANotification = {
326
- title: "Success",
327
- message: "Annotation copied",
311
+ title: 'Success',
312
+ message: 'Annotation copied',
328
313
  type: NotificationType.SUCCESS,
329
- };
314
+ }
330
315
 
331
- onNotification(copySuccessNotification);
316
+ onNotification(copySuccessNotification)
332
317
  }
333
- };
318
+ }
334
319
 
335
320
  const pasteAnnotation = () => {
336
321
  const clipboardAnnotation: string | null = localStorage.getItem(
337
- "lostAnnotationClipboard",
338
- );
339
- if (clipboardAnnotation == undefined) return;
322
+ 'lostAnnotationClipboard',
323
+ )
324
+ if (clipboardAnnotation == undefined) return
340
325
 
341
- const annotationToPaste: Annotation = JSON.parse(clipboardAnnotation);
326
+ const annotationToPaste: Annotation = JSON.parse(clipboardAnnotation)
342
327
 
343
328
  // get a new temporary id
344
- annotationToPaste.internalId = onRequestNewAnnoId();
345
- annotationToPaste.externalId = "";
329
+ annotationToPaste.internalId = onRequestNewAnnoId()
330
+ annotationToPaste.externalId = ''
346
331
 
347
- onAnnoCreationFinished(annotationToPaste, true);
332
+ onAnnoCreationFinished(annotationToPaste, true)
348
333
 
349
- onSelectAnnotation(annotationToPaste);
350
- };
334
+ onSelectAnnotation(annotationToPaste)
335
+ }
351
336
 
352
337
  const handleKeyAction = (keyAction: KeyAction) => {
353
338
  switch (keyAction) {
354
339
  case KeyAction.EDIT_LABEL:
355
- if (selectedAnnotation) setIsLabelInputVisible(true);
356
- break;
340
+ if (selectedAnnotation) setIsLabelInputVisible(true)
341
+ break
357
342
  case KeyAction.DELETE_ANNO:
358
- if (selectedAnnotation)
359
- onShouldDeleteAnno(selectedAnnotation.internalId);
360
- break;
343
+ if (selectedAnnotation) onShouldDeleteAnno(selectedAnnotation.internalId)
344
+ break
361
345
  // case KeyAction.TOGGLE_ANNO_COMMENT_INPUT:
362
346
  // console.log("KeyAction TODO: TOGGLE_ANNO_COMMENT_INPUT");
363
347
  // break;
364
348
  case KeyAction.DELETE_ANNO_IN_CREATION:
365
349
  // remove an unfinished annotation
366
350
  if (editorMode === EditorModes.CREATE) {
367
- onShouldDeleteAnno(selectedAnnotation!.internalId);
368
- setEditorMode(EditorModes.VIEW);
351
+ onShouldDeleteAnno(selectedAnnotation!.internalId)
352
+ setEditorMode(EditorModes.VIEW)
369
353
  }
370
- break;
354
+ break
371
355
  case KeyAction.ENTER_ANNO_ADD_MODE:
372
- console.log("KeyAction TODO: ENTER_ANNO_ADD_MODE");
373
- break;
356
+ console.log('KeyAction TODO: ENTER_ANNO_ADD_MODE')
357
+ break
374
358
  case KeyAction.LEAVE_ANNO_ADD_MODE:
375
- console.log("KeyAction TODO: LEAVE_ANNO_ADD_MODE");
376
- break;
359
+ console.log('KeyAction TODO: LEAVE_ANNO_ADD_MODE')
360
+ break
377
361
  case KeyAction.UNDO:
378
- console.log("KeyAction TODO: UNDO");
379
- break;
362
+ console.log('KeyAction TODO: UNDO')
363
+ break
380
364
  case KeyAction.REDO:
381
- console.log("KeyAction TODO: REDO");
382
- break;
365
+ console.log('KeyAction TODO: REDO')
366
+ break
383
367
  case KeyAction.TRAVERSE_ANNOS:
384
- traverseAnnos();
385
- break;
368
+ traverseAnnos()
369
+ break
386
370
  case KeyAction.TRAVERSE_ANNOS_BACKWARDS:
387
- traverseAnnosBackwards();
388
- break;
371
+ traverseAnnosBackwards()
372
+ break
389
373
  case KeyAction.CAM_MOVE_LEFT:
390
- moveCamera(20 * svgScale, 0);
391
- break;
374
+ moveCamera(20 * svgScale, 0)
375
+ break
392
376
  case KeyAction.CAM_MOVE_RIGHT:
393
- moveCamera(-20 * svgScale, 0);
394
- break;
377
+ moveCamera(-20 * svgScale, 0)
378
+ break
395
379
  case KeyAction.CAM_MOVE_UP:
396
- moveCamera(0, 20 * svgScale);
397
- break;
380
+ moveCamera(0, 20 * svgScale)
381
+ break
398
382
  case KeyAction.CAM_MOVE_DOWN:
399
- moveCamera(0, -20 * svgScale);
400
- break;
383
+ moveCamera(0, -20 * svgScale)
384
+ break
401
385
  case KeyAction.CAM_MOVE_STOP:
402
- console.log("KeyAction TODO: CAM_MOVE_STOP");
403
- break;
386
+ console.log('KeyAction TODO: CAM_MOVE_STOP')
387
+ break
404
388
  case KeyAction.COPY_ANNOTATION:
405
- copyCurrentAnnotation();
406
- break;
389
+ copyCurrentAnnotation()
390
+ break
407
391
  case KeyAction.PASTE_ANNOTATION:
408
- pasteAnnotation();
409
- break;
392
+ pasteAnnotation()
393
+ break
410
394
  case KeyAction.RECREATE_ANNO:
411
- console.log("KeyAction TODO: RECREATE_ANNO");
412
- editSelectedAnnotation();
413
- break;
395
+ console.log('KeyAction TODO: RECREATE_ANNO')
396
+ editSelectedAnnotation()
397
+ break
398
+ case KeyAction.TOGGLE_IMAGE_JUNK:
399
+ // dont junk image while creating annotation
400
+ if (editorMode === EditorModes.ADD || editorMode === EditorModes.CREATE) return
401
+ onSetIsImageJunk(!isImageJunk)
402
+ break
414
403
  default:
415
- console.log("Unknown KeyAction", keyAction);
416
- break;
404
+ console.log('Unknown KeyAction', keyAction)
405
+ break
417
406
  }
418
- };
407
+ }
419
408
 
420
409
  const moveCamera = (movementX: number, movementY: number) => {
421
- let newTransX = svgTranslation.x + movementX / svgScale;
422
- let newTransY = svgTranslation.y + movementY / svgScale;
410
+ let newTransX = svgTranslation.x + movementX / svgScale
411
+ let newTransY = svgTranslation.y + movementY / svgScale
423
412
 
424
- // at least one quarter of the image should always be visible
425
- const minTransX = canvasSize.x * -0.25;
426
- const minTransY = canvasSize.y * -0.25;
427
- const maxTransX = canvasSize.x * 0.75;
428
- const maxTransY = canvasSize.y * 0.75;
413
+ const vXMin = canvasSize.x * 0.45
414
+ const vXMax = canvasSize.x * 0.55
415
+ const yXMin = canvasSize.y * 0.45
416
+ const yXMax = canvasSize.y * 0.55
429
417
 
430
- // move image a bit back inside the canvas
431
- if (newTransX < minTransX) newTransX += 25;
432
- if (newTransX > maxTransX) newTransX -= 25;
433
- if (newTransY < minTransY) newTransY += 25;
434
- if (newTransY > maxTransY) newTransY -= 25;
418
+ const topLeftPoint: Point = { x: 0, y: 0 }
419
+ const vLeft = windowViewport.getViewportCoordinates(
420
+ svgTranslation,
421
+ canvasSize,
422
+ svgScale,
423
+ topLeftPoint,
424
+ )
425
+ const vRight = windowViewport.getViewportCoordinates(
426
+ svgTranslation,
427
+ canvasSize,
428
+ svgScale,
429
+ canvasSize,
430
+ )
431
+
432
+ if (vLeft.vX >= vXMin) newTransX = svgTranslation.x - 5
433
+ else if (vRight.vX <= vXMax) newTransX = svgTranslation.x + 5
434
+ else if (vLeft.vY >= yXMin) newTransY = svgTranslation.y - 5
435
+ else if (vRight.vY <= yXMax) newTransY = svgTranslation.y + 5
435
436
 
436
- setSvgTranslation({ x: newTransX, y: newTransY });
437
- };
437
+ setSvgTranslation({ x: newTransX, y: newTransY })
438
+ }
438
439
 
439
440
  const calculateScaledAnnotations = (_annotations: Annotation[]) => {
440
- if (
441
- stageSize.x <= 0 ||
442
- stageSize.y <= 0 ||
443
- imgSize.x <= 0 ||
444
- imgSize.y <= 0
445
- )
446
- return [];
441
+ if (stageSize.x <= 0 || stageSize.y <= 0 || imgSize.x <= 0 || imgSize.y <= 0)
442
+ return []
447
443
 
448
444
  const newScaledAnnotations = annotations.map((annotation: Annotation) => ({
449
445
  ...annotation,
450
- coordinates: transform2.convertPercentagedCoordinatesToStage(
446
+ coordinates: transform.convertPercentagedCoordinatesToStage(
451
447
  annotation.coordinates,
452
448
  imgSize,
453
449
  stageSize,
454
450
  ),
455
- }));
451
+ }))
456
452
 
457
- return newScaledAnnotations;
458
- };
453
+ return newScaledAnnotations
454
+ }
459
455
 
460
- const scaledAnnotations = calculateScaledAnnotations(annotations);
456
+ const scaledAnnotations = calculateScaledAnnotations(annotations)
461
457
 
462
458
  const resetCanvas = () => {
463
- setEditorMode(EditorModes.VIEW);
459
+ setEditorMode(EditorModes.VIEW)
464
460
 
465
461
  // largest possible annotation size fitting the whole image
466
- setStageSize({ x: -1, y: -1 });
462
+ setStageSize({ x: -1, y: -1 })
467
463
 
468
464
  if (imageRef.current !== null) {
469
- const { width, height } = imageRef.current.getBoundingClientRect();
470
- setImgSize({ x: width, y: height });
465
+ const { width, height } = imageRef.current.getBoundingClientRect()
466
+ setImgSize({ x: width, y: height })
471
467
  }
472
468
 
473
- setSvgScale(1.0);
474
- setSvgTranslation({ x: 0, y: 0 });
469
+ setSvgScale(1.0)
470
+ setSvgTranslation({ x: 0, y: 0 })
475
471
 
476
- setLabelInputPosition(undefined);
477
- setIsLabelInputVisible(false);
478
- };
472
+ setLabelInputPosition(undefined)
473
+ setIsLabelInputVisible(false)
474
+ }
479
475
 
480
476
  // image changed after init -> reset everything
481
477
  useEffect(() => {
482
478
  if (canvasRef?.current !== undefined) {
483
- const { width, height } = canvasRef!.current!.getBoundingClientRect();
479
+ const { width, height } = canvasRef!.current!.getBoundingClientRect()
484
480
 
485
481
  // for whatever reason the ref adds the toolbars height to the available space, leading to a container size reaching outside the bottom
486
482
  // remove its height here manually
487
- const heightWithoutToolbar: number = height - toolbarHeight;
483
+ const heightWithoutToolbar: number = height - toolbarHeight
488
484
 
489
- setCanvasSize({ x: width, y: heightWithoutToolbar });
485
+ setCanvasSize({ x: width, y: heightWithoutToolbar })
490
486
 
491
487
  // listen for size changes on div element
492
488
  const resizeObserver = new ResizeObserver(() => {
493
- const { width, height } = canvasRef!.current!.getBoundingClientRect();
494
- const heightWithoutToolbar: number = height - toolbarHeight;
489
+ const { width, height } = canvasRef!.current!.getBoundingClientRect()
490
+ const heightWithoutToolbar: number = height - toolbarHeight
495
491
 
496
- setCanvasSize({ x: width, y: heightWithoutToolbar });
497
- });
498
- resizeObserver.observe(canvasRef!.current!);
492
+ setCanvasSize({ x: width, y: heightWithoutToolbar })
493
+ })
494
+ resizeObserver.observe(canvasRef!.current!)
499
495
 
500
496
  // cleanup
501
- return () => resizeObserver.disconnect();
497
+ return () => resizeObserver.disconnect()
502
498
  }
503
499
 
504
- resetCanvas();
505
- }, [image, isFullscreen]);
500
+ resetCanvas()
501
+ }, [image, isFullscreen])
506
502
 
507
503
  useEffect(() => {
508
- calculatePageToCanvasOffset();
509
- }, [imageRef, svgTranslation, canvasSize]);
504
+ calculatePageToCanvasOffset()
505
+ }, [imageRef, svgTranslation, canvasSize])
510
506
 
511
507
  // notify component about available size
512
508
  useEffect(() => {
513
- if (canvasRef.current === null) return;
514
- const { width, height } = canvasRef.current.getBoundingClientRect();
509
+ if (canvasRef.current === null) return
510
+ const { width, height } = canvasRef.current.getBoundingClientRect()
515
511
 
516
512
  // for whatever reason the ref adds the toolbars height to the available space, leading to a container size reaching outside the bottom
517
513
  // remove its height here manually
518
- const heightWithoutToolbar: number = height - toolbarHeight;
514
+ const heightWithoutToolbar: number = height - toolbarHeight
519
515
 
520
- setCanvasSize({ x: width, y: heightWithoutToolbar });
521
- }, [canvasRef]);
516
+ setCanvasSize({ x: width, y: heightWithoutToolbar })
517
+ }, [canvasRef])
522
518
 
523
519
  // notify component about default image size
524
520
  useEffect(() => {
525
- if (imageRef.current === null) return;
521
+ if (imageRef.current === null) return
526
522
 
527
- const { width, height } = imageRef.current.getBoundingClientRect();
523
+ const { width, height } = imageRef.current.getBoundingClientRect()
528
524
 
529
- setImgSize({ x: width, y: height });
525
+ setImgSize({ x: width, y: height })
530
526
 
531
527
  // listen for size changes on div element
532
528
  const imgResizeObserver = new ResizeObserver(() => {
533
- const { width, height } = imageRef!.current!.getBoundingClientRect();
529
+ const { width, height } = imageRef!.current!.getBoundingClientRect()
534
530
 
535
- setImgSize({ x: width, y: height });
536
- });
537
- imgResizeObserver.observe(imageRef!.current!);
531
+ setImgSize({ x: width, y: height })
532
+ })
533
+ imgResizeObserver.observe(imageRef!.current!)
538
534
 
539
- return () => imgResizeObserver.disconnect();
540
- }, [imageRef]);
535
+ return () => imgResizeObserver.disconnect()
536
+ }, [imageRef])
541
537
 
542
538
  useEffect(() => {
543
- if (imageToStageFactor === 0) return;
539
+ if (imageToStageFactor === 0) return
544
540
 
545
541
  const newStageSize: Vector2 = {
546
542
  x: imgSize.x * imageToStageFactor,
547
543
  y: imgSize.y * imageToStageFactor,
548
- };
544
+ }
549
545
 
550
- setStageSize(newStageSize);
551
- }, [imageToStageFactor, imgSize]);
546
+ setStageSize(newStageSize)
547
+ }, [imageToStageFactor, imgSize])
552
548
 
553
549
  useEffect(() => {
554
- if (!isPolygonSelectionMode) return;
555
- if (polygonOperationResult.polygonsToCreate === undefined) return;
550
+ if (!isPolygonSelectionMode) return
551
+ if (polygonOperationResult.polygonsToCreate === undefined) return
556
552
 
557
553
  // create all polygons calculated from the outside world
558
554
  polygonOperationResult.polygonsToCreate.forEach(
559
555
  (polygonToCreate: ToolCoordinates) => {
560
- const newAnnotationInternalId: number = onRequestNewAnnoId();
556
+ const newAnnotationInternalId: number = onRequestNewAnnoId()
561
557
  const newAnnotation: Annotation = new Annotation(
562
558
  newAnnotationInternalId,
563
559
  polygonToCreate.type,
564
- transform2.convertPercentagedCoordinatesToStage(
560
+ transform.convertPercentagedCoordinatesToStage(
565
561
  polygonToCreate.coordinates,
566
562
  imgSize,
567
563
  stageSize,
568
564
  ),
569
565
  AnnotationMode.VIEW,
570
566
  AnnotationStatus.CREATED,
571
- );
567
+ )
572
568
 
573
- onFinishCreateAnno(newAnnotation);
569
+ onFinishCreateAnno(newAnnotation)
574
570
  },
575
- );
576
- }, [polygonOperationResult]);
571
+ )
572
+ }, [polygonOperationResult])
577
573
 
578
574
  const onFinishCreateAnno = (fullyCreatedAnnotation: Annotation) => {
579
- setEditorMode(EditorModes.VIEW);
575
+ setEditorMode(EditorModes.VIEW)
580
576
 
581
577
  const newAnnotation: Annotation = {
582
578
  ...fullyCreatedAnnotation,
583
579
  mode: AnnotationMode.VIEW,
584
- };
580
+ }
585
581
 
586
582
  // handle annoTime (not for points though - they are created in only one frame)
587
583
  if (fullyCreatedAnnotation.type !== AnnotationTool.Point) {
588
584
  const annoEditDuration: number = TimeUtils.getRoundedDuration(
589
585
  annoTimestamp,
590
586
  performance.now(),
591
- );
592
- newAnnotation.annoTime = annoEditDuration;
587
+ )
588
+ newAnnotation.annoTime = annoEditDuration
593
589
  }
594
590
 
595
591
  // convert the coordinates from our local scaled sytem into the percentaged one
596
- const percentagedCoordinates =
597
- transform2.convertStageCoordinatesToPercentaged(
598
- fullyCreatedAnnotation.coordinates,
599
- imageToStageFactor,
600
- imgSize,
601
- );
602
- newAnnotation.coordinates = percentagedCoordinates;
592
+ const percentagedCoordinates = transform.convertStageCoordinatesToPercentaged(
593
+ fullyCreatedAnnotation.coordinates,
594
+ imageToStageFactor,
595
+ imgSize,
596
+ )
597
+ newAnnotation.coordinates = percentagedCoordinates
603
598
 
604
- onAnnoChanged(newAnnotation);
599
+ onAnnoChanged(newAnnotation)
605
600
 
606
601
  // inform annotation that we just created it
607
602
  const hasAnnoJustBeenCreated: boolean =
608
- selectedAnnoTool === AnnotationTool.Point || isPolygonSelectionMode;
603
+ selectedAnnoTool === AnnotationTool.Point || isPolygonSelectionMode
609
604
 
610
- onAnnoCreationFinished(newAnnotation, hasAnnoJustBeenCreated);
611
- };
605
+ onAnnoCreationFinished(newAnnotation, hasAnnoJustBeenCreated)
606
+ }
612
607
 
613
608
  const onKeyDown = (e: KeyboardEvent) => {
614
- e.preventDefault();
609
+ e.preventDefault()
615
610
 
616
- keyMapper.keyDown(e.key, e.shiftKey, e.ctrlKey);
617
- };
611
+ keyMapper.keyDown(e.key, e.shiftKey, e.ctrlKey)
612
+ }
618
613
 
619
614
  const onKeyUp = (e: KeyboardEvent) => {
620
- e.preventDefault();
615
+ e.preventDefault()
621
616
  // @TODO implement keyMapper
622
617
  // this.keyMapper.keyUp(e.key);
623
618
  // if (propsOnKeyUp) propsOnKeyUp(e);
624
- };
619
+ }
625
620
 
626
- const onMouseDown = (e: React.MouseEvent<SVGImageElement, MouseEvent>) => {
621
+ const onMouseDown = (e: React.MouseEvent<SVGElement, MouseEvent>) => {
627
622
  if (e.button === 0) {
628
623
  // left click
629
624
  } else if (e.button === 1) {
630
625
  // click on mouse wheel
631
- setEditorMode(EditorModes.CAMERA_MOVE);
626
+ setEditorMode(EditorModes.CAMERA_MOVE)
632
627
  } else if (e.button === 2) {
633
628
  // check if annotation creation allowed in settings
634
- if (!annotationSettings!.canCreate) return;
629
+ if (!annotationSettings!.canCreate) return
635
630
 
636
- if (editorMode === EditorModes.ADD) return;
631
+ if (editorMode === EditorModes.ADD || editorMode === EditorModes.CREATE) return
637
632
 
638
633
  // right click -> start new annotation
639
634
  // clicks during annotation creation will be handled inside the AnnotationComponent
640
635
  const antiScaledMouseStageMovedPosition: Point =
641
- mouse2.getAntiScaledMouseStagePosition(
636
+ mouse.getAntiScaledMouseStagePosition(
642
637
  e,
643
638
  pageToStageOffset,
644
639
  svgScale,
645
640
  svgTranslation,
646
- );
641
+ )
647
642
 
648
643
  // remove translation when image was horizontally centered
649
644
  const antiScaledMouseStagePosition: Point = {
650
645
  x: antiScaledMouseStageMovedPosition.x - imageCenteringSpace,
651
646
  y: antiScaledMouseStageMovedPosition.y,
652
- };
647
+ }
653
648
 
654
- createNewAnnotation(antiScaledMouseStagePosition);
649
+ createNewAnnotation(antiScaledMouseStagePosition)
655
650
  }
656
- };
651
+ }
657
652
 
658
653
  const onMouseOver = () => {
659
654
  // this.svg.current.focus();
660
655
  //Prevent scrolling on svg
661
656
  if (preventScrolling) {
662
- document.body.style.overflow = "hidden";
657
+ document.body.style.overflow = 'hidden'
663
658
  }
664
- };
659
+ }
665
660
 
666
661
  const onMouseUp = (e) => {
667
662
  switch (e.button) {
668
663
  case 1:
669
- setEditorMode(EditorModes.VIEW);
670
- break;
664
+ setEditorMode(EditorModes.VIEW)
665
+ break
671
666
  default:
672
- break;
667
+ break
673
668
  }
674
- };
669
+ }
675
670
 
676
671
  const onMouseMove = (movementX: number, movementY: number) => {
677
672
  if (editorMode === EditorModes.CAMERA_MOVE) {
678
- moveCamera(movementX, movementY);
673
+ moveCamera(movementX, movementY)
679
674
  }
680
- };
675
+ }
681
676
 
682
677
  const onMouseLeave = () => {
683
678
  if (preventScrolling) {
684
- document.body.style.overflow = "";
679
+ document.body.style.overflow = ''
685
680
  }
686
- };
681
+ }
687
682
 
688
683
  const onWheel = (e: WheelEvent) => {
689
- const scaleFactor = 1.25;
690
- const scrollDirection = e.deltaY < 0 ? 1 : -1;
684
+ const scaleFactor = 1.25
685
+ const scrollDirection = e.deltaY < 0 ? 1 : -1
691
686
 
692
687
  // calculate scaling based on scroll wheel direction
693
- const newScale =
694
- scrollDirection > 0 ? svgScale * scaleFactor : svgScale / scaleFactor;
688
+ const newScale = scrollDirection > 0 ? svgScale * scaleFactor : svgScale / scaleFactor
695
689
 
696
690
  // zoom in/out without affecting the pixel the mouse is at
697
- const mousePositionInStage: Point = mouse2.getAntiScaledMouseStagePosition(
691
+ const mousePositionInStage: Point = mouse.getAntiScaledMouseStagePosition(
698
692
  e,
699
693
  pageToStageOffset,
700
694
  svgScale,
701
695
  svgTranslation,
702
- );
696
+ )
703
697
 
704
698
  // set translation around mouse pixel
705
- const scaleChangeFactor: number = svgScale / newScale;
699
+ const scaleChangeFactor: number = svgScale / newScale
706
700
  const newTranslation: Vector2 = {
707
701
  x:
708
702
  scaleChangeFactor * (mousePositionInStage.x + svgTranslation.x) -
@@ -710,94 +704,91 @@ const Canvas = ({
710
704
  y:
711
705
  scaleChangeFactor * (mousePositionInStage.y + svgTranslation.y) -
712
706
  mousePositionInStage.y,
713
- };
707
+ }
714
708
 
715
709
  // contstrain zoom
716
710
  if (newScale < 1.0) {
717
- setSvgScale(1);
711
+ setSvgScale(1)
718
712
  if (svgTranslation.x != 0 || svgTranslation.y != 0)
719
- setSvgTranslation({ x: 0, y: 0 });
713
+ setSvgTranslation({ x: 0, y: 0 })
720
714
  } else if (newScale > 200) {
721
- setSvgScale(200);
722
- setSvgTranslation(newTranslation);
715
+ setSvgScale(200)
716
+ setSvgTranslation(newTranslation)
723
717
  } else {
724
- setSvgScale(newScale);
725
- setSvgTranslation(newTranslation);
718
+ setSvgScale(newScale)
719
+ setSvgTranslation(newTranslation)
726
720
  }
727
- };
721
+ }
728
722
 
729
723
  const onAnnoAction = (annotation: Annotation, canvasAction: CanvasAction) => {
730
724
  if (canvasAction !== CanvasAction.ANNO_SELECTED) {
731
- console.log("Unknown Canvas Action:", canvasAction);
732
- return;
725
+ console.log('Unknown Canvas Action:', canvasAction)
726
+ return
733
727
  }
734
728
 
735
729
  const percentagedAnnotation = {
736
730
  ...annotation,
737
- coordinates: transform2.convertStageCoordinatesToPercentaged(
731
+ coordinates: transform.convertStageCoordinatesToPercentaged(
738
732
  [...annotation.coordinates],
739
733
  imageToStageFactor,
740
734
  imgSize,
741
735
  ),
742
- };
736
+ }
743
737
 
744
- onSelectAnnotation(percentagedAnnotation);
738
+ onSelectAnnotation(percentagedAnnotation)
745
739
 
746
740
  // get top left point of annotation
747
- const leftPoints: Point[] = transform2.getMostLeftPoints(
748
- annotation.coordinates,
749
- );
750
- const topLeftPoint: Point = transform2.getTopPoint(leftPoints)[0];
751
- const pageTopLeftPoint: Point = transform2.convertStageToPage(
741
+ const leftPoints: Point[] = transform.getMostLeftPoints(annotation.coordinates)
742
+ const topLeftPoint: Point = transform.getTopPoint(leftPoints)[0]
743
+ const pageTopLeftPoint: Point = transform.convertStageToPage(
752
744
  topLeftPoint,
753
745
  pageToStageOffset,
754
746
  svgScale,
755
747
  svgTranslation,
756
- );
748
+ )
757
749
 
758
- setLabelInputPosition(pageTopLeftPoint);
759
- };
750
+ setLabelInputPosition(pageTopLeftPoint)
751
+ }
760
752
 
761
753
  const handleOnAnnoChanged = (annotation: Annotation) => {
762
- const percentagedCoordinates =
763
- transform2.convertStageCoordinatesToPercentaged(
764
- annotation.coordinates,
765
- imageToStageFactor,
766
- imgSize,
767
- );
754
+ const percentagedCoordinates = transform.convertStageCoordinatesToPercentaged(
755
+ annotation.coordinates,
756
+ imageToStageFactor,
757
+ imgSize,
758
+ )
768
759
 
769
760
  const newAnnotation = {
770
761
  ...annotation,
771
762
  coordinates: percentagedCoordinates,
772
- };
763
+ }
773
764
 
774
765
  // mark loaded annotations as changed (they wont be saved otherwise)
775
766
  if (newAnnotation.status === AnnotationStatus.LOADED)
776
- newAnnotation.status = AnnotationStatus.CHANGED;
767
+ newAnnotation.status = AnnotationStatus.CHANGED
777
768
 
778
769
  // send event to parent component
779
- onAnnoChanged(newAnnotation);
780
- };
770
+ onAnnoChanged(newAnnotation)
771
+ }
781
772
 
782
773
  const renderAnnotations = (): ReactElement => {
783
774
  // hide all annotations when image is moved
784
- if (editorMode === EditorModes.CAMERA_MOVE) return <></>;
775
+ if (editorMode === EditorModes.CAMERA_MOVE) return <></>
785
776
 
786
777
  const editorModesOtherAnnosShouldBeHiddenIn = [
787
778
  EditorModes.CREATE,
788
779
  EditorModes.ADD,
789
780
  EditorModes.MOVE,
790
- ];
781
+ ]
791
782
 
792
783
  const shouldHideOtherAnnos: boolean =
793
- editorModesOtherAnnosShouldBeHiddenIn.includes(editorMode);
784
+ editorModesOtherAnnosShouldBeHiddenIn.includes(editorMode)
794
785
 
795
786
  // draw the annotation using the AnnotationComponent and the scaled coordinates
796
787
  const annos: ReactElement[] = scaledAnnotations.map(
797
788
  (scaledAnnotation: Annotation): ReactElement => {
798
789
  // only show selected anno in specific editor modes
799
790
  const isAnnoSelected: boolean =
800
- scaledAnnotation.internalId === selectedAnnotation?.internalId;
791
+ scaledAnnotation.internalId === selectedAnnotation?.internalId
801
792
 
802
793
  if (shouldHideOtherAnnos && !isAnnoSelected)
803
794
  return (
@@ -805,7 +796,7 @@ const Canvas = ({
805
796
  // we still need to provide a key, otherwise we got 10 nothings and react cannot differ between them (it wants to)
806
797
  // use an empty svg g element because <></> cannot have a key
807
798
  <g key={`annotationComponent_${scaledAnnotation.internalId}`} />
808
- );
799
+ )
809
800
 
810
801
  return (
811
802
  <AnnotationComponent
@@ -828,22 +819,21 @@ const Canvas = ({
828
819
  onAction={onAnnoAction}
829
820
  onAnnoChanged={handleOnAnnoChanged}
830
821
  onAnnotationModeChange={(annotationMode: AnnotationMode) => {
831
- if (annotationMode === AnnotationMode.MOVE)
832
- setEditorMode(EditorModes.MOVE);
822
+ if (annotationMode === AnnotationMode.MOVE) setEditorMode(EditorModes.MOVE)
833
823
  if (
834
824
  editorMode === EditorModes.MOVE &&
835
825
  annotationMode === AnnotationMode.VIEW
836
826
  )
837
- setEditorMode(EditorModes.VIEW);
827
+ setEditorMode(EditorModes.VIEW)
838
828
  }}
839
829
  onNotification={onNotification}
840
830
  />
841
- );
831
+ )
842
832
  },
843
- );
833
+ )
844
834
 
845
- return <g>{annos}</g>;
846
- };
835
+ return <g>{annos}</g>
836
+ }
847
837
 
848
838
  const renderInfiniteSelectionArea = (): ReactElement => {
849
839
  // block changing annotations while label selector is open
@@ -852,36 +842,36 @@ const Canvas = ({
852
842
  <circle
853
843
  cx={stageSize.x / 2}
854
844
  cy={stageSize.y / 2}
855
- r={"100%"}
845
+ r={'100%'}
856
846
  style={{ opacity: 0 }}
857
847
  onContextMenu={(e) => e.preventDefault()}
858
848
  onClick={() => {
859
- setIsLabelInputVisible(false);
849
+ setIsLabelInputVisible(false)
860
850
  }}
861
851
  />
862
- );
863
- };
852
+ )
853
+ }
864
854
 
865
855
  // calculate the center of the canvas in page coordinates
866
856
  const junkTextStart: Vector2 = {
867
857
  x: pageToCanvasOffset.x + canvasSize.x / 2,
868
858
  y: pageToCanvasOffset.y + canvasSize.y / 2,
869
- };
859
+ }
870
860
 
871
861
  return (
872
862
  <div
873
863
  ref={canvasRef}
874
864
  style={{
875
- width: "100%",
876
- height: "100%",
865
+ width: '100%',
866
+ height: '100%',
877
867
  }}
878
868
  >
879
869
  <div
880
870
  style={{
881
- position: "absolute",
871
+ position: 'absolute',
882
872
  left: labelInputPosition?.x !== undefined ? labelInputPosition.x : 0,
883
873
  top: labelInputPosition?.y !== undefined ? labelInputPosition.y : 0,
884
- display: labelInputPosition?.y !== undefined ? "inherit" : "none",
874
+ display: labelInputPosition?.y !== undefined ? 'inherit' : 'none',
885
875
  zIndex: isLabelInputVisible ? 7000 : -1,
886
876
  }}
887
877
  >
@@ -893,17 +883,16 @@ const Canvas = ({
893
883
  isMultilabel={annotationSettings!.canHaveMultipleLabels}
894
884
  onLabelSelect={(selectedLabelIds: number[]) => {
895
885
  // close the input popup
896
- setIsLabelInputVisible(false);
886
+ setIsLabelInputVisible(false)
897
887
 
898
888
  // inform parent which label was chosen
899
889
  if (selectedLabelIds.length > 0) {
900
890
  const newLabelIds: number[] = selectedLabelIds.filter(
901
- (labelId: number) =>
902
- !selectedAnnotation!.labelIds!.includes(labelId),
903
- );
891
+ (labelId: number) => !selectedAnnotation!.labelIds!.includes(labelId),
892
+ )
904
893
 
905
894
  if (newLabelIds.length > 0) {
906
- setCurrentLabelId(newLabelIds[0]);
895
+ setCurrentLabelId(newLabelIds[0])
907
896
  }
908
897
  }
909
898
 
@@ -911,22 +900,22 @@ const Canvas = ({
911
900
  const newAnnotationStatus: AnnotationStatus =
912
901
  selectedAnnotation.status === AnnotationStatus.LOADED
913
902
  ? AnnotationStatus.CHANGED
914
- : selectedAnnotation.status;
903
+ : selectedAnnotation.status
915
904
 
916
905
  // selectedAnnotation comes from SIA and is therefore in the percentaged system
917
906
  // convert it first
918
907
  // also update the new labels
919
908
  const updatedAnno: Annotation = {
920
909
  ...selectedAnnotation,
921
- coordinates: transform2.convertPercentagedCoordinatesToStage(
910
+ coordinates: transform.convertPercentagedCoordinatesToStage(
922
911
  selectedAnnotation!.coordinates,
923
912
  imgSize,
924
913
  stageSize,
925
914
  ),
926
915
  labelIds: [...selectedLabelIds],
927
916
  status: newAnnotationStatus,
928
- };
929
- handleOnAnnoChanged(updatedAnno);
917
+ }
918
+ handleOnAnnoChanged(updatedAnno)
930
919
  }}
931
920
  />
932
921
  </div>
@@ -934,12 +923,12 @@ const Canvas = ({
934
923
  {isImageJunk && (
935
924
  <div
936
925
  style={{
937
- position: "absolute",
926
+ position: 'absolute',
938
927
  left: junkTextStart.x,
939
928
  top: junkTextStart.y,
940
- transform: "translate(-50%, -50%)",
941
- textAlign: "center",
942
- color: "white",
929
+ transform: 'translate(-50%, -50%)',
930
+ textAlign: 'center',
931
+ color: 'white',
943
932
  }}
944
933
  >
945
934
  <FontAwesomeIcon
@@ -952,29 +941,13 @@ const Canvas = ({
952
941
  )}
953
942
 
954
943
  <svg
955
- // ref={svgRef}
956
- // width={svgSize[0] > 0 ? svgSize[0] : "100%"}
957
- // height={svgSize[1] > 0 ? svgSize[1] : "100%"}
958
944
  width="100%"
959
945
  height="100%"
960
- // width={
961
- // this.props.fixedImageSize
962
- // ? this.props.fixedImageSize
963
- // : this.state.svg.width
964
- // }
965
- // height={
966
- // this.props.fixedImageSize
967
- // ? this.props.fixedImageSize
968
- // : this.state.svg.height
969
- // }
970
946
  onKeyDown={onKeyDown}
971
947
  onKeyUp={onKeyUp}
972
948
  onMouseMove={(e) => onMouseMove(e.movementX, e.movementY)}
973
- // onMouseMove={(e) => this.handleSvgMouseMove(e)}
974
949
  tabIndex={0}
975
- // width="100%"
976
- // height="100%"
977
- // style={{ position: "absolute" }}
950
+ onMouseDown={(e) => onMouseDown(e)}
978
951
  >
979
952
  <g
980
953
  transform={`scale(${svgScale}) translate(${centeredSvgTranslation.x}, ${centeredSvgTranslation.y})`}
@@ -986,12 +959,11 @@ const Canvas = ({
986
959
  onMouseMove={(e) => onMouseMove(e.movementX, e.movementY)}
987
960
  onClick={() => {
988
961
  // clicked onto canvas => clear selected anno
989
- onSelectAnnotation(undefined);
962
+ onSelectAnnotation(undefined)
990
963
  }}
991
964
  >
992
965
  <image
993
966
  onContextMenu={(e) => e.preventDefault()}
994
- onMouseDown={(e) => onMouseDown(e)}
995
967
  href={image}
996
968
  ref={imageRef}
997
969
  // undefined -> use default (unscaled) size of image
@@ -1012,13 +984,13 @@ const Canvas = ({
1012
984
  style={{ opacity: 0.8 }}
1013
985
  onContextMenu={(e) => e.preventDefault()}
1014
986
  onClick={() => {
1015
- setLabelInputPosition(undefined);
987
+ setLabelInputPosition(undefined)
1016
988
  }}
1017
989
  />
1018
990
  )}
1019
991
  </svg>
1020
992
  </div>
1021
- );
1022
- };
993
+ )
994
+ }
1023
995
 
1024
- export default Canvas;
996
+ export default Canvas