Skip to content

Instantly share code, notes, and snippets.

@w8r
Last active March 10, 2024 15:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save w8r/3d71deced973fb651ad7d3d98fb51bf1 to your computer and use it in GitHub Desktop.
Save w8r/3d71deced973fb651ad7d3d98fb51bf1 to your computer and use it in GitHub Desktop.
Graph to SVG
type Id = string | number;
interface Node {
id: Id;
color: string;
radius: number;
x: number;
y: number;
strokeWidth?: number;
strokeColor?: string;
}
interface Edge {
source: Id;
target: Id;
color?: string;
}
interface Graph {
nodes: Node[];
edges: Edge[];
}
export function svg(
{ nodes = [], edges = [] }: Graph,
nodeColor = '#404040',
edgeColor = '#909090',
viewSize = 500,
buffer = 1.2
): string {
const nodesMap = new Map<Id,Node>();
const bbox = [Infinity, Infinity, -Infinity, -Infinity];
if (nodes.length === 0) {
nodes = [{
id: 0,
x: 0,
y: 0,
radius: 10,
color: 'none',
strokeWidth: 1,
strokeColor: nodeColor
}];
bbox[0] = -100;
bbox[1] = -100;
bbox[2] = 100;
bbox[3] = 100;
}
nodes.forEach((node) => {
const { x = 0, y = 0, radius = 5 } = node;
bbox[0] = Math.min(x - radius, bbox[0]);
bbox[1] = Math.min(y - radius, bbox[1]);
bbox[2] = Math.max(x + radius, bbox[2]);
bbox[3] = Math.max(y + radius, bbox[3]);
});
const [x0, y0, x1, y1] = bbox;
const w = Math.abs(x1 - x0) || 0;
const h = Math.abs(y1 - y0) || 0;
const cx = (x0 + x1) / 2 || 0;
const ratio = ((Math.max(w, h) * buffer) / viewSize) * 4;
const renderedNodes = nodes.map((node) => {
nodesMap.set(node.id, node);
const { x = 0, y = 0, radius = 5 } = node;
const strokeColor = node.strokeColor
? ` stroke="${node.strokeColor}"`
: '';
const strokeWidth = node.strokeWidth
? ` stroke-width="${node.strokeWidth}"`
: '';
return `<circle
cx="${x}" cy="${y}"
r="${radius * ratio}" fill="${
node.color || nodeColor
}" ${strokeColor} ${strokeWidth} />`.replace(/\s+/g, ' ');
});
const renderedEdges = edges.map((edge) => {
const source = nodesMap.get(edge.source) as Node;
const target = nodesMap.get(edge.target) as Node;
const sx = source.x || 0;
const sy = source.y || 0;
const tx = target.x || 0;
const ty = target.y || 0;
return `<line
x1="${sx}"
y1="${sy}"
x2="${tx}"
y2="${ty}"
stroke="${edge.color || edgeColor}" />`;
});
const s = Math.max(w, h) * buffer;
return `
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="${cx - s / 2} ${-(s - h)} ${s} ${s}"
width="${viewSize}" height="${viewSize} ">
<g>
<g>${renderedEdges.join('')}</g>
<g>${renderedNodes.join('')}</g>
</g>
</svg>
`.trim();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment