@gmod/jbrowse
Version:
JBrowse - client-side genome browser
165 lines (140 loc) • 6.03 kB
JavaScript
define([
'dojo/_base/declare',
'dojo/_base/lang',
'dojo/_base/array',
'JBrowse/View/FeatureGlyph/Box',
'JBrowse/View/FeatureGlyph/UnprocessedTranscript',
'JBrowse/View/FeatureGlyph/ProcessedTranscript'
],
function(
declare,
lang,
array,
BoxGlyph,
UnprocessedTranscript,
ProcessedTranscriptGlyph
) {
return declare( BoxGlyph, {
_defaultConfig: function() {
return this._mergeConfigs(
this.inherited(arguments),
{
transcriptType: 'mRNA',
noncodingType: ['ncRNA', 'lnc_RNA', 'lncRNA', 'miRNA'],
style: {
transcriptLabelFont: 'normal 10px Univers,Helvetica,Arial,sans-serif',
transcriptLabelColor: 'black',
textFont: 'bold 12px Univers,Helvetica,Arial,sans-serif'
},
labelTranscripts: true,
marginBottom: 0
});
},
_boxGlyph: function() {
return this.__boxGlyph || ( this.__boxGlyph = new BoxGlyph({ track: this.track, browser: this.browser, config: this.config }) );
},
_ntGlyph: function() {
return this.__ntGlyph || ( this.__ntGlyph = new UnprocessedTranscript({ track: this.track, browser: this.browser, config: this.config }) );
},
_ptGlyph: function() {
return this.__ptGlyph || ( this.__ptGlyph = new ProcessedTranscriptGlyph({ track: this.track, browser: this.browser, config: this.config }) );
},
_getFeatureRectangle( viewArgs, feature ) {
// we need to lay out rects for each of the subfeatures
var subArgs = lang.mixin( {}, viewArgs );
subArgs.showDescriptions = subArgs.showLabels = false;
var subfeatures = feature.children();
// if this gene weirdly has no subfeatures, just render as a box
if (!subfeatures || !subfeatures.length) return this.inherited(arguments)
// get the rects for the children
var padding = 1;
var fRect = {
l: 0,
h: 0,
r: 0,
w: 0,
subRects: [],
viewInfo: viewArgs,
f: feature,
glyph: this
};
if( subfeatures && subfeatures.length ) {
// sort the children by name
subfeatures.sort( function( a, b ) { return (a.get('name') || a.get('id') || '').localeCompare( b.get('name') || b.get('id') || '' ); } );
fRect.l = Infinity;
fRect.r = -Infinity;
var transcriptType = this.getConfForFeature( 'transcriptType', feature );
var noncodingType = this.getConfForFeature( 'noncodingType', feature );
for( var i = 0; i < subfeatures.length; i++ ) {
var subRect = ( subfeatures[i].get('type') == transcriptType
? this._ptGlyph()
: (noncodingType.includes(subfeatures[i].get('type')) ? this._ntGlyph() : this._boxGlyph())
)._getFeatureRectangle( subArgs, subfeatures[i] );
padding = i == subfeatures.length-1 ? 0 : 1;
subRect.t = subRect.rect.t = fRect.h && viewArgs.displayMode != 'collapsed' ? fRect.h+padding : 0;
if( viewArgs.showLabels && this.getConfForFeature( 'labelTranscripts', subfeatures[i] ) ) {
var transcriptLabel = this.makeSideLabel(
this.getFeatureLabel(subfeatures[i]),
this.getStyle( subfeatures[i], 'transcriptLabelFont'),
subRect
);
if( transcriptLabel ) {
transcriptLabel.fill = this.getStyle( subfeatures[i], 'transcriptLabelColor' );
subRect.label = transcriptLabel;
subRect.l -= transcriptLabel.w;
subRect.w += transcriptLabel.w;
if( transcriptLabel.h > subRect.h )
subRect.h = transcriptLabel.h;
transcriptLabel.yOffset = Math.floor(subRect.h/2);
transcriptLabel.xOffset = 0;
}
}
fRect.subRects.push( subRect );
fRect.r = Math.max( fRect.r, subRect.l+subRect.w-1 );
fRect.l = Math.min( fRect.l, subRect.l );
fRect.h = subRect.t+subRect.h+padding;
}
}
// calculate the width
fRect.w = Math.max( fRect.r - fRect.l + 1, 2 );
delete fRect.r;
fRect.rect = { l: fRect.l, h: fRect.h, w: fRect.w };
if( viewArgs.displayMode != 'compact' )
fRect.h += this.getStyle( feature, 'marginBottom' ) || 0;
// no labels or descriptions if displayMode is collapsed, so stop here
if( viewArgs.displayMode == "collapsed")
return fRect;
// expand the fRect to accommodate labels if necessary
this._expandRectangleWithLabels( viewArgs, feature, fRect );
this._addMasksToRect( viewArgs, feature, fRect );
return fRect;
},
layoutFeature: function( viewInfo, layout, feature ) {
var fRect = this.inherited( arguments );
if( fRect )
array.forEach( fRect.subRects, function( subrect ) {
subrect.t += fRect.t;
subrect.rect.t += fRect.t;
});
return fRect;
},
renderFeature( context, fRect ) {
const subRects = fRect.subRects;
if (!subRects || subRects.length === 0) return this.inherited(arguments)
if( fRect.viewInfo.displayMode != 'collapsed' )
context.clearRect( Math.floor(fRect.l), fRect.t, Math.ceil(fRect.w-Math.floor(fRect.l)+fRect.l), fRect.h );
for( var i = 0; i < subRects.length; i++ ) {
subRects[i].glyph.renderFeature( context, subRects[i] );
}
this.renderLabel( context, fRect );
this.renderDescription( context, fRect );
},
updateStaticElements: function( context, fRect, viewArgs ) {
this.inherited( arguments );
var subRects = fRect.subRects || [];
for( var i = 0; i < subRects.length; i++ ) {
subRects[i].glyph.updateStaticElements( context, subRects[i], viewArgs );
}
}
});
});