lost-sia 3.2.0-alpha0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Canvas/Canvas.js +1 -1
- package/dist/Canvas/LabelInput.js +1 -1
- package/dist/Sia.js +1 -1
- package/dist/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.d.ts +2 -1
- package/dist/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.js +1 -1
- package/dist/Toolbar/ToolbarItems/ImageToolItems/TagLabel.d.ts +2 -1
- package/dist/Toolbar/ToolbarItems/ImageToolItems/TagLabel.js +1 -1
- package/dist/Toolbar/ToolbarItems/ImageTools.js +1 -1
- package/dist/assets/SIA-CoOqcT1a.css +1 -0
- package/dist/assets/brand-icons-Cu_C0hZ4.svg +1008 -0
- package/dist/assets/brand-icons-F3SPCeH1.woff +0 -0
- package/dist/assets/brand-icons-XL9sxUpA.woff2 +0 -0
- package/dist/assets/brand-icons-sqJ2Pg7a.eot +0 -0
- package/dist/assets/brand-icons-ubhWoxly.ttf +0 -0
- package/dist/assets/flags-DOLqOU7Y.png +0 -0
- package/dist/assets/icons-BOCtAERH.woff +0 -0
- package/dist/assets/icons-CHzK1VD9.eot +0 -0
- package/dist/assets/icons-D29ZQHHw.ttf +0 -0
- package/dist/assets/icons-Du6TOHnR.woff2 +0 -0
- package/dist/assets/icons-RwhydX30.svg +1518 -0
- package/dist/assets/node_modules/semantic-ui-css/semantic.min-09YPtVE6.css +1 -0
- package/dist/assets/outline-icons-BfdLr8tr.svg +366 -0
- package/dist/assets/outline-icons-DD8jm0uy.ttf +0 -0
- package/dist/assets/outline-icons-DInHoiqI.woff2 +0 -0
- package/dist/assets/outline-icons-LX8adJ4n.eot +0 -0
- package/dist/assets/outline-icons-aQ88nltS.woff +0 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -1
- package/dist/stories/Canvas/Canvas.stories.d.ts +3 -3
- package/dist/stories/Canvas/CanvasWithOffset.stories.d.ts +6 -6
- package/dist/stories/Toolbar/ImageTools/TagLabel.stories.d.ts +2 -1
- package/package.json +4 -3
- package/src/AnnoExampleViewer.jsx +69 -0
- package/src/Canvas/Canvas.tsx +45 -17
- package/src/Canvas/LabelInput.tsx +11 -9
- package/src/InfoBoxes/AnnoDetails.jsx +165 -0
- package/src/InfoBoxes/AnnoStats.jsx +106 -0
- package/src/InfoBoxes/InfoBox.jsx +76 -0
- package/src/InfoBoxes/InfoBoxArea.jsx +152 -0
- package/src/InfoBoxes/LabelInfo.jsx +107 -0
- package/src/SIA.scss +13 -0
- package/src/SIASettingButton.jsx +126 -0
- package/src/Sia.tsx +70 -54
- package/src/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.tsx +74 -24
- package/src/Toolbar/ToolbarItems/ImageToolItems/TagLabel.tsx +33 -0
- package/src/Toolbar/ToolbarItems/ImageTools.tsx +1 -0
- package/src/index.ts +1 -1
- package/src/stories/exampleData/exampleExternalAnnotations.ts +22 -22
- package/dist/SiaViewer.d.ts +0 -19
- package/dist/SiaViewer.js +0 -1
- package/dist/assets/SIA-BV1tYu3P.css +0 -1
- package/dist/stories/SIA/SiaViewer.stories.d.ts +0 -52
- package/src/SiaViewer.tsx +0 -125
- package/src/stories/SIA/SiaViewer.stories.tsx +0 -105
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { useState } from 'react'
|
|
1
|
+
import { useState, useRef, useCallback } from 'react'
|
|
2
|
+
import React from 'react'
|
|
2
3
|
import {
|
|
3
4
|
CDropdown,
|
|
4
5
|
CDropdownMenu,
|
|
@@ -11,11 +12,13 @@ import {
|
|
|
11
12
|
import { Label } from '../../../types'
|
|
12
13
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
13
14
|
import { faTag } from '@fortawesome/free-solid-svg-icons'
|
|
15
|
+
import { IconProps } from 'semantic-ui-react'
|
|
14
16
|
import TagLabel from './TagLabel'
|
|
15
17
|
|
|
16
18
|
type ImageLabelInputProps = {
|
|
17
19
|
isDisabled: boolean
|
|
18
20
|
isVisible: boolean
|
|
21
|
+
isFullscreen?: boolean
|
|
19
22
|
selectedLabelsIds: number[] | undefined
|
|
20
23
|
possibleLabels: Label[]
|
|
21
24
|
isMultilabel?: boolean
|
|
@@ -25,12 +28,30 @@ type ImageLabelInputProps = {
|
|
|
25
28
|
const ImageLabelInput = ({
|
|
26
29
|
isDisabled,
|
|
27
30
|
isVisible,
|
|
31
|
+
isFullscreen = false,
|
|
28
32
|
selectedLabelsIds,
|
|
29
33
|
possibleLabels,
|
|
30
34
|
isMultilabel = false,
|
|
31
35
|
onLabelSelect,
|
|
32
36
|
}: ImageLabelInputProps) => {
|
|
33
37
|
const [filter, setFilter] = useState('')
|
|
38
|
+
const tagElRef = useRef<HTMLDivElement | null>(null)
|
|
39
|
+
const tagHandlerRef = useRef<((e: WheelEvent) => void) | null>(null)
|
|
40
|
+
const tagScrollRef = useCallback((el: HTMLDivElement | null) => {
|
|
41
|
+
// cleanup previous listener
|
|
42
|
+
if (tagElRef.current && tagHandlerRef.current) {
|
|
43
|
+
tagElRef.current.removeEventListener('wheel', tagHandlerRef.current)
|
|
44
|
+
}
|
|
45
|
+
tagElRef.current = el
|
|
46
|
+
tagHandlerRef.current = null
|
|
47
|
+
if (!el) return
|
|
48
|
+
const handler = (e: WheelEvent) => {
|
|
49
|
+
e.preventDefault()
|
|
50
|
+
el.scrollLeft += e.deltaY * 0.2
|
|
51
|
+
}
|
|
52
|
+
tagHandlerRef.current = handler
|
|
53
|
+
el.addEventListener('wheel', handler, { passive: false })
|
|
54
|
+
}, [])
|
|
34
55
|
|
|
35
56
|
const filteredLabels: Label[] = possibleLabels.filter((label: Label) =>
|
|
36
57
|
label.name.toLowerCase().includes(filter.toLowerCase()),
|
|
@@ -67,22 +88,41 @@ const ImageLabelInput = ({
|
|
|
67
88
|
const renderLabels = () => {
|
|
68
89
|
if (!selectedLabelsIds || selectedLabelsIds.length === 0)
|
|
69
90
|
return (
|
|
70
|
-
<div
|
|
71
|
-
<FontAwesomeIcon icon={faTag} />
|
|
91
|
+
<div>
|
|
92
|
+
<FontAwesomeIcon icon={faTag as IconProps} />
|
|
72
93
|
</div>
|
|
73
94
|
)
|
|
74
95
|
|
|
75
96
|
const selectedLabels = getSelectedLabels()
|
|
76
|
-
return
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
return (
|
|
98
|
+
<div
|
|
99
|
+
ref={tagScrollRef}
|
|
100
|
+
style={
|
|
101
|
+
{
|
|
102
|
+
display: 'flex',
|
|
103
|
+
flexWrap: 'nowrap',
|
|
104
|
+
overflowX: 'auto',
|
|
105
|
+
maxWidth: isFullscreen ? '500px' : '300px',
|
|
106
|
+
alignItems: 'center',
|
|
107
|
+
gap: '10px',
|
|
108
|
+
scrollbarWidth: 'thin',
|
|
109
|
+
} as React.CSSProperties
|
|
110
|
+
}
|
|
111
|
+
className="tag-scroll"
|
|
112
|
+
>
|
|
113
|
+
{selectedLabels.map((label: Label) => (
|
|
114
|
+
<TagLabel
|
|
115
|
+
key={label.name}
|
|
116
|
+
name={label.name}
|
|
117
|
+
color={label.color}
|
|
118
|
+
size={25}
|
|
119
|
+
triangleSize={17}
|
|
120
|
+
style={{ flexShrink: 0 }}
|
|
121
|
+
onRemove={() => updateSelectedLabels(label)}
|
|
122
|
+
/>
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
)
|
|
86
126
|
}
|
|
87
127
|
|
|
88
128
|
return (
|
|
@@ -93,12 +133,12 @@ const ImageLabelInput = ({
|
|
|
93
133
|
variant="outline"
|
|
94
134
|
caret={false}
|
|
95
135
|
color={isDisabled ? 'secondary' : 'primary'}
|
|
96
|
-
style={{ paddingTop: 0, paddingBottom: 0 }}
|
|
136
|
+
// style={{ paddingTop: 0, paddingBottom: 0 }}
|
|
97
137
|
as="div"
|
|
98
138
|
>
|
|
99
139
|
{renderLabels()}
|
|
100
140
|
</CDropdownToggle>
|
|
101
|
-
<CDropdownMenu>
|
|
141
|
+
<CDropdownMenu as="div" style={{ padding: 0 }}>
|
|
102
142
|
<div className="px-3 py-2">
|
|
103
143
|
<CFormInput
|
|
104
144
|
placeholder="Filter label..."
|
|
@@ -108,15 +148,25 @@ const ImageLabelInput = ({
|
|
|
108
148
|
/>
|
|
109
149
|
</div>
|
|
110
150
|
<CDropdownDivider />
|
|
111
|
-
{
|
|
112
|
-
filteredLabels.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
151
|
+
<div style={{ maxHeight: '200px', overflowY: 'auto' }}>
|
|
152
|
+
{filteredLabels.length > 0 ? (
|
|
153
|
+
filteredLabels.map((label: Label) => {
|
|
154
|
+
const isSelected = selectedLabelsIds?.includes(label.id)
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<CDropdownItem
|
|
158
|
+
key={label.id}
|
|
159
|
+
onClick={() => updateSelectedLabels(label)}
|
|
160
|
+
active={isSelected}
|
|
161
|
+
>
|
|
162
|
+
{label.name}
|
|
163
|
+
</CDropdownItem>
|
|
164
|
+
)
|
|
165
|
+
})
|
|
166
|
+
) : (
|
|
167
|
+
<CDropdownItem disabled>No results</CDropdownItem>
|
|
168
|
+
)}
|
|
169
|
+
</div>
|
|
120
170
|
</CDropdownMenu>
|
|
121
171
|
</CDropdown>
|
|
122
172
|
</CTooltip>
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { CSSProperties } from 'react'
|
|
2
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
3
|
+
import { faXmark } from '@fortawesome/free-solid-svg-icons'
|
|
4
|
+
import { IconProps } from 'semantic-ui-react'
|
|
2
5
|
|
|
3
6
|
type TagLabelProps = {
|
|
4
7
|
name: string
|
|
@@ -7,6 +10,7 @@ type TagLabelProps = {
|
|
|
7
10
|
style?: CSSProperties
|
|
8
11
|
triangleSize?: number
|
|
9
12
|
onClick?: () => void
|
|
13
|
+
onRemove?: () => void
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
const TagLabel = ({
|
|
@@ -16,6 +20,7 @@ const TagLabel = ({
|
|
|
16
20
|
style = {},
|
|
17
21
|
triangleSize = 22,
|
|
18
22
|
onClick,
|
|
23
|
+
onRemove,
|
|
19
24
|
}: TagLabelProps) => {
|
|
20
25
|
const containerStyle: CSSProperties = {
|
|
21
26
|
display: 'inline-flex',
|
|
@@ -50,6 +55,34 @@ const TagLabel = ({
|
|
|
50
55
|
<button style={containerStyle} onClick={onClick}>
|
|
51
56
|
<span style={triangleStyle} />
|
|
52
57
|
{name}
|
|
58
|
+
{onRemove && (
|
|
59
|
+
<span
|
|
60
|
+
role="button"
|
|
61
|
+
tabIndex={0}
|
|
62
|
+
onClick={(e) => {
|
|
63
|
+
e.stopPropagation()
|
|
64
|
+
onRemove()
|
|
65
|
+
}}
|
|
66
|
+
onKeyDown={(e) => {
|
|
67
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
68
|
+
e.stopPropagation()
|
|
69
|
+
onRemove()
|
|
70
|
+
}
|
|
71
|
+
}}
|
|
72
|
+
style={{
|
|
73
|
+
marginLeft: '6px',
|
|
74
|
+
cursor: 'pointer',
|
|
75
|
+
opacity: 0.6,
|
|
76
|
+
fontSize: `${bodySize * 0.4}px`,
|
|
77
|
+
lineHeight: 1,
|
|
78
|
+
display: 'inline-flex',
|
|
79
|
+
alignItems: 'center',
|
|
80
|
+
}}
|
|
81
|
+
aria-label="Remove tag"
|
|
82
|
+
>
|
|
83
|
+
<FontAwesomeIcon icon={faXmark as IconProps} />
|
|
84
|
+
</span>
|
|
85
|
+
)}
|
|
53
86
|
</button>
|
|
54
87
|
)
|
|
55
88
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import 'semantic-ui-css/semantic.min.css'
|
|
1
2
|
import './SIA.scss'
|
|
2
3
|
|
|
3
4
|
// export all type definitions
|
|
@@ -5,6 +6,5 @@ export * from './types'
|
|
|
5
6
|
|
|
6
7
|
export { default as IconButton } from './IconButton'
|
|
7
8
|
export { default as Sia } from './Sia'
|
|
8
|
-
export { default as SiaViewer } from './SiaViewer'
|
|
9
9
|
export { default as transform } from './utils/transform'
|
|
10
10
|
export { default as TagLabel } from './Toolbar/ToolbarItems/ImageToolItems/TagLabel'
|
|
@@ -37,10 +37,10 @@ const point: ExternalAnnotation[] = [
|
|
|
37
37
|
const line: ExternalAnnotation[] = [
|
|
38
38
|
{
|
|
39
39
|
coordinates: [
|
|
40
|
-
{ x:
|
|
41
|
-
{ x:
|
|
42
|
-
{ x:
|
|
43
|
-
{ x:
|
|
40
|
+
{ x: 50, y: 50 },
|
|
41
|
+
{ x: 200, y: 100 },
|
|
42
|
+
{ x: 250, y: 100 },
|
|
43
|
+
{ x: 250, y: 200 },
|
|
44
44
|
],
|
|
45
45
|
labelIds: [5],
|
|
46
46
|
type: AnnotationTool.Line,
|
|
@@ -48,11 +48,11 @@ const line: ExternalAnnotation[] = [
|
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
coordinates: [
|
|
51
|
-
{ x:
|
|
52
|
-
{ x:
|
|
53
|
-
{ x:
|
|
54
|
-
{ x:
|
|
55
|
-
{ x:
|
|
51
|
+
{ x: 259.883, y: 300.424 },
|
|
52
|
+
{ x: 350, y: 331.5263919270834 },
|
|
53
|
+
{ x: 355, y: 320 },
|
|
54
|
+
{ x: 370, y: 300 },
|
|
55
|
+
{ x: 270, y: 250 },
|
|
56
56
|
],
|
|
57
57
|
labelIds: [8, 11],
|
|
58
58
|
type: AnnotationTool.Line,
|
|
@@ -63,8 +63,8 @@ const line: ExternalAnnotation[] = [
|
|
|
63
63
|
const bbox: ExternalAnnotation[] = [
|
|
64
64
|
{
|
|
65
65
|
coordinates: [
|
|
66
|
-
{ x:
|
|
67
|
-
{ x:
|
|
66
|
+
{ x: 50, y: 50 },
|
|
67
|
+
{ x: 200, y: 200 },
|
|
68
68
|
],
|
|
69
69
|
labelIds: [5],
|
|
70
70
|
type: AnnotationTool.BBox,
|
|
@@ -72,8 +72,8 @@ const bbox: ExternalAnnotation[] = [
|
|
|
72
72
|
},
|
|
73
73
|
{
|
|
74
74
|
coordinates: [
|
|
75
|
-
{ x:
|
|
76
|
-
{ x:
|
|
75
|
+
{ x: 250, y: 100 },
|
|
76
|
+
{ x: 450, y: 150 },
|
|
77
77
|
],
|
|
78
78
|
labelIds: [8, 11],
|
|
79
79
|
type: AnnotationTool.BBox,
|
|
@@ -84,10 +84,10 @@ const bbox: ExternalAnnotation[] = [
|
|
|
84
84
|
const polygon: ExternalAnnotation[] = [
|
|
85
85
|
{
|
|
86
86
|
coordinates: [
|
|
87
|
-
{ x:
|
|
88
|
-
{ x:
|
|
89
|
-
{ x:
|
|
90
|
-
{ x:
|
|
87
|
+
{ x: 50, y: 50 },
|
|
88
|
+
{ x: 200, y: 100 },
|
|
89
|
+
{ x: 250, y: 100 },
|
|
90
|
+
{ x: 250, y: 200 },
|
|
91
91
|
],
|
|
92
92
|
labelIds: [5],
|
|
93
93
|
status: AnnotationStatus.LOADED,
|
|
@@ -95,11 +95,11 @@ const polygon: ExternalAnnotation[] = [
|
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
coordinates: [
|
|
98
|
-
{ x:
|
|
99
|
-
{ x:
|
|
100
|
-
{ x:
|
|
101
|
-
{ x:
|
|
102
|
-
{ x:
|
|
98
|
+
{ x: 259.883, y: 300.424 },
|
|
99
|
+
{ x: 350, y: 331.5263919270834 },
|
|
100
|
+
{ x: 355, y: 320 },
|
|
101
|
+
{ x: 370, y: 300 },
|
|
102
|
+
{ x: 270, y: 250 },
|
|
103
103
|
],
|
|
104
104
|
labelIds: [8, 11],
|
|
105
105
|
status: AnnotationStatus.LOADED,
|
package/dist/SiaViewer.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { default as Annotation } from './Annotation/logic/Annotation';
|
|
2
|
-
import { ExternalAnnotation, Label, UiConfig } from './types';
|
|
3
|
-
type SiaViewerProps = {
|
|
4
|
-
image: string;
|
|
5
|
-
annotations?: ExternalAnnotation[];
|
|
6
|
-
possibleLabels: Label[];
|
|
7
|
-
isJunk?: boolean;
|
|
8
|
-
uiConfig?: Partial<UiConfig>;
|
|
9
|
-
enableZoom?: boolean;
|
|
10
|
-
canSelect?: boolean;
|
|
11
|
-
onSelectAnnotation?: (annotation?: Annotation) => void;
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* View-only SIA component. Renders image + annotations without a toolbar and
|
|
15
|
-
* blocks all mutations (create / edit / delete / label). Zoom and selection
|
|
16
|
-
* can be toggled via props.
|
|
17
|
-
*/
|
|
18
|
-
declare const SiaViewer: ({ image, annotations: propAnnotations, possibleLabels, isJunk, uiConfig: propUiConfig, enableZoom, canSelect, onSelectAnnotation, }: SiaViewerProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
|
-
export default SiaViewer;
|
package/dist/SiaViewer.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{jsx as r}from"react/jsx-runtime";import{useState as s,useEffect as c}from"react";import p from"./Canvas/Canvas.js";import N from"./models/AnnotationMode.js";import h from"./models/AnnotationTool.js";const E={canCreate:!1,canEdit:!1,canLabel:!1,canHaveMultipleLabels:!1},f={nodeRadius:4,strokeWidth:4,imageCentered:!1},y={flex:"1 1 auto",minHeight:0,display:"flex",flexDirection:"column"},H=({image:n,annotations:e,possibleLabels:u,isJunk:m=!1,uiConfig:a,enableZoom:A=!0,canSelect:l=!0,onSelectAnnotation:I=()=>{}})=>{const[S,d]=s([]),[T,t]=s(),[C,g]=s(f);return c(()=>{if(n===void 0||e===void 0){d([]),t(void 0);return}let o=0;const v=e.map(i=>({...i,internalId:o++,mode:N.VIEW,selectedNode:1,status:i.status,annoTime:i.annoTime??0}));d(v),t(void 0)},[e,n]),c(()=>{g({...f,...a})},[a]),r("div",{style:y,onWheelCapture:o=>{A||o.stopPropagation()},children:r(p,{annotations:S,annotationSettings:E,image:n,isImageJunk:m,isPolygonSelectionMode:!1,possibleLabels:u,selectedAnnotation:l?T:void 0,selectedAnnoTool:h.Point,uiConfig:C,onAnnoCreated:()=>{},onAnnoChanged:()=>{},onAnnoCreationFinished:()=>{},onAnnoEditing:()=>{},onNotification:()=>{},onRequestNewAnnoId:()=>0,onSelectAnnotation:l?o=>{t(o),I(o)}:()=>{},onSetIsImageJunk:()=>{},onSetSelectedTool:()=>{},onShouldDeleteAnno:()=>{},onTraverseAnnotationHistory:()=>{}})})};export{H as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.sia-fullscreen{position:fixed;top:0;left:0;z-index:6000;width:100%;height:100%;background-color:#fff}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react';
|
|
2
|
-
export declare const ActionsData: {};
|
|
3
|
-
declare const meta: {
|
|
4
|
-
title: string;
|
|
5
|
-
component: ({ image, annotations: propAnnotations, possibleLabels, isJunk, uiConfig: propUiConfig, enableZoom, canSelect, onSelectAnnotation, }: {
|
|
6
|
-
image: string;
|
|
7
|
-
annotations?: import('../..').ExternalAnnotation[];
|
|
8
|
-
possibleLabels: import('../..').Label[];
|
|
9
|
-
isJunk?: boolean;
|
|
10
|
-
uiConfig?: Partial<import('../..').UiConfig>;
|
|
11
|
-
enableZoom?: boolean;
|
|
12
|
-
canSelect?: boolean;
|
|
13
|
-
onSelectAnnotation?: (annotation?: import('../../models').Annotation) => void;
|
|
14
|
-
}) => import("react/jsx-runtime").JSX.Element;
|
|
15
|
-
parameters: {
|
|
16
|
-
layout: string;
|
|
17
|
-
};
|
|
18
|
-
tags: string[];
|
|
19
|
-
excludeStories: RegExp;
|
|
20
|
-
args: {};
|
|
21
|
-
};
|
|
22
|
-
export default meta;
|
|
23
|
-
type Story = StoryObj<typeof meta>;
|
|
24
|
-
/**
|
|
25
|
-
* SiaViewer with point annotations. No toolbar, no editing.
|
|
26
|
-
* Try clicking annotations to select them and scrolling to zoom.
|
|
27
|
-
*/
|
|
28
|
-
export declare const WithPointAnnotations: Story;
|
|
29
|
-
/**
|
|
30
|
-
* SiaViewer with bbox annotations.
|
|
31
|
-
*/
|
|
32
|
-
export declare const WithBBoxAnnotations: Story;
|
|
33
|
-
/**
|
|
34
|
-
* SiaViewer with line annotations.
|
|
35
|
-
*/
|
|
36
|
-
export declare const WithLineAnnotations: Story;
|
|
37
|
-
/**
|
|
38
|
-
* SiaViewer with polygon annotations.
|
|
39
|
-
*/
|
|
40
|
-
export declare const WithPolygonAnnotations: Story;
|
|
41
|
-
/**
|
|
42
|
-
* SiaViewer with zoom disabled.
|
|
43
|
-
*/
|
|
44
|
-
export declare const ZoomDisabled: Story;
|
|
45
|
-
/**
|
|
46
|
-
* SiaViewer with selection disabled. Annotations cannot be clicked.
|
|
47
|
-
*/
|
|
48
|
-
export declare const SelectionDisabled: Story;
|
|
49
|
-
/**
|
|
50
|
-
* SiaViewer marked as junk.
|
|
51
|
-
*/
|
|
52
|
-
export declare const Junk: Story;
|
package/src/SiaViewer.tsx
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { CSSProperties, useEffect, useState, WheelEvent } from 'react'
|
|
2
|
-
import Canvas from './Canvas/Canvas'
|
|
3
|
-
import Annotation from './Annotation/logic/Annotation'
|
|
4
|
-
import AnnotationMode from './models/AnnotationMode'
|
|
5
|
-
import AnnotationTool from './models/AnnotationTool'
|
|
6
|
-
import { AnnotationSettings, ExternalAnnotation, Label, UiConfig } from './types'
|
|
7
|
-
|
|
8
|
-
type SiaViewerProps = {
|
|
9
|
-
image: string
|
|
10
|
-
annotations?: ExternalAnnotation[]
|
|
11
|
-
possibleLabels: Label[]
|
|
12
|
-
isJunk?: boolean
|
|
13
|
-
uiConfig?: Partial<UiConfig>
|
|
14
|
-
enableZoom?: boolean
|
|
15
|
-
canSelect?: boolean
|
|
16
|
-
onSelectAnnotation?: (annotation?: Annotation) => void
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const VIEW_ANNOTATION_SETTINGS: AnnotationSettings = {
|
|
20
|
-
canCreate: false,
|
|
21
|
-
canEdit: false,
|
|
22
|
-
canLabel: false,
|
|
23
|
-
canHaveMultipleLabels: false,
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const DEFAULT_UI_CONFIG: UiConfig = {
|
|
27
|
-
nodeRadius: 4,
|
|
28
|
-
strokeWidth: 4,
|
|
29
|
-
imageCentered: false,
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const containerStyle: CSSProperties = {
|
|
33
|
-
flex: '1 1 auto',
|
|
34
|
-
minHeight: 0,
|
|
35
|
-
display: 'flex',
|
|
36
|
-
flexDirection: 'column',
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* View-only SIA component. Renders image + annotations without a toolbar and
|
|
41
|
-
* blocks all mutations (create / edit / delete / label). Zoom and selection
|
|
42
|
-
* can be toggled via props.
|
|
43
|
-
*/
|
|
44
|
-
const SiaViewer = ({
|
|
45
|
-
image,
|
|
46
|
-
annotations: propAnnotations,
|
|
47
|
-
possibleLabels,
|
|
48
|
-
isJunk = false,
|
|
49
|
-
uiConfig: propUiConfig,
|
|
50
|
-
enableZoom = true,
|
|
51
|
-
canSelect = true,
|
|
52
|
-
onSelectAnnotation = () => {},
|
|
53
|
-
}: SiaViewerProps) => {
|
|
54
|
-
const [annotations, setAnnotations] = useState<Annotation[]>([])
|
|
55
|
-
const [selectedAnnotation, setSelectedAnnotation] = useState<
|
|
56
|
-
Annotation | undefined
|
|
57
|
-
>()
|
|
58
|
-
const [uiConfig, setUiConfig] = useState<UiConfig>(DEFAULT_UI_CONFIG)
|
|
59
|
-
|
|
60
|
-
// (re)initialize annotations whenever the annotations or image prop changes
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
if (image === undefined || propAnnotations === undefined) {
|
|
63
|
-
setAnnotations([])
|
|
64
|
-
setSelectedAnnotation(undefined)
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
let internalId = 0
|
|
69
|
-
const next: Annotation[] = propAnnotations.map((externalAnno) => ({
|
|
70
|
-
...externalAnno,
|
|
71
|
-
internalId: internalId++,
|
|
72
|
-
mode: AnnotationMode.VIEW,
|
|
73
|
-
selectedNode: 1,
|
|
74
|
-
status: externalAnno.status,
|
|
75
|
-
annoTime: externalAnno.annoTime ?? 0,
|
|
76
|
-
}))
|
|
77
|
-
|
|
78
|
-
setAnnotations(next)
|
|
79
|
-
setSelectedAnnotation(undefined)
|
|
80
|
-
}, [propAnnotations, image])
|
|
81
|
-
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
setUiConfig({ ...DEFAULT_UI_CONFIG, ...propUiConfig })
|
|
84
|
-
}, [propUiConfig])
|
|
85
|
-
|
|
86
|
-
const stopZoom = (e: WheelEvent) => {
|
|
87
|
-
if (!enableZoom) e.stopPropagation()
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return (
|
|
91
|
-
<div style={containerStyle} onWheelCapture={stopZoom}>
|
|
92
|
-
<Canvas
|
|
93
|
-
annotations={annotations}
|
|
94
|
-
annotationSettings={VIEW_ANNOTATION_SETTINGS}
|
|
95
|
-
image={image}
|
|
96
|
-
isImageJunk={isJunk}
|
|
97
|
-
isPolygonSelectionMode={false}
|
|
98
|
-
possibleLabels={possibleLabels}
|
|
99
|
-
selectedAnnotation={canSelect ? selectedAnnotation : undefined}
|
|
100
|
-
selectedAnnoTool={AnnotationTool.Point}
|
|
101
|
-
uiConfig={uiConfig}
|
|
102
|
-
onAnnoCreated={() => {}}
|
|
103
|
-
onAnnoChanged={() => {}}
|
|
104
|
-
onAnnoCreationFinished={() => {}}
|
|
105
|
-
onAnnoEditing={() => {}}
|
|
106
|
-
onNotification={() => {}}
|
|
107
|
-
onRequestNewAnnoId={() => 0}
|
|
108
|
-
onSelectAnnotation={
|
|
109
|
-
canSelect
|
|
110
|
-
? (annotation) => {
|
|
111
|
-
setSelectedAnnotation(annotation)
|
|
112
|
-
onSelectAnnotation(annotation)
|
|
113
|
-
}
|
|
114
|
-
: () => {}
|
|
115
|
-
}
|
|
116
|
-
onSetIsImageJunk={() => {}}
|
|
117
|
-
onSetSelectedTool={() => {}}
|
|
118
|
-
onShouldDeleteAnno={() => {}}
|
|
119
|
-
onTraverseAnnotationHistory={() => {}}
|
|
120
|
-
/>
|
|
121
|
-
</div>
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export default SiaViewer
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
-
|
|
3
|
-
import SiaViewer from '../../SiaViewer'
|
|
4
|
-
|
|
5
|
-
import exampleImage from '../exampleData/exampleImage'
|
|
6
|
-
import exampleLabels from '../exampleData/exampleLabels'
|
|
7
|
-
import exampleExternalAnnotations from '../exampleData/exampleExternalAnnotations'
|
|
8
|
-
|
|
9
|
-
export const ActionsData = {}
|
|
10
|
-
|
|
11
|
-
const meta = {
|
|
12
|
-
title: 'Components/SiaViewer',
|
|
13
|
-
component: SiaViewer,
|
|
14
|
-
parameters: {
|
|
15
|
-
layout: 'fullscreen',
|
|
16
|
-
},
|
|
17
|
-
tags: ['!autodocs'],
|
|
18
|
-
excludeStories: /.*Data$/,
|
|
19
|
-
args: {
|
|
20
|
-
...ActionsData,
|
|
21
|
-
},
|
|
22
|
-
} satisfies Meta<typeof SiaViewer>
|
|
23
|
-
|
|
24
|
-
export default meta
|
|
25
|
-
type Story = StoryObj<typeof meta>
|
|
26
|
-
|
|
27
|
-
const defaultArgs = {
|
|
28
|
-
...ActionsData,
|
|
29
|
-
image: exampleImage,
|
|
30
|
-
possibleLabels: exampleLabels.voc,
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* SiaViewer with point annotations. No toolbar, no editing.
|
|
35
|
-
* Try clicking annotations to select them and scrolling to zoom.
|
|
36
|
-
*/
|
|
37
|
-
export const WithPointAnnotations: Story = {
|
|
38
|
-
args: {
|
|
39
|
-
...defaultArgs,
|
|
40
|
-
annotations: exampleExternalAnnotations.point,
|
|
41
|
-
},
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* SiaViewer with bbox annotations.
|
|
46
|
-
*/
|
|
47
|
-
export const WithBBoxAnnotations: Story = {
|
|
48
|
-
args: {
|
|
49
|
-
...defaultArgs,
|
|
50
|
-
annotations: exampleExternalAnnotations.bbox,
|
|
51
|
-
},
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* SiaViewer with line annotations.
|
|
56
|
-
*/
|
|
57
|
-
export const WithLineAnnotations: Story = {
|
|
58
|
-
args: {
|
|
59
|
-
...defaultArgs,
|
|
60
|
-
annotations: exampleExternalAnnotations.line,
|
|
61
|
-
},
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* SiaViewer with polygon annotations.
|
|
66
|
-
*/
|
|
67
|
-
export const WithPolygonAnnotations: Story = {
|
|
68
|
-
args: {
|
|
69
|
-
...defaultArgs,
|
|
70
|
-
annotations: exampleExternalAnnotations.polygon,
|
|
71
|
-
},
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* SiaViewer with zoom disabled.
|
|
76
|
-
*/
|
|
77
|
-
export const ZoomDisabled: Story = {
|
|
78
|
-
args: {
|
|
79
|
-
...defaultArgs,
|
|
80
|
-
annotations: exampleExternalAnnotations.polygon,
|
|
81
|
-
enableZoom: false,
|
|
82
|
-
},
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* SiaViewer with selection disabled. Annotations cannot be clicked.
|
|
87
|
-
*/
|
|
88
|
-
export const SelectionDisabled: Story = {
|
|
89
|
-
args: {
|
|
90
|
-
...defaultArgs,
|
|
91
|
-
annotations: exampleExternalAnnotations.polygon,
|
|
92
|
-
canSelect: false,
|
|
93
|
-
},
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* SiaViewer marked as junk.
|
|
98
|
-
*/
|
|
99
|
-
export const Junk: Story = {
|
|
100
|
-
args: {
|
|
101
|
-
...defaultArgs,
|
|
102
|
-
annotations: exampleExternalAnnotations.polygon,
|
|
103
|
-
isJunk: true,
|
|
104
|
-
},
|
|
105
|
-
}
|