@searpent/react-image-annotate 2.0.75 → 2.0.76

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 (224) hide show
  1. package/.babelrc +6 -0
  2. package/.env +1 -0
  3. package/.flowconfig +2 -0
  4. package/.github/workflows/release-on-master.yml +32 -0
  5. package/.github/workflows/test.yml +16 -0
  6. package/.prettierrc +3 -0
  7. package/.releaserc.js +18 -0
  8. package/.storybook/addons.js +2 -0
  9. package/.storybook/config.js +16 -0
  10. package/LICENSE +21 -0
  11. package/package.json +1 -1
  12. package/public/favicon.ico +0 -0
  13. package/public/index.html +38 -0
  14. package/src/Annotator/bike-pic.png +0 -0
  15. package/src/Annotator/bike-pic2.png +0 -0
  16. package/src/Annotator/dab-keyframes.story.json +1 -0
  17. package/src/Annotator/exampleImages.js +48 -0
  18. package/src/Annotator/examplePhotos.js +7603 -0
  19. package/src/Annotator/index.js +380 -0
  20. package/src/Annotator/index.story.js +877 -0
  21. package/src/Annotator/poses.story.js +150 -0
  22. package/src/Annotator/reducers/combine-reducers.js +7 -0
  23. package/src/Annotator/reducers/convert-expanding-line-to-polygon.js +53 -0
  24. package/{Annotator → src/Annotator}/reducers/fix-twisted.js +5 -3
  25. package/src/Annotator/reducers/general-reducer.js +1228 -0
  26. package/src/Annotator/reducers/get-active-image.js +21 -0
  27. package/src/Annotator/reducers/get-implied-video-regions.js +115 -0
  28. package/src/Annotator/reducers/history-handler.js +60 -0
  29. package/src/Annotator/reducers/image-reducer.js +23 -0
  30. package/src/Annotator/reducers/video-reducer.js +85 -0
  31. package/src/Annotator/video.story.js +51 -0
  32. package/src/ClassSelectionMenu/index.js +112 -0
  33. package/src/Crosshairs/index.js +64 -0
  34. package/src/DebugSidebarBox/index.js +36 -0
  35. package/src/DemoSite/Editor.js +235 -0
  36. package/src/DemoSite/ErrorBoundaryDialog.js +34 -0
  37. package/src/DemoSite/index.js +41 -0
  38. package/src/DemoSite/index.story.js +10 -0
  39. package/src/DemoSite/simple-segmentation-example.json +572 -0
  40. package/src/Editor/annotation-plugin/annotation.js +536 -0
  41. package/src/Editor/index.js +50 -0
  42. package/src/Editor/readOnly.js +21 -0
  43. package/{Editor → src/Editor}/tools.js +3 -2
  44. package/src/Errorer/index.js +13 -0
  45. package/src/FullImageSegmentationAnnotator/hard1.story.jpg +0 -0
  46. package/src/FullImageSegmentationAnnotator/hard2.story.jpg +0 -0
  47. package/src/FullImageSegmentationAnnotator/hard3.story.jpg +0 -0
  48. package/src/FullImageSegmentationAnnotator/index.js +7 -0
  49. package/src/FullImageSegmentationAnnotator/index.story.js +177 -0
  50. package/src/FullImageSegmentationAnnotator/orange.story.png +0 -0
  51. package/src/GroupSelectorSidebarBox/index.js +48 -0
  52. package/src/GroupsEditorSidebarBox/index.js +108 -0
  53. package/src/HelpSidebarBox/index.js +43 -0
  54. package/src/HighlightBox/index.js +143 -0
  55. package/src/HistorySidebarBox/index.js +78 -0
  56. package/src/ImageCanvas/dancing-man.story.jpg +0 -0
  57. package/src/ImageCanvas/index.js +515 -0
  58. package/src/ImageCanvas/index.story.js +314 -0
  59. package/src/ImageCanvas/mouse_mask.story.png +0 -0
  60. package/src/ImageCanvas/region-tools.js +171 -0
  61. package/src/ImageCanvas/seves_desk.story.jpg +0 -0
  62. package/{ImageCanvas → src/ImageCanvas}/styles.js +8 -12
  63. package/src/ImageCanvas/use-mouse.js +168 -0
  64. package/src/ImageCanvas/use-project-box.js +23 -0
  65. package/src/ImageCanvas/use-wasd-mode.js +50 -0
  66. package/src/ImageMask/index.js +127 -0
  67. package/src/ImageMask/load-image.js +32 -0
  68. package/src/ImageSelectorSidebarBox/index.js +54 -0
  69. package/src/KeyframeTimeline/get-time-string.js +25 -0
  70. package/src/KeyframeTimeline/index.js +223 -0
  71. package/src/KeyframesSelectorSidebarBox/index.js +93 -0
  72. package/src/LandingPage/content.md +57 -0
  73. package/src/LandingPage/github-markdown.css +964 -0
  74. package/src/LandingPage/index.js +147 -0
  75. package/src/Locker/index.js +13 -0
  76. package/src/MainLayout/RightSidebarItemsWrapper.js +21 -0
  77. package/src/MainLayout/icon-dictionary.js +79 -0
  78. package/src/MainLayout/index.js +564 -0
  79. package/src/MainLayout/index.story.js +240 -0
  80. package/{MainLayout → src/MainLayout}/styles.js +7 -6
  81. package/src/MainLayout/types.js +171 -0
  82. package/src/MainLayout/use-implied-video-regions.js +17 -0
  83. package/src/MetadataEditorSidebarBox/index.js +160 -0
  84. package/src/PageSelector/index.js +159 -0
  85. package/src/PointDistances/index.js +90 -0
  86. package/src/PreventScrollToParents/index.js +48 -0
  87. package/src/PreventScrollToParents/index.story.js +23 -0
  88. package/src/RegionLabel/index.js +236 -0
  89. package/{RegionLabel → src/RegionLabel}/styles.js +15 -12
  90. package/src/RegionSelectAndTransformBoxes/index.js +236 -0
  91. package/src/RegionSelectorSidebarBox/index.js +220 -0
  92. package/{RegionSelectorSidebarBox → src/RegionSelectorSidebarBox}/styles.js +14 -13
  93. package/src/RegionShapes/index.js +254 -0
  94. package/src/RegionTags/index.js +136 -0
  95. package/src/SettingsDialog/index.js +58 -0
  96. package/src/SettingsProvider/index.js +57 -0
  97. package/src/Shortcuts/ShortcutField.js +44 -0
  98. package/src/Shortcuts/index.js +129 -0
  99. package/src/ShortcutsManager/index.js +162 -0
  100. package/src/Sidebar/index.js +117 -0
  101. package/src/SidebarBoxContainer/index.js +93 -0
  102. package/src/SmallToolButton/index.js +57 -0
  103. package/src/TagsSidebarBox/index.js +93 -0
  104. package/src/TaskDescriptionSidebarBox/index.js +43 -0
  105. package/src/Theme/index.js +36 -0
  106. package/src/VideoOrImageCanvasBackground/index.js +170 -0
  107. package/src/colors.js +32 -0
  108. package/src/hooks/use-colors.js +75 -0
  109. package/src/hooks/use-event-callback.js +11 -0
  110. package/src/hooks/use-exclude-pattern.js +27 -0
  111. package/src/hooks/use-load-image.js +21 -0
  112. package/src/hooks/use-window-size.js +46 -0
  113. package/{hooks → src/hooks}/xpattern.js +1 -1
  114. package/src/hooks/xpattern.png +0 -0
  115. package/src/index.js +18 -0
  116. package/src/lib.js +7 -0
  117. package/src/screenshot.png +0 -0
  118. package/src/site.css +5 -0
  119. package/src/stories.js +2 -0
  120. package/src/utils/blocks-to-article.js +61 -0
  121. package/{utils → src/utils}/blocks-to-article.test.js +8 -5
  122. package/{utils → src/utils}/default-locked-until.js +1 -2
  123. package/{utils → src/utils}/filter-only-unique.js +1 -1
  124. package/src/utils/get-from-local-storage.js +7 -0
  125. package/src/utils/get-hotkey-help-text.js +11 -0
  126. package/src/utils/get-landmarks-with-transform.js +23 -0
  127. package/src/utils/photosToImages.js +67 -0
  128. package/src/utils/regions-groups.js +19 -0
  129. package/src/utils/regions-to-blocks.js +16 -0
  130. package/src/utils/saveable-actions-enum.js +5 -0
  131. package/src/utils/set-in-local-storage.js +6 -0
  132. package/src/utils/sleep.js +3 -0
  133. package/src/utils/uuid-to-hash.js +5 -0
  134. package/Annotator/exampleImages.js +0 -41
  135. package/Annotator/examplePhotos.js +0 -6980
  136. package/Annotator/index.js +0 -417
  137. package/Annotator/reducers/combine-reducers.js +0 -14
  138. package/Annotator/reducers/convert-expanding-line-to-polygon.js +0 -73
  139. package/Annotator/reducers/general-reducer.js +0 -1430
  140. package/Annotator/reducers/get-active-image.js +0 -27
  141. package/Annotator/reducers/get-implied-video-regions.js +0 -180
  142. package/Annotator/reducers/history-handler.js +0 -38
  143. package/Annotator/reducers/image-reducer.js +0 -20
  144. package/Annotator/reducers/video-reducer.js +0 -88
  145. package/ClassSelectionMenu/index.js +0 -140
  146. package/Crosshairs/index.js +0 -53
  147. package/DebugSidebarBox/index.js +0 -20
  148. package/DemoSite/Editor.js +0 -194
  149. package/DemoSite/ErrorBoundaryDialog.js +0 -64
  150. package/DemoSite/index.js +0 -40
  151. package/Editor/annotation-plugin/annotation.js +0 -647
  152. package/Editor/index.js +0 -93
  153. package/Editor/readOnly.js +0 -73
  154. package/Errorer/index.js +0 -11
  155. package/FullImageSegmentationAnnotator/index.js +0 -7
  156. package/GroupSelectorSidebarBox/index.js +0 -63
  157. package/GroupsEditorSidebarBox/index.js +0 -138
  158. package/HelpSidebarBox/index.js +0 -58
  159. package/HighlightBox/index.js +0 -102
  160. package/HistorySidebarBox/index.js +0 -71
  161. package/ImageCanvas/index.js +0 -441
  162. package/ImageCanvas/region-tools.js +0 -165
  163. package/ImageCanvas/use-mouse.js +0 -180
  164. package/ImageCanvas/use-project-box.js +0 -27
  165. package/ImageCanvas/use-wasd-mode.js +0 -62
  166. package/ImageMask/index.js +0 -133
  167. package/ImageMask/load-image.js +0 -25
  168. package/ImageSelectorSidebarBox/index.js +0 -60
  169. package/KeyframeTimeline/get-time-string.js +0 -27
  170. package/KeyframeTimeline/index.js +0 -233
  171. package/KeyframesSelectorSidebarBox/index.js +0 -93
  172. package/LandingPage/index.js +0 -159
  173. package/Locker/index.js +0 -11
  174. package/MainLayout/RightSidebarItemsWrapper.js +0 -19
  175. package/MainLayout/icon-dictionary.js +0 -104
  176. package/MainLayout/index.js +0 -526
  177. package/MainLayout/types.js +0 -0
  178. package/MainLayout/use-implied-video-regions.js +0 -13
  179. package/MetadataEditorSidebarBox/index.js +0 -231
  180. package/PageSelector/index.js +0 -180
  181. package/PointDistances/index.js +0 -73
  182. package/PreventScrollToParents/index.js +0 -51
  183. package/RegionLabel/index.js +0 -232
  184. package/RegionSelectAndTransformBoxes/index.js +0 -169
  185. package/RegionSelectorSidebarBox/index.js +0 -254
  186. package/RegionShapes/index.js +0 -294
  187. package/RegionTags/index.js +0 -144
  188. package/SettingsDialog/index.js +0 -52
  189. package/SettingsProvider/index.js +0 -60
  190. package/Shortcuts/ShortcutField.js +0 -46
  191. package/Shortcuts/index.js +0 -133
  192. package/ShortcutsManager/index.js +0 -155
  193. package/Sidebar/index.js +0 -69
  194. package/SidebarBoxContainer/index.js +0 -93
  195. package/SmallToolButton/index.js +0 -42
  196. package/TagsSidebarBox/index.js +0 -105
  197. package/TaskDescriptionSidebarBox/index.js +0 -58
  198. package/Theme/index.js +0 -30
  199. package/VideoOrImageCanvasBackground/index.js +0 -151
  200. package/colors.js +0 -14
  201. package/hooks/use-colors.js +0 -97
  202. package/hooks/use-event-callback.js +0 -10
  203. package/hooks/use-exclude-pattern.js +0 -24
  204. package/hooks/use-load-image.js +0 -26
  205. package/hooks/use-window-size.js +0 -46
  206. package/index.js +0 -3
  207. package/lib.js +0 -3
  208. package/stories.js +0 -5
  209. package/utils/blocks-to-article.js +0 -60
  210. package/utils/get-from-local-storage.js +0 -7
  211. package/utils/get-hotkey-help-text.js +0 -9
  212. package/utils/get-landmarks-with-transform.js +0 -40
  213. package/utils/photosToImages.js +0 -85
  214. package/utils/regions-groups.js +0 -28
  215. package/utils/regions-to-blocks.js +0 -18
  216. package/utils/saveable-actions-enum.js +0 -3
  217. package/utils/set-in-local-storage.js +0 -3
  218. package/utils/sleep.js +0 -7
  219. package/utils/uuid-to-hash.js +0 -5
  220. /package/{Editor → src/Editor}/annotation-plugin/annotation.css +0 -0
  221. /package/{Errorer → src/Errorer}/errorer.css +0 -0
  222. /package/{Locker → src/Locker}/locker.css +0 -0
  223. /package/{PageSelector → src/PageSelector}/page-selector.css +0 -0
  224. /package/{utils → src/utils}/next-group-id.js +0 -0
@@ -0,0 +1,254 @@
1
+ // @flow
2
+
3
+ import React, { memo } from "react"
4
+ import colorAlpha from "color-alpha"
5
+ import useColors from '../hooks/use-colors';
6
+
7
+
8
+ function clamp(num, min, max) {
9
+ return num <= min ? min : num >= max ? max : num
10
+ }
11
+
12
+ const RegionComponents = {
13
+ point: memo(({ region, iw, ih }) => (
14
+ <g transform={`translate(${region.x * iw} ${region.y * ih})`}>
15
+ <path
16
+ d={"M0 8L8 0L0 -8L-8 0Z"}
17
+ strokeWidth={2}
18
+ stroke={region.color}
19
+ fill="transparent"
20
+ />
21
+ </g>
22
+ )),
23
+ line: memo(({ region, iw, ih }) => (
24
+ <g transform={`translate(${region.x1 * iw} ${region.y1 * ih})`}>
25
+ <line
26
+ strokeWidth={2}
27
+ x1={0}
28
+ y1={0}
29
+ x2={(region.x2 - region.x1) * iw}
30
+ y2={(region.y2 - region.y1) * ih}
31
+ stroke={colorAlpha(region.color, 0.75)}
32
+ fill={colorAlpha(region.color, 0.25)}
33
+ />
34
+ </g>
35
+ )),
36
+ box: memo(({ region, iw, ih }) => {
37
+ const { clsColor, groupColor } = useColors();
38
+
39
+ if (region.groupId !== undefined) {
40
+ return <g transform={`translate(${region.x * iw} ${region.y * ih})`}>
41
+ <rect
42
+ strokeWidth={(region.groupHighlighted) ? 3 : 0}
43
+ x={0}
44
+ y={0}
45
+ width={Math.max(region.w * iw, 0)}
46
+ height={Math.max(region.h * ih, 0)}
47
+ stroke={colorAlpha(clsColor(region.cls), 0.85)}
48
+ fill={(region.groupHighlighted) ? colorAlpha(clsColor(region.cls), 0.15) : colorAlpha(groupColor(region.groupId), 0.5)}
49
+ />
50
+ </g>
51
+ } else {
52
+ return <g transform={`translate(${region.x * iw} ${region.y * ih})`}>
53
+ <rect
54
+ strokeWidth={2}
55
+ x={0}
56
+ y={0}
57
+ width={Math.max(region.w * iw, 0)}
58
+ height={Math.max(region.h * ih, 0)}
59
+ stroke={colorAlpha(clsColor(region.cls), 0.85)}
60
+ fill={colorAlpha(clsColor(region.cls), 0.85)}
61
+ />
62
+ </g>
63
+ }
64
+ }
65
+ ),
66
+ polygon: memo(({ region, iw, ih, fullSegmentationMode }) => {
67
+ const Component = region.open ? "polyline" : "polygon"
68
+ const alphaBase = fullSegmentationMode ? 0.5 : 1
69
+ return (
70
+ <Component
71
+ points={region.points
72
+ .map(([x, y]) => [x * iw, y * ih])
73
+ .map((a) => a.join(" "))
74
+ .join(" ")}
75
+ strokeWidth={2}
76
+ stroke={colorAlpha(region.color, 0.75)}
77
+ fill={colorAlpha(region.color, 0.25)}
78
+ />
79
+ )
80
+ }),
81
+ keypoints: ({ region, iw, ih, keypointDefinitions }) => {
82
+ const { points, keypointsDefinitionId } = region
83
+ if (!keypointDefinitions[keypointsDefinitionId]) {
84
+ throw new Error(
85
+ `No definition for keypoint configuration "${keypointsDefinitionId}"`
86
+ )
87
+ }
88
+ const { landmarks, connections } =
89
+ keypointDefinitions[keypointsDefinitionId]
90
+ return (
91
+ <g>
92
+ {Object.entries(points).map(([keypointId, { x, y }], i) => (
93
+ <g key={i} transform={`translate(${x * iw} ${y * ih})`}>
94
+ <path
95
+ d={"M0 8L8 0L0 -8L-8 0Z"}
96
+ strokeWidth={2}
97
+ stroke={landmarks[keypointId].color}
98
+ fill="transparent"
99
+ />
100
+ </g>
101
+ ))}
102
+ {connections.map(([kp1Id, kp2Id]) => {
103
+ const kp1 = points[kp1Id]
104
+ const kp2 = points[kp2Id]
105
+ const midPoint = { x: (kp1.x + kp2.x) / 2, y: (kp1.y + kp2.y) / 2 }
106
+
107
+ return (
108
+ <g key={`${kp1.x},${kp1.y}.${kp2.x},${kp2.y}`}>
109
+ <line
110
+ x1={kp1.x * iw}
111
+ y1={kp1.y * ih}
112
+ x2={midPoint.x * iw}
113
+ y2={midPoint.y * ih}
114
+ strokeWidth={2}
115
+ stroke={landmarks[kp1Id].color}
116
+ />
117
+ <line
118
+ x1={kp2.x * iw}
119
+ y1={kp2.y * ih}
120
+ x2={midPoint.x * iw}
121
+ y2={midPoint.y * ih}
122
+ strokeWidth={2}
123
+ stroke={landmarks[kp2Id].color}
124
+ />
125
+ </g>
126
+ )
127
+ })}
128
+ </g>
129
+ )
130
+ },
131
+ "expanding-line": memo(({ region, iw, ih }) => {
132
+ let { expandingWidth = 0.005, points } = region
133
+ expandingWidth = points.slice(-1)[0].width || expandingWidth
134
+ const pointPairs = points.map(({ x, y, angle, width }, i) => {
135
+ if (!angle) {
136
+ const n = points[clamp(i + 1, 0, points.length - 1)]
137
+ const p = points[clamp(i - 1, 0, points.length - 1)]
138
+ angle = Math.atan2(p.x - n.x, p.y - n.y) + Math.PI / 2
139
+ }
140
+ const dx = (Math.sin(angle) * (width || expandingWidth)) / 2
141
+ const dy = (Math.cos(angle) * (width || expandingWidth)) / 2
142
+ return [
143
+ { x: x + dx, y: y + dy },
144
+ { x: x - dx, y: y - dy },
145
+ ]
146
+ })
147
+ const firstSection = pointPairs.map(([p1, p2]) => p1)
148
+ const secondSection = pointPairs.map(([p1, p2]) => p2).asMutable()
149
+ secondSection.reverse()
150
+ const lastPoint = points.slice(-1)[0]
151
+ return (
152
+ <>
153
+ <polygon
154
+ points={firstSection
155
+ .concat(region.candidatePoint ? [region.candidatePoint] : [])
156
+ .concat(secondSection)
157
+ .map((p) => `${p.x * iw} ${p.y * ih}`)
158
+ .join(" ")}
159
+ strokeWidth={2}
160
+ stroke={colorAlpha(region.color, 0.75)}
161
+ fill={colorAlpha(region.color, 0.25)}
162
+ />
163
+ {points.map(({ x, y, angle }, i) => (
164
+ <g
165
+ key={i}
166
+ transform={`translate(${x * iw} ${y * ih}) rotate(${(-(angle || 0) * 180) / Math.PI
167
+ })`}
168
+ >
169
+ <g>
170
+ <rect
171
+ x={-5}
172
+ y={-5}
173
+ width={10}
174
+ height={10}
175
+ strokeWidth={2}
176
+ stroke={colorAlpha(region.color, 0.75)}
177
+ fill={colorAlpha(region.color, 0.25)}
178
+ />
179
+ </g>
180
+ </g>
181
+ ))}
182
+ <rect
183
+ x={lastPoint.x * iw - 8}
184
+ y={lastPoint.y * ih - 8}
185
+ width={16}
186
+ height={16}
187
+ strokeWidth={4}
188
+ stroke={colorAlpha(region.color, 0.5)}
189
+ fill={"transparent"}
190
+ />
191
+ </>
192
+ )
193
+ }),
194
+ pixel: () => null,
195
+ }
196
+
197
+ export const WrappedRegionList = memo(
198
+ ({ regions, keypointDefinitions, iw, ih, fullSegmentationMode }) => {
199
+ return regions
200
+ .filter((r) => r.visible !== false)
201
+ .map((r) => {
202
+ const Component = RegionComponents[r.type]
203
+ return (
204
+ <Component
205
+ key={r.id}
206
+ region={r}
207
+ iw={iw}
208
+ ih={ih}
209
+ keypointDefinitions={keypointDefinitions}
210
+ fullSegmentationMode={fullSegmentationMode}
211
+ />
212
+ )
213
+ })
214
+ },
215
+ (n, p) => n.regions === p.regions && n.iw === p.iw && n.ih === p.ih
216
+ )
217
+
218
+ export const RegionShapes = ({
219
+ mat,
220
+ imagePosition,
221
+ regions = [],
222
+ keypointDefinitions,
223
+ fullSegmentationMode,
224
+ }) => {
225
+ const iw = imagePosition.bottomRight.x - imagePosition.topLeft.x
226
+ const ih = imagePosition.bottomRight.y - imagePosition.topLeft.y
227
+ if (isNaN(iw) || isNaN(ih)) return null
228
+ return (
229
+ <svg
230
+ width={iw}
231
+ height={ih}
232
+ style={{
233
+ position: "absolute",
234
+ zIndex: 2,
235
+ left: imagePosition.topLeft.x,
236
+ top: imagePosition.topLeft.y,
237
+ pointerEvents: "none",
238
+ width: iw,
239
+ height: ih,
240
+ }}
241
+ >
242
+ <WrappedRegionList
243
+ key="wrapped-region-list"
244
+ regions={regions}
245
+ iw={iw}
246
+ ih={ih}
247
+ keypointDefinitions={keypointDefinitions}
248
+ fullSegmentationMode={fullSegmentationMode}
249
+ />
250
+ </svg>
251
+ )
252
+ }
253
+
254
+ export default RegionShapes
@@ -0,0 +1,136 @@
1
+ // @flow weak
2
+
3
+ import React from "react"
4
+ import Paper from "@mui/material/Paper"
5
+ import DefaultRegionLabel from "../RegionLabel"
6
+ import LockIcon from "@mui/icons-material/Lock"
7
+
8
+ const copyWithout = (obj, ...args) => {
9
+ const newObj = { ...obj }
10
+ for (const arg of args) {
11
+ delete newObj[arg]
12
+ }
13
+ return newObj
14
+ }
15
+
16
+ export const RegionTags = ({
17
+ regions,
18
+ projectRegionBox,
19
+ mouseEvents,
20
+ regionClsList,
21
+ regionTagList,
22
+ onBeginRegionEdit,
23
+ onChangeRegion,
24
+ onCloseRegionEdit,
25
+ onDeleteRegion,
26
+ onDeleteGroup,
27
+ layoutParams,
28
+ imageSrc,
29
+ RegionEditLabel,
30
+ onRegionClassAdded,
31
+ allowComments,
32
+ hideNotEditingLabel,
33
+ allowedGroups
34
+ }) => {
35
+ const RegionLabel =
36
+ RegionEditLabel != null ? RegionEditLabel : DefaultRegionLabel
37
+ return regions
38
+ .filter((r) => r.visible || r.visible === undefined)
39
+ .map((region) => {
40
+ const pbox = projectRegionBox(region)
41
+ const { iw, ih } = layoutParams.current
42
+ let margin = 8
43
+ if (region.highlighted && region.type === "box") margin += 6
44
+ const labelBoxHeight =
45
+ region.editingLabels && !region.locked ? 170 : region.tags ? 60 : 50
46
+ const displayOnTop = pbox.y > labelBoxHeight
47
+
48
+ const coords = displayOnTop
49
+ ? {
50
+ left: pbox.x,
51
+ top: pbox.y - margin / 2,
52
+ }
53
+ : { left: pbox.x, top: pbox.y + pbox.h + margin / 2 }
54
+ if (region.locked) {
55
+ return (
56
+ <div
57
+ key={region.id}
58
+ style={{
59
+ position: "absolute",
60
+ ...coords,
61
+ zIndex: 10 + (region.editingLabels ? 5 : 0),
62
+ }}
63
+ >
64
+ <Paper
65
+ style={{
66
+ position: "absolute",
67
+ left: 0,
68
+ ...(displayOnTop ? { bottom: 0 } : { top: 0 }),
69
+ zIndex: 10,
70
+ backgroundColor: "#fff",
71
+ borderRadius: 4,
72
+ padding: 2,
73
+ paddingBottom: 0,
74
+ opacity: 0.5,
75
+ pointerEvents: "none",
76
+ }}
77
+ >
78
+ <LockIcon style={{ width: 16, height: 16, color: "#333" }} />
79
+ </Paper>
80
+ </div>
81
+ )
82
+ }
83
+ return (
84
+ <div
85
+ key={region.id}
86
+ style={{
87
+ position: "absolute",
88
+ ...coords,
89
+ zIndex: 10 + (region.editingLabels ? 5 : 0),
90
+ width: 200,
91
+ }}
92
+ onMouseDown={(e) => e.preventDefault()}
93
+ onMouseUp={(e) => e.preventDefault()}
94
+ onMouseEnter={(e) => {
95
+ if (region.editingLabels) {
96
+ mouseEvents.onMouseUp(e)
97
+ e.button = 1
98
+ mouseEvents.onMouseUp(e)
99
+ }
100
+ }}
101
+ >
102
+ <div
103
+ style={{
104
+ position: "absolute",
105
+ zIndex: 20,
106
+ left: 0,
107
+ ...(displayOnTop ? { bottom: 0 } : { top: 0 }),
108
+ }}
109
+ {...(!region.editingLabels
110
+ ? copyWithout(mouseEvents, "onMouseDown", "onMouseUp")
111
+ : {})}
112
+ >
113
+ <RegionLabel
114
+ allowedClasses={regionClsList}
115
+ allowedTags={regionTagList}
116
+ onOpen={onBeginRegionEdit}
117
+ onChange={onChangeRegion}
118
+ onClose={onCloseRegionEdit}
119
+ onDelete={onDeleteRegion}
120
+ onDeleteGroup={onDeleteGroup}
121
+ editing={region.editingLabels}
122
+ region={region}
123
+ regions={regions}
124
+ imageSrc={imageSrc}
125
+ onRegionClassAdded={onRegionClassAdded}
126
+ allowComments={allowComments}
127
+ hideNotEditingLabel={hideNotEditingLabel}
128
+ allowedGroups={allowedGroups}
129
+ />
130
+ </div>
131
+ </div>
132
+ )
133
+ })
134
+ }
135
+
136
+ export default RegionTags
@@ -0,0 +1,58 @@
1
+ // @flow
2
+
3
+ import React from "react"
4
+ import Dialog from "@mui/material/Dialog"
5
+ import DialogTitle from "@mui/material/DialogTitle"
6
+ import DialogContent from "@mui/material/DialogContent"
7
+ import DialogActions from "@mui/material/DialogActions"
8
+ import Button from "@mui/material/Button"
9
+ import Survey from "material-survey/components/Survey"
10
+ import { useSettings } from "../SettingsProvider"
11
+
12
+ export const SettingsDialog = ({ open, onClose }) => {
13
+ const settings = useSettings()
14
+ return (
15
+ <Dialog open={open || false} onClose={onClose}>
16
+ <DialogTitle>Settings</DialogTitle>
17
+ <DialogContent style={{ minWidth: 400 }}>
18
+ <Survey
19
+ variant="flat"
20
+ noActions
21
+ defaultAnswers={settings}
22
+ onQuestionChange={(q, a, answers) => settings.changeSetting(q, a)}
23
+ form={{
24
+ questions: [
25
+ {
26
+ type: "boolean",
27
+ title: "Show Crosshairs",
28
+ name: "showCrosshairs",
29
+ },
30
+ {
31
+ type: "boolean",
32
+ title: "Show Highlight Box",
33
+ name: "showHighlightBox",
34
+ },
35
+ {
36
+ type: "boolean",
37
+ title: "WASD Mode",
38
+ name: "wasdMode",
39
+ },
40
+ {
41
+ type: "dropdown",
42
+ title: "Video Playback Speed",
43
+ name: "videoPlaybackSpeed",
44
+ defaultValue: "1x",
45
+ choices: ["0.25x", "0.5x", "1x", "2x"],
46
+ },
47
+ ],
48
+ }}
49
+ />
50
+ </DialogContent>
51
+ <DialogActions>
52
+ <Button onClick={onClose}>Close</Button>
53
+ </DialogActions>
54
+ </Dialog>
55
+ )
56
+ }
57
+
58
+ export default SettingsDialog
@@ -0,0 +1,57 @@
1
+ // @flow
2
+
3
+ import React, { createContext, useContext, useState } from "react"
4
+
5
+ const defaultSettings = {
6
+ showCrosshairs: false,
7
+ showHighlightBox: true,
8
+ wasdMode: true,
9
+ }
10
+
11
+ const DEFAULT_GROUP_COLORS = [
12
+ "#3853F1",
13
+ "#F6E54C",
14
+ "#39D33C",
15
+ "#CF24CF",
16
+ "#22E3ED",
17
+ "#EF3029",
18
+ ]
19
+
20
+ export const SettingsContext = createContext(defaultSettings)
21
+
22
+ const pullSettingsFromLocalStorage = () => {
23
+ if (!window || !window.localStorage) return {}
24
+ let settings = {}
25
+ for (let i = 0; i < window.localStorage.length; i++) {
26
+ const key = window.localStorage.key(i)
27
+ if (key.startsWith("settings_")) {
28
+ try {
29
+ settings[key.replace("settings_", "")] = JSON.parse(
30
+ window.localStorage.getItem(key)
31
+ )
32
+ } catch (e) { }
33
+ }
34
+ }
35
+ return settings
36
+ }
37
+
38
+ export const useSettings = () => useContext(SettingsContext)
39
+
40
+ export const SettingsProvider = ({ children, clsColors, groupColors = DEFAULT_GROUP_COLORS }) => {
41
+ const [state, changeState] = useState({
42
+ ...() => pullSettingsFromLocalStorage(),
43
+ clsColors,
44
+ groupColors,
45
+ })
46
+ const changeSetting = (setting: string, value: any) => {
47
+ changeState({ ...state, [setting]: value })
48
+ window.localStorage.setItem(`settings_${setting}`, JSON.stringify(value))
49
+ }
50
+ return (
51
+ <SettingsContext.Provider value={{ ...state, changeSetting }}>
52
+ {children}
53
+ </SettingsContext.Provider>
54
+ )
55
+ }
56
+
57
+ export default SettingsProvider
@@ -0,0 +1,44 @@
1
+ import React from "react"
2
+ import TextField from "@mui/material/TextField"
3
+ import { makeStyles } from "@mui/styles"
4
+ import { createTheme, ThemeProvider } from "@mui/material/styles"
5
+
6
+ const theme = createTheme()
7
+ const useStyles = makeStyles((theme) => ({
8
+ shortcutKeyFieldWrapper: {
9
+ paddingTop: 8,
10
+ display: "inline-flex",
11
+ width: "100%",
12
+ },
13
+ shortcutKeyText: {
14
+ lineHeight: 0,
15
+ },
16
+ shortcutTextfield: {
17
+ width: "100%",
18
+ boxSizing: "border-box",
19
+ textAlign: "center",
20
+ },
21
+ }))
22
+
23
+ const ShortcutField = ({ actionId, actionName, keyName, onChangeShortcut }) => {
24
+ const classes = useStyles()
25
+
26
+ return (
27
+ <ThemeProvider theme={theme}>
28
+ <div className={classes.shortcutKeyFieldWrapper}>
29
+ <TextField
30
+ variant="outlined"
31
+ label={actionName}
32
+ className={classes.shortcutTextfield}
33
+ value={keyName}
34
+ onKeyPress={(e) => {
35
+ onChangeShortcut(actionId, e.key)
36
+ e.stopPropagation()
37
+ }}
38
+ />
39
+ </div>
40
+ </ThemeProvider>
41
+ )
42
+ }
43
+
44
+ export default ShortcutField
@@ -0,0 +1,129 @@
1
+ import React, { useState, useEffect } from "react"
2
+ import SidebarBoxContainer from "../SidebarBoxContainer"
3
+ import { setIn } from "seamless-immutable"
4
+ import ShortcutField from "./ShortcutField"
5
+
6
+ const defaultShortcuts = {
7
+ select: {
8
+ action: {
9
+ type: "SELECT_TOOL",
10
+ },
11
+ name: "Select Region",
12
+ key: "Escape",
13
+ },
14
+ zoom: {
15
+ action: {
16
+ type: "SELECT_TOOL",
17
+ },
18
+ name: "Zoom In/Out",
19
+ key: "z",
20
+ },
21
+ "create-point": {
22
+ action: {
23
+ type: "SELECT_TOOL",
24
+ },
25
+ name: "Create Point",
26
+ },
27
+ "create-box": {
28
+ action: {
29
+ type: "SELECT_TOOL",
30
+ },
31
+ name: "Add Bounding Box",
32
+ key: "b",
33
+ },
34
+ pan: {
35
+ action: {
36
+ type: "SELECT_TOOL",
37
+ },
38
+ name: "Pan",
39
+ },
40
+ "create-polygon": {
41
+ action: {
42
+ type: "SELECT_TOOL",
43
+ },
44
+ name: "Create Polygon",
45
+ },
46
+ "create-pixel": {
47
+ action: {
48
+ type: "SELECT_TOOL",
49
+ },
50
+ name: "Create Pixel",
51
+ },
52
+ "prev-image": {
53
+ action: {
54
+ type: "HEADER_BUTTON_CLICKED",
55
+ buttonName: "Prev",
56
+ },
57
+ name: "Previous Image",
58
+ key: "a",
59
+ },
60
+ "next-image": {
61
+ action: {
62
+ type: "HEADER_BUTTON_CLICKED",
63
+ buttonName: "Next",
64
+ },
65
+ name: "Next Image",
66
+ key: "d", //"ArrowRight"
67
+ },
68
+ }
69
+
70
+ export default ({ onShortcutActionDispatched }) => {
71
+ const [shortcuts, setShortcuts] = useState({}) // useLocalStorage
72
+
73
+ useEffect(() => {
74
+ const newShortcuts = { ...shortcuts }
75
+ for (const actionId of Object.keys(defaultShortcuts)) {
76
+ if (!newShortcuts[actionId]) {
77
+ newShortcuts[actionId] = defaultShortcuts[actionId]
78
+ }
79
+ }
80
+ setShortcuts(newShortcuts)
81
+ }, [])
82
+
83
+ const onChangeShortcut = (actionId, keyName) => {
84
+ setShortcuts(setIn(shortcuts, [actionId, "key"], keyName))
85
+ }
86
+
87
+ useEffect(() => {
88
+ const handleKeyPress = (e) => {
89
+ for (const actionId in shortcuts) {
90
+ const shortcut = shortcuts[actionId]
91
+ if (!shortcut || !shortcut.key) {
92
+ continue
93
+ }
94
+ if (e.key === shortcut.key) {
95
+ onShortcutActionDispatched({
96
+ ...shortcut.action,
97
+ selectedTool: actionId,
98
+ })
99
+ }
100
+ }
101
+ }
102
+
103
+ window.addEventListener("keypress", handleKeyPress)
104
+
105
+ return () => {
106
+ window.removeEventListener("keypress", handleKeyPress)
107
+ document.activeElement.blur()
108
+ }
109
+ }, [shortcuts])
110
+
111
+ return (
112
+ <SidebarBoxContainer title="Shortcuts">
113
+ {Object.keys(shortcuts)
114
+ .map((actionId, index) => {
115
+ if (!shortcuts[actionId]) return null
116
+ return (
117
+ <ShortcutField
118
+ key={actionId}
119
+ actionId={actionId}
120
+ actionName={shortcuts[actionId].name}
121
+ keyName={shortcuts[actionId].key || ""}
122
+ onChangeShortcut={onChangeShortcut}
123
+ />
124
+ )
125
+ })
126
+ .filter(Boolean)}
127
+ </SidebarBoxContainer>
128
+ )
129
+ }