Форк Rambox
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

313 lines
11 KiB

/**
* @class Ext.sparkline.Box
* Generates a box plot graph from the provided {@link #values} array.
*
* See <a href="http://en.wikipedia.org/wiki/Box_plot">Wikipedia Box Plots</a>
*
* See {@link Ext.sparkline.Base the base class} for a simple example.
*/
Ext.define('Ext.sparkline.Box', {
extend: 'Ext.sparkline.Base',
alias: 'widget.sparklinebox',
config: {
/**
* @cfg {Boolean} [raw=false] By default the points are calciulated from the input values array. Set this to true to passthe pre-calculated points in the values config.
*/
raw: false,
/**
* @cfg {String} [boxLineColor=#000] The color of the box outline.
*/
boxLineColor: '#000',
/**
* @cfg {String} [boxFillColor=#cdf] The color of the box fill.
*/
boxFillColor: '#cdf',
/**
* @cfg {String} [whiskerColor=#000] The color of the whiskers.
*/
whiskerColor: '#000',
/**
* @cfg {String} [outlierLineColor=#333] The color of the outlier circles' outline.
*/
outlierLineColor: '#333',
/**
* @cfg {String} [outlierFillColor=#fff] The fill color of the outlier circles.
*/
outlierFillColor: '#fff',
/**
* @cfg {String} [medianColor=#f00] The color of the median line.
*/
medianColor: '#f00',
/**
* @cfg {Boolean} [showOutliers=true] Configure as `false` to not show outlier circles.
*/
showOutliers: true,
/**
* @cfg {Number} [outlierIQR=1.5] The inter-quartile range multipler used to calculate values that qualify as an outlier.
*/
outlierIQR: 1.5,
/**
* @cfg {Number} [spotRadius=1.5] The radius of the outlier circles.
*/
spotRadius: 1.5,
/**
* @cfg {Number} [target] If set, a crosshair will be drawn at the specified value point.
*/
target: null,
/**
* @cfg {Number} [targetColor=#4a2] The color of the crosshair drawn at the pointe specified by {@link #target}.
*/
targetColor: '#4a2',
/**
* @cfg {Number} [chartRangeMin] The minimum value to use for the range of Y values of the chart - Defaults to the minimum value supplied.
*/
chartRangeMin: null,
/**
* @cfg {Number} [chartRangeMax] The maximum value to use for the range of Y values of the chart - Defaults to the minimum value supplied.
*/
chartRangeMax: null,
tipTpl: new Ext.XTemplate('{field:this.fields}: {value}', {
fields: function(v) {
var fields = {
lq: 'Lower Quartile',
med: 'Median',
uq: 'Upper Quartile',
lo: 'Left Outlier',
ro: 'Right Outlier',
lw: 'Left Whisker',
rw: 'Right Whisker'
};
return fields[v];
}
}),
tooltipFormatFieldlistKey: 'field'
},
quartile: function (values, q) {
var vl;
if (q === 2) {
vl = Math.floor(values.length / 2);
return values.length % 2 ? values[vl] : (values[vl-1] + values[vl]) / 2;
} else {
if (values.length % 2 ) { // odd
vl = (values.length * q + q) / 4;
return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl-1];
} else { //even
vl = (values.length * q + 2) / 4;
return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl-1];
}
}
},
// Ensure values is an array of numbers
applyValues: function(newValues) {
newValues = Ext.Array.map(Ext.Array.from(newValues), Number);
this.disabled = !(newValues && newValues.length);
return newValues;
},
/*
* Simulate a single region
*/
getRegion: function () {
return 1;
},
getRegionFields: function () {
var result = [
{ field: 'lq', value: this.quartiles[0] },
{ field: 'med', value: this.quartiles[1] },
{ field: 'uq', value: this.quartiles[2] }
];
if (this.loutlier !== undefined) {
result.push({ field: 'lo', value: this.loutlier});
}
if (this.routlier !== undefined) {
result.push({ field: 'ro', value: this.routlier});
}
if (this.lwhisker !== undefined) {
result.push({ field: 'lw', value: this.lwhisker});
}
if (this.rwhisker !== undefined) {
result.push({ field: 'rw', value: this.rwhisker});
}
return result;
},
renderHighlight: Ext.emptyFn,
renderGraph: function () {
var me = this,
canvas = me.canvas,
values = me.values,
vlen = values.length,
canvasWidth = me.getWidth(),
canvasHeight = me.getHeight(),
chartRangeMin = me.getChartRangeMin(),
chartRangeMax = me.getChartRangeMax(),
minValue = chartRangeMin == null ? Math.min.apply(Math, values) : chartRangeMin,
maxValue = chartRangeMax == null ? Math.max.apply(Math, values) : chartRangeMax,
canvasLeft = 0,
lwhisker, loutlier, iqr, q1, q2, q3, rwhisker, routlier, i,
size, unitSize,
spotRadius = me.getSpotRadius(),
outlierLineColor = me.getOutlierLineColor(),
outlierFillColor = me.getOutlierFillColor(),
showOutliers = me.getShowOutliers(),
outlierIQR = me.getOutlierIQR(),
lineColor = me.getLineColor(),
whiskerColor = me.getWhiskerColor(),
targetColor = me.getTargetColor();
if (!me.callParent()) {
return;
}
if (me.raw) {
if (showOutliers && values.length > 5) {
loutlier = values[0];
lwhisker = values[1];
q1 = values[2];
q2 = values[3];
q3 = values[4];
rwhisker = values[5];
routlier = values[6];
} else {
lwhisker = values[0];
q1 = values[1];
q2 = values[2];
q3 = values[3];
rwhisker = values[4];
}
} else {
values.sort(function (a, b) { return a - b; });
q1 = me.quartile(values, 1);
q2 = me.quartile(values, 2);
q3 = me.quartile(values, 3);
iqr = q3 - q1;
if (showOutliers) {
lwhisker = rwhisker = null;
for (i = 0; i < vlen; i++) {
if (lwhisker == null && values[i] > q1 - (iqr * outlierIQR)) {
lwhisker = values[i];
}
if (values[i] < q3 + (iqr * outlierIQR)) {
rwhisker = values[i];
}
}
loutlier = values[0];
routlier = values[vlen - 1];
} else {
lwhisker = values[0];
rwhisker = values[vlen - 1];
}
}
me.quartiles = [q1, q2, q3];
me.lwhisker = lwhisker;
me.rwhisker = rwhisker;
me.loutlier = loutlier;
me.routlier = routlier;
unitSize = canvasWidth / (maxValue - minValue + 1);
if (showOutliers) {
canvasLeft = Math.ceil(spotRadius);
canvasWidth -= 2 * Math.ceil(spotRadius);
unitSize = canvasWidth / (maxValue - minValue + 1);
if (loutlier < lwhisker) {
canvas.drawCircle((loutlier - minValue) * unitSize + canvasLeft,
canvasHeight / 2,
spotRadius,
outlierLineColor,
outlierFillColor).append();
}
if (routlier > rwhisker) {
canvas.drawCircle((routlier - minValue) * unitSize + canvasLeft,
canvasHeight / 2,
spotRadius,
outlierLineColor,
outlierFillColor).append();
}
}
// box
canvas.drawRect(
Math.round((q1 - minValue) * unitSize + canvasLeft),
Math.round(canvasHeight * 0.1),
Math.round((q3 - q1) * unitSize),
Math.round(canvasHeight * 0.8),
me.getBoxLineColor(),
me.getBoxFillColor()).append();
// left whisker
canvas.drawLine(
Math.round((lwhisker - minValue) * unitSize + canvasLeft),
Math.round(canvasHeight / 2),
Math.round((q1 - minValue) * unitSize + canvasLeft),
Math.round(canvasHeight / 2),
lineColor).append();
canvas.drawLine(
Math.round((lwhisker - minValue) * unitSize + canvasLeft),
Math.round(canvasHeight / 4),
Math.round((lwhisker - minValue) * unitSize + canvasLeft),
Math.round(canvasHeight - canvasHeight / 4),
whiskerColor).append();
// right whisker
canvas.drawLine(Math.round((rwhisker - minValue) * unitSize + canvasLeft),
Math.round(canvasHeight / 2),
Math.round((q3 - minValue) * unitSize + canvasLeft),
Math.round(canvasHeight / 2),
lineColor).append();
canvas.drawLine(
Math.round((rwhisker - minValue) * unitSize + canvasLeft),
Math.round(canvasHeight / 4),
Math.round((rwhisker - minValue) * unitSize + canvasLeft),
Math.round(canvasHeight - canvasHeight / 4),
whiskerColor).append();
// median line
canvas.drawLine(
Math.round((q2 - minValue) * unitSize + canvasLeft),
Math.round(canvasHeight * 0.1),
Math.round((q2 - minValue) * unitSize + canvasLeft),
Math.round(canvasHeight * 0.9),
me.getMedianColor()).append();
if (me.target) {
size = Math.ceil(me.spotRadius);
canvas.drawLine(
Math.round((me.target - minValue) * unitSize + canvasLeft),
Math.round((canvasHeight / 2) - size),
Math.round((me.target - minValue) * unitSize + canvasLeft),
Math.round((canvasHeight / 2) + size),
targetColor).append();
canvas.drawLine(
Math.round((me.target - minValue) * unitSize + canvasLeft - size),
Math.round(canvasHeight / 2),
Math.round((me.target - minValue) * unitSize + canvasLeft + size),
Math.round(canvasHeight / 2),
targetColor).append();
}
// If mouse is over, re-apply the highlight
if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) {
me.currentRegion = null;
me.updateDisplay();
}
canvas.render();
}
});