@gmod/jbrowse
Version:
JBrowse - client-side genome browser
183 lines (166 loc) • 6.15 kB
JavaScript
/**
* The scaling used for drawing a Wiggle track, which is the data's
* origin.
*
* Has numeric attributes range, min, max, origin, and offset.
*/
define([
'dojo/_base/lang',
'JBrowse/Util',
'JBrowse/Digest/Crc32'
],
function( lang,
Util,
Digest
) {
return Util.fastDeclare({
// Returns a boolean value saying whether a stats object is needed
// to calculate the scale for the given configuration.
//
// This is invokable either on the class (prototype) or on
// the object itself, so does not use `this` in its implementation.
needStats: function( config ) {
return !(
( 'min_score' in config )
&& ( 'max_score' in config )
&& ( config.bicolor_pivot != 'z_score' && config.bicolor_pivot != 'mean' )
&& ( config.scale != 'z_score' )
);
},
constructor: function( config, stats ) {
var needStats = this.needStats( config );
if( needStats && !stats )
throw 'No stats object provided, cannot calculate scale';
if( needStats && stats.scoreMin == stats.scoreMax ) {
stats = lang.mixin( {}, stats );
if( stats.scoreMin < 0 )
stats.scoreMax = 0;
else
stats.scoreMin = 0;
}
// if either autoscale or scale is set to z_score, the other one should default to z_score
if( config.autoscale == 'z_score' && ! config.scale
|| config.scale == 'z_score' && !config.autoscale
) {
config.scale = 'z_score';
config.autoscale = 'z_score';
}
var z_score_bound = parseFloat( config.z_score_bound ) || 4;
var min = 'min_score' in config ? parseFloat( config.min_score ) :
(function() {
switch( config.autoscale ) {
case 'z_score':
return Math.max( -z_score_bound, (stats.scoreMin-stats.scoreMean) / stats.scoreStdDev );
case 'global':
case 'local':
return stats.scoreMin;
case 'clipped_global':
/* fall through */
default:
return Math.max( stats.scoreMin, stats.scoreMean - z_score_bound * stats.scoreStdDev );
}
})();
var max = 'max_score' in config ? parseFloat( config.max_score ) :
(function() {
switch( config.autoscale ) {
case 'z_score':
return Math.min( z_score_bound, (stats.scoreMax-stats.scoreMean) / stats.scoreStdDev );
case 'global':
case 'local':
return stats.scoreMax;
case 'clipped_global':
/* fall through */
default:
return Math.min( stats.scoreMax, stats.scoreMean + z_score_bound * stats.scoreStdDev );
}
})();
if( typeof min != 'number' || isNaN(min) ) {
min = 0;
}
if( typeof max != 'number' || isNaN(max) ) {
max = min + 10;
}
var offset = parseFloat( config.data_offset ) || 0;
if( config.scale == 'log' ) {
max = this.log( max + offset );
min = this.log( min + offset );
}
else {
max += offset;
min += offset;
}
var origin = (function() {
if ( 'bicolor_pivot' in config ) {
if ( config.bicolor_pivot == 'mean' ) {
return stats.scoreMean || 0;
} else if ( config.bicolor_pivot == 'zero' ) {
return 0;
} else {
return parseFloat( config.bicolor_pivot );
}
} else if ( config.scale == 'z_score' ) {
return stats.scoreMean || 0;
} else if ( config.scale == 'log' ) {
return 1;
} else {
return 0;
}
})();
lang.mixin( this, {
offset: offset,
min: min,
max: max,
range: max - min,
origin: origin,
_statsFingerprint: Digest.objectFingerprint( stats )
});
if( needStats ) {
this.scoreMean = stats.scoreMean;
this.scoreStdDev = stats.scoreStdDev;
}
// make this.normalize a func that converts wiggle values to a
// range between 0 and 1, depending on what kind of scale we
// are using
this.normalize = (function(config) {
switch( config.scale ) {
case 'z_score':
return function( value ) {
return (value+this.offset-this.scoreMean) / this.scoreStdDev-this.min / this.range;
};
case 'log':
return function( value ) {
return ( this.log(value+this.offset) - this.min )/this.range;
};
case 'linear':
/* fall through */
default:
return function( value ) {
return ( value + this.offset - this.min ) / this.range;
};
}
})(config);
},
log: function( value ) {
return value ? Math.log( Math.abs( value ) ) * ( value < 0 ? -1 : 1 )
: 0;
},
/**
* Standard comparison function, compare this scale to another one.
*/
compare: function( b ) {
if( ! b ) return 1;
var a = this;
return ( a.offset - b.offset )
|| ( a.min - b.min )
|| ( a.max - b.max )
|| ( a.range - b.range )
|| ( a.origin - b.origin );
},
/**
* Return true if this scaling was generated from the same set of stats.
*/
sameStats: function( stats ) {
return this._statsFingerprint == Digest.objectFingerprint( stats );
}
});
});