Created
March 31, 2024 08:02
-
-
Save luketn/117015418a3d67539f7fa45f2bccda60 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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