I'm working with a D3 example file for a force directed voronoi graph... however, I mainly just needed a simplified version with only three vertices... so I simplified the file and have included a JSFiddle example of where my file stands currently. My issue is how the edge condition is handled. Right now, the voronoi edges extend out to the edge of the container div. However, I'd like to clip each voronoi cell by a circle boundary. I've included two images to help explain my problem. The first image shows the script as it exists now, whereas the second is made with photoshop - showing the circular clipping boundary. It seems like D3's polygon.clip method would be the best option, but I don't really know how to implement the clipping method into my script. Any suggestions would be greatly appreciated.
<!DOCTYPE html>
<html>
<head>
<title>Voronoi Diagram with Force Directed Nodes and Delaunay Links</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
path
{
stroke: #EFEDF5;
stroke-width: 4px;
}
</style>
</head>
<body>
<div id="chart">
</div>
<script type="text/javascript">
var w = window.innerWidth > 960 ? 960 : (window.innerWidth || 960),
h = window.innerHeight > 500 ? 500 : (window.innerHeight || 500),
radius = 5.25,
links = [],
simulate = true,
zoomToAdd = true,
cc = ["#FFA94A","#F58A3A","#F85A19"]
var numVertices = (w*h) / 200000;
var vertices = d3.range(numVertices).map(function(i) {
angle = radius * (i+10);
return {x: angle*Math.cos(angle)+(w/2), y: angle*Math.sin(angle)+(h/2)};
});
var d3_geom_voronoi = d3.geom.voronoi().x(function(d) { return d.x;}).y(function(d) { return d.y; })
var prevEventScale = 1;
var zoom = d3.behavior.zoom().on("zoom", function(d,i) {
if (zoomToAdd){
if (d3.event.scale > prevEventScale) {
angle = radius * vertices.length;
}
force.nodes(vertices).start()
} else {
if (d3.event.scale > prevEventScale) {
radius+= .01
} else {
radius -= .01
}
vertices.forEach(function(d, i) {
angle = radius * (i+10);
vertices[i] = {x: angle*Math.cos(angle)+(w/2), y: angle*Math.sin(angle)+(h/2)};
});
force.nodes(vertices).start()
}
prevEventScale = d3.event.scale;
});
d3.select(window)
.on("keydown", function() {
// shift
if(d3.event.keyCode == 16) {
zoomToAdd = false
}
})
.on("keyup", function() {
zoomToAdd = true
})
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h)
.call(zoom)
var force = d3.layout.force()
.charge(-300)
.size([w, h])
.on("tick", update);
force.nodes(vertices).start();
var path = svg.selectAll("path");
function update(e) {
path = path.data(d3_geom_voronoi(vertices))
path.enter().append("path")
// drag node by dragging cell
.call(d3.behavior.drag()
.on("drag", function(d, i) {
vertices[i] = {x: vertices[i].x + d3.event.dx, y: vertices[i].y + d3.event.dy}
})
)
.style("fill", function(d, i) { return cc[0] })
path.attr("d", function(d) { return "M" + d.join("L") + "Z"; })
.transition().duration(150)
.style("fill", function(d, i) { return cc[i] })
path.exit().remove();
if(!simulate) force.stop()
}
Related
I'm fairly new to D3 and I am trying to use d3.js & Lasso in order to allow users to select dots on a scatterplot. I've found an example of how to do this right here: http://bl.ocks.org/skokenes/511c5b658c405ad68941
This works perfectly fine in D3 with V5 but I have a requirement to upgrade to D3 V6 and the code breaks.
d3-lasso.js:819 Uncaught TypeError: Cannot read property 'sourceEvent' of undefined
at SVGRectElement.dragmove (d3-lasso.js:819)
at nt.call (d3.min.js:2)
at Object.e [as mouse] (d3.min.js:2)
at p (d3.min.js:2)
at d3.min.js:2
Any help would be appreciated ?
-- 2023 solution for D3.v7
I rewrite the lasso function from scratch using the d3 drag event.
Below shows you an example of how to perform lasso selection on a scatter plot. The basic idea is to render a path when your cursor moves on the screen and determine which points are inside your polygon using an existing algorithm.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>d3-lasso</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
#chart {
width: 600px;
height: 400px;
background-color: wheat;
}
</style>
</head>
<body>
<div>
<svg id="chart">
</svg>
</div>
<script>
const data = [
{ x: -4, y: 14, id: 1 },
{ x: -26, y: -49, id: 2 },
{ x: 28, y: 11, id: 3 },
{ x: 0, y: -30, id: 4 },
{ x: 6.9, y: -63, id: 5 }
];
let xScale = d3
.scaleLinear()
.domain(d3.extent(data.map((d) => d.x)))
.range([50, 550]);
let yScale = d3
.scaleLinear()
.domain(d3.extent(data.map((d) => d.y)))
.range([50, 350]);
const circles = d3
.select("#chart")
.selectAll("circle")
.data(data)
.join("circle")
.attr("id", (d) => {
return "dot-" + d.id;
})
.attr("cx", (d) => {
return xScale(d.x);
})
.attr("cy", (d) => {
return yScale(d.y);
})
.attr("r", 10)
.attr("opacity", 0.5)
.attr("fill", 'steelblue');
// lasso selection based on the drag events
let coords = [];
const lineGenerator = d3.line();
const pointInPolygon = function (point, vs) {
// console.log(point, vs);
// ray-casting algorithm based on
// https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html/pnpoly.html
var x = point[0],
y = point[1];
var inside = false;
for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
var xi = vs[i][0],
yi = vs[i][1];
var xj = vs[j][0],
yj = vs[j][1];
var intersect =
yi > y != yj > y &&
x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
if (intersect) inside = !inside;
}
return inside;
};
function drawPath() {
d3.select("#lasso")
.style("stroke", "black")
.style("stroke-width", 2)
.style("fill", "#00000054")
.attr("d", lineGenerator(coords));
}
function dragStart() {
coords = [];
circles.attr("fill", "steelblue");
d3.select("#lasso").remove();
d3.select("#chart")
.append("path")
.attr("id", "lasso");
}
function dragMove(event) {
let mouseX = event.sourceEvent.offsetX;
let mouseY = event.sourceEvent.offsetY;
coords.push([mouseX, mouseY]);
drawPath();
}
function dragEnd() {
let selectedDots = [];
circles.each((d, i) => {
let point = [
xScale(d.x),
yScale(d.y),
];
if (pointInPolygon(point, coords)) {
d3.select("#dot-" + d.id).attr("fill", "red");
selectedDots.push(d.id);
}
});
console.log(`select: ${selectedDots}`);
}
const drag = d3
.drag()
.on("start", dragStart)
.on("drag", dragMove)
.on("end", dragEnd);
d3.select("#chart").call(drag);
</script>
</body>
</html>
How to draw the line using bezierCurveTo method in d3.js such that lines appears as below image
I have just referred bezier Curve but i'm not getting any idea on it.
There are a lot of ways this could be done. One could make a custom curve that achieves this.
But, we could keep it simpler too. The datum passed to the link generator, such as d3.linkHorizontal, from a d3 layout generally contains a source and target property, each of these usually contain x and y properties. Assuming this structure, we could create a function that uses these and creates and returns the appropriate path data with a bezier curve:
var linker = function(d) {
var x0 = d.source.x;
var y0 = d.source.y;
var y1 = d.target.y;
var x1 = d.target.x;
var k = 120;
var path = d3.path()
path.moveTo(y0,x0)
path.bezierCurveTo(y1-k,x0,y0,x1,y1-k,x1);
path.lineTo(y1,x1);
return path.toString();
}
The above is pretty basic, it uses d3.path but you could easily just construct the SVG path string yourself. There are lots of interactive bezier curve explorers online so you can figure out what control points work best. As the tree layout I've used is vertical, I've turned it horizontal by inverting x and y, which is why my coordinates are [y,x]. I use k above to offset the bezier curve to a small portion of the overall link on the left:
But you could easily toy with this to place the curve in the middle of the link:
Here's it in action:
var data = { "name": "Parent", "children": [
{ "name": "Child A", "children": [ { "name": "Grandchild1"}, {"name":"Grandchild2" } ] },
{ "name": "Child B", }
] };
var width = 400;
var height = 300;
margin = {left: 50, top: 10, right:30, bottom: 10}
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g").attr('transform','translate('+ margin.left +','+ margin.right +')');
var root = d3.hierarchy(data);
var tree = d3.tree()
.size([height-margin.top-margin.bottom,width-margin.left-margin.right]);
var linker = function(d) {
var x0 = d.source.x;
var y0 = d.source.y;
var y1 = d.target.y;
var x1 = d.target.x;
var k = (y1-y0)/2;
var path = d3.path()
path.moveTo(y0,x0)
path.lineTo(y0+k/2,x0)
path.bezierCurveTo(y1-k,x0,y0+k,x1,y1-k/2,x1);
path.lineTo(y1,x1);
return path.toString();
}
var link = g.selectAll(".link")
.data(tree(root).links())
.enter().append("path")
.attr("class", "link")
.attr("d", linker);
var node = g.selectAll(".node")
.data(root.descendants())
.enter().append("g")
.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
node.append("circle")
.attr("r", 2.5);
node.append("text")
.text(function(d) { return d.data.name; })
.attr('y',-10)
.attr('x',-10)
.attr('text-anchor','middle');
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
But, in reading the comments I notice that your question may be more about dagreD3 - which changes things considerably. Dagre D3 offers better ease of use relative to D3 at the cost of the some of D3's flexibility. If you want to provide a certain type of curve to DagreD3, then you should use a d3 curve, or some custom curve (as in the linked answer above). You can specify the curve when adding edges easily enough.
But that doesn't solve the issue of edges originating from the same point as in your image. I'll provide a d3 based solution - which probably breaks edge label placement, transitions, etc - so it should be built out a bit if you need that functionality. I'll use the bezier generator from above. The following is inspired by this:
var g = new dagreD3.graphlib.Graph()
.setGraph({rankdir: 'LR'})
.setDefaultEdgeLabel(function() { return {}; });
g.setNode(0, { label: "0"});
g.setNode(1, { label: "1"});
g.setNode(2, { label: "2"});
g.setNode(3, { label: "3"});
g.setNode(4, { label: "4"});
g.setEdge(0, 1);
g.setEdge(0, 2);
g.setEdge(1, 3);
g.setEdge(1, 4);
var render = new dagreD3.render().createEdgePaths(createEdgePaths);
var svg = d3.select("svg"),
svgGroup = svg.append("g"),
zoom = d3.zoom().on("zoom", function() {
svgGroup.attr("transform", d3.event.transform);
});
svg.call(zoom);
render(svgGroup, g);
function createEdgePaths(selection, g, arrows) {
selection.selectAll("g.edgePath")
.data(g.edges())
.enter()
.append("path")
.attr("d", function(e) {
return calcPoints(g,e);
});
}
function calcPoints(g, e) {
var source = g.node(e.v);
var target = g.node(e.w);
var x0 = source.x + source.width/2;
var x1 = target.x - target.width/2;
var y0 = source.y;
var y1 = target.y;
return linker(x0,y0,x1,y1)
}
function linker(x0,y0,x1,y1) {
var dx = x1 -x0;
var k = dx/3;
var path = d3.path()
path.moveTo(x0,y0)
path.bezierCurveTo(x1-k,y0,x0,y1,x1-k,y1);
path.lineTo(x1,y1);
return path.toString();
}
path {
stroke: #333;
stroke-width: 1.5px;
fill: none;
}
rect {
fill: none;
stroke:black;
stroke-width: 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dagre-d3#0.6.1/dist/dagre-d3.min.js"></script>
<svg width="800" height="600"></svg>
I'm creating an org chart in D3 based on Bernhard Zuba's D3.js Organizational Chart. The org chart models an organization in which any given person (represented by a white square) may have a hundred or so people immediately beneath them (a very flat tree structure with a black bezier curve representing each parent-child relationship).
Here's a screencap of part of the tree:
And here's a zoom-in on the bottom of the parent node in the above picture:
The problem is that the links between child and parent nodes tend to all bunch up together, resulting in a very thick black line with a very gradual slope, which can be a bit of an eyesore.
The function I'm using to generate the links is as follows:
// Diagonal function
var diagonal = d3.svg.diagonal()
.source(function (d) {
return {
x: d.source.x + (rectW / 2),
y: d.source.y + rectH - 10
};
})
.target(function (d) {
return {
x: d.target.x + (rectW / 2),
y: d.target.y + 10
};
})
.projection(function (d) {
return [d.x, d.y];
});
Here, rectW is the width of each node and rectH is the height of each node.
What I'd like to do is make some slight adjustments to the bezier function used to generate the links. Specifically, I'd like to flatten out the control points a little so that the curves at the start and end of the curve are more dramatic. If anyone can show me how to alter the function used by diagonal() to generate the bezier curve, I can figure out the rest.
If you look at the source code to svg.diagonal, I can't really see a direct way to adjust just the control points. You'd think you could use projection for this, but that'll transform all 4 points used to generate the path. Now, I guess we could get a little hacky with projection and do something like this:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.17" data-semver="3.5.17" src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script>
var data = [{
source: {
x: 10,
y: 10
},
target: {
x: 200,
y: 200
}
}, {
source: {
x: 50,
y: 50
},
target: {
x: 200,
y: 200
}
}];
var svg = d3.select('body')
.append('svg')
.attr('width', 205)
.attr('height', 205);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
if (!this.times) this.times = 0;
this.times++;
console.log(this.times);
if (this.times === 1) {
return [d.x, d.y];
} else if (this.times === 2) {
return [d.x - 25, d.y]
} else if (this.times === 3) {
return [d.x + 25, d.y];
} else if (this.times === 4) {
this.times = 0;
return [d.x, d.y];
}
});
svg.selectAll('path')
.data(data)
.enter()
.append('path')
.attr('d', diagonal)
.style('fill', 'none')
.style('stroke', 'black');
</script>
</body>
</html>
I might be over thinking this. You'd probably just be better drawing the arc yourself:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.17" data-semver="3.5.17" src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script>
var data = [{
source: {
x: 10,
y: 10
},
target: {
x: 200,
y: 200
}
}, {
source: {
x: 200,
y: 10
},
target: {
x: 10,
y: 200
}
}];
var svg = d3.select('body')
.append('svg')
.attr('width', 205)
.attr('height', 205);
svg.selectAll('path')
.data(data)
.enter()
.append('path')
.attr('d', function(d){
var s = d.source,
t = d.target,
m = (s.y + t.y) / 2,
p0 = [s.x, s.y],
p1 = [s.x, m],
p2 = [t.x, m],
p3 = [t.x, t.y];
// adjust constrol points
p1[0] -= 25;
p2[0] += 25;
return "M" + p0 + "C" + p1 + " " + p2 + " " + p3;
})
.style('fill', 'none')
.style('stroke', 'black');
</script>
</body>
</html>
i have a problem when i do a zoom in my map because the labels appears duplicate. I know that my problem appears because in my zoom i dont delete the label. I know where is the problem, i need to delete the oldest label when i do a zoom but i dont know how and where to solve this.
Any idea? Ty for all.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
margin: 0;
}
path {
fill: none;
stroke: green;
stroke-linejoin: round;
stroke-width: 1.5px;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
<script src="d3.geo.tile.min.js"></script>
<script>
var width = Math.max(960, window.innerWidth),
height = Math.max(600, window.innerHeight);
var tile = d3.geo.tile()
.size([width, height]);
var projection = d3.geo.mercator()
.scale((3 << 12) / 2 / Math.PI)
.translate([width / 2, height / 2]);
var center = projection([-3, 36]);
var path = d3.geo.path()
.projection(projection);
var zoom = d3.behavior.zoom()
.scale(projection.scale() * 2 * Math.PI)
.translate([width - center[0], height - center[1]])
.on("zoom", zoomed);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var raster = svg.append("g");
var g = svg.append("g");
var vector = svg.append("path");
d3.json("es.json", function(error, es) {
if (error) throw error;
svg.call(zoom);
vector.datum(topojson.mesh(es, es.objects.provinces));
zoomed();
});
function zoomed() {
d3.csv("data/country-capitals.csv", function(err, capitals) {
capitals.forEach(function(i){
addpoint(i.CapitalLongitude, i.CapitalLatitude, i.CapitalName );
});
});
var tiles = tile
.scale(zoom.scale())
.translate(zoom.translate())
();
projection
.scale(zoom.scale() / 2 / Math.PI)
.translate(zoom.translate());
vector
.attr("d", path);
var image = raster
.attr("transform", "scale(" + tiles.scale + ")translate(" + tiles.translate + ")")
.selectAll("image")
.data(tiles, function(d) { return d; });
image.exit()
.remove();
image.enter().append("image")
.attr("xlink:href", function(d) { return "http://" + ["a", "b", "c"][Math.random() * 3 | 0] + ".tile.openstreetmap.org/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; })
.attr("width", 1)
.attr("height", 1)
.attr("x", function(d) { return d[0]; })
.attr("y", function(d) { return d[1]; });
}
function addpoint(lat,lon,text) {
var gpoint = g.append("g").attr("class", "gpoint");
var x = projection([lat,lon])[0];
var y = projection([lat,lon])[1];
gpoint.append("svg:circle")
.attr("cx", x)
.attr("cy", y)
.attr("class","point")
.attr("r", 1.5);
//conditional in case a point has no associated text
if(text.length>0){
gpoint.append("text")
.attr("x", x+2)
.attr("y", y+2)
.attr("class","text")
.text(text);
}
}
</script>
CSV is here:
CountryName,CapitalName,CapitalLatitude,CapitalLongitude,CountryCode,ContinentName
Brazil,Brasilia,-15.783333333333333,-47.916667,BR,South America
Colombia,Bogota,4.6,-74.083333,CO,South America
Egypt,Cairo,30.05,31.250000,EG,Africa
France,Paris,48.86666666666667,2.333333,FR,Europe
Iraq,Baghdad,33.333333333333336,44.400000,IQ,Asia
South Korea,Seoul,37.55,126.983333,KR,Asia
Kosovo,Pristina,42.666666666666664,21.166667,KO,Europe
Mexico,Mexico City,19.433333333333334,-99.133333,MX,Central America
Before you zoom you can remove all the group containing text and circle like this:
function zoomed() {
d3.selectAll(".gpoint").remove();
d3.csv("my.csv", function(err, capitals) {
capitals.forEach(function(i){
addpoint(i.CapitalLongitude, i.CapitalLatitude, i.CapitalName );
});
});
//your code
Working code here
I started working with this d3.js Donut Chart: JSFiddleI am trying to change it into a Pie Chart without the circle in the middle. I am new to d3.js. I have tried several different ideas but have been unable to get this to remove the circle in the middle of the chart. Any and all help is appreciated
Here is my code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<style>
.label-text {
alignment-baseline : middle;
font-size: 12px;
font-family: arial,helvetica,"sans-serif";
fill: #393939;
}
.label-line {
stroke-width: 1;
stroke: #393939;
}
.label-circle {
fill: #393939;
}
</style>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg>
<g id="canvas">
<g id="art" />
<g id="labels" /></g>
</svg>
<script>
var data = [{
label: 'Star Wars',
instances: 207
}, {
label: 'Lost In Space',
instances: 3
}, {
label: 'the Boston Pops',
instances: 20
}, {
label: 'Indiana Jones',
instances: 150
}, {
label: 'Harry Potter',
instances: 75
}, {
label: 'Jaws',
instances: 5
}, {
label: 'Lincoln',
instances: 1
}];
svg = d3.select("svg");
canvas = d3.select("#canvas");
art = d3.select("#art");
labels = d3.select("#labels");
// Create the pie layout function.
// This function will add convenience
// data to our existing data, like
// the start angle and end angle
// for each data element.
jhw_pie = d3.layout.pie();
jhw_pie.sort(null);
jhw_pie.value(function (d) {
// Tells the layout function what
// property of our data object to
// use as the value.
return d.instances;
});
// Store our chart dimensions
cDim = {
height: 500,
width: 500,
innerRadius: 50,
outerRadius: 150,
labelRadius: 175
}
// Set the size of our SVG element
svg.attr({
height: cDim.height,
width: cDim.width
});
// This translate property moves the origin of the group's coordinate
// space to the center of the SVG element, saving us translating every
// coordinate individually.
canvas.attr("transform", "translate(" + (cDim.width / 2) + "," + (cDim.height / 2) + ")");
pied_data = jhw_pie(data);
// The pied_arc function we make here will calculate the path
// information for each wedge based on the data set. This is
// used in the "d" attribute.
pied_arc = d3.svg.arc()
.innerRadius(50)
.outerRadius(150);
// This is an ordinal scale that returns 10 predefined colors.
// It is part of d3 core.
pied_colors = d3.scale.ordinal()
.range(["#04B486", "#F2F2F2", "#F5F6CE", "#00BFFF","orange","purple","pink"]);
// Let's start drawing the arcs.
enteringArcs = art.selectAll(".wedge").data(pied_data)
.enter();
enteringArcs
.append("g")
.attr("class", "wedge")
.append("path")
.attr("d", pied_arc)
.style("fill", function (d, i) {
return pied_colors(i);
});
// Now we'll draw our label lines, etc.
enteringLabels = labels.selectAll(".label").data(pied_data).enter();
labelGroups = enteringLabels.append("g").attr("class", "label");
labelGroups.append("circle").attr({
x: 0,
y: 0,
r: 2,
fill: "#000",
transform: function (d, i) {
centroid = pied_arc.centroid(d);
return "translate(" + pied_arc.centroid(d) + ")";
},
'class': "label-circle"
});
// "When am I ever going to use this?" I said in
// 10th grade trig.
textLines = labelGroups.append("line").attr({
x1: function (d, i) {
return pied_arc.centroid(d)[0];
},
y1: function (d, i) {
return pied_arc.centroid(d)[1];
},
x2: function (d, i) {
centroid = pied_arc.centroid(d);
midAngle = Math.atan2(centroid[1], centroid[0]);
x = Math.cos(midAngle) * cDim.labelRadius;
return x;
},
y2: function (d, i) {
centroid = pied_arc.centroid(d);
midAngle = Math.atan2(centroid[1], centroid[0]);
y = Math.sin(midAngle) * cDim.labelRadius;
return y;
},
'class': "label-line"
});
textLabels = labelGroups.append("text").attr({
x: function (d, i) {
centroid = pied_arc.centroid(d);
midAngle = Math.atan2(centroid[1], centroid[0]);
x = Math.cos(midAngle) * cDim.labelRadius;
sign = (x > 0) ? 1 : -1
labelX = x + (5 * sign)
return labelX;
},
y: function (d, i) {
centroid = pied_arc.centroid(d);
midAngle = Math.atan2(centroid[1], centroid[0]);
y = Math.sin(midAngle) * cDim.labelRadius;
return y;
},
'text-anchor': function (d, i) {
centroid = pied_arc.centroid(d);
midAngle = Math.atan2(centroid[1], centroid[0]);
x = Math.cos(midAngle) * cDim.labelRadius;
return (x > 0) ? "start" : "end";
},
'class': 'label-text'
}).text(function (d) {
return d.data.label
});
alpha = 0.5;
spacing = 12;
function relax() {
again = false;
textLabels.each(function (d, i) {
a = this;
da = d3.select(a);
y1 = da.attr("y");
textLabels.each(function (d, j) {
b = this;
// a & b are the same element and don't collide.
if (a == b) return;
db = d3.select(b);
// a & b are on opposite sides of the chart and
// don't collide
if (da.attr("text-anchor") != db.attr("text-anchor")) return;
// Now let's calculate the distance between
// these elements.
y2 = db.attr("y");
deltaY = y1 - y2;
// Our spacing is greater than our specified spacing,
// so they don't collide.
if (Math.abs(deltaY) > spacing) return;
// If the labels collide, we'll push each
// of the two labels up and down a little bit.
again = true;
sign = deltaY > 0 ? 1 : -1;
adjust = sign * alpha;
da.attr("y", +y1 + adjust);
db.attr("y", +y2 - adjust);
});
});
// Adjust our line leaders here
// so that they follow the labels.
if (again) {
labelElements = textLabels[0];
textLines.attr("y2", function (d, i) {
labelForLine = d3.select(labelElements[i]);
return labelForLine.attr("y");
});
setTimeout(relax, 20)
}
}
relax();
</script>
</body>
</html>
Thanks
See this updated fiddle.
The code contained the following lines, of which the innerRadious was changed to 0.
pied_arc = d3.svg.arc()
.innerRadius(00) // <- this
.outerRadius(150);
It's a bit misleading, as there's an innerRadius variable somewhere before that, but it's not used at this point. While you're at it, you might want to align all of that stuff.