/** * @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 calculated from the * input values array. Set this to true to pass the 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 multiplier 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 {String} [targetColor=#4a2] The color of the crosshair drawn at the point * 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: ['{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); // If we are calculating the values, they need to be sorted if (!this.raw) { newValues.sort(function(a, b) { return a - b; }); } this.disabled = !(newValues && newValues.length); this.updateConfigChange(); 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 { 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.canvasRegion.contains(me.currentPageXY)) { me.currentRegion = null; me.updateDisplay(); } canvas.render(); }});