@jbrowse/plugin-alignments 1.6.5 → 1.6.8
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 +2 -10
- package/dist/BamAdapter/MismatchParser.d.ts +3 -5
- package/dist/BamAdapter/configSchema.d.ts +1 -1
- package/dist/CramAdapter/CramSlightlyLazyFeature.d.ts +1 -2
- 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/LinearSNPCoverageDisplay/models/model.d.ts +2 -2
- package/dist/PileupRenderer/PileupRenderer.d.ts +20 -6
- package/dist/PileupRenderer/configSchema.d.ts +1 -1
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +3 -11
- 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 +795 -682
- 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 +797 -684
- package/dist/plugin-alignments.esm.js.map +1 -1
- package/dist/util.d.ts +4 -0
- package/package.json +4 -4
- package/src/AlignmentsFeatureDetail/AlignmentsFeatureDetail.tsx +23 -14
- package/src/BamAdapter/BamAdapter.ts +10 -7
- package/src/BamAdapter/BamSlightlyLazyFeature.ts +11 -75
- package/src/BamAdapter/MismatchParser.test.ts +53 -297
- package/src/BamAdapter/MismatchParser.ts +54 -116
- package/src/BamAdapter/configSchema.ts +0 -4
- package/src/CramAdapter/CramSlightlyLazyFeature.ts +3 -10
- package/src/LinearAlignmentsDisplay/components/AlignmentsDisplay.tsx +38 -30
- package/src/LinearAlignmentsDisplay/models/model.tsx +10 -9
- package/src/LinearPileupDisplay/components/ColorByModifications.tsx +76 -80
- package/src/LinearPileupDisplay/components/ColorByTag.tsx +24 -23
- package/src/LinearPileupDisplay/components/FilterByTag.tsx +73 -68
- package/src/LinearPileupDisplay/components/SetFeatureHeight.tsx +28 -26
- package/src/LinearPileupDisplay/components/SetMaxHeight.tsx +24 -13
- package/src/LinearPileupDisplay/components/SortByTag.tsx +29 -21
- package/src/LinearPileupDisplay/model.ts +12 -6
- package/src/LinearSNPCoverageDisplay/components/Tooltip.tsx +5 -3
- package/src/LinearSNPCoverageDisplay/models/configSchema.ts +4 -5
- package/src/PileupRenderer/PileupRenderer.tsx +202 -70
- package/src/PileupRenderer/components/PileupRendering.tsx +5 -3
- package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +192 -237
- package/src/SNPCoverageRenderer/SNPCoverageRenderer.ts +91 -60
- package/src/SNPCoverageRenderer/configSchema.js +1 -1
- package/src/util.ts +25 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { toArray } from 'rxjs/operators'
|
|
1
|
+
import Color from 'color'
|
|
3
2
|
import BoxRendererType, {
|
|
4
3
|
RenderArgs,
|
|
5
4
|
RenderArgsSerialized,
|
|
@@ -10,14 +9,20 @@ import BoxRendererType, {
|
|
|
10
9
|
} from '@jbrowse/core/pluggableElementTypes/renderers/BoxRendererType'
|
|
11
10
|
import { Theme } from '@material-ui/core'
|
|
12
11
|
import { createJBrowseTheme } from '@jbrowse/core/ui'
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
import {
|
|
13
|
+
bpSpanPx,
|
|
14
|
+
iterMap,
|
|
15
|
+
measureText,
|
|
16
|
+
Region,
|
|
17
|
+
Feature,
|
|
18
|
+
} from '@jbrowse/core/util'
|
|
17
19
|
import { renderToAbstractCanvas } from '@jbrowse/core/util/offscreenCanvasUtils'
|
|
18
20
|
import { BaseLayout } from '@jbrowse/core/util/layouts/BaseLayout'
|
|
19
21
|
import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache'
|
|
20
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
readConfObject,
|
|
24
|
+
AnyConfigurationModel,
|
|
25
|
+
} from '@jbrowse/core/configuration'
|
|
21
26
|
|
|
22
27
|
// locals
|
|
23
28
|
import {
|
|
@@ -27,7 +32,12 @@ import {
|
|
|
27
32
|
getNextRefPos,
|
|
28
33
|
} from '../BamAdapter/MismatchParser'
|
|
29
34
|
import { sortFeature } from './sortUtil'
|
|
30
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
getTagAlt,
|
|
37
|
+
orientationTypes,
|
|
38
|
+
fetchSequence,
|
|
39
|
+
shouldFetchReferenceSequence,
|
|
40
|
+
} from '../util'
|
|
31
41
|
import {
|
|
32
42
|
PileupLayoutSession,
|
|
33
43
|
PileupLayoutSessionProps,
|
|
@@ -44,6 +54,15 @@ function getColorBaseMap(theme: Theme) {
|
|
|
44
54
|
}
|
|
45
55
|
}
|
|
46
56
|
|
|
57
|
+
function getContrastBaseMap(theme: Theme) {
|
|
58
|
+
return Object.fromEntries(
|
|
59
|
+
Object.entries(getColorBaseMap(theme)).map(([key, value]) => [
|
|
60
|
+
key,
|
|
61
|
+
theme.palette.getContrastText(value),
|
|
62
|
+
]),
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
47
66
|
export interface RenderArgsDeserialized extends BoxRenderArgsDeserialized {
|
|
48
67
|
colorBy?: { type: string; tag?: string }
|
|
49
68
|
colorTagMap?: Record<string, string>
|
|
@@ -263,6 +282,64 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
263
282
|
return strand === 1 ? 'color_fwd_strand' : 'color_rev_strand'
|
|
264
283
|
}
|
|
265
284
|
|
|
285
|
+
colorByPerBaseLettering(
|
|
286
|
+
ctx: CanvasRenderingContext2D,
|
|
287
|
+
feat: LayoutFeature,
|
|
288
|
+
_config: AnyConfigurationModel,
|
|
289
|
+
region: Region,
|
|
290
|
+
bpPerPx: number,
|
|
291
|
+
props: {
|
|
292
|
+
colorForBase: Record<string, string>
|
|
293
|
+
contrastForBase: Record<string, string>
|
|
294
|
+
charWidth: number
|
|
295
|
+
charHeight: number
|
|
296
|
+
},
|
|
297
|
+
) {
|
|
298
|
+
const { colorForBase, contrastForBase, charWidth, charHeight } = props
|
|
299
|
+
const heightLim = charHeight - 2
|
|
300
|
+
const { feature, topPx, heightPx } = feat
|
|
301
|
+
const seq = feature.get('seq') as string
|
|
302
|
+
const cigarOps = parseCigar(feature.get('CIGAR'))
|
|
303
|
+
const widthPx = 1 / bpPerPx
|
|
304
|
+
const start = feature.get('start')
|
|
305
|
+
let soffset = 0 // sequence offset
|
|
306
|
+
let roffset = 0 // reference offset
|
|
307
|
+
|
|
308
|
+
for (let i = 0; i < cigarOps.length; i += 2) {
|
|
309
|
+
const len = +cigarOps[i]
|
|
310
|
+
const op = cigarOps[i + 1]
|
|
311
|
+
if (op === 'S' || op === 'I') {
|
|
312
|
+
soffset += len
|
|
313
|
+
} else if (op === 'D' || op === 'N') {
|
|
314
|
+
roffset += len
|
|
315
|
+
} else if (op === 'M' || op === 'X' || op === '=') {
|
|
316
|
+
for (let m = 0; m < len; m++) {
|
|
317
|
+
const letter = seq[soffset + m]
|
|
318
|
+
ctx.fillStyle = colorForBase[letter]
|
|
319
|
+
const [leftPx] = bpSpanPx(
|
|
320
|
+
start + roffset + m,
|
|
321
|
+
start + roffset + m + 1,
|
|
322
|
+
region,
|
|
323
|
+
bpPerPx,
|
|
324
|
+
)
|
|
325
|
+
ctx.fillRect(leftPx, topPx, widthPx + 0.5, heightPx)
|
|
326
|
+
|
|
327
|
+
if (widthPx >= charWidth && heightPx >= heightLim) {
|
|
328
|
+
// normal SNP coloring
|
|
329
|
+
ctx.fillStyle = contrastForBase[letter]
|
|
330
|
+
|
|
331
|
+
ctx.fillText(
|
|
332
|
+
letter,
|
|
333
|
+
leftPx + (widthPx - charWidth) / 2 + 1,
|
|
334
|
+
topPx + heightPx,
|
|
335
|
+
)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
soffset += len
|
|
339
|
+
roffset += len
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
266
343
|
colorByPerBaseQuality(
|
|
267
344
|
ctx: CanvasRenderingContext2D,
|
|
268
345
|
feat: LayoutFeature,
|
|
@@ -276,27 +353,30 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
276
353
|
const cigarOps = parseCigar(feature.get('CIGAR'))
|
|
277
354
|
const width = 1 / bpPerPx
|
|
278
355
|
const start = feature.get('start')
|
|
356
|
+
let soffset = 0 // sequence offset
|
|
357
|
+
let roffset = 0 // reference offset
|
|
279
358
|
|
|
280
|
-
for (let i = 0
|
|
359
|
+
for (let i = 0; i < cigarOps.length; i += 2) {
|
|
281
360
|
const len = +cigarOps[i]
|
|
282
361
|
const op = cigarOps[i + 1]
|
|
283
362
|
if (op === 'S' || op === 'I') {
|
|
284
|
-
|
|
363
|
+
soffset += len
|
|
285
364
|
} else if (op === 'D' || op === 'N') {
|
|
286
|
-
|
|
365
|
+
roffset += len
|
|
287
366
|
} else if (op === 'M' || op === 'X' || op === '=') {
|
|
288
367
|
for (let m = 0; m < len; m++) {
|
|
289
|
-
const score = scores[
|
|
368
|
+
const score = scores[soffset + m]
|
|
290
369
|
ctx.fillStyle = `hsl(${score === 255 ? 150 : score * 1.5},55%,50%)`
|
|
291
370
|
const [leftPx] = bpSpanPx(
|
|
292
|
-
start +
|
|
293
|
-
start +
|
|
371
|
+
start + roffset + m,
|
|
372
|
+
start + roffset + m + 1,
|
|
294
373
|
region,
|
|
295
374
|
bpPerPx,
|
|
296
375
|
)
|
|
297
376
|
ctx.fillRect(leftPx, topPx, width + 0.5, heightPx)
|
|
298
377
|
}
|
|
299
|
-
|
|
378
|
+
soffset += len
|
|
379
|
+
roffset += len
|
|
300
380
|
}
|
|
301
381
|
}
|
|
302
382
|
}
|
|
@@ -345,7 +425,8 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
345
425
|
|
|
346
426
|
// probIndex applies across multiple modifications e.g.
|
|
347
427
|
let probIndex = 0
|
|
348
|
-
|
|
428
|
+
for (let i = 0; i < modifications.length; i++) {
|
|
429
|
+
const { type, positions } = modifications[i]
|
|
349
430
|
const col = modificationTagMap[type] || 'black'
|
|
350
431
|
const base = Color(col)
|
|
351
432
|
for (const readPos of getNextRefPos(cigarOps, positions)) {
|
|
@@ -367,12 +448,12 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
367
448
|
}
|
|
368
449
|
probIndex++
|
|
369
450
|
}
|
|
370
|
-
}
|
|
451
|
+
}
|
|
371
452
|
}
|
|
372
453
|
|
|
373
454
|
// Color by methylation is slightly modified version of color by
|
|
374
|
-
// modifications
|
|
375
|
-
//
|
|
455
|
+
// modifications that focuses on CpG sites, with non-methylated CpG colored
|
|
456
|
+
// blue
|
|
376
457
|
colorByMethylation(
|
|
377
458
|
ctx: CanvasRenderingContext2D,
|
|
378
459
|
layoutFeature: LayoutFeature,
|
|
@@ -399,7 +480,9 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
399
480
|
const { start: rstart, end: rend } = region
|
|
400
481
|
|
|
401
482
|
const methBins = new Array(rend - rstart).fill(0)
|
|
402
|
-
getModificationPositions(mm, seq, strand)
|
|
483
|
+
const modifications = getModificationPositions(mm, seq, strand)
|
|
484
|
+
for (let i = 0; i < modifications.length; i++) {
|
|
485
|
+
const { type, positions } = modifications[i]
|
|
403
486
|
if (type === 'm' && positions) {
|
|
404
487
|
for (const pos of getNextRefPos(cigarOps, positions)) {
|
|
405
488
|
const epos = pos + fstart - rstart
|
|
@@ -408,7 +491,7 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
408
491
|
}
|
|
409
492
|
}
|
|
410
493
|
}
|
|
411
|
-
}
|
|
494
|
+
}
|
|
412
495
|
|
|
413
496
|
for (let j = fstart; j < fend; j++) {
|
|
414
497
|
const i = j - rstart
|
|
@@ -500,6 +583,10 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
500
583
|
ctx: CanvasRenderingContext2D,
|
|
501
584
|
feat: LayoutFeature,
|
|
502
585
|
props: RenderArgsDeserializedWithFeaturesAndLayout & {
|
|
586
|
+
colorForBase: Record<string, string>
|
|
587
|
+
contrastForBase: Record<string, string>
|
|
588
|
+
charWidth: number
|
|
589
|
+
charHeight: number
|
|
503
590
|
defaultColor: boolean
|
|
504
591
|
},
|
|
505
592
|
) {
|
|
@@ -510,6 +597,10 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
510
597
|
regions,
|
|
511
598
|
colorBy,
|
|
512
599
|
colorTagMap = {},
|
|
600
|
+
colorForBase,
|
|
601
|
+
contrastForBase,
|
|
602
|
+
charWidth,
|
|
603
|
+
charHeight,
|
|
513
604
|
} = props
|
|
514
605
|
const { tag = '', type: colorType = '' } = colorBy || {}
|
|
515
606
|
const { feature } = feat
|
|
@@ -574,6 +665,19 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
574
665
|
case 'insertSizeAndPairOrientation':
|
|
575
666
|
break
|
|
576
667
|
|
|
668
|
+
case 'modifications':
|
|
669
|
+
case 'methylation':
|
|
670
|
+
// this coloring is similar to igv.js, and is helpful to color negative
|
|
671
|
+
// strand reads differently because their c-g will be flipped (e.g. g-c
|
|
672
|
+
// read right to left)
|
|
673
|
+
const flags = feature.get('flags')
|
|
674
|
+
if (flags & 16) {
|
|
675
|
+
ctx.fillStyle = '#c8dcc8'
|
|
676
|
+
} else {
|
|
677
|
+
ctx.fillStyle = '#c8c8c8'
|
|
678
|
+
}
|
|
679
|
+
break
|
|
680
|
+
|
|
577
681
|
case 'normal':
|
|
578
682
|
default:
|
|
579
683
|
if (defaultColor) {
|
|
@@ -594,6 +698,15 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
594
698
|
this.colorByPerBaseQuality(ctx, feat, config, region, bpPerPx)
|
|
595
699
|
break
|
|
596
700
|
|
|
701
|
+
case 'perBaseLettering':
|
|
702
|
+
this.colorByPerBaseLettering(ctx, feat, config, region, bpPerPx, {
|
|
703
|
+
colorForBase,
|
|
704
|
+
contrastForBase,
|
|
705
|
+
charWidth,
|
|
706
|
+
charHeight,
|
|
707
|
+
})
|
|
708
|
+
break
|
|
709
|
+
|
|
597
710
|
case 'modifications':
|
|
598
711
|
this.colorByModifications(ctx, feat, config, region, bpPerPx, props)
|
|
599
712
|
break
|
|
@@ -608,9 +721,9 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
608
721
|
ctx: CanvasRenderingContext2D,
|
|
609
722
|
feat: LayoutFeature,
|
|
610
723
|
props: RenderArgsDeserializedWithFeaturesAndLayout,
|
|
611
|
-
theme: Theme,
|
|
612
|
-
colorForBase: { [key: string]: string },
|
|
613
724
|
opts: {
|
|
725
|
+
colorForBase: { [key: string]: string }
|
|
726
|
+
contrastForBase: { [key: string]: string }
|
|
614
727
|
mismatchAlpha?: boolean
|
|
615
728
|
drawSNPs?: boolean
|
|
616
729
|
drawIndels?: boolean
|
|
@@ -621,13 +734,15 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
621
734
|
},
|
|
622
735
|
) {
|
|
623
736
|
const {
|
|
624
|
-
minSubfeatureWidth
|
|
737
|
+
minSubfeatureWidth,
|
|
625
738
|
largeInsertionIndicatorScale,
|
|
626
739
|
mismatchAlpha,
|
|
627
740
|
drawSNPs = true,
|
|
628
741
|
drawIndels = true,
|
|
629
742
|
charWidth,
|
|
630
743
|
charHeight,
|
|
744
|
+
colorForBase,
|
|
745
|
+
contrastForBase,
|
|
631
746
|
} = opts
|
|
632
747
|
const { bpPerPx, regions } = props
|
|
633
748
|
const { heightPx, topPx, feature } = feat
|
|
@@ -635,7 +750,7 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
635
750
|
const start = feature.get('start')
|
|
636
751
|
|
|
637
752
|
const pxPerBp = Math.min(1 / bpPerPx, 2)
|
|
638
|
-
const w = Math.max(
|
|
753
|
+
const w = Math.max(minSubfeatureWidth, pxPerBp)
|
|
639
754
|
const mismatches: Mismatch[] = feature.get('mismatches')
|
|
640
755
|
const heightLim = charHeight - 2
|
|
641
756
|
|
|
@@ -650,6 +765,12 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
650
765
|
return color
|
|
651
766
|
}
|
|
652
767
|
|
|
768
|
+
// extraHorizontallyFlippedOffset is used to draw interbase items, which
|
|
769
|
+
// are located to the left when forward and right when reversed
|
|
770
|
+
const extraHorizontallyFlippedOffset = region.reversed
|
|
771
|
+
? 1 / bpPerPx + 1
|
|
772
|
+
: -1
|
|
773
|
+
|
|
653
774
|
// two pass rendering: first pass, draw all the mismatches except wide
|
|
654
775
|
// insertion markers
|
|
655
776
|
for (let i = 0; i < mismatches.length; i += 1) {
|
|
@@ -658,7 +779,7 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
658
779
|
const mlen = mismatch.length
|
|
659
780
|
const mbase = mismatch.base
|
|
660
781
|
const [leftPx, rightPx] = bpSpanPx(mstart, mstart + mlen, region, bpPerPx)
|
|
661
|
-
const widthPx = Math.max(
|
|
782
|
+
const widthPx = Math.max(minSubfeatureWidth, Math.abs(leftPx - rightPx))
|
|
662
783
|
if (mismatch.type === 'mismatch' && drawSNPs) {
|
|
663
784
|
const baseColor = colorForBase[mismatch.base] || '#888'
|
|
664
785
|
|
|
@@ -669,7 +790,7 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
669
790
|
if (widthPx >= charWidth && heightPx >= heightLim) {
|
|
670
791
|
// normal SNP coloring
|
|
671
792
|
ctx.fillStyle = getAlphaColor(
|
|
672
|
-
|
|
793
|
+
contrastForBase[mismatch.base],
|
|
673
794
|
mismatch,
|
|
674
795
|
)
|
|
675
796
|
ctx.fillText(
|
|
@@ -683,37 +804,36 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
683
804
|
ctx.fillStyle = baseColor
|
|
684
805
|
ctx.fillRect(leftPx, topPx, widthPx, heightPx)
|
|
685
806
|
const txt = `${mismatch.length}`
|
|
686
|
-
const
|
|
687
|
-
if (widthPx >=
|
|
688
|
-
ctx.fillStyle =
|
|
807
|
+
const rwidth = measureText(txt, 10)
|
|
808
|
+
if (widthPx >= rwidth && heightPx >= heightLim) {
|
|
809
|
+
ctx.fillStyle = contrastForBase.deletion
|
|
689
810
|
ctx.fillText(
|
|
690
811
|
txt,
|
|
691
|
-
leftPx +
|
|
812
|
+
(leftPx + rightPx) / 2 - rwidth / 2,
|
|
692
813
|
topPx + heightPx,
|
|
693
814
|
)
|
|
694
815
|
}
|
|
695
816
|
} else if (mismatch.type === 'insertion' && drawIndels) {
|
|
696
817
|
ctx.fillStyle = 'purple'
|
|
697
|
-
const pos = leftPx
|
|
818
|
+
const pos = leftPx + extraHorizontallyFlippedOffset
|
|
698
819
|
const len = +mismatch.base || mismatch.length
|
|
820
|
+
const insW = Math.max(minSubfeatureWidth, Math.min(1.2, 1 / bpPerPx))
|
|
699
821
|
if (len < 10) {
|
|
700
|
-
ctx.fillRect(pos, topPx,
|
|
701
|
-
if (1 / bpPerPx >= charWidth) {
|
|
702
|
-
ctx.fillRect(pos - w, topPx, w * 3, 1)
|
|
703
|
-
ctx.fillRect(pos - w, topPx + heightPx - 1, w * 3, 1)
|
|
704
|
-
}
|
|
822
|
+
ctx.fillRect(pos, topPx, insW, heightPx)
|
|
705
823
|
if (1 / bpPerPx >= charWidth && heightPx >= heightLim) {
|
|
706
|
-
ctx.
|
|
824
|
+
ctx.fillRect(pos - insW, topPx, insW * 3, 1)
|
|
825
|
+
ctx.fillRect(pos - insW, topPx + heightPx - 1, insW * 3, 1)
|
|
826
|
+
ctx.fillText(`(${mismatch.base})`, pos + 3, topPx + heightPx)
|
|
707
827
|
}
|
|
708
828
|
}
|
|
709
829
|
} else if (mismatch.type === 'hardclip' || mismatch.type === 'softclip') {
|
|
710
830
|
ctx.fillStyle = mismatch.type === 'hardclip' ? 'red' : 'blue'
|
|
711
|
-
const pos = leftPx
|
|
712
|
-
ctx.fillRect(pos, topPx
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
ctx.fillText(`(${mismatch.base})`,
|
|
831
|
+
const pos = leftPx + extraHorizontallyFlippedOffset
|
|
832
|
+
ctx.fillRect(pos, topPx, w, heightPx)
|
|
833
|
+
if (1 / bpPerPx >= charWidth && heightPx >= heightLim) {
|
|
834
|
+
ctx.fillRect(pos - w, topPx, w * 3, 1)
|
|
835
|
+
ctx.fillRect(pos - w, topPx + heightPx - 1, w * 3, 1)
|
|
836
|
+
ctx.fillText(`(${mismatch.base})`, pos + 3, topPx + heightPx)
|
|
717
837
|
}
|
|
718
838
|
} else if (mismatch.type === 'skip') {
|
|
719
839
|
// fix to avoid bad rendering note that this was also related to chrome
|
|
@@ -797,10 +917,9 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
797
917
|
.filter(mismatch => mismatch.type === 'softclip')
|
|
798
918
|
.forEach(mismatch => {
|
|
799
919
|
const softClipLength = mismatch.cliplen || 0
|
|
920
|
+
const s = feature.get('start')
|
|
800
921
|
const softClipStart =
|
|
801
|
-
mismatch.start === 0
|
|
802
|
-
? feature.get('start') - softClipLength
|
|
803
|
-
: feature.get('start') + mismatch.start
|
|
922
|
+
mismatch.start === 0 ? s - softClipLength : s + mismatch.start
|
|
804
923
|
|
|
805
924
|
for (let k = 0; k < softClipLength; k += 1) {
|
|
806
925
|
const base = seq.charAt(k + mismatch.start)
|
|
@@ -854,6 +973,7 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
854
973
|
|
|
855
974
|
const theme = createJBrowseTheme(configTheme)
|
|
856
975
|
const colorForBase = getColorBaseMap(theme)
|
|
976
|
+
const contrastForBase = getContrastBaseMap(theme)
|
|
857
977
|
if (!layout) {
|
|
858
978
|
throw new Error(`layout required`)
|
|
859
979
|
}
|
|
@@ -871,8 +991,12 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
871
991
|
this.drawAlignmentRect(ctx, feat, {
|
|
872
992
|
...props,
|
|
873
993
|
defaultColor,
|
|
994
|
+
colorForBase,
|
|
995
|
+
contrastForBase,
|
|
996
|
+
charWidth,
|
|
997
|
+
charHeight,
|
|
874
998
|
})
|
|
875
|
-
this.drawMismatches(ctx, feat, props,
|
|
999
|
+
this.drawMismatches(ctx, feat, props, {
|
|
876
1000
|
mismatchAlpha,
|
|
877
1001
|
drawSNPs: shouldDrawMismatches(colorBy?.type),
|
|
878
1002
|
drawIndels: shouldDrawMismatches(colorBy?.type),
|
|
@@ -880,6 +1004,8 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
880
1004
|
minSubfeatureWidth,
|
|
881
1005
|
charWidth,
|
|
882
1006
|
charHeight,
|
|
1007
|
+
colorForBase,
|
|
1008
|
+
contrastForBase,
|
|
883
1009
|
})
|
|
884
1010
|
if (showSoftClip) {
|
|
885
1011
|
this.drawSoftClipping(ctx, feat, props, config, theme)
|
|
@@ -931,34 +1057,40 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
931
1057
|
return layoutRecords
|
|
932
1058
|
}
|
|
933
1059
|
|
|
934
|
-
async
|
|
935
|
-
const { sessionId,
|
|
1060
|
+
async fetchSequence(renderProps: RenderArgsDeserialized) {
|
|
1061
|
+
const { sessionId, regions, adapterConfig } = renderProps
|
|
936
1062
|
const { sequenceAdapter } = adapterConfig
|
|
1063
|
+
if (!sequenceAdapter) {
|
|
1064
|
+
return undefined
|
|
1065
|
+
}
|
|
1066
|
+
const { dataAdapter } = await getAdapter(
|
|
1067
|
+
this.pluginManager,
|
|
1068
|
+
sessionId,
|
|
1069
|
+
sequenceAdapter,
|
|
1070
|
+
)
|
|
1071
|
+
const [region] = regions
|
|
1072
|
+
return fetchSequence(region, dataAdapter as BaseFeatureDataAdapter)
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
async render(renderProps: RenderArgsDeserialized) {
|
|
937
1076
|
const features = await this.getFeatures(renderProps)
|
|
938
1077
|
const layout = this.createLayoutInWorker(renderProps)
|
|
1078
|
+
const { regions, bpPerPx } = renderProps
|
|
939
1079
|
|
|
940
|
-
const layoutRecords = this.layoutFeats({
|
|
1080
|
+
const layoutRecords = this.layoutFeats({
|
|
1081
|
+
...renderProps,
|
|
1082
|
+
features,
|
|
1083
|
+
layout,
|
|
1084
|
+
})
|
|
941
1085
|
const [region] = regions
|
|
942
|
-
let regionSequence: string | undefined
|
|
943
|
-
const { end, start, originalRefName, refName } = region
|
|
944
|
-
|
|
945
|
-
if (sequenceAdapter) {
|
|
946
|
-
const { dataAdapter } = await getAdapter(
|
|
947
|
-
this.pluginManager,
|
|
948
|
-
sessionId,
|
|
949
|
-
sequenceAdapter,
|
|
950
|
-
)
|
|
951
1086
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
.toPromise()
|
|
960
|
-
regionSequence = feats[0]?.get('seq')
|
|
961
|
-
}
|
|
1087
|
+
// only need reference sequence if there are features and only for some
|
|
1088
|
+
// cases
|
|
1089
|
+
const regionSequence =
|
|
1090
|
+
features.size && shouldFetchReferenceSequence(renderProps.colorBy?.type)
|
|
1091
|
+
? await this.fetchSequence(renderProps)
|
|
1092
|
+
: undefined
|
|
1093
|
+
const { end, start } = region
|
|
962
1094
|
|
|
963
1095
|
const width = (end - start) / bpPerPx
|
|
964
1096
|
const height = Math.max(layout.getTotalHeight(), 1)
|
|
@@ -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
|