lost-sia 2.0.1-alpha8 → 3.0.0

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 (280) hide show
  1. package/README.md +4 -0
  2. package/dist/Annotation/logic/Annotation.d.ts +17 -0
  3. package/dist/Annotation/logic/Annotation.js +1 -0
  4. package/dist/Annotation/ui/AnnotationComponent.d.ts +24 -0
  5. package/dist/Annotation/ui/AnnotationComponent.js +1 -0
  6. package/dist/Annotation/ui/atoms/AnnoBar.d.ts +15 -0
  7. package/dist/Annotation/ui/atoms/AnnoBar.js +1 -0
  8. package/dist/Annotation/ui/atoms/DaviIcon.d.ts +9 -0
  9. package/dist/Annotation/ui/atoms/DaviIcon.js +19 -0
  10. package/dist/Annotation/ui/atoms/Edge.d.ts +17 -0
  11. package/dist/Annotation/ui/atoms/Edge.js +1 -0
  12. package/dist/Annotation/ui/atoms/Node.d.ts +17 -0
  13. package/dist/Annotation/ui/atoms/Node.js +1 -0
  14. package/dist/Annotation/ui/atoms/PolygonArea.d.ts +16 -0
  15. package/dist/Annotation/ui/atoms/PolygonArea.js +1 -0
  16. package/dist/Annotation/ui/tools/BBox.d.ts +21 -0
  17. package/dist/Annotation/ui/tools/BBox.js +1 -0
  18. package/dist/Annotation/ui/tools/Line.d.ts +21 -0
  19. package/dist/Annotation/ui/tools/Line.js +1 -0
  20. package/dist/Annotation/ui/tools/Point.d.ts +16 -0
  21. package/dist/Annotation/ui/tools/Point.js +1 -0
  22. package/dist/Annotation/ui/tools/Polygon.d.ts +23 -0
  23. package/dist/Annotation/ui/tools/Polygon.js +1 -0
  24. package/dist/Canvas/Canvas.d.ts +31 -0
  25. package/dist/Canvas/Canvas.js +1 -0
  26. package/dist/Canvas/LabelInput.d.ts +11 -0
  27. package/dist/Canvas/LabelInput.js +1 -0
  28. package/dist/IconButton.d.ts +25 -0
  29. package/dist/IconButton.js +1 -0
  30. package/dist/Sia.d.ts +33 -0
  31. package/dist/Sia.js +1 -0
  32. package/dist/Toolbar/Toolbar.d.ts +21 -0
  33. package/dist/Toolbar/Toolbar.js +1 -0
  34. package/dist/Toolbar/ToolbarItems/AccessibilityTools.d.ts +7 -0
  35. package/dist/Toolbar/ToolbarItems/AccessibilityTools.js +1 -0
  36. package/dist/Toolbar/ToolbarItems/AnnoToolSelector.d.ts +11 -0
  37. package/dist/Toolbar/ToolbarItems/AnnoToolSelector.js +1 -0
  38. package/dist/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.d.ts +11 -0
  39. package/dist/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.js +1 -0
  40. package/dist/Toolbar/ToolbarItems/ImageToolItems/TagLabel.d.ts +11 -0
  41. package/dist/Toolbar/ToolbarItems/ImageToolItems/TagLabel.js +1 -0
  42. package/dist/Toolbar/ToolbarItems/ImageTools.d.ts +13 -0
  43. package/dist/Toolbar/ToolbarItems/ImageTools.js +1 -0
  44. package/dist/Toolbar/ToolbarItems/Instructions.d.ts +2 -0
  45. package/dist/Toolbar/ToolbarItems/Instructions.js +1 -0
  46. package/dist/Toolbar/ToolbarItems/InstructionsModal.d.ts +6 -0
  47. package/dist/Toolbar/ToolbarItems/InstructionsModal.js +1 -0
  48. package/dist/assets/SIA-BV1tYu3P.css +1 -0
  49. package/dist/assets/brand-icons-Cu_C0hZ4.svg +1008 -0
  50. package/dist/assets/brand-icons-F3SPCeH1.woff +0 -0
  51. package/dist/assets/brand-icons-XL9sxUpA.woff2 +0 -0
  52. package/dist/assets/brand-icons-sqJ2Pg7a.eot +0 -0
  53. package/dist/assets/brand-icons-ubhWoxly.ttf +0 -0
  54. package/dist/assets/flags-DOLqOU7Y.png +0 -0
  55. package/dist/assets/icons-BOCtAERH.woff +0 -0
  56. package/dist/assets/icons-CHzK1VD9.eot +0 -0
  57. package/dist/assets/icons-D29ZQHHw.ttf +0 -0
  58. package/dist/assets/icons-Du6TOHnR.woff2 +0 -0
  59. package/dist/assets/icons-RwhydX30.svg +1518 -0
  60. package/dist/assets/node_modules/semantic-ui-css/semantic.min-Bvulf91l.css +346 -0
  61. package/dist/assets/outline-icons-BfdLr8tr.svg +366 -0
  62. package/dist/assets/outline-icons-DD8jm0uy.ttf +0 -0
  63. package/dist/assets/outline-icons-DInHoiqI.woff2 +0 -0
  64. package/dist/assets/outline-icons-LX8adJ4n.eot +0 -0
  65. package/dist/assets/outline-icons-aQ88nltS.woff +0 -0
  66. package/dist/index.d.ts +5 -0
  67. package/dist/index.js +1 -0
  68. package/dist/models/AnnotationMode.d.ts +11 -0
  69. package/dist/models/AnnotationMode.js +1 -0
  70. package/dist/models/AnnotationStatus.d.ts +8 -0
  71. package/dist/models/AnnotationStatus.js +1 -0
  72. package/dist/models/AnnotationTool.d.ts +7 -0
  73. package/dist/models/AnnotationTool.js +1 -0
  74. package/dist/models/CanvasAction.d.ts +28 -0
  75. package/dist/models/CanvasAction.js +1 -0
  76. package/dist/models/Direction.d.ts +7 -0
  77. package/dist/models/Direction.js +1 -0
  78. package/dist/models/EditorModes.d.ts +11 -0
  79. package/dist/models/EditorModes.js +1 -0
  80. package/dist/models/KeyAction.d.ts +22 -0
  81. package/dist/models/KeyAction.js +1 -0
  82. package/dist/models/NotificationType.d.ts +7 -0
  83. package/dist/models/NotificationType.js +1 -0
  84. package/dist/models/index.d.ts +6 -0
  85. package/dist/models/index.js +1 -0
  86. package/dist/stories/AnnotationTools.stories.d.ts +40 -0
  87. package/dist/stories/Canvas/Canvas.stories.d.ts +50 -0
  88. package/dist/stories/Canvas/CanvasOffset.d.ts +13 -0
  89. package/dist/stories/Canvas/CanvasWithOffset.stories.d.ts +36 -0
  90. package/dist/stories/FilterDropdown.stories.d.ts +19 -0
  91. package/dist/stories/MinimalSia.stories.d.ts +66 -0
  92. package/dist/stories/SIA/DemoWrapper.d.ts +8 -0
  93. package/dist/stories/SIA/DemoWrapper.stories.d.ts +27 -0
  94. package/dist/stories/SIA/SIA.stories.d.ts +72 -0
  95. package/dist/stories/Toolbar/ImageTools/TagLabel.stories.d.ts +21 -0
  96. package/dist/stories/Toolbar/Instructions.stories.d.ts +11 -0
  97. package/dist/stories/Toolbar/Toolbar.stories.d.ts +37 -0
  98. package/dist/stories/exampleData/exampleAnnotations.d.ts +8 -0
  99. package/dist/stories/exampleData/exampleExternalAnnotations.d.ts +8 -0
  100. package/dist/stories/exampleData/exampleImage.d.ts +2 -0
  101. package/dist/stories/exampleData/exampleLabels.d.ts +6 -0
  102. package/dist/types.d.ts +57 -0
  103. package/dist/types.js +1 -0
  104. package/dist/utils/KeyMapper.d.ts +9 -0
  105. package/dist/utils/KeyMapper.js +1 -0
  106. package/dist/utils/TimeUtils.d.ts +4 -0
  107. package/dist/utils/TimeUtils.js +1 -0
  108. package/dist/utils/color.d.ts +2 -0
  109. package/dist/utils/color.js +1 -0
  110. package/dist/utils/index.d.ts +2 -0
  111. package/dist/utils/index.js +1 -0
  112. package/dist/utils/mouse.d.ts +6 -0
  113. package/dist/utils/mouse.js +1 -0
  114. package/dist/utils/siaIcons.js +12 -0
  115. package/dist/utils/transform.d.ts +28 -0
  116. package/dist/utils/transform.js +1 -0
  117. package/dist/utils/uiConfig.js +1 -0
  118. package/dist/utils/windowViewport.d.ts +22 -0
  119. package/dist/utils/windowViewport.js +1 -0
  120. package/package.json +19 -16
  121. package/src/AnnoExampleViewer.jsx +18 -18
  122. package/src/Annotation/logic/Annotation.ts +24 -26
  123. package/src/Annotation/ui/AnnotationComponent.tsx +115 -86
  124. package/src/Annotation/ui/atoms/AnnoBar.tsx +51 -53
  125. package/src/Annotation/ui/atoms/DaviIcon.tsx +12 -22
  126. package/src/Annotation/ui/atoms/Edge.tsx +25 -22
  127. package/src/Annotation/ui/atoms/Node.tsx +56 -50
  128. package/src/Annotation/ui/atoms/PolygonArea.tsx +30 -35
  129. package/src/Annotation/ui/tools/BBox.tsx +136 -150
  130. package/src/Annotation/ui/tools/Line.tsx +94 -91
  131. package/src/Annotation/ui/tools/Point.tsx +19 -17
  132. package/src/Annotation/ui/tools/Polygon.tsx +126 -95
  133. package/src/Canvas/Canvas.tsx +748 -594
  134. package/src/Canvas/LabelInput.tsx +68 -45
  135. package/src/IconButton.tsx +119 -0
  136. package/src/InfoBoxes/AnnoDetails.jsx +53 -53
  137. package/src/InfoBoxes/AnnoStats.jsx +41 -41
  138. package/src/InfoBoxes/InfoBox.jsx +16 -16
  139. package/src/InfoBoxes/InfoBoxArea.jsx +32 -34
  140. package/src/InfoBoxes/LabelInfo.jsx +30 -30
  141. package/src/SIASettingButton.jsx +25 -25
  142. package/src/Sia.tsx +484 -0
  143. package/src/Toolbar/Toolbar.tsx +38 -31
  144. package/src/Toolbar/ToolbarItems/AccessibilityTools.tsx +26 -46
  145. package/src/Toolbar/ToolbarItems/AnnoToolSelector.tsx +53 -46
  146. package/src/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.tsx +127 -0
  147. package/src/Toolbar/ToolbarItems/ImageToolItems/TagLabel.tsx +29 -28
  148. package/src/Toolbar/ToolbarItems/ImageTools.tsx +43 -40
  149. package/src/Toolbar/ToolbarItems/Instructions.tsx +47 -50
  150. package/src/Toolbar/ToolbarItems/InstructionsModal.tsx +8 -8
  151. package/src/index.ts +9 -13
  152. package/src/models/{AnnotationMode.tsx → AnnotationMode.ts} +1 -1
  153. package/src/models/{AnnotationStatus.tsx → AnnotationStatus.ts} +1 -1
  154. package/src/models/{AnnotationTool.tsx → AnnotationTool.ts} +1 -1
  155. package/src/models/{CanvasAction.tsx → CanvasAction.ts} +1 -1
  156. package/src/models/{Direction.tsx → Direction.ts} +1 -1
  157. package/src/models/{EditorModes.tsx → EditorModes.ts} +1 -1
  158. package/src/models/{KeyAction.tsx → KeyAction.ts} +3 -1
  159. package/src/models/NotificationType.ts +8 -0
  160. package/src/models/index.ts +6 -7
  161. package/src/siaDummyData.js +71 -71
  162. package/src/stories/AnnotationTools.mdx +27 -0
  163. package/src/stories/AnnotationTools.stories.tsx +104 -0
  164. package/src/stories/Canvas/Canvas.stories.tsx +59 -113
  165. package/src/stories/Canvas/CanvasOffset.tsx +54 -38
  166. package/src/stories/Canvas/CanvasWithOffset.stories.tsx +42 -113
  167. package/src/stories/FilterDropdown.stories.ts +13 -11
  168. package/src/stories/MinimalSIA.mdx +20 -0
  169. package/src/stories/MinimalSia.stories.tsx +90 -0
  170. package/src/stories/SIA/DemoWrapper.stories.tsx +71 -0
  171. package/src/stories/SIA/DemoWrapper.tsx +55 -0
  172. package/src/stories/SIA/SIA.stories.tsx +79 -45
  173. package/src/stories/Toolbar/ImageTools/TagLabel.stories.tsx +11 -12
  174. package/src/stories/Toolbar/Instructions.stories.tsx +11 -11
  175. package/src/stories/Toolbar/Toolbar.stories.tsx +32 -47
  176. package/src/stories/Welcome.mdx +5 -0
  177. package/src/stories/development/CoordinateSystems.mdx +25 -0
  178. package/src/stories/exampleData/exampleAnnotations.ts +65 -0
  179. package/src/stories/exampleData/exampleExternalAnnotations.ts +115 -0
  180. package/src/stories/{siaDummyData2.ts → exampleData/exampleImage.ts} +3 -264
  181. package/src/stories/exampleData/exampleLabels.ts +146 -0
  182. package/src/stories/main.scss +6 -0
  183. package/src/styles/style.scss +1 -26
  184. package/src/types.ts +67 -0
  185. package/src/utils/KeyMapper.ts +76 -74
  186. package/src/utils/TimeUtils.ts +11 -0
  187. package/src/utils/color.ts +25 -25
  188. package/src/utils/hist.js +22 -22
  189. package/src/utils/index.ts +2 -3
  190. package/src/utils/mouse.ts +45 -0
  191. package/src/utils/siaIcons.jsx +5 -7
  192. package/src/utils/transform.ts +186 -0
  193. package/src/utils/uiConfig.js +19 -22
  194. package/src/utils/windowViewport.ts +34 -0
  195. package/src/AnnoLabelInput.jsx +0 -109
  196. package/src/AnnoToolBar.jsx +0 -153
  197. package/src/Annotation/AnnoBar.jsx +0 -154
  198. package/src/Annotation/Annotation.jsx +0 -395
  199. package/src/Annotation/Annotation.scss +0 -47
  200. package/src/Annotation/BBox.jsx +0 -299
  201. package/src/Annotation/Edge.jsx +0 -92
  202. package/src/Annotation/InfSelectionArea.jsx +0 -72
  203. package/src/Annotation/Line.jsx +0 -68
  204. package/src/Annotation/Node.jsx +0 -282
  205. package/src/Annotation/Point.jsx +0 -200
  206. package/src/Annotation/Polygon.jsx +0 -404
  207. package/src/Annotation/logic/AnnotationUtils.ts +0 -30
  208. package/src/Canvas.jsx +0 -2194
  209. package/src/ImgBar.jsx +0 -131
  210. package/src/LabelInput.jsx +0 -238
  211. package/src/Prompt.jsx +0 -45
  212. package/src/SIAFilterButton.jsx +0 -186
  213. package/src/Sia.jsx +0 -478
  214. package/src/Sia2.tsx +0 -392
  215. package/src/SiaPopup.jsx +0 -15
  216. package/src/ToolBar.jsx +0 -463
  217. package/src/Toolbar/NavigationButtons.tsx +0 -21
  218. package/src/Toolbar/ToolbarItem.jsx +0 -30
  219. package/src/Toolbar/ToolbarItems/ImageToolItems/ImageLabel.tsx +0 -62
  220. package/src/Toolbar.css +0 -13
  221. package/src/ToolbarItem.jsx +0 -31
  222. package/src/filterTools.js +0 -5
  223. package/src/models/AllowedTools.tsx +0 -9
  224. package/src/models/AnnotationSettings.tsx +0 -9
  225. package/src/models/ExternalAnnotation.ts +0 -15
  226. package/src/models/Label.tsx +0 -8
  227. package/src/models/UiConfig.tsx +0 -6
  228. package/src/stories/Button.jsx +0 -54
  229. package/src/stories/Button.stories.js +0 -48
  230. package/src/stories/Header.jsx +0 -69
  231. package/src/stories/Header.stories.js +0 -28
  232. package/src/stories/Page.jsx +0 -87
  233. package/src/stories/Page.stories.js +0 -28
  234. package/src/stories/SIA2/DemoWrapper.stories.tsx +0 -167
  235. package/src/stories/SIA2/DemoWrapper.tsx +0 -54
  236. package/src/stories/SIA2/Sia2.stories.tsx +0 -62
  237. package/src/stories/Toolbar/ImageTools/ImageLabel.stories.tsx +0 -32
  238. package/src/stories/assets/accessibility.png +0 -0
  239. package/src/stories/assets/accessibility.svg +0 -5
  240. package/src/stories/assets/addon-library.png +0 -0
  241. package/src/stories/assets/assets.png +0 -0
  242. package/src/stories/assets/avif-test-image.avif +0 -0
  243. package/src/stories/assets/context.png +0 -0
  244. package/src/stories/assets/discord.svg +0 -15
  245. package/src/stories/assets/docs.png +0 -0
  246. package/src/stories/assets/figma-plugin.png +0 -0
  247. package/src/stories/assets/github.svg +0 -3
  248. package/src/stories/assets/share.png +0 -0
  249. package/src/stories/assets/styling.png +0 -0
  250. package/src/stories/assets/testing.png +0 -0
  251. package/src/stories/assets/theming.png +0 -0
  252. package/src/stories/assets/tutorials.svg +0 -12
  253. package/src/stories/assets/youtube.svg +0 -4
  254. package/src/stories/button.css +0 -30
  255. package/src/stories/header.css +0 -32
  256. package/src/stories/lost.js +0 -54
  257. package/src/stories/page.css +0 -69
  258. package/src/stories/siaDummyData.js +0 -263
  259. package/src/stories/store.js +0 -18
  260. package/src/test.js +0 -7
  261. package/src/types/annoStatus.js +0 -4
  262. package/src/types/canvasActions.js +0 -58
  263. package/src/types/cursorstyles.js +0 -3
  264. package/src/types/modes.js +0 -9
  265. package/src/types/notificationType.js +0 -11
  266. package/src/types/toolbarEvents.js +0 -35
  267. package/src/types/tools.js +0 -17
  268. package/src/types.tsx +0 -11
  269. package/src/utils/annoConversion.js +0 -145
  270. package/src/utils/annoConversion2.ts +0 -145
  271. package/src/utils/colorlut.js +0 -68
  272. package/src/utils/constraints.js +0 -81
  273. package/src/utils/index.js +0 -1
  274. package/src/utils/keyActions.js +0 -113
  275. package/src/utils/mouse.js +0 -14
  276. package/src/utils/mouse2.ts +0 -35
  277. package/src/utils/transform.js +0 -336
  278. package/src/utils/transform2.ts +0 -343
  279. package/src/utils/windowViewport.js +0 -34
  280. package/src/utils/windowViewport2.ts +0 -50
package/src/Canvas.jsx DELETED
@@ -1,2194 +0,0 @@
1
- import React, { Component } from "react";
2
- import { uniqueId } from "lodash-es";
3
- import Annotation from "./Annotation/Annotation";
4
- import AnnoLabelInput from "./AnnoLabelInput";
5
- import AnnoToolBar from "./AnnoToolBar";
6
- import ImgBar from "./ImgBar";
7
- import LabelInput from "./LabelInput";
8
- import Prompt from "./Prompt";
9
-
10
- import { Button, Dimmer, Header, Icon, Loader } from "semantic-ui-react";
11
- import * as annoStatus from "./types/annoStatus";
12
- import * as canvasActions from "./types/canvasActions";
13
- import * as modes from "./types/modes";
14
- import * as notificationType from "./types/notificationType";
15
- import * as TOOLS from "./types/tools";
16
- import * as annoConversion from "./utils/annoConversion";
17
- import * as colorlut from "./utils/colorlut";
18
- import UndoRedo from "./utils/hist";
19
- import KeyMapper, * as keyActions from "./utils/keyActions";
20
- import * as mouse from "./utils/mouse";
21
- import * as transformAnnos from "./utils/transform";
22
- import * as wv from "./utils/windowViewport";
23
-
24
- import InfoBoxes from "./InfoBoxes/InfoBoxArea";
25
- import "./SIA.scss";
26
-
27
- /**
28
- * SIA Canvas element that handles annotations within an image
29
- *
30
- * @param {React.Ref} container - A react ref to a div that defines the
31
- * space where this Canvas lives in.
32
- * @param {object} annos - A json object containing all annotation
33
- * information for an image
34
- * {
35
- * image : {
36
- * id: int,
37
- * number: int,
38
- * amount: int,
39
- * isFirst: bool,
40
- * isLast: bool,
41
- * description: string, // -> optional
42
- * imgActions: list of string, // -> optional
43
- * },
44
- * annotations: {
45
- * bBoxes: [{
46
- * id: int, // -> Not required if status === annoStatus.NEW
47
- * data: {},
48
- * labelIds: list of int, // -> optional
49
- * status: see annoStatus, // -> optional
50
- * annoTime: float, // -> optional
51
- * },...],
52
- * points: []
53
- * lines: []
54
- * polygons: []
55
- * }
56
- * }
57
- * @param {object} annoSaveResponse - Backend response when updating an annotation in backend
58
- * {
59
- * tempId: int or str, // temporal frontend Id
60
- * dbId: int, // Id from backend
61
- * newStatus: str // new Status for the annotation
62
- * }
63
- * @param {object} possibleLabels - Possible labels that can be assigned to
64
- * an annotation.
65
- * {
66
- * id: int,
67
- * description: str,
68
- * label: str, (name of the label)
69
- * color: str (color is optional)
70
- * }
71
- * @param {blob} imageBlob - The actual image blob that will be displayed
72
- * @param {object} uiConfig - User interface configs
73
- * {
74
- * nodesRadius: int, strokeWidth: int,
75
- * layoutOffset: {left:int, top:int, right:int, bottom:int}, -> Offset of the canvas inside the container
76
- * imgBarVisible: bool,
77
- * imgLabelInputVisible: bool,
78
- * centerCanvasInContainer: bool, -> Center the canvas in the middle of the container.
79
- * maxCanvas: bool -> Maximize Canvas Size. Do not fit canvas to image size.
80
- * }
81
- * @param {int} layoutUpdate - A counter that triggers a layout update
82
- * everytime it is incremented.
83
- * @param {string} selectedTool - The tool that is selected to draw an
84
- * annotation. Possible choices are: 'bBox', 'point', 'line', 'polygon'
85
- * @param {object} canvasConfig - Configuration for this canvas
86
- * {
87
- * annos:{
88
- * tools: {
89
- * point: bool,
90
- * line: bool,
91
- * polygon: bool,
92
- * bbox: bool
93
- * },
94
- * multilabels: bool,
95
- * actions: {
96
- * draw: bool,
97
- * label: bool,
98
- * edit: bool,
99
- * },
100
- * maxAnnos: null or int,
101
- * minArea: int
102
- * },
103
- * img: {
104
- * multilabels: bool,
105
- * actions: {
106
- * label: bool,
107
- * }
108
- * },
109
- * allowedToMarkExample: bool, -> Indicates wether the current user is allowed to mark an annotation as example.
110
- * }
111
- * @param {str or int} defaultLabel (optional) - Name or ID of the default label that is used
112
- * when no label was selected by the annotator. If not set "no label" will be used.
113
- * If ID is used, it needs to be one of the possible label ids.
114
- * @param {bool} blocked Block canvas view with loading dimmer.
115
- * @param {bool} preventScrolling Prevent scrolling on mouseEnter
116
- * @param {bool} lockedAnnos A list of AnnoIds of annos that should only be displayed.
117
- * Such annos can not be edited in any way.
118
- * @event onAnnoSaveEvent - Callback with update information for a single
119
- * annotation or the current image that can be used for backend updates
120
- * args: {
121
- * action: the action that was performed in frontend,
122
- * anno: anno information,
123
- * img: image information
124
- * }
125
- * @event onNotification - Callback for Notification messages
126
- * args: {title: str, message: str, type: str}
127
- * @event onKeyDown - Fires for keyDown on canvas
128
- * @event onKeyUp - Fires for keyUp on canvas
129
- * @event onAnnoEvent - Fires when an anno performed an action
130
- * args: {anno: annoObject, newAnnos: list of annoObjects, pAction: str}
131
- * @event onCanvasEvent - Fires on canvas event
132
- * args: {action: action, data: dataObject}
133
- * action -> CANVAS_SVG_UPDATE
134
- * data: {width: int, height: int, scale: float, translateX: float,
135
- * translateY:float}
136
- * action -> CANVAS_UI_CONFIG_UPDATE
137
- * action -> CANVAS_LABEL_INPUT_CLOSE
138
- * action -> CANVAS_IMG_LOADED
139
- * action -> CANVAS_IMGBAR_CLOSE
140
- * @event onImgBarClose - Fires when close button on ImgBar was hit.
141
- * @event onGetFunction - Get special canvas functions for manipulation from outside canvas
142
- * deleteAllAnnos()
143
- * unloadImage()
144
- * resetZoom()
145
- * getAnnos(annos,removeFrontendIds)
146
- */
147
- class Canvas extends Component {
148
- constructor(props) {
149
- super(props);
150
- this.state = {
151
- svg: {
152
- width: "100%",
153
- height: "100%",
154
- scale: 1.0,
155
- translateX: 0,
156
- translateY: 0,
157
- },
158
- image: {
159
- width: undefined,
160
- height: undefined,
161
- },
162
- imageOffset: {
163
- x: 0,
164
- y: 0,
165
- },
166
- annos: [],
167
- mode: modes.VIEW,
168
- selectedAnnoId: undefined,
169
- showSingleAnno: undefined,
170
- showLabelInput: false,
171
- imageLoaded: false,
172
- imgLoadCount: 0,
173
- imgLabelIds: [],
174
- imgLabelChanged: false,
175
- imgAnnoTime: 0,
176
- imgLoadTimestamp: 0,
177
- performedImageInit: false,
178
- prevLabel: [],
179
- imageBlob: undefined,
180
- isJunk: props.isJunk,
181
- imgBarVisible: false,
182
- annoToolBarVisible: false,
183
- possibleLabels: undefined,
184
- annoCommentInputTrigger: 0,
185
- imgActions: [],
186
- isDrawingSamBBox: false, // Flag to indicate if a SAM bbox is currently being drawn,
187
- samBBoxStartPoint: null,
188
- };
189
- this.img = React.createRef();
190
- this.svg = React.createRef();
191
- this.container = React.createRef();
192
- this.hist = new UndoRedo();
193
- this.keyMapper = new KeyMapper((keyAction) =>
194
- this.handleKeyAction(keyAction),
195
- );
196
- this.mousePosAbs = undefined;
197
- this.clipboard = undefined;
198
- this.delayedBackendUpdates = {};
199
- this.tempIdMap = {};
200
- }
201
-
202
- componentDidMount() {
203
- this.updatePossibleLabels();
204
- if (Number.isInteger(this.props.defaultLabel)) {
205
- this.setState({ prevLabel: [this.props.defaultLabel] });
206
- }
207
- if (this.props.onGetFunction) {
208
- this.props.onGetFunction({
209
- deleteAllAnnos: () => this.deleteAllAnnos(),
210
- unloadImage: () => this.unloadImage(),
211
- resetZoom: () => this.resetZoom(),
212
- getAnnos: (annos, removeFrontendIds) =>
213
- this.getAnnos(annos, removeFrontendIds),
214
- });
215
- }
216
- }
217
-
218
- componentDidUpdate(prevProps, prevState) {
219
- if (prevProps.annoSaveResponse !== this.props.annoSaveResponse) {
220
- this.updateAnnoBySaveResponse(this.props.annoSaveResponse);
221
- }
222
- if (prevProps.imageMeta !== this.props.imageMeta) {
223
- if (this.props.imageMeta) {
224
- this.setState({
225
- imgLabelIds: this.props.imageMeta.labelIds,
226
- imgAnnoTime: this.props.imageMeta.annoTime,
227
- imgActions: this.props.imageMeta.imgActions
228
- ? this.props.imageMeta.imgActions
229
- : [],
230
- imgLoadTimestamp: performance.now(),
231
- });
232
- }
233
- }
234
- if (prevProps.annos !== this.props.annos) {
235
- if (this.state.imageBlob) {
236
- this.updateCanvasView(annoConversion.fixBackendAnnos(this.props.annos));
237
- }
238
- }
239
- if (prevProps.isJunk !== this.props.isJunk) {
240
- if (this.state.isJunk !== this.props.isJunk) {
241
- this.setState({
242
- isJunk: this.props.isJunk,
243
- });
244
-
245
- // do not save junk changes when image is currently changing (comparing junk state of previous and next image)
246
- if (this.state.imageLoaded && !this.props.isImageChanging) {
247
- this.handleAnnoSaveEvent(canvasActions.IMG_JUNK_UPDATE, undefined, {
248
- isJunk: this.props.isJunk,
249
- });
250
- }
251
- }
252
- }
253
- if (this.state.imageBlob !== this.props.imageBlob) {
254
- this.setState({ imageBlob: this.props.imageBlob });
255
- }
256
- if (this.props.possibleLabels !== prevProps.possibleLabels) {
257
- this.updatePossibleLabels();
258
- }
259
- if (this.state.performedImageInit) {
260
- // Initialize canvas history
261
- this.setState({
262
- performedImageInit: false,
263
- annoToolBarVisible: false,
264
- });
265
- if (this.props.uiConfig.imgBarVisible) {
266
- this.setState({ imgBarVisible: true });
267
- }
268
- this.hist.clearHist();
269
- this.hist.push(
270
- {
271
- ...this.getAnnos(),
272
- selectedAnnoId: undefined,
273
- },
274
- "init",
275
- );
276
- }
277
- if (this.state.imageLoaded) {
278
- // Selected annotation should be on top
279
- this.putSelectedOnTop(prevState);
280
- if (prevState.imgLoadCount !== this.state.imgLoadCount) {
281
- this.updateCanvasView(annoConversion.fixBackendAnnos(this.props.annos));
282
- if (this.props.imageMeta) {
283
- this.setImageLabels(this.props.imageMeta.labelIds);
284
- this.setState({
285
- performedImageInit: true,
286
- });
287
- }
288
- }
289
- if (prevProps.layoutUpdate !== this.props.layoutUpdate) {
290
- this.selectAnnotation(undefined);
291
- this.updateCanvasView(
292
- annoConversion.canvasToBackendAnnos(
293
- this.state.annos,
294
- this.state.svg,
295
- false,
296
- this.state.imageOffset,
297
- ),
298
- );
299
- }
300
- }
301
- }
302
-
303
- onImageLoad() {
304
- this.setState({
305
- imageLoaded: true,
306
- imgLoadCount: this.state.imgLoadCount + 1,
307
- showLabelInput: false,
308
- showSingleAnno: undefined,
309
- selectedAnnoId: undefined,
310
- });
311
- this.triggerCanvasEvent(canvasActions.CANVAS_IMG_LOADED);
312
- }
313
-
314
- onMouseOver() {
315
- this.svg.current.focus();
316
- //Prevent scrolling on svg
317
- if (this.props.preventScrolling) {
318
- document.body.style.overflow = "hidden";
319
- }
320
- }
321
-
322
- onMouseLeave() {
323
- if (this.props.preventScrolling) {
324
- document.body.style.overflow = "";
325
- }
326
- }
327
-
328
- onWheel(e) {
329
- // Zoom implementation. Note that svg is first scaled and
330
- // second translated!
331
- const up = e.deltaY < 0;
332
- const mousePos = this.getMousePosition(e);
333
- const zoomFactor = 1.25;
334
- let nextScale;
335
- if (up) {
336
- nextScale = this.state.svg.scale * zoomFactor;
337
- } else {
338
- nextScale = this.state.svg.scale / zoomFactor;
339
- }
340
- let newTranslation;
341
- //Constrain zoom
342
- if (nextScale < 1.0) {
343
- nextScale = 1.0;
344
- newTranslation = { x: 0, y: 0 };
345
- } else if (nextScale > 200.0) {
346
- nextScale = 200.0;
347
- newTranslation = wv.getZoomTranslation(
348
- mousePos,
349
- this.state.svg,
350
- nextScale,
351
- );
352
- } else {
353
- newTranslation = wv.getZoomTranslation(
354
- mousePos,
355
- this.state.svg,
356
- nextScale,
357
- );
358
- }
359
- this.setState({
360
- svg: {
361
- ...this.state.svg,
362
- scale: nextScale,
363
- // translateX: -1*(mousePos.x * nextScale - mousePos.x)/nextScale,
364
- // translateY: -1*(mousePos.y * nextScale - mousePos.y)/nextScale
365
- translateX: newTranslation.x,
366
- translateY: newTranslation.y,
367
- },
368
- });
369
- return false;
370
- }
371
-
372
- onRightClick(e) {
373
- e.preventDefault();
374
- }
375
-
376
- onMouseDown(e) {
377
- if (e.button === 0) {
378
- this.selectAnnotation(undefined);
379
- } else if (e.button === 1) {
380
- this.setMode(modes.CAMERA_MOVE);
381
- } else if (e.button === 2) {
382
- if (
383
- this.props.selectedTool === TOOLS.POSITIVE_POINT ||
384
- this.props.selectedTool === TOOLS.NEGATIVE_POINT ||
385
- this.props.selectedTool === TOOLS.SAM_BBOX
386
- ) {
387
- return; // do nothing for sam tools
388
- }
389
- //Create annotation on right click
390
- this.createNewAnnotation(e);
391
- }
392
- }
393
-
394
- onAnnoMouseDown(e) {
395
- if (e.button === 1) {
396
- // this.collectAnnos()
397
- this.setMode(modes.CAMERA_MOVE);
398
- } else if (e.button === 2) {
399
- if (
400
- this.props.selectedTool === TOOLS.POSITIVE_POINT ||
401
- this.props.selectedTool === TOOLS.NEGATIVE_POINT ||
402
- this.props.selectedTool === TOOLS.SAM_BBOX
403
- ) {
404
- return; // Note: do nothing for sam tools
405
- }
406
- //Create annotation on right click
407
- this.createNewAnnotation(e);
408
- } else if (e.button === 0) {
409
- if (this.state.showLabelInput) {
410
- const anno = this.findAnno(this.state.selectedAnnoId);
411
- this.updateSelectedAnno(anno, modes.VIEW);
412
- this.showSingleAnno(undefined);
413
- this.showLabelInput(false);
414
- }
415
- }
416
- }
417
-
418
- onMouseUp(e) {
419
- switch (e.button) {
420
- case 1:
421
- this.setMode(modes.VIEW);
422
- break;
423
- default:
424
- break;
425
- }
426
- }
427
-
428
- updateAnnoComment(comment) {
429
- const anno = this.findAnno(this.state.selectedAnnoId);
430
- anno.comment = comment;
431
- this.handleAnnoEvent(anno, canvasActions.ANNO_COMMENT_UPDATE);
432
- }
433
-
434
- handleKeyAction(action) {
435
- const anno = this.findAnno(this.state.selectedAnnoId);
436
- const camKeyStepSize = 20 * this.state.svg.scale;
437
- console.log("handleKeyAction", action, anno);
438
-
439
- switch (action) {
440
- case keyActions.EDIT_LABEL:
441
- // Need to get the newest version of annotation data directly
442
- // from annotation object, when editing label/ hitting enter
443
- // in create mode, since annotation data in canvas are not updated
444
- // to this point in time.
445
- const ar = this.findAnnoRef(this.state.selectedAnnoId);
446
- let myAnno = undefined;
447
- if (ar !== undefined) {
448
- myAnno = ar.current.myAnno.current.getResult();
449
- }
450
- this.editAnnoLabel(myAnno);
451
- break;
452
- case keyActions.DELETE_ANNO:
453
- this.deleteAnnotation(anno);
454
- break;
455
- case keyActions.TOGGLE_ANNO_COMMENT_INPUT:
456
- if (this.state.selectedAnnoId) {
457
- this.setState({
458
- annoCommentInputTrigger: this.state.annoCommentInputTrigger + 1,
459
- });
460
- }
461
- break;
462
- case keyActions.DELETE_ANNO_IN_CREATION:
463
- this.deleteAnnoInCreationMode(anno);
464
- break;
465
- case keyActions.ENTER_ANNO_ADD_MODE:
466
- if (anno) {
467
- this.updateSelectedAnno(anno, modes.ADD);
468
- }
469
- break;
470
- case keyActions.LEAVE_ANNO_ADD_MODE:
471
- if (anno) {
472
- this.updateSelectedAnno(anno, modes.VIEW);
473
- }
474
- break;
475
- case keyActions.UNDO:
476
- this.undo();
477
- break;
478
- case keyActions.REDO:
479
- this.redo();
480
- break;
481
- case keyActions.TRAVERSE_ANNOS:
482
- this.traverseAnnos();
483
- break;
484
- case keyActions.CAM_MOVE_LEFT:
485
- this.moveCamera(camKeyStepSize, 0);
486
- break;
487
- case keyActions.CAM_MOVE_RIGHT:
488
- this.moveCamera(-camKeyStepSize, 0);
489
- break;
490
- case keyActions.CAM_MOVE_UP:
491
- this.moveCamera(0, camKeyStepSize);
492
- break;
493
- case keyActions.CAM_MOVE_DOWN:
494
- this.moveCamera(0, -camKeyStepSize);
495
- break;
496
- case keyActions.CAM_MOVE_STOP:
497
- break;
498
- case keyActions.COPY_ANNOTATION:
499
- this.copyAnnotation();
500
- break;
501
- case keyActions.PASTE_ANNOTATION:
502
- this.pasteAnnotation(0);
503
- break;
504
- case keyActions.RECREATE_ANNO:
505
- // recreate selected annotation using the anno id
506
- if (this.state.selectedAnnoId)
507
- this.recreateAnnotation(this.state.selectedAnnoId);
508
- break;
509
- default:
510
- // console.warn("Unknown key action", action);
511
- }
512
- }
513
-
514
- onKeyDown(e) {
515
- e.preventDefault();
516
- this.keyMapper.keyDown(e.key);
517
- if (this.props.onKeyDown) {
518
- this.props.onKeyDown(e);
519
- }
520
- }
521
-
522
- onKeyUp(e) {
523
- e.preventDefault();
524
- this.keyMapper.keyUp(e.key);
525
- if (this.props.onKeyUp) {
526
- this.props.onKeyUp(e);
527
- }
528
- }
529
-
530
- onMouseMove(e) {
531
- if (this.state.mode === modes.CAMERA_MOVE) {
532
- this.moveCamera(e.movementX, e.movementY);
533
- }
534
- }
535
-
536
- onLabelInputDeleteClick(annoId) {
537
- this.removeSelectedAnno();
538
- }
539
-
540
- /**
541
- * Trigger canvas event
542
- * @param {String} action Action that was performed
543
- * @param {Object} data Data object of the action
544
- */
545
- triggerCanvasEvent(action, data) {
546
- if (this.props.onCanvasEvent) {
547
- this.props.onCanvasEvent(action, data);
548
- }
549
- }
550
-
551
- checkAndCorrectAnno(anno) {
552
- // Check if annoation is within image bounds
553
- const corrected = transformAnnos.correctAnnotation(
554
- anno.data,
555
- this.state.svg,
556
- this.state.imageOffset,
557
- );
558
- let newAnno = { ...anno, data: corrected };
559
- const area = transformAnnos.getArea(
560
- corrected,
561
- this.state.svg,
562
- anno.type,
563
- this.state.image,
564
- );
565
- if (area !== undefined) {
566
- if (area < this.props.canvasConfig.annos.minArea) {
567
- this.handleNotification({
568
- title: "Annotation to small",
569
- message:
570
- "Annotation area was " +
571
- Math.round(area) +
572
- "px but needs to be bigger than " +
573
- this.props.canvasConfig.annos.minArea +
574
- " px",
575
- type: notificationType.WARNING,
576
- });
577
- // newAnno = {...newAnno, mode: modes.DELETED}
578
- newAnno = { ...newAnno, mode: modes.DELETED };
579
- }
580
- }
581
- if (!this.checkAnnoLength(anno)) {
582
- newAnno = { ...newAnno, mode: modes.DELETED };
583
- }
584
- return newAnno;
585
- }
586
-
587
- /**
588
- * Handle actions that have been performed by an annotation
589
- * @param {Number} anno Id of the annotation
590
- * @param {String} pAction Action that was performed
591
- */
592
- handleAnnoEvent(anno, pAction) {
593
- // console.log("handleAnnoEvent", pAction, anno);
594
- let newAnnos = undefined;
595
- let actionHistoryStore = undefined;
596
- console.log("canvasActions: ", anno, pAction);
597
-
598
- switch (pAction) {
599
- case canvasActions.ANNO_ENTER_CREATE_MODE:
600
- break;
601
- case canvasActions.ANNO_MARK_EXAMPLE:
602
- newAnnos = this.updateSelectedAnno(anno, modes.VIEW);
603
- this.pushHist(newAnnos, anno.id, pAction, this.state.showSingleAnno);
604
- this.handleAnnoSaveEvent(pAction, anno);
605
- break;
606
- case canvasActions.ANNO_SELECTED:
607
- this.selectAnnotation(anno.id);
608
- // this.pushHist(
609
- // this.state.annos, anno.id,
610
- // pAction, this.state.showSingleAnno
611
- // )
612
- break;
613
- case canvasActions.ANNO_START_CREATING:
614
- newAnnos = this.updateSelectedAnno(anno);
615
- this.pushHist(newAnnos, anno.id, pAction, this.state.showSingleAnno);
616
- break;
617
- case canvasActions.ANNO_CREATED:
618
- actionHistoryStore = [...this.state.imgActions, pAction];
619
- anno = this.stopAnnotimeMeasure(anno);
620
- newAnnos = this.updateSelectedAnno(
621
- { ...anno, status: annoStatus.DATABASE },
622
- modes.VIEW,
623
- );
624
- this.pushHist(newAnnos, anno.id, pAction, undefined);
625
- this.showSingleAnno(undefined);
626
- this.setState({ annoToolBarVisible: true });
627
- this.handleAnnoSaveEvent(pAction, anno, {
628
- imgActions: actionHistoryStore,
629
- });
630
- break;
631
- case canvasActions.ANNO_MOVED:
632
- actionHistoryStore = [...this.state.imgActions, pAction];
633
- anno = this.stopAnnotimeMeasure(anno);
634
- newAnnos = this.updateSelectedAnno(anno, modes.VIEW);
635
- this.showSingleAnno(undefined);
636
- this.pushHist(newAnnos, anno.id, pAction, undefined);
637
- this.setState({ annoToolBarVisible: true });
638
- this.handleAnnoSaveEvent(pAction, anno, {
639
- imgActions: actionHistoryStore,
640
- });
641
- break;
642
- case canvasActions.ANNO_ENTER_MOVE_MODE:
643
- anno = this.startAnnotimeMeasure(anno);
644
- this.updateSelectedAnno(anno, modes.MOVE);
645
- this.showSingleAnno(anno.id);
646
- this.setState({ annoToolBarVisible: false });
647
- break;
648
- case canvasActions.ANNO_ENTER_EDIT_MODE:
649
- anno = this.startAnnotimeMeasure(anno);
650
- this.updateSelectedAnno(anno, modes.EDIT);
651
- // this.showSingleAnno(anno.id)
652
- this.setState({ annoToolBarVisible: false });
653
- break;
654
- case canvasActions.ANNO_ADDED_NODE:
655
- actionHistoryStore = [...this.state.imgActions, pAction];
656
- newAnnos = this.updateSelectedAnno(anno, modes.ADD);
657
- this.pushHist(newAnnos, anno.id, pAction, this.state.showSingleAnno);
658
- this.handleAnnoSaveEvent(pAction, anno, {
659
- imgActions: actionHistoryStore,
660
- });
661
- break;
662
- case canvasActions.ANNO_REMOVED_NODE:
663
- actionHistoryStore = [...this.state.imgActions, pAction];
664
- if (!this.checkAnnoLength(anno)) {
665
- newAnnos = this.updateSelectedAnno(anno, modes.DELETED);
666
- this.showSingleAnno(undefined);
667
- } else {
668
- newAnnos = this.updateSelectedAnno(anno, modes.CREATE);
669
- }
670
- this.pushHist(newAnnos, anno.id, pAction, this.state.showSingleAnno);
671
- if (anno.status !== annoStatus.NEW) {
672
- this.handleAnnoSaveEvent(pAction, anno, {
673
- imgActions: actionHistoryStore,
674
- });
675
- }
676
- break;
677
- case canvasActions.ANNO_REMOVED_SPECIFIC_NODE:
678
- actionHistoryStore = [...this.state.imgActions, pAction];
679
- if (!this.checkAnnoLength(anno)) {
680
- newAnnos = this.updateSelectedAnno(anno, modes.DELETED);
681
- this.showSingleAnno(undefined);
682
- } else {
683
- newAnnos = this.updateSelectedAnno(anno, modes.ADD);
684
- }
685
- this.pushHist(newAnnos, anno.id, pAction, this.state.showSingleAnno);
686
- if (anno.status !== annoStatus.NEW) {
687
- this.handleAnnoSaveEvent(pAction, anno, {
688
- imgActions: actionHistoryStore,
689
- });
690
- }
691
- break;
692
- case canvasActions.ANNO_EDITED:
693
- actionHistoryStore = [...this.state.imgActions, pAction];
694
- anno = this.stopAnnotimeMeasure(anno);
695
- newAnnos = this.updateSelectedAnno(anno, modes.VIEW);
696
- this.pushHist(newAnnos, anno.id, pAction, this.state.showSingleAnno);
697
- this.setState({ annoToolBarVisible: true });
698
- this.handleAnnoSaveEvent(pAction, anno, {
699
- imgActions: actionHistoryStore,
700
- });
701
- break;
702
- case canvasActions.ANNO_DELETED:
703
- actionHistoryStore = [...this.state.imgActions, pAction];
704
- const res = this.updateSelectedAnno(anno, modes.DELETED, true);
705
- newAnnos = res.newAnnos;
706
- this.selectAnnotation(undefined);
707
- this.showSingleAnno(undefined);
708
- this.pushHist(newAnnos, undefined, pAction, this.state.showSingleAnno);
709
- this.handleAnnoSaveEvent(pAction, res.newAnno, {
710
- imgActions: actionHistoryStore,
711
- });
712
- break;
713
- case canvasActions.ANNO_COMMENT_UPDATE:
714
- actionHistoryStore = [...this.state.imgActions, pAction];
715
- const res_comment = this.updateSelectedAnno(anno, modes.VIEW, true);
716
- newAnnos = res_comment.newAnnos;
717
- this.pushHist(newAnnos, anno.id, pAction, this.state.showSingleAnno);
718
- this.handleNotification({
719
- title: "Saved comment",
720
- message: `Saved comment: ${anno.comment}`,
721
- type: notificationType.SUCCESS,
722
- });
723
- this.handleAnnoSaveEvent(pAction, res_comment.newAnno, {
724
- imgActions: actionHistoryStore,
725
- });
726
- break;
727
- case canvasActions.ANNO_LABEL_UPDATE:
728
- actionHistoryStore = [...this.state.imgActions, pAction];
729
- anno = this.stopAnnotimeMeasure(anno);
730
- anno = this.checkAndCorrectAnno(anno);
731
- // console.log("ANNO_LABEL_UPDATE aftercheckAndCorrect", anno);
732
- // this.updateSelectedAnno(anno, anno.mode)
733
- if (anno.mode === modes.DELETED) {
734
- this.updateSelectedAnno(anno, modes.DELETED);
735
- } else {
736
- this.updateSelectedAnno(
737
- { ...anno, status: annoStatus.DATABASE },
738
- modes.VIEW,
739
- );
740
- }
741
- // if (!this.checkAnnoLength(anno)){
742
- // newAnnos = this.updateSelectedAnno(anno, modes.DELETED)
743
- // } else {
744
- // newAnnos = this.updateSelectedAnno(anno, modes.VIEW)
745
- // }
746
- this.setState({ annoToolBarVisible: true });
747
- if (anno.mode !== modes.DELETED) {
748
- this.pushHist(newAnnos, anno.id, pAction, undefined);
749
- this.handleAnnoSaveEvent(pAction, anno, {
750
- imgActions: actionHistoryStore,
751
- });
752
- }
753
- break;
754
- case canvasActions.ANNO_CREATED_NODE:
755
- actionHistoryStore = [...this.state.imgActions, pAction];
756
- anno = this.stopAnnotimeMeasure(anno);
757
- newAnnos = this.updateSelectedAnno(anno, modes.CREATE);
758
- this.pushHist(newAnnos, anno.id, pAction, this.state.showSingleAnno);
759
- break;
760
- case canvasActions.ANNO_CREATED_FINAL_NODE:
761
- actionHistoryStore = [...this.state.imgActions, pAction];
762
- anno = this.stopAnnotimeMeasure(anno);
763
- newAnnos = this.updateSelectedAnno(
764
- { ...anno, status: annoStatus.DATABASE },
765
- modes.VIEW,
766
- );
767
- this.pushHist(newAnnos, anno.id, pAction, undefined);
768
- this.showSingleAnno(undefined);
769
- this.setState({ annoToolBarVisible: true });
770
- this.handleAnnoSaveEvent(pAction, anno, {
771
- imgActions: actionHistoryStore,
772
- });
773
- break;
774
- default:
775
- // console.warn("Action not handled", pAction);
776
- break;
777
- }
778
- if (actionHistoryStore) {
779
- this.setState({ imgActions: actionHistoryStore });
780
- }
781
- if (this.props.onAnnoEvent) {
782
- this.props.onAnnoEvent(anno, newAnnos, pAction);
783
- }
784
- }
785
-
786
- handleAnnoSaveEvent(action, anno, img) {
787
- const imgData = {
788
- ...img,
789
- imgId: this.props.imageMeta.id,
790
- annoTime:
791
- this.props.imageMeta.annoTime +
792
- (performance.now() - this.state.imgLoadTimestamp) / 1000,
793
- };
794
- let backendAnno = undefined;
795
- if (anno) {
796
- let myAnno = this.addDelayedBackendUpdate(anno, action);
797
- if (!myAnno) return;
798
- if (myAnno.id in this.tempIdMap) {
799
- myAnno = { ...myAnno, id: this.tempIdMap[myAnno.id] };
800
- }
801
- backendAnno = annoConversion.canvasToBackendSingleAnno(
802
- myAnno,
803
- this.state.svg,
804
- false,
805
- this.state.imageOffset,
806
- );
807
- }
808
- const saveData = {
809
- anno: backendAnno,
810
- img: imgData,
811
- action,
812
- };
813
- if (this.props.onAnnoSaveEvent) {
814
- this.props.onAnnoSaveEvent(saveData);
815
- }
816
- }
817
-
818
- onAnnoLabelInputUpdate(anno) {
819
- this.updateSelectedAnno(anno);
820
- }
821
-
822
- onAnnoLabelInputClose() {
823
- this.svg.current.focus();
824
- this.showLabelInput(false);
825
- this.showSingleAnno(undefined);
826
- const anno = this.findAnno(this.state.selectedAnnoId);
827
- this.handleAnnoEvent(anno, canvasActions.ANNO_LABEL_UPDATE);
828
- }
829
-
830
- handleImgBarClose() {
831
- this.triggerCanvasEvent(canvasActions.CANVAS_IMGBAR_CLOSE);
832
- }
833
-
834
- gotNewLabel(label) {
835
- let ret = false;
836
- if (label.length === 0) {
837
- if (this.state.imgLabelIds.length !== 0) {
838
- return true;
839
- } else {
840
- return false;
841
- }
842
- }
843
- label.forEach((e) => {
844
- if (!this.state.imgLabelIds.includes(e)) ret = true;
845
- });
846
- return ret;
847
- }
848
-
849
- handleImgLabelUpdate(label) {
850
- if (this.gotNewLabel(label)) {
851
- const imgActions = [
852
- ...this.state.imgActions,
853
- canvasActions.IMG_LABEL_UPDATE,
854
- ];
855
- // console.log("gotNewLabel", label);
856
- this.setState({
857
- imgLabelIds: label,
858
- imgLabelChanged: true,
859
- imgActions: imgActions,
860
- });
861
- this.pushHist(
862
- this.state.annos,
863
- this.state.selectedAnnoId,
864
- canvasActions.IMG_LABEL_UPDATE,
865
- this.state.showSingleAnno,
866
- label,
867
- );
868
- const imgData = {
869
- imgLabelIds: label,
870
- imgLabelChanged: true,
871
- imgActions: imgActions,
872
- };
873
- this.handleAnnoSaveEvent(
874
- canvasActions.IMG_LABEL_UPDATE,
875
- undefined,
876
- imgData,
877
- );
878
- }
879
- }
880
-
881
- handleCanvasClick(e) {
882
- if (this.props.uiConfig.imgBarVisible) {
883
- this.setState({ imgBarVisible: true });
884
- }
885
- }
886
-
887
- handleContextMenu(e) {
888
- e.preventDefault();
889
- if (
890
- this.props.selectedTool === TOOLS.POSITIVE_POINT ||
891
- this.props.selectedTool === TOOLS.NEGATIVE_POINT
892
- ) {
893
- const { x, y } = this.getMousePosition(e);
894
-
895
- const imgWidth = this.state.svg.width - 2 * this.state.imageOffset.x;
896
- const imgHeight = this.state.svg.height - 2 * this.state.imageOffset.y;
897
-
898
- const normX = (x - this.state.imageOffset.x) / imgWidth;
899
- const normY = (y - this.state.imageOffset.y) / imgHeight;
900
-
901
- const type =
902
- this.props.selectedTool === TOOLS.POSITIVE_POINT
903
- ? "positive"
904
- : "negative";
905
-
906
- this.props.onSamPointClick(x, y, normX, normY, type);
907
- }
908
- }
909
-
910
- handleMouseDown(e) {
911
- if (this.props.selectedTool === TOOLS.SAM_BBOX) {
912
- if (e.button !== 2) {
913
- return;
914
- }
915
-
916
- e.preventDefault();
917
- const { x, y } = this.getMousePosition(e);
918
-
919
- this.setState({
920
- isDrawingSamBBox: true,
921
- samBBoxStartPoint: { x, y },
922
- });
923
-
924
- // start a new rectangle
925
- this.props.onUpdateSamBBox({
926
- x,
927
- y,
928
- width: 0,
929
- height: 0,
930
- });
931
- }
932
- }
933
-
934
- handleImgBarMouseEnter(e) {
935
- this.setState({ imgBarVisible: false });
936
- }
937
-
938
- handleImgLabelInputClose() {
939
- this.triggerCanvasEvent(canvasActions.CANVAS_LABEL_INPUT_CLOSE);
940
- }
941
-
942
- handleSvgMouseMove(e) {
943
- this.mousePosAbs = mouse.getMousePositionAbs(e, this.state.svg);
944
-
945
- if (this.props.selectedTool === TOOLS.SAM_BBOX) {
946
- if (!this.state.isDrawingSamBBox || !this.state.samBBoxStartPoint) {
947
- // If not drawing a SAM bbox, do nothing
948
- return;
949
- }
950
-
951
- const { x, y } = this.getMousePosition(e);
952
- const startPoint = this.state.samBBoxStartPoint;
953
-
954
- const imgWidth = this.state.svg.width - 2 * this.state.imageOffset.x;
955
- const imgHeight = this.state.svg.height - 2 * this.state.imageOffset.y;
956
-
957
- const xMin = Math.min(startPoint.x, x);
958
- const yMin = Math.min(startPoint.y, y);
959
- const width = Math.abs(x - startPoint.x);
960
- const height = Math.abs(y - startPoint.y);
961
-
962
- // normalized coordinates
963
- const xMinNorm = (xMin - this.state.imageOffset.x) / imgWidth;
964
- const yMinNorm = (yMin - this.state.imageOffset.y) / imgHeight;
965
- const xMaxNorm = (xMin + width - this.state.imageOffset.x) / imgWidth;
966
- const yMaxNorm = (yMin + height - this.state.imageOffset.y) / imgHeight;
967
-
968
- // update the rectangle with the new width and height
969
- this.props.onUpdateSamBBox({
970
- x: xMin,
971
- y: yMin,
972
- width,
973
- height,
974
- xMinNorm,
975
- yMinNorm,
976
- xMaxNorm,
977
- yMaxNorm,
978
- });
979
- }
980
- }
981
-
982
- handleMouseUp(e) {
983
- if (this.props.selectedTool === TOOLS.SAM_BBOX) {
984
- if (e.button !== 2) {
985
- return;
986
- }
987
-
988
- e.preventDefault();
989
- this.setState({
990
- isDrawingSamBBox: false,
991
- });
992
- }
993
- }
994
-
995
- handleMouseLeave(e) {
996
- if (this.props.selectedTool === TOOLS.SAM_BBOX) {
997
- // If mouse leaves the canvas while drawing a SAM bbox, reset the state
998
- this.setState({
999
- isDrawingSamBBox: false,
1000
- });
1001
- }
1002
- }
1003
-
1004
- handleNotification(messageObj) {
1005
- if (this.props.onNotification) {
1006
- this.props.onNotification(messageObj);
1007
- }
1008
- }
1009
-
1010
- handleHideLbl(lbl, hide) {
1011
- let hiddenSelected = false;
1012
- const newAnnos = this.state.annos.map((anno) => {
1013
- const newAnno = { ...anno };
1014
- if (anno.labelIds.includes(lbl.id)) {
1015
- newAnno.visible = !hide;
1016
- if (anno.id === this.state.selectedAnnoId) hiddenSelected = true;
1017
- } else if (anno.labelIds.length === 0) {
1018
- // no label case
1019
- if (lbl.id === -1) {
1020
- // -1 indicates no label
1021
- newAnno.visible = !hide;
1022
- if (anno.id === this.state.selectedAnnoId) hiddenSelected = true;
1023
- }
1024
- }
1025
- return newAnno;
1026
- });
1027
- this.setState({ annos: newAnnos });
1028
- if (hiddenSelected) {
1029
- this.selectAnnotation(undefined);
1030
- }
1031
- }
1032
-
1033
- handleMarkExample(anno) {
1034
- const newAnno = { ...anno };
1035
- if (newAnno.isExample == undefined) {
1036
- newAnno.isExample = true;
1037
- } else if (newAnno.isExample) {
1038
- newAnno.isExample = false;
1039
- } else {
1040
- newAnno.isExample = true;
1041
- }
1042
- this.handleAnnoEvent(newAnno, canvasActions.ANNO_MARK_EXAMPLE);
1043
- }
1044
-
1045
- /*************
1046
- * LOGIC *
1047
- **************/
1048
- copyAnnotation() {
1049
- this.clipboard = this.findAnno(this.state.selectedAnnoId);
1050
- this.handleNotification({
1051
- title: "Copyed annotation to clipboard",
1052
- message: "Copyed " + this.clipboard.type,
1053
- type: notificationType.SUCCESS,
1054
- });
1055
- }
1056
-
1057
- pasteAnnotation(offset = 0) {
1058
- // const corrected = transform.correctAnnotation(anno.data, this.props.svg, this.props.imageOffset)
1059
- if (this.clipboard) {
1060
- // let annos = [...this.state.annos]
1061
- const uid = uniqueId("new");
1062
- // this.handleAnnoEvent()
1063
- const newData = this.clipboard.data.map((e) => {
1064
- return { x: e.x + offset, y: e.y + offset };
1065
- });
1066
- const newAnno = {
1067
- ...this.clipboard,
1068
- id: uid,
1069
- annoTime: 0,
1070
- status: annoStatus.NEW,
1071
- mode: modes.VIEW,
1072
- data: transformAnnos.correctAnnotation(
1073
- newData,
1074
- this.state.svg,
1075
- this.state.imageOffset,
1076
- ),
1077
- };
1078
- // annos.push(newAnno)
1079
- // this.setState({annos: annos, selectedAnnoId: uid})
1080
- this.handleNotification({
1081
- title: "Pasted annotation to canvas",
1082
- message: "Pasted and selected " + this.clipboard.type,
1083
- type: notificationType.SUCCESS,
1084
- });
1085
- this.handleAnnoEvent(newAnno, canvasActions.ANNO_CREATED);
1086
- // this.handleAnnoSaveEvent(canvasActions.ANNO_CREATED, newAnno)
1087
- }
1088
- }
1089
-
1090
- checkAnnoLength(anno) {
1091
- if (anno.type === "polygon" && anno.data.length < 3) {
1092
- this.handleNotification({
1093
- title: "Invalid polygon!",
1094
- message: "A vaild polygon needs at least 3 points!",
1095
- type: notificationType.WARNING,
1096
- });
1097
- return false;
1098
- }
1099
- return true;
1100
- }
1101
-
1102
- startAnnotimeMeasure(anno) {
1103
- anno.timestamp = performance.now();
1104
- return anno;
1105
- }
1106
-
1107
- stopAnnotimeMeasure(anno) {
1108
- if (anno.timestamp === undefined) {
1109
- // console.warn(
1110
- // "No timestamp for annotime measurement. Check if you started measurement",
1111
- // anno,
1112
- // );
1113
- } else {
1114
- let now = performance.now();
1115
- anno.annoTime += (now - anno.timestamp) / 1000;
1116
- anno.timestamp = now;
1117
- return anno;
1118
- }
1119
- return anno;
1120
- }
1121
-
1122
- updatePossibleLabels() {
1123
- if (!this.props.possibleLabels) return;
1124
- if (this.props.possibleLabels.length <= 0) return;
1125
- let lbls = this.props.possibleLabels;
1126
- lbls = lbls.map((e) => {
1127
- if (!("color" in e)) {
1128
- return {
1129
- ...e,
1130
- color: colorlut.getColor(e.id),
1131
- };
1132
- } else {
1133
- return { ...e };
1134
- }
1135
- });
1136
- this.setState({
1137
- possibleLabels: [...lbls],
1138
- });
1139
- }
1140
-
1141
- editAnnoLabel(anno) {
1142
- if (this.state.selectedAnnoId) {
1143
- let myAnno;
1144
- if (anno === undefined) {
1145
- myAnno = this.findAnno(this.state.selectedAnnoId);
1146
- } else {
1147
- myAnno = { ...anno };
1148
- }
1149
- myAnno = this.startAnnotimeMeasure(myAnno);
1150
- this.showLabelInput();
1151
- this.updateSelectedAnno(myAnno, modes.EDIT_LABEL);
1152
- }
1153
- }
1154
- unloadImage() {
1155
- // console.log("unloadImage", this.state, this.props.imageMeta);
1156
- if (this.state.imageLoaded) {
1157
- this.setState({ imageLoaded: false });
1158
- }
1159
- this.handleAnnoSaveEvent(
1160
- canvasActions.IMG_ANNO_TIME_UPDATE,
1161
- undefined,
1162
- undefined,
1163
- );
1164
- }
1165
- /**
1166
- * Find a annotation by id in current state
1167
- *
1168
- * @param {int} annoId - Id of the annotation to find
1169
- */
1170
- findAnno(annoId) {
1171
- return this.state.annos.find((e) => {
1172
- return e.id === annoId;
1173
- });
1174
- }
1175
-
1176
- findAnnoRef(annoId) {
1177
- if (this.state.selectedAnnoId === undefined) return undefined;
1178
- return this.annoRefs.find((e) => {
1179
- if (e.current) {
1180
- return e.current.isSelected();
1181
- } else {
1182
- return false;
1183
- }
1184
- });
1185
- }
1186
-
1187
- pushHist(
1188
- annos,
1189
- selectedAnnoId,
1190
- pAction,
1191
- showSingleAnno,
1192
- imgLabelIds = this.state.imgLabelIds,
1193
- ) {
1194
- this.hist.push(
1195
- {
1196
- ...this.getAnnos(annos, false),
1197
- selectedAnnoId: selectedAnnoId,
1198
- showSingleAnno: showSingleAnno,
1199
- imgLabelIds: imgLabelIds,
1200
- },
1201
- pAction,
1202
- );
1203
- // console.log("hist", this.hist);
1204
- }
1205
-
1206
- undo() {
1207
- this.handleNotification({
1208
- title: "Redo/ Undo not supported",
1209
- message: `Redo and Undo functions are currently not supported`,
1210
- type: notificationType.WARNING,
1211
- });
1212
- return;
1213
- //TODO: Make UNDO great again
1214
- // if (!this.hist.isEmpty()){
1215
- // const cState = this.hist.undo()
1216
- // // console.log('hist', this.hist)
1217
- // this.setCanvasState(
1218
- // cState.entry.annotations,
1219
- // cState.entry.imgLabelIds,
1220
- // cState.entry.selectedAnnoId,
1221
- // cState.entry.showSingleAnno)
1222
- // }
1223
- }
1224
-
1225
- redo() {
1226
- this.handleNotification({
1227
- title: "Redo/ Undo not supported",
1228
- message: `Redo and Undo functions are currently not supported`,
1229
- type: notificationType.WARNING,
1230
- });
1231
- return;
1232
- //TODO: Make REDO great again
1233
- // if (!this.hist.isEmpty()){
1234
- // const cState = this.hist.redo()
1235
- // // console.log('hist', this.hist)
1236
- // this.setCanvasState(
1237
- // cState.entry.annotations,
1238
- // cState.entry.imgLabelIds,
1239
- // cState.entry.selectedAnnoId,
1240
- // cState.entry.showSingleAnno
1241
- // )
1242
- // }
1243
- }
1244
-
1245
- deleteAnnotation(anno) {
1246
- if (anno) {
1247
- if (anno.mode === modes.CREATE) {
1248
- const ar = this.findAnnoRef(this.state.selectedAnnoId);
1249
- if (ar !== undefined) ar.current.myAnno.current.removeLastNode();
1250
- } else {
1251
- this.handleAnnoEvent(anno, canvasActions.ANNO_DELETED);
1252
- }
1253
- }
1254
- }
1255
-
1256
- deleteAnnoInCreationMode(anno) {
1257
- if (anno) {
1258
- if (anno.mode === modes.CREATE) {
1259
- this.handleAnnoEvent(anno, canvasActions.ANNO_DELETED);
1260
- } else {
1261
- }
1262
- }
1263
- }
1264
-
1265
- deleteAllAnnos() {
1266
- let newAnnos = [];
1267
- this.state.annos.forEach((e) => {
1268
- if (typeof e.id !== "string") {
1269
- const anno = { ...e, status: annoStatus.DELETED };
1270
- this.handleAnnoEvent(anno, canvasActions.ANNO_DELETED);
1271
- }
1272
- });
1273
- this.selectAnnotation(undefined);
1274
- this.showSingleAnno(undefined);
1275
- }
1276
-
1277
- /**
1278
- * Set state of Canvas annotations and imageLabels.
1279
- *
1280
- * @param {list} annotations - Annotations in backend format
1281
- * @param {list} imgLabelIds - IDs of the image labels
1282
- * @param {object} selectedAnno - The selected annotation
1283
- * @param {int} showSingleAnno - The id of the single annotation
1284
- * that should be visible
1285
- */
1286
- setCanvasState(annotations, imgLabelIds, selectedAnnoId, showSingleAnno) {
1287
- this.updateCanvasView({ ...annotations });
1288
- this.setImageLabels([...imgLabelIds]);
1289
- this.selectAnnotation(selectedAnnoId);
1290
- this.setState({ showSingleAnno: showSingleAnno });
1291
- }
1292
-
1293
- isLocked(annoId) {
1294
- if (this.props.lockedAnnos) {
1295
- if (this.props.lockedAnnos.includes(annoId)) {
1296
- return true;
1297
- }
1298
- }
1299
- return false;
1300
- }
1301
-
1302
- selectAnnotation(annoId) {
1303
- if (this.isLocked(annoId)) {
1304
- this.handleNotification({
1305
- title: "Annotation locked",
1306
- message: `Annotation with id ${annoId} is locked and can not be edited`,
1307
- type: notificationType.WARNING,
1308
- });
1309
- return;
1310
- }
1311
- if (annoId) {
1312
- const anno = this.findAnno(annoId);
1313
- this.setState({
1314
- selectedAnnoId: annoId,
1315
- });
1316
- if (anno) {
1317
- if (anno.mode !== modes.CREATE) {
1318
- this.setState({
1319
- annoToolBarVisible: true,
1320
- });
1321
- }
1322
- }
1323
- } else {
1324
- this.setState({
1325
- selectedAnnoId: undefined,
1326
- annoToolBarVisible: false,
1327
- });
1328
- if (this.state.showLabelInput) {
1329
- this.onAnnoLabelInputClose();
1330
- }
1331
- }
1332
- }
1333
-
1334
- /**
1335
- * Traverse annotations by key hit
1336
- */
1337
- traverseAnnos() {
1338
- if (this.state.annos.length > 0) {
1339
- const myAnnos = this.state.annos.filter((e) => {
1340
- return (
1341
- e.status !== annoStatus.DELETED &&
1342
- !this.isLocked(e.id) &&
1343
- !(e.visible === false)
1344
- );
1345
- });
1346
- if (myAnnos.length > 0) {
1347
- if (!this.state.selectedAnnoId) {
1348
- this.selectAnnotation(myAnnos[0].id);
1349
- } else {
1350
- let currentIdx = myAnnos.findIndex((e) => {
1351
- return e.id === this.state.selectedAnnoId;
1352
- });
1353
- if (currentIdx + 1 < myAnnos.length) {
1354
- this.selectAnnotation(myAnnos[currentIdx + 1].id);
1355
- } else {
1356
- this.selectAnnotation(myAnnos[0].id);
1357
- }
1358
- }
1359
- }
1360
- }
1361
- }
1362
-
1363
- getAnnos(annos = undefined, removeFrontedIds = true) {
1364
- const myAnnos = annos ? annos : this.state.annos;
1365
- // const backendFormat = this.getAnnoBackendFormat(removeFrontedIds, myAnnos)
1366
- const backendFormat = annoConversion.canvasToBackendAnnos(
1367
- myAnnos,
1368
- this.state.svg,
1369
- removeFrontedIds,
1370
- this.state.imageOffset,
1371
- );
1372
- const finalData = {
1373
- imgId: this.props.imageMeta.id,
1374
- imgLabelIds: this.state.imgLabelIds,
1375
- imgLabelChanged: this.state.imgLabelChanged,
1376
- imgActions: this.state.imgActions,
1377
- annotations: backendFormat,
1378
- isJunk: this.state.isJunk,
1379
- annoTime:
1380
- this.props.imageMeta.annoTime +
1381
- (performance.now() - this.state.imgLoadTimestamp) / 1000,
1382
- };
1383
- return finalData;
1384
- }
1385
-
1386
- /**
1387
- * Reset zoom level on Canvas
1388
- */
1389
- resetZoom() {
1390
- this.setState({
1391
- svg: {
1392
- ...this.state.svg,
1393
- translateX: 0,
1394
- translateY: 0,
1395
- scale: 1.0,
1396
- },
1397
- });
1398
- }
1399
-
1400
- moveCamera(movementX, movementY) {
1401
- let trans_x = this.state.svg.translateX + movementX / this.state.svg.scale;
1402
- let trans_y = this.state.svg.translateY + movementY / this.state.svg.scale;
1403
- const vXMin = this.state.svg.width * 0.25;
1404
- const vXMax = this.state.svg.width * 0.75;
1405
- const yXMin = this.state.svg.height * 0.25;
1406
- const yXMax = this.state.svg.height * 0.75;
1407
- const vLeft = wv.getViewportCoordinates({ x: 0, y: 0 }, this.state.svg);
1408
- const vRight = wv.getViewportCoordinates(
1409
- { x: this.state.svg.width, y: this.state.svg.height },
1410
- this.state.svg,
1411
- );
1412
- if (vLeft.vX >= vXMin) {
1413
- trans_x = this.state.svg.translateX - 5;
1414
- } else if (vRight.vX <= vXMax) {
1415
- trans_x = this.state.svg.translateX + 5;
1416
- }
1417
- if (vLeft.vY >= yXMin) {
1418
- trans_y = this.state.svg.translateY - 5;
1419
- } else if (vRight.vY <= yXMax) {
1420
- trans_y = this.state.svg.translateY + 5;
1421
- }
1422
- this.setState({
1423
- svg: {
1424
- ...this.state.svg,
1425
- translateX: trans_x,
1426
- translateY: trans_y,
1427
- },
1428
- });
1429
- }
1430
-
1431
- setMode(mode) {
1432
- if (this.state.mode !== mode) {
1433
- this.setState({ mode: mode });
1434
- }
1435
- }
1436
-
1437
- getMousePosition(e) {
1438
- const absPos = this.getMousePositionAbs(e);
1439
- return {
1440
- x: absPos.x / this.state.svg.scale - this.state.svg.translateX,
1441
- y: absPos.y / this.state.svg.scale - this.state.svg.translateY,
1442
- };
1443
- }
1444
-
1445
- getMousePositionAbs(e) {
1446
- return {
1447
- x: e.pageX - this.svg.current.getBoundingClientRect().left,
1448
- y: e.pageY - this.svg.current.getBoundingClientRect().top,
1449
- };
1450
- }
1451
-
1452
- showLabelInput(visible = true) {
1453
- this.setState({
1454
- showLabelInput: visible,
1455
- });
1456
- if (visible) {
1457
- this.showSingleAnno(this.state.selectedAnnoId);
1458
- }
1459
- }
1460
-
1461
- createNewAnnotation(e) {
1462
- //Do not create new Annotation if controlKey was pressed!
1463
- let allowed = false;
1464
- if (this.keyMapper.controlDown) return;
1465
- if (this.keyMapper.shiftDown) return;
1466
- if (this.props.selectedTool) {
1467
- const maxAnnos = this.props.canvasConfig.annos.maxAnnos;
1468
- if (maxAnnos) {
1469
- if (this.state.annos.length < maxAnnos) {
1470
- allowed = true;
1471
- } else {
1472
- // console.warn(
1473
- // "Maximum number of annotations reached! MaxAnnos:",
1474
- // maxAnnos,
1475
- // );
1476
- this.handleNotification({
1477
- title: "Maximum number of annotations reached!",
1478
- message: `Only ${maxAnnos} annotations per image are allowed by config`,
1479
- type: notificationType.WARNING,
1480
- });
1481
- }
1482
- } else {
1483
- allowed = true;
1484
- }
1485
- } else {
1486
- // console.warn("No annotation tool selected!");
1487
- this.handleNotification({
1488
- title: "No tool selected!",
1489
- message: "Please select an annotation tool in the toolbar.",
1490
- type: notificationType.INFO,
1491
- });
1492
- }
1493
- if (allowed) {
1494
- const mousePos = this.getMousePosition(e);
1495
- // const selAnno = this.findAnno(this.state.selectedAnnoId)
1496
- let newAnno = {
1497
- id: this.props.nextAnnoId ? this.props.nextAnnoId : uniqueId("new"),
1498
- type: this.props.selectedTool,
1499
- data: [
1500
- {
1501
- x: mousePos.x,
1502
- y: mousePos.y,
1503
- },
1504
- {
1505
- x: mousePos.x,
1506
- y: mousePos.y,
1507
- },
1508
- ],
1509
- mode: modes.CREATE,
1510
- status: annoStatus.NEW,
1511
- labelIds: this.state.prevLabel,
1512
- selectedNode: 1,
1513
- annoTime: 0.0,
1514
- };
1515
- newAnno = this.startAnnotimeMeasure(newAnno);
1516
- this.setState({
1517
- annos: [...this.state.annos, newAnno],
1518
- selectedAnnoId: newAnno.id,
1519
- showSingleAnno: newAnno.id,
1520
- annoToolBarVisible: false,
1521
- });
1522
- if (
1523
- this.props.selectedTool !== TOOLS.BBOX &&
1524
- this.props.selectedTool !== TOOLS.POINT
1525
- ) {
1526
- const merged = this.mergeSelectedAnno(newAnno);
1527
- this.pushHist(
1528
- merged.newAnnos,
1529
- newAnno.id,
1530
- canvasActions.ANNO_CREATED_NODE,
1531
- newAnno.id,
1532
- );
1533
- }
1534
- this.handleAnnoEvent(newAnno, canvasActions.ANNO_ENTER_CREATE_MODE);
1535
- }
1536
- }
1537
-
1538
- /**
1539
- * recreate an existing annotation in case the creation process was not finished
1540
- * @param {string} id of annotation
1541
- */
1542
- recreateAnnotation(annoID) {
1543
- // console.log("AnnoSave -> recreateAnnotation ", annoID);
1544
-
1545
- let annos = this.state.annos;
1546
-
1547
- // search for id of selected anno in all annos (should normally be last item in list, but to be sure)
1548
- let annoIndex;
1549
- let anno;
1550
-
1551
- for (var k in annos)
1552
- if (annos[k].id == annoID) {
1553
- annoIndex = k;
1554
- anno = annos[k];
1555
- break;
1556
- }
1557
-
1558
- // editing is only allowed on line and polygon
1559
- if (!["line", "polygon"].includes(anno.type)) return; // console.log(
1560
- // "Cant recreate annotation: Type " + anno.type + " is forbidden",
1561
- // );
1562
-
1563
- // remove the old annotation
1564
- this.state.annos.splice(annoIndex, 1);
1565
-
1566
- // create a new annotation based on the datapoints of the old annotation
1567
- let newAnno = {
1568
- id: anno.id,
1569
- type: anno.type,
1570
- data: anno.data,
1571
- mode: modes.CREATE,
1572
- status:
1573
- anno.status === "database" || anno.status === "changed"
1574
- ? annoStatus.CHANGED
1575
- : annoStatus.NEW,
1576
- labelIds: anno.labelIds,
1577
- selectedNode: anno.data.length - 1,
1578
- annoTime: anno.annoTime,
1579
- };
1580
-
1581
- newAnno = this.startAnnotimeMeasure(newAnno);
1582
- this.setState({
1583
- annos: [...this.state.annos, newAnno],
1584
- selectedAnnoId: newAnno.id,
1585
- showSingleAnno: newAnno.id,
1586
- annoToolBarVisible: false,
1587
- });
1588
-
1589
- // console.log("Annotation recreated");
1590
- this.handleAnnoEvent(newAnno, canvasActions.ANNO_ENTER_CREATE_MODE);
1591
- }
1592
-
1593
- putSelectedOnTop(prevState) {
1594
- // The selected annotation need to be rendered as last one in
1595
- // oder to be above all other annotations.
1596
- if (this.state.selectedAnnoId) {
1597
- if (prevState.selectedAnnoId !== this.state.selectedAnnoId) {
1598
- const annos = this.state.annos.filter((el) => {
1599
- return el.id !== this.state.selectedAnnoId;
1600
- });
1601
- const lastAnno = this.state.annos.find((el) => {
1602
- return el.id === this.state.selectedAnnoId;
1603
- });
1604
- annos.push(lastAnno);
1605
- this.setState({
1606
- annos: [...annos],
1607
- });
1608
- }
1609
- }
1610
- }
1611
-
1612
- getLabel(lblId) {
1613
- return this.state.possibleLabels.find((e) => {
1614
- return e.id === lblId;
1615
- });
1616
- }
1617
-
1618
- getAnnoColor() {
1619
- if (this.state.selectedAnnoId) {
1620
- const anno = this.findAnno(this.state.selectedAnnoId);
1621
- if (anno) {
1622
- if (anno.labelIds.length > 0) {
1623
- return this.getLabel(anno.labelIds[0]).color;
1624
- }
1625
- }
1626
- }
1627
- return colorlut.getDefaultColor();
1628
- }
1629
-
1630
- updateDelayedBackendUpdates(tempId, dbId) {
1631
- // console.log(
1632
- // "updateDelayedBackendUpdates ",
1633
- // tempId,
1634
- // dbId,
1635
- // this.delayedBackendUpdates,
1636
- // );
1637
- if (tempId !== dbId) this.tempIdMap[tempId] = dbId;
1638
- if (tempId in this.delayedBackendUpdates) {
1639
- if (this.delayedBackendUpdates[tempId] !== null) {
1640
- const { anno, action } = this.delayedBackendUpdates[tempId];
1641
- const myAnno = {
1642
- ...anno,
1643
- status:
1644
- anno.status === annoStatus.NEW ? annoStatus.CHANGED : anno.status,
1645
- };
1646
- delete this.delayedBackendUpdates[tempId];
1647
- // console.log("PerformDelayedBackendUpdate", action, myAnno);
1648
- this.handleAnnoSaveEvent(action, myAnno);
1649
- } else {
1650
- delete this.delayedBackendUpdates[tempId];
1651
- }
1652
- }
1653
- }
1654
-
1655
- addDelayedBackendUpdate(anno, action) {
1656
- // take care of tempIds while receiving a dbId from backend.
1657
- // handling tempIds is only required if instant anno backend update is
1658
- // used.
1659
- if (this.props.onAnnoSaveEvent) {
1660
- if (typeof anno.id === "string") {
1661
- if (!(anno.id in this.tempIdMap)) {
1662
- let myAnno = undefined;
1663
- if (anno.id in this.delayedBackendUpdates) {
1664
- this.delayedBackendUpdates[anno.id] = { anno, action };
1665
- } else {
1666
- this.delayedBackendUpdates[anno.id] = null;
1667
- myAnno = anno;
1668
- }
1669
- // console.log(
1670
- // "addDelayedBackendUpdate ",
1671
- // myAnno,
1672
- // action,
1673
- // anno,
1674
- // this.delayedBackendUpdates,
1675
- // );
1676
- return myAnno;
1677
- }
1678
- }
1679
- // else if ((typeof anno.id) === "string"){
1680
- // this.delayedBackendUpdates[anno.id] = {anno, action}
1681
- // }
1682
- } else {
1683
- console.error(
1684
- "onAnnoSaveEvent needs to be provided in order to use SIA Canvas in a correct war!",
1685
- );
1686
- }
1687
- return anno;
1688
- }
1689
-
1690
- updateAnnoBySaveResponse(res) {
1691
- if (!res) return;
1692
- if (res.tempId !== res.dbId) {
1693
- //TODO: Replace tempId with dbId in undo/redo hist
1694
- const anno = this.findAnno(res.tempId);
1695
- if (!anno) return;
1696
- // anno.id = res.dbId
1697
- // anno.status = annoStatus.DATABASE
1698
- //TODO: Should not update if the anno is currently in edit or move mode
1699
- // this.updateAnno(anno)
1700
- // if (this.state.selectedAnnoId === res.tempId) this.setState({selectedAnnoId: res.dbId})
1701
- this.updateDelayedBackendUpdates(res.tempId, res.dbId);
1702
- }
1703
- // else {
1704
- // const anno = this.findAnno(res.dbId)
1705
- // if (!anno) return
1706
- // anno.status = res.newStatus
1707
- // // this.updateAnno(anno)
1708
- // }
1709
- }
1710
-
1711
- /**
1712
- * Update selected anno and override mode if desired
1713
- *
1714
- * @param {object} anno - The new annotation that becomes the selected anno
1715
- * @param {string} mode - The new mode for the selected anno
1716
- * @returns The new anno that was set as selectedAnno in state and
1717
- * the new annos list that was set in state
1718
- */
1719
- updateSelectedAnno(anno, mode = undefined, returnNewAnno = false) {
1720
- // console.log('updateSelectedAnno: ', mode, anno)
1721
- if (!anno) return;
1722
- const { newAnnos, newAnno } = this.mergeSelectedAnno(anno, mode);
1723
- this.setState({
1724
- annos: newAnnos,
1725
- selectedAnnoId: anno.id,
1726
- prevLabel: anno.labelIds,
1727
- });
1728
- if (returnNewAnno) {
1729
- return { newAnnos, newAnno };
1730
- } else {
1731
- return newAnnos;
1732
- }
1733
- }
1734
-
1735
- updateAnno(anno, mode = undefined, returnNewAnno = false) {
1736
- if (!anno) return;
1737
- const { newAnnos, newAnno } = this.mergeSelectedAnno(anno, mode);
1738
- this.setState({
1739
- annos: newAnnos,
1740
- });
1741
- if (returnNewAnno) {
1742
- return { newAnnos, newAnno };
1743
- } else {
1744
- return newAnnos;
1745
- }
1746
- }
1747
-
1748
- mergeSelectedAnno(anno, mode = undefined) {
1749
- let filtered = this.state.annos.filter((el) => {
1750
- return el.id !== anno.id;
1751
- });
1752
- filtered = filtered.map((e) => {
1753
- return { ...e, mode: modes.VIEW };
1754
- });
1755
- let newAnno;
1756
- if (mode) {
1757
- newAnno = { ...anno, mode: mode };
1758
- if (mode === modes.DELETED) {
1759
- if (anno.status !== annoStatus.NEW) {
1760
- newAnno = {
1761
- ...newAnno,
1762
- status: annoStatus.DELETED,
1763
- };
1764
- } else {
1765
- newAnno = null;
1766
- }
1767
- } else {
1768
- newAnno = {
1769
- ...newAnno,
1770
- status:
1771
- anno.status !== annoStatus.NEW
1772
- ? annoStatus.CHANGED
1773
- : annoStatus.NEW,
1774
- };
1775
- }
1776
- } else {
1777
- newAnno = { ...anno };
1778
- }
1779
- if (newAnno !== null) {
1780
- filtered.push(newAnno);
1781
- }
1782
- const newAnnos = [...filtered];
1783
- return { newAnnos, newAnno };
1784
- }
1785
-
1786
- showSingleAnno(annoId) {
1787
- if (this.state.showSingleAnno !== annoId) {
1788
- this.setState({ showSingleAnno: annoId });
1789
- }
1790
- }
1791
-
1792
- updateImageSize() {
1793
- var container = this.props.container.current.getBoundingClientRect();
1794
- var clientHeight = document.documentElement.clientHeight;
1795
- var canvasTop;
1796
- var canvasLeft;
1797
- var maxImgHeight;
1798
- var maxImgWidth;
1799
- const layoutOffset = this.props.uiConfig.layoutOffset;
1800
- if (layoutOffset) {
1801
- canvasTop = container.top + layoutOffset.top;
1802
- canvasLeft = container.left + layoutOffset.left;
1803
- maxImgHeight =
1804
- clientHeight - container.top - layoutOffset.bottom - layoutOffset.top;
1805
- maxImgWidth = container.right - canvasLeft - layoutOffset.right;
1806
- } else {
1807
- canvasTop = container.top;
1808
- canvasLeft = container.left;
1809
- maxImgHeight = clientHeight - container.top;
1810
- maxImgWidth = container.right - canvasLeft;
1811
- }
1812
- var ratio = this.img.current.naturalWidth / this.img.current.naturalHeight;
1813
- var imgWidth = "100%";
1814
- var imgHeight = "100%";
1815
- if (maxImgHeight * ratio > maxImgWidth) {
1816
- imgWidth = maxImgWidth;
1817
- imgHeight = maxImgWidth / ratio;
1818
- } else {
1819
- imgWidth = maxImgHeight * ratio;
1820
- imgHeight = maxImgHeight;
1821
- }
1822
- var svg;
1823
- const imgOffset = { x: 0, y: 0 };
1824
- if (this.props.uiConfig.maxCanvas) {
1825
- imgOffset.x = (maxImgWidth - imgWidth) / 2;
1826
- imgOffset.y = (maxImgHeight - imgHeight) / 2;
1827
- // console.log(`imgOffset: `, imgOffset);
1828
- svg = {
1829
- ...this.state.svg,
1830
- width: maxImgWidth,
1831
- height: maxImgHeight,
1832
- left: canvasLeft,
1833
- top: canvasTop,
1834
- };
1835
- } else {
1836
- if (this.props.uiConfig.centerCanvasInContainer) {
1837
- const resSpaceX = maxImgWidth - imgWidth;
1838
- if (resSpaceX > 2) {
1839
- canvasLeft = canvasLeft + resSpaceX / 2;
1840
- }
1841
- const resSpaceY = maxImgHeight - imgHeight;
1842
- if (resSpaceY > 2) {
1843
- canvasTop = canvasTop + resSpaceY / 2;
1844
- }
1845
- }
1846
- svg = {
1847
- ...this.state.svg,
1848
- width: imgWidth,
1849
- height: imgHeight,
1850
- left: canvasLeft,
1851
- top: canvasTop,
1852
- };
1853
- }
1854
- this.setState({
1855
- svg,
1856
- image: {
1857
- width: this.img.current.naturalWidth,
1858
- height: this.img.current.naturalHeight,
1859
- },
1860
- imageOffset: imgOffset,
1861
- });
1862
- this.svgUpdate(svg);
1863
- return {
1864
- imgWidth: this.props.fixedImageSize
1865
- ? this.props.fixedImageSize
1866
- : imgWidth,
1867
- imgHeight: this.props.fixedImageSize
1868
- ? this.props.fixedImageSize
1869
- : imgHeight,
1870
- imgOffset,
1871
- };
1872
- }
1873
-
1874
- svgUpdate(svg) {
1875
- this.triggerCanvasEvent(canvasActions.CANVAS_SVG_UPDATE, svg);
1876
- }
1877
-
1878
- setImageLabels(labelIds) {
1879
- if (labelIds !== this.state.imgLabelIds) {
1880
- this.setState({
1881
- imgLabelIds: labelIds,
1882
- });
1883
- }
1884
- }
1885
-
1886
- updateCanvasView(annotations) {
1887
- var annos = [];
1888
- //Annotation data should be present and a pixel accurate value
1889
- //for svg should be calculated
1890
- if (annotations) {
1891
- const imgSize = this.updateImageSize();
1892
- this.setState({
1893
- annos: [
1894
- ...annoConversion.backendAnnosToCanvas(
1895
- annotations,
1896
- { width: imgSize.imgWidth, height: imgSize.imgHeight },
1897
- imgSize.imgOffset,
1898
- ),
1899
- ],
1900
- });
1901
- }
1902
- }
1903
-
1904
- renderAnnotations() {
1905
- // Do not render annotations while moving the camera!
1906
- if (this.state.mode !== modes.CAMERA_MOVE) {
1907
- this.annoRefs = [];
1908
- const annos = this.state.annos.map((el) => {
1909
- this.annoRefs.push(React.createRef());
1910
- return (
1911
- <Annotation
1912
- type={el.type}
1913
- data={el}
1914
- key={el.id}
1915
- svg={{ ...this.state.svg }}
1916
- imageOffset={this.state.imageOffset}
1917
- ref={this.annoRefs[this.annoRefs.length - 1]}
1918
- onMouseDown={(e) => this.onAnnoMouseDown(e)}
1919
- onAction={(anno, pAction) => this.handleAnnoEvent(anno, pAction)}
1920
- selectedAnno={this.state.selectedAnnoId}
1921
- // onModeChange={(anno) => this.onAnnoModeChange(anno)}
1922
- showSingleAnno={this.state.showSingleAnno}
1923
- uiConfig={this.props.uiConfig}
1924
- allowedActions={this.props.canvasConfig.annos.actions}
1925
- possibleLabels={this.state.possibleLabels}
1926
- image={this.state.image}
1927
- canvasConfig={this.props.canvasConfig}
1928
- onNotification={(messageObj) => this.handleNotification(messageObj)}
1929
- defaultLabel={this.props.defaultLabel}
1930
- />
1931
- );
1932
- });
1933
- return <g>{annos}</g>;
1934
- } else {
1935
- return null;
1936
- }
1937
- }
1938
-
1939
- renderImgLabelInput() {
1940
- if (!this.props.imageMeta) return null;
1941
- return (
1942
- <Prompt
1943
- onClick={() => this.handleImgLabelInputClose()}
1944
- active={this.props.uiConfig.imgLabelInputVisible}
1945
- header={<div>Add label for the whole image</div>}
1946
- content={
1947
- <div>
1948
- <LabelInput
1949
- // multilabels={true}
1950
- multilabels={this.props.canvasConfig.img.multilabels}
1951
- // relatedId={this.props.annos.image.id}
1952
- visible={true}
1953
- onLabelUpdate={(label) => this.handleImgLabelUpdate(label)}
1954
- possibleLabelsProp={this.state.possibleLabels}
1955
- initLabelIds={this.state.imgLabelIds}
1956
- relatedId={this.props.imageMeta.id}
1957
- defaultLabel={this.props.defaultLabel}
1958
- // onLabelConfirmed = {label => this.handleImgLabelUpdate(label)}
1959
- // disabled={!this.props.allowedActions.label}
1960
- // renderPopup
1961
- />
1962
- <Button
1963
- basic
1964
- color="green"
1965
- inverted
1966
- onClick={() => this.handleImgLabelInputClose()}
1967
- >
1968
- <Icon name="check"></Icon>
1969
- OK
1970
- </Button>
1971
- </div>
1972
- }
1973
- />
1974
- );
1975
- }
1976
-
1977
- renderAnnoToolBar(selectedAnno) {
1978
- let visible = this.state.annoToolBarVisible;
1979
- if (this.state.mode === modes.CAMERA_MOVE) visible = false;
1980
- return (
1981
- <AnnoToolBar
1982
- visible={visible}
1983
- selectedAnno={selectedAnno}
1984
- svg={this.state.svg}
1985
- onClick={() => this.editAnnoLabel()}
1986
- color={this.getAnnoColor()}
1987
- />
1988
- );
1989
- }
1990
-
1991
- renderAnnoLabelInpput(selectedAnno) {
1992
- let visible = this.state.showLabelInput;
1993
- if (this.state.mode === modes.CAMERA_MOVE) visible = false;
1994
- return (
1995
- <AnnoLabelInput
1996
- svg={this.state.svg}
1997
- // svgRef={this.svg}
1998
- onClose={() => this.onAnnoLabelInputClose()}
1999
- onDeleteClick={(annoId) => this.onLabelInputDeleteClick(annoId)}
2000
- selectedAnno={selectedAnno}
2001
- visible={visible}
2002
- onLabelUpdate={(anno) => this.onAnnoLabelInputUpdate(anno)}
2003
- possibleLabels={this.state.possibleLabels}
2004
- allowedActions={this.props.canvasConfig.annos.actions}
2005
- multilabels={this.props.canvasConfig.annos.multilabels}
2006
- mousePos={this.mousePosAbs}
2007
- defaultLabel={this.props.defaultLabel}
2008
- />
2009
- );
2010
- }
2011
-
2012
- renderSamPoints() {
2013
- return this.props.samPoints.map((point, index) => (
2014
- <circle
2015
- key={index}
2016
- cx={point.x}
2017
- cy={point.y}
2018
- r={5}
2019
- fill={point.type === "positive" ? "lightgreen" : "lightcoral"}
2020
- />
2021
- ));
2022
- }
2023
-
2024
- renderSamBBox() {
2025
- return (
2026
- this.props.samBBox && (
2027
- <rect
2028
- x={this.props.samBBox.x}
2029
- y={this.props.samBBox.y}
2030
- width={this.props.samBBox.width}
2031
- height={this.props.samBBox.height}
2032
- fill="rgba(0, 0, 255, 0.1)"
2033
- stroke="blue"
2034
- strokeWidth="2"
2035
- />
2036
- )
2037
- );
2038
- }
2039
-
2040
- render() {
2041
- const selectedAnno = this.findAnno(this.state.selectedAnnoId);
2042
- return (
2043
- <div ref={this.container}>
2044
- <div
2045
- height={this.state.svg.height}
2046
- style={{
2047
- position: this.props.isStaticPosition ? "static" : "fixed",
2048
- top: this.state.svg.top,
2049
- left: this.state.svg.left,
2050
- }}
2051
- >
2052
- {/* {this.renderAnnoCommentInput(selectedAnno)} */}
2053
- {this.renderImgLabelInput()}
2054
- <ImgBar
2055
- container={this.container}
2056
- visible={this.state.imgBarVisible}
2057
- possibleLabels={this.state.possibleLabels}
2058
- annos={this.props.annos}
2059
- annoTaskId={this.props.annoTaskId}
2060
- svg={this.state.svg}
2061
- imageMeta={this.props.imageMeta}
2062
- onClose={() => this.handleImgBarClose()}
2063
- imgLabelIds={this.state.imgLabelIds}
2064
- // onLabelUpdate={label => this.handleImgLabelUpdate(label)}
2065
- // imgLabelIds={this.state.imgLabelIds}
2066
- // multilabels={this.props.canvasConfig.img.multilabels}
2067
- // allowedActions={this.props.canvasConfig.img.actions}
2068
- onMouseEnter={(e) => this.handleImgBarMouseEnter(e)}
2069
- />
2070
-
2071
- <Dimmer active={!this.state.imageLoaded || this.props.blocked}>
2072
- {(!this.state.imageLoaded || this.props.blocked) && (
2073
- <Loader>Loading</Loader>
2074
- )}
2075
- </Dimmer>
2076
- <Dimmer active={this.state.isJunk}>
2077
- {this.state.isJunk && (
2078
- <Header
2079
- as="h2"
2080
- icon
2081
- inverted
2082
- style={{ background: "rgba(0,0,0,0)" }}
2083
- >
2084
- <Icon name="ban" />
2085
- Marked as Junk
2086
- </Header>
2087
- )}
2088
- </Dimmer>
2089
-
2090
- {this.renderAnnoToolBar(selectedAnno)}
2091
- {/* <div style={{position: 'fixed', top: this.props.container.top, left: this.props.container.left}}> */}
2092
- {this.renderAnnoLabelInpput(selectedAnno)}
2093
- <InfoBoxes
2094
- container={this.props.container}
2095
- layoutUpdate={this.props.layoutUpdate}
2096
- annos={this.state.annos}
2097
- selectedAnno={selectedAnno}
2098
- possibleLabels={this.state.possibleLabels}
2099
- allowedToMarkExample={this.props.canvasConfig.allowedToMarkExample}
2100
- uiConfig={this.props.uiConfig}
2101
- imgLoadCount={this.state.imgLoadCount}
2102
- onCommentUpdate={(comment) => this.updateAnnoComment(comment)}
2103
- onUiConfigUpdate={(e) =>
2104
- this.triggerCanvasEvent(canvasActions.CANVAS_UI_CONFIG_UPDATE, e)
2105
- }
2106
- onHideLbl={(lbl, hide) => this.handleHideLbl(lbl, hide)}
2107
- onMarkExample={(anno) => this.handleMarkExample(anno)}
2108
- commentInputTrigger={this.state.annoCommentInputTrigger}
2109
- onGetAnnoExample={(exampleArgs) =>
2110
- this.props.onGetAnnoExample
2111
- ? this.props.onGetAnnoExample(exampleArgs)
2112
- : {}
2113
- }
2114
- exampleImg={this.props.exampleImg}
2115
- />
2116
- <svg
2117
- ref={this.svg}
2118
- width={
2119
- this.props.fixedImageSize
2120
- ? this.props.fixedImageSize
2121
- : this.state.svg.width
2122
- }
2123
- height={
2124
- this.props.fixedImageSize
2125
- ? this.props.fixedImageSize
2126
- : this.state.svg.height
2127
- }
2128
- onKeyDown={(e) => this.onKeyDown(e)}
2129
- onKeyUp={(e) => this.onKeyUp(e)}
2130
- onClick={(e) => this.handleCanvasClick(e)}
2131
- onMouseMove={(e) => this.handleSvgMouseMove(e)}
2132
- onContextMenu={(e) => this.handleContextMenu(e)}
2133
- onMouseDown={(e) => this.handleMouseDown(e)}
2134
- onMouseUp={(e) => this.handleMouseUp(e)}
2135
- onMouseLeave={(e) => this.handleMouseLeave(e)}
2136
- tabIndex="0"
2137
- >
2138
- <g
2139
- transform={`scale(${this.state.svg.scale}) translate(${this.state.svg.translateX}, ${this.state.svg.translateY})`}
2140
- onMouseOver={() => {
2141
- this.onMouseOver();
2142
- }}
2143
- onMouseLeave={() => {
2144
- this.onMouseLeave();
2145
- }}
2146
- // onMouseEnter={() => this.svg.current.focus()}
2147
- onMouseUp={(e) => {
2148
- this.onMouseUp(e);
2149
- }}
2150
- onWheel={(e) => this.onWheel(e)}
2151
- onMouseMove={(e) => {
2152
- this.onMouseMove(e);
2153
- }}
2154
- >
2155
- <image
2156
- onContextMenu={(e) => this.onRightClick(e)}
2157
- onMouseDown={(e) => this.onMouseDown(e)}
2158
- href={this.props.imageBlob}
2159
- width={
2160
- this.props.fixedImageSize
2161
- ? this.props.fixedImageSize
2162
- : this.state.svg.width
2163
- }
2164
- height={
2165
- this.props.fixedImageSize
2166
- ? this.props.fixedImageSize
2167
- : this.state.svg.height
2168
- }
2169
- />
2170
- {this.renderAnnotations()}
2171
- {this.renderSamPoints()}
2172
- {this.renderSamBBox()}
2173
- </g>
2174
- </svg>
2175
- <img
2176
- alt="sia"
2177
- style={{ display: "none" }}
2178
- ref={this.img}
2179
- onLoad={() => {
2180
- this.onImageLoad();
2181
- }}
2182
- src={this.state.imageBlob}
2183
- width="100%"
2184
- height="100%"
2185
- />
2186
- </div>
2187
- {/* Placeholder for Layout*/}
2188
- <div style={{ minHeight: this.state.svg.height }}></div>
2189
- </div>
2190
- );
2191
- }
2192
- }
2193
-
2194
- export default Canvas;