@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-alignments",
3
- "version": "1.6.5",
3
+ "version": "1.6.6",
4
4
  "description": "JBrowse 2 alignments adapters, tracks, etc.",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -35,7 +35,7 @@
35
35
  "useSrc": "node ../../scripts/useSrc.js"
36
36
  },
37
37
  "dependencies": {
38
- "@gmod/bam": "^1.1.12",
38
+ "@gmod/bam": "^1.1.14",
39
39
  "@gmod/cram": "^1.6.1",
40
40
  "@material-ui/icons": "^4.9.1",
41
41
  "abortable-promise-cache": "^1.5.0",
@@ -44,7 +44,7 @@
44
44
  "fast-deep-equal": "^3.1.3",
45
45
  "generic-filehandle": "^2.2.2",
46
46
  "json-stable-stringify": "^1.0.1",
47
- "react-d3-axis": "^0.1.2"
47
+ "react-d3-axis-mod": "^0.1.3"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@jbrowse/core": "^1.0.0",
@@ -61,5 +61,5 @@
61
61
  "publishConfig": {
62
62
  "access": "public"
63
63
  },
64
- "gitHead": "ab41f017840ffef09f5d60b008281cedaa5abe26"
64
+ "gitHead": "9fcca7793af0df2d6e527ddaace2b368c8ed2879"
65
65
  }
@@ -136,13 +136,17 @@ function SupplementaryAlignments(props: { tag: string; model: any }) {
136
136
  <Link
137
137
  onClick={() => {
138
138
  const { view } = model
139
- if (view) {
140
- view.navToLocString(locString)
141
- } else {
142
- session.notify(
143
- 'No view associated with this feature detail panel anymore',
144
- 'warning',
145
- )
139
+ try {
140
+ if (view) {
141
+ view.navToLocString(locString)
142
+ } else {
143
+ throw new Error(
144
+ 'No view associated with this view anymore',
145
+ )
146
+ }
147
+ } catch (e) {
148
+ console.error(e)
149
+ session.notify(`${e}`)
146
150
  }
147
151
  }}
148
152
  href="#"
@@ -164,13 +168,18 @@ function PairLink({ locString, model }: { locString: string; model: any }) {
164
168
  <Link
165
169
  onClick={() => {
166
170
  const { view } = model
167
- if (view) {
168
- view.navToLocString(locString)
169
- } else {
170
- session.notify(
171
- 'No view associated with this feature detail panel anymore',
172
- 'warning',
173
- )
171
+ try {
172
+ if (view) {
173
+ view.navToLocString(locString)
174
+ } else {
175
+ session.notify(
176
+ 'No view associated with this feature detail panel anymore',
177
+ 'warning',
178
+ )
179
+ }
180
+ } catch (e) {
181
+ console.error(e)
182
+ session.notify(`${e}`)
174
183
  }
175
184
  }}
176
185
  href="#"
@@ -15,6 +15,7 @@ import {
15
15
  import BamAdapter from './BamAdapter'
16
16
 
17
17
  export default class BamSlightlyLazyFeature implements Feature {
18
+ private cachedMD = ''
18
19
  constructor(
19
20
  private record: BamRecord,
20
21
  private adapter: BamAdapter,
@@ -70,10 +71,13 @@ export default class BamSlightlyLazyFeature implements Feature {
70
71
  }
71
72
 
72
73
  _get_MD() {
73
- const md = this.record.get('MD') as string | undefined
74
- const seq = this.get('seq') as string
75
- if (!md && seq && this.ref) {
76
- return generateMD(this.ref, this.record.getReadBases(), this.get('CIGAR'))
74
+ const md = this.record.get('MD') || this.cachedMD
75
+ if (!md) {
76
+ const seq = this.get('seq')
77
+ if (seq && this.ref) {
78
+ this.cachedMD = generateMD(this.ref, this.get('seq'), this.get('CIGAR'))
79
+ return this.cachedMD
80
+ }
77
81
  }
78
82
  return md
79
83
  }
@@ -2,47 +2,55 @@ import React from 'react'
2
2
  import { observer } from 'mobx-react'
3
3
  import { getConf } from '@jbrowse/core/configuration'
4
4
  import { ResizeHandle } from '@jbrowse/core/ui'
5
+ import { makeStyles } from '@material-ui/core'
5
6
  import { AlignmentsDisplayModel } from '../models/model'
6
7
 
8
+ const useStyles = makeStyles(() => ({
9
+ resizeHandle: {
10
+ height: 2,
11
+ position: 'absolute',
12
+ zIndex: 2,
13
+ },
14
+ }))
15
+
7
16
  function AlignmentsDisplay({ model }: { model: AlignmentsDisplayModel }) {
8
17
  const { PileupDisplay, SNPCoverageDisplay, showPileup, showCoverage } = model
18
+ const classes = useStyles()
19
+ const top = SNPCoverageDisplay.height
9
20
  return (
10
21
  <div
11
22
  data-testid={`display-${getConf(model, 'displayId')}`}
12
23
  style={{ position: 'relative' }}
13
24
  >
14
- <div data-testid="Blockset-snpcoverage">
15
- {showCoverage ? (
16
- <SNPCoverageDisplay.RenderingComponent model={SNPCoverageDisplay} />
17
- ) : null}
18
- </div>
19
- <ResizeHandle
20
- onDrag={delta => {
21
- if (SNPCoverageDisplay) {
22
- SNPCoverageDisplay.setHeight(SNPCoverageDisplay.height + delta)
23
- return delta
24
- }
25
- return 0
26
- }}
27
- style={{
28
- position: 'absolute',
29
- top: showCoverage ? SNPCoverageDisplay.height + 2 : 0,
30
- height: 3,
31
- }}
32
- />
25
+ {showCoverage ? (
26
+ <>
27
+ <div data-testid="Blockset-snpcoverage">
28
+ <SNPCoverageDisplay.RenderingComponent model={SNPCoverageDisplay} />
29
+ </div>
30
+ <ResizeHandle
31
+ onDrag={delta => {
32
+ SNPCoverageDisplay.setHeight(SNPCoverageDisplay.height + delta)
33
+ return delta
34
+ }}
35
+ className={classes.resizeHandle}
36
+ style={{
37
+ top,
38
+ }}
39
+ />
40
+ </>
41
+ ) : null}
33
42
 
34
- <div
35
- data-testid="Blockset-pileup"
36
- style={{
37
- position: 'absolute',
38
- top: showCoverage ? SNPCoverageDisplay.height + 5 : 0,
39
- height: 3,
40
- }}
41
- >
42
- {showPileup ? (
43
+ {showPileup ? (
44
+ <div
45
+ data-testid="Blockset-pileup"
46
+ style={{
47
+ position: 'absolute',
48
+ top: showCoverage ? SNPCoverageDisplay.height : 0,
49
+ }}
50
+ >
43
51
  <PileupDisplay.RenderingComponent model={PileupDisplay} />
44
- ) : null}
45
- </div>
52
+ </div>
53
+ ) : null}
46
54
  </div>
47
55
  )
48
56
  }
@@ -1,13 +1,16 @@
1
1
  import React from 'react'
2
- import { ConfigurationReference, getConf } from '@jbrowse/core/configuration'
3
- import { AnyConfigurationModel } from '@jbrowse/core/configuration/configurationSchema'
2
+ import {
3
+ ConfigurationReference,
4
+ AnyConfigurationModel,
5
+ getConf,
6
+ } from '@jbrowse/core/configuration'
4
7
  import { BaseDisplay } from '@jbrowse/core/pluggableElementTypes/models'
5
8
  import PluginManager from '@jbrowse/core/PluginManager'
6
9
  import { MenuItem } from '@jbrowse/core/ui'
7
- import deepEqual from 'fast-deep-equal'
8
10
  import { autorun, when } from 'mobx'
9
11
  import { addDisposer, getSnapshot, Instance, types } from 'mobx-state-tree'
10
12
  import { getContainingTrack } from '@jbrowse/core/util'
13
+ import deepEqual from 'fast-deep-equal'
11
14
  import { AlignmentsConfigModel } from './configSchema'
12
15
 
13
16
  const minDisplayHeight = 20
@@ -460,7 +460,7 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
460
460
  {
461
461
  label: 'Sort by tag...',
462
462
  onClick: () => {
463
- getSession(self).queueDialog((doneCallback: Function) => [
463
+ getSession(self).queueDialog(doneCallback => [
464
464
  SortByTagDlg,
465
465
  { model: self, handleClose: doneCallback },
466
466
  ])
@@ -509,7 +509,7 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
509
509
  {
510
510
  label: 'Modifications or methylation',
511
511
  onClick: () => {
512
- getSession(self).queueDialog((doneCallback: Function) => [
512
+ getSession(self).queueDialog(doneCallback => [
513
513
  ModificationsDlg,
514
514
  { model: self, handleClose: doneCallback },
515
515
  ])
@@ -530,7 +530,7 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
530
530
  {
531
531
  label: 'Color by tag...',
532
532
  onClick: () => {
533
- getSession(self).queueDialog((doneCallback: Function) => [
533
+ getSession(self).queueDialog(doneCallback => [
534
534
  ColorByTagDlg,
535
535
  { model: self, handleClose: doneCallback },
536
536
  ])
@@ -542,7 +542,7 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
542
542
  label: 'Filter by',
543
543
  icon: FilterListIcon,
544
544
  onClick: () => {
545
- getSession(self).queueDialog((doneCallback: Function) => [
545
+ getSession(self).queueDialog(doneCallback => [
546
546
  FilterByTagDlg,
547
547
  { model: self, handleClose: doneCallback },
548
548
  ])
@@ -551,7 +551,7 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
551
551
  {
552
552
  label: 'Set feature height',
553
553
  onClick: () => {
554
- getSession(self).queueDialog((doneCallback: Function) => [
554
+ getSession(self).queueDialog(doneCallback => [
555
555
  SetFeatureHeightDlg,
556
556
  { model: self, handleClose: doneCallback },
557
557
  ])
@@ -560,7 +560,7 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
560
560
  {
561
561
  label: 'Set max height',
562
562
  onClick: () => {
563
- getSession(self).queueDialog((doneCallback: Function) => [
563
+ getSession(self).queueDialog(doneCallback => [
564
564
  SetMaxHeightDlg,
565
565
  { model: self, handleClose: doneCallback },
566
566
  ])
@@ -27,11 +27,11 @@ const TooltipContents = React.forwardRef(
27
27
  const start = feature.get('start')
28
28
  const end = feature.get('end')
29
29
  const name = feature.get('refName')
30
+ const info = feature.get('snpinfo') as SNPInfo
30
31
  const loc = [name, start === end ? en(start) : `${en(start)}..${en(end)}`]
31
32
  .filter(f => !!f)
32
33
  .join(':')
33
34
 
34
- const info = feature.get('snpinfo') as SNPInfo
35
35
  const total = info?.total
36
36
 
37
37
  return (
@@ -64,7 +64,9 @@ const TooltipContents = React.forwardRef(
64
64
  <td>
65
65
  {base === 'total' || base === 'skip'
66
66
  ? '---'
67
- : `${Math.floor((score.total / total) * 100)}%`}
67
+ : `${Math.floor(
68
+ (score.total / (total || score.total || 1)) * 100,
69
+ )}%`}
68
70
  </td>
69
71
  <td>
70
72
  {strands['-1'] ? `${strands['-1']}(-)` : ''}
@@ -90,7 +92,7 @@ const SNPCoverageTooltip = observer(
90
92
  height: number
91
93
  offsetMouseCoord: Coord
92
94
  clientMouseCoord: Coord
93
- clientRect?: ClientRect
95
+ clientRect?: DOMRect
94
96
  }) => {
95
97
  const { model } = props
96
98
  const { featureUnderMouse: feat } = model
@@ -40,11 +40,10 @@ export default function SNPCoverageConfigFactory(pluginManager: PluginManager) {
40
40
  defaultValue: false,
41
41
  },
42
42
 
43
- headroom: {
44
- type: 'number',
45
- description:
46
- 'round the upper value of the domain scale to the nearest N',
47
- defaultValue: 0,
43
+ multiTicks: {
44
+ type: 'boolean',
45
+ description: 'Display multiple values for the ticks',
46
+ defaultValue: false,
48
47
  },
49
48
 
50
49
  renderers: ConfigurationSchema('RenderersConfiguration', {
@@ -345,7 +345,8 @@ export default class PileupRenderer extends BoxRendererType {
345
345
 
346
346
  // probIndex applies across multiple modifications e.g.
347
347
  let probIndex = 0
348
- modifications.forEach(({ type, positions }) => {
348
+ for (let i = 0; i < modifications.length; i++) {
349
+ const { type, positions } = modifications[i]
349
350
  const col = modificationTagMap[type] || 'black'
350
351
  const base = Color(col)
351
352
  for (const readPos of getNextRefPos(cigarOps, positions)) {
@@ -367,7 +368,7 @@ export default class PileupRenderer extends BoxRendererType {
367
368
  }
368
369
  probIndex++
369
370
  }
370
- })
371
+ }
371
372
  }
372
373
 
373
374
  // Color by methylation is slightly modified version of color by
@@ -399,7 +400,9 @@ export default class PileupRenderer extends BoxRendererType {
399
400
  const { start: rstart, end: rend } = region
400
401
 
401
402
  const methBins = new Array(rend - rstart).fill(0)
402
- getModificationPositions(mm, seq, strand).forEach(({ type, positions }) => {
403
+ const modifications = getModificationPositions(mm, seq, strand)
404
+ for (let i = 0; i < modifications.length; i++) {
405
+ const { type, positions } = modifications[i]
403
406
  if (type === 'm' && positions) {
404
407
  for (const pos of getNextRefPos(cigarOps, positions)) {
405
408
  const epos = pos + fstart - rstart
@@ -408,7 +411,7 @@ export default class PileupRenderer extends BoxRendererType {
408
411
  }
409
412
  }
410
413
  }
411
- })
414
+ }
412
415
 
413
416
  for (let j = fstart; j < fend; j++) {
414
417
  const i = j - rstart
@@ -650,6 +653,12 @@ export default class PileupRenderer extends BoxRendererType {
650
653
  return color
651
654
  }
652
655
 
656
+ // extraHorizontallyFlippedOffset is used to draw interbase items, which
657
+ // are located to the left when forward and right when reversed
658
+ const extraHorizontallyFlippedOffset = region.reversed
659
+ ? 1 / bpPerPx + 1
660
+ : -1
661
+
653
662
  // two pass rendering: first pass, draw all the mismatches except wide
654
663
  // insertion markers
655
664
  for (let i = 0; i < mismatches.length; i += 1) {
@@ -694,26 +703,29 @@ export default class PileupRenderer extends BoxRendererType {
694
703
  }
695
704
  } else if (mismatch.type === 'insertion' && drawIndels) {
696
705
  ctx.fillStyle = 'purple'
697
- const pos = leftPx - 1
706
+ const pos = leftPx + extraHorizontallyFlippedOffset
698
707
  const len = +mismatch.base || mismatch.length
708
+ const insW = Math.max(minWidth, Math.min(1.2, 1 / bpPerPx))
699
709
  if (len < 10) {
700
- ctx.fillRect(pos, topPx, w, heightPx)
710
+ ctx.fillRect(pos, topPx, insW, heightPx)
701
711
  if (1 / bpPerPx >= charWidth) {
702
- ctx.fillRect(pos - w, topPx, w * 3, 1)
703
- ctx.fillRect(pos - w, topPx + heightPx - 1, w * 3, 1)
712
+ ctx.fillRect(pos - insW, topPx, insW * 3, 1)
713
+ ctx.fillRect(pos - insW, topPx + heightPx - 1, insW * 3, 1)
704
714
  }
705
715
  if (1 / bpPerPx >= charWidth && heightPx >= heightLim) {
706
- ctx.fillText(`(${mismatch.base})`, leftPx + 2, topPx + heightPx)
716
+ ctx.fillText(`(${mismatch.base})`, pos + 3, topPx + heightPx)
707
717
  }
708
718
  }
709
719
  } else if (mismatch.type === 'hardclip' || mismatch.type === 'softclip') {
710
720
  ctx.fillStyle = mismatch.type === 'hardclip' ? 'red' : 'blue'
711
- const pos = leftPx - 1
712
- ctx.fillRect(pos, topPx + 1, w, heightPx - 2)
713
- ctx.fillRect(pos - w, topPx, w * 3, 1)
714
- ctx.fillRect(pos - w, topPx + heightPx - 1, w * 3, 1)
721
+ const pos = leftPx + extraHorizontallyFlippedOffset
722
+ ctx.fillRect(pos, topPx, w, heightPx)
723
+ if (1 / bpPerPx >= charWidth) {
724
+ ctx.fillRect(pos - w, topPx, w * 3, 1)
725
+ ctx.fillRect(pos - w, topPx + heightPx - 1, w * 3, 1)
726
+ }
715
727
  if (widthPx >= charWidth && heightPx >= heightLim) {
716
- ctx.fillText(`(${mismatch.base})`, leftPx + 2, topPx + heightPx)
728
+ ctx.fillText(`(${mismatch.base})`, pos + 3, topPx + heightPx)
717
729
  }
718
730
  } else if (mismatch.type === 'skip') {
719
731
  // fix to avoid bad rendering note that this was also related to chrome
@@ -797,10 +809,9 @@ export default class PileupRenderer extends BoxRendererType {
797
809
  .filter(mismatch => mismatch.type === 'softclip')
798
810
  .forEach(mismatch => {
799
811
  const softClipLength = mismatch.cliplen || 0
812
+ const s = feature.get('start')
800
813
  const softClipStart =
801
- mismatch.start === 0
802
- ? feature.get('start') - softClipLength
803
- : feature.get('start') + mismatch.start
814
+ mismatch.start === 0 ? s - softClipLength : s + mismatch.start
804
815
 
805
816
  for (let k = 0; k < softClipLength; k += 1) {
806
817
  const base = seq.charAt(k + mismatch.start)
@@ -136,9 +136,11 @@ function PileupRendering(props: {
136
136
  }
137
137
  let offsetX = 0
138
138
  let offsetY = 0
139
- if (highlightOverlayCanvas.current) {
140
- offsetX = highlightOverlayCanvas.current.getBoundingClientRect().left
141
- offsetY = highlightOverlayCanvas.current.getBoundingClientRect().top
139
+ const canvas = highlightOverlayCanvas.current
140
+ if (canvas) {
141
+ const { left, top } = canvas.getBoundingClientRect()
142
+ offsetX = left
143
+ offsetY = top
142
144
  }
143
145
  offsetX = event.clientX - offsetX
144
146
  offsetY = event.clientY - offsetY
@@ -88,20 +88,18 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
88
88
  )
89
89
 
90
90
  bins.forEach((bin, index) => {
91
- if (bin.total) {
92
- observer.next(
93
- new SimpleFeature({
94
- id: `${this.id}-${region.start}-${index}`,
95
- data: {
96
- score: bin.total,
97
- snpinfo: bin,
98
- start: region.start + index,
99
- end: region.start + index + 1,
100
- refName: region.refName,
101
- },
102
- }),
103
- )
104
- }
91
+ observer.next(
92
+ new SimpleFeature({
93
+ id: `${this.id}-${region.start}-${index}`,
94
+ data: {
95
+ score: bin.total,
96
+ snpinfo: bin,
97
+ start: region.start + index,
98
+ end: region.start + index + 1,
99
+ refName: region.refName,
100
+ },
101
+ }),
102
+ )
105
103
  })
106
104
 
107
105
  // make fake features from the coverage
@@ -198,7 +196,7 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
198
196
  const fstrand = feature.get('strand')
199
197
  const cigarOps = parseCigar(cigar)
200
198
 
201
- for (let j = fstart; j < fend; j++) {
199
+ for (let j = fstart; j < fend + 1; j++) {
202
200
  const i = j - region.start
203
201
  if (i >= 0 && i < binMax) {
204
202
  const bin = bins[i] || {
@@ -209,8 +207,10 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
209
207
  noncov: {} as BinType,
210
208
  ref: {} as BinType,
211
209
  }
212
- bin.total++
213
- inc(bin, fstrand, 'ref', 'ref')
210
+ if (j !== fend) {
211
+ bin.total++
212
+ inc(bin, fstrand, 'ref', 'ref')
213
+ }
214
214
  bins[i] = bin
215
215
  }
216
216
  }
@@ -243,7 +243,15 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
243
243
  epos < bins.length &&
244
244
  pos + fstart < fend
245
245
  ) {
246
- const bin = bins[epos]
246
+ const bin = bins[epos] || {
247
+ total: 0,
248
+ lowqual: {} as BinType,
249
+ cov: {} as BinType,
250
+ delskips: {} as BinType,
251
+ noncov: {} as BinType,
252
+ ref: {} as BinType,
253
+ }
254
+
247
255
  if (probabilities[probIndex] > 0.5) {
248
256
  inc(bin, fstrand, 'cov', mod)
249
257
  } else {
@@ -317,12 +325,8 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
317
325
  if (mismatches) {
318
326
  for (let i = 0; i < mismatches.length; i++) {
319
327
  const mismatch = mismatches[i]
320
- const mstart = fstart + mismatch.start
321
- for (
322
- let j = mstart;
323
- j < mstart + mismatchLen(mismatch);
324
- j++
325
- ) {
328
+ const ms = fstart + mismatch.start
329
+ for (let j = ms; j < ms + mismatchLen(mismatch); j++) {
326
330
  const epos = j - region.start
327
331
  if (epos >= 0 && epos < bins.length) {
328
332
  const bin = bins[epos]