lib.coffee
functions =
draw_axis: (axis, svg) ->
a_rad = axis.angle * (Math.PI / 180.0)
r_beg = axis.r_beg
r_end = r_beg + axis.r_len
[ height, width ] = [ svg.attr('height'), svg.attr('width') ]
[ x_ctr, y_ctr ] = [ height * 0.5, width * 0.5 ]
[ x_mul, y_mul ] = [ Math.sin(a_rad), Math.cos(a_rad) ]
axis.x_beg = x_ctr + x_mul * r_beg
axis.x_end = x_ctr + x_mul * r_end
axis.x_len = axis.x_end - axis.x_beg
axis.x_mul = x_mul
axis.y_beg = y_ctr + y_mul * r_beg
axis.y_end = y_ctr + y_mul * r_end
axis.y_len = axis.y_end - axis.y_beg
axis.y_mul = y_mul
a_func = () -> "rotate(#{ -axis.angle } " +
"#{ axis.x_beg } #{ axis.y_beg })"
axis_attrs =
class: 'axis_rect'
fill: axis.color
height: axis.r_len
transform: a_func()
stroke: axis.color
width: 2
x: axis.x_beg
y: axis.y_beg
svg.append('rect')
.call(set_attrs, axis_attrs)
.append('title').text(axis.title)
edge_dir: (axes, edge) ->
edge_dir_h( axes[ edge[0] ].angle,
axes[ edge[2] ].angle )
edge_dir_h: (a_beg, a_end) ->
diff = a_end - a_beg
abs = Math.abs(diff)
tmp = if diff < 0 then -1 else 1
if abs < 180 then tmp else -tmp
edge_path: (edge, axes, cp_off) ->
edge_dir = edge[5]
end_points = (end_key) ->
if end_key == 'from' then [ dir, ndx ] = [ edge_dir, 0 ]
else [ dir, ndx ] = [ -edge_dir, 2 ]
name = edge[ndx]
start = edge[ndx+1]
axis = axes[name]
x_ip = axis.x_beg + start * axis.x_len
y_ip = axis.y_beg + start * axis.y_len
x_off = cp_off * axis.y_mul
y_off = cp_off * axis.x_mul
x_cp = x_ip + x_off * dir
y_cp = y_ip - y_off * dir
[ [ x_ip, y_ip ], [ x_cp, y_cp ] ]
from = end_points('from')
to = end_points('to').reverse()
out = from.concat(to)
set_attrs: (object, hash) ->
object.attr(key, val) for key, val of hash
window[key] = val for key, val of functions
main.coffee
do ->
svg = d3.select('#chart').append('svg:svg')
.attr('width', 350)
.attr('height', 350)
axes = {}
axes.foo = { angle: 0, color: '#666', title: 'foo' }
axes.bar = { angle: 120, color: '#666', title: 'bar' }
axes.baz = { angle: 240, color: '#666', title: 'baz' }
axis.r_beg = 20 for name, axis of axes
axis.r_len = 100 for name, axis of axes
draw_axis(axis, svg) for name, axis of axes
edges1 = (['foo', Math.random(),
'bar', Math.random(),
'rgba(0,0,0,0.05)'] for i in [1..300])
edges2 = (['bar', Math.random(),
'baz', Math.sqrt(Math.random()),
'rgba(0,0,0,0.05)'] for i in [1..300])
edges3 = (['baz', Math.sqrt(Math.random()),
'foo', Math.pow(Math.random(), 3),
'rgba(0,0,0,0.05)'] for i in [1..300])
edges = [].concat edges1, edges2, edges3
edge.push( edge_dir(axes, edge) ) for edge in edges
cp_off = 30
paths = ( edge_path(edge, axes, cp_off) for edge in edges )
df = d3.svg.line()
.x( (d) -> d[0] )
.y( (d) -> d[1] )
.interpolate('cardinal')
.tension(15)
svg.selectAll('path.edge')
.data(paths)
.enter()
.append('svg:path')
.attr('d', df )
.attr('stroke', (d,i) -> edges[i][4] )