jbrowse-plugin-graphgenomeviewer 1.0.0 → 3.0.3

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 (283) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +202 -24
  3. package/dist/chunks/AddTrackWorkflow-Y3OBHM33.js +2 -0
  4. package/dist/chunks/AddTrackWorkflow-Y3OBHM33.js.map +7 -0
  5. package/dist/chunks/GbzBaseSyntenyAdapter-EFIOI2SN.js +8 -0
  6. package/dist/chunks/GbzBaseSyntenyAdapter-EFIOI2SN.js.map +7 -0
  7. package/dist/chunks/GraphGenomeView-UD5WCV3Q.js +14 -0
  8. package/dist/chunks/GraphGenomeView-UD5WCV3Q.js.map +7 -0
  9. package/dist/chunks/MinigraphBubbleAdapter-B75VQK7T.js +2 -0
  10. package/dist/chunks/MinigraphBubbleAdapter-B75VQK7T.js.map +7 -0
  11. package/dist/chunks/RgfaTabixAdapter-IT7I5RR3.js +3 -0
  12. package/dist/chunks/RgfaTabixAdapter-IT7I5RR3.js.map +7 -0
  13. package/dist/chunks/bandage-layout-FIOZ7B3E.js +1660 -0
  14. package/dist/chunks/bandage-layout-FIOZ7B3E.js.map +7 -0
  15. package/dist/chunks/chunk-D7XFR2IV.js +2 -0
  16. package/dist/chunks/chunk-D7XFR2IV.js.map +7 -0
  17. package/dist/chunks/chunk-F4C3BEPV.js +2 -0
  18. package/dist/chunks/chunk-F4C3BEPV.js.map +7 -0
  19. package/dist/chunks/chunk-FCWXYIFC.js +2 -0
  20. package/dist/chunks/chunk-FCWXYIFC.js.map +7 -0
  21. package/dist/chunks/chunk-FY3BTKY4.js +2 -0
  22. package/dist/chunks/chunk-FY3BTKY4.js.map +7 -0
  23. package/dist/chunks/chunk-J4OLMLS5.js +2 -0
  24. package/dist/chunks/chunk-J4OLMLS5.js.map +7 -0
  25. package/dist/chunks/chunk-JCG7RLMB.js +2 -0
  26. package/dist/chunks/chunk-JCG7RLMB.js.map +7 -0
  27. package/dist/chunks/chunk-KWAXLUPQ.js +2 -0
  28. package/dist/chunks/chunk-KWAXLUPQ.js.map +7 -0
  29. package/dist/chunks/chunk-LGKM2PZH.js +2 -0
  30. package/dist/chunks/chunk-LGKM2PZH.js.map +7 -0
  31. package/dist/chunks/chunk-TN6FK5UD.js +2 -0
  32. package/dist/chunks/chunk-TN6FK5UD.js.map +7 -0
  33. package/dist/chunks/chunk-X3X74OGB.js +2 -0
  34. package/dist/chunks/chunk-X3X74OGB.js.map +7 -0
  35. package/dist/chunks/chunk-XEJ47DLD.js +2 -0
  36. package/dist/chunks/chunk-XEJ47DLD.js.map +7 -0
  37. package/dist/chunks/chunk-ZNGDWZ6Z.js +2 -0
  38. package/dist/chunks/chunk-ZNGDWZ6Z.js.map +7 -0
  39. package/dist/chunks/executeDiagonalize-XMPEDHVZ.js +2 -0
  40. package/dist/chunks/executeDiagonalize-XMPEDHVZ.js.map +7 -0
  41. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +3 -0
  42. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +7 -0
  43. package/package.json +94 -49
  44. package/src/GbzBaseSyntenyAdapter/GbzBaseSyntenyAdapter.test.ts +427 -0
  45. package/src/GbzBaseSyntenyAdapter/GbzBaseSyntenyAdapter.ts +461 -0
  46. package/src/GbzBaseSyntenyAdapter/configSchema.ts +181 -0
  47. package/src/GbzBaseSyntenyAdapter/index.ts +27 -0
  48. package/src/GbzBaseSyntenyAdapter/test_data/micb-kir3dl1.gbz.db +0 -0
  49. package/src/GbzBaseSyntenyAdapter/test_data/micb-kir3dl1.haplotype-index.db +0 -0
  50. package/src/GetSubgraph.test.ts +193 -0
  51. package/src/GetSubgraph.ts +93 -0
  52. package/src/GraphAddTrackWorkflow/AddTrackWorkflow.tsx +167 -0
  53. package/src/GraphAddTrackWorkflow/buildTrackConfig.test.ts +150 -0
  54. package/src/GraphAddTrackWorkflow/buildTrackConfig.ts +114 -0
  55. package/src/GraphAddTrackWorkflow/index.ts +20 -0
  56. package/src/GraphComputeLayout.ts +46 -0
  57. package/src/GraphGenomeView/anchoredNodes.ts +101 -0
  58. package/src/GraphGenomeView/bubbleSpreads.ts +102 -0
  59. package/src/GraphGenomeView/bubbles/bubbleHalos.test.ts +58 -0
  60. package/src/GraphGenomeView/bubbles/bubbleHalos.ts +149 -0
  61. package/src/GraphGenomeView/bubbles/bubbleRoutes.test.ts +141 -0
  62. package/src/GraphGenomeView/bubbles/bubblesFromGraph.test.ts +239 -0
  63. package/src/GraphGenomeView/bubbles/bubblesFromGraph.ts +210 -0
  64. package/src/GraphGenomeView/bubbles/classifyBubble.test.ts +172 -0
  65. package/src/GraphGenomeView/bubbles/classifyBubble.ts +138 -0
  66. package/src/GraphGenomeView/bubbles/popBubble.test.ts +73 -0
  67. package/src/GraphGenomeView/bubbles/popBubble.ts +26 -0
  68. package/src/GraphGenomeView/colorSchemes.ts +36 -0
  69. package/src/GraphGenomeView/components/BubbleHalos.tsx +162 -0
  70. package/src/GraphGenomeView/components/BubbleOverlay.tsx +181 -0
  71. package/src/GraphGenomeView/components/ColorSchemeSelect.tsx +41 -0
  72. package/src/GraphGenomeView/components/GenePins.tsx +107 -0
  73. package/src/GraphGenomeView/components/GraphCanvas.tsx +759 -0
  74. package/src/GraphGenomeView/components/GraphGenomeView.test.tsx +77 -0
  75. package/src/GraphGenomeView/components/GraphGenomeView.tsx +18 -17
  76. package/src/GraphGenomeView/components/GraphLoadStatus.tsx +84 -0
  77. package/src/GraphGenomeView/components/GraphSettingsDialog.tsx +327 -0
  78. package/src/GraphGenomeView/components/GraphStats.tsx +82 -0
  79. package/src/GraphGenomeView/components/GraphToolbar.tsx +84 -0
  80. package/src/GraphGenomeView/components/ImportForm.tsx +124 -0
  81. package/src/GraphGenomeView/components/LabelChip.tsx +65 -0
  82. package/src/GraphGenomeView/components/LayoutSelect.tsx +56 -0
  83. package/src/GraphGenomeView/components/RepeatSelect.tsx +51 -0
  84. package/src/GraphGenomeView/components/SettingsMenu.tsx +82 -0
  85. package/src/GraphGenomeView/components/SubgraphContextSelect.tsx +75 -0
  86. package/src/GraphGenomeView/components/SubgraphHaplotypesField.tsx +86 -0
  87. package/src/GraphGenomeView/components/WalkRowsOverlay.tsx +252 -0
  88. package/src/GraphGenomeView/components/WalkSelect.tsx +51 -0
  89. package/src/GraphGenomeView/components/overlayLabels.test.ts +56 -0
  90. package/src/GraphGenomeView/components/overlayLabels.ts +75 -0
  91. package/src/GraphGenomeView/deletionEdges.test.ts +198 -0
  92. package/src/GraphGenomeView/deletionEdges.ts +217 -0
  93. package/src/GraphGenomeView/genes/geneFeatures.ts +107 -0
  94. package/src/GraphGenomeView/genes/genePins.ts +130 -0
  95. package/src/GraphGenomeView/genes/genes.test.ts +153 -0
  96. package/src/GraphGenomeView/gfa/gfaConverter.test.ts +394 -0
  97. package/src/GraphGenomeView/gfa/gfaConverter.ts +235 -0
  98. package/src/GraphGenomeView/graphLabels.budget.test.ts +36 -0
  99. package/src/GraphGenomeView/graphLabels.test.ts +548 -0
  100. package/src/GraphGenomeView/graphLabels.ts +640 -0
  101. package/src/GraphGenomeView/index.ts +29 -7
  102. package/src/GraphGenomeView/laneRamp.test.ts +97 -0
  103. package/src/GraphGenomeView/laneRamp.ts +87 -0
  104. package/src/GraphGenomeView/launchKeys.test.ts +63 -0
  105. package/src/GraphGenomeView/layout/anchoredLayout.test.ts +213 -0
  106. package/src/GraphGenomeView/layout/anchoredLayout.ts +80 -0
  107. package/src/GraphGenomeView/layout/bubbleCrossing.test.ts +190 -0
  108. package/src/GraphGenomeView/layout/drawnScale.ts +199 -0
  109. package/src/GraphGenomeView/layout/mergeRuns.test.ts +100 -0
  110. package/src/GraphGenomeView/layout/mergeRuns.ts +203 -0
  111. package/src/GraphGenomeView/layout/orderedLayout.test.ts +212 -0
  112. package/src/GraphGenomeView/layout/orderedLayout.ts +396 -0
  113. package/src/GraphGenomeView/layout/orientToReference.test.ts +110 -0
  114. package/src/GraphGenomeView/layout/orientToReference.ts +79 -0
  115. package/src/GraphGenomeView/layout/placeOffReference.ts +147 -0
  116. package/src/GraphGenomeView/layout/referenceSeeds.test.ts +79 -0
  117. package/src/GraphGenomeView/layout/referenceSeeds.ts +89 -0
  118. package/src/GraphGenomeView/layout/rowSpacing.ts +5 -0
  119. package/src/GraphGenomeView/layout/sampleRowLayout.test.ts +318 -0
  120. package/src/GraphGenomeView/layout/sampleRowLayout.ts +134 -0
  121. package/src/GraphGenomeView/layout/variantMapLayout.test.ts +59 -0
  122. package/src/GraphGenomeView/layout/variantMapLayout.ts +20 -0
  123. package/src/GraphGenomeView/layout/walkRowLayout.ts +54 -0
  124. package/src/GraphGenomeView/layout/walkRows.test.ts +178 -0
  125. package/src/GraphGenomeView/layout/walkRows.ts +177 -0
  126. package/src/GraphGenomeView/layoutModes.ts +131 -0
  127. package/src/GraphGenomeView/model.test.ts +2696 -0
  128. package/src/GraphGenomeView/model.ts +2672 -94
  129. package/src/GraphGenomeView/nodeWidths.ts +67 -0
  130. package/src/GraphGenomeView/pathAnchoring.test.ts +252 -0
  131. package/src/GraphGenomeView/pathAnchoring.ts +164 -0
  132. package/src/GraphGenomeView/pathColors.test.ts +83 -0
  133. package/src/GraphGenomeView/pathColors.ts +122 -0
  134. package/src/GraphGenomeView/referenceSpan.test.ts +84 -0
  135. package/src/GraphGenomeView/referenceSpan.ts +133 -0
  136. package/src/GraphGenomeView/renderPasses.bench.ts +172 -0
  137. package/src/GraphGenomeView/renderPipeline.test.ts +226 -0
  138. package/src/GraphGenomeView/renderer/Canvas2DRenderer.test.ts +205 -0
  139. package/src/GraphGenomeView/renderer/Canvas2DRenderer.ts +246 -0
  140. package/src/GraphGenomeView/renderer/GeometryBuilder.test.ts +723 -0
  141. package/src/GraphGenomeView/renderer/GeometryBuilder.ts +882 -0
  142. package/src/GraphGenomeView/renderer/GraphRenderer.ts +13 -0
  143. package/src/GraphGenomeView/renderer/colorBits.ts +43 -0
  144. package/src/GraphGenomeView/renderer/recordingCanvas.ts +64 -0
  145. package/src/GraphGenomeView/renderer/types.ts +84 -0
  146. package/src/GraphGenomeView/renderer/walkFade.test.ts +100 -0
  147. package/src/GraphGenomeView/repeats/repeatFeatures.test.ts +114 -0
  148. package/src/GraphGenomeView/repeats/repeatFeatures.ts +202 -0
  149. package/src/GraphGenomeView/repeats/test_data/abca7_trgt_walks.json +575 -0
  150. package/src/GraphGenomeView/repeats/walkCalls.test.ts +127 -0
  151. package/src/GraphGenomeView/repeats/walkCalls.ts +132 -0
  152. package/src/GraphGenomeView/stress.test.ts +214 -0
  153. package/src/GraphGenomeView/subgraphLoad.test.ts +245 -0
  154. package/src/GraphGenomeView/types.ts +141 -0
  155. package/src/GraphGenomeView/util/SpatialIndex.test.ts +73 -0
  156. package/src/GraphGenomeView/util/SpatialIndex.ts +312 -0
  157. package/src/GraphGenomeView/util/edgeCurves.test.ts +93 -0
  158. package/src/GraphGenomeView/util/edgeCurves.ts +94 -0
  159. package/src/GraphGenomeView/util/geometry.test.ts +447 -0
  160. package/src/GraphGenomeView/util/geometry.ts +656 -0
  161. package/src/GraphGenomeView/util/hitDetection.test.ts +307 -0
  162. package/src/GraphGenomeView/util/hitDetection.ts +330 -0
  163. package/src/GraphGenomeView/util/wheelZoom.test.ts +40 -0
  164. package/src/GraphGenomeView/util/wheelZoom.ts +26 -0
  165. package/src/GraphGenomeView/walkHighlight.test.ts +54 -0
  166. package/src/GraphGenomeView/walkHighlight.ts +56 -0
  167. package/src/LaunchGraphGenomeView.ts +46 -0
  168. package/src/MinigraphBubbleAdapter/MinigraphBubbleAdapter.test.ts +139 -0
  169. package/src/MinigraphBubbleAdapter/MinigraphBubbleAdapter.ts +86 -0
  170. package/src/MinigraphBubbleAdapter/bubbleLine.ts +92 -0
  171. package/src/MinigraphBubbleAdapter/configSchema.ts +109 -0
  172. package/src/MinigraphBubbleAdapter/index.ts +22 -0
  173. package/src/MinigraphBubbleAdapter/test_data/hprc_bubbles_mhc.bed.gz +0 -0
  174. package/src/MinigraphBubbleAdapter/test_data/hprc_bubbles_mhc.bed.gz.tbi +0 -0
  175. package/src/RgfaTabixAdapter/RgfaTabixAdapter.test.ts +285 -0
  176. package/src/RgfaTabixAdapter/RgfaTabixAdapter.ts +194 -0
  177. package/src/RgfaTabixAdapter/bubbleTier.test.ts +84 -0
  178. package/src/RgfaTabixAdapter/configSchema.ts +142 -0
  179. package/src/RgfaTabixAdapter/index.ts +24 -0
  180. package/src/RgfaTabixAdapter/rgfaBed.test.ts +158 -0
  181. package/src/RgfaTabixAdapter/rgfaBed.ts +156 -0
  182. package/src/RgfaTabixAdapter/test_data/bubble_tier_chrY.links.bed.gz +0 -0
  183. package/src/RgfaTabixAdapter/test_data/bubble_tier_chrY.links.bed.gz.tbi +0 -0
  184. package/src/RgfaTabixAdapter/test_data/bubble_tier_chrY.segs.bed.gz +0 -0
  185. package/src/RgfaTabixAdapter/test_data/bubble_tier_chrY.segs.bed.gz.tbi +0 -0
  186. package/src/RgfaTabixAdapter/test_data/pggb_ecoli.links.bed.gz +0 -0
  187. package/src/RgfaTabixAdapter/test_data/pggb_ecoli.links.bed.gz.tbi +0 -0
  188. package/src/RgfaTabixAdapter/test_data/pggb_ecoli.segs.bed.gz +0 -0
  189. package/src/RgfaTabixAdapter/test_data/pggb_ecoli.segs.bed.gz.tbi +0 -0
  190. package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.links.bed.gz +0 -0
  191. package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.links.bed.gz.tbi +0 -0
  192. package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.segs.bed.gz +0 -0
  193. package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.segs.bed.gz.tbi +0 -0
  194. package/src/alleleProjection/projectAlleles.test.ts +75 -0
  195. package/src/alleleProjection/projectAlleles.ts +190 -0
  196. package/src/bandage/README.md +121 -0
  197. package/src/bandage/bandage-layout.d.ts +14 -0
  198. package/src/bandage/bandage-layout.js +0 -0
  199. package/src/bandage/native/CMakeLists.txt +45 -0
  200. package/src/bandage/native/include/graph.h +188 -0
  201. package/src/bandage/native/include/graphlayout.h +74 -0
  202. package/src/bandage/native/include/settings.h +86 -0
  203. package/src/bandage/native/include/types.h +56 -0
  204. package/src/bandage/native/src/bindings.cpp +164 -0
  205. package/src/bandage/native/src/graphlayout.cpp +621 -0
  206. package/src/bandage/resizableDecode.test.ts +58 -0
  207. package/src/gfa-core/gfaParser.bench.ts +55 -0
  208. package/src/gfa-core/gfaParser.test.ts +168 -0
  209. package/src/gfa-core/gfaParser.ts +237 -0
  210. package/src/gfa-core/index.ts +10 -0
  211. package/src/hoverSync/GraphNodeHighlight.tsx +62 -0
  212. package/src/hoverSync/graphViewHighlights.test.ts +158 -0
  213. package/src/hoverSync/graphViewHighlights.ts +119 -0
  214. package/src/hoverSync/index.test.tsx +36 -0
  215. package/src/hoverSync/index.tsx +19 -0
  216. package/src/hoverSync/lgvHover.test.ts +202 -0
  217. package/src/hoverSync/lgvHover.ts +136 -0
  218. package/src/index.ts +46 -15
  219. package/src/launchFromGraph/contributors.test.ts +197 -0
  220. package/src/launchFromGraph/contributors.ts +0 -0
  221. package/src/launchFromGraph/graphMenuItems.ts +211 -0
  222. package/src/launchFromGraph/launchFromGraph.test.ts +489 -0
  223. package/src/launchFromGraph/launchFromGraph.ts +265 -0
  224. package/src/launchFromGraph/launchTracks.test.ts +72 -0
  225. package/src/launchFromGraph/launchTracks.ts +50 -0
  226. package/src/launchFromGraph/linearViewTarget.test.ts +82 -0
  227. package/src/launchFromGraph/linearViewTarget.ts +75 -0
  228. package/src/launchFromGraph/syntenyTracks.ts +47 -0
  229. package/src/launchSubgraph/index.ts +152 -0
  230. package/src/launchSubgraph/launchSubgraph.test.ts +264 -0
  231. package/src/launchSubgraph/launchSubgraphView.ts +128 -0
  232. package/src/launchSubgraph/linearViewMenuItems.test.ts +176 -0
  233. package/src/launchSubgraph/linearViewMenuItems.ts +107 -0
  234. package/src/launchSubgraph/multiWayLaunch.test.ts +289 -0
  235. package/src/launchSubgraph/subgraphMenuItems.ts +73 -0
  236. package/src/launchSubgraph/subgraphTracks.test.ts +165 -0
  237. package/src/launchSubgraph/subgraphTracks.ts +151 -0
  238. package/src/launchSubgraph/testEnv.ts +258 -0
  239. package/src/loadBandage.ts +22 -0
  240. package/src/panSNTabix.test.ts +72 -0
  241. package/src/panSNTabix.ts +149 -0
  242. package/src/pansn.ts +106 -0
  243. package/src/pluginName.ts +3 -0
  244. package/src/synteny/ComparativeAdapterBase.ts +77 -0
  245. package/src/synteny/SyntenyFeature.ts +116 -0
  246. package/src/synteny/clipFeatureToRegion.test.ts +329 -0
  247. package/src/synteny/clipFeatureToRegion.ts +167 -0
  248. package/src/version.ts +1 -0
  249. package/dist/GraphGenomeView/components/FeatureDialog.d.ts +0 -5
  250. package/dist/GraphGenomeView/components/FeatureDialog.js +0 -18
  251. package/dist/GraphGenomeView/components/FeatureDialog.js.map +0 -1
  252. package/dist/GraphGenomeView/components/GraphGenomeView.d.ts +0 -6
  253. package/dist/GraphGenomeView/components/GraphGenomeView.js +0 -17
  254. package/dist/GraphGenomeView/components/GraphGenomeView.js.map +0 -1
  255. package/dist/GraphGenomeView/components/GraphPanel.d.ts +0 -6
  256. package/dist/GraphGenomeView/components/GraphPanel.js +0 -17
  257. package/dist/GraphGenomeView/components/GraphPanel.js.map +0 -1
  258. package/dist/GraphGenomeView/components/Header.d.ts +0 -6
  259. package/dist/GraphGenomeView/components/Header.js +0 -59
  260. package/dist/GraphGenomeView/components/Header.js.map +0 -1
  261. package/dist/GraphGenomeView/components/SettingsDialog.d.ts +0 -7
  262. package/dist/GraphGenomeView/components/SettingsDialog.js +0 -24
  263. package/dist/GraphGenomeView/components/SettingsDialog.js.map +0 -1
  264. package/dist/GraphGenomeView/index.d.ts +0 -2
  265. package/dist/GraphGenomeView/index.js +0 -15
  266. package/dist/GraphGenomeView/index.js.map +0 -1
  267. package/dist/GraphGenomeView/model.d.ts +0 -80
  268. package/dist/GraphGenomeView/model.js +0 -96
  269. package/dist/GraphGenomeView/model.js.map +0 -1
  270. package/dist/GraphGenomeView/util.d.ts +0 -1
  271. package/dist/GraphGenomeView/util.js +0 -8
  272. package/dist/GraphGenomeView/util.js.map +0 -1
  273. package/dist/index.d.ts +0 -8
  274. package/dist/index.js +0 -24
  275. package/dist/index.js.map +0 -1
  276. package/dist/jbrowse-plugin-graphgenomeviewer.umd.production.min.js +0 -67
  277. package/dist/jbrowse-plugin-graphgenomeviewer.umd.production.min.js.map +0 -7
  278. package/src/GraphGenomeView/components/FeatureDialog.tsx +0 -51
  279. package/src/GraphGenomeView/components/GraphPanel.tsx +0 -37
  280. package/src/GraphGenomeView/components/Header.tsx +0 -90
  281. package/src/GraphGenomeView/components/SettingsDialog.tsx +0 -71
  282. package/src/GraphGenomeView/util.ts +0 -7
  283. package/src/declare.d.ts +0 -1
@@ -0,0 +1,205 @@
1
+ import { Canvas2DRenderer } from './Canvas2DRenderer'
2
+ import { buildGeometry } from './GeometryBuilder'
3
+ import { recordingCanvas } from './recordingCanvas'
4
+
5
+ import type { TransformUniform } from './types'
6
+
7
+ // isotropic: one scale for both axes, which is every layout but the row ones
8
+ const iso = (scale = 1) => ({ scaleX: scale, scaleY: scale })
9
+
10
+ function makeRenderer() {
11
+ const { canvas, strokes, fills, lineWidths, points } = recordingCanvas()
12
+ return {
13
+ renderer: new Canvas2DRenderer(canvas),
14
+ strokes,
15
+ fills,
16
+ lineWidths,
17
+ points,
18
+ }
19
+ }
20
+
21
+ const TRANSFORM: TransformUniform = {
22
+ scaleX: 1,
23
+ scaleY: 1,
24
+ translateX: 0,
25
+ translateY: 0,
26
+ dpr: 1,
27
+ }
28
+
29
+ // three nodes in a row wired A -> B -> C, so edge 0 and edge 1 are distinct
30
+ // strokes and a highlight on one must not touch the other
31
+ const nodes = [
32
+ { id: 'A+', name: 'A', length: 10, depth: 1 },
33
+ { id: 'B+', name: 'B', length: 10, depth: 1 },
34
+ { id: 'C+', name: 'C', length: 10, depth: 1 },
35
+ ]
36
+
37
+ function batchOf2Edges() {
38
+ return buildGeometry({
39
+ nodePositions: {
40
+ 'A+': [
41
+ { x: 0, y: 0 },
42
+ { x: 10, y: 0 },
43
+ ],
44
+ 'B+': [
45
+ { x: 40, y: 0 },
46
+ { x: 50, y: 0 },
47
+ ],
48
+ 'C+': [
49
+ { x: 80, y: 0 },
50
+ { x: 90, y: 0 },
51
+ ],
52
+ },
53
+ graph: {
54
+ name: 'test',
55
+ nodes,
56
+ edges: [
57
+ { from: 'A+', to: 'B+' },
58
+ { from: 'B+', to: 'C+' },
59
+ ],
60
+ },
61
+ nodeById: new Map(nodes.map(n => [n.id, n])),
62
+ colorScheme: 'uniform',
63
+ contigThickness: 10,
64
+ connectorThickness: 4,
65
+ drawPaths: false,
66
+ axis: iso(),
67
+ })
68
+ }
69
+
70
+ function channels(rgba: string) {
71
+ const m = /rgba\((\d+),(\d+),(\d+)/.exec(rgba)
72
+ return [Number(m?.[1]), Number(m?.[2]), Number(m?.[3])]
73
+ }
74
+
75
+ function expectBrighter(a: string, b: string) {
76
+ const [r, g, bl] = channels(a)
77
+ const [br, bg, bb] = channels(b)
78
+ expect(r).toBeGreaterThan(br!)
79
+ expect(g).toBeGreaterThan(bg!)
80
+ expect(bl).toBeGreaterThan(bb!)
81
+ }
82
+
83
+ // Two edges of one colour and weight, three nodes of one colour: a drawing is
84
+ // mostly runs of one paint, and each run is one path. This is the whole
85
+ // performance story of the renderer, so it is pinned exactly.
86
+ test('strokes every edge of one paint as one path, and every node likewise', () => {
87
+ const { renderer, strokes } = makeRenderer()
88
+ renderer.uploadGeometry(batchOf2Edges())
89
+ renderer.updateTransform(TRANSFORM)
90
+ renderer.render([1, 1, 1, 1])
91
+
92
+ expect(strokes).toHaveLength(2)
93
+ })
94
+
95
+ test('a highlighted edge is stroked brighter, apart from its neighbour', () => {
96
+ const { renderer, strokes } = makeRenderer()
97
+ renderer.uploadGeometry(batchOf2Edges())
98
+ renderer.updateTransform(TRANSFORM)
99
+ renderer.render([1, 1, 1, 1])
100
+ const [edges, nodeStroke] = strokes as [string, string]
101
+
102
+ strokes.length = 0
103
+ renderer.setEdgeHighlight(1, 1.6)
104
+ renderer.render([1, 1, 1, 1])
105
+
106
+ // edge 0 in its own path, then the nodes, then edge 1 on top of everything
107
+ expect(strokes).toEqual([edges, expect.any(String), nodeStroke])
108
+ expect(strokes[1]).not.toBe(edges)
109
+ expectBrighter(strokes[1]!, edges)
110
+ })
111
+
112
+ test('clearing the highlight restores the base stroke', () => {
113
+ const { renderer, strokes } = makeRenderer()
114
+ renderer.uploadGeometry(batchOf2Edges())
115
+ renderer.updateTransform(TRANSFORM)
116
+ renderer.setEdgeHighlight(0, 1.6)
117
+ renderer.render([1, 1, 1, 1])
118
+ expect(strokes).toHaveLength(3)
119
+
120
+ strokes.length = 0
121
+ renderer.setEdgeHighlight(null, 1.6)
122
+ renderer.render([1, 1, 1, 1])
123
+
124
+ expect(strokes).toHaveLength(2)
125
+ })
126
+
127
+ // A rebuild renumbers the strokes, so an edge captured against the old batch
128
+ // could brighten an unrelated one. The model re-applies the current hover after
129
+ // every upload; the renderer's job is not to keep pointing at a stale run.
130
+ test('uploading a new batch drops the previous edge highlight', () => {
131
+ const { renderer, strokes } = makeRenderer()
132
+ renderer.uploadGeometry(batchOf2Edges())
133
+ renderer.updateTransform(TRANSFORM)
134
+ renderer.setEdgeHighlight(1, 1.6)
135
+ renderer.render([1, 1, 1, 1])
136
+ expect(strokes).toHaveLength(3)
137
+
138
+ strokes.length = 0
139
+ renderer.uploadGeometry(batchOf2Edges())
140
+ renderer.render([1, 1, 1, 1])
141
+
142
+ expect(strokes).toHaveLength(2)
143
+ })
144
+
145
+ // Node highlights are keyed by id rather than by position in the batch, so
146
+ // they survive a rebuild on their own and the hovered node lands on top.
147
+ test('a highlighted node is stroked brighter, last, and survives a rebuild', () => {
148
+ const { renderer, strokes } = makeRenderer()
149
+ renderer.uploadGeometry(batchOf2Edges())
150
+ renderer.updateTransform(TRANSFORM)
151
+ renderer.render([1, 1, 1, 1])
152
+ const nodeStroke = strokes[1]!
153
+
154
+ strokes.length = 0
155
+ renderer.setNodeHighlights(new Map([['B+', 1.4]]))
156
+ renderer.render([1, 1, 1, 1])
157
+ expect(strokes).toHaveLength(3)
158
+ expect(strokes[1]).toBe(nodeStroke)
159
+ expectBrighter(strokes[2]!, nodeStroke)
160
+
161
+ strokes.length = 0
162
+ renderer.uploadGeometry(batchOf2Edges())
163
+ renderer.render([1, 1, 1, 1])
164
+ expect(strokes).toHaveLength(3)
165
+ })
166
+
167
+ // A thickness is quoted in CSS pixels and expanded AFTER the transform, so it
168
+ // is the one term the dpr-scaled transform does not reach. Left alone, every
169
+ // tube, connector and arrowhead came out 1/dpr of its weight on a hidpi
170
+ // display, with the positions between them correct.
171
+ //
172
+ // Asserted as "twice the backing-store pixels at twice the ratio", which is the
173
+ // same drawing in css px. The whole point is that a figure does not change
174
+ // weight with the machine it is opened on.
175
+ describe('a thickness is css pixels, whatever the device ratio', () => {
176
+ function drawn(dpr: number) {
177
+ const { renderer, points, lineWidths } = makeRenderer()
178
+ renderer.uploadGeometry(batchOf2Edges())
179
+ renderer.updateTransform({ ...TRANSFORM, scaleX: dpr, scaleY: dpr, dpr })
180
+ renderer.render([1, 1, 1, 1])
181
+ const xs = points.map(p => p.x)
182
+ return {
183
+ spanPx: Math.max(...xs) - Math.min(...xs),
184
+ edgeWidth: lineWidths[0]!,
185
+ nodeWidth: lineWidths[1]!,
186
+ }
187
+ }
188
+
189
+ test('positions go through the transform', () => {
190
+ // three 10-unit nodes over a 90-unit span
191
+ expect(drawn(1).spanPx).toBeCloseTo(90, 5)
192
+ expect(drawn(2).spanPx).toBeCloseTo(180, 5)
193
+ })
194
+
195
+ test('a node stroke scales its width with the ratio', () => {
196
+ expect(drawn(1).nodeWidth).toBeCloseTo(10, 5)
197
+ expect(drawn(2).nodeWidth).toBeCloseTo(20, 5)
198
+ })
199
+
200
+ test('an edge stroke scales its width with the ratio', () => {
201
+ // connectorThickness 4 is a half-width of 2, so a 4 px stroke at ratio 1
202
+ expect(drawn(1).edgeWidth).toBeCloseTo(4, 5)
203
+ expect(drawn(2).edgeWidth).toBeCloseTo(8, 5)
204
+ })
205
+ })
@@ -0,0 +1,246 @@
1
+ import {
2
+ abgrToCssRgba,
3
+ normalizedRgbToCssRgba,
4
+ } from '@jbrowse/core/util/colorBits'
5
+ import { syncCanvasSize } from '@jbrowse/render-core/canvas2dUtils'
6
+ import { Canvas2DRenderingBackendBase } from '@jbrowse/render-core/renderingBackendBase'
7
+
8
+ import { brightenAbgr } from './colorBits'
9
+
10
+ import type { RenderBatch, Renderer, TransformUniform } from './types'
11
+
12
+ // Everything is drawn as a handful of paths, one per distinct (colour, weight),
13
+ // rather than one path per thing. A drawing is mostly runs of one colour, and
14
+ // Canvas2D's cost is per path verb and per fill or stroke, not per pixel: the
15
+ // same 15k-node graph took 414 ms a frame as one fill per mesh triangle and 11
16
+ // ms as batched strokes (agent-docs/GRAPH_SCALE_AND_LOD.md). Highlighted things
17
+ // are drawn last, in their own paths, so they sit on top of what they brighten.
18
+ //
19
+ // Extends the shared Canvas2D base rather than standing alone, which is how
20
+ // `setErrorHandler` arrives — `useRenderingBackend` requires it. The base's
21
+ // no-op implementation is the correct one here: Canvas2D allocates no GPU
22
+ // resources, so there is no OOM channel to forward.
23
+ export class Canvas2DRenderer
24
+ extends Canvas2DRenderingBackendBase
25
+ implements Renderer
26
+ {
27
+ private transform: TransformUniform | null = null
28
+ private batch: RenderBatch | null = null
29
+ private nodeHighlights: ReadonlyMap<string, number> = new Map()
30
+ private highlightedEdge: number | null = null
31
+ private highlightFactor = 1
32
+
33
+ // render-core's, not a local `width * devicePixelRatio`: it clamps the
34
+ // backing store at MAX_CANVAS_DIM_PX, reads the ratio through `getDpr()` so
35
+ // this agrees with the transform the model builds, and writes the css size
36
+ // independently of the backing size.
37
+ resize(width: number, height: number) {
38
+ syncCanvasSize(this.ctx.canvas, width, height)
39
+ }
40
+
41
+ uploadGeometry(batch: RenderBatch) {
42
+ this.batch = batch
43
+ // A rebuild renumbers the strokes, so the old edge no longer addresses the
44
+ // same run; the model re-applies the current hover against the new batch.
45
+ this.highlightedEdge = null
46
+ }
47
+
48
+ setNodeHighlights(factors: ReadonlyMap<string, number>) {
49
+ this.nodeHighlights = factors
50
+ }
51
+
52
+ setEdgeHighlight(edgeIndex: number | null, factor: number) {
53
+ this.highlightedEdge = edgeIndex
54
+ this.highlightFactor = factor
55
+ }
56
+
57
+ updateTransform(transform: TransformUniform) {
58
+ this.transform = transform
59
+ }
60
+
61
+ render(clearColor: [number, number, number, number]) {
62
+ const t = this.transform
63
+ if (!t) {
64
+ return
65
+ }
66
+ const ctx = this.ctx
67
+ const { width, height } = ctx.canvas
68
+
69
+ ctx.fillStyle = normalizedRgbToCssRgba(
70
+ [clearColor[0], clearColor[1], clearColor[2]],
71
+ clearColor[3],
72
+ )
73
+ ctx.fillRect(0, 0, width, height)
74
+
75
+ const batch = this.batch
76
+ if (!batch) {
77
+ return
78
+ }
79
+ ctx.lineCap = 'round'
80
+ ctx.lineJoin = 'round'
81
+ this.renderEdges(batch)
82
+ this.renderNodes(batch)
83
+ this.renderArrows(batch)
84
+ }
85
+
86
+ private renderEdges(batch: RenderBatch) {
87
+ const t = this.transform!
88
+ const ctx = this.ctx
89
+ const hl =
90
+ this.highlightedEdge === null
91
+ ? undefined
92
+ : batch.edgeCurveRuns.get(this.highlightedEdge)
93
+ for (const { color, items } of groupByPaint(batch.edgeCurves, (e, i) => ({
94
+ color: inRun(hl, i)
95
+ ? brightenAbgr(e.color, this.highlightFactor)
96
+ : e.color,
97
+ weight: e.thickness,
98
+ last: inRun(hl, i),
99
+ }))) {
100
+ ctx.strokeStyle = abgrToCssRgba(color)
101
+ // thickness is the half-width in css px, so the stroke is twice it at
102
+ // the device ratio the transform already carries
103
+ ctx.lineWidth = items[0]!.thickness * 2 * t.dpr
104
+ ctx.beginPath()
105
+ for (const e of items) {
106
+ const first = e.curves[0]!
107
+ ctx.moveTo(
108
+ first.x0 * t.scaleX + t.translateX,
109
+ first.y0 * t.scaleY + t.translateY,
110
+ )
111
+ for (const c of e.curves) {
112
+ ctx.bezierCurveTo(
113
+ c.cx0 * t.scaleX + t.translateX,
114
+ c.cy0 * t.scaleY + t.translateY,
115
+ c.cx1 * t.scaleX + t.translateX,
116
+ c.cy1 * t.scaleY + t.translateY,
117
+ c.x1 * t.scaleX + t.translateX,
118
+ c.y1 * t.scaleY + t.translateY,
119
+ )
120
+ }
121
+ }
122
+ ctx.stroke()
123
+ }
124
+ }
125
+
126
+ private renderNodes(batch: RenderBatch) {
127
+ const t = this.transform!
128
+ const ctx = this.ctx
129
+ // stroke index -> brighten factor, for the nodes the model lifted
130
+ const lifted = new Map<number, number>()
131
+ for (const [nodeId, factor] of this.nodeHighlights) {
132
+ const run = batch.nodeStrokeRuns.get(nodeId)
133
+ if (run) {
134
+ for (let i = run.start; i < run.start + run.count; i++) {
135
+ lifted.set(i, factor)
136
+ }
137
+ }
138
+ }
139
+ for (const { color, items } of groupByPaint(batch.nodeStrokes, (s, i) => {
140
+ const factor = lifted.get(i)
141
+ return {
142
+ color: factor === undefined ? s.color : brightenAbgr(s.color, factor),
143
+ weight: s.thickness,
144
+ last: factor !== undefined,
145
+ }
146
+ })) {
147
+ ctx.strokeStyle = abgrToCssRgba(color)
148
+ ctx.lineWidth = items[0]!.thickness * 2 * t.dpr
149
+ ctx.beginPath()
150
+ for (const s of items) {
151
+ const p0 = s.points[0]!
152
+ ctx.moveTo(
153
+ p0.x * t.scaleX + t.translateX,
154
+ p0.y * t.scaleY + t.translateY,
155
+ )
156
+ for (let i = 1, l = s.points.length; i < l; i++) {
157
+ const p = s.points[i]!
158
+ ctx.lineTo(
159
+ p.x * t.scaleX + t.translateX,
160
+ p.y * t.scaleY + t.translateY,
161
+ )
162
+ }
163
+ }
164
+ ctx.stroke()
165
+ }
166
+ }
167
+
168
+ private renderArrows(batch: RenderBatch) {
169
+ const t = this.transform!
170
+ const ctx = this.ctx
171
+ const hl =
172
+ this.highlightedEdge === null
173
+ ? undefined
174
+ : batch.arrowRuns.get(this.highlightedEdge)
175
+ for (const { color, items } of groupByPaint(batch.arrows, (a, i) => ({
176
+ color: inRun(hl, i)
177
+ ? brightenAbgr(a.color, this.highlightFactor)
178
+ : a.color,
179
+ weight: 0,
180
+ last: inRun(hl, i),
181
+ }))) {
182
+ ctx.fillStyle = abgrToCssRgba(color)
183
+ ctx.beginPath()
184
+ for (const a of items) {
185
+ // The tip sits on the edge's end; the two barbs are `size` css px back
186
+ // along either side of the tangent, expanded after the transform so
187
+ // the head is the same size at every zoom.
188
+ const tipX = a.x * t.scaleX + t.translateX
189
+ const tipY = a.y * t.scaleY + t.translateY
190
+ const reach = a.size * t.dpr
191
+ ctx.moveTo(tipX, tipY)
192
+ ctx.lineTo(
193
+ tipX - Math.cos(a.angle - 0.5) * reach,
194
+ tipY - Math.sin(a.angle - 0.5) * reach,
195
+ )
196
+ ctx.lineTo(
197
+ tipX - Math.cos(a.angle + 0.5) * reach,
198
+ tipY - Math.sin(a.angle + 0.5) * reach,
199
+ )
200
+ ctx.closePath()
201
+ }
202
+ ctx.fill()
203
+ }
204
+ }
205
+
206
+ override dispose() {
207
+ super.dispose()
208
+ this.batch = null
209
+ this.nodeHighlights = new Map()
210
+ this.highlightedEdge = null
211
+ }
212
+ }
213
+
214
+ function inRun(run: { start: number; count: number } | undefined, i: number) {
215
+ return run !== undefined && i >= run.start && i < run.start + run.count
216
+ }
217
+
218
+ interface Paint {
219
+ color: number
220
+ weight: number
221
+ // drawn after everything else, so a highlight lands on top of its neighbours
222
+ last: boolean
223
+ }
224
+
225
+ // Items bucketed by what they are painted with, in first-seen order, with the
226
+ // `last` ones after the rest.
227
+ function groupByPaint<T>(
228
+ items: T[],
229
+ paintOf: (item: T, index: number) => Paint,
230
+ ) {
231
+ const groups = new Map<string, { color: number; items: T[] }>()
232
+ const deferred = new Map<string, { color: number; items: T[] }>()
233
+ for (let i = 0, l = items.length; i < l; i++) {
234
+ const item = items[i]!
235
+ const { color, weight, last } = paintOf(item, i)
236
+ const into = last ? deferred : groups
237
+ const key = `${color}:${weight}`
238
+ const group = into.get(key)
239
+ if (group) {
240
+ group.items.push(item)
241
+ } else {
242
+ into.set(key, { color, items: [item] })
243
+ }
244
+ }
245
+ return [...groups.values(), ...deferred.values()]
246
+ }