Skip to content
This is the alpha v4 version website. Looking for the v3 documentation?

Shapes

By default, sigma renders all nodes as circles. To use other shapes, declare them in primitives.nodes.shapes first, and then assign shapes to nodes via a style rule.

Built-in shapes

Sigma ships with four built-in SDF shapes: sdfCircle(), sdfSquare(), sdfTriangle(), and sdfDiamond(). Each node selects its shape through the shape style property:

import Graph from "graphology";
import Sigma from "sigma";
import { layerFill, sdfCircle, sdfDiamond, sdfSquare, sdfTriangle } from "sigma/rendering";
import { DEFAULT_STYLES } from "sigma/types";
const container = document.getElementById("sigma-container") as HTMLElement;
const graph = new Graph();
const SHAPES = ["circle", "square", "triangle", "diamond"];
const COLORS = ["#e22653", "#0055ff", "#33cc33", "#ff9900"];
// Create a grid of nodes showing each shape at different sizes
for (let row = 0; row < SHAPES.length; row++) {
const shape = SHAPES[row];
const color = COLORS[row];
for (let col = 0; col < 4; col++) {
const size = 10 + col * 8;
graph.addNode(`${shape}-${col}`, {
x: col * 200,
y: -row * 200,
size,
color,
shape,
label: col === 0 ? shape : "",
});
}
}
new Sigma(graph, container, {
primitives: {
nodes: {
shapes: [sdfCircle(), sdfSquare(), sdfTriangle(), sdfDiamond()],
layers: [layerFill()],
},
},
styles: {
nodes: [
DEFAULT_STYLES.nodes,
{
shape: { attribute: "shape" },
},
],
},
});

Fixed shape for all nodes

If you want all nodes to use a single non-default shape, declare just that shape and set it as a fixed value in styles:

import { sdfSquare } from "sigma/rendering";
new Sigma(graph, container, {
primitives: {
nodes: {
shapes: [sdfSquare()],
},
},
styles: {
nodes: [{ shape: "square", color: { attribute: "color" }, size: { attribute: "size" } }],
},
});

Custom shapes

You can create custom shapes by implementing the SDFShape interface. A shape is defined by a GLSL signed distance function, i.e. a function that returns the distance from a point to the shape boundary (negative inside, positive outside).

The GLSL function must follow the naming convention sdf_{name}(vec2 uv, float size, ...). The uv parameter is the pixel offset from the node center, and size is the node radius. Additional parameters correspond to uniforms declared in the uniforms array.

The inradiusFactor controls how edge endpoints are clamped to the shape boundary (ratio of inradius to circumradius). For a circle it’s 1.0, for a square it’s ~0.707 (half of Math.sqrt(2)).

For a comprehensive catalog of 2D SDF primitives, see Inigo Quilez’s reference.

The example below defines a custom heart shape and uses it alongside the built-in circle:

import Sigma from "sigma";
import { layerFill, sdfCircle } from "sigma/rendering";
import type { SDFShape } from "sigma/rendering";
import { DEFAULT_STYLES } from "sigma/types";
import { getSmallGraph } from "../_data/small-graph";
const container = document.getElementById("sigma-container") as HTMLElement;
/**
* Custom heart SDF shape.
* Based on Inigo Quilez's 2D distance functions:
* https://iquilezles.org/articles/distfunctions2d/
*/
function sdfHeart(): SDFShape {
// The IQ heart SDF has its tip at the origin and extends up to ~y=1.
// We shift p.y by -0.5 to center the heart on the node origin,
// and scale by 0.7 so it fits within the node quad with AA room.
const SCALE = 0.7;
const Y_CENTER = 0.5;
// language=GLSL
const glsl = /*glsl*/ `
float dot2_heart(vec2 v) { return dot(v, v); }
float sdf_heart(vec2 uv, float size) {
float scale = ${SCALE.toFixed(4)};
// Normalize to unit space, then shift so the heart's center is at the origin
vec2 p = uv / (size * scale);
p.y += ${Y_CENTER.toFixed(4)};
p.x = abs(p.x);
float d;
if (p.y + p.x > 1.0) {
d = sqrt(dot2_heart(p - vec2(0.25, 0.75))) - sqrt(2.0) / 4.0;
} else {
d = sqrt(min(dot2_heart(p - vec2(0.0, 1.0)), dot2_heart(p - 0.5 * max(p.x + p.y, 0.0)))) * sign(p.x - p.y);
}
return d * size * scale;
}
`;
return {
name: "heart",
glsl,
uniforms: [],
inradiusFactor: 0.5,
};
}
const graph = getSmallGraph();
graph.forEachNode((node) => {
graph.setNodeAttribute(node, "shape", node === "a" ? "circle" : "heart");
});
new Sigma(graph, container, {
primitives: {
nodes: {
shapes: [sdfCircle(), sdfHeart()],
layers: [layerFill()],
},
},
styles: {
nodes: [DEFAULT_STYLES.nodes, { shape: { attribute: "shape" } }],
},
});