react-msaview 7.0.0 → 8.1.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 +11 -10
- package/bundle/index.js +122 -111
- package/bundle/index.js.map +4 -4
- package/dist/calculateBlocks.js +5 -3
- package/dist/calculateBlocks.js.map +1 -1
- package/dist/colorSchemes.js +1 -2
- package/dist/colorSchemes.js.map +1 -1
- package/dist/columnStats.d.ts +28 -0
- package/dist/columnStats.js +34 -0
- package/dist/columnStats.js.map +1 -0
- package/dist/components/DragHandle.d.ts +3 -2
- package/dist/components/DragHandle.js +11 -5
- package/dist/components/DragHandle.js.map +1 -1
- package/dist/components/Loading.js +2 -8
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAView.js +17 -6
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +64 -7
- package/dist/components/MSAViewer.js +128 -23
- package/dist/components/MSAViewer.js.map +1 -1
- package/dist/components/PortalTooltip.d.ts +11 -0
- package/dist/components/PortalTooltip.js +29 -0
- package/dist/components/PortalTooltip.js.map +1 -0
- package/dist/components/ResizeHandles.js +11 -29
- package/dist/components/ResizeHandles.js.map +1 -1
- package/dist/components/SequenceTextArea.js +3 -4
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/Track.js +43 -24
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/AboutDialog.js +6 -6
- package/dist/components/dialogs/AboutDialog.js.map +1 -1
- package/dist/components/dialogs/AnnotationFileDialog.js +5 -2
- package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
- package/dist/components/dialogs/ExportSVGDialog.js +6 -4
- package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +8 -3
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/dialogs/TrackInfoDialog.js +1 -1
- package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
- package/dist/components/header/GappynessSlider.js +6 -11
- package/dist/components/header/GappynessSlider.js.map +1 -1
- package/dist/components/header/Header.js +4 -1
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/LoadWarnings.js +6 -23
- package/dist/components/header/LoadWarnings.js.map +1 -1
- package/dist/components/header/ScrollZoomIcons.d.ts +6 -0
- package/dist/components/header/ScrollZoomIcons.js +35 -0
- package/dist/components/header/ScrollZoomIcons.js.map +1 -0
- package/dist/components/header/TreeSettingsMenu.js +1 -3
- package/dist/components/header/TreeSettingsMenu.js.map +1 -1
- package/dist/components/header/UnshareableDataWarning.js +2 -4
- package/dist/components/header/UnshareableDataWarning.js.map +1 -1
- package/dist/components/header/ZoomControls.js +49 -6
- package/dist/components/header/ZoomControls.js.map +1 -1
- package/dist/components/header/getAnnotationsMenu.js +3 -6
- package/dist/components/header/getAnnotationsMenu.js.map +1 -1
- package/dist/components/header/settingsMenuItems.js +12 -9
- package/dist/components/header/settingsMenuItems.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/util.js +1 -2
- package/dist/components/import/util.js.map +1 -1
- package/dist/components/minimap/Minimap.js +1 -2
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/msa/AnnotationLegend.js +27 -19
- package/dist/components/msa/AnnotationLegend.js.map +1 -1
- package/dist/components/msa/MSACanvas.js +2 -4
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +13 -15
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/MSAMouseoverCanvas.js +2 -1
- package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
- package/dist/components/msa/domainBandCursor.d.ts +7 -11
- package/dist/components/msa/domainBandCursor.js +7 -11
- package/dist/components/msa/domainBandCursor.js.map +1 -1
- package/dist/components/msa/drawFeatureSpans.d.ts +59 -0
- package/dist/components/msa/drawFeatureSpans.js +113 -0
- package/dist/components/msa/drawFeatureSpans.js.map +1 -0
- package/dist/components/msa/legendRows.d.ts +8 -0
- package/dist/components/msa/legendRows.js +16 -0
- package/dist/components/msa/legendRows.js.map +1 -0
- package/dist/components/msa/msaRaster.d.ts +24 -31
- package/dist/components/msa/msaRaster.js +30 -39
- package/dist/components/msa/msaRaster.js.map +1 -1
- package/dist/components/msa/packDomainLanes.d.ts +20 -0
- package/dist/components/msa/packDomainLanes.js +33 -0
- package/dist/components/msa/packDomainLanes.js.map +1 -0
- package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +3 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +50 -87
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderHighlights.d.ts +15 -13
- package/dist/components/msa/renderHighlights.js +43 -20
- package/dist/components/msa/renderHighlights.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +12 -12
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/tileColor.js +4 -7
- package/dist/components/msa/tileColor.js.map +1 -1
- package/dist/components/msa/useMsaBlockMouse.d.ts +5 -3
- package/dist/components/msa/useMsaBlockMouse.js +6 -6
- package/dist/components/msa/useMsaBlockMouse.js.map +1 -1
- package/dist/components/rowpanels/RowPanelBlock.d.ts +9 -0
- package/dist/components/rowpanels/RowPanelBlock.js +86 -0
- package/dist/components/rowpanels/RowPanelBlock.js.map +1 -0
- package/dist/components/rowpanels/RowPanelHeaders.d.ts +12 -0
- package/dist/components/rowpanels/RowPanelHeaders.js +38 -0
- package/dist/components/rowpanels/RowPanelHeaders.js.map +1 -0
- package/dist/components/{msa/ColumnStats.d.ts → rowpanels/RowPanels.d.ts} +2 -2
- package/dist/components/rowpanels/RowPanels.js +32 -0
- package/dist/components/rowpanels/RowPanels.js.map +1 -0
- package/dist/components/rowpanels/headerLayout.d.ts +1 -0
- package/dist/components/rowpanels/headerLayout.js +5 -0
- package/dist/components/rowpanels/headerLayout.js.map +1 -0
- package/dist/components/rowpanels/renderFeaturePanel.d.ts +21 -0
- package/dist/components/rowpanels/renderFeaturePanel.js +37 -0
- package/dist/components/rowpanels/renderFeaturePanel.js.map +1 -0
- package/dist/components/rowpanels/renderRowPanel.d.ts +23 -0
- package/dist/components/rowpanels/renderRowPanel.js +22 -0
- package/dist/components/rowpanels/renderRowPanel.js.map +1 -0
- package/dist/components/rowpanels/renderStrip.d.ts +20 -0
- package/dist/components/rowpanels/renderStrip.js +26 -0
- package/dist/components/rowpanels/renderStrip.js.map +1 -0
- package/dist/components/tracks/TrackBlocks.js +3 -5
- package/dist/components/tracks/TrackBlocks.js.map +1 -1
- package/dist/components/tracks/TrackTooltipContent.d.ts +15 -0
- package/dist/components/tracks/TrackTooltipContent.js +109 -0
- package/dist/components/tracks/TrackTooltipContent.js.map +1 -0
- package/dist/components/tracks/drawTracks.d.ts +4 -4
- package/dist/components/tracks/drawTracks.js +25 -39
- package/dist/components/tracks/drawTracks.js.map +1 -1
- package/dist/components/tracks/useTrackHover.d.ts +22 -0
- package/dist/components/tracks/useTrackHover.js +30 -0
- package/dist/components/tracks/useTrackHover.js.map +1 -0
- package/dist/components/tree/CladeLabels.d.ts +12 -0
- package/dist/components/tree/CladeLabels.js +48 -0
- package/dist/components/tree/CladeLabels.js.map +1 -0
- package/dist/components/tree/TreeBranchMenu.js +1 -1
- package/dist/components/tree/TreeBranchMenu.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js +5 -4
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +4 -21
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeNodeMenu.js +1 -1
- package/dist/components/tree/TreeNodeMenu.js.map +1 -1
- package/dist/components/tree/TreeOverview.d.ts +14 -0
- package/dist/components/tree/TreeOverview.js +75 -0
- package/dist/components/tree/TreeOverview.js.map +1 -0
- package/dist/components/tree/TreeRuler.js +9 -13
- package/dist/components/tree/TreeRuler.js.map +1 -1
- package/dist/components/tree/cladeBrackets.d.ts +25 -0
- package/dist/components/tree/cladeBrackets.js +51 -0
- package/dist/components/tree/cladeBrackets.js.map +1 -0
- package/dist/components/tree/dialogs/TreeNodeInfoDialog.js +2 -2
- package/dist/components/tree/dialogs/TreeNodeInfoDialog.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.js +160 -25
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/components/tree/renderTreeOverview.d.ts +52 -0
- package/dist/components/tree/renderTreeOverview.js +138 -0
- package/dist/components/tree/renderTreeOverview.js.map +1 -0
- package/dist/components/tree/useTreeHover.js +2 -4
- package/dist/components/tree/useTreeHover.js.map +1 -1
- package/dist/constants.d.ts +16 -0
- package/dist/constants.js +52 -22
- package/dist/constants.js.map +1 -1
- package/dist/createPaletteMap.d.ts +7 -6
- package/dist/createPaletteMap.js +10 -9
- package/dist/createPaletteMap.js.map +1 -1
- package/dist/element.d.ts +13 -0
- package/dist/element.js +269 -0
- package/dist/element.js.map +1 -0
- package/dist/featureFields.d.ts +11 -0
- package/dist/featureFields.js +17 -0
- package/dist/featureFields.js.map +1 -0
- package/dist/flatToTree.js +1 -2
- package/dist/flatToTree.js.map +1 -1
- package/dist/ggplotPalettes.d.ts +10 -0
- package/dist/ggplotPalettes.js +58 -0
- package/dist/ggplotPalettes.js.map +1 -1
- package/dist/hierarchy.d.ts +27 -0
- package/dist/hierarchy.js +66 -9
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/model/DataModel.js +2 -5
- package/dist/model/DataModel.js.map +1 -1
- package/dist/model/msaModel.d.ts +3 -2
- package/dist/model/msaModel.js +3 -2
- package/dist/model/msaModel.js.map +1 -1
- package/dist/model/treeModel.d.ts +30 -0
- package/dist/model/treeModel.js +37 -1
- package/dist/model/treeModel.js.map +1 -1
- package/dist/model.d.ts +549 -294
- package/dist/model.js +1412 -621
- package/dist/model.js.map +1 -1
- package/dist/mount.d.ts +11 -0
- package/dist/mount.js +22 -0
- package/dist/mount.js.map +1 -0
- package/dist/neighborJoining.js +8 -32
- package/dist/neighborJoining.js.map +1 -1
- package/dist/renderToStaticMarkup.d.ts +10 -19
- package/dist/renderToStaticMarkup.js +13 -24
- package/dist/renderToStaticMarkup.js.map +1 -1
- package/dist/renderToSvg.js +180 -56
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.d.ts +4 -6
- package/dist/rowCoordinateCalculations.js +4 -6
- package/dist/rowCoordinateCalculations.js.map +1 -1
- package/dist/scales.d.ts +22 -0
- package/dist/scales.js +30 -0
- package/dist/scales.js.map +1 -0
- package/dist/seqPosToGlobalCol.d.ts +2 -3
- package/dist/seqPosToGlobalCol.js +2 -3
- package/dist/seqPosToGlobalCol.js.map +1 -1
- package/dist/sequenceLogo.d.ts +6 -11
- package/dist/sequenceLogo.js +6 -11
- package/dist/sequenceLogo.js.map +1 -1
- package/dist/stripDefault.d.ts +7 -14
- package/dist/stripDefault.js +7 -14
- package/dist/stripDefault.js.map +1 -1
- package/dist/svgTestUtil.d.ts +186 -70
- package/dist/types.d.ts +221 -21
- package/dist/umd.d.ts +2 -0
- package/dist/umd.js +4 -2
- package/dist/umd.js.map +1 -1
- package/dist/useCanvasAutorun.d.ts +5 -10
- package/dist/useCanvasAutorun.js +5 -10
- package/dist/useCanvasAutorun.js.map +1 -1
- package/dist/useMsaSvgFigure.d.ts +20 -0
- package/dist/useMsaSvgFigure.js +46 -0
- package/dist/useMsaSvgFigure.js.map +1 -0
- package/dist/useWheelScroll.js +0 -4
- package/dist/useWheelScroll.js.map +1 -1
- package/dist/util.d.ts +5 -6
- package/dist/util.js +13 -6
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +19 -12
- package/src/calculateBlocks.ts +5 -3
- package/src/colorSchemes.ts +1 -2
- package/src/columnStats.ts +58 -0
- package/src/components/DragHandle.tsx +18 -7
- package/src/components/Loading.tsx +4 -36
- package/src/components/MSAView.tsx +19 -5
- package/src/components/MSAViewer.tsx +240 -28
- package/src/components/PortalTooltip.tsx +47 -0
- package/src/components/ResizeHandles.tsx +17 -32
- package/src/components/SequenceTextArea.tsx +3 -4
- package/src/components/Track.tsx +57 -19
- package/src/components/dialogs/AboutDialog.tsx +35 -6
- package/src/components/dialogs/AnnotationFileDialog.tsx +10 -1
- package/src/components/dialogs/ExportSVGDialog.tsx +8 -3
- package/src/components/dialogs/FeatureDialog.tsx +11 -3
- package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
- package/src/components/header/GappynessSlider.tsx +6 -11
- package/src/components/header/Header.tsx +7 -1
- package/src/components/header/LoadWarnings.tsx +11 -25
- package/src/components/header/ScrollZoomIcons.tsx +52 -0
- package/src/components/header/TreeSettingsMenu.tsx +1 -3
- package/src/components/header/UnshareableDataWarning.tsx +2 -4
- package/src/components/header/ZoomControls.tsx +74 -14
- package/src/components/header/getAnnotationsMenu.ts +3 -6
- package/src/components/header/settingsMenuItems.ts +13 -8
- package/src/components/import/ImportForm.tsx +3 -5
- package/src/components/import/ImportFormExamples.tsx +2 -2
- package/src/components/import/util.ts +1 -2
- package/src/components/minimap/Minimap.tsx +1 -2
- package/src/components/msa/AnnotationLegend.tsx +48 -38
- package/src/components/msa/MSACanvas.tsx +3 -8
- package/src/components/msa/MSACanvasBlock.tsx +13 -16
- package/src/components/msa/MSAMouseoverCanvas.tsx +2 -1
- package/src/components/msa/domainBandCursor.ts +7 -11
- package/src/components/msa/drawFeatureSpans.ts +205 -0
- package/src/components/msa/legendRows.ts +24 -0
- package/src/components/msa/msaRaster.ts +30 -39
- package/src/components/msa/packDomainLanes.ts +36 -0
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +67 -123
- package/src/components/msa/renderHighlights.ts +53 -20
- package/src/components/msa/renderMSABlock.ts +14 -11
- package/src/components/msa/tileColor.ts +4 -7
- package/src/components/msa/useMsaBlockMouse.ts +6 -6
- package/src/components/rowpanels/RowPanelBlock.tsx +139 -0
- package/src/components/rowpanels/RowPanelHeaders.tsx +57 -0
- package/src/components/rowpanels/RowPanels.tsx +77 -0
- package/src/components/rowpanels/headerLayout.ts +5 -0
- package/src/components/rowpanels/renderFeaturePanel.ts +66 -0
- package/src/components/rowpanels/renderRowPanel.ts +41 -0
- package/src/components/rowpanels/renderStrip.ts +46 -0
- package/src/components/tracks/TrackBlocks.tsx +3 -5
- package/src/components/tracks/TrackTooltipContent.tsx +156 -0
- package/src/components/tracks/drawTracks.ts +25 -50
- package/src/components/tracks/useTrackHover.ts +42 -0
- package/src/components/tree/CladeLabels.tsx +77 -0
- package/src/components/tree/TreeBranchMenu.tsx +1 -2
- package/src/components/tree/TreeCanvas.tsx +2 -3
- package/src/components/tree/TreeCanvasBlock.tsx +8 -32
- package/src/components/tree/TreeNodeMenu.tsx +1 -2
- package/src/components/tree/TreeOverview.tsx +106 -0
- package/src/components/tree/TreeRuler.tsx +11 -22
- package/src/components/tree/cladeBrackets.ts +73 -0
- package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +2 -2
- package/src/components/tree/renderTreeCanvas.ts +242 -21
- package/src/components/tree/renderTreeOverview.ts +212 -0
- package/src/components/tree/useTreeHover.ts +2 -4
- package/src/constants.ts +61 -22
- package/src/createPaletteMap.ts +10 -9
- package/src/element.ts +321 -0
- package/src/featureFields.ts +19 -0
- package/src/flatToTree.ts +1 -2
- package/src/ggplotPalettes.ts +60 -0
- package/src/hierarchy.ts +77 -9
- package/src/index.ts +31 -7
- package/src/model/DataModel.ts +2 -5
- package/src/model/msaModel.ts +3 -2
- package/src/model/treeModel.ts +42 -0
- package/src/model.ts +1677 -643
- package/src/mount.tsx +32 -0
- package/src/neighborJoining.ts +8 -32
- package/src/renderToStaticMarkup.ts +13 -24
- package/src/renderToSvg.tsx +335 -68
- package/src/rowCoordinateCalculations.ts +4 -6
- package/src/scales.ts +47 -0
- package/src/seqPosToGlobalCol.ts +2 -3
- package/src/sequenceLogo.ts +6 -11
- package/src/stripDefault.ts +7 -14
- package/src/types.ts +260 -45
- package/src/umd.ts +4 -2
- package/src/useCanvasAutorun.ts +5 -10
- package/src/useMsaSvgFigure.ts +72 -0
- package/src/useWheelScroll.ts +0 -4
- package/src/util.ts +15 -6
- package/src/version.ts +1 -1
- package/dist/components/msa/ColumnStats.js +0 -42
- package/dist/components/msa/ColumnStats.js.map +0 -1
- package/src/components/msa/ColumnStats.tsx +0 -54
package/src/renderToSvg.tsx
CHANGED
|
@@ -5,15 +5,30 @@ import { when } from 'mobx'
|
|
|
5
5
|
|
|
6
6
|
import { visibleRowRange } from './components/getVisibleLeaves.ts'
|
|
7
7
|
import MinimapSVG from './components/minimap/MinimapSVG.tsx'
|
|
8
|
+
import { legendRows } from './components/msa/legendRows.ts'
|
|
8
9
|
import { rasterImageHref, rasterSupported } from './components/msa/msaRaster.ts'
|
|
9
10
|
import { renderBoxFeatureCanvasBlock } from './components/msa/renderBoxFeatureCanvasBlock.ts'
|
|
10
11
|
import { renderPersistentHighlights } from './components/msa/renderHighlights.ts'
|
|
11
12
|
import { renderMSABlock } from './components/msa/renderMSABlock.ts'
|
|
12
13
|
import { visibleColRange } from './components/msa/visibleColRange.ts'
|
|
14
|
+
import { headerRunsAcross } from './components/rowpanels/headerLayout.ts'
|
|
15
|
+
import { renderRowPanels } from './components/rowpanels/renderRowPanel.ts'
|
|
13
16
|
import { renderAllTracks } from './components/tracks/drawTracks.ts'
|
|
17
|
+
import {
|
|
18
|
+
bracketGap,
|
|
19
|
+
cladeHeight,
|
|
20
|
+
cladeInGutter,
|
|
21
|
+
cladeLabelHorizontal,
|
|
22
|
+
cladeLabelOffset,
|
|
23
|
+
} from './components/tree/cladeBrackets.ts'
|
|
14
24
|
import { renderTreeCanvas } from './components/tree/renderTreeCanvas.ts'
|
|
25
|
+
import { renderTreeOverview } from './components/tree/renderTreeOverview.ts'
|
|
26
|
+
import { treeScaleBarHeight } from './constants.ts'
|
|
27
|
+
import { measureTextCanvas } from './measureTextCanvas.ts'
|
|
15
28
|
import { renderToStaticMarkup, svgSafeColors } from './renderToStaticMarkup.ts'
|
|
29
|
+
import { outlineColor } from './util.ts'
|
|
16
30
|
|
|
31
|
+
import type { LegendRow } from './components/msa/legendRows.ts'
|
|
17
32
|
import type { MsaViewModel } from './model.ts'
|
|
18
33
|
import type { Context as ContextType } from '@jbrowse/svgcanvas'
|
|
19
34
|
import type { Theme } from '@mui/material'
|
|
@@ -25,28 +40,29 @@ export interface ExportSvgOptions {
|
|
|
25
40
|
exportType: 'entire' | 'viewport'
|
|
26
41
|
}
|
|
27
42
|
|
|
28
|
-
//
|
|
29
|
-
//
|
|
43
|
+
// legend geometry, shared by the width calculation and the renderer so the
|
|
44
|
+
// reserved column on the right exactly fits the drawn legends
|
|
30
45
|
const LEGEND_PAD = 8
|
|
31
46
|
const LEGEND_ROW_H = 16
|
|
32
47
|
const LEGEND_SWATCH = 12
|
|
33
48
|
const LEGEND_FONT = 12
|
|
34
49
|
const LEGEND_CHAR_W = 7
|
|
35
50
|
|
|
51
|
+
// the width of a character as a fraction of the font size, close enough for a
|
|
52
|
+
// sans-serif average
|
|
53
|
+
const CHAR_WIDTH = 0.6
|
|
54
|
+
|
|
36
55
|
const LEGEND_MAX_W = 360
|
|
37
56
|
const LEGEND_TEXT_X = LEGEND_PAD + LEGEND_SWATCH + 6
|
|
38
57
|
|
|
39
|
-
function getLegendWidth(
|
|
40
|
-
const maxLen =
|
|
41
|
-
(a, d) => Math.max(a, d.name.length),
|
|
42
|
-
0,
|
|
43
|
-
)
|
|
58
|
+
function getLegendWidth(rows: LegendRow[]) {
|
|
59
|
+
const maxLen = rows.reduce((a, r) => Math.max(a, r.label.length), 0)
|
|
44
60
|
const w = LEGEND_TEXT_X + LEGEND_PAD + maxLen * LEGEND_CHAR_W
|
|
45
61
|
return Math.min(LEGEND_MAX_W, Math.max(120, w))
|
|
46
62
|
}
|
|
47
63
|
|
|
48
|
-
function legendHeight(
|
|
49
|
-
return LEGEND_PAD * 2 +
|
|
64
|
+
function legendHeight(rows: LegendRow[]) {
|
|
65
|
+
return LEGEND_PAD * 2 + rows.length * LEGEND_ROW_H
|
|
50
66
|
}
|
|
51
67
|
|
|
52
68
|
// resolved sizes/offsets (in svg user units) for the chosen export, shared by
|
|
@@ -58,6 +74,9 @@ interface Layout {
|
|
|
58
74
|
height: number
|
|
59
75
|
contentHeight: number
|
|
60
76
|
trackHeight: number
|
|
77
|
+
// the band the minimap, the row panel headers and the tree overview stacked
|
|
78
|
+
// on the scale bar share across the top
|
|
79
|
+
topHeight: number
|
|
61
80
|
offsetX: number
|
|
62
81
|
offsetY: number
|
|
63
82
|
includeMinimap: boolean
|
|
@@ -71,11 +90,15 @@ function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
|
|
|
71
90
|
totalWidth,
|
|
72
91
|
totalHeight,
|
|
73
92
|
treeAreaWidth,
|
|
93
|
+
rowPanelsWidth,
|
|
94
|
+
rowPanelsHeaderHeight,
|
|
74
95
|
totalTrackAreaHeight,
|
|
75
96
|
minimapHeight,
|
|
76
97
|
msaAreaHeight,
|
|
77
98
|
msaCanvasWidth,
|
|
78
99
|
showHorizontalScrollbar,
|
|
100
|
+
treeOverviewHeight,
|
|
101
|
+
treeScaleBar,
|
|
79
102
|
} = model
|
|
80
103
|
const trackHeight = opts.includeTracks ? totalTrackAreaHeight : 0
|
|
81
104
|
// the minimap reflects the live viewport scroll position, so it's only
|
|
@@ -86,10 +109,14 @@ function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
|
|
|
86
109
|
opts.exportType === 'viewport' &&
|
|
87
110
|
!!opts.includeMinimap &&
|
|
88
111
|
showHorizontalScrollbar
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
112
|
+
const rows = legendRows(model.legends)
|
|
113
|
+
const legendWidth = rows.length > 0 ? getLegendWidth(rows) : 0
|
|
114
|
+
const topHeight = Math.max(
|
|
115
|
+
includeMinimap ? minimapHeight : 0,
|
|
116
|
+
rowPanelsHeaderHeight,
|
|
117
|
+
treeOverviewHeight + (treeScaleBar ? treeScaleBarHeight : 0),
|
|
118
|
+
)
|
|
119
|
+
const gutterWidth = treeAreaWidth + rowPanelsWidth
|
|
93
120
|
|
|
94
121
|
// width stays content-only; the legend occupies an extra column added at the
|
|
95
122
|
// svg root in MsaSvg. The viewport export takes the alignment canvas's own
|
|
@@ -98,23 +125,24 @@ function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
|
|
|
98
125
|
// columns the scrollbars hide on screen
|
|
99
126
|
return opts.exportType === 'entire'
|
|
100
127
|
? {
|
|
101
|
-
width: totalWidth +
|
|
128
|
+
width: totalWidth + gutterWidth,
|
|
102
129
|
msaAreaWidth: totalWidth,
|
|
103
|
-
height: totalHeight + trackHeight,
|
|
130
|
+
height: totalHeight + trackHeight + topHeight,
|
|
104
131
|
contentHeight: totalHeight,
|
|
105
132
|
trackHeight,
|
|
133
|
+
topHeight,
|
|
106
134
|
offsetX: 0,
|
|
107
135
|
offsetY: 0,
|
|
108
136
|
includeMinimap,
|
|
109
137
|
legendWidth,
|
|
110
138
|
}
|
|
111
139
|
: {
|
|
112
|
-
width: msaCanvasWidth +
|
|
140
|
+
width: msaCanvasWidth + gutterWidth,
|
|
113
141
|
msaAreaWidth: msaCanvasWidth,
|
|
114
|
-
height:
|
|
115
|
-
msaAreaHeight + (includeMinimap ? minimapHeight : 0) + trackHeight,
|
|
142
|
+
height: msaAreaHeight + topHeight + trackHeight,
|
|
116
143
|
contentHeight: msaAreaHeight,
|
|
117
144
|
trackHeight,
|
|
145
|
+
topHeight,
|
|
118
146
|
offsetX: -scrollX,
|
|
119
147
|
offsetY: -scrollY,
|
|
120
148
|
includeMinimap,
|
|
@@ -147,7 +175,7 @@ export async function renderToSvg(model: MsaViewModel, opts: ExportSvgOptions) {
|
|
|
147
175
|
return svgSafeColors(
|
|
148
176
|
markup.replaceAll(/<g data-layer="([^"]+)"><\/g>/g, (match, id: string) => {
|
|
149
177
|
const ctx = layers.get(id)
|
|
150
|
-
return ctx ? `<g>${ctx.getSvg().innerHTML}</g>` : match
|
|
178
|
+
return ctx ? `<g id="${id}">${ctx.getSvg().innerHTML}</g>` : match
|
|
151
179
|
}),
|
|
152
180
|
)
|
|
153
181
|
}
|
|
@@ -165,15 +193,18 @@ function MsaSvg({
|
|
|
165
193
|
layout: Layout
|
|
166
194
|
layers: LayerMap
|
|
167
195
|
}) {
|
|
168
|
-
const { width, trackHeight, includeMinimap, legendWidth } = layout
|
|
169
|
-
const { treeAreaWidth,
|
|
196
|
+
const { width, trackHeight, includeMinimap, legendWidth, topHeight } = layout
|
|
197
|
+
const { treeAreaWidth, rowPanelsWidth } = model
|
|
170
198
|
const totalWidth = width + legendWidth
|
|
171
|
-
const legendTop =
|
|
172
|
-
// a short alignment with many
|
|
199
|
+
const legendTop = topHeight
|
|
200
|
+
// a short alignment with many legend entries has a key taller than its rows,
|
|
173
201
|
// and the page has to hold the whole key
|
|
174
202
|
const height =
|
|
175
203
|
legendWidth > 0
|
|
176
|
-
? Math.max(
|
|
204
|
+
? Math.max(
|
|
205
|
+
layout.height,
|
|
206
|
+
legendTop + legendHeight(legendRows(model.legends)),
|
|
207
|
+
)
|
|
177
208
|
: layout.height
|
|
178
209
|
const props = { Context, model, theme, layout, layers }
|
|
179
210
|
|
|
@@ -204,18 +235,37 @@ function MsaSvg({
|
|
|
204
235
|
height="100%"
|
|
205
236
|
fill={theme.palette.background.default}
|
|
206
237
|
/>
|
|
207
|
-
{
|
|
238
|
+
{topHeight > 0 ? (
|
|
208
239
|
<>
|
|
209
|
-
|
|
210
|
-
<
|
|
211
|
-
|
|
212
|
-
|
|
240
|
+
{includeMinimap ? (
|
|
241
|
+
<g
|
|
242
|
+
id="minimap-panel"
|
|
243
|
+
transform={`translate(${treeAreaWidth + rowPanelsWidth} 0)`}
|
|
244
|
+
>
|
|
245
|
+
<MinimapSVG model={model} theme={theme} />
|
|
246
|
+
</g>
|
|
247
|
+
) : null}
|
|
248
|
+
<RowPanelHeadersSVG
|
|
249
|
+
model={model}
|
|
250
|
+
theme={theme}
|
|
251
|
+
bandHeight={topHeight}
|
|
252
|
+
/>
|
|
253
|
+
{model.showTreeOverview ? (
|
|
254
|
+
<TreeOverviewSVG
|
|
255
|
+
model={model}
|
|
256
|
+
theme={theme}
|
|
257
|
+
Context={Context}
|
|
258
|
+
layers={layers}
|
|
259
|
+
/>
|
|
260
|
+
) : null}
|
|
261
|
+
<TreeScaleBarSVG model={model} theme={theme} />
|
|
262
|
+
<g transform={`translate(0 ${topHeight})`}>{body}</g>
|
|
213
263
|
</>
|
|
214
264
|
) : (
|
|
215
265
|
body
|
|
216
266
|
)}
|
|
217
267
|
{legendWidth > 0 ? (
|
|
218
|
-
<g transform={`translate(${width} ${legendTop})`}>
|
|
268
|
+
<g id="legend-panel" transform={`translate(${width} ${legendTop})`}>
|
|
219
269
|
<LegendSVG model={model} theme={theme} width={legendWidth} />
|
|
220
270
|
</g>
|
|
221
271
|
) : null}
|
|
@@ -223,8 +273,185 @@ function MsaSvg({
|
|
|
223
273
|
)
|
|
224
274
|
}
|
|
225
275
|
|
|
226
|
-
//
|
|
227
|
-
//
|
|
276
|
+
// The tree overview in the top band, over the tree column, where the live view
|
|
277
|
+
// puts it. It is part of the figure rather than chrome: the inset is what says
|
|
278
|
+
// where the focused clade sits in the whole tree.
|
|
279
|
+
function TreeOverviewSVG({
|
|
280
|
+
model,
|
|
281
|
+
theme,
|
|
282
|
+
Context,
|
|
283
|
+
layers,
|
|
284
|
+
}: {
|
|
285
|
+
model: MsaViewModel
|
|
286
|
+
theme: Theme
|
|
287
|
+
Context: typeof ContextType
|
|
288
|
+
layers: LayerMap
|
|
289
|
+
}) {
|
|
290
|
+
const { treeAreaWidth, overviewHeight, id } = model
|
|
291
|
+
const ctx = new Context(treeAreaWidth, overviewHeight)
|
|
292
|
+
renderTreeOverview({
|
|
293
|
+
model,
|
|
294
|
+
ctx,
|
|
295
|
+
theme,
|
|
296
|
+
width: treeAreaWidth,
|
|
297
|
+
height: overviewHeight,
|
|
298
|
+
})
|
|
299
|
+
return (
|
|
300
|
+
<ClipGroup
|
|
301
|
+
panelId="tree-overview"
|
|
302
|
+
clipId={`tree-overview-${id}`}
|
|
303
|
+
width={treeAreaWidth}
|
|
304
|
+
height={overviewHeight}
|
|
305
|
+
ctx={ctx}
|
|
306
|
+
layers={layers}
|
|
307
|
+
/>
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// The branch-length scale bar under the tree overview, where the live view puts
|
|
312
|
+
// it (see TreeRuler). A phylogram whose branch lengths are drawn to a scale
|
|
313
|
+
// nothing states is a figure a reader cannot measure, so the export carries the
|
|
314
|
+
// bar even though it is chrome on screen.
|
|
315
|
+
function TreeScaleBarSVG({
|
|
316
|
+
model,
|
|
317
|
+
theme,
|
|
318
|
+
}: {
|
|
319
|
+
model: MsaViewModel
|
|
320
|
+
theme: Theme
|
|
321
|
+
}) {
|
|
322
|
+
const { marginLeft, treeScaleBar: bar, treeOverviewHeight } = model
|
|
323
|
+
if (!bar) {
|
|
324
|
+
return null
|
|
325
|
+
}
|
|
326
|
+
const y = treeOverviewHeight + treeScaleBarHeight
|
|
327
|
+
const color = theme.palette.text.primary
|
|
328
|
+
return (
|
|
329
|
+
<g id="tree-scalebar">
|
|
330
|
+
<text x={marginLeft} y={y - 9} fontSize={10} fill={color}>
|
|
331
|
+
{bar.label}
|
|
332
|
+
</text>
|
|
333
|
+
<path
|
|
334
|
+
d={`M${marginLeft} ${y - 7} v6 h${bar.px} v-6`}
|
|
335
|
+
fill="none"
|
|
336
|
+
stroke={color}
|
|
337
|
+
/>
|
|
338
|
+
</g>
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Each strip's header over the column it names, turned on its side, where the
|
|
343
|
+
// live view puts it (see RowPanelHeaders). The baseline runs up the figure, so
|
|
344
|
+
// the name sits to the left of it, the way TrackLabelsSVG offsets a horizontal
|
|
345
|
+
// baseline by a third of the font size.
|
|
346
|
+
function RowPanelHeadersSVG({
|
|
347
|
+
model,
|
|
348
|
+
theme,
|
|
349
|
+
bandHeight,
|
|
350
|
+
}: {
|
|
351
|
+
model: MsaViewModel
|
|
352
|
+
theme: Theme
|
|
353
|
+
bandHeight: number
|
|
354
|
+
}) {
|
|
355
|
+
const { resolvedRowPanels, treeAreaWidth, fontSize } = model
|
|
356
|
+
const y = bandHeight - 4
|
|
357
|
+
return resolvedRowPanels.length === 0 ? null : (
|
|
358
|
+
<g id="rowpanel-headers">
|
|
359
|
+
{resolvedRowPanels.map(panel => {
|
|
360
|
+
const across = headerRunsAcross(panel.width)
|
|
361
|
+
const size = across ? fontSize : Math.min(fontSize, panel.width)
|
|
362
|
+
const center = treeAreaWidth + panel.offsetX + panel.width / 2
|
|
363
|
+
const x = across ? center : center + size / 3
|
|
364
|
+
const room = across ? panel.width : y - LEGEND_PAD
|
|
365
|
+
const maxChars = Math.floor(room / (size * CHAR_WIDTH))
|
|
366
|
+
return size < 5 ? null : (
|
|
367
|
+
<text
|
|
368
|
+
key={panel.id}
|
|
369
|
+
x={x}
|
|
370
|
+
y={y}
|
|
371
|
+
{...(across
|
|
372
|
+
? { textAnchor: 'middle' }
|
|
373
|
+
: { transform: `rotate(-90 ${x} ${y})` })}
|
|
374
|
+
fontSize={size}
|
|
375
|
+
fill={theme.palette.text.primary}
|
|
376
|
+
>
|
|
377
|
+
{panel.header.length > maxChars
|
|
378
|
+
? `${panel.header.slice(0, Math.max(1, maxChars - 1))}…`
|
|
379
|
+
: panel.header}
|
|
380
|
+
</text>
|
|
381
|
+
)
|
|
382
|
+
})}
|
|
383
|
+
</g>
|
|
384
|
+
)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// A label past the room it has, cut to what fits with an ellipsis. The measure
|
|
388
|
+
// is the one cladeGutterWidth sizes the gutter with, so a label the gutter
|
|
389
|
+
// holds keeps every character.
|
|
390
|
+
function clipCladeLabel(label: string, room: number, fontSize: number) {
|
|
391
|
+
const width = measureTextCanvas(label, fontSize)
|
|
392
|
+
if (width <= room) {
|
|
393
|
+
return label
|
|
394
|
+
}
|
|
395
|
+
const fits = Math.floor((label.length * room) / width)
|
|
396
|
+
return `${label.slice(0, Math.max(1, fits - 1))}…`
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Each bracket's label in the gutter beside its bar, where the live view puts
|
|
400
|
+
// it (see CladeLabels). The canvas layer draws the bar; a label longer than the
|
|
401
|
+
// gutter or the clade is clipped, since text that overruns runs off the figure.
|
|
402
|
+
function CladeLabelsSVG({
|
|
403
|
+
model,
|
|
404
|
+
theme,
|
|
405
|
+
offsetY,
|
|
406
|
+
}: {
|
|
407
|
+
model: MsaViewModel
|
|
408
|
+
theme: Theme
|
|
409
|
+
offsetY: number
|
|
410
|
+
}) {
|
|
411
|
+
const {
|
|
412
|
+
resolvedClades,
|
|
413
|
+
cladeGutterWidth,
|
|
414
|
+
rowHeight,
|
|
415
|
+
treeAreaWidth,
|
|
416
|
+
fontSize,
|
|
417
|
+
} = model
|
|
418
|
+
if (cladeGutterWidth === 0) {
|
|
419
|
+
return null
|
|
420
|
+
}
|
|
421
|
+
const gutterLeft = treeAreaWidth - cladeGutterWidth
|
|
422
|
+
return (
|
|
423
|
+
<g id="clade-labels">
|
|
424
|
+
{resolvedClades.map((clade, index) => {
|
|
425
|
+
const { label, markColor, rows } = clade
|
|
426
|
+
if (!label || !cladeInGutter(clade)) {
|
|
427
|
+
return null
|
|
428
|
+
}
|
|
429
|
+
const left = gutterLeft + cladeLabelOffset(clade)
|
|
430
|
+
const top = rows[0] * rowHeight - offsetY
|
|
431
|
+
const height = cladeHeight(clade, rowHeight)
|
|
432
|
+
const horizontal = cladeLabelHorizontal(clade, rowHeight, fontSize)
|
|
433
|
+
const room = horizontal ? treeAreaWidth - left - bracketGap : height
|
|
434
|
+
const x = horizontal ? left : left + fontSize
|
|
435
|
+
const y = horizontal ? top + height / 2 + fontSize / 3 : top + height
|
|
436
|
+
return (
|
|
437
|
+
<text
|
|
438
|
+
key={`${rows[0]}-${rows[1]}-${index}`}
|
|
439
|
+
x={x}
|
|
440
|
+
y={y}
|
|
441
|
+
transform={horizontal ? undefined : `rotate(-90 ${x} ${y})`}
|
|
442
|
+
fontSize={fontSize}
|
|
443
|
+
fill={markColor ?? theme.palette.text.primary}
|
|
444
|
+
>
|
|
445
|
+
{clipCladeLabel(label, room, fontSize)}
|
|
446
|
+
</text>
|
|
447
|
+
)
|
|
448
|
+
})}
|
|
449
|
+
</g>
|
|
450
|
+
)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// the color keys drawn as a reserved column to the right of the alignment,
|
|
454
|
+
// mirroring the on-screen AnnotationLegend overlay
|
|
228
455
|
function LegendSVG({
|
|
229
456
|
model,
|
|
230
457
|
theme,
|
|
@@ -234,8 +461,8 @@ function LegendSVG({
|
|
|
234
461
|
theme: Theme
|
|
235
462
|
width: number
|
|
236
463
|
}) {
|
|
237
|
-
const
|
|
238
|
-
const boxHeight = legendHeight(
|
|
464
|
+
const rows = legendRows(model.legends)
|
|
465
|
+
const boxHeight = legendHeight(rows)
|
|
239
466
|
// the column is capped at LEGEND_MAX_W, so a name too long for it has to be
|
|
240
467
|
// clipped here -- text that overruns the reserved width runs off the figure
|
|
241
468
|
const maxChars = Math.floor(
|
|
@@ -252,27 +479,30 @@ function LegendSVG({
|
|
|
252
479
|
stroke={theme.palette.divider}
|
|
253
480
|
rx={2}
|
|
254
481
|
/>
|
|
255
|
-
{
|
|
482
|
+
{rows.map((row, i) => {
|
|
256
483
|
const y = LEGEND_PAD + i * LEGEND_ROW_H
|
|
257
484
|
return (
|
|
258
|
-
<g key={
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
485
|
+
<g key={row.key}>
|
|
486
|
+
{row.color ? (
|
|
487
|
+
<rect
|
|
488
|
+
x={LEGEND_PAD}
|
|
489
|
+
y={y}
|
|
490
|
+
width={LEGEND_SWATCH}
|
|
491
|
+
height={LEGEND_SWATCH}
|
|
492
|
+
fill={row.color}
|
|
493
|
+
stroke={outlineColor(row.color)}
|
|
494
|
+
/>
|
|
495
|
+
) : null}
|
|
267
496
|
<text
|
|
268
|
-
x={LEGEND_TEXT_X}
|
|
497
|
+
x={row.color ? LEGEND_TEXT_X : LEGEND_PAD}
|
|
269
498
|
y={y + LEGEND_SWATCH - 1}
|
|
270
499
|
fontSize={LEGEND_FONT}
|
|
500
|
+
fontWeight={row.color ? undefined : 'bold'}
|
|
271
501
|
fill={theme.palette.text.primary}
|
|
272
502
|
>
|
|
273
|
-
{
|
|
274
|
-
? `${
|
|
275
|
-
:
|
|
503
|
+
{row.label.length > maxChars
|
|
504
|
+
? `${row.label.slice(0, Math.max(1, maxChars - 1))}…`
|
|
505
|
+
: row.label}
|
|
276
506
|
</text>
|
|
277
507
|
</g>
|
|
278
508
|
)
|
|
@@ -293,7 +523,7 @@ interface LayerProps {
|
|
|
293
523
|
|
|
294
524
|
function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
|
|
295
525
|
const { contentHeight, offsetX, offsetY, msaAreaWidth } = layout
|
|
296
|
-
const { treeAreaWidth, id } = model
|
|
526
|
+
const { treeAreaWidth, rowPanelsWidth, id } = model
|
|
297
527
|
|
|
298
528
|
const treeCtx = new Context(treeAreaWidth, contentHeight)
|
|
299
529
|
renderTreeCanvas({
|
|
@@ -305,11 +535,26 @@ function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
|
|
|
305
535
|
highResScaleFactorOverride: 1,
|
|
306
536
|
})
|
|
307
537
|
|
|
538
|
+
const rowPanelsCtx = rowPanelsWidth
|
|
539
|
+
? new Context(rowPanelsWidth, contentHeight)
|
|
540
|
+
: undefined
|
|
541
|
+
if (rowPanelsCtx) {
|
|
542
|
+
renderRowPanels({
|
|
543
|
+
model,
|
|
544
|
+
theme,
|
|
545
|
+
ctx: rowPanelsCtx,
|
|
546
|
+
offsetY,
|
|
547
|
+
blockSizeYOverride: contentHeight,
|
|
548
|
+
highResScaleFactorOverride: 1,
|
|
549
|
+
})
|
|
550
|
+
}
|
|
551
|
+
|
|
308
552
|
const raster = rasterBackground({ model, theme, layout })
|
|
309
553
|
|
|
310
554
|
const msaCtx = new Context(msaAreaWidth, contentHeight)
|
|
311
555
|
renderBoxFeatureCanvasBlock({
|
|
312
556
|
model,
|
|
557
|
+
theme,
|
|
313
558
|
ctx: msaCtx,
|
|
314
559
|
offsetX,
|
|
315
560
|
offsetY,
|
|
@@ -344,17 +589,31 @@ function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
|
|
|
344
589
|
return (
|
|
345
590
|
<>
|
|
346
591
|
<ClipGroup
|
|
592
|
+
panelId="tree-panel"
|
|
347
593
|
clipId={`tree-${id}`}
|
|
348
594
|
width={treeAreaWidth}
|
|
349
595
|
height={contentHeight}
|
|
350
596
|
ctx={treeCtx}
|
|
351
597
|
layers={layers}
|
|
352
598
|
/>
|
|
599
|
+
<CladeLabelsSVG model={model} theme={theme} offsetY={offsetY} />
|
|
600
|
+
{rowPanelsCtx ? (
|
|
601
|
+
<ClipGroup
|
|
602
|
+
panelId="rowpanels-panel"
|
|
603
|
+
clipId={`rowpanels-${id}`}
|
|
604
|
+
width={rowPanelsWidth}
|
|
605
|
+
height={contentHeight}
|
|
606
|
+
transform={`translate(${treeAreaWidth} 0)`}
|
|
607
|
+
ctx={rowPanelsCtx}
|
|
608
|
+
layers={layers}
|
|
609
|
+
/>
|
|
610
|
+
) : null}
|
|
353
611
|
<ClipGroup
|
|
612
|
+
panelId="msa-panel"
|
|
354
613
|
clipId={`msa-${id}`}
|
|
355
614
|
width={msaAreaWidth}
|
|
356
615
|
height={contentHeight}
|
|
357
|
-
transform={`translate(${treeAreaWidth} 0)`}
|
|
616
|
+
transform={`translate(${treeAreaWidth + rowPanelsWidth} 0)`}
|
|
358
617
|
ctx={msaCtx}
|
|
359
618
|
layers={layers}
|
|
360
619
|
underlay={raster}
|
|
@@ -364,15 +623,13 @@ function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
|
|
|
364
623
|
}
|
|
365
624
|
|
|
366
625
|
/**
|
|
367
|
-
* The alignment background as a single <image>,
|
|
368
|
-
*
|
|
369
|
-
*
|
|
626
|
+
* The alignment background as a single <image>, under the live canvas's
|
|
627
|
+
* conditions: no domain overlay boxes, background coloring on, and a canvas
|
|
628
|
+
* that reads back.
|
|
370
629
|
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
* image-rendering keeps those edges hard rather than smearing them, making it
|
|
375
|
-
* pixel-for-pixel what the rects drew.
|
|
630
|
+
* The vector path's <rect> per cell exhausts the heap on a real alignment. The
|
|
631
|
+
* image is one pixel per cell, and image-rendering keeps the upscaled cell
|
|
632
|
+
* edges hard, so it matches the rects pixel for pixel.
|
|
376
633
|
*/
|
|
377
634
|
function rasterBackground({
|
|
378
635
|
model,
|
|
@@ -423,7 +680,7 @@ function rasterBackground({
|
|
|
423
680
|
|
|
424
681
|
function TrackRendering({ model, theme, layout, Context, layers }: LayerProps) {
|
|
425
682
|
const { trackHeight, offsetX, msaAreaWidth } = layout
|
|
426
|
-
const { treeAreaWidth, id } = model
|
|
683
|
+
const { treeAreaWidth, rowPanelsWidth, id } = model
|
|
427
684
|
|
|
428
685
|
const ctx = new Context(msaAreaWidth, trackHeight)
|
|
429
686
|
renderAllTracks({
|
|
@@ -438,8 +695,9 @@ function TrackRendering({ model, theme, layout, Context, layers }: LayerProps) {
|
|
|
438
695
|
return (
|
|
439
696
|
<>
|
|
440
697
|
<TrackLabelsSVG model={model} theme={theme} />
|
|
441
|
-
<g transform={`translate(${treeAreaWidth} 0)`}>
|
|
698
|
+
<g transform={`translate(${treeAreaWidth + rowPanelsWidth} 0)`}>
|
|
442
699
|
<ClipGroup
|
|
700
|
+
panelId="tracks-panel"
|
|
443
701
|
clipId={`tracks-${id}`}
|
|
444
702
|
width={msaAreaWidth}
|
|
445
703
|
height={trackHeight}
|
|
@@ -465,14 +723,17 @@ function TrackLabelsSVG({
|
|
|
465
723
|
if (!drawLabels) {
|
|
466
724
|
return null
|
|
467
725
|
}
|
|
468
|
-
|
|
726
|
+
const labels = turnedOnTracks.map((track, i) => {
|
|
727
|
+
const { id, name, height } = track.model
|
|
728
|
+
const size = Math.min(height, fontSize)
|
|
729
|
+
const top = turnedOnTracks
|
|
730
|
+
.slice(0, i)
|
|
731
|
+
.reduce((acc, t) => acc + t.model.height, 0)
|
|
732
|
+
return { id, name, size, baseline: top + height / 2 + size / 3 }
|
|
733
|
+
})
|
|
469
734
|
return (
|
|
470
735
|
<>
|
|
471
|
-
{
|
|
472
|
-
const { id, name, height } = track.model
|
|
473
|
-
const size = Math.min(height, fontSize)
|
|
474
|
-
const baseline = y + height / 2 + size / 3
|
|
475
|
-
y += height
|
|
736
|
+
{labels.map(({ id, name, size, baseline }) => {
|
|
476
737
|
return size < 5 ? null : (
|
|
477
738
|
<text
|
|
478
739
|
key={id}
|
|
@@ -493,7 +754,12 @@ function TrackLabelsSVG({
|
|
|
493
754
|
// Clips a svgcanvas Context to its box and leaves a placeholder for renderToSvg
|
|
494
755
|
// to splice the layer's own markup into. `underlay` draws beneath that markup,
|
|
495
756
|
// inside the same clip, for the parts of a layer React emits directly.
|
|
757
|
+
//
|
|
758
|
+
// `panelId` names the spliced group in the output, the same name in every
|
|
759
|
+
// export, so a figure can be taken apart in Illustrator or svgutils. `clipId`
|
|
760
|
+
// carries the model id, which keeps two viewers on one page from colliding.
|
|
496
761
|
function ClipGroup({
|
|
762
|
+
panelId,
|
|
497
763
|
clipId,
|
|
498
764
|
width,
|
|
499
765
|
height,
|
|
@@ -502,6 +768,7 @@ function ClipGroup({
|
|
|
502
768
|
layers,
|
|
503
769
|
underlay,
|
|
504
770
|
}: {
|
|
771
|
+
panelId: string
|
|
505
772
|
clipId: string
|
|
506
773
|
width: number
|
|
507
774
|
height: number
|
|
@@ -510,7 +777,7 @@ function ClipGroup({
|
|
|
510
777
|
layers: LayerMap
|
|
511
778
|
underlay?: React.ReactNode
|
|
512
779
|
}) {
|
|
513
|
-
layers.set(
|
|
780
|
+
layers.set(panelId, ctx)
|
|
514
781
|
return (
|
|
515
782
|
<>
|
|
516
783
|
<defs>
|
|
@@ -520,7 +787,7 @@ function ClipGroup({
|
|
|
520
787
|
</defs>
|
|
521
788
|
<g clipPath={`url(#${clipId})`} transform={transform}>
|
|
522
789
|
{underlay}
|
|
523
|
-
<g data-layer={
|
|
790
|
+
<g data-layer={panelId} />
|
|
524
791
|
</g>
|
|
525
792
|
</>
|
|
526
793
|
)
|
|
@@ -79,9 +79,8 @@ function countBlanksBefore(blanks: number[], globalCol: number) {
|
|
|
79
79
|
* that is its own visible index; for a hidden one it is the index of the next
|
|
80
80
|
* visible column, i.e. where the hidden column collapses to.
|
|
81
81
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* in a hidden column.
|
|
82
|
+
* Spans use this, since globalColToVisibleCol drops a span whenever either end
|
|
83
|
+
* lands in a hidden column.
|
|
85
84
|
*
|
|
86
85
|
* @param blanks - Sorted array of global column indices that are hidden
|
|
87
86
|
* @param globalCol - The global column index in the full MSA
|
|
@@ -94,9 +93,8 @@ export function visibleColsBefore(blanks: number[], globalCol: number) {
|
|
|
94
93
|
* The ungapped position a row holds at a global column, or undefined when that
|
|
95
94
|
* column is a gap in the row or past its end.
|
|
96
95
|
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* the whole frame.
|
|
96
|
+
* Binary-searches the row's ascending seqPos index; it runs on every mouse
|
|
97
|
+
* move, and scanning a 30k-column row per event takes the whole frame.
|
|
100
98
|
*
|
|
101
99
|
* @param index - The row's seqPos -> global column index (buildSeqPosIndex)
|
|
102
100
|
* @param globalCol - The global column index in the full MSA
|