diff --git a/plots/scatter-text/implementations/python/highcharts.py b/plots/scatter-text/implementations/python/highcharts.py index c56137abe6..d333cb9355 100644 --- a/plots/scatter-text/implementations/python/highcharts.py +++ b/plots/scatter-text/implementations/python/highcharts.py @@ -1,9 +1,11 @@ -""" pyplots.ai +""" anyplot.ai scatter-text: Scatter Plot with Text Labels Instead of Points -Library: highcharts unknown | Python 3.13.11 -Quality: 91/100 | Created: 2026-01-09 +Library: highcharts unknown | Python 3.13.13 +Quality: 94/100 | Updated: 2026-05-17 """ +import os +import ssl import tempfile import time import urllib.request @@ -17,35 +19,46 @@ from selenium.webdriver.chrome.options import Options +# Theme tokens +THEME = os.getenv("ANYPLOT_THEME", "light") +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" +GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)" + +# Okabe-Ito palette +OKABE_ITO = [ + "#009E73", # position 1 (brand green - ALWAYS first series) + "#D55E00", # position 2 + "#0072B2", # position 3 + "#CC79A7", # position 4 +] + # Data - Simulated word embeddings after dimensionality reduction np.random.seed(42) -# Create clusters of related words in 2D space programming_words = ["Python", "JavaScript", "Java", "C++", "Ruby", "Rust", "Go", "Swift"] data_words = ["Pandas", "NumPy", "TensorFlow", "PyTorch", "Scikit-learn", "Keras"] web_words = ["React", "Vue", "Angular", "Django", "Flask", "Node.js"] database_words = ["PostgreSQL", "MongoDB", "Redis", "MySQL", "SQLite", "Cassandra"] -# Create clustered positions - more compact to avoid clipping +# Create clustered positions x_coords = [] y_coords = [] -# Programming languages cluster (center-left) for _ in programming_words: x_coords.append(np.random.normal(-2.5, 0.7)) y_coords.append(np.random.normal(2, 0.7)) -# Data science tools cluster (top-right) for _ in data_words: x_coords.append(np.random.normal(2.5, 0.6)) y_coords.append(np.random.normal(2.5, 0.6)) -# Web frameworks cluster (bottom-right) for _ in web_words: x_coords.append(np.random.normal(2, 0.5)) y_coords.append(np.random.normal(-2, 0.5)) -# Databases cluster (bottom-left) for _ in database_words: x_coords.append(np.random.normal(-2, 0.6)) y_coords.append(np.random.normal(-2, 0.6)) @@ -55,90 +68,79 @@ # Download Highcharts JS highcharts_url = "https://code.highcharts.com/highcharts.js" -with urllib.request.urlopen(highcharts_url, timeout=30) as response: +ssl_context = ssl._create_unverified_context() +request = urllib.request.Request(highcharts_url, headers={"User-Agent": "Mozilla/5.0", "Referer": "https://anyplot.ai"}) +with urllib.request.urlopen(request, timeout=30, context=ssl_context) as response: highcharts_js = response.read().decode("utf-8") -# Create chart with container +# Create chart chart = Chart(container="container") chart.options = HighchartsOptions() -# Chart configuration with margins to prevent clipping +# Chart configuration chart.options.chart = { "type": "scatter", "width": 4800, "height": 2700, - "backgroundColor": "#ffffff", + "backgroundColor": PAGE_BG, "marginLeft": 250, "marginRight": 300, "marginTop": 200, "marginBottom": 300, } -# Title - large font for 4800px canvas +# Title chart.options.title = { - "text": "scatter-text · highcharts · pyplots.ai", - "style": {"fontSize": "72px", "fontWeight": "bold", "color": "#333333"}, -} - -# Subtitle -chart.options.subtitle = { - "text": "Tech Stack Word Embeddings (2D Projection)", - "style": {"fontSize": "48px", "color": "#666666"}, + "text": "scatter-text · Python · highcharts · anyplot.ai", + "style": {"fontSize": "28px", "fontWeight": "500", "color": INK}, } -# Axes - scaled for large canvas +# Axes chart.options.x_axis = { - "title": {"text": "Dimension 1", "style": {"fontSize": "48px", "color": "#333333"}}, - "labels": {"style": {"fontSize": "36px", "color": "#333333"}}, + "title": {"text": "Dimension 1", "style": {"fontSize": "22px", "color": INK}}, + "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}}, "gridLineWidth": 1, - "gridLineColor": "rgba(0,0,0,0.15)", - "lineWidth": 2, - "lineColor": "#333333", - "tickWidth": 2, - "tickLength": 15, + "gridLineColor": GRID, + "lineColor": INK_SOFT, + "tickColor": INK_SOFT, } chart.options.y_axis = { - "title": {"text": "Dimension 2", "style": {"fontSize": "48px", "color": "#333333"}}, - "labels": {"style": {"fontSize": "36px", "color": "#333333"}}, + "title": {"text": "Dimension 2", "style": {"fontSize": "22px", "color": INK}}, + "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}}, "gridLineWidth": 1, - "gridLineColor": "rgba(0,0,0,0.15)", - "lineWidth": 2, - "lineColor": "#333333", - "tickWidth": 2, - "tickLength": 15, + "gridLineColor": GRID, + "lineColor": INK_SOFT, + "tickColor": INK_SOFT, } -# Legend - show categories at top right to not interfere with data +# Legend chart.options.legend = { "enabled": True, - "itemStyle": {"fontSize": "36px", "fontWeight": "normal"}, + "itemStyle": {"fontSize": "18px", "color": INK_SOFT}, "layout": "vertical", "align": "right", "verticalAlign": "top", "x": -50, "y": 100, "borderWidth": 1, - "borderColor": "#cccccc", - "backgroundColor": "#ffffff", + "borderColor": INK_SOFT, + "backgroundColor": ELEVATED_BG, "padding": 20, } -# Create data points with text labels using dataLabels -# Group by category for different colors +# Create data with text labels categories = { - "Programming Languages": (programming_words, "#306998", 0), - "Data Science": (data_words, "#FFD43B", len(programming_words)), - "Web Frameworks": (web_words, "#9467BD", len(programming_words) + len(data_words)), - "Databases": (database_words, "#17BECF", len(programming_words) + len(data_words) + len(web_words)), + "Programming Languages": (programming_words, OKABE_ITO[0], 0), + "Data Science": (data_words, OKABE_ITO[1], len(programming_words)), + "Web Frameworks": (web_words, OKABE_ITO[2], len(programming_words) + len(data_words)), + "Databases": (database_words, OKABE_ITO[3], len(programming_words) + len(data_words) + len(web_words)), } -# Add series for each category for cat_name, (words, color, start_idx) in categories.items(): series = ScatterSeries() series.name = cat_name - # Create data points with labels - large fonts for 4800px canvas data_points = [] for i, word in enumerate(words): idx = start_idx + i @@ -150,24 +152,21 @@ "dataLabels": { "enabled": True, "format": word, - "style": {"fontSize": "42px", "fontWeight": "bold", "color": color, "textOutline": "3px white"}, - "allowOverlap": True, + "style": {"fontSize": "24px", "fontWeight": "500", "color": color, "textOutline": f"2px {PAGE_BG}"}, + "allowOverlap": False, }, } ) series.data = data_points - - # Hide markers - text labels are the visual elements series.marker = {"enabled": False} - series.color = color chart.add_series(series) -# Plot options for data labels +# Plot options chart.options.plot_options = { - "scatter": {"marker": {"enabled": False}, "dataLabels": {"enabled": True, "style": {"fontSize": "42px"}}} + "scatter": {"marker": {"enabled": False}, "dataLabels": {"enabled": True, "style": {"fontSize": "24px"}}} } # Generate HTML with inline scripts @@ -178,13 +177,17 @@ - +
""" -# Write temp HTML and capture screenshot +# Save HTML artifact +with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f: + f.write(html_content) + +# 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 @@ -199,23 +202,7 @@ driver = webdriver.Chrome(options=chrome_options) driver.get(f"file://{temp_path}") time.sleep(5) -driver.save_screenshot("plot.png") +driver.save_screenshot(f"plot-{THEME}.png") driver.quit() Path(temp_path).unlink() - -# Save HTML for interactive version -with open("plot.html", "w", encoding="utf-8") as f: - # Use CDN for HTML file (works in browsers) - html_interactive = f""" - - - - - - -
- - -""" - f.write(html_interactive) diff --git a/plots/scatter-text/metadata/python/highcharts.yaml b/plots/scatter-text/metadata/python/highcharts.yaml index a540896980..4bc17446fd 100644 --- a/plots/scatter-text/metadata/python/highcharts.yaml +++ b/plots/scatter-text/metadata/python/highcharts.yaml @@ -1,152 +1,168 @@ library: highcharts +language: python specification_id: scatter-text created: '2026-01-09T14:28:29Z' -updated: '2026-01-09T14:31:34Z' -generated_by: claude-opus-4-5-20251101 -workflow_run: 20854885714 +updated: '2026-05-17T23:43:46Z' +generated_by: claude-haiku +workflow_run: 26005947992 issue: 3482 -python_version: 3.13.11 +language_version: 3.13.13 library_version: unknown -preview_url: https://storage.googleapis.com/anyplot-images/plots/scatter-text/highcharts/plot.png -preview_html: https://storage.googleapis.com/anyplot-images/plots/scatter-text/highcharts/plot.html -quality_score: 91 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/scatter-text/python/highcharts/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/scatter-text/python/highcharts/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/scatter-text/python/highcharts/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/scatter-text/python/highcharts/plot-dark.html +quality_score: 94 review: strengths: - - Excellent implementation of text-as-markers concept using Highcharts dataLabels - - Well-organized code with clear clustering of related tech terms - - Colorblind-safe palette with good visual distinction between categories - - Proper use of textOutline for text readability on white background - - Good canvas utilization and margins prevent clipping - - Realistic and engaging tech stack embedding scenario + - 'Flawless theme adaptation: data colors stay identical, chrome correctly swaps + between light/dark tokens' + - Text outline styling (2px PAGE_BG) elegantly solves overlap in dense regions while + maintaining readability + - Professional color palette and clear semantic clustering with meaningful domain + groupings + - 'Idiomatic Highcharts usage: ScatterSeries, dataLabels, and marker disabling correctly + implemented' + - Clean, reproducible data generation with meaningful category structure weaknesses: - - Minor text overlap in Data Science cluster (Keras/Pandas/Scikit-learn region) - - Axis labels could include context (e.g., Dimension 1 (UMAP component)) - - Could leverage more Highcharts interactive features (zooming, click events) - image_description: 'The plot displays a scatter visualization where text labels - serve as data points instead of markers, positioned in a 2D space representing - tech stack word embeddings. The title "scatter-text · highcharts · pyplots.ai" - appears at the top in bold black text with a gray subtitle "Tech Stack Word Embeddings - (2D Projection)". Four distinct clusters are visible: Programming Languages (dark - blue/navy - Python, JavaScript, Java, C++, Ruby, Rust, Go, Swift) in the upper-left; - Data Science tools (yellow/gold - Pandas, NumPy, TensorFlow, PyTorch, Scikit-learn, - Keras) in the upper-right; Web Frameworks (purple - React, Vue, Angular, Django, - Flask, Node.js) in the lower-right; and Databases (cyan - PostgreSQL, MongoDB, - Redis, MySQL, SQLite, Cassandra) in the lower-left. A bordered legend in the top-right - identifies all four categories. Axes are labeled "Dimension 1" (x) and "Dimension - 2" (y) with subtle gray grid lines. Minor text overlap occurs in the Data Science - cluster where "Keras", "Pandas", and "Scikit-learn" are close together.' + - Minor text overlap in lower-left cluster (MongoDB, Cassandra, Redis) acceptable + but could be reduced with label rotation or smaller font + image_description: |- + Light render (plot-light.png): + Background: Warm off-white (#FAF8F1) — correct light theme surface + Chrome: Title "scatter-text · Python · highcharts · anyplot.ai" in dark text (INK), axis labels "Dimension 1"/"Dimension 2" in INK, tick labels in INK_SOFT — all clearly readable against light background + Data: Four categories in Okabe-Ito order — Programming Languages #009E73 (upper-left), Data Science #D55E00 (upper-right), Web Frameworks #0072B2 (lower-right), Databases #CC79A7 (lower). All labels rendered at 24px with 2px white outline for clarity. No unreadable elements. + Legend: Right-aligned with category names and color indicators + Legibility verdict: PASS — all text clearly visible, no light-on-light failures + + Dark render (plot-dark.png): + Background: Warm near-black (#1A1A17) — correct dark theme surface + Chrome: Title and axis labels now in light text (INK #F0EFE8), tick labels in INK_SOFT #B8B7B0, legend background in ELEVATED_BG #242420 — all clearly readable against dark background + Data: All four category colors identical to light render (#009E73, #D55E00, #0072B2, #CC79A7) — confirming theme-correct color preservation (only chrome changes) + Grid: Subtle lines visible with ~10% opacity + Legibility verdict: PASS — all text clearly visible, no dark-on-dark failures, no legibility issues criteria_checklist: visual_quality: - score: 36 - max: 40 + score: 29 + max: 30 items: - id: VQ-01 name: Text Legibility - score: 9 - max: 10 + score: 8 + max: 8 passed: true - comment: All text readable with good font sizes, slightly deducted for minor - crowding in Data Science cluster + comment: All text at appropriate sizes; readable in both light and dark themes - id: VQ-02 name: No Overlap - score: 6 - max: 8 + score: 5 + max: 6 passed: true - comment: Minor overlap in Data Science cluster (Keras/Pandas area) + comment: Minor overlap in dense regions mitigated by text outline; still readable - id: VQ-03 name: Element Visibility - score: 8 - max: 8 + score: 6 + max: 6 passed: true - comment: Text labels are perfectly sized and visible as data elements + comment: All text labels clearly distinguishable from background - id: VQ-04 name: Color Accessibility - score: 5 - max: 5 + score: 2 + max: 2 passed: true - comment: Colorblind-safe palette (blue, yellow, purple, cyan) + comment: Okabe-Ito palette is CVD-safe; strong contrast - id: VQ-05 - name: Layout Balance - score: 5 - max: 5 + name: Layout & Canvas + score: 4 + max: 4 passed: true - comment: Excellent canvas utilization with good margins + comment: Proper proportions; generous margins; nothing cut off - id: VQ-06 - name: Axis Labels - score: 1 + name: Axis Labels & Title + score: 2 max: 2 passed: true - comment: Descriptive labels but no units + comment: Descriptive axis labels; complete title - id: VQ-07 - name: Grid & Legend + name: Palette Compliance score: 2 max: 2 passed: true - comment: Subtle grid, well-placed legend with border + comment: 'First series #009E73; Okabe-Ito order; correct backgrounds; colors + identical between themes' + design_excellence: + score: 16 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: Professional Okabe-Ito usage; intentional text outline; clear hierarchy + - id: DE-02 + name: Visual Refinement + score: 5 + max: 6 + passed: true + comment: Clean grid styling; generous whitespace; polished legend + - id: DE-03 + name: Data Storytelling + score: 5 + max: 6 + passed: true + comment: Clear semantic clustering; spatial grouping creates visual narrative spec_compliance: - score: 25 - max: 25 + score: 15 + max: 15 items: - id: SC-01 name: Plot Type - score: 8 - max: 8 - passed: true - comment: Correct scatter-text plot with text labels as visual elements - - id: SC-02 - name: Data Mapping score: 5 max: 5 passed: true - comment: X/Y coordinates correctly map text positions - - id: SC-03 + comment: Scatter plot with text labels correctly implemented + - id: SC-02 name: Required Features - score: 5 - max: 5 + score: 4 + max: 4 passed: true - comment: Text labels visible, color encodes categorical info - - id: SC-04 - name: Data Range + comment: All spec features present + - id: SC-03 + name: Data Mapping score: 3 max: 3 passed: true - comment: Axes show all data with appropriate padding - - id: SC-05 - name: Legend Accuracy - score: 2 - max: 2 - passed: true - comment: Legend correctly identifies all four categories - - id: SC-06 - name: Title Format - score: 2 - max: 2 + comment: X/Y axes correctly mapped; all data visible + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 passed: true - comment: Uses correct format {spec-id} · {library} · pyplots.ai + comment: Title format correct; legend labels match data_quality: - score: 18 - max: 20 + score: 15 + max: 15 items: - id: DQ-01 name: Feature Coverage - score: 7 - max: 8 + score: 6 + max: 6 passed: true - comment: Shows multiple clusters with different densities + comment: Complete scatter-text visualization with realistic clustering - id: DQ-02 name: Realistic Context - score: 7 - max: 7 + score: 5 + max: 5 passed: true - comment: Tech stack word embeddings is realistic and neutral + comment: Simulated word embeddings; neutral, plausible tech domain - id: DQ-03 name: Appropriate Scale score: 4 - max: 5 + max: 4 passed: true - comment: Sensible values for dimensionality reduction output + comment: Embedding-appropriate dimensions; balanced density code_quality: - score: 9 + score: 10 max: 10 items: - id: CQ-01 @@ -154,53 +170,57 @@ review: score: 3 max: 3 passed: true - comment: 'Linear flow: imports → data → chart config → export' + comment: No functions or classes; direct script - id: CQ-02 name: Reproducibility - score: 3 - max: 3 + score: 2 + max: 2 passed: true - comment: Uses np.random.seed(42) + comment: Seed set; deterministic output - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: All imports are used + comment: All imports used; no redundant - id: CQ-04 - name: No Deprecated API - score: 1 - max: 1 + name: Code Elegance + score: 2 + max: 2 passed: true - comment: Uses current highcharts-core API + comment: Appropriate complexity; clean category structure - id: CQ-05 - name: Output Correct - score: 0 + name: Output & API + score: 1 max: 1 - passed: false - comment: Creates plot.html in addition to plot.png (minor issue) - library_features: - score: 3 - max: 5 + passed: true + comment: Saves as plot-{THEME}.png and .html + library_mastery: + score: 9 + max: 10 items: - - id: LF-01 + - id: LM-01 + name: Idiomatic Usage + score: 5 + max: 5 + passed: true + comment: High-level Chart API; correct ScatterSeries pattern + - id: LM-02 name: Distinctive Features - score: 3 + score: 4 max: 5 passed: true - comment: Good use of dataLabels for text positioning, but no advanced interactive - features + comment: Leverages dataLabels with textOutline; series.color for categorical + coloring verdict: APPROVED impl_tags: dependencies: - selenium techniques: - html-export - - annotations patterns: - data-generation - iteration-over-groups dataprep: [] styling: - - alpha-blending - - edge-highlighting + - publication-ready