@searpent/react-image-annotate 2.0.3 → 2.0.4

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 (187) 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 +4 -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/examplePhotos.js +7601 -0
  18. package/src/Annotator/index.js +291 -0
  19. package/src/Annotator/index.story.js +807 -0
  20. package/src/Annotator/poses.story.js +150 -0
  21. package/src/Annotator/reducers/combine-reducers.js +7 -0
  22. package/src/Annotator/reducers/convert-expanding-line-to-polygon.js +53 -0
  23. package/{Annotator → src/Annotator}/reducers/fix-twisted.js +5 -3
  24. package/src/Annotator/reducers/general-reducer.js +996 -0
  25. package/src/Annotator/reducers/get-active-image.js +21 -0
  26. package/src/Annotator/reducers/get-implied-video-regions.js +115 -0
  27. package/src/Annotator/reducers/history-handler.js +60 -0
  28. package/src/Annotator/reducers/image-reducer.js +23 -0
  29. package/src/Annotator/reducers/video-reducer.js +85 -0
  30. package/src/Annotator/video.story.js +51 -0
  31. package/src/ClassSelectionMenu/index.js +108 -0
  32. package/src/Crosshairs/index.js +64 -0
  33. package/src/DebugSidebarBox/index.js +36 -0
  34. package/src/DemoSite/Editor.js +235 -0
  35. package/src/DemoSite/ErrorBoundaryDialog.js +34 -0
  36. package/src/DemoSite/index.js +41 -0
  37. package/src/DemoSite/index.story.js +10 -0
  38. package/src/DemoSite/simple-segmentation-example.json +572 -0
  39. package/src/Editor/annotation-plugin/annotation.css +46 -0
  40. package/src/Editor/annotation-plugin/annotation.js +515 -0
  41. package/src/Editor/index.js +24 -0
  42. package/src/Editor/tools.js +6 -0
  43. package/src/FullImageSegmentationAnnotator/hard1.story.jpg +0 -0
  44. package/src/FullImageSegmentationAnnotator/hard2.story.jpg +0 -0
  45. package/src/FullImageSegmentationAnnotator/hard3.story.jpg +0 -0
  46. package/src/FullImageSegmentationAnnotator/index.js +7 -0
  47. package/src/FullImageSegmentationAnnotator/index.story.js +177 -0
  48. package/src/FullImageSegmentationAnnotator/orange.story.png +0 -0
  49. package/src/GroupSelectorSidebarBox/index.js +48 -0
  50. package/src/HighlightBox/index.js +143 -0
  51. package/src/HistorySidebarBox/index.js +78 -0
  52. package/src/ImageCanvas/dancing-man.story.jpg +0 -0
  53. package/src/ImageCanvas/index.js +494 -0
  54. package/src/ImageCanvas/index.story.js +314 -0
  55. package/src/ImageCanvas/mouse_mask.story.png +0 -0
  56. package/src/ImageCanvas/region-tools.js +171 -0
  57. package/src/ImageCanvas/seves_desk.story.jpg +0 -0
  58. package/{ImageCanvas → src/ImageCanvas}/styles.js +8 -12
  59. package/src/ImageCanvas/use-mouse.js +168 -0
  60. package/src/ImageCanvas/use-project-box.js +23 -0
  61. package/src/ImageCanvas/use-wasd-mode.js +50 -0
  62. package/src/ImageMask/index.js +127 -0
  63. package/src/ImageMask/load-image.js +32 -0
  64. package/src/ImageSelectorSidebarBox/index.js +54 -0
  65. package/src/KeyframeTimeline/get-time-string.js +25 -0
  66. package/src/KeyframeTimeline/index.js +223 -0
  67. package/src/KeyframesSelectorSidebarBox/index.js +93 -0
  68. package/src/LandingPage/content.md +57 -0
  69. package/src/LandingPage/github-markdown.css +964 -0
  70. package/src/LandingPage/index.js +147 -0
  71. package/src/MainLayout/icon-dictionary.js +79 -0
  72. package/src/MainLayout/index.js +510 -0
  73. package/src/MainLayout/index.story.js +240 -0
  74. package/{MainLayout → src/MainLayout}/styles.js +7 -6
  75. package/src/MainLayout/types.js +164 -0
  76. package/src/MainLayout/use-implied-video-regions.js +17 -0
  77. package/src/MetadataEditorSidebarBox/index.js +98 -0
  78. package/src/PageSelector/index.js +76 -0
  79. package/src/PageSelector/page-selector.css +69 -0
  80. package/src/PointDistances/index.js +90 -0
  81. package/src/PreventScrollToParents/index.js +48 -0
  82. package/src/PreventScrollToParents/index.story.js +23 -0
  83. package/src/RegionLabel/index.js +222 -0
  84. package/{RegionLabel → src/RegionLabel}/styles.js +15 -12
  85. package/src/RegionSelectAndTransformBoxes/index.js +234 -0
  86. package/src/RegionSelectorSidebarBox/index.js +216 -0
  87. package/{RegionSelectorSidebarBox → src/RegionSelectorSidebarBox}/styles.js +14 -13
  88. package/src/RegionShapes/index.js +254 -0
  89. package/src/RegionTags/index.js +134 -0
  90. package/src/SettingsDialog/index.js +58 -0
  91. package/src/SettingsProvider/index.js +48 -0
  92. package/src/Shortcuts/ShortcutField.js +44 -0
  93. package/src/Shortcuts/index.js +129 -0
  94. package/src/ShortcutsManager/index.js +162 -0
  95. package/src/Sidebar/index.js +117 -0
  96. package/src/SidebarBoxContainer/index.js +93 -0
  97. package/src/SmallToolButton/index.js +57 -0
  98. package/src/TagsSidebarBox/index.js +93 -0
  99. package/src/TaskDescriptionSidebarBox/index.js +43 -0
  100. package/src/Theme/index.js +36 -0
  101. package/src/VideoOrImageCanvasBackground/index.js +170 -0
  102. package/src/colors.js +32 -0
  103. package/src/hooks/use-colors.js +74 -0
  104. package/src/hooks/use-event-callback.js +11 -0
  105. package/src/hooks/use-exclude-pattern.js +27 -0
  106. package/src/hooks/use-load-image.js +21 -0
  107. package/src/hooks/use-window-size.js +46 -0
  108. package/{hooks → src/hooks}/xpattern.js +1 -1
  109. package/src/hooks/xpattern.png +0 -0
  110. package/src/index.js +18 -0
  111. package/src/lib.js +7 -0
  112. package/src/screenshot.png +0 -0
  113. package/src/site.css +5 -0
  114. package/src/stories.js +2 -0
  115. package/src/utils/filter-only-unique.js +5 -0
  116. package/src/utils/get-from-local-storage.js +7 -0
  117. package/src/utils/get-hotkey-help-text.js +11 -0
  118. package/src/utils/get-landmarks-with-transform.js +23 -0
  119. package/src/utils/photosToImages.js +40 -0
  120. package/src/utils/regions-to-blocks.js +14 -0
  121. package/src/utils/set-in-local-storage.js +6 -0
  122. package/Annotator/index.js +0 -169
  123. package/Annotator/reducers/combine-reducers.js +0 -14
  124. package/Annotator/reducers/convert-expanding-line-to-polygon.js +0 -73
  125. package/Annotator/reducers/general-reducer.js +0 -1058
  126. package/Annotator/reducers/get-active-image.js +0 -27
  127. package/Annotator/reducers/get-implied-video-regions.js +0 -180
  128. package/Annotator/reducers/history-handler.js +0 -38
  129. package/Annotator/reducers/image-reducer.js +0 -20
  130. package/Annotator/reducers/video-reducer.js +0 -88
  131. package/ClassSelectionMenu/index.js +0 -135
  132. package/Crosshairs/index.js +0 -53
  133. package/DebugSidebarBox/index.js +0 -20
  134. package/DemoSite/Editor.js +0 -194
  135. package/DemoSite/ErrorBoundaryDialog.js +0 -64
  136. package/DemoSite/index.js +0 -40
  137. package/FullImageSegmentationAnnotator/index.js +0 -7
  138. package/GroupSelectorSidebarBox/index.js +0 -63
  139. package/HighlightBox/index.js +0 -99
  140. package/HistorySidebarBox/index.js +0 -71
  141. package/ImageCanvas/index.js +0 -424
  142. package/ImageCanvas/region-tools.js +0 -165
  143. package/ImageCanvas/use-mouse.js +0 -180
  144. package/ImageCanvas/use-project-box.js +0 -27
  145. package/ImageCanvas/use-wasd-mode.js +0 -62
  146. package/ImageMask/index.js +0 -133
  147. package/ImageMask/load-image.js +0 -25
  148. package/ImageSelectorSidebarBox/index.js +0 -60
  149. package/KeyframeTimeline/get-time-string.js +0 -27
  150. package/KeyframeTimeline/index.js +0 -233
  151. package/KeyframesSelectorSidebarBox/index.js +0 -93
  152. package/LandingPage/index.js +0 -159
  153. package/MainLayout/icon-dictionary.js +0 -104
  154. package/MainLayout/index.js +0 -366
  155. package/MainLayout/types.js +0 -0
  156. package/MainLayout/use-implied-video-regions.js +0 -13
  157. package/PointDistances/index.js +0 -73
  158. package/PreventScrollToParents/index.js +0 -51
  159. package/RegionLabel/index.js +0 -191
  160. package/RegionSelectAndTransformBoxes/index.js +0 -167
  161. package/RegionSelectorSidebarBox/index.js +0 -248
  162. package/RegionShapes/index.js +0 -274
  163. package/RegionTags/index.js +0 -138
  164. package/SettingsDialog/index.js +0 -52
  165. package/SettingsProvider/index.js +0 -53
  166. package/Shortcuts/ShortcutField.js +0 -46
  167. package/Shortcuts/index.js +0 -133
  168. package/ShortcutsManager/index.js +0 -155
  169. package/Sidebar/index.js +0 -69
  170. package/SidebarBoxContainer/index.js +0 -93
  171. package/SmallToolButton/index.js +0 -42
  172. package/TagsSidebarBox/index.js +0 -105
  173. package/TaskDescriptionSidebarBox/index.js +0 -58
  174. package/Theme/index.js +0 -30
  175. package/VideoOrImageCanvasBackground/index.js +0 -151
  176. package/colors.js +0 -14
  177. package/hooks/use-event-callback.js +0 -10
  178. package/hooks/use-exclude-pattern.js +0 -24
  179. package/hooks/use-load-image.js +0 -26
  180. package/hooks/use-window-size.js +0 -46
  181. package/index.js +0 -3
  182. package/lib.js +0 -3
  183. package/stories.js +0 -5
  184. package/utils/get-from-local-storage.js +0 -7
  185. package/utils/get-hotkey-help-text.js +0 -9
  186. package/utils/get-landmarks-with-transform.js +0 -40
  187. package/utils/set-in-local-storage.js +0 -3
@@ -0,0 +1,90 @@
1
+ // @flow weak
2
+
3
+ import React, { Fragment } from "react"
4
+ import { styled } from "@mui/material/styles"
5
+ import { createTheme, ThemeProvider } from "@mui/material/styles"
6
+
7
+ const theme = createTheme()
8
+ const Svg = styled("svg")(({ theme }) => ({
9
+ pointerEvents: "none",
10
+ position: "absolute",
11
+ zIndex: 1,
12
+ left: 0,
13
+ top: 0,
14
+ width: "100%",
15
+ height: "100%",
16
+ "& text": {
17
+ fill: "#fff",
18
+ },
19
+ "& path": {
20
+ vectorEffect: "non-scaling-stroke",
21
+ strokeWidth: 2,
22
+ opacity: 0.5,
23
+ stroke: "#FFF",
24
+ fill: "none",
25
+ strokeDasharray: 5,
26
+ animationDuration: "4s",
27
+ animationTimingFunction: "linear",
28
+ animationIterationCount: "infinite",
29
+ animationPlayState: "running",
30
+ },
31
+ }))
32
+
33
+ export const PointDistances = ({
34
+ projectRegionBox,
35
+ regions,
36
+ pointDistancePrecision,
37
+ realSize,
38
+ }) => {
39
+ return (
40
+ <ThemeProvider theme={theme}>
41
+ <Svg>
42
+ {regions
43
+ .filter((r1) => r1.type === "point")
44
+ .flatMap((r1, i1) =>
45
+ regions
46
+ .filter((r2, i2) => i2 > i1)
47
+ .filter((r2) => r2.type === "point")
48
+ .map((r2) => {
49
+ const pr1 = projectRegionBox(r1)
50
+ const pr2 = projectRegionBox(r2)
51
+ const prm = {
52
+ x: (pr1.x + pr1.w / 2 + pr2.x + pr2.w / 2) / 2,
53
+ y: (pr1.y + pr1.h / 2 + pr2.y + pr2.h / 2) / 2,
54
+ }
55
+ let displayDistance
56
+ if (realSize) {
57
+ const { w, h, unitName } = realSize
58
+ displayDistance =
59
+ Math.sqrt(
60
+ Math.pow(r1.x * w - r2.x * w, 2) +
61
+ Math.pow(r1.y * h - r2.y * h, 2)
62
+ ).toFixed(pointDistancePrecision) + unitName
63
+ } else {
64
+ displayDistance =
65
+ (
66
+ Math.sqrt(
67
+ Math.pow(r1.x - r2.x, 2) + Math.pow(r1.y - r2.y, 2)
68
+ ) * 100
69
+ ).toFixed(pointDistancePrecision) + "%"
70
+ }
71
+ return (
72
+ <Fragment>
73
+ <path
74
+ d={`M${pr1.x + pr1.w / 2},${pr1.y + pr1.h / 2} L${
75
+ pr2.x + pr2.w / 2
76
+ },${pr2.y + pr2.h / 2}`}
77
+ />
78
+ <text x={prm.x} y={prm.y}>
79
+ {displayDistance}
80
+ </text>
81
+ </Fragment>
82
+ )
83
+ })
84
+ )}
85
+ </Svg>
86
+ </ThemeProvider>
87
+ )
88
+ }
89
+
90
+ export default PointDistances
@@ -0,0 +1,48 @@
1
+ // @flow
2
+
3
+ import React, { useState } from "react"
4
+ import { RemoveScroll } from "react-remove-scroll"
5
+ import { styled } from "@mui/material/styles"
6
+ import { createTheme, ThemeProvider } from "@mui/material/styles"
7
+ import useEventCallback from "use-event-callback"
8
+
9
+ const theme = createTheme()
10
+ const Container = styled("div")(({ theme }) => ({
11
+ "& > div": {
12
+ width: "100%",
13
+ height: "100%",
14
+ },
15
+ }))
16
+
17
+ export const PreventScrollToParents = ({ children, ...otherProps }) => {
18
+ const [mouseOver, changeMouseOver] = useState(false)
19
+ const onMouseMove = useEventCallback((e) => {
20
+ if (!mouseOver) changeMouseOver(true)
21
+ if (otherProps.onMouseMove) {
22
+ otherProps.onMouseMove(e)
23
+ }
24
+ })
25
+ const onMouseLeave = useEventCallback((e) => {
26
+ setTimeout(() => {
27
+ if (mouseOver) {
28
+ changeMouseOver(false)
29
+ }
30
+ }, 100)
31
+ })
32
+
33
+ return (
34
+ <ThemeProvider theme={theme}>
35
+ <Container
36
+ {...otherProps}
37
+ onMouseMove={onMouseMove}
38
+ onMouseLeave={onMouseLeave}
39
+ >
40
+ <RemoveScroll enabled={mouseOver} removeScrollBar={false}>
41
+ {children}
42
+ </RemoveScroll>
43
+ </Container>
44
+ </ThemeProvider>
45
+ )
46
+ }
47
+
48
+ export default PreventScrollToParents
@@ -0,0 +1,23 @@
1
+ // @flow
2
+
3
+ import React from "react"
4
+
5
+ import { storiesOf } from "@storybook/react"
6
+ import { action } from "@storybook/addon-actions"
7
+
8
+ import RemoveScrollOnChildren from "./"
9
+
10
+ storiesOf("RemoveScrollOnChildren", module).add("Basic", () => (
11
+ <div style={{ width: "100vh", textAlign: "center", height: "200vh" }}>
12
+ <RemoveScrollOnChildren>
13
+ <div
14
+ style={{
15
+ display: "inline-block",
16
+ width: 100,
17
+ height: 100,
18
+ backgroundColor: "red",
19
+ }}
20
+ />
21
+ </RemoveScrollOnChildren>
22
+ </div>
23
+ ))
@@ -0,0 +1,222 @@
1
+ // @flow
2
+
3
+ import React, { useRef, memo } from "react"
4
+ import Paper from "@mui/material/Paper"
5
+ import { makeStyles } from "@mui/styles"
6
+ import { createTheme, ThemeProvider } from "@mui/material/styles"
7
+ import styles from "./styles"
8
+ import classnames from "classnames"
9
+ import type { Region } from "../ImageCanvas/region-tools.js"
10
+ import IconButton from "@mui/material/IconButton"
11
+ import Button from "@mui/material/Button"
12
+ import TrashIcon from "@mui/icons-material/Delete"
13
+ import CheckIcon from "@mui/icons-material/Check"
14
+ import TextField from "@mui/material/TextField"
15
+ import Select from "react-select"
16
+ import CreatableSelect from "react-select/creatable"
17
+
18
+ import { asMutable } from "seamless-immutable"
19
+
20
+ const theme = createTheme()
21
+ const useStyles = makeStyles((theme) => styles)
22
+
23
+ type Props = {
24
+ region: Region,
25
+ editing?: boolean,
26
+ allowedClasses?: Array<string>,
27
+ allowedTags?: Array<string>,
28
+ allowedGroups?: Array<{ value: String, label: String }>,
29
+ cls?: string,
30
+ tags?: Array<string>,
31
+ onDelete: (Region) => null,
32
+ onChange: (Region) => null,
33
+ onClose: (Region) => null,
34
+ onOpen: (Region) => null,
35
+ onRegionClassAdded: () => {},
36
+ allowComments?: boolean,
37
+ hideNotEditingLabel?: boolean,
38
+ }
39
+
40
+ export const RegionLabel = ({
41
+ region,
42
+ editing,
43
+ allowedClasses,
44
+ allowedTags,
45
+ onDelete,
46
+ onChange,
47
+ onClose,
48
+ onOpen,
49
+ onRegionClassAdded,
50
+ allowComments,
51
+ hideNotEditingLabel,
52
+ allowedGroups,
53
+ }: Props) => {
54
+ const classes = useStyles()
55
+ const commentInputRef = useRef(null)
56
+ const onCommentInputClick = (_) => {
57
+ // The TextField wraps the <input> tag with two divs
58
+ const commentInput = commentInputRef.current.children[0].children[0]
59
+
60
+ if (commentInput) return commentInput.focus()
61
+ }
62
+
63
+ if (hideNotEditingLabel && !editing) return null
64
+
65
+ return (
66
+ <ThemeProvider theme={theme}>
67
+ <Paper
68
+ onClick={() => (!editing ? onOpen(region) : null)}
69
+ className={classnames(classes.regionInfo, {
70
+ highlighted: region.highlighted,
71
+ })}
72
+ >
73
+ {!editing ? (
74
+ <div>
75
+ {region.cls && (
76
+ <div className="name">
77
+ <div
78
+ className="circle"
79
+ style={{ backgroundColor: region.color }}
80
+ />
81
+ {region.cls}
82
+ </div>
83
+ )}
84
+ {region.tags && (
85
+ <div className="tags">
86
+ {region.tags.map((t) => (
87
+ <div key={t} className="tag">
88
+ {t}
89
+ </div>
90
+ ))}
91
+ </div>
92
+ )}
93
+ </div>
94
+ ) : (
95
+ <div style={{ width: 200 }}>
96
+ <div style={{ display: "flex", flexDirection: "row" }}>
97
+ <div
98
+ style={{
99
+ display: "flex",
100
+ backgroundColor: region.color || "#888",
101
+ color: "#fff",
102
+ padding: 4,
103
+ paddingLeft: 8,
104
+ paddingRight: 8,
105
+ borderRadius: 4,
106
+ fontWeight: "bold",
107
+ textShadow: "0px 0px 5px rgba(0,0,0,0.4)",
108
+ }}
109
+ >
110
+ {region.type}
111
+ </div>
112
+ <div style={{ flexGrow: 1 }} />
113
+ <IconButton
114
+ onClick={() => onDelete(region)}
115
+ tabIndex={-1}
116
+ style={{ width: 22, height: 22 }}
117
+ size="small"
118
+ variant="outlined"
119
+ >
120
+ <TrashIcon style={{ marginTop: -8, width: 16, height: 16 }} />
121
+ </IconButton>
122
+ </div>
123
+ {(allowedClasses || []).length > 0 && (
124
+ <div style={{ marginTop: 6 }}>
125
+ <CreatableSelect
126
+ placeholder="Classification"
127
+ onChange={(o, actionMeta) => {
128
+ if (actionMeta.action == "create-option") {
129
+ onRegionClassAdded(o.value)
130
+ }
131
+ return onChange({
132
+ ...(region: any),
133
+ cls: o.value,
134
+ })
135
+ }}
136
+ value={
137
+ region.cls ? { label: region.cls, value: region.cls } : null
138
+ }
139
+ options={asMutable(
140
+ allowedClasses.map((c) => ({ value: c, label: c }))
141
+ )}
142
+ />
143
+ </div>
144
+ )}
145
+ {(allowedGroups || []).length > 0 && (
146
+ <Select
147
+ onChange={(newGroup) => onChange({
148
+ ...(region: any),
149
+ groupId: newGroup.value,
150
+ })
151
+
152
+ }
153
+ placeholder="Group"
154
+ value={
155
+ allowedGroups.filter(g => g.value === region.groupId)
156
+ }
157
+ options={allowedGroups}
158
+ />
159
+ )}
160
+ {(allowedTags || []).length > 0 && (
161
+ <div style={{ marginTop: 4 }}>
162
+ <Select
163
+ onChange={(newTags) =>
164
+ onChange({
165
+ ...(region: any),
166
+ tags: newTags.map((t) => t.value),
167
+ })
168
+ }
169
+ placeholder="Tags"
170
+ value={(region.tags || []).map((c) => ({
171
+ label: c,
172
+ value: c,
173
+ }))}
174
+ isMulti
175
+ options={asMutable(
176
+ allowedTags.map((c) => ({ value: c, label: c }))
177
+ )}
178
+ />
179
+ </div>
180
+ )}
181
+ {allowComments && (
182
+ <TextField
183
+ InputProps={{
184
+ className: classes.commentBox,
185
+ }}
186
+ fullWidth
187
+ multiline
188
+ rows={3}
189
+ ref={commentInputRef}
190
+ onClick={onCommentInputClick}
191
+ value={region.comment || ""}
192
+ onChange={(event) =>
193
+ onChange({ ...(region: any), comment: event.target.value })
194
+ }
195
+ />
196
+ )}
197
+ {onClose && (
198
+ <div style={{ marginTop: 4, display: "flex" }}>
199
+ <div style={{ flexGrow: 1 }} />
200
+ <Button
201
+ onClick={() => onClose(region)}
202
+ size="small"
203
+ variant="contained"
204
+ color="primary"
205
+ >
206
+ <CheckIcon />
207
+ </Button>
208
+ </div>
209
+ )}
210
+ </div>
211
+ )}
212
+ </Paper>
213
+ </ThemeProvider>
214
+ )
215
+ }
216
+
217
+ export default memo(
218
+ RegionLabel,
219
+ (prevProps, nextProps) =>
220
+ prevProps.editing === nextProps.editing &&
221
+ prevProps.region === nextProps.region
222
+ )
@@ -1,4 +1,7 @@
1
- import { grey } from "@mui/material/colors";
1
+ // @flow
2
+
3
+ import { grey } from "@mui/material/colors"
4
+
2
5
  export default {
3
6
  regionInfo: {
4
7
  fontSize: 12,
@@ -7,13 +10,13 @@ export default {
7
10
  opacity: 0.5,
8
11
  "&:hover": {
9
12
  opacity: 0.9,
10
- cursor: "pointer"
13
+ cursor: "pointer",
11
14
  },
12
15
  "&.highlighted": {
13
16
  opacity: 0.9,
14
17
  "&:hover": {
15
- opacity: 1
16
- }
18
+ opacity: 1,
19
+ },
17
20
  },
18
21
  // pointerEvents: "none",
19
22
  fontWeight: 600,
@@ -28,8 +31,8 @@ export default {
28
31
  boxShadow: "0px 0px 2px rgba(0,0,0,0.4)",
29
32
  width: 10,
30
33
  height: 10,
31
- borderRadius: 5
32
- }
34
+ borderRadius: 5,
35
+ },
33
36
  },
34
37
  "& .tags": {
35
38
  "& .tag": {
@@ -37,12 +40,12 @@ export default {
37
40
  display: "inline-block",
38
41
  margin: 1,
39
42
  fontSize: 10,
40
- textDecoration: "underline"
41
- }
42
- }
43
+ textDecoration: "underline",
44
+ },
45
+ },
43
46
  },
44
47
  commentBox: {
45
48
  fontWeight: 400,
46
- fontSize: 13
47
- }
48
- };
49
+ fontSize: 13,
50
+ },
51
+ }
@@ -0,0 +1,234 @@
1
+ import React, { Fragment, memo } from "react"
2
+ import HighlightBox from "../HighlightBox"
3
+ import { styled } from "@mui/material/styles"
4
+ import { createTheme, ThemeProvider } from "@mui/material/styles"
5
+ import PreventScrollToParents from "../PreventScrollToParents"
6
+ import Tooltip from "@mui/material/Tooltip"
7
+
8
+ const theme = createTheme()
9
+ const TransformGrabber = styled("div")(({ theme }) => ({
10
+ width: 8,
11
+ height: 8,
12
+ zIndex: 2,
13
+ border: "2px solid #FFF",
14
+ position: "absolute",
15
+ }))
16
+
17
+ const boxCursorMap = [
18
+ ["nw-resize", "n-resize", "ne-resize"],
19
+ ["w-resize", "grab", "e-resize"],
20
+ ["sw-resize", "s-resize", "se-resize"],
21
+ ]
22
+
23
+ const arePropsEqual = (prev, next) => {
24
+ return (
25
+ prev.region === next.region &&
26
+ prev.dragWithPrimary === next.dragWithPrimary &&
27
+ prev.createWithPrimary === next.createWithPrimary &&
28
+ prev.zoomWithPrimary === next.zoomWithPrimary &&
29
+ prev.mat === next.mat
30
+ )
31
+ }
32
+
33
+ export const RegionSelectAndTransformBox = memo(
34
+ ({
35
+ region: r,
36
+ mouseEvents,
37
+ projectRegionBox,
38
+ dragWithPrimary,
39
+ createWithPrimary,
40
+ zoomWithPrimary,
41
+ onBeginMovePoint,
42
+ onSelectRegion,
43
+ layoutParams,
44
+ fullImageSegmentationMode = false,
45
+ mat,
46
+ onBeginBoxTransform,
47
+ onBeginMovePolygonPoint,
48
+ onBeginMoveKeypoint,
49
+ onAddPolygonPoint,
50
+ showHighlightBox,
51
+ }) => {
52
+ const pbox = projectRegionBox(r)
53
+ const { iw, ih } = layoutParams.current
54
+ return (
55
+ <ThemeProvider theme={theme}>
56
+ <Fragment>
57
+ <PreventScrollToParents>
58
+ {showHighlightBox && r.type !== "polygon" && (
59
+ <HighlightBox
60
+ region={r}
61
+ mouseEvents={mouseEvents}
62
+ dragWithPrimary={dragWithPrimary}
63
+ createWithPrimary={createWithPrimary}
64
+ zoomWithPrimary={zoomWithPrimary}
65
+ onBeginMovePoint={onBeginMovePoint}
66
+ onSelectRegion={onSelectRegion}
67
+ pbox={pbox}
68
+ />
69
+ )}
70
+ {r.type === "box" &&
71
+ !dragWithPrimary &&
72
+ !zoomWithPrimary &&
73
+ !r.locked &&
74
+ r.highlighted &&
75
+ mat.a < 1.2 &&
76
+ [
77
+ [0, 0],
78
+ [0.5, 0],
79
+ [1, 0],
80
+ [1, 0.5],
81
+ [1, 1],
82
+ [0.5, 1],
83
+ [0, 1],
84
+ [0, 0.5],
85
+ [0.5, 0.5],
86
+ ].map(([px, py], i) => (
87
+ <TransformGrabber
88
+ key={i}
89
+ {...mouseEvents}
90
+ onMouseDown={(e) => {
91
+ if (e.button === 0)
92
+ return onBeginBoxTransform(r, [px * 2 - 1, py * 2 - 1])
93
+ mouseEvents.onMouseDown(e)
94
+ }}
95
+ style={{
96
+ left: pbox.x - 4 - 2 + pbox.w * px,
97
+ top: pbox.y - 4 - 2 + pbox.h * py,
98
+ cursor: boxCursorMap[py * 2][px * 2],
99
+ borderRadius: px === 0.5 && py === 0.5 ? 4 : undefined,
100
+ }}
101
+ />
102
+ ))}
103
+ {r.type === "polygon" &&
104
+ !dragWithPrimary &&
105
+ !zoomWithPrimary &&
106
+ !r.locked &&
107
+ r.highlighted &&
108
+ r.points.map(([px, py], i) => {
109
+ const proj = mat
110
+ .clone()
111
+ .inverse()
112
+ .applyToPoint(px * iw, py * ih)
113
+ return (
114
+ <TransformGrabber
115
+ key={i}
116
+ {...mouseEvents}
117
+ onMouseDown={(e) => {
118
+ if (e.button === 0 && (!r.open || i === 0))
119
+ return onBeginMovePolygonPoint(r, i)
120
+ mouseEvents.onMouseDown(e)
121
+ }}
122
+ style={{
123
+ cursor: !r.open
124
+ ? "move"
125
+ : i === 0
126
+ ? "pointer"
127
+ : undefined,
128
+ zIndex: 10,
129
+ pointerEvents:
130
+ r.open && i === r.points.length - 1
131
+ ? "none"
132
+ : undefined,
133
+ left: proj.x - 4,
134
+ top: proj.y - 4,
135
+ }}
136
+ />
137
+ )
138
+ })}
139
+ {r.type === "polygon" &&
140
+ r.highlighted &&
141
+ !dragWithPrimary &&
142
+ !zoomWithPrimary &&
143
+ !r.locked &&
144
+ !r.open &&
145
+ r.points.length > 1 &&
146
+ r.points
147
+ .map((p1, i) => [p1, r.points[(i + 1) % r.points.length]])
148
+ .map(([p1, p2]) => [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2])
149
+ .map((pa, i) => {
150
+ const proj = mat
151
+ .clone()
152
+ .inverse()
153
+ .applyToPoint(pa[0] * iw, pa[1] * ih)
154
+ return (
155
+ <TransformGrabber
156
+ key={i}
157
+ {...mouseEvents}
158
+ onMouseDown={(e) => {
159
+ if (e.button === 0)
160
+ return onAddPolygonPoint(r, pa, i + 1)
161
+ mouseEvents.onMouseDown(e)
162
+ }}
163
+ style={{
164
+ cursor: "copy",
165
+ zIndex: 10,
166
+ left: proj.x - 4,
167
+ top: proj.y - 4,
168
+ border: "2px dotted #fff",
169
+ opacity: 0.5,
170
+ }}
171
+ />
172
+ )
173
+ })}
174
+ {r.type === "keypoints" &&
175
+ !dragWithPrimary &&
176
+ !zoomWithPrimary &&
177
+ !r.locked &&
178
+ r.highlighted &&
179
+ Object.entries(r.points).map(
180
+ ([keypointId, { x: px, y: py }], i) => {
181
+ const proj = mat
182
+ .clone()
183
+ .inverse()
184
+ .applyToPoint(px * iw, py * ih)
185
+ return (
186
+ <Tooltip title={keypointId} key={i}>
187
+ <TransformGrabber
188
+ key={i}
189
+ {...mouseEvents}
190
+ onMouseDown={(e) => {
191
+ if (e.button === 0 && (!r.open || i === 0))
192
+ return onBeginMoveKeypoint(r, keypointId)
193
+ mouseEvents.onMouseDown(e)
194
+ }}
195
+ style={{
196
+ cursor: !r.open
197
+ ? "move"
198
+ : i === 0
199
+ ? "pointer"
200
+ : undefined,
201
+ zIndex: 10,
202
+ pointerEvents:
203
+ r.open && i === r.points.length - 1
204
+ ? "none"
205
+ : undefined,
206
+ left: proj.x - 4,
207
+ top: proj.y - 4,
208
+ }}
209
+ />
210
+ </Tooltip>
211
+ )
212
+ }
213
+ )}
214
+ </PreventScrollToParents>
215
+ </Fragment>
216
+ </ThemeProvider>
217
+ )
218
+ },
219
+ arePropsEqual
220
+ )
221
+
222
+ export const RegionSelectAndTransformBoxes = memo(
223
+ (props) => {
224
+ return props.regions
225
+ .filter((r) => r.visible || r.visible === undefined)
226
+ .filter((r) => !r.locked)
227
+ .map((r, i) => {
228
+ return <RegionSelectAndTransformBox key={r.id} {...props} region={r} />
229
+ })
230
+ },
231
+ (n, p) => n.regions === p.regions && n.mat === p.mat
232
+ )
233
+
234
+ export default RegionSelectAndTransformBoxes