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
package/src/neighborJoining.ts
CHANGED
|
@@ -1,48 +1,7 @@
|
|
|
1
1
|
// Neighbor Joining tree construction using BLOSUM62 distances
|
|
2
2
|
// Based on Saitou & Nei (1987) "The neighbor-joining method"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
const BLOSUM62_KEYS = 'ARNDCQEGHILKMFPSTWYVBZX*'
|
|
6
|
-
// prettier-ignore
|
|
7
|
-
const BLOSUM62_DATA = [
|
|
8
|
-
[ 4,-1,-2,-2, 0,-1,-1, 0,-2,-1,-1,-1,-1,-2,-1, 1, 0,-3,-2, 0,-2,-1, 0,-4],
|
|
9
|
-
[-1, 5, 0,-2,-3, 1, 0,-2, 0,-3,-2, 2,-1,-3,-2,-1,-1,-3,-2,-3,-1, 0,-1,-4],
|
|
10
|
-
[-2, 0, 6, 1,-3, 0, 0, 0, 1,-3,-3, 0,-2,-3,-2, 1, 0,-4,-2,-3, 3, 0,-1,-4],
|
|
11
|
-
[-2,-2, 1, 6,-3, 0, 2,-1,-1,-3,-4,-1,-3,-3,-1, 0,-1,-4,-3,-3, 4, 1,-1,-4],
|
|
12
|
-
[ 0,-3,-3,-3, 9,-3,-4,-3,-3,-1,-1,-3,-1,-2,-3,-1,-1,-2,-2,-1,-3,-3,-2,-4],
|
|
13
|
-
[-1, 1, 0, 0,-3, 5, 2,-2, 0,-3,-2, 1, 0,-3,-1, 0,-1,-2,-1,-2, 0, 3,-1,-4],
|
|
14
|
-
[-1, 0, 0, 2,-4, 2, 5,-2, 0,-3,-3, 1,-2,-3,-1, 0,-1,-3,-2,-2, 1, 4,-1,-4],
|
|
15
|
-
[ 0,-2, 0,-1,-3,-2,-2, 6,-2,-4,-4,-2,-3,-3,-2, 0,-2,-2,-3,-3,-1,-2,-1,-4],
|
|
16
|
-
[-2, 0, 1,-1,-3, 0, 0,-2, 8,-3,-3,-1,-2,-1,-2,-1,-2,-2, 2,-3, 0, 0,-1,-4],
|
|
17
|
-
[-1,-3,-3,-3,-1,-3,-3,-4,-3, 4, 2,-3, 1, 0,-3,-2,-1,-3,-1, 3,-3,-3,-1,-4],
|
|
18
|
-
[-1,-2,-3,-4,-1,-2,-3,-4,-3, 2, 4,-2, 2, 0,-3,-2,-1,-2,-1, 1,-4,-3,-1,-4],
|
|
19
|
-
[-1, 2, 0,-1,-3, 1, 1,-2,-1,-3,-2, 5,-1,-3,-1, 0,-1,-3,-2,-2, 0, 1,-1,-4],
|
|
20
|
-
[-1,-1,-2,-3,-1, 0,-2,-3,-2, 1, 2,-1, 5, 0,-2,-1,-1,-1,-1, 1,-3,-1,-1,-4],
|
|
21
|
-
[-2,-3,-3,-3,-2,-3,-3,-3,-1, 0, 0,-3, 0, 6,-4,-2,-2, 1, 3,-1,-3,-3,-1,-4],
|
|
22
|
-
[-1,-2,-2,-1,-3,-1,-1,-2,-2,-3,-3,-1,-2,-4, 7,-1,-1,-4,-3,-2,-2,-1,-2,-4],
|
|
23
|
-
[ 1,-1, 1, 0,-1, 0, 0, 0,-1,-2,-2, 0,-1,-2,-1, 4, 1,-3,-2,-2, 0, 0, 0,-4],
|
|
24
|
-
[ 0,-1, 0,-1,-1,-1,-1,-2,-2,-1,-1,-1,-1,-2,-1, 1, 5,-2,-2, 0,-1,-1, 0,-4],
|
|
25
|
-
[-3,-3,-4,-4,-2,-2,-3,-2,-2,-3,-2,-3,-1, 1,-4,-3,-2,11, 2,-3,-4,-3,-2,-4],
|
|
26
|
-
[-2,-2,-2,-3,-2,-1,-2,-3, 2,-1,-1,-2,-1, 3,-3,-2,-2, 2, 7,-1,-3,-2,-1,-4],
|
|
27
|
-
[ 0,-3,-3,-3,-1,-2,-2,-3,-3, 3, 1,-2, 1,-1,-2,-2, 0,-3,-1, 4,-3,-2,-1,-4],
|
|
28
|
-
[-2,-1, 3, 4,-3, 0, 1,-1, 0,-3,-4, 0,-3,-3,-2, 0,-1,-4,-3,-3, 4, 1,-1,-4],
|
|
29
|
-
[-1, 0, 0, 1,-3, 3, 4,-2, 0,-3,-3, 1,-1,-3,-1, 0,-1,-3,-2,-2, 1, 4,-1,-4],
|
|
30
|
-
[ 0,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2, 0, 0,-2,-1,-1,-1,-1,-1,-4],
|
|
31
|
-
[-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4, 1],
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
const NUM_SYMBOLS = BLOSUM62_KEYS.length
|
|
35
|
-
const BLOSUM62 = Int8Array.from(BLOSUM62_DATA.flat())
|
|
36
|
-
|
|
37
|
-
// charCode -> row/column in BLOSUM62, -1 for anything the matrix does not name.
|
|
38
|
-
// On a 120x800 alignment a Map keyed by upper-cased character took 2.6s to
|
|
39
|
-
// build a tree, against 0.34s for this table.
|
|
40
|
-
const symbolOfCode = new Int8Array(128).fill(-1)
|
|
41
|
-
for (let i = 0; i < NUM_SYMBOLS; i++) {
|
|
42
|
-
const key = BLOSUM62_KEYS[i]!
|
|
43
|
-
symbolOfCode[key.charCodeAt(0)] = i
|
|
44
|
-
symbolOfCode[key.toLowerCase().charCodeAt(0)] = i
|
|
45
|
-
}
|
|
4
|
+
import { BLOSUM62, NUM_SYMBOLS, symbolOfCode } from './blosum62.ts'
|
|
46
5
|
|
|
47
6
|
// gap, or past the end of a row shorter than the alignment
|
|
48
7
|
const GAP = -2
|
package/src/renderToSvg.tsx
CHANGED
|
@@ -6,8 +6,7 @@ import { when } from 'mobx'
|
|
|
6
6
|
import { visibleRowRange } from './components/getVisibleLeaves.ts'
|
|
7
7
|
import MinimapSVG from './components/minimap/MinimapSVG.tsx'
|
|
8
8
|
import { legendRows } from './components/msa/legendRows.ts'
|
|
9
|
-
import {
|
|
10
|
-
import { renderBoxFeatureCanvasBlock } from './components/msa/renderBoxFeatureCanvasBlock.ts'
|
|
9
|
+
import { rasterApplies, rasterImageHref } from './components/msa/msaRaster.ts'
|
|
11
10
|
import { renderPersistentHighlights } from './components/msa/renderHighlights.ts'
|
|
12
11
|
import { renderMSABlock } from './components/msa/renderMSABlock.ts'
|
|
13
12
|
import { visibleColRange } from './components/msa/visibleColRange.ts'
|
|
@@ -15,141 +14,32 @@ import { headerRunsAcross } from './components/rowpanels/headerLayout.ts'
|
|
|
15
14
|
import { renderRowPanels } from './components/rowpanels/renderRowPanel.ts'
|
|
16
15
|
import { renderAllTracks } from './components/tracks/drawTracks.ts'
|
|
17
16
|
import ScaleBarSVG from './components/tree/ScaleBarSVG.tsx'
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
cladeHeight,
|
|
21
|
-
cladeInGutter,
|
|
22
|
-
cladeLabelHorizontal,
|
|
23
|
-
cladeLabelOffset,
|
|
24
|
-
} from './components/tree/cladeBrackets.ts'
|
|
17
|
+
import { cladeLabelBoxes } from './components/tree/cladeBrackets.ts'
|
|
18
|
+
import { ellipsize } from './components/tree/ellipsize.ts'
|
|
25
19
|
import { renderTreeCanvas } from './components/tree/renderTreeCanvas.ts'
|
|
26
20
|
import { renderTreeOverview } from './components/tree/renderTreeOverview.ts'
|
|
27
21
|
import { treeScaleBarHeight } from './constants.ts'
|
|
22
|
+
import {
|
|
23
|
+
LEGEND_CHAR_W,
|
|
24
|
+
LEGEND_FONT,
|
|
25
|
+
LEGEND_PAD,
|
|
26
|
+
LEGEND_ROW_H,
|
|
27
|
+
LEGEND_SWATCH,
|
|
28
|
+
LEGEND_TEXT_X,
|
|
29
|
+
exportPageSize,
|
|
30
|
+
getLayout,
|
|
31
|
+
legendHeight,
|
|
32
|
+
} from './exportLayout.ts'
|
|
28
33
|
import { measureTextCanvas } from './measureTextCanvas.ts'
|
|
29
34
|
import { renderToStaticMarkup, svgSafeColors } from './renderToStaticMarkup.ts'
|
|
30
35
|
import { outlineColor } from './util.ts'
|
|
31
36
|
|
|
32
|
-
import type {
|
|
37
|
+
import type { ExportSvgOptions, Layout } from './exportLayout.ts'
|
|
33
38
|
import type { MsaViewModel } from './model.ts'
|
|
34
39
|
import type { Context as ContextType } from '@jbrowse/svgcanvas'
|
|
35
40
|
import type { Theme } from '@mui/material'
|
|
36
41
|
|
|
37
|
-
export
|
|
38
|
-
theme: Theme
|
|
39
|
-
includeMinimap?: boolean
|
|
40
|
-
includeTracks?: boolean
|
|
41
|
-
exportType: 'entire' | 'viewport'
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// legend geometry, shared by the width calculation and the renderer so the
|
|
45
|
-
// reserved column on the right exactly fits the drawn legends
|
|
46
|
-
const LEGEND_PAD = 8
|
|
47
|
-
const LEGEND_ROW_H = 16
|
|
48
|
-
const LEGEND_SWATCH = 12
|
|
49
|
-
const LEGEND_FONT = 12
|
|
50
|
-
const LEGEND_CHAR_W = 7
|
|
51
|
-
|
|
52
|
-
// the width of a character as a fraction of the font size, close enough for a
|
|
53
|
-
// sans-serif average
|
|
54
|
-
const CHAR_WIDTH = 0.6
|
|
55
|
-
|
|
56
|
-
const LEGEND_MAX_W = 360
|
|
57
|
-
const LEGEND_TEXT_X = LEGEND_PAD + LEGEND_SWATCH + 6
|
|
58
|
-
|
|
59
|
-
function getLegendWidth(rows: LegendRow[]) {
|
|
60
|
-
const maxLen = rows.reduce((a, r) => Math.max(a, r.label.length), 0)
|
|
61
|
-
const w = LEGEND_TEXT_X + LEGEND_PAD + maxLen * LEGEND_CHAR_W
|
|
62
|
-
return Math.min(LEGEND_MAX_W, Math.max(120, w))
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function legendHeight(rows: LegendRow[]) {
|
|
66
|
-
return LEGEND_PAD * 2 + rows.length * LEGEND_ROW_H
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// resolved sizes/offsets (in svg user units) for the chosen export, shared by
|
|
70
|
-
// every layer; 'entire' renders the whole alignment unscrolled, 'viewport'
|
|
71
|
-
// mirrors the live scroll position
|
|
72
|
-
interface Layout {
|
|
73
|
-
width: number
|
|
74
|
-
msaAreaWidth: number
|
|
75
|
-
height: number
|
|
76
|
-
contentHeight: number
|
|
77
|
-
trackHeight: number
|
|
78
|
-
// the band the minimap, the row panel headers and the tree overview stacked
|
|
79
|
-
// on the scale bar share across the top
|
|
80
|
-
topHeight: number
|
|
81
|
-
offsetX: number
|
|
82
|
-
offsetY: number
|
|
83
|
-
includeMinimap: boolean
|
|
84
|
-
legendWidth: number
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
|
|
88
|
-
const {
|
|
89
|
-
scrollX,
|
|
90
|
-
scrollY,
|
|
91
|
-
totalWidth,
|
|
92
|
-
totalHeight,
|
|
93
|
-
effectiveTreeAreaWidth,
|
|
94
|
-
rowPanelsWidth,
|
|
95
|
-
rowPanelsHeaderHeight,
|
|
96
|
-
totalTrackAreaHeight,
|
|
97
|
-
minimapHeight,
|
|
98
|
-
msaAreaHeight,
|
|
99
|
-
msaCanvasWidth,
|
|
100
|
-
showHorizontalScrollbar,
|
|
101
|
-
treeOverviewHeight,
|
|
102
|
-
treeScaleBar,
|
|
103
|
-
} = model
|
|
104
|
-
const trackHeight = opts.includeTracks ? totalTrackAreaHeight : 0
|
|
105
|
-
// the minimap reflects the live viewport scroll position, so it's only
|
|
106
|
-
// meaningful for a viewport export, never for the entire alignment -- and
|
|
107
|
-
// only when the live view shows one at all, since a minimap over an alignment
|
|
108
|
-
// that already fits marks a viewport wider than the bar
|
|
109
|
-
const includeMinimap =
|
|
110
|
-
opts.exportType === 'viewport' &&
|
|
111
|
-
!!opts.includeMinimap &&
|
|
112
|
-
showHorizontalScrollbar
|
|
113
|
-
const rows = legendRows(model.legends)
|
|
114
|
-
const legendWidth = rows.length > 0 ? getLegendWidth(rows) : 0
|
|
115
|
-
const topHeight = Math.max(
|
|
116
|
-
includeMinimap ? minimapHeight : 0,
|
|
117
|
-
rowPanelsHeaderHeight,
|
|
118
|
-
treeOverviewHeight + (treeScaleBar ? treeScaleBarHeight : 0),
|
|
119
|
-
)
|
|
120
|
-
const gutterWidth = effectiveTreeAreaWidth + rowPanelsWidth
|
|
121
|
-
|
|
122
|
-
// width stays content-only; the legend occupies an extra column added at the
|
|
123
|
-
// svg root in MsaSvg. The viewport export takes the alignment canvas's own
|
|
124
|
-
// size rather than the widget's -- the widget box also covers the header, the
|
|
125
|
-
// resize handle and the scrollbars, and rendering that box exports rows and
|
|
126
|
-
// columns the scrollbars hide on screen
|
|
127
|
-
return opts.exportType === 'entire'
|
|
128
|
-
? {
|
|
129
|
-
width: totalWidth + gutterWidth,
|
|
130
|
-
msaAreaWidth: totalWidth,
|
|
131
|
-
height: totalHeight + trackHeight + topHeight,
|
|
132
|
-
contentHeight: totalHeight,
|
|
133
|
-
trackHeight,
|
|
134
|
-
topHeight,
|
|
135
|
-
offsetX: 0,
|
|
136
|
-
offsetY: 0,
|
|
137
|
-
includeMinimap,
|
|
138
|
-
legendWidth,
|
|
139
|
-
}
|
|
140
|
-
: {
|
|
141
|
-
width: msaCanvasWidth + gutterWidth,
|
|
142
|
-
msaAreaWidth: msaCanvasWidth,
|
|
143
|
-
height: msaAreaHeight + topHeight + trackHeight,
|
|
144
|
-
contentHeight: msaAreaHeight,
|
|
145
|
-
trackHeight,
|
|
146
|
-
topHeight,
|
|
147
|
-
offsetX: -scrollX,
|
|
148
|
-
offsetY: -scrollY,
|
|
149
|
-
includeMinimap,
|
|
150
|
-
legendWidth,
|
|
151
|
-
}
|
|
152
|
-
}
|
|
42
|
+
export type { ExportSvgOptions } from './exportLayout.ts'
|
|
153
43
|
|
|
154
44
|
export async function renderToSvg(model: MsaViewModel, opts: ExportSvgOptions) {
|
|
155
45
|
// dataInitialized stays false while the model holds a load error, so waiting
|
|
@@ -173,12 +63,13 @@ export async function renderToSvg(model: MsaViewModel, opts: ExportSvgOptions) {
|
|
|
173
63
|
layers={layers}
|
|
174
64
|
/>,
|
|
175
65
|
)
|
|
66
|
+
// innerHTML serializes as HTML, and an XML parser rejects its
|
|
176
67
|
return svgSafeColors(
|
|
177
68
|
markup.replaceAll(/<g data-layer="([^"]+)"><\/g>/g, (match, id: string) => {
|
|
178
69
|
const ctx = layers.get(id)
|
|
179
70
|
return ctx ? `<g id="${id}">${ctx.getSvg().innerHTML}</g>` : match
|
|
180
71
|
}),
|
|
181
|
-
)
|
|
72
|
+
).replaceAll(' ', ' ')
|
|
182
73
|
}
|
|
183
74
|
|
|
184
75
|
function MsaSvg({
|
|
@@ -196,17 +87,8 @@ function MsaSvg({
|
|
|
196
87
|
}) {
|
|
197
88
|
const { width, trackHeight, includeMinimap, legendWidth, topHeight } = layout
|
|
198
89
|
const { effectiveTreeAreaWidth, rowPanelsWidth } = model
|
|
199
|
-
const totalWidth =
|
|
90
|
+
const { width: totalWidth, height } = exportPageSize(model, layout)
|
|
200
91
|
const legendTop = topHeight
|
|
201
|
-
// a short alignment with many legend entries has a key taller than its rows,
|
|
202
|
-
// and the page has to hold the whole key
|
|
203
|
-
const height =
|
|
204
|
-
legendWidth > 0
|
|
205
|
-
? Math.max(
|
|
206
|
-
layout.height,
|
|
207
|
-
legendTop + legendHeight(legendRows(model.legends)),
|
|
208
|
-
)
|
|
209
|
-
: layout.height
|
|
210
92
|
const props = { Context, model, theme, layout, layers }
|
|
211
93
|
|
|
212
94
|
const body = (
|
|
@@ -227,6 +109,9 @@ function MsaSvg({
|
|
|
227
109
|
xmlns="http://www.w3.org/2000/svg"
|
|
228
110
|
xmlnsXlink="http://www.w3.org/1999/xlink"
|
|
229
111
|
viewBox={`0 0 ${totalWidth} ${height}`}
|
|
112
|
+
// the canvas layers name sans-serif on each glyph; the legend, labels
|
|
113
|
+
// and headers inherit it here instead of an editor's default serif
|
|
114
|
+
fontFamily="sans-serif"
|
|
230
115
|
>
|
|
231
116
|
{/* every layer below draws in the theme's colors, so the page has to be
|
|
232
117
|
the theme's background too -- a hardcoded white one turns a dark-theme
|
|
@@ -359,7 +244,6 @@ function RowPanelHeadersSVG({
|
|
|
359
244
|
const center = effectiveTreeAreaWidth + panel.offsetX + panel.width / 2
|
|
360
245
|
const x = across ? center : center + size / 3
|
|
361
246
|
const room = across ? panel.width : y - LEGEND_PAD
|
|
362
|
-
const maxChars = Math.floor(room / (size * CHAR_WIDTH))
|
|
363
247
|
return size < 5 ? null : (
|
|
364
248
|
<text
|
|
365
249
|
key={panel.id}
|
|
@@ -371,9 +255,10 @@ function RowPanelHeadersSVG({
|
|
|
371
255
|
fontSize={size}
|
|
372
256
|
fill={theme.palette.text.primary}
|
|
373
257
|
>
|
|
374
|
-
{
|
|
375
|
-
|
|
376
|
-
|
|
258
|
+
{
|
|
259
|
+
ellipsize(t => measureTextCanvas(t, size), panel.header, room)
|
|
260
|
+
.text
|
|
261
|
+
}
|
|
377
262
|
</text>
|
|
378
263
|
)
|
|
379
264
|
})}
|
|
@@ -381,21 +266,11 @@ function RowPanelHeadersSVG({
|
|
|
381
266
|
)
|
|
382
267
|
}
|
|
383
268
|
|
|
384
|
-
// A label past the room it has, cut to what fits with an ellipsis. The measure
|
|
385
|
-
// is the one cladeGutterWidth sizes the gutter with, so a label the gutter
|
|
386
|
-
// holds keeps every character.
|
|
387
|
-
function clipCladeLabel(label: string, room: number, fontSize: number) {
|
|
388
|
-
const width = measureTextCanvas(label, fontSize)
|
|
389
|
-
if (width <= room) {
|
|
390
|
-
return label
|
|
391
|
-
}
|
|
392
|
-
const fits = Math.floor((label.length * room) / width)
|
|
393
|
-
return `${label.slice(0, Math.max(1, fits - 1))}…`
|
|
394
|
-
}
|
|
395
|
-
|
|
396
269
|
// Each bracket's label in the gutter beside its bar, where the live view puts
|
|
397
270
|
// it (see CladeLabels). The canvas layer draws the bar; a label longer than the
|
|
398
271
|
// gutter or the clade is clipped, since text that overruns runs off the figure.
|
|
272
|
+
// cladeGutterWidth sizes the gutter with the same measureTextCanvas, so a label
|
|
273
|
+
// the gutter holds keeps every character.
|
|
399
274
|
function CladeLabelsSVG({
|
|
400
275
|
model,
|
|
401
276
|
theme,
|
|
@@ -405,43 +280,32 @@ function CladeLabelsSVG({
|
|
|
405
280
|
theme: Theme
|
|
406
281
|
offsetY: number
|
|
407
282
|
}) {
|
|
408
|
-
const {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
rowHeight,
|
|
412
|
-
effectiveTreeAreaWidth,
|
|
413
|
-
fontSize,
|
|
414
|
-
} = model
|
|
415
|
-
if (cladeGutterWidth === 0) {
|
|
416
|
-
return null
|
|
417
|
-
}
|
|
418
|
-
const gutterLeft = effectiveTreeAreaWidth - cladeGutterWidth
|
|
419
|
-
return (
|
|
283
|
+
const { fontSize } = model
|
|
284
|
+
const boxes = cladeLabelBoxes(model)
|
|
285
|
+
return boxes.length === 0 ? null : (
|
|
420
286
|
<g id="clade-labels">
|
|
421
|
-
{
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const top = rows[0] * rowHeight - offsetY
|
|
428
|
-
const height = cladeHeight(clade, rowHeight)
|
|
429
|
-
const horizontal = cladeLabelHorizontal(clade, rowHeight, fontSize)
|
|
430
|
-
const room = horizontal
|
|
431
|
-
? effectiveTreeAreaWidth - left - bracketGap
|
|
432
|
-
: height
|
|
433
|
-
const x = horizontal ? left : left + fontSize
|
|
434
|
-
const y = horizontal ? top + height / 2 + fontSize / 3 : top + height
|
|
287
|
+
{boxes.map(box => {
|
|
288
|
+
const top = box.top - offsetY
|
|
289
|
+
const x = box.horizontal ? box.left : box.left + fontSize
|
|
290
|
+
const y = box.horizontal
|
|
291
|
+
? top + box.height / 2 + fontSize / 3
|
|
292
|
+
: top + box.height
|
|
435
293
|
return (
|
|
436
294
|
<text
|
|
437
|
-
key={
|
|
295
|
+
key={box.key}
|
|
438
296
|
x={x}
|
|
439
297
|
y={y}
|
|
440
|
-
transform={horizontal ? undefined : `rotate(-90 ${x} ${y})`}
|
|
298
|
+
transform={box.horizontal ? undefined : `rotate(-90 ${x} ${y})`}
|
|
441
299
|
fontSize={fontSize}
|
|
442
|
-
fill={
|
|
300
|
+
fill={box.color ?? theme.palette.text.primary}
|
|
443
301
|
>
|
|
444
|
-
{
|
|
302
|
+
{
|
|
303
|
+
ellipsize(
|
|
304
|
+
t => measureTextCanvas(t, fontSize),
|
|
305
|
+
box.label,
|
|
306
|
+
box.horizontal ? box.width : box.height,
|
|
307
|
+
).text
|
|
308
|
+
}
|
|
445
309
|
</text>
|
|
446
310
|
)
|
|
447
311
|
})}
|
|
@@ -464,9 +328,7 @@ function LegendSVG({
|
|
|
464
328
|
const boxHeight = legendHeight(rows)
|
|
465
329
|
// the column is capped at LEGEND_MAX_W, so a name too long for it has to be
|
|
466
330
|
// clipped here -- text that overruns the reserved width runs off the figure
|
|
467
|
-
const
|
|
468
|
-
(width - LEGEND_TEXT_X - LEGEND_PAD) / LEGEND_CHAR_W,
|
|
469
|
-
)
|
|
331
|
+
const room = width - LEGEND_TEXT_X - LEGEND_PAD
|
|
470
332
|
return (
|
|
471
333
|
<g>
|
|
472
334
|
<rect
|
|
@@ -499,9 +361,7 @@ function LegendSVG({
|
|
|
499
361
|
fontWeight={row.color ? undefined : 'bold'}
|
|
500
362
|
fill={theme.palette.text.primary}
|
|
501
363
|
>
|
|
502
|
-
{row.label.
|
|
503
|
-
? `${row.label.slice(0, Math.max(1, maxChars - 1))}…`
|
|
504
|
-
: row.label}
|
|
364
|
+
{ellipsize(t => t.length * LEGEND_CHAR_W, row.label, room).text}
|
|
505
365
|
</text>
|
|
506
366
|
</g>
|
|
507
367
|
)
|
|
@@ -551,18 +411,6 @@ function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
|
|
|
551
411
|
const raster = rasterBackground({ model, theme, layout })
|
|
552
412
|
|
|
553
413
|
const msaCtx = new Context(msaAreaWidth, contentHeight)
|
|
554
|
-
renderBoxFeatureCanvasBlock({
|
|
555
|
-
model,
|
|
556
|
-
theme,
|
|
557
|
-
ctx: msaCtx,
|
|
558
|
-
offsetX,
|
|
559
|
-
offsetY,
|
|
560
|
-
// the overlay culls bands to this width; without it the export falls back
|
|
561
|
-
// to the on-screen 500px block size and drops every band past it
|
|
562
|
-
blockSizeXOverride: msaAreaWidth,
|
|
563
|
-
blockSizeYOverride: contentHeight,
|
|
564
|
-
highResScaleFactorOverride: 1,
|
|
565
|
-
})
|
|
566
414
|
renderMSABlock({
|
|
567
415
|
model,
|
|
568
416
|
theme,
|
|
@@ -573,6 +421,7 @@ function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
|
|
|
573
421
|
blockSizeYOverride: contentHeight,
|
|
574
422
|
highResScaleFactorOverride: 1,
|
|
575
423
|
rasterTiles: !!raster,
|
|
424
|
+
domains: true,
|
|
576
425
|
})
|
|
577
426
|
msaCtx.resetTransform()
|
|
578
427
|
renderPersistentHighlights({
|
|
@@ -622,9 +471,8 @@ function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
|
|
|
622
471
|
}
|
|
623
472
|
|
|
624
473
|
/**
|
|
625
|
-
* The alignment background as a single <image>,
|
|
626
|
-
*
|
|
627
|
-
* that reads back.
|
|
474
|
+
* The alignment background as a single <image>, wherever the live canvas
|
|
475
|
+
* draws the raster.
|
|
628
476
|
*
|
|
629
477
|
* The vector path's <rect> per cell exhausts the heap on a real alignment. The
|
|
630
478
|
* image is one pixel per cell, and image-rendering keeps the upscaled cell
|
|
@@ -640,8 +488,8 @@ function rasterBackground({
|
|
|
640
488
|
layout: Layout
|
|
641
489
|
}) {
|
|
642
490
|
const { contentHeight, offsetX, offsetY, msaAreaWidth } = layout
|
|
643
|
-
const { colWidth, rowHeight
|
|
644
|
-
if (
|
|
491
|
+
const { colWidth, rowHeight } = model
|
|
492
|
+
if (!rasterApplies(model)) {
|
|
645
493
|
return undefined
|
|
646
494
|
}
|
|
647
495
|
const { xStart, xEnd } = visibleColRange({
|
|
@@ -728,7 +576,8 @@ function TrackLabelsSVG({
|
|
|
728
576
|
const top = turnedOnTracks
|
|
729
577
|
.slice(0, i)
|
|
730
578
|
.reduce((acc, t) => acc + t.model.height, 0)
|
|
731
|
-
|
|
579
|
+
// pinned to the top of the track, as the live view's flex row pins it
|
|
580
|
+
return { id, name, size, baseline: top + size * 0.8 }
|
|
732
581
|
})
|
|
733
582
|
return (
|
|
734
583
|
<>
|
package/src/rerootTree.ts
CHANGED
|
@@ -13,19 +13,18 @@ export interface TreeNode {
|
|
|
13
13
|
children?: TreeNode[]
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
function preorder<N extends TreeNode>(root: N) {
|
|
16
|
+
export function preorder<N extends TreeNode>(root: N, parents?: Map<N, N>) {
|
|
17
17
|
const order: N[] = []
|
|
18
|
-
const parents = new Map<N, N>()
|
|
19
18
|
const stack = [root]
|
|
20
19
|
while (stack.length > 0) {
|
|
21
20
|
const node = stack.pop()!
|
|
22
21
|
order.push(node)
|
|
23
22
|
for (const child of (node.children ?? []) as N[]) {
|
|
24
|
-
parents
|
|
23
|
+
parents?.set(child, node)
|
|
25
24
|
stack.push(child)
|
|
26
25
|
}
|
|
27
26
|
}
|
|
28
|
-
return
|
|
27
|
+
return order
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
function hasLengths(order: TreeNode[]) {
|
|
@@ -56,7 +55,8 @@ export function rerootTree<N extends TreeNode>(
|
|
|
56
55
|
target: N,
|
|
57
56
|
offset?: number,
|
|
58
57
|
): TreeNode {
|
|
59
|
-
const
|
|
58
|
+
const parents = new Map<N, N>()
|
|
59
|
+
const order = preorder(root, parents)
|
|
60
60
|
if (!parents.has(target)) {
|
|
61
61
|
return root
|
|
62
62
|
}
|
|
@@ -112,7 +112,8 @@ export function rerootTree<N extends TreeNode>(
|
|
|
112
112
|
* counts branches.
|
|
113
113
|
*/
|
|
114
114
|
export function midpointRoot<N extends TreeNode>(root: N): TreeNode {
|
|
115
|
-
const
|
|
115
|
+
const parents = new Map<N, N>()
|
|
116
|
+
const order = preorder(root, parents)
|
|
116
117
|
const tips = order.filter(n => !n.children?.length)
|
|
117
118
|
if (tips.length < 2) {
|
|
118
119
|
return root
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { colord } from 'colord'
|
|
2
|
+
|
|
3
|
+
import { tileColorFn } from './components/msa/tileColor.ts'
|
|
4
|
+
|
|
5
|
+
import type { MsaViewModel } from './model.ts'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A reader for the color the active scheme gives a row's residue, as hex, for a
|
|
9
|
+
* host that paints another view, such as a structure, with the alignment's own
|
|
10
|
+
* colors. `seqPos` is 1-based, like `structureResidue`. The reader returns
|
|
11
|
+
* undefined where the scheme leaves the residue uncolored, the row lacks it, or
|
|
12
|
+
* its column is hidden.
|
|
13
|
+
*
|
|
14
|
+
* Build it inside a reaction: a new scheme, a hidden gap column or a collapsed
|
|
15
|
+
* clade then re-runs the caller.
|
|
16
|
+
*/
|
|
17
|
+
export function schemeResidueColor(model: MsaViewModel) {
|
|
18
|
+
const colorAt = tileColorFn(model)
|
|
19
|
+
const { columns } = model
|
|
20
|
+
const hexes = new Map<string, string | undefined>()
|
|
21
|
+
const toHex = (color: string) => {
|
|
22
|
+
if (!hexes.has(color)) {
|
|
23
|
+
const parsed = colord(color)
|
|
24
|
+
hexes.set(color, parsed.isValid() ? parsed.toHex() : undefined)
|
|
25
|
+
}
|
|
26
|
+
return hexes.get(color)
|
|
27
|
+
}
|
|
28
|
+
return (rowName: string, seqPos: number) => {
|
|
29
|
+
const col = model.seqPosToVisibleCol(rowName, seqPos - 1)
|
|
30
|
+
const letter = col === undefined ? undefined : columns.get(rowName)?.[col]
|
|
31
|
+
const color =
|
|
32
|
+
col === undefined || letter === undefined
|
|
33
|
+
? undefined
|
|
34
|
+
: colorAt(col, letter)
|
|
35
|
+
return color === undefined ? undefined : toHex(color)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Similarity shading, after biotite's plot_alignment_similarity_based and
|
|
2
|
+
// Jalview's BLOSUM62 score scheme. Each residue takes a shade of one hue by how
|
|
3
|
+
// similar it is, on average, to the other residues of its column.
|
|
4
|
+
|
|
5
|
+
import { BLOSUM62, NUM_SYMBOLS, symbolOfCode } from './blosum62.ts'
|
|
6
|
+
import { letterOfResidueSlot, numResidueSlots } from './columnCounts.ts'
|
|
7
|
+
|
|
8
|
+
import type { ColumnCounts } from './columnCounts.ts'
|
|
9
|
+
|
|
10
|
+
const AMINO_ACIDS = 20
|
|
11
|
+
const HUE = 240
|
|
12
|
+
const SATURATION = 50
|
|
13
|
+
const LIGHTEST = 96
|
|
14
|
+
const DARKEST = 40
|
|
15
|
+
|
|
16
|
+
// BLOSUM62 scaled per row to 0..1 over the twenty amino acids, so a residue's
|
|
17
|
+
// own score is 1 and its worst substitution 0. Not symmetric, like biotite's.
|
|
18
|
+
const normalized = new Float32Array(NUM_SYMBOLS * NUM_SYMBOLS)
|
|
19
|
+
for (let a = 0; a < NUM_SYMBOLS; a++) {
|
|
20
|
+
let min = Infinity
|
|
21
|
+
let max = -Infinity
|
|
22
|
+
for (let b = 0; b < AMINO_ACIDS; b++) {
|
|
23
|
+
const score = BLOSUM62[a * NUM_SYMBOLS + b]!
|
|
24
|
+
min = Math.min(min, score)
|
|
25
|
+
max = Math.max(max, score)
|
|
26
|
+
}
|
|
27
|
+
for (let b = 0; b < NUM_SYMBOLS; b++) {
|
|
28
|
+
const scaled = (BLOSUM62[a * NUM_SYMBOLS + b]! - min) / (max - min)
|
|
29
|
+
normalized[a * NUM_SYMBOLS + b] = Math.min(Math.max(scaled, 0), 1)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// residue slot -> row of the matrix, -1 for a letter the matrix does not name
|
|
34
|
+
const symbolOfSlot = Int8Array.from({ length: numResidueSlots }, (_, slot) => {
|
|
35
|
+
const letter = letterOfResidueSlot(slot)
|
|
36
|
+
return letter.length === 1 ? symbolOfCode[letter.charCodeAt(0)]! : -1
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
function shade(similarity: number) {
|
|
40
|
+
const lightness = Math.round(LIGHTEST - (LIGHTEST - DARKEST) * similarity)
|
|
41
|
+
return `hsl(${HUE}, ${SATURATION}%, ${lightness}%)`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface Measure {
|
|
45
|
+
/** whether a residue counts toward the means at all */
|
|
46
|
+
counted: (slot: number) => boolean
|
|
47
|
+
/** whether a residue takes a shade, given it is counted */
|
|
48
|
+
shaded: (slot: number) => boolean
|
|
49
|
+
/** similarity of residue `a` to residue `b`, 0..1 */
|
|
50
|
+
score: (a: number, b: number) => number
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Shades each residue of one column by its mean similarity to the column's
|
|
55
|
+
* other residues, gaps left out of the mean. A column of fewer than two counted
|
|
56
|
+
* residues has nothing to compare and colors nothing.
|
|
57
|
+
*/
|
|
58
|
+
function shadeColumn(counts: ColumnCounts, col: number, measure: Measure) {
|
|
59
|
+
const { counted, shaded, score } = measure
|
|
60
|
+
const present: [slot: number, count: number][] = []
|
|
61
|
+
let total = 0
|
|
62
|
+
counts.forEachResidue(col, (slot, count) => {
|
|
63
|
+
if (counted(slot)) {
|
|
64
|
+
present.push([slot, count])
|
|
65
|
+
total += count
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const colors: Record<string, string> = {}
|
|
70
|
+
if (total < 2) {
|
|
71
|
+
return colors
|
|
72
|
+
}
|
|
73
|
+
for (const [a] of present) {
|
|
74
|
+
if (shaded(a)) {
|
|
75
|
+
let sum = -score(a, a)
|
|
76
|
+
for (const [b, count] of present) {
|
|
77
|
+
sum += count * score(a, b)
|
|
78
|
+
}
|
|
79
|
+
colors[letterOfResidueSlot(a)] = shade(sum / (total - 1))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return colors
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const blosum62: Measure = {
|
|
86
|
+
counted: slot => symbolOfSlot[slot]! >= 0,
|
|
87
|
+
shaded: slot => symbolOfSlot[slot]! < AMINO_ACIDS,
|
|
88
|
+
score: (a, b) =>
|
|
89
|
+
normalized[symbolOfSlot[a]! * NUM_SYMBOLS + symbolOfSlot[b]!]!,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const identity: Measure = {
|
|
93
|
+
counted: () => true,
|
|
94
|
+
shaded: () => true,
|
|
95
|
+
score: (a, b) => (a === b ? 1 : 0),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The amino acids of a column shaded by BLOSUM62 similarity. Letters the matrix
|
|
100
|
+
* does not name, such as U, O and J, count for nothing and take no shade.
|
|
101
|
+
*/
|
|
102
|
+
export function blosum62ColumnColors(counts: ColumnCounts, col: number) {
|
|
103
|
+
return shadeColumn(counts, col, blosum62)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Nucleotides and anything else without a substitution matrix: a residue's
|
|
108
|
+
* similarity is the fraction of the rest of its column that equals it.
|
|
109
|
+
*/
|
|
110
|
+
export function identityColumnColors(counts: ColumnCounts, col: number) {
|
|
111
|
+
return shadeColumn(counts, col, identity)
|
|
112
|
+
}
|