diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 992333a25c2..ad9946a3078 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -162,6 +162,10 @@ module.exports = DatasetController.extend({ label: me.chart.data.labels[index] }; + if (helpers.isArray(dataset.data[index])) { + rectangle._model.borderSkipped = null; + } + me._updateElementGeometry(rectangle, index, reset); rectangle.pivot(); @@ -285,12 +289,13 @@ module.exports = DatasetController.extend({ var scale = me._getValueScale(); var isHorizontal = scale.isHorizontal(); var datasets = chart.data.datasets; - var value = +scale.getRightValue(datasets[datasetIndex].data[index]); + var value = scale._parseValue(datasets[datasetIndex].data[index]); var minBarLength = scale.options.minBarLength; var stacked = scale.options.stacked; var stack = meta.stack; - var start = 0; - var i, imeta, ivalue, base, head, size; + var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max; + var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max; + var i, imeta, ivalue, base, head, size, stackLength; if (stacked || (stacked === undefined && stack !== undefined)) { for (i = 0; i < datasetIndex; ++i) { @@ -301,8 +306,10 @@ module.exports = DatasetController.extend({ imeta.controller._getValueScaleId() === scale.id && chart.isDatasetVisible(i)) { - ivalue = +scale.getRightValue(datasets[i].data[index]); - if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { + stackLength = scale._parseValue(datasets[i].data[index]); + ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min; + + if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) { start += ivalue; } } @@ -310,12 +317,12 @@ module.exports = DatasetController.extend({ } base = scale.getPixelForValue(start); - head = scale.getPixelForValue(start + value); + head = scale.getPixelForValue(start + length); size = head - base; if (minBarLength !== undefined && Math.abs(size) < minBarLength) { size = minBarLength; - if (value >= 0 && !isHorizontal || value < 0 && isHorizontal) { + if (length >= 0 && !isHorizontal || length < 0 && isHorizontal) { head = base - minBarLength; } else { head = base + minBarLength; @@ -366,7 +373,8 @@ module.exports = DatasetController.extend({ helpers.canvas.clipArea(chart.ctx, chart.chartArea); for (; i < ilen; ++i) { - if (!isNaN(scale.getRightValue(dataset.data[i]))) { + var val = scale._parseValue(dataset.data[i]); + if (!isNaN(val.min) && !isNaN(val.max)) { rects[i].draw(); } } diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 1f65bc564b6..aa8879928a5 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -523,6 +523,10 @@ module.exports = Element.extend({ if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) { return NaN; } + // Float-bar support. Handling arrays + if (helpers.isArray(rawValue)) { + return [+this.getRightValue(rawValue[0]), +this.getRightValue(rawValue[1])]; + } // If it is in fact an object, dive in one more level if (rawValue) { if (this.isHorizontal()) { @@ -538,6 +542,40 @@ module.exports = Element.extend({ return rawValue; }, + _parseValue: function(raw) { + var value, start, end, min, max; + + if (helpers.isArray(raw)) { + value = this.getRightValue(raw); + start = value[0]; + end = value[1]; + min = Math.min(start, end); + max = Math.max(start, end); + } else { + value = +this.getRightValue(raw); + start = undefined; + end = value; + min = value; + max = value; + } + + return { + min: min, + max: max, + start: start, + end: end + }; + }, + + getScaleLabel: function(rawValue) { + var v = this._parseValue(rawValue); + if (v.start !== undefined) { + return '[' + v.start + ', ' + v.end + ']'; + } + + return +this.getRightValue(rawValue); + }, + /** * Used to get the value to display in the tooltip for the data at the given index * @param index diff --git a/src/scales/scale.linear.js b/src/scales/scale.linear.js index a2199b66e78..b1e47e3a01d 100644 --- a/src/scales/scale.linear.js +++ b/src/scales/scale.linear.js @@ -70,20 +70,25 @@ module.exports = LinearScaleBase.extend({ if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); - if (isNaN(value) || meta.data[index].hidden) { + var value = me._parseValue(rawValue); + + if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden) { return; } positiveValues[index] = positiveValues[index] || 0; negativeValues[index] = negativeValues[index] || 0; + if (value.min === 0 && !opts.ticks.beginAtZero) { + value.min = value.max; + } + if (opts.relativePoints) { positiveValues[index] = 100; - } else if (value < 0) { - negativeValues[index] += value; + } else if (value.min < 0 || value.max < 0) { + negativeValues[index] += value.min; } else { - positiveValues[index] += value; + positiveValues[index] += value.max; } }); } @@ -102,21 +107,18 @@ module.exports = LinearScaleBase.extend({ var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); - if (isNaN(value) || meta.data[index].hidden) { + var value = me._parseValue(rawValue); + + if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden) { return; } - if (me.min === null) { - me.min = value; - } else if (value < me.min) { - me.min = value; + if (me.min === null || me.min > value.min) { + me.min = value.min; } - if (me.max === null) { - me.max = value; - } else if (value > me.max) { - me.max = value; + if (me.max === null || me.max < value.max) { + me.max = value.max; } }); } @@ -151,7 +153,7 @@ module.exports = LinearScaleBase.extend({ }, getLabelForIndex: function(index, datasetIndex) { - return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + return this.getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]); }, // Utils diff --git a/src/scales/scale.logarithmic.js b/src/scales/scale.logarithmic.js index fd67f0b19a4..9f9252d773b 100644 --- a/src/scales/scale.logarithmic.js +++ b/src/scales/scale.logarithmic.js @@ -118,13 +118,13 @@ module.exports = Scale.extend({ helpers.each(dataset.data, function(rawValue, index) { var values = valuesPerStack[key]; - var value = +me.getRightValue(rawValue); + var value = me._parseValue(rawValue); // invalid, hidden and negative values are ignored - if (isNaN(value) || meta.data[index].hidden || value < 0) { + if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden || value.max < 0 || value.max < 0) { return; } values[index] = values[index] || 0; - values[index] += value; + values[index] += value.max; }); } }); @@ -143,26 +143,22 @@ module.exports = Scale.extend({ var meta = chart.getDatasetMeta(datasetIndex); if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { helpers.each(dataset.data, function(rawValue, index) { - var value = +me.getRightValue(rawValue); + var value = me._parseValue(rawValue); // invalid, hidden and negative values are ignored - if (isNaN(value) || meta.data[index].hidden || value < 0) { + if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden || value.min < 0 || value.max < 0) { return; } - if (me.min === null) { - me.min = value; - } else if (value < me.min) { - me.min = value; + if (me.min === null || me.min > value.min) { + me.min = value.min; } - if (me.max === null) { - me.max = value; - } else if (value > me.max) { - me.max = value; + if (me.max === null || me.max < value.max) { + me.max = value.max; } - if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { - me.minNotZero = value; + if (value.min !== 0 && (me.minNotZero === null || value.min < me.minNotZero)) { + me.minNotZero = value.min; } }); } @@ -247,7 +243,7 @@ module.exports = Scale.extend({ // Get the correct tooltip label getLabelForIndex: function(index, datasetIndex) { - return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + return this.getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]); }, getPixelForTick: function(index) { diff --git a/test/fixtures/controller.bar/floatBar/float-bar-horizontal.json b/test/fixtures/controller.bar/floatBar/float-bar-horizontal.json new file mode 100644 index 00000000000..040a6867122 --- /dev/null +++ b/test/fixtures/controller.bar/floatBar/float-bar-horizontal.json @@ -0,0 +1,43 @@ +{ + "config": { + "type": "horizontalBar", + "data": { + "labels": ["2030", "2034", "2038", "2042"], + "datasets": [{ + "backgroundColor": "#FF6384", + "data": [11, [6,2], [-4,-7], -2] + }, { + "backgroundColor": "#36A2EB", + "data": [[1,2], [3,4], [-2,-3], [1,4]] + }, { + "backgroundColor": "#FFCE56", + "data": [[0,1], [1,2], [-2,-1], [1,-7]] + }] + }, + "options": { + "title": false, + "legend": false, + "scales": { + "xAxes": [{ + "ticks": { + "source": "labels", + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false, + "beginAtZero": true + } + }] + } + } + }, + "debug": false, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/controller.bar/floatBar/float-bar-horizontal.png b/test/fixtures/controller.bar/floatBar/float-bar-horizontal.png new file mode 100644 index 00000000000..389c135e6a8 Binary files /dev/null and b/test/fixtures/controller.bar/floatBar/float-bar-horizontal.png differ diff --git a/test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.json b/test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.json new file mode 100644 index 00000000000..a1f06925272 --- /dev/null +++ b/test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.json @@ -0,0 +1,45 @@ +{ + "config": { + "type": "horizontalBar", + "data": { + "labels": ["2030", "2034", "2038", "2042"], + "datasets": [{ + "backgroundColor": "#FF6384", + "data": [11, [6,2], [-4,-7], -2] + }, { + "backgroundColor": "#36A2EB", + "data": [[1,2], [3,4], [-2,-3], [1,4]] + }, { + "backgroundColor": "#FFCE56", + "data": [[0,1], [1,2], [-2,-1], [1,-7]] + }] + }, + "options": { + "title": false, + "legend": false, + "scales": { + "xAxes": [{ + "stacked": true, + "ticks": { + "source": "labels", + "display": false + } + }], + "yAxes": [{ + "stacked": true, + "ticks": { + "display": false, + "beginAtZero": true + } + }] + } + } + }, + "debug": false, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.png b/test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.png new file mode 100644 index 00000000000..40207caa269 Binary files /dev/null and b/test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.png differ diff --git a/test/fixtures/controller.bar/floatBar/float-bar-stacked.json b/test/fixtures/controller.bar/floatBar/float-bar-stacked.json new file mode 100644 index 00000000000..d721f3f1079 --- /dev/null +++ b/test/fixtures/controller.bar/floatBar/float-bar-stacked.json @@ -0,0 +1,43 @@ +{ + "config": { + "type": "bar", + "data": { + "labels": ["2030", "2034", "2038", "2042"], + "datasets": [{ + "backgroundColor": "#FF6384", + "data": [11, [6,2], [-4,-7], -2] + }, { + "backgroundColor": "#36A2EB", + "data": [[1,2], [3,4], [-2,-3], [1,4]] + }, { + "backgroundColor": "#FFCE56", + "data": [[0,1], [1,2], [-2,-1], [1,-7]] + }] + }, + "options": { + "title": false, + "legend": false, + "scales": { + "xAxes": [{ + "stacked": true, + "ticks": { + "display": false + } + }], + "yAxes": [{ + "stacked": true, + "ticks": { + "display": false + } + }] + } + } + }, + "debug": false, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/controller.bar/floatBar/float-bar-stacked.png b/test/fixtures/controller.bar/floatBar/float-bar-stacked.png new file mode 100644 index 00000000000..2ad01619382 Binary files /dev/null and b/test/fixtures/controller.bar/floatBar/float-bar-stacked.png differ diff --git a/test/fixtures/controller.bar/floatBar/float-bar.json b/test/fixtures/controller.bar/floatBar/float-bar.json new file mode 100644 index 00000000000..2e81c6491a1 --- /dev/null +++ b/test/fixtures/controller.bar/floatBar/float-bar.json @@ -0,0 +1,43 @@ +{ + "config": { + "type": "bar", + "data": { + "labels": ["2030", "2034", "2038", "2042"], + "datasets": [{ + "backgroundColor": "#FF6384", + "data": [11, [6,2], [-4,-7], -2] + }, { + "backgroundColor": "#36A2EB", + "data": [[1,2], [3,4], [-2,-3], [1,4]] + }, { + "backgroundColor": "#FFCE56", + "data": [[0,1], [1,2], [-2,-1], [1,-7]] + }] + }, + "options": { + "title": false, + "legend": false, + "scales": { + "xAxes": [{ + "ticks": { + "source": "labels", + "display": false + } + }], + "yAxes": [{ + "ticks": { + "display": false, + "beginAtZero": true + } + }] + } + } + }, + "debug": false, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/controller.bar/floatBar/float-bar.png b/test/fixtures/controller.bar/floatBar/float-bar.png new file mode 100644 index 00000000000..3b1f72df9b8 Binary files /dev/null and b/test/fixtures/controller.bar/floatBar/float-bar.png differ