react-msaview 5.1.0 → 5.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/bundle/index.js +35 -135
- package/bundle/index.js.map +1 -1
- package/dist/calculateBlocks.d.ts +4 -10
- package/dist/calculateBlocks.js +12 -20
- package/dist/calculateBlocks.js.map +1 -1
- package/dist/components/ConservationTrack.js +18 -24
- package/dist/components/ConservationTrack.js.map +1 -1
- package/dist/components/TextTrack.js +24 -34
- package/dist/components/TextTrack.js.map +1 -1
- package/dist/components/dialogs/ExportSVGDialog.js +20 -20
- package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
- package/dist/components/dialogs/InterProScanDialog.js +6 -1
- package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
- package/dist/components/header/FileMenu.js +2 -1
- package/dist/components/header/FileMenu.js.map +1 -1
- package/dist/components/header/Header.js +1 -1
- package/dist/components/header/settingsMenuItems.js +24 -8
- package/dist/components/header/settingsMenuItems.js.map +1 -1
- package/dist/components/minimap/Minimap.js +1 -6
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/minimap/MinimapSVG.js +4 -8
- package/dist/components/minimap/MinimapSVG.js.map +1 -1
- package/dist/components/msa/getVisibleLeaves.d.ts +6 -0
- package/dist/components/msa/getVisibleLeaves.js +9 -0
- package/dist/components/msa/getVisibleLeaves.js.map +1 -0
- package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +2 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +5 -6
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.d.ts +2 -1
- package/dist/components/msa/renderMSABlock.js +98 -84
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +3 -3
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/msa/visibleColRange.d.ts +8 -0
- package/dist/components/msa/visibleColRange.js +10 -0
- package/dist/components/msa/visibleColRange.js.map +1 -0
- package/dist/components/renderCtx.d.ts +7 -0
- package/dist/components/renderCtx.js +2 -0
- package/dist/components/renderCtx.js.map +1 -0
- package/dist/components/tracks/renderTracksSvg.d.ts +6 -5
- package/dist/components/tracks/renderTracksSvg.js +11 -4
- package/dist/components/tracks/renderTracksSvg.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +2 -10
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.d.ts +5 -4
- package/dist/components/tree/renderTreeCanvas.js +16 -24
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/components/util.d.ts +0 -14
- package/dist/components/util.js +0 -58
- package/dist/components/util.js.map +1 -1
- package/dist/hierarchy.d.ts +2 -0
- package/dist/hierarchy.js +19 -0
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/model.d.ts +26 -30
- package/dist/model.js +74 -82
- package/dist/model.js.map +1 -1
- package/dist/renderToSvg.d.ts +1 -1
- package/dist/renderToSvg.js +20 -26
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.d.ts +0 -9
- package/dist/rowCoordinateCalculations.js +0 -20
- package/dist/rowCoordinateCalculations.js.map +1 -1
- package/dist/util.d.ts +1 -0
- package/dist/util.js +23 -1
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -5
- package/src/calculateBlocks.ts +18 -51
- package/src/components/ConservationTrack.tsx +18 -27
- package/src/components/TextTrack.tsx +30 -38
- package/src/components/dialogs/ExportSVGDialog.tsx +27 -17
- package/src/components/dialogs/InterProScanDialog.tsx +6 -1
- package/src/components/header/FileMenu.tsx +2 -1
- package/src/components/header/Header.tsx +1 -1
- package/src/components/header/settingsMenuItems.ts +24 -8
- package/src/components/minimap/Minimap.tsx +1 -6
- package/src/components/minimap/MinimapSVG.tsx +4 -8
- package/src/components/msa/getVisibleLeaves.ts +21 -0
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +9 -9
- package/src/components/msa/renderMSABlock.ts +119 -118
- package/src/components/msa/renderMSAMouseover.ts +2 -3
- package/src/components/msa/visibleColRange.ts +17 -0
- package/src/components/renderCtx.ts +29 -0
- package/src/components/tracks/renderTracksSvg.ts +18 -9
- package/src/components/tree/TreeCanvas.tsx +3 -1
- package/src/components/tree/TreeCanvasBlock.tsx +2 -12
- package/src/components/tree/renderTreeCanvas.ts +25 -28
- package/src/components/util.ts +0 -81
- package/src/hierarchy.ts +27 -0
- package/src/index.ts +7 -0
- package/src/model.ts +81 -98
- package/src/renderToSvg.tsx +23 -31
- package/src/rowCoordinateCalculations.test.ts +0 -51
- package/src/rowCoordinateCalculations.ts +0 -23
- package/src/util.ts +25 -1
- package/src/version.ts +1 -1
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { getVisibleLeaves } from './getVisibleLeaves.ts'
|
|
2
|
+
|
|
1
3
|
import type { HierarchyNode } from '../../hierarchy.ts'
|
|
2
4
|
import type { MsaViewModel } from '../../model.ts'
|
|
3
5
|
import type { NodeWithIdsAndLength } from '../../types.ts'
|
|
6
|
+
import type { RenderCtx } from '../renderCtx.ts'
|
|
4
7
|
|
|
5
8
|
export function renderBoxFeatureCanvasBlock({
|
|
6
9
|
model,
|
|
@@ -13,22 +16,19 @@ export function renderBoxFeatureCanvasBlock({
|
|
|
13
16
|
offsetX: number
|
|
14
17
|
offsetY: number
|
|
15
18
|
model: MsaViewModel
|
|
16
|
-
ctx:
|
|
19
|
+
ctx: RenderCtx
|
|
17
20
|
highResScaleFactorOverride?: number
|
|
18
21
|
blockSizeYOverride?: number
|
|
19
22
|
}) {
|
|
20
|
-
const {
|
|
21
|
-
model
|
|
23
|
+
const { blockSize, rowHeight, highResScaleFactor, showDomains } = model
|
|
22
24
|
if (showDomains) {
|
|
23
|
-
const k = highResScaleFactorOverride
|
|
24
|
-
const by = blockSizeYOverride
|
|
25
|
+
const k = highResScaleFactorOverride ?? highResScaleFactor
|
|
26
|
+
const by = blockSizeYOverride ?? blockSize
|
|
25
27
|
ctx.resetTransform()
|
|
26
28
|
ctx.scale(k, k)
|
|
27
29
|
ctx.translate(-offsetX, rowHeight / 2 - offsetY)
|
|
28
30
|
|
|
29
|
-
const
|
|
30
|
-
const yEnd = Math.max(0, Math.ceil((offsetY + by + rowHeight) / rowHeight))
|
|
31
|
-
const visibleLeaves = leaves.slice(yStart, yEnd)
|
|
31
|
+
const visibleLeaves = getVisibleLeaves({ model, offsetY, blockSizeY: by })
|
|
32
32
|
|
|
33
33
|
drawTiles({
|
|
34
34
|
model,
|
|
@@ -44,7 +44,7 @@ function drawTiles({
|
|
|
44
44
|
visibleLeaves,
|
|
45
45
|
}: {
|
|
46
46
|
model: MsaViewModel
|
|
47
|
-
ctx:
|
|
47
|
+
ctx: RenderCtx
|
|
48
48
|
visibleLeaves: HierarchyNode<NodeWithIdsAndLength>[]
|
|
49
49
|
}) {
|
|
50
50
|
const {
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { getVisibleLeaves } from './getVisibleLeaves.ts'
|
|
2
|
+
import { visibleColRange } from './visibleColRange.ts'
|
|
3
|
+
import { adjustColorForContrast } from '../../util.ts'
|
|
4
|
+
|
|
1
5
|
import type { HierarchyNode } from '../../hierarchy.ts'
|
|
2
6
|
import type { MsaViewModel } from '../../model.ts'
|
|
3
7
|
import type { NodeWithIdsAndLength } from '../../types.ts'
|
|
8
|
+
import type { RenderCtx } from '../renderCtx.ts'
|
|
4
9
|
import type { Theme } from '@mui/material'
|
|
5
10
|
|
|
6
11
|
export function renderMSABlock({
|
|
@@ -19,7 +24,7 @@ export function renderMSABlock({
|
|
|
19
24
|
theme: Theme
|
|
20
25
|
model: MsaViewModel
|
|
21
26
|
contrastScheme: Record<string, string>
|
|
22
|
-
ctx:
|
|
27
|
+
ctx: RenderCtx
|
|
23
28
|
highResScaleFactorOverride?: number
|
|
24
29
|
blockSizeXOverride?: number
|
|
25
30
|
blockSizeYOverride?: number
|
|
@@ -31,12 +36,11 @@ export function renderMSABlock({
|
|
|
31
36
|
fontSize,
|
|
32
37
|
highResScaleFactor,
|
|
33
38
|
actuallyShowDomains,
|
|
34
|
-
leaves,
|
|
35
39
|
bgColor,
|
|
36
40
|
} = model
|
|
37
|
-
const k = highResScaleFactorOverride
|
|
38
|
-
const bx = blockSizeXOverride
|
|
39
|
-
const by = blockSizeYOverride
|
|
41
|
+
const k = highResScaleFactorOverride ?? highResScaleFactor
|
|
42
|
+
const bx = blockSizeXOverride ?? blockSize
|
|
43
|
+
const by = blockSizeYOverride ?? blockSize
|
|
40
44
|
ctx.resetTransform()
|
|
41
45
|
ctx.scale(k, k)
|
|
42
46
|
ctx.translate(-offsetX, rowHeight / 2 - offsetY)
|
|
@@ -49,32 +53,40 @@ export function renderMSABlock({
|
|
|
49
53
|
`${bgColor ? '' : 'bold '}${fontSize}px`,
|
|
50
54
|
)
|
|
51
55
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
const { xStart, xEnd } = visibleColRange({
|
|
57
|
+
offsetX,
|
|
58
|
+
blockWidth: bx,
|
|
59
|
+
colWidth,
|
|
60
|
+
})
|
|
61
|
+
const visibleLeaves = getVisibleLeaves({ model, offsetY, blockSizeY: by })
|
|
62
|
+
const { relativeTo, columns } = model
|
|
63
|
+
const referenceSeq = relativeTo
|
|
64
|
+
? columns.get(relativeTo)?.slice(xStart, xEnd)
|
|
65
|
+
: null
|
|
57
66
|
|
|
58
67
|
if (!actuallyShowDomains) {
|
|
59
|
-
|
|
68
|
+
drawTilesAndText({
|
|
60
69
|
model,
|
|
61
70
|
ctx,
|
|
62
71
|
theme,
|
|
72
|
+
contrastScheme,
|
|
63
73
|
offsetX,
|
|
64
74
|
xStart,
|
|
65
75
|
xEnd,
|
|
66
76
|
visibleLeaves,
|
|
77
|
+
referenceSeq,
|
|
78
|
+
})
|
|
79
|
+
} else {
|
|
80
|
+
drawText({
|
|
81
|
+
model,
|
|
82
|
+
ctx,
|
|
83
|
+
offsetX,
|
|
84
|
+
xStart,
|
|
85
|
+
xEnd,
|
|
86
|
+
visibleLeaves,
|
|
87
|
+
referenceSeq,
|
|
67
88
|
})
|
|
68
89
|
}
|
|
69
|
-
drawText({
|
|
70
|
-
model,
|
|
71
|
-
ctx,
|
|
72
|
-
offsetX,
|
|
73
|
-
contrastScheme,
|
|
74
|
-
xStart,
|
|
75
|
-
xEnd,
|
|
76
|
-
visibleLeaves,
|
|
77
|
-
})
|
|
78
90
|
drawInsertionIndicators({
|
|
79
91
|
model,
|
|
80
92
|
ctx,
|
|
@@ -85,22 +97,26 @@ export function renderMSABlock({
|
|
|
85
97
|
ctx.resetTransform()
|
|
86
98
|
}
|
|
87
99
|
|
|
88
|
-
function
|
|
100
|
+
function drawTilesAndText({
|
|
89
101
|
model,
|
|
90
102
|
offsetX,
|
|
91
103
|
ctx,
|
|
92
|
-
visibleLeaves,
|
|
93
104
|
theme,
|
|
105
|
+
contrastScheme,
|
|
106
|
+
visibleLeaves,
|
|
94
107
|
xStart,
|
|
95
108
|
xEnd,
|
|
109
|
+
referenceSeq,
|
|
96
110
|
}: {
|
|
97
111
|
model: MsaViewModel
|
|
98
112
|
offsetX: number
|
|
99
113
|
theme: Theme
|
|
100
|
-
ctx:
|
|
114
|
+
ctx: RenderCtx
|
|
115
|
+
contrastScheme: Record<string, string>
|
|
101
116
|
visibleLeaves: HierarchyNode<NodeWithIdsAndLength>[]
|
|
102
117
|
xStart: number
|
|
103
118
|
xEnd: number
|
|
119
|
+
referenceSeq: string | null | undefined
|
|
104
120
|
}) {
|
|
105
121
|
const {
|
|
106
122
|
bgColor,
|
|
@@ -110,131 +126,116 @@ function drawTiles({
|
|
|
110
126
|
colWidth,
|
|
111
127
|
rowHeight,
|
|
112
128
|
relativeTo,
|
|
129
|
+
showMsaLetters,
|
|
113
130
|
} = model
|
|
114
131
|
|
|
115
|
-
// Get reference sequence if relativeTo is set
|
|
116
|
-
const referenceSeq = relativeTo
|
|
117
|
-
? columns[relativeTo]?.slice(xStart, xEnd)
|
|
118
|
-
: null
|
|
119
|
-
|
|
120
132
|
const isClustalX = colorSchemeName === 'clustalx_protein_dynamic'
|
|
121
133
|
const isPercentIdentity = colorSchemeName === 'percent_identity_dynamic'
|
|
134
|
+
const drawBgTiles = bgColor || isClustalX || isPercentIdentity
|
|
135
|
+
if (!drawBgTiles && !showMsaLetters) {
|
|
136
|
+
return
|
|
137
|
+
}
|
|
122
138
|
const offsetXAligned = offsetX - (offsetX % colWidth)
|
|
139
|
+
const halfColWidth = colWidth / 2
|
|
140
|
+
const quarterRowHeight = rowHeight / 4
|
|
123
141
|
|
|
124
142
|
for (let i = 0, l1 = visibleLeaves.length; i < l1; i++) {
|
|
125
143
|
const node = visibleLeaves[i]!
|
|
126
|
-
const {
|
|
127
|
-
data: { name },
|
|
128
|
-
} = node
|
|
144
|
+
const { name } = node.data
|
|
129
145
|
const y = node.x!
|
|
130
|
-
const str = columns
|
|
131
|
-
if (str) {
|
|
132
|
-
|
|
133
|
-
|
|
146
|
+
const str = columns.get(name)?.slice(xStart, xEnd)
|
|
147
|
+
if (!str) {
|
|
148
|
+
continue
|
|
149
|
+
}
|
|
150
|
+
const tileY = y - rowHeight
|
|
151
|
+
const textY = y - quarterRowHeight
|
|
134
152
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
153
|
+
for (let j = 0, l2 = str.length; j < l2; j++) {
|
|
154
|
+
const letter = str[j]!
|
|
155
|
+
const x = j * colWidth + offsetXAligned
|
|
156
|
+
const isMatchingReference =
|
|
157
|
+
referenceSeq && name !== relativeTo && letter === referenceSeq[j]
|
|
138
158
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
let color: string | undefined
|
|
160
|
+
if (isClustalX) {
|
|
161
|
+
color = model.colClustalX[xStart + j]![letter]
|
|
162
|
+
} else if (isPercentIdentity) {
|
|
163
|
+
const consensus = model.colConsensus[xStart + j]!
|
|
164
|
+
color = letter === consensus.letter ? consensus.color : undefined
|
|
165
|
+
} else {
|
|
166
|
+
color = colorScheme[letter]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (drawBgTiles) {
|
|
170
|
+
ctx.fillStyle = isMatchingReference
|
|
171
|
+
? theme.palette.action.hover
|
|
172
|
+
: color || theme.palette.background.default
|
|
173
|
+
ctx.fillRect(x, tileY, colWidth, rowHeight)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (showMsaLetters) {
|
|
177
|
+
ctx.fillStyle = bgColor
|
|
178
|
+
? (contrastScheme[letter] ?? 'black')
|
|
179
|
+
: color
|
|
180
|
+
? adjustColorForContrast(color, theme.palette.background.default)
|
|
181
|
+
: theme.palette.text.primary
|
|
182
|
+
ctx.fillText(
|
|
183
|
+
isMatchingReference ? '.' : letter,
|
|
184
|
+
x + halfColWidth,
|
|
185
|
+
textY,
|
|
186
|
+
)
|
|
161
187
|
}
|
|
162
188
|
}
|
|
163
189
|
}
|
|
164
190
|
}
|
|
165
191
|
|
|
192
|
+
// When domains are shown the background tiles come from renderBoxFeatureCanvasBlock,
|
|
193
|
+
// so we only need to draw text here.
|
|
166
194
|
function drawText({
|
|
167
195
|
model,
|
|
168
196
|
offsetX,
|
|
169
|
-
contrastScheme,
|
|
170
197
|
ctx,
|
|
171
198
|
visibleLeaves,
|
|
172
199
|
xStart,
|
|
173
200
|
xEnd,
|
|
201
|
+
referenceSeq,
|
|
174
202
|
}: {
|
|
175
203
|
offsetX: number
|
|
176
204
|
model: MsaViewModel
|
|
177
|
-
|
|
178
|
-
ctx: CanvasRenderingContext2D
|
|
205
|
+
ctx: RenderCtx
|
|
179
206
|
visibleLeaves: HierarchyNode<NodeWithIdsAndLength>[]
|
|
180
207
|
xStart: number
|
|
181
208
|
xEnd: number
|
|
209
|
+
referenceSeq: string | null | undefined
|
|
182
210
|
}) {
|
|
183
|
-
const {
|
|
184
|
-
bgColor,
|
|
185
|
-
actuallyShowDomains,
|
|
186
|
-
showMsaLetters,
|
|
187
|
-
colorScheme,
|
|
188
|
-
columns,
|
|
189
|
-
colWidth,
|
|
190
|
-
rowHeight,
|
|
191
|
-
relativeTo,
|
|
192
|
-
} = model
|
|
211
|
+
const { showMsaLetters, columns, colWidth, rowHeight, relativeTo } = model
|
|
193
212
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const quarterRowHeight = rowHeight / 4
|
|
203
|
-
|
|
204
|
-
for (let i = 0, l1 = visibleLeaves.length; i < l1; i++) {
|
|
205
|
-
const node = visibleLeaves[i]!
|
|
206
|
-
const {
|
|
207
|
-
data: { name },
|
|
208
|
-
} = node
|
|
209
|
-
const y = node.x! - quarterRowHeight
|
|
210
|
-
const str = columns[name]?.slice(xStart, xEnd)
|
|
211
|
-
if (str) {
|
|
212
|
-
for (let j = 0, l2 = str.length; j < l2; j++) {
|
|
213
|
-
const letter = str[j]!
|
|
214
|
-
|
|
215
|
-
// Check if this position matches the reference
|
|
216
|
-
const isMatchingReference =
|
|
217
|
-
referenceSeq && name !== relativeTo && letter === referenceSeq[j]
|
|
218
|
-
|
|
219
|
-
// Show dot for matching positions, original letter for differences
|
|
220
|
-
const displayLetter = isMatchingReference ? '.' : letter
|
|
221
|
-
|
|
222
|
-
const color = colorScheme[letter.toUpperCase()]
|
|
223
|
-
const contrast = contrastScheme[letter.toUpperCase()] || 'black'
|
|
213
|
+
if (!showMsaLetters) {
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
const offsetXAligned = offsetX - (offsetX % colWidth)
|
|
217
|
+
const halfColWidth = colWidth / 2
|
|
218
|
+
const quarterRowHeight = rowHeight / 4
|
|
219
|
+
// note: -rowHeight/4 matches +rowHeight/4 in tree
|
|
220
|
+
ctx.fillStyle = 'black'
|
|
224
221
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
222
|
+
for (let i = 0, l1 = visibleLeaves.length; i < l1; i++) {
|
|
223
|
+
const node = visibleLeaves[i]!
|
|
224
|
+
const { name } = node.data
|
|
225
|
+
const y = node.x! - quarterRowHeight
|
|
226
|
+
const str = columns.get(name)?.slice(xStart, xEnd)
|
|
227
|
+
if (!str) {
|
|
228
|
+
continue
|
|
229
|
+
}
|
|
230
|
+
for (let j = 0, l2 = str.length; j < l2; j++) {
|
|
231
|
+
const letter = str[j]!
|
|
232
|
+
const isMatchingReference =
|
|
233
|
+
referenceSeq && name !== relativeTo && letter === referenceSeq[j]
|
|
234
|
+
ctx.fillText(
|
|
235
|
+
isMatchingReference ? '.' : letter,
|
|
236
|
+
j * colWidth + offsetXAligned + halfColWidth,
|
|
237
|
+
y,
|
|
238
|
+
)
|
|
238
239
|
}
|
|
239
240
|
}
|
|
240
241
|
}
|
|
@@ -247,7 +248,7 @@ function drawInsertionIndicators({
|
|
|
247
248
|
xEnd,
|
|
248
249
|
}: {
|
|
249
250
|
model: MsaViewModel
|
|
250
|
-
ctx:
|
|
251
|
+
ctx: RenderCtx
|
|
251
252
|
visibleLeaves: HierarchyNode<NodeWithIdsAndLength>[]
|
|
252
253
|
xStart: number
|
|
253
254
|
xEnd: number
|
|
@@ -273,7 +274,7 @@ function drawZigZag({
|
|
|
273
274
|
offset,
|
|
274
275
|
}: {
|
|
275
276
|
model: MsaViewModel
|
|
276
|
-
ctx:
|
|
277
|
+
ctx: RenderCtx
|
|
277
278
|
visibleLeaves: HierarchyNode<NodeWithIdsAndLength>[]
|
|
278
279
|
xStart: number
|
|
279
280
|
xEnd: number
|
|
@@ -25,7 +25,6 @@ export function renderMouseover({
|
|
|
25
25
|
mouseClickRow,
|
|
26
26
|
mouseClickCol,
|
|
27
27
|
relativeTo,
|
|
28
|
-
rowNamesSet,
|
|
29
28
|
hoveredTreeNode,
|
|
30
29
|
highlightedColumns,
|
|
31
30
|
} = model
|
|
@@ -35,7 +34,7 @@ export function renderMouseover({
|
|
|
35
34
|
|
|
36
35
|
// Highlight reference row (relativeTo) persistently
|
|
37
36
|
if (relativeTo) {
|
|
38
|
-
const referenceRowIndex = rowNamesSet.get(relativeTo)
|
|
37
|
+
const referenceRowIndex = model.rowNamesSet.get(relativeTo)
|
|
39
38
|
if (referenceRowIndex !== undefined) {
|
|
40
39
|
ctx.fillStyle = referenceColor
|
|
41
40
|
ctx.fillRect(0, referenceRowIndex * rowHeight + scrollY, width, rowHeight)
|
|
@@ -46,7 +45,7 @@ export function renderMouseover({
|
|
|
46
45
|
if (hoveredTreeNode) {
|
|
47
46
|
ctx.fillStyle = multiRowHoverColor
|
|
48
47
|
for (const descendantName of hoveredTreeNode.descendantNames) {
|
|
49
|
-
const rowIndex = rowNamesSet.get(descendantName)
|
|
48
|
+
const rowIndex = model.rowNamesSet.get(descendantName)
|
|
50
49
|
if (rowIndex !== undefined) {
|
|
51
50
|
ctx.fillRect(0, rowIndex * rowHeight + scrollY, width, rowHeight)
|
|
52
51
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// The half-open range of alignment columns [xStart, xEnd) intersecting a block
|
|
2
|
+
// of width blockWidth at horizontal offset offsetX. X-axis counterpart to
|
|
3
|
+
// getVisibleLeaves.
|
|
4
|
+
export function visibleColRange({
|
|
5
|
+
offsetX,
|
|
6
|
+
blockWidth,
|
|
7
|
+
colWidth,
|
|
8
|
+
}: {
|
|
9
|
+
offsetX: number
|
|
10
|
+
blockWidth: number
|
|
11
|
+
colWidth: number
|
|
12
|
+
}) {
|
|
13
|
+
return {
|
|
14
|
+
xStart: Math.max(0, Math.floor(offsetX / colWidth)),
|
|
15
|
+
xEnd: Math.max(0, Math.ceil((offsetX + blockWidth) / colWidth)),
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The subset of the canvas 2D API that the block renderers use. Both a real
|
|
3
|
+
* `CanvasRenderingContext2D` and the `@jbrowse/svgcanvas` `Context` (used for
|
|
4
|
+
* SVG export) satisfy this, so renderers can be typed against it without an
|
|
5
|
+
* `as any` cast at the SVG-export boundary.
|
|
6
|
+
*/
|
|
7
|
+
export type RenderCtx = Pick<
|
|
8
|
+
CanvasRenderingContext2D,
|
|
9
|
+
| 'arc'
|
|
10
|
+
| 'beginPath'
|
|
11
|
+
| 'clearRect'
|
|
12
|
+
| 'fill'
|
|
13
|
+
| 'fillRect'
|
|
14
|
+
| 'fillStyle'
|
|
15
|
+
| 'fillText'
|
|
16
|
+
| 'font'
|
|
17
|
+
| 'lineTo'
|
|
18
|
+
| 'lineWidth'
|
|
19
|
+
| 'measureText'
|
|
20
|
+
| 'moveTo'
|
|
21
|
+
| 'resetTransform'
|
|
22
|
+
| 'scale'
|
|
23
|
+
| 'setLineDash'
|
|
24
|
+
| 'stroke'
|
|
25
|
+
| 'strokeRect'
|
|
26
|
+
| 'strokeStyle'
|
|
27
|
+
| 'textAlign'
|
|
28
|
+
| 'translate'
|
|
29
|
+
>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { visibleColRange } from '../msa/visibleColRange.ts'
|
|
2
|
+
|
|
1
3
|
import type { MsaViewModel } from '../../model.ts'
|
|
2
4
|
import type { BasicTrack } from '../../types.ts'
|
|
5
|
+
import type { RenderCtx } from '../renderCtx.ts'
|
|
3
6
|
|
|
4
7
|
export function drawConservationBars({
|
|
5
8
|
ctx,
|
|
@@ -9,15 +12,18 @@ export function drawConservationBars({
|
|
|
9
12
|
offsetX,
|
|
10
13
|
blockSize,
|
|
11
14
|
}: {
|
|
12
|
-
ctx:
|
|
15
|
+
ctx: RenderCtx
|
|
13
16
|
conservation: number[]
|
|
14
17
|
colWidth: number
|
|
15
18
|
trackHeight: number
|
|
16
19
|
offsetX: number
|
|
17
20
|
blockSize: number
|
|
18
21
|
}) {
|
|
19
|
-
const xStart =
|
|
20
|
-
|
|
22
|
+
const { xStart, xEnd } = visibleColRange({
|
|
23
|
+
offsetX,
|
|
24
|
+
blockWidth: blockSize,
|
|
25
|
+
colWidth,
|
|
26
|
+
})
|
|
21
27
|
|
|
22
28
|
ctx.fillStyle = 'gray'
|
|
23
29
|
for (let i = xStart; i < xEnd && i < conservation.length; i++) {
|
|
@@ -39,7 +45,7 @@ export function drawTextTrackContent({
|
|
|
39
45
|
offsetX,
|
|
40
46
|
blockSize,
|
|
41
47
|
}: {
|
|
42
|
-
ctx:
|
|
48
|
+
ctx: RenderCtx
|
|
43
49
|
data: string | undefined
|
|
44
50
|
colorScheme: Record<string, string>
|
|
45
51
|
contrastScheme: Record<string, string>
|
|
@@ -49,8 +55,11 @@ export function drawTextTrackContent({
|
|
|
49
55
|
offsetX: number
|
|
50
56
|
blockSize: number
|
|
51
57
|
}) {
|
|
52
|
-
const xStart =
|
|
53
|
-
|
|
58
|
+
const { xStart, xEnd } = visibleColRange({
|
|
59
|
+
offsetX,
|
|
60
|
+
blockWidth: blockSize,
|
|
61
|
+
colWidth,
|
|
62
|
+
})
|
|
54
63
|
const str = data?.slice(xStart, xEnd)
|
|
55
64
|
|
|
56
65
|
for (let i = 0; str && i < str.length; i++) {
|
|
@@ -78,7 +87,7 @@ export function renderConservationTrack({
|
|
|
78
87
|
highResScaleFactorOverride,
|
|
79
88
|
}: {
|
|
80
89
|
model: MsaViewModel
|
|
81
|
-
ctx:
|
|
90
|
+
ctx: RenderCtx
|
|
82
91
|
offsetX: number
|
|
83
92
|
offsetY: number
|
|
84
93
|
trackHeight: number
|
|
@@ -116,7 +125,7 @@ export function renderTextTrack({
|
|
|
116
125
|
highResScaleFactorOverride,
|
|
117
126
|
}: {
|
|
118
127
|
model: MsaViewModel
|
|
119
|
-
ctx:
|
|
128
|
+
ctx: RenderCtx
|
|
120
129
|
track: BasicTrack
|
|
121
130
|
offsetX: number
|
|
122
131
|
offsetY: number
|
|
@@ -169,7 +178,7 @@ export function renderAllTracks({
|
|
|
169
178
|
highResScaleFactorOverride,
|
|
170
179
|
}: {
|
|
171
180
|
model: MsaViewModel
|
|
172
|
-
ctx:
|
|
181
|
+
ctx: RenderCtx
|
|
173
182
|
offsetX: number
|
|
174
183
|
contrastScheme: Record<string, string>
|
|
175
184
|
blockSizeXOverride?: number
|
|
@@ -43,7 +43,9 @@ const TreeCanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
43
43
|
ctx.resetTransform()
|
|
44
44
|
ctx.clearRect(0, 0, w, h)
|
|
45
45
|
|
|
46
|
-
const leafByName = new Map(
|
|
46
|
+
const leafByName = new Map(
|
|
47
|
+
leaves.map(leaf => [leaf.data.name, leaf]),
|
|
48
|
+
)
|
|
47
49
|
const fillRow = (name: string) => {
|
|
48
50
|
const leaf = leafByName.get(name)
|
|
49
51
|
if (leaf) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react'
|
|
2
2
|
|
|
3
3
|
import { useTheme } from '@mui/material'
|
|
4
4
|
import Flatbush from 'flatbush'
|
|
@@ -62,7 +62,6 @@ class ClickMapIndex {
|
|
|
62
62
|
finish() {
|
|
63
63
|
if (this.entries.length === 0) {
|
|
64
64
|
this.flatbush = null
|
|
65
|
-
return
|
|
66
65
|
} else {
|
|
67
66
|
this.flatbush = new Flatbush(this.entries.length)
|
|
68
67
|
for (const entry of this.entries) {
|
|
@@ -114,15 +113,6 @@ const TreeCanvasBlock = observer(function ({
|
|
|
114
113
|
const w2 = width * highResScaleFactor
|
|
115
114
|
const h2 = height * highResScaleFactor
|
|
116
115
|
|
|
117
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies:
|
|
118
|
-
const vref = useCallback(
|
|
119
|
-
(arg: HTMLCanvasElement) => {
|
|
120
|
-
model.incrementRef()
|
|
121
|
-
ref.current = arg
|
|
122
|
-
},
|
|
123
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
124
|
-
[model, height, width],
|
|
125
|
-
)
|
|
126
116
|
useEffect(() => {
|
|
127
117
|
const ctx = ref.current?.getContext('2d')
|
|
128
118
|
if (!ctx) {
|
|
@@ -289,7 +279,7 @@ const TreeCanvasBlock = observer(function ({
|
|
|
289
279
|
model.setHoveredTreeNode(undefined)
|
|
290
280
|
model.setMousePos(undefined, undefined)
|
|
291
281
|
}}
|
|
292
|
-
ref={
|
|
282
|
+
ref={ref}
|
|
293
283
|
/>
|
|
294
284
|
<canvas
|
|
295
285
|
style={{
|