Skip to content

Instantly share code, notes, and snippets.

@bilal-fazlani
Created April 19, 2023 17:25
Show Gist options
  • Save bilal-fazlani/c7a9abb7358bdfefa7fd9f1f29780a17 to your computer and use it in GitHub Desktop.
Save bilal-fazlani/c7a9abb7358bdfefa7fd9f1f29780a17 to your computer and use it in GitHub Desktop.
D3 chart for bi-directional connections
{
"types": [
"licensing",
"suit",
"resolved"
],
"nodes": [
{
"id": "Microsoft"
},
{
"id": "Amazon"
},
{
"id": "HTC"
},
{
"id": "Samsung"
},
{
"id": "Apple"
},
{
"id": "Motorola"
},
{
"id": "Nokia"
},
{
"id": "Kodak"
},
{
"id": "Barnes & Noble"
},
{
"id": "Foxconn"
},
{
"id": "Oracle"
},
{
"id": "Google"
},
{
"id": "Inventec"
},
{
"id": "LG"
},
{
"id": "RIM"
},
{
"id": "Sony"
},
{
"id": "Qualcomm"
},
{
"id": "Huawei"
},
{
"id": "ZTE"
},
{
"id": "Ericsson"
}
],
"links": [
{
"source": "Microsoft",
"target": "Amazon",
"type": "licensing"
},
{
"source": "Microsoft",
"target": "HTC",
"type": "licensing"
},
{
"source": "Samsung",
"target": "Apple",
"type": "suit"
},
{
"source": "Motorola",
"target": "Apple",
"type": "suit"
},
{
"source": "Nokia",
"target": "Apple",
"type": "resolved"
},
{
"source": "HTC",
"target": "Apple",
"type": "suit"
},
{
"source": "Kodak",
"target": "Apple",
"type": "suit"
},
{
"source": "Microsoft",
"target": "Barnes & Noble",
"type": "suit"
},
{
"source": "Microsoft",
"target": "Foxconn",
"type": "suit"
},
{
"source": "Oracle",
"target": "Google",
"type": "suit"
},
{
"source": "Apple",
"target": "HTC",
"type": "suit"
},
{
"source": "Microsoft",
"target": "Inventec",
"type": "suit"
},
{
"source": "Samsung",
"target": "Kodak",
"type": "resolved"
},
{
"source": "LG",
"target": "Kodak",
"type": "resolved"
},
{
"source": "RIM",
"target": "Kodak",
"type": "suit"
},
{
"source": "Sony",
"target": "LG",
"type": "suit"
},
{
"source": "Kodak",
"target": "LG",
"type": "resolved"
},
{
"source": "Apple",
"target": "Nokia",
"type": "resolved"
},
{
"source": "Qualcomm",
"target": "Nokia",
"type": "resolved"
},
{
"source": "Apple",
"target": "Motorola",
"type": "suit"
},
{
"source": "Microsoft",
"target": "Motorola",
"type": "suit"
},
{
"source": "Motorola",
"target": "Microsoft",
"type": "suit"
},
{
"source": "Huawei",
"target": "ZTE",
"type": "suit"
},
{
"source": "Ericsson",
"target": "ZTE",
"type": "suit"
},
{
"source": "Kodak",
"target": "Samsung",
"type": "resolved"
},
{
"source": "Apple",
"target": "Samsung",
"type": "suit"
},
{
"source": "Kodak",
"target": "RIM",
"type": "suit"
},
{
"source": "Nokia",
"target": "Qualcomm",
"type": "suit"
}
]
}
<html>
<head>
<title>D3 chart for bi-directional connections</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<h1>D3 chart for bi-directional connections</h1>
<svg></svg>
<script type="module">
function linkArc(d) {
const r = Math.hypot(d.target.x - d.source.x, d.target.y - d.source.y);
return `
M${d.source.x},${d.source.y}
A${r},${r} 0 0,1 ${d.target.x},${d.target.y}
`;
}
const drag = simulation => {
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
const width = 1024;
const height = 1024;
const data = await d3.json("/data.json");
function createSvg(data) {
const links = data.links;//.map(d => Object.create(d));
const nodes = data.nodes;//.map(d => Object.create(d));
const types = data.types;
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody().strength(-400))
.force("x", d3.forceX())
.force("y", d3.forceY());
const svg = d3.select("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height])
.style("font", "12px sans-serif");
const color = d3.scaleOrdinal(types, d3.schemeCategory10)
// Per-type markers, as they don't inherit styles.
svg.append("defs")
.selectAll("marker")
.data(types)
.join("marker")
.attr("id", d => `arrow-${d}`)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -0.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("fill", color)
.attr("d", "M0,-5L10,0L0,5");
const link = svg.append("g")
.attr("fill", "none")
.attr("stroke-width", 1.5)
.selectAll("path")
.data(links)
.join("path")
.attr("stroke", d => color(d.type))
.attr("marker-end", d => `url(${new URL(`#arrow-${d.type}`, location)})`);
const node = svg.append("g")
.attr("fill", "currentColor")
.attr("stroke-linecap", "round")
.attr("stroke-linejoin", "round")
.selectAll("g")
.data(nodes)
.join("g")
.call(drag(simulation));
node.append("circle")
.attr("stroke", "white")
.attr("stroke-width", 1.5)
.attr("r", 4);
node.append("text")
.attr("x", 8)
.attr("y", "0.31em")
.text(d => d.id)
.clone(true).lower()
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-width", 3);
simulation.on("tick", () => {
link.attr("d", linkArc);
node.attr("transform", d => `translate(${d.x},${d.y})`);
});
// invalidation.then(() => simulation.stop());
return svg.node();
};
createSvg(data);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment