Skip to content

Instantly share code, notes, and snippets.

@luketn
Created March 31, 2024 08:02
Show Gist options
  • Save luketn/117015418a3d67539f7fa45f2bccda60 to your computer and use it in GitHub Desktop.
Save luketn/117015418a3d67539f7fa45f2bccda60 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HNSW Graph Visualization</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
.link {
stroke: #999;
stroke-opacity: 0.6;
}
.node circle {
stroke: #fff;
stroke-width: 1.5px;
}
.node text {
font-size: 12px;
font-family: Arial;
}
.layer-label {
font-size: 14px;
font-family: Arial;
fill: #333;
}
</style>
</head>
<body>
<svg width="960" height="600"></svg>
<script>
function generateMockHNSWData(layers, nodesPerLayer) {
let nodes = [], links = [];
// Generate nodes for each layer
for (let layer = 0; layer < layers; layer++) {
for (let i = 0; i < nodesPerLayer[layer]; i++) {
const nodeId = `L${layer}N${i}`;
nodes.push({id: nodeId, layer: layer});
// Connect to previous node in the same layer
if (i > 0) {
links.push({source: `L${layer}N${i - 1}`, target: nodeId});
}
// Create bridges to the next layer (if not the bottom layer)
if (layer < layers - 1) {
const targetNodeId = `L${layer + 1}N${Math.floor(i / 2)}`;
links.push({source: nodeId, target: targetNodeId});
}
}
}
return {nodes, links};
}
const width = 640;
const height = 480;
// Generate mock data
const {nodes, links} = generateMockHNSWData(3, [5, 10, 20]); // Adjust parameters as needed
const svg = d3.select("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height]);
// Define the simulation here but don't start it yet
const simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(d => d.id))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(0, 0));
// Now define the drag functionality
function 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);
}
// Draw links
const link = svg.append("g")
.selectAll("line")
.data(links)
.join("line")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6);
// Draw nodes
const node = svg.append("g")
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", 5)
.attr("fill", d => d3.schemeCategory10[d.layer % 10])
.call(drag(simulation)); // Now we can safely pass the simulation
node.append("title")
.text(d => d.id);
// Apply the data to the simulation
simulation
.nodes(nodes)
.on("tick", () => {
link.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node.attr("cx", d => d.x)
.attr("cy", d => d.y);
});
simulation.force("link")
.links(links);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment