study-buddy / app /agents /d3 /templates /flow_bubble.py
GitHub Actions
deploy d092bea3608b7a29952f16357fda39b7a29e399b
2e818da
Raw
History Blame Contribute Delete
4.45 kB
"""Bubble chart template — flow family.
Substitution note: d3-graph-gallery.com's "circular packing" pages
(https://d3-graph-gallery.com/graph/circularpacking_basic.html and
siblings, confirmed by re-checking https://d3-graph-gallery.com/
circularpacking.html's link list) are all `d3.forceSimulation()` bubble
demos with flat dummy data, not `d3.pack()`/`d3.hierarchy()` layouts — the
same substitution note hier_circle_pack.py already made for its own nested
version of this problem. `flow_bubble` needs a FLAT, already-packed bubble
chart (not the nested tree hier_circle_pack.py uses), so this is adapted
instead from a different, real, verified flat `d3.pack()` bubble-chart
source: https://multimedia.report/classes/coding/2018/exercises/basicbubblepackchart/
— which already builds its hierarchy from a flat list exactly the way this
template needs (`d3.hierarchy({children: data}).sum(d => d.value)` then
`d3.pack().size([...]).padding(...)`, one circle + centered label per leaf),
so no nested-to-flat adaptation was even needed here. Field names are wired
to this template's own flat `BubbleData` schema (`label`/`value` instead of
the fetched source's CSV `Fruit`/`Amount` columns); circle fill color and
the radius-proportional label truncation follow the same conventions
already used by hier_circle_pack.py for its own leaf circles.
"""
from __future__ import annotations
from pydantic import BaseModel
from app.agents.d3.registry import D3Template, register
class Bubble(BaseModel):
label: str
value: float
class BubbleData(BaseModel):
title: str = ""
data: list[Bubble]
_HTML = """<!-- Adapted from https://multimedia.report/classes/coding/2018/exercises/basicbubblepackchart/ (flat-list d3.pack() bubble chart) -->
<!-- d3-graph-gallery.com's circular-packing pages are force-simulation demos, not hierarchy layouts; see module docstring -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
html, body { margin: 0; padding: 0; width: 100vw; height: 100vh; background: #FAF7F2; color: #1A3557; font-family: Georgia, 'Libre Caslon Text', serif; overflow: hidden; }
#my_dataviz { width: 100vw; height: 100vh; }
.chart-title { fill: #1A3557; font-size: 18px; text-anchor: middle; }
.node-circle { stroke: #1A3557; stroke-width: 1px; }
.node-label { fill: #1A3557; text-anchor: middle; }
</style>
</head>
<body>
<div id="my_dataviz"></div>
<script>
const data = __DATA__;
const margin = {top: 50, right: 10, bottom: 10, left: 10},
width = 960 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
const svg = d3.select("#my_dataviz")
.append("svg")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.attr("preserveAspectRatio", "xMidYMid meet")
.style("width", "100%")
.style("height", "100%");
svg.append("text")
.attr("class", "chart-title")
.attr("x", (width + margin.left + margin.right) / 2)
.attr("y", 25)
.text(data.title);
const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
const root = d3.hierarchy({ children: data.data })
.sum(d => d.value);
d3.pack()
.size([width - 4, height - 4])
.padding(3)
(root);
const palette = ['#4A7FB5', '#7FA9D6', '#3D6690', '#A7C6E8', '#2C4A66', '#B5764A'];
const node = g.selectAll("g.node")
.data(root.leaves())
.join("g")
.attr("class", "node")
.attr("transform", d => `translate(${d.x},${d.y})`);
node.append("circle")
.attr("class", "node-circle")
.attr("r", d => d.r)
.style("fill", (d, i) => palette[i % palette.length]);
node.append("text")
.attr("class", "node-label")
.attr("dy", "0.31em")
.style("font-size", d => Math.min(14, d.r / 2.5) + "px")
.text(d => d.data.label.substring(0, d.r / 3));
</script>
</body>
</html>
"""
golden_sample = BubbleData(
title="Demo",
data=[
Bubble(label="A", value=10),
Bubble(label="B", value=20),
Bubble(label="C", value=5),
],
)
register(
D3Template(
id="flow_bubble",
family="flow",
title="Bubble chart",
when_to_use="Show relative size of a flat set of named quantities as packed circles (not a hierarchy).",
data_requirements="A flat list of labeled values (no nesting).",
schema=BubbleData,
html_template=_HTML,
golden_sample=golden_sample,
)
)