|
function colorBar(){ |
|
var orient = "right", |
|
lineWidth = 40, //Function?... because that would be coooooool... not sure if it is compatible with axis.js |
|
size_ = 300, |
|
tickFormat = d3.format("3e"), |
|
color = d3.scale.linear().domain([0, 0.5, 1]).range(["blue", "green", "red"]), //v -> color |
|
line = d3.svg.line().interpolate("basis"), |
|
precision = 8, |
|
points_, |
|
tickSize_; |
|
|
|
function component(selection){ |
|
selection.each(function(data,index){ |
|
var container = d3.select(this), |
|
tickSize = tickSize_ || lineWidth, |
|
n, |
|
points = points_ || (((orient == "left") || (orient == "right"))?[[0,size_],[0,0]]:[[size_,0],[0,0]]), |
|
quads = quad(sample(line(points),precision)), |
|
size = (points)?n:size_, |
|
aScale = color.copy().domain(d3.extent(color.domain())).range([size,0]), //v -> px |
|
colorExtent = d3.extent(color.domain()), |
|
normScale = color.copy().domain(color.domain().map(function(d){ return (d - colorExtent[0])/ (colorExtent[1] - colorExtent[0])})), |
|
|
|
//Save values for transitions |
|
oldLineWidth = this.__lineWidth__ || lineWidth; |
|
oldQuads = this.__quads__ || quads; |
|
this.__quads__ = quads; |
|
this.__lineWidth__ = lineWidth; |
|
|
|
//Enters |
|
var bar = container.selectAll("path.c").data(d3.range(quads.length), function(d){return d}); |
|
|
|
console.log(bar); |
|
console.log(bar.enter()); |
|
console.log(bar.exit()); |
|
|
|
var bEnter = bar.enter().insert("path","g.axis").classed("c",true), |
|
bExit = d3.transition(bar.exit()).remove(), |
|
bUpdate = d3.transition(bar), |
|
bTransform = function(selection,f,lw){ |
|
selection.style("fill", function(d) { return normScale(f(d).t); }) |
|
.style("stroke", function(d) { return normScale(f(d).t); }) |
|
.attr("d", function(d) { var p = f(d); return lineJoin(p[0], p[1], p[2], p[3], lw); });}; |
|
|
|
bEnter.call(bTransform,function(d){return oldQuads[oldQuads.length - 1]},oldLineWidth); // enter from last of oldQuad |
|
bExit.call(bTransform,function(d){return quads[quads.length - 1]},lineWidth); //exit from last of quads |
|
bUpdate.call(bTransform,function(d){return quads[d]},lineWidth) |
|
|
|
colorBarAxis = d3.svg.axis().scale(aScale).orient(orient) |
|
.tickSize(tickSize).tickFormat(tickFormat), |
|
a = container.selectAll("g.axis").data(function(d){return (aScale)?[1]:[]}), //axis container |
|
aEnter = a.enter().append("g").classed("axis",true), |
|
aExit = d3.transition(a.exit()).remove(), |
|
aUpdate = d3.transition(a).call(colorBarAxis), |
|
aTransform = function(selection,lw){ |
|
selection.attr("transform", "translate(" |
|
+ (((orient == "right") || (orient == "left"))?-lw/2:0) + "," |
|
+ (((orient == "right") || (orient =="left"))?0:lw/2) + ")");}; |
|
|
|
aEnter.call(aTransform,oldLineWidth); |
|
aExit.call(aTransform,lineWidth); |
|
aUpdate.call(aTransform,lineWidth); |
|
|
|
// Sample the SVG path string "d" uniformly with the specified precision. |
|
function sample(d,pre) { |
|
var path = document.createElementNS(d3.ns.prefix.svg, "path"); |
|
path.setAttribute("d", d); |
|
|
|
n = path.getTotalLength(); |
|
|
|
var t = [0], i = 0, dt = pre; |
|
while ((i += dt) < n) t.push(i); |
|
t.push(n); |
|
|
|
return t.map(function(t) { |
|
var p = path.getPointAtLength(t), a = [p.x, p.y]; |
|
a.t = t / n; |
|
return a; |
|
}); |
|
|
|
document.removeChild(path); |
|
} |
|
|
|
// Compute quads of adjacent points [p0, p1, p2, p3]. |
|
function quad(pts) { |
|
return d3.range(pts.length - 1).map(function(i) { |
|
var a = [pts[i - 1], pts[i], pts[i + 1], pts[i + 2]]; |
|
a.t = (pts[i].t + pts[i + 1].t) / 2; |
|
return a; |
|
}); |
|
} |
|
|
|
// Compute stroke outline for segment p12. |
|
function lineJoin(p0, p1, p2, p3, width) { |
|
var u12 = perp(p1, p2), |
|
r = width / 2, |
|
a = [p1[0] + u12[0] * r, p1[1] + u12[1] * r], |
|
b = [p2[0] + u12[0] * r, p2[1] + u12[1] * r], |
|
c = [p2[0] - u12[0] * r, p2[1] - u12[1] * r], |
|
d = [p1[0] - u12[0] * r, p1[1] - u12[1] * r]; |
|
|
|
if (p0) { // clip ad and dc using average of u01 and u12 |
|
var u01 = perp(p0, p1), e = [p1[0] + u01[0] + u12[0], p1[1] + u01[1] + u12[1]]; |
|
a = lineIntersect(p1, e, a, b); |
|
d = lineIntersect(p1, e, d, c); |
|
} |
|
|
|
if (p3) { // clip ab and dc using average of u12 and u23 |
|
var u23 = perp(p2, p3), e = [p2[0] + u23[0] + u12[0], p2[1] + u23[1] + u12[1]]; |
|
b = lineIntersect(p2, e, a, b); |
|
c = lineIntersect(p2, e, d, c); |
|
} |
|
|
|
return "M" + a + "L" + b + " " + c + " " + d + "Z"; |
|
} |
|
|
|
// Compute intersection of two infinite lines ab and cd. |
|
function lineIntersect(a, b, c, d) { |
|
var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, |
|
y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, |
|
ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); |
|
return [x1 + ua * x21, y1 + ua * y21]; |
|
} |
|
|
|
// Compute unit vector perpendicular to p01. |
|
function perp(p0, p1) { |
|
var u01x = p0[1] - p1[1], u01y = p1[0] - p0[0], |
|
u01d = Math.sqrt(u01x * u01x + u01y * u01y); |
|
return [u01x / u01d, u01y / u01d]; |
|
} |
|
|
|
})} |
|
|
|
component.orient = function(_) { |
|
if (!arguments.length) return orient; |
|
orient = _; |
|
return component; |
|
}; |
|
|
|
component.lineWidth = function(_) { |
|
if (!arguments.length) return lineWidth; |
|
lineWidth = _; |
|
return component; |
|
}; |
|
|
|
component.size = function(_) { |
|
if (!arguments.length) return size_; |
|
size_ = _; |
|
return component; |
|
}; |
|
|
|
component.tickFormat = function(_) { |
|
if (!arguments.length) return tickFormat; |
|
tickFormat = _; |
|
return component; |
|
}; |
|
|
|
component.tickFormat = function(_) { |
|
if (!arguments.length) return tickSize_; |
|
tickSize_ = _; |
|
return component; |
|
}; |
|
|
|
component.color = function(_) { |
|
if (!arguments.length) return color; |
|
color = _; |
|
return component; |
|
}; |
|
|
|
component.precision = function(_) { |
|
if (!arguments.length) return precision; |
|
precision = _; |
|
return component; |
|
}; |
|
|
|
component.points = function(_) { |
|
if (!arguments.length) return points_; |
|
points_ = _; |
|
return component; |
|
}; |
|
|
|
component.line = function(_) { |
|
if (!arguments.length) return line; |
|
line = _; |
|
return component; |
|
}; |
|
|
|
return component; |
|
} |