@gmod/jbrowse
Version:
JBrowse - client-side genome browser
190 lines (162 loc) • 6.47 kB
JavaScript
define([
'dojo/_base/declare',
'dojo/_base/array',
'dojo/aspect',
'JBrowse/Component'
],
function(
declare,
array,
aspect,
Component
) {
return declare( Component, {
constructor: function( args ) {
this.track = args.track;
this.booleanAlpha = 0.17;
// This allows any features that are completely masked to have their transparency set before being rendered,
// saving the hassle of clearing and rerendering later on.
aspect.before(this, 'renderFeature',
function( context, fRect ) {
if (fRect.m) {
var l = Math.floor(fRect.l);
var w = Math.ceil(fRect.w + fRect.l) - l;
fRect.m.sort(function(a,b) { return a.l - b.l; });
var m = fRect.m[0];
if (m.l <= l) {
// Determine whether the feature is entirely masked.
var end = fRect.m[0].l;
for(var i in fRect.m) {
var m = fRect.m[i];
if(m.l > end)
break;
end = m.l + m.w;
}
if(end >= l + w) {
context.globalAlpha = this.booleanAlpha;
fRect.noMask = true;
}
}
}
}, true);
// after rendering the features, do masking if required
aspect.after(this, 'renderFeature',
function( context, fRect ) {
if (fRect.m && !fRect.noMask) {
this.maskBySpans( context, fRect );
} else if ( fRect.noMask) {
delete fRect.noMask;
context.globalAlpha = 1;
}
}, true);
},
getStyle: function( feature, name ) {
return this.getConfForFeature( 'style.'+name, feature );
},
/**
* Like getConf, but get a conf value that explicitly can vary
* feature by feature. Provides a uniform function signature for
* user-defined callbacks.
*/
getConfForFeature: function( path, feature ) {
return this.getConf( path, [feature, path, this, this.track ] );
},
mouseoverFeature: function( context, fRect ) {
this.renderFeature( context, fRect );
// highlight the feature rectangle if we're moused over
context.fillStyle = this.getStyle( fRect.f, 'mouseovercolor' );
context.fillRect( fRect.rect.l, fRect.t, fRect.rect.w, fRect.rect.h );
},
/**
* Get the dimensions of the rendered feature in pixels.
*/
_getFeatureRectangle: function( viewInfo, feature ) {
var block = viewInfo.block;
var fRect = {
l: block.bpToX( feature.get('start') ),
h: this._getFeatureHeight( viewInfo, feature ),
viewInfo: viewInfo,
f: feature,
glyph: this
};
fRect.w = block.bpToX( feature.get('end') ) - fRect.l;
this._addMasksToRect( viewInfo, feature, fRect );
},
_addMasksToRect: function( viewArgs, feature, fRect ) {
// if the feature has masks, add them to the fRect.
var block = viewArgs.block;
if( feature.masks ) {
fRect.m = [];
array.forEach( feature.masks, function(m) {
var tempM = { l: block.bpToX( m.start ) };
tempM.w = block.bpToX( m.end ) - tempM.l;
fRect.m.push(tempM);
});
}
return fRect;
},
layoutFeature: function( viewArgs, layout, feature ) {
var fRect = this._getFeatureRectangle( viewArgs, feature );
var scale = viewArgs.scale;
var leftBase = viewArgs.leftBase;
var startbp = fRect.l/scale + leftBase;
var endbp = (fRect.l+fRect.w)/scale + leftBase;
fRect.t = layout.addRect(
feature.id(),
startbp,
endbp,
fRect.h,
feature
);
if( fRect.t === null )
return null;
fRect.f = feature;
return fRect;
},
//stub
renderFeature: function( context, fRect ) {
},
/* If it's a boolean track, mask accordingly */
maskBySpans: function( context, fRect ) {
var canvasHeight = context.canvas.height;
var thisB = this;
// make a temporary canvas to store image data
var tempCan = dojo.create( 'canvas', {height: canvasHeight, width: context.canvas.width} );
var ctx2 = tempCan.getContext('2d');
var l = Math.floor(fRect.l);
var w = Math.ceil(fRect.w + fRect.l) - l;
/* note on the above: the rightmost pixel is determined
by l+w. If either of these is a float, then canvas
methods will not behave as desired (i.e. clear and
draw will not treat borders in the same way).*/
array.forEach( fRect.m, function(m) { try {
if ( m.l < l ) {
m.w += m.l-l;
m.l = l;
}
if ( m.w > w )
m.w = w;
if ( m.l < 0 ) {
m.w += m.l;
m.l = 0;
}
if ( m.l + m.w > l + w )
m.w = w + l - m.l;
if ( m.l + m.w > context.canvas.width )
m.w = context.canvas.width-m.l;
ctx2.drawImage(context.canvas, m.l, fRect.t, m.w, fRect.h, m.l, fRect.t, m.w, fRect.h);
context.globalAlpha = thisB.booleanAlpha;
// clear masked region and redraw at lower opacity.
context.clearRect(m.l, fRect.t, m.w, fRect.h);
context.drawImage(tempCan, m.l, fRect.t, m.w, fRect.h, m.l, fRect.t, m.w, fRect.h);
context.globalAlpha = 1;
} catch(e) {};
});
},
_getFeatureHeight: function( viewArgs, feature ) {
return this.getStyle( feature, 'height');
},
updateStaticElements: function( context, fRect, viewArgs ) {
}
});
});