Lasso Plugin won't work with D3 upgrade to V6 - d3.js

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>

Related

D3 transition along segments of path and pause at coordinate values

I would like to be able to click on a circle (coordinate points); bring the marker to the position of the circle and pause at the position of the circle and then resume again along the path.
In addition I would like to activate a circle when marker is paused on them - they are clicked (or their Voronoi cell is clicked). My intention is to have an on click function to an href for the circle coordinates eventually.
I think I need to pass the index of the path coordinates into the translateAlong function instead of the time variables but can't work out how to do this.
I’m not sure if the Voronoi cells are necessary - I tried to add this thinking I could pause my transition and activate my circles with the Voronoi cells. In any case I can’t activate the circle with the Voronoi cell.
I was helped considerably recently on Stackoverflow d3 on click on circle pause and resume transition of marker along line
and I am hoping for assistance again
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>basic_animateBetweenCircles</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
path {
stroke: #848484;
fill: none;
}
circle {
fill: steelblue;
stroke: steelblue;
stroke-width: 3px;
}
.line {
fill: none;
stroke: #FE642E;
stroke-width: 4;
stroke-dasharray: 4px, 8px;
}
.point{
fill:#DF013A;
}
</style>
</head>
<body>
<script>
var width = 960,
height = 500;
var data = [
[480, 200],
[580, 400],
[680, 100],
[780, 300],
[180, 300],
[280, 100],
[380, 400]
];
//check index of path data
for (var i = 0; i < data.length; i++) {
var coordindex = i + " " + data[i];
console.log("Coordindex: " + coordindex);
//return coordindex;
};
var duration = 20000;
var line = d3.line()
.x(function(d) {return (d)[0];})
.y(function(d) {return (d)[1];});
var voronoi = d3.voronoi()
.extent([[0, 0], [width, height]]);
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
//path to animate - marker transitions along this path
var path = svg.append("path")
.data([data])
.attr("d", line)
.attr('class', 'line')
.attr("d", function(d) {
return line(d)
});
//voronoi
var voronoiPath = svg.append("g")
.selectAll("path")
.data(voronoi.polygons(data))
.enter().append("path")
.attr("d", polygon)
.on("touchmove mousemove", function() {
d3.select(this)
.style("fill", "purple");
});
//Want to activate circles when marker paused on them / in voronoi cell - intention is to have on click to href
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class", "point")
.attr("r", 10)
.attr("transform", function(d) { return "translate(" + d + ")"; })
.on('click', function(d, i) {
d3.select(this)
.style("fill", "green");
if (d3.active(this)) {
marker.transition();
setTimeout(function() {
pauseValues.lastTime = pauseValues.currentTime;
//console.log(pauseValues);
}, 100);
} else {
transition();
}
});
var pauseValues = {
lastTime: 0,
currentTime: 0
};
//marker to transition along path
var marker = svg.append("circle")
.attr("r", 19)
.attr("transform", "translate(" + (data[0]) + ")")
.on('click', function(d, i) {
if (d3.active(this)) {
marker.transition();
setTimeout(function() {
pauseValues.lastTime = pauseValues.currentTime;
//console.log(pauseValues);
}, 100);
} else {
transition();
}
});
function transition() {
marker.transition()
.duration(duration - (duration * pauseValues.lastTime))
.attrTween("transform", translateAlong(path.node()))
.on("end", function() {
pauseValues = {
lastTime: 0,
currentTime: 0
};
transition()
});
}
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
t += pauseValues.lastTime;
var p = path.getPointAtLength(t * l);
pauseValues.currentTime = t;
return "translate(" + p.x + "," + p.y + ")";
};
};
}
function polygon(d) {
return "M" + d.join("L") + "Z";
}
</script>
</body>
If you want to pause at points, I would not run one transition across the entire path. Instead, I would break it up into N transitions, moving from point to point. Before starting the circle on it's next leg, you can pause it for a time. To do this, I would just transition along each line segment with a little algebra:
// copy our data
transData = data.slice();
function transition() {
marker.transition()
.ease(d3.easeLinear)
.duration(duration)
.attrTween("transform", function(){
// get our two points
// slope between them
// and intercetp
var p0 = transData.shift(),
p1 = transData[0];
m = (p0[1] - p1[1]) / (p0[0] - p1[0]),
b = p0[1] - (m * p0[0]),
i = d3.interpolateNumber(p0[0], p1[0]);
// move the point along the line
return function(t){
var x = i(t),
y = m*x + b;
return "translate(" + x + "," + y + ")";
}
})
// one line segment is complete
.on("end", function(){
// if no more movements, stop
if (transData.length <= 1) return;
iter++;
// determine if this is a "pause"
setTimeout(transition, pausePoints.indexOf(iter) !== -1 ? pauseTime : 0);
});
Running code, click a dot to start you can pause a multiple points:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>basic_animateBetweenCircles</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
path {
stroke: #848484;
fill: none;
}
circle {
fill: steelblue;
stroke: steelblue;
stroke-width: 3px;
}
.line {
fill: none;
stroke: #FE642E;
stroke-width: 4;
stroke-dasharray: 4px, 8px;
}
.point {
fill: #DF013A;
}
</style>
</head>
<body>
<script>
var width = 960,
height = 500;
var data = [
[480, 200],
[580, 400],
[680, 100],
[780, 300],
[180, 300],
[280, 100],
[380, 400]
];
var duration = 20000/data.length,
pauseTime = 2000;
var line = d3.line()
.x(function(d) {
return (d)[0];
})
.y(function(d) {
return (d)[1];
});
var voronoi = d3.voronoi()
.extent([
[0, 0],
[width, height]
]);
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
//path to animate - marker transitions along this path
var path = svg.append("path")
.data([data])
.attr("d", line)
.attr('class', 'line')
.attr("d", function(d) {
return line(d)
});
//voronoi
var voronoiPath = svg.append("g")
.selectAll("path")
.data(voronoi.polygons(data))
.enter().append("path")
.attr("d", polygon);
//Want to activate circles when marker paused on them / in voronoi cell - intention is to have on click to href
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class", "point")
.attr("r", 10)
.attr("transform", function(d) {
return "translate(" + d + ")";
})
.on('click', function(d, i) {
d3.select(this)
.style("fill", "green");
pausePoints.push(i);
if (pausePoints.length === 1)
transition();
});
//marker to transition along path
var marker = svg.append("circle")
.attr("r", 19)
.attr("transform", "translate(" + (data[0]) + ")");
var pausePoints = [],
iter = 0,
transData = data.slice();
function transition() {
marker.transition()
.ease(d3.easeLinear)
.duration(duration)
.attrTween("transform", function(){
var p0 = transData.shift(),
p1 = transData[0];
m = (p0[1] - p1[1]) / (p0[0] - p1[0]),
b = p0[1] - (m * p0[0]),
i = d3.interpolateNumber(p0[0], p1[0]);
return function(t){
var x = i(t),
y = m*x + b;
return "translate(" + x + "," + y + ")";
}
})
.on("end", function(){
if (transData.length <= 1) return;
iter++;
setTimeout(transition, pausePoints.indexOf(iter) !== -1 ? pauseTime : 0);
});
}
function polygon(d) {
return "M" + d.join("L") + "Z";
}
</script>
</body>

d3.js thickening only one arc

I am new to javascript and d3.js. I am trying to create pie chart.
I want to create something like this.
I am using d3.js. I want to thicken one of the arcs. I cannot find out to do this. I just want to thicken only "Engaged" item. It is not related to percentiles.
Here is my js code:
var engaged = 10;
var notEngaged = 50;
var maybeEngaged = 50;
var engagedYuzdelik = (100*engaged)/(engaged+notEngaged+maybeEngaged);
var notEngagedYuzdelik = (100*notEngaged)/(engaged+notEngaged+maybeEngaged);
var maybeEngagedYuzdelik=(100*maybeEngaged)/(engaged+notEngaged+maybeEngaged);
var dataset = [
{ name: 'Engaged', percent: [engagedYuzdelik.toFixed(2)] },
{ name: 'Not Engaged', percent: [notEngagedYuzdelik.toFixed(2)] },
{ name: 'Maybe Not Engaged', percent: [maybeEngagedYuzdelik.toFixed(2)] },
];
var pie=d3.layout.pie()
.value(function(d){return d.percent})
.sort(null);
var w=300,h=300;
var radius = Math.min(w, h) / 2;
var outerRadius=w/2;
var innerRadius=100;
var color = d3.scale.ordinal()
.range(["#65A6BF", "#9AC4D7", "#CCE2EA"]);
var arc=d3.svg.arc()
.outerRadius(radius - 50)
.innerRadius(radius - 60);
var svg=d3.select("#chart")
.append("svg")
.attr({
width:w,
height:h,
class:'shadow'
}).append('g')
.attr({
transform:'translate('+w/2+','+h/2+')'
});
var path=svg.selectAll('path')
.data(pie(dataset))
.enter()
.append('path')
.attr({
d:arc,
fill:function(d,i){
return color(d.data.name);
}
});
path.transition()
.duration(1000)
.attrTween('d', function(d) {
var interpolate = d3.interpolate({startAngle: 0, endAngle: 0}, d);
return function(t) {
return arc(interpolate(t));
};
});
};
Make the arc drawing conditional:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.17" data-semver="3.5.17" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
var engaged = 10;
var notEngaged = 50;
var maybeEngaged = 50;
var engagedYuzdelik = (100*engaged)/(engaged+notEngaged+maybeEngaged);
var notEngagedYuzdelik = (100*notEngaged)/(engaged+notEngaged+maybeEngaged);
var maybeEngagedYuzdelik=(100*maybeEngaged)/(engaged+notEngaged+maybeEngaged);
var dataset = [
{ name: 'Engaged', percent: [engagedYuzdelik.toFixed(2)] },
{ name: 'Not Engaged', percent: [notEngagedYuzdelik.toFixed(2)] },
{ name: 'Maybe Not Engaged', percent: [maybeEngagedYuzdelik.toFixed(2)] },
];
var pie=d3.layout.pie()
.value(function(d){return d.percent})
.sort(null);
var w=300,h=300;
var radius = Math.min(w, h) / 2;
var outerRadius=w/2;
var innerRadius=100;
var color = d3.scale.ordinal()
.range(["#65A6BF", "#9AC4D7", "#CCE2EA"]);
var thinArc = d3.svg.arc()
.outerRadius(radius - 50)
.innerRadius(radius - 60),
thickArc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 100);
var svg=d3.select("#chart")
.append("svg")
.attr({
width:w,
height:h,
class:'shadow'
}).append('g')
.attr({
transform:'translate('+w/2+','+h/2+')'
});
var path=svg.selectAll('path')
.data(pie(dataset))
.enter()
.append('path')
.attr({
fill:function(d,i){
return color(d.data.name);
}
});
path.transition()
.duration(1000)
.attrTween('d', function(d) {
var interpolate = d3.interpolate({startAngle: 0, endAngle: 0}, d);
return function(t) {
if (d.data.name === "Maybe Not Engaged"){
return thickArc(interpolate(t));
} else {
return thinArc(interpolate(t));
}
};
});
</script>
</body>
</html>

How can I alter the bezier function used by D3's diagonal()?

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>

d3.js Pie Chart With label

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.

D3 Voronoi Edge Condition

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()
}

Resources