diff options
Diffstat (limited to '')
-rw-r--r-- | debian/missing-sources/epoch/src/basic.coffee | 243 | ||||
-rw-r--r-- | debian/missing-sources/epoch/src/basic/area.coffee | 51 | ||||
-rw-r--r-- | debian/missing-sources/epoch/src/basic/bar.coffee | 273 | ||||
-rw-r--r-- | debian/missing-sources/epoch/src/basic/histogram.coffee | 61 | ||||
-rw-r--r-- | debian/missing-sources/epoch/src/basic/line.coffee | 46 | ||||
-rw-r--r-- | debian/missing-sources/epoch/src/basic/pie.coffee | 59 | ||||
-rw-r--r-- | debian/missing-sources/epoch/src/basic/scatter.coffee | 59 |
7 files changed, 792 insertions, 0 deletions
diff --git a/debian/missing-sources/epoch/src/basic.coffee b/debian/missing-sources/epoch/src/basic.coffee new file mode 100644 index 0000000..8af3465 --- /dev/null +++ b/debian/missing-sources/epoch/src/basic.coffee @@ -0,0 +1,243 @@ +# Base class for all two-dimensional basic d3 charts. This class handles axes and +# margins so that subclasses can focus on the construction of particular chart +# types. +class Epoch.Chart.Plot extends Epoch.Chart.SVG + defaults = + domain: null, + range: null, + axes: ['left', 'bottom'] + ticks: + top: 14 + bottom: 14 + left: 5 + right: 5 + tickFormats: + top: Epoch.Formats.regular + bottom: Epoch.Formats.regular + left: Epoch.Formats.si + right: Epoch.Formats.si + + defaultAxisMargins = + top: 25 + right: 50 + bottom: 25 + left: 50 + + optionListeners = + 'option:margins.top': 'marginsChanged' + 'option:margins.right': 'marginsChanged' + 'option:margins.bottom': 'marginsChanged' + 'option:margins.left': 'marginsChanged' + 'option:axes': 'axesChanged' + 'option:ticks.top': 'ticksChanged' + 'option:ticks.right': 'ticksChanged' + 'option:ticks.bottom': 'ticksChanged' + 'option:ticks.left': 'ticksChanged' + 'option:tickFormats.top': 'tickFormatsChanged' + 'option:tickFormats.right': 'tickFormatsChanged' + 'option:tickFormats.bottom': 'tickFormatsChanged' + 'option:tickFormats.left': 'tickFormatsChanged' + 'option:domain': 'domainChanged' + 'option:range': 'rangeChanged' + + # Creates a new plot chart. + # @param [Object] options Options to use when constructing the plot. + # @option options [Object] margins For setting explicit values for the top, + # right, bottom, and left margins in the visualization. Normally these can + # be omitted and the class will set appropriately sized margins given which + # axes are specified. + # @option options [Array] axes A list of axes to display (top, left, bottom, right). + # @option options [Object] ticks Number of ticks to place on the top, left bottom + # and right axes. + # @option options [Object] tickFormats What tick formatting functions to use for + # the top, bottom, left, and right axes. + constructor: (@options={}) -> + givenMargins = Epoch.Util.copy(@options.margins) or {} + super(@options = Epoch.Util.defaults(@options, defaults)) + + # Margins are used in a special way and only for making room for axes. + # However, a user may explicitly set margins in the options, so we need + # to determine if they did so, and zero out the ones they didn't if no + # axis is present. + @margins = {} + for pos in ['top', 'right', 'bottom', 'left'] + @margins[pos] = if @options.margins? and @options.margins[pos]? + @options.margins[pos] + else if @hasAxis(pos) + defaultAxisMargins[pos] + else + 6 + + # Add a translation for the top and left margins + @g = @svg.append("g") + .attr("transform", "translate(#{@margins.left}, #{@margins.top})") + + # Register option change events + @onAll optionListeners + + # Sets the tick formatting function to use on the given axis. + # @param [String] axis Name of the axis. + # @param [Function] fn Formatting function to use. + setTickFormat: (axis, fn) -> + @options.tickFormats[axis] = fn + + # @return [Boolean] <code>true</code> if the chart has an axis with a given name, <code>false</code> otherwise. + # @param [String] axis Name of axis to check. + hasAxis: (axis) -> + @options.axes.indexOf(axis) > -1 + + # @return [Number] Width of the visualization portion of the chart (width - margins). + innerWidth: -> + @width - (@margins.left + @margins.right) + + # @return [Number] Height of the visualization portion of the chart (height - margins). + innerHeight: -> + @height - (@margins.top + @margins.bottom) + + # @return [Function] The x scale for the visualization. + x: -> + domain = @options.domain ? @extent((d) -> d.x) + d3.scale.linear() + .domain(domain) + .range([0, @innerWidth()]) + + # @return [Function] The y scale for the visualization. + y: (givenDomain) -> + d3.scale.linear() + .domain(@_getScaleDomain(givenDomain)) + .range([@innerHeight(), 0]) + + # @return [Function] d3 axis to use for the bottom of the visualization. + bottomAxis: -> + d3.svg.axis().scale(@x()).orient('bottom') + .ticks(@options.ticks.bottom) + .tickFormat(@options.tickFormats.bottom) + + # @return [Function] d3 axis to use for the top of the visualization. + topAxis: -> + d3.svg.axis().scale(@x()).orient('top') + .ticks(@options.ticks.top) + .tickFormat(@options.tickFormats.top) + + # @return [Function] d3 axis to use on the left of the visualization. + leftAxis: -> + range = if @options.range then @options.range.left else null + d3.svg.axis().scale(@y(range)).orient('left') + .ticks(@options.ticks.left) + .tickFormat(@options.tickFormats.left) + + # @return [Function] d3 axis to use on the right of the visualization. + rightAxis: -> + range = if @options.range then @options.range.right else null + d3.svg.axis().scale(@y(range)).orient('right') + .ticks(@options.ticks.right) + .tickFormat(@options.tickFormats.right) + + # Renders the axes for the visualization (subclasses must implement specific + # drawing routines). + draw: -> + if @_axesDrawn + @_redrawAxes() + else + @_drawAxes() + super() + + # Redraws the axes for the visualization. + _redrawAxes: -> + if @hasAxis('bottom') + @g.selectAll('.x.axis.bottom').transition() + .duration(500) + .ease('linear') + .call(@bottomAxis()) + if @hasAxis('top') + @g.selectAll('.x.axis.top').transition() + .duration(500) + .ease('linear') + .call(@topAxis()) + if @hasAxis('left') + @g.selectAll('.y.axis.left').transition() + .duration(500) + .ease('linear') + .call(@leftAxis()) + if @hasAxis('right') + @g.selectAll('.y.axis.right').transition() + .duration(500) + .ease('linear') + .call(@rightAxis()) + + # Draws the initial axes for the visualization. + _drawAxes: -> + if @hasAxis('bottom') + @g.append("g") + .attr("class", "x axis bottom") + .attr("transform", "translate(0, #{@innerHeight()})") + .call(@bottomAxis()) + if @hasAxis('top') + @g.append("g") + .attr('class', 'x axis top') + .call(@topAxis()) + if @hasAxis('left') + @g.append("g") + .attr("class", "y axis left") + .call(@leftAxis()) + if @hasAxis('right') + @g.append('g') + .attr('class', 'y axis right') + .attr('transform', "translate(#{@innerWidth()}, 0)") + .call(@rightAxis()) + @_axesDrawn = true + + dimensionsChanged: -> + super() + @g.selectAll('.axis').remove() + @_axesDrawn = false + @draw() + + # Updates margins in response to a <code>option:margin.*</code> event. + marginsChanged: -> + return unless @options.margins? + for own pos, size of @options.margins + unless size? + @margins[pos] = 6 + else + @margins[pos] = size + + @g.transition() + .duration(750) + .attr("transform", "translate(#{@margins.left}, #{@margins.top})") + + @draw() + + # Updates axes in response to a <code>option:axes</code> event. + axesChanged: -> + # Remove default axis margins + for pos in ['top', 'right', 'bottom', 'left'] + continue if @options.margins? and @options.margins[pos]? + if @hasAxis(pos) + @margins[pos] = defaultAxisMargins[pos] + else + @margins[pos] = 6 + + # Update the margin offset + @g.transition() + .duration(750) + .attr("transform", "translate(#{@margins.left}, #{@margins.top})") + + # Remove the axes and redraw + @g.selectAll('.axis').remove() + @_axesDrawn = false + @draw() + + # Updates ticks in response to a <code>option:ticks.*</code> event. + ticksChanged: -> @draw() + + # Updates tick formats in response to a <code>option:tickFormats.*</code> event. + tickFormatsChanged: -> @draw() + + # Updates chart in response to a <code>option:domain</code> event. + domainChanged: -> @draw() + + # Updates chart in response to a <code>option:range</code> event. + rangeChanged: -> @draw() + +# "They will see us waving from such great heights, come down now..." - The Postal Service diff --git a/debian/missing-sources/epoch/src/basic/area.coffee b/debian/missing-sources/epoch/src/basic/area.coffee new file mode 100644 index 0000000..e44b480 --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/area.coffee @@ -0,0 +1,51 @@ + +# Static stacked area chart implementation using d3. +class Epoch.Chart.Area extends Epoch.Chart.Plot + constructor: (@options={}) -> + @options.type ?= 'area' + super(@options) + @draw() + + # Generates a scale needed to appropriately render the stacked visualization. + # @return [Function] The y scale for the visualization. + y: -> + a = [] + for layer in @getVisibleLayers() + for own k, v of layer.values + a[k] += v.y if a[k]? + a[k] = v.y unless a[k]? + d3.scale.linear() + .domain(@options.range ? [0, d3.max(a)]) + .range([@height - @margins.top - @margins.bottom, 0]) + + # Renders the SVG elements needed to display the stacked area chart. + draw: -> + [x, y, layers] = [@x(), @y(), @getVisibleLayers()] + + @g.selectAll('.layer').remove() + return if layers.length == 0 + + area = d3.svg.area() + .x((d) -> x(d.x)) + .y0((d) -> y(d.y0)) + .y1((d) -> y(d.y0 + d.y)) + + stack = d3.layout.stack() + .values((d) -> d.values) + + data = stack layers + + layer = @g.selectAll('.layer') + .data(layers, (d) -> d.category) + + layer.select('.area') + .attr('d', (d) -> area(d.values)) + + layer.enter().append('g') + .attr('class', (d) -> d.className) + + layer.append('path') + .attr('class', 'area') + .attr('d', (d) -> area(d.values)) + + super() diff --git a/debian/missing-sources/epoch/src/basic/bar.coffee b/debian/missing-sources/epoch/src/basic/bar.coffee new file mode 100644 index 0000000..8fbc427 --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/bar.coffee @@ -0,0 +1,273 @@ +# Static bar chart implementation (using d3). +class Epoch.Chart.Bar extends Epoch.Chart.Plot + defaults = + type: 'bar' + style: 'grouped' + orientation: 'vertical' + padding: + bar: 0.08 + group: 0.1 + outerPadding: + bar: 0.08 + group: 0.1 + + horizontal_specific = + tickFormats: + top: Epoch.Formats.si + bottom: Epoch.Formats.si + left: Epoch.Formats.regular + right: Epoch.Formats.regular + + horizontal_defaults = Epoch.Util.defaults(horizontal_specific, defaults) + + optionListeners = + 'option:orientation': 'orientationChanged' + 'option:padding': 'paddingChanged' + 'option:outerPadding': 'paddingChanged' + 'option:padding:bar': 'paddingChanged' + 'option:padding:group': 'paddingChanged' + 'option:outerPadding:bar': 'paddingChanged' + 'option:outerPadding:group': 'paddingChanged' + + constructor: (@options={}) -> + if @_isHorizontal() + @options = Epoch.Util.defaults(@options, horizontal_defaults) + else + @options = Epoch.Util.defaults(@options, defaults) + super(@options) + @onAll optionListeners + @draw() + + # @return [Boolean] True if the chart is vertical, false otherwise + _isVertical: -> + @options.orientation == 'vertical' + + # @return [Boolean] True if the chart is horizontal, false otherwise + _isHorizontal: -> + @options.orientation == 'horizontal' + + # @return [Function] The scale used to generate the chart's x scale. + x: -> + if @_isVertical() + d3.scale.ordinal() + .domain(Epoch.Util.domain(@getVisibleLayers())) + .rangeRoundBands([0, @innerWidth()], @options.padding.group, @options.outerPadding.group) + else + extent = @extent((d) -> d.y) + extent[0] = Math.min(0, extent[0]) + d3.scale.linear() + .domain(extent) + .range([0, @width - @margins.left - @margins.right]) + + # @return [Function] The x scale used to render the horizontal bar chart. + x1: (x0) -> + d3.scale.ordinal() + .domain((layer.category for layer in @getVisibleLayers())) + .rangeRoundBands([0, x0.rangeBand()], @options.padding.bar, @options.outerPadding.bar) + + # @return [Function] The y scale used to render the bar chart. + y: -> + if @_isVertical() + extent = @extent((d) -> d.y) + extent[0] = Math.min(0, extent[0]) + d3.scale.linear() + .domain(extent) + .range([@height - @margins.top - @margins.bottom, 0]) + else + d3.scale.ordinal() + .domain(Epoch.Util.domain(@getVisibleLayers())) + .rangeRoundBands([0, @innerHeight()], @options.padding.group, @options.outerPadding.group) + + # @return [Function] The x scale used to render the vertical bar chart. + y1: (y0) -> + d3.scale.ordinal() + .domain((layer.category for layer in @getVisibleLayers())) + .rangeRoundBands([0, y0.rangeBand()], @options.padding.bar, @options.outerPadding.bar) + + # Remaps the bar chart data into a form that is easier to display. + # @return [Array] The reorganized data. + _remapData: -> + map = {} + for layer in @getVisibleLayers() + 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 } + ({group: k, values: v} for own k, v of map) + + # Draws the bar char. + draw: -> + if @_isVertical() + @_drawVertical() + else + @_drawHorizontal() + super() + + # Draws the bar chart with a vertical orientation + _drawVertical: -> + [x0, y] = [@x(), @y()] + x1 = @x1(x0) + height = @height - @margins.top - @margins.bottom + data = @_remapData() + + # 1) Join + layer = @g.selectAll(".layer") + .data(data, (d) -> d.group) + + # 2) Update + layer.transition().duration(750) + .attr("transform", (d) -> "translate(#{x0(d.group)}, 0)") + + # 3) Enter / Create + layer.enter().append("g") + .attr('class', 'layer') + .attr("transform", (d) -> "translate(#{x0(d.group)}, 0)") + + rects = layer.selectAll('rect') + .data((group) -> group.values) + + 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)) + + rects.exit().transition() + .duration(150) + .style('opacity', '0') + .remove() + + # 4) Update new and existing + + # 5) Exit / Remove + layer.exit() + .transition() + .duration(750) + .style('opacity', '0') + .remove() + + # Draws the bar chart with a horizontal orientation + _drawHorizontal: -> + [x, y0] = [@x(), @y()] + y1 = @y1(y0) + width = @width - @margins.left - @margins.right + data = @_remapData() + + # 1) Join + layer = @g.selectAll(".layer") + .data(data, (d) -> d.group) + + # 2) Update + layer.transition().duration(750) + .attr("transform", (d) -> "translate(0, #{y0(d.group)})") + + # 3) Enter / Create + layer.enter().append("g") + .attr('class', 'layer') + .attr("transform", (d) -> "translate(0, #{y0(d.group)})") + + rects = layer.selectAll('rect') + .data((group) -> group.values) + + 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)) + + rects.exit().transition() + .duration(150) + .style('opacity', '0') + .remove() + + # 4) Update new and existing + + # 5) Exit / Remove + layer.exit() + .transition() + .duration(750) + .style('opacity', '0') + .remove() + + # Generates specific tick marks to emulate d3's linear scale axis ticks + # for ordinal scales. Note: this should only be called if the user has + # defined a set number of ticks for a given axis. + # @param [Number] numTicks Number of ticks to generate + # @param [String] dataKey Property name of a datum to use for the tick value + # @return [Array] The ticks for the given axis + _getTickValues: (numTicks, dataKey='x') -> + return [] unless @data[0]? + total = @data[0].values.length + step = Math.ceil(total / numTicks)|0 + tickValues = (@data[0].values[i].x for i in [0...total] by step) + + # @return [Function] d3 axis to use for the bottom of the visualization. + bottomAxis: -> + axis = d3.svg.axis().scale(@x()).orient('bottom') + .ticks(@options.ticks.bottom) + .tickFormat(@options.tickFormats.bottom) + if @_isVertical() and @options.ticks.bottom? + axis.tickValues @_getTickValues(@options.ticks.bottom) + axis + + # @return [Function] d3 axis to use for the top of the visualization. + topAxis: -> + axis = d3.svg.axis().scale(@x()).orient('top') + .ticks(@options.ticks.top) + .tickFormat(@options.tickFormats.top) + if @_isVertical() and @options.ticks.top? + axis.tickValues @_getTickValues(@options.ticks.top) + axis + + # @return [Function] d3 axis to use on the left of the visualization. + leftAxis: -> + axis = d3.svg.axis().scale(@y()).orient('left') + .ticks(@options.ticks.left) + .tickFormat(@options.tickFormats.left) + if @_isHorizontal() and @options.ticks.left? + axis.tickValues @_getTickValues(@options.ticks.left) + axis + + # @return [Function] d3 axis to use on the right of the visualization. + rightAxis: -> + axis = d3.svg.axis().scale(@y()).orient('right') + .ticks(@options.ticks.right) + .tickFormat(@options.tickFormats.right) + if @_isHorizontal() and @options.ticks.right? + axis.tickValues @_getTickValues(@options.ticks.right) + axis + + # Updates orientation in response <code>option:orientation</code>. + orientationChanged: -> + top = @options.tickFormats.top + bottom = @options.tickFormats.bottom + left = @options.tickFormats.left + right = @options.tickFormats.right + + @options.tickFormats.left = top + @options.tickFormats.right = bottom + @options.tickFormats.top = left + @options.tickFormats.bottom = right + + @draw() + + # Updates padding in response to <code>option:padding:*</code> and <code>option:outerPadding:*</code>. + paddingChanged: -> @draw() diff --git a/debian/missing-sources/epoch/src/basic/histogram.coffee b/debian/missing-sources/epoch/src/basic/histogram.coffee new file mode 100644 index 0000000..4548e67 --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/histogram.coffee @@ -0,0 +1,61 @@ +class Epoch.Chart.Histogram extends Epoch.Chart.Bar + defaults = + type: 'histogram' + domain: [0, 100] + bucketRange: [0, 100] + buckets: 10 + cutOutliers: false + + optionListeners = + 'option:bucketRange': 'bucketRangeChanged' + 'option:buckets': 'bucketsChanged' + 'option:cutOutliers': 'cutOutliersChanged' + + constructor: (@options={}) -> + super(@options = Epoch.Util.defaults(@options, defaults)) + @onAll optionListeners + @draw() + + # Prepares data by sorting it into histogram buckets as instructed by the chart options. + # @param [Array] data Data to prepare for rendering. + # @return [Array] The data prepared to be displayed as a histogram. + _prepareData: (data) -> + bucketSize = (@options.bucketRange[1] - @options.bucketRange[0]) / @options.buckets + + prepared = [] + for layer in data + buckets = (0 for i in [0...@options.buckets]) + for point in layer.values + index = parseInt((point.x - @options.bucketRange[0]) / bucketSize) + + if @options.cutOutliers and ((index < 0) or (index >= @options.buckets)) + continue + if index < 0 + index = 0 + else if index >= @options.buckets + index = @options.buckets - 1 + + buckets[index] += parseInt point.y + + preparedLayer = { values: (buckets.map (d, i) -> {x: parseInt(i) * bucketSize, y: d}) } + for own k, v of layer + preparedLayer[k] = v unless k == 'values' + + prepared.push preparedLayer + + return prepared + + # Called when options change, this prepares the raw data for the chart according to the new + # options, sets it, and renders the chart. + resetData: -> + @setData @rawData + @draw() + + # Updates the chart in response to an <code>option:bucketRange</code> event. + bucketRangeChanged: -> @resetData() + + # Updates the chart in response to an <code>option:buckets</code> event. + bucketsChanged: -> @resetData() + + # Updates the chart in response to an <code>option:cutOutliers</code> event. + cutOutliersChanged: -> @resetData() diff --git a/debian/missing-sources/epoch/src/basic/line.coffee b/debian/missing-sources/epoch/src/basic/line.coffee new file mode 100644 index 0000000..de56780 --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/line.coffee @@ -0,0 +1,46 @@ +# Static line chart implementation (using d3). +class Epoch.Chart.Line extends Epoch.Chart.Plot + constructor: (@options={}) -> + @options.type ?= 'line' + super(@options) + @draw() + + # @return [Function] The line generator used to construct the plot. + line: (layer) -> + [x, y] = [@x(), @y(layer.range)] + d3.svg.line() + .x((d) -> x(d.x)) + .y((d) -> y(d.y)) + + # Draws the line chart. + draw: -> + [x, y, layers] = [@x(), @y(), @getVisibleLayers()] + + # Zero visible layers, just drop all and get out + if layers.length == 0 + return @g.selectAll('.layer').remove() + + # 1) Join + layer = @g.selectAll('.layer') + .data(layers, (d) -> d.category) + + # 2) Update (only existing) + layer.select('.line').transition().duration(500) + .attr('d', (l) => @line(l)(l.values)) + + # 3) Enter (Create) + layer.enter().append('g') + .attr('class', (l) -> l.className) + .append('path') + .attr('class', 'line') + .attr('d', (l) => @line(l)(l.values)) + + # 4) Update (existing & new) + # Nuuupp + + # 5) Exit (Remove) + layer.exit().transition().duration(750) + .style('opacity', '0') + .remove() + + super() diff --git a/debian/missing-sources/epoch/src/basic/pie.coffee b/debian/missing-sources/epoch/src/basic/pie.coffee new file mode 100644 index 0000000..e794a2f --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/pie.coffee @@ -0,0 +1,59 @@ + +# Static Pie Chart implementation (using d3). +class Epoch.Chart.Pie extends Epoch.Chart.SVG + defaults = + type: 'pie' + margin: 10 + inner: 0 + + # Creates a new pie chart. + # @param [Object] options Options for the pie chart. + # @option options [Number] margin Margins to add around the pie chart (default: 10). + # @option options [Number] inner The inner radius for the chart (default: 0). + constructor: (@options={}) -> + super(@options = Epoch.Util.defaults(@options, defaults)) + @pie = d3.layout.pie().sort(null) + .value (d) -> d.value + @arc = d3.svg.arc() + .outerRadius(=> (Math.max(@width, @height) / 2) - @options.margin) + .innerRadius(=> @options.inner) + @g = @svg.append('g') + .attr("transform", "translate(#{@width/2}, #{@height/2})") + @on 'option:margin', 'marginChanged' + @on 'option:inner', 'innerChanged' + @draw() + + # Draws the pie chart + draw: -> + @g.selectAll('.arc').remove() + + arcs = @g.selectAll(".arc") + .data(@pie(@getVisibleLayers()), (d) -> d.data.category) + + arcs.enter().append('g') + .attr('class', (d) -> "arc pie " + d.data.className) + + arcs.select('path') + .attr('d', @arc) + + arcs.select('text') + .attr("transform", (d) => "translate(#{@arc.centroid(d)})") + .text((d) -> d.data.label or d.data.category) + + path = arcs.append("path") + .attr("d", @arc) + .each((d) -> @._current = d) + + text = arcs.append("text") + .attr("transform", (d) => "translate(#{@arc.centroid(d)})") + .attr("dy", ".35em") + .style("text-anchor", "middle") + .text((d) -> d.data.label or d.data.category) + + super() + + # Updates margins in response to an <code>option:margin</code> event. + marginChanged: -> @draw() + + # Updates inner margin in response to an <code>option:inner</code> event. + innerChanged: -> @draw() diff --git a/debian/missing-sources/epoch/src/basic/scatter.coffee b/debian/missing-sources/epoch/src/basic/scatter.coffee new file mode 100644 index 0000000..b8563ec --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/scatter.coffee @@ -0,0 +1,59 @@ + +# Static scatter plot implementation (using d3). +class Epoch.Chart.Scatter extends Epoch.Chart.Plot + defaults = + type: 'scatter' + radius: 3.5 + axes: ['top', 'bottom', 'left', 'right'] + + # Creates a new scatter plot. + # @param [Object] options Options for the plot. + # @option options [Number] radius The default radius to use for the points in + # the plot (default 3.5). This can be overrwitten by individual points. + constructor: (@options={}) -> + super(@options = Epoch.Util.defaults(@options, defaults)) + @on 'option:radius', 'radiusChanged' + @draw() + + # Draws the scatter plot. + draw: -> + [x, y, layers] = [@x(), @y(), @getVisibleLayers()] + radius = @options.radius + + if layers.length == 0 + return @g.selectAll('.layer').remove() + + layer = @g.selectAll('.layer') + .data(layers, (d) -> d.category) + + layer.enter().append('g') + .attr('class', (d) -> d.className) + + dots = layer.selectAll('.dot') + .data((l) -> l.values) + + dots.transition().duration(500) + .attr("r", (d) -> d.r ? radius) + .attr("cx", (d) -> x(d.x)) + .attr("cy", (d) -> y(d.y)) + + dots.enter().append('circle') + .attr('class', 'dot') + .attr("r", (d) -> d.r ? radius) + .attr("cx", (d) -> x(d.x)) + .attr("cy", (d) -> y(d.y)) + + dots.exit().transition() + .duration(750) + .style('opacity', 0) + .remove() + + layer.exit().transition() + .duration(750) + .style('opacity', 0) + .remove() + + super() + + # Updates radius in response to an <code>option:radius</code> event. + radiusChanged: -> @draw() |