diff --git a/plots/arc-basic/implementations/pygal.py b/plots/arc-basic/implementations/pygal.py
index 58689a03bf..49913ec31a 100644
--- a/plots/arc-basic/implementations/pygal.py
+++ b/plots/arc-basic/implementations/pygal.py
@@ -1,7 +1,7 @@
""" pyplots.ai
arc-basic: Basic Arc Diagram
-Library: pygal 3.1.0 | Python 3.13.11
-Quality: 94/100 | Created: 2025-12-17
+Library: pygal 3.1.0 | Python 3.14.3
+Quality: 90/100 | Created: 2026-02-23
"""
import math
@@ -39,19 +39,31 @@
# Node positions along x-axis (1 to 10 range)
x_positions = np.linspace(1, 10, n_nodes)
-y_baseline = 1.0
+y_baseline = 0.5
-# Build color tuple: Python Blue for all arcs, Python Yellow for nodes
-n_edges = len(edges)
-colors = tuple(["#306998"] * n_edges + ["#FFD43B"])
+# Color palette: distinct hues for immediate weight differentiation (colorblind-safe)
+arc_colors = {1: "#93C5E8", 2: "#D4770B", 3: "#08306B"}
-# Custom style for the chart
+# Thickness: wide range for immediate visual distinction
+arc_widths = {1: 4, 2: 12, 3: 22}
+
+# Weight labels for tooltip context
+weight_labels = {1: "Weak", 2: "Moderate", 3: "Strong"}
+
+# Build colors tuple: legend entries first, then edges, then nodes
+colors = tuple(
+ [arc_colors[3], arc_colors[2], arc_colors[1]] # Legend (series 1-3)
+ + [arc_colors[w] for _, _, w in edges] # Edges (series 4-18)
+ + ["#B8860B", "#FFD43B"] # Node outline + fill
+)
+
+# Custom style — clean white background, no borders
custom_style = Style(
background="white",
plot_background="white",
foreground="#333333",
- foreground_strong="#306998",
- foreground_subtle="#666666",
+ foreground_strong="#08306B",
+ foreground_subtle="transparent",
colors=colors,
title_font_size=72,
label_font_size=40,
@@ -59,17 +71,21 @@
legend_font_size=40,
value_font_size=32,
stroke_width=3,
- opacity=0.65,
+ opacity=0.85,
opacity_hover=1.0,
)
-# Create XY chart
+# Create XY chart — fill=False prevents area filling under arcs
chart = pygal.XY(
width=4800,
height=2700,
style=custom_style,
+ fill=False,
title="Character Interactions · arc-basic · pygal · pyplots.ai",
- show_legend=False,
+ show_legend=True,
+ legend_at_bottom=True,
+ legend_at_bottom_columns=3,
+ legend_box_size=30,
x_title="",
y_title="",
show_x_guides=False,
@@ -78,17 +94,36 @@
show_y_labels=False,
stroke=True,
dots_size=0,
- stroke_style={"width": 3, "linecap": "round"},
- range=(0, 6),
+ stroke_style={"width": 6, "linecap": "round"},
+ range=(0, 4.6),
xrange=(0, 11),
- x_labels=nodes,
- x_labels_major_count=n_nodes,
+ x_labels=[{"value": float(x_positions[i]), "label": nodes[i]} for i in range(n_nodes)],
truncate_label=-1,
+ css=[
+ "file://style.css",
+ "inline:.plot .background {fill: white; stroke: none !important;}",
+ "inline:.axis .line {stroke: none !important;}",
+ "inline:.axis .guides .line {stroke: none !important;}",
+ "inline:.plot .axis {stroke: none !important;}",
+ "inline:.series .line {fill: none !important;}",
+ # Hide all legend entries after the 3 weight categories
+ "inline:.legends > g:nth-child(n+4) {display: none !important;}",
+ ],
+ js=[],
)
+# Add weight legend entries first (series 1-3, visible in legend)
+for w_val, w_label in [(3, "Strong"), (2, "Moderate"), (1, "Weak")]:
+ chart.add(
+ f"{w_label} connection",
+ [None],
+ stroke=True,
+ show_dots=False,
+ stroke_style={"width": arc_widths[w_val], "linecap": "round"},
+ )
+
# Generate arc points for each edge
-# Each arc is drawn as a series of points forming a semi-circle
-arc_resolution = 30 # Number of points per arc
+arc_resolution = 50
for start_idx, end_idx, weight in edges:
x_start = x_positions[start_idx]
@@ -98,41 +133,42 @@
x_center = (x_start + x_end) / 2
arc_radius = abs(x_end - x_start) / 2
- # Arc height proportional to the distance between nodes
+ # Arc height proportional to node distance
distance = abs(end_idx - start_idx)
height_scale = 0.4 * distance
# Generate arc points (semi-circle above baseline)
arc_points = []
for i in range(arc_resolution + 1):
- theta = math.pi * i / arc_resolution # 0 to pi
+ theta = math.pi * i / arc_resolution
x = x_center - arc_radius * math.cos(theta)
y = y_baseline + height_scale * math.sin(theta)
- arc_points.append((x, y))
+ arc_points.append(
+ {"value": (x, y), "label": f"{nodes[start_idx]} ↔ {nodes[end_idx]} ({weight_labels[weight]})"}
+ )
- # Line thickness based on weight
chart.add(
- f"Arc {start_idx}-{end_idx}",
- arc_points,
- stroke=True,
- show_dots=False,
- fill=False,
- stroke_style={"width": 2 + weight * 2, "linecap": "round"},
+ "", arc_points, stroke=True, show_dots=False, stroke_style={"width": arc_widths[weight], "linecap": "round"}
)
-# Add nodes as a separate series (last, so uses Python Yellow)
-node_points = []
-for i, name in enumerate(nodes):
- x = x_positions[i]
- node_points.append({"value": (x, y_baseline), "label": name})
+# Add node outline ring (dark goldenrod border effect)
+node_points = [
+ {
+ "value": (float(x_positions[i]), y_baseline),
+ "label": f"{nodes[i]} ({sum(1 for s, t, _ in edges if s == i or t == i)} connections)",
+ }
+ for i in range(n_nodes)
+]
+chart.add("", node_points, stroke=False, dots_size=42)
-chart.add("Characters", node_points, stroke=False, dots_size=35)
+# Add node fill on top (Python Yellow)
+chart.add("", node_points, stroke=False, dots_size=32)
# Save outputs
chart.render_to_file("plot.svg")
chart.render_to_png("plot.png")
-# Also save HTML for interactive version
+# Save HTML for interactive version with hover tooltips
with open("plot.html", "w") as f:
f.write(
"""
@@ -140,7 +176,7 @@
Character Interactions · arc-basic · pygal · pyplots.ai
diff --git a/plots/arc-basic/metadata/pygal.yaml b/plots/arc-basic/metadata/pygal.yaml
index 835337c0f5..a17460fc30 100644
--- a/plots/arc-basic/metadata/pygal.yaml
+++ b/plots/arc-basic/metadata/pygal.yaml
@@ -1,40 +1,242 @@
library: pygal
specification_id: arc-basic
created: 2025-12-17 10:58:08+00:00
-updated: 2025-12-17 10:58:08+00:00
-generated_by: claude-opus-4-5-20251101
+updated: '2026-02-23T22:12:02Z'
+generated_by: claude-opus-4-6
workflow_run: 20300358253
issue: 991
-python_version: 3.13.11
+python_version: 3.14.3
library_version: 3.1.0
preview_url: https://storage.googleapis.com/pyplots-images/plots/arc-basic/pygal/plot.png
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/arc-basic/pygal/plot_thumb.png
preview_html: https://storage.googleapis.com/pyplots-images/plots/arc-basic/pygal/plot.html
-quality_score: 94
+quality_score: 90
impl_tags:
dependencies: []
techniques:
- - html-export
- custom-legend
+ - html-export
patterns:
- data-generation
- iteration-over-groups
- - matrix-construction
dataprep: []
- styling: []
+ styling:
+ - minimal-chrome
+ - alpha-blending
+ - edge-highlighting
review:
- strengths: []
- weaknesses: []
+ strengths:
+ - Multi-hue palette (navy, orange, light blue) provides immediate weight differentiation
+ — major improvement over monochromatic blue
+ - Perfect spec compliance with all required arc diagram features implemented correctly
+ - Clean professional design with all visual clutter removed via CSS and two-layer
+ polished node rendering
+ - 'Excellent code quality: KISS structure, deterministic data, elegant arc generation'
+ - 'Strong pygal mastery: CSS-based chrome removal, nth-child legend hack, interactive
+ SVG tooltips'
+ weaknesses:
+ - Weak connection arcs (thin light blue) somewhat subtle against white background
+ - Title right-aligned by pygal default rather than centered
+ - No isolated or single-connection node to demonstrate full range of connectivity
+ patterns
improvements: []
- image_description: The plot displays an arc diagram with 10 character names (Alice,
- Bob, Carol, David, Eve, Frank, Grace, Henry, Iris, Jack) arranged horizontally
- along the bottom on a white background. Each character is represented by a yellow/gold
- circular node (#FFD43B). The connections between characters are shown as blue
- curved arcs (#306998) above the horizontal baseline. The arcs vary in height proportional
- to the distance between connected nodes - longer-range connections (like Alice-Jack)
- have higher arcs, while shorter-range connections (like Bob-Carol) have lower
- arcs. Arc thickness varies based on connection weight, with stronger connections
- appearing thicker. The title "Character Interactions · arc-basic · pygal · pyplots.ai"
- is displayed at the top in blue. The arcs have semi-transparency allowing overlapping
- arcs to be distinguishable.
+ image_description: 'The plot displays a basic arc diagram with 10 character nodes
+ (Alice, Bob, Carol, David, Eve, Frank, Grace, Henry, Iris, Jack) arranged as yellow
+ circles with dark goldenrod outlines along a horizontal baseline near the bottom
+ of the canvas. Curved arcs connect pairs of nodes above the baseline, with arc
+ height proportional to the distance between connected nodes — the tallest arcs
+ (Alice-Jack, Alice-Henry) span most of the canvas width and reach the top of the
+ plot. Three distinct colors encode connection weight: dark navy (#08306B) thick
+ arcs for strong connections, orange (#D4770B) medium arcs for moderate connections,
+ and light blue (#93C5E8) thin arcs for weak connections. The title "Character
+ Interactions · arc-basic · pygal · pyplots.ai" appears at the top right. Node
+ labels are positioned below the baseline. A three-item legend at the bottom shows
+ "Strong connection", "Moderate connection", and "Weak connection" with colored
+ squares in the respective hues. The background is clean white with no grid lines,
+ axis borders, or visual clutter.'
verdict: APPROVED
+ criteria_checklist:
+ visual_quality:
+ score: 28
+ max: 30
+ items:
+ - id: VQ-01
+ name: Text Legibility
+ score: 8
+ max: 8
+ passed: true
+ comment: All font sizes explicitly set (title=72, labels=40, major_labels=40,
+ legend=40, value=32). All text clearly readable at full resolution.
+ - id: VQ-02
+ name: No Overlap
+ score: 6
+ max: 6
+ passed: true
+ comment: No text collisions. Node labels well-spaced, arcs overlap naturally
+ and semi-transparency handles it well.
+ - id: VQ-03
+ name: Element Visibility
+ score: 5
+ max: 6
+ passed: true
+ comment: Strong and moderate arcs immediately visible. Weak arcs (thin light
+ blue) visible but could be slightly more prominent.
+ - id: VQ-04
+ name: Color Accessibility
+ score: 4
+ max: 4
+ passed: true
+ comment: Multi-hue palette (navy, orange, light blue) is fully colorblind-safe.
+ All three weight levels distinguishable by both hue and luminance.
+ - id: VQ-05
+ name: Layout & Canvas
+ score: 3
+ max: 4
+ passed: true
+ comment: Good horizontal distribution. Arcs fill upper 60% well. Some unused
+ space below legend. Title right-aligned creates slight imbalance.
+ - id: VQ-06
+ name: Axis Labels & Title
+ score: 2
+ max: 2
+ passed: true
+ comment: Descriptive title with correct format. Node labels clear and appropriate
+ for arc diagram.
+ design_excellence:
+ score: 15
+ max: 20
+ items:
+ - id: DE-01
+ name: Aesthetic Sophistication
+ score: 6
+ max: 8
+ passed: true
+ comment: Multi-hue palette with intentional weight hierarchy. Yellow nodes
+ with goldenrod outlines. Clean white canvas. Clearly above configured defaults.
+ - id: DE-02
+ name: Visual Refinement
+ score: 5
+ max: 6
+ passed: true
+ comment: All chrome removed via inline CSS. Clean white background. Round
+ linecaps. Polished two-layer node rendering.
+ - id: DE-03
+ name: Data Storytelling
+ score: 4
+ max: 6
+ passed: true
+ comment: Three-level weight encoding via color and thickness creates immediate
+ visual hierarchy. Alice emerges as central hub. Arc height reveals connection
+ distance.
+ spec_compliance:
+ score: 15
+ max: 15
+ items:
+ - id: SC-01
+ name: Plot Type
+ score: 5
+ max: 5
+ passed: true
+ comment: 'Correct arc diagram: nodes on horizontal line, curved arcs above
+ baseline.'
+ - id: SC-02
+ name: Required Features
+ score: 4
+ max: 4
+ passed: true
+ comment: 'All spec features present: arcs above axis, height proportional
+ to distance, semi-transparency, color coding, thickness by weight, readable
+ labels.'
+ - id: SC-03
+ name: Data Mapping
+ score: 3
+ max: 3
+ passed: true
+ comment: Nodes correctly positioned along x-axis. 15 edges correctly rendered
+ as arcs. Heights proportional to index distance.
+ - id: SC-04
+ name: Title & Legend
+ score: 3
+ max: 3
+ passed: true
+ comment: Title format correct. Legend shows three weight categories with matching
+ colors.
+ data_quality:
+ score: 14
+ max: 15
+ items:
+ - id: DQ-01
+ name: Feature Coverage
+ score: 5
+ max: 6
+ passed: true
+ comment: Shows short-range, medium-range, and long-range connections. Three
+ weight levels. Varying node connectivity. Could include an isolated node.
+ - id: DQ-02
+ name: Realistic Context
+ score: 5
+ max: 5
+ passed: true
+ comment: Character interactions in a story chapter. Realistic, neutral, comprehensible
+ scenario.
+ - id: DQ-03
+ name: Appropriate Scale
+ score: 4
+ max: 4
+ passed: true
+ comment: 10 nodes within spec range. 15 edges provide good density. Weights
+ 1-3 sensible.
+ code_quality:
+ score: 10
+ max: 10
+ items:
+ - id: CQ-01
+ name: KISS Structure
+ score: 3
+ max: 3
+ passed: true
+ comment: 'Linear flow: imports, data, style, chart config, legend, arc loop,
+ nodes, save. No functions or classes.'
+ - id: CQ-02
+ name: Reproducibility
+ score: 2
+ max: 2
+ passed: true
+ comment: np.random.seed(42) set. All data deterministic.
+ - id: CQ-03
+ name: Clean Imports
+ score: 2
+ max: 2
+ passed: true
+ comment: 'All imports used: math, numpy, pygal, Style.'
+ - id: CQ-04
+ name: Code Elegance
+ score: 2
+ max: 2
+ passed: true
+ comment: Clean arc generation loop. CSS-based chrome removal. Two-layer node
+ rendering. No over-engineering.
+ - id: CQ-05
+ name: Output & API
+ score: 1
+ max: 1
+ passed: true
+ comment: Saves plot.png via render_to_png(). Current pygal 3.1.0 API.
+ library_mastery:
+ score: 8
+ max: 10
+ items:
+ - id: LM-01
+ name: Idiomatic Usage
+ score: 4
+ max: 5
+ passed: true
+ comment: Good use of pygal.XY, Style class, chart.add() with stroke_style
+ per-series, x_labels with value/label dicts.
+ - id: LM-02
+ name: Distinctive Features
+ score: 4
+ max: 5
+ passed: true
+ comment: Inline CSS customization, nth-child legend hiding, label dicts for
+ tooltips, multi-format output, truncate_label=-1.