@jbrowse/plugin-alignments 1.6.4 → 1.6.7
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.
- package/dist/AlignmentsFeatureDetail/index.d.ts +1 -1
- package/dist/BamAdapter/BamSlightlyLazyFeature.d.ts +3 -2
- package/dist/BamAdapter/configSchema.d.ts +1 -1
- package/dist/CramAdapter/configSchema.d.ts +1 -1
- package/dist/HtsgetBamAdapter/configSchema.d.ts +1 -1
- package/dist/LinearAlignmentsDisplay/models/configSchema.d.ts +1 -1
- package/dist/LinearAlignmentsDisplay/models/model.d.ts +1 -1
- package/dist/LinearPileupDisplay/configSchema.d.ts +1 -1
- package/dist/LinearSNPCoverageDisplay/components/Tooltip.d.ts +1 -1
- package/dist/LinearSNPCoverageDisplay/models/configSchema.d.ts +1 -1
- package/dist/PileupRenderer/configSchema.d.ts +1 -1
- package/dist/SNPCoverageAdapter/configSchema.d.ts +1 -1
- package/dist/SNPCoverageRenderer/SNPCoverageRenderer.d.ts +1 -1
- package/dist/SNPCoverageRenderer/configSchema.d.ts +1 -1
- package/dist/SNPCoverageRenderer/index.d.ts +1 -1
- package/dist/plugin-alignments.cjs.development.js +291 -223
- package/dist/plugin-alignments.cjs.development.js.map +1 -1
- package/dist/plugin-alignments.cjs.production.min.js +1 -1
- package/dist/plugin-alignments.cjs.production.min.js.map +1 -1
- package/dist/plugin-alignments.esm.js +291 -223
- package/dist/plugin-alignments.esm.js.map +1 -1
- package/package.json +6 -6
- package/src/AlignmentsFeatureDetail/AlignmentsFeatureDetail.tsx +23 -14
- package/src/BamAdapter/BamAdapter.ts +3 -4
- package/src/BamAdapter/BamSlightlyLazyFeature.ts +8 -4
- package/src/LinearAlignmentsDisplay/components/AlignmentsDisplay.tsx +38 -30
- package/src/LinearAlignmentsDisplay/models/model.tsx +10 -9
- package/src/LinearPileupDisplay/model.ts +6 -6
- package/src/LinearSNPCoverageDisplay/components/Tooltip.tsx +5 -3
- package/src/LinearSNPCoverageDisplay/models/configSchema.ts +4 -5
- package/src/PileupRenderer/PileupRenderer.tsx +39 -27
- package/src/PileupRenderer/components/PileupRendering.tsx +5 -3
- package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +188 -169
- package/src/SNPCoverageRenderer/SNPCoverageRenderer.ts +86 -56
- 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.
|
|
3
|
+
"version": "1.6.7",
|
|
4
4
|
"description": "JBrowse 2 alignments adapters, tracks, etc.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -35,16 +35,16 @@
|
|
|
35
35
|
"useSrc": "node ../../scripts/useSrc.js"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@gmod/bam": "^1.1.
|
|
39
|
-
"@gmod/cram": "^1.
|
|
38
|
+
"@gmod/bam": "^1.1.14",
|
|
39
|
+
"@gmod/cram": "^1.6.1",
|
|
40
40
|
"@material-ui/icons": "^4.9.1",
|
|
41
|
-
"abortable-promise-cache": "^1.
|
|
41
|
+
"abortable-promise-cache": "^1.5.0",
|
|
42
42
|
"color": "^3.1.2",
|
|
43
43
|
"copy-to-clipboard": "^3.3.1",
|
|
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.
|
|
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": "
|
|
64
|
+
"gitHead": "02012ec299c36647f755316571775d36b0fee5ec"
|
|
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
|
-
|
|
140
|
-
view
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
168
|
-
view
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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="#"
|
|
@@ -197,11 +197,10 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
|
|
|
197
197
|
|
|
198
198
|
async estimateRegionsStats(regions: Region[], opts?: BaseOptions) {
|
|
199
199
|
const { bam } = await this.configure()
|
|
200
|
-
// @ts-ignore
|
|
201
|
-
const index = bam.index
|
|
202
200
|
// this is a method to avoid calling on htsget adapters
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
// @ts-ignore
|
|
202
|
+
if (bam.index.filehandle !== '?') {
|
|
203
|
+
const bytes = await bytesForRegions(regions, bam)
|
|
205
204
|
const fetchSizeLimit = readConfObject(this.config, 'fetchSizeLimit')
|
|
206
205
|
return { bytes, fetchSizeLimit }
|
|
207
206
|
} else {
|
|
@@ -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')
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
45
|
-
|
|
52
|
+
</div>
|
|
53
|
+
) : null}
|
|
46
54
|
</div>
|
|
47
55
|
)
|
|
48
56
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
3
|
-
|
|
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
|
|
@@ -222,12 +225,10 @@ const stateModelFactory = (
|
|
|
222
225
|
<>
|
|
223
226
|
<g>{await self.SNPCoverageDisplay.renderSvg(opts)}</g>
|
|
224
227
|
<g transform={`translate(0 ${self.SNPCoverageDisplay.height})`}>
|
|
225
|
-
{
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
})
|
|
230
|
-
}
|
|
228
|
+
{await self.PileupDisplay.renderSvg({
|
|
229
|
+
...opts,
|
|
230
|
+
overrideHeight: pileupHeight,
|
|
231
|
+
})}
|
|
231
232
|
</g>
|
|
232
233
|
</>
|
|
233
234
|
)
|
|
@@ -460,7 +460,7 @@ const stateModelFactory = (configSchema: LinearPileupDisplayConfigModel) =>
|
|
|
460
460
|
{
|
|
461
461
|
label: 'Sort by tag...',
|
|
462
462
|
onClick: () => {
|
|
463
|
-
getSession(self).queueDialog(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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?:
|
|
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
|
-
|
|
44
|
-
type: '
|
|
45
|
-
description:
|
|
46
|
-
|
|
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
|
-
|
|
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)
|
|
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,42 +703,46 @@ 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
|
|
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,
|
|
710
|
+
ctx.fillRect(pos, topPx, insW, heightPx)
|
|
701
711
|
if (1 / bpPerPx >= charWidth) {
|
|
702
|
-
ctx.fillRect(pos -
|
|
703
|
-
ctx.fillRect(pos -
|
|
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})`,
|
|
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
|
|
712
|
-
ctx.fillRect(pos, topPx
|
|
713
|
-
|
|
714
|
-
|
|
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})`,
|
|
728
|
+
ctx.fillText(`(${mismatch.base})`, pos + 3, topPx + heightPx)
|
|
717
729
|
}
|
|
718
730
|
} else if (mismatch.type === 'skip') {
|
|
719
|
-
// fix to avoid bad rendering
|
|
720
|
-
//
|
|
721
|
-
// ref #1236
|
|
731
|
+
// fix to avoid bad rendering note that this was also related to chrome
|
|
732
|
+
// bug https://bugs.chromium.org/p/chromium/issues/detail?id=1131528
|
|
733
|
+
// also affected firefox ref #1236 #2750
|
|
722
734
|
if (leftPx + widthPx > 0) {
|
|
723
735
|
// make small exons more visible when zoomed far out
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
736
|
+
const adjustPx = widthPx - (bpPerPx > 10 ? 1.5 : 0)
|
|
737
|
+
ctx.clearRect(leftPx, topPx, adjustPx, heightPx)
|
|
738
|
+
ctx.fillStyle = '#333'
|
|
739
|
+
ctx.fillRect(
|
|
740
|
+
Math.max(0, leftPx),
|
|
741
|
+
topPx + heightPx / 2 - 1,
|
|
742
|
+
adjustPx + (leftPx < 0 ? leftPx : 0),
|
|
743
|
+
2,
|
|
729
744
|
)
|
|
730
745
|
}
|
|
731
|
-
ctx.fillStyle = '#333'
|
|
732
|
-
ctx.fillRect(leftPx, topPx + heightPx / 2, widthPx, 2)
|
|
733
746
|
}
|
|
734
747
|
}
|
|
735
748
|
|
|
@@ -796,10 +809,9 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
796
809
|
.filter(mismatch => mismatch.type === 'softclip')
|
|
797
810
|
.forEach(mismatch => {
|
|
798
811
|
const softClipLength = mismatch.cliplen || 0
|
|
812
|
+
const s = feature.get('start')
|
|
799
813
|
const softClipStart =
|
|
800
|
-
mismatch.start === 0
|
|
801
|
-
? feature.get('start') - softClipLength
|
|
802
|
-
: feature.get('start') + mismatch.start
|
|
814
|
+
mismatch.start === 0 ? s - softClipLength : s + mismatch.start
|
|
803
815
|
|
|
804
816
|
for (let k = 0; k < softClipLength; k += 1) {
|
|
805
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|