react-msaview 6.4.2 → 7.0.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/README.md +6 -5
- package/bundle/index.js +100 -101
- package/bundle/index.js.map +4 -4
- package/dist/components/Loading.js +2 -2
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +10 -2
- package/dist/components/MSAViewer.js +90 -2
- package/dist/components/MSAViewer.js.map +1 -1
- package/dist/components/ResizeHandles.d.ts +3 -3
- package/dist/components/ResizeHandles.js +28 -21
- package/dist/components/ResizeHandles.js.map +1 -1
- package/dist/components/dialogs/{InterProScanDialog.d.ts → AnnotationFileDialog.d.ts} +2 -2
- package/dist/components/dialogs/AnnotationFileDialog.js +92 -0
- package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -0
- package/dist/components/dialogs/FeatureDialog.js +13 -9
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/header/Header.js +4 -0
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/HeaderStatusArea.js +0 -2
- package/dist/components/header/HeaderStatusArea.js.map +1 -1
- package/dist/components/header/LoadWarnings.d.ts +6 -0
- package/dist/components/header/LoadWarnings.js +36 -0
- package/dist/components/header/LoadWarnings.js.map +1 -0
- package/dist/components/header/MultiAlignmentSelector.js +0 -0
- package/dist/components/header/MultiAlignmentSelector.js.map +1 -1
- package/dist/components/header/TreeSettingsMenu.js +9 -1
- package/dist/components/header/TreeSettingsMenu.js.map +1 -1
- package/dist/components/header/UnshareableDataWarning.d.ts +6 -0
- package/dist/components/header/UnshareableDataWarning.js +42 -0
- package/dist/components/header/UnshareableDataWarning.js.map +1 -0
- package/dist/components/header/getAnnotationsMenu.js +14 -14
- package/dist/components/header/getAnnotationsMenu.js.map +1 -1
- package/dist/components/import/ImportForm.js +1 -1
- package/dist/components/import/ImportForm.js.map +1 -1
- package/dist/components/import/ImportFormExamples.js +2 -6
- package/dist/components/import/ImportFormExamples.js.map +1 -1
- package/dist/components/import/util.js +3 -0
- package/dist/components/import/util.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +4 -5
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/msaRaster.d.ts +13 -3
- package/dist/components/msa/msaRaster.js +173 -61
- package/dist/components/msa/msaRaster.js.map +1 -1
- package/dist/components/msa/renderHighlights.d.ts +39 -1
- package/dist/components/msa/renderHighlights.js +71 -6
- package/dist/components/msa/renderHighlights.js.map +1 -1
- package/dist/components/msa/renderMSABlock.d.ts +1 -2
- package/dist/components/msa/renderMSABlock.js +24 -28
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +9 -30
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/renderCtx.d.ts +1 -1
- package/dist/components/tracks/TrackBlocks.js +10 -11
- package/dist/components/tracks/TrackBlocks.js.map +1 -1
- package/dist/components/tracks/drawTracks.d.ts +26 -8
- package/dist/components/tracks/drawTracks.js +123 -13
- package/dist/components/tracks/drawTracks.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js +2 -3
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +4 -2
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeRuler.js +13 -3
- package/dist/components/tree/TreeRuler.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.d.ts +0 -1
- package/dist/components/tree/renderTreeCanvas.js +70 -30
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/components/tree/scaleBar.d.ts +10 -0
- package/dist/components/tree/scaleBar.js +24 -0
- package/dist/components/tree/scaleBar.js.map +1 -0
- package/dist/constants.d.ts +3 -1
- package/dist/constants.js +18 -1
- package/dist/constants.js.map +1 -1
- package/dist/fetchUtils.d.ts +7 -7
- package/dist/fetchUtils.js +4 -32
- package/dist/fetchUtils.js.map +1 -1
- package/dist/hierarchy.d.ts +2 -1
- package/dist/hierarchy.js +16 -10
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/model/DataModel.js +5 -2
- package/dist/model/DataModel.js.map +1 -1
- package/dist/model.d.ts +454 -177
- package/dist/model.js +1180 -511
- package/dist/model.js.map +1 -1
- package/dist/renderToStaticMarkup.d.ts +11 -0
- package/dist/renderToStaticMarkup.js +30 -7
- package/dist/renderToStaticMarkup.js.map +1 -1
- package/dist/renderToSvg.js +25 -19
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.d.ts +25 -25
- package/dist/rowCoordinateCalculations.js +12 -36
- package/dist/rowCoordinateCalculations.js.map +1 -1
- package/dist/seqPosToGlobalCol.js +3 -1
- package/dist/seqPosToGlobalCol.js.map +1 -1
- package/dist/svgTestUtil.d.ts +144 -82
- package/dist/types.d.ts +97 -3
- package/dist/util.d.ts +11 -4
- package/dist/util.js +30 -10
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/wuss.d.ts +14 -0
- package/dist/wuss.js +55 -0
- package/dist/wuss.js.map +1 -0
- package/package.json +10 -8
- package/src/components/Loading.tsx +12 -2
- package/src/components/MSAViewer.tsx +106 -2
- package/src/components/ResizeHandles.tsx +31 -31
- package/src/components/dialogs/AnnotationFileDialog.tsx +162 -0
- package/src/components/dialogs/FeatureDialog.tsx +13 -9
- package/src/components/header/Header.tsx +4 -0
- package/src/components/header/HeaderStatusArea.tsx +0 -5
- package/src/components/header/LoadWarnings.tsx +49 -0
- package/src/components/header/MultiAlignmentSelector.tsx +0 -0
- package/src/components/header/TreeSettingsMenu.tsx +9 -1
- package/src/components/header/UnshareableDataWarning.tsx +58 -0
- package/src/components/header/getAnnotationsMenu.ts +16 -17
- package/src/components/import/ImportForm.tsx +2 -2
- package/src/components/import/ImportFormExamples.tsx +2 -6
- package/src/components/import/util.ts +3 -0
- package/src/components/msa/MSACanvasBlock.tsx +4 -5
- package/src/components/msa/msaRaster.ts +216 -59
- package/src/components/msa/renderHighlights.ts +103 -5
- package/src/components/msa/renderMSABlock.ts +25 -36
- package/src/components/msa/renderMSAMouseover.ts +9 -39
- package/src/components/renderCtx.ts +1 -0
- package/src/components/tracks/TrackBlocks.tsx +10 -13
- package/src/components/tracks/drawTracks.ts +165 -17
- package/src/components/tree/TreeCanvas.tsx +2 -3
- package/src/components/tree/TreeCanvasBlock.tsx +4 -2
- package/src/components/tree/TreeRuler.tsx +38 -3
- package/src/components/tree/renderTreeCanvas.ts +79 -35
- package/src/components/tree/scaleBar.ts +23 -0
- package/src/constants.ts +20 -1
- package/src/fetchUtils.ts +7 -40
- package/src/hierarchy.ts +20 -11
- package/src/index.ts +18 -2
- package/src/model/DataModel.ts +6 -2
- package/src/model.ts +1352 -616
- package/src/rawImports.d.ts +9 -0
- package/src/renderToStaticMarkup.ts +34 -7
- package/src/renderToSvg.tsx +33 -27
- package/src/rowCoordinateCalculations.ts +15 -59
- package/src/seqPosToGlobalCol.ts +3 -1
- package/src/types.ts +131 -4
- package/src/util.ts +31 -14
- package/src/version.ts +1 -1
- package/src/wuss.ts +62 -0
- package/dist/components/dialogs/InterProScanDialog.js +0 -137
- package/dist/components/dialogs/InterProScanDialog.js.map +0 -1
- package/dist/components/dialogs/InterProScanFileDialog.d.ts +0 -7
- package/dist/components/dialogs/InterProScanFileDialog.js +0 -64
- package/dist/components/dialogs/InterProScanFileDialog.js.map +0 -1
- package/dist/launchInterProScan.d.ts +0 -13
- package/dist/launchInterProScan.js +0 -110
- package/dist/launchInterProScan.js.map +0 -1
- package/dist/useColorContrast.d.ts +0 -10
- package/dist/useColorContrast.js +0 -13
- package/dist/useColorContrast.js.map +0 -1
- package/src/clustalX.test.ts +0 -74
- package/src/colSpace.test.ts +0 -67
- package/src/collapseLogic.test.ts +0 -115
- package/src/colorSchemes.docs.test.ts +0 -57
- package/src/columnCounts.test.ts +0 -73
- package/src/columnStats.test.ts +0 -94
- package/src/columnTracks.test.ts +0 -97
- package/src/components/dialogs/InterProScanDialog.tsx +0 -214
- package/src/components/dialogs/InterProScanFileDialog.tsx +0 -138
- package/src/components/minimap/minimapLayout.test.ts +0 -71
- package/src/components/msa/msaRaster.test.ts +0 -145
- package/src/components/msa/renderBoxFeatureCanvasBlock.test.ts +0 -51
- package/src/components/tree/renderTreeCanvas.test.ts +0 -296
- package/src/dataInitialized.test.ts +0 -42
- package/src/domainBands.test.ts +0 -122
- package/src/domainLetterColor.test.ts +0 -80
- package/src/exportFileName.test.ts +0 -40
- package/src/fetchUtils.test.ts +0 -83
- package/src/flatToTree.test.ts +0 -51
- package/src/hierarchy.test.ts +0 -228
- package/src/highlights.test.ts +0 -70
- package/src/impgRender.test.tsx +0 -68
- package/src/largeTree.test.ts +0 -67
- package/src/launchInterProScan.ts +0 -167
- package/src/loadErrorDisplay.test.tsx +0 -102
- package/src/modelFilehandleLoaders.test.ts +0 -137
- package/src/modelReset.test.ts +0 -120
- package/src/neighborJoining.test.ts +0 -164
- package/src/parseAsn1.test.ts +0 -36
- package/src/propertyConservation.test.ts +0 -36
- package/src/renderColumnTracksSvg.test.tsx +0 -108
- package/src/renderDomainsSvg.test.tsx +0 -118
- package/src/renderHighlightsSvg.test.tsx +0 -85
- package/src/renderMinimapSvg.test.tsx +0 -62
- package/src/renderRasterSvg.test.tsx +0 -144
- package/src/renderSequenceLogoSvg.test.tsx +0 -165
- package/src/renderToSvg.test.tsx +0 -65
- package/src/reset.test.ts +0 -61
- package/src/rowCoordinateCalculations.test.ts +0 -211
- package/src/rowSpace.test.ts +0 -46
- package/src/segmentOverlay.test.ts +0 -70
- package/src/seqPosToGlobalCol.test.ts +0 -59
- package/src/sequenceLogo.test.ts +0 -88
- package/src/setFontSize.test.ts +0 -33
- package/src/stripDefault.test.ts +0 -48
- package/src/stripDefaultSnapshot.test.ts +0 -87
- package/src/treeMetadata.test.ts +0 -39
- package/src/useCanvasAutorun.test.tsx +0 -98
- package/src/useColorContrast.ts +0 -18
- package/src/viewChrome.test.tsx +0 -134
- package/src/wheelZoom.test.ts +0 -46
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
hoverColor,
|
|
4
|
-
multiRowHoverColor,
|
|
5
|
-
referenceColor,
|
|
6
|
-
} from '../overlayColors.ts'
|
|
7
|
-
import { renderHighlights } from './renderHighlights.ts'
|
|
1
|
+
import { clickColor, hoverColor, multiRowHoverColor } from '../overlayColors.ts'
|
|
2
|
+
import { renderPersistentHighlights } from './renderHighlights.ts'
|
|
8
3
|
|
|
9
4
|
import type { MsaViewModel } from '../../model.ts'
|
|
10
5
|
import type { Theme } from '@mui/material'
|
|
11
6
|
|
|
12
|
-
// the persistent highlightColumns overlay: a stronger fill plus a solid border
|
|
13
|
-
// so a domain/motif band reads clearly over the colored alignment cells (the
|
|
14
|
-
// faint hover-style wash alone is invisible against clustalx coloring)
|
|
15
|
-
const highlightColumnsFill = 'rgba(255,140,0,0.28)'
|
|
16
|
-
const highlightColumnsBorder = 'rgba(210,90,0,0.95)'
|
|
17
|
-
|
|
18
7
|
export function renderMouseover({
|
|
19
8
|
ctx,
|
|
20
9
|
model,
|
|
@@ -36,9 +25,7 @@ export function renderMouseover({
|
|
|
36
25
|
mouseClickRow,
|
|
37
26
|
mouseClickCol,
|
|
38
27
|
highResScaleFactor,
|
|
39
|
-
referenceRowIndex,
|
|
40
28
|
hoveredRowIndices,
|
|
41
|
-
highlightedColumnRuns,
|
|
42
29
|
} = model
|
|
43
30
|
ctx.resetTransform()
|
|
44
31
|
ctx.clearRect(0, 0, width * highResScaleFactor, height * highResScaleFactor)
|
|
@@ -53,30 +40,7 @@ export function renderMouseover({
|
|
|
53
40
|
ctx.fillRect(index * colWidth + scrollX, 0, colWidth, height)
|
|
54
41
|
}
|
|
55
42
|
|
|
56
|
-
|
|
57
|
-
// tree node
|
|
58
|
-
if (referenceRowIndex !== undefined) {
|
|
59
|
-
ctx.fillStyle = referenceColor
|
|
60
|
-
rowBand(referenceRowIndex)
|
|
61
|
-
}
|
|
62
|
-
ctx.fillStyle = multiRowHoverColor
|
|
63
|
-
for (const rowIndex of hoveredRowIndices) {
|
|
64
|
-
rowBand(rowIndex)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// each contiguous run of highlighted columns draws as one filled, bordered
|
|
68
|
-
// band, so a domain/motif highlight stays visible over the alignment
|
|
69
|
-
ctx.lineWidth = 2
|
|
70
|
-
for (const { start, end } of highlightedColumnRuns) {
|
|
71
|
-
const x = start * colWidth + scrollX
|
|
72
|
-
const w = (end - start + 1) * colWidth
|
|
73
|
-
ctx.fillStyle = highlightColumnsFill
|
|
74
|
-
ctx.fillRect(x, 0, w, height)
|
|
75
|
-
ctx.strokeStyle = highlightColumnsBorder
|
|
76
|
-
ctx.strokeRect(x, 0, w, height)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
renderHighlights({
|
|
43
|
+
renderPersistentHighlights({
|
|
80
44
|
ctx,
|
|
81
45
|
model,
|
|
82
46
|
theme,
|
|
@@ -86,6 +50,12 @@ export function renderMouseover({
|
|
|
86
50
|
height,
|
|
87
51
|
})
|
|
88
52
|
|
|
53
|
+
// every tip under a hovered tree node stays lit
|
|
54
|
+
ctx.fillStyle = multiRowHoverColor
|
|
55
|
+
for (const rowIndex of hoveredRowIndices) {
|
|
56
|
+
rowBand(rowIndex)
|
|
57
|
+
}
|
|
58
|
+
|
|
89
59
|
ctx.fillStyle = hoverColor
|
|
90
60
|
if (mouseCol !== undefined) {
|
|
91
61
|
colBand(mouseCol)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
+
import { useTheme } from '@mui/material'
|
|
3
4
|
import { observer } from 'mobx-react'
|
|
4
5
|
|
|
5
6
|
import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
|
|
6
|
-
import { useColorContrast } from '../../useColorContrast.ts'
|
|
7
7
|
import { TrackResizeHandle } from '../ResizeHandles.tsx'
|
|
8
8
|
import { drawTrackBlock } from './drawTracks.ts'
|
|
9
9
|
|
|
@@ -19,11 +19,9 @@ const TrackBlock = observer(function ({
|
|
|
19
19
|
track: BasicTrack
|
|
20
20
|
offsetX: number
|
|
21
21
|
}) {
|
|
22
|
-
const { blockSize,
|
|
23
|
-
const { height
|
|
24
|
-
const
|
|
25
|
-
customColorScheme ?? colorScheme,
|
|
26
|
-
)
|
|
22
|
+
const { blockSize, highResScaleFactor } = model
|
|
23
|
+
const { height } = track.model
|
|
24
|
+
const theme = useTheme()
|
|
27
25
|
const canvasWidth = blockSize * highResScaleFactor
|
|
28
26
|
const canvasHeight = height * highResScaleFactor
|
|
29
27
|
|
|
@@ -31,11 +29,11 @@ const TrackBlock = observer(function ({
|
|
|
31
29
|
draw: ctx => {
|
|
32
30
|
ctx.resetTransform()
|
|
33
31
|
ctx.clearRect(0, 0, canvasWidth, canvasHeight)
|
|
34
|
-
drawTrackBlock({ model, ctx, track, offsetX, theme
|
|
32
|
+
drawTrackBlock({ model, ctx, track, offsetX, theme })
|
|
35
33
|
},
|
|
36
34
|
width: canvasWidth,
|
|
37
35
|
height: canvasHeight,
|
|
38
|
-
deps: [model, track, offsetX, theme
|
|
36
|
+
deps: [model, track, offsetX, theme],
|
|
39
37
|
})
|
|
40
38
|
|
|
41
39
|
return (
|
|
@@ -72,11 +70,10 @@ const TrackBlocks = observer(function ({
|
|
|
72
70
|
// last columns appear in the tracks but not in the alignment, and the hovered
|
|
73
71
|
// -column indicator (sized to the canvas) stops short of the track it crosses
|
|
74
72
|
const { blocksX, msaCanvasWidth, scrollX } = model
|
|
75
|
-
const { kind, height, data } = track.model
|
|
73
|
+
const { kind, height, data, arcs } = track.model
|
|
76
74
|
|
|
77
|
-
// a
|
|
78
|
-
|
|
79
|
-
if (kind === 'text' && !data) {
|
|
75
|
+
// a track with nothing to draw would still take up its vertical space
|
|
76
|
+
if ((kind === 'text' && !data) || (kind === 'arc' && !arcs?.length)) {
|
|
80
77
|
return null
|
|
81
78
|
}
|
|
82
79
|
|
|
@@ -103,7 +100,7 @@ const TrackBlocks = observer(function ({
|
|
|
103
100
|
<TrackBlock key={bx} model={model} track={track} offsetX={bx} />
|
|
104
101
|
))}
|
|
105
102
|
</div>
|
|
106
|
-
<TrackResizeHandle model={model}
|
|
103
|
+
<TrackResizeHandle model={model} track={track} />
|
|
107
104
|
</div>
|
|
108
105
|
)
|
|
109
106
|
})
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { columnLogoStack } from '../../sequenceLogo.ts'
|
|
2
2
|
import { setFontSize } from '../../setFontSize.ts'
|
|
3
|
+
import { contrastTextFn } from '../../util.ts'
|
|
3
4
|
import { visibleColRange } from '../msa/visibleColRange.ts'
|
|
4
5
|
|
|
5
6
|
import type { ColumnCounts } from '../../columnCounts.ts'
|
|
6
7
|
import type { MsaViewModel } from '../../model.ts'
|
|
7
|
-
import type { BasicTrack } from '../../types.ts'
|
|
8
|
+
import type { Arc, BasicTrack } from '../../types.ts'
|
|
8
9
|
import type { RenderCtx } from '../renderCtx.ts'
|
|
9
10
|
import type { Theme } from '@mui/material'
|
|
10
11
|
|
|
@@ -52,13 +53,65 @@ export function barTrackValues(model: MsaViewModel, trackId: string) {
|
|
|
52
53
|
: model.conservation
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
// An arc's apex scales with how far it reaches, so a short helix stays shallow
|
|
57
|
+
// and a long-range pair sweeps the full track height. The scale is sqrt rather
|
|
58
|
+
// than linear because a contact map's spans are heavily skewed towards short
|
|
59
|
+
// ones, which linear scaling flattens into a smear along the baseline.
|
|
60
|
+
export function drawArcs({
|
|
61
|
+
ctx,
|
|
62
|
+
arcs,
|
|
63
|
+
color,
|
|
64
|
+
colWidth,
|
|
65
|
+
trackHeight,
|
|
66
|
+
offsetX,
|
|
67
|
+
blockSize,
|
|
68
|
+
}: {
|
|
69
|
+
ctx: RenderCtx
|
|
70
|
+
arcs: Arc[] | undefined
|
|
71
|
+
color: string
|
|
72
|
+
colWidth: number
|
|
73
|
+
trackHeight: number
|
|
74
|
+
offsetX: number
|
|
75
|
+
blockSize: number
|
|
76
|
+
}) {
|
|
77
|
+
if (!arcs?.length) {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
const { xStart, xEnd } = visibleColRange({
|
|
81
|
+
offsetX,
|
|
82
|
+
blockWidth: blockSize,
|
|
83
|
+
colWidth,
|
|
84
|
+
})
|
|
85
|
+
let maxSpan = 1
|
|
86
|
+
for (const arc of arcs) {
|
|
87
|
+
maxSpan = Math.max(maxSpan, arc.end - arc.start)
|
|
88
|
+
}
|
|
89
|
+
// a full-height apex would put the stroke half outside the track
|
|
90
|
+
const apexRoom = trackHeight - 1
|
|
91
|
+
ctx.lineWidth = 1
|
|
92
|
+
for (const arc of arcs) {
|
|
93
|
+
// an arc that spans the block draws even though neither foot is inside it
|
|
94
|
+
if (arc.end < xStart || arc.start > xEnd) {
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
const x1 = (arc.start + 0.5) * colWidth
|
|
98
|
+
const x2 = (arc.end + 0.5) * colWidth
|
|
99
|
+
const apex = apexRoom * Math.sqrt((arc.end - arc.start) / maxSpan)
|
|
100
|
+
ctx.strokeStyle = arc.color ?? color
|
|
101
|
+
ctx.beginPath()
|
|
102
|
+
ctx.moveTo(x1, trackHeight)
|
|
103
|
+
ctx.quadraticCurveTo((x1 + x2) / 2, trackHeight - 2 * apex, x2, trackHeight)
|
|
104
|
+
ctx.stroke()
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
55
108
|
export function drawTextTrackContent({
|
|
56
109
|
ctx,
|
|
57
110
|
data,
|
|
58
111
|
colorScheme,
|
|
59
|
-
|
|
112
|
+
contrastText,
|
|
60
113
|
bgColor,
|
|
61
|
-
|
|
114
|
+
drawLetters,
|
|
62
115
|
colWidth,
|
|
63
116
|
rowHeight,
|
|
64
117
|
offsetX,
|
|
@@ -67,9 +120,9 @@ export function drawTextTrackContent({
|
|
|
67
120
|
ctx: RenderCtx
|
|
68
121
|
data: string | undefined
|
|
69
122
|
colorScheme: Record<string, string>
|
|
70
|
-
|
|
123
|
+
contrastText: (color: string | undefined) => string
|
|
71
124
|
bgColor: boolean
|
|
72
|
-
|
|
125
|
+
drawLetters: boolean
|
|
73
126
|
colWidth: number
|
|
74
127
|
rowHeight: number
|
|
75
128
|
offsetX: number
|
|
@@ -81,7 +134,6 @@ export function drawTextTrackContent({
|
|
|
81
134
|
colWidth,
|
|
82
135
|
})
|
|
83
136
|
const str = data?.slice(xStart, xEnd)
|
|
84
|
-
const drawLetters = rowHeight >= 10 && colWidth >= rowHeight / 2
|
|
85
137
|
|
|
86
138
|
for (let i = 0; str && i < str.length; i++) {
|
|
87
139
|
const letter = str[i]!
|
|
@@ -93,11 +145,73 @@ export function drawTextTrackContent({
|
|
|
93
145
|
ctx.fillRect(x, 0, colWidth, rowHeight)
|
|
94
146
|
}
|
|
95
147
|
if (drawLetters) {
|
|
96
|
-
// a letter on a colored tile needs
|
|
97
|
-
// sits on the plain background and takes the theme's text color
|
|
98
|
-
ctx.fillStyle = fill
|
|
99
|
-
ctx.fillText(letter, x + colWidth / 2, rowHeight / 2
|
|
148
|
+
// a letter on a colored tile needs that tile's contrast color; otherwise
|
|
149
|
+
// it sits on the plain background and takes the theme's text color
|
|
150
|
+
ctx.fillStyle = contrastText(fill)
|
|
151
|
+
ctx.fillText(letter, x + colWidth / 2, rowHeight / 2)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Column numbers every `step` columns, with a tick under each. The step is the
|
|
157
|
+
// smallest 1/2/5 x 10^n that leaves room for a label, so a zoom changes how many
|
|
158
|
+
// numbers appear rather than crowding them.
|
|
159
|
+
export function rulerStep(colWidth: number, minPixels = 55) {
|
|
160
|
+
let step = 1
|
|
161
|
+
const mantissas = [1, 2, 5]
|
|
162
|
+
for (let i = 0; step * colWidth < minPixels; i++) {
|
|
163
|
+
step = mantissas[i % 3]! * 10 ** Math.floor(i / 3)
|
|
164
|
+
}
|
|
165
|
+
return step
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const RULER_FONT_SIZE = 10
|
|
169
|
+
const TICK_HEIGHT = 4
|
|
170
|
+
|
|
171
|
+
export function drawColumnRuler({
|
|
172
|
+
ctx,
|
|
173
|
+
numColumns,
|
|
174
|
+
label,
|
|
175
|
+
textColor,
|
|
176
|
+
colWidth,
|
|
177
|
+
trackHeight,
|
|
178
|
+
offsetX,
|
|
179
|
+
blockSize,
|
|
180
|
+
}: {
|
|
181
|
+
ctx: RenderCtx
|
|
182
|
+
numColumns: number
|
|
183
|
+
label: (col: number) => string | undefined
|
|
184
|
+
textColor: string
|
|
185
|
+
colWidth: number
|
|
186
|
+
trackHeight: number
|
|
187
|
+
offsetX: number
|
|
188
|
+
blockSize: number
|
|
189
|
+
}) {
|
|
190
|
+
const { xStart, xEnd } = visibleColRange({
|
|
191
|
+
offsetX,
|
|
192
|
+
blockWidth: blockSize,
|
|
193
|
+
colWidth,
|
|
194
|
+
})
|
|
195
|
+
const step = rulerStep(colWidth)
|
|
196
|
+
const end = Math.min(xEnd, numColumns)
|
|
197
|
+
setFontSize(ctx, RULER_FONT_SIZE)
|
|
198
|
+
ctx.textAlign = 'center'
|
|
199
|
+
ctx.textBaseline = 'alphabetic'
|
|
200
|
+
ctx.fillStyle = textColor
|
|
201
|
+
ctx.strokeStyle = textColor
|
|
202
|
+
ctx.lineWidth = 1
|
|
203
|
+
|
|
204
|
+
for (let col = Math.ceil(xStart / step) * step; col < end; col += step) {
|
|
205
|
+
const text = label(col)
|
|
206
|
+
if (text === undefined) {
|
|
207
|
+
continue
|
|
100
208
|
}
|
|
209
|
+
const x = (col + 0.5) * colWidth
|
|
210
|
+
ctx.fillText(text, x, trackHeight - TICK_HEIGHT - 2)
|
|
211
|
+
ctx.beginPath()
|
|
212
|
+
ctx.moveTo(x, trackHeight - TICK_HEIGHT)
|
|
213
|
+
ctx.lineTo(x, trackHeight)
|
|
214
|
+
ctx.stroke()
|
|
101
215
|
}
|
|
102
216
|
}
|
|
103
217
|
|
|
@@ -197,7 +311,6 @@ export function drawTrackBlock({
|
|
|
197
311
|
offsetX,
|
|
198
312
|
offsetY = 0,
|
|
199
313
|
theme,
|
|
200
|
-
contrastScheme,
|
|
201
314
|
blockSizeXOverride,
|
|
202
315
|
highResScaleFactorOverride,
|
|
203
316
|
}: {
|
|
@@ -207,7 +320,6 @@ export function drawTrackBlock({
|
|
|
207
320
|
offsetX: number
|
|
208
321
|
offsetY?: number
|
|
209
322
|
theme: Theme
|
|
210
|
-
contrastScheme: Record<string, string>
|
|
211
323
|
blockSizeXOverride?: number
|
|
212
324
|
highResScaleFactorOverride?: number
|
|
213
325
|
}) {
|
|
@@ -219,6 +331,7 @@ export function drawTrackBlock({
|
|
|
219
331
|
colorScheme: modelColorScheme,
|
|
220
332
|
fontSize,
|
|
221
333
|
rowHeight,
|
|
334
|
+
showMsaLetters,
|
|
222
335
|
alphabetMaxBits,
|
|
223
336
|
highResScaleFactor,
|
|
224
337
|
} = model
|
|
@@ -227,6 +340,8 @@ export function drawTrackBlock({
|
|
|
227
340
|
kind,
|
|
228
341
|
height: trackHeight,
|
|
229
342
|
barColor,
|
|
343
|
+
arcColor,
|
|
344
|
+
arcs,
|
|
230
345
|
customColorScheme,
|
|
231
346
|
data,
|
|
232
347
|
} = track.model
|
|
@@ -251,6 +366,18 @@ export function drawTrackBlock({
|
|
|
251
366
|
})
|
|
252
367
|
break
|
|
253
368
|
}
|
|
369
|
+
case 'arc': {
|
|
370
|
+
drawArcs({
|
|
371
|
+
ctx,
|
|
372
|
+
arcs,
|
|
373
|
+
color: arcColor ?? theme.palette.text.primary,
|
|
374
|
+
colWidth,
|
|
375
|
+
trackHeight,
|
|
376
|
+
offsetX,
|
|
377
|
+
blockSize: blockSizeX,
|
|
378
|
+
})
|
|
379
|
+
break
|
|
380
|
+
}
|
|
254
381
|
case 'logo': {
|
|
255
382
|
drawSequenceLogo({
|
|
256
383
|
ctx,
|
|
@@ -265,16 +392,40 @@ export function drawTrackBlock({
|
|
|
265
392
|
})
|
|
266
393
|
break
|
|
267
394
|
}
|
|
395
|
+
case 'ruler': {
|
|
396
|
+
const { relativeTo, numColumns } = model
|
|
397
|
+
drawColumnRuler({
|
|
398
|
+
ctx,
|
|
399
|
+
numColumns,
|
|
400
|
+
// numbered by the reference row's own residues when the alignment is
|
|
401
|
+
// drawn relative to one, which is what the rest of the view counts in
|
|
402
|
+
label: relativeTo
|
|
403
|
+
? col => {
|
|
404
|
+
const pos = model.visibleColToSeqPosOneBased(relativeTo, col)
|
|
405
|
+
return pos === undefined ? undefined : `${pos}`
|
|
406
|
+
}
|
|
407
|
+
: col => `${col + 1}`,
|
|
408
|
+
textColor,
|
|
409
|
+
colWidth,
|
|
410
|
+
trackHeight,
|
|
411
|
+
offsetX,
|
|
412
|
+
blockSize: blockSizeX,
|
|
413
|
+
})
|
|
414
|
+
break
|
|
415
|
+
}
|
|
268
416
|
case 'text': {
|
|
269
417
|
ctx.textAlign = 'center'
|
|
418
|
+
ctx.textBaseline = 'middle'
|
|
270
419
|
setFontSize(ctx, fontSize)
|
|
271
420
|
drawTextTrackContent({
|
|
272
421
|
ctx,
|
|
273
422
|
data,
|
|
274
423
|
colorScheme: customColorScheme ?? modelColorScheme,
|
|
275
|
-
|
|
424
|
+
contrastText: contrastTextFn(theme),
|
|
276
425
|
bgColor,
|
|
277
|
-
|
|
426
|
+
// a text track is one alignment row tall, so it shows letters exactly
|
|
427
|
+
// when the alignment does
|
|
428
|
+
drawLetters: showMsaLetters,
|
|
278
429
|
colWidth,
|
|
279
430
|
rowHeight,
|
|
280
431
|
offsetX,
|
|
@@ -295,7 +446,6 @@ export function renderAllTracks({
|
|
|
295
446
|
model,
|
|
296
447
|
ctx,
|
|
297
448
|
offsetX,
|
|
298
|
-
contrastScheme,
|
|
299
449
|
theme,
|
|
300
450
|
blockSizeXOverride,
|
|
301
451
|
highResScaleFactorOverride,
|
|
@@ -303,7 +453,6 @@ export function renderAllTracks({
|
|
|
303
453
|
model: MsaViewModel
|
|
304
454
|
ctx: RenderCtx
|
|
305
455
|
offsetX: number
|
|
306
|
-
contrastScheme: Record<string, string>
|
|
307
456
|
theme: Theme
|
|
308
457
|
blockSizeXOverride?: number
|
|
309
458
|
highResScaleFactorOverride?: number
|
|
@@ -317,7 +466,6 @@ export function renderAllTracks({
|
|
|
317
466
|
offsetX,
|
|
318
467
|
offsetY: currentY,
|
|
319
468
|
theme,
|
|
320
|
-
contrastScheme,
|
|
321
469
|
blockSizeXOverride,
|
|
322
470
|
highResScaleFactorOverride,
|
|
323
471
|
})
|
|
@@ -7,13 +7,12 @@ import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
|
|
|
7
7
|
import { useWheelScroll } from '../../useWheelScroll.ts'
|
|
8
8
|
import { referenceColor, treeHoverColor } from '../overlayColors.ts'
|
|
9
9
|
import TreeCanvasBlock from './TreeCanvasBlock.tsx'
|
|
10
|
-
import { padding } from './renderTreeCanvas.ts'
|
|
11
10
|
|
|
12
11
|
import type { MsaViewModel } from '../../model.ts'
|
|
13
12
|
|
|
14
13
|
const TreeCanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
15
14
|
const ref = useRef<HTMLDivElement>(null)
|
|
16
|
-
const {
|
|
15
|
+
const { height, blocksY, treeAreaWidth, scrollY } = model
|
|
17
16
|
const onScrollY = useCallback(
|
|
18
17
|
(d: number) => {
|
|
19
18
|
model.doScrollY(d)
|
|
@@ -77,7 +76,7 @@ const TreeCanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
77
76
|
style={{
|
|
78
77
|
height,
|
|
79
78
|
position: 'relative',
|
|
80
|
-
width:
|
|
79
|
+
width: treeAreaWidth,
|
|
81
80
|
}}
|
|
82
81
|
>
|
|
83
82
|
{/* one transform for the whole block set, matching MSACanvas: the tree
|
|
@@ -8,7 +8,7 @@ import { createPortal } from 'react-dom'
|
|
|
8
8
|
import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
|
|
9
9
|
import TreeBranchMenu from './TreeBranchMenu.tsx'
|
|
10
10
|
import TreeNodeMenu from './TreeNodeMenu.tsx'
|
|
11
|
-
import {
|
|
11
|
+
import { renderTreeCanvas } from './renderTreeCanvas.ts'
|
|
12
12
|
import { useTreeHover } from './useTreeHover.ts'
|
|
13
13
|
|
|
14
14
|
import type { MsaViewModel } from '../../model.ts'
|
|
@@ -58,7 +58,9 @@ const TreeCanvasBlock = observer(function ({
|
|
|
58
58
|
useTreeHover({ model, offsetY })
|
|
59
59
|
|
|
60
60
|
const { treeAreaWidth, blockSize, highResScaleFactor } = model
|
|
61
|
-
|
|
61
|
+
// TreePanel clips to treeAreaWidth, so anything wider is backing store the
|
|
62
|
+
// viewer never sees -- it was 600px of it per block, at dpr squared
|
|
63
|
+
const width = treeAreaWidth
|
|
62
64
|
const height = blockSize
|
|
63
65
|
|
|
64
66
|
const canvasWidth = width * highResScaleFactor
|
|
@@ -1,16 +1,51 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
+
import { useTheme } from '@mui/material'
|
|
3
4
|
import { observer } from 'mobx-react'
|
|
4
5
|
|
|
6
|
+
import { scaleBarLength } from './scaleBar.ts'
|
|
7
|
+
|
|
5
8
|
import type { MsaViewModel } from '../../model.ts'
|
|
6
9
|
|
|
10
|
+
const BAR_HEIGHT = 22
|
|
11
|
+
|
|
7
12
|
// The gutter above the tree panel. It spans the resize handle too, so whatever
|
|
8
13
|
// follows it in the top row starts exactly where the alignment canvas does --
|
|
9
|
-
// MainArea has TreePanel + VerticalResizeHandle before the alignment.
|
|
14
|
+
// MainArea has TreePanel + VerticalResizeHandle before the alignment. A
|
|
15
|
+
// phylogram fills it with a scale bar: without one the branch lengths are drawn
|
|
16
|
+
// to a scale nothing states.
|
|
10
17
|
const TreeRuler = observer(({ model }: { model: MsaViewModel }) => {
|
|
11
|
-
const { treeAreaWidth, resizeHandleWidth } =
|
|
18
|
+
const { treeAreaWidth, resizeHandleWidth, marginLeft, pxPerBranchLength } =
|
|
19
|
+
model
|
|
20
|
+
const theme = useTheme()
|
|
21
|
+
const width = treeAreaWidth + resizeHandleWidth
|
|
22
|
+
const bar = scaleBarLength(pxPerBranchLength, treeAreaWidth - marginLeft * 2)
|
|
23
|
+
|
|
12
24
|
return (
|
|
13
|
-
<div style={{ flexShrink: 0, width
|
|
25
|
+
<div style={{ flexShrink: 0, width }}>
|
|
26
|
+
{bar ? (
|
|
27
|
+
<svg
|
|
28
|
+
width={width}
|
|
29
|
+
height={BAR_HEIGHT}
|
|
30
|
+
style={{ display: 'block' }}
|
|
31
|
+
color={theme.palette.text.primary}
|
|
32
|
+
>
|
|
33
|
+
<text
|
|
34
|
+
x={marginLeft}
|
|
35
|
+
y={BAR_HEIGHT - 9}
|
|
36
|
+
fontSize={10}
|
|
37
|
+
fill="currentColor"
|
|
38
|
+
>
|
|
39
|
+
{bar.label}
|
|
40
|
+
</text>
|
|
41
|
+
<path
|
|
42
|
+
d={`M${marginLeft} ${BAR_HEIGHT - 7} v6 h${bar.px} v-6`}
|
|
43
|
+
fill="none"
|
|
44
|
+
stroke="currentColor"
|
|
45
|
+
/>
|
|
46
|
+
</svg>
|
|
47
|
+
) : null}
|
|
48
|
+
</div>
|
|
14
49
|
)
|
|
15
50
|
})
|
|
16
51
|
|