linuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacos
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
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(); |
|
} |
|
}); |