Skip to content

Commit 079fdbe

Browse files
feat(highcharts): implement rug-basic (#5591)
## Implementation: `rug-basic` - python/highcharts Implements the **python/highcharts** version of `rug-basic`. **File:** `plots/rug-basic/implementations/python/highcharts.py` **Parent Issue:** #978 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/25148823253)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent 10b29b8 commit 079fdbe

2 files changed

Lines changed: 218 additions & 202 deletions

File tree

Lines changed: 39 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
rug-basic: Basic Rug Plot
3-
Library: highcharts unknown | Python 3.13.11
4-
Quality: 92/100 | Created: 2025-12-23
3+
Library: highcharts unknown | Python 3.13.13
4+
Quality: 89/100 | Updated: 2026-04-30
55
"""
66

7+
import os
78
import tempfile
89
import time
910
import urllib.request
@@ -17,99 +18,89 @@
1718
from selenium.webdriver.chrome.options import Options
1819

1920

20-
# Data - response times (ms) with realistic clustering and gaps
21+
# Theme tokens
22+
THEME = os.getenv("ANYPLOT_THEME", "light")
23+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
24+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
25+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
26+
TICK_COLOR = "rgba(0, 158, 115, 0.7)" # #009E73 (Okabe-Ito pos 1) with alpha
27+
28+
# Data
2129
np.random.seed(42)
2230
values = np.concatenate(
2331
[
2432
np.random.normal(50, 8, 40), # Fast responses cluster
2533
np.random.normal(120, 15, 35), # Medium responses cluster
2634
np.random.normal(250, 20, 15), # Slow responses cluster
27-
np.array([380, 420, 510]), # Outliers (occasional slow requests)
35+
np.array([380, 420, 510]), # Outliers
2836
]
2937
)
3038
values = np.clip(values, 10, 600)
3139

32-
# Create chart
40+
# Chart
3341
chart = Chart(container="container")
3442
chart.options = HighchartsOptions()
3543

36-
# Chart configuration - optimize margins for tight layout
3744
chart.options.chart = {
3845
"width": 4800,
3946
"height": 2700,
40-
"backgroundColor": "#ffffff",
47+
"backgroundColor": PAGE_BG,
4148
"marginBottom": 200,
42-
"marginTop": 250, # More top margin to balance with title/subtitle
49+
"marginTop": 250,
4350
"marginLeft": 150,
4451
"marginRight": 150,
4552
}
4653

47-
# Title
4854
chart.options.title = {
49-
"text": "rug-basic · highcharts · pyplots.ai",
50-
"style": {"fontSize": "72px", "fontWeight": "bold"},
55+
"text": "rug-basic · highcharts · anyplot.ai",
56+
"style": {"fontSize": "72px", "fontWeight": "bold", "color": INK},
5157
}
5258

53-
# Subtitle
54-
chart.options.subtitle = {"text": "API Response Times (ms)", "style": {"fontSize": "48px"}}
59+
chart.options.subtitle = {"text": "API Response Times (ms)", "style": {"fontSize": "48px", "color": INK_SOFT}}
5560

56-
# X-axis - continuous scale for response times
57-
# Removed grid lines per feedback - reduces visual clutter for rug plot
5861
chart.options.x_axis = {
59-
"title": {"text": "Response Time (ms)", "style": {"fontSize": "48px"}},
60-
"labels": {"style": {"fontSize": "36px"}},
61-
"gridLineWidth": 0, # No grid lines - cleaner look for rug plot
62+
"title": {"text": "Response Time (ms)", "style": {"fontSize": "48px", "color": INK}},
63+
"labels": {"style": {"fontSize": "36px", "color": INK_SOFT}},
64+
"gridLineWidth": 0,
6265
"min": 0,
6366
"max": 600,
6467
"tickInterval": 50,
6568
"lineWidth": 3,
66-
"lineColor": "#333333",
69+
"lineColor": INK_SOFT,
70+
"tickColor": INK_SOFT,
6771
}
6872

69-
# Y-axis - tight range to minimize whitespace (key fix!)
70-
# Max set to 1.2 to leave minimal space above tick marks
7173
chart.options.y_axis = {
7274
"title": {"text": None},
7375
"labels": {"enabled": False},
7476
"gridLineWidth": 0,
7577
"min": 0,
76-
"max": 1.2, # Tight! Rug marks go to y=1, minimal whitespace above
78+
"max": 1.2,
7779
"visible": False,
78-
"plotLines": [{"value": 0, "width": 3, "color": "#333333", "zIndex": 2}], # Baseline
80+
"plotLines": [{"value": 0, "width": 3, "color": INK_SOFT, "zIndex": 2}],
7981
}
8082

81-
# Legend and credits
8283
chart.options.legend = {"enabled": False}
8384
chart.options.credits = {"enabled": False}
84-
85-
# Tooltip disabled for cleaner look
8685
chart.options.tooltip = {"enabled": False}
8786

88-
# Add rug ticks as a single LineSeries with multiple line segments
89-
# Use LineSeries with data containing multiple segments encoded as breaks
90-
# Highcharts approach: Each rug tick is a very short vertical line
91-
# We use one LineSeries per tick, but this is unavoidable in Highcharts
92-
# without columnrange/dumbbell extensions
93-
94-
# Create all rug ticks - vertical lines from y=0 to y=1
87+
# Rug ticks: short vertical marks at axis edge (0 to 0.15 = 12.5% of y-range)
9588
for v in sorted(values):
9689
tick_series = LineSeries()
97-
# Vertical line from baseline to tick height (fills most of vertical space now)
98-
tick_series.data = [[float(v), 0], [float(v), 1]]
99-
tick_series.color = "rgba(48, 105, 152, 0.6)" # Python Blue with transparency
100-
tick_series.line_width = 5 # Visible but thin ticks
90+
tick_series.data = [[float(v), 0], [float(v), 0.15]]
91+
tick_series.color = TICK_COLOR
92+
tick_series.line_width = 7
10193
tick_series.marker = {"enabled": False}
10294
tick_series.enable_mouse_tracking = False
10395
tick_series.states = {"hover": {"enabled": False}}
10496
tick_series.show_in_legend = False
10597
chart.add_series(tick_series)
10698

107-
# Download Highcharts JS (required for headless Chrome)
108-
highcharts_url = "https://code.highcharts.com/highcharts.js"
99+
# Download Highcharts JS (required for headless Chrome — CDN blocked on file://)
100+
highcharts_url = "https://cdn.jsdelivr.net/npm/highcharts@latest/highcharts.js"
109101
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
110102
highcharts_js = response.read().decode("utf-8")
111103

112-
# Generate HTML with inline scripts
113104
html_str = chart.to_js_literal()
114105
html_content = f"""<!DOCTYPE html>
115106
<html>
@@ -118,7 +109,7 @@
118109
<script>{highcharts_js}</script>
119110
<style>
120111
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
121-
html, body {{ width: 4800px; height: 2700px; overflow: hidden; }}
112+
html, body {{ width: 4800px; height: 2700px; overflow: hidden; background: {PAGE_BG}; }}
122113
#container {{ width: 4800px; height: 2700px; }}
123114
</style>
124115
</head>
@@ -128,7 +119,11 @@
128119
</body>
129120
</html>"""
130121

131-
# Write temp HTML and take screenshot
122+
# Save HTML artifact
123+
with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f:
124+
f.write(html_content)
125+
126+
# Screenshot via headless Chrome
132127
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
133128
f.write(html_content)
134129
temp_path = f.name
@@ -144,29 +139,8 @@
144139
driver.get(f"file://{temp_path}")
145140
time.sleep(5)
146141

147-
# Take screenshot of the container element for exact dimensions
148142
container = driver.find_element("id", "container")
149-
container.screenshot("plot.png")
143+
container.screenshot(f"plot-{THEME}.png")
150144
driver.quit()
151145

152146
Path(temp_path).unlink()
153-
154-
# Also save HTML for interactive version
155-
with open("plot.html", "w", encoding="utf-8") as f:
156-
interactive_html = f"""<!DOCTYPE html>
157-
<html>
158-
<head>
159-
<meta charset="utf-8">
160-
<script src="https://code.highcharts.com/highcharts.js"></script>
161-
<style>
162-
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
163-
html, body {{ width: 100%; height: 100vh; overflow: hidden; }}
164-
#container {{ width: 100%; height: 100%; }}
165-
</style>
166-
</head>
167-
<body>
168-
<div id="container"></div>
169-
<script>{html_str}</script>
170-
</body>
171-
</html>"""
172-
f.write(interactive_html)

0 commit comments

Comments
 (0)