diff --git a/src/basic/bar.coffee b/src/basic/bar.coffee index d1441be..340849c 100644 --- a/src/basic/bar.coffee +++ b/src/basic/bar.coffee @@ -45,6 +45,10 @@ class Epoch.Chart.Bar extends Epoch.Chart.Plot _isHorizontal: -> @options.orientation == 'horizontal' + # @return [Boolean] True if the chart style is stacked, false otherwise + _isStacked: -> + @options.style == 'stacked' + # @return [Function] The scale used to generate the chart's x scale. x: -> if @_isVertical() @@ -52,7 +56,10 @@ class Epoch.Chart.Bar extends Epoch.Chart.Plot .domain(Epoch.Util.domain(@getVisibleLayers())) .rangeRoundBands([0, @innerWidth()], @options.padding.group, @options.outerPadding.group) else - extent = @extent((d) -> d.y) + if @_isStacked() + extent = @extent((d) -> d.y + d.y0) + else + extent = @extent((d) -> d.y) extent[0] = Math.min(0, extent[0]) d3.scale.linear() .domain(extent) @@ -67,7 +74,10 @@ class Epoch.Chart.Bar extends Epoch.Chart.Plot # @return [Function] The y scale used to render the bar chart. y: -> if @_isVertical() - extent = @extent((d) -> d.y) + if @_isStacked() + extent = @extent((d) -> d.y + d.y0) + else + extent = @extent((d) -> d.y) extent[0] = Math.min(0, extent[0]) d3.scale.linear() .domain(extent) @@ -83,6 +93,20 @@ class Epoch.Chart.Bar extends Epoch.Chart.Plot .domain((layer.category for layer in @getVisibleLayers())) .rangeRoundBands([0, y0.rangeBand()], @options.padding.bar, @options.outerPadding.bar) + # Performs post formatted data preparation. + # Override to add stacking (y0) if needed. + # @param data Data to prepare before setting. + # @return The prepared data. + _prepareData: (data) -> + if @_isStacked() + stack = d3.layout.stack() + .values( (d) -> d.values ) + .out( (d, y0, y) -> + + ) + stack data + data + # Remaps the bar chart data into a form that is easier to display. # @return [Array] The reorganized data. _remapData: -> @@ -91,7 +115,7 @@ class Epoch.Chart.Bar extends Epoch.Chart.Plot className = 'bar ' + layer.className.replace(/\s*layer\s*/, '') for entry in layer.values map[entry.x] ?= [] - map[entry.x].push { label: layer.category, y: entry.y, className: className } + map[entry.x].push Epoch.Util.defaults(entry, { label: layer.category, className: className }) ({group: k, values: v} for own k, v of map) # Draws the bar char. @@ -104,7 +128,7 @@ class Epoch.Chart.Bar extends Epoch.Chart.Plot # Draws the bar chart with a vertical orientation _drawVertical: -> - [x0, y] = [@x(), @y()] + [x0, y, isStacked] = [@x(), @y(), @_isStacked()] x1 = @x1(x0) height = @height - @margins.top - @margins.bottom data = @_remapData() @@ -127,18 +151,32 @@ class Epoch.Chart.Bar extends Epoch.Chart.Plot rects.attr('class', (d) -> d.className) - rects.transition().duration(600) - .attr('x', (d) -> x1(d.label)) - .attr('y', (d) -> y(d.y)) - .attr('width', x1.rangeBand()) - .attr('height', (d) -> height - y(d.y)) - - rects.enter().append('rect') - .attr('class', (d) -> d.className) - .attr('x', (d) -> x1(d.label)) - .attr('y', (d) -> y(d.y)) - .attr('width', x1.rangeBand()) - .attr('height', (d) -> height - y(d.y)) + if isStacked + rects.transition().duration(600) + .attr('x', (d) -> x0(d.category)) + .attr('y', (d) -> y(d.y + d.y0)) + .attr('width', x0.rangeBand()) + .attr('height', (d) -> height - y(d.y)) + + rects.enter().append('rect') + .attr('class', (d) -> d.className) + .attr('x', (d) -> x0(d.category)) + .attr('y', (d) -> y(d.y + d.y0)) + .attr('width', x0.rangeBand()) + .attr('height', (d) -> height - y(d.y)) + else + rects.transition().duration(600) + .attr('x', (d) -> x1(d.label)) + .attr('y', (d) -> y(d.y)) + .attr('width', x1.rangeBand()) + .attr('height', (d) -> height - y(d.y)) + + rects.enter().append('rect') + .attr('class', (d) -> d.className) + .attr('x', (d) -> x1(d.label)) + .attr('y', (d) -> y(d.y)) + .attr('width', x1.rangeBand()) + .attr('height', (d) -> height - y(d.y)) rects.exit().transition() .duration(150) @@ -156,7 +194,7 @@ class Epoch.Chart.Bar extends Epoch.Chart.Plot # Draws the bar chart with a horizontal orientation _drawHorizontal: -> - [x, y0] = [@x(), @y()] + [x, y0, isStacked] = [@x(), @y(),@_isStacked()] y1 = @y1(y0) width = @width - @margins.left - @margins.right data = @_remapData() @@ -179,18 +217,32 @@ class Epoch.Chart.Bar extends Epoch.Chart.Plot rects.attr('class', (d) -> d.className) - rects.transition().duration(600) - .attr('x', (d) -> 0) - .attr('y', (d) -> y1(d.label)) - .attr('height', y1.rangeBand()) - .attr('width', (d) -> x(d.y)) - - rects.enter().append('rect') - .attr('class', (d) -> d.className) - .attr('x', (d) -> 0) - .attr('y', (d) -> y1(d.label)) - .attr('height', y1.rangeBand()) - .attr('width', (d) -> x(d.y)) + if isStacked + rects.transition().duration(600) + .attr('x', (d) -> x(d.y0)) + .attr('y', (d) -> y0(d.category)) + .attr('height', y0.rangeBand()) + .attr('width', (d) -> x(d.y)) + + rects.enter().append('rect') + .attr('class', (d) -> d.className) + .attr('x', (d) -> x(d.y0)) + .attr('y', (d) -> y0(d.category)) + .attr('height', y0.rangeBand()) + .attr('width', (d) -> x(d.y)) + else + rects.transition().duration(600) + .attr('x', (d) -> 0) + .attr('y', (d) -> y1(d.label)) + .attr('height', y1.rangeBand()) + .attr('width', (d) -> x(d.y)) + + rects.enter().append('rect') + .attr('class', (d) -> d.className) + .attr('x', (d) -> 0) + .attr('y', (d) -> y1(d.label)) + .attr('height', y1.rangeBand()) + .attr('width', (d) -> x(d.y)) rects.exit().transition() .duration(150) diff --git a/tests/render/basic/bar-stacked.html b/tests/render/basic/bar-stacked.html new file mode 100644 index 0000000..023daa8 --- /dev/null +++ b/tests/render/basic/bar-stacked.html @@ -0,0 +1,762 @@ + + + + + + + + + + + +

Basic Stacked Bar Chart Test

+ + +
    +
  1. Single Series
  2. +
  3. Single Series II
  4. +
  5. Multi Series
  6. +
  7. Multi Series II
  8. +
  9. Single Series Transition
  10. +
  11. Multi Series Transition
  12. +
  13. Single Series to Multi Series Transition
  14. +
  15. Layer Color Override
  16. +
  17. Categorical Color Switching
  18. +
  19. Multi Series without Labels
  20. +
  21. Horizontally Oriented Single Series
  22. +
  23. Horizontally Oriented Multi Series
  24. +
  25. Horizontally Oriented Multi Series Transition
  26. +
  27. Vertical to Horizontal Transition
  28. +
  29. Padding Changes
  30. +
  31. Hide/Show Layers
  32. +
  33. Data Formatting
  34. +
  35. Many bars
  36. +
+ + + +
+

1. Single Series

+

Display a plot of y = cos(x) + 1 over the range [0, 2π).

+
+
+ + + + + +
+

2. Single Series II

+

Display a plot of y = sin(x) + 1 over the range [0, 2π).

+
+
+ + + + + +
+

3. Multi-series Plot

+

+ Display a plot of the following functions stacked atop one another: +

+ over the range [0, 10). +

+
+
+ + + + + +
+

4. Multi-series Plot II

+

+ Display a plot of the following functions stacked atop one another: +

+ over the range [-1, 1). +

+
+
+ + + + + +
+

5. Single Series Transition

+

+ Correctly transition between the plots y = |x| over the range [-10, 10) and y = x2 over the range [-20, 20). The transition is initiated by pressing the buttons below the plot. +

+
+

+ + +

+
+ + + + +
+

6. Multi Series Transition

+

+ Correctly render and transition between Set A: +

+ over the range [1, 100). and Set B: + + over the range [1, 100). The transition is initiated by pressing the buttons below the plot. +
+

+ + +

+
+ + + + + +
+

7. Single Series to Multi Series Transition

+

+ Correctly transition between a single series, plotting the functions: +

+ To a multi series set, plotting the functions: + + over the range [1, 4) for all plots. The transition is initiated by pressing the buttons below the plot. +

+
+

+ + +

+
+ + + + +
+

8. Layer Color Override

+

+ Display the first layer of the plot as pink, the second layer as green, and the third layer as blue. +

+
+
+ + + + + + +
+

9. Categorical Color Switching

+

+ Change layer colors automatically when switching between the following categorical color classes on the containing element: +

+ Change the categorical colors by pressing the buttons below the chart. +

+ +
+ +

+ + + + +

+
+ + + +
+

10. Multi Series without Labels

+

+ Correctly render a multi-series plot of: +

+ where the layers are given without labels. +

+
+
+ + + +
+

11. Horizontally Oriented Single Series

+

+ Correctly render the single series plot of: +

+ using a horizontal orientation. +

+
+
+ + + +
+

12. Horizontally Oriented Multi Series

+

+ Correctly render the multi series plot of: +

+ using a horizontal orientation. +

+
+
+ + + +
+

13. Horizontally Oriented Multi Series Transition

+

+ Correctly render the Horizontally oriented multi series plot of: +

+ and transition to the single series plot: + +

+
+

+ + +

+
+ + +
+

14. Vertical to Horizontal Transition

+
+

+ + +

+
+ + +
+

15. Padding Changes

+
+

+ + +

+
+ + +
+

16. Hide/Show Layers

+
+

+ + + + | + +

+
+ + +
+

17. Data Formatting

+

Ensure the chart works with the array, tuple, and key-value data formats.

+

+
+
+
+ + +
+

18. Bar Ticks

+

Ensure that we can use ticks option with bar charts.

+

+
+ + +