|
d3.svg.ribbon = function() { |
|
var _lineConstructor = d3.svg.line(); |
|
var _xAccessor = function (d) {return d.x} |
|
var _yAccessor = function (d) {return d.y} |
|
var _rAccessor = function (d) {return d.r} |
|
var _interpolator = "linear-closed"; |
|
|
|
function _ribbon(pathData) { |
|
|
|
var bothPoints = buildRibbon(pathData); |
|
|
|
return _lineConstructor.x(_xAccessor).y(_yAccessor).interpolate(_interpolator)(bothPoints); |
|
} |
|
|
|
_ribbon.x = function (_value) { |
|
if (!arguments.length) return _xAccessor; |
|
|
|
_xAccessor = _value; |
|
return _ribbon; |
|
} |
|
|
|
_ribbon.y = function (_value) { |
|
if (!arguments.length) return _yAccessor; |
|
|
|
_yAccessor = _value; |
|
return _ribbon; |
|
} |
|
|
|
_ribbon.r = function (_value) { |
|
if (!arguments.length) return _rAccessor; |
|
|
|
_rAccessor = _value; |
|
return _ribbon; |
|
} |
|
|
|
_ribbon.interpolate = function(_value) { |
|
if (!arguments.length) return _interpolator; |
|
|
|
_interpolator = _value; |
|
return _ribbon; |
|
} |
|
|
|
return _ribbon; |
|
|
|
function offsetEdge(d) { |
|
var diffX = _yAccessor(d.target) - _yAccessor(d.source); |
|
var diffY = _xAccessor(d.target) - _xAccessor(d.source); |
|
|
|
var angle0 = ( Math.atan2( diffY, diffX ) + ( Math.PI / 2 ) ); |
|
var angle1 = angle0 + ( Math.PI * 0.5 ); |
|
var angle2 = angle0 + ( Math.PI * 0.5 ); |
|
|
|
var x1 = _xAccessor(d.source) + (_rAccessor(d.source) * Math.cos(angle1)); |
|
var y1 = _yAccessor(d.source) - (_rAccessor(d.source) * Math.sin(angle1)); |
|
var x2 = _xAccessor(d.target) + (_rAccessor(d.target) * Math.cos(angle2)); |
|
var y2 = _yAccessor(d.target) - (_rAccessor(d.target) * Math.sin(angle2)); |
|
|
|
return {x1: x1, y1: y1, x2: x2, y2: y2} |
|
} |
|
|
|
function buildRibbon(points) { |
|
var bothCode = []; |
|
var x = 0; |
|
var transformedPoints = {}; |
|
|
|
while (x < points.length) { |
|
if (x !== points.length - 1) { |
|
transformedPoints = offsetEdge({source: points[x], target: points[x + 1]}); |
|
var p1 = {x: transformedPoints.x1, y: transformedPoints.y1}; |
|
var p2 = {x: transformedPoints.x2, y: transformedPoints.y2}; |
|
bothCode.push(p1,p2); |
|
if (bothCode.length > 3) { |
|
var l = bothCode.length - 1; |
|
var lineA = {a: bothCode[l - 3], b: bothCode[l - 2]}; |
|
var lineB = {a: bothCode[l - 1], b: bothCode[l]}; |
|
var intersect = findIntersect(lineA.a.x, lineA.a.y, lineA.b.x, lineA.b.y, lineB.a.x, lineB.a.y, lineB.b.x, lineB.b.y); |
|
if (intersect.found == true) { |
|
lineA.b.x = intersect.x; |
|
lineA.b.y = intersect.y; |
|
lineB.a.x = intersect.x; |
|
lineB.a.y = intersect.y; |
|
} |
|
} |
|
} |
|
|
|
x++; |
|
} |
|
x--; |
|
//Back |
|
while (x >= 0) { |
|
if (x !== 0) { |
|
transformedPoints = offsetEdge({source: points[x], target: points[x - 1]}); |
|
var p1 = {x: transformedPoints.x1, y: transformedPoints.y1}; |
|
var p2 = {x: transformedPoints.x2, y: transformedPoints.y2}; |
|
bothCode.push(p1,p2); |
|
if (bothCode.length > 3) { |
|
var l = bothCode.length - 1; |
|
var lineA = {a: bothCode[l - 3], b: bothCode[l - 2]}; |
|
var lineB = {a: bothCode[l - 1], b: bothCode[l]}; |
|
var intersect = findIntersect(lineA.a.x, lineA.a.y, lineA.b.x, lineA.b.y, lineB.a.x, lineB.a.y, lineB.b.x, lineB.b.y); |
|
if (intersect.found == true) { |
|
lineA.b.x = intersect.x; |
|
lineA.b.y = intersect.y; |
|
lineB.a.x = intersect.x; |
|
lineB.a.y = intersect.y; |
|
} |
|
} |
|
} |
|
|
|
x--; |
|
} |
|
|
|
return bothCode; |
|
} |
|
|
|
function findIntersect(l1x1, l1y1, l1x2, l1y2, l2x1, l2y1, l2x2, l2y2) { |
|
var d, a, b, n1, n2, result = { |
|
x: null, |
|
y: null, |
|
found: false |
|
}; |
|
d = ((l2y2 - l2y1) * (l1x2 - l1x1)) - ((l2x2 - l2x1) * (l1y2 - l1y1)); |
|
if (d == 0) { |
|
return result; |
|
} |
|
a = l1y1 - l2y1; |
|
b = l1x1 - l2x1; |
|
n1 = ((l2x2 - l2x1) * a) - ((l2y2 - l2y1) * b); |
|
n2 = ((l1x2 - l1x1) * a) - ((l1y2 - l1y1) * b); |
|
a = n1 / d; |
|
b = n2 / d; |
|
|
|
result.x = l1x1 + (a * (l1x2 - l1x1)); |
|
result.y = l1y1 + (a * (l1y2 - l1y1)); |
|
|
|
if ((a > 0 && a < 1) && (b > 0 && b < 1)) { |
|
result.found = true; |
|
} |
|
|
|
return result; |
|
}; |
|
|
|
} |