Skip to content

Instantly share code, notes, and snippets.

@IPWright83
Last active May 4, 2021 09:44
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 IPWright83/932373f52dcc843bb2ccf9a87d14636d to your computer and use it in GitHub Desktop.
Save IPWright83/932373f52dcc843bb2ccf9a87d14636d to your computer and use it in GitHub Desktop.

Example System DAG

This is an example of a System DAG using a forceSimulation with images

<meta charset="utf-8" />
<title>System Diagram</title>
<style>
.graph {
position: absolute;
top: 0;
left: 0;
}
.node-layer {
position: absolute;
}
svg {
position: absolute;
top: 0;
left: 0;
}
text {
font-family: sans-serif;
font-size: 12px;
text-anchor: middle;
user-select: none;
}
</style>
<svg width="1600" height="800">
<defs>
<marker
id="arrowhead"
viewBox="-0 -5 10 10"
refX="26"
refY="0"
orient="auto"
markerWidth="15"
markerHeight="15"
xoverflow="visible"
>
<path d="M 0,-5 L 9 ,0 L 0,5" fill="#000" />
</marker>
</defs>
<g class="node-layer" />
<g class="link-layer" />
</svg>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script>
const width = 1600;
const height = 800;
const systems = [
{ id: 'SystemA', dependencies: [{ id: 'SystemL' }]},
{ id: 'SystemB' },
{ id: 'SystemC', dependencies: [{ id: 'SystemK' }]},
{ id: 'SystemD', dependencies: [{ id: 'SystemE' }]},
{ id: 'SystemE', dependencies: [{ id: 'SystemA' }]},
{ id: 'SystemF', dependencies: [{ id: 'esg-aws-application-load-balancer' }]},
{ id: 'SystemG', dependencies: [{ id: 'SystemI' }]},
{ id: 'esg-aws-application-load-balancer' },
{ id: 'SystemH', dependencies: [{ id: 'SystemM' }]},
{ id: 'SystemI' },
{ id: 'SystemJ', dependencies: [{ id: 'SystemF' },{ id: 'SystemD' },{ id: 'SystemG' },{ id: 'SystemC' },{ id: 'SystemH' }]},
{ id: 'SystemK' },
{ id: 'SystemL', dependencies: [{ id: 'SystemB' }]},
{ id: 'SystemM' },
];
const data = {
nodes: systems,
links: systems.flatMap((system) =>
(system.dependencies || []).map((dependency) => ({
...dependency,
source: system.id,
target: dependency.id,
}))
),
};
// Clear the layers completely first
d3.select('.node-layer').selectAll().remove();
d3.select('.node-layer').selectAll().remove();
// Hook up zoom/panning events
const svg = d3.select('svg').call(
d3.zoom().on('zoom', (event) => {
svg.attr('transform', event.transform);
})
);
// Zoom/Pan handlers
const drag = (simulation) => {
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
return d3.drag().on('start', dragstarted).on('drag', dragged).on('end', dragended);
};
// Create the force layout
const simulation = d3
.forceSimulation(data.nodes)
.force(
'link',
d3.forceLink(data.links).id((d) => d.id)
)
.force('charge', d3.forceManyBody().strength(-1000))
.force('center', d3.forceCenter(width / 2, height / 2))
.on('tick', () => {
links
.attr('x1', (d) => d.source.x)
.attr('y1', (d) => d.source.y)
.attr('x2', (d) => d.target.x)
.attr('y2', (d) => d.target.y);
nodes.attr('transform', (d) => `translate(${d.x},${d.y})`);
});
// Add the links to the DOM
const links = d3
.select('.link-layer')
.selectAll('line')
.data(data.links)
.enter()
.append('line')
.attr('class', 'link')
.attr('stroke', 'black')
.attr('marker-end', 'url(#arrowhead)')
.style('pointer-events', 'none');
// Nodes & their labels will move around as a group
const nodes = d3
.select('.node-layer')
.selectAll('.node')
.data(data.nodes)
.enter()
.append('g')
.attr('class', 'node');
// Add the node images
nodes
.append('svg:image')
.attr('class', 'node')
.attr('xlink:href', 'https://hackernoon.com/hn-images/1*lAR9Uh_gJ7dp23e0vhy5Hg.png')
.attr('x', -25)
.attr('y', -25)
.attr('height', 50)
.attr('width', 50)
.call(drag(simulation));
nodes
.append('text')
.attr('class', 'label')
.attr('x', 0)
.attr('y', 40)
.text((d) => d.name)
.style('pointer-events', 'none');
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment