summaryrefslogtreecommitdiffstats
path: root/debian/missing-sources/epoch/src/basic
diff options
context:
space:
mode:
Diffstat (limited to 'debian/missing-sources/epoch/src/basic')
-rw-r--r--debian/missing-sources/epoch/src/basic/area.coffee51
-rw-r--r--debian/missing-sources/epoch/src/basic/bar.coffee273
-rw-r--r--debian/missing-sources/epoch/src/basic/histogram.coffee61
-rw-r--r--debian/missing-sources/epoch/src/basic/line.coffee46
-rw-r--r--debian/missing-sources/epoch/src/basic/pie.coffee59
-rw-r--r--debian/missing-sources/epoch/src/basic/scatter.coffee59
6 files changed, 549 insertions, 0 deletions
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()