react-msaview 9.0.0 → 9.1.1
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 +19 -10
- package/bundle/index.js +111 -115
- package/bundle/index.js.map +4 -4
- package/dist/blosum62.d.ts +3 -0
- package/dist/blosum62.js +43 -0
- package/dist/blosum62.js.map +1 -0
- package/dist/colorSchemes.js +2 -3
- package/dist/colorSchemes.js.map +1 -1
- package/dist/columnCounts.d.ts +0 -1
- package/dist/columnCounts.js +1 -1
- package/dist/columnCounts.js.map +1 -1
- package/dist/components/Loading.d.ts +3 -2
- package/dist/components/Loading.js +7 -7
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAView.d.ts +3 -2
- package/dist/components/MSAView.js +7 -4
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +1941 -4
- package/dist/components/MSAViewer.js +144 -201
- package/dist/components/MSAViewer.js.map +1 -1
- package/dist/components/MetadataTable.d.ts +4 -1
- package/dist/components/MetadataTable.js +55 -16
- package/dist/components/MetadataTable.js.map +1 -1
- package/dist/components/ResizeHandles.js +3 -2
- package/dist/components/ResizeHandles.js.map +1 -1
- package/dist/components/Track.js +8 -1
- package/dist/components/Track.js.map +1 -1
- package/dist/components/controls.d.ts +11 -0
- package/dist/components/controls.js +49 -0
- package/dist/components/controls.js.map +1 -0
- package/dist/components/dialogs/AnnotationFileDialog.js +12 -18
- package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
- package/dist/components/dialogs/ExportSVGDialog.js +5 -7
- package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +2 -2
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/{header/TreeSettingsMenu.d.ts → dialogs/GapThresholdDialog.d.ts} +3 -2
- package/dist/components/dialogs/GapThresholdDialog.js +15 -0
- package/dist/components/dialogs/GapThresholdDialog.js.map +1 -0
- package/dist/components/{header → dialogs}/GappynessSlider.d.ts +1 -1
- package/dist/components/{header → dialogs}/GappynessSlider.js +3 -10
- package/dist/components/dialogs/GappynessSlider.js.map +1 -0
- package/dist/components/{header → dialogs}/GoToBox.d.ts +4 -2
- package/dist/components/dialogs/GoToBox.js +48 -0
- package/dist/components/dialogs/GoToBox.js.map +1 -0
- package/dist/components/dialogs/GoToDialog.d.ts +6 -0
- package/dist/components/dialogs/GoToDialog.js +10 -0
- package/dist/components/dialogs/GoToDialog.js.map +1 -0
- package/dist/components/dialogs/HelpDialog.d.ts +6 -0
- package/dist/components/dialogs/HelpDialog.js +30 -0
- package/dist/components/dialogs/HelpDialog.js.map +1 -0
- package/dist/components/dialogs/MetadataDialog.js +11 -3
- package/dist/components/dialogs/MetadataDialog.js.map +1 -1
- package/dist/components/dialogs/goTo.d.ts +5 -0
- package/dist/components/{header → dialogs}/goTo.js +1 -1
- package/dist/components/dialogs/goTo.js.map +1 -0
- package/dist/components/dialogs/metadataLabels.d.ts +4 -0
- package/dist/components/dialogs/metadataLabels.js +52 -0
- package/dist/components/dialogs/metadataLabels.js.map +1 -0
- package/dist/components/dialogs/metadataRecord.d.ts +1 -0
- package/dist/components/dialogs/metadataRecord.js +38 -0
- package/dist/components/dialogs/metadataRecord.js.map +1 -0
- package/dist/components/header/Header.d.ts +6 -2
- package/dist/components/header/Header.js +30 -34
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/HiddenColumnsChip.d.ts +6 -0
- package/dist/components/header/HiddenColumnsChip.js +15 -0
- package/dist/components/header/HiddenColumnsChip.js.map +1 -0
- package/dist/components/header/{FileMenu.d.ts → MenuBar.d.ts} +3 -3
- package/dist/components/header/MenuBar.js +33 -0
- package/dist/components/header/MenuBar.js.map +1 -0
- package/dist/components/header/ScrollZoomCheckbox.d.ts +7 -0
- package/dist/components/header/ScrollZoomCheckbox.js +19 -0
- package/dist/components/header/ScrollZoomCheckbox.js.map +1 -0
- package/dist/components/header/ZoomControls.js +12 -48
- package/dist/components/header/ZoomControls.js.map +1 -1
- package/dist/components/header/colorSchemeMenuItems.d.ts +4 -0
- package/dist/components/header/colorSchemeMenuItems.js +106 -0
- package/dist/components/header/colorSchemeMenuItems.js.map +1 -0
- package/dist/components/header/getAnnotationsMenu.d.ts +3 -30
- package/dist/components/header/getAnnotationsMenu.js +48 -54
- package/dist/components/header/getAnnotationsMenu.js.map +1 -1
- package/dist/components/header/menuBarItems.d.ts +11 -0
- package/dist/components/header/menuBarItems.js +277 -0
- package/dist/components/header/menuBarItems.js.map +1 -0
- package/dist/components/import/ImportForm.js +12 -6
- package/dist/components/import/ImportForm.js.map +1 -1
- package/dist/components/import/data/seq2.d.ts +0 -1
- package/dist/components/import/data/seq2.js +0 -17
- package/dist/components/import/data/seq2.js.map +1 -1
- package/dist/components/minimap/Minimap.js +7 -3
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/minimap/MinimapSVG.js +2 -1
- package/dist/components/minimap/MinimapSVG.js.map +1 -1
- package/dist/components/minimap/minimapLayout.d.ts +12 -0
- package/dist/components/minimap/minimapLayout.js +20 -0
- package/dist/components/minimap/minimapLayout.js.map +1 -1
- package/dist/components/msa/MSACanvas.js +28 -10
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +17 -16
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/MSAMouseoverCanvas.js +2 -2
- package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
- package/dist/components/msa/WheelZoomHint.d.ts +7 -0
- package/dist/components/msa/WheelZoomHint.js +38 -0
- package/dist/components/msa/WheelZoomHint.js.map +1 -0
- package/dist/components/msa/drawFeatureSpans.d.ts +50 -11
- package/dist/components/msa/drawFeatureSpans.js +99 -56
- package/dist/components/msa/drawFeatureSpans.js.map +1 -1
- package/dist/components/msa/msaKeys.js +3 -0
- package/dist/components/msa/msaKeys.js.map +1 -1
- package/dist/components/msa/msaRaster.d.ts +14 -2
- package/dist/components/msa/msaRaster.js +20 -10
- package/dist/components/msa/msaRaster.js.map +1 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +9 -0
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +39 -8
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderHighlights.d.ts +2 -5
- package/dist/components/msa/renderHighlights.js +5 -6
- package/dist/components/msa/renderHighlights.js.map +1 -1
- package/dist/components/msa/renderMSABlock.d.ts +6 -1
- package/dist/components/msa/renderMSABlock.js +44 -17
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +2 -1
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/msa/tileColor.js +7 -0
- package/dist/components/msa/tileColor.js.map +1 -1
- package/dist/components/renderCtx.d.ts +1 -1
- package/dist/components/rowpanels/RowPanelBlock.js +2 -2
- package/dist/components/rowpanels/RowPanelBlock.js.map +1 -1
- package/dist/components/rowpanels/renderFeaturePanel.d.ts +5 -1
- package/dist/components/rowpanels/renderFeaturePanel.js +12 -5
- package/dist/components/rowpanels/renderFeaturePanel.js.map +1 -1
- package/dist/components/settings/MenuItemsForm.d.ts +10 -0
- package/dist/components/settings/MenuItemsForm.js +80 -0
- package/dist/components/settings/MenuItemsForm.js.map +1 -0
- package/dist/components/settings/SettingsPanel.d.ts +12 -0
- package/dist/components/settings/SettingsPanel.js +92 -0
- package/dist/components/settings/SettingsPanel.js.map +1 -0
- package/dist/components/{header/ZoomMenu.d.ts → settings/TrackList.d.ts} +2 -2
- package/dist/components/settings/TrackList.js +55 -0
- package/dist/components/settings/TrackList.js.map +1 -0
- package/dist/components/tracks/TrackBlocks.js +2 -2
- package/dist/components/tracks/TrackBlocks.js.map +1 -1
- package/dist/components/tracks/drawTracks.d.ts +1 -53
- package/dist/components/tracks/drawTracks.js +5 -5
- package/dist/components/tracks/drawTracks.js.map +1 -1
- package/dist/components/tree/CladeLabels.d.ts +1 -1
- package/dist/components/tree/CladeLabels.js +22 -34
- package/dist/components/tree/CladeLabels.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +2 -2
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeMenu.js +4 -6
- package/dist/components/tree/TreeMenu.js.map +1 -1
- package/dist/components/tree/TreeOverview.js +2 -2
- package/dist/components/tree/TreeOverview.js.map +1 -1
- package/dist/components/tree/cladeBrackets.d.ts +17 -10
- package/dist/components/tree/cladeBrackets.js +33 -3
- package/dist/components/tree/cladeBrackets.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.js +2 -8
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/components/tree/renderTreeOverview.js +4 -11
- package/dist/components/tree/renderTreeOverview.js.map +1 -1
- package/dist/constants.d.ts +4 -1
- package/dist/constants.js +7 -1
- package/dist/constants.js.map +1 -1
- package/dist/darkReader.d.ts +7 -0
- package/dist/darkReader.js +42 -0
- package/dist/darkReader.js.map +1 -0
- package/dist/element.js +25 -53
- package/dist/element.js.map +1 -1
- package/dist/exportLayout.d.ts +39 -0
- package/dist/exportLayout.js +78 -0
- package/dist/exportLayout.js.map +1 -0
- package/dist/featurePanels.d.ts +37 -0
- package/dist/featurePanels.js +187 -0
- package/dist/featurePanels.js.map +1 -0
- package/dist/filehandleLoader.d.ts +41 -0
- package/dist/filehandleLoader.js +99 -0
- package/dist/filehandleLoader.js.map +1 -0
- package/dist/headlessRenderEnv.d.ts +12 -39
- package/dist/headlessRenderEnv.js +18 -12
- package/dist/headlessRenderEnv.js.map +1 -1
- package/dist/hierarchy.d.ts +5 -8
- package/dist/hierarchy.js +5 -79
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/measureTextCanvas.d.ts +7 -0
- package/dist/measureTextCanvas.js +8 -6
- package/dist/measureTextCanvas.js.map +1 -1
- package/dist/model/treeModel.d.ts +6 -2
- package/dist/model/treeModel.js +8 -4
- package/dist/model/treeModel.js.map +1 -1
- package/dist/model.d.ts +180 -76
- package/dist/model.js +358 -618
- package/dist/model.js.map +1 -1
- package/dist/neighborJoining.js +1 -40
- package/dist/neighborJoining.js.map +1 -1
- package/dist/renderToSvg.d.ts +2 -7
- package/dist/renderToSvg.js +33 -133
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rerootTree.d.ts +1 -0
- package/dist/rerootTree.js +7 -6
- package/dist/rerootTree.js.map +1 -1
- package/dist/schemeResidueColor.d.ts +12 -0
- package/dist/schemeResidueColor.js +33 -0
- package/dist/schemeResidueColor.js.map +1 -0
- package/dist/similarityColors.d.ts +11 -0
- package/dist/similarityColors.js +90 -0
- package/dist/similarityColors.js.map +1 -0
- package/dist/svgTestUtil.d.ts +74 -32
- package/dist/treeDisplay.d.ts +37 -0
- package/dist/treeDisplay.js +209 -0
- package/dist/treeDisplay.js.map +1 -0
- package/dist/types.d.ts +6 -3
- package/dist/useWheelScroll.d.ts +3 -1
- package/dist/useWheelScroll.js +36 -17
- package/dist/useWheelScroll.js.map +1 -1
- package/dist/util.d.ts +2 -0
- package/dist/util.js +2 -0
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/viewerSnapshot.d.ts +169 -0
- package/dist/viewerSnapshot.js +100 -0
- package/dist/viewerSnapshot.js.map +1 -0
- package/dist/wheelZoom.d.ts +8 -3
- package/dist/wheelZoom.js +44 -32
- package/dist/wheelZoom.js.map +1 -1
- package/dist/zoomSnapshot.d.ts +29 -0
- package/dist/zoomSnapshot.js +47 -0
- package/dist/zoomSnapshot.js.map +1 -0
- package/package.json +11 -11
- package/src/blosum62.ts +45 -0
- package/src/colorSchemes.ts +2 -4
- package/src/columnCounts.ts +1 -1
- package/src/components/Loading.tsx +25 -14
- package/src/components/MSAView.tsx +22 -5
- package/src/components/MSAViewer.tsx +275 -251
- package/src/components/MetadataTable.tsx +83 -26
- package/src/components/ResizeHandles.tsx +9 -2
- package/src/components/Track.tsx +15 -1
- package/src/components/controls.ts +62 -0
- package/src/components/dialogs/AnnotationFileDialog.tsx +16 -21
- package/src/components/dialogs/ExportSVGDialog.tsx +8 -19
- package/src/components/dialogs/FeatureDialog.tsx +2 -5
- package/src/components/dialogs/GapThresholdDialog.tsx +36 -0
- package/src/components/{header → dialogs}/GappynessSlider.tsx +3 -10
- package/src/components/{header → dialogs}/GoToBox.tsx +36 -10
- package/src/components/dialogs/GoToDialog.tsx +30 -0
- package/src/components/dialogs/HelpDialog.tsx +79 -0
- package/src/components/dialogs/MetadataDialog.tsx +32 -4
- package/src/components/{header → dialogs}/goTo.ts +1 -1
- package/src/components/dialogs/metadataLabels.ts +54 -0
- package/src/components/dialogs/metadataRecord.tsx +61 -0
- package/src/components/header/Header.tsx +75 -44
- package/src/components/header/HiddenColumnsChip.tsx +39 -0
- package/src/components/header/MenuBar.tsx +64 -0
- package/src/components/header/ScrollZoomCheckbox.tsx +46 -0
- package/src/components/header/ZoomControls.tsx +23 -74
- package/src/components/header/colorSchemeMenuItems.ts +121 -0
- package/src/components/header/getAnnotationsMenu.ts +52 -56
- package/src/components/header/menuBarItems.ts +357 -0
- package/src/components/import/ImportForm.tsx +19 -7
- package/src/components/import/data/seq2.ts +0 -18
- package/src/components/minimap/Minimap.tsx +11 -3
- package/src/components/minimap/MinimapSVG.tsx +17 -1
- package/src/components/minimap/minimapLayout.ts +27 -0
- package/src/components/msa/MSACanvas.tsx +24 -4
- package/src/components/msa/MSACanvasBlock.tsx +20 -17
- package/src/components/msa/MSAMouseoverCanvas.tsx +2 -2
- package/src/components/msa/WheelZoomHint.tsx +56 -0
- package/src/components/msa/drawFeatureSpans.ts +160 -79
- package/src/components/msa/msaKeys.ts +3 -0
- package/src/components/msa/msaRaster.ts +26 -10
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +62 -24
- package/src/components/msa/renderHighlights.ts +5 -6
- package/src/components/msa/renderMSABlock.ts +51 -15
- package/src/components/msa/renderMSAMouseover.ts +5 -1
- package/src/components/msa/tileColor.ts +7 -0
- package/src/components/renderCtx.ts +0 -1
- package/src/components/rowpanels/RowPanelBlock.tsx +2 -2
- package/src/components/rowpanels/renderFeaturePanel.ts +26 -7
- package/src/components/settings/MenuItemsForm.tsx +225 -0
- package/src/components/settings/SettingsPanel.tsx +156 -0
- package/src/components/settings/TrackList.tsx +119 -0
- package/src/components/tracks/TrackBlocks.tsx +2 -2
- package/src/components/tracks/drawTracks.ts +5 -5
- package/src/components/tree/CladeLabels.tsx +30 -54
- package/src/components/tree/TreeCanvasBlock.tsx +2 -2
- package/src/components/tree/TreeMenu.tsx +4 -6
- package/src/components/tree/TreeOverview.tsx +2 -2
- package/src/components/tree/cladeBrackets.ts +50 -3
- package/src/components/tree/renderTreeCanvas.ts +2 -13
- package/src/components/tree/renderTreeOverview.ts +7 -14
- package/src/constants.ts +9 -1
- package/src/darkReader.ts +57 -0
- package/src/element.ts +37 -64
- package/src/exportLayout.ts +134 -0
- package/src/featurePanels.ts +269 -0
- package/src/filehandleLoader.ts +136 -0
- package/src/headlessRenderEnv.ts +24 -11
- package/src/hierarchy.ts +5 -88
- package/src/index.ts +9 -5
- package/src/measureTextCanvas.ts +8 -6
- package/src/model/treeModel.ts +9 -3
- package/src/model.ts +452 -807
- package/src/neighborJoining.ts +1 -42
- package/src/renderToSvg.tsx +56 -207
- package/src/rerootTree.ts +7 -6
- package/src/schemeResidueColor.ts +37 -0
- package/src/similarityColors.ts +112 -0
- package/src/treeDisplay.ts +278 -0
- package/src/types.ts +6 -4
- package/src/useWheelScroll.ts +43 -22
- package/src/util.ts +3 -0
- package/src/version.ts +1 -1
- package/src/viewerSnapshot.ts +147 -0
- package/src/wheelZoom.ts +55 -33
- package/src/zoomSnapshot.ts +80 -0
- package/dist/components/header/ColorSchemeMenu.d.ts +0 -12
- package/dist/components/header/ColorSchemeMenu.js +0 -37
- package/dist/components/header/ColorSchemeMenu.js.map +0 -1
- package/dist/components/header/FileMenu.js +0 -66
- package/dist/components/header/FileMenu.js.map +0 -1
- package/dist/components/header/GappynessSlider.js.map +0 -1
- package/dist/components/header/GoToBox.js +0 -31
- package/dist/components/header/GoToBox.js.map +0 -1
- package/dist/components/header/MSASettingsMenu.d.ts +0 -6
- package/dist/components/header/MSASettingsMenu.js +0 -11
- package/dist/components/header/MSASettingsMenu.js.map +0 -1
- package/dist/components/header/ScrollZoomIcons.d.ts +0 -6
- package/dist/components/header/ScrollZoomIcons.js +0 -35
- package/dist/components/header/ScrollZoomIcons.js.map +0 -1
- package/dist/components/header/TreeSettingsMenu.js +0 -42
- package/dist/components/header/TreeSettingsMenu.js.map +0 -1
- package/dist/components/header/ZoomMenu.js +0 -37
- package/dist/components/header/ZoomMenu.js.map +0 -1
- package/dist/components/header/goTo.d.ts +0 -20
- package/dist/components/header/goTo.js.map +0 -1
- package/dist/components/header/settingsMenuItems.d.ts +0 -4
- package/dist/components/header/settingsMenuItems.js +0 -127
- package/dist/components/header/settingsMenuItems.js.map +0 -1
- package/src/components/header/ColorSchemeMenu.tsx +0 -56
- package/src/components/header/FileMenu.tsx +0 -81
- package/src/components/header/MSASettingsMenu.tsx +0 -26
- package/src/components/header/ScrollZoomIcons.tsx +0 -52
- package/src/components/header/TreeSettingsMenu.tsx +0 -60
- package/src/components/header/ZoomMenu.tsx +0 -48
- package/src/components/header/settingsMenuItems.ts +0 -157
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import React, { useCallback } from 'react'
|
|
2
2
|
|
|
3
|
-
import { useTheme } from '@mui/material'
|
|
4
3
|
import { observer } from 'mobx-react'
|
|
5
4
|
|
|
5
|
+
import { useCanvasTheme } from '../../darkReader.ts'
|
|
6
6
|
import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
|
|
7
7
|
import DragHandle, { scrollbarThumbFill } from '../DragHandle.tsx'
|
|
8
8
|
import { msaThumbnail } from '../msa/msaRaster.ts'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
MINIMAP_BAR_HEIGHT,
|
|
11
|
+
getMinimapLayout,
|
|
12
|
+
minimapDomainRects,
|
|
13
|
+
} from './minimapLayout.ts'
|
|
10
14
|
|
|
11
15
|
import type { MsaViewModel } from '../../model.ts'
|
|
12
16
|
|
|
13
17
|
const Minimap = observer(function ({ model }: { model: MsaViewModel }) {
|
|
14
18
|
const { minimapHeight, msaCanvasWidth, highResScaleFactor } = model
|
|
15
|
-
const theme =
|
|
19
|
+
const theme = useCanvasTheme()
|
|
16
20
|
const { unit, s, w, polygonHeight, polygonPoints } = getMinimapLayout(model)
|
|
17
21
|
|
|
18
22
|
// the whole alignment, sampled down to the bar
|
|
@@ -41,6 +45,10 @@ const Minimap = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
41
45
|
barHeight,
|
|
42
46
|
)
|
|
43
47
|
}
|
|
48
|
+
for (const r of minimapDomainRects(model, barWidth, barHeight)) {
|
|
49
|
+
ctx.fillStyle = r.fill
|
|
50
|
+
ctx.fillRect(r.x, r.y, r.w, r.h)
|
|
51
|
+
}
|
|
44
52
|
},
|
|
45
53
|
width: barWidth,
|
|
46
54
|
height: barHeight,
|
|
@@ -2,7 +2,11 @@ import React from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { scrollbarThumbFill } from '../DragHandle.tsx'
|
|
4
4
|
import { canvasHref, msaThumbnail } from '../msa/msaRaster.ts'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
MINIMAP_BAR_HEIGHT,
|
|
7
|
+
getMinimapLayout,
|
|
8
|
+
minimapDomainRects,
|
|
9
|
+
} from './minimapLayout.ts'
|
|
6
10
|
|
|
7
11
|
import type { MsaViewModel } from '../../model.ts'
|
|
8
12
|
import type { Theme } from '@mui/material'
|
|
@@ -37,6 +41,18 @@ export default function MinimapSVG({
|
|
|
37
41
|
preserveAspectRatio="none"
|
|
38
42
|
/>
|
|
39
43
|
) : null}
|
|
44
|
+
{minimapDomainRects(model, msaCanvasWidth, MINIMAP_BAR_HEIGHT).map(
|
|
45
|
+
({ x, y, w, h, fill }) => (
|
|
46
|
+
<rect
|
|
47
|
+
key={`${x},${y},${w},${fill}`}
|
|
48
|
+
x={x}
|
|
49
|
+
y={y}
|
|
50
|
+
width={w}
|
|
51
|
+
height={h}
|
|
52
|
+
fill={fill}
|
|
53
|
+
/>
|
|
54
|
+
),
|
|
55
|
+
)}
|
|
40
56
|
<rect
|
|
41
57
|
x={0}
|
|
42
58
|
y={0}
|
|
@@ -19,3 +19,30 @@ export function getMinimapLayout(model: MsaViewModel) {
|
|
|
19
19
|
const polygonPoints = `${s + w},0 ${s},0 0,${polygonHeight} ${msaCanvasWidth},${polygonHeight}`
|
|
20
20
|
return { unit, s, w, polygonHeight, polygonPoints }
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The domain overlay's boxes scaled onto the minimap bar, one per row's band,
|
|
25
|
+
* in the colors the alignment draws them, so the overview shows the domains
|
|
26
|
+
* the alignment does. Empty unless the domains fill the cells.
|
|
27
|
+
*/
|
|
28
|
+
export function minimapDomainRects(
|
|
29
|
+
model: MsaViewModel,
|
|
30
|
+
width: number,
|
|
31
|
+
height: number,
|
|
32
|
+
) {
|
|
33
|
+
const { cellFill, domainBands, featureColors, leaves, numColumns } = model
|
|
34
|
+
if (cellFill !== 'domain' || numColumns <= 0 || leaves.length === 0) {
|
|
35
|
+
return []
|
|
36
|
+
}
|
|
37
|
+
const colUnit = width / numColumns
|
|
38
|
+
const rowUnit = height / leaves.length
|
|
39
|
+
return leaves.flatMap((leaf, row) =>
|
|
40
|
+
(domainBands.get(leaf.data.name) ?? []).map(band => ({
|
|
41
|
+
x: band.startCol * colUnit,
|
|
42
|
+
y: row * rowUnit,
|
|
43
|
+
w: Math.max(1, (band.endCol - band.startCol) * colUnit),
|
|
44
|
+
h: Math.max(1, rowUnit),
|
|
45
|
+
fill: featureColors.get(band.annotation)?.fill ?? 'grey',
|
|
46
|
+
})),
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -3,8 +3,11 @@ import React, { useCallback, useRef } from 'react'
|
|
|
3
3
|
import { observer } from 'mobx-react'
|
|
4
4
|
|
|
5
5
|
import { useWheelScroll } from '../../useWheelScroll.ts'
|
|
6
|
+
import { zoomSnapshotScale } from '../../zoomSnapshot.ts'
|
|
7
|
+
import { alignmentAriaLabel } from '../controls.ts'
|
|
6
8
|
import Loading from './Loading.tsx'
|
|
7
9
|
import MSACanvasBlock from './MSACanvasBlock.tsx'
|
|
10
|
+
import { WheelZoomHint, useWheelZoomHint } from './WheelZoomHint.tsx'
|
|
8
11
|
import { onMsaKey } from './msaKeys.ts'
|
|
9
12
|
|
|
10
13
|
import type { MsaViewModel } from '../../model.ts'
|
|
@@ -19,7 +22,16 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
19
22
|
scrollZoom,
|
|
20
23
|
scrollX,
|
|
21
24
|
scrollY,
|
|
25
|
+
zoomSnapshot,
|
|
22
26
|
} = model
|
|
27
|
+
const blocks = zoomSnapshot
|
|
28
|
+
? zoomSnapshot.blocksY.flatMap(by =>
|
|
29
|
+
zoomSnapshot.blocksX.map(bx => [bx, by] as const),
|
|
30
|
+
)
|
|
31
|
+
: blocks2d
|
|
32
|
+
const scale = zoomSnapshot
|
|
33
|
+
? zoomSnapshotScale(model, zoomSnapshot)
|
|
34
|
+
: undefined
|
|
23
35
|
const ref = useRef<HTMLDivElement>(null)
|
|
24
36
|
const onScrollX = useCallback(
|
|
25
37
|
(d: number) => {
|
|
@@ -35,15 +47,21 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
35
47
|
)
|
|
36
48
|
const onZoom = useCallback(
|
|
37
49
|
(scaleFactor: number, offsetX: number, offsetY: number) => {
|
|
38
|
-
model.
|
|
50
|
+
model.wheelZoomToPos(scaleFactor, offsetX, offsetY)
|
|
39
51
|
},
|
|
40
52
|
[model],
|
|
41
53
|
)
|
|
54
|
+
const onZoomSettle = useCallback(() => {
|
|
55
|
+
model.endWheelZoom()
|
|
56
|
+
}, [model])
|
|
57
|
+
const hint = useWheelZoomHint(model)
|
|
42
58
|
const { onMouseDown, onMouseUp } = useWheelScroll({
|
|
43
59
|
ref,
|
|
44
60
|
onScrollX,
|
|
45
61
|
onScrollY,
|
|
46
62
|
onZoom,
|
|
63
|
+
onZoomSettle,
|
|
64
|
+
onWheelScroll: hint.onWheelScroll,
|
|
47
65
|
scrollZoom,
|
|
48
66
|
})
|
|
49
67
|
|
|
@@ -54,7 +72,7 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
54
72
|
// col*colWidth + scrollX, row*rowHeight + scrollY
|
|
55
73
|
data-testid="msa_canvas"
|
|
56
74
|
tabIndex={0}
|
|
57
|
-
aria-label=
|
|
75
|
+
aria-label={alignmentAriaLabel}
|
|
58
76
|
onKeyDown={event => {
|
|
59
77
|
if (onMsaKey(model, event)) {
|
|
60
78
|
event.preventDefault()
|
|
@@ -87,11 +105,12 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
87
105
|
position: 'absolute',
|
|
88
106
|
top: 0,
|
|
89
107
|
left: 0,
|
|
90
|
-
transform: `translate(${scrollX}px, ${scrollY}px)`,
|
|
108
|
+
transform: `translate(${scrollX}px, ${scrollY}px)${scale ? ` scale(${scale.sx}, ${scale.sy})` : ''}`,
|
|
109
|
+
transformOrigin: '0 0',
|
|
91
110
|
willChange: 'transform',
|
|
92
111
|
}}
|
|
93
112
|
>
|
|
94
|
-
{
|
|
113
|
+
{blocks.map(([bx, by]) => (
|
|
95
114
|
<MSACanvasBlock
|
|
96
115
|
key={`${bx}_${by}`}
|
|
97
116
|
model={model}
|
|
@@ -103,6 +122,7 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
103
122
|
) : (
|
|
104
123
|
<Loading />
|
|
105
124
|
)}
|
|
125
|
+
{hint.visible ? <WheelZoomHint /> : null}
|
|
106
126
|
</div>
|
|
107
127
|
)
|
|
108
128
|
})
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useRef } from 'react'
|
|
2
2
|
|
|
3
3
|
import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
|
|
4
|
-
import { useTheme } from '@mui/material'
|
|
5
4
|
import { observer } from 'mobx-react'
|
|
6
5
|
|
|
6
|
+
import { useCanvasTheme } from '../../darkReader.ts'
|
|
7
7
|
import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
|
|
8
|
-
import { drawMsaRaster,
|
|
9
|
-
import { renderBoxFeatureCanvasBlock } from './renderBoxFeatureCanvasBlock.ts'
|
|
8
|
+
import { drawMsaRaster, rasterApplies } from './msaRaster.ts'
|
|
10
9
|
import { renderMSABlock } from './renderMSABlock.ts'
|
|
11
10
|
import { useMsaBlockMouse } from './useMsaBlockMouse.ts'
|
|
12
11
|
|
|
13
12
|
import type { MsaViewModel } from '../../model.ts'
|
|
13
|
+
import type { ZoomSnapshot } from '../../zoomSnapshot.ts'
|
|
14
14
|
|
|
15
15
|
const MSACanvasBlock = observer(function ({
|
|
16
16
|
model,
|
|
@@ -22,27 +22,29 @@ const MSACanvasBlock = observer(function ({
|
|
|
22
22
|
offsetY: number
|
|
23
23
|
}) {
|
|
24
24
|
const { blockSize, highResScaleFactor } = model
|
|
25
|
-
const theme =
|
|
25
|
+
const theme = useCanvasTheme()
|
|
26
26
|
const { anchor, onMouseMove, onMouseDown, onClick, onMouseLeave } =
|
|
27
27
|
useMsaBlockMouse({ model, offsetX, offsetY })
|
|
28
28
|
|
|
29
29
|
const canvasSize = blockSize * highResScaleFactor
|
|
30
|
+
const drawnAt = useRef<ZoomSnapshot>(undefined)
|
|
30
31
|
const ref = useCanvasAutorun({
|
|
31
32
|
draw: ctx => {
|
|
33
|
+
// during a wheel zoom MSACanvas scales what this block drew at the
|
|
34
|
+
// snapshot, so the block redraws only when a new snapshot is taken
|
|
35
|
+
const snap = model.zoomSnapshot
|
|
36
|
+
if (
|
|
37
|
+
snap &&
|
|
38
|
+
(snap === drawnAt.current ||
|
|
39
|
+
snap.colWidth !== model.colWidth ||
|
|
40
|
+
snap.rowHeight !== model.rowHeight)
|
|
41
|
+
) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
drawnAt.current = snap
|
|
32
45
|
ctx.resetTransform()
|
|
33
46
|
ctx.clearRect(0, 0, canvasSize, canvasSize)
|
|
34
|
-
|
|
35
|
-
ctx,
|
|
36
|
-
theme,
|
|
37
|
-
offsetX,
|
|
38
|
-
offsetY,
|
|
39
|
-
model,
|
|
40
|
-
})
|
|
41
|
-
// the alignment background comes from zoom-independent raster tiles; the
|
|
42
|
-
// domain overlay paints its own boxes instead, and letter-color mode has
|
|
43
|
-
// no background to paint
|
|
44
|
-
const rasterTiles =
|
|
45
|
-
!model.actuallyShowDomains && model.bgColor && rasterSupported()
|
|
47
|
+
const rasterTiles = rasterApplies(model)
|
|
46
48
|
if (rasterTiles) {
|
|
47
49
|
drawMsaRaster({ ctx, model, theme, offsetX, offsetY })
|
|
48
50
|
}
|
|
@@ -53,6 +55,7 @@ const MSACanvasBlock = observer(function ({
|
|
|
53
55
|
offsetY,
|
|
54
56
|
model,
|
|
55
57
|
rasterTiles,
|
|
58
|
+
domains: true,
|
|
56
59
|
})
|
|
57
60
|
},
|
|
58
61
|
width: canvasSize,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
3
|
import { isAlive } from '@jbrowse/mobx-state-tree'
|
|
4
|
-
import { useTheme } from '@mui/material'
|
|
5
4
|
import { observer } from 'mobx-react'
|
|
6
5
|
|
|
7
6
|
import { msaOverlayZIndex } from '../../constants.ts'
|
|
7
|
+
import { useCanvasTheme } from '../../darkReader.ts'
|
|
8
8
|
import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
|
|
9
9
|
import { renderMouseover } from './renderMSAMouseover.ts'
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ const MSAMouseoverCanvas = observer(function ({
|
|
|
16
16
|
model: MsaViewModel
|
|
17
17
|
}) {
|
|
18
18
|
const { height, msaCanvasWidth: width, highResScaleFactor } = model
|
|
19
|
-
const theme =
|
|
19
|
+
const theme = useCanvasTheme()
|
|
20
20
|
const canvasWidth = width * highResScaleFactor
|
|
21
21
|
const canvasHeight = height * highResScaleFactor
|
|
22
22
|
const ref = useCanvasAutorun({
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { useCallback, useRef, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { Paper, Typography } from '@mui/material'
|
|
4
|
+
|
|
5
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
6
|
+
|
|
7
|
+
const hinted = new WeakSet<MsaViewModel>()
|
|
8
|
+
|
|
9
|
+
const zoomKey =
|
|
10
|
+
typeof navigator !== 'undefined' && navigator.platform.startsWith('Mac')
|
|
11
|
+
? '⌘'
|
|
12
|
+
: 'Ctrl'
|
|
13
|
+
|
|
14
|
+
export function useWheelZoomHint(model: MsaViewModel) {
|
|
15
|
+
const [visible, setVisible] = useState(false)
|
|
16
|
+
const timer = useRef<ReturnType<typeof setTimeout>>(undefined)
|
|
17
|
+
const onWheelScroll = useCallback(
|
|
18
|
+
(event: WheelEvent) => {
|
|
19
|
+
if (
|
|
20
|
+
!model.scrollZoom &&
|
|
21
|
+
Math.abs(event.deltaY) > Math.abs(event.deltaX) &&
|
|
22
|
+
!hinted.has(model)
|
|
23
|
+
) {
|
|
24
|
+
hinted.add(model)
|
|
25
|
+
setVisible(true)
|
|
26
|
+
clearTimeout(timer.current)
|
|
27
|
+
timer.current = setTimeout(() => {
|
|
28
|
+
setVisible(false)
|
|
29
|
+
}, 2500)
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
[model],
|
|
33
|
+
)
|
|
34
|
+
return { visible, onWheelScroll }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function WheelZoomHint() {
|
|
38
|
+
return (
|
|
39
|
+
<Paper
|
|
40
|
+
elevation={3}
|
|
41
|
+
style={{
|
|
42
|
+
position: 'absolute',
|
|
43
|
+
top: 12,
|
|
44
|
+
left: '50%',
|
|
45
|
+
transform: 'translateX(-50%)',
|
|
46
|
+
padding: '4px 12px',
|
|
47
|
+
pointerEvents: 'none',
|
|
48
|
+
zIndex: 2,
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
<Typography variant="body2">
|
|
52
|
+
{zoomKey} + scroll to zoom, or check Zoom on scroll
|
|
53
|
+
</Typography>
|
|
54
|
+
</Paper>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -37,6 +37,42 @@ export interface SpanLayout {
|
|
|
37
37
|
headRise?: (height: number) => number
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* What a renderer hands the span mark: its rows, where a band lands in them,
|
|
42
|
+
* its x mapping, the colors and the label each feature takes.
|
|
43
|
+
*/
|
|
44
|
+
export interface SpanSource<T extends SpanBand> {
|
|
45
|
+
rows: SpanRow<T>[]
|
|
46
|
+
layout: SpanLayout
|
|
47
|
+
xOf: (band: T) => [number, number]
|
|
48
|
+
colors: Map<Annotation, { fill: string; stroke: string }>
|
|
49
|
+
labelOf?: (band: T) => string | undefined
|
|
50
|
+
labelDrawn?: (band: T) => void
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** a span as drawn: a box, or an arrow's outline where it carries a strand */
|
|
54
|
+
export interface SpanShape<T extends SpanBand> {
|
|
55
|
+
band: T
|
|
56
|
+
fill: string
|
|
57
|
+
stroke: string
|
|
58
|
+
box?: { x: number; y: number; w: number; h: number }
|
|
59
|
+
outline?: [number, number][]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface SpanLabel {
|
|
63
|
+
text: string
|
|
64
|
+
x: number
|
|
65
|
+
y: number
|
|
66
|
+
fill: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** one row's shapes, then the labels drawn over them at `fontSize` */
|
|
70
|
+
export interface SpanMarkRow<T extends SpanBand> {
|
|
71
|
+
shapes: SpanShape<T>[]
|
|
72
|
+
labels: SpanLabel[]
|
|
73
|
+
fontSize: number
|
|
74
|
+
}
|
|
75
|
+
|
|
40
76
|
/** the rows of `bandsByRow` that `leaves` covers, in display order */
|
|
41
77
|
export function spanRows<T extends SpanBand>(
|
|
42
78
|
leaves: HierarchyNode<NodeWithIdsAndLength>[],
|
|
@@ -53,56 +89,49 @@ export function spanRows<T extends SpanBand>(
|
|
|
53
89
|
}
|
|
54
90
|
|
|
55
91
|
/**
|
|
56
|
-
* The span mark, in whatever x space
|
|
57
|
-
* columns for the domain overlay, panel pixels for a `features` row
|
|
58
|
-
* feature with a strand
|
|
59
|
-
*
|
|
92
|
+
* The span mark as geometry, in whatever x space `xOf` maps its bands into:
|
|
93
|
+
* alignment columns for the domain overlay, panel pixels for a `features` row
|
|
94
|
+
* panel. A feature with a strand is an arrow, one without a box, and a label
|
|
95
|
+
* lands inside the span wherever `measureText` says the text fits.
|
|
60
96
|
*
|
|
61
|
-
* `
|
|
62
|
-
* the context is translated so that `y - rowHeight` is the row's top edge.
|
|
97
|
+
* `y - rowHeight` is the row's top edge in the coordinates `rows` gives.
|
|
63
98
|
*/
|
|
64
|
-
export function
|
|
65
|
-
ctx,
|
|
99
|
+
export function featureSpanMarks<T extends SpanBand>({
|
|
66
100
|
rows,
|
|
67
101
|
layout,
|
|
68
102
|
xOf,
|
|
69
103
|
colors,
|
|
70
104
|
labelOf,
|
|
71
105
|
labelDrawn,
|
|
72
|
-
|
|
106
|
+
measureText,
|
|
73
107
|
xMin,
|
|
74
108
|
xMax,
|
|
75
|
-
}: {
|
|
76
|
-
|
|
77
|
-
rows: SpanRow<T>[]
|
|
78
|
-
layout: SpanLayout
|
|
79
|
-
xOf: (band: T) => [number, number]
|
|
80
|
-
colors: Map<Annotation, { fill: string; stroke: string }>
|
|
81
|
-
labelOf?: (band: T) => string | undefined
|
|
82
|
-
labelDrawn?: (band: T) => void
|
|
83
|
-
contrastText: (color: string) => string
|
|
109
|
+
}: SpanSource<T> & {
|
|
110
|
+
measureText: (text: string, fontSize: number) => number
|
|
84
111
|
xMin: number
|
|
85
112
|
xMax: number
|
|
86
|
-
}) {
|
|
87
|
-
|
|
88
|
-
// longest-first so a nested one lands on top, and drawing each label with its
|
|
89
|
-
// own span put the child's box over the parent's text: `S` covered `RBD`, and
|
|
90
|
-
// `RBD` read as `RB`. Reused across rows rather than allocated per row.
|
|
91
|
-
const placed: {
|
|
92
|
-
band: T
|
|
93
|
-
xStart: number
|
|
94
|
-
w: number
|
|
95
|
-
t: number
|
|
96
|
-
head: number
|
|
97
|
-
fill: string
|
|
98
|
-
}[] = []
|
|
99
|
-
|
|
113
|
+
}): SpanMarkRow<T>[] {
|
|
114
|
+
const out: SpanMarkRow<T>[] = []
|
|
100
115
|
for (const { y, bands } of rows) {
|
|
101
116
|
const h = layout.height(bands[0]!.laneCount)
|
|
102
117
|
const headLen = layout.headLength(h)
|
|
103
118
|
const rise = layout.headRise?.(h) ?? 0
|
|
104
119
|
const labelled = labelOf !== undefined && h >= minFeatureLabelHeight
|
|
105
|
-
|
|
120
|
+
const fontSize = Math.min(h - 2, 11)
|
|
121
|
+
const shapes: SpanShape<T>[] = []
|
|
122
|
+
const labels: SpanLabel[] = []
|
|
123
|
+
// one row's spans are drawn before any of its labels. Bands are ordered
|
|
124
|
+
// longest-first so a nested one lands on top, and drawing each label with
|
|
125
|
+
// its own span put the child's box over the parent's text: `S` covered
|
|
126
|
+
// `RBD`, and `RBD` read as `RB`.
|
|
127
|
+
const placed: {
|
|
128
|
+
band: T
|
|
129
|
+
xStart: number
|
|
130
|
+
w: number
|
|
131
|
+
t: number
|
|
132
|
+
head: number
|
|
133
|
+
fill: string
|
|
134
|
+
}[] = []
|
|
106
135
|
|
|
107
136
|
for (const band of bands) {
|
|
108
137
|
const [xStart, xEnd] = xOf(band)
|
|
@@ -112,55 +141,111 @@ export function drawFeatureSpans<T extends SpanBand>({
|
|
|
112
141
|
}
|
|
113
142
|
const t = layout.top(y, band.lane, h)
|
|
114
143
|
const { fill, stroke } = colors.get(band.annotation)!
|
|
115
|
-
ctx.fillStyle = fill
|
|
116
|
-
ctx.strokeStyle = stroke
|
|
117
144
|
const { strand } = band.annotation
|
|
118
145
|
const head = strand === undefined ? 0 : Math.min(headLen, w)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
146
|
+
shapes.push(
|
|
147
|
+
strand === undefined
|
|
148
|
+
? { band, fill, stroke, box: { x: xStart, y: t, w, h } }
|
|
149
|
+
: {
|
|
150
|
+
band,
|
|
151
|
+
fill,
|
|
152
|
+
stroke,
|
|
153
|
+
outline: geneArrow({ x: xStart, t, w, h, head, rise, strand }),
|
|
154
|
+
},
|
|
155
|
+
)
|
|
125
156
|
if (labelled) {
|
|
126
157
|
placed.push({ band, xStart, w, t, head, fill })
|
|
127
158
|
}
|
|
128
159
|
}
|
|
129
160
|
|
|
130
|
-
|
|
131
|
-
|
|
161
|
+
for (const { band, xStart, w, t, head, fill } of placed) {
|
|
162
|
+
// asking for the label here rather than in the pass above keeps the
|
|
163
|
+
// once-per-block segment numbering in drawing order
|
|
164
|
+
const text = labelOf!(band)
|
|
165
|
+
// the head narrows to a point, so only about half of it holds text
|
|
166
|
+
if (
|
|
167
|
+
text !== undefined &&
|
|
168
|
+
measureText(text, fontSize) + 2 <= w - head / 2
|
|
169
|
+
) {
|
|
170
|
+
labelDrawn?.(band)
|
|
171
|
+
labels.push({ text, x: xStart + w / 2, y: t + h / 2, fill })
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
out.push({ shapes, labels, fontSize })
|
|
175
|
+
}
|
|
176
|
+
return out
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* The span mark, drawn into `ctx` from `featureSpanMarks`, with the context
|
|
181
|
+
* translated so that `y - rowHeight` is the row's top edge.
|
|
182
|
+
*/
|
|
183
|
+
export function drawFeatureSpans<T extends SpanBand>({
|
|
184
|
+
ctx,
|
|
185
|
+
contrastText,
|
|
186
|
+
...source
|
|
187
|
+
}: SpanSource<T> & {
|
|
188
|
+
ctx: RenderCtx
|
|
189
|
+
contrastText: (color: string) => string
|
|
190
|
+
xMin: number
|
|
191
|
+
xMax: number
|
|
192
|
+
}) {
|
|
193
|
+
let font = ''
|
|
194
|
+
const setFont = (fontSize: number) => {
|
|
195
|
+
const next = `${fontSize}px sans-serif`
|
|
196
|
+
if (next !== font) {
|
|
197
|
+
ctx.font = next
|
|
198
|
+
font = next
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const rows = featureSpanMarks({
|
|
202
|
+
...source,
|
|
203
|
+
measureText: (text, fontSize) => {
|
|
204
|
+
setFont(fontSize)
|
|
205
|
+
return ctx.measureText(text).width
|
|
206
|
+
},
|
|
207
|
+
})
|
|
208
|
+
for (const { shapes, labels, fontSize } of rows) {
|
|
209
|
+
for (const { fill, stroke, box, outline } of shapes) {
|
|
210
|
+
ctx.fillStyle = fill
|
|
211
|
+
ctx.strokeStyle = stroke
|
|
212
|
+
if (box) {
|
|
213
|
+
ctx.fillRect(box.x, box.y, box.w, box.h)
|
|
214
|
+
ctx.strokeRect(box.x, box.y, box.w, box.h)
|
|
215
|
+
} else if (outline) {
|
|
216
|
+
ctx.beginPath()
|
|
217
|
+
ctx.moveTo(...outline[0]!)
|
|
218
|
+
for (let i = 1; i < outline.length; i++) {
|
|
219
|
+
ctx.lineTo(...outline[i]!)
|
|
220
|
+
}
|
|
221
|
+
ctx.closePath()
|
|
222
|
+
ctx.fill()
|
|
223
|
+
ctx.stroke()
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (labels.length > 0) {
|
|
227
|
+
setFont(fontSize)
|
|
132
228
|
ctx.textAlign = 'center'
|
|
133
229
|
ctx.textBaseline = 'middle'
|
|
134
|
-
for (const {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const label = labelOf!(band)
|
|
138
|
-
// the head narrows to a point, so only about half of it holds text
|
|
139
|
-
if (
|
|
140
|
-
label !== undefined &&
|
|
141
|
-
ctx.measureText(label).width + 2 <= w - head / 2
|
|
142
|
-
) {
|
|
143
|
-
labelDrawn?.(band)
|
|
144
|
-
ctx.fillStyle = contrastText(fill)
|
|
145
|
-
ctx.fillText(label, xStart + w / 2, t + h / 2)
|
|
146
|
-
}
|
|
230
|
+
for (const { text, x, y, fill } of labels) {
|
|
231
|
+
ctx.fillStyle = contrastText(fill)
|
|
232
|
+
ctx.fillText(text, x, y)
|
|
147
233
|
}
|
|
148
234
|
}
|
|
149
235
|
}
|
|
150
236
|
}
|
|
151
237
|
|
|
152
238
|
/**
|
|
153
|
-
* A gene arrow,
|
|
154
|
-
* last `head` pixels *of* the feature, tapering to a point at its end, and
|
|
155
|
-
* feature shorter than the head is all head. The glyph therefore covers its
|
|
239
|
+
* A gene arrow's outline, the way gggenes and gggenomes draw one: the head is
|
|
240
|
+
* the last `head` pixels *of* the feature, tapering to a point at its end, and
|
|
241
|
+
* a feature shorter than the head is all head. The glyph therefore covers its
|
|
156
242
|
* start..end span and nothing else, so adjacent genes butt together instead of
|
|
157
243
|
* biting triangles out of each other, a neighbour never draws over a label, and
|
|
158
244
|
* a gene 20 columns wide reads as 20 columns wide.
|
|
159
245
|
*
|
|
160
246
|
* `rise` lifts the head above the band, for a bar too thin to taper.
|
|
161
247
|
*/
|
|
162
|
-
function
|
|
163
|
-
ctx,
|
|
248
|
+
function geneArrow({
|
|
164
249
|
x,
|
|
165
250
|
t,
|
|
166
251
|
w,
|
|
@@ -169,7 +254,6 @@ function drawGeneArrow({
|
|
|
169
254
|
rise,
|
|
170
255
|
strand,
|
|
171
256
|
}: {
|
|
172
|
-
ctx: RenderCtx
|
|
173
257
|
x: number
|
|
174
258
|
t: number
|
|
175
259
|
w: number
|
|
@@ -177,29 +261,26 @@ function drawGeneArrow({
|
|
|
177
261
|
head: number
|
|
178
262
|
rise: number
|
|
179
263
|
strand: number
|
|
180
|
-
}) {
|
|
264
|
+
}): [number, number][] {
|
|
181
265
|
const tip = strand > 0 ? x + w : x
|
|
182
266
|
const tail = strand > 0 ? x : x + w
|
|
183
267
|
const flange = strand > 0 ? tip - head : tip + head
|
|
184
268
|
const bottom = t + h
|
|
185
269
|
const crown = t - rise
|
|
186
270
|
const point = (crown + bottom) / 2
|
|
187
|
-
ctx.beginPath()
|
|
188
271
|
if (head >= w) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
ctx.lineTo(flange, t)
|
|
195
|
-
if (rise > 0) {
|
|
196
|
-
ctx.lineTo(flange, crown)
|
|
197
|
-
}
|
|
198
|
-
ctx.lineTo(tip, point)
|
|
199
|
-
ctx.lineTo(flange, bottom)
|
|
200
|
-
ctx.lineTo(tail, bottom)
|
|
272
|
+
return [
|
|
273
|
+
[tail, crown],
|
|
274
|
+
[tip, point],
|
|
275
|
+
[tail, bottom],
|
|
276
|
+
]
|
|
201
277
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
278
|
+
return [
|
|
279
|
+
[tail, t],
|
|
280
|
+
[flange, t],
|
|
281
|
+
...(rise > 0 ? [[flange, crown] as [number, number]] : []),
|
|
282
|
+
[tip, point],
|
|
283
|
+
[flange, bottom],
|
|
284
|
+
[tail, bottom],
|
|
285
|
+
]
|
|
205
286
|
}
|