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.