File size: 4,686 Bytes
5f5806d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Danbooru Tree from JSON</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body {
font-family: sans-serif;
}
svg {
width: 100%;
height: 1500px;
}
.node circle {
fill: steelblue;
}
.node text {
font-size: 12px;
fill: #333;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<h2>Danbooru Categories</h2>
<button id="downloadBtn">Download SVG</button>
<svg></svg>
<script>
d3.json("json/danbooru_flat.json").then(function(data) {
const svg = d3.select("svg");
const width = 750;
const height = 1200;
// ---- STEP 1: Limit second-level children ----
function limitSecondLevel(node, depth = 0) {
if (node.children && depth === 1 && node.children.length > 5) {
const visible = node.children.slice(0, 5);
visible.push({ name: "...", children: [] });
node.children = visible;
}
if (node.children) {
node.children.forEach(child => limitSecondLevel(child, depth + 1));
}
return node;
}
const limited = limitSecondLevel(structuredClone(data));
const root = d3.hierarchy(limited, d => d.children);
// ---- STEP 2: Track root category for coloring ----
root.eachAfter(d => {
if (d.depth === 1) {
d.data.rootCategory = d.data.name;
} else if (d.parent) {
d.data.rootCategory = d.parent.data.rootCategory;
}
});
const treeLayout = d3.tree().size([height, width]);
treeLayout(root);
// ---- STEP 3: Scale for tag_count → radius ----
const allTagCounts = root.descendants()
.filter(d => d.depth > 0)
.map(d => d.data.tag_count || 0);
const sizeScale = d3.scaleSqrt()
.domain([0, d3.max(allTagCounts)])
.range([4, 20]);
// ---- STEP 4: Color Map ----
const categoryColors = {
"attire": "#f4a261",
"body": "#e76f51",
"characters": "#2a9d8f",
"copyrights": "#264653",
"creatures": "#8ecae6",
"drawing software": "#219ebc",
"games": "#3a86ff",
"metatags": "#ffbe0b",
"more": "#b5179e",
"objects": "#6d6875",
"plant": "#7cb518",
"real_world": "#a5a58d",
"sex": "#ef476f",
"visual_characteristics": "#06d6a0",
"subject": "#ffd166",
"uncategorized": "#adb5bd",
"actions_and_expressions": "#d00000",
"objects_and_backgrounds": "#118ab2"
};
function getColor(d) {
if (d.data.name === "...") return "gray";
const category = d.data.rootCategory?.toLowerCase();
return categoryColors[category] || "steelblue";
}
// ---- STEP 5: Draw Links ----
svg.selectAll(".link")
.data(root.links())
.join("path")
.attr("class", "link")
.attr("d", d3.linkHorizontal()
.x(d => d.y)
.y(d => d.x)
);
// ---- STEP 6: Draw Nodes ----
const node = svg.selectAll(".node")
.data(root.descendants())
.join("g")
.attr("class", "node")
.attr("transform", d => `translate(${d.y},${d.x})`);
node.append("circle")
.attr("r", d => d.depth === 0 ? 6 : sizeScale(d.data.tag_count || 0))
.style("fill", d => d.data.color || "steelblue");
node.append("title")
.text(d => `${d.data.name}\nTags: ${d.data.tag_count || 0}`);
node.append("text")
.attr("x", 10)
.style("font-weight", "bold")
.attr("dy", "0.32em")
.text(d => d.data.name);
});
document.getElementById("downloadBtn").addEventListener("click", () => {
const svgNode = document.querySelector("svg");
// Clone the SVG node to preserve original
const clonedSvg = svgNode.cloneNode(true);
const outer = document.createElement("div");
outer.appendChild(clonedSvg);
// Add xmlns so it can be saved properly
clonedSvg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
// Serialize the SVG
const svgData = new XMLSerializer().serializeToString(clonedSvg);
const svgBlob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" });
// Create download link
const url = URL.createObjectURL(svgBlob);
const a = document.createElement("a");
a.href = url;
a.download = "danbooru_tree.svg";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
</script>
</body>
</html>
|