@searpent/react-image-annotate 2.0.4 → 2.0.7

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 (195) hide show
  1. package/Annotator/examplePhotos.js +6976 -0
  2. package/Annotator/index.js +262 -0
  3. package/Annotator/reducers/combine-reducers.js +14 -0
  4. package/Annotator/reducers/convert-expanding-line-to-polygon.js +73 -0
  5. package/{src/Annotator → Annotator}/reducers/fix-twisted.js +3 -5
  6. package/Annotator/reducers/general-reducer.js +1152 -0
  7. package/Annotator/reducers/get-active-image.js +27 -0
  8. package/Annotator/reducers/get-implied-video-regions.js +180 -0
  9. package/Annotator/reducers/history-handler.js +38 -0
  10. package/Annotator/reducers/image-reducer.js +20 -0
  11. package/Annotator/reducers/video-reducer.js +88 -0
  12. package/ClassSelectionMenu/index.js +135 -0
  13. package/Crosshairs/index.js +53 -0
  14. package/DebugSidebarBox/index.js +20 -0
  15. package/DemoSite/Editor.js +194 -0
  16. package/DemoSite/ErrorBoundaryDialog.js +64 -0
  17. package/DemoSite/index.js +40 -0
  18. package/{src/Editor → Editor}/annotation-plugin/annotation.css +0 -0
  19. package/Editor/annotation-plugin/annotation.js +613 -0
  20. package/Editor/index.js +57 -0
  21. package/{src/Editor → Editor}/tools.js +2 -3
  22. package/FullImageSegmentationAnnotator/index.js +7 -0
  23. package/GroupSelectorSidebarBox/index.js +63 -0
  24. package/HighlightBox/index.js +99 -0
  25. package/HistorySidebarBox/index.js +71 -0
  26. package/ImageCanvas/index.js +429 -0
  27. package/ImageCanvas/region-tools.js +165 -0
  28. package/{src/ImageCanvas → ImageCanvas}/styles.js +12 -8
  29. package/ImageCanvas/use-mouse.js +180 -0
  30. package/ImageCanvas/use-project-box.js +27 -0
  31. package/ImageCanvas/use-wasd-mode.js +62 -0
  32. package/ImageMask/index.js +133 -0
  33. package/ImageMask/load-image.js +25 -0
  34. package/ImageSelectorSidebarBox/index.js +60 -0
  35. package/KeyframeTimeline/get-time-string.js +27 -0
  36. package/KeyframeTimeline/index.js +233 -0
  37. package/KeyframesSelectorSidebarBox/index.js +93 -0
  38. package/LandingPage/index.js +159 -0
  39. package/MainLayout/icon-dictionary.js +104 -0
  40. package/MainLayout/index.js +469 -0
  41. package/{src/MainLayout → MainLayout}/styles.js +6 -7
  42. package/MainLayout/types.js +0 -0
  43. package/MainLayout/use-implied-video-regions.js +13 -0
  44. package/MetadataEditorSidebarBox/index.js +126 -0
  45. package/PageSelector/index.js +67 -0
  46. package/{src/PageSelector → PageSelector}/page-selector.css +0 -0
  47. package/PointDistances/index.js +73 -0
  48. package/PreventScrollToParents/index.js +51 -0
  49. package/RegionLabel/index.js +205 -0
  50. package/{src/RegionLabel → RegionLabel}/styles.js +12 -15
  51. package/RegionSelectAndTransformBoxes/index.js +167 -0
  52. package/RegionSelectorSidebarBox/index.js +248 -0
  53. package/{src/RegionSelectorSidebarBox → RegionSelectorSidebarBox}/styles.js +13 -14
  54. package/RegionShapes/index.js +294 -0
  55. package/RegionTags/index.js +142 -0
  56. package/SettingsDialog/index.js +52 -0
  57. package/SettingsProvider/index.js +58 -0
  58. package/Shortcuts/ShortcutField.js +46 -0
  59. package/Shortcuts/index.js +133 -0
  60. package/ShortcutsManager/index.js +155 -0
  61. package/Sidebar/index.js +69 -0
  62. package/SidebarBoxContainer/index.js +93 -0
  63. package/SmallToolButton/index.js +42 -0
  64. package/TagsSidebarBox/index.js +105 -0
  65. package/TaskDescriptionSidebarBox/index.js +58 -0
  66. package/Theme/index.js +30 -0
  67. package/VideoOrImageCanvasBackground/index.js +151 -0
  68. package/colors.js +14 -0
  69. package/hooks/use-colors.js +101 -0
  70. package/hooks/use-event-callback.js +10 -0
  71. package/hooks/use-exclude-pattern.js +24 -0
  72. package/hooks/use-load-image.js +26 -0
  73. package/hooks/use-window-size.js +46 -0
  74. package/{src/hooks → hooks}/xpattern.js +1 -1
  75. package/index.js +3 -0
  76. package/lib.js +3 -0
  77. package/package.json +1 -1
  78. package/stories.js +5 -0
  79. package/{src/utils → utils}/filter-only-unique.js +1 -1
  80. package/utils/get-from-local-storage.js +7 -0
  81. package/utils/get-hotkey-help-text.js +9 -0
  82. package/utils/get-landmarks-with-transform.js +40 -0
  83. package/utils/photosToImages.js +53 -0
  84. package/utils/regions-to-blocks.js +16 -0
  85. package/utils/set-in-local-storage.js +3 -0
  86. package/.babelrc +0 -6
  87. package/.env +0 -1
  88. package/.flowconfig +0 -2
  89. package/.github/workflows/release-on-master.yml +0 -32
  90. package/.github/workflows/test.yml +0 -16
  91. package/.prettierrc +0 -3
  92. package/.releaserc.js +0 -18
  93. package/.storybook/addons.js +0 -2
  94. package/.storybook/config.js +0 -16
  95. package/LICENSE +0 -21
  96. package/public/favicon.ico +0 -0
  97. package/public/index.html +0 -38
  98. package/src/Annotator/bike-pic.png +0 -0
  99. package/src/Annotator/bike-pic2.png +0 -0
  100. package/src/Annotator/dab-keyframes.story.json +0 -1
  101. package/src/Annotator/examplePhotos.js +0 -7601
  102. package/src/Annotator/index.js +0 -291
  103. package/src/Annotator/index.story.js +0 -807
  104. package/src/Annotator/poses.story.js +0 -150
  105. package/src/Annotator/reducers/combine-reducers.js +0 -7
  106. package/src/Annotator/reducers/convert-expanding-line-to-polygon.js +0 -53
  107. package/src/Annotator/reducers/general-reducer.js +0 -996
  108. package/src/Annotator/reducers/get-active-image.js +0 -21
  109. package/src/Annotator/reducers/get-implied-video-regions.js +0 -115
  110. package/src/Annotator/reducers/history-handler.js +0 -60
  111. package/src/Annotator/reducers/image-reducer.js +0 -23
  112. package/src/Annotator/reducers/video-reducer.js +0 -85
  113. package/src/Annotator/video.story.js +0 -51
  114. package/src/ClassSelectionMenu/index.js +0 -108
  115. package/src/Crosshairs/index.js +0 -64
  116. package/src/DebugSidebarBox/index.js +0 -36
  117. package/src/DemoSite/Editor.js +0 -235
  118. package/src/DemoSite/ErrorBoundaryDialog.js +0 -34
  119. package/src/DemoSite/index.js +0 -41
  120. package/src/DemoSite/index.story.js +0 -10
  121. package/src/DemoSite/simple-segmentation-example.json +0 -572
  122. package/src/Editor/annotation-plugin/annotation.js +0 -515
  123. package/src/Editor/index.js +0 -24
  124. package/src/FullImageSegmentationAnnotator/hard1.story.jpg +0 -0
  125. package/src/FullImageSegmentationAnnotator/hard2.story.jpg +0 -0
  126. package/src/FullImageSegmentationAnnotator/hard3.story.jpg +0 -0
  127. package/src/FullImageSegmentationAnnotator/index.js +0 -7
  128. package/src/FullImageSegmentationAnnotator/index.story.js +0 -177
  129. package/src/FullImageSegmentationAnnotator/orange.story.png +0 -0
  130. package/src/GroupSelectorSidebarBox/index.js +0 -48
  131. package/src/HighlightBox/index.js +0 -143
  132. package/src/HistorySidebarBox/index.js +0 -78
  133. package/src/ImageCanvas/dancing-man.story.jpg +0 -0
  134. package/src/ImageCanvas/index.js +0 -494
  135. package/src/ImageCanvas/index.story.js +0 -314
  136. package/src/ImageCanvas/mouse_mask.story.png +0 -0
  137. package/src/ImageCanvas/region-tools.js +0 -171
  138. package/src/ImageCanvas/seves_desk.story.jpg +0 -0
  139. package/src/ImageCanvas/use-mouse.js +0 -168
  140. package/src/ImageCanvas/use-project-box.js +0 -23
  141. package/src/ImageCanvas/use-wasd-mode.js +0 -50
  142. package/src/ImageMask/index.js +0 -127
  143. package/src/ImageMask/load-image.js +0 -32
  144. package/src/ImageSelectorSidebarBox/index.js +0 -54
  145. package/src/KeyframeTimeline/get-time-string.js +0 -25
  146. package/src/KeyframeTimeline/index.js +0 -223
  147. package/src/KeyframesSelectorSidebarBox/index.js +0 -93
  148. package/src/LandingPage/content.md +0 -57
  149. package/src/LandingPage/github-markdown.css +0 -964
  150. package/src/LandingPage/index.js +0 -147
  151. package/src/MainLayout/icon-dictionary.js +0 -79
  152. package/src/MainLayout/index.js +0 -510
  153. package/src/MainLayout/index.story.js +0 -240
  154. package/src/MainLayout/types.js +0 -164
  155. package/src/MainLayout/use-implied-video-regions.js +0 -17
  156. package/src/MetadataEditorSidebarBox/index.js +0 -98
  157. package/src/PageSelector/index.js +0 -76
  158. package/src/PointDistances/index.js +0 -90
  159. package/src/PreventScrollToParents/index.js +0 -48
  160. package/src/PreventScrollToParents/index.story.js +0 -23
  161. package/src/RegionLabel/index.js +0 -222
  162. package/src/RegionSelectAndTransformBoxes/index.js +0 -234
  163. package/src/RegionSelectorSidebarBox/index.js +0 -216
  164. package/src/RegionShapes/index.js +0 -254
  165. package/src/RegionTags/index.js +0 -134
  166. package/src/SettingsDialog/index.js +0 -58
  167. package/src/SettingsProvider/index.js +0 -48
  168. package/src/Shortcuts/ShortcutField.js +0 -44
  169. package/src/Shortcuts/index.js +0 -129
  170. package/src/ShortcutsManager/index.js +0 -162
  171. package/src/Sidebar/index.js +0 -117
  172. package/src/SidebarBoxContainer/index.js +0 -93
  173. package/src/SmallToolButton/index.js +0 -57
  174. package/src/TagsSidebarBox/index.js +0 -93
  175. package/src/TaskDescriptionSidebarBox/index.js +0 -43
  176. package/src/Theme/index.js +0 -36
  177. package/src/VideoOrImageCanvasBackground/index.js +0 -170
  178. package/src/colors.js +0 -32
  179. package/src/hooks/use-colors.js +0 -74
  180. package/src/hooks/use-event-callback.js +0 -11
  181. package/src/hooks/use-exclude-pattern.js +0 -27
  182. package/src/hooks/use-load-image.js +0 -21
  183. package/src/hooks/use-window-size.js +0 -46
  184. package/src/hooks/xpattern.png +0 -0
  185. package/src/index.js +0 -18
  186. package/src/lib.js +0 -7
  187. package/src/screenshot.png +0 -0
  188. package/src/site.css +0 -5
  189. package/src/stories.js +0 -2
  190. package/src/utils/get-from-local-storage.js +0 -7
  191. package/src/utils/get-hotkey-help-text.js +0 -11
  192. package/src/utils/get-landmarks-with-transform.js +0 -23
  193. package/src/utils/photosToImages.js +0 -40
  194. package/src/utils/regions-to-blocks.js +0 -14
  195. package/src/utils/set-in-local-storage.js +0 -6
@@ -1,996 +0,0 @@
1
- // @flow
2
- import type { MainLayoutState, Action } from "../../MainLayout/types"
3
- import { moveRegion } from "../../ImageCanvas/region-tools.js"
4
- import { getIn, setIn, updateIn } from "seamless-immutable"
5
- import moment from "moment"
6
- import isEqual from "lodash/isEqual"
7
- import getActiveImage from "./get-active-image"
8
- import { saveToHistory } from "./history-handler.js"
9
- import colors from "../../colors"
10
- import fixTwisted from "./fix-twisted"
11
- import convertExpandingLineToPolygon from "./convert-expanding-line-to-polygon"
12
- import clamp from "clamp"
13
- import getLandmarksWithTransform from "../../utils/get-landmarks-with-transform"
14
- import setInLocalStorage from "../../utils/set-in-local-storage"
15
- import onlyUnique from "../../utils/filter-only-unique"
16
-
17
- const getRandomId = () => Math.random().toString().split(".")[1]
18
-
19
- export default (state: MainLayoutState, action: Action) => {
20
- if (
21
- state.allowedArea &&
22
- state.selectedTool !== "modify-allowed-area" &&
23
- ["MOUSE_DOWN", "MOUSE_UP", "MOUSE_MOVE"].includes(action.type)
24
- ) {
25
- const aa = state.allowedArea
26
- action.x = clamp(action.x, aa.x, aa.x + aa.w)
27
- action.y = clamp(action.y, aa.y, aa.y + aa.h)
28
- }
29
-
30
- if (action.type === "ON_CLS_ADDED" && action.cls && action.cls !== "") {
31
- const oldRegionClsList = state.regionClsList
32
- const newState = {
33
- ...state,
34
- regionClsList: oldRegionClsList.concat(action.cls),
35
- }
36
- return newState
37
- }
38
-
39
- // Throttle certain actions
40
- if (action.type === "MOUSE_MOVE") {
41
- if (Date.now() - ((state: any).lastMouseMoveCall || 0) < 16) return state
42
- state = setIn(state, ["lastMouseMoveCall"], Date.now())
43
- }
44
- if (!action.type.includes("MOUSE")) {
45
- state = setIn(state, ["lastAction"], action)
46
- }
47
-
48
- const { currentImageIndex, pathToActiveImage, activeImage } =
49
- getActiveImage(state)
50
-
51
- const getRegionIndex = (region) => {
52
- const regionId =
53
- typeof region === "string" || typeof region === "number"
54
- ? region
55
- : region.id
56
- if (!activeImage) return null
57
- const regionIndex = (activeImage.regions || []).findIndex(
58
- (r) => r.id === regionId
59
- )
60
- return regionIndex === -1 ? null : regionIndex
61
- }
62
- const getRegion = (regionId) => {
63
- if (!activeImage) return null
64
- const regionIndex = getRegionIndex(regionId)
65
- if (regionIndex === null) return [null, null]
66
- const region = activeImage.regions[regionIndex]
67
- return [region, regionIndex]
68
- }
69
- const modifyRegion = (regionId, obj) => {
70
- const [region, regionIndex] = getRegion(regionId)
71
- if (!region) return state
72
- if (obj !== null) {
73
- return setIn(state, [...pathToActiveImage, "regions", regionIndex], {
74
- ...region,
75
- ...obj,
76
- })
77
- } else {
78
- // delete region
79
- const regions = activeImage.regions
80
- return setIn(
81
- state,
82
- [...pathToActiveImage, "regions"],
83
- (regions || []).filter((r) => r.id !== region.id)
84
- )
85
- }
86
- }
87
- const unselectRegions = (state: MainLayoutState) => {
88
- if (!activeImage) return state
89
- return setIn(
90
- state,
91
- [...pathToActiveImage, "regions"],
92
- (activeImage.regions || []).map((r) => ({
93
- ...r,
94
- highlighted: false,
95
- }))
96
- )
97
- }
98
-
99
- const closeEditors = (state: MainLayoutState) => {
100
- if (currentImageIndex === null) return state
101
- return setIn(
102
- state,
103
- [...pathToActiveImage, "regions"],
104
- (activeImage.regions || []).map((r) => ({
105
- ...r,
106
- editingLabels: false,
107
- }))
108
- )
109
- }
110
-
111
- const setNewImage = (img: string | Object, index: number) => {
112
- let { src, frameTime } = typeof img === "object" ? img : { src: img }
113
- return setIn(
114
- setIn(state, ["selectedImage"], index),
115
- ["selectedImageFrameTime"],
116
- frameTime
117
- )
118
- }
119
-
120
- switch (action.type) {
121
- case "@@INIT": {
122
- return state
123
- }
124
- case "SELECT_IMAGE": {
125
- return setNewImage(action.image, action.imageIndex)
126
- }
127
- case "SELECT_CLASSIFICATION": {
128
- return setIn(state, ["selectedCls"], action.cls)
129
- }
130
- case "CHANGE_REGION": {
131
- const regionIndex = getRegionIndex(action.region)
132
- if (regionIndex === null) return state
133
- const oldRegion = activeImage.regions[regionIndex]
134
- if (oldRegion.cls !== action.region.cls) {
135
- state = saveToHistory(state, "Change Region Classification")
136
- const clsIndex = state.regionClsList.indexOf(action.region.cls)
137
- if (clsIndex !== -1) {
138
- state = setIn(state, ["selectedCls"], action.region.cls)
139
- action.region.color = colors[clsIndex % colors.length]
140
- }
141
- }
142
- if (!isEqual(oldRegion.tags, action.region.tags)) {
143
- state = saveToHistory(state, "Change Region Tags")
144
- }
145
- if (!isEqual(oldRegion.comment, action.region.comment)) {
146
- state = saveToHistory(state, "Change Region Comment")
147
- }
148
- return setIn(
149
- state,
150
- [...pathToActiveImage, "regions", regionIndex],
151
- action.region
152
- )
153
- }
154
- case "CHANGE_IMAGE": {
155
- if (!activeImage) return state
156
- const { delta } = action
157
- for (const key of Object.keys(delta)) {
158
- if (key === "cls") saveToHistory(state, "Change Image Class")
159
- if (key === "tags") saveToHistory(state, "Change Image Tags")
160
- state = setIn(state, [...pathToActiveImage, key], delta[key])
161
- }
162
- return state
163
- }
164
- case "SELECT_REGION": {
165
- const { region } = action
166
- const regionIndex = getRegionIndex(action.region)
167
- if (regionIndex === null) return state
168
- const regions = [...(activeImage.regions || [])].map((r) => ({
169
- ...r,
170
- highlighted: r.id === region.id,
171
- groupHighlighted: (r.groupId && r.groupId === region.groupId) ? true : false,
172
- editingLabels: r.id === region.id,
173
- }))
174
-
175
- const selectedGroupIds = regions.filter(i => i.highlighted).map(r => r.groupId || '').filter(onlyUnique);
176
- if (selectedGroupIds.length === 1) {
177
- state = setIn(state, [...pathToActiveImage, "selectedGroupId"], selectedGroupIds[0])
178
- }
179
- if (selectedGroupIds.length === 0) {
180
- state = setIn(state, [...pathToActiveImage, "selectedGroupId"], null)
181
- }
182
- return setIn(state, [...pathToActiveImage, "regions"], regions)
183
- }
184
- case "BEGIN_MOVE_POINT": {
185
- state = closeEditors(state)
186
- return setIn(state, ["mode"], {
187
- mode: "MOVE_REGION",
188
- regionId: action.point.id,
189
- })
190
- }
191
- case "BEGIN_BOX_TRANSFORM": {
192
- const { box, directions } = action
193
- state = closeEditors(state)
194
- if (directions[0] === 0 && directions[1] === 0) {
195
- return setIn(state, ["mode"], { mode: "MOVE_REGION", regionId: box.id })
196
- } else {
197
- return setIn(state, ["mode"], {
198
- mode: "RESIZE_BOX",
199
- regionId: box.id,
200
- freedom: directions,
201
- original: { x: box.x, y: box.y, w: box.w, h: box.h },
202
- })
203
- }
204
- }
205
- case "BEGIN_MOVE_POLYGON_POINT": {
206
- const { polygon, pointIndex } = action
207
- state = closeEditors(state)
208
- if (
209
- state.mode &&
210
- state.mode.mode === "DRAW_POLYGON" &&
211
- pointIndex === 0
212
- ) {
213
- return setIn(
214
- modifyRegion(polygon, {
215
- points: polygon.points.slice(0, -1),
216
- open: false,
217
- }),
218
- ["mode"],
219
- null
220
- )
221
- } else {
222
- state = saveToHistory(state, "Move Polygon Point")
223
- }
224
- return setIn(state, ["mode"], {
225
- mode: "MOVE_POLYGON_POINT",
226
- regionId: polygon.id,
227
- pointIndex,
228
- })
229
- }
230
- case "BEGIN_MOVE_KEYPOINT": {
231
- const { region, keypointId } = action
232
- state = closeEditors(state)
233
- state = saveToHistory(state, "Move Keypoint")
234
- return setIn(state, ["mode"], {
235
- mode: "MOVE_KEYPOINT",
236
- regionId: region.id,
237
- keypointId,
238
- })
239
- }
240
- case "ADD_POLYGON_POINT": {
241
- const { polygon, point, pointIndex } = action
242
- const regionIndex = getRegionIndex(polygon)
243
- if (regionIndex === null) return state
244
- const points = [...polygon.points]
245
- points.splice(pointIndex, 0, point)
246
- return setIn(state, [...pathToActiveImage, "regions", regionIndex], {
247
- ...polygon,
248
- points,
249
- })
250
- }
251
- case "MOUSE_MOVE": {
252
- const { x, y } = action
253
-
254
- if (!state.mode) return state
255
- if (!activeImage) return state
256
- const { mouseDownAt } = state
257
- switch (state.mode.mode) {
258
- case "MOVE_POLYGON_POINT": {
259
- const { pointIndex, regionId } = state.mode
260
- const regionIndex = getRegionIndex(regionId)
261
- if (regionIndex === null) return state
262
- return setIn(
263
- state,
264
- [
265
- ...pathToActiveImage,
266
- "regions",
267
- regionIndex,
268
- "points",
269
- pointIndex,
270
- ],
271
- [x, y]
272
- )
273
- }
274
- case "MOVE_KEYPOINT": {
275
- const { keypointId, regionId } = state.mode
276
- const [region, regionIndex] = getRegion(regionId)
277
- if (regionIndex === null) return state
278
- return setIn(
279
- state,
280
- [
281
- ...pathToActiveImage,
282
- "regions",
283
- regionIndex,
284
- "points",
285
- keypointId,
286
- ],
287
- { ...(region: any).points[keypointId], x, y }
288
- )
289
- }
290
- case "MOVE_REGION": {
291
- const { regionId } = state.mode
292
- if (regionId === "$$allowed_area") {
293
- const {
294
- allowedArea: { w, h },
295
- } = state
296
- return setIn(state, ["allowedArea"], {
297
- x: x - w / 2,
298
- y: y - h / 2,
299
- w,
300
- h,
301
- })
302
- }
303
- const regionIndex = getRegionIndex(regionId)
304
- if (regionIndex === null) return state
305
- return setIn(
306
- state,
307
- [...pathToActiveImage, "regions", regionIndex],
308
- moveRegion(activeImage.regions[regionIndex], x, y)
309
- )
310
- }
311
- case "RESIZE_BOX": {
312
- const {
313
- regionId,
314
- freedom: [xFree, yFree],
315
- original: { x: ox, y: oy, w: ow, h: oh },
316
- } = state.mode
317
-
318
- const dx = xFree === 0 ? ox : xFree === -1 ? Math.min(ox + ow, x) : ox
319
- const dw =
320
- xFree === 0
321
- ? ow
322
- : xFree === -1
323
- ? ow + (ox - dx)
324
- : Math.max(0, ow + (x - ox - ow))
325
- const dy = yFree === 0 ? oy : yFree === -1 ? Math.min(oy + oh, y) : oy
326
- const dh =
327
- yFree === 0
328
- ? oh
329
- : yFree === -1
330
- ? oh + (oy - dy)
331
- : Math.max(0, oh + (y - oy - oh))
332
-
333
- // determine if we should switch the freedom
334
- if (dw <= 0.001) {
335
- state = setIn(state, ["mode", "freedom"], [xFree * -1, yFree])
336
- }
337
- if (dh <= 0.001) {
338
- state = setIn(state, ["mode", "freedom"], [xFree, yFree * -1])
339
- }
340
-
341
- if (regionId === "$$allowed_area") {
342
- return setIn(state, ["allowedArea"], {
343
- x: dx,
344
- w: dw,
345
- y: dy,
346
- h: dh,
347
- })
348
- }
349
-
350
- const regionIndex = getRegionIndex(regionId)
351
- if (regionIndex === null) return state
352
- const box = activeImage.regions[regionIndex]
353
-
354
- return setIn(state, [...pathToActiveImage, "regions", regionIndex], {
355
- ...box,
356
- x: dx,
357
- w: dw,
358
- y: dy,
359
- h: dh,
360
- })
361
- }
362
- case "RESIZE_KEYPOINTS": {
363
- const { regionId, landmarks, centerX, centerY } = state.mode
364
- const distFromCenter = Math.sqrt(
365
- (centerX - x) ** 2 + (centerY - y) ** 2
366
- )
367
- const scale = distFromCenter / 0.15
368
- return modifyRegion(regionId, {
369
- points: getLandmarksWithTransform({
370
- landmarks,
371
- center: { x: centerX, y: centerY },
372
- scale,
373
- }),
374
- })
375
- }
376
- case "DRAW_POLYGON": {
377
- const { regionId } = state.mode
378
- const [region, regionIndex] = getRegion(regionId)
379
- if (!region) return setIn(state, ["mode"], null)
380
- return setIn(
381
- state,
382
- [
383
- ...pathToActiveImage,
384
- "regions",
385
- regionIndex,
386
- "points",
387
- (region: any).points.length - 1,
388
- ],
389
- [x, y]
390
- )
391
- }
392
- case "DRAW_LINE": {
393
- const { regionId } = state.mode
394
- const [region, regionIndex] = getRegion(regionId)
395
- if (!region) return setIn(state, ["mode"], null)
396
- return setIn(state, [...pathToActiveImage, "regions", regionIndex], {
397
- ...region,
398
- x2: x,
399
- y2: y,
400
- })
401
- }
402
- case "DRAW_EXPANDING_LINE": {
403
- const { regionId } = state.mode
404
- const [expandingLine, regionIndex] = getRegion(regionId)
405
- if (!expandingLine) return state
406
- const isMouseDown = Boolean(state.mouseDownAt)
407
- if (isMouseDown) {
408
- // If the mouse is down, set width/angle
409
- const lastPoint = expandingLine.points.slice(-1)[0]
410
- const mouseDistFromLastPoint = Math.sqrt(
411
- (lastPoint.x - x) ** 2 + (lastPoint.y - y) ** 2
412
- )
413
- if (mouseDistFromLastPoint < 0.002 && !lastPoint.width) return state
414
-
415
- const newState = setIn(
416
- state,
417
- [...pathToActiveImage, "regions", regionIndex, "points"],
418
- expandingLine.points.slice(0, -1).concat([
419
- {
420
- ...lastPoint,
421
- width: mouseDistFromLastPoint * 2,
422
- angle: Math.atan2(lastPoint.x - x, lastPoint.y - y),
423
- },
424
- ])
425
- )
426
- return newState
427
- } else {
428
- // If mouse is up, move the next candidate point
429
- return setIn(
430
- state,
431
- [...pathToActiveImage, "regions", regionIndex],
432
- {
433
- ...expandingLine,
434
- candidatePoint: { x, y },
435
- }
436
- )
437
- }
438
-
439
- return state
440
- }
441
- case "SET_EXPANDING_LINE_WIDTH": {
442
- const { regionId } = state.mode
443
- const [expandingLine, regionIndex] = getRegion(regionId)
444
- if (!expandingLine) return state
445
- const lastPoint = expandingLine.points.slice(-1)[0]
446
- const { mouseDownAt } = state
447
- return setIn(
448
- state,
449
- [...pathToActiveImage, "regions", regionIndex, "expandingWidth"],
450
- Math.sqrt((lastPoint.x - x) ** 2 + (lastPoint.y - y) ** 2)
451
- )
452
- }
453
- default:
454
- return state
455
- }
456
- }
457
- case "MOUSE_DOWN": {
458
- if (!activeImage) return state
459
- const { x, y } = action
460
-
461
- state = setIn(state, ["mouseDownAt"], { x, y })
462
-
463
- if (state.mode) {
464
- switch (state.mode.mode) {
465
- case "DRAW_POLYGON": {
466
- const [polygon, regionIndex] = getRegion(state.mode.regionId)
467
- if (!polygon) break
468
- return setIn(
469
- state,
470
- [...pathToActiveImage, "regions", regionIndex],
471
- { ...polygon, points: polygon.points.concat([[x, y]]) }
472
- )
473
- }
474
- case "DRAW_LINE": {
475
- const [line, regionIndex] = getRegion(state.mode.regionId)
476
- if (!line) break
477
- setIn(state, [...pathToActiveImage, "regions", regionIndex], {
478
- ...line,
479
- x2: x,
480
- y2: y,
481
- })
482
- return setIn(state, ["mode"], null)
483
- }
484
- case "DRAW_EXPANDING_LINE": {
485
- const [expandingLine, regionIndex] = getRegion(state.mode.regionId)
486
- if (!expandingLine) break
487
- const lastPoint = expandingLine.points.slice(-1)[0]
488
- if (
489
- expandingLine.points.length > 1 &&
490
- Math.sqrt((lastPoint.x - x) ** 2 + (lastPoint.y - y) ** 2) < 0.002
491
- ) {
492
- if (!lastPoint.width) {
493
- return setIn(state, ["mode"], {
494
- mode: "SET_EXPANDING_LINE_WIDTH",
495
- regionId: state.mode.regionId,
496
- })
497
- } else {
498
- return state
499
- .setIn(
500
- [...pathToActiveImage, "regions", regionIndex],
501
- convertExpandingLineToPolygon(expandingLine)
502
- )
503
- .setIn(["mode"], null)
504
- }
505
- }
506
-
507
- // Create new point
508
- return setIn(
509
- state,
510
- [...pathToActiveImage, "regions", regionIndex, "points"],
511
- expandingLine.points.concat([{ x, y, angle: null, width: null }])
512
- )
513
- }
514
- case "SET_EXPANDING_LINE_WIDTH": {
515
- const [expandingLine, regionIndex] = getRegion(state.mode.regionId)
516
- if (!expandingLine) break
517
- const { expandingWidth } = expandingLine
518
- return state
519
- .setIn(
520
- [...pathToActiveImage, "regions", regionIndex],
521
- convertExpandingLineToPolygon({
522
- ...expandingLine,
523
- points: expandingLine.points.map((p) =>
524
- p.width ? p : { ...p, width: expandingWidth }
525
- ),
526
- expandingWidth: undefined,
527
- })
528
- )
529
- .setIn(["mode"], null)
530
- }
531
- default:
532
- break
533
- }
534
- }
535
-
536
- let newRegion
537
- let defaultRegionCls = state.selectedCls,
538
- defaultRegionColor = "#ff0000"
539
-
540
- const clsIndex = (state.regionClsList || []).indexOf(defaultRegionCls)
541
- if (clsIndex !== -1) {
542
- defaultRegionColor = colors[clsIndex % colors.length]
543
- }
544
-
545
- switch (state.selectedTool) {
546
- case "create-point": {
547
- state = saveToHistory(state, "Create Point")
548
- newRegion = {
549
- type: "point",
550
- x,
551
- y,
552
- highlighted: true,
553
- editingLabels: true,
554
- color: defaultRegionColor,
555
- id: getRandomId(),
556
- cls: defaultRegionCls,
557
- }
558
- break
559
- }
560
- case "create-box": {
561
- state = saveToHistory(state, "Create Box")
562
- newRegion = {
563
- type: "box",
564
- x: x,
565
- y: y,
566
- w: 0,
567
- h: 0,
568
- highlighted: true,
569
- editingLabels: false,
570
- color: defaultRegionColor,
571
- cls: defaultRegionCls,
572
- id: getRandomId(),
573
- }
574
- state = setIn(state, ["mode"], {
575
- mode: "RESIZE_BOX",
576
- editLabelEditorAfter: true,
577
- regionId: newRegion.id,
578
- freedom: [1, 1],
579
- original: { x, y, w: newRegion.w, h: newRegion.h },
580
- isNew: true,
581
- })
582
- break
583
- }
584
- case "create-polygon": {
585
- if (state.mode && state.mode.mode === "DRAW_POLYGON") break
586
- state = saveToHistory(state, "Create Polygon")
587
- newRegion = {
588
- type: "polygon",
589
- points: [
590
- [x, y],
591
- [x, y],
592
- ],
593
- open: true,
594
- highlighted: true,
595
- color: defaultRegionColor,
596
- cls: defaultRegionCls,
597
- id: getRandomId(),
598
- }
599
- state = setIn(state, ["mode"], {
600
- mode: "DRAW_POLYGON",
601
- regionId: newRegion.id,
602
- })
603
- break
604
- }
605
- case "create-expanding-line": {
606
- state = saveToHistory(state, "Create Expanding Line")
607
- newRegion = {
608
- type: "expanding-line",
609
- unfinished: true,
610
- points: [{ x, y, angle: null, width: null }],
611
- open: true,
612
- highlighted: true,
613
- color: defaultRegionColor,
614
- cls: defaultRegionCls,
615
- id: getRandomId(),
616
- }
617
- state = setIn(state, ["mode"], {
618
- mode: "DRAW_EXPANDING_LINE",
619
- regionId: newRegion.id,
620
- })
621
- break
622
- }
623
- case "create-line": {
624
- if (state.mode && state.mode.mode === "DRAW_LINE") break
625
- state = saveToHistory(state, "Create Line")
626
- newRegion = {
627
- type: "line",
628
- x1: x,
629
- y1: y,
630
- x2: x,
631
- y2: y,
632
- highlighted: true,
633
- editingLabels: false,
634
- color: defaultRegionColor,
635
- cls: defaultRegionCls,
636
- id: getRandomId(),
637
- }
638
- state = setIn(state, ["mode"], {
639
- mode: "DRAW_LINE",
640
- regionId: newRegion.id,
641
- })
642
- break
643
- }
644
- case "create-keypoints": {
645
- state = saveToHistory(state, "Create Keypoints")
646
- const [[keypointsDefinitionId, { landmarks, connections }]] =
647
- (Object.entries(state.keypointDefinitions): any)
648
-
649
- newRegion = {
650
- type: "keypoints",
651
- keypointsDefinitionId,
652
- points: getLandmarksWithTransform({
653
- landmarks,
654
- center: { x, y },
655
- scale: 1,
656
- }),
657
- highlighted: true,
658
- editingLabels: false,
659
- id: getRandomId(),
660
- }
661
- state = setIn(state, ["mode"], {
662
- mode: "RESIZE_KEYPOINTS",
663
- landmarks,
664
- centerX: x,
665
- centerY: y,
666
- regionId: newRegion.id,
667
- isNew: true,
668
- })
669
- break
670
- }
671
- default:
672
- break
673
- }
674
-
675
- const regions = [...(getIn(state, pathToActiveImage).regions || [])]
676
- .map((r) =>
677
- setIn(r, ["editingLabels"], false).setIn(["highlighted"], false).setIn(["groupHighlighted"], false)
678
- )
679
- .concat(newRegion ? [newRegion] : [])
680
-
681
- state = setIn(state, [...pathToActiveImage, "selectedGroupId"], null)
682
-
683
- return setIn(state, [...pathToActiveImage, "regions"], regions)
684
- }
685
- case "MOUSE_UP": {
686
- const { x, y } = action
687
-
688
- const { mouseDownAt = { x, y } } = state
689
- if (!state.mode) return state
690
- state = setIn(state, ["mouseDownAt"], null)
691
- switch (state.mode.mode) {
692
- case "RESIZE_BOX": {
693
- if (state.mode.isNew) {
694
- if (
695
- Math.abs(state.mode.original.x - x) < 0.002 ||
696
- Math.abs(state.mode.original.y - y) < 0.002
697
- ) {
698
- return setIn(
699
- modifyRegion(state.mode.regionId, null),
700
- ["mode"],
701
- null
702
- )
703
- }
704
- }
705
- if (state.mode.editLabelEditorAfter) {
706
- return {
707
- ...modifyRegion(state.mode.regionId, { editingLabels: true }),
708
- mode: null,
709
- }
710
- }
711
- }
712
- case "MOVE_REGION":
713
- case "RESIZE_KEYPOINTS":
714
- case "MOVE_POLYGON_POINT": {
715
- return { ...state, mode: null }
716
- }
717
- case "MOVE_KEYPOINT": {
718
- return { ...state, mode: null }
719
- }
720
- case "CREATE_POINT_LINE": {
721
- return state
722
- }
723
- case "DRAW_EXPANDING_LINE": {
724
- const [expandingLine, regionIndex] = getRegion(state.mode.regionId)
725
- if (!expandingLine) return state
726
- let newExpandingLine = expandingLine
727
- const lastPoint =
728
- expandingLine.points.length !== 0
729
- ? expandingLine.points.slice(-1)[0]
730
- : mouseDownAt
731
- let jointStart
732
- if (expandingLine.points.length > 1) {
733
- jointStart = expandingLine.points.slice(-2)[0]
734
- } else {
735
- jointStart = lastPoint
736
- }
737
- const mouseDistFromLastPoint = Math.sqrt(
738
- (lastPoint.x - x) ** 2 + (lastPoint.y - y) ** 2
739
- )
740
- if (mouseDistFromLastPoint > 0.002) {
741
- // The user is drawing has drawn the width for the last point
742
- const newPoints = [...expandingLine.points]
743
- for (let i = 0; i < newPoints.length - 1; i++) {
744
- if (newPoints[i].width) continue
745
- newPoints[i] = {
746
- ...newPoints[i],
747
- width: lastPoint.width,
748
- }
749
- }
750
- newExpandingLine = setIn(
751
- expandingLine,
752
- ["points"],
753
- fixTwisted(newPoints)
754
- )
755
- } else {
756
- return state
757
- }
758
- return setIn(
759
- state,
760
- [...pathToActiveImage, "regions", regionIndex],
761
- newExpandingLine
762
- )
763
- }
764
- default:
765
- return state
766
- }
767
- }
768
- case "OPEN_REGION_EDITOR": {
769
- const { region } = action
770
- const regionIndex = getRegionIndex(action.region)
771
- if (regionIndex === null) return state
772
- const newRegions = setIn(
773
- activeImage.regions.map((r) => ({
774
- ...r,
775
- highlighted: false,
776
- editingLabels: false,
777
- })),
778
- [regionIndex],
779
- {
780
- ...(activeImage.regions || [])[regionIndex],
781
- highlighted: true,
782
- editingLabels: true,
783
- }
784
- )
785
- return setIn(state, [...pathToActiveImage, "regions"], newRegions)
786
- }
787
- case "CLOSE_REGION_EDITOR": {
788
- const { region } = action
789
- const regionIndex = getRegionIndex(action.region)
790
- if (regionIndex === null) return state
791
- return setIn(state, [...pathToActiveImage, "regions", regionIndex], {
792
- ...(activeImage.regions || [])[regionIndex],
793
- editingLabels: false,
794
- })
795
- }
796
- case "DELETE_REGION": {
797
- const regionIndex = getRegionIndex(action.region)
798
- if (regionIndex === null) return state
799
- return setIn(
800
- state,
801
- [...pathToActiveImage, "regions"],
802
- (activeImage.regions || []).filter((r) => r.id !== action.region.id)
803
- )
804
- }
805
- case "DELETE_SELECTED_REGION": {
806
- return setIn(
807
- state,
808
- [...pathToActiveImage, "regions"],
809
- (activeImage.regions || []).filter((r) => !r.highlighted)
810
- )
811
- }
812
- case "HEADER_BUTTON_CLICKED": {
813
- const buttonName = action.buttonName.toLowerCase()
814
- switch (buttonName) {
815
- case "prev": {
816
- if (currentImageIndex === null) return state
817
- if (currentImageIndex === 0) return state
818
- return setNewImage(
819
- state.images[currentImageIndex - 1],
820
- currentImageIndex - 1
821
- )
822
- }
823
- case "next": {
824
- if (currentImageIndex === null) return state
825
- if (currentImageIndex === state.images.length - 1) return state
826
- return setNewImage(
827
- state.images[currentImageIndex + 1],
828
- currentImageIndex + 1
829
- )
830
- }
831
- case "clone": {
832
- if (currentImageIndex === null) return state
833
- if (currentImageIndex === state.images.length - 1) return state
834
- return setIn(
835
- setNewImage(
836
- state.images[currentImageIndex + 1],
837
- currentImageIndex + 1
838
- ),
839
- ["images", currentImageIndex + 1, "regions"],
840
- activeImage.regions
841
- )
842
- }
843
- case "settings": {
844
- return setIn(state, ["settingsOpen"], !state.settingsOpen)
845
- }
846
- case "help": {
847
- return state
848
- }
849
- case "fullscreen": {
850
- return setIn(state, ["fullScreen"], true)
851
- }
852
- case "exit fullscreen":
853
- case "window": {
854
- return setIn(state, ["fullScreen"], false)
855
- }
856
- case "hotkeys": {
857
- return state
858
- }
859
- case "exit":
860
- case "done": {
861
- return state
862
- }
863
- default:
864
- return state
865
- }
866
- }
867
- case "SELECT_TOOL": {
868
- if (action.selectedTool === "show-tags") {
869
- setInLocalStorage("showTags", !state.showTags)
870
- return setIn(state, ["showTags"], !state.showTags)
871
- } else if (action.selectedTool === "show-mask") {
872
- return setIn(state, ["showMask"], !state.showMask)
873
- }
874
- if (action.selectedTool === "modify-allowed-area" && !state.allowedArea) {
875
- state = setIn(state, ["allowedArea"], { x: 0, y: 0, w: 1, h: 1 })
876
- }
877
- state = setIn(state, ["mode"], null)
878
- return setIn(state, ["selectedTool"], action.selectedTool)
879
- }
880
- case "CANCEL": {
881
- const { mode } = state
882
- if (mode) {
883
- switch (mode.mode) {
884
- case "DRAW_EXPANDING_LINE":
885
- case "SET_EXPANDING_LINE_WIDTH":
886
- case "DRAW_POLYGON": {
887
- const { regionId } = mode
888
- return modifyRegion(regionId, null)
889
- }
890
- case "MOVE_POLYGON_POINT":
891
- case "RESIZE_BOX":
892
- case "MOVE_REGION": {
893
- return setIn(state, ["mode"], null)
894
- }
895
- default:
896
- return state
897
- }
898
- }
899
- // Close any open boxes
900
- const regions: any = activeImage.regions
901
- if (regions && regions.some((r) => r.editingLabels)) {
902
- return setIn(
903
- state,
904
- [...pathToActiveImage, "regions"],
905
- regions.map((r) => ({
906
- ...r,
907
- editingLabels: false,
908
- }))
909
- )
910
- } else if (regions) {
911
- return setIn(
912
- state,
913
- [...pathToActiveImage, "regions"],
914
- regions.map((r) => ({
915
- ...r,
916
- highlighted: false,
917
- }))
918
- )
919
- }
920
- break
921
- }
922
- case "UPDATE_REGIONS": {
923
- const { imageIndex, regions: newRegions } = action;
924
- const updatedRegions = state.images[imageIndex].regions.map(r => {
925
- const updatedRegion = newRegions.find(i => i.id === r.id)
926
- if (!updatedRegion) {
927
- return r
928
- }
929
- return {
930
- ...r,
931
- cls: updatedRegion.cls,
932
- text: updatedRegion.text
933
- }
934
- })
935
- // TODO: add mutation of order and deletion of regions - SI-1967
936
- return setIn(
937
- state,
938
- ["images", imageIndex, "regions"],
939
- updatedRegions
940
- )
941
- }
942
- case "IMAGES_UPDATED": {
943
- const { updatedAt } = action;
944
- return setIn(
945
- state,
946
- ["imagesUpdatedAt"],
947
- updatedAt
948
- )
949
- }
950
- case "IMAGES_SAVED": {
951
- const { savedAt } = action;
952
- return setIn(
953
- state,
954
- ["imagesSavedAt"],
955
- savedAt
956
- )
957
- }
958
- case "UPDATE_METADATA": {
959
- const { name, value, imageIndex } = action;
960
- if (isNaN(imageIndex)) {
961
- // update global metadata
962
- const metadataIndex = state.metadata?.findIndex(mt => mt.key === name)
963
- if (metadataIndex < 0) {
964
- console.error(`can't find metadata by key "${name}"`)
965
- return
966
- }
967
- return setIn(
968
- state,
969
- ["metadata", metadataIndex],
970
- {
971
- key: name,
972
- value: value
973
- }
974
- )
975
- } else {
976
- // update local metadata of imageIndex
977
- const metadataIndex = state.images[imageIndex]?.metadata?.findIndex(mt => mt.key === name)
978
- if (metadataIndex < 0) {
979
- console.error(`can't find metadata by key "${name}"`)
980
- return
981
- }
982
- return setIn(
983
- state,
984
- ["images", imageIndex, "metadata", metadataIndex],
985
- {
986
- key: name,
987
- value: value
988
- }
989
- )
990
- }
991
- }
992
- default:
993
- break
994
- }
995
- return state
996
- }