@jbrowse/plugin-circular-view 2.1.4 → 2.1.5
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/BaseChordDisplay/components/DisplayError.d.ts +6 -5
- package/dist/BaseChordDisplay/components/DisplayError.js +6 -13
- package/dist/BaseChordDisplay/components/DisplayError.js.map +1 -1
- package/dist/BaseChordDisplay/components/RpcRenderedSvgGroup.d.ts +10 -2
- package/dist/BaseChordDisplay/components/RpcRenderedSvgGroup.js +1 -1
- package/dist/BaseChordDisplay/components/RpcRenderedSvgGroup.js.map +1 -1
- package/dist/BaseChordDisplay/models/BaseChordDisplayModel.d.ts +5 -2
- package/dist/BaseChordDisplay/models/BaseChordDisplayModel.js +29 -31
- package/dist/BaseChordDisplay/models/BaseChordDisplayModel.js.map +1 -1
- package/dist/BaseChordDisplay/models/renderReaction.d.ts +22 -27
- package/dist/BaseChordDisplay/models/renderReaction.js +53 -49
- package/dist/BaseChordDisplay/models/renderReaction.js.map +1 -1
- package/dist/CircularView/components/CircularView.d.ts +5 -3
- package/dist/CircularView/components/CircularView.js +8 -4
- package/dist/CircularView/components/CircularView.js.map +1 -1
- package/dist/CircularView/components/Ruler.d.ts +7 -4
- package/dist/CircularView/components/Ruler.js +11 -5
- package/dist/CircularView/components/Ruler.js.map +1 -1
- package/dist/CircularView/models/CircularView.d.ts +5 -4
- package/dist/CircularView/models/CircularView.js +5 -3
- package/dist/CircularView/models/CircularView.js.map +1 -1
- package/dist/CircularView/models/slices.d.ts +6 -1
- package/dist/CircularView/models/slices.js +1 -4
- package/dist/CircularView/models/slices.js.map +1 -1
- package/dist/CircularView/models/viewportVisibleRegion.d.ts +2 -2
- package/dist/CircularView/models/viewportVisibleRegion.js.map +1 -1
- package/esm/BaseChordDisplay/components/DisplayError.d.ts +6 -5
- package/esm/BaseChordDisplay/components/DisplayError.js +6 -13
- package/esm/BaseChordDisplay/components/DisplayError.js.map +1 -1
- package/esm/BaseChordDisplay/components/RpcRenderedSvgGroup.d.ts +10 -2
- package/esm/BaseChordDisplay/components/RpcRenderedSvgGroup.js +1 -1
- package/esm/BaseChordDisplay/components/RpcRenderedSvgGroup.js.map +1 -1
- package/esm/BaseChordDisplay/models/BaseChordDisplayModel.d.ts +5 -2
- package/esm/BaseChordDisplay/models/BaseChordDisplayModel.js +29 -31
- package/esm/BaseChordDisplay/models/BaseChordDisplayModel.js.map +1 -1
- package/esm/BaseChordDisplay/models/renderReaction.d.ts +22 -27
- package/esm/BaseChordDisplay/models/renderReaction.js +50 -49
- package/esm/BaseChordDisplay/models/renderReaction.js.map +1 -1
- package/esm/CircularView/components/CircularView.d.ts +5 -3
- package/esm/CircularView/components/CircularView.js +8 -4
- package/esm/CircularView/components/CircularView.js.map +1 -1
- package/esm/CircularView/components/Ruler.d.ts +7 -4
- package/esm/CircularView/components/Ruler.js +11 -5
- package/esm/CircularView/components/Ruler.js.map +1 -1
- package/esm/CircularView/models/CircularView.d.ts +5 -4
- package/esm/CircularView/models/CircularView.js +5 -3
- package/esm/CircularView/models/CircularView.js.map +1 -1
- package/esm/CircularView/models/slices.d.ts +6 -1
- package/esm/CircularView/models/slices.js +1 -4
- package/esm/CircularView/models/slices.js.map +1 -1
- package/esm/CircularView/models/viewportVisibleRegion.d.ts +2 -2
- package/esm/CircularView/models/viewportVisibleRegion.js.map +1 -1
- package/package.json +2 -2
- package/src/BaseChordDisplay/components/{DisplayError.js → DisplayError.tsx} +9 -28
- package/src/BaseChordDisplay/components/{RpcRenderedSvgGroup.js → RpcRenderedSvgGroup.tsx} +14 -2
- package/src/BaseChordDisplay/models/BaseChordDisplayModel.ts +49 -52
- package/src/BaseChordDisplay/models/renderReaction.ts +72 -0
- package/src/CircularView/components/{CircularView.js → CircularView.tsx} +80 -69
- package/src/CircularView/components/Ruler.tsx +260 -0
- package/src/CircularView/models/CircularView.ts +7 -4
- package/src/CircularView/models/slices.ts +4 -3
- package/src/CircularView/models/viewportVisibleRegion.ts +4 -4
- package/src/BaseChordDisplay/models/renderReaction.js +0 -70
- package/src/CircularView/components/Ruler.js +0 -222
|
@@ -2,6 +2,7 @@ import React from 'react'
|
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
3
|
import { ResizeHandle, ErrorMessage } from '@jbrowse/core/ui'
|
|
4
4
|
import { assembleLocString } from '@jbrowse/core/util'
|
|
5
|
+
import { CircularViewModel } from '../models/CircularView'
|
|
5
6
|
import { IconButton } from '@mui/material'
|
|
6
7
|
import { makeStyles } from 'tss-react/mui'
|
|
7
8
|
import { grey } from '@mui/material/colors'
|
|
@@ -57,12 +58,13 @@ const useStyles = makeStyles()(theme => ({
|
|
|
57
58
|
},
|
|
58
59
|
}))
|
|
59
60
|
|
|
60
|
-
const Slices = observer(({ model }) => {
|
|
61
|
+
const Slices = observer(({ model }: { model: CircularViewModel }) => {
|
|
61
62
|
return (
|
|
62
63
|
<>
|
|
63
64
|
{model.staticSlices.map(slice => (
|
|
64
65
|
<Ruler
|
|
65
66
|
key={assembleLocString(
|
|
67
|
+
// @ts-ignore
|
|
66
68
|
slice.region.elided ? slice.region.regions[0] : slice.region,
|
|
67
69
|
)}
|
|
68
70
|
model={model}
|
|
@@ -83,84 +85,94 @@ const Slices = observer(({ model }) => {
|
|
|
83
85
|
)
|
|
84
86
|
})
|
|
85
87
|
|
|
86
|
-
const Controls = observer(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
>
|
|
109
|
-
<ZoomInIcon />
|
|
110
|
-
</IconButton>
|
|
88
|
+
const Controls = observer(
|
|
89
|
+
({
|
|
90
|
+
model,
|
|
91
|
+
showingFigure,
|
|
92
|
+
}: {
|
|
93
|
+
model: CircularViewModel
|
|
94
|
+
showingFigure: boolean
|
|
95
|
+
}) => {
|
|
96
|
+
const { classes } = useStyles()
|
|
97
|
+
return (
|
|
98
|
+
<div className={classes.controls}>
|
|
99
|
+
<IconButton
|
|
100
|
+
onClick={model.zoomOutButton}
|
|
101
|
+
className={classes.iconButton}
|
|
102
|
+
title={model.lockedFitToWindow ? 'unlock to zoom out' : 'zoom out'}
|
|
103
|
+
disabled={
|
|
104
|
+
!showingFigure || model.atMaxBpPerPx || model.lockedFitToWindow
|
|
105
|
+
}
|
|
106
|
+
color="secondary"
|
|
107
|
+
>
|
|
108
|
+
<ZoomOutIcon />
|
|
109
|
+
</IconButton>
|
|
111
110
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
111
|
+
<IconButton
|
|
112
|
+
onClick={model.zoomInButton}
|
|
113
|
+
className={classes.iconButton}
|
|
114
|
+
title="zoom in"
|
|
115
|
+
disabled={!showingFigure || model.atMinBpPerPx}
|
|
116
|
+
color="secondary"
|
|
117
|
+
>
|
|
118
|
+
<ZoomInIcon />
|
|
119
|
+
</IconButton>
|
|
121
120
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
121
|
+
<IconButton
|
|
122
|
+
onClick={model.rotateCounterClockwiseButton}
|
|
123
|
+
className={classes.iconButton}
|
|
124
|
+
title="rotate counter-clockwise"
|
|
125
|
+
disabled={!showingFigure}
|
|
126
|
+
color="secondary"
|
|
127
|
+
>
|
|
128
|
+
<RotateLeftIcon />
|
|
129
|
+
</IconButton>
|
|
131
130
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
color="secondary"
|
|
142
|
-
>
|
|
143
|
-
{model.lockedFitToWindow ? <LockIcon /> : <LockOpenIcon />}
|
|
144
|
-
</IconButton>
|
|
131
|
+
<IconButton
|
|
132
|
+
onClick={model.rotateClockwiseButton}
|
|
133
|
+
className={classes.iconButton}
|
|
134
|
+
title="rotate clockwise"
|
|
135
|
+
disabled={!showingFigure}
|
|
136
|
+
color="secondary"
|
|
137
|
+
>
|
|
138
|
+
<RotateRightIcon />
|
|
139
|
+
</IconButton>
|
|
145
140
|
|
|
146
|
-
{model.hideTrackSelectorButton ? null : (
|
|
147
141
|
<IconButton
|
|
148
|
-
onClick={model.
|
|
149
|
-
|
|
150
|
-
|
|
142
|
+
onClick={model.toggleFitToWindowLock}
|
|
143
|
+
className={classes.iconButton}
|
|
144
|
+
title={
|
|
145
|
+
model.lockedFitToWindow
|
|
146
|
+
? 'locked model to window size'
|
|
147
|
+
: 'unlocked model to zoom further'
|
|
148
|
+
}
|
|
149
|
+
disabled={model.tooSmallToLock}
|
|
151
150
|
color="secondary"
|
|
152
151
|
>
|
|
153
|
-
<
|
|
152
|
+
{model.lockedFitToWindow ? <LockIcon /> : <LockOpenIcon />}
|
|
154
153
|
</IconButton>
|
|
155
|
-
)}
|
|
156
|
-
</div>
|
|
157
|
-
)
|
|
158
|
-
})
|
|
159
154
|
|
|
160
|
-
|
|
155
|
+
{model.hideTrackSelectorButton ? null : (
|
|
156
|
+
<IconButton
|
|
157
|
+
onClick={model.activateTrackSelector}
|
|
158
|
+
title="Open track selector"
|
|
159
|
+
data-testid="circular_track_select"
|
|
160
|
+
color="secondary"
|
|
161
|
+
>
|
|
162
|
+
<TrackSelectorIcon />
|
|
163
|
+
</IconButton>
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
)
|
|
167
|
+
},
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
const CircularView = observer(({ model }: { model: CircularViewModel }) => {
|
|
161
171
|
const { classes } = useStyles()
|
|
162
172
|
const initialized =
|
|
163
|
-
!!model.displayedRegions.length &&
|
|
173
|
+
!!model.displayedRegions.length &&
|
|
174
|
+
!!model.figureWidth &&
|
|
175
|
+
!!model.figureHeight
|
|
164
176
|
|
|
165
177
|
const showImportForm = !initialized && !model.disableImportForm
|
|
166
178
|
const showFigure = initialized && !showImportForm
|
|
@@ -189,7 +201,6 @@ const CircularView = observer(({ model }) => {
|
|
|
189
201
|
}}
|
|
190
202
|
>
|
|
191
203
|
<div
|
|
192
|
-
className={classes.rotator}
|
|
193
204
|
style={{
|
|
194
205
|
transform: [`rotate(${model.offsetRadians}rad)`].join(' '),
|
|
195
206
|
transition: 'transform 0.5s',
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
import {
|
|
4
|
+
getSession,
|
|
5
|
+
polarToCartesian,
|
|
6
|
+
radToDeg,
|
|
7
|
+
assembleLocString,
|
|
8
|
+
} from '@jbrowse/core/util'
|
|
9
|
+
import { makeContrasting } from '@jbrowse/core/util/color'
|
|
10
|
+
import { useTheme } from '@mui/material/styles'
|
|
11
|
+
import { makeStyles } from 'tss-react/mui'
|
|
12
|
+
|
|
13
|
+
// locals
|
|
14
|
+
import { Slice } from '../models/slices'
|
|
15
|
+
import { CircularViewModel } from '../models/CircularView'
|
|
16
|
+
|
|
17
|
+
const useStyles = makeStyles()({
|
|
18
|
+
rulerLabel: {
|
|
19
|
+
fontSize: '0.8rem',
|
|
20
|
+
fontWeight: 500,
|
|
21
|
+
lineHeight: 1.6,
|
|
22
|
+
letterSpacing: '0.0075em',
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
function sliceArcPath(
|
|
27
|
+
slice: Slice,
|
|
28
|
+
radiusPx: number,
|
|
29
|
+
startBase: number,
|
|
30
|
+
endBase: number,
|
|
31
|
+
) {
|
|
32
|
+
// A rx ry x-axis-rotation large-arc-flag sweep-flag x y
|
|
33
|
+
if (slice.flipped) {
|
|
34
|
+
;[startBase, endBase] = [endBase, startBase]
|
|
35
|
+
}
|
|
36
|
+
const startXY = slice.bpToXY(startBase, radiusPx)
|
|
37
|
+
const endXY = slice.bpToXY(endBase, radiusPx)
|
|
38
|
+
const largeArc =
|
|
39
|
+
Math.abs(endBase - startBase) / slice.bpPerRadian > Math.PI ? '1' : '0'
|
|
40
|
+
const sweepFlag = '1'
|
|
41
|
+
return [
|
|
42
|
+
'M',
|
|
43
|
+
...startXY,
|
|
44
|
+
'A',
|
|
45
|
+
radiusPx,
|
|
46
|
+
radiusPx,
|
|
47
|
+
'0',
|
|
48
|
+
largeArc,
|
|
49
|
+
sweepFlag,
|
|
50
|
+
...endXY,
|
|
51
|
+
].join(' ')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const ElisionRulerArc = observer(
|
|
55
|
+
({ model, slice }: { model: CircularViewModel; slice: Slice }) => {
|
|
56
|
+
const theme = useTheme()
|
|
57
|
+
const { radiusPx: modelRadiusPx } = model
|
|
58
|
+
const radiusPx = modelRadiusPx + 1
|
|
59
|
+
const { endRadians, startRadians, region } = slice
|
|
60
|
+
const startXY = polarToCartesian(radiusPx, startRadians)
|
|
61
|
+
const endXY = polarToCartesian(radiusPx, endRadians)
|
|
62
|
+
const widthPx = (endRadians - startRadians) * radiusPx
|
|
63
|
+
const largeArc = endRadians - startRadians > Math.PI ? '1' : '0'
|
|
64
|
+
// TODO: draw the elision
|
|
65
|
+
const centerRadians = (endRadians + startRadians) / 2
|
|
66
|
+
const regionCountString = `[${Number(
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
region.regions.length,
|
|
69
|
+
).toLocaleString()}]`
|
|
70
|
+
return (
|
|
71
|
+
<>
|
|
72
|
+
<RulerLabel
|
|
73
|
+
text={regionCountString}
|
|
74
|
+
view={model}
|
|
75
|
+
maxWidthPx={widthPx}
|
|
76
|
+
radians={centerRadians}
|
|
77
|
+
radiusPx={radiusPx}
|
|
78
|
+
title={`${Number(
|
|
79
|
+
// @ts-ignore
|
|
80
|
+
region.regions.length,
|
|
81
|
+
).toLocaleString()} more regions`}
|
|
82
|
+
color={theme.palette.text.primary}
|
|
83
|
+
/>
|
|
84
|
+
<path
|
|
85
|
+
d={[
|
|
86
|
+
'M',
|
|
87
|
+
...startXY,
|
|
88
|
+
'A',
|
|
89
|
+
radiusPx,
|
|
90
|
+
radiusPx,
|
|
91
|
+
'0',
|
|
92
|
+
largeArc,
|
|
93
|
+
'1',
|
|
94
|
+
...endXY,
|
|
95
|
+
].join(' ')}
|
|
96
|
+
stroke={theme.palette.text.secondary}
|
|
97
|
+
strokeWidth={2}
|
|
98
|
+
strokeDasharray="2,2"
|
|
99
|
+
fill="none"
|
|
100
|
+
/>
|
|
101
|
+
</>
|
|
102
|
+
)
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
const RulerLabel = observer(
|
|
107
|
+
({
|
|
108
|
+
view,
|
|
109
|
+
text,
|
|
110
|
+
maxWidthPx,
|
|
111
|
+
radians,
|
|
112
|
+
radiusPx,
|
|
113
|
+
title,
|
|
114
|
+
color,
|
|
115
|
+
}: {
|
|
116
|
+
view: CircularViewModel
|
|
117
|
+
text: string
|
|
118
|
+
maxWidthPx: number
|
|
119
|
+
radiusPx: number
|
|
120
|
+
radians: number
|
|
121
|
+
title?: string
|
|
122
|
+
color: string
|
|
123
|
+
}) => {
|
|
124
|
+
const { classes } = useStyles()
|
|
125
|
+
const textXY = polarToCartesian(radiusPx + 5, radians)
|
|
126
|
+
if (!text) {
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (text.length * 6.5 < maxWidthPx) {
|
|
131
|
+
// text is rotated parallel to the ruler arc
|
|
132
|
+
return (
|
|
133
|
+
<text
|
|
134
|
+
x={0}
|
|
135
|
+
y={0}
|
|
136
|
+
className={classes.rulerLabel}
|
|
137
|
+
textAnchor="middle"
|
|
138
|
+
dominantBaseline="baseline"
|
|
139
|
+
transform={`translate(${textXY}) rotate(${radToDeg(radians) + 90})`}
|
|
140
|
+
style={{ fill: color }}
|
|
141
|
+
>
|
|
142
|
+
{text}
|
|
143
|
+
<title>{title || text}</title>
|
|
144
|
+
</text>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
if (maxWidthPx > 4) {
|
|
148
|
+
// text is rotated perpendicular to the ruler arc
|
|
149
|
+
const overallRotation = radToDeg(
|
|
150
|
+
radians + view.offsetRadians - Math.PI / 2,
|
|
151
|
+
)
|
|
152
|
+
if (overallRotation >= 180) {
|
|
153
|
+
return (
|
|
154
|
+
<text
|
|
155
|
+
x={0}
|
|
156
|
+
y={0}
|
|
157
|
+
className={classes.rulerLabel}
|
|
158
|
+
textAnchor="start"
|
|
159
|
+
dominantBaseline="middle"
|
|
160
|
+
transform={`translate(${textXY}) rotate(${radToDeg(radians)})`}
|
|
161
|
+
style={{ fill: color }}
|
|
162
|
+
>
|
|
163
|
+
{text}
|
|
164
|
+
<title>{title || text}</title>
|
|
165
|
+
</text>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
return (
|
|
169
|
+
<text
|
|
170
|
+
x={0}
|
|
171
|
+
y={0}
|
|
172
|
+
className={classes.rulerLabel}
|
|
173
|
+
textAnchor="end"
|
|
174
|
+
dominantBaseline="middle"
|
|
175
|
+
transform={`translate(${textXY}) rotate(${radToDeg(radians) + 180})`}
|
|
176
|
+
style={{ fill: color }}
|
|
177
|
+
>
|
|
178
|
+
{text}
|
|
179
|
+
<title>{title || text}</title>
|
|
180
|
+
</text>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// if you get here there is no room for the text at all
|
|
185
|
+
return null
|
|
186
|
+
},
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
const RegionRulerArc = observer(
|
|
190
|
+
({ model, slice }: { model: CircularViewModel; slice: Slice }) => {
|
|
191
|
+
const theme = useTheme()
|
|
192
|
+
const { radiusPx } = model
|
|
193
|
+
const { region, endRadians, startRadians } = slice
|
|
194
|
+
const centerRadians = (endRadians + startRadians) / 2
|
|
195
|
+
const widthPx = (endRadians - startRadians) * radiusPx
|
|
196
|
+
const session = getSession(model)
|
|
197
|
+
let color
|
|
198
|
+
const assembly = session.assemblyManager.get(slice.region.assemblyName)
|
|
199
|
+
if (assembly) {
|
|
200
|
+
color = assembly.getRefNameColor(region.refName)
|
|
201
|
+
}
|
|
202
|
+
if (color) {
|
|
203
|
+
try {
|
|
204
|
+
color = makeContrasting(color, theme.palette.background.paper)
|
|
205
|
+
} catch (error) {
|
|
206
|
+
color = theme.palette.text.primary
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
color = theme.palette.text.primary
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// TODO: slice flipping
|
|
213
|
+
return (
|
|
214
|
+
<>
|
|
215
|
+
<RulerLabel
|
|
216
|
+
text={region.refName}
|
|
217
|
+
view={model}
|
|
218
|
+
maxWidthPx={widthPx}
|
|
219
|
+
radians={centerRadians}
|
|
220
|
+
radiusPx={radiusPx}
|
|
221
|
+
color={color}
|
|
222
|
+
/>
|
|
223
|
+
<path
|
|
224
|
+
d={sliceArcPath(slice, radiusPx + 1, region.start, region.end)}
|
|
225
|
+
stroke={color}
|
|
226
|
+
strokeWidth={2}
|
|
227
|
+
fill="none"
|
|
228
|
+
>
|
|
229
|
+
<title>{region.refName}</title>
|
|
230
|
+
</path>
|
|
231
|
+
</>
|
|
232
|
+
)
|
|
233
|
+
},
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
const CircularRuler = observer(
|
|
237
|
+
({ model, slice }: { model: CircularViewModel; slice: Slice }) => {
|
|
238
|
+
if (slice.region.elided) {
|
|
239
|
+
return (
|
|
240
|
+
<ElisionRulerArc
|
|
241
|
+
key={
|
|
242
|
+
/* @ts-ignore */
|
|
243
|
+
assembleLocString(slice.region.regions[0])
|
|
244
|
+
}
|
|
245
|
+
model={model}
|
|
246
|
+
slice={slice}
|
|
247
|
+
/>
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
return (
|
|
251
|
+
<RegionRulerArc
|
|
252
|
+
key={assembleLocString(slice.region)}
|
|
253
|
+
model={model}
|
|
254
|
+
slice={slice}
|
|
255
|
+
/>
|
|
256
|
+
)
|
|
257
|
+
},
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
export default CircularRuler
|
|
@@ -67,9 +67,7 @@ export default function CircularView(pluginManager: PluginManager) {
|
|
|
67
67
|
get staticSlices() {
|
|
68
68
|
return calculateStaticSlices(self)
|
|
69
69
|
},
|
|
70
|
-
|
|
71
|
-
return this.staticSlices.filter(sliceIsVisible.bind(this, self))
|
|
72
|
-
},
|
|
70
|
+
|
|
73
71
|
get visibleSection() {
|
|
74
72
|
return viewportVisibleSection(
|
|
75
73
|
[
|
|
@@ -140,7 +138,7 @@ export default function CircularView(pluginManager: PluginManager) {
|
|
|
140
138
|
get tooSmallToLock() {
|
|
141
139
|
return this.minBpPerPx <= 0.0000000001
|
|
142
140
|
},
|
|
143
|
-
get figureDimensions() {
|
|
141
|
+
get figureDimensions(): [number, number] {
|
|
144
142
|
return [
|
|
145
143
|
this.radiusPx * 2 + 2 * self.paddingPx,
|
|
146
144
|
this.radiusPx * 2 + 2 * self.paddingPx,
|
|
@@ -206,6 +204,11 @@ export default function CircularView(pluginManager: PluginManager) {
|
|
|
206
204
|
)
|
|
207
205
|
},
|
|
208
206
|
}))
|
|
207
|
+
.views(self => ({
|
|
208
|
+
get visibleStaticSlices() {
|
|
209
|
+
return self.staticSlices.filter(s => sliceIsVisible(self, s))
|
|
210
|
+
},
|
|
211
|
+
}))
|
|
209
212
|
.volatile(() => ({
|
|
210
213
|
error: undefined as unknown,
|
|
211
214
|
}))
|
|
@@ -62,10 +62,11 @@ function calculateStaticSlices(self: any) {
|
|
|
62
62
|
return slices
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
function sliceIsVisible(
|
|
66
|
+
self: { offsetRadians: number; visibleSection: { theta: [number, number] } },
|
|
67
|
+
slice: Slice,
|
|
68
|
+
) {
|
|
67
69
|
const {
|
|
68
|
-
// rho: visibleRhoRange,
|
|
69
70
|
theta: [visibleThetaMin, visibleThetaMax],
|
|
70
71
|
} = self.visibleSection
|
|
71
72
|
|
|
@@ -124,8 +124,8 @@ export function viewportVisibleSection(
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
return {
|
|
127
|
-
rho: [0, Math.min(circleRadius, maxRho)],
|
|
128
|
-
theta: [0, 2 * Math.PI],
|
|
127
|
+
rho: [0, Math.min(circleRadius, maxRho)] as [number, number],
|
|
128
|
+
theta: [0, 2 * Math.PI] as [number, number],
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
// const viewportCompletelyContainsCircle =
|
|
@@ -268,7 +268,7 @@ export function viewportVisibleSection(
|
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
return {
|
|
271
|
-
rho: [rhoMin, Math.min(circleRadius, rhoMax)],
|
|
272
|
-
theta: [thetaMin, thetaMax],
|
|
271
|
+
rho: [rhoMin, Math.min(circleRadius, rhoMax)] as [number, number],
|
|
272
|
+
theta: [thetaMin, thetaMax] as [number, number],
|
|
273
273
|
}
|
|
274
274
|
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
export default ({ jbrequire }) => {
|
|
2
|
-
const { getRpcSessionId } = jbrequire('@jbrowse/core/util/tracks')
|
|
3
|
-
const { getContainingView } = jbrequire('@jbrowse/core/util')
|
|
4
|
-
const { getSession } = jbrequire('@jbrowse/core/util')
|
|
5
|
-
|
|
6
|
-
function renderReactionData(self) {
|
|
7
|
-
const view = getContainingView(self)
|
|
8
|
-
const { rendererType } = self
|
|
9
|
-
const { rpcManager } = getSession(view)
|
|
10
|
-
|
|
11
|
-
return {
|
|
12
|
-
rendererType,
|
|
13
|
-
rpcManager,
|
|
14
|
-
renderProps: self.renderProps(),
|
|
15
|
-
renderArgs: {
|
|
16
|
-
assemblyName: view.displayedRegions[0]?.assemblyName,
|
|
17
|
-
adapterConfig: JSON.parse(JSON.stringify(self.adapterConfig)),
|
|
18
|
-
rendererType: rendererType.name,
|
|
19
|
-
regions: JSON.parse(JSON.stringify(view.displayedRegions)),
|
|
20
|
-
blockDefinitions: self.blockDefinitions,
|
|
21
|
-
sessionId: getRpcSessionId(self),
|
|
22
|
-
timeout: 1000000,
|
|
23
|
-
},
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function renderReactionEffect(props, signal, self) {
|
|
28
|
-
if (!props) {
|
|
29
|
-
throw new Error('cannot render with no props')
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const {
|
|
33
|
-
rendererType,
|
|
34
|
-
rpcManager,
|
|
35
|
-
cannotBeRenderedReason,
|
|
36
|
-
renderArgs,
|
|
37
|
-
renderProps,
|
|
38
|
-
} = props
|
|
39
|
-
|
|
40
|
-
if (cannotBeRenderedReason) {
|
|
41
|
-
return { message: cannotBeRenderedReason }
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// don't try to render 0 or NaN radius or no regions
|
|
45
|
-
if (
|
|
46
|
-
!props.renderProps.radius ||
|
|
47
|
-
!props.renderArgs.regions ||
|
|
48
|
-
!props.renderArgs.regions.length
|
|
49
|
-
) {
|
|
50
|
-
return { message: 'Skipping render' }
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// check renderertype compatibility
|
|
54
|
-
if (!self.isCompatibleWithRenderer(rendererType)) {
|
|
55
|
-
throw new Error(
|
|
56
|
-
`renderer ${rendererType.name} is not compatible with this display type`,
|
|
57
|
-
)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const { html, ...data } = await rendererType.renderInClient(rpcManager, {
|
|
61
|
-
...renderArgs,
|
|
62
|
-
...renderProps,
|
|
63
|
-
signal,
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
return { html, data, renderingComponent: rendererType.ReactComponent }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return { renderReactionData, renderReactionEffect }
|
|
70
|
-
}
|