Related
I'm new to d3 and want to connect nodes using elbows. Searching online I found one solution similar to what is required Similar solution however this solution does not work for d3 v4+.
Additionally, I have found a viable approach from d3 named d3.line().curve(d3.curveStepAfter) (I'm not sure if this is the correct use) an example can be seen here. However I can not find a way to implement this for my current set-up which uses x1, y1, and x2, y2.
Data
var data = {
"nodes": [
{
"name": "Node 1",
fx: 50,
fy: 50
},
{
"name": "Node 2",
fx: 50,
fy: 100
},
{
"name": "Node 3",
fx: 200,
fy: 50
},
{
"name": "Node 4",
fx: 350,
fy: 50
},
{
"name": "Node 5",
fx: 200,
fy: 150
}].map(function(d, i) { return (d.fixed = true, d) }),
"links": [
{
"source": 0,
"target": 2
},
{
"source": 1,
"target": 2
},
{
"source": 2,
"target": 3
},
{
"source": 2,
"target": 4
}]
}
Code
var width = 560
var height = 500;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var force = self.force = d3.forceSimulation(data.nodes)
.force("link", d3.forceLink(data.links))
.force("collide", d3.forceCollide())
.force("center", d3.forceCenter(width / 2, height / 2))
.on("tick", ticked);
var link = svg.selectAll("line.link")
.data(data.links)
.enter()
.append("line")
.style("stroke", "black")
.attr("x1", function (d) { return d.source.x; })
.attr("y1", function (d) { return d.source.y; })
.attr("x2", function (d) { return d.target.x; })
.attr("y2", function (d) { return d.target.y; })
var node = svg.selectAll("g.node")
.data(data.nodes)
.enter()
.append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
d3.selectAll(".node")
.append("circle")
.style("fill", "red")
.attr("r", 15);
function ticked() {
link
.attr("x1", function (d) { return d.source.x; })
.attr("y1", function (d) { return d.source.y; })
.attr("x2", function (d) { return d.target.x; })
.attr("y2", function (d) { return d.target.y; })
node.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
};
function dragstarted(event) {
if (!event.active) force.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = Math.ceil(event.x/5)*5;
event.subject.fy = Math.ceil(event.y/5)*5;
}
function dragended(event) {
if (!event.active) force.alphaTarget(0);
}
The code renders the nodes and connects them with straight lines, the goal is to add the curveStepAfter to create an elbow join as it looks neater for the type of diagram I require.
Any help is appreciated.
Here's a complete example.
<html>
<head>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<script>
const data = {
nodes: [
{ name: "Node 1", x: 50, y: 50 },
{ name: "Node 2", x: 50, y: 100 },
{ name: "Node 3", x: 200, y: 50 },
{ name: "Node 4", x: 350, y: 50 },
{ name: "Node 5", x: 200, y: 150 },
],
links: [
{ source: 0, target: 2 },
{ source: 1, target: 2 },
{ source: 2, target: 3 },
{ source: 2, target: 4 },
],
};
const segments = data.links.map(({ source, target }) => [
data.nodes[source],
data.nodes[target],
]);
const width = 560
const height = 500;
const svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
const line = d3.line()
.x(d => d.x)
.y(d => d.y)
.curve(d3.curveStep);
const link = svg.selectAll("path.link")
.data(segments)
.join("path")
.attr("d", line)
.attr("stroke", "black")
.attr("fill", "none")
const node = svg.selectAll("g.node")
.data(data.nodes)
.join("g")
.attr("class", "node")
.attr("transform", d => `translate(${d.x},${d.y})`)
.call(d3.drag()
.on("drag", dragged));
node.append("circle")
.style("fill", "red")
.attr("r", 15);
function dragged(event, d) {
d.x = Math.ceil(event.x / 5) * 5;
d.y = Math.ceil(event.y / 5) * 5;
link.attr("d", line);
d3.select(this)
.attr("transform", `translate(${d.x},${d.y})`)
}
</script>
</body>
</html>
The main idea is that
const segments = data.links.map(({ source, target }) => [
data.nodes[source],
data.nodes[target],
]);
is an array of line segments in the network. We can pass each segment to a d3.line() to create a <path> for the segment. I've also removed the dependency on d3-force, which isn't needed.
I am a newbie in d3.js. I have tried to create a static architecture with 5 nodes and link them with each other according to preferences, the nodes should be organized like so:
At the beginning I set the position of the nodes and then create the links. Though, when the nodes get linked, the architecture changes and the result is the one displayed below:
Here is my code:
var width = 640,
height = 400;
var nodes = [
{ x: 60, y: 0, id: 0},
{ x: 150, y: height/4, id: 1},
{ x: 220, y: height/4, id: 2},
{ x: 340, y: height/4, id: 3},
{ x: 420, y: height/2, id: 4},
{ x: 480, y: height/2, id: 5}
];
var links = [
{ source: 1, target: 5 },
{ source: 0, target: 5 },
{ source: 2, target: 1 },
{ source: 3, target: 2 },
{ source: 4, target: 5 }
];
var graph = d3.select('#graph');
var svg = graph.append('svg')
.attr('width', width)
.attr('height', height);
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
.links(links);
force.linkDistance(width/2);
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 1e-6);
var node = svg.selectAll('.node')
.data(nodes)
.enter().append("circle")
.attr("cx", d=> d.x)
.attr("cy", d=> d.y)
.attr('class', 'node')
.on("mouseover", function(d){
d3.select(this)
.transition()
.duration(500)
.style("cursor", "pointer")
div
.transition()
.duration(300)
.style("opacity", "1")
.style("display", "block")
console.log("label", d.label);
div
.html("IP: " + d.label + " x: " + d.x + " y: " + d.y)
.style("left", (d3.event.pageX ) + "px")
.style("top", (d3.event.pageY) + "px");
})
.on("mouseout", mouseout);
function mouseout() {
div.transition()
.duration(300)
.style("opacity", "0")
}
console.log("wait...");
force.on('end', function() {
node.attr('r', width/25)
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; });
link.attr('x1', function(d) { console.log("LINE x1-> ", d.source.x); return d.source.x; })
.attr('y1', function(d) { console.log("LINE y1-> ", d.source.y); return d.source.y; })
.attr('x2', function(d) { console.log("LINE x2-> ", d.source.x); return d.target.x; })
.attr('y2', function(d) { console.log("LINE y2-> ", d.source.y); return d.target.y; })
.attr("stroke-width", 2)
.attr("stroke","black");
});
force.start();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="graph"></div>
Could you please help me?
Thank you in advance.
A force layout offers some advantages that derive from its nature as a self organizing layout:
It places nodes and links automatically avoiding manual positioning of potentially thousands of elements
It organizes nodes and links based on assigned forces to an ideal spacing and layout
You have nodes to which you have already assigned positions, the two advantages listed above do not apply. You've already manually done the first item, and the second item will disturb and overwrite the positions you manually set.
We could fix the node positions, but if we do this with all nodes, it defeats the purpose of the force layout: to position nodes by simulating forces.
Instead, if you have the position of all nodes, we can skip the force and just append everything based on the data. The snippet below places the links first (so they are behind the nodes) using the index contained in d.source/d.target to access the specific node in the nodes array and get the appropriate x or y coordinate. The nodes are positioned normally.
It appears you have adjusted the code to use circles in your question though the screenshot uses images (as you also used in a previous question), I'll just use circles here. Based on the coordinates you've given some lines overlap. I modified the first node so that the y value wasn't 0 (which would have pushed half the circle off the svg)
var width = 640,
height = 400;
var nodes = [
{ x: 60, y: height/8, id: 0},
{ x: 150, y: height/4, id: 1},
{ x: 220, y: height/4, id: 2},
{ x: 340, y: height/4, id: 3},
{ x: 420, y: height/2, id: 4},
{ x: 480, y: height/2, id: 5}
];
var links = [
{ source: 1, target: 5 },
{ source: 0, target: 5 },
{ source: 2, target: 1 },
{ source: 3, target: 2 },
{ source: 4, target: 5 }
];
var graph = d3.select('#graph');
var svg = graph.append('svg')
.attr('width', width)
.attr('height', height);
// append links:
svg.selectAll()
.data(links)
.enter()
.append("line")
.attr("x1", function(d) { return nodes[d.source].x; })
.attr("y1", function(d) { return nodes[d.source].y; })
.attr("x2", function(d) { return nodes[d.target].x; })
.attr("y2", function(d) { return nodes[d.target].y; })
.attr("stroke-width", 2)
.attr("stroke","black");
// append nodes:
svg.selectAll()
.data(nodes)
.enter()
.append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 8);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="graph"></div>
This code works perfectly with a JSON file where the source and index are in the form of indices. However, when I switch to a format with the source and target as strings, it throws up TypeError: e[u.source.index] is undefined. How do I overcome this?
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500,
active = d3.select(null);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 8])
.on("zoom", zoomed);
var force = d3.layout.force()
.size([width, height])
.charge(-400)
.linkDistance(40)
.on("tick", tick);
var drag = force.drag()
.on("dragstart", dragstart);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
// .on("click", reset);
var g = svg.append("g");
var link = g.selectAll(".link"),
node = g.selectAll(".node");
svg
.call(zoom) // delete this line to disable free zooming
.call(zoom.event);
d3.json("data/miserables.json", function(error, graph) {
if (error) throw error;
force
.nodes(graph.nodes)
.links(graph.links)
.start();
link = link.data(graph.links)
.enter().append("line")
.attr("class", "links")
.style("stroke", "#999");
node = node.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 12)
.on("click", clicked)
//.call(drag);
});
function tick() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
function clicked(d){
if (active.node() === this) return reset();
active.classed("active", false);
active = d3.select(this).classed("active", true);
var bbox = active.node().getBBox(),
bounds = [[bbox.x, bbox.y],[bbox.x + bbox.width, bbox.y + bbox.height]];
var dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = Math.max(1, Math.min(8, 0.9 / Math.max(dx / width, dy / height))),
translate = [width / 2 - scale * x, height / 2 - scale * y];
svg.transition()
.duration(750)
.call(zoom.translate(translate).scale(scale).event);
}
function reset() {
active.classed("active", false);
active = d3.select(null);
svg.transition()
.duration(750)
.call(zoom.translate([0, 0]).scale(1).event);
}
function dragstart(d) {
d3.select(this).classed("fixed", d.fixed = true);
}
function zoomed() {
console.log(d3.event)
g.style("stroke-width", 1.5 / d3.event.scale + "px");
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
</script>
You're using D3 v3.x. While linking by name is an easy task in D3 v4.x, it seems that it's not possible in D3 v3.x. See this issue in D3 v3.x, and this explanation in the API:
Note: the values of the source and target attributes may be initially specified as indexes into the nodes array; these will be replaced by references after the call to start.
Thus, the snippet below won't work (the code is not mine, I just found it online and changed the links array from indices to names):
var nodes = [{
name: "node1"
}, {
name: "node2"
}, {
name: "node3"
}, {
name: "node4"
}, {
name: "node5"
}, {
name: "node6"
}, {
name: "node7"
}];
var edges = [{
source: "node1",
target: "node3"
}, {
source: "node1",
target: "node2"
}, {
source: "node1",
target: "node4"
}, {
source: "node2",
target: "node3"
}, {
source: "node2",
target: "node5"
}, {
source: "node2",
target: "node6"
}, {
source: "node3",
target: "node"
}];
var width = 400;
var height = 400;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.nodes(nodes)
.links(edges)
.size([width, height])
.linkDistance(150)
.charge(-400);
force.start();
var svg_edges = svg.selectAll("line")
.data(edges)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var color = d3.scale.category20();
var svg_nodes = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r", 20)
.style("fill", function(d, i) {
return color(i);
})
.call(force.drag);
var svg_texts = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d) {
return d.name;
});
force.on("tick", function() {
svg_edges.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
svg_nodes.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
svg_texts.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
If you click "run snippet", you'll only see an error:
Uncaught TypeError: Cannot read property 'force' of undefined
Solution: Keep your links array with indices. However, if you already have/receive an array with names, you can change it to indices:
var nodeByName = d3.map(nodes, function(d) {
return d.name;
});
edges.forEach(function(d) {
d.source = nodeByName.get(d.source);
d.target = nodeByName.get(d.target);
});
Here is the same code of the first snippet with the above-mentioned changes. Now it works:
var nodes = [{
name: "node1"
}, {
name: "node2"
}, {
name: "node3"
}, {
name: "node4"
}, {
name: "node5"
}, {
name: "node6"
}, {
name: "node7"
}];
var edges = [{
source: "node1",
target: "node3"
}, {
source: "node1",
target: "node2"
}, {
source: "node1",
target: "node4"
}, {
source: "node2",
target: "node3"
}, {
source: "node2",
target: "node5"
}, {
source: "node2",
target: "node6"
}, {
source: "node2",
target: "node7"
}];
var nodeByName = d3.map(nodes, function(d) {
return d.name;
});
edges.forEach(function(d) {
d.source = nodeByName.get(d.source);
d.target = nodeByName.get(d.target);
});
var width = 400;
var height = 400;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.nodes(nodes)
.links(edges)
.size([width, height])
.linkDistance(150)
.charge(-400);
force.start();
var svg_edges = svg.selectAll("line")
.data(edges)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var color = d3.scale.category20();
var svg_nodes = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r", 20)
.style("fill", function(d, i) {
return color(i);
})
.call(force.drag);
var svg_texts = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d) {
return d.name;
});
force.on("tick", function() {
svg_edges.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
svg_nodes.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
svg_texts.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
});
<script src="https://d3js.org/d3.v3.min.js"></script>
I have been trying to add text and arrow to a d3js force layout diagram but was not being able to get the desired results.
My script looks like the following:
var width = 600;
height = 400;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(100)
.size([width, height]);
var svg = d3.select("#t").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("topology.json", function(error, graph) {
if (error) throw error;
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return 2*(d.value); });//Math.sqrt
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 10)
.style("fill", function(d) { return color(d.group); })
.call(force.drag);
node.append("title")
.text(function(d) { return d.name; });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
I tried to add the following snippet to add text
node.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
The sample json data is
{
"nodes": [{
"name": "0",
"group": 2
}, {
"name": "1",
"group": 2
}, {
"name": "2",
"group": 2
}, {
"name": "3",
"group": 1
}],
"links": [{
"source": 0,
"target": 1,
"value": 1
}, {
"source": 1,
"target": 3,
"value": 2
}, {
"source": 2,
"target": 3,
"value": 1
}]
}
Any help with adding text to the circles and arrows would be great. Sorry if I sounded a noob.
Thanks :) :)
Mobashyr,I have used the below sample for fulfill my requirement :
// Restart the force layout.
var force.nodes(nodes)
.links(links)
.charge(-1000)
.linkDistance(120)
.alpha(-15)
.start();
var link = vis.selectAll(".link")
.data(links);
link.enter().insert("svg:line", ".node")
.attr("class", "link")
.style("stroke", "#ccc")
.attr("cursor", "pointer")
.style("stroke-width", "0")
.style("stroke-width", function(d) { return d.weight;})
.on("mouseover", function() { d3.select(this).style("stroke", "#555555").attr("stroke-opacity", "1.0").attr("stroke-width","10");})
.on("mouseout", function() { d3.select(this).style("stroke", "#ccc").attr("stroke-opacity", "1.0").attr("stroke-width","4") });
link.exit().remove();
var node = vis.selectAll("g.node")
.data(nodes)
var groups = node.enter().append("g")
.attr("class", "node")
.attr("id", function (d) {
return d.entityType;
})
.on('click', click)
groups.append("circle")
.attr("cursor", "pointer")
.style("fill", function(d) { return color(d.entityType); })
.style("fill", "#fff")
.style("stroke-width", "0")
.style("stroke", "#ddd")
.attr("r", 20);
groups.append("text")
.attr("dy", 18)
.style("font-size", "2.5px")
.style("text-anchor", "middle")
.style('fill','#000')
.attr("refX", 15)
.attr("refY", -1.5)
.text(function (d) {
return d.text;
});
node.exit().remove();
force.on("tick", function () {
link.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
So I have been doing a lot of experimenting with D3 but have never attempted to turn it into an AngularJS directive, and I am not even sure of the proper way to change it over. I have an example that I believe I turned into an Angular D3 chart however I believe there are more efficient ways in loading the data , maybe in a $scope.data... Please use my jsfiddle to reference or correct.
https://jsfiddle.net/bcnmLrns/1/
var app = angular.module("chartApp", []); {
var data = {
"nodes": [{
"name": "hblodget",
"group": 1,
"size": 1,
"image": null
}, {
"name": "DowntownDonna69",
"group": 1,
"size": 20,
"image": "http://pbs.twimg.com/profile_images/636139174672732160/L5cd008s_normal.jpg"
}, {
"name": "PupsherLive",
"group": 1,
"size": 19,
"image": "http://pbs.twimg.com/profile_images/378800000210840839/93a8ba3852a8e20364957eb8b907b6b3_normal.jpeg"
}],
"links": [{
"source": 1,
"target": 0,
"value": 1
}, {
"source": 2,
"target": 0,
"value": 1
}]
};
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json(data, function(error, graph) {
if (error) throw error;
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) {
return color(d.group);
})
.call(force.drag);
node.append("title")
.text(function(d) {
return d.name;
});
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
});
});
}
You can use other directives and services people have made to make D3 easier to use in Angular (see comments on question), but I still like the easy setup of modularizing graphs and such inside a simple directive:
myApp.directive('graph', function() {
var graphLink = function(scope, element) {
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select(element[0]).append("svg") // attach d3 to directive element
.attr("width", width)
.attr("height", height);
d3.json(data, function(error, graph) {
if (error) throw error;
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link") // there might be a more angular way to do this...
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) {
return color(d.group);
})
.call(force.drag);
node.append("title")
.text(function(d) {
return d.name;
});
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
});
};
return {
link: graphLink, // pass in your link function here
scope: {
data: '=' // pass in your data as an attribute
// this makes this reusable, and you can redraw if the data changes
}
};
});