Skip to content
  1. Examples

Minimum Spanning Tree

This example shows how to use the minimumSpanningTree algorithm.

ts
import Ogma, { NodeId } from '@linkurious/ogma';

const ogma = new Ogma({ container: 'graph-container' });
const className = 'minimum-spanning-tree';
const secondaryClassName = 'secondary';

ogma.styles.createClass({
  name: className,
  nodeAttributes: {
    color: '#55A3E8',
    radius: 30
  },
  edgeAttributes: {
    color: '#55A3E8',
    width: 18
  }
});

ogma.styles.createClass({
  name: secondaryClassName,
  edgeAttributes: {
    opacity: 0.2
  }
});

ogma.styles.addNodeRule({
  radius: 12
});

ogma.generate
  .erdosRenyi({ nodes: 150, edges: 2000 })
  .then(graph => ogma.setGraph(graph))
  .then(() => ogma.layouts.force({ duration: 0, locate: true }));

document.getElementById('btn')!.addEventListener('click', () => {
  const subGraphs = ogma.algorithms.minimumSpanningTree();
  const visible = new Set<NodeId>();
  subGraphs.forEach(subGraph =>
    subGraph.edges.forEach(edge => visible.add(edge.getId()))
  );

  ogma
    .getEdges()
    .filter(edge => {
      return !visible.has(edge.getId());
    })
    .setAttributes({ opacity: 0.2, detectable: false });

  const edges = ogma.getEdges().filter(edge => visible.has(edge.getId()));
  edges.addClass(className);
  ogma.getNodes().addClass(className);
  edges.inverse().addClass(secondaryClassName);

  return ogma.layouts.force({
    locate: true,
    gravity: 0, // to let the tree spead over the larger surface
    edges: edges // so that the secondary edges have no influence on the layout
  });
});
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>
    <div class="panel">
      <button id="btn" class="cleanup">Spanning Tree</button>
    </div>
    <script type="module" src="index.ts"></script>
  </body>
</html>
css
html,
body {
  font-family: 'Inter', sans-serif;
}

:root {
  --base-color: #4999f7;
  --active-color: var(--base-color);
  --gray: #d9d9d9;
  --white: #ffffff;
  --lighter-gray: #f4f4f4;
  --light-gray: #e6e6e6;
  --inactive-color: #cee5ff;
  --group-color: #525fe1;
  --group-inactive-color: #c2c8ff;
  --selection-color: #04ddcb;
  --darker-gray: #b6b6b6;
  --dark-gray: #555;
  --dark-color: #3a3535;
  --edge-color: var(--dark-color);
  --border-radius: 5px;
  --button-border-radius: var(--border-radius);
  --edge-inactive-color: var(--light-gray);
  --button-background-color: #ffffff;
  --shadow-color: rgba(0, 0, 0, 0.25);
  --shadow-hover-color: rgba(0, 0, 0, 0.5);
  --button-shadow: 0 0 4px var(--shadow-color);
  --button-shadow-hover: 0 0 4px var(--shadow-hover-color);
  --button-icon-color: #000000;
  --button-icon-hover-color: var(--active-color);
}

#graph-container {
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  position: absolute;
  margin: 0;
  overflow: hidden;
}

.ui {
  position: absolute;
  display: flex;
  flex-direction: column;
  gap: 0.5em;
}

#custom-group-btn {
  top: 40px;
}

.panel {
  background: var(--button-background-color);
  border-radius: var(--button-border-radius);
  box-shadow: var(--button-shadow);
  padding: 10px;
}

.panel {
  position: absolute;
  top: 1em !important;
  left: 1em !important;
}

.panel h2 {
  text-transform: uppercase;
  font-weight: 400;
  font-size: 14px;
  margin: 0;
}

.panel .section {
  margin-top: 1px;
  padding: 5px 10px;
  text-align: center;
}

.panel button {
  background: var(--button-background-color);
  border: none;
  border-radius: var(--button-border-radius);
  border-color: var(--shadow-color);
  padding: 5px 10px;
  cursor: pointer;
  width: 100%;
  color: var(--dark-gray);
  border: 1px solid var(--light-gray);
}

.panel .section button:hover {
  background: var(--lighter-gray);
  border: 1px solid var(--darker-gray);
}

.panel .section button[disabled] {
  color: var(--light-gray);
  border: 1px solid var(--light-gray);
  background-color: var(--lighter-gray);
}