codecombat/app/core/d3_utils.coffee
2015-11-10 17:26:15 -08:00

128 lines
4.6 KiB
CoffeeScript

# Caller needs require 'vendor/d3'
module.exports.createContiguousDays = (timeframeDays, skipToday=true) ->
# Return list of last 'timeframeDays' contiguous days in yyyy-mm-dd format
days = []
currentDate = new Date()
currentDate.setUTCDate(currentDate.getUTCDate() - timeframeDays)
currentDate.setUTCDate(currentDate.getUTCDate() - 1) if skipToday
for i in [0...timeframeDays]
currentDay = currentDate.toISOString().substr(0, 10)
days.push(currentDay)
currentDate.setUTCDate(currentDate.getUTCDate() + 1)
days
module.exports.createLineChart = (containerSelector, chartLines) ->
# Creates a line chart within 'containerSelector' based on chartLines
return unless chartLines?.length > 0 and containerSelector
margin = 20
keyHeight = 20
xAxisHeight = 20
yAxisWidth = 40
containerWidth = $(containerSelector).width()
containerHeight = $(containerSelector).height()
yScaleCount = 0
yScaleCount++ for line in chartLines when line.showYScale
svg = d3.select(containerSelector).append("svg")
.attr("width", containerWidth)
.attr("height", containerHeight)
width = containerWidth - margin * 2 - yAxisWidth * yScaleCount
height = containerHeight - margin * 2 - xAxisHeight - keyHeight * chartLines.length
currentLine = 0
currentYScale = 0
# Horizontal guidelines
marks = (Math.round(i * height / 5) for i in [1..5])
yRange = d3.scale.linear().range([height, 0]).domain([0, height])
svg.selectAll(".line")
.data(marks)
.enter()
.append("line")
.attr("x1", margin + yAxisWidth * yScaleCount)
.attr("y1", (d) -> margin + yRange(d))
.attr("x2", margin + yAxisWidth * yScaleCount + width)
.attr("y2", (d) -> margin + yRange(d))
.attr("stroke", 'gray')
.style("opacity", "0.3")
for line in chartLines
# continue unless line.enabled
xRange = d3.scale.linear().range([0, width]).domain([d3.min(line.points, (d) -> d.x), d3.max(line.points, (d) -> d.x)])
yRange = d3.scale.linear().range([height, 0]).domain([line.min, line.max])
# x-Axis
if currentLine is 0
startDay = new Date(line.points[0].day)
endDay = new Date(line.points[line.points.length - 1].day)
xAxisRange = d3.time.scale()
.domain([startDay, endDay])
.range([0, width])
xAxis = d3.svg.axis()
.scale(xAxisRange)
svg.append("g")
.attr("class", "x axis")
.call(xAxis)
.selectAll("text")
.attr("dy", ".35em")
.attr("transform", "translate(" + (margin + yAxisWidth) + "," + (height + margin) + ")")
.style("text-anchor", "start")
if line.showYScale
# y-Axis
yAxisRange = d3.scale.linear().range([height, 0]).domain([line.min, line.max])
yAxis = d3.svg.axis()
.scale(yRange)
.orient("left")
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + (margin + yAxisWidth * currentYScale) + "," + margin + ")")
.style("color", line.lineColor)
.call(yAxis)
.selectAll("text")
.attr("y", 0)
.attr("x", 0)
.attr("fill", line.lineColor)
.style("text-anchor", "start")
currentYScale++
# Key
svg.append("line")
.attr("x1", margin)
.attr("y1", margin + height + xAxisHeight + keyHeight * currentLine + keyHeight / 2)
.attr("x2", margin + 40)
.attr("y2", margin + height + xAxisHeight + keyHeight * currentLine + keyHeight / 2)
.attr("stroke", line.lineColor)
.attr("class", "key-line")
svg.append("text")
.attr("x", margin + 40 + 10)
.attr("y", margin + height + xAxisHeight + keyHeight * currentLine + (keyHeight + 10) / 2)
.attr("fill", line.lineColor)
.attr("class", "key-text")
.text(line.description)
# Path and points
svg.selectAll(".circle")
.data(line.points)
.enter()
.append("circle")
.attr("transform", "translate(" + (margin + yAxisWidth * yScaleCount) + "," + margin + ")")
.attr("cx", (d) -> xRange(d.x))
.attr("cy", (d) -> yRange(d.y))
.attr("r", 2)
.attr("fill", line.lineColor)
.attr("stroke-width", 1)
.attr("class", "graph-point")
.attr("data-pointid", (d) -> "#{line.lineID}#{d.x}")
d3line = d3.svg.line()
.x((d) -> xRange(d.x))
.y((d) -> yRange(d.y))
.interpolate("linear")
svg.append("path")
.attr("d", d3line(line.points))
.attr("transform", "translate(" + (margin + yAxisWidth * yScaleCount) + "," + margin + ")")
.style("stroke-width", line.strokeWidth)
.style("stroke", line.lineColor)
.style("fill", "none")
currentLine++