react-msaview 6.5.0 → 8.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 +13 -11
- package/bundle/index.js +122 -112
- package/bundle/index.js.map +4 -4
- 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 -2
- 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 +69 -4
- package/dist/components/MSAViewer.js +201 -8
- 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.d.ts +3 -3
- package/dist/components/ResizeHandles.js +13 -24
- package/dist/components/ResizeHandles.js.map +1 -1
- package/dist/components/SequenceTextArea.js +3 -3
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/Track.js +41 -22
- package/dist/components/Track.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/ExportSVGDialog.js +5 -3
- package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +13 -9
- 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 +7 -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 +35 -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/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 +7 -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 +40 -0
- package/dist/components/header/UnshareableDataWarning.js.map +1 -0
- package/dist/components/header/ZoomControls.js +49 -6
- package/dist/components/header/ZoomControls.js.map +1 -1
- package/dist/components/header/getAnnotationsMenu.js +11 -14
- package/dist/components/header/getAnnotationsMenu.js.map +1 -1
- package/dist/components/header/settingsMenuItems.js +10 -7
- 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/ImportFormExamples.js +2 -6
- package/dist/components/import/ImportFormExamples.js.map +1 -1
- package/dist/components/import/util.js +2 -0
- 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 +1 -1
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +17 -20
- 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 +53 -0
- package/dist/components/msa/drawFeatureSpans.js +78 -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 +30 -27
- package/dist/components/msa/msaRaster.js +193 -90
- 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 +51 -87
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderHighlights.d.ts +44 -4
- package/dist/components/msa/renderHighlights.js +99 -11
- package/dist/components/msa/renderHighlights.js.map +1 -1
- package/dist/components/msa/renderMSABlock.d.ts +1 -2
- package/dist/components/msa/renderMSABlock.js +28 -31
- 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/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/renderCtx.d.ts +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 +13 -16
- 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 +30 -12
- package/dist/components/tracks/drawTracks.js +127 -34
- 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/TreeCanvas.js +6 -4
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +6 -21
- package/dist/components/tree/TreeCanvasBlock.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 +11 -5
- 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.d.ts +0 -1
- package/dist/components/tree/renderTreeCanvas.js +221 -47
- 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/scaleBar.d.ts +10 -0
- package/dist/components/tree/scaleBar.js +24 -0
- package/dist/components/tree/scaleBar.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 +18 -1
- package/dist/constants.js +51 -8
- 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/fetchUtils.d.ts +7 -7
- package/dist/fetchUtils.js +4 -32
- package/dist/fetchUtils.js.map +1 -1
- 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 +29 -1
- package/dist/hierarchy.js +82 -19
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/model/DataModel.js +4 -4
- 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 +792 -259
- package/dist/model.js +2092 -675
- 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 +15 -13
- package/dist/renderToStaticMarkup.js +37 -25
- package/dist/renderToStaticMarkup.js.map +1 -1
- package/dist/renderToSvg.js +198 -68
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.d.ts +26 -28
- package/dist/rowCoordinateCalculations.js +13 -39
- 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 +5 -4
- 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 +306 -124
- package/dist/types.d.ts +295 -3
- 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/util.d.ts +10 -4
- package/dist/util.js +37 -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 +23 -14
- package/src/colorSchemes.ts +1 -2
- package/src/columnStats.ts +58 -0
- package/src/components/DragHandle.tsx +18 -7
- package/src/components/Loading.tsx +14 -8
- package/src/components/MSAView.tsx +19 -5
- package/src/components/MSAViewer.tsx +326 -10
- package/src/components/PortalTooltip.tsx +47 -0
- package/src/components/ResizeHandles.tsx +21 -36
- package/src/components/SequenceTextArea.tsx +3 -3
- package/src/components/Track.tsx +55 -18
- package/src/components/dialogs/AnnotationFileDialog.tsx +162 -0
- package/src/components/dialogs/ExportSVGDialog.tsx +7 -2
- package/src/components/dialogs/FeatureDialog.tsx +13 -9
- package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
- package/src/components/header/GappynessSlider.tsx +6 -11
- package/src/components/header/Header.tsx +10 -0
- package/src/components/header/HeaderStatusArea.tsx +0 -5
- package/src/components/header/LoadWarnings.tsx +48 -0
- package/src/components/header/MultiAlignmentSelector.tsx +0 -0
- package/src/components/header/ScrollZoomIcons.tsx +52 -0
- package/src/components/header/TreeSettingsMenu.tsx +7 -1
- package/src/components/header/UnshareableDataWarning.tsx +56 -0
- package/src/components/header/ZoomControls.tsx +74 -14
- package/src/components/header/getAnnotationsMenu.ts +13 -17
- package/src/components/header/settingsMenuItems.ts +11 -6
- package/src/components/import/ImportForm.tsx +2 -2
- package/src/components/import/ImportFormExamples.tsx +4 -8
- package/src/components/import/util.ts +2 -0
- package/src/components/minimap/Minimap.tsx +1 -2
- package/src/components/msa/AnnotationLegend.tsx +48 -38
- package/src/components/msa/MSACanvas.tsx +3 -5
- package/src/components/msa/MSACanvasBlock.tsx +17 -21
- package/src/components/msa/MSAMouseoverCanvas.tsx +2 -1
- package/src/components/msa/domainBandCursor.ts +7 -11
- package/src/components/msa/drawFeatureSpans.ts +150 -0
- package/src/components/msa/legendRows.ts +24 -0
- package/src/components/msa/msaRaster.ts +236 -88
- package/src/components/msa/packDomainLanes.ts +36 -0
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +69 -123
- package/src/components/msa/renderHighlights.ts +141 -10
- package/src/components/msa/renderMSABlock.ts +29 -38
- package/src/components/msa/renderMSAMouseover.ts +9 -39
- package/src/components/msa/tileColor.ts +4 -7
- package/src/components/msa/useMsaBlockMouse.ts +6 -6
- package/src/components/renderCtx.ts +1 -0
- 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 +13 -18
- package/src/components/tracks/TrackTooltipContent.tsx +156 -0
- package/src/components/tracks/drawTracks.ts +169 -38
- package/src/components/tracks/useTrackHover.ts +42 -0
- package/src/components/tree/CladeLabels.tsx +77 -0
- package/src/components/tree/TreeCanvas.tsx +4 -3
- package/src/components/tree/TreeCanvasBlock.tsx +10 -32
- package/src/components/tree/TreeOverview.tsx +106 -0
- package/src/components/tree/TreeRuler.tsx +29 -5
- package/src/components/tree/cladeBrackets.ts +73 -0
- package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +2 -2
- package/src/components/tree/renderTreeCanvas.ts +312 -48
- package/src/components/tree/renderTreeOverview.ts +212 -0
- package/src/components/tree/scaleBar.ts +23 -0
- package/src/components/tree/useTreeHover.ts +2 -4
- package/src/constants.ts +61 -8
- package/src/createPaletteMap.ts +10 -9
- package/src/element.ts +321 -0
- package/src/featureFields.ts +19 -0
- package/src/fetchUtils.ts +7 -40
- package/src/flatToTree.ts +1 -2
- package/src/ggplotPalettes.ts +60 -0
- package/src/hierarchy.ts +97 -20
- package/src/index.ts +46 -6
- package/src/model/DataModel.ts +5 -4
- package/src/model/msaModel.ts +3 -2
- package/src/model/treeModel.ts +42 -0
- package/src/model.ts +2492 -793
- package/src/mount.tsx +32 -0
- package/src/neighborJoining.ts +8 -32
- package/src/rawImports.d.ts +9 -0
- package/src/renderToStaticMarkup.ts +41 -25
- package/src/renderToSvg.tsx +365 -92
- package/src/rowCoordinateCalculations.ts +16 -62
- package/src/scales.ts +47 -0
- package/src/seqPosToGlobalCol.ts +5 -4
- package/src/sequenceLogo.ts +6 -11
- package/src/stripDefault.ts +7 -14
- package/src/types.ts +352 -12
- package/src/umd.ts +4 -2
- package/src/useCanvasAutorun.ts +5 -10
- package/src/useMsaSvgFigure.ts +72 -0
- package/src/util.ts +40 -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/components/msa/ColumnStats.js +0 -42
- package/dist/components/msa/ColumnStats.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/ColumnStats.tsx +0 -54
- 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
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { HierarchyNode } from '../../hierarchy.ts'
|
|
2
|
+
import type { Annotation, NodeWithIdsAndLength } from '../../types.ts'
|
|
3
|
+
import type { RenderCtx } from '../renderCtx.ts'
|
|
4
|
+
|
|
5
|
+
/** one drawn span: a feature, the lane it sits in, and its row's depth */
|
|
6
|
+
export interface SpanBand {
|
|
7
|
+
annotation: Annotation
|
|
8
|
+
lane: number
|
|
9
|
+
laneCount: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** one row's spans, at the row baseline `y` the tree layout gives it */
|
|
13
|
+
export interface SpanRow<T extends SpanBand> {
|
|
14
|
+
y: number
|
|
15
|
+
bands: T[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Where a band lands inside its row. The alignment overlay fills the row, bars
|
|
20
|
+
* it along the bottom, or stacks thin sub-rows; a `features` row panel divides
|
|
21
|
+
* the row between its lanes.
|
|
22
|
+
*/
|
|
23
|
+
export interface SpanLayout {
|
|
24
|
+
/** the band height in a row needing `laneCount` lanes */
|
|
25
|
+
height: (laneCount: number) => number
|
|
26
|
+
/** the band's top edge, from its row baseline, its lane and its height */
|
|
27
|
+
top: (y: number, lane: number, height: number) => number
|
|
28
|
+
/** how far a strand head reaches past the body */
|
|
29
|
+
headLength: (height: number) => number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** the rows of `bandsByRow` that `leaves` covers, in display order */
|
|
33
|
+
export function spanRows<T extends SpanBand>(
|
|
34
|
+
leaves: HierarchyNode<NodeWithIdsAndLength>[],
|
|
35
|
+
bandsByRow: Map<string, T[]>,
|
|
36
|
+
): SpanRow<T>[] {
|
|
37
|
+
const rows: SpanRow<T>[] = []
|
|
38
|
+
for (const leaf of leaves) {
|
|
39
|
+
const bands = bandsByRow.get(leaf.data.name)
|
|
40
|
+
if (bands && bands.length > 0) {
|
|
41
|
+
rows.push({ y: leaf.x!, bands })
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return rows
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The span mark, in whatever x space the caller maps its bands into: alignment
|
|
49
|
+
* columns for the domain overlay, panel pixels for a `features` row panel. A
|
|
50
|
+
* feature with a strand draws as an arrow, one without as a box, and a label
|
|
51
|
+
* draws inside the span wherever the text fits.
|
|
52
|
+
*
|
|
53
|
+
* `xOf` returns the span's `[xStart, xEnd]` in the context's coordinates, and
|
|
54
|
+
* the context is translated so that `y - rowHeight` is the row's top edge.
|
|
55
|
+
*/
|
|
56
|
+
export function drawFeatureSpans<T extends SpanBand>({
|
|
57
|
+
ctx,
|
|
58
|
+
rows,
|
|
59
|
+
layout,
|
|
60
|
+
xOf,
|
|
61
|
+
colors,
|
|
62
|
+
labelOf,
|
|
63
|
+
labelDrawn,
|
|
64
|
+
contrastText,
|
|
65
|
+
xMin,
|
|
66
|
+
xMax,
|
|
67
|
+
}: {
|
|
68
|
+
ctx: RenderCtx
|
|
69
|
+
rows: SpanRow<T>[]
|
|
70
|
+
layout: SpanLayout
|
|
71
|
+
xOf: (band: T) => [number, number]
|
|
72
|
+
colors: Map<Annotation, { fill: string; stroke: string }>
|
|
73
|
+
labelOf?: (band: T) => string | undefined
|
|
74
|
+
labelDrawn?: (band: T) => void
|
|
75
|
+
contrastText: (color: string) => string
|
|
76
|
+
xMin: number
|
|
77
|
+
xMax: number
|
|
78
|
+
}) {
|
|
79
|
+
for (const { y, bands } of rows) {
|
|
80
|
+
const h = layout.height(bands[0]!.laneCount)
|
|
81
|
+
const headLen = layout.headLength(h)
|
|
82
|
+
for (const band of bands) {
|
|
83
|
+
const [xStart, xEnd] = xOf(band)
|
|
84
|
+
const w = xEnd - xStart
|
|
85
|
+
if (xEnd < xMin || xStart > xMax) {
|
|
86
|
+
continue
|
|
87
|
+
}
|
|
88
|
+
const t = layout.top(y, band.lane, h)
|
|
89
|
+
const { fill, stroke } = colors.get(band.annotation)!
|
|
90
|
+
ctx.fillStyle = fill
|
|
91
|
+
ctx.strokeStyle = stroke
|
|
92
|
+
const { strand } = band.annotation
|
|
93
|
+
if (strand === undefined) {
|
|
94
|
+
ctx.fillRect(xStart, t, w, h)
|
|
95
|
+
ctx.strokeRect(xStart, t, w, h)
|
|
96
|
+
} else {
|
|
97
|
+
drawGeneArrow({ ctx, x: xStart, t, w, h, headLen, strand })
|
|
98
|
+
}
|
|
99
|
+
const label = labelOf?.(band)
|
|
100
|
+
if (label !== undefined) {
|
|
101
|
+
const fontSize = Math.min(h - 2, 11)
|
|
102
|
+
ctx.font = `${fontSize}px sans-serif`
|
|
103
|
+
if (ctx.measureText(label).width + 2 <= w) {
|
|
104
|
+
labelDrawn?.(band)
|
|
105
|
+
ctx.fillStyle = contrastText(fill)
|
|
106
|
+
ctx.textAlign = 'center'
|
|
107
|
+
ctx.textBaseline = 'middle'
|
|
108
|
+
ctx.fillText(label, xStart + w / 2, t + h / 2)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// A gene arrow: a full-width rectangular body spanning the feature's
|
|
116
|
+
// start..end columns, plus a triangular head that points *beyond* that end in
|
|
117
|
+
// the strand direction (right for +, left for -). Keeping the body aligned to
|
|
118
|
+
// the exact start/end columns means + and - strand features read as having the
|
|
119
|
+
// same boundaries; the arrow lives outside them purely to show transcription
|
|
120
|
+
// direction, preserving the column-by-column homology down the rows.
|
|
121
|
+
function drawGeneArrow({
|
|
122
|
+
ctx,
|
|
123
|
+
x,
|
|
124
|
+
t,
|
|
125
|
+
w,
|
|
126
|
+
h,
|
|
127
|
+
headLen,
|
|
128
|
+
strand,
|
|
129
|
+
}: {
|
|
130
|
+
ctx: RenderCtx
|
|
131
|
+
x: number
|
|
132
|
+
t: number
|
|
133
|
+
w: number
|
|
134
|
+
h: number
|
|
135
|
+
headLen: number
|
|
136
|
+
strand: number
|
|
137
|
+
}) {
|
|
138
|
+
const dir = strand > 0 ? 1 : -1
|
|
139
|
+
const bodyStart = strand > 0 ? x : x + w
|
|
140
|
+
const bodyEnd = strand > 0 ? x + w : x
|
|
141
|
+
ctx.beginPath()
|
|
142
|
+
ctx.moveTo(bodyStart, t)
|
|
143
|
+
ctx.lineTo(bodyEnd, t)
|
|
144
|
+
ctx.lineTo(bodyEnd + dir * headLen, t + h / 2)
|
|
145
|
+
ctx.lineTo(bodyEnd, t + h)
|
|
146
|
+
ctx.lineTo(bodyStart, t + h)
|
|
147
|
+
ctx.closePath()
|
|
148
|
+
ctx.fill()
|
|
149
|
+
ctx.stroke()
|
|
150
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Legend } from '../../types.ts'
|
|
2
|
+
|
|
3
|
+
export interface LegendRow {
|
|
4
|
+
key: string
|
|
5
|
+
label: string
|
|
6
|
+
color?: string
|
|
7
|
+
hint?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// the legends flattened into the rows both the overlay and the export stack
|
|
11
|
+
// top to bottom. A title row separates one legend from the next, and a lone
|
|
12
|
+
// legend needs no title.
|
|
13
|
+
export function legendRows(legends: Legend[]): LegendRow[] {
|
|
14
|
+
const titled = legends.length > 1
|
|
15
|
+
return legends.flatMap(legend => [
|
|
16
|
+
...(titled ? [{ key: legend.id, label: legend.title }] : []),
|
|
17
|
+
...legend.entries.map(entry => ({
|
|
18
|
+
key: `${legend.id}:${entry.id}`,
|
|
19
|
+
label: entry.label,
|
|
20
|
+
color: entry.color,
|
|
21
|
+
hint: entry.id,
|
|
22
|
+
})),
|
|
23
|
+
])
|
|
24
|
+
}
|
|
@@ -36,9 +36,8 @@ const colorCache = new Map<string, number>()
|
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* CSS color string -> the packed pixel an ImageData holds for it. Painting a 1x1
|
|
39
|
-
* canvas and reading it back
|
|
40
|
-
*
|
|
41
|
-
* each distinct color is resolved once per session rather than once per cell.
|
|
39
|
+
* canvas and reading it back parses hex, rgb(), hsl() and named colors; the
|
|
40
|
+
* cache resolves each distinct color once per session.
|
|
42
41
|
*/
|
|
43
42
|
export function cssColorToPixel(css: string) {
|
|
44
43
|
const hit = colorCache.get(css)
|
|
@@ -63,14 +62,29 @@ export function cssColorToPixel(css: string) {
|
|
|
63
62
|
return packed
|
|
64
63
|
}
|
|
65
64
|
|
|
65
|
+
function cellPixelFn(spec: RasterSpec, toPixel: (css: string) => number) {
|
|
66
|
+
const { columns, relativeTo, colorAt, bg, hover } = spec
|
|
67
|
+
const bgPixel = toPixel(bg)
|
|
68
|
+
const hoverPixel = toPixel(hover)
|
|
69
|
+
const reference = relativeTo ? columns.get(relativeTo) : undefined
|
|
70
|
+
return (name: string, col: number, letter: string) => {
|
|
71
|
+
if (reference && name !== relativeTo && letter === reference[col]) {
|
|
72
|
+
return hoverPixel
|
|
73
|
+
}
|
|
74
|
+
const color = colorAt(col, letter)
|
|
75
|
+
return color === undefined ? bgPixel : toPixel(color)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
/**
|
|
67
80
|
* Packs a rectangle of the alignment into one pixel per cell, in `rowNames`
|
|
68
81
|
* order, ready to hand to putImageData. Cells past the end of a row, and rows
|
|
69
|
-
* with no sequence
|
|
70
|
-
* also leaves behind.
|
|
82
|
+
* with no sequence, stay transparent, as in the per-cell painter.
|
|
71
83
|
*
|
|
72
|
-
* `colStep`/`rowStep` sample
|
|
73
|
-
*
|
|
84
|
+
* `colStep`/`rowStep` sample every nth cell, which fits a whole alignment into
|
|
85
|
+
* the minimap's 12px bar. `colSpan`/`rowSpan` average each block of that many
|
|
86
|
+
* cells into one pixel, so a zoom below a device pixel per column still shows
|
|
87
|
+
* every column's color without blending neighbouring rows.
|
|
74
88
|
*/
|
|
75
89
|
export function rasterPixels({
|
|
76
90
|
spec,
|
|
@@ -80,7 +94,10 @@ export function rasterPixels({
|
|
|
80
94
|
height,
|
|
81
95
|
colStep = 1,
|
|
82
96
|
rowStep = 1,
|
|
97
|
+
colSpan = 1,
|
|
98
|
+
rowSpan = 1,
|
|
83
99
|
toPixel = cssColorToPixel,
|
|
100
|
+
out = new Uint32Array(width * height),
|
|
84
101
|
}: {
|
|
85
102
|
spec: RasterSpec
|
|
86
103
|
col0: number
|
|
@@ -89,13 +106,27 @@ export function rasterPixels({
|
|
|
89
106
|
height: number
|
|
90
107
|
colStep?: number
|
|
91
108
|
rowStep?: number
|
|
109
|
+
colSpan?: number
|
|
110
|
+
rowSpan?: number
|
|
92
111
|
toPixel?: (css: string) => number
|
|
112
|
+
out?: Uint32Array
|
|
93
113
|
}) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
114
|
+
if (colSpan > 1 || rowSpan > 1) {
|
|
115
|
+
averagePixels({
|
|
116
|
+
spec,
|
|
117
|
+
col0,
|
|
118
|
+
row0,
|
|
119
|
+
width,
|
|
120
|
+
height,
|
|
121
|
+
colSpan,
|
|
122
|
+
rowSpan,
|
|
123
|
+
toPixel,
|
|
124
|
+
out,
|
|
125
|
+
})
|
|
126
|
+
return out
|
|
127
|
+
}
|
|
128
|
+
const { rowNames, columns } = spec
|
|
129
|
+
const cellPixel = cellPixelFn(spec, toPixel)
|
|
99
130
|
|
|
100
131
|
for (let r = 0; r < height; r++) {
|
|
101
132
|
const name = rowNames[row0 + r * rowStep]
|
|
@@ -106,7 +137,6 @@ export function rasterPixels({
|
|
|
106
137
|
if (!str) {
|
|
107
138
|
continue
|
|
108
139
|
}
|
|
109
|
-
const isReference = name === relativeTo
|
|
110
140
|
const base = r * width
|
|
111
141
|
for (let c = 0; c < width; c++) {
|
|
112
142
|
const col = col0 + c * colStep
|
|
@@ -114,21 +144,92 @@ export function rasterPixels({
|
|
|
114
144
|
if (letter === undefined) {
|
|
115
145
|
break
|
|
116
146
|
}
|
|
117
|
-
|
|
118
|
-
out[base + c] = hoverPixel
|
|
119
|
-
} else {
|
|
120
|
-
const color = colorAt(col, letter)
|
|
121
|
-
out[base + c] = color === undefined ? bgPixel : toPixel(color)
|
|
122
|
-
}
|
|
147
|
+
out[base + c] = cellPixel(name, col, letter)
|
|
123
148
|
}
|
|
124
149
|
}
|
|
125
150
|
return out
|
|
126
151
|
}
|
|
127
152
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
153
|
+
function averagePixels({
|
|
154
|
+
spec,
|
|
155
|
+
col0,
|
|
156
|
+
row0,
|
|
157
|
+
width,
|
|
158
|
+
height,
|
|
159
|
+
colSpan,
|
|
160
|
+
rowSpan,
|
|
161
|
+
toPixel,
|
|
162
|
+
out,
|
|
163
|
+
}: {
|
|
164
|
+
spec: RasterSpec
|
|
165
|
+
col0: number
|
|
166
|
+
row0: number
|
|
167
|
+
width: number
|
|
168
|
+
height: number
|
|
169
|
+
colSpan: number
|
|
170
|
+
rowSpan: number
|
|
171
|
+
toPixel: (css: string) => number
|
|
172
|
+
out: Uint32Array
|
|
173
|
+
}) {
|
|
174
|
+
const { rowNames, columns } = spec
|
|
175
|
+
const cellPixel = cellPixelFn(spec, toPixel)
|
|
176
|
+
const cells = colSpan * rowSpan
|
|
177
|
+
const rShift = littleEndian ? 0 : 24
|
|
178
|
+
const gShift = littleEndian ? 8 : 16
|
|
179
|
+
const bShift = littleEndian ? 16 : 8
|
|
180
|
+
const aShift = littleEndian ? 24 : 0
|
|
181
|
+
const names: string[] = []
|
|
182
|
+
const strs: string[] = []
|
|
183
|
+
|
|
184
|
+
for (let r = 0; r < height; r++) {
|
|
185
|
+
names.length = 0
|
|
186
|
+
strs.length = 0
|
|
187
|
+
for (let dr = 0; dr < rowSpan; dr++) {
|
|
188
|
+
const name = rowNames[row0 + r * rowSpan + dr]
|
|
189
|
+
const str = name === undefined ? undefined : columns.get(name)
|
|
190
|
+
if (name !== undefined && str) {
|
|
191
|
+
names.push(name)
|
|
192
|
+
strs.push(str)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const base = r * width
|
|
196
|
+
for (let c = 0; c < width; c++) {
|
|
197
|
+
// premultiplied, so a cell past the end of a row dilutes the alpha of its
|
|
198
|
+
// pixel rather than darkening its color
|
|
199
|
+
let sumR = 0
|
|
200
|
+
let sumG = 0
|
|
201
|
+
let sumB = 0
|
|
202
|
+
let sumA = 0
|
|
203
|
+
for (let i = 0; i < strs.length; i++) {
|
|
204
|
+
const str = strs[i]!
|
|
205
|
+
const name = names[i]!
|
|
206
|
+
for (let dc = 0; dc < colSpan; dc++) {
|
|
207
|
+
const col = col0 + c * colSpan + dc
|
|
208
|
+
const letter = str[col]
|
|
209
|
+
if (letter === undefined) {
|
|
210
|
+
break
|
|
211
|
+
}
|
|
212
|
+
const p = cellPixel(name, col, letter)
|
|
213
|
+
const a = (p >>> aShift) & 255
|
|
214
|
+
sumR += ((p >>> rShift) & 255) * a
|
|
215
|
+
sumG += ((p >>> gShift) & 255) * a
|
|
216
|
+
sumB += ((p >>> bShift) & 255) * a
|
|
217
|
+
sumA += a
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
out[base + c] =
|
|
221
|
+
sumA === 0
|
|
222
|
+
? 0
|
|
223
|
+
: packRgba(
|
|
224
|
+
Math.round(sumR / sumA),
|
|
225
|
+
Math.round(sumG / sumA),
|
|
226
|
+
Math.round(sumB / sumA),
|
|
227
|
+
Math.round(sumA / cells),
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
132
233
|
function usableRasterCtx(ctx: unknown): ctx is RasterCtx {
|
|
133
234
|
const c = ctx as Record<string, unknown> | null
|
|
134
235
|
return (
|
|
@@ -154,13 +255,17 @@ function makeRasterCanvas(width: number, height: number) {
|
|
|
154
255
|
: undefined
|
|
155
256
|
}
|
|
156
257
|
|
|
157
|
-
function paint(
|
|
258
|
+
function paint(
|
|
259
|
+
width: number,
|
|
260
|
+
height: number,
|
|
261
|
+
fill: (out: Uint32Array) => void,
|
|
262
|
+
) {
|
|
158
263
|
const made = makeRasterCanvas(width, height)
|
|
159
264
|
if (!made) {
|
|
160
265
|
return undefined
|
|
161
266
|
}
|
|
162
267
|
const image = made.ctx.createImageData(width, height)
|
|
163
|
-
new Uint32Array(image.data.buffer)
|
|
268
|
+
fill(new Uint32Array(image.data.buffer))
|
|
164
269
|
made.ctx.putImageData(image, 0, 0)
|
|
165
270
|
return made.canvas
|
|
166
271
|
}
|
|
@@ -185,8 +290,8 @@ interface RasterCache {
|
|
|
185
290
|
|
|
186
291
|
const caches = new WeakMap<object, RasterCache>()
|
|
187
292
|
|
|
188
|
-
// Everything a cell's color depends on
|
|
189
|
-
//
|
|
293
|
+
// Everything a cell's color depends on; a change to any of them starts a fresh
|
|
294
|
+
// cache.
|
|
190
295
|
function rasterKeys(model: MsaViewModel, theme: Theme) {
|
|
191
296
|
return [
|
|
192
297
|
model.columns,
|
|
@@ -223,8 +328,29 @@ function getCache(model: MsaViewModel, theme: Theme) {
|
|
|
223
328
|
return next
|
|
224
329
|
}
|
|
225
330
|
|
|
226
|
-
|
|
227
|
-
|
|
331
|
+
// Cells averaged into one tile pixel along an axis: the smallest power of two
|
|
332
|
+
// that brings a pixel back to at least one device pixel. Powers of two keep the
|
|
333
|
+
// number of distinct tile sets a zoom sweep builds to a handful.
|
|
334
|
+
function cellsPerPixel(devicePixelsPerCell: number) {
|
|
335
|
+
return devicePixelsPerCell >= 1
|
|
336
|
+
? 1
|
|
337
|
+
: 2 ** Math.ceil(Math.log2(1 / devicePixelsPerCell))
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function getTile({
|
|
341
|
+
cache,
|
|
342
|
+
tileRow,
|
|
343
|
+
tileCol,
|
|
344
|
+
colSpan,
|
|
345
|
+
rowSpan,
|
|
346
|
+
}: {
|
|
347
|
+
cache: RasterCache
|
|
348
|
+
tileRow: number
|
|
349
|
+
tileCol: number
|
|
350
|
+
colSpan: number
|
|
351
|
+
rowSpan: number
|
|
352
|
+
}) {
|
|
353
|
+
const key = `${colSpan}_${rowSpan}_${tileRow}_${tileCol}`
|
|
228
354
|
const hit = cache.tiles.get(key)
|
|
229
355
|
if (hit) {
|
|
230
356
|
// re-inserting makes this the newest entry: a Map iterates in insertion
|
|
@@ -233,18 +359,31 @@ function getTile(cache: RasterCache, tileRow: number, tileCol: number) {
|
|
|
233
359
|
cache.tiles.set(key, hit)
|
|
234
360
|
return hit
|
|
235
361
|
}
|
|
236
|
-
const col0 = tileCol * rasterTileSize
|
|
237
|
-
const row0 = tileRow * rasterTileSize
|
|
238
|
-
const width = Math.min(
|
|
239
|
-
|
|
362
|
+
const col0 = tileCol * rasterTileSize * colSpan
|
|
363
|
+
const row0 = tileRow * rasterTileSize * rowSpan
|
|
364
|
+
const width = Math.min(
|
|
365
|
+
rasterTileSize,
|
|
366
|
+
Math.ceil((cache.numColumns - col0) / colSpan),
|
|
367
|
+
)
|
|
368
|
+
const height = Math.min(
|
|
369
|
+
rasterTileSize,
|
|
370
|
+
Math.ceil((cache.numRows - row0) / rowSpan),
|
|
371
|
+
)
|
|
240
372
|
if (width <= 0 || height <= 0) {
|
|
241
373
|
return undefined
|
|
242
374
|
}
|
|
243
|
-
const canvas = paint(
|
|
244
|
-
rasterPixels({
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
375
|
+
const canvas = paint(width, height, out => {
|
|
376
|
+
rasterPixels({
|
|
377
|
+
spec: cache.spec,
|
|
378
|
+
col0,
|
|
379
|
+
row0,
|
|
380
|
+
width,
|
|
381
|
+
height,
|
|
382
|
+
colSpan,
|
|
383
|
+
rowSpan,
|
|
384
|
+
out,
|
|
385
|
+
})
|
|
386
|
+
})
|
|
248
387
|
if (!canvas) {
|
|
249
388
|
return undefined
|
|
250
389
|
}
|
|
@@ -261,8 +400,12 @@ function getTile(cache: RasterCache, tileRow: number, tileCol: number) {
|
|
|
261
400
|
/**
|
|
262
401
|
* Paints the alignment background of one MSA block from cached
|
|
263
402
|
* one-pixel-per-cell tiles. The tiles do not depend on the zoom level, so a zoom
|
|
264
|
-
* frame costs a handful of drawImage calls
|
|
265
|
-
*
|
|
403
|
+
* frame costs a handful of drawImage calls. A fillRect per visible cell measured
|
|
404
|
+
* 885ms per block at the minimum column width.
|
|
405
|
+
*
|
|
406
|
+
* Below a device pixel per cell the tiles average cells together, per axis.
|
|
407
|
+
* The browser's image smoothing blurs both axes, which turns every row
|
|
408
|
+
* boundary into a gradient at fit-to-width, where only the columns are narrow.
|
|
266
409
|
*/
|
|
267
410
|
export function drawMsaRaster({
|
|
268
411
|
ctx,
|
|
@@ -279,17 +422,18 @@ export function drawMsaRaster({
|
|
|
279
422
|
}) {
|
|
280
423
|
const { colWidth, rowHeight, blockSize, highResScaleFactor } = model
|
|
281
424
|
const cache = getCache(model, theme)
|
|
425
|
+
const colSpan = cellsPerPixel(colWidth * highResScaleFactor)
|
|
426
|
+
const rowSpan = cellsPerPixel(rowHeight * highResScaleFactor)
|
|
282
427
|
|
|
283
428
|
ctx.resetTransform()
|
|
284
429
|
ctx.scale(highResScaleFactor, highResScaleFactor)
|
|
285
430
|
ctx.translate(-offsetX, -offsetY)
|
|
286
|
-
|
|
287
|
-
// for, so let the browser downsample it rather than letting whichever cell
|
|
288
|
-
// landed last win
|
|
289
|
-
ctx.imageSmoothingEnabled = colWidth < 1 || rowHeight < 1
|
|
431
|
+
ctx.imageSmoothingEnabled = false
|
|
290
432
|
|
|
291
|
-
const
|
|
292
|
-
const
|
|
433
|
+
const pixelWidth = colWidth * colSpan
|
|
434
|
+
const pixelHeight = rowHeight * rowSpan
|
|
435
|
+
const tileWidth = rasterTileSize * pixelWidth
|
|
436
|
+
const tileHeight = rasterTileSize * pixelHeight
|
|
293
437
|
const firstCol = Math.max(0, Math.floor(offsetX / tileWidth))
|
|
294
438
|
const lastCol = Math.floor((offsetX + blockSize) / tileWidth)
|
|
295
439
|
const firstRow = Math.max(0, Math.floor(offsetY / tileHeight))
|
|
@@ -297,7 +441,7 @@ export function drawMsaRaster({
|
|
|
297
441
|
|
|
298
442
|
for (let tileRow = firstRow; tileRow <= lastRow; tileRow++) {
|
|
299
443
|
for (let tileCol = firstCol; tileCol <= lastCol; tileCol++) {
|
|
300
|
-
const tile = getTile(cache, tileRow, tileCol)
|
|
444
|
+
const tile = getTile({ cache, tileRow, tileCol, colSpan, rowSpan })
|
|
301
445
|
if (tile) {
|
|
302
446
|
ctx.drawImage(
|
|
303
447
|
tile,
|
|
@@ -307,8 +451,8 @@ export function drawMsaRaster({
|
|
|
307
451
|
tile.height,
|
|
308
452
|
tileCol * tileWidth,
|
|
309
453
|
tileRow * tileHeight,
|
|
310
|
-
tile.width *
|
|
311
|
-
tile.height *
|
|
454
|
+
tile.width * pixelWidth,
|
|
455
|
+
tile.height * pixelHeight,
|
|
312
456
|
)
|
|
313
457
|
}
|
|
314
458
|
}
|
|
@@ -320,12 +464,11 @@ export function drawMsaRaster({
|
|
|
320
464
|
/**
|
|
321
465
|
* A raster canvas as a PNG data URI, for embedding in the SVG export.
|
|
322
466
|
*
|
|
323
|
-
* toDataURL is the synchronous read-back. The DOM canvas
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
* signal to fall back to drawing the thing in vector.
|
|
467
|
+
* toDataURL is the synchronous read-back. The DOM canvas and a headless node
|
|
468
|
+
* canvas have it; a browser OffscreenCanvas, which the tile and thumbnail
|
|
469
|
+
* caches hold where it exists, is copied onto a DOM canvas first. Returns
|
|
470
|
+
* undefined where read-back is unavailable (jsdom), and the caller then draws
|
|
471
|
+
* in vector.
|
|
329
472
|
*/
|
|
330
473
|
export function canvasHref(canvas: RasterCanvas | undefined) {
|
|
331
474
|
if (!canvas) {
|
|
@@ -352,10 +495,10 @@ export function canvasHref(canvas: RasterCanvas | undefined) {
|
|
|
352
495
|
}
|
|
353
496
|
}
|
|
354
497
|
|
|
355
|
-
// A canvas much past this many pixels fails to allocate, and the browser's
|
|
356
|
-
// per-side limit is lower still, so a raster
|
|
357
|
-
//
|
|
358
|
-
//
|
|
498
|
+
// A canvas much past this many pixels fails to allocate, and the browser's
|
|
499
|
+
// per-side limit is lower still, so a larger raster averages down per axis and
|
|
500
|
+
// is drawn across the same rectangle. An alignment that hits the limit on
|
|
501
|
+
// columns alone keeps every row.
|
|
359
502
|
const maxImagePixels = 64e6
|
|
360
503
|
const maxImageSide = 16384
|
|
361
504
|
|
|
@@ -363,14 +506,12 @@ const maxImageSide = 16384
|
|
|
363
506
|
* The alignment rectangle [col0, col0+numCols) x [row0, row0+numRows) as a PNG
|
|
364
507
|
* data URI, for the SVG export.
|
|
365
508
|
*
|
|
366
|
-
* SVG has no blit, so the export
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
* path emits a <rect> per cell, and a 200x500 alignment exhausts the heap
|
|
370
|
-
* building them.
|
|
509
|
+
* SVG has no blit, so the export draws the whole background as one <image>. The
|
|
510
|
+
* vector path emits a <rect> per cell, and a 200x500 alignment exhausts the
|
|
511
|
+
* heap building them.
|
|
371
512
|
*
|
|
372
|
-
* Returns undefined
|
|
373
|
-
*
|
|
513
|
+
* Returns undefined where a canvas cannot be read back (jsdom), and the caller
|
|
514
|
+
* then keeps the per-cell path.
|
|
374
515
|
*/
|
|
375
516
|
export function rasterImageHref({
|
|
376
517
|
model,
|
|
@@ -391,38 +532,46 @@ export function rasterImageHref({
|
|
|
391
532
|
return undefined
|
|
392
533
|
}
|
|
393
534
|
const cache = getCache(model, theme)
|
|
394
|
-
let
|
|
535
|
+
let colSpan = 1
|
|
536
|
+
let rowSpan = 1
|
|
537
|
+
while (Math.ceil(numCols / colSpan) > maxImageSide) {
|
|
538
|
+
colSpan *= 2
|
|
539
|
+
}
|
|
540
|
+
while (Math.ceil(numRows / rowSpan) > maxImageSide) {
|
|
541
|
+
rowSpan *= 2
|
|
542
|
+
}
|
|
395
543
|
while (
|
|
396
|
-
Math.ceil(numCols /
|
|
397
|
-
|
|
398
|
-
Math.ceil(numRows / step) > maxImageSide
|
|
544
|
+
Math.ceil(numCols / colSpan) * Math.ceil(numRows / rowSpan) >
|
|
545
|
+
maxImagePixels
|
|
399
546
|
) {
|
|
400
|
-
|
|
547
|
+
if (numCols / colSpan >= numRows / rowSpan) {
|
|
548
|
+
colSpan *= 2
|
|
549
|
+
} else {
|
|
550
|
+
rowSpan *= 2
|
|
551
|
+
}
|
|
401
552
|
}
|
|
402
|
-
const width = Math.ceil(numCols /
|
|
403
|
-
const height = Math.ceil(numRows /
|
|
553
|
+
const width = Math.ceil(numCols / colSpan)
|
|
554
|
+
const height = Math.ceil(numRows / rowSpan)
|
|
404
555
|
|
|
405
556
|
return canvasHref(
|
|
406
|
-
paint(
|
|
557
|
+
paint(width, height, out => {
|
|
407
558
|
rasterPixels({
|
|
408
559
|
spec: cache.spec,
|
|
409
560
|
col0,
|
|
410
561
|
row0,
|
|
411
562
|
width,
|
|
412
563
|
height,
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
),
|
|
564
|
+
colSpan,
|
|
565
|
+
rowSpan,
|
|
566
|
+
out,
|
|
567
|
+
})
|
|
568
|
+
}),
|
|
419
569
|
)
|
|
420
570
|
}
|
|
421
571
|
|
|
422
572
|
/**
|
|
423
|
-
* The whole alignment sampled down to a strip
|
|
424
|
-
*
|
|
425
|
-
* exactly the same conditions.
|
|
573
|
+
* The whole alignment sampled down to a strip behind the minimap thumb, cached
|
|
574
|
+
* with the block tiles since the same keys invalidate both.
|
|
426
575
|
*/
|
|
427
576
|
export function msaThumbnail({
|
|
428
577
|
model,
|
|
@@ -447,7 +596,7 @@ export function msaThumbnail({
|
|
|
447
596
|
const h = Math.ceil(numRows / rowStep)
|
|
448
597
|
cache.thumbnail = {
|
|
449
598
|
height,
|
|
450
|
-
canvas: paint(
|
|
599
|
+
canvas: paint(w, h, out => {
|
|
451
600
|
rasterPixels({
|
|
452
601
|
spec: cache.spec,
|
|
453
602
|
col0: 0,
|
|
@@ -456,10 +605,9 @@ export function msaThumbnail({
|
|
|
456
605
|
height: h,
|
|
457
606
|
colStep,
|
|
458
607
|
rowStep,
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
),
|
|
608
|
+
out,
|
|
609
|
+
})
|
|
610
|
+
}),
|
|
463
611
|
}
|
|
464
612
|
}
|
|
465
613
|
return cache.thumbnail.canvas
|