Want to improve this question? Add details and clarify the problem by editing this post.
Closed 20 hours ago.
//latest fiddle http://jsfiddle.net/rbLk2fbe/2/
I am trying to build this particular chart where I can control the curved paths to show a network. Two trunks that are spaced correctly - and then curved branches that leaf off to the various people.
var w = 600;
var h = 600;
var data = [{
"userName": "You",
"userImage": "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcSTzjaQlkAJswpiRZByvgsb3CVrfNNLLwjFHMrkZ_bzdPOWdxDE2Q"
}, {
"userName": "Johnny",
"userImage": "https://crossovercomicblog.files.wordpress.com/2012/08/johnny-depp-sexy.jpg"
}, {
"userName": "Jeri",
"userImage": "https://68.media.tumblr.com/avatar_3b6d6241698f_128.png"
}, {
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Charlize",
"userImage": "https://cdn.imza.com/indir/logo/128/charlize-theron.png"
},{
"userName": "Angelina",
"userImage": "https://pbs.twimg.com/profile_images/713650489032908800/nO1dMt6M_400x400.jpg"
}, {
"userName": "Them",
"userImage": "https://68.media.tumblr.com/avatar_8f199caf2d82_128.png"
}];
var viz = d3.select("#viz")
.append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(40,100)");
var patternsSvg = viz.append('g')
.attr('class', 'patterns');
var labelholder = viz.append("g")
.attr("class", "labelholder");
var treeholder = viz.append("g")
.attr("class", "treeholder");
var userholder = viz.append("g")
.attr("class", "userholder");
var smallRadius = 20;
var bigRadius = 30;
var smallX = smallRadius + (bigRadius/2);
var bigX = (bigRadius*2) + (smallRadius/2);
var verticalGap = (bigRadius * 2) - 5;
var count = data.length;
var extendedY = (count-2 * (smallRadius*2)) + ((bigRadius*2) * 2);
var arcRadiusLeft = (bigRadius / 2);
var arcRadiusRight = -(bigRadius / 2);
$.each(data, function(index, value) {
var defs = patternsSvg.append('svg:defs');
//big design
defs.append('svg:pattern')
.attr('id', index + "--" + value.userName.toLowerCase())
.attr('width', 1)
.attr('height', 1)
.append('svg:image')
.attr('xlink:href', value.userImage)
.attr('x', 0)
.attr('y', 0)
.attr('width', bigRadius * 2)
.attr('height', bigRadius * 2);
//small design
defs.append('svg:pattern')
.attr('id', index + "-" + value.userName.toLowerCase())
.attr('width', 1)
.attr('height', 1)
.append('svg:image')
.attr('xlink:href', value.userImage)
.attr('x', 0)
.attr('y', 0)
.attr('width', smallRadius * 2)
.attr('height', smallRadius * 2);
});
//plot people circles
var circle = userholder.append("g").selectAll("circle")
.data(data);
circle
.enter()
.append("svg:circle")
.attr("id", function(d) {
return d.userName.toLowerCase();
})
.attr("r", function(d, i) {
var rad = smallRadius;
//first and last items -- so you and them
if (i == 0 || i == count - 1) {
rad = bigRadius;
}
return rad;
})
.attr("cx", function(d, i) {
var cx;
if (i == 0) {
cx = 0; //first one
} else if (i == count - 1) {
cx = bigX; //last one
} else {
cx = smallX; //small ones
}
return cx;
})
.attr("cy", function(d, i) {
var cy;
if (i == 0) {
cy = 0;
} else if (i == count - 1) {
cy = verticalGap * (i-1) + extendedY + bigRadius;
} else {
cy = verticalGap * i
}
return cy;
})
.style("fill", function(d, i) {
var id = i + "-" + d.userName.toLowerCase(); //small circles
//large circles
if (i == 0 || i == count - 1) {
id = i + "--" + d.userName.toLowerCase();
}
return "url(#" + id + ")";
});
//plot people circles
//__labels
var labelholder = d3.select(".labelholder");
//__ enter
var labels = labelholder.selectAll("text")
.data(data);
labels.enter()
.append("text")
.attr("text-anchor", "left")
//__ update
labels
.attr("x", function(d, i) {
var displacement = (bigRadius/2) + smallRadius;
var cx = (smallRadius * 2);
if (i == 0) {
cx = bigRadius;
displacement = bigRadius/2;
}
if (i == count - 1) {
cx = (bigRadius * 2) + bigRadius;
displacement = bigRadius;
}
cx += displacement;
return cx;
})
.attr("y", function(d, i) {
var cy = verticalGap * i;
if (i == count - 1) {
cy += extendedY - (bigRadius/2);
}
return cy;
})
.text(function(d) {
return d.userName;
});
//__labels
var backbone = treeholder.append("g")
.append("svg:path");
backbone.attr("d", function(d, i) {
var sx = (bigRadius / 2) - (bigRadius / 2);
var tx = (bigRadius / 2) - (bigRadius / 2);
var r = smallRadius;
if (i == 0 || i == count - 1) {
r = bigRadius;
}
var sy = ((r / 2) * i) + (r);
var ty = verticalGap * (count - 2) - arcRadiusLeft;
var dr = 0;
return "M" + sx + "," + sy + "A" + dr + "," + dr + " 0 0,1 " + tx + "," + ty;
});
var displaceYBackboneRight = (bigRadius / 2) + 5;
var backbone = treeholder.append("g")
.append("svg:path");
backbone.attr("d", function(d, i) {
var sx = (bigRadius * 2) + smallRadius/2;
var tx = (bigRadius * 2) + smallRadius/2;
var r = smallRadius;
if (i == 0 || i == count - 1) {
r = bigRadius;
}
var sy = ((r / 2) * i) + (r) + smallRadius + displaceYBackboneRight;
var ty = verticalGap * (count - 2) + extendedY;
var dr = 0;
return "M" + sx + "," + sy + "A" + dr + "," + dr + " 0 0,1 " + tx + "," + ty;
});
//branches on the left
var leftpath = treeholder.append("g").selectAll("path.leftpath")
.data(data)
leftpath
.enter().append("svg:path")
.attr("class", function(d) {
return "leftpath";
});
leftpath.attr("d", function(d, i) {
var sx = 0;
var tx = arcRadiusLeft;
var sy = verticalGap * i - arcRadiusLeft;
var ty = verticalGap * i;
if (i != 0 && i != count - 1) {
return "M" + sx + "," + sy + "A" + arcRadiusLeft + "," + arcRadiusLeft + " 0 0,0 " + tx + "," + ty;
}
});
//branches on the left
var rightpath = treeholder.append("g").selectAll("path.rightpath")
.data(data)
rightpath
.enter().append("svg:path")
.attr("class", function(d) {
return "rightpath";
});
rightpath.attr("d", function(d, i) {
var sx = (bigRadius*2) + (smallRadius/2);
var tx = arcRadiusRight + (bigRadius*2) + (smallRadius/2);
var sy = verticalGap * i + (bigRadius / 2);
var ty = verticalGap * i + arcRadiusRight + (bigRadius / 2);
if (i != 0 && i != count - 1) {
return "M" + sx + "," + sy + "A" + arcRadiusLeft + "," + arcRadiusLeft + " 0 0,0 " + tx + "," + ty;
}
});
//old http://jsfiddle.net/NYEaX/1811/
So the key here is to create the small arcs correctly using maths.
Something like this
path.attr("d", function (d, i) {
const sx = 0;
const sy = height/2;
//width - total width of chart from subject a to subject b
const a = width/2;
//a - distance between subject a and center
const b = (1.5-i)*distanceBetween;
//b - distance between trait 1 and trait 2 (between the dots)
const c = Math.sqrt(a * a + b * b);
//c - is the diagonal distance between subject a and trait 1
const angle=Math.atan(a/b);
//angle - between group to subject a
const r=1/2*c/Math.cos(angle);
//r - 1/2 the distance of c -- divided by cos of angle -- will create a radius to draw the arc
//also equals c/b*(c/2)
// const r=c/b*(c/2);
return `M${sx},${sy} A${r},${r} 0 0,${b>0?1:0} ${width},${height/2}`;
});