@searpent/react-image-annotate 2.0.75 → 2.0.77

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 +899 -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/{Editor → src/Editor}/annotation-plugin/annotation.css +20 -0
  41. package/src/Editor/annotation-plugin/annotation.js +546 -0
  42. package/src/Editor/index.js +50 -0
  43. package/src/Editor/readOnly.js +31 -0
  44. package/{Editor → src/Editor}/tools.js +3 -2
  45. package/src/Errorer/index.js +13 -0
  46. package/src/FullImageSegmentationAnnotator/hard1.story.jpg +0 -0
  47. package/src/FullImageSegmentationAnnotator/hard2.story.jpg +0 -0
  48. package/src/FullImageSegmentationAnnotator/hard3.story.jpg +0 -0
  49. package/src/FullImageSegmentationAnnotator/index.js +7 -0
  50. package/src/FullImageSegmentationAnnotator/index.story.js +177 -0
  51. package/src/FullImageSegmentationAnnotator/orange.story.png +0 -0
  52. package/src/GroupSelectorSidebarBox/index.js +48 -0
  53. package/src/GroupsEditorSidebarBox/index.js +108 -0
  54. package/src/HelpSidebarBox/index.js +43 -0
  55. package/src/HighlightBox/index.js +143 -0
  56. package/src/HistorySidebarBox/index.js +78 -0
  57. package/src/ImageCanvas/dancing-man.story.jpg +0 -0
  58. package/src/ImageCanvas/index.js +515 -0
  59. package/src/ImageCanvas/index.story.js +314 -0
  60. package/src/ImageCanvas/mouse_mask.story.png +0 -0
  61. package/src/ImageCanvas/region-tools.js +171 -0
  62. package/src/ImageCanvas/seves_desk.story.jpg +0 -0
  63. package/{ImageCanvas → src/ImageCanvas}/styles.js +8 -12
  64. package/src/ImageCanvas/use-mouse.js +168 -0
  65. package/src/ImageCanvas/use-project-box.js +23 -0
  66. package/src/ImageCanvas/use-wasd-mode.js +50 -0
  67. package/src/ImageMask/index.js +127 -0
  68. package/src/ImageMask/load-image.js +32 -0
  69. package/src/ImageSelectorSidebarBox/index.js +54 -0
  70. package/src/KeyframeTimeline/get-time-string.js +25 -0
  71. package/src/KeyframeTimeline/index.js +223 -0
  72. package/src/KeyframesSelectorSidebarBox/index.js +93 -0
  73. package/src/LandingPage/content.md +57 -0
  74. package/src/LandingPage/github-markdown.css +964 -0
  75. package/src/LandingPage/index.js +147 -0
  76. package/src/Locker/index.js +13 -0
  77. package/src/MainLayout/RightSidebarItemsWrapper.js +21 -0
  78. package/src/MainLayout/icon-dictionary.js +79 -0
  79. package/src/MainLayout/index.js +564 -0
  80. package/src/MainLayout/index.story.js +240 -0
  81. package/{MainLayout → src/MainLayout}/styles.js +7 -6
  82. package/src/MainLayout/types.js +171 -0
  83. package/src/MainLayout/use-implied-video-regions.js +17 -0
  84. package/src/MetadataEditorSidebarBox/index.js +160 -0
  85. package/src/PageSelector/index.js +159 -0
  86. package/src/PointDistances/index.js +90 -0
  87. package/src/PreventScrollToParents/index.js +48 -0
  88. package/src/PreventScrollToParents/index.story.js +23 -0
  89. package/src/RegionLabel/index.js +236 -0
  90. package/{RegionLabel → src/RegionLabel}/styles.js +15 -12
  91. package/src/RegionSelectAndTransformBoxes/index.js +236 -0
  92. package/src/RegionSelectorSidebarBox/index.js +220 -0
  93. package/{RegionSelectorSidebarBox → src/RegionSelectorSidebarBox}/styles.js +14 -13
  94. package/src/RegionShapes/index.js +254 -0
  95. package/src/RegionTags/index.js +136 -0
  96. package/src/SettingsDialog/index.js +58 -0
  97. package/src/SettingsProvider/index.js +57 -0
  98. package/src/Shortcuts/ShortcutField.js +44 -0
  99. package/src/Shortcuts/index.js +129 -0
  100. package/src/ShortcutsManager/index.js +162 -0
  101. package/src/Sidebar/index.js +117 -0
  102. package/src/SidebarBoxContainer/index.js +93 -0
  103. package/src/SmallToolButton/index.js +57 -0
  104. package/src/TagsSidebarBox/index.js +93 -0
  105. package/src/TaskDescriptionSidebarBox/index.js +43 -0
  106. package/src/Theme/index.js +36 -0
  107. package/src/VideoOrImageCanvasBackground/index.js +170 -0
  108. package/src/colors.js +32 -0
  109. package/src/hooks/use-colors.js +95 -0
  110. package/src/hooks/use-event-callback.js +11 -0
  111. package/src/hooks/use-exclude-pattern.js +27 -0
  112. package/src/hooks/use-load-image.js +21 -0
  113. package/src/hooks/use-window-size.js +46 -0
  114. package/{hooks → src/hooks}/xpattern.js +1 -1
  115. package/src/hooks/xpattern.png +0 -0
  116. package/src/index.js +18 -0
  117. package/src/lib.js +7 -0
  118. package/src/screenshot.png +0 -0
  119. package/src/site.css +5 -0
  120. package/src/stories.js +2 -0
  121. package/src/utils/blocks-to-article.js +61 -0
  122. package/{utils → src/utils}/blocks-to-article.test.js +8 -5
  123. package/{utils → src/utils}/default-locked-until.js +1 -2
  124. package/{utils → src/utils}/filter-only-unique.js +1 -1
  125. package/src/utils/get-from-local-storage.js +7 -0
  126. package/src/utils/get-hotkey-help-text.js +11 -0
  127. package/src/utils/get-landmarks-with-transform.js +23 -0
  128. package/src/utils/photosToImages.js +67 -0
  129. package/src/utils/regions-groups.js +19 -0
  130. package/src/utils/regions-to-blocks.js +16 -0
  131. package/src/utils/saveable-actions-enum.js +5 -0
  132. package/src/utils/set-in-local-storage.js +6 -0
  133. package/src/utils/sleep.js +3 -0
  134. package/src/utils/uuid-to-hash.js +5 -0
  135. package/Annotator/exampleImages.js +0 -41
  136. package/Annotator/examplePhotos.js +0 -6980
  137. package/Annotator/index.js +0 -417
  138. package/Annotator/reducers/combine-reducers.js +0 -14
  139. package/Annotator/reducers/convert-expanding-line-to-polygon.js +0 -73
  140. package/Annotator/reducers/general-reducer.js +0 -1430
  141. package/Annotator/reducers/get-active-image.js +0 -27
  142. package/Annotator/reducers/get-implied-video-regions.js +0 -180
  143. package/Annotator/reducers/history-handler.js +0 -38
  144. package/Annotator/reducers/image-reducer.js +0 -20
  145. package/Annotator/reducers/video-reducer.js +0 -88
  146. package/ClassSelectionMenu/index.js +0 -140
  147. package/Crosshairs/index.js +0 -53
  148. package/DebugSidebarBox/index.js +0 -20
  149. package/DemoSite/Editor.js +0 -194
  150. package/DemoSite/ErrorBoundaryDialog.js +0 -64
  151. package/DemoSite/index.js +0 -40
  152. package/Editor/annotation-plugin/annotation.js +0 -647
  153. package/Editor/index.js +0 -93
  154. package/Editor/readOnly.js +0 -73
  155. package/Errorer/index.js +0 -11
  156. package/FullImageSegmentationAnnotator/index.js +0 -7
  157. package/GroupSelectorSidebarBox/index.js +0 -63
  158. package/GroupsEditorSidebarBox/index.js +0 -138
  159. package/HelpSidebarBox/index.js +0 -58
  160. package/HighlightBox/index.js +0 -102
  161. package/HistorySidebarBox/index.js +0 -71
  162. package/ImageCanvas/index.js +0 -441
  163. package/ImageCanvas/region-tools.js +0 -165
  164. package/ImageCanvas/use-mouse.js +0 -180
  165. package/ImageCanvas/use-project-box.js +0 -27
  166. package/ImageCanvas/use-wasd-mode.js +0 -62
  167. package/ImageMask/index.js +0 -133
  168. package/ImageMask/load-image.js +0 -25
  169. package/ImageSelectorSidebarBox/index.js +0 -60
  170. package/KeyframeTimeline/get-time-string.js +0 -27
  171. package/KeyframeTimeline/index.js +0 -233
  172. package/KeyframesSelectorSidebarBox/index.js +0 -93
  173. package/LandingPage/index.js +0 -159
  174. package/Locker/index.js +0 -11
  175. package/MainLayout/RightSidebarItemsWrapper.js +0 -19
  176. package/MainLayout/icon-dictionary.js +0 -104
  177. package/MainLayout/index.js +0 -526
  178. package/MainLayout/types.js +0 -0
  179. package/MainLayout/use-implied-video-regions.js +0 -13
  180. package/MetadataEditorSidebarBox/index.js +0 -231
  181. package/PageSelector/index.js +0 -180
  182. package/PointDistances/index.js +0 -73
  183. package/PreventScrollToParents/index.js +0 -51
  184. package/RegionLabel/index.js +0 -232
  185. package/RegionSelectAndTransformBoxes/index.js +0 -169
  186. package/RegionSelectorSidebarBox/index.js +0 -254
  187. package/RegionShapes/index.js +0 -294
  188. package/RegionTags/index.js +0 -144
  189. package/SettingsDialog/index.js +0 -52
  190. package/SettingsProvider/index.js +0 -60
  191. package/Shortcuts/ShortcutField.js +0 -46
  192. package/Shortcuts/index.js +0 -133
  193. package/ShortcutsManager/index.js +0 -155
  194. package/Sidebar/index.js +0 -69
  195. package/SidebarBoxContainer/index.js +0 -93
  196. package/SmallToolButton/index.js +0 -42
  197. package/TagsSidebarBox/index.js +0 -105
  198. package/TaskDescriptionSidebarBox/index.js +0 -58
  199. package/Theme/index.js +0 -30
  200. package/VideoOrImageCanvasBackground/index.js +0 -151
  201. package/colors.js +0 -14
  202. package/hooks/use-colors.js +0 -97
  203. package/hooks/use-event-callback.js +0 -10
  204. package/hooks/use-exclude-pattern.js +0 -24
  205. package/hooks/use-load-image.js +0 -26
  206. package/hooks/use-window-size.js +0 -46
  207. package/index.js +0 -3
  208. package/lib.js +0 -3
  209. package/stories.js +0 -5
  210. package/utils/blocks-to-article.js +0 -60
  211. package/utils/get-from-local-storage.js +0 -7
  212. package/utils/get-hotkey-help-text.js +0 -9
  213. package/utils/get-landmarks-with-transform.js +0 -40
  214. package/utils/photosToImages.js +0 -85
  215. package/utils/regions-groups.js +0 -28
  216. package/utils/regions-to-blocks.js +0 -18
  217. package/utils/saveable-actions-enum.js +0 -3
  218. package/utils/set-in-local-storage.js +0 -3
  219. package/utils/sleep.js +0 -7
  220. package/utils/uuid-to-hash.js +0 -5
  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
+ }