I'm trying to create a forceSimulation in d3 v4 which does not let the nodes float outside the boundries of the svg in the same way that this example has it for d3 v3 https://bl.ocks.org/mbostock/1129492.
Have tried a few different things in simulation.on("tick", ticked) to no avail. My codePen is below. Any ideas on how to achieve this?
https://codepen.io/mtsvelik/pen/rzxVrE
//Read the data from the mis element
var graph = document.getElementById('json').innerHTML;
graph = JSON.parse(graph);
render(graph);
function render(graph){
// Dimensions of sunburst.
var radius = 6;
var maxValue = d3.max(graph.links, function(d, i, data) {
return d.value;
});
//sub-in max-value from
d3.select("div").html('<form class="force-control" ng-if="formControl">Link threshold 0 <input type="range" id="thersholdSlider" name="points" value="0" min="0" max="'+ maxValue +'">'+ maxValue +'</form>');
document.getElementById("thersholdSlider").onchange = function() {threshold(this.value);};
var svg = d3.select("svg");
var width = svg.attr("width");
var height = svg.attr("height");
console.log(graph);
var graphRec = JSON.parse(JSON.stringify(graph)); //Add this line
//graphRec = graph; //Add this line
console.log(graphRec);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(Number(-1000 + (1.25*graph.links.length)))) //default force is -30, making weaker to increase size of chart
.force("center", d3.forceCenter(width / 2, height / 2));
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", radius)
.attr("fill", function(d) { return d.color; })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
console.log(link.data(graph.links));
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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
function threshold(thresh) {
thresh = Number(thresh);
graph.links.splice(0, graph.links.length);
for (var i = 0; i < graphRec.links.length; i++) {
if (graphRec.links[i].value > thresh) {graph.links.push(graphRec.links[i]);}
}
console.log(graph.links);
/*var threshold_links = graph.links.filter(function(d){ return (d.value > thresh);});
console.log(graph.links);
restart(threshold_links);*/
restart();
}
//Restart the visualisation after any node and link changes
// function restart(threshold_links) {
function restart() {
//DATA JOIN
//link = link.data(threshold_links);
link = link.data(graph.links);
console.log(link);
//EXIT
link.exit().remove();
console.log(link);
// ENTER - https://bl.ocks.org/colbenkharrl/21b3808492b93a21de841bc5ceac4e47
// Create new links as needed.
link = link.enter().append("line")
.attr("class", "link")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); }).merge(link);
console.log(link);
// DATA JOIN
node = node.data(graph.nodes);
/*
// EXIT
node.exit().remove();
// ENTER
node = node.enter().append("circle")
.attr("class", "node")
.attr("r", radius)
.attr("fill", function(d) {return d.color;})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
)
.merge(node);
node.append("title")
.text(function(d) { return d.id; });
*/
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
simulation.alphaTarget(0.3).restart();
}
}
In the tick function restrict the nodes to move out from the boundary:
node
.attr("cx", function(d) {
return (d.x = Math.max(radius, Math.min(width - radius, d.x)));
})
.attr("cy", function(d) {
return (d.y = Math.max(radius, Math.min(height - radius, d.y)));
})
//now update the links.
working code here
You can also use d3.forceBoundary that allows you to set a boundary with a strength. In your code
import it
<script src="https://unpkg.com/d3-force-boundary#0.0.1/dist/d3-force-boundary.min.js"></script>
then
var simulation = d3.forceSimulation()
.force("boundary", forceBoundary(0,0,width, height))
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(Number(-1000 + (1.25*graph.links.length)))) //default force is -30, making weaker to increase size of chart
.force("center", d3.forceCenter(width / 2, height / 2));
your pen fixed https://codepen.io/duto_guerra/pen/XWXagqm
Related
I want to display the nodes and relationship between them from json file.
I am using force layout d3.js but there is alot of movement of nodes when the page is opened.
I want to stop these movements and also want to stop the other node movements when one node is dragged.
Code : http://jsfiddle.net/bhgq017u/13/
var colors = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
node,
link;
svg.append('defs').append('marker')
.attrs({'id':'arrowhead',
'viewBox':'-0 -5 10 10',
'refX':13,
'refY':0,
'orient':'auto',
'markerWidth':8,
'markerHeight':8,
'xoverflow':'visible'})
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', "gray");
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) {return d.id;}).distance(500).strength(1))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json('https://api.myjson.com/bins/beqb0', function (error, graph) {
if (error) throw error;
update(graph.links, graph.nodes);
})
function update(links, nodes) {
link = svg.selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class", "link")
.attr('marker-end','url(#arrowhead)')
link.append("title")
.text(function (d) {return d.type;});
edgepaths = svg.selectAll(".edgepath")
.data(links)
.enter()
.append('path')
.attrs({
'class': 'edgepath',
'fill-opacity': 0,
'stroke-opacity': 0,
'id': function (d, i) {return 'edgepath' + i}
})
.style("pointer-events", "none");
edgelabels = svg.selectAll(".edgelabel")
.data(links)
.enter()
.append('text')
.style("pointer-events", "none")
.attrs({
'class': 'edgelabel',
'id': function (d, i) {return 'edgelabel' + i},
'font-size': 10,
'fill': 'black'
});
edgelabels.append('textPath')
.attr('xlink:href', function (d, i) {return '#edgepath' + i})
.style("text-anchor", "middle")
.style("pointer-events", "none")
.attr("startOffset", "50%")
.text(function (d) {return d.type});
node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
// .on("end", dragended)
);
node.append("circle")
.attr("r", 8)
.style("fill", function (d, i) {return colors(i);})
node.append("text")
.attr("dy", -10)
.text(function (d) {return d.name});
simulation
.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(links);
}
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 + ")";});
edgepaths.attr('d', function (d) {
return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
});
edgelabels.attr('transform', function (d) {
if (d.target.x < d.source.x) {
var bbox = this.getBBox();
rx = bbox.x + bbox.width / 2;
ry = bbox.y + bbox.height / 2;
return 'rotate(180 ' + rx + ' ' + ry + ')';
}
else {
return 'rotate(0)';
}
});
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart()
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
In the dragend you must set the alphaTraget to 0.
Remove the forces on dragstart
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
simulation.force("link", null).force("charge", null).force("center", null);
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
}
Edit
If you also want to restrict at startup use
function update(links, nodes) {
// ...
simulation
.nodes(nodes)
.on("tick", ticked)
.stop();
simulation.force("link")
.links(links);
d3.range(100).forEach( () => simulation.tick() );
ticked();
simulation.force("link", null).force("charge", null).force("center", null);
simulation.start();
}
The code is based on Denise's one on bl.ocks. How can I add to the existing code text and an icon to a node? I have almost got it by appending the needed elements but the problem is that circles appear at (0, 0) coordinate.
A picture is worth a thousand words,
and my target is having inside the nodes and where the nodes need to be, a text in the middle and an icon,
This is my current code, that works perfectly as long as the commented part is not uncommented (that's what I have tried to do)
let translateVar = [0,0];
let scaleVar = 1;
let radius = 50;
function create_pan_zoomable_svg(html_element, width, height) {
let svg = d3.select("body")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.style("background-color", "#eeeeee")
.call(_zoom).on("dblclick.zoom", null)
.append("g");
d3.select("#zoom_in").on('click', function() { _zoom.scaleBy(svg, 2)});
d3.select("#zoom_out").on('click', function() { _zoom.scaleBy(svg, 0.5)});
create_marker(svg);
initialize_link_node(svg);
return svg;
}
var _zoom = d3.zoom()
.on("zoom", function() {
translateVar[0] = d3.event.transform.x;
translateVar[1] = d3.event.transform.y;
scaleVar = d3.event.transform.k;
svg.attr('transform', 'translate(' + translateVar[0] + ',' + translateVar[1] + ') scale(' + scaleVar + ')');
});
function create_marker(svg) {
let defs = svg.append("defs");
defs.append("marker")
.attr("id", "arrow")
.attr("viewBox", "0 -5 10 10")
.attr("refX", 40)
.attr("refY", 0)
.attr("markerWidth", 8)
.attr("markerHeight", 8)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
}
function getScreenInfo() {
return {
width : ($(window).width()-translateVar[0])/scaleVar,
height : ($(window).height()-translateVar[1])/scaleVar,
centerx : (($(window).width()-translateVar[0])/scaleVar)/2,
centery : (($(window).height()-translateVar[1])/scaleVar)/2
};
}
let link, node, simulation;
function initialize_link_node(svg) {
let currentScreen = getScreenInfo();
simulation = d3.forceSimulation()
.force("link", d3.forceLink()
.id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody()
.strength(function(d) { return -20000;}))
.force("center", d3.forceCenter(currentScreen.centerx, currentScreen.centery));
link = svg.append("g").selectAll(".link");
node = svg.append("g").selectAll(".node");
}
function spawn_nodes(svg, graph, around_node, filtering_options) {
node = node.data(theData.nodes, function(d) {
return d.id;
});
let newNode = node.enter().append("g");
newNode = newNode.append("circle")
.attr("class", "node")
.attr("r", radius)
.attr("fill", function(d) {
return colors[d.type]
})
.attr("id", function(d) {
return d.id
});
/****************************************************
newNode.append("text")
.text( function(d) {
return d.id;
})
.style("fill", "red")
.style("text-anchor", "middle");
newNode.append("svg:image")
.attr("href", function(d) {
return dict_type_icon[d.type];
}).attr("width", "30")
.attr("height", "30");**************************/
newNode.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);
node = node.merge(newNode);
link = link.data(theData.edges, function(d) {
return d.id;
});
let newLink = link.enter().append("line").attr("class", "link").attr("marker-end", "url(#arrow)");
newLink.append("title").text(function(d) {
return d.labeled;
});
link = link.merge(newLink);
node = node.merge(newNode);
simulation.nodes(theData.nodes).on("tick", ticked);
simulation.force("link").links(theData.edges);
simulation.alpha(1).alphaTarget(0).restart();
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
function ticked() {
let currentScreen = getScreenInfo();
node
.attr("cx", function(d) {
return d.x = Math.max(radius, Math.min(currentScreen.width - radius, d.x));
})
.attr("cy", function(d) { return d.y = Math.max(radius, Math.min(currentScreen.height - radius, d.y)); });
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; });
}
When I uncomment the circles appear at (0,0) point as you may have seen in the image.
I would appreciate if you could help me! Thank you.
Right now, you're turning newNode into a circles' selection:
newNode = newNode.append("circle")
//etc...
So, in order to keep it as a groups' selection, change that line to:
newNode.append("circle")
//etc...
Then, uncomment the commented part.
Finally, since that now node.merge(newNode) will be a groups' selection, change the cx and cy in ticked function to translate:
node.attr("transform", function(d) {
return "translate(" + (d.x = Math.max(radius, Math.min(currentScreen.width - radius, d.x))) +
"," + (d.y = Math.max(radius, Math.min(currentScreen.height - radius, d.y))) + ")";
});
I have a a D3 v4 force simulation with nodes moving around the screen. Each node is a group consisting of a circle and some text below it. How do I order this so that the circles are on a bottom layer and the text on a top layer always. It's okay for a circle to overlap a circle, but it's never okay for a circle to overlap on top of text. Here is what I've got. Currently, the node's circle that is ahead of the other node will overlap that node's text.
this.centerNode = this.d3Graph.selectAll(null)
.data(this.nodes.slice(10,20))
.enter()
.append("g")
this.centerNode.append("circle")
.attr("class", "backCircle")
.attr("r", 60)
.attr("fill", "red")
this.centerNode
.append("text")
.attr("fill", "black")
.attr("font-size", "20px")
.attr("y", -60)
.text("test text" )
You cannot achieve the desired outcome with your current approach. The reason is simple: each group has a text and a circle. However, the painting order depends on the order of the groups:
<g>
<circle></circle>
<text></text><!-- this text here... -->
</g>
<g>
<circle></circle><!-- ...will be behind this circle here -->
<text></text>
</g>
<!-- etc... -->
So, grouping the texts and the circles inside <g> elements, you will have the groups painted in a given order and, consequently, a circle over a text (the circle of a given group is painted over the texts of all groups before it).
Here is a demo (the Baz circle will be on top of all texts, and the Bar circle will be on top of Foo text):
var width = 300;
var height = 200;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "Foo"
}, {
name: "Bar"
}, {
name: "Baz"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var color = d3.scaleOrdinal(d3.schemeCategory20);
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var nodeCircle = node.append("circle")
.attr("r", 20)
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", function(d, i) {
return color(i)
});
var nodeTexts = node.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d) {
return d.name;
});
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.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", (d) => "translate(" + d.x + "," + d.y + ")")
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
Solution
A possible solution is creating two selections, one for the circles and one for the texts. Append the circles before, and the texts later. Remember to use the same nodes array for both:
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("circle")
//etc...
var nodeTexts = svg.selectAll(null)
.data(nodes)
.enter()
.append("text")
//etc...
That way, the texts will be always on top of the circles.
Check the demo:
var width = 300;
var height = 200;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "Foo"
}, {
name: "Bar"
}, {
name: "Baz"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var color = d3.scaleOrdinal(d3.schemeCategory20);
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("circle")
.attr("r", 20)
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", function(d, i) {
return color(i)
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var nodeTexts = svg.selectAll(null)
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d) {
return d.name;
});
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.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", (d) => "translate(" + d.x + "," + d.y + ")")
nodeTexts.attr("transform", (d) => "translate(" + d.x + "," + d.y + ")")
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
So I'm working on freecodecamp's D3 force layout challenge : https://www.freecodecamp.com/challenges/show-national-contiguity-with-a-force-directed-graph
And as part of the challenge, I'm trying to append images of flags as the nodes in a force layout.
I've managed to append the flags and they are showing. When you click and drag on them, the links also move too. The problem is that they are stuck in the same position.
This is what I mean:
javascript (it's made within React):
createForceGraph() {
const { nodes, links } = this.state;
console.log(nodes);
console.log(links);
const w = 800;
const h = 500;
const margin = {
top: 30,
right: 30,
bottom: 80,
left: 80
};
const svg = d3.select('.chart')
.append('svg')
.attr('width', w)
.attr('height', h);
const simulation = d3.forceSimulation()
.force('link', d3.forceLink().id(function(d, i) { return i }).distance(1))
.force('charge', d3.forceManyBody().strength(1))
.force('center', d3.forceCenter(w / 2, h / 2))
.force('collision', d3.forceCollide(12));
const link = svg.append('g')
.attr('class', 'links')
.selectAll('line')
.data(links)
.enter()
.append('line')
.attr('stroke', 'black');
const node = d3.select('.nodes')
.selectAll('img')
.data(nodes)
.enter()
.append('img')
.attr('class', d => {
return `flag flag-${d.code}`;
})
.call(d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended));
simulation.nodes(nodes)
.on('tick', ticked);
simulation.force('link')
.links(links);
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
.style("left", function(d) { return d.x + 'px'; })
.style("top", function(d) { return d.y + 'px'; });
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
}
HTML:
<div>D3 Force-Directed Layout
<div className='chart'>
<div className='nodes'></div>
</div>
</div>
Maybe this can help you solve your problem?
https://bl.ocks.org/mbostock/950642
When user drag node by mouse click and release mouse click, node is rotating continuously on mouse event as shown in
JS Fiddle Example
This issue is reproduce on IE11 in windows 10.
What is the solution to this issue?
var width = 500;
var height = 500;
var nodeWidth = 40;
var nodeHeight = 40;
var circleRadius = 5;
var diagramLayout;
var graphData = {"nodes":[{"uid":"Term20","name":"Term20","image":"images/Term.png"},{"uid":"glossforArrow","name":"glossforArrow","image":"images/Glossary.png"},{"uid":"Term43","name":"Term43","image":"images/Term.png"},{"uid":"Term1","name":"Term43","image":"images/Term.png"},{"uid":"Term2","name":"Term43","image":"images/Term.png"}],"links":[{"source":"glossforArrow","target":"Term20","direction":"output","label":"Owned Terms"},{"source":"glossforArrow","target":"Term43","direction":"output","label":"Owned Terms"},{"source":"glossforArrow","target":"Term1","direction":"output","label":"Owned Terms"},{"source":"glossforArrow","target":"Term2","direction":"output","label":"Owned Terms"}]};
forceInitialize(graphData)
function forceInitialize(graphData){
diagramLayout = d3.select("#diagramLayout")
.attr("id", "diagramLayout")//set id
.attr("width", width)//set width
.attr("height", height)//set height
.append("g")
.attr("transform", "translate(" + 20 + "," + 20 + ")")
markerRefx = 35;
simulation = d3.forceSimulation();
alphaMulti = 1;
simulation.force("link", d3.forceLink().id(function(d) { return d.uid; }).distance(70).strength(0))
.force("charge", d3.forceManyBody().distanceMin(20).distanceMax(50))
.force("centre", d3.forceCenter(width/2 , height/2))
.force("x", d3.forceX(2))
.force("y", d3.forceY(10))
.force("collide", d3.forceCollide().radius(function(d) { return 80; }).iterations(2))
simulation.on('end', function() {
simulation.force("link", d3.forceLink().id(function(d) { return d.uid; }).distance(30).strength(0.0).iterations(10))
.force("x", d3.forceX().strength(0))
.force("y", d3.forceX().strength(0))
});
force(graphData);
}
//Force Layout
function force(graphData) {
var linkEnter = diagramLayout.selectAll(".links");
linkEnter = linkEnter.data(graphData.links)
.enter().append("g")
.attr("class", "links")
var link = linkEnter.append("line")
.attr("stroke-width", function(d) { return Math.sqrt(2); })
.attr("stroke-opacity", "0.3")
.attr("stroke", "#000" )
var node = diagramLayout.selectAll(".node");
node = node.data(graphData.nodes, function(d) { return d.uid; });
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("height", nodeHeight)
.attr("width", nodeWidth)
.on('contextmenu', menuCall);
var nodeIcon = nodeEnter.append("rect")
.attr("class", "rect")
.attr("x", -20)
.attr("y", -20)
.attr("rx", 10)
.attr("width", 40)
.attr("height", 40)
.attr("stroke-width", function(d) { return Math.sqrt(2); })
.attr("stroke-opacity", "0.3")
.attr("stroke", "#000" )
.attr("fill", "blue" )
nodeIcon.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
simulation
.nodes(graphData.nodes)
.on("tick", ticked);
setTimeout(function tick(){
simulation.tick();
if(simulation.alpha() >= .005);
setTimeout(tick, 0);
}, 0);
simulation.force("link")
.links(graphData.links);
simulation.restart();
function ticked(e) {
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; });
nodeEnter.attr("transform", function(d) {d.fixed = true; return "translate(" + d.x + "," + d.y + ")"; });
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
d3.select(this).classed("fixed", d.fixed = false);
d3.selectAll(".node").fixed = true;
}
function menuCall(di,i) {
}
}