@jbrowse/plugin-alignments 1.7.8 → 1.7.11
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/AlignmentsFeatureDetail.js +13 -3
- package/dist/AlignmentsFeatureDetail/index.d.ts +28 -3
- package/dist/AlignmentsFeatureDetail/index.js +6 -17
- package/dist/BamAdapter/MismatchParser.js +4 -2
- package/dist/LinearPileupDisplay/model.d.ts +7 -4
- package/dist/LinearPileupDisplay/model.js +131 -50
- package/dist/LinearSNPCoverageDisplay/components/Tooltip.js +35 -14
- package/dist/LinearSNPCoverageDisplay/models/model.d.ts +3 -3
- package/dist/PileupRenderer/PileupRenderer.d.ts +66 -9
- package/dist/PileupRenderer/PileupRenderer.js +199 -124
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +2 -0
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js +3 -0
- package/dist/SNPCoverageRenderer/configSchema.d.ts +1 -1
- package/package.json +2 -2
- package/src/AlignmentsFeatureDetail/AlignmentsFeatureDetail.tsx +14 -3
- package/src/AlignmentsFeatureDetail/index.ts +7 -17
- package/src/BamAdapter/MismatchParser.ts +4 -2
- package/src/LinearPileupDisplay/model.ts +75 -23
- package/src/LinearSNPCoverageDisplay/components/Tooltip.tsx +44 -30
- package/src/PileupRenderer/PileupRenderer.tsx +366 -180
- package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +5 -0
|
@@ -44,6 +44,25 @@ import {
|
|
|
44
44
|
} from './PileupLayoutSession'
|
|
45
45
|
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
46
46
|
|
|
47
|
+
function fillRect(
|
|
48
|
+
ctx: CanvasRenderingContext2D,
|
|
49
|
+
l: number,
|
|
50
|
+
t: number,
|
|
51
|
+
w: number,
|
|
52
|
+
h: number,
|
|
53
|
+
cw: number,
|
|
54
|
+
color?: string,
|
|
55
|
+
) {
|
|
56
|
+
if (l + w < 0 || l > cw) {
|
|
57
|
+
return
|
|
58
|
+
} else {
|
|
59
|
+
if (color) {
|
|
60
|
+
ctx.fillStyle = color
|
|
61
|
+
}
|
|
62
|
+
ctx.fillRect(l, t, w, h)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
47
66
|
function getColorBaseMap(theme: Theme) {
|
|
48
67
|
return {
|
|
49
68
|
A: theme.palette.bases.A.main,
|
|
@@ -132,8 +151,8 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
132
151
|
// get width and height of chars the height is an approximation: width
|
|
133
152
|
// letter M is approximately the height
|
|
134
153
|
getCharWidthHeight(ctx: CanvasRenderingContext2D) {
|
|
135
|
-
const charWidth =
|
|
136
|
-
const charHeight =
|
|
154
|
+
const charWidth = measureText('A')
|
|
155
|
+
const charHeight = measureText('M')
|
|
137
156
|
return { charWidth, charHeight }
|
|
138
157
|
}
|
|
139
158
|
|
|
@@ -287,25 +306,32 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
287
306
|
return strand === 1 ? 'color_fwd_strand' : 'color_rev_strand'
|
|
288
307
|
}
|
|
289
308
|
|
|
290
|
-
colorByPerBaseLettering(
|
|
291
|
-
ctx
|
|
292
|
-
feat
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
309
|
+
colorByPerBaseLettering({
|
|
310
|
+
ctx,
|
|
311
|
+
feat,
|
|
312
|
+
region,
|
|
313
|
+
bpPerPx,
|
|
314
|
+
colorForBase,
|
|
315
|
+
contrastForBase,
|
|
316
|
+
charWidth,
|
|
317
|
+
charHeight,
|
|
318
|
+
canvasWidth,
|
|
319
|
+
}: {
|
|
320
|
+
ctx: CanvasRenderingContext2D
|
|
321
|
+
feat: LayoutFeature
|
|
322
|
+
region: Region
|
|
323
|
+
bpPerPx: number
|
|
324
|
+
colorForBase: Record<string, string>
|
|
325
|
+
contrastForBase: Record<string, string>
|
|
326
|
+
charWidth: number
|
|
327
|
+
charHeight: number
|
|
328
|
+
canvasWidth: number
|
|
329
|
+
}) {
|
|
304
330
|
const heightLim = charHeight - 2
|
|
305
331
|
const { feature, topPx, heightPx } = feat
|
|
306
332
|
const seq = feature.get('seq') as string
|
|
307
333
|
const cigarOps = parseCigar(feature.get('CIGAR'))
|
|
308
|
-
const
|
|
334
|
+
const w = 1 / bpPerPx
|
|
309
335
|
const start = feature.get('start')
|
|
310
336
|
let soffset = 0 // sequence offset
|
|
311
337
|
let roffset = 0 // reference offset
|
|
@@ -320,22 +346,25 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
320
346
|
} else if (op === 'M' || op === 'X' || op === '=') {
|
|
321
347
|
for (let m = 0; m < len; m++) {
|
|
322
348
|
const letter = seq[soffset + m]
|
|
323
|
-
|
|
324
|
-
const [leftPx] = bpSpanPx(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
349
|
+
const r = start + roffset + m
|
|
350
|
+
const [leftPx] = bpSpanPx(r, r + 1, region, bpPerPx)
|
|
351
|
+
fillRect(
|
|
352
|
+
ctx,
|
|
353
|
+
leftPx,
|
|
354
|
+
topPx,
|
|
355
|
+
w + 0.5,
|
|
356
|
+
heightPx,
|
|
357
|
+
canvasWidth,
|
|
358
|
+
colorForBase[letter],
|
|
329
359
|
)
|
|
330
|
-
ctx.fillRect(leftPx, topPx, widthPx + 0.5, heightPx)
|
|
331
360
|
|
|
332
|
-
if (
|
|
361
|
+
if (w >= charWidth && heightPx >= heightLim) {
|
|
333
362
|
// normal SNP coloring
|
|
334
363
|
ctx.fillStyle = contrastForBase[letter]
|
|
335
364
|
|
|
336
365
|
ctx.fillText(
|
|
337
366
|
letter,
|
|
338
|
-
leftPx + (
|
|
367
|
+
leftPx + (w - charWidth) / 2 + 1,
|
|
339
368
|
topPx + heightPx,
|
|
340
369
|
)
|
|
341
370
|
}
|
|
@@ -345,13 +374,19 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
345
374
|
}
|
|
346
375
|
}
|
|
347
376
|
}
|
|
348
|
-
colorByPerBaseQuality(
|
|
349
|
-
ctx
|
|
350
|
-
feat
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
377
|
+
colorByPerBaseQuality({
|
|
378
|
+
ctx,
|
|
379
|
+
feat,
|
|
380
|
+
region,
|
|
381
|
+
bpPerPx,
|
|
382
|
+
canvasWidth,
|
|
383
|
+
}: {
|
|
384
|
+
ctx: CanvasRenderingContext2D
|
|
385
|
+
feat: LayoutFeature
|
|
386
|
+
region: Region
|
|
387
|
+
bpPerPx: number
|
|
388
|
+
canvasWidth: number
|
|
389
|
+
}) {
|
|
355
390
|
const { feature, topPx, heightPx } = feat
|
|
356
391
|
const qual: string = feature.get('qual') || ''
|
|
357
392
|
const scores = qual.split(' ').map(val => +val)
|
|
@@ -371,14 +406,21 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
371
406
|
} else if (op === 'M' || op === 'X' || op === '=') {
|
|
372
407
|
for (let m = 0; m < len; m++) {
|
|
373
408
|
const score = scores[soffset + m]
|
|
374
|
-
ctx.fillStyle = `hsl(${score === 255 ? 150 : score * 1.5},55%,50%)`
|
|
375
409
|
const [leftPx] = bpSpanPx(
|
|
376
410
|
start + roffset + m,
|
|
377
411
|
start + roffset + m + 1,
|
|
378
412
|
region,
|
|
379
413
|
bpPerPx,
|
|
380
414
|
)
|
|
381
|
-
|
|
415
|
+
fillRect(
|
|
416
|
+
ctx,
|
|
417
|
+
leftPx,
|
|
418
|
+
topPx,
|
|
419
|
+
width + 0.5,
|
|
420
|
+
heightPx,
|
|
421
|
+
canvasWidth,
|
|
422
|
+
`hsl(${score === 255 ? 150 : score * 1.5},55%,50%)`,
|
|
423
|
+
)
|
|
382
424
|
}
|
|
383
425
|
soffset += len
|
|
384
426
|
roffset += len
|
|
@@ -395,16 +437,23 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
395
437
|
// has very high likelihood basecalls at that point, we really only care
|
|
396
438
|
// about low qual calls <20 approx
|
|
397
439
|
//
|
|
398
|
-
colorByModifications(
|
|
399
|
-
ctx
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
440
|
+
colorByModifications({
|
|
441
|
+
ctx,
|
|
442
|
+
feat,
|
|
443
|
+
region,
|
|
444
|
+
bpPerPx,
|
|
445
|
+
renderArgs,
|
|
446
|
+
canvasWidth,
|
|
447
|
+
}: {
|
|
448
|
+
ctx: CanvasRenderingContext2D
|
|
449
|
+
feat: LayoutFeature
|
|
450
|
+
region: Region
|
|
451
|
+
bpPerPx: number
|
|
452
|
+
renderArgs: RenderArgsDeserializedWithFeaturesAndLayout
|
|
453
|
+
canvasWidth: number
|
|
454
|
+
}) {
|
|
455
|
+
const { feature, topPx, heightPx } = feat
|
|
456
|
+
const { modificationTagMap = {} } = renderArgs
|
|
408
457
|
|
|
409
458
|
const mm = (getTagAlt(feature, 'MM', 'Mm') as string) || ''
|
|
410
459
|
|
|
@@ -440,14 +489,21 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
440
489
|
// give it a little boost of 0.1 to not make them fully
|
|
441
490
|
// invisible to avoid confusion
|
|
442
491
|
const prob = probabilities[probIndex]
|
|
443
|
-
|
|
492
|
+
|
|
493
|
+
fillRect(
|
|
494
|
+
ctx,
|
|
495
|
+
leftPx,
|
|
496
|
+
topPx,
|
|
497
|
+
rightPx - leftPx + 0.5,
|
|
498
|
+
heightPx,
|
|
499
|
+
canvasWidth,
|
|
444
500
|
prob && prob !== 1
|
|
445
501
|
? base
|
|
446
502
|
.alpha(prob + 0.1)
|
|
447
503
|
.hsl()
|
|
448
504
|
.string()
|
|
449
|
-
: col
|
|
450
|
-
|
|
505
|
+
: col,
|
|
506
|
+
)
|
|
451
507
|
probIndex++
|
|
452
508
|
}
|
|
453
509
|
}
|
|
@@ -456,16 +512,23 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
456
512
|
// Color by methylation is slightly modified version of color by
|
|
457
513
|
// modifications that focuses on CpG sites, with non-methylated CpG colored
|
|
458
514
|
// blue
|
|
459
|
-
colorByMethylation(
|
|
460
|
-
ctx
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
515
|
+
colorByMethylation({
|
|
516
|
+
ctx,
|
|
517
|
+
feat,
|
|
518
|
+
region,
|
|
519
|
+
bpPerPx,
|
|
520
|
+
renderArgs,
|
|
521
|
+
canvasWidth,
|
|
522
|
+
}: {
|
|
523
|
+
ctx: CanvasRenderingContext2D
|
|
524
|
+
feat: LayoutFeature
|
|
525
|
+
region: Region
|
|
526
|
+
bpPerPx: number
|
|
527
|
+
renderArgs: RenderArgsDeserializedWithFeaturesAndLayout
|
|
528
|
+
canvasWidth: number
|
|
529
|
+
}) {
|
|
530
|
+
const { regionSequence } = renderArgs
|
|
531
|
+
const { feature, topPx, heightPx } = feat
|
|
469
532
|
|
|
470
533
|
const mm: string = getTagAlt(feature, 'MM', 'Mm') || ''
|
|
471
534
|
|
|
@@ -505,8 +568,15 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
505
568
|
if (l1 === 'c' && l2 === 'g') {
|
|
506
569
|
const s = region.start + i
|
|
507
570
|
const [leftPx, rightPx] = bpSpanPx(s, s + 2, region, bpPerPx)
|
|
508
|
-
|
|
509
|
-
|
|
571
|
+
fillRect(
|
|
572
|
+
ctx,
|
|
573
|
+
leftPx,
|
|
574
|
+
topPx,
|
|
575
|
+
rightPx - leftPx + 0.5,
|
|
576
|
+
heightPx,
|
|
577
|
+
canvasWidth,
|
|
578
|
+
methBins[i] || methBins[i + 1] ? 'red' : 'blue',
|
|
579
|
+
)
|
|
510
580
|
}
|
|
511
581
|
}
|
|
512
582
|
// if we are zoomed in, color the c inside the cpg
|
|
@@ -515,12 +585,26 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
515
585
|
if (l1 === 'c' && l2 === 'g') {
|
|
516
586
|
const s = region.start + i
|
|
517
587
|
const [leftPx, rightPx] = bpSpanPx(s, s + 1, region, bpPerPx)
|
|
518
|
-
|
|
519
|
-
|
|
588
|
+
fillRect(
|
|
589
|
+
ctx,
|
|
590
|
+
leftPx,
|
|
591
|
+
topPx,
|
|
592
|
+
rightPx - leftPx + 0.5,
|
|
593
|
+
heightPx,
|
|
594
|
+
canvasWidth,
|
|
595
|
+
methBins[i] ? 'red' : 'blue',
|
|
596
|
+
)
|
|
520
597
|
|
|
521
598
|
const [leftPx2, rightPx2] = bpSpanPx(s + 1, s + 2, region, bpPerPx)
|
|
522
|
-
|
|
523
|
-
|
|
599
|
+
fillRect(
|
|
600
|
+
ctx,
|
|
601
|
+
leftPx2,
|
|
602
|
+
topPx,
|
|
603
|
+
rightPx2 - leftPx2 + 0.5,
|
|
604
|
+
heightPx,
|
|
605
|
+
canvasWidth,
|
|
606
|
+
methBins[i + 1] ? 'red' : 'blue',
|
|
607
|
+
)
|
|
524
608
|
}
|
|
525
609
|
}
|
|
526
610
|
}
|
|
@@ -568,29 +652,29 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
568
652
|
}
|
|
569
653
|
}
|
|
570
654
|
|
|
571
|
-
drawAlignmentRect(
|
|
572
|
-
ctx
|
|
573
|
-
feat
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
655
|
+
drawAlignmentRect({
|
|
656
|
+
ctx,
|
|
657
|
+
feat,
|
|
658
|
+
renderArgs,
|
|
659
|
+
colorForBase,
|
|
660
|
+
contrastForBase,
|
|
661
|
+
charWidth,
|
|
662
|
+
charHeight,
|
|
663
|
+
defaultColor,
|
|
664
|
+
canvasWidth,
|
|
665
|
+
}: {
|
|
666
|
+
ctx: CanvasRenderingContext2D
|
|
667
|
+
feat: LayoutFeature
|
|
668
|
+
renderArgs: RenderArgsDeserializedWithFeaturesAndLayout
|
|
669
|
+
colorForBase: Record<string, string>
|
|
670
|
+
contrastForBase: Record<string, string>
|
|
671
|
+
charWidth: number
|
|
672
|
+
charHeight: number
|
|
673
|
+
defaultColor: boolean
|
|
674
|
+
canvasWidth: number
|
|
675
|
+
}) {
|
|
676
|
+
const { config, bpPerPx, regions, colorBy, colorTagMap = {} } = renderArgs
|
|
677
|
+
|
|
594
678
|
const { tag = '', type: colorType = '' } = colorBy || {}
|
|
595
679
|
const { feature } = feat
|
|
596
680
|
const region = regions[0]
|
|
@@ -677,62 +761,89 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
677
761
|
break
|
|
678
762
|
}
|
|
679
763
|
|
|
680
|
-
this.drawRect(ctx, feat,
|
|
764
|
+
this.drawRect(ctx, feat, renderArgs)
|
|
681
765
|
|
|
682
766
|
// second pass for color types that render per-base things that go over the
|
|
683
767
|
// existing drawing
|
|
684
768
|
switch (colorType) {
|
|
685
769
|
case 'perBaseQuality':
|
|
686
|
-
this.colorByPerBaseQuality(
|
|
770
|
+
this.colorByPerBaseQuality({
|
|
771
|
+
ctx,
|
|
772
|
+
feat,
|
|
773
|
+
region,
|
|
774
|
+
bpPerPx,
|
|
775
|
+
canvasWidth,
|
|
776
|
+
})
|
|
687
777
|
break
|
|
688
778
|
|
|
689
779
|
case 'perBaseLettering':
|
|
690
|
-
this.colorByPerBaseLettering(
|
|
780
|
+
this.colorByPerBaseLettering({
|
|
781
|
+
ctx,
|
|
782
|
+
feat,
|
|
783
|
+
region,
|
|
784
|
+
bpPerPx,
|
|
691
785
|
colorForBase,
|
|
692
786
|
contrastForBase,
|
|
693
787
|
charWidth,
|
|
694
788
|
charHeight,
|
|
789
|
+
canvasWidth,
|
|
695
790
|
})
|
|
696
791
|
break
|
|
697
792
|
|
|
698
793
|
case 'modifications':
|
|
699
|
-
this.colorByModifications(
|
|
794
|
+
this.colorByModifications({
|
|
795
|
+
ctx,
|
|
796
|
+
feat,
|
|
797
|
+
region,
|
|
798
|
+
bpPerPx,
|
|
799
|
+
renderArgs,
|
|
800
|
+
canvasWidth,
|
|
801
|
+
})
|
|
700
802
|
break
|
|
701
803
|
|
|
702
804
|
case 'methylation':
|
|
703
|
-
this.colorByMethylation(
|
|
805
|
+
this.colorByMethylation({
|
|
806
|
+
ctx,
|
|
807
|
+
feat,
|
|
808
|
+
region,
|
|
809
|
+
bpPerPx,
|
|
810
|
+
renderArgs,
|
|
811
|
+
canvasWidth,
|
|
812
|
+
})
|
|
704
813
|
break
|
|
705
814
|
}
|
|
706
815
|
}
|
|
707
816
|
|
|
708
|
-
drawMismatches(
|
|
709
|
-
ctx
|
|
710
|
-
feat
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
817
|
+
drawMismatches({
|
|
818
|
+
ctx,
|
|
819
|
+
feat,
|
|
820
|
+
renderArgs,
|
|
821
|
+
minSubfeatureWidth,
|
|
822
|
+
largeInsertionIndicatorScale,
|
|
823
|
+
mismatchAlpha,
|
|
824
|
+
charWidth,
|
|
825
|
+
charHeight,
|
|
826
|
+
colorForBase,
|
|
827
|
+
contrastForBase,
|
|
828
|
+
canvasWidth,
|
|
829
|
+
drawSNPs = true,
|
|
830
|
+
drawIndels = true,
|
|
831
|
+
}: {
|
|
832
|
+
ctx: CanvasRenderingContext2D
|
|
833
|
+
feat: LayoutFeature
|
|
834
|
+
renderArgs: RenderArgsDeserializedWithFeaturesAndLayout
|
|
835
|
+
colorForBase: { [key: string]: string }
|
|
836
|
+
contrastForBase: { [key: string]: string }
|
|
837
|
+
mismatchAlpha?: boolean
|
|
838
|
+
drawSNPs?: boolean
|
|
839
|
+
drawIndels?: boolean
|
|
840
|
+
minSubfeatureWidth: number
|
|
841
|
+
largeInsertionIndicatorScale: number
|
|
842
|
+
charWidth: number
|
|
843
|
+
charHeight: number
|
|
844
|
+
canvasWidth: number
|
|
845
|
+
}) {
|
|
846
|
+
const { bpPerPx, regions } = renderArgs
|
|
736
847
|
const { heightPx, topPx, feature } = feat
|
|
737
848
|
const [region] = regions
|
|
738
849
|
const start = feature.get('start')
|
|
@@ -760,16 +871,22 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
760
871
|
if (mismatch.type === 'mismatch' && drawSNPs) {
|
|
761
872
|
const baseColor = colorForBase[mismatch.base] || '#888'
|
|
762
873
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
874
|
+
fillRect(
|
|
875
|
+
ctx,
|
|
876
|
+
leftPx,
|
|
877
|
+
topPx,
|
|
878
|
+
widthPx,
|
|
879
|
+
heightPx,
|
|
880
|
+
canvasWidth,
|
|
881
|
+
!mismatchAlpha
|
|
882
|
+
? baseColor
|
|
883
|
+
: mismatch.qual !== undefined
|
|
884
|
+
? Color(baseColor)
|
|
885
|
+
.alpha(Math.min(1, mismatch.qual / 50))
|
|
886
|
+
.hsl()
|
|
887
|
+
.string()
|
|
888
|
+
: baseColor,
|
|
889
|
+
)
|
|
773
890
|
|
|
774
891
|
if (widthPx >= charWidth && heightPx >= heightLim) {
|
|
775
892
|
// normal SNP coloring
|
|
@@ -789,9 +906,15 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
789
906
|
)
|
|
790
907
|
}
|
|
791
908
|
} else if (mismatch.type === 'deletion' && drawIndels) {
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
909
|
+
fillRect(
|
|
910
|
+
ctx,
|
|
911
|
+
leftPx,
|
|
912
|
+
topPx,
|
|
913
|
+
widthPx,
|
|
914
|
+
heightPx,
|
|
915
|
+
canvasWidth,
|
|
916
|
+
colorForBase.deletion,
|
|
917
|
+
)
|
|
795
918
|
const txt = `${mismatch.length}`
|
|
796
919
|
const rwidth = measureText(txt, 10)
|
|
797
920
|
if (widthPx >= rwidth && heightPx >= heightLim) {
|
|
@@ -811,20 +934,34 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
811
934
|
Math.min(1.2, 1 / bpPerPx),
|
|
812
935
|
)
|
|
813
936
|
if (len < 10) {
|
|
814
|
-
|
|
937
|
+
fillRect(ctx, pos, topPx, insW, heightPx, canvasWidth, 'purple')
|
|
815
938
|
if (1 / bpPerPx >= charWidth && heightPx >= heightLim) {
|
|
816
|
-
|
|
817
|
-
|
|
939
|
+
fillRect(ctx, pos - insW, topPx, insW * 3, 1, canvasWidth)
|
|
940
|
+
fillRect(
|
|
941
|
+
ctx,
|
|
942
|
+
pos - insW,
|
|
943
|
+
topPx + heightPx - 1,
|
|
944
|
+
insW * 3,
|
|
945
|
+
1,
|
|
946
|
+
canvasWidth,
|
|
947
|
+
)
|
|
818
948
|
ctx.fillText(`(${mismatch.base})`, pos + 3, topPx + heightPx)
|
|
819
949
|
}
|
|
820
950
|
}
|
|
821
951
|
} else if (mismatch.type === 'hardclip' || mismatch.type === 'softclip') {
|
|
822
|
-
ctx.fillStyle = mismatch.type === 'hardclip' ? 'red' : 'blue'
|
|
823
952
|
const pos = leftPx + extraHorizontallyFlippedOffset
|
|
824
|
-
|
|
953
|
+
fillRect(
|
|
954
|
+
ctx,
|
|
955
|
+
pos,
|
|
956
|
+
topPx,
|
|
957
|
+
w,
|
|
958
|
+
heightPx,
|
|
959
|
+
canvasWidth,
|
|
960
|
+
mismatch.type === 'hardclip' ? 'red' : 'blue',
|
|
961
|
+
)
|
|
825
962
|
if (1 / bpPerPx >= charWidth && heightPx >= heightLim) {
|
|
826
|
-
|
|
827
|
-
|
|
963
|
+
fillRect(ctx, pos - w, topPx, w * 3, 1, canvasWidth)
|
|
964
|
+
fillRect(ctx, pos - w, topPx + heightPx - 1, w * 3, 1, canvasWidth)
|
|
828
965
|
ctx.fillText(`(${mismatch.base})`, pos + 3, topPx + heightPx)
|
|
829
966
|
}
|
|
830
967
|
} else if (mismatch.type === 'skip') {
|
|
@@ -835,12 +972,14 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
835
972
|
// make small exons more visible when zoomed far out
|
|
836
973
|
const adjustPx = widthPx - (bpPerPx > 10 ? 1.5 : 0)
|
|
837
974
|
ctx.clearRect(leftPx, topPx, adjustPx, heightPx)
|
|
838
|
-
|
|
839
|
-
|
|
975
|
+
fillRect(
|
|
976
|
+
ctx,
|
|
840
977
|
Math.max(0, leftPx),
|
|
841
978
|
topPx + heightPx / 2 - 1,
|
|
842
979
|
adjustPx + (leftPx < 0 ? leftPx : 0),
|
|
843
980
|
2,
|
|
981
|
+
canvasWidth,
|
|
982
|
+
'#333',
|
|
844
983
|
)
|
|
845
984
|
}
|
|
846
985
|
}
|
|
@@ -857,39 +996,55 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
857
996
|
const txt = `${len}`
|
|
858
997
|
if (mismatch.type === 'insertion' && len >= 10) {
|
|
859
998
|
if (bpPerPx > largeInsertionIndicatorScale) {
|
|
860
|
-
ctx
|
|
861
|
-
ctx.fillRect(leftPx - 1, topPx, 2, heightPx)
|
|
999
|
+
fillRect(ctx, leftPx - 1, topPx, 2, heightPx, canvasWidth, 'purple')
|
|
862
1000
|
} else if (heightPx > charHeight) {
|
|
863
1001
|
const rwidth = measureText(txt)
|
|
864
1002
|
const padding = 5
|
|
865
|
-
|
|
866
|
-
|
|
1003
|
+
fillRect(
|
|
1004
|
+
ctx,
|
|
867
1005
|
leftPx - rwidth / 2 - padding,
|
|
868
1006
|
topPx,
|
|
869
1007
|
rwidth + 2 * padding,
|
|
870
1008
|
heightPx,
|
|
1009
|
+
canvasWidth,
|
|
1010
|
+
'purple',
|
|
871
1011
|
)
|
|
872
1012
|
ctx.fillStyle = 'white'
|
|
873
1013
|
ctx.fillText(txt, leftPx - rwidth / 2, topPx + heightPx)
|
|
874
1014
|
} else {
|
|
875
1015
|
const padding = 2
|
|
876
|
-
|
|
877
|
-
|
|
1016
|
+
fillRect(
|
|
1017
|
+
ctx,
|
|
1018
|
+
leftPx - padding,
|
|
1019
|
+
topPx,
|
|
1020
|
+
2 * padding,
|
|
1021
|
+
heightPx,
|
|
1022
|
+
canvasWidth,
|
|
1023
|
+
'purple',
|
|
1024
|
+
)
|
|
878
1025
|
}
|
|
879
1026
|
}
|
|
880
1027
|
}
|
|
881
1028
|
}
|
|
882
1029
|
}
|
|
883
1030
|
|
|
884
|
-
drawSoftClipping(
|
|
885
|
-
ctx
|
|
886
|
-
feat
|
|
887
|
-
|
|
888
|
-
config
|
|
889
|
-
theme
|
|
890
|
-
|
|
1031
|
+
drawSoftClipping({
|
|
1032
|
+
ctx,
|
|
1033
|
+
feat,
|
|
1034
|
+
renderArgs,
|
|
1035
|
+
config,
|
|
1036
|
+
theme,
|
|
1037
|
+
canvasWidth,
|
|
1038
|
+
}: {
|
|
1039
|
+
ctx: CanvasRenderingContext2D
|
|
1040
|
+
feat: LayoutFeature
|
|
1041
|
+
renderArgs: RenderArgsDeserializedWithFeaturesAndLayout
|
|
1042
|
+
config: AnyConfigurationModel
|
|
1043
|
+
theme: Theme
|
|
1044
|
+
canvasWidth: number
|
|
1045
|
+
}) {
|
|
891
1046
|
const { feature, topPx, heightPx } = feat
|
|
892
|
-
const { regions, bpPerPx } =
|
|
1047
|
+
const { regions, bpPerPx } = renderArgs
|
|
893
1048
|
const [region] = regions
|
|
894
1049
|
const minFeatWidth = readConfObject(config, 'minSubfeatureWidth')
|
|
895
1050
|
const mismatches: Mismatch[] = feature.get('mismatches')
|
|
@@ -937,7 +1092,14 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
937
1092
|
// show in soft clipping
|
|
938
1093
|
const baseColor = colorForBase[base] || '#000000'
|
|
939
1094
|
ctx.fillStyle = baseColor
|
|
940
|
-
|
|
1095
|
+
fillRect(
|
|
1096
|
+
ctx,
|
|
1097
|
+
softClipLeftPx,
|
|
1098
|
+
topPx,
|
|
1099
|
+
softClipWidthPx,
|
|
1100
|
+
heightPx,
|
|
1101
|
+
canvasWidth,
|
|
1102
|
+
)
|
|
941
1103
|
|
|
942
1104
|
if (softClipWidthPx >= charWidth && heightPx >= charHeight - 5) {
|
|
943
1105
|
ctx.fillStyle = theme.palette.getContrastText(baseColor)
|
|
@@ -952,12 +1114,24 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
952
1114
|
}
|
|
953
1115
|
}
|
|
954
1116
|
|
|
955
|
-
makeImageData(
|
|
956
|
-
ctx
|
|
957
|
-
layoutRecords
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1117
|
+
makeImageData({
|
|
1118
|
+
ctx,
|
|
1119
|
+
layoutRecords,
|
|
1120
|
+
canvasWidth,
|
|
1121
|
+
renderArgs,
|
|
1122
|
+
}: {
|
|
1123
|
+
ctx: CanvasRenderingContext2D
|
|
1124
|
+
canvasWidth: number
|
|
1125
|
+
layoutRecords: (LayoutFeature | null)[]
|
|
1126
|
+
renderArgs: RenderArgsDeserializedWithFeaturesAndLayout
|
|
1127
|
+
}) {
|
|
1128
|
+
const {
|
|
1129
|
+
layout,
|
|
1130
|
+
config,
|
|
1131
|
+
showSoftClip,
|
|
1132
|
+
colorBy,
|
|
1133
|
+
theme: configTheme,
|
|
1134
|
+
} = renderArgs
|
|
961
1135
|
const mismatchAlpha = readConfObject(config, 'mismatchAlpha')
|
|
962
1136
|
const minSubfeatureWidth = readConfObject(config, 'minSubfeatureWidth')
|
|
963
1137
|
const largeInsertionIndicatorScale = readConfObject(
|
|
@@ -986,15 +1160,21 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
986
1160
|
continue
|
|
987
1161
|
}
|
|
988
1162
|
|
|
989
|
-
this.drawAlignmentRect(
|
|
990
|
-
|
|
1163
|
+
this.drawAlignmentRect({
|
|
1164
|
+
ctx,
|
|
1165
|
+
feat,
|
|
1166
|
+
renderArgs,
|
|
991
1167
|
defaultColor,
|
|
992
1168
|
colorForBase,
|
|
993
1169
|
contrastForBase,
|
|
994
1170
|
charWidth,
|
|
995
1171
|
charHeight,
|
|
1172
|
+
canvasWidth,
|
|
996
1173
|
})
|
|
997
|
-
this.drawMismatches(
|
|
1174
|
+
this.drawMismatches({
|
|
1175
|
+
ctx,
|
|
1176
|
+
feat,
|
|
1177
|
+
renderArgs,
|
|
998
1178
|
mismatchAlpha,
|
|
999
1179
|
drawSNPs,
|
|
1000
1180
|
drawIndels,
|
|
@@ -1004,15 +1184,17 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
1004
1184
|
charHeight,
|
|
1005
1185
|
colorForBase,
|
|
1006
1186
|
contrastForBase,
|
|
1187
|
+
canvasWidth,
|
|
1007
1188
|
})
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1189
|
+
if (showSoftClip) {
|
|
1190
|
+
this.drawSoftClipping({
|
|
1191
|
+
ctx,
|
|
1192
|
+
feat,
|
|
1193
|
+
renderArgs,
|
|
1194
|
+
config,
|
|
1195
|
+
theme,
|
|
1196
|
+
canvasWidth,
|
|
1197
|
+
})
|
|
1016
1198
|
}
|
|
1017
1199
|
}
|
|
1018
1200
|
}
|
|
@@ -1097,17 +1279,21 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
1097
1279
|
|
|
1098
1280
|
const width = (end - start) / bpPerPx
|
|
1099
1281
|
const height = Math.max(layout.getTotalHeight(), 1)
|
|
1100
|
-
|
|
1101
1282
|
const res = await renderToAbstractCanvas(
|
|
1102
1283
|
width,
|
|
1103
1284
|
height,
|
|
1104
1285
|
renderProps,
|
|
1105
1286
|
(ctx: CanvasRenderingContext2D) =>
|
|
1106
|
-
this.makeImageData(
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1287
|
+
this.makeImageData({
|
|
1288
|
+
ctx,
|
|
1289
|
+
layoutRecords,
|
|
1290
|
+
canvasWidth: width,
|
|
1291
|
+
renderArgs: {
|
|
1292
|
+
...renderProps,
|
|
1293
|
+
layout,
|
|
1294
|
+
features,
|
|
1295
|
+
regionSequence,
|
|
1296
|
+
},
|
|
1111
1297
|
}),
|
|
1112
1298
|
)
|
|
1113
1299
|
|