UNPKG

@gmod/jbrowse

Version:

JBrowse - client-side genome browser

197 lines (177 loc) 7.71 kB
define([ 'dojo/_base/declare', 'dojo/_base/array', 'dojo/_base/lang', 'JBrowse/View/FeatureGlyph/Alignment', 'JBrowse/View/FeatureGlyph/AlignmentColoring', 'JBrowse/Util' ], function( declare, array, lang, Alignment, AlignmentColoring, Util ) { return declare(Alignment, { clearFeat(context, fRect) { if( this.track.displayMode != 'collapsed' ) context.clearRect( Math.floor(fRect.l), fRect.t, Math.ceil(fRect.w), fRect.h ); }, renderFeature(context, fRect) { const f = fRect.f this.clearFeat(context, fRect) if (f.pairedFeature()) { this.renderConnector( context, fRect ) this.renderSegments( context, fRect ) if (fRect.w > 2) { if (fRect.viewInfo.scale > 0.2) { this._drawMismatches( context, fRect, this._getMismatches( f.read1 ), f.read1 ) this._drawMismatches( context, fRect, this._getMismatches( f.read2 ), f.read2 ) } else { this._drawMismatches( context, fRect, this._getSkipsAndDeletions( f.read1 ), f.read1 ) this._drawMismatches( context, fRect, this._getSkipsAndDeletions( f.read2 ), f.read2 ) } } const x1 = f.read1.get('start') const x2 = f.read1.get('end') const y1 = f.read2.get('start') const y2 = f.read2.get('end') if(Util.intersect(x1, x2, y1, y2)) { const s1 = x2 > y2 ? x1 : y1 const s2 = x1 > y1 ? y2 : x2 const block = fRect.viewInfo.block const l = block.bpToX(s1) const r = block.bpToX(s2) // avoid drawing small overlaps if(r - l > 2) { context.fillStyle = this.getStyle(f, 'overlapColor') context.fillRect( l, // left fRect.rect.t, r-l, // width fRect.rect.h) var s = this.getStyle(f, 'overlapStroke') if(s) { context.strokeStyle = s context.strokeRect( l, // left fRect.rect.t, r-l, // width fRect.rect.h) } if(fRect.viewInfo.scale > 0.2) { var m1 = this._getMismatches( f.read1 ); var m2 = this._getMismatches( f.read2 ); if(!m1 && !m2) { return; } else { var mismatches = []; for(var i = 0; i < m1.length; i++) { let foundMatching = false; for(var j = 0; j < m2.length; j++) { if(x1+m1[i].start == y1+m2[j].start && s1 <= x1+m1[i].start && s2 >= x1+m1[i].start) { foundMatching = true mismatches.push({ start: m1[i].start, base1: m1[i].base, base2: m2[j].base, type: 'mismatch', length: 1 }); } } if(m1[i].type == 'mismatch' && s1 <= x1+m1[i].start && s2 >= x1+m1[i].start && !foundMatching ) { mismatches.push({ start: m1[i].start, base1: m1[i].base, base2: '-', type: 'mismatch', length: 1 }); } } if(mismatches.length !== 0) { this._drawOverlappingMismatches(context, fRect, mismatches, x1) } mismatches = []; for(var i = 0; i < m2.length; i++) { let foundMatching = false; for(var j = 0; j < m1.length; j++) { if(x1+m1[j].start == y1+m2[i].start && s1 <= y1+m2[i].start && s2 >= y1+m2[i].start) { // mismatches.push({ start: m2[i].start, base1: m2[i].base, base2: m1[j].base, type: 'mismatch', length: 1 }); // would have been found in above loop also previous iteration foundMatching = true } } if(m2[i].type == 'mismatch' && s1 <= y1+m2[i].start && s2 >= y1+m2[i].start && !foundMatching) { mismatches.push({ start: m2[i].start, base1: m2[i].base, base2: '-', type: 'mismatch', length: 1 }); } } if(mismatches.length !== 0) { this._drawOverlappingMismatches(context, fRect, mismatches, y1) } } } } } } else { this.inherited(arguments) } }, renderSegments(context, fRect) { this.renderBox(context, fRect.viewInfo, fRect.f.read1, fRect.t, fRect.rect.h, fRect.f); this.renderBox(context, fRect.viewInfo, fRect.f.read2, fRect.t, fRect.rect.h, fRect.f); }, renderConnector(context, fRect) { // connector var connectorColor = this.getStyle( fRect.f, 'connectorColor' ); if (connectorColor) { context.fillStyle = connectorColor; var connectorThickness = this.getStyle( fRect.f, 'connectorThickness' ); context.fillRect( fRect.rect.l, // left Math.round(fRect.rect.t+(fRect.rect.h-connectorThickness)/2), // top fRect.rect.w, // width connectorThickness ); } }, // draw both gaps and mismatches _drawOverlappingMismatches(context, fRect, mismatches, fstart) { var block = fRect.viewInfo.block; var scale = block.scale; var charSize = this.getCharacterMeasurements( context ); context.textBaseline = 'middle'; // reset to alphabetic (the default) after loop array.forEach( mismatches, function( mismatch ) { var start = fstart + mismatch.start; var end = fstart + mismatch.start + mismatch.length; var mRect = { h: (fRect.rect||{}).h || fRect.h, l: block.bpToX( start ), t: fRect.rect.t }; mRect.w = Math.max( block.bpToX( end ) - mRect.l, 1 ); if( mismatch.type == 'mismatch' ) { if(mismatch.base1 == mismatch.base2) { context.fillStyle = this.track.colorForBase( mismatch.type == 'deletion' ? 'deletion' : mismatch.base1 ); } else { context.fillStyle = 'black'; } context.fillRect( mRect.l, mRect.t, mRect.w, mRect.h ); if( mRect.w >= charSize.w && mRect.h >= charSize.h-3 ) { context.font = this.config.style.mismatchFont; if(mismatch.base1 == mismatch.base2) { context.fillStyle = 'black' context.fillText( mismatch.base1, mRect.l+(mRect.w-charSize.w)/2+1, mRect.t+mRect.h/2 ); } else if(scale >= 10) { context.fillStyle = 'white' context.fillText( mismatch.base1 + '/' +mismatch.base2, mRect.l+(mRect.w-charSize.w*2)/2+1, mRect.t+mRect.h/2 ); } } } },this); context.textBaseline = 'alphabetic'; }, _defaultConfig() { return this._mergeConfigs(dojo.clone( this.inherited(arguments) ), { style: { connectorColor: AlignmentColoring.connectorColor, connectorThickness: 1, overlapColor: 'lightgrey', overlapStroke: 'grey' } }); } }); });