react-msaview 4.5.0 → 4.8.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 +99 -99
- package/bundle/index.js.LICENSE.txt +6 -6
- package/bundle/index.js.map +1 -1
- package/dist/__snapshots__/parseAsn1.test.js.snap +2400 -0
- package/dist/components/header/HeaderInfoArea.js +3 -4
- package/dist/components/header/HeaderInfoArea.js.map +1 -1
- package/dist/components/import/ImportForm.js +6 -2
- package/dist/components/import/ImportForm.js.map +1 -1
- package/dist/components/import/util.d.ts +1 -1
- package/dist/components/import/util.js +4 -1
- package/dist/components/import/util.js.map +1 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +7 -2
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +20 -18
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +8 -1
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.d.ts +0 -1
- package/dist/components/tree/renderTreeCanvas.js +32 -31
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/model.d.ts +168 -16
- package/dist/model.js +116 -29
- package/dist/model.js.map +1 -1
- package/dist/rowCoordinateCalculations.d.ts +69 -9
- package/dist/rowCoordinateCalculations.js +118 -46
- package/dist/rowCoordinateCalculations.js.map +1 -1
- package/dist/rowCoordinateCalculations.test.js +152 -52
- package/dist/rowCoordinateCalculations.test.js.map +1 -1
- package/dist/seqPosToGlobalCol.d.ts +19 -0
- package/dist/seqPosToGlobalCol.js +34 -0
- package/dist/seqPosToGlobalCol.js.map +1 -0
- package/dist/seqPosToGlobalCol.test.js +60 -0
- package/dist/seqPosToGlobalCol.test.js.map +1 -0
- package/dist/util.d.ts +1 -2
- package/dist/util.js +0 -9
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -9
- package/src/components/header/HeaderInfoArea.tsx +2 -5
- package/src/components/import/ImportForm.tsx +6 -1
- package/src/components/import/util.ts +4 -0
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +7 -2
- package/src/components/msa/renderMSABlock.ts +26 -19
- package/src/components/msa/renderMSAMouseover.ts +9 -0
- package/src/components/tree/renderTreeCanvas.ts +35 -42
- package/src/declare.d.ts +0 -1
- package/src/model.ts +143 -42
- package/src/rowCoordinateCalculations.test.ts +167 -74
- package/src/rowCoordinateCalculations.ts +138 -63
- package/src/seqPosToGlobalCol.test.ts +71 -0
- package/src/seqPosToGlobalCol.ts +40 -0
- package/src/util.ts +1 -19
- package/src/version.ts +1 -1
- package/dist/parseGFF.d.ts +0 -10
- package/dist/parseGFF.js +0 -31
- package/dist/parseGFF.js.map +0 -1
- package/dist/parseNewick.d.ts +0 -60
- package/dist/parseNewick.js +0 -95
- package/dist/parseNewick.js.map +0 -1
- package/dist/parsers/A3mMSA.d.ts +0 -43
- package/dist/parsers/A3mMSA.js +0 -277
- package/dist/parsers/A3mMSA.js.map +0 -1
- package/dist/parsers/A3mMSA.test.js +0 -138
- package/dist/parsers/A3mMSA.test.js.map +0 -1
- package/dist/parsers/ClustalMSA.d.ts +0 -30
- package/dist/parsers/ClustalMSA.js +0 -55
- package/dist/parsers/ClustalMSA.js.map +0 -1
- package/dist/parsers/EmfMSA.d.ts +0 -27
- package/dist/parsers/EmfMSA.js +0 -53
- package/dist/parsers/EmfMSA.js.map +0 -1
- package/dist/parsers/EmfTree.d.ts +0 -5
- package/dist/parsers/EmfTree.js +0 -8
- package/dist/parsers/EmfTree.js.map +0 -1
- package/dist/parsers/FastaMSA.d.ts +0 -19
- package/dist/parsers/FastaMSA.js +0 -69
- package/dist/parsers/FastaMSA.js.map +0 -1
- package/dist/parsers/StockholmMSA.d.ts +0 -68
- package/dist/parsers/StockholmMSA.js +0 -107
- package/dist/parsers/StockholmMSA.js.map +0 -1
- package/dist/seqCoordToRowSpecificGlobalCoord.d.ts +0 -4
- package/dist/seqCoordToRowSpecificGlobalCoord.js +0 -19
- package/dist/seqCoordToRowSpecificGlobalCoord.js.map +0 -1
- package/dist/seqCoordToRowSpecificGlobalCoord.test.d.ts +0 -1
- package/dist/seqCoordToRowSpecificGlobalCoord.test.js +0 -42
- package/dist/seqCoordToRowSpecificGlobalCoord.test.js.map +0 -1
- package/src/parseGFF.ts +0 -34
- package/src/parseNewick.ts +0 -94
- package/src/parsers/A3mMSA.test.ts +0 -164
- package/src/parsers/A3mMSA.ts +0 -321
- package/src/parsers/ClustalMSA.ts +0 -69
- package/src/parsers/EmfMSA.ts +0 -67
- package/src/parsers/EmfTree.ts +0 -9
- package/src/parsers/FastaMSA.ts +0 -82
- package/src/parsers/StockholmMSA.ts +0 -140
- package/src/seqCoordToRowSpecificGlobalCoord.test.ts +0 -53
- package/src/seqCoordToRowSpecificGlobalCoord.ts +0 -25
- /package/dist/{parsers/A3mMSA.test.d.ts → seqPosToGlobalCol.test.d.ts} +0 -0
|
@@ -65,7 +65,6 @@ export function renderMSABlock({
|
|
|
65
65
|
ctx,
|
|
66
66
|
offsetX,
|
|
67
67
|
contrastScheme,
|
|
68
|
-
theme,
|
|
69
68
|
xStart,
|
|
70
69
|
xEnd,
|
|
71
70
|
visibleLeaves,
|
|
@@ -112,6 +111,10 @@ function drawTiles({
|
|
|
112
111
|
? columns[relativeTo]?.slice(xStart, xEnd)
|
|
113
112
|
: null
|
|
114
113
|
|
|
114
|
+
const isClustalX = colorSchemeName === 'clustalx_protein_dynamic'
|
|
115
|
+
const isPercentIdentity = colorSchemeName === 'percent_identity_dynamic'
|
|
116
|
+
const offsetXAligned = offsetX - (offsetX % colWidth)
|
|
117
|
+
|
|
115
118
|
for (let i = 0, l1 = visibleLeaves.length; i < l1; i++) {
|
|
116
119
|
const node = visibleLeaves[i]!
|
|
117
120
|
const {
|
|
@@ -120,31 +123,29 @@ function drawTiles({
|
|
|
120
123
|
const y = node.x!
|
|
121
124
|
const str = columns[name]?.slice(xStart, xEnd)
|
|
122
125
|
if (str) {
|
|
123
|
-
for (let
|
|
124
|
-
const letter = str[
|
|
126
|
+
for (let j = 0, l2 = str.length; j < l2; j++) {
|
|
127
|
+
const letter = str[j]!
|
|
125
128
|
|
|
126
129
|
// Use a muted background for positions that match reference
|
|
127
130
|
const isMatchingReference =
|
|
128
|
-
referenceSeq && name !== relativeTo && letter === referenceSeq[
|
|
131
|
+
referenceSeq && name !== relativeTo && letter === referenceSeq[j]
|
|
129
132
|
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
? model.colClustalX[xStart + i]![letter]
|
|
134
|
-
: r2
|
|
133
|
+
const color = isClustalX
|
|
134
|
+
? model.colClustalX[xStart + j]![letter]
|
|
135
|
+
: isPercentIdentity
|
|
135
136
|
? (() => {
|
|
136
|
-
const consensus = model.colConsensus[xStart +
|
|
137
|
+
const consensus = model.colConsensus[xStart + j]!
|
|
137
138
|
return letter === consensus.letter ? consensus.color : undefined
|
|
138
139
|
})()
|
|
139
140
|
: colorScheme[letter.toUpperCase()]
|
|
140
|
-
if (bgColor ||
|
|
141
|
+
if (bgColor || isClustalX || isPercentIdentity) {
|
|
141
142
|
// Use a very light background for matching positions in relative mode
|
|
142
143
|
const finalColor = isMatchingReference
|
|
143
144
|
? theme.palette.action.hover
|
|
144
145
|
: color || theme.palette.background.default
|
|
145
146
|
ctx.fillStyle = finalColor
|
|
146
147
|
ctx.fillRect(
|
|
147
|
-
|
|
148
|
+
j * colWidth + offsetXAligned,
|
|
148
149
|
y - rowHeight,
|
|
149
150
|
colWidth,
|
|
150
151
|
rowHeight,
|
|
@@ -167,7 +168,6 @@ function drawText({
|
|
|
167
168
|
offsetX: number
|
|
168
169
|
model: MsaViewModel
|
|
169
170
|
contrastScheme: Record<string, string>
|
|
170
|
-
theme: Theme
|
|
171
171
|
ctx: CanvasRenderingContext2D
|
|
172
172
|
visibleLeaves: HierarchyNode<NodeWithIdsAndLength>[]
|
|
173
173
|
xStart: number
|
|
@@ -191,20 +191,24 @@ function drawText({
|
|
|
191
191
|
: null
|
|
192
192
|
|
|
193
193
|
if (showMsaLetters) {
|
|
194
|
+
const offsetXAligned = offsetX - (offsetX % colWidth)
|
|
195
|
+
const halfColWidth = colWidth / 2
|
|
196
|
+
const quarterRowHeight = rowHeight / 4
|
|
197
|
+
|
|
194
198
|
for (let i = 0, l1 = visibleLeaves.length; i < l1; i++) {
|
|
195
199
|
const node = visibleLeaves[i]!
|
|
196
200
|
const {
|
|
197
201
|
data: { name },
|
|
198
202
|
} = node
|
|
199
|
-
const y = node.x!
|
|
203
|
+
const y = node.x! - quarterRowHeight
|
|
200
204
|
const str = columns[name]?.slice(xStart, xEnd)
|
|
201
205
|
if (str) {
|
|
202
|
-
for (let
|
|
203
|
-
const letter = str[
|
|
206
|
+
for (let j = 0, l2 = str.length; j < l2; j++) {
|
|
207
|
+
const letter = str[j]!
|
|
204
208
|
|
|
205
209
|
// Check if this position matches the reference
|
|
206
210
|
const isMatchingReference =
|
|
207
|
-
referenceSeq && name !== relativeTo && letter === referenceSeq[
|
|
211
|
+
referenceSeq && name !== relativeTo && letter === referenceSeq[j]
|
|
208
212
|
|
|
209
213
|
// Show dot for matching positions, original letter for differences
|
|
210
214
|
const displayLetter = isMatchingReference ? '.' : letter
|
|
@@ -213,7 +217,6 @@ function drawText({
|
|
|
213
217
|
const contrast = contrastLettering
|
|
214
218
|
? contrastScheme[letter.toUpperCase()] || 'black'
|
|
215
219
|
: 'black'
|
|
216
|
-
const x = i * colWidth + offsetX - (offsetX % colWidth)
|
|
217
220
|
|
|
218
221
|
// note: -rowHeight/4 matches +rowHeight/4 in tree
|
|
219
222
|
ctx.fillStyle = actuallyShowDomains
|
|
@@ -221,7 +224,11 @@ function drawText({
|
|
|
221
224
|
: bgColor
|
|
222
225
|
? contrast
|
|
223
226
|
: color || 'black'
|
|
224
|
-
ctx.fillText(
|
|
227
|
+
ctx.fillText(
|
|
228
|
+
displayLetter,
|
|
229
|
+
j * colWidth + offsetXAligned + halfColWidth,
|
|
230
|
+
y,
|
|
231
|
+
)
|
|
225
232
|
}
|
|
226
233
|
}
|
|
227
234
|
}
|
|
@@ -28,6 +28,7 @@ export function renderMouseover({
|
|
|
28
28
|
relativeTo,
|
|
29
29
|
rowNamesSet,
|
|
30
30
|
hoveredTreeNode,
|
|
31
|
+
highlightedColumns,
|
|
31
32
|
} = model
|
|
32
33
|
ctx.resetTransform()
|
|
33
34
|
ctx.clearRect(0, 0, width, height)
|
|
@@ -52,6 +53,14 @@ export function renderMouseover({
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
// Highlight multiple columns
|
|
57
|
+
if (highlightedColumns?.length) {
|
|
58
|
+
ctx.fillStyle = highlightColor
|
|
59
|
+
for (const col of highlightedColumns) {
|
|
60
|
+
ctx.fillRect(col * colWidth + scrollX, 0, colWidth, height)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
55
64
|
if (mouseCol !== undefined) {
|
|
56
65
|
ctx.fillStyle = hoverColor
|
|
57
66
|
ctx.fillRect(mouseCol * colWidth + scrollX, 0, colWidth, height)
|
|
@@ -42,15 +42,9 @@ export function renderTree({
|
|
|
42
42
|
theme: Theme
|
|
43
43
|
blockSizeYOverride?: number
|
|
44
44
|
}) {
|
|
45
|
-
const {
|
|
46
|
-
hierarchy,
|
|
47
|
-
allBranchesLength0,
|
|
48
|
-
showBranchLen: showBranchLenPre,
|
|
49
|
-
blockSize,
|
|
50
|
-
} = model
|
|
45
|
+
const { hierarchy, showBranchLenEffective: showBranchLen, blockSize } = model
|
|
51
46
|
const by = blockSizeYOverride || blockSize
|
|
52
47
|
ctx.strokeStyle = theme.palette.text.primary
|
|
53
|
-
const showBranchLen = allBranchesLength0 ? false : showBranchLenPre
|
|
54
48
|
for (const link of hierarchy.links()) {
|
|
55
49
|
const { source, target } = link
|
|
56
50
|
if (target.height === 0 && !showBranchLen) {
|
|
@@ -58,10 +52,11 @@ export function renderTree({
|
|
|
58
52
|
}
|
|
59
53
|
const sy = source.x!
|
|
60
54
|
const ty = target.x!
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
const tx = showBranchLen ? (target as { len?: number }).len : target.y
|
|
56
|
+
const sx = showBranchLen ? (source as { len?: number }).len : source.y
|
|
57
|
+
if (tx === undefined || sx === undefined) {
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
65
60
|
|
|
66
61
|
const y1 = Math.min(sy, ty)
|
|
67
62
|
const y2 = Math.max(sy, ty)
|
|
@@ -89,23 +84,22 @@ export function renderNodeBubbles({
|
|
|
89
84
|
clickMap?: ClickMapIndex
|
|
90
85
|
offsetY: number
|
|
91
86
|
model: MsaViewModel
|
|
92
|
-
theme: Theme
|
|
93
87
|
blockSizeYOverride?: number
|
|
94
88
|
}) {
|
|
95
89
|
const {
|
|
96
90
|
hierarchy,
|
|
97
|
-
|
|
98
|
-
allBranchesLength0,
|
|
91
|
+
showBranchLenEffective: showBranchLen,
|
|
99
92
|
collapsed,
|
|
100
93
|
blockSize,
|
|
101
94
|
marginLeft: ml,
|
|
102
95
|
} = model
|
|
103
96
|
const by = blockSizeYOverride || blockSize
|
|
104
|
-
const showBranchLen = allBranchesLength0 ? false : showBranchLenPre
|
|
105
97
|
for (const node of hierarchy.descendants()) {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
const x = showBranchLen ? (node as { len?: number }).len : node.y
|
|
99
|
+
if (x === undefined) {
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
const { data } = node
|
|
109
103
|
const y = node.x!
|
|
110
104
|
const { id, name } = data
|
|
111
105
|
if (
|
|
@@ -150,8 +144,7 @@ export function renderTreeLabels({
|
|
|
150
144
|
}) {
|
|
151
145
|
const {
|
|
152
146
|
fontSize,
|
|
153
|
-
|
|
154
|
-
allBranchesLength0,
|
|
147
|
+
showBranchLenEffective: showBranchLen,
|
|
155
148
|
treeMetadata,
|
|
156
149
|
hierarchy,
|
|
157
150
|
collapsed,
|
|
@@ -167,19 +160,18 @@ export function renderTreeLabels({
|
|
|
167
160
|
noTree,
|
|
168
161
|
} = model
|
|
169
162
|
const by = blockSizeYOverride || blockSize
|
|
163
|
+
const emHeight = ctx.measureText('M').width
|
|
170
164
|
if (labelsAlignRight) {
|
|
171
165
|
ctx.textAlign = 'right'
|
|
172
166
|
ctx.setLineDash([1, 3])
|
|
173
167
|
} else {
|
|
174
168
|
ctx.textAlign = 'start'
|
|
175
169
|
}
|
|
176
|
-
const showBranchLen = allBranchesLength0 ? false : showBranchLenPre
|
|
177
170
|
for (const node of leaves) {
|
|
178
171
|
const {
|
|
179
172
|
data: { name, id },
|
|
180
|
-
// @ts-expect-error
|
|
181
|
-
len,
|
|
182
173
|
} = node
|
|
174
|
+
const len = (node as { len?: number }).len
|
|
183
175
|
const y = node.x!
|
|
184
176
|
const x = node.y!
|
|
185
177
|
|
|
@@ -187,28 +179,29 @@ export function renderTreeLabels({
|
|
|
187
179
|
if (y > offsetY - extendBounds && y < offsetY + by + extendBounds) {
|
|
188
180
|
// note: +rowHeight/4 matches with -rowHeight/4 in msa
|
|
189
181
|
const yp = y + fontSize / 4
|
|
190
|
-
let xp =
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
182
|
+
let xp = 0
|
|
183
|
+
if (!noTree) {
|
|
184
|
+
xp = (showBranchLen ? len : x) || 0
|
|
185
|
+
if (
|
|
186
|
+
!showBranchLen &&
|
|
187
|
+
!collapsed.includes(id) &&
|
|
188
|
+
!collapsedLeaves.includes(id)
|
|
189
|
+
) {
|
|
190
|
+
// this subtraction is a hack to compensate for the leafnode rendering
|
|
191
|
+
// glitch (issue #71). the context is that an extra leaf node is added
|
|
192
|
+
// so that 'collapsing/hiding leaf nodes is possible' but this causes
|
|
193
|
+
// weird workarounds
|
|
194
|
+
xp -= treeWidth / hierarchy.height
|
|
195
|
+
}
|
|
201
196
|
}
|
|
202
197
|
|
|
203
198
|
const { width } = ctx.measureText(displayName)
|
|
204
|
-
const height = ctx.measureText('M').width // use an 'em' for height
|
|
205
199
|
|
|
206
200
|
ctx.fillStyle = theme.palette.text.primary
|
|
207
201
|
if (labelsAlignRight) {
|
|
208
202
|
const smallPadding = 2
|
|
209
203
|
const offset = treeAreaWidthMinusMargin - smallPadding
|
|
210
204
|
if (drawTree && !noTree) {
|
|
211
|
-
const { width } = ctx.measureText(displayName)
|
|
212
205
|
ctx.moveTo(xp + radius + 2, y)
|
|
213
206
|
ctx.lineTo(offset - smallPadding - width, y)
|
|
214
207
|
ctx.stroke()
|
|
@@ -217,17 +210,18 @@ export function renderTreeLabels({
|
|
|
217
210
|
clickMap?.insert({
|
|
218
211
|
minX: treeAreaWidth - width,
|
|
219
212
|
maxX: treeAreaWidth,
|
|
220
|
-
minY: yp -
|
|
213
|
+
minY: yp - emHeight,
|
|
221
214
|
maxY: yp,
|
|
222
215
|
name,
|
|
223
216
|
id,
|
|
224
217
|
})
|
|
225
218
|
} else {
|
|
226
|
-
|
|
219
|
+
const labelX = noTree ? 2 : xp + d
|
|
220
|
+
ctx.fillText(displayName, labelX, yp)
|
|
227
221
|
clickMap?.insert({
|
|
228
|
-
minX:
|
|
229
|
-
maxX:
|
|
230
|
-
minY: yp -
|
|
222
|
+
minX: labelX + marginLeft,
|
|
223
|
+
maxX: labelX + width + marginLeft,
|
|
224
|
+
minY: yp - emHeight,
|
|
231
225
|
maxY: yp,
|
|
232
226
|
name,
|
|
233
227
|
id,
|
|
@@ -303,7 +297,6 @@ export function renderTreeCanvas({
|
|
|
303
297
|
offsetY,
|
|
304
298
|
clickMap,
|
|
305
299
|
model,
|
|
306
|
-
theme,
|
|
307
300
|
blockSizeYOverride,
|
|
308
301
|
})
|
|
309
302
|
}
|
package/src/declare.d.ts
CHANGED
package/src/model.ts
CHANGED
|
@@ -9,14 +9,25 @@ import {
|
|
|
9
9
|
} from '@jbrowse/core/util'
|
|
10
10
|
import { openLocation } from '@jbrowse/core/util/io'
|
|
11
11
|
import { ElementId, FileLocation } from '@jbrowse/core/util/types/mst'
|
|
12
|
+
import {
|
|
13
|
+
A3mMSA,
|
|
14
|
+
ClustalMSA,
|
|
15
|
+
EmfMSA,
|
|
16
|
+
FastaMSA,
|
|
17
|
+
StockholmMSA,
|
|
18
|
+
generateNodeIds,
|
|
19
|
+
gffToInterProResults,
|
|
20
|
+
parseEmfTree,
|
|
21
|
+
parseGFF,
|
|
22
|
+
parseNewick,
|
|
23
|
+
stockholmSniff,
|
|
24
|
+
} from '@react-msaview/parsers'
|
|
12
25
|
import { colord } from 'colord'
|
|
13
26
|
import { ascending } from 'd3-array'
|
|
14
27
|
import { cluster, hierarchy } from 'd3-hierarchy'
|
|
15
|
-
import { parseEmfTree } from 'emf-js'
|
|
16
28
|
import { saveAs } from 'file-saver'
|
|
17
29
|
import { autorun, transaction } from 'mobx'
|
|
18
30
|
import { addDisposer, cast, types } from 'mobx-state-tree'
|
|
19
|
-
import Stockholm from 'stockholm-js'
|
|
20
31
|
|
|
21
32
|
import { blocksX, blocksY } from './calculateBlocks'
|
|
22
33
|
import colorSchemes from './colorSchemes'
|
|
@@ -55,26 +66,14 @@ import { MSAModelF } from './model/msaModel'
|
|
|
55
66
|
import { TreeModelF } from './model/treeModel'
|
|
56
67
|
import { calculateNeighborJoiningTree } from './neighborJoining'
|
|
57
68
|
import { parseAsn1 } from './parseAsn1'
|
|
58
|
-
import parseNewick from './parseNewick'
|
|
59
|
-
import A3mMSA from './parsers/A3mMSA'
|
|
60
|
-
import ClustalMSA from './parsers/ClustalMSA'
|
|
61
|
-
import EmfMSA from './parsers/EmfMSA'
|
|
62
|
-
import FastaMSA from './parsers/FastaMSA'
|
|
63
|
-
import StockholmMSA from './parsers/StockholmMSA'
|
|
64
69
|
import { reparseTree } from './reparseTree'
|
|
65
70
|
import {
|
|
66
|
-
|
|
67
|
-
|
|
71
|
+
globalColToVisibleCol,
|
|
72
|
+
visibleColToGlobalCol,
|
|
73
|
+
visibleColToSeqPosForRow,
|
|
68
74
|
} from './rowCoordinateCalculations'
|
|
69
|
-
import {
|
|
70
|
-
import {
|
|
71
|
-
collapse,
|
|
72
|
-
generateNodeIds,
|
|
73
|
-
len,
|
|
74
|
-
maxLength,
|
|
75
|
-
setBrLength,
|
|
76
|
-
skipBlanks,
|
|
77
|
-
} from './util'
|
|
75
|
+
import { seqPosToGlobalCol } from './seqPosToGlobalCol'
|
|
76
|
+
import { collapse, len, maxLength, setBrLength, skipBlanks } from './util'
|
|
78
77
|
|
|
79
78
|
import type { InterProScanResults } from './launchInterProScan'
|
|
80
79
|
import type {
|
|
@@ -193,6 +192,12 @@ function stateModelFactory() {
|
|
|
193
192
|
*/
|
|
194
193
|
treeMetadataFilehandle: types.maybe(FileLocation),
|
|
195
194
|
|
|
195
|
+
/**
|
|
196
|
+
* #property
|
|
197
|
+
* filehandle object for InterProScan GFF file
|
|
198
|
+
*/
|
|
199
|
+
gffFilehandle: types.maybe(FileLocation),
|
|
200
|
+
|
|
196
201
|
/**
|
|
197
202
|
* #property
|
|
198
203
|
*
|
|
@@ -321,6 +326,12 @@ function stateModelFactory() {
|
|
|
321
326
|
| { nodeId: string; descendantNames: string[] }
|
|
322
327
|
| undefined,
|
|
323
328
|
|
|
329
|
+
/**
|
|
330
|
+
* #volatile
|
|
331
|
+
* array of column indices to highlight
|
|
332
|
+
*/
|
|
333
|
+
highlightedColumns: undefined as number[] | undefined,
|
|
334
|
+
|
|
324
335
|
/**
|
|
325
336
|
* #volatile
|
|
326
337
|
* a dummy variable that is incremented when ref changes so autorun for
|
|
@@ -453,6 +464,13 @@ function stateModelFactory() {
|
|
|
453
464
|
|
|
454
465
|
self.hoveredTreeNode = { nodeId, descendantNames }
|
|
455
466
|
},
|
|
467
|
+
/**
|
|
468
|
+
* #action
|
|
469
|
+
* set highlighted columns
|
|
470
|
+
*/
|
|
471
|
+
setHighlightedColumns(columns?: number[]) {
|
|
472
|
+
self.highlightedColumns = columns
|
|
473
|
+
},
|
|
456
474
|
/**
|
|
457
475
|
* #action
|
|
458
476
|
*/
|
|
@@ -556,6 +574,13 @@ function stateModelFactory() {
|
|
|
556
574
|
self.treeFilehandle = treeFilehandle
|
|
557
575
|
},
|
|
558
576
|
|
|
577
|
+
/**
|
|
578
|
+
* #action
|
|
579
|
+
*/
|
|
580
|
+
setGFFFilehandle(gffFilehandle?: FileLocationType) {
|
|
581
|
+
self.gffFilehandle = gffFilehandle
|
|
582
|
+
},
|
|
583
|
+
|
|
559
584
|
/**
|
|
560
585
|
* #action
|
|
561
586
|
*/
|
|
@@ -677,7 +702,7 @@ function stateModelFactory() {
|
|
|
677
702
|
get MSA() {
|
|
678
703
|
const text = self.data.msa
|
|
679
704
|
if (text) {
|
|
680
|
-
if (
|
|
705
|
+
if (stockholmSniff(text)) {
|
|
681
706
|
return new StockholmMSA(text, self.currentAlignment)
|
|
682
707
|
} else if (A3mMSA.sniff(text)) {
|
|
683
708
|
return new A3mMSA(text)
|
|
@@ -1217,6 +1242,14 @@ function stateModelFactory() {
|
|
|
1217
1242
|
get allBranchesLength0() {
|
|
1218
1243
|
return this.hierarchy.links().every(s => !s.source.data.length)
|
|
1219
1244
|
},
|
|
1245
|
+
|
|
1246
|
+
/**
|
|
1247
|
+
* #getter
|
|
1248
|
+
* effective showBranchLen accounting for allBranchesLength0
|
|
1249
|
+
*/
|
|
1250
|
+
get showBranchLenEffective() {
|
|
1251
|
+
return this.allBranchesLength0 ? false : self.showBranchLen
|
|
1252
|
+
},
|
|
1220
1253
|
}))
|
|
1221
1254
|
.views(self => ({
|
|
1222
1255
|
/**
|
|
@@ -1464,7 +1497,7 @@ function stateModelFactory() {
|
|
|
1464
1497
|
.map(t => ({
|
|
1465
1498
|
model: {
|
|
1466
1499
|
...t,
|
|
1467
|
-
data: hideGapsEffective ? skipBlanks(blanks, t.data!) : t.data
|
|
1500
|
+
data: hideGapsEffective ? skipBlanks(blanks, t.data!) : t.data!,
|
|
1468
1501
|
height: rowHeight,
|
|
1469
1502
|
} as TextTrackModel,
|
|
1470
1503
|
ReactComponent: TextTrack,
|
|
@@ -1510,22 +1543,30 @@ function stateModelFactory() {
|
|
|
1510
1543
|
|
|
1511
1544
|
/**
|
|
1512
1545
|
* #method
|
|
1513
|
-
*
|
|
1546
|
+
* Return a row-specific letter at a visible column, or undefined if gap.
|
|
1547
|
+
*
|
|
1548
|
+
* @param rowName - The name of the row
|
|
1549
|
+
* @param visibleCol - The visible column index (what the user sees on screen)
|
|
1550
|
+
* @returns The letter at that position, or undefined if it's a gap
|
|
1514
1551
|
*/
|
|
1515
|
-
|
|
1552
|
+
visibleColToRowLetter(rowName: string, visibleCol: number) {
|
|
1516
1553
|
const { rowMap, blanks } = self
|
|
1517
|
-
return rowMap.get(rowName)?.[
|
|
1554
|
+
return rowMap.get(rowName)?.[visibleColToGlobalCol(blanks, visibleCol)]
|
|
1518
1555
|
},
|
|
1519
1556
|
|
|
1520
1557
|
/**
|
|
1521
1558
|
* #method
|
|
1522
|
-
*
|
|
1523
|
-
*
|
|
1559
|
+
* Convert a visible column to a row-specific sequence position (0-based).
|
|
1560
|
+
* Returns undefined if the position is a gap in the sequence.
|
|
1561
|
+
*
|
|
1562
|
+
* @param rowName - The name of the row
|
|
1563
|
+
* @param visibleCol - The visible column index
|
|
1564
|
+
* @returns The sequence position (0-based), or undefined if it's a gap
|
|
1524
1565
|
*/
|
|
1525
|
-
|
|
1526
|
-
return
|
|
1566
|
+
visibleColToSeqPos(rowName: string, visibleCol: number) {
|
|
1567
|
+
return visibleColToSeqPosForRow({
|
|
1527
1568
|
rowName,
|
|
1528
|
-
|
|
1569
|
+
visibleCol,
|
|
1529
1570
|
rowMap: self.rowMap,
|
|
1530
1571
|
blanks: self.blanks,
|
|
1531
1572
|
})
|
|
@@ -1533,32 +1574,67 @@ function stateModelFactory() {
|
|
|
1533
1574
|
|
|
1534
1575
|
/**
|
|
1535
1576
|
* #method
|
|
1536
|
-
*
|
|
1537
|
-
*
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1577
|
+
* Convert a visible column to a row-specific sequence position (1-based).
|
|
1578
|
+
* Returns undefined if the position is a gap in the sequence.
|
|
1579
|
+
*
|
|
1580
|
+
* @param rowName - The name of the row
|
|
1581
|
+
* @param visibleCol - The visible column index
|
|
1582
|
+
* @returns The sequence position (1-based), or undefined if it's a gap
|
|
1583
|
+
*/
|
|
1584
|
+
visibleColToSeqPosOneBased(rowName: string, visibleCol: number) {
|
|
1585
|
+
const val = this.visibleColToSeqPos(rowName, visibleCol)
|
|
1544
1586
|
return val !== undefined ? val + 1 : undefined
|
|
1545
1587
|
},
|
|
1546
1588
|
|
|
1547
1589
|
/**
|
|
1548
1590
|
* #method
|
|
1549
|
-
*
|
|
1550
|
-
*
|
|
1591
|
+
* Convert a global column index to a visible column index.
|
|
1592
|
+
* Returns undefined if the column is hidden (in blanks).
|
|
1593
|
+
* This is the inverse of visibleColToGlobalCol.
|
|
1594
|
+
*
|
|
1595
|
+
* @param globalCol - The global column index in the full MSA
|
|
1596
|
+
* @returns The visible column index, or undefined if the column is hidden
|
|
1551
1597
|
*/
|
|
1552
|
-
|
|
1598
|
+
globalColToVisibleCol(globalCol: number) {
|
|
1599
|
+
const { blanks, hideGapsEffective } = self
|
|
1600
|
+
if (!hideGapsEffective) {
|
|
1601
|
+
return globalCol
|
|
1602
|
+
}
|
|
1603
|
+
return globalColToVisibleCol(blanks, globalCol)
|
|
1604
|
+
},
|
|
1605
|
+
|
|
1606
|
+
/**
|
|
1607
|
+
* #method
|
|
1608
|
+
* Convert a sequence position (ungapped) to a global column index.
|
|
1609
|
+
*
|
|
1610
|
+
* @param rowName - The name of the row
|
|
1611
|
+
* @param seqPos - The sequence position (0-based, ungapped)
|
|
1612
|
+
* @returns The global column index in the full MSA
|
|
1613
|
+
*/
|
|
1614
|
+
seqPosToGlobalCol(rowName: string, seqPos: number) {
|
|
1553
1615
|
const { rowNames, rows } = self
|
|
1554
1616
|
const index = rowNames.indexOf(rowName)
|
|
1555
1617
|
return index !== -1 && rows[index]
|
|
1556
|
-
?
|
|
1618
|
+
? seqPosToGlobalCol({
|
|
1557
1619
|
row: rows[index][1],
|
|
1558
|
-
|
|
1620
|
+
seqPos,
|
|
1559
1621
|
})
|
|
1560
1622
|
: 0
|
|
1561
1623
|
},
|
|
1624
|
+
|
|
1625
|
+
/**
|
|
1626
|
+
* #method
|
|
1627
|
+
* Convert a sequence position (ungapped) directly to a visible column index.
|
|
1628
|
+
* This combines seqPosToGlobalCol and globalColToVisibleCol.
|
|
1629
|
+
*
|
|
1630
|
+
* @param rowName - The name of the row
|
|
1631
|
+
* @param seqPos - The sequence position (0-based, ungapped)
|
|
1632
|
+
* @returns The visible column index, or undefined if the column is hidden
|
|
1633
|
+
*/
|
|
1634
|
+
seqPosToVisibleCol(rowName: string, seqPos: number) {
|
|
1635
|
+
const globalCol = this.seqPosToGlobalCol(rowName, seqPos)
|
|
1636
|
+
return this.globalColToVisibleCol(globalCol)
|
|
1637
|
+
},
|
|
1562
1638
|
}))
|
|
1563
1639
|
|
|
1564
1640
|
.views(self => ({
|
|
@@ -1830,6 +1906,31 @@ function stateModelFactory() {
|
|
|
1830
1906
|
}),
|
|
1831
1907
|
)
|
|
1832
1908
|
|
|
1909
|
+
// autorun opens gffFilehandle for InterProScan domains
|
|
1910
|
+
addDisposer(
|
|
1911
|
+
self,
|
|
1912
|
+
autorun(async () => {
|
|
1913
|
+
const { gffFilehandle } = self
|
|
1914
|
+
if (gffFilehandle) {
|
|
1915
|
+
try {
|
|
1916
|
+
const gffText = await fetchAndMaybeUnzipText(
|
|
1917
|
+
openLocation(gffFilehandle),
|
|
1918
|
+
)
|
|
1919
|
+
const gffRecords = parseGFF(gffText)
|
|
1920
|
+
const interProResults = gffToInterProResults(gffRecords)
|
|
1921
|
+
self.setInterProAnnotations(interProResults)
|
|
1922
|
+
self.setShowDomains(true)
|
|
1923
|
+
if (gffFilehandle.locationType === 'BlobLocation') {
|
|
1924
|
+
self.setGFFFilehandle(undefined)
|
|
1925
|
+
}
|
|
1926
|
+
} catch (e) {
|
|
1927
|
+
console.error(e)
|
|
1928
|
+
self.setError(e)
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
}),
|
|
1932
|
+
)
|
|
1933
|
+
|
|
1833
1934
|
// autorun opens msaFilehandle
|
|
1834
1935
|
addDisposer(
|
|
1835
1936
|
self,
|