Created
August 1, 2024 13:44
-
-
Save JasonGoemaat/02c584094a05d47f3fe9f65cabce4126 to your computer and use it in GitHub Desktop.
Claude D3 Graph
This file contains 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"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Vertical Algorithm Topics Tree with Progress Bars</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
margin: 0; | |
padding: 0; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
height: 100vh; | |
background-color: #f0f0f0; | |
} | |
#tree-container { | |
width: 95vw; | |
height: 95vh; | |
background-color: white; | |
border-radius: 10px; | |
box-shadow: 0 0 10px rgba(0,0,0,0.1); | |
} | |
.node rect { | |
fill: #fff; | |
stroke: steelblue; | |
stroke-width: 2px; | |
} | |
.node text { | |
font: 12px sans-serif; | |
} | |
.link { | |
fill: none; | |
stroke: #ccc; | |
stroke-width: 2px; | |
} | |
.progress-bar { | |
fill: #4CAF50; | |
} | |
.progress-bar-bg { | |
fill: #e0e0e0; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="tree-container"></div> | |
<script> | |
const treeData = { | |
"Arrays & Hashing": { | |
"children": [ | |
"Two Pointers", | |
"Stack" | |
] | |
}, | |
"Two Pointers": { | |
"children": [ | |
"Binary Search", | |
"Sliding Window", | |
"Linked List" | |
] | |
}, | |
"Stack": { | |
"children": [] | |
}, | |
"Binary Search": { | |
"children": [ | |
"Trees" | |
] | |
}, | |
"Sliding Window": { | |
"children": [] | |
}, | |
"Linked List": { | |
"children": [ | |
"Trees" | |
] | |
}, | |
"Trees": { | |
"children": [ | |
"Tries", | |
"Backtracking", | |
"Heap / Priority Queue" | |
] | |
}, | |
"Tries": { | |
"children": [] | |
}, | |
"Backtracking": { | |
"children": [ | |
"Graphs", | |
"1-D DP" | |
] | |
}, | |
"Heap / Priority Queue": { | |
"children": [ | |
"Intervals", | |
"Greedy", | |
"Advanced Graphs" | |
] | |
}, | |
"Graphs": { | |
"children": [ | |
"Advanced Graphs", | |
"2-D DP", | |
"Math & Geometry" | |
] | |
}, | |
"1-D DP": { | |
"children": [ | |
"2-D DP", | |
"Bit Manipulation" | |
] | |
}, | |
"Intervals": { | |
"children": [] | |
}, | |
"Greedy": { | |
"children": [] | |
}, | |
"Advanced Graphs": { | |
"children": [] | |
}, | |
"2-D DP": { | |
"children": [] | |
}, | |
"Bit Manipulation": { | |
"children": [ | |
"Math & Geometry" | |
] | |
}, | |
"Math & Geometry": { | |
"children": [] | |
} | |
}; | |
function createGraph(data) { | |
const graph = { nodes: {}, links: [] }; | |
function addNode(name) { | |
if (!graph.nodes[name]) { | |
graph.nodes[name] = { | |
name, | |
children: new Set(), | |
progress: Math.random() // Random progress for demonstration | |
}; | |
} | |
return graph.nodes[name]; | |
} | |
Object.entries(data).forEach(([parent, info]) => { | |
const parentNode = addNode(parent); | |
info.children.forEach(child => { | |
const childNode = addNode(child); | |
parentNode.children.add(child); | |
graph.links.push({ source: parent, target: child }); | |
}); | |
}); | |
return graph; | |
} | |
const graph = createGraph(treeData); | |
const margin = { top: 40, right: 120, bottom: 50, left: 120 }; | |
const width = window.innerWidth - margin.left - margin.right; | |
const height = window.innerHeight - margin.top - margin.bottom; | |
const svg = d3.select("#tree-container").append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", `translate(${margin.left},${margin.top})`); | |
function createHierarchy(graph, root) { | |
const visited = new Set(); | |
function dfs(nodeName) { | |
if (visited.has(nodeName)) return null; | |
visited.add(nodeName); | |
const node = { | |
name: nodeName, | |
children: [], | |
progress: graph.nodes[nodeName].progress | |
}; | |
graph.nodes[nodeName].children.forEach(childName => { | |
const childNode = dfs(childName); | |
if (childNode) node.children.push(childNode); | |
}); | |
return node.children.length ? node : { name: nodeName, progress: node.progress }; | |
} | |
return dfs(root); | |
} | |
const hierarchicalData = createHierarchy(graph, "Arrays & Hashing"); | |
const tree = d3.tree().size([width, height]); | |
const root = d3.hierarchy(hierarchicalData); | |
tree(root); | |
const link = svg.selectAll(".link") | |
.data(root.descendants().slice(1)) | |
.enter().append("path") | |
.attr("class", "link") | |
.attr("d", d => ` | |
M${d.x},${d.y} | |
C${d.x},${(d.y + d.parent.y) / 2} | |
${d.parent.x},${(d.y + d.parent.y) / 2} | |
${d.parent.x},${d.parent.y} | |
`); | |
const node = svg.selectAll(".node") | |
.data(root.descendants()) | |
.enter().append("g") | |
.attr("class", "node") | |
.attr("transform", d => `translate(${d.x},${d.y})`); | |
const boxWidth = 120; | |
const boxHeight = 60; | |
node.append("rect") | |
.attr("width", boxWidth) | |
.attr("height", boxHeight) | |
.attr("x", -boxWidth / 2) | |
.attr("y", -boxHeight / 2) | |
.attr("rx", 5) | |
.attr("ry", 5); | |
node.append("text") | |
.attr("dy", "-0.5em") | |
.attr("text-anchor", "middle") | |
.text(d => d.data.name) | |
.call(wrap, boxWidth - 10); | |
const progressBarHeight = 10; | |
const progressBarY = boxHeight / 4; | |
node.append("rect") | |
.attr("class", "progress-bar-bg") | |
.attr("width", boxWidth - 10) | |
.attr("height", progressBarHeight) | |
.attr("x", -boxWidth / 2 + 5) | |
.attr("y", progressBarY); | |
node.append("rect") | |
.attr("class", "progress-bar") | |
.attr("width", d => (boxWidth - 10) * d.data.progress) | |
.attr("height", progressBarHeight) | |
.attr("x", -boxWidth / 2 + 5) | |
.attr("y", progressBarY); | |
node.append("text") | |
.attr("dy", "2.5em") | |
.attr("text-anchor", "middle") | |
.text(d => `${Math.round(d.data.progress * 100)}%`); | |
const additionalLinks = graph.links.filter(link => | |
!root.descendants().some(d => | |
d.data.name === link.source && | |
d.children && d.children.some(child => child.data.name === link.target) | |
) | |
); | |
svg.selectAll(".additional-link") | |
.data(additionalLinks) | |
.enter().append("path") | |
.attr("class", "link") | |
.attr("d", d => { | |
const source = root.descendants().find(node => node.data.name === d.source); | |
const target = root.descendants().find(node => node.data.name === d.target); | |
return ` | |
M${source.x},${source.y} | |
C${source.x},${(source.y + target.y) / 2} | |
${target.x},${(source.y + target.y) / 2} | |
${target.x},${target.y} | |
`; | |
}); | |
function wrap(text, width) { | |
text.each(function() { | |
var text = d3.select(this), | |
words = text.text().split(/\s+/).reverse(), | |
word, | |
line = [], | |
lineNumber = 0, | |
lineHeight = 1.1, // ems | |
y = text.attr("y"), | |
dy = parseFloat(text.attr("dy")), | |
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em"); | |
while (word = words.pop()) { | |
line.push(word); | |
tspan.text(line.join(" ")); | |
if (tspan.node().getComputedTextLength() > width) { | |
line.pop(); | |
tspan.text(line.join(" ")); | |
line = [word]; | |
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); | |
} | |
} | |
}); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment