@@ -30,15 +30,17 @@ import {IGVColor, StringUtils} from '../node_modules/igv-utils/src/index.js'
30
30
import ColorScale from './colorScale.js'
31
31
import HICEvent from './hicEvent.js'
32
32
import * as hicUtils from './hicUtils.js'
33
+ import { getLocus } from "./genomicUtils.js"
33
34
34
35
const DRAG_THRESHOLD = 2
35
36
const DOUBLE_TAP_DIST_THRESHOLD = 20
36
37
const DOUBLE_TAP_TIME_THRESHOLD = 300
37
38
38
39
const imageTileDimension = 685
39
40
40
- class ContactMatrixView {
41
+ const doLegacyTrack2DRendering = false
41
42
43
+ class ContactMatrixView {
42
44
43
45
constructor ( browser , $viewport , sweepZoom , scrollbarWidget , colorScale , ratioColorScale , backgroundColor ) {
44
46
@@ -86,7 +88,7 @@ class ContactMatrixView {
86
88
setBackgroundColor ( rgb ) {
87
89
this . backgroundColor = rgb
88
90
this . backgroundRGBString = IGVColor . rgbColor ( rgb . r , rgb . g , rgb . b )
89
- this . repaint ( )
91
+ this . update ( )
90
92
}
91
93
92
94
stringifyBackgroundColor ( ) {
@@ -177,6 +179,10 @@ class ContactMatrixView {
177
179
178
180
await this . repaint ( )
179
181
182
+ if ( this . browser . dataset && false === doLegacyTrack2DRendering ) {
183
+ await this . render2DTracks ( this . browser . tracks2D , this . browser . dataset , this . browser . state )
184
+ }
185
+
180
186
}
181
187
182
188
async repaint ( ) {
@@ -416,95 +422,83 @@ class ContactMatrixView {
416
422
}
417
423
418
424
ctx . putImageData ( id , 0 , 0 )
419
- } else {
420
- //console.log("No block for " + blockNumber);
421
425
}
422
426
423
- //Draw 2D tracks
424
- ctx . save ( )
425
- ctx . lineWidth = 2
426
427
428
+ if ( true === doLegacyTrack2DRendering ) {
427
429
428
- const onDiagonalTile = sameChr && row === column
430
+ //Draw 2D tracks
431
+ ctx . save ( )
432
+ ctx . lineWidth = 2
429
433
430
- for ( let track2D of this . browser . tracks2D ) {
431
- const skip =
432
- ! track2D . isVisible ||
433
- ( sameChr && "lower" === track2D . displayMode && row < column ) ||
434
- ( sameChr && "upper" === track2D . displayMode && row > column )
434
+ const onDiagonalTile = sameChr && row === column
435
435
436
- if ( ! skip ) {
436
+ for ( let track2D of this . browser . tracks2D ) {
437
+ const skip =
438
+ ! track2D . isVisible ||
439
+ ( sameChr && "lower" === track2D . displayMode && row < column ) ||
440
+ ( sameChr && "upper" === track2D . displayMode && row > column )
437
441
438
- const chr1Name = zd . chr1 . name
439
- const chr2Name = zd . chr2 . name
442
+ if ( ! skip ) {
440
443
441
- const features = track2D . getFeatures ( chr1Name , chr2Name )
444
+ const chr1Name = zd . chr1 . name
445
+ const chr2Name = zd . chr2 . name
442
446
443
- if ( features ) {
447
+ const features = track2D . getFeatures ( chr1Name , chr2Name )
444
448
445
- // console.log(`contactMatrixView - getImageTile(): render 2D annotations ${ features.length }`)
449
+ if ( features ) {
446
450
447
- for ( let { chr1, x1, x2, y1, y2, color} of features ) {
451
+ for ( let { chr1, x1, x2, y1, y2, color} of features ) {
448
452
449
- ctx . strokeStyle = track2D . color || color
453
+ ctx . strokeStyle = track2D . color || color
450
454
451
- // Chr name order -- test for equality of zoom data chr1 and feature chr1
452
- const flip = chr1Name !== chr1
455
+ // Chr name order -- test for equality of zoom data chr1 and feature chr1
456
+ const flip = chr1Name !== chr1
453
457
454
- //Note: transpose = sameChr && row < column
455
- const fx1 = transpose || flip ? y1 : x1
456
- const fx2 = transpose || flip ? y2 : x2
457
- const fy1 = transpose || flip ? x1 : y1
458
- const fy2 = transpose || flip ? x2 : y2
458
+ //Note: transpose = sameChr && row < column
459
+ const fx1 = transpose || flip ? y1 : x1
460
+ const fx2 = transpose || flip ? y2 : x2
461
+ const fy1 = transpose || flip ? x1 : y1
462
+ const fy2 = transpose || flip ? x2 : y2
459
463
460
- let px1 = ( fx1 - x0bp ) / zd . zoom . binSize
461
- let px2 = ( fx2 - x0bp ) / zd . zoom . binSize
462
- let py1 = ( fy1 - y0bp ) / zd . zoom . binSize
463
- let py2 = ( fy2 - y0bp ) / zd . zoom . binSize
464
- let w = Math . round ( Math . max ( 1 , px2 - px1 ) )
465
- let h = Math . round ( Math . max ( 1 , py2 - py1 ) )
464
+ let px1 = ( fx1 - x0bp ) / zd . zoom . binSize
465
+ let px2 = ( fx2 - x0bp ) / zd . zoom . binSize
466
+ let py1 = ( fy1 - y0bp ) / zd . zoom . binSize
467
+ let py2 = ( fy2 - y0bp ) / zd . zoom . binSize
468
+ let w = px2 - px1
469
+ let h = py2 - py1
466
470
467
- const dim = Math . max ( image . width , image . height )
468
- if ( px2 > 0 && px1 < dim && py2 > 0 && py1 < dim ) {
471
+ const dim = Math . max ( image . width , image . height )
472
+ if ( px2 > 0 && px1 < dim && py2 > 0 && py1 < dim ) {
469
473
470
474
471
- if ( ! onDiagonalTile || "upper" !== track2D . displayMode ) {
472
- const xx = Math . round ( px1 )
473
- const yy = Math . round ( py1 )
474
- const startStr = `xStart ${ StringUtils . numberFormatter ( xx ) } yStart ${ StringUtils . numberFormatter ( yy ) } `
475
- const endStr = `width ${ StringUtils . numberFormatter ( w ) } height ${ StringUtils . numberFormatter ( h ) } `
476
- // console.log(`render ${ chr1 } ${ startStr } ${ endStr }`)
477
- ctx . strokeRect ( xx , yy , w , h )
478
- }
475
+ if ( ! onDiagonalTile || "upper" !== track2D . displayMode ) {
476
+ ctx . strokeRect ( px1 , py1 , w , h )
477
+ }
479
478
480
- // By convention intra-chromosome data is always stored in lower diagonal coordinates.
481
- // If we are on a diagonal tile, draw the symettrical reflection unless display mode is lower
482
- if ( onDiagonalTile && "lower" !== track2D . displayMode ) {
483
- const xx = Math . round ( py1 )
484
- const yy = Math . round ( px1 )
485
- const startStr = `xStart ${ StringUtils . numberFormatter ( xx ) } yStart ${ StringUtils . numberFormatter ( yy ) } `
486
- const endStr = `width ${ StringUtils . numberFormatter ( h ) } height ${ StringUtils . numberFormatter ( w ) } `
487
- // console.log(`render ${ chr1 } ${ startStr } ${ endStr }`)
488
- ctx . strokeRect ( xx , yy , h , w )
479
+ // By convention intra-chromosome data is always stored in lower diagonal coordinates.
480
+ // If we are on a diagonal tile, draw the symmetrical reflection unless display mode is lower
481
+ if ( onDiagonalTile && "lower" !== track2D . displayMode ) {
482
+ ctx . strokeRect ( py1 , px1 , h , w )
483
+ }
489
484
}
490
485
}
491
- }
492
486
493
- } // if (features)
487
+ } // if (features)
494
488
495
- } // if (track2D.isVisible)
496
- }
489
+ }
490
+ }
497
491
492
+ ctx . restore ( )
498
493
499
- ctx . restore ( )
494
+ } // if (true === doLegacyTrack2DRendering )
500
495
501
496
// Uncomment to reveal tile boundaries for debugging.
502
- // ctx.fillStyle = "rgb(255,255,255)"
497
+ // ctx.strokeStyle = "rgb(255,255,255)"
498
+ // ctx.strokeStyle = "pink"
503
499
// ctx.strokeRect(0, 0, image.width - 1, image.height - 1)
504
500
505
-
506
- var imageTile = { row : row , column : column , blockBinCount : imageTileDimension , image : image }
507
-
501
+ const imageTile = { row, column, blockBinCount : imageTileDimension , image }
508
502
509
503
if ( this . imageTileCacheLimit > 0 ) {
510
504
if ( this . imageTileCacheKeys . length > this . imageTileCacheLimit ) {
@@ -518,7 +512,6 @@ class ContactMatrixView {
518
512
return imageTile
519
513
520
514
} finally {
521
- //console.log("Finish load for " + key)
522
515
this . drawsInProgress . delete ( key )
523
516
this . stopSpinner ( )
524
517
}
@@ -533,7 +526,7 @@ class ContactMatrixView {
533
526
imageData . data [ index + 3 ] = a
534
527
}
535
528
536
- } ;
529
+ }
537
530
538
531
/**
539
532
* Return a promise to adjust the color scale, if needed. This function might need to load the contact
@@ -678,7 +671,6 @@ class ContactMatrixView {
678
671
this . spinnerCount = Math . max ( 0 , this . spinnerCount ) // This should not be neccessary
679
672
}
680
673
681
-
682
674
addMouseHandlers ( $viewport ) {
683
675
684
676
let isMouseDown = false
@@ -856,7 +848,6 @@ class ContactMatrixView {
856
848
}
857
849
}
858
850
859
-
860
851
/**
861
852
* Add touch handlers. Touches are mapped to one of the following application level events
862
853
* - double tap, equivalent to double click
@@ -1023,6 +1014,84 @@ class ContactMatrixView {
1023
1014
1024
1015
}
1025
1016
1017
+ async render2DTracks ( track2DList , dataset , state ) {
1018
+
1019
+ const matrix = await dataset . getMatrix ( state . chr1 , state . chr2 )
1020
+ const zoomData = matrix . getZoomDataByIndex ( state . zoom , 'BP' )
1021
+
1022
+ const { width, height } = this . getViewDimensions ( )
1023
+ const bpPerPixel = zoomData . zoom . binSize / state . pixelSize
1024
+ const { xStartBP, yStartBP, xEndBP, yEndBP } = getLocus ( dataset , state , width , height , bpPerPixel )
1025
+
1026
+ const chr1Name = zoomData . chr1 . name
1027
+ const chr2Name = zoomData . chr2 . name
1028
+
1029
+ const sameChr = zoomData . chr1 . index === zoomData . chr2 . index
1030
+
1031
+ this . ctx . save ( )
1032
+ this . ctx . lineWidth = 2
1033
+
1034
+ const renderFeatures = ( xS , xE , yS , yE ) => {
1035
+
1036
+ if ( xE < xStartBP || xS > xEndBP || yE < yStartBP || yS > yEndBP ) {
1037
+ // trivially reject
1038
+ } else {
1039
+ const w = Math . max ( 1 , ( xE - xS ) / bpPerPixel )
1040
+ const h = Math . max ( 1 , ( yE - yS ) / bpPerPixel )
1041
+ const x = Math . floor ( ( xS - xStartBP ) / bpPerPixel )
1042
+ const y = Math . floor ( ( yS - yStartBP ) / bpPerPixel )
1043
+ this . ctx . strokeRect ( x , y , w , h )
1044
+ }
1045
+
1046
+ }
1047
+
1048
+ const renderLowerFeatures = ( track2D , features ) => {
1049
+ for ( const { chr1, x1 :xS , x2 :xE , y1 :yS , y2 :yE , color } of features ) {
1050
+
1051
+ const flip = chr1Name !== chr1
1052
+
1053
+ this . ctx . strokeStyle = track2D . color || color
1054
+ renderFeatures ( xS , xE , yS , yE )
1055
+ }
1056
+ }
1057
+
1058
+ const renderUpperFeatures = ( track2D , features ) => {
1059
+ for ( const { chr1, x1 :yS , x2 :yE , y1 :xS , y2 :xE , color } of features ) {
1060
+
1061
+ const flip = chr1Name !== chr1
1062
+
1063
+ this . ctx . strokeStyle = track2D . color || color
1064
+ renderFeatures ( xS , xE , yS , yE )
1065
+ }
1066
+ }
1067
+
1068
+ for ( const track2D of track2DList ) {
1069
+
1070
+ if ( false === track2D . isVisible ) {
1071
+ continue
1072
+ }
1073
+
1074
+ const features = track2D . getFeatures ( zoomData . chr1 . name , zoomData . chr1 . name )
1075
+
1076
+ if ( features ) {
1077
+
1078
+ if ( 'COLLAPSED' === track2D . displayMode || undefined === track2D . displayMode ) {
1079
+ renderLowerFeatures ( track2D , features )
1080
+ renderUpperFeatures ( track2D , features )
1081
+ } else if ( 'lower' === track2D . displayMode ) {
1082
+ renderLowerFeatures ( track2D , features )
1083
+ } else if ( 'upper' === track2D . displayMode ) {
1084
+ renderUpperFeatures ( track2D , features )
1085
+ }
1086
+
1087
+ }
1088
+
1089
+
1090
+ }
1091
+
1092
+ this . ctx . restore ( )
1093
+
1094
+ }
1026
1095
}
1027
1096
1028
1097
ContactMatrixView . defaultBackgroundColor = { r : 255 , g : 255 , b : 255 }
@@ -1079,7 +1148,6 @@ function getMatrices(chr1, chr2) {
1079
1148
return Promise . all ( promises )
1080
1149
}
1081
1150
1082
-
1083
1151
function computePercentile ( records , p ) {
1084
1152
const counts = records . map ( r => r . counts )
1085
1153
counts . sort ( function ( a , b ) {
0 commit comments