|
define(['datgui'], function(datgui) { |
|
|
|
var v = datgui('Bandline tweaks', 'close', { |
|
ease: {value: 'elastic', type: Categorical, categories: easeCategories}, |
|
advanceEase: {value: 'linear', type: Categorical, categories: easeCategories}, |
|
duration: {value: 300, type: Number, min: 0, max: 1000} |
|
}) |
|
|
|
var styling = datgui('Bandline colors', 'close', { |
|
bandlineColor: {value: [5, 48, 97], type: Color}, |
|
opacity: {value: 1, type: Number, min: 0, max: 1}, |
|
fillOpacity: {value: 0.75, type: Number, min: 0, max: 1}, |
|
fillBand0: {value: [0,0,0] , type: Color}, |
|
fillBand1: {value: [253, 219, 199], type: Color}, |
|
fillBand2: {value: [244, 165, 130], type: Color}, |
|
fillBand3: {value: [146, 197, 222], type: Color}, |
|
fillBand4: {value: [209, 229, 240], type: Color}, |
|
fillBand5: {value: [0,0,0,0] , type: Color}, |
|
fillBand6: {value: [0,0,0,0] , type: Color}, |
|
strokeOpacity: {value: 1, type: Number, min: 0, max: 1}, |
|
strokeBand0: {value: [244, 165, 130], type: Color}, |
|
strokeBand1: {value: [0,0,0,0] , type: Color}, |
|
strokeBand2: {value: [0,0,0,0] , type: Color}, |
|
strokeBand3: {value: [0,0,0,0] , type: Color}, |
|
strokeBand4: {value: [0,0,0,0] , type: Color}, |
|
strokeBand5: {value: [146, 197, 222], type: Color}, |
|
strokeBand6: {value: [255, 255, 255], type: Color} |
|
}) |
|
|
|
var bandlineFills = _.tupleOf( |
|
styling.fillBand0, |
|
styling.fillBand1, |
|
styling.fillBand2, |
|
styling.fillBand3, |
|
styling.fillBand4, |
|
styling.fillBand5, |
|
styling.fillBand6 |
|
) |
|
|
|
var bandlineStrokes = _.tupleOf( |
|
styling.strokeBand0, |
|
styling.strokeBand1, |
|
styling.strokeBand2, |
|
styling.strokeBand3, |
|
styling.strokeBand4, |
|
styling.strokeBand5, |
|
styling.strokeBand6 |
|
) |
|
|
|
var bandlineColor = toColor(styling.bandlineColor) |
|
|
|
var transitionedAttribute = transitionAttribute(v.duration) |
|
var easeAttribute = transitionedAttribute(v.ease) |
|
var quickEasedAttribute = transitionedAttribute($('cubic-out')) |
|
var advanceTransform = transitionAttribute(R.__, v.advanceEase, R.__, 'transform') |
|
|
|
/** |
|
* Bandline renderer |
|
*/ |
|
|
|
var rectanglePath = function(xr, yr) {return d3.svg.line()([[xr[0], yr[0]], [xr[1], yr[0]], [xr[1], yr[1]], [xr[0], yr[1]]]) + 'Z'} |
|
|
|
var bandlinePath = R.curry(function(valueAccessor, xScale, d) { |
|
return d3.svg.line().defined(R.compose(defined, R.prop(1)))(valueAccessor(d).map(function(s) {return [xScale(s.key), s.value]})) |
|
}) |
|
|
|
var bandData = _(R.curry(function(bands, yScaler, d) {return bands.map(function(band, i) {return {key: i, value: band, yScale: yScaler(d)}})})) |
|
|
|
var bandPath = _(R.unapply(R.converge(rectanglePath))) |
|
|
|
function renderBands(root, bands, yScaler, xRanger, yRanger) { |
|
var bandSet = bind(root, 'bands', 'g', of) |
|
var band = bind(bandSet, 'band', 'path', bandData(bands, yScaler)) |
|
var color = _(R.curry(function(a, d, i) {return R.type(a[i]) === 'String' ? a[i] : 'rgb(' + a[i].map(Math.round).join(',') + ')'})) |
|
setAttributes(band, { |
|
filter: $('url(#bandFilter)'), |
|
fill: color(bandlineFills), |
|
stroke: color(bandlineStrokes), |
|
opacity: styling.opacity, |
|
'fill-opacity': styling.fillOpacity, |
|
'stroke-opacity': styling.strokeOpacity |
|
}) |
|
easeAttribute(band, 'd', bandPath(xRanger, yRanger)) |
|
} |
|
|
|
var pointData = _(R.curry(function(valueAccessor, d) { |
|
return valueAccessor(d).map(function(value) {return {key: value.key, value: value.value, o: d}}).filter(R.compose(defined, value)) |
|
})) |
|
|
|
function renderPoints(root, valueAccessor, pointStyleAccessor, rScale, xSpec, ySpec) { |
|
var points = pointData(valueAccessor) |
|
var valuePoints = bind(root, 'valuePoints', 'g', points) |
|
setAttributes(valuePoints, {transform: _(translate)(xSpec, ySpec)}) |
|
var point = bind(valuePoints, 'point', 'circle', of) |
|
setAttributes(point, { |
|
class: _(R.curry(function(accessor, d) {return 'point ' + accessor(d.value)}))(pointStyleAccessor), |
|
fill: bandlineColor, |
|
stroke: bandlineColor |
|
}) |
|
quickEasedAttribute(point, 'r', _(R.curry(function(accessor, scale, d) {return scale(accessor(d.value))}))(pointStyleAccessor, rScale)) |
|
exitRemove(valuePoints) |
|
} |
|
|
|
var valuesExtent = function(valueAccessor, d) {return d3.extent(valueAccessor(d).map(value).filter(defined))} |
|
|
|
var extentBoxPathStream = _(R.curry(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] |
|
) |
|
return path |
|
} |
|
)) |
|
|
|
function renderExtent(root, valueAccessor, xScale, yRange) { |
|
var extentBox = streamBind(root, 'valueBox', 'path') |
|
streamsAttr(extentBox, 'filter', $('url(#lineFilter)')) |
|
streamsAttr(extentBox, 'stroke', bandlineColor) |
|
easeAttribute(extentBox, 'd', extentBoxPathStream(valueAccessor, xScale, yRange)) |
|
} |
|
|
|
function renderValueLine(root, valueAccessor, xScale, yScaler) { |
|
|
|
var line = streamBind(root, 'valueLine', 'path') |
|
|
|
var scaler = R.curry(function(yScaler, d) {return 'scale(1,' + (yScaler(d)(1) - yScaler(d)(0)) + ') translate(0,' + -d3.mean(yScaler(d).domain()) + ') '}) |
|
|
|
setAttributes(line, { |
|
d: _(bandlinePath)(valueAccessor, xScale), |
|
stroke: bandlineColor |
|
}) |
|
|
|
easeAttribute(line, 'transform', _(scaler)(yScaler)) |
|
} |
|
|
|
function renderBandLineData(root, valueAccessor, xScaleOfBandLine, yScalerOfBandLine, pointStyleAccessor, rScaleOfBandLine, advanceDuration) { |
|
|
|
var clippedRoot = streamBind(root, 'sparklineSliderClippedStationaryPart') |
|
streamsAttr(clippedRoot, 'clip-path', $('url(#bandlinePaddedClippath)')) |
|
var xform = _(function(scale) {return translateX(scale(0) - scale(1))}) |
|
var holder = streamBind(clippedRoot, 'bandlineHolder') |
|
streamsAttr(holder, 'filter', $('url(#lineFilter)')) |
|
var holderTransform = advanceTransform(R.__, holder) |
|
holderTransform($(0), $(null)) |
|
holderTransform(advanceDuration, xform(xScaleOfBandLine)) |
|
|
|
renderValueLine(holder, valueAccessor, xScaleOfBandLine, yScalerOfBandLine) |
|
renderPoints(holder, valueAccessor, pointStyleAccessor, rScaleOfBandLine, |
|
_(function(scale) {return R.compose(scale, key)})(xScaleOfBandLine), |
|
_(R.curry(function(scale, d) {return scale(d.o)(d.value)}))(yScalerOfBandLine)) |
|
} |
|
|
|
var deriveScale = _(R.curry(function(domain, range, d3scaleBasis) {return makeScale(d3scaleBasis.copy(), domain, range)})) |
|
|
|
var streamBandlineYScaler = _(R.curry(function(accessor, range, d) {return d3.scale.linear().domain(valuesExtent(accessor, d)).range(range)})) |
|
|
|
var streamConstantMeanScale = _(function(a) {return R.always(d3.mean(a))}) |
|
|
|
var streamSortedArray = function(yRange, direction) {return _(function(range) {return range.slice().sort(direction)})(yRange)} |
|
|
|
var clippathPaddingStream = _(function(scale) {return d3.max(scale.range()) + 1}) // assuming a 1px thick circle stroke |
|
|
|
var paddedRange = _(function(range, padding) {return [range[0] - padding, range[1] + padding]}) |
|
|
|
var bandlineUnpaddedClippath = "bandlineUnpaddedClippath" |
|
|
|
return R.curry(function bandline(valueAccessor, contextValueAccessor, rDomainOfBandLine, bands, pointStyleAccessor, |
|
xDomainOfBandLine, xDomainOfSparkStrip) { |
|
return function bandline_view(xRangeOfBandLine, xRangeOfSparkStrip, rRangeOfBandLine, |
|
rScaleOfSparkStrip, yRange, yRangeOfSparkStrip, advanceDuration, rootSvgStream) { |
|
|
|
// Scale streams |
|
var xScaleOfBandLine = deriveScale(xDomainOfBandLine, xRangeOfBandLine, $(d3.scale.linear())) |
|
var xScaleOfSparkStrip = deriveScale(xDomainOfSparkStrip, xRangeOfSparkStrip, $(d3.scale.linear())) |
|
var rScaleOfBandLine = deriveScale(rDomainOfBandLine, rRangeOfBandLine, $(d3.scale.ordinal())) |
|
var yScalerOfBandLine = streamBandlineYScaler(contextValueAccessor, yRange) |
|
var yScalerOfSparkStrip = streamConstantMeanScale(yRangeOfSparkStrip) |
|
var clipPath = _(function(xScale, yRange) {return rectanglePath(xScale.range(), yRange)}) |
|
|
|
function addDefs(rootSvg) { |
|
var yRangeUnpadded = streamSortedArray(yRange, d3.ascending) |
|
var clippathPadding = clippathPaddingStream(rScaleOfBandLine) |
|
var yRangePadded = paddedRange(yRangeUnpadded, clippathPadding) |
|
var defs = bind(rootSvg, 'defs', 'defs', _(function(root) {return root.datum() ? root.data : [{key: 0}]})(rootSvg)) |
|
var paddedClip = streamBind(defs, 'paddedClipPath', 'clipPath') |
|
streamsAttr(paddedClip, 'id', $('bandlinePaddedClippath')) |
|
var paddedClipPath = streamBind(paddedClip, 'path', 'path', [{key: 0}]) |
|
streamsAttr(paddedClipPath, 'd', clipPath(xScaleOfBandLine, yRangePadded)) |
|
var unpaddedClip = streamBind(defs, 'unpaddedClipPath', 'clipPath') |
|
streamsAttr(unpaddedClip, 'id', $(bandlineUnpaddedClippath)) |
|
var unpaddedClipPath = streamBind(unpaddedClip, 'path', 'path', [{key: 0}]) |
|
streamsAttr(unpaddedClipPath, 'd', clipPath(xScaleOfBandLine, yRangeUnpadded)) |
|
} |
|
|
|
function renderBandLine(root) { |
|
|
|
var bandline = streamBind(root, 'bandline') |
|
var clippedBands = streamBind(bandline, 'bandlineHolderClipped') |
|
streamsAttr(clippedBands, 'clip-path', $(url(bandlineUnpaddedClippath))) |
|
renderBands(clippedBands, bands, yScalerOfBandLine, _(function(scale) { |
|
return R.always(scale.range()) |
|
})(xScaleOfBandLine), $(function(d) {return d.value.map(d.yScale)})) |
|
renderBandLineData(bandline, valueAccessor, xScaleOfBandLine, yScalerOfBandLine, pointStyleAccessor, rScaleOfBandLine, advanceDuration) |
|
} |
|
|
|
function renderSparkStrip(root) { |
|
|
|
var sparkStrip = streamBind(root, 'sparkStrip') |
|
renderBands(sparkStrip, bands, yScalerOfSparkStrip, _(R.curry(function(scale, d) {return d.value.map(scale)}))(xScaleOfSparkStrip), _(R.always)(yRangeOfSparkStrip)) |
|
renderExtent(sparkStrip, valueAccessor, xScaleOfSparkStrip, yRangeOfSparkStrip) |
|
renderPoints(sparkStrip, valueAccessor, pointStyleAccessor, rScaleOfSparkStrip, _(function(fun) {return R.compose(fun, value)})(xScaleOfSparkStrip), yScalerOfSparkStrip) |
|
} |
|
|
|
addDefs(rootSvgStream) |
|
|
|
return function(_bandlineRoot, _sparkStripRoot) { |
|
renderBandLine(_bandlineRoot) |
|
renderSparkStrip(_sparkStripRoot) |
|
} |
|
} |
|
|
|
}) |
|
|
|
}) |