Skip to content

Commit

Permalink
Fixed miscalculation of Bar width.
Browse files Browse the repository at this point in the history
for Bar and horizontalBar type,
include stacked scale.
issue chartjs#3589
  • Loading branch information
KoyoSE authored and etimberg committed Dec 2, 2016
1 parent 8c61d38 commit 71235cb
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 33 deletions.
39 changes: 8 additions & 31 deletions src/controllers/controller.bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ module.exports = function(Chart) {
rectangle._datasetIndex = me.index;
rectangle._index = index;

var ruler = me.getRuler(index);
var ruler = me.getRuler(index); // The index argument for compatible
rectangle._model = {
x: me.calculateBarX(index, me.index, ruler),
y: reset ? scaleBase : me.calculateBarY(index, me.index),
Expand Down Expand Up @@ -121,29 +121,17 @@ module.exports = function(Chart) {
return yScale.getBasePixel();
},

getRuler: function(index) {
getRuler: function() {
var me = this;
var meta = me.getMeta();
var xScale = me.getScaleForId(meta.xAxisID);
var datasetCount = me.getBarCount();

var tickWidth;

if (xScale.options.type === 'category') {
tickWidth = xScale.getPixelForTick(index + 1) - xScale.getPixelForTick(index);
} else {
// Average width
tickWidth = xScale.width / xScale.ticks.length;
}
var tickWidth = xScale.width / xScale.ticks.length;
var categoryWidth = tickWidth * xScale.options.categoryPercentage;
var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2;
var fullBarWidth = categoryWidth / datasetCount;

if (xScale.ticks.length !== me.chart.data.labels.length) {
var perc = xScale.ticks.length / me.chart.data.labels.length;
fullBarWidth = fullBarWidth * perc;
}

var barWidth = fullBarWidth * xScale.options.barPercentage;
var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage);

Expand All @@ -163,7 +151,7 @@ module.exports = function(Chart) {
if (xScale.options.barThickness) {
return xScale.options.barThickness;
}
return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth;
return xScale.options.stacked ? ruler.categoryWidth * xScale.options.barPercentage : ruler.barWidth;
},

// Get bar index from the given dataset index accounting for the fact that not all bars are visible
Expand Down Expand Up @@ -346,7 +334,7 @@ module.exports = function(Chart) {
rectangle._datasetIndex = me.index;
rectangle._index = index;

var ruler = me.getRuler(index);
var ruler = me.getRuler(index); // The index argument for compatible
rectangle._model = {
x: reset ? scaleBase : me.calculateBarX(index, me.index),
y: me.calculateBarY(index, me.index, ruler),
Expand Down Expand Up @@ -449,28 +437,17 @@ module.exports = function(Chart) {
return xScale.getBasePixel();
},

getRuler: function(index) {
getRuler: function() {
var me = this;
var meta = me.getMeta();
var yScale = me.getScaleForId(meta.yAxisID);
var datasetCount = me.getBarCount();

var tickHeight;
if (yScale.options.type === 'category') {
tickHeight = yScale.getPixelForTick(index + 1) - yScale.getPixelForTick(index);
} else {
// Average width
tickHeight = yScale.width / yScale.ticks.length;
}
var tickHeight = yScale.height / yScale.ticks.length;
var categoryHeight = tickHeight * yScale.options.categoryPercentage;
var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2;
var fullBarHeight = categoryHeight / datasetCount;

if (yScale.ticks.length !== me.chart.data.labels.length) {
var perc = yScale.ticks.length / me.chart.data.labels.length;
fullBarHeight = fullBarHeight * perc;
}

var barHeight = fullBarHeight * yScale.options.barPercentage;
var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage);

Expand All @@ -491,7 +468,7 @@ module.exports = function(Chart) {
if (yScale.options.barThickness) {
return yScale.options.barThickness;
}
return yScale.options.stacked ? ruler.categoryHeight : ruler.barHeight;
return yScale.options.stacked ? ruler.categoryHeight * yScale.options.barPercentage : ruler.barHeight;
},

calculateBarX: function(index, datasetIndex) {
Expand Down
156 changes: 154 additions & 2 deletions test/controller.bar.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ describe('Bar controller tests', function() {
scales: {
xAxes: [{
type: 'category',
stacked: true
stacked: true,
barPercentage: 1
}],
yAxes: [{
type: 'linear',
Expand Down Expand Up @@ -296,7 +297,8 @@ describe('Bar controller tests', function() {
scales: {
xAxes: [{
type: 'category',
stacked: true
stacked: true,
barPercentage: 1
}],
yAxes: [{
type: 'linear',
Expand Down Expand Up @@ -487,4 +489,154 @@ describe('Bar controller tests', function() {
expect(bar._model.borderColor).toBe('rgb(0, 255, 0)');
expect(bar._model.borderWidth).toBe(1.5);
});

describe('Bar width', function() {
beforeEach(function() {
// 2 datasets
this.data = {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
data: [10, 20, 30, 40, 50, 60, 70],
}, {
data: [10, 20, 30, 40, 50, 60, 70],
}]
};
});

afterEach(function() {
var chart = window.acquireChart(this.config);
var meta = chart.getDatasetMeta(0);
var xScale = chart.scales[meta.xAxisID];

var categoryPercentage = xScale.options.categoryPercentage;
var barPercentage = xScale.options.barPercentage;
var stacked = xScale.options.stacked;

var totalBarWidth = 0;
for (var i = 0; i < chart.data.datasets.length; i++) {
var bars = chart.getDatasetMeta(i).data;
for (var j = xScale.minIndex; j <= xScale.maxIndex; j++) {
totalBarWidth += bars[j]._model.width;
}
if (stacked) {
break;
}
}

var actualValue = totalBarWidth;
var expectedValue = xScale.width * categoryPercentage * barPercentage;
expect(actualValue).toBeCloseToPixel(expectedValue);

});

it('should correctly set bar width when min and max option is set.', function() {
this.config = {
type: 'bar',
data: this.data,
options: {
scales: {
xAxes: [{
ticks: {
min: 'March',
max: 'May',
},
}]
}
}
};
});

it('should correctly set bar width when scale are stacked with min and max options.', function() {
this.config = {
type: 'bar',
data: this.data,
options: {
scales: {
xAxes: [{
ticks: {
min: 'March',
max: 'May',
},
stacked: true,
}]
}
}
};
});
});

describe('Bar height (horizontalBar type)', function() {
beforeEach(function() {
// 2 datasets
this.data = {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
data: [10, 20, 30, 40, 50, 60, 70],
}, {
data: [10, 20, 30, 40, 50, 60, 70],
}]
};
});

afterEach(function() {
var chart = window.acquireChart(this.config);
var meta = chart.getDatasetMeta(0);
var yScale = chart.scales[meta.yAxisID];

var categoryPercentage = yScale.options.categoryPercentage;
var barPercentage = yScale.options.barPercentage;
var stacked = yScale.options.stacked;

var totalBarHeight = 0;
for (var i = 0; i < chart.data.datasets.length; i++) {
var bars = chart.getDatasetMeta(i).data;
for (var j = yScale.minIndex; j <= yScale.maxIndex; j++) {
totalBarHeight += bars[j]._model.height;
}
if (stacked) {
break;
}
}

var actualValue = totalBarHeight;
var expectedValue = yScale.height * categoryPercentage * barPercentage;
expect(actualValue).toBeCloseToPixel(expectedValue);

});

it('should correctly set bar height when min and max option is set.', function() {
this.config = {
type: 'horizontalBar',
data: this.data,
options: {
scales: {
yAxes: [{
ticks: {
min: 'March',
max: 'May',
},
}]
}
}
};
});

it('should correctly set bar height when scale are stacked with min and max options.', function() {
this.config = {
type: 'horizontalBar',
data: this.data,
options: {
scales: {
yAxes: [{
ticks: {
min: 'March',
max: 'May',
},
stacked: true,
}]
}
}
};
});
});
});

0 comments on commit 71235cb

Please sign in to comment.