Appearance
Force layout
This example shows how to use the force-directed layout which is the most common layout used to position nodes in a graph. 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.
ts
import Ogma, { LocateOptions, RawGraph } from '@linkurious/ogma';
const ogma = new Ogma({
container: 'graph-container',
options: { interactions: { drag: { enabled: false } } }
});
const padding = 100;
const animationDuration = 300;
// disable node dragging
//ogma.setOptions({ interactions: { drag: { enabled: false }}});
const randomize = () => {
ogma.getNodes().forEach(node => {
node.setAttributes({
x: Math.random() * 150,
y: Math.random() * 150
});
});
};
// generate random graph
ogma.generate
.barabasiAlbert({
nodes: 500,
m0: 10,
m: 1
})
.then(graph => {
// add some disconnected components and 'orphan' nodes
graph.nodes.forEach(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(() => {
return run({ padding });
});
const form = document.querySelector<HTMLFormElement>('#params')!;
let callTimer = 0;
let charge: number, gravity: number, elasticity: number, edgeStrength: number;
const 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.toString();
document.getElementById('gravity-value')!.innerHTML = gravity.toString();
document.getElementById('elasticity-value')!.innerHTML =
elasticity.toString();
document.getElementById('edgeStrength-value')!.innerHTML =
edgeStrength.toString();
};
form.addEventListener(
'input',
() => {
clearTimeout(callTimer);
update();
callTimer = setTimeout(run, 100) as unknown as number;
},
true
);
document.querySelector('#randomize')!.addEventListener('click', randomize);
document.querySelector('#run')!.addEventListener('click', () => {
run();
});
const run = (locate?: LocateOptions) => {
const start = Date.now();
return ogma.layouts
.force({
//useWebWorker: false,
charge: charge,
gravity: gravity,
elasticity: elasticity,
locate: locate,
duration: animationDuration,
edgeStrength: edgeStrength
})
.then(() => {
const duration = Date.now() - start;
document.getElementById('time')!.innerHTML =
'done in ' + ((duration - animationDuration) / 1000).toFixed(2) + 's';
});
};
function addLooseNodes(graph: RawGraph, n: number) {
const baseId = graph.nodes.length;
for (let i = 0; i < n; i++) {
graph.nodes.push({ id: baseId + i });
}
}
function addSmallComponents(graph: RawGraph, n: number, m: number) {
for (let i = 0; i < n; i++) {
const 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 });
}
}
}
update();
html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<link type="text/css" rel="stylesheet" href="styles.css" />
</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 type="module" src="index.ts"></script>
</body>
</html>
css
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;
}