react-msaview 9.1.0 → 9.2.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.
Files changed (278) hide show
  1. package/bundle/index.js +111 -115
  2. package/bundle/index.js.map +4 -4
  3. package/dist/blosum62.d.ts +3 -0
  4. package/dist/blosum62.js +43 -0
  5. package/dist/blosum62.js.map +1 -0
  6. package/dist/colorSchemes.js +1 -0
  7. package/dist/colorSchemes.js.map +1 -1
  8. package/dist/components/Loading.d.ts +3 -2
  9. package/dist/components/Loading.js +2 -2
  10. package/dist/components/Loading.js.map +1 -1
  11. package/dist/components/MSAView.d.ts +3 -2
  12. package/dist/components/MSAView.js +7 -4
  13. package/dist/components/MSAView.js.map +1 -1
  14. package/dist/components/MSAViewer.d.ts +64 -12
  15. package/dist/components/MetadataTable.d.ts +4 -1
  16. package/dist/components/MetadataTable.js +55 -16
  17. package/dist/components/MetadataTable.js.map +1 -1
  18. package/dist/components/Track.js +6 -0
  19. package/dist/components/Track.js.map +1 -1
  20. package/dist/components/controls.js +2 -1
  21. package/dist/components/controls.js.map +1 -1
  22. package/dist/components/dialogs/AnnotationFileDialog.js +7 -2
  23. package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
  24. package/dist/components/dialogs/FeatureDialog.js +2 -2
  25. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  26. package/dist/components/{header/MSASettingsMenu.d.ts → dialogs/GapThresholdDialog.d.ts} +3 -2
  27. package/dist/components/dialogs/GapThresholdDialog.js +15 -0
  28. package/dist/components/dialogs/GapThresholdDialog.js.map +1 -0
  29. package/dist/components/{header → dialogs}/GappynessSlider.d.ts +1 -1
  30. package/dist/components/{header → dialogs}/GappynessSlider.js +3 -10
  31. package/dist/components/dialogs/GappynessSlider.js.map +1 -0
  32. package/dist/components/{header → dialogs}/GoToBox.d.ts +4 -2
  33. package/dist/components/dialogs/GoToBox.js +48 -0
  34. package/dist/components/dialogs/GoToBox.js.map +1 -0
  35. package/dist/components/dialogs/GoToDialog.d.ts +6 -0
  36. package/dist/components/dialogs/GoToDialog.js +10 -0
  37. package/dist/components/dialogs/GoToDialog.js.map +1 -0
  38. package/dist/components/dialogs/MetadataDialog.js +11 -3
  39. package/dist/components/dialogs/MetadataDialog.js.map +1 -1
  40. package/dist/components/dialogs/goTo.d.ts +5 -0
  41. package/dist/components/{header → dialogs}/goTo.js +1 -1
  42. package/dist/components/dialogs/goTo.js.map +1 -0
  43. package/dist/components/dialogs/metadataLabels.d.ts +4 -0
  44. package/dist/components/dialogs/metadataLabels.js +52 -0
  45. package/dist/components/dialogs/metadataLabels.js.map +1 -0
  46. package/dist/components/dialogs/metadataRecord.d.ts +1 -0
  47. package/dist/components/dialogs/metadataRecord.js +38 -0
  48. package/dist/components/dialogs/metadataRecord.js.map +1 -0
  49. package/dist/components/header/Header.d.ts +6 -2
  50. package/dist/components/header/Header.js +30 -32
  51. package/dist/components/header/Header.js.map +1 -1
  52. package/dist/components/header/{TreeSettingsMenu.d.ts → HiddenColumnsChip.d.ts} +2 -2
  53. package/dist/components/header/HiddenColumnsChip.js +15 -0
  54. package/dist/components/header/HiddenColumnsChip.js.map +1 -0
  55. package/dist/components/header/MenuBar.d.ts +6 -0
  56. package/dist/components/header/MenuBar.js +33 -0
  57. package/dist/components/header/MenuBar.js.map +1 -0
  58. package/dist/components/header/ScrollZoomCheckbox.d.ts +7 -0
  59. package/dist/components/header/ScrollZoomCheckbox.js +19 -0
  60. package/dist/components/header/ScrollZoomCheckbox.js.map +1 -0
  61. package/dist/components/header/ZoomControls.js +10 -30
  62. package/dist/components/header/ZoomControls.js.map +1 -1
  63. package/dist/components/header/colorSchemeMenuItems.d.ts +4 -0
  64. package/dist/components/header/colorSchemeMenuItems.js +106 -0
  65. package/dist/components/header/colorSchemeMenuItems.js.map +1 -0
  66. package/dist/components/header/getAnnotationsMenu.d.ts +3 -30
  67. package/dist/components/header/getAnnotationsMenu.js +48 -54
  68. package/dist/components/header/getAnnotationsMenu.js.map +1 -1
  69. package/dist/components/header/menuBarItems.d.ts +11 -0
  70. package/dist/components/header/menuBarItems.js +277 -0
  71. package/dist/components/header/menuBarItems.js.map +1 -0
  72. package/dist/components/import/ImportForm.js +12 -6
  73. package/dist/components/import/ImportForm.js.map +1 -1
  74. package/dist/components/minimap/Minimap.js +7 -3
  75. package/dist/components/minimap/Minimap.js.map +1 -1
  76. package/dist/components/minimap/MinimapSVG.js +2 -1
  77. package/dist/components/minimap/MinimapSVG.js.map +1 -1
  78. package/dist/components/minimap/minimapLayout.d.ts +12 -0
  79. package/dist/components/minimap/minimapLayout.js +20 -0
  80. package/dist/components/minimap/minimapLayout.js.map +1 -1
  81. package/dist/components/msa/MSACanvas.js +26 -9
  82. package/dist/components/msa/MSACanvas.js.map +1 -1
  83. package/dist/components/msa/MSACanvasBlock.js +15 -11
  84. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  85. package/dist/components/msa/MSAMouseoverCanvas.js +2 -2
  86. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  87. package/dist/components/msa/WheelZoomHint.d.ts +7 -0
  88. package/dist/components/msa/WheelZoomHint.js +38 -0
  89. package/dist/components/msa/WheelZoomHint.js.map +1 -0
  90. package/dist/components/msa/msaKeys.js +3 -0
  91. package/dist/components/msa/msaKeys.js.map +1 -1
  92. package/dist/components/msa/msaRaster.d.ts +4 -5
  93. package/dist/components/msa/msaRaster.js +5 -5
  94. package/dist/components/msa/msaRaster.js.map +1 -1
  95. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +22 -2
  96. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  97. package/dist/components/msa/renderHighlights.d.ts +0 -3
  98. package/dist/components/msa/renderHighlights.js +3 -3
  99. package/dist/components/msa/renderHighlights.js.map +1 -1
  100. package/dist/components/msa/renderMSABlock.d.ts +6 -1
  101. package/dist/components/msa/renderMSABlock.js +44 -17
  102. package/dist/components/msa/renderMSABlock.js.map +1 -1
  103. package/dist/components/msa/tileColor.js +7 -0
  104. package/dist/components/msa/tileColor.js.map +1 -1
  105. package/dist/components/rowpanels/RowPanelBlock.js +2 -2
  106. package/dist/components/rowpanels/RowPanelBlock.js.map +1 -1
  107. package/dist/components/settings/MenuItemsForm.d.ts +10 -0
  108. package/dist/components/settings/MenuItemsForm.js +80 -0
  109. package/dist/components/settings/MenuItemsForm.js.map +1 -0
  110. package/dist/components/settings/SettingsPanel.d.ts +12 -0
  111. package/dist/components/settings/SettingsPanel.js +92 -0
  112. package/dist/components/settings/SettingsPanel.js.map +1 -0
  113. package/dist/components/{header/FileMenu.d.ts → settings/TrackList.d.ts} +2 -2
  114. package/dist/components/settings/TrackList.js +58 -0
  115. package/dist/components/settings/TrackList.js.map +1 -0
  116. package/dist/components/tracks/TrackBlocks.js +2 -2
  117. package/dist/components/tracks/TrackBlocks.js.map +1 -1
  118. package/dist/components/tracks/TrackTooltipContent.js +2 -1
  119. package/dist/components/tracks/TrackTooltipContent.js.map +1 -1
  120. package/dist/components/tracks/drawTracks.js +13 -6
  121. package/dist/components/tracks/drawTracks.js.map +1 -1
  122. package/dist/components/tree/TreeCanvasBlock.js +2 -2
  123. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  124. package/dist/components/tree/TreeMenu.js +4 -6
  125. package/dist/components/tree/TreeMenu.js.map +1 -1
  126. package/dist/components/tree/TreeOverview.js +2 -2
  127. package/dist/components/tree/TreeOverview.js.map +1 -1
  128. package/dist/constants.d.ts +4 -1
  129. package/dist/constants.js +7 -1
  130. package/dist/constants.js.map +1 -1
  131. package/dist/darkReader.d.ts +7 -0
  132. package/dist/darkReader.js +42 -0
  133. package/dist/darkReader.js.map +1 -0
  134. package/dist/element.js +22 -47
  135. package/dist/element.js.map +1 -1
  136. package/dist/featurePanels.d.ts +37 -0
  137. package/dist/featurePanels.js +187 -0
  138. package/dist/featurePanels.js.map +1 -0
  139. package/dist/filehandleLoader.d.ts +41 -0
  140. package/dist/filehandleLoader.js +99 -0
  141. package/dist/filehandleLoader.js.map +1 -0
  142. package/dist/index.d.ts +3 -0
  143. package/dist/index.js +2 -0
  144. package/dist/index.js.map +1 -1
  145. package/dist/model/treeModel.d.ts +9 -0
  146. package/dist/model/treeModel.js +12 -1
  147. package/dist/model/treeModel.js.map +1 -1
  148. package/dist/model.d.ts +148 -32
  149. package/dist/model.js +291 -569
  150. package/dist/model.js.map +1 -1
  151. package/dist/neighborJoining.js +1 -40
  152. package/dist/neighborJoining.js.map +1 -1
  153. package/dist/renderToSvg.js +1 -13
  154. package/dist/renderToSvg.js.map +1 -1
  155. package/dist/rerootTree.d.ts +1 -0
  156. package/dist/rerootTree.js +7 -6
  157. package/dist/rerootTree.js.map +1 -1
  158. package/dist/schemeResidueColor.d.ts +12 -0
  159. package/dist/schemeResidueColor.js +33 -0
  160. package/dist/schemeResidueColor.js.map +1 -0
  161. package/dist/similarityColors.d.ts +11 -0
  162. package/dist/similarityColors.js +90 -0
  163. package/dist/similarityColors.js.map +1 -0
  164. package/dist/svgTestUtil.d.ts +64 -12
  165. package/dist/treeDisplay.d.ts +37 -0
  166. package/dist/treeDisplay.js +209 -0
  167. package/dist/treeDisplay.js.map +1 -0
  168. package/dist/types.d.ts +7 -3
  169. package/dist/useWheelScroll.d.ts +3 -1
  170. package/dist/useWheelScroll.js +36 -17
  171. package/dist/useWheelScroll.js.map +1 -1
  172. package/dist/util.d.ts +2 -0
  173. package/dist/util.js +2 -0
  174. package/dist/util.js.map +1 -1
  175. package/dist/version.d.ts +1 -1
  176. package/dist/version.js +1 -1
  177. package/dist/wheelZoom.d.ts +8 -3
  178. package/dist/wheelZoom.js +44 -32
  179. package/dist/wheelZoom.js.map +1 -1
  180. package/dist/zoomSnapshot.d.ts +29 -0
  181. package/dist/zoomSnapshot.js +47 -0
  182. package/dist/zoomSnapshot.js.map +1 -0
  183. package/package.json +11 -11
  184. package/src/blosum62.ts +45 -0
  185. package/src/colorSchemes.ts +1 -0
  186. package/src/components/Loading.tsx +6 -2
  187. package/src/components/MSAView.tsx +22 -5
  188. package/src/components/MetadataTable.tsx +83 -26
  189. package/src/components/Track.tsx +13 -0
  190. package/src/components/controls.ts +2 -1
  191. package/src/components/dialogs/AnnotationFileDialog.tsx +12 -1
  192. package/src/components/dialogs/FeatureDialog.tsx +2 -5
  193. package/src/components/dialogs/GapThresholdDialog.tsx +36 -0
  194. package/src/components/{header → dialogs}/GappynessSlider.tsx +3 -10
  195. package/src/components/{header → dialogs}/GoToBox.tsx +36 -10
  196. package/src/components/dialogs/GoToDialog.tsx +30 -0
  197. package/src/components/dialogs/MetadataDialog.tsx +32 -4
  198. package/src/components/{header → dialogs}/goTo.ts +1 -1
  199. package/src/components/dialogs/metadataLabels.ts +54 -0
  200. package/src/components/dialogs/metadataRecord.tsx +61 -0
  201. package/src/components/header/Header.tsx +75 -42
  202. package/src/components/header/HiddenColumnsChip.tsx +39 -0
  203. package/src/components/header/MenuBar.tsx +64 -0
  204. package/src/components/header/ScrollZoomCheckbox.tsx +46 -0
  205. package/src/components/header/ZoomControls.tsx +20 -35
  206. package/src/components/header/colorSchemeMenuItems.ts +121 -0
  207. package/src/components/header/getAnnotationsMenu.ts +52 -56
  208. package/src/components/header/menuBarItems.ts +357 -0
  209. package/src/components/import/ImportForm.tsx +19 -7
  210. package/src/components/minimap/Minimap.tsx +11 -3
  211. package/src/components/minimap/MinimapSVG.tsx +17 -1
  212. package/src/components/minimap/minimapLayout.ts +27 -0
  213. package/src/components/msa/MSACanvas.tsx +22 -3
  214. package/src/components/msa/MSACanvasBlock.tsx +18 -11
  215. package/src/components/msa/MSAMouseoverCanvas.tsx +2 -2
  216. package/src/components/msa/WheelZoomHint.tsx +56 -0
  217. package/src/components/msa/msaKeys.ts +3 -0
  218. package/src/components/msa/msaRaster.ts +6 -8
  219. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +28 -5
  220. package/src/components/msa/renderHighlights.ts +3 -3
  221. package/src/components/msa/renderMSABlock.ts +51 -15
  222. package/src/components/msa/tileColor.ts +7 -0
  223. package/src/components/rowpanels/RowPanelBlock.tsx +2 -2
  224. package/src/components/settings/MenuItemsForm.tsx +225 -0
  225. package/src/components/settings/SettingsPanel.tsx +156 -0
  226. package/src/components/settings/TrackList.tsx +124 -0
  227. package/src/components/tracks/TrackBlocks.tsx +2 -2
  228. package/src/components/tracks/TrackTooltipContent.tsx +2 -1
  229. package/src/components/tracks/drawTracks.ts +15 -7
  230. package/src/components/tree/TreeCanvasBlock.tsx +2 -2
  231. package/src/components/tree/TreeMenu.tsx +4 -6
  232. package/src/components/tree/TreeOverview.tsx +2 -2
  233. package/src/constants.ts +9 -1
  234. package/src/darkReader.ts +57 -0
  235. package/src/element.ts +34 -57
  236. package/src/featurePanels.ts +269 -0
  237. package/src/filehandleLoader.ts +136 -0
  238. package/src/index.ts +4 -0
  239. package/src/model/treeModel.ts +13 -0
  240. package/src/model.ts +370 -748
  241. package/src/neighborJoining.ts +1 -42
  242. package/src/renderToSvg.tsx +1 -13
  243. package/src/rerootTree.ts +7 -6
  244. package/src/schemeResidueColor.ts +37 -0
  245. package/src/similarityColors.ts +112 -0
  246. package/src/treeDisplay.ts +278 -0
  247. package/src/types.ts +7 -4
  248. package/src/useWheelScroll.ts +43 -22
  249. package/src/util.ts +3 -0
  250. package/src/version.ts +1 -1
  251. package/src/wheelZoom.ts +55 -33
  252. package/src/zoomSnapshot.ts +80 -0
  253. package/dist/components/header/ColorSchemeMenu.d.ts +0 -12
  254. package/dist/components/header/ColorSchemeMenu.js +0 -37
  255. package/dist/components/header/ColorSchemeMenu.js.map +0 -1
  256. package/dist/components/header/FileMenu.js +0 -92
  257. package/dist/components/header/FileMenu.js.map +0 -1
  258. package/dist/components/header/GappynessSlider.js.map +0 -1
  259. package/dist/components/header/GoToBox.js +0 -31
  260. package/dist/components/header/GoToBox.js.map +0 -1
  261. package/dist/components/header/MSASettingsMenu.js +0 -11
  262. package/dist/components/header/MSASettingsMenu.js.map +0 -1
  263. package/dist/components/header/ScrollZoomIcons.d.ts +0 -6
  264. package/dist/components/header/ScrollZoomIcons.js +0 -35
  265. package/dist/components/header/ScrollZoomIcons.js.map +0 -1
  266. package/dist/components/header/TreeSettingsMenu.js +0 -11
  267. package/dist/components/header/TreeSettingsMenu.js.map +0 -1
  268. package/dist/components/header/goTo.d.ts +0 -20
  269. package/dist/components/header/goTo.js.map +0 -1
  270. package/dist/components/header/settingsMenuItems.d.ts +0 -5
  271. package/dist/components/header/settingsMenuItems.js +0 -171
  272. package/dist/components/header/settingsMenuItems.js.map +0 -1
  273. package/src/components/header/ColorSchemeMenu.tsx +0 -56
  274. package/src/components/header/FileMenu.tsx +0 -107
  275. package/src/components/header/MSASettingsMenu.tsx +0 -26
  276. package/src/components/header/ScrollZoomIcons.tsx +0 -52
  277. package/src/components/header/TreeSettingsMenu.tsx +0 -30
  278. package/src/components/header/settingsMenuItems.ts +0 -216
@@ -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
- // prettier-ignore
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
@@ -7,7 +7,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
9
  import { rasterApplies, rasterImageHref } from './components/msa/msaRaster.ts'
10
- import { renderBoxFeatureCanvasBlock } from './components/msa/renderBoxFeatureCanvasBlock.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'
@@ -412,18 +411,6 @@ function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
412
411
  const raster = rasterBackground({ model, theme, layout })
413
412
 
414
413
  const msaCtx = new Context(msaAreaWidth, contentHeight)
415
- renderBoxFeatureCanvasBlock({
416
- model,
417
- theme,
418
- ctx: msaCtx,
419
- offsetX,
420
- offsetY,
421
- // the overlay culls bands to this width; without it the export falls back
422
- // to the on-screen 500px block size and drops every band past it
423
- blockSizeXOverride: msaAreaWidth,
424
- blockSizeYOverride: contentHeight,
425
- highResScaleFactorOverride: 1,
426
- })
427
414
  renderMSABlock({
428
415
  model,
429
416
  theme,
@@ -434,6 +421,7 @@ function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
434
421
  blockSizeYOverride: contentHeight,
435
422
  highResScaleFactorOverride: 1,
436
423
  rasterTiles: !!raster,
424
+ domains: true,
437
425
  })
438
426
  msaCtx.resetTransform()
439
427
  renderPersistentHighlights({
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.set(child, node)
23
+ parents?.set(child, node)
25
24
  stack.push(child)
26
25
  }
27
26
  }
28
- return { order, parents }
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 { order, parents } = preorder(root)
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 { order, parents } = preorder(root)
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
+ }
@@ -0,0 +1,278 @@
1
+ import { generateNodeIds, parseEmfTree, parseNewick } from 'msa-parsers'
2
+
3
+ import { cladeHighlightAlpha, cladeHighlightColor } from './constants.ts'
4
+ import { flatToTree } from './flatToTree.ts'
5
+ import {
6
+ collapse,
7
+ find,
8
+ forEachDescendant,
9
+ hierarchy,
10
+ leafIndex,
11
+ leaves,
12
+ mrca,
13
+ sort,
14
+ sum as hierarchySum,
15
+ } from './hierarchy.ts'
16
+ import { parseAsn1 } from './parseAsn1.ts'
17
+ import { midpointRoot, preorder, rerootTree } from './rerootTree.ts'
18
+ import { withAlpha } from './util.ts'
19
+
20
+ import type { HierarchyNode } from './hierarchy.ts'
21
+ import type { TreeNode } from './rerootTree.ts'
22
+ import type {
23
+ Clade,
24
+ CladeMark,
25
+ NodeWithIds,
26
+ ResolvedClade,
27
+ TreeOrder,
28
+ TreeRoot,
29
+ } from './types.ts'
30
+
31
+ export function parseTreeText(text: string) {
32
+ if (text.startsWith('BioTreeContainer')) {
33
+ return flatToTree(parseAsn1(text))
34
+ }
35
+ return parseNewick(text.startsWith('SEQ') ? parseEmfTree(text).tree : text)
36
+ }
37
+
38
+ // the marks that set tree state from a node rather than drawing
39
+ const seedingMarks = new Set<CladeMark>(['collapse', 'focus', 'rotate'])
40
+
41
+ /**
42
+ * Each tip of the full tree in the order the tree draws it, with the on-screen
43
+ * row that shows it: its own, or the row of the collapsed node standing for it
44
+ */
45
+ function tipRows(root: HierarchyNode<NodeWithIds>) {
46
+ const rows = new Map<string, { order: number; row: number }>()
47
+ let row = 0
48
+ const pushReversed = (
49
+ stack: HierarchyNode<NodeWithIds>[],
50
+ nodes: HierarchyNode<NodeWithIds>[],
51
+ ) => {
52
+ for (let i = nodes.length - 1; i >= 0; i--) {
53
+ stack.push(nodes[i]!)
54
+ }
55
+ }
56
+ const stack = [root]
57
+ while (stack.length > 0) {
58
+ const node = stack.pop()!
59
+ if (node.children?.length) {
60
+ pushReversed(stack, node.children)
61
+ } else {
62
+ const hidden = [node]
63
+ while (hidden.length > 0) {
64
+ const tip = hidden.pop()!
65
+ const kids = tip.children ?? tip._children
66
+ if (kids?.length) {
67
+ pushReversed(hidden, kids)
68
+ } else {
69
+ rows.set(tip.data.name, { order: rows.size, row })
70
+ }
71
+ }
72
+ row++
73
+ }
74
+ }
75
+ return rows
76
+ }
77
+
78
+ /**
79
+ * The rows a clade covers and the node an `mrca` names, or undefined when the
80
+ * clade does not resolve: a tip name the tree does not have or has twice, or a
81
+ * leaf count `tips` disagrees with. A `range` counts its tips in the order
82
+ * `tips` gives them, so a collapsed clade inside it counts every leaf it
83
+ * stands for. `index` is the memoized leaf pass over the tree.
84
+ */
85
+ function cladeRows(
86
+ clade: Clade,
87
+ root: HierarchyNode<NodeWithIds>,
88
+ index: Map<string, HierarchyNode<NodeWithIds> | undefined>,
89
+ tips: Map<string, { order: number; row: number }>,
90
+ ): { rows: [number, number]; nodeId?: string } | undefined {
91
+ if (clade.range) {
92
+ const [a, b] = clade.range.map(name =>
93
+ index.get(name) ? tips.get(name) : undefined,
94
+ )
95
+ if (a === undefined || b === undefined) {
96
+ return undefined
97
+ }
98
+ const [first, last] = a.order <= b.order ? [a, b] : [b, a]
99
+ return last.order - first.order + 1 === clade.tips
100
+ ? { rows: [first.row, last.row] }
101
+ : undefined
102
+ }
103
+ const node = clade.mrca ? mrca(root, clade.mrca, index) : undefined
104
+ if (!node) {
105
+ return undefined
106
+ }
107
+ const nodeTips = leaves(node)
108
+ if (nodeTips.length !== clade.tips) {
109
+ return undefined
110
+ }
111
+ let first = Infinity
112
+ let last = -Infinity
113
+ for (const tip of nodeTips) {
114
+ const row = tips.get(tip.data.name)?.row
115
+ if (row !== undefined) {
116
+ first = Math.min(first, row)
117
+ last = Math.max(last, row)
118
+ }
119
+ }
120
+ return first <= last
121
+ ? { rows: [first, last], nodeId: node.data.id }
122
+ : undefined
123
+ }
124
+
125
+ /**
126
+ * Every clade that resolves, with its fill color settled, against the rows of
127
+ * `displayed`, the tree as drawn. The tree panel resolves against the
128
+ * displayed rows and the overview against its own, so both take the tree they
129
+ * draw. A `range` record names no node, so the seeding marks, which need one,
130
+ * drop it.
131
+ */
132
+ export function resolveClades(
133
+ clades: Clade[],
134
+ root: HierarchyNode<NodeWithIds>,
135
+ displayed: HierarchyNode<NodeWithIds>,
136
+ ): ResolvedClade[] {
137
+ const index = leafIndex(root)
138
+ const tips = tipRows(displayed)
139
+ return clades.flatMap(clade => {
140
+ const resolved = cladeRows(clade, root, index, tips)
141
+ const seeding = seedingMarks.has(clade.mark)
142
+ return resolved && !(seeding && resolved.nodeId === undefined)
143
+ ? [
144
+ {
145
+ ...resolved,
146
+ mark: clade.mark,
147
+ color: withAlpha(
148
+ clade.color ?? cladeHighlightColor,
149
+ cladeHighlightAlpha,
150
+ ),
151
+ markColor: clade.color,
152
+ label: clade.label,
153
+ },
154
+ ]
155
+ : []
156
+ })
157
+ }
158
+
159
+ const childOrders: Record<
160
+ TreeOrder,
161
+ ((a: HierarchyNode, b: HierarchyNode) => number) | undefined
162
+ > = {
163
+ input: undefined,
164
+ branchLength: (a, b) => (a.data.length ?? 1) - (b.data.length ?? 1),
165
+ ladderize: (a, b) => a.value! - b.value!,
166
+ ladderizeReverse: (a, b) => b.value! - a.value!,
167
+ }
168
+
169
+ /**
170
+ * The tree with the display transforms applied: leaf counts summed, children
171
+ * put in `order` and each `rotated` node's reversed, `showOnly` taken as the
172
+ * new root, and each `collapsed` clade folded. The overview builds the same
173
+ * tree without the focus, so it shows the focused subtree inside the whole.
174
+ */
175
+ export function buildTreeRoot(
176
+ tree: NodeWithIds,
177
+ {
178
+ collapsed,
179
+ rotated,
180
+ order,
181
+ showOnly,
182
+ }: {
183
+ collapsed: readonly string[]
184
+ rotated: readonly string[]
185
+ order: TreeOrder
186
+ showOnly?: string
187
+ },
188
+ ) {
189
+ let hier = hierarchy(tree, d => d.children)
190
+ hierarchySum(hier, d => (d.children.length > 0 ? 0 : 1))
191
+ const compare = childOrders[order]
192
+ if (compare) {
193
+ sort(hier, compare)
194
+ }
195
+ if (rotated.length > 0) {
196
+ const flip = new Set(rotated)
197
+ forEachDescendant(hier, n => {
198
+ if (flip.has(n.data.id)) {
199
+ n.children?.reverse()
200
+ }
201
+ })
202
+ }
203
+
204
+ if (showOnly) {
205
+ const res = find(hier, n => n.data.id === showOnly)
206
+ if (res) {
207
+ hier = res
208
+ }
209
+ }
210
+
211
+ for (const collapsedId of collapsed) {
212
+ const node = find(hier, n => n.data.id === collapsedId)
213
+ if (node) {
214
+ if (node.children) {
215
+ collapse(node)
216
+ } else if (node.parent?.children) {
217
+ node.parent.children = node.parent.children.filter(
218
+ c => c.data.id !== collapsedId,
219
+ )
220
+ }
221
+ }
222
+ }
223
+
224
+ return hier
225
+ }
226
+
227
+ /**
228
+ * The node whose branch an outgroup roots the tree on: the outgroup's most
229
+ * recent common ancestor, or the rest of the tips' where that ancestor is the
230
+ * root, since the outgroup then straddles the root and the branch above the
231
+ * other side is the same branch of the unrooted tree.
232
+ */
233
+ export function outgroupNode(root: HierarchyNode, outgroup: string[]) {
234
+ const index = leafIndex(root)
235
+ const node = mrca(root, outgroup, index)
236
+ if (node !== root) {
237
+ return node
238
+ }
239
+ const out = new Set(outgroup)
240
+ const rest = leaves(root)
241
+ .map(n => n.data.name)
242
+ .filter(name => !out.has(name))
243
+ const other = mrca(root, rest, index)
244
+ return other === root ? undefined : other
245
+ }
246
+
247
+ /**
248
+ * `tree` rerooted as `treeRoot` asks, with fresh path ids. The ids of the tree
249
+ * as written become names on unnamed nodes, and a rerooting would carry them
250
+ * along as labels, so they come off first.
251
+ */
252
+ export function rootTree(tree: NodeWithIds, treeRoot: TreeRoot) {
253
+ const plain = new Map<NodeWithIds, TreeNode>()
254
+ for (const node of preorder(tree).reverse()) {
255
+ plain.set(node, {
256
+ name: node.name === node.id ? undefined : node.name,
257
+ length: node.length,
258
+ children: node.children.map(c => plain.get(c)!),
259
+ })
260
+ }
261
+ const input = plain.get(tree)!
262
+ if (treeRoot === 'midpoint') {
263
+ return generateNodeIds(midpointRoot(input))
264
+ }
265
+ const target = outgroupNode(
266
+ hierarchy(tree, d => d.children),
267
+ treeRoot.outgroup,
268
+ )
269
+ return target
270
+ ? generateNodeIds(rerootTree(input, plain.get(target.data)!))
271
+ : tree
272
+ }
273
+
274
+ // the inclusive tip indices a node covers, from its xMin/xMax in a layout one
275
+ // row per tip
276
+ export function tipRange(node: HierarchyNode): [number, number] {
277
+ return [Math.round(node.xMin! - 0.5), Math.round(node.xMax! - 0.5)]
278
+ }
package/src/types.ts CHANGED
@@ -8,7 +8,7 @@ export interface Accession {
8
8
  }
9
9
  // every kind draws into the same per-column space; drawTracks.ts dispatches on
10
10
  // it to pick the draw function
11
- export type TrackKind = 'text' | 'bar' | 'logo' | 'arc' | 'ruler'
11
+ export type TrackKind = 'text' | 'bar' | 'logo' | 'arc' | 'ruler' | 'reference'
12
12
 
13
13
  /**
14
14
  * Which channel the color scheme paints. `fill` colors the cell a residue sits
@@ -27,14 +27,11 @@ export type ColorScheme = string | Required<Pick<ScaleSpec, 'map'>>
27
27
  export interface BasicTrackModel {
28
28
  id: string
29
29
  name: string
30
- associatedRowName?: string
31
30
  height: number
32
31
  kind: TrackKind
33
32
  // the model height this track's divider writes, shared with every track
34
33
  // carrying the same key. Absent on a track no divider resizes.
35
34
  heightKey?: string
36
- // hidden until the user asks for it, unless they have already chosen
37
- defaultOff?: boolean
38
35
  }
39
36
 
40
37
  export interface TextTrackModel extends BasicTrackModel {
@@ -209,6 +206,12 @@ export interface MappedStructure {
209
206
  id: string
210
207
  kind?: 'experimental' | 'predicted'
211
208
  asymId?: string
209
+ /**
210
+ * which id `asymId` is, the file's `label_asym_id` or the author's
211
+ * `auth_asym_id`. Unset reads as either, label first, which is right for the
212
+ * common entry whose two ids agree and wrong where they collide.
213
+ */
214
+ asymIdKind?: 'label' | 'auth'
212
215
  url?: string
213
216
  }
214
217