diff --git a/dc.js b/dc.js index b06fd0896..75d9f2c52 100644 --- a/dc.js +++ b/dc.js @@ -4195,9 +4195,11 @@ dc.barChart = function (parent, chartGroup) { var _gap = DEFAULT_GAP_BETWEEN_BARS; var _centerBar = false; var _alwaysUseRounding = false; - + var _barWidth; + var _renderType = 'stack'; + dc.override(_chart, 'rescale', function () { _chart._rescale(); _barWidth = undefined; @@ -4232,10 +4234,15 @@ dc.barChart = function (parent, chartGroup) { }); }; - function barHeight(d) { + function stackBarHeight(d) { return dc.utils.safeNumber(Math.abs(_chart.y()(d.y + d.y0) - _chart.y()(d.y0))); } + function groupBarHeight(d){ + var margin = _chart.margins(); + return dc.utils.safeNumber(Math.abs(_chart.height() - margin.top - margin.bottom - _chart.y()(d.y))); + } + function renderBars(layer, layerIndex, d) { var bars = layer.selectAll('rect.bar') .data(d.values, dc.pluck('x')); @@ -4254,34 +4261,88 @@ dc.barChart = function (parent, chartGroup) { if (_chart.isOrdinal()) { bars.on('click', _chart.onClick); } - - dc.transition(bars, _chart.transitionDuration()) - .attr('x', function (d) { - var x = _chart.x()(d.x); - if (_centerBar) { - x -= _barWidth / 2; - } - if (_chart.isOrdinal() && _gap !== undefined) { - x += _gap / 2; - } - return dc.utils.safeNumber(x); + + if(_renderType === 'stack'){ + dc.transition(bars, _chart.transitionDuration()) + .attr('x', function (d) { + var x = _chart.x()(d.x); + if (_centerBar) { + x -= _barWidth / 2; + } + if (_chart.isOrdinal() && _gap !== undefined) { + x += _gap / 2; + } + return dc.utils.safeNumber(x); + }) + .attr('y', function (d) { + var y = _chart.y()(d.y + d.y0); + + if (d.y < 0) { + y -= stackBarHeight(d); + } + + return dc.utils.safeNumber(y); + }) + .attr('width', _barWidth) + .attr('height', function (d) { + return stackBarHeight(d); + }) + .attr('fill', dc.pluck('data', _chart.getColor)) + .select('title').text(dc.pluck('data', _chart.title(d.name))); + + } + else if(_renderType === 'group'){ + var groups = _chart.stack().map(function(d){ return d.name;}); + var groupRange = d3.scale.ordinal().domain(groups).rangePoints([0, _barWidth - _barWidth/2]); + dc.transition(bars, _chart.transitionDuration()) + .attr('x', function(d, i){ + var x = _chart.x()(d.x) + groupRange(d.layer); + if (_centerBar) { + x -= _barWidth / 2; + } + if (_chart.isOrdinal() && _gap !== undefined) { + x += _gap / 2; + } + return dc.utils.safeNumber(x); }) - .attr('y', function (d) { - var y = _chart.y()(d.y + d.y0); - - if (d.y < 0) { - y -= barHeight(d); - } - + .attr('width', (_barWidth/_chart.stack().length) - 3) + .attr('y', function(d) { + var y = _chart.y()(d.y); + + if (d.y < 0) { + y -= groupBarHeight(d); + } return dc.utils.safeNumber(y); }) - .attr('width', _barWidth) - .attr('height', function (d) { - return barHeight(d); + .attr('height', function(d){ + return groupBarHeight(d); + }); + } + else if(_renderType === 'overlap'){ + dc.transition(bars, _chart.transitionDuration()) + .attr('x', function(d, i){ + var x = _chart.x()(d.x); + if (_centerBar) { + x -= _barWidth / 2; + } + if (_chart.isOrdinal() && _gap !== undefined) { + x += _gap / 2; + } + return dc.utils.safeNumber(x); }) - .attr('fill', dc.pluck('data', _chart.getColor)) - .select('title').text(dc.pluck('data', _chart.title(d.name))); - + .attr('y', function(d) { + var y = _chart.y()(d.y); + + if (d.y < 0) { + y -= groupBarHeight(d); + } + return dc.utils.safeNumber(y); + }) + .attr('width', _barWidth) + .attr('height', function(d){ + return groupBarHeight(d); + }); + } dc.transition(bars.exit(), _chart.transitionDuration()) .attr('height', 0) .remove(); @@ -4453,6 +4514,14 @@ dc.barChart = function (parent, chartGroup) { .classed('fadeout', false); }; + _chart.renderType = function(_rendtype){ + if(!arguments.length){ + return _renderType; + } + _renderType = _rendtype; + return _chart; + }; + dc.override(_chart, 'xAxisMax', function () { var max = this._xAxisMax(); if ('resolution' in _chart.xUnits()) { @@ -4462,6 +4531,24 @@ dc.barChart = function (parent, chartGroup) { return max; }); + dc.override(_chart, 'yAxisMax', function(){ + var max; + if(_renderType === 'stack'){ + max = d3.max(flattenStack(), function(p){ + return p.y + p.y0; + }); + } + else if(_renderType === 'group' || _renderType === 'overlap'){ + max = d3.max(flattenStack(), dc.pluck('y')); + } + return dc.utils.add(max, _chart.yAxisPadding()); + }); + + // Not dry but need flattenStack here and don't want to expose it, possible move to util + function flattenStack() { + var valueses = _chart.data().map(function (layer) { return layer.values; }); + return Array.prototype.concat.apply([], valueses); + } return _chart.anchor(parent, chartGroup); }; diff --git a/src/bar-chart.js b/src/bar-chart.js index 4655a83d2..e1a9df004 100644 --- a/src/bar-chart.js +++ b/src/bar-chart.js @@ -42,9 +42,11 @@ dc.barChart = function (parent, chartGroup) { var _gap = DEFAULT_GAP_BETWEEN_BARS; var _centerBar = false; var _alwaysUseRounding = false; - + var _barWidth; + var _renderType = 'stack'; + dc.override(_chart, 'rescale', function () { _chart._rescale(); _barWidth = undefined; @@ -79,10 +81,15 @@ dc.barChart = function (parent, chartGroup) { }); }; - function barHeight(d) { + function stackBarHeight(d) { return dc.utils.safeNumber(Math.abs(_chart.y()(d.y + d.y0) - _chart.y()(d.y0))); } + function groupBarHeight(d){ + var margin = _chart.margins(); + return dc.utils.safeNumber(Math.abs(_chart.height() - margin.top - margin.bottom - _chart.y()(d.y))); + } + function renderBars(layer, layerIndex, d) { var bars = layer.selectAll('rect.bar') .data(d.values, dc.pluck('x')); @@ -101,34 +108,88 @@ dc.barChart = function (parent, chartGroup) { if (_chart.isOrdinal()) { bars.on('click', _chart.onClick); } + + if(_renderType === 'stack'){ + dc.transition(bars, _chart.transitionDuration()) + .attr('x', function (d) { + var x = _chart.x()(d.x); + if (_centerBar) { + x -= _barWidth / 2; + } + if (_chart.isOrdinal() && _gap !== undefined) { + x += _gap / 2; + } + return dc.utils.safeNumber(x); + }) + .attr('y', function (d) { + var y = _chart.y()(d.y + d.y0); + + if (d.y < 0) { + y -= stackBarHeight(d); + } + + return dc.utils.safeNumber(y); + }) + .attr('width', _barWidth) + .attr('height', function (d) { + return stackBarHeight(d); + }) + .attr('fill', dc.pluck('data', _chart.getColor)) + .select('title').text(dc.pluck('data', _chart.title(d.name))); - dc.transition(bars, _chart.transitionDuration()) - .attr('x', function (d) { - var x = _chart.x()(d.x); - if (_centerBar) { - x -= _barWidth / 2; - } - if (_chart.isOrdinal() && _gap !== undefined) { - x += _gap / 2; - } - return dc.utils.safeNumber(x); + } + else if(_renderType === 'group'){ + var groups = _chart.stack().map(function(d){ return d.name;}); + var groupRange = d3.scale.ordinal().domain(groups).rangePoints([0, _barWidth - _barWidth/2]); + dc.transition(bars, _chart.transitionDuration()) + .attr('x', function(d, i){ + var x = _chart.x()(d.x) + groupRange(d.layer); + if (_centerBar) { + x -= _barWidth / 2; + } + if (_chart.isOrdinal() && _gap !== undefined) { + x += _gap / 2; + } + return dc.utils.safeNumber(x); }) - .attr('y', function (d) { - var y = _chart.y()(d.y + d.y0); - - if (d.y < 0) { - y -= barHeight(d); - } - + .attr('width', (_barWidth/_chart.stack().length) - 3) + .attr('y', function(d) { + var y = _chart.y()(d.y); + + if (d.y < 0) { + y -= groupBarHeight(d); + } return dc.utils.safeNumber(y); }) - .attr('width', _barWidth) - .attr('height', function (d) { - return barHeight(d); + .attr('height', function(d){ + return groupBarHeight(d); + }); + } + else if(_renderType === 'overlap'){ + dc.transition(bars, _chart.transitionDuration()) + .attr('x', function(d, i){ + var x = _chart.x()(d.x); + if (_centerBar) { + x -= _barWidth / 2; + } + if (_chart.isOrdinal() && _gap !== undefined) { + x += _gap / 2; + } + return dc.utils.safeNumber(x); }) - .attr('fill', dc.pluck('data', _chart.getColor)) - .select('title').text(dc.pluck('data', _chart.title(d.name))); - + .attr('y', function(d) { + var y = _chart.y()(d.y); + + if (d.y < 0) { + y -= groupBarHeight(d); + } + return dc.utils.safeNumber(y); + }) + .attr('width', _barWidth) + .attr('height', function(d){ + return groupBarHeight(d); + }); + } dc.transition(bars.exit(), _chart.transitionDuration()) .attr('height', 0) .remove(); @@ -300,6 +361,14 @@ dc.barChart = function (parent, chartGroup) { .classed('fadeout', false); }; + _chart.renderType = function(_rendtype){ + if(!arguments.length){ + return _renderType; + } + _renderType = _rendtype; + return _chart; + }; + dc.override(_chart, 'xAxisMax', function () { var max = this._xAxisMax(); if ('resolution' in _chart.xUnits()) { @@ -309,5 +378,23 @@ dc.barChart = function (parent, chartGroup) { return max; }); + dc.override(_chart, 'yAxisMax', function(){ + var max; + if(_renderType === 'stack'){ + max = d3.max(flattenStack(), function(p){ + return p.y + p.y0; + }); + } + else if(_renderType === 'group' || _renderType === 'overlap'){ + max = d3.max(flattenStack(), dc.pluck('y')); + } + return dc.utils.add(max, _chart.yAxisPadding()); + }); + + // Not dry but need flattenStack here and don't want to expose it, possible move to util + function flattenStack() { + var valueses = _chart.data().map(function (layer) { return layer.values; }); + return Array.prototype.concat.apply([], valueses); + } return _chart.anchor(parent, chartGroup); };