Appearance
Force layout large graph
This example shows how the force-directed layout performs with large graphs. You can play with the theta
parametter to make it faster/less precise. But we recommend to use the GPU version of the force layout for large graphs. The GPU
mode is much faster and provides better final layout quality.
ts
import Ogma from '@linkurious/ogma';
const ogma = new Ogma({
container: 'graph-container',
options: { interactions: { drag: { enabled: false } } }
});
const padding = 100;
const run = (locate?: boolean) => {
const timeOutput = document.getElementById('time')!;
timeOutput.innerHTML = timeOutput.getAttribute('data-placeholder')!;
const start = Date.now();
return ogma.layouts
.force({
steps: steps,
charge: charge,
theta: theta,
gravity: gravity,
elasticity: elasticity,
locate
})
.then(() => {
timeOutput.innerHTML = (Date.now() - start) / 1000 + 's';
});
};
const randomize = () => {
ogma.getNodes().forEach(node => {
node.setAttributes({
x: Math.random() * 150,
y: Math.random() * 150
});
});
};
// generate random graph
Ogma.parse
.jsonFromUrl('files/relativity.json')
.then(graph => {
graph.nodes.forEach(node => {
node.attributes = { radius: 15 /*color: '#0E948A' */ };
});
graph.edges.forEach(edge => {
//edge.attributes = { color: '#aaaaaa' };
});
document.getElementById('n')!.textContent = 'nodes: ' + graph.nodes.length;
document.getElementById('e')!.textContent = 'edges: ' + graph.edges.length;
return ogma.setGraph(graph);
})
.then(() => {
randomize();
ogma.view.setZoom(0.1, { ignoreZoomLimits: true });
})
.then(() => {
return run();
})
.then(() => {
return ogma.view.locateGraph({ padding: padding });
});
const form = document.querySelector('#params') as HTMLFormElement;
let callTimer: ReturnType<typeof setTimeout>;
let charge: number,
gravity: number,
theta: number,
elasticity: number,
steps: number;
const update = () => {
steps = parseFloat(form['steps'].value);
charge = parseFloat(form['charge'].value);
gravity = parseFloat(form['gravity'].value);
elasticity = parseFloat(form['elasticity'].value);
theta = parseFloat(form['theta'].value);
document.getElementById('steps-value')!.innerHTML = steps.toString();
document.getElementById('charge-value')!.innerHTML = charge.toString();
document.getElementById('gravity-value')!.innerHTML = gravity.toString();
document.getElementById('elasticity-value')!.innerHTML =
elasticity.toString();
document.getElementById('theta-value')!.innerHTML = theta.toString();
};
form.addEventListener(
'input',
() => {
clearTimeout(callTimer);
update();
callTimer = setTimeout(run, 100);
},
true
);
document.querySelector('#randomize')!.addEventListener('click', randomize);
document.querySelector('#run')!.addEventListener('click', () => run());
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="steps-value" class="value">80</span>
<input
type="range"
min="10"
max="300"
value="80"
name="steps"
step="10"
/>
Steps
</label>
<label>
<span id="charge-value" class="value">10</span>
<input
type="range"
min="0.5"
max="50"
value="10"
name="charge"
step="0.5"
/>
Charge
</label>
<label>
<span id="theta-value" class="value">0.81</span>
<input
type="range"
min="0.15"
max="1"
value="0.81"
name="theta"
step="0.01"
/>
θ (precision)
</label>
<label>
<span id="elasticity-value" class="value">0.1</span>
<input
type="range"
min="0"
max="1.0"
value="0.1"
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>
<div id="time" data-placeholder="…">…</div>
<p>
<button type="button" id="randomize">Randomize</button
><button type="button" id="run">Run</button>
</p>
</form>
<div id="n" class="info n">
loading a large graph, it can take a few seconds...
</div>
<div id="e" class="info e"></div>
<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;
}
.control #time {
text-align: center;
}
.info {
position: absolute;
color: #fff;
background: #141229;
font-size: 12px;
font-family: monospace;
padding: 5px;
}
.info.n {
top: 0;
left: 0;
}
.info.e {
top: 20px;
left: 0;
}