diff --git a/plots/arc-basic/implementations/highcharts.py b/plots/arc-basic/implementations/highcharts.py new file mode 100644 index 0000000000..45279f7332 --- /dev/null +++ b/plots/arc-basic/implementations/highcharts.py @@ -0,0 +1,237 @@ +""" pyplots.ai +arc-basic: Basic Arc Diagram +Library: highcharts 1.10.3 | Python 3.14.3 +Quality: 87/100 | Created: 2026-02-23 +""" + +import json +import tempfile +import time +import urllib.request +from pathlib import Path + +from PIL import Image +from selenium import webdriver +from selenium.webdriver.chrome.options import Options + + +# Data: Character interactions in a story chapter +nodes = ["Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace", "Henry", "Iris", "Jack"] + +# Edges: (source, target, weight) — dialogue exchange count +edges = [ + ("Alice", "Bob", 5), + ("Alice", "David", 3), + ("Bob", "Carol", 3), + ("Carol", "Eve", 2), + ("David", "Frank", 3), + ("Eve", "Grace", 1), + ("Alice", "Henry", 2), + ("Bob", "Frank", 3), + ("Carol", "David", 4), + ("Frank", "Iris", 1), + ("Grace", "Jack", 2), + ("Alice", "Jack", 1), + ("David", "Henry", 3), + ("Henry", "Iris", 2), + ("Iris", "Jack", 3), +] + +# Node connection counts for marker sizing +degree = dict.fromkeys(nodes, 0) +for src, tgt, _ in edges: + degree[src] += 1 + degree[tgt] += 1 + +# Colorblind-safe palette — Python Blue anchor with complementary tones +node_colors = { + "Alice": "#306998", + "Bob": "#E8A317", + "Carol": "#17BECF", + "David": "#9467BD", + "Eve": "#2CA02C", + "Frank": "#D4652F", + "Grace": "#8C564B", + "Henry": "#1F77B4", + "Iris": "#E377C2", + "Jack": "#5DA88A", +} + +# Node config with degree-scaled markers (3-4x default for 4800x2700) +nodes_data = [] +for name in nodes: + nodes_data.append( + { + "id": name, + "color": node_colors[name], + "marker": {"radius": 95 + degree[name] * 14, "lineWidth": 5, "lineColor": "#ffffff"}, + } + ) + +# Scale weights up for visual node sizing (arc diagram sizes nodes by total weight flow) +weight_scale = 14 +links_data = [{"from": src, "to": tgt, "weight": w * weight_scale} for src, tgt, w in edges] + +# Chart options (raw JS — highcharts_core doesn't support arcdiagram type) +chart_options = { + "chart": { + "width": 4800, + "height": 2700, + "backgroundColor": "#ffffff", + "marginTop": 150, + "marginBottom": 10, + "marginLeft": 200, + "marginRight": 200, + "spacingTop": 20, + "spacingBottom": 0, + }, + "title": { + "text": "arc-basic \u00b7 highcharts \u00b7 pyplots.ai", + "style": {"fontSize": "56px", "fontWeight": "bold", "color": "#333333"}, + "margin": 30, + }, + "subtitle": { + "text": "Character interactions — Dialogue exchanges between characters in a story chapter", + "style": {"fontSize": "36px", "color": "#666666"}, + }, + "accessibility": {"enabled": False}, + "tooltip": { + "style": {"fontSize": "32px"}, + "nodeFormat": "{point.name}: {point.sum} exchanges", + "pointFormat": "{point.fromNode.name} \u2192 {point.toNode.name}: {point.weight} exchanges", + }, + "series": [ + { + "type": "arcdiagram", + "name": "Interactions", + "keys": ["from", "to", "weight"], + "nodes": nodes_data, + "data": links_data, + "colorByPoint": True, + "centeredLinks": True, + "linkColorMode": "from", + "linkOpacity": 0.5, + "linkWeight": 18, + "equalNodes": False, + "nodeWidth": 110, + "minLinkWidth": 8, + "marker": {"radius": 110, "lineWidth": 5, "lineColor": "#ffffff"}, + "dataLabels": [ + { + "enabled": True, + "rotation": 0, + "y": 80, + "align": "center", + "style": { + "fontSize": "48px", + "fontWeight": "bold", + "textOutline": "3px #ffffff", + "color": "#333333", + }, + } + ], + } + ], + "legend": {"enabled": False}, + "credits": {"enabled": False}, +} + +options_json = json.dumps(chart_options) + +# Download Highcharts JS, sankey module (dependency), and arc-diagram module +cache_dir = Path("/tmp") +urls = { + "highcharts": ("https://cdn.jsdelivr.net/npm/highcharts@11.4.8/highcharts.js", cache_dir / "highcharts.js"), + "sankey": ("https://cdn.jsdelivr.net/npm/highcharts@11.4.8/modules/sankey.js", cache_dir / "hc_sankey.js"), + "arcdiagram": ( + "https://cdn.jsdelivr.net/npm/highcharts@11.4.8/modules/arc-diagram.js", + cache_dir / "hc_arc_diagram.js", + ), +} +js_scripts = {} +for name, (url, cache_path) in urls.items(): + if cache_path.exists() and cache_path.stat().st_size > 1000: + js_scripts[name] = cache_path.read_text(encoding="utf-8") + else: + for attempt in range(5): + try: + with urllib.request.urlopen(url, timeout=30) as resp: + content = resp.read().decode("utf-8") + cache_path.write_text(content, encoding="utf-8") + js_scripts[name] = content + break + except urllib.error.HTTPError: + time.sleep(3 * (attempt + 1)) +highcharts_js = js_scripts["highcharts"] +sankey_js = js_scripts["sankey"] +arcdiagram_js = js_scripts["arcdiagram"] + +# Chart init JS: use formatter function to show only node names (no DOM manipulation) +chart_init_js = f""" +(function() {{ + var opts = {options_json}; + opts.series[0].dataLabels[0].formatter = function() {{ + return this.point.isNode ? this.point.name : ''; + }}; + Highcharts.chart('container', opts); +}})(); +""" + +# Build HTML with inline JS (no post-render DOM manipulation) +html_content = f""" + + + + + + + + +
+ + +""" + +# Save interactive HTML version (CDN links for standalone use) +standalone_html = f""" + + + + + + + + +
+ + +""" + +with open("plot.html", "w", encoding="utf-8") as f: + f.write(standalone_html) + +# Write temp HTML and take screenshot +with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f: + f.write(html_content) + temp_path = f.name + +chrome_options = Options() +chrome_options.add_argument("--headless") +chrome_options.add_argument("--no-sandbox") +chrome_options.add_argument("--disable-dev-shm-usage") +chrome_options.add_argument("--disable-gpu") +chrome_options.add_argument("--window-size=4800,2900") + +driver = webdriver.Chrome(options=chrome_options) +driver.get(f"file://{temp_path}") +time.sleep(5) +driver.save_screenshot("plot_raw.png") +driver.quit() + +# Crop to exact 4800x2700 dimensions +img = Image.open("plot_raw.png") +img_cropped = img.crop((0, 0, 4800, 2700)) +img_cropped.save("plot.png") +Path("plot_raw.png").unlink() + +Path(temp_path).unlink() diff --git a/plots/arc-basic/metadata/highcharts.yaml b/plots/arc-basic/metadata/highcharts.yaml new file mode 100644 index 0000000000..ce640edb8b --- /dev/null +++ b/plots/arc-basic/metadata/highcharts.yaml @@ -0,0 +1,239 @@ +library: highcharts +specification_id: arc-basic +created: '2026-02-23T12:00:00+00:00' +updated: '2026-02-23T22:16:27Z' +generated_by: claude-opus-4-6 +workflow_run: null +issue: 991 +python_version: 3.14.3 +library_version: 1.10.3 +preview_url: https://storage.googleapis.com/pyplots-images/plots/arc-basic/highcharts/plot.png +preview_thumb: https://storage.googleapis.com/pyplots-images/plots/arc-basic/highcharts/plot_thumb.png +preview_html: https://storage.googleapis.com/pyplots-images/plots/arc-basic/highcharts/plot.html +quality_score: 87 +impl_tags: + dependencies: + - selenium + - pillow + techniques: + - html-export + - hover-tooltips + patterns: + - data-generation + - iteration-over-groups + dataprep: [] + styling: + - alpha-blending + - edge-highlighting +review: + strengths: + - Correct use of Highcharts native arcdiagram module with proper sankey dependency + - Custom colorblind-safe palette with degree-based node sizing creates clear visual + hierarchy + - Semi-transparent arcs with source-colored links effectively show connection patterns + - Clean deterministic data representing a comprehensible character interaction scenario + - Both PNG and interactive HTML output for maximum utility + weaknesses: + - Title and subtitle text could be scaled slightly larger for the 4800x2700 canvas + - Two blue-toned nodes (Alice and Henry) could be more distinct in the palette + - Some bottom whitespace below node labels is unused + image_description: 'The plot displays a basic arc diagram with 10 character nodes + (Alice, Bob, Carol, David, Eve, Frank, Grace, Henry, Iris, Jack) arranged along + a horizontal line near the bottom of the canvas. Each node is rendered as a colored + circle with white borders, sized proportionally to connection degree — Alice is + the largest as the most connected character. Arcs curve above the horizontal line + connecting character pairs, with arc height proportional to the distance between + connected nodes. Long-range connections (e.g., Alice→Jack, Alice→Henry) form tall + arcs spanning the full width, while short-range connections (e.g., Carol→David, + Eve→Grace) form smaller arcs. Arcs are semi-transparent (50% opacity) with colors + inherited from the source node. The title "arc-basic · highcharts · pyplots.ai" + appears at the top in bold dark text, with a subtitle "Character interactions + — Dialogue exchanges between characters in a story chapter" below it. The background + is white, and the layout is clean with generous margins. Colors used: Alice (dark + blue), Bob (golden yellow), Carol (cyan), David (purple), Eve (green), Frank (orange), + Grace (brown), Henry (blue), Iris (pink), Jack (teal).' + criteria_checklist: + visual_quality: + score: 26 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: 'All font sizes explicitly set (title 56px, subtitle 36px, data labels + 48px). Clear and readable. Minor: title could be slightly larger for 4800px + canvas.' + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No overlapping text or elements. Node labels well-spaced along horizontal + axis. + - id: VQ-03 + name: Element Visibility + score: 5 + max: 6 + passed: true + comment: Nodes clearly visible with distinct colors and white borders. Some + thinner arcs slightly less prominent but still discernible. + - id: VQ-04 + name: Color Accessibility + score: 3 + max: 4 + passed: true + comment: 'Custom colorblind-safe palette. Minor: Alice and Henry are both + blue tones, mitigated by direct labeling.' + - id: VQ-05 + name: Layout & Canvas + score: 3 + max: 4 + passed: true + comment: Good horizontal spread with 200px margins. Arcs fill upper portion + well. Some unused bottom whitespace. + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Correct title format. Subtitle provides context. Descriptive node + labels. No axes needed for arc diagrams. + design_excellence: + score: 14 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: Custom color palette, semi-transparent arcs with source-colored links, + degree-based node sizing, white borders. Above library defaults. + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Clean layout with disabled legend/credits, no clutter, good whitespace. + Could benefit from typography refinement. + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: Node sizing by degree shows Alice as most connected. Arc height reveals + long vs short range. Link color from source aids tracing. + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct arc diagram with nodes on horizontal line and curved arcs + above. + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: 'All spec features: proportional arc height, semi-transparent arcs, + readable labels, color-coded edges, weight-based rendering.' + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: Nodes correctly ordered. Edges correctly connect pairs. Weights properly + mapped. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title in correct format. Legend appropriately disabled since nodes + are directly labeled. + data_quality: + score: 14 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 5 + max: 6 + passed: true + comment: 10 nodes with varying connectivity, mix of short and long range connections, + weights 1-5. Could include isolated node. + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Character interactions in a story chapter. Neutral, comprehensible, + real-world scenario. + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 10 nodes, 15 edges appropriate for readability. Weights 1-5 sensible + for dialogue counts. + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: 'Linear flow: imports, data, chart config, render, save. No functions + or classes.' + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Fully deterministic with hardcoded data. + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: All imports used. + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean, well-organized. Pragmatic raw JS approach for unsupported + arcdiagram type. + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves plot.png and plot.html. Uses current Highcharts 11.4.8. + library_mastery: + score: 8 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Uses native arcdiagram series with correct sankey dependency. Raw + JS due to highcharts_core limitation. + - id: LM-02 + name: Distinctive Features + score: 4 + max: 5 + passed: true + comment: Leverages Highcharts-specific arcdiagram module with centeredLinks, + linkColorMode, linkOpacity, equalNodes options. + verdict: REJECTED