@jbrowse/plugin-alignments 1.7.9 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) hide show
  1. package/dist/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js +192 -207
  2. package/dist/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js.map +1 -0
  3. package/dist/AlignmentsFeatureDetail/index.d.ts +28 -3
  4. package/dist/AlignmentsFeatureDetail/index.js +48 -55
  5. package/dist/AlignmentsFeatureDetail/index.js.map +1 -0
  6. package/dist/AlignmentsTrack/index.js +24 -32
  7. package/dist/AlignmentsTrack/index.js.map +1 -0
  8. package/dist/BamAdapter/BamAdapter.js +345 -585
  9. package/dist/BamAdapter/BamAdapter.js.map +1 -0
  10. package/dist/BamAdapter/BamSlightlyLazyFeature.js +143 -174
  11. package/dist/BamAdapter/BamSlightlyLazyFeature.js.map +1 -0
  12. package/dist/BamAdapter/MismatchParser.js +340 -416
  13. package/dist/BamAdapter/MismatchParser.js.map +1 -0
  14. package/dist/BamAdapter/configSchema.js +33 -46
  15. package/dist/BamAdapter/configSchema.js.map +1 -0
  16. package/dist/BamAdapter/index.js +36 -32
  17. package/dist/BamAdapter/index.js.map +1 -0
  18. package/dist/CramAdapter/CramAdapter.js +376 -644
  19. package/dist/CramAdapter/CramAdapter.js.map +1 -0
  20. package/dist/CramAdapter/CramSlightlyLazyFeature.js +374 -439
  21. package/dist/CramAdapter/CramSlightlyLazyFeature.js.map +1 -0
  22. package/dist/CramAdapter/CramTestAdapters.js +169 -227
  23. package/dist/CramAdapter/CramTestAdapters.js.map +1 -0
  24. package/dist/CramAdapter/configSchema.js +28 -38
  25. package/dist/CramAdapter/configSchema.js.map +1 -0
  26. package/dist/CramAdapter/index.js +37 -32
  27. package/dist/CramAdapter/index.js.map +1 -0
  28. package/dist/HtsgetBamAdapter/HtsgetBamAdapter.js +91 -93
  29. package/dist/HtsgetBamAdapter/HtsgetBamAdapter.js.map +1 -0
  30. package/dist/HtsgetBamAdapter/configSchema.js +19 -29
  31. package/dist/HtsgetBamAdapter/configSchema.js.map +1 -0
  32. package/dist/HtsgetBamAdapter/index.js +44 -38
  33. package/dist/HtsgetBamAdapter/index.js.map +1 -0
  34. package/dist/LinearAlignmentsDisplay/components/AlignmentsDisplay.js +36 -65
  35. package/dist/LinearAlignmentsDisplay/components/AlignmentsDisplay.js.map +1 -0
  36. package/dist/LinearAlignmentsDisplay/index.js +22 -28
  37. package/dist/LinearAlignmentsDisplay/index.js.map +1 -0
  38. package/dist/LinearAlignmentsDisplay/models/configSchema.js +12 -23
  39. package/dist/LinearAlignmentsDisplay/models/configSchema.js.map +1 -0
  40. package/dist/LinearAlignmentsDisplay/models/model.d.ts +10 -10
  41. package/dist/LinearAlignmentsDisplay/models/model.js +257 -245
  42. package/dist/LinearAlignmentsDisplay/models/model.js.map +1 -0
  43. package/dist/LinearPileupDisplay/components/ColorByModifications.js +98 -116
  44. package/dist/LinearPileupDisplay/components/ColorByModifications.js.map +1 -0
  45. package/dist/LinearPileupDisplay/components/ColorByTag.js +82 -91
  46. package/dist/LinearPileupDisplay/components/ColorByTag.js.map +1 -0
  47. package/dist/LinearPileupDisplay/components/FilterByTag.js +156 -192
  48. package/dist/LinearPileupDisplay/components/FilterByTag.js.map +1 -0
  49. package/dist/LinearPileupDisplay/components/LinearPileupDisplayBlurb.js +15 -29
  50. package/dist/LinearPileupDisplay/components/LinearPileupDisplayBlurb.js.map +1 -0
  51. package/dist/LinearPileupDisplay/components/SetFeatureHeight.js +79 -93
  52. package/dist/LinearPileupDisplay/components/SetFeatureHeight.js.map +1 -0
  53. package/dist/LinearPileupDisplay/components/SetMaxHeight.js +78 -81
  54. package/dist/LinearPileupDisplay/components/SetMaxHeight.js.map +1 -0
  55. package/dist/LinearPileupDisplay/components/SortByTag.js +80 -88
  56. package/dist/LinearPileupDisplay/components/SortByTag.js.map +1 -0
  57. package/dist/LinearPileupDisplay/configSchema.js +40 -42
  58. package/dist/LinearPileupDisplay/configSchema.js.map +1 -0
  59. package/dist/LinearPileupDisplay/index.js +21 -27
  60. package/dist/LinearPileupDisplay/index.js.map +1 -0
  61. package/dist/LinearPileupDisplay/model.d.ts +33 -20
  62. package/dist/LinearPileupDisplay/model.js +702 -716
  63. package/dist/LinearPileupDisplay/model.js.map +1 -0
  64. package/dist/LinearSNPCoverageDisplay/components/Tooltip.d.ts +1 -1
  65. package/dist/LinearSNPCoverageDisplay/components/Tooltip.js +105 -57
  66. package/dist/LinearSNPCoverageDisplay/components/Tooltip.js.map +1 -0
  67. package/dist/LinearSNPCoverageDisplay/index.js +21 -27
  68. package/dist/LinearSNPCoverageDisplay/index.js.map +1 -0
  69. package/dist/LinearSNPCoverageDisplay/models/configSchema.js +45 -55
  70. package/dist/LinearSNPCoverageDisplay/models/configSchema.js.map +1 -0
  71. package/dist/LinearSNPCoverageDisplay/models/model.d.ts +14 -12
  72. package/dist/LinearSNPCoverageDisplay/models/model.js +257 -230
  73. package/dist/LinearSNPCoverageDisplay/models/model.js.map +1 -0
  74. package/dist/NestedFrequencyTable.js +104 -139
  75. package/dist/NestedFrequencyTable.js.map +1 -0
  76. package/dist/PileupRPC/rpcMethods.js +199 -278
  77. package/dist/PileupRPC/rpcMethods.js.map +1 -0
  78. package/dist/PileupRenderer/PileupLayoutSession.js +56 -76
  79. package/dist/PileupRenderer/PileupLayoutSession.js.map +1 -0
  80. package/dist/PileupRenderer/PileupRenderer.d.ts +56 -11
  81. package/dist/PileupRenderer/PileupRenderer.js +942 -1134
  82. package/dist/PileupRenderer/PileupRenderer.js.map +1 -0
  83. package/dist/PileupRenderer/components/PileupRendering.d.ts +1 -1
  84. package/dist/PileupRenderer/components/PileupRendering.js +173 -253
  85. package/dist/PileupRenderer/components/PileupRendering.js.map +1 -0
  86. package/dist/PileupRenderer/configSchema.js +65 -71
  87. package/dist/PileupRenderer/configSchema.js.map +1 -0
  88. package/dist/PileupRenderer/index.js +17 -22
  89. package/dist/PileupRenderer/index.js.map +1 -0
  90. package/dist/PileupRenderer/sortUtil.js +83 -107
  91. package/dist/PileupRenderer/sortUtil.js.map +1 -0
  92. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +2 -0
  93. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js +436 -586
  94. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js.map +1 -0
  95. package/dist/SNPCoverageAdapter/configSchema.js +10 -20
  96. package/dist/SNPCoverageAdapter/configSchema.js.map +1 -0
  97. package/dist/SNPCoverageAdapter/index.js +46 -41
  98. package/dist/SNPCoverageAdapter/index.js.map +1 -0
  99. package/dist/SNPCoverageRenderer/SNPCoverageRenderer.d.ts +1 -1
  100. package/dist/SNPCoverageRenderer/SNPCoverageRenderer.js +265 -290
  101. package/dist/SNPCoverageRenderer/SNPCoverageRenderer.js.map +1 -0
  102. package/dist/SNPCoverageRenderer/configSchema.js +30 -39
  103. package/dist/SNPCoverageRenderer/configSchema.js.map +1 -0
  104. package/dist/SNPCoverageRenderer/index.js +19 -30
  105. package/dist/SNPCoverageRenderer/index.js.map +1 -0
  106. package/dist/index.js +135 -152
  107. package/dist/index.js.map +1 -0
  108. package/dist/shared.js +84 -92
  109. package/dist/shared.js.map +1 -0
  110. package/dist/util.js +130 -121
  111. package/dist/util.js.map +1 -0
  112. package/esm/AlignmentsFeatureDetail/AlignmentsFeatureDetail.d.ts +6 -0
  113. package/esm/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js +145 -0
  114. package/esm/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js.map +1 -0
  115. package/esm/AlignmentsFeatureDetail/index.d.ts +38 -0
  116. package/esm/AlignmentsFeatureDetail/index.js +23 -0
  117. package/esm/AlignmentsFeatureDetail/index.js.map +1 -0
  118. package/esm/AlignmentsTrack/index.d.ts +2 -0
  119. package/esm/AlignmentsTrack/index.js +23 -0
  120. package/esm/AlignmentsTrack/index.js.map +1 -0
  121. package/esm/BamAdapter/BamAdapter.d.ts +40 -0
  122. package/esm/BamAdapter/BamAdapter.js +173 -0
  123. package/esm/BamAdapter/BamAdapter.js.map +1 -0
  124. package/esm/BamAdapter/BamSlightlyLazyFeature.d.ts +33 -0
  125. package/esm/BamAdapter/BamSlightlyLazyFeature.js +107 -0
  126. package/esm/BamAdapter/BamSlightlyLazyFeature.js.map +1 -0
  127. package/esm/BamAdapter/MismatchParser.d.ts +25 -0
  128. package/esm/BamAdapter/MismatchParser.js +294 -0
  129. package/esm/BamAdapter/MismatchParser.js.map +1 -0
  130. package/esm/BamAdapter/configSchema.d.ts +2 -0
  131. package/esm/BamAdapter/configSchema.js +31 -0
  132. package/esm/BamAdapter/configSchema.js.map +1 -0
  133. package/esm/BamAdapter/index.d.ts +3 -0
  134. package/esm/BamAdapter/index.js +10 -0
  135. package/esm/BamAdapter/index.js.map +1 -0
  136. package/esm/CramAdapter/CramAdapter.d.ts +53 -0
  137. package/esm/CramAdapter/CramAdapter.js +228 -0
  138. package/esm/CramAdapter/CramAdapter.js.map +1 -0
  139. package/esm/CramAdapter/CramSlightlyLazyFeature.d.ts +49 -0
  140. package/esm/CramAdapter/CramSlightlyLazyFeature.js +349 -0
  141. package/esm/CramAdapter/CramSlightlyLazyFeature.js.map +1 -0
  142. package/esm/CramAdapter/CramTestAdapters.d.ts +29 -0
  143. package/esm/CramAdapter/CramTestAdapters.js +70 -0
  144. package/esm/CramAdapter/CramTestAdapters.js.map +1 -0
  145. package/esm/CramAdapter/configSchema.d.ts +3 -0
  146. package/esm/CramAdapter/configSchema.js +26 -0
  147. package/esm/CramAdapter/configSchema.js.map +1 -0
  148. package/esm/CramAdapter/index.d.ts +3 -0
  149. package/esm/CramAdapter/index.js +11 -0
  150. package/esm/CramAdapter/index.js.map +1 -0
  151. package/esm/HtsgetBamAdapter/HtsgetBamAdapter.d.ts +9 -0
  152. package/esm/HtsgetBamAdapter/HtsgetBamAdapter.js +27 -0
  153. package/esm/HtsgetBamAdapter/HtsgetBamAdapter.js.map +1 -0
  154. package/esm/HtsgetBamAdapter/configSchema.d.ts +2 -0
  155. package/esm/HtsgetBamAdapter/configSchema.js +17 -0
  156. package/esm/HtsgetBamAdapter/configSchema.js.map +1 -0
  157. package/esm/HtsgetBamAdapter/index.d.ts +3 -0
  158. package/esm/HtsgetBamAdapter/index.js +16 -0
  159. package/esm/HtsgetBamAdapter/index.js.map +1 -0
  160. package/esm/LinearAlignmentsDisplay/components/AlignmentsDisplay.d.ts +7 -0
  161. package/esm/LinearAlignmentsDisplay/components/AlignmentsDisplay.js +34 -0
  162. package/esm/LinearAlignmentsDisplay/components/AlignmentsDisplay.js.map +1 -0
  163. package/esm/LinearAlignmentsDisplay/index.d.ts +2 -0
  164. package/esm/LinearAlignmentsDisplay/index.js +19 -0
  165. package/esm/LinearAlignmentsDisplay/index.js.map +1 -0
  166. package/esm/LinearAlignmentsDisplay/models/configSchema.d.ts +4 -0
  167. package/esm/LinearAlignmentsDisplay/models/configSchema.js +12 -0
  168. package/esm/LinearAlignmentsDisplay/models/configSchema.js.map +1 -0
  169. package/esm/LinearAlignmentsDisplay/models/model.d.ts +105 -0
  170. package/esm/LinearAlignmentsDisplay/models/model.js +181 -0
  171. package/esm/LinearAlignmentsDisplay/models/model.js.map +1 -0
  172. package/esm/LinearPileupDisplay/components/ColorByModifications.d.ts +14 -0
  173. package/esm/LinearPileupDisplay/components/ColorByModifications.js +71 -0
  174. package/esm/LinearPileupDisplay/components/ColorByModifications.js.map +1 -0
  175. package/esm/LinearPileupDisplay/components/ColorByTag.d.ts +9 -0
  176. package/esm/LinearPileupDisplay/components/ColorByTag.js +45 -0
  177. package/esm/LinearPileupDisplay/components/ColorByTag.js.map +1 -0
  178. package/esm/LinearPileupDisplay/components/FilterByTag.d.ts +18 -0
  179. package/esm/LinearPileupDisplay/components/FilterByTag.js +123 -0
  180. package/esm/LinearPileupDisplay/components/FilterByTag.js.map +1 -0
  181. package/esm/LinearPileupDisplay/components/LinearPileupDisplayBlurb.d.ts +13 -0
  182. package/esm/LinearPileupDisplay/components/LinearPileupDisplayBlurb.js +13 -0
  183. package/esm/LinearPileupDisplay/components/LinearPileupDisplayBlurb.js.map +1 -0
  184. package/esm/LinearPileupDisplay/components/SetFeatureHeight.d.ts +16 -0
  185. package/esm/LinearPileupDisplay/components/SetFeatureHeight.js +41 -0
  186. package/esm/LinearPileupDisplay/components/SetFeatureHeight.js.map +1 -0
  187. package/esm/LinearPileupDisplay/components/SetMaxHeight.d.ts +10 -0
  188. package/esm/LinearPileupDisplay/components/SetMaxHeight.js +43 -0
  189. package/esm/LinearPileupDisplay/components/SetMaxHeight.js.map +1 -0
  190. package/esm/LinearPileupDisplay/components/SortByTag.d.ts +9 -0
  191. package/esm/LinearPileupDisplay/components/SortByTag.js +43 -0
  192. package/esm/LinearPileupDisplay/components/SortByTag.js.map +1 -0
  193. package/esm/LinearPileupDisplay/configSchema.d.ts +6 -0
  194. package/esm/LinearPileupDisplay/configSchema.js +41 -0
  195. package/esm/LinearPileupDisplay/configSchema.js.map +1 -0
  196. package/esm/LinearPileupDisplay/index.d.ts +2 -0
  197. package/esm/LinearPileupDisplay/index.js +18 -0
  198. package/esm/LinearPileupDisplay/index.js.map +1 -0
  199. package/esm/LinearPileupDisplay/model.d.ts +332 -0
  200. package/esm/LinearPileupDisplay/model.js +576 -0
  201. package/esm/LinearPileupDisplay/model.js.map +1 -0
  202. package/esm/LinearSNPCoverageDisplay/components/Tooltip.d.ts +10 -0
  203. package/esm/LinearSNPCoverageDisplay/components/Tooltip.js +57 -0
  204. package/esm/LinearSNPCoverageDisplay/components/Tooltip.js.map +1 -0
  205. package/esm/LinearSNPCoverageDisplay/index.d.ts +2 -0
  206. package/esm/LinearSNPCoverageDisplay/index.js +18 -0
  207. package/esm/LinearSNPCoverageDisplay/index.js.map +1 -0
  208. package/esm/LinearSNPCoverageDisplay/models/configSchema.d.ts +2 -0
  209. package/esm/LinearSNPCoverageDisplay/models/configSchema.js +44 -0
  210. package/esm/LinearSNPCoverageDisplay/models/configSchema.js.map +1 -0
  211. package/esm/LinearSNPCoverageDisplay/models/model.d.ts +348 -0
  212. package/esm/LinearSNPCoverageDisplay/models/model.js +185 -0
  213. package/esm/LinearSNPCoverageDisplay/models/model.js.map +1 -0
  214. package/esm/NestedFrequencyTable.d.ts +14 -0
  215. package/esm/NestedFrequencyTable.js +101 -0
  216. package/esm/NestedFrequencyTable.js.map +1 -0
  217. package/esm/PileupRPC/rpcMethods.d.ts +34 -0
  218. package/esm/PileupRPC/rpcMethods.js +70 -0
  219. package/esm/PileupRPC/rpcMethods.js.map +1 -0
  220. package/esm/PileupRenderer/PileupLayoutSession.d.ts +32 -0
  221. package/esm/PileupRenderer/PileupLayoutSession.js +32 -0
  222. package/esm/PileupRenderer/PileupLayoutSession.js.map +1 -0
  223. package/esm/PileupRenderer/PileupRenderer.d.ts +182 -0
  224. package/esm/PileupRenderer/PileupRenderer.js +830 -0
  225. package/esm/PileupRenderer/PileupRenderer.js.map +1 -0
  226. package/esm/PileupRenderer/components/PileupRendering.d.ts +23 -0
  227. package/esm/PileupRenderer/components/PileupRendering.js +138 -0
  228. package/esm/PileupRenderer/components/PileupRendering.js.map +1 -0
  229. package/esm/PileupRenderer/configSchema.d.ts +2 -0
  230. package/esm/PileupRenderer/configSchema.js +64 -0
  231. package/esm/PileupRenderer/configSchema.js.map +1 -0
  232. package/esm/PileupRenderer/index.d.ts +2 -0
  233. package/esm/PileupRenderer/index.js +12 -0
  234. package/esm/PileupRenderer/index.js.map +1 -0
  235. package/esm/PileupRenderer/sortUtil.d.ts +8 -0
  236. package/esm/PileupRenderer/sortUtil.js +80 -0
  237. package/esm/PileupRenderer/sortUtil.js.map +1 -0
  238. package/esm/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +67 -0
  239. package/esm/SNPCoverageAdapter/SNPCoverageAdapter.js +259 -0
  240. package/esm/SNPCoverageAdapter/SNPCoverageAdapter.js.map +1 -0
  241. package/esm/SNPCoverageAdapter/configSchema.d.ts +3 -0
  242. package/esm/SNPCoverageAdapter/configSchema.js +6 -0
  243. package/esm/SNPCoverageAdapter/configSchema.js.map +1 -0
  244. package/esm/SNPCoverageAdapter/index.d.ts +3 -0
  245. package/esm/SNPCoverageAdapter/index.js +18 -0
  246. package/esm/SNPCoverageAdapter/index.js.map +1 -0
  247. package/esm/SNPCoverageRenderer/SNPCoverageRenderer.d.ts +20 -0
  248. package/esm/SNPCoverageRenderer/SNPCoverageRenderer.js +185 -0
  249. package/esm/SNPCoverageRenderer/SNPCoverageRenderer.js.map +1 -0
  250. package/esm/SNPCoverageRenderer/configSchema.d.ts +2 -0
  251. package/esm/SNPCoverageRenderer/configSchema.js +29 -0
  252. package/esm/SNPCoverageRenderer/configSchema.js.map +1 -0
  253. package/esm/SNPCoverageRenderer/index.d.ts +3 -0
  254. package/esm/SNPCoverageRenderer/index.js +14 -0
  255. package/esm/SNPCoverageRenderer/index.js.map +1 -0
  256. package/esm/index.d.ts +10 -0
  257. package/esm/index.js +91 -0
  258. package/esm/index.js.map +1 -0
  259. package/esm/shared.d.ts +25 -0
  260. package/esm/shared.js +28 -0
  261. package/esm/shared.js.map +1 -0
  262. package/esm/util.d.ts +19 -0
  263. package/esm/util.js +83 -0
  264. package/esm/util.js.map +1 -0
  265. package/package.json +19 -11
  266. package/src/AlignmentsFeatureDetail/AlignmentsFeatureDetail.tsx +16 -6
  267. package/src/AlignmentsFeatureDetail/__snapshots__/index.test.js.snap +321 -397
  268. package/src/AlignmentsFeatureDetail/index.ts +7 -17
  269. package/src/BamAdapter/MismatchParser.ts +1 -0
  270. package/src/LinearAlignmentsDisplay/components/AlignmentsDisplay.tsx +3 -3
  271. package/src/LinearPileupDisplay/components/ColorByModifications.tsx +7 -7
  272. package/src/LinearPileupDisplay/components/ColorByTag.tsx +5 -5
  273. package/src/LinearPileupDisplay/components/FilterByTag.tsx +5 -5
  274. package/src/LinearPileupDisplay/components/LinearPileupDisplayBlurb.tsx +1 -1
  275. package/src/LinearPileupDisplay/components/SetFeatureHeight.tsx +9 -9
  276. package/src/LinearPileupDisplay/components/SetMaxHeight.tsx +5 -5
  277. package/src/LinearPileupDisplay/components/SortByTag.tsx +5 -5
  278. package/src/LinearPileupDisplay/model.ts +90 -32
  279. package/src/LinearSNPCoverageDisplay/components/Tooltip.tsx +44 -30
  280. package/src/LinearSNPCoverageDisplay/models/model.ts +25 -25
  281. package/src/PileupRenderer/PileupRenderer.tsx +399 -198
  282. package/src/PileupRenderer/components/PileupRendering.tsx +11 -11
  283. package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +5 -0
  284. package/src/SNPCoverageRenderer/SNPCoverageRenderer.ts +7 -5
  285. package/dist/AlignmentsFeatureDetail/index.test.js +0 -60
  286. package/dist/BamAdapter/BamAdapter.test.js +0 -177
  287. package/dist/BamAdapter/MismatchParser.test.js +0 -251
  288. package/dist/CramAdapter/CramAdapter.test.js +0 -138
  289. package/dist/LinearAlignmentsDisplay/models/configSchema.test.js +0 -83
  290. package/dist/LinearPileupDisplay/configSchema.test.js +0 -92
  291. package/dist/LinearSNPCoverageDisplay/models/configSchema.test.js +0 -62
  292. package/dist/PileupRenderer/components/PileupRendering.test.js +0 -36
  293. package/dist/declare.d.js +0 -1
  294. package/dist/index.test.js +0 -26
@@ -0,0 +1,830 @@
1
+ import BoxRendererType from '@jbrowse/core/pluggableElementTypes/renderers/BoxRendererType';
2
+ import { createJBrowseTheme } from '@jbrowse/core/ui';
3
+ import { bpSpanPx, iterMap, measureText, } from '@jbrowse/core/util';
4
+ import { renderToAbstractCanvas } from '@jbrowse/core/util/offscreenCanvasUtils';
5
+ import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache';
6
+ import { readConfObject, } from '@jbrowse/core/configuration';
7
+ // locals
8
+ import { parseCigar, getModificationPositions, getNextRefPos, } from '../BamAdapter/MismatchParser';
9
+ import { sortFeature } from './sortUtil';
10
+ import { getTagAlt, orientationTypes, fetchSequence, shouldFetchReferenceSequence, } from '../util';
11
+ import { PileupLayoutSession, } from './PileupLayoutSession';
12
+ function fillRect(ctx, l, t, w, h, cw, color) {
13
+ if (l + w < 0 || l > cw) {
14
+ return;
15
+ }
16
+ else {
17
+ if (color) {
18
+ ctx.fillStyle = color;
19
+ }
20
+ ctx.fillRect(l, t, w, h);
21
+ }
22
+ }
23
+ function getColorBaseMap(theme) {
24
+ // @ts-ignore
25
+ const { bases } = theme.palette;
26
+ return {
27
+ A: bases.A.main,
28
+ C: bases.C.main,
29
+ G: bases.G.main,
30
+ T: bases.T.main,
31
+ deletion: '#808080', // gray
32
+ };
33
+ }
34
+ function getContrastBaseMap(theme) {
35
+ return Object.fromEntries(Object.entries(getColorBaseMap(theme)).map(([key, value]) => [
36
+ key,
37
+ theme.palette.getContrastText(value),
38
+ ]));
39
+ }
40
+ const alignmentColoring = {
41
+ color_fwd_strand_not_proper: '#ECC8C8',
42
+ color_rev_strand_not_proper: '#BEBED8',
43
+ color_fwd_strand: '#EC8B8B',
44
+ color_rev_strand: '#8F8FD8',
45
+ color_fwd_missing_mate: '#D11919',
46
+ color_rev_missing_mate: '#1919D1',
47
+ color_fwd_diff_chr: '#000',
48
+ color_rev_diff_chr: '#969696',
49
+ color_pair_lr: '#c8c8c8',
50
+ color_pair_rr: 'navy',
51
+ color_pair_rl: 'teal',
52
+ color_pair_ll: 'green',
53
+ color_nostrand: '#c8c8c8',
54
+ color_interchrom: 'orange',
55
+ color_longinsert: 'red',
56
+ color_shortinsert: 'pink',
57
+ };
58
+ function shouldDrawSNPsMuted(type) {
59
+ return ['methylation', 'modifications'].includes(type || '');
60
+ }
61
+ function shouldDrawIndels(type) {
62
+ return true;
63
+ }
64
+ export default class PileupRenderer extends BoxRendererType {
65
+ constructor() {
66
+ super(...arguments);
67
+ this.supportsSVG = true;
68
+ }
69
+ // get width and height of chars the height is an approximation: width
70
+ // letter M is approximately the height
71
+ getCharWidthHeight(ctx) {
72
+ const charWidth = measureText('A');
73
+ const charHeight = measureText('M') - 2;
74
+ return { charWidth, charHeight };
75
+ }
76
+ layoutFeature({ feature, layout, bpPerPx, region, showSoftClip, heightPx, displayMode, }) {
77
+ let expansionBefore = 0;
78
+ let expansionAfter = 0;
79
+ // Expand the start and end of feature when softclipping enabled
80
+ if (showSoftClip) {
81
+ const mismatches = feature.get('mismatches');
82
+ const seq = feature.get('seq');
83
+ if (seq) {
84
+ for (let i = 0; i < mismatches.length; i += 1) {
85
+ const { type, start, cliplen = 0 } = mismatches[i];
86
+ if (type === 'softclip') {
87
+ start === 0
88
+ ? (expansionBefore = cliplen)
89
+ : (expansionAfter = cliplen);
90
+ }
91
+ }
92
+ }
93
+ }
94
+ const [leftPx, rightPx] = bpSpanPx(feature.get('start') - expansionBefore, feature.get('end') + expansionAfter, region, bpPerPx);
95
+ if (displayMode === 'compact') {
96
+ heightPx /= 3;
97
+ }
98
+ if (feature.get('refName') !== region.refName) {
99
+ throw new Error(`feature ${feature.id()} is not on the current region's reference sequence ${region.refName}`);
100
+ }
101
+ const topPx = layout.addRect(feature.id(), feature.get('start') - expansionBefore, feature.get('end') + expansionAfter, heightPx, feature);
102
+ if (topPx === null) {
103
+ return null;
104
+ }
105
+ return {
106
+ feature,
107
+ leftPx,
108
+ rightPx,
109
+ topPx: displayMode === 'collapse' ? 0 : topPx,
110
+ heightPx,
111
+ };
112
+ }
113
+ // expands region for clipping to use. possible improvement: use average read
114
+ // size to set the heuristic maxClippingSize expansion (e.g. short reads
115
+ // don't have to expand a softclipping size a lot, but long reads might)
116
+ getExpandedRegion(region, renderArgs) {
117
+ const { config, showSoftClip } = renderArgs;
118
+ const maxClippingSize = readConfObject(config, 'maxClippingSize');
119
+ const { start, end } = region;
120
+ const len = end - start;
121
+ const bpExpansion = Math.max(len, showSoftClip ? Math.round(maxClippingSize) : 0);
122
+ return {
123
+ // xref https://github.com/mobxjs/mobx-state-tree/issues/1524 for Omit
124
+ ...region,
125
+ start: Math.floor(Math.max(start - bpExpansion, 0)),
126
+ end: Math.ceil(end + bpExpansion),
127
+ };
128
+ }
129
+ colorByOrientation(feature, config) {
130
+ return alignmentColoring[this.getOrientation(feature, config) || 'color_nostrand'];
131
+ }
132
+ getOrientation(feature, config) {
133
+ const orientationType = readConfObject(config, 'orientationType');
134
+ const type = orientationTypes[orientationType];
135
+ const orientation = type[feature.get('pair_orientation')];
136
+ const map = {
137
+ LR: 'color_pair_lr',
138
+ RR: 'color_pair_rr',
139
+ RL: 'color_pair_rl',
140
+ LL: 'color_pair_ll',
141
+ };
142
+ return map[orientation];
143
+ }
144
+ colorByInsertSize(feature, _config) {
145
+ return feature.get('is_paired') &&
146
+ feature.get('seq_id') !== feature.get('next_seq_id')
147
+ ? '#555'
148
+ : `hsl(${Math.abs(feature.get('template_length')) / 10},50%,50%)`;
149
+ }
150
+ colorByStranded(feature, _config) {
151
+ const flags = feature.get('flags');
152
+ const strand = feature.get('strand');
153
+ // is paired
154
+ if (flags & 1) {
155
+ const revflag = flags & 64;
156
+ const flipper = revflag ? -1 : 1;
157
+ // proper pairing
158
+ if (flags & 2) {
159
+ return strand * flipper === 1 ? 'color_rev_strand' : 'color_fwd_strand';
160
+ }
161
+ if (feature.get('multi_segment_next_segment_unmapped')) {
162
+ return strand * flipper === 1
163
+ ? 'color_rev_missing_mate'
164
+ : 'color_fwd_missing_mate';
165
+ }
166
+ if (feature.get('seq_id') === feature.get('next_seq_id')) {
167
+ return strand * flipper === 1
168
+ ? 'color_rev_strand_not_proper'
169
+ : 'color_fwd_strand_not_proper';
170
+ }
171
+ // should only leave aberrant chr
172
+ return strand === 1 ? 'color_fwd_diff_chr' : 'color_rev_diff_chr';
173
+ }
174
+ return strand === 1 ? 'color_fwd_strand' : 'color_rev_strand';
175
+ }
176
+ colorByPerBaseLettering({ ctx, feat, region, bpPerPx, colorForBase, contrastForBase, charWidth, charHeight, canvasWidth, }) {
177
+ const heightLim = charHeight - 2;
178
+ const { feature, topPx, heightPx } = feat;
179
+ const seq = feature.get('seq');
180
+ const cigarOps = parseCigar(feature.get('CIGAR'));
181
+ const w = 1 / bpPerPx;
182
+ const start = feature.get('start');
183
+ let soffset = 0; // sequence offset
184
+ let roffset = 0; // reference offset
185
+ for (let i = 0; i < cigarOps.length; i += 2) {
186
+ const len = +cigarOps[i];
187
+ const op = cigarOps[i + 1];
188
+ if (op === 'S' || op === 'I') {
189
+ soffset += len;
190
+ }
191
+ else if (op === 'D' || op === 'N') {
192
+ roffset += len;
193
+ }
194
+ else if (op === 'M' || op === 'X' || op === '=') {
195
+ for (let m = 0; m < len; m++) {
196
+ const letter = seq[soffset + m];
197
+ const r = start + roffset + m;
198
+ const [leftPx] = bpSpanPx(r, r + 1, region, bpPerPx);
199
+ fillRect(ctx, leftPx, topPx, w + 0.5, heightPx, canvasWidth, colorForBase[letter]);
200
+ if (w >= charWidth && heightPx >= heightLim) {
201
+ // normal SNP coloring
202
+ ctx.fillStyle = contrastForBase[letter];
203
+ ctx.fillText(letter, leftPx + (w - charWidth) / 2 + 1, topPx + heightPx);
204
+ }
205
+ }
206
+ soffset += len;
207
+ roffset += len;
208
+ }
209
+ }
210
+ }
211
+ colorByPerBaseQuality({ ctx, feat, region, bpPerPx, canvasWidth, }) {
212
+ const { feature, topPx, heightPx } = feat;
213
+ const qual = feature.get('qual') || '';
214
+ const scores = qual.split(' ').map(val => +val);
215
+ const cigarOps = parseCigar(feature.get('CIGAR'));
216
+ const width = 1 / bpPerPx;
217
+ const start = feature.get('start');
218
+ let soffset = 0; // sequence offset
219
+ let roffset = 0; // reference offset
220
+ for (let i = 0; i < cigarOps.length; i += 2) {
221
+ const len = +cigarOps[i];
222
+ const op = cigarOps[i + 1];
223
+ if (op === 'S' || op === 'I') {
224
+ soffset += len;
225
+ }
226
+ else if (op === 'D' || op === 'N') {
227
+ roffset += len;
228
+ }
229
+ else if (op === 'M' || op === 'X' || op === '=') {
230
+ for (let m = 0; m < len; m++) {
231
+ const score = scores[soffset + m];
232
+ const [leftPx] = bpSpanPx(start + roffset + m, start + roffset + m + 1, region, bpPerPx);
233
+ fillRect(ctx, leftPx, topPx, width + 0.5, heightPx, canvasWidth, `hsl(${score === 255 ? 150 : score * 1.5},55%,50%)`);
234
+ }
235
+ soffset += len;
236
+ roffset += len;
237
+ }
238
+ }
239
+ }
240
+ // ML stores probabilities as array of numerics and MP is scaled phred scores
241
+ // https://github.com/samtools/hts-specs/pull/418/files#diff-e765c6479316309f56b636f88189cdde8c40b854c7bdcce9ee7fe87a4e76febcR596
242
+ //
243
+ // if we have ML or Ml, it is an 8bit probability, divide by 255
244
+ //
245
+ // if we have MP or Mp it is phred scaled ASCII, which can go up to 90 but
246
+ // has very high likelihood basecalls at that point, we really only care
247
+ // about low qual calls <20 approx
248
+ //
249
+ colorByModifications({ ctx, feat, region, bpPerPx, renderArgs, canvasWidth, }) {
250
+ const { feature, topPx, heightPx } = feat;
251
+ const { Color, modificationTagMap = {} } = renderArgs;
252
+ const mm = getTagAlt(feature, 'MM', 'Mm') || '';
253
+ const ml = getTagAlt(feature, 'ML', 'Ml') || [];
254
+ const probabilities = ml
255
+ ? (typeof ml === 'string' ? ml.split(',').map(e => +e) : ml).map(e => e / 255)
256
+ : getTagAlt(feature, 'MP', 'Mp')
257
+ .split('')
258
+ .map(s => s.charCodeAt(0) - 33)
259
+ .map(elt => Math.min(1, elt / 50));
260
+ const cigar = feature.get('CIGAR');
261
+ const start = feature.get('start');
262
+ const seq = feature.get('seq');
263
+ const strand = feature.get('strand');
264
+ const cigarOps = parseCigar(cigar);
265
+ const modifications = getModificationPositions(mm, seq, strand);
266
+ // probIndex applies across multiple modifications e.g.
267
+ let probIndex = 0;
268
+ for (let i = 0; i < modifications.length; i++) {
269
+ const { type, positions } = modifications[i];
270
+ const col = modificationTagMap[type] || 'black';
271
+ // @ts-ignore
272
+ const base = Color(col);
273
+ for (const readPos of getNextRefPos(cigarOps, positions)) {
274
+ const r = start + readPos;
275
+ const [leftPx, rightPx] = bpSpanPx(r, r + 1, region, bpPerPx);
276
+ // give it a little boost of 0.1 to not make them fully
277
+ // invisible to avoid confusion
278
+ const prob = probabilities[probIndex];
279
+ fillRect(ctx, leftPx, topPx, rightPx - leftPx + 0.5, heightPx, canvasWidth, prob && prob !== 1
280
+ ? base
281
+ .alpha(prob + 0.1)
282
+ .hsl()
283
+ .string()
284
+ : col);
285
+ probIndex++;
286
+ }
287
+ }
288
+ }
289
+ // Color by methylation is slightly modified version of color by
290
+ // modifications that focuses on CpG sites, with non-methylated CpG colored
291
+ // blue
292
+ colorByMethylation({ ctx, feat, region, bpPerPx, renderArgs, canvasWidth, }) {
293
+ const { regionSequence } = renderArgs;
294
+ const { feature, topPx, heightPx } = feat;
295
+ const mm = getTagAlt(feature, 'MM', 'Mm') || '';
296
+ if (!regionSequence) {
297
+ throw new Error('region sequence required for methylation');
298
+ }
299
+ const cigar = feature.get('CIGAR');
300
+ const fstart = feature.get('start');
301
+ const fend = feature.get('end');
302
+ const seq = feature.get('seq');
303
+ const strand = feature.get('strand');
304
+ const cigarOps = parseCigar(cigar);
305
+ const methBins = new Array(region.end - region.start).fill(0);
306
+ const modifications = getModificationPositions(mm, seq, strand);
307
+ for (let i = 0; i < modifications.length; i++) {
308
+ const { type, positions } = modifications[i];
309
+ if (type === 'm' && positions) {
310
+ for (const pos of getNextRefPos(cigarOps, positions)) {
311
+ const epos = pos + fstart - region.start;
312
+ if (epos >= 0 && epos < methBins.length) {
313
+ methBins[epos] = 1;
314
+ }
315
+ }
316
+ }
317
+ }
318
+ for (let j = fstart; j < fend; j++) {
319
+ const i = j - region.start;
320
+ if (i >= 0 && i < methBins.length) {
321
+ const l1 = regionSequence[i].toLowerCase();
322
+ const l2 = regionSequence[i + 1].toLowerCase();
323
+ // if we are zoomed out, display just a block over the cpg
324
+ if (bpPerPx > 2) {
325
+ if (l1 === 'c' && l2 === 'g') {
326
+ const s = region.start + i;
327
+ const [leftPx, rightPx] = bpSpanPx(s, s + 2, region, bpPerPx);
328
+ fillRect(ctx, leftPx, topPx, rightPx - leftPx + 0.5, heightPx, canvasWidth, methBins[i] || methBins[i + 1] ? 'red' : 'blue');
329
+ }
330
+ }
331
+ // if we are zoomed in, color the c inside the cpg
332
+ else {
333
+ // color
334
+ if (l1 === 'c' && l2 === 'g') {
335
+ const s = region.start + i;
336
+ const [leftPx, rightPx] = bpSpanPx(s, s + 1, region, bpPerPx);
337
+ fillRect(ctx, leftPx, topPx, rightPx - leftPx + 0.5, heightPx, canvasWidth, methBins[i] ? 'red' : 'blue');
338
+ const [leftPx2, rightPx2] = bpSpanPx(s + 1, s + 2, region, bpPerPx);
339
+ fillRect(ctx, leftPx2, topPx, rightPx2 - leftPx2 + 0.5, heightPx, canvasWidth, methBins[i + 1] ? 'red' : 'blue');
340
+ }
341
+ }
342
+ }
343
+ }
344
+ }
345
+ drawRect(ctx, feat, props) {
346
+ const { regions, bpPerPx } = props;
347
+ const { heightPx, topPx, feature } = feat;
348
+ const [region] = regions;
349
+ const [leftPx, rightPx] = bpSpanPx(feature.get('start'), feature.get('end'), region, bpPerPx);
350
+ const flip = region.reversed ? -1 : 1;
351
+ const strand = feature.get('strand') * flip;
352
+ if (bpPerPx < 10) {
353
+ if (strand === -1) {
354
+ ctx.beginPath();
355
+ ctx.moveTo(leftPx - 5, topPx + heightPx / 2);
356
+ ctx.lineTo(leftPx, topPx + heightPx);
357
+ ctx.lineTo(rightPx, topPx + heightPx);
358
+ ctx.lineTo(rightPx, topPx);
359
+ ctx.lineTo(leftPx, topPx);
360
+ ctx.closePath();
361
+ ctx.fill();
362
+ }
363
+ else {
364
+ ctx.beginPath();
365
+ ctx.moveTo(leftPx, topPx);
366
+ ctx.lineTo(leftPx, topPx + heightPx);
367
+ ctx.lineTo(rightPx, topPx + heightPx);
368
+ ctx.lineTo(rightPx + 5, topPx + heightPx / 2);
369
+ ctx.lineTo(rightPx, topPx);
370
+ ctx.closePath();
371
+ ctx.fill();
372
+ }
373
+ }
374
+ else {
375
+ ctx.fillRect(leftPx, topPx, rightPx - leftPx, heightPx);
376
+ }
377
+ }
378
+ drawAlignmentRect({ ctx, feat, renderArgs, colorForBase, contrastForBase, charWidth, charHeight, defaultColor, canvasWidth, }) {
379
+ const { config, bpPerPx, regions, colorBy, colorTagMap = {} } = renderArgs;
380
+ const { tag = '', type: colorType = '' } = colorBy || {};
381
+ const { feature } = feat;
382
+ const region = regions[0];
383
+ // first pass for simple color changes that change the color of the
384
+ // alignment
385
+ switch (colorType) {
386
+ case 'insertSize':
387
+ ctx.fillStyle = this.colorByInsertSize(feature, config);
388
+ break;
389
+ case 'strand':
390
+ ctx.fillStyle = feature.get('strand') === -1 ? '#8F8FD8' : '#EC8B8B';
391
+ break;
392
+ case 'mappingQuality':
393
+ ctx.fillStyle = `hsl(${feature.get('mq')},50%,50%)`;
394
+ break;
395
+ case 'pairOrientation':
396
+ ctx.fillStyle = this.colorByOrientation(feature, config);
397
+ break;
398
+ case 'stranded':
399
+ ctx.fillStyle = alignmentColoring[this.colorByStranded(feature, config)];
400
+ break;
401
+ case 'xs':
402
+ case 'tag': {
403
+ const tags = feature.get('tags');
404
+ const val = tags ? tags[tag] : feature.get(tag);
405
+ // special for for XS/TS tag
406
+ if (tag === 'XS' || tag === 'TS') {
407
+ const map = {
408
+ '-': 'color_rev_strand',
409
+ '+': 'color_fwd_strand',
410
+ };
411
+ ctx.fillStyle = alignmentColoring[map[val] || 'color_nostrand'];
412
+ }
413
+ // lower case 'ts' from minimap2 is flipped from xs
414
+ if (tag === 'ts') {
415
+ const map = {
416
+ '-': feature.get('strand') === -1
417
+ ? 'color_fwd_strand'
418
+ : 'color_rev_strand',
419
+ '+': feature.get('strand') === -1
420
+ ? 'color_rev_strand'
421
+ : 'color_fwd_strand',
422
+ };
423
+ ctx.fillStyle = alignmentColoring[map[val] || 'color_nostrand'];
424
+ }
425
+ // tag is not one of the autofilled tags, has color-value pairs from
426
+ // fetchValues
427
+ else {
428
+ const foundValue = colorTagMap[val];
429
+ ctx.fillStyle = foundValue || alignmentColoring['color_nostrand'];
430
+ }
431
+ break;
432
+ }
433
+ case 'insertSizeAndPairOrientation':
434
+ break;
435
+ case 'modifications':
436
+ case 'methylation':
437
+ // this coloring is similar to igv.js, and is helpful to color negative
438
+ // strand reads differently because their c-g will be flipped (e.g. g-c
439
+ // read right to left)
440
+ if (feature.get('flags') & 16) {
441
+ ctx.fillStyle = '#c8dcc8';
442
+ }
443
+ else {
444
+ ctx.fillStyle = '#c8c8c8';
445
+ }
446
+ break;
447
+ case 'normal':
448
+ default:
449
+ if (defaultColor) {
450
+ // avoid a readConfObject call here
451
+ ctx.fillStyle = '#c8c8c8';
452
+ }
453
+ else {
454
+ ctx.fillStyle = readConfObject(config, 'color', { feature });
455
+ }
456
+ break;
457
+ }
458
+ this.drawRect(ctx, feat, renderArgs);
459
+ // second pass for color types that render per-base things that go over the
460
+ // existing drawing
461
+ switch (colorType) {
462
+ case 'perBaseQuality':
463
+ this.colorByPerBaseQuality({
464
+ ctx,
465
+ feat,
466
+ region,
467
+ bpPerPx,
468
+ canvasWidth,
469
+ });
470
+ break;
471
+ case 'perBaseLettering':
472
+ this.colorByPerBaseLettering({
473
+ ctx,
474
+ feat,
475
+ region,
476
+ bpPerPx,
477
+ colorForBase,
478
+ contrastForBase,
479
+ charWidth,
480
+ charHeight,
481
+ canvasWidth,
482
+ });
483
+ break;
484
+ case 'modifications':
485
+ this.colorByModifications({
486
+ ctx,
487
+ feat,
488
+ region,
489
+ bpPerPx,
490
+ renderArgs,
491
+ canvasWidth,
492
+ });
493
+ break;
494
+ case 'methylation':
495
+ this.colorByMethylation({
496
+ ctx,
497
+ feat,
498
+ region,
499
+ bpPerPx,
500
+ renderArgs,
501
+ canvasWidth,
502
+ });
503
+ break;
504
+ }
505
+ }
506
+ drawMismatches({ ctx, feat, renderArgs, minSubfeatureWidth, largeInsertionIndicatorScale, mismatchAlpha, charWidth, charHeight, colorForBase, contrastForBase, canvasWidth, drawSNPsMuted, drawIndels = true, }) {
507
+ const { Color, bpPerPx, regions } = renderArgs;
508
+ const { heightPx, topPx, feature } = feat;
509
+ const [region] = regions;
510
+ const start = feature.get('start');
511
+ const pxPerBp = Math.min(1 / bpPerPx, 2);
512
+ const w = Math.max(minSubfeatureWidth, pxPerBp);
513
+ const mismatches = feature.get('mismatches');
514
+ const heightLim = charHeight - 2;
515
+ // extraHorizontallyFlippedOffset is used to draw interbase items, which
516
+ // are located to the left when forward and right when reversed
517
+ const extraHorizontallyFlippedOffset = region.reversed
518
+ ? 1 / bpPerPx + 1
519
+ : -1;
520
+ // two pass rendering: first pass, draw all the mismatches except wide
521
+ // insertion markers
522
+ for (let i = 0; i < mismatches.length; i += 1) {
523
+ const mismatch = mismatches[i];
524
+ const mstart = start + mismatch.start;
525
+ const mlen = mismatch.length;
526
+ const mbase = mismatch.base;
527
+ const [leftPx, rightPx] = bpSpanPx(mstart, mstart + mlen, region, bpPerPx);
528
+ const widthPx = Math.max(minSubfeatureWidth, Math.abs(leftPx - rightPx));
529
+ if (mismatch.type === 'mismatch') {
530
+ if (!drawSNPsMuted) {
531
+ const baseColor = colorForBase[mismatch.base] || '#888';
532
+ fillRect(ctx, leftPx, topPx, widthPx, heightPx, canvasWidth, !mismatchAlpha
533
+ ? baseColor
534
+ : mismatch.qual !== undefined
535
+ ? // @ts-ignore
536
+ Color(baseColor)
537
+ .alpha(Math.min(1, mismatch.qual / 50))
538
+ .hsl()
539
+ .string()
540
+ : baseColor);
541
+ }
542
+ if (widthPx >= charWidth && heightPx >= heightLim) {
543
+ // normal SNP coloring
544
+ const contrastColor = drawSNPsMuted
545
+ ? 'black'
546
+ : contrastForBase[mismatch.base] || 'black';
547
+ ctx.fillStyle = !mismatchAlpha
548
+ ? contrastColor
549
+ : mismatch.qual !== undefined
550
+ ? // @ts-ignore
551
+ Color(contrastColor)
552
+ .alpha(Math.min(1, mismatch.qual / 50))
553
+ .hsl()
554
+ .string()
555
+ : contrastColor;
556
+ ctx.fillText(mbase, leftPx + (widthPx - charWidth) / 2 + 1, topPx + heightPx);
557
+ }
558
+ }
559
+ else if (mismatch.type === 'deletion' && drawIndels) {
560
+ fillRect(ctx, leftPx, topPx, widthPx, heightPx, canvasWidth, colorForBase.deletion);
561
+ const txt = `${mismatch.length}`;
562
+ const rwidth = measureText(txt, 10);
563
+ if (widthPx >= rwidth && heightPx >= heightLim) {
564
+ ctx.fillStyle = contrastForBase.deletion;
565
+ ctx.fillText(txt, (leftPx + rightPx) / 2 - rwidth / 2, topPx + heightPx);
566
+ }
567
+ }
568
+ else if (mismatch.type === 'insertion' && drawIndels) {
569
+ ctx.fillStyle = 'purple';
570
+ const pos = leftPx + extraHorizontallyFlippedOffset;
571
+ const len = +mismatch.base || mismatch.length;
572
+ const insW = Math.max(minSubfeatureWidth / 2, Math.min(1.2, 1 / bpPerPx));
573
+ if (len < 10) {
574
+ fillRect(ctx, pos, topPx, insW, heightPx, canvasWidth, 'purple');
575
+ if (1 / bpPerPx >= charWidth && heightPx >= heightLim) {
576
+ fillRect(ctx, pos - insW, topPx, insW * 3, 1, canvasWidth);
577
+ fillRect(ctx, pos - insW, topPx + heightPx - 1, insW * 3, 1, canvasWidth);
578
+ ctx.fillText(`(${mismatch.base})`, pos + 3, topPx + heightPx);
579
+ }
580
+ }
581
+ }
582
+ else if (mismatch.type === 'hardclip' || mismatch.type === 'softclip') {
583
+ const pos = leftPx + extraHorizontallyFlippedOffset;
584
+ fillRect(ctx, pos, topPx, w, heightPx, canvasWidth, mismatch.type === 'hardclip' ? 'red' : 'blue');
585
+ if (1 / bpPerPx >= charWidth && heightPx >= heightLim) {
586
+ fillRect(ctx, pos - w, topPx, w * 3, 1, canvasWidth);
587
+ fillRect(ctx, pos - w, topPx + heightPx - 1, w * 3, 1, canvasWidth);
588
+ ctx.fillText(`(${mismatch.base})`, pos + 3, topPx + heightPx);
589
+ }
590
+ }
591
+ else if (mismatch.type === 'skip') {
592
+ // fix to avoid bad rendering note that this was also related to chrome
593
+ // bug https://bugs.chromium.org/p/chromium/issues/detail?id=1131528
594
+ // also affected firefox ref #1236 #2750
595
+ if (leftPx + widthPx > 0) {
596
+ // make small exons more visible when zoomed far out
597
+ const adjustPx = widthPx - (bpPerPx > 10 ? 1.5 : 0);
598
+ ctx.clearRect(leftPx, topPx, adjustPx, heightPx);
599
+ fillRect(ctx, Math.max(0, leftPx), topPx + heightPx / 2 - 1, adjustPx + (leftPx < 0 ? leftPx : 0), 2, canvasWidth, '#333');
600
+ }
601
+ }
602
+ }
603
+ // second pass, draw wide insertion markers on top
604
+ if (drawIndels) {
605
+ for (let i = 0; i < mismatches.length; i += 1) {
606
+ const mismatch = mismatches[i];
607
+ const mstart = start + mismatch.start;
608
+ const mlen = mismatch.length;
609
+ const [leftPx] = bpSpanPx(mstart, mstart + mlen, region, bpPerPx);
610
+ const len = +mismatch.base || mismatch.length;
611
+ const txt = `${len}`;
612
+ if (mismatch.type === 'insertion' && len >= 10) {
613
+ if (bpPerPx > largeInsertionIndicatorScale) {
614
+ fillRect(ctx, leftPx - 1, topPx, 2, heightPx, canvasWidth, 'purple');
615
+ }
616
+ else if (heightPx > charHeight) {
617
+ const rwidth = measureText(txt);
618
+ const padding = 5;
619
+ fillRect(ctx, leftPx - rwidth / 2 - padding, topPx, rwidth + 2 * padding, heightPx, canvasWidth, 'purple');
620
+ ctx.fillStyle = 'white';
621
+ ctx.fillText(txt, leftPx - rwidth / 2, topPx + heightPx);
622
+ }
623
+ else {
624
+ const padding = 2;
625
+ fillRect(ctx, leftPx - padding, topPx, 2 * padding, heightPx, canvasWidth, 'purple');
626
+ }
627
+ }
628
+ }
629
+ }
630
+ }
631
+ drawSoftClipping({ ctx, feat, renderArgs, config, theme, canvasWidth, }) {
632
+ const { feature, topPx, heightPx } = feat;
633
+ const { regions, bpPerPx } = renderArgs;
634
+ const [region] = regions;
635
+ const minFeatWidth = readConfObject(config, 'minSubfeatureWidth');
636
+ const mismatches = feature.get('mismatches');
637
+ const seq = feature.get('seq');
638
+ const { charWidth, charHeight } = this.getCharWidthHeight(ctx);
639
+ // @ts-ignore
640
+ const { bases } = theme.palette;
641
+ const colorForBase = {
642
+ A: bases.A.main,
643
+ C: bases.C.main,
644
+ G: bases.G.main,
645
+ T: bases.T.main,
646
+ deletion: '#808080', // gray
647
+ };
648
+ // Display all bases softclipped off in lightened colors
649
+ if (seq) {
650
+ mismatches
651
+ .filter(mismatch => mismatch.type === 'softclip')
652
+ .forEach(mismatch => {
653
+ const softClipLength = mismatch.cliplen || 0;
654
+ const s = feature.get('start');
655
+ const softClipStart = mismatch.start === 0 ? s - softClipLength : s + mismatch.start;
656
+ for (let k = 0; k < softClipLength; k += 1) {
657
+ const base = seq.charAt(k + mismatch.start);
658
+ // If softclip length+start is longer than sequence, no need to
659
+ // continue showing base
660
+ if (!base) {
661
+ return;
662
+ }
663
+ const [softClipLeftPx, softClipRightPx] = bpSpanPx(softClipStart + k, softClipStart + k + 1, region, bpPerPx);
664
+ const softClipWidthPx = Math.max(minFeatWidth, Math.abs(softClipLeftPx - softClipRightPx));
665
+ // Black accounts for IUPAC ambiguity code bases such as N that
666
+ // show in soft clipping
667
+ const baseColor = colorForBase[base] || '#000000';
668
+ ctx.fillStyle = baseColor;
669
+ fillRect(ctx, softClipLeftPx, topPx, softClipWidthPx, heightPx, canvasWidth);
670
+ if (softClipWidthPx >= charWidth && heightPx >= charHeight - 5) {
671
+ ctx.fillStyle = theme.palette.getContrastText(baseColor);
672
+ ctx.fillText(base, softClipLeftPx + (softClipWidthPx - charWidth) / 2 + 1, topPx + heightPx);
673
+ }
674
+ }
675
+ });
676
+ }
677
+ }
678
+ makeImageData({ ctx, layoutRecords, canvasWidth, renderArgs, }) {
679
+ const { layout, config, showSoftClip, colorBy, theme: configTheme, } = renderArgs;
680
+ const mismatchAlpha = readConfObject(config, 'mismatchAlpha');
681
+ const minSubfeatureWidth = readConfObject(config, 'minSubfeatureWidth');
682
+ const largeInsertionIndicatorScale = readConfObject(config, 'largeInsertionIndicatorScale');
683
+ const defaultColor = readConfObject(config, 'color') === '#f0f';
684
+ const theme = createJBrowseTheme(configTheme);
685
+ const colorForBase = getColorBaseMap(theme);
686
+ const contrastForBase = getContrastBaseMap(theme);
687
+ if (!layout) {
688
+ throw new Error(`layout required`);
689
+ }
690
+ if (!layout.addRect) {
691
+ throw new Error('invalid layout object');
692
+ }
693
+ ctx.font = 'bold 10px Courier New,monospace';
694
+ const { charWidth, charHeight } = this.getCharWidthHeight(ctx);
695
+ const drawSNPsMuted = shouldDrawSNPsMuted(colorBy === null || colorBy === void 0 ? void 0 : colorBy.type);
696
+ const drawIndels = shouldDrawIndels(colorBy === null || colorBy === void 0 ? void 0 : colorBy.type);
697
+ for (let i = 0; i < layoutRecords.length; i++) {
698
+ const feat = layoutRecords[i];
699
+ if (feat === null) {
700
+ continue;
701
+ }
702
+ this.drawAlignmentRect({
703
+ ctx,
704
+ feat,
705
+ renderArgs,
706
+ defaultColor,
707
+ colorForBase,
708
+ contrastForBase,
709
+ charWidth,
710
+ charHeight,
711
+ canvasWidth,
712
+ });
713
+ this.drawMismatches({
714
+ ctx,
715
+ feat,
716
+ renderArgs,
717
+ mismatchAlpha,
718
+ drawSNPsMuted,
719
+ drawIndels,
720
+ largeInsertionIndicatorScale,
721
+ minSubfeatureWidth,
722
+ charWidth,
723
+ charHeight,
724
+ colorForBase,
725
+ contrastForBase,
726
+ canvasWidth,
727
+ });
728
+ if (showSoftClip) {
729
+ this.drawSoftClipping({
730
+ ctx,
731
+ feat,
732
+ renderArgs,
733
+ config,
734
+ theme,
735
+ canvasWidth,
736
+ });
737
+ }
738
+ }
739
+ }
740
+ // we perform a full layout before render as a separate method because the
741
+ // layout determines the height of the canvas that we use to render
742
+ layoutFeats(props) {
743
+ const { layout, features, sortedBy, config, bpPerPx, showSoftClip, regions, } = props;
744
+ const [region] = regions;
745
+ if (!layout) {
746
+ throw new Error(`layout required`);
747
+ }
748
+ if (!layout.addRect) {
749
+ throw new Error('invalid layout object');
750
+ }
751
+ const featureMap = (sortedBy === null || sortedBy === void 0 ? void 0 : sortedBy.type) && region.start === sortedBy.pos
752
+ ? sortFeature(features, sortedBy)
753
+ : features;
754
+ const heightPx = readConfObject(config, 'height');
755
+ const displayMode = readConfObject(config, 'displayMode');
756
+ return iterMap(featureMap.values(), feature => this.layoutFeature({
757
+ feature,
758
+ layout,
759
+ bpPerPx,
760
+ region,
761
+ showSoftClip,
762
+ heightPx,
763
+ displayMode,
764
+ }), featureMap.size);
765
+ }
766
+ async fetchSequence(renderProps) {
767
+ const { sessionId, regions, adapterConfig } = renderProps;
768
+ const { sequenceAdapter } = adapterConfig;
769
+ if (!sequenceAdapter) {
770
+ return undefined;
771
+ }
772
+ const { dataAdapter } = await getAdapter(this.pluginManager, sessionId, sequenceAdapter);
773
+ const [region] = regions;
774
+ return fetchSequence(region, dataAdapter);
775
+ }
776
+ async render(renderProps) {
777
+ var _a;
778
+ const features = await this.getFeatures(renderProps);
779
+ const layout = this.createLayoutInWorker(renderProps);
780
+ const { regions, bpPerPx } = renderProps;
781
+ const layoutRecords = this.layoutFeats({
782
+ ...renderProps,
783
+ features,
784
+ layout,
785
+ });
786
+ const [region] = regions;
787
+ // only need reference sequence if there are features and only for some
788
+ // cases
789
+ const regionSequence = features.size && shouldFetchReferenceSequence((_a = renderProps.colorBy) === null || _a === void 0 ? void 0 : _a.type)
790
+ ? await this.fetchSequence(renderProps)
791
+ : undefined;
792
+ const { end, start } = region;
793
+ const width = (end - start) / bpPerPx;
794
+ const height = Math.max(layout.getTotalHeight(), 1);
795
+ const Color = await import('color').then(f => f.default);
796
+ const res = await renderToAbstractCanvas(width, height, renderProps, (ctx) => this.makeImageData({
797
+ ctx,
798
+ layoutRecords,
799
+ canvasWidth: width,
800
+ renderArgs: {
801
+ ...renderProps,
802
+ layout,
803
+ features,
804
+ regionSequence,
805
+ Color,
806
+ },
807
+ }));
808
+ const results = await super.render({
809
+ ...renderProps,
810
+ ...res,
811
+ features,
812
+ layout,
813
+ height,
814
+ width,
815
+ });
816
+ return {
817
+ ...results,
818
+ ...res,
819
+ features: new Map(),
820
+ layout,
821
+ height,
822
+ width,
823
+ maxHeightReached: layout.maxHeightReached,
824
+ };
825
+ }
826
+ createSession(args) {
827
+ return new PileupLayoutSession(args);
828
+ }
829
+ }
830
+ //# sourceMappingURL=PileupRenderer.js.map