@jbrowse/plugin-alignments 1.6.5 → 1.6.6

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 (34) hide show
  1. package/dist/AlignmentsFeatureDetail/index.d.ts +1 -1
  2. package/dist/BamAdapter/BamSlightlyLazyFeature.d.ts +3 -2
  3. package/dist/BamAdapter/configSchema.d.ts +1 -1
  4. package/dist/CramAdapter/configSchema.d.ts +1 -1
  5. package/dist/HtsgetBamAdapter/configSchema.d.ts +1 -1
  6. package/dist/LinearAlignmentsDisplay/models/configSchema.d.ts +1 -1
  7. package/dist/LinearAlignmentsDisplay/models/model.d.ts +1 -1
  8. package/dist/LinearPileupDisplay/configSchema.d.ts +1 -1
  9. package/dist/LinearSNPCoverageDisplay/components/Tooltip.d.ts +1 -1
  10. package/dist/LinearSNPCoverageDisplay/models/configSchema.d.ts +1 -1
  11. package/dist/PileupRenderer/configSchema.d.ts +1 -1
  12. package/dist/SNPCoverageAdapter/configSchema.d.ts +1 -1
  13. package/dist/SNPCoverageRenderer/SNPCoverageRenderer.d.ts +1 -1
  14. package/dist/SNPCoverageRenderer/configSchema.d.ts +1 -1
  15. package/dist/SNPCoverageRenderer/index.d.ts +1 -1
  16. package/dist/plugin-alignments.cjs.development.js +254 -180
  17. package/dist/plugin-alignments.cjs.development.js.map +1 -1
  18. package/dist/plugin-alignments.cjs.production.min.js +1 -1
  19. package/dist/plugin-alignments.cjs.production.min.js.map +1 -1
  20. package/dist/plugin-alignments.esm.js +254 -180
  21. package/dist/plugin-alignments.esm.js.map +1 -1
  22. package/package.json +4 -4
  23. package/src/AlignmentsFeatureDetail/AlignmentsFeatureDetail.tsx +23 -14
  24. package/src/BamAdapter/BamSlightlyLazyFeature.ts +8 -4
  25. package/src/LinearAlignmentsDisplay/components/AlignmentsDisplay.tsx +38 -30
  26. package/src/LinearAlignmentsDisplay/models/model.tsx +6 -3
  27. package/src/LinearPileupDisplay/model.ts +6 -6
  28. package/src/LinearSNPCoverageDisplay/components/Tooltip.tsx +5 -3
  29. package/src/LinearSNPCoverageDisplay/models/configSchema.ts +4 -5
  30. package/src/PileupRenderer/PileupRenderer.tsx +28 -17
  31. package/src/PileupRenderer/components/PileupRendering.tsx +5 -3
  32. package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +28 -24
  33. package/src/SNPCoverageRenderer/SNPCoverageRenderer.ts +86 -56
  34. package/src/SNPCoverageRenderer/configSchema.js +1 -1
@@ -1,8 +1,7 @@
1
1
  import { createJBrowseTheme } from '@jbrowse/core/ui'
2
- import { featureSpanPx } from '@jbrowse/core/util'
2
+ import { featureSpanPx, bpSpanPx } from '@jbrowse/core/util'
3
3
  import { Feature } from '@jbrowse/core/util/simpleFeature'
4
4
  import { readConfObject } from '@jbrowse/core/configuration'
5
- import { bpSpanPx } from '@jbrowse/core/util'
6
5
  import { RenderArgsDeserialized as FeatureRenderArgsDeserialized } from '@jbrowse/core/pluggableElementTypes/renderers/FeatureRendererType'
7
6
  import {
8
7
  getOrigin,
@@ -24,7 +23,7 @@ export interface RenderArgsDeserializedWithFeatures
24
23
  features: Map<string, Feature>
25
24
  ticks: { values: number[] }
26
25
  displayCrossHatches: boolean
27
- modificationTagMap: Record<string, string>
26
+ modificationTagMap?: Record<string, string>
28
27
  }
29
28
 
30
29
  type Counts = {
@@ -49,12 +48,12 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
49
48
  regions,
50
49
  bpPerPx,
51
50
  displayCrossHatches,
52
- modificationTagMap,
51
+ modificationTagMap = {},
53
52
  scaleOpts,
54
53
  height: unadjustedHeight,
55
54
  theme: configTheme,
56
55
  config: cfg,
57
- ticks: { values },
56
+ ticks,
58
57
  } = props
59
58
  const theme = createJBrowseTheme(configTheme)
60
59
  const [region] = regions
@@ -66,9 +65,23 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
66
65
  const offset = YSCALEBAR_LABEL_OFFSET
67
66
  const height = unadjustedHeight - offset * 2
68
67
 
68
+ const { domain } = scaleOpts
69
+ if (!domain) {
70
+ return
71
+ }
69
72
  const opts = { ...scaleOpts, range: [0, height] }
70
73
  const viewScale = getScale(opts)
71
- const snpViewScale = getScale({ ...opts, scaleType: 'linear' })
74
+ const snpViewScale = getScale({
75
+ ...opts,
76
+ range: [0, height],
77
+ scaleType: 'linear',
78
+ })
79
+ // clipping and insertion indicators, uses a smaller height/2 scale
80
+ const indicatorViewScale = getScale({
81
+ ...opts,
82
+ range: [0, height / 2],
83
+ scaleType: 'linear',
84
+ })
72
85
  const originY = getOrigin(scaleOpts.scaleType)
73
86
  const snpOriginY = getOrigin('linear')
74
87
 
@@ -83,7 +96,11 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
83
96
 
84
97
  // this is always linear scale, even when plotted on top of log scale
85
98
  const snpToY = (n: number) => height - (snpViewScale(n) || 0) + offset
99
+ const indicatorToY = (n: number) =>
100
+ height - (indicatorViewScale(n) || 0) + offset
86
101
  const snpToHeight = (n: number) => snpToY(snpOriginY) - snpToY(n)
102
+ const indicatorToHeight = (n: number) =>
103
+ indicatorToY(snpOriginY) - indicatorToY(n)
87
104
 
88
105
  const colorForBase: { [key: string]: string } = {
89
106
  A: theme.palette.bases.A.main,
@@ -106,91 +123,104 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
106
123
  // Use two pass rendering, which helps in visualizing the SNPs at higher
107
124
  // bpPerPx First pass: draw the gray background
108
125
  ctx.fillStyle = colorForBase.total
109
- coverage.forEach(feature => {
126
+ for (let i = 0; i < coverage.length; i++) {
127
+ const feature = coverage[i]
110
128
  const [leftPx, rightPx] = featureSpanPx(feature, region, bpPerPx)
111
129
  const w = rightPx - leftPx + 0.3
112
130
  const score = feature.get('score') as number
113
131
  ctx.fillRect(leftPx, toY(score), w, toHeight(score))
114
- })
115
- ctx.fillStyle = 'grey'
116
- ctx.beginPath()
117
- ctx.lineTo(0, 0)
118
- ctx.moveTo(0, width)
119
- ctx.stroke()
132
+ }
133
+
134
+ // Keep track of previous total which we will use it to draw the interbase
135
+ // indicator (if there is a sudden clip, there will be no read coverage but
136
+ // there will be "clip" coverage) at that position beyond the read. if the
137
+ // clip is right at a block boundary then prevTotal will not be available,
138
+ // so this is a best attempt to plot interbase indicator at the "cliffs"
139
+ let prevTotal = 0
140
+
141
+ // extraHorizontallyFlippedOffset is used to draw interbase items, which
142
+ // are located to the left when forward and right when reversed
143
+ const extraHorizontallyFlippedOffset = region.reversed ? 1 / bpPerPx : 0
120
144
 
121
145
  // Second pass: draw the SNP data, and add a minimum feature width of 1px
122
146
  // which can be wider than the actual bpPerPx This reduces overdrawing of
123
147
  // the grey background over the SNPs
124
- coverage.forEach(feature => {
148
+
149
+ for (let i = 0; i < coverage.length; i++) {
150
+ const feature = coverage[i]
125
151
  const [leftPx, rightPx] = featureSpanPx(feature, region, bpPerPx)
126
152
 
127
153
  const snpinfo = feature.get('snpinfo') as SNPInfo
128
154
  const w = Math.max(rightPx - leftPx + 0.3, 1)
129
155
  const totalScore = snpinfo.total
156
+ const keys = Object.keys(snpinfo.cov).sort()
130
157
 
131
- Object.entries(snpinfo.cov)
132
- .sort(([a], [b]) => {
133
- if (a < b) {
134
- return -1
135
- }
136
- if (a > b) {
137
- return 1
138
- }
139
- return 0
140
- })
141
- .reduce((curr, [base, { total }]) => {
142
- ctx.fillStyle =
143
- colorForBase[base] ||
144
- modificationTagMap[base.replace('mod_', '')] ||
145
- 'red'
146
- ctx.fillRect(leftPx, snpToY(total + curr), w, snpToHeight(total))
147
- return curr + total
148
- }, 0)
149
-
150
- const interbaseEvents = Object.entries(snpinfo.noncov)
158
+ let curr = 0
159
+ for (let i = 0; i < keys.length; i++) {
160
+ const base = keys[i]
161
+ const { total } = snpinfo.cov[base]
162
+ ctx.fillStyle =
163
+ colorForBase[base] ||
164
+ modificationTagMap[base.replace('mod_', '')] ||
165
+ '#888'
166
+ ctx.fillRect(leftPx, snpToY(total + curr), w, snpToHeight(total))
167
+ curr += total
168
+ }
169
+
170
+ const interbaseEvents = Object.keys(snpinfo.noncov)
151
171
  const indicatorHeight = 4.5
152
172
  if (drawInterbaseCounts) {
153
- interbaseEvents.reduce((curr, [base, { total }]) => {
173
+ let curr = 0
174
+ for (let i = 0; i < interbaseEvents.length; i++) {
175
+ const base = interbaseEvents[i]
176
+ const { total } = snpinfo.noncov[base]
154
177
  ctx.fillStyle = colorForBase[base]
155
178
  ctx.fillRect(
156
- leftPx - 0.6,
157
- indicatorHeight + snpToHeight(curr),
179
+ leftPx - 0.6 + extraHorizontallyFlippedOffset,
180
+ indicatorHeight + indicatorToHeight(curr),
158
181
  1.2,
159
- snpToHeight(total),
182
+ indicatorToHeight(total),
160
183
  )
161
- return curr + total
162
- }, 0)
184
+ curr += total
185
+ }
163
186
  }
164
187
 
165
188
  if (drawIndicators) {
166
189
  let accum = 0
167
190
  let max = 0
168
191
  let maxBase = ''
169
- interbaseEvents.forEach(([base, { total }]) => {
192
+ for (let i = 0; i < interbaseEvents.length; i++) {
193
+ const base = interbaseEvents[i]
194
+ const { total } = snpinfo.noncov[base]
170
195
  accum += total
171
196
  if (total > max) {
172
197
  max = total
173
198
  maxBase = base
174
199
  }
175
- })
200
+ }
176
201
 
177
202
  // avoid drawing a bunch of indicators if coverage is very low e.g.
178
- // less than 7
179
- if (accum > totalScore * indicatorThreshold && totalScore > 7) {
203
+ // less than 7, uses the prev total in the case of the "cliff"
204
+ const indicatorComparatorScore = Math.max(totalScore, prevTotal)
205
+ if (
206
+ accum > indicatorComparatorScore * indicatorThreshold &&
207
+ indicatorComparatorScore > 7
208
+ ) {
180
209
  ctx.fillStyle = colorForBase[maxBase]
181
210
  ctx.beginPath()
182
- ctx.moveTo(leftPx - 3, 0)
183
- ctx.lineTo(leftPx + 3, 0)
184
- ctx.lineTo(leftPx, indicatorHeight)
211
+ const l = leftPx + extraHorizontallyFlippedOffset
212
+ ctx.moveTo(l - 3.5, 0)
213
+ ctx.lineTo(l + 3.5, 0)
214
+ ctx.lineTo(l, indicatorHeight)
185
215
  ctx.fill()
186
216
  }
187
217
  }
188
- })
189
-
190
- ctx.globalAlpha = 0.7
218
+ prevTotal = totalScore
219
+ }
191
220
 
192
221
  if (drawArcs) {
193
- skips.forEach(f => {
222
+ for (let i = 0; i < skips.length; i++) {
223
+ const f = skips[i]
194
224
  const [left, right] = bpSpanPx(
195
225
  f.get('start'),
196
226
  f.get('end'),
@@ -201,9 +231,9 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
201
231
  ctx.beginPath()
202
232
  const str = f.get('strand') as number
203
233
  const xs = f.get('xs') as string
204
- const pos = 'rgb(255,200,200)'
205
- const neg = 'rgb(200,200,255)'
206
- const neutral = 'rgb(200,200,200)'
234
+ const pos = 'rgba(255,200,200,0.7)'
235
+ const neg = 'rgba(200,200,255,0.7)'
236
+ const neutral = 'rgba(200,200,200,0.7)'
207
237
 
208
238
  if (xs === '+') {
209
239
  ctx.strokeStyle = pos
@@ -221,13 +251,13 @@ export default class SNPCoverageRenderer extends WiggleBaseRenderer {
221
251
  ctx.moveTo(left, height - offset * 2)
222
252
  ctx.bezierCurveTo(left, 0, right, 0, right, height - offset * 2)
223
253
  ctx.stroke()
224
- })
254
+ }
225
255
  }
226
256
 
227
257
  if (displayCrossHatches) {
228
258
  ctx.lineWidth = 1
229
259
  ctx.strokeStyle = 'rgba(140,140,140,0.8)'
230
- values.forEach(tick => {
260
+ ticks.values.forEach(tick => {
231
261
  ctx.beginPath()
232
262
  ctx.moveTo(0, Math.round(toY(tick)))
233
263
  ctx.lineTo(width, Math.round(toY(tick)))
@@ -12,7 +12,7 @@ export default ConfigurationSchema(
12
12
  type: 'number',
13
13
  description:
14
14
  'the proportion of reads containing a insertion/clip indicator',
15
- defaultValue: 0.3,
15
+ defaultValue: 0.4,
16
16
  },
17
17
  drawArcs: {
18
18
  type: 'boolean',