Force layout
New general-purpose force-directed layout. charge
defines the strength with which the nodes repel each other. gravity
stands for the global force, pulling the graph to its mass center. elasticity
defines how the nodes collide with each other.
Open in a new window.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="../build/ogma.min.js"></script>
<style>
html,
body {
font-family: Helvetica, Arial, Helvetica, sans-serif;
padding: 0;
margin: 0;
}
#graph-container {
top: 0;
bottom: 0;
left: 0;
right: 0;
position: absolute;
margin: 0;
overflow: hidden;
}
.control {
position: absolute;
top: 20px;
right: 20px;
padding: 10px;
border-radius: 5px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
background: #fff;
}
.control label {
display: block;
padding: 5px 0;
}
.control p {
text-align: center;
}
.control .value {
width: 50px;
display: inline-block;
font-family: Georgia, 'Times New Roman', Times, serif;
font-weight: bold;
}
#time {
text-align: center;
font-family: Georgia, 'Times New Roman', Times, serif;
}
</style>
</head>
<body>
<div id="graph-container"></div>
<form class="control" id="params">
<label>
<span id="charge-value" class="value">5</span>
<input type="range" min="0.5" max="50" value="5" name="charge"
step="0.5" /> Charge
</label>
<label>
<span id="elasticity-value" class="value">0.9</span>
<input type="range" min="0" max="1.0" value="0.9" name="elasticity"
step="0.01" /> Elasticity
</label>
<label>
<span id="gravity-value" class="value">0.01</span>
<input type="range" min="0.005" max="0.15" value="0.01" name="gravity"
step="0.005" /> Gravity
</label>
<label>
<span id="edgeStrength-value" class="value">0.75</span>
<input type="range" min="0.0" max="30" value="0.75" name="edgeStrength"
step="0.25" /> Edge strength
</label>
<p><button type="button" id="randomize">Randomize</button><button
type="button" id="run">Run</button></p>
<div id="time"></div>
</form>
<script>
'use strict';
let ogma = new Ogma({
container: 'graph-container',
options: { interactions: { drag: { enabled: false } } }
});
let padding = 100;
let animationDuration = 300;
// disable node dragging
//ogma.setOptions({ interactions: { drag: { enabled: false }}});
// generate random graph
ogma.generate
.barabasiAlbert({
nodes: 500,
m0: 10,
m: 1
})
.then(function (graph) {
// add some disconnected components and 'orphan' nodes
graph.nodes.forEach(function (node) {
delete node.attributes;
});
addSmallComponents(graph, Math.floor(Math.log(graph.nodes.length)), 5);
addLooseNodes(graph, Math.floor(graph.nodes.length / 3));
ogma.setGraph(graph);
return ogma.view.locateGraph({ padding: 100 });
})
.then(function () {
return run({ padding: padding });
});
let form = document.querySelector('#params');
let callTimer = 0;
let charge, gravity, elasticity, edgeStrength;
function update() {
charge = parseFloat(form['charge'].value);
gravity = parseFloat(form['gravity'].value);
elasticity = parseFloat(form['elasticity'].value);
edgeStrength = parseFloat(form['edgeStrength'].value);
document.getElementById('charge-value').innerHTML = charge;
document.getElementById('gravity-value').innerHTML = gravity;
document.getElementById('elasticity-value').innerHTML = elasticity;
document.getElementById('edgeStrength-value').innerHTML = edgeStrength;
}
form.addEventListener(
'input',
function () {
clearTimeout(callTimer);
update();
callTimer = setTimeout(run, 100);
},
true
);
document.querySelector('#randomize').addEventListener('click', randomize);
document.querySelector('#run').addEventListener('click', function () {
run();
});
function run(locate) {
let start = Date.now();
return ogma.layouts
.force({
//useWebWorker: false,
charge: charge,
gravity: gravity,
elasticity: elasticity,
locate: locate,
duartion: animationDuration,
edgeStrength: edgeStrength
})
.then(function () {
let duration = Date.now() - start;
document.getElementById('time').innerHTML =
'done in ' +
((duration - animationDuration) / 1000).toFixed(2) +
's';
});
}
update();
function addLooseNodes(graph, n) {
let baseId = graph.nodes.length;
for (let i = 0; i < n; i++) {
graph.nodes.push({ id: baseId + i });
}
}
function addSmallComponents(graph, n, m) {
for (let i = 0; i < n; i++) {
let baseId = graph.nodes.length;
for (let j = 0; j < m + 1; j++) {
graph.nodes.push({ id: baseId + j });
}
for (let k = 1; k < m + 1; k++) {
graph.edges.push({ source: baseId, target: baseId + k });
}
}
}
function randomize() {
ogma.getNodes().forEach(function (node) {
node.setAttributes({
x: Math.random() * 150,
y: Math.random() * 150
});
});
}
</script>
</body>
</html>