jbrowse-plugin-mafviewer 1.4.3 → 1.4.6
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/README.md +1 -1
- package/dist/BigMafAdapter/BigMafAdapter.js +4 -5
- package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
- package/dist/BigMafAdapter/configSchema.d.ts +2 -2
- package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js +39 -109
- package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js.map +1 -1
- package/dist/LinearMafDisplay/components/MAFTooltip.d.ts +0 -3
- package/dist/LinearMafDisplay/components/MAFTooltip.js.map +1 -1
- package/dist/LinearMafDisplay/components/MsaHighlightOverlay.d.ts +9 -0
- package/dist/LinearMafDisplay/components/MsaHighlightOverlay.js +34 -0
- package/dist/LinearMafDisplay/components/MsaHighlightOverlay.js.map +1 -0
- package/dist/LinearMafDisplay/components/Sidebar/ColorLegend.js +2 -2
- package/dist/LinearMafDisplay/components/Sidebar/ColorLegend.js.map +1 -1
- package/dist/LinearMafDisplay/components/Sidebar/RectBg.d.ts +1 -1
- package/dist/LinearMafDisplay/components/Sidebar/RectBg.js +2 -3
- package/dist/LinearMafDisplay/components/Sidebar/RectBg.js.map +1 -1
- package/dist/LinearMafDisplay/components/Sidebar/SvgWrapper.js +81 -11
- package/dist/LinearMafDisplay/components/Sidebar/SvgWrapper.js.map +1 -1
- package/dist/LinearMafDisplay/components/Sidebar/Tree.js +30 -9
- package/dist/LinearMafDisplay/components/Sidebar/Tree.js.map +1 -1
- package/dist/LinearMafDisplay/components/Sidebar/YScaleBars.d.ts +0 -1
- package/dist/LinearMafDisplay/components/Sidebar/YScaleBars.js.map +1 -1
- package/dist/LinearMafDisplay/components/useDragSelection.d.ts +25 -0
- package/dist/LinearMafDisplay/components/useDragSelection.js +103 -0
- package/dist/LinearMafDisplay/components/useDragSelection.js.map +1 -0
- package/dist/LinearMafDisplay/configSchema.d.ts +3 -30
- package/dist/LinearMafDisplay/renderSvg.js +1 -1
- package/dist/LinearMafDisplay/renderSvg.js.map +1 -1
- package/dist/LinearMafDisplay/stateModel.d.ts +1090 -102
- package/dist/LinearMafDisplay/stateModel.js +156 -17
- package/dist/LinearMafDisplay/stateModel.js.map +1 -1
- package/dist/LinearMafDisplay/types.d.ts +2 -2
- package/dist/LinearMafDisplay/util.d.ts +6 -0
- package/dist/LinearMafDisplay/util.js +28 -6
- package/dist/LinearMafDisplay/util.js.map +1 -1
- package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +43 -10
- package/dist/LinearMafRenderer/LinearMafRenderer.js +1 -1
- package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
- package/dist/LinearMafRenderer/components/LinearMafRendering.d.ts +14 -5
- package/dist/LinearMafRenderer/components/LinearMafRendering.js +40 -20
- package/dist/LinearMafRenderer/components/LinearMafRendering.js.map +1 -1
- package/dist/LinearMafRenderer/configSchema.d.ts +1 -6
- package/dist/LinearMafRenderer/configSchema.js +1 -6
- package/dist/LinearMafRenderer/configSchema.js.map +1 -1
- package/dist/LinearMafRenderer/makeImageData.js +6 -7
- package/dist/LinearMafRenderer/makeImageData.js.map +1 -1
- package/dist/LinearMafRenderer/rendering/features.d.ts +0 -1
- package/dist/LinearMafRenderer/rendering/features.js +1 -14
- package/dist/LinearMafRenderer/rendering/features.js.map +1 -1
- package/dist/LinearMafRenderer/rendering/insertions.d.ts +1 -1
- package/dist/LinearMafRenderer/rendering/insertions.js +10 -8
- package/dist/LinearMafRenderer/rendering/insertions.js.map +1 -1
- package/dist/LinearMafRenderer/rendering/matches.d.ts +1 -1
- package/dist/LinearMafRenderer/rendering/matches.js +3 -15
- package/dist/LinearMafRenderer/rendering/matches.js.map +1 -1
- package/dist/LinearMafRenderer/rendering/mismatches.d.ts +1 -1
- package/dist/LinearMafRenderer/rendering/mismatches.js +3 -3
- package/dist/LinearMafRenderer/rendering/spatialIndex.js +8 -2
- package/dist/LinearMafRenderer/rendering/spatialIndex.js.map +1 -1
- package/dist/LinearMafRenderer/rendering/text.js +1 -3
- package/dist/LinearMafRenderer/rendering/text.js.map +1 -1
- package/dist/LinearMafRenderer/rendering/types.d.ts +6 -5
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
- package/dist/MafAddTrackWorkflow/index.js +1 -1
- package/dist/MafAddTrackWorkflow/index.js.map +1 -1
- package/dist/MafGetSequences/MafGetSequences.d.ts +1 -0
- package/dist/MafGetSequences/MafGetSequences.js +2 -1
- package/dist/MafGetSequences/MafGetSequences.js.map +1 -1
- package/dist/MafSequenceWidget/LabelsCanvas.d.ts +8 -0
- package/dist/MafSequenceWidget/LabelsCanvas.js +37 -0
- package/dist/MafSequenceWidget/LabelsCanvas.js.map +1 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlight.d.ts +6 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlight.js +52 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlight.js.map +1 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.d.ts +2 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.js +12 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.js.map +1 -0
- package/dist/MafSequenceWidget/MafSequenceWidget.d.ts +6 -0
- package/dist/MafSequenceWidget/MafSequenceWidget.js +189 -0
- package/dist/MafSequenceWidget/MafSequenceWidget.js.map +1 -0
- package/dist/MafSequenceWidget/SequenceCanvas.d.ts +12 -0
- package/dist/MafSequenceWidget/SequenceCanvas.js +86 -0
- package/dist/MafSequenceWidget/SequenceCanvas.js.map +1 -0
- package/dist/MafSequenceWidget/SequenceDisplay.d.ts +12 -0
- package/dist/MafSequenceWidget/SequenceDisplay.js +117 -0
- package/dist/MafSequenceWidget/SequenceDisplay.js.map +1 -0
- package/dist/MafSequenceWidget/SequenceTooltip.d.ts +11 -0
- package/dist/MafSequenceWidget/SequenceTooltip.js +39 -0
- package/dist/MafSequenceWidget/SequenceTooltip.js.map +1 -0
- package/dist/MafSequenceWidget/baseColors.d.ts +3 -0
- package/dist/MafSequenceWidget/baseColors.js +64 -0
- package/dist/MafSequenceWidget/baseColors.js.map +1 -0
- package/dist/MafSequenceWidget/colToGenomePos.d.ts +13 -0
- package/dist/MafSequenceWidget/colToGenomePos.js +32 -0
- package/dist/MafSequenceWidget/colToGenomePos.js.map +1 -0
- package/dist/MafSequenceWidget/colToGenomePos.test.d.ts +1 -0
- package/dist/MafSequenceWidget/colToGenomePos.test.js +136 -0
- package/dist/MafSequenceWidget/colToGenomePos.test.js.map +1 -0
- package/dist/MafSequenceWidget/configSchema.d.ts +1 -0
- package/dist/MafSequenceWidget/configSchema.js +3 -0
- package/dist/MafSequenceWidget/configSchema.js.map +1 -0
- package/dist/MafSequenceWidget/constants.d.ts +4 -0
- package/dist/MafSequenceWidget/constants.js +5 -0
- package/dist/MafSequenceWidget/constants.js.map +1 -0
- package/dist/MafSequenceWidget/index.d.ts +2 -0
- package/dist/MafSequenceWidget/index.js +16 -0
- package/dist/MafSequenceWidget/index.js.map +1 -0
- package/dist/MafSequenceWidget/stateModelFactory.d.ts +67 -0
- package/dist/MafSequenceWidget/stateModelFactory.js +21 -0
- package/dist/MafSequenceWidget/stateModelFactory.js.map +1 -0
- package/dist/MafTabixAdapter/MafTabixAdapter.js +4 -35
- package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
- package/dist/MafTabixAdapter/configSchema.d.ts +4 -4
- package/dist/MafTrack/configSchema.d.ts +16 -11
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +12 -24
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
- package/dist/util/clipboard.d.ts +2 -0
- package/dist/util/clipboard.js +28 -0
- package/dist/util/clipboard.js.map +1 -0
- package/dist/util/fastaUtils.d.ts +2 -1
- package/dist/util/fastaUtils.js +93 -50
- package/dist/util/fastaUtils.js.map +1 -1
- package/dist/util/fastaUtils.test.js +190 -0
- package/dist/util/fastaUtils.test.js.map +1 -1
- package/dist/util/parseAssemblyName.d.ts +32 -0
- package/dist/util/parseAssemblyName.js +87 -0
- package/dist/util/parseAssemblyName.js.map +1 -0
- package/dist/util/parseAssemblyName.test.d.ts +1 -0
- package/dist/util/parseAssemblyName.test.js +269 -0
- package/dist/util/parseAssemblyName.test.js.map +1 -0
- package/package.json +12 -12
- package/src/BigMafAdapter/BigMafAdapter.ts +5 -5
- package/src/LinearMafDisplay/components/LinearMafDisplayComponent.tsx +63 -145
- package/src/LinearMafDisplay/components/MAFTooltip.tsx +0 -3
- package/src/LinearMafDisplay/components/MsaHighlightOverlay.tsx +62 -0
- package/src/LinearMafDisplay/components/Sidebar/ColorLegend.tsx +2 -6
- package/src/LinearMafDisplay/components/Sidebar/RectBg.tsx +8 -3
- package/src/LinearMafDisplay/components/Sidebar/SvgWrapper.tsx +117 -15
- package/src/LinearMafDisplay/components/Sidebar/Tree.tsx +53 -8
- package/src/LinearMafDisplay/components/Sidebar/YScaleBars.tsx +0 -1
- package/src/LinearMafDisplay/components/useDragSelection.ts +159 -0
- package/src/LinearMafDisplay/renderSvg.tsx +1 -1
- package/src/LinearMafDisplay/stateModel.ts +215 -20
- package/src/LinearMafDisplay/types.ts +2 -2
- package/src/LinearMafDisplay/util.ts +35 -7
- package/src/LinearMafRenderer/LinearMafRenderer.ts +3 -5
- package/src/LinearMafRenderer/components/LinearMafRendering.tsx +67 -33
- package/src/LinearMafRenderer/configSchema.ts +1 -6
- package/src/LinearMafRenderer/makeImageData.ts +5 -14
- package/src/LinearMafRenderer/rendering/features.ts +2 -36
- package/src/LinearMafRenderer/rendering/insertions.ts +13 -8
- package/src/LinearMafRenderer/rendering/matches.ts +2 -27
- package/src/LinearMafRenderer/rendering/mismatches.ts +3 -3
- package/src/LinearMafRenderer/rendering/spatialIndex.ts +9 -2
- package/src/LinearMafRenderer/rendering/text.ts +1 -2
- package/src/LinearMafRenderer/rendering/types.ts +8 -5
- package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +1 -1
- package/src/MafAddTrackWorkflow/index.ts +1 -1
- package/src/MafGetSequences/MafGetSequences.ts +10 -2
- package/src/MafSequenceWidget/LabelsCanvas.tsx +58 -0
- package/src/MafSequenceWidget/MafSequenceHoverHighlight.tsx +83 -0
- package/src/MafSequenceWidget/MafSequenceHoverHighlightExtension.tsx +24 -0
- package/src/MafSequenceWidget/MafSequenceWidget.tsx +294 -0
- package/src/MafSequenceWidget/SequenceCanvas.tsx +136 -0
- package/src/MafSequenceWidget/SequenceDisplay.tsx +188 -0
- package/src/MafSequenceWidget/SequenceTooltip.tsx +70 -0
- package/src/MafSequenceWidget/baseColors.ts +76 -0
- package/src/MafSequenceWidget/colToGenomePos.test.ts +166 -0
- package/src/MafSequenceWidget/colToGenomePos.ts +40 -0
- package/src/MafSequenceWidget/configSchema.ts +3 -0
- package/src/MafSequenceWidget/constants.ts +4 -0
- package/src/MafSequenceWidget/index.ts +24 -0
- package/src/MafSequenceWidget/stateModelFactory.ts +43 -0
- package/src/MafTabixAdapter/MafTabixAdapter.ts +12 -51
- package/src/index.ts +2 -0
- package/src/util/__snapshots__/fastaUtils.test.ts.snap +35 -0
- package/src/util/clipboard.ts +35 -0
- package/src/util/fastaUtils.test.ts +199 -0
- package/src/util/fastaUtils.ts +118 -51
- package/src/util/parseAssemblyName.test.ts +350 -0
- package/src/util/parseAssemblyName.ts +106 -0
- package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.d.ts +0 -11
- package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.js +0 -97
- package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.js.map +0 -1
- package/dist/LinearMafDisplay/components/util.d.ts +0 -1
- package/dist/LinearMafDisplay/components/util.js +0 -8
- package/dist/LinearMafDisplay/components/util.js.map +0 -1
- package/dist/LinearMafRenderer/components/util.d.ts +0 -1
- package/dist/LinearMafRenderer/components/util.js +0 -13
- package/dist/LinearMafRenderer/components/util.js.map +0 -1
- package/dist/util/fetchSequences.d.ts +0 -18
- package/dist/util/fetchSequences.js +0 -39
- package/dist/util/fetchSequences.js.map +0 -1
- package/dist/util/useSequences.d.ts +0 -21
- package/dist/util/useSequences.js +0 -64
- package/dist/util/useSequences.js.map +0 -1
- package/src/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.tsx +0 -175
- package/src/LinearMafDisplay/components/util.ts +0 -7
- package/src/LinearMafRenderer/components/util.ts +0 -13
- package/src/util/fetchSequences.ts +0 -57
- package/src/util/useSequences.ts +0 -90
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import { lazy } from 'react'
|
|
2
2
|
|
|
3
3
|
import { ConfigurationReference, getConf } from '@jbrowse/core/configuration'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
SessionWithWidgets,
|
|
6
|
+
getContainingTrack,
|
|
7
|
+
getContainingView,
|
|
8
|
+
getEnv,
|
|
9
|
+
getSession,
|
|
10
|
+
max,
|
|
11
|
+
measureText,
|
|
12
|
+
} from '@jbrowse/core/util'
|
|
5
13
|
import { getRpcSessionId } from '@jbrowse/core/util/tracks'
|
|
14
|
+
import { addDisposer, isAlive, types } from '@jbrowse/mobx-state-tree'
|
|
6
15
|
import { ascending } from 'd3-array'
|
|
7
16
|
import { cluster, hierarchy } from 'd3-hierarchy'
|
|
8
17
|
import deepEqual from 'fast-deep-equal'
|
|
9
18
|
import { autorun } from 'mobx'
|
|
10
|
-
import { addDisposer, isAlive, types } from 'mobx-state-tree'
|
|
11
19
|
|
|
12
|
-
import { maxLength, setBrLength } from './util'
|
|
20
|
+
import { computeNodeDescendantNames, maxLength, setBrLength } from './util'
|
|
13
21
|
import { normalize } from '../util'
|
|
14
22
|
|
|
15
23
|
import type { NodeWithIds, NodeWithIdsAndLength, Sample } from './types'
|
|
@@ -18,9 +26,18 @@ import type {
|
|
|
18
26
|
AnyConfigurationModel,
|
|
19
27
|
AnyConfigurationSchemaType,
|
|
20
28
|
} from '@jbrowse/core/configuration'
|
|
29
|
+
import type { Instance } from '@jbrowse/mobx-state-tree'
|
|
21
30
|
import type { ExportSvgDisplayOptions } from '@jbrowse/plugin-linear-genome-view'
|
|
22
31
|
import type { HierarchyNode } from 'd3-hierarchy'
|
|
23
|
-
|
|
32
|
+
|
|
33
|
+
const defaultRowHeight = 15
|
|
34
|
+
const defaultRowProportion = 0.8
|
|
35
|
+
const defaultShowAllLetters = false
|
|
36
|
+
const defaultMismatchRendering = true
|
|
37
|
+
const defaultShowBranchLen = false
|
|
38
|
+
const defaultTreeAreaWidth = 80
|
|
39
|
+
const defaultShowAsUpperCase = true
|
|
40
|
+
const defaultShowSidebar = true
|
|
24
41
|
|
|
25
42
|
const SetRowHeightDialog = lazy(
|
|
26
43
|
() => import('./components/SetRowHeightDialog/SetRowHeightDialog'),
|
|
@@ -55,33 +72,37 @@ export default function stateModelFactory(
|
|
|
55
72
|
/**
|
|
56
73
|
* #property
|
|
57
74
|
*/
|
|
58
|
-
rowHeight:
|
|
75
|
+
rowHeight: defaultRowHeight,
|
|
59
76
|
/**
|
|
60
77
|
* #property
|
|
61
78
|
*/
|
|
62
|
-
rowProportion:
|
|
79
|
+
rowProportion: defaultRowProportion,
|
|
63
80
|
/**
|
|
64
81
|
* #property
|
|
65
82
|
*/
|
|
66
|
-
showAllLetters:
|
|
83
|
+
showAllLetters: defaultShowAllLetters,
|
|
67
84
|
/**
|
|
68
85
|
* #property
|
|
69
86
|
*/
|
|
70
|
-
mismatchRendering:
|
|
87
|
+
mismatchRendering: defaultMismatchRendering,
|
|
71
88
|
|
|
72
89
|
/**
|
|
73
90
|
* #property
|
|
74
91
|
*/
|
|
75
|
-
showBranchLen:
|
|
92
|
+
showBranchLen: defaultShowBranchLen,
|
|
76
93
|
|
|
77
94
|
/**
|
|
78
95
|
* #property
|
|
79
96
|
*/
|
|
80
|
-
treeAreaWidth:
|
|
97
|
+
treeAreaWidth: defaultTreeAreaWidth,
|
|
98
|
+
/**
|
|
99
|
+
* #property
|
|
100
|
+
*/
|
|
101
|
+
showAsUpperCase: defaultShowAsUpperCase,
|
|
81
102
|
/**
|
|
82
103
|
* #property
|
|
83
104
|
*/
|
|
84
|
-
|
|
105
|
+
showSidebar: defaultShowSidebar,
|
|
85
106
|
}),
|
|
86
107
|
)
|
|
87
108
|
.volatile(() => ({
|
|
@@ -100,7 +121,15 @@ export default function stateModelFactory(
|
|
|
100
121
|
/**
|
|
101
122
|
* #volatile
|
|
102
123
|
*/
|
|
103
|
-
volatileTree: undefined as
|
|
124
|
+
volatileTree: undefined as NodeWithIds | undefined,
|
|
125
|
+
/**
|
|
126
|
+
* #volatile
|
|
127
|
+
*/
|
|
128
|
+
highlightedRowNames: undefined as string[] | undefined,
|
|
129
|
+
/**
|
|
130
|
+
* #volatile
|
|
131
|
+
*/
|
|
132
|
+
hoveredTreeNode: undefined as { x: number; y: number } | undefined,
|
|
104
133
|
}))
|
|
105
134
|
.actions(self => ({
|
|
106
135
|
/**
|
|
@@ -136,7 +165,13 @@ export default function stateModelFactory(
|
|
|
136
165
|
/**
|
|
137
166
|
* #action
|
|
138
167
|
*/
|
|
139
|
-
setSamples({
|
|
168
|
+
setSamples({
|
|
169
|
+
samples,
|
|
170
|
+
tree,
|
|
171
|
+
}: {
|
|
172
|
+
samples: Sample[]
|
|
173
|
+
tree: NodeWithIds | undefined
|
|
174
|
+
}) {
|
|
140
175
|
if (!deepEqual(samples, self.volatileSamples)) {
|
|
141
176
|
self.volatileSamples = samples
|
|
142
177
|
}
|
|
@@ -150,6 +185,62 @@ export default function stateModelFactory(
|
|
|
150
185
|
setShowAsUpperCase(arg: boolean) {
|
|
151
186
|
self.showAsUpperCase = arg
|
|
152
187
|
},
|
|
188
|
+
/**
|
|
189
|
+
* #action
|
|
190
|
+
*/
|
|
191
|
+
setTreeAreaWidth(width: number) {
|
|
192
|
+
self.treeAreaWidth = width
|
|
193
|
+
},
|
|
194
|
+
/**
|
|
195
|
+
* #action
|
|
196
|
+
*/
|
|
197
|
+
setShowSidebar(arg: boolean) {
|
|
198
|
+
self.showSidebar = arg
|
|
199
|
+
},
|
|
200
|
+
/**
|
|
201
|
+
* #action
|
|
202
|
+
*/
|
|
203
|
+
setHighlightedRowNames(
|
|
204
|
+
names?: string[],
|
|
205
|
+
nodePosition?: { x: number; y: number },
|
|
206
|
+
) {
|
|
207
|
+
self.highlightedRowNames = names
|
|
208
|
+
self.hoveredTreeNode = nodePosition
|
|
209
|
+
},
|
|
210
|
+
/**
|
|
211
|
+
* #action
|
|
212
|
+
*/
|
|
213
|
+
showInsertionSequenceDialog(insertionData: {
|
|
214
|
+
sequence: string
|
|
215
|
+
sampleLabel: string
|
|
216
|
+
chr: string
|
|
217
|
+
pos: number
|
|
218
|
+
}) {
|
|
219
|
+
const { sequence, sampleLabel, chr, pos } = insertionData
|
|
220
|
+
const session = getSession(self) as SessionWithWidgets
|
|
221
|
+
const featureWidget = session.addWidget(
|
|
222
|
+
'BaseFeatureWidget',
|
|
223
|
+
'baseFeature',
|
|
224
|
+
{
|
|
225
|
+
featureData: {
|
|
226
|
+
uniqueId: `insertion-${chr}-${pos}-${sampleLabel}`,
|
|
227
|
+
type: 'insertion',
|
|
228
|
+
refName: chr,
|
|
229
|
+
start: pos,
|
|
230
|
+
end: pos + 1,
|
|
231
|
+
sample: sampleLabel,
|
|
232
|
+
insertionLength: sequence.length,
|
|
233
|
+
sequence: self.showAsUpperCase
|
|
234
|
+
? sequence.toUpperCase()
|
|
235
|
+
: sequence.toLowerCase(),
|
|
236
|
+
},
|
|
237
|
+
view: getContainingView(self),
|
|
238
|
+
track: getContainingTrack(self),
|
|
239
|
+
},
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
session.showWidget(featureWidget)
|
|
243
|
+
},
|
|
153
244
|
}))
|
|
154
245
|
.views(self => ({
|
|
155
246
|
/**
|
|
@@ -183,8 +274,7 @@ export default function stateModelFactory(
|
|
|
183
274
|
get root() {
|
|
184
275
|
return self.volatileTree
|
|
185
276
|
? hierarchy(self.volatileTree, d => d.children)
|
|
186
|
-
|
|
187
|
-
.sum(d => (d.children ? 0 : 1))
|
|
277
|
+
.sum(d => (d.children?.length ? 0 : 1))
|
|
188
278
|
.sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
|
|
189
279
|
: undefined
|
|
190
280
|
},
|
|
@@ -198,11 +288,18 @@ export default function stateModelFactory(
|
|
|
198
288
|
const r = self.root
|
|
199
289
|
if (r) {
|
|
200
290
|
const width = self.treeAreaWidth
|
|
291
|
+
// Use totalHeight - rowHeight so leaves are centered in rows
|
|
292
|
+
// (first leaf at rowHeight/2, last at totalHeight - rowHeight/2)
|
|
201
293
|
const clust = cluster<NodeWithIds>()
|
|
202
|
-
.size([this.totalHeight, width])
|
|
294
|
+
.size([this.totalHeight - self.rowHeight, width])
|
|
203
295
|
.separation(() => 1)
|
|
204
296
|
clust(r)
|
|
205
|
-
|
|
297
|
+
// Offset all nodes by rowHeight/2 to center in rows
|
|
298
|
+
for (const node of r.descendants()) {
|
|
299
|
+
node.x = node.x! + self.rowHeight / 2
|
|
300
|
+
}
|
|
301
|
+
r.data.length = 0
|
|
302
|
+
setBrLength(r, 0, width / maxLength(r))
|
|
206
303
|
return r as HierarchyNode<NodeWithIdsAndLength>
|
|
207
304
|
} else {
|
|
208
305
|
return undefined
|
|
@@ -238,6 +335,22 @@ export default function stateModelFactory(
|
|
|
238
335
|
get leaves() {
|
|
239
336
|
return self.root?.leaves()
|
|
240
337
|
},
|
|
338
|
+
/**
|
|
339
|
+
* #getter
|
|
340
|
+
*/
|
|
341
|
+
get leafMap() {
|
|
342
|
+
return new Map(this.leaves?.map(leaf => [leaf.data.name, leaf]))
|
|
343
|
+
},
|
|
344
|
+
/**
|
|
345
|
+
* #getter
|
|
346
|
+
* Precomputed map from hierarchy node to its descendant leaf names
|
|
347
|
+
*/
|
|
348
|
+
get nodeDescendantNames() {
|
|
349
|
+
if (this.hierarchy) {
|
|
350
|
+
return computeNodeDescendantNames(this.hierarchy)
|
|
351
|
+
}
|
|
352
|
+
return new Map<HierarchyNode<NodeWithIdsAndLength>, string[]>()
|
|
353
|
+
},
|
|
241
354
|
/**
|
|
242
355
|
* #getter
|
|
243
356
|
*/
|
|
@@ -276,7 +389,7 @@ export default function stateModelFactory(
|
|
|
276
389
|
return {
|
|
277
390
|
...s,
|
|
278
391
|
notReady:
|
|
279
|
-
(!self.volatileSamples && !self.volatileTree) ||
|
|
392
|
+
(!self.volatileSamples && !self.volatileTree) || s.notReady,
|
|
280
393
|
config: rendererConfig,
|
|
281
394
|
samples,
|
|
282
395
|
rowHeight,
|
|
@@ -348,11 +461,50 @@ export default function stateModelFactory(
|
|
|
348
461
|
self.setMismatchRendering(!self.mismatchRendering)
|
|
349
462
|
},
|
|
350
463
|
},
|
|
464
|
+
{
|
|
465
|
+
label: 'Show sidebar',
|
|
466
|
+
type: 'checkbox',
|
|
467
|
+
checked: self.showSidebar,
|
|
468
|
+
onClick: () => {
|
|
469
|
+
self.setShowSidebar(!self.showSidebar)
|
|
470
|
+
},
|
|
471
|
+
},
|
|
351
472
|
]
|
|
352
473
|
},
|
|
353
474
|
}
|
|
354
475
|
})
|
|
355
476
|
.views(self => ({
|
|
477
|
+
/**
|
|
478
|
+
* #getter
|
|
479
|
+
* Get highlight regions from connected MSA views
|
|
480
|
+
*/
|
|
481
|
+
get msaHighlights() {
|
|
482
|
+
const session = getSession(self)
|
|
483
|
+
const view = getContainingView(self)
|
|
484
|
+
const highlights: { refName: string; start: number; end: number }[] = []
|
|
485
|
+
|
|
486
|
+
// Find MSA views that are connected to our parent view
|
|
487
|
+
for (const v of session.views) {
|
|
488
|
+
if (
|
|
489
|
+
(v as { type?: string }).type === 'MsaView' &&
|
|
490
|
+
(v as { connectedViewId?: string }).connectedViewId === view.id
|
|
491
|
+
) {
|
|
492
|
+
const msaView = v as {
|
|
493
|
+
connectedHighlights?: {
|
|
494
|
+
refName: string
|
|
495
|
+
start: number
|
|
496
|
+
end: number
|
|
497
|
+
}[]
|
|
498
|
+
}
|
|
499
|
+
if (msaView.connectedHighlights) {
|
|
500
|
+
for (const h of msaView.connectedHighlights) {
|
|
501
|
+
highlights.push(h)
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return highlights
|
|
507
|
+
},
|
|
356
508
|
/**
|
|
357
509
|
* #getter
|
|
358
510
|
*/
|
|
@@ -377,6 +529,12 @@ export default function stateModelFactory(
|
|
|
377
529
|
0,
|
|
378
530
|
)
|
|
379
531
|
},
|
|
532
|
+
/**
|
|
533
|
+
* #getter
|
|
534
|
+
*/
|
|
535
|
+
get sidebarWidth() {
|
|
536
|
+
return self.showSidebar ? this.labelWidth + 5 + self.treeWidth : 0
|
|
537
|
+
},
|
|
380
538
|
}))
|
|
381
539
|
.actions(self => ({
|
|
382
540
|
afterCreate() {
|
|
@@ -392,10 +550,10 @@ export default function stateModelFactory(
|
|
|
392
550
|
adapterConfig: self.adapterConfig,
|
|
393
551
|
statusCallback: (message: string) => {
|
|
394
552
|
if (isAlive(self)) {
|
|
395
|
-
self.
|
|
553
|
+
self.setStatusMessage(message)
|
|
396
554
|
}
|
|
397
555
|
},
|
|
398
|
-
})) as { samples: Sample[]; tree:
|
|
556
|
+
})) as { samples: Sample[]; tree: NodeWithIds | undefined },
|
|
399
557
|
)
|
|
400
558
|
} catch (e) {
|
|
401
559
|
console.error(e)
|
|
@@ -418,6 +576,43 @@ export default function stateModelFactory(
|
|
|
418
576
|
},
|
|
419
577
|
}
|
|
420
578
|
})
|
|
579
|
+
.postProcessSnapshot(snap => {
|
|
580
|
+
const {
|
|
581
|
+
rowHeight,
|
|
582
|
+
rowProportion,
|
|
583
|
+
showAllLetters,
|
|
584
|
+
mismatchRendering,
|
|
585
|
+
showBranchLen,
|
|
586
|
+
treeAreaWidth,
|
|
587
|
+
showAsUpperCase,
|
|
588
|
+
showSidebar,
|
|
589
|
+
...rest
|
|
590
|
+
} = snap as typeof snap & {
|
|
591
|
+
rowHeight?: number
|
|
592
|
+
rowProportion?: number
|
|
593
|
+
showAllLetters?: boolean
|
|
594
|
+
mismatchRendering?: boolean
|
|
595
|
+
showBranchLen?: boolean
|
|
596
|
+
treeAreaWidth?: number
|
|
597
|
+
showAsUpperCase?: boolean
|
|
598
|
+
showSidebar?: boolean
|
|
599
|
+
}
|
|
600
|
+
return {
|
|
601
|
+
...(rest as Omit<typeof rest, symbol>),
|
|
602
|
+
...(rowHeight !== defaultRowHeight ? { rowHeight } : {}),
|
|
603
|
+
...(rowProportion !== defaultRowProportion ? { rowProportion } : {}),
|
|
604
|
+
...(showAllLetters !== defaultShowAllLetters ? { showAllLetters } : {}),
|
|
605
|
+
...(mismatchRendering !== defaultMismatchRendering
|
|
606
|
+
? { mismatchRendering }
|
|
607
|
+
: {}),
|
|
608
|
+
...(showBranchLen !== defaultShowBranchLen ? { showBranchLen } : {}),
|
|
609
|
+
...(treeAreaWidth !== defaultTreeAreaWidth ? { treeAreaWidth } : {}),
|
|
610
|
+
...(showAsUpperCase !== defaultShowAsUpperCase
|
|
611
|
+
? { showAsUpperCase }
|
|
612
|
+
: {}),
|
|
613
|
+
...(showSidebar !== defaultShowSidebar ? { showSidebar } : {}),
|
|
614
|
+
}
|
|
615
|
+
})
|
|
421
616
|
}
|
|
422
617
|
|
|
423
618
|
export type LinearMafDisplayStateModel = ReturnType<typeof stateModelFactory>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export interface NodeWithIds {
|
|
2
2
|
id: string
|
|
3
3
|
name: string
|
|
4
|
-
children
|
|
4
|
+
children?: NodeWithIds[]
|
|
5
5
|
length?: number
|
|
6
6
|
noTree?: boolean
|
|
7
7
|
}
|
|
@@ -9,7 +9,7 @@ export interface NodeWithIds {
|
|
|
9
9
|
export interface NodeWithIdsAndLength {
|
|
10
10
|
id: string
|
|
11
11
|
name: string
|
|
12
|
-
children
|
|
12
|
+
children?: NodeWithIdsAndLength[]
|
|
13
13
|
noTree?: boolean
|
|
14
14
|
length: number
|
|
15
15
|
}
|
|
@@ -6,10 +6,13 @@ import type { HierarchyNode } from 'd3-hierarchy'
|
|
|
6
6
|
|
|
7
7
|
export interface HoveredInfo {
|
|
8
8
|
sampleId: string
|
|
9
|
+
sampleLabel: string
|
|
9
10
|
pos: number
|
|
10
11
|
base: string
|
|
11
12
|
chr: string
|
|
12
|
-
|
|
13
|
+
isInsertion?: boolean
|
|
14
|
+
isLargeInsertion?: boolean
|
|
15
|
+
[key: string]: unknown
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
export interface GenomicPosition {
|
|
@@ -44,15 +47,16 @@ export function generateTooltipContent(
|
|
|
44
47
|
contentLines.push(`Ref: ${p2.refName}:${toLocale(p2.coord)}`)
|
|
45
48
|
|
|
46
49
|
if (hoveredInfo) {
|
|
47
|
-
const { base,
|
|
50
|
+
const { base, sampleLabel, pos, chr, isInsertion } = hoveredInfo
|
|
48
51
|
const thresh = 20
|
|
49
52
|
const len = base.length
|
|
50
53
|
const lengthSuffix = len > 1 ? ` ${len}bp` : ''
|
|
51
54
|
const baseDisplay =
|
|
52
55
|
base.length > thresh ? base.slice(0, thresh) + '...' : base
|
|
56
|
+
const insertionLabel = isInsertion ? ' Insertion' : ''
|
|
53
57
|
|
|
54
58
|
contentLines.push(
|
|
55
|
-
`Alt ${
|
|
59
|
+
`Alt ${sampleLabel}: ${chr}:${pos.toLocaleString('en-US')} (${baseDisplay}${lengthSuffix}${insertionLabel})`,
|
|
56
60
|
)
|
|
57
61
|
}
|
|
58
62
|
}
|
|
@@ -73,12 +77,36 @@ export function setBrLength(
|
|
|
73
77
|
y0: number,
|
|
74
78
|
k: number,
|
|
75
79
|
) {
|
|
80
|
+
const newY0 = y0 + Math.max(d.data.length || 0, 0)
|
|
76
81
|
// @ts-expect-error
|
|
77
|
-
d.len =
|
|
82
|
+
d.len = newY0 * k
|
|
78
83
|
|
|
79
84
|
if (d.children) {
|
|
80
|
-
d.children
|
|
81
|
-
setBrLength(
|
|
82
|
-
}
|
|
85
|
+
for (const child of d.children) {
|
|
86
|
+
setBrLength(child, newY0, k)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function computeNodeDescendantNames<T extends { name: string }>(
|
|
92
|
+
root: HierarchyNode<T>,
|
|
93
|
+
): Map<HierarchyNode<T>, string[]> {
|
|
94
|
+
const map = new Map<HierarchyNode<T>, string[]>()
|
|
95
|
+
function visit(node: HierarchyNode<T>): string[] {
|
|
96
|
+
if (!node.children || node.children.length === 0) {
|
|
97
|
+
const names = [node.data.name]
|
|
98
|
+
map.set(node, names)
|
|
99
|
+
return names
|
|
100
|
+
}
|
|
101
|
+
const names: string[] = []
|
|
102
|
+
for (const child of node.children) {
|
|
103
|
+
for (const name of visit(child)) {
|
|
104
|
+
names.push(name)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
map.set(node, names)
|
|
108
|
+
return names
|
|
83
109
|
}
|
|
110
|
+
visit(root)
|
|
111
|
+
return map
|
|
84
112
|
}
|
|
@@ -8,10 +8,8 @@ import {
|
|
|
8
8
|
|
|
9
9
|
import { makeImageData } from './makeImageData'
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
color?: string
|
|
14
|
-
}
|
|
11
|
+
import type { Sample } from '../LinearMafDisplay/types'
|
|
12
|
+
|
|
15
13
|
interface RenderArgs extends RenderArgsDeserialized {
|
|
16
14
|
samples: Sample[]
|
|
17
15
|
rowHeight: number
|
|
@@ -28,7 +26,7 @@ export default class LinearMafRenderer extends FeatureRendererType {
|
|
|
28
26
|
const bpExpansion = 1
|
|
29
27
|
|
|
30
28
|
return {
|
|
31
|
-
// xref https://github.com/mobxjs/mobx-state-tree/issues/1524 for Omit
|
|
29
|
+
// xref https://github.com/mobxjs/@jbrowse/mobx-state-tree/issues/1524 for Omit
|
|
32
30
|
...(region as Omit<typeof region, symbol>),
|
|
33
31
|
start: Math.floor(Math.max(start - bpExpansion, 0)),
|
|
34
32
|
end: Math.ceil(end + bpExpansion),
|
|
@@ -1,66 +1,100 @@
|
|
|
1
|
-
import React, { useMemo, useRef } from 'react'
|
|
1
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
|
2
2
|
|
|
3
3
|
import { PrerenderedCanvas } from '@jbrowse/core/ui'
|
|
4
4
|
import Flatbush from 'flatbush'
|
|
5
5
|
import { observer } from 'mobx-react'
|
|
6
6
|
|
|
7
|
-
import { Sample } from '../../LinearMafDisplay/types'
|
|
8
|
-
import { RenderedBase } from '../rendering'
|
|
7
|
+
import type { Sample } from '../../LinearMafDisplay/types'
|
|
8
|
+
import type { RenderedBase } from '../rendering'
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface DisplayModel {
|
|
11
|
+
setHoveredInfo?: (info: Record<string, unknown> | undefined) => void
|
|
12
|
+
setHighlightedRowNames?: (names: string[] | undefined) => void
|
|
13
|
+
showInsertionSequenceDialog?: (data: {
|
|
14
|
+
sequence: string
|
|
15
|
+
sampleLabel: string
|
|
16
|
+
chr: string
|
|
17
|
+
pos: number
|
|
18
|
+
}) => void
|
|
19
|
+
}
|
|
11
20
|
|
|
12
21
|
const LinearMafRendering = observer(function (props: {
|
|
13
22
|
width: number
|
|
14
23
|
height: number
|
|
15
|
-
displayModel:
|
|
16
|
-
flatbush:
|
|
24
|
+
displayModel: DisplayModel
|
|
25
|
+
flatbush: ArrayBuffer
|
|
17
26
|
items: RenderedBase[]
|
|
18
27
|
samples: Sample[]
|
|
19
28
|
}) {
|
|
20
29
|
const { items, displayModel, height, samples, flatbush } = props
|
|
21
30
|
const ref = useRef<HTMLDivElement>(null)
|
|
22
|
-
const
|
|
31
|
+
const flatbush2 = useMemo(() => Flatbush.from(flatbush), [flatbush])
|
|
32
|
+
const [isOverInsertion, setIsOverInsertion] = useState(false)
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
const getFeatureUnderMouse = useCallback(
|
|
35
|
+
(eventClientX: number, eventClientY: number) => {
|
|
36
|
+
let offsetX = 0
|
|
37
|
+
let offsetY = 0
|
|
38
|
+
if (ref.current) {
|
|
39
|
+
const rect = ref.current.getBoundingClientRect()
|
|
40
|
+
offsetX = eventClientX - rect.left
|
|
41
|
+
offsetY = eventClientY - rect.top
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const hits = flatbush2.search(offsetX, offsetY, offsetX + 1, offsetY + 1)
|
|
45
|
+
if (hits.length === 0) {
|
|
46
|
+
return undefined
|
|
47
|
+
}
|
|
32
48
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
const insertionHit = hits.find(idx => items[idx]?.isInsertion)
|
|
50
|
+
const hitIdx = insertionHit ?? hits[0]
|
|
51
|
+
const item = hitIdx !== undefined ? items[hitIdx] : undefined
|
|
52
|
+
if (!item) {
|
|
53
|
+
return undefined
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const sample = samples[item.rowIndex]
|
|
38
57
|
return {
|
|
39
|
-
...
|
|
40
|
-
sampleId:
|
|
58
|
+
...item,
|
|
59
|
+
sampleId: sample?.id ?? 'unknown',
|
|
60
|
+
sampleLabel: sample?.label || sample?.id || 'unknown',
|
|
41
61
|
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
62
|
+
},
|
|
63
|
+
[flatbush2, items, samples],
|
|
64
|
+
)
|
|
65
|
+
|
|
46
66
|
return (
|
|
47
67
|
<div
|
|
48
68
|
ref={ref}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
69
|
+
onClick={e => {
|
|
70
|
+
const feature = getFeatureUnderMouse(e.clientX, e.clientY)
|
|
71
|
+
if (feature?.isInsertion) {
|
|
72
|
+
displayModel.showInsertionSequenceDialog?.({
|
|
73
|
+
sequence: feature.base,
|
|
74
|
+
sampleLabel: feature.sampleLabel,
|
|
75
|
+
chr: feature.chr,
|
|
76
|
+
pos: feature.pos,
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
}}
|
|
80
|
+
onMouseMove={e => {
|
|
81
|
+
const feature = getFeatureUnderMouse(e.clientX, e.clientY)
|
|
82
|
+
displayModel.setHoveredInfo?.(feature)
|
|
83
|
+
displayModel.setHighlightedRowNames?.(
|
|
84
|
+
feature?.sampleId ? [feature.sampleId] : undefined,
|
|
52
85
|
)
|
|
53
|
-
|
|
54
|
-
onMouseLeave={() => {
|
|
55
|
-
displayModel.setHoveredInfo?.(undefined)
|
|
86
|
+
setIsOverInsertion(!!feature?.isInsertion)
|
|
56
87
|
}}
|
|
57
|
-
|
|
88
|
+
onMouseLeave={() => {
|
|
58
89
|
displayModel.setHoveredInfo?.(undefined)
|
|
90
|
+
displayModel.setHighlightedRowNames?.(undefined)
|
|
91
|
+
setIsOverInsertion(false)
|
|
59
92
|
}}
|
|
60
93
|
style={{
|
|
61
94
|
overflow: 'visible',
|
|
62
95
|
position: 'relative',
|
|
63
96
|
height,
|
|
97
|
+
cursor: isOverInsertion ? 'pointer' : 'default',
|
|
64
98
|
}}
|
|
65
99
|
>
|
|
66
100
|
<PrerenderedCanvas
|
|
@@ -7,12 +7,7 @@ function x() {} // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
|
7
7
|
|
|
8
8
|
const configSchema = ConfigurationSchema(
|
|
9
9
|
'LinearMafRenderer',
|
|
10
|
-
{
|
|
11
|
-
baseColor: {
|
|
12
|
-
type: 'color',
|
|
13
|
-
defaultValue: 'lightgrey',
|
|
14
|
-
},
|
|
15
|
-
},
|
|
10
|
+
{},
|
|
16
11
|
{
|
|
17
12
|
/**
|
|
18
13
|
* #baseConfiguration
|
|
@@ -8,9 +8,8 @@ import {
|
|
|
8
8
|
RenderingContext,
|
|
9
9
|
Sample,
|
|
10
10
|
processFeatureAlignment,
|
|
11
|
-
processFeatureInsertions,
|
|
12
11
|
} from './rendering'
|
|
13
|
-
import { getColorBaseMap, getContrastBaseMap } from './util'
|
|
12
|
+
import { getCharWidthHeight, getColorBaseMap, getContrastBaseMap } from './util'
|
|
14
13
|
|
|
15
14
|
interface RenderArgs extends RenderArgsDeserialized {
|
|
16
15
|
samples: Sample[]
|
|
@@ -53,12 +52,14 @@ export function makeImageData({
|
|
|
53
52
|
const scale = 1 / bpPerPx
|
|
54
53
|
const hp2 = h / 2
|
|
55
54
|
const offset = (rowHeight - h) / 2
|
|
55
|
+
const { charWidth, charHeight } = getCharWidthHeight()
|
|
56
56
|
|
|
57
57
|
ctx.font = FONT_CONFIG
|
|
58
58
|
|
|
59
59
|
const renderingContext: RenderingContext = {
|
|
60
60
|
ctx,
|
|
61
61
|
scale,
|
|
62
|
+
bpPerPx,
|
|
62
63
|
canvasWidth,
|
|
63
64
|
rowHeight,
|
|
64
65
|
h,
|
|
@@ -69,12 +70,13 @@ export function makeImageData({
|
|
|
69
70
|
showAllLetters,
|
|
70
71
|
mismatchRendering,
|
|
71
72
|
showAsUpperCase,
|
|
73
|
+
charWidth,
|
|
74
|
+
charHeight,
|
|
72
75
|
spatialIndex: [],
|
|
73
76
|
spatialIndexCoords: [],
|
|
74
77
|
lastInsertedX: -Infinity, // Start with -Infinity so first item is always inserted
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
// First pass: render alignments (gaps, matches, mismatches, text)
|
|
78
80
|
for (const feature of features.values()) {
|
|
79
81
|
processFeatureAlignment(
|
|
80
82
|
feature,
|
|
@@ -84,17 +86,6 @@ export function makeImageData({
|
|
|
84
86
|
renderingContext,
|
|
85
87
|
)
|
|
86
88
|
}
|
|
87
|
-
|
|
88
|
-
// Second pass: render insertions on top
|
|
89
|
-
for (const feature of features.values()) {
|
|
90
|
-
processFeatureInsertions(
|
|
91
|
-
feature,
|
|
92
|
-
region,
|
|
93
|
-
bpPerPx,
|
|
94
|
-
sampleToRowMap,
|
|
95
|
-
renderingContext,
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
89
|
const flatbush = new Flatbush(renderingContext.spatialIndex.length || 1)
|
|
99
90
|
if (renderingContext.spatialIndex.length === 0) {
|
|
100
91
|
flatbush.add(0, 0, 1, 1)
|