<div id="sigma-container"></div>
import { layerBorder } from "@sigma/node-border";
import Graph from "graphology";
import ForceSupervisor from "graphology-layout-force/worker";
import circular from "graphology-layout/circular";
import Papa from "papaparse";
import Sigma from "sigma";
import { layerDashed, layerFill, layerPlain, pathCurved, pathLine } from "sigma/rendering";
import { DEFAULT_STYLES } from "sigma/types";
const container = document.getElementById("sigma-container") as HTMLElement;
// Padgett & Ansell's Florentine families: 16 Renaissance families, tied by
// marriage alliances and business relationships. Some pairs have both ties,
// which sigma renders as parallel edges.
// Source: UCINET data (PADGETT.DAT and PADGW.DAT), mirrored at
// https://github.com/bavla/Nets/tree/master/data/Pajek/ucinet
const [nodesCsv, edgesCsv] = await Promise.all([
fetch("/data/padgett-nodes.csv").then((res) => res.text()),
fetch("/data/padgett-edges.csv").then((res) => res.text()),
const { data: nodes } = Papa.parse<{ id: string; label: string; wealth: string; priorates: string }>(nodesCsv, {
const { data: edges } = Papa.parse<{ source: string; target: string; type: string }>(edgesCsv, {
const graph = new Graph({ multi: true, type: "undirected" });
nodes.forEach(({ id, label, wealth }) => {
graph.addNode(id, { label, wealth: +wealth });
edges.forEach(({ source, target, type }) => {
graph.addEdge(source, target, { type });
circular.assign(graph, { scale: 100 });
const renderer = new Sigma(graph, container, {
{ size: 0.12, color: "#43302B" },
{ size: 0.12, color: "#ffffff" },
{ color: "transparent", fill: true },
dashColor: { type: "color", default: "#000" },
dashSize: { type: "number", default: 0 },
gapColor: { type: "color", default: "transparent" },
paths: [pathLine(), pathCurved()],
dashColor: { attribute: "dashColor" },
dashSize: { attribute: "dashSize", default: 0, mode: "pixels" },
gapColor: { attribute: "gapColor" },
gapSize: { value: 4, mode: "pixels" },
size: { attribute: "wealth", min: 4, max: 12, minValue: 0, maxValue: 150 },
labelVisibility: "visible",
labelFont: "Georgia, serif",
labelBackgroundColor: "#ffffffaa",
then: { cursor: "grabbing" },
{ size: 1.5, parallelPath: "curved", parallelSpread: 0.5 },
marriage: { color: "#E8684A" },
business: { color: "transparent", dashColor: "#5B8FF9", gapColor: "#BBD4FF", dashSize: 4 },
then: { cursor: "grabbing" },
itemSizesReference: "positions",
// Force layout: pin nodes while they are being dragged
const layout = new ForceSupervisor(graph, {
isNodeFixed: (node) => renderer.getNodeState(node).isDragged,