From 755af6e9d4a027c7aa2c77b2e4c5fbb1abf5b291 Mon Sep 17 00:00:00 2001 From: Bob Monteverde Date: Tue, 26 Jun 2012 03:57:01 -0400 Subject: [PATCH] Issue #11 major refactor of stackedArea, still need to tweak some things, BUT now works with custom accessors --- deprecated/stackedArea.js | 286 +++++++++++++++++++++++++++++++ deprecated/stackedAreaChart.html | 183 ++++++++++++++++++++ examples/stackedAreaChart.html | 20 ++- nv.d3.js | 231 ++++++++++++++++++------- nv.d3.min.js | 2 +- src/models/stackedArea.js | 137 ++++++++------- 6 files changed, 727 insertions(+), 132 deletions(-) create mode 100644 deprecated/stackedArea.js create mode 100644 deprecated/stackedAreaChart.html diff --git a/deprecated/stackedArea.js b/deprecated/stackedArea.js new file mode 100644 index 0000000..c5851cb --- /dev/null +++ b/deprecated/stackedArea.js @@ -0,0 +1,286 @@ + +nv.models.stackedArea = function() { + //Default Settings + var margin = {top: 0, right: 0, bottom: 0, left: 0}, + width = 960, + height = 500, + color = d3.scale.category20().range(), // array of colors to be used in order + id = Math.floor(Math.random() * 100000), //Create semi-unique ID incase user doesn't selet one + getX = function(d) { return d.x }, // accessor to get the x value from a data point + getY = function(d) { return d.y }, // accessor to get the y value from a data point + style = 'stack', + offset = 'zero', + order = 'default', + clipEdge = false; // if true, masks lines within x and y scale + +/************************************ + * offset: + * 'wiggle' (stream) + * 'zero' (stacked) + * 'expand' (normalize to 100%) + * 'silhouette' (simple centered) + * + * order: + * 'inside-out' (stream) + * 'default' (input order) + ************************************/ + + var scatter= nv.models.scatter() + .size(2.2) // default size + .sizeDomain([2.5]), //set to speed up calculation, needs to be unset if there is a cstom size accessor + x = scatter.xScale(), + y = scatter.yScale(), + dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'areaClick', 'areaMouseover', 'areaMouseout'); + + function chart(selection) { + selection.each(function(data) { + // Need to leave data alone to switch between stacked, stream, and expanded + var dataCopy = JSON.parse(JSON.stringify(data)), + availableWidth = width - margin.left - margin.right, + availableHeight = height - margin.top - margin.bottom; + + //TODO: try to get rid of the need for copying the data, and use the data directly + + //console.log(dataCopy); + dataCopy = dataCopy.map(function(series,i) { + if (series.disabled) + series.values = series.values.map(function(d,i) { + d._y = d.y || d._y; + d.y = 0; //TODO: need to use value from getY, not always d.y + return d + }); + return series; + }); + + + //TODO: deal with negative stacked charts + + //compute the data based on offset and order (calc's y0 for every point) + dataCopy = d3.layout.stack() + .offset(offset) + .order(order) + .values(function(d){ return d.values }) + .y(getY) + (dataCopy); + + + + var wrap = d3.select(this).selectAll('g.wrap.stackedarea').data([dataCopy]); + var wrapEnter = wrap.enter().append('g').attr('class', 'wrap nvd3 stackedarea'); + var defsEnter = wrapEnter.append('defs'); + var gEnter = wrapEnter.append('g'); + var g = wrap.select('g'); + + gEnter.append('g').attr('class', 'areaWrap'); + + + scatter + .width(availableWidth) + .height(availableHeight) + //.x(getX) + .y(function(d) { return d.y + d.y0 }) // TODO: allow for getY to be other than d.y + .forceY([0]) + .color(dataCopy.map(function(d,i) { + return d.color || color[i % color.length]; + }).filter(function(d,i) { return !dataCopy[i].disabled })); + + gEnter.append('g').attr('class', 'scatterWrap'); + var scatterWrap= g.select('.scatterWrap') + .datum(dataCopy.filter(function(d) { return !d.disabled })) + + d3.transition(scatterWrap).call(scatter); + + + + + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + + + defsEnter.append('clipPath') + .attr('id', 'edge-clip-' + id) + .append('rect'); + + wrap.select('#edge-clip-' + id + ' rect') + .attr('width', availableWidth) + .attr('height', availableHeight); + + g .attr('clip-path', clipEdge ? 'url(#edge-clip-' + id + ')' : ''); + + + + + var area = d3.svg.area() + .x(function(d,i) { return x(scatter.x()(d,i)) }) + .y0(function(d) { return y(d.y0) }) + .y1(function(d) { return y(d.y + d.y0) }); + + var zeroArea = d3.svg.area() + .x(function(d,i) { return x(scatter.x()(d,i)) }) + .y0(function(d) { return y(d.y0) }) + .y1(function(d) { return y(d.y0) }); + + + var path = g.select('.areaWrap').selectAll('path.area') + .data(function(d) { return d }); + //.data(function(d) { return d }, function(d) { return d.key }); + path.enter().append('path').attr('class', function(d,i) { return 'area area-' + i }) + .on('mouseover', function(d,i) { + d3.select(this).classed('hover', true); + dispatch.areaMouseover({ + point: d, + series: d.key, + //pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top], + pos: [d3.event.pageX, d3.event.pageY], + seriesIndex: i + //pointIndex: d.point + }); + }) + .on('mouseout', function(d,i) { + d3.select(this).classed('hover', false); + dispatch.areaMouseout({ + point: d, + series: d.key, + //pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top], + pos: [d3.event.pageX, d3.event.pageY], + seriesIndex: i + //pointIndex: d.point + }); + }) + .on('click', function(d,i) { + d3.select(this).classed('hover', false); + dispatch.areaClick({ + point: d, + series: d.key, + //pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top], + pos: [d3.event.pageX, d3.event.pageY], + seriesIndex: i + //pointIndex: d.point + }); + }) + d3.transition(path.exit()) + .attr('d', function(d,i) { return zeroArea(d.values,i) }) // TODO: fix this so transition is still fluid + .remove(); + path + .style('fill', function(d,i){ return d.color || color[i % color.length] }) + .style('stroke', function(d,i){ return d.color || color[i % color.length] }); + d3.transition(path) + .attr('d', function(d,i) { return area(d.values,i) }) + + + scatter.dispatch.on('elementClick.area', function(e) { + dispatch.areaClick(e); + }) + scatter.dispatch.on('elementMouseover.area', function(e) { + g.select('.area-' + e.seriesIndex).classed('hover', true); + }); + scatter.dispatch.on('elementMouseout.area', function(e) { + g.select('.area-' + e.seriesIndex).classed('hover', false); + }); + + }); + + + return chart; + } + + + chart.dispatch = dispatch; + chart.scatter = scatter; + + d3.rebind(chart, scatter, 'x', 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius'); + + /* + chart.x = function(_) { + if (!arguments.length) return getX; + getX = d3.functor(_); + scatter.x(_); + return chart; + }; + */ + + chart.y = function(_) { + if (!arguments.length) return getY; + getY = d3.functor(_); + //scatter.y(_); + return chart; + } + + chart.margin = function(_) { + if (!arguments.length) return margin; + margin = _; + return chart; + }; + + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; + + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; + + chart.clipEdge = function(_) { + if (!arguments.length) return clipEdge; + clipEdge = _; + return chart; + }; + + chart.color = function(_) { + if (!arguments.length) return color; + color = _; + return chart; + }; + + chart.offset = function(_) { + if (!arguments.length) return offset; + offset = _; + return chart; + }; + + chart.order = function(_) { + if (!arguments.length) return order; + order = _; + return chart; + }; + + //shortcut for offset + order + chart.style = function(_) { + if (!arguments.length) return style; + style = _; + + switch (style) { + case 'stack': + offset = 'zero'; + order = 'default'; + break; + case 'stream': + offset = 'wiggle'; + order = 'inside-out'; + break; + case 'expand': + offset = 'expand'; + order = 'default'; + break; + } + + return chart; + }; + + + + scatter.dispatch.on('elementMouseover.tooltip', function(e) { + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top], + dispatch.tooltipShow(e); + }); + + scatter.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); + + + return chart; +} diff --git a/deprecated/stackedAreaChart.html b/deprecated/stackedAreaChart.html new file mode 100644 index 0000000..340d742 --- /dev/null +++ b/deprecated/stackedAreaChart.html @@ -0,0 +1,183 @@ + + + + + + + + +
+ +
+ +
+ +
+ + + + + + + + + + + diff --git a/examples/stackedAreaChart.html b/examples/stackedAreaChart.html index 9d49d8a..3cbdac3 100644 --- a/examples/stackedAreaChart.html +++ b/examples/stackedAreaChart.html @@ -35,7 +35,7 @@ text { - +