/** * Bandline renderer */ var defined = R.complement(R.isNil) var ease = 'cubic-out' function rectanglePath(xr, yr) { if(xr[0] < 1e-100 && xr[0] > -1e-100 && xr[0] !== 0) debugger return d3.svg.line()([[xr[0], yr[0]], [xr[1], yr[0]], [xr[1], yr[1]], [xr[0], yr[1]]]) + 'Z' } function bandLinePath(valueAccessor, xScale, yScaler, d) { var drawer = d3.svg.line().defined(R.compose(defined, R.prop(1))) return drawer(valueAccessor(d).map(function(s) {return [xScale(s.key), yScaler(d)(s.value)]})) } function bandData(bands, yScaler, d) { var yScale = yScaler(d) return bands.map(function(band, i) { return {key: i, value: band, yScale: yScale} }) } function renderBands(root, bands, yScaler, xRanger, yRanger) { bind(bind(root, 'bands'), 'band', 'path', bandData.bind(0, bands, yScaler)) .transition() .ease(ease) .attr('class', function(d, i) {return 'band s' + i}) .attr('d', function(d) {return rectanglePath(xRanger(d), yRanger(d))}) } function pointData(valueAccessor, d) { return valueAccessor(d) .map(function(value) {if(value.key === undefined) debugger; return {key: value.key, value: value.value, o: d}}) .filter(R.compose(defined, value)) } function renderPoints(root, valueAccessor, pointStyleAccessor, rScale, xSpec, ySpec) { bind(root, 'valuePoints', 'g', pointData.bind(0, valueAccessor)) .entered .attr('transform', translate(xSpec, ySpec)) root['valuePoints'] .attr('transform', translate(xSpec, ySpec)) bind(root['valuePoints'], 'point', 'circle') .attr('class', function(d) {return 'point ' + pointStyleAccessor(d.value)}) .transition() .attr('r', function(d) {return rScale(pointStyleAccessor(d.value))}) root['valuePoints'].exit().remove() } function valuesExtent(valueAccessor, d) { return d3.extent(valueAccessor(d).map(value).filter(defined)) } function sparkStripBoxPath(valueAccessor, xScale, yRange, d) { var midY = d3.mean(yRange) var halfHeight = (yRange[1] - yRange[0]) / 2 var path = rectanglePath( valuesExtent(valueAccessor, d).map(xScale).map(Math.floor), [midY - halfHeight / 3, midY + halfHeight / 3] ) //console.log(path) return path } function renderExtent(root, valueAccessor, xScale, yRange) { bind(root, 'valueBox', 'path') .transition() .ease(ease) .attr('d', sparkStripBoxPath.bind(0, valueAccessor, xScale, yRange)) } function renderValueLine(root, valueAccessor, xScale, yScaler) { var line = bind(root, 'valueLine', 'path') var scaler = function(d) { var y = yScaler(d) return 'scale(1,' + (y(1) - y(0)) + ') translate(0,' + -d3.mean(y.domain()) + ') ' } line .attr('d', bandLinePath.bind(0, valueAccessor, xScale, function() {return d3.scale.linear()})) .entered .attr('transform', scaler) line .transition() .ease(ease) .attr('transform', scaler) } function renderBandLineData(root, _valueAccessor, _xScaleOfBandLine, _yScalerOfBandLine, _pointStyleAccessor, _rScaleOfBandLine) { var clippedRoot = bind(root) .attr('clip-path', 'url(#bandlinePaddedClippath)') var holder = bind(clippedRoot, 'bandLineHolder') holder .transition() .attr('transform', null) holder .transition().duration(timeCadence) .ease('linear') .attr('transform', translateX(_xScaleOfBandLine(0) - _xScaleOfBandLine(1))) var clippedHolder = bind(holder) //.attr('clip-path', 'url(#bandlineClippath)') renderValueLine(holder, _valueAccessor, _xScaleOfBandLine, _yScalerOfBandLine) renderPoints(holder, _valueAccessor, _pointStyleAccessor, _rScaleOfBandLine, R.compose(_xScaleOfBandLine, key), function(d) {return _yScalerOfBandLine(d.o)(d.value)}) } function bandLine() { function addDefs(rootSvg) { var yRange = _yRange.slice().sort(d3.ascending) var clippathPadding = d3.max(_rScaleOfBandLine.range()) var yRangePadded = [yRange[0] - clippathPadding, yRange[1] + clippathPadding] var defs = bind(rootSvg, 'defs', 'defs', rootSvg.datum() ? rootSvg.data() : [{key: 0}]) bind(defs, 'paddedClipPath', 'clipPath') .attr('id', 'bandlinePaddedClippath') bind(defs['paddedClipPath'], 'path', 'path', [{key: 0}]) .attr('d', rectanglePath(_xScaleOfBandLine.range(), yRangePadded)) bind(defs, 'unpaddedClipPath', 'clipPath') .attr('id', 'bandlineUnpaddedClippath') bind(defs['unpaddedClipPath'], 'path', 'path', [{key: 0}]) .attr('d', rectanglePath(_xScaleOfBandLine.range(), yRange)) } function renderBandLine(root) { var bandLine = bind(root, 'bandLine') var clippedBands = bind(bandLine) .attr('clip-path', 'url(#bandlineUnpaddedClippath)') renderBands(clippedBands, _bands, _yScalerOfBandLine, R.always(_xScaleOfBandLine.range()), function(d) {return d.value.map(d.yScale)}) renderBandLineData(bandLine, _valueAccessor, _xScaleOfBandLine, _yScalerOfBandLine, _pointStyleAccessor, _rScaleOfBandLine) } function renderSparkStrip(root) { var sparkStrip = bind(root, 'sparkStrip') renderBands(sparkStrip, _bands, _yScalerOfSparkStrip, function(d) { return d.value.map(_xScaleOfSparkStrip) }, R.always(_yRangeOfSparkStrip)) renderExtent(sparkStrip, _valueAccessor, _xScaleOfSparkStrip, _yRange) renderPoints(sparkStrip, _valueAccessor, _pointStyleAccessor, _rScaleOfSparkStrip, R.compose(_xScaleOfSparkStrip, value), _yScalerOfSparkStrip()) } function yScalerOfBandLineCalc() { return function(d) { return d3.scale.linear() .domain(valuesExtent(_contextValueAccessor, d)) .range(_yRange) } } var _bands = [[0, 0.25], [0.25, 0.5], [0.5, 0.75], [0.75, 1]] var bands = function(spec) { if(spec !== void(0)) { _bands = spec return functionalObject } else { return bands } } var _valueAccessor = function(d) {return {key: d.key, value: d.value}} var valueAccessor = function(spec) { if(spec !== void(0)) { _valueAccessor = spec _yScalerOfBandLine = yScalerOfBandLineCalc() return functionalObject } else { return _valueAccessor } } var _contextValueAccessor = function(d) {return {key: d.key, value: d.value}} var contextValueAccessor = function(spec) { if(spec !== void(0)) { _contextValueAccessor = spec _yScalerOfBandLine = yScalerOfBandLineCalc() return functionalObject } else { return _contextValueAccessor } } var _xScaleOfBandLine = d3.scale.linear() var xScaleOfBandLine = function(spec) { if(spec !== void(0)) { _xScaleOfBandLine = spec return functionalObject } else { return _xScaleOfBandLine } } var _xScaleOfSparkStrip = d3.scale.linear() var xScaleOfSparkStrip = function(spec) { if(spec !== void(0)) { _xScaleOfSparkStrip = spec return functionalObject } else { return _xScaleOfSparkStrip } } var _rScaleOfBandLine = R.always(2) var rScaleOfBandLine = function(spec) { if(spec !== void(0)) { _rScaleOfBandLine = spec return functionalObject } else { return _rScaleOfBandLine } } var _rScaleOfSparkStrip = R.always(2) var rScaleOfSparkStrip = function(spec) { if(spec !== void(0)) { _rScaleOfSparkStrip = spec return functionalObject } else { return _rScaleOfSparkStrip } } var _yRange = [0, 1] var _yScalerOfBandLine var yRange = function(spec) { if(spec !== void(0)) { _yRange = spec _yScalerOfBandLine = yScalerOfBandLineCalc() return functionalObject } else { return _yRange } } var _yRangeOfSparkStrip = [0, 1] var _yScalerOfSparkStrip var yRangeOfSparkStrip = function(spec) { if(spec !== void(0)) { _yRangeOfSparkStrip = spec _yScalerOfSparkStrip = R.always(d3.mean(_yRangeOfSparkStrip)) return functionalObject } else { return _yRangeOfSparkStrip } } var _pointStyleAccessor = R.always('normal') var pointStyleAccessor = function(spec) { if(spec !== void(0)) { _pointStyleAccessor = spec return functionalObject } else { return _pointStyleAccessor } } var functionalObject = { // For reference: http://bost.ocks.org/mike/chart/ renderBandLine: renderBandLine, renderSparkStrip: renderSparkStrip, addDefs: addDefs, bands: bands, valueAccessor: valueAccessor, contextValueAccessor: contextValueAccessor, xScaleOfBandLine: xScaleOfBandLine, xScaleOfSparkStrip: xScaleOfSparkStrip, rScaleOfBandLine: rScaleOfBandLine, rScaleOfSparkStrip: rScaleOfSparkStrip, yRange: yRange, yRangeOfSparkStrip: yRangeOfSparkStrip, pointStyleAccessor: pointStyleAccessor } return functionalObject }