Skip to content

Commit 94ee94c

Browse files
Merge branch 'main' into implementation/rose-basic/altair
2 parents 773605d + 3ee48f1 commit 94ee94c

14 files changed

Lines changed: 1552 additions & 1060 deletions

File tree

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,60 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
rose-basic: Basic Rose Chart
3-
Library: bokeh 3.8.1 | Python 3.13.11
4-
Quality: 92/100 | Created: 2025-12-23
3+
Library: bokeh 3.9.0 | Python 3.13.13
4+
Quality: 84/100 | Updated: 2026-04-30
55
"""
66

7+
import os
8+
import sys
9+
10+
11+
# Remove this script's directory from sys.path to prevent bokeh.py from
12+
# shadowing the installed bokeh package when Python adds the script dir.
13+
_script_dir = os.path.dirname(os.path.abspath(__file__))
14+
sys.path = [p for p in sys.path if os.path.abspath(p or ".") != _script_dir]
15+
716
import numpy as np
817
from bokeh.io import export_png, output_file, save
918
from bokeh.models import ColumnDataSource
1019
from bokeh.plotting import figure
1120

1221

22+
# Theme tokens
23+
THEME = os.getenv("ANYPLOT_THEME", "light")
24+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
25+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
26+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
27+
BRAND = "#009E73"
28+
1329
# Data - Monthly rainfall (mm) showing seasonal patterns
14-
np.random.seed(42)
1530
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
1631
values = [85, 70, 65, 45, 30, 20, 15, 25, 40, 60, 75, 90]
17-
32+
max_val = max(values)
1833
n = len(months)
34+
angle_width = 2 * np.pi / n
1935

2036
# Calculate wedge angles (equal slices, starting from top/north)
21-
angle_width = 2 * np.pi / n
2237
start_angles = np.array([np.pi / 2 - angle_width / 2 - i * angle_width for i in range(n)])
2338
end_angles = start_angles - angle_width
39+
center_angles = (start_angles + end_angles) / 2
2440

25-
# Normalize values to radius (max value = 1.0 for full radius)
26-
max_val = max(values)
41+
# Normalize values to radius (max value = 1.0)
2742
radii = [v / max_val for v in values]
2843

29-
# Colors - gradient from Python Yellow to Python Blue based on value
30-
colors = ["#306998" if v >= 60 else "#4A89B8" if v >= 40 else "#7AB3D8" if v >= 25 else "#FFD43B" for v in values]
44+
# Brand green with alpha varying by rainfall intensity
45+
alphas = [0.35 + 0.65 * (v / max_val) for v in values]
3146

3247
source = ColumnDataSource(
33-
data={
34-
"start_angle": start_angles,
35-
"end_angle": end_angles,
36-
"radius": radii,
37-
"months": months,
38-
"values": values,
39-
"colors": colors,
40-
}
48+
data={"start_angle": start_angles, "end_angle": end_angles, "radius": radii, "alphas": alphas}
4149
)
4250

43-
# Create figure with matching x/y ranges for circular shape
51+
# Create figure
4452
p = figure(
4553
width=4800,
4654
height=2700,
47-
title="Monthly Rainfall · rose-basic · bokeh · pyplots.ai",
48-
x_range=(-1.3, 1.3),
49-
y_range=(-1.2, 1.1),
55+
title="Monthly Rainfall · rose-basic · bokeh · anyplot.ai",
56+
x_range=(-1.5, 1.5),
57+
y_range=(-1.3, 1.35),
5058
tools="",
5159
toolbar_location=None,
5260
)
@@ -59,56 +67,56 @@
5967
start_angle="end_angle",
6068
end_angle="start_angle",
6169
source=source,
62-
fill_color="colors",
63-
fill_alpha=0.8,
64-
line_color="white",
70+
fill_color=BRAND,
71+
fill_alpha="alphas",
72+
line_color=PAGE_BG,
6573
line_width=2,
6674
)
6775

68-
# Add radial gridlines (concentric circles)
76+
# Radial gridlines (concentric circles)
77+
theta = np.linspace(0, 2 * np.pi, 200)
6978
for r in [0.25, 0.5, 0.75, 1.0]:
70-
theta = np.linspace(0, 2 * np.pi, 100)
71-
p.line(r * np.cos(theta), r * np.sin(theta), line_color="gray", line_alpha=0.3, line_width=1, line_dash="dashed")
79+
p.line(r * np.cos(theta), r * np.sin(theta), line_color=INK, line_alpha=0.22, line_width=1.5, line_dash="dashed")
7280

73-
# Add radial lines from center
81+
# Radial divider lines from center
7482
for i in range(n):
7583
angle = np.pi / 2 - i * angle_width
76-
p.line([0, 1.05 * np.cos(angle)], [0, 1.05 * np.sin(angle)], line_color="gray", line_alpha=0.2, line_width=1)
84+
p.line([0, 1.05 * np.cos(angle)], [0, 1.05 * np.sin(angle)], line_color=INK, line_alpha=0.18, line_width=1)
7785

78-
# Add month labels around the outside
79-
label_radius = 1.12
86+
# Month labels centered in each wedge
87+
label_radius = 1.15
8088
for i, month in enumerate(months):
81-
angle = np.pi / 2 - i * angle_width
82-
x = label_radius * np.cos(angle)
83-
y = label_radius * np.sin(angle)
89+
angle = center_angles[i]
8490
p.text(
85-
x=[x],
86-
y=[y],
91+
x=[label_radius * np.cos(angle)],
92+
y=[label_radius * np.sin(angle)],
8793
text=[month],
8894
text_align="center",
8995
text_baseline="middle",
90-
text_font_size="18pt",
91-
text_color="#333333",
96+
text_font_size="20pt",
97+
text_color=INK,
9298
)
9399

94-
# Add value scale labels on right side
95-
p.text(x=[1.05], y=[0.25], text=["25%"], text_font_size="14pt", text_color="gray", text_align="left")
96-
p.text(x=[1.05], y=[0.5], text=["50%"], text_font_size="14pt", text_color="gray", text_align="left")
97-
p.text(x=[1.05], y=[0.75], text=["75%"], text_font_size="14pt", text_color="gray", text_align="left")
98-
p.text(x=[1.05], y=[1.0], text=["100%"], text_font_size="14pt", text_color="gray", text_align="left")
100+
# Rainfall scale labels (actual mm values, right side)
101+
for r in [0.25, 0.5, 0.75, 1.0]:
102+
val_label = f"{int(r * max_val + 0.5)} mm"
103+
p.text(x=[1.2], y=[r], text=[val_label], text_font_size="15pt", text_color=INK_SOFT, text_align="left")
99104

100-
# Styling
105+
# Title and chrome
101106
p.title.text_font_size = "28pt"
102107
p.title.align = "center"
103-
p.background_fill_color = "white"
104-
p.border_fill_color = "white"
108+
p.title.text_color = INK
109+
p.title.text_font_style = "normal"
110+
111+
p.background_fill_color = PAGE_BG
112+
p.border_fill_color = PAGE_BG
105113
p.outline_line_color = None
106114

107115
# Hide axes (not needed for rose chart)
108116
p.axis.visible = False
109117
p.grid.visible = False
110118

111-
# Save as PNG and HTML
112-
export_png(p, filename="plot.png")
113-
output_file("plot.html")
119+
# Save
120+
export_png(p, filename=f"plot-{THEME}.png")
121+
output_file(f"plot-{THEME}.html")
114122
save(p)
Lines changed: 81 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
rose-basic: Basic Rose Chart
3-
Library: highcharts unknown | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-23
3+
Library: highcharts unknown | Python 3.13.13
4+
Quality: 85/100 | Updated: 2026-04-30
55
"""
66

7+
import os
78
import tempfile
89
import time
910
import urllib.request
@@ -16,66 +17,112 @@
1617
from selenium.webdriver.chrome.options import Options
1718

1819

19-
# Data - Monthly rainfall in mm (showing natural 12-month cycle)
20+
# Theme tokens
21+
THEME = os.getenv("ANYPLOT_THEME", "light")
22+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
23+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
24+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
25+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
26+
GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)"
27+
BRAND = "#009E73" # Okabe-Ito position 1
28+
29+
# Data - Monthly rainfall in mm (UK-like temperate oceanic climate)
2030
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
2131
rainfall = [78, 52, 65, 45, 38, 25, 18, 22, 42, 68, 85, 92]
2232

33+
# Value-proportional color gradient: light mint (low) → brand green (peak)
34+
# Encodes rainfall intensity visually — wet months appear darker and more saturated
35+
_lo = (200, 232, 222) # #C8E8DE light mint base
36+
_hi = (0, 158, 115) # #009E73 brand green
37+
_mn, _mx = min(rainfall), max(rainfall)
38+
colors = [
39+
"#{:02X}{:02X}{:02X}".format(
40+
int(_lo[0] + (v - _mn) / (_mx - _mn) * (_hi[0] - _lo[0])),
41+
int(_lo[1] + (v - _mn) / (_mx - _mn) * (_hi[1] - _lo[1])),
42+
int(_lo[2] + (v - _mn) / (_mx - _mn) * (_hi[2] - _lo[2])),
43+
)
44+
for v in rainfall
45+
]
46+
2347
# Create chart with polar/rose configuration
2448
chart = Chart(container="container")
2549
chart.options = HighchartsOptions()
2650

27-
# Chart configuration for polar column (rose chart)
28-
chart.options.chart = {"polar": True, "type": "column", "width": 4800, "height": 2700, "backgroundColor": "#ffffff"}
51+
# Square canvas — optimal geometry for a circular rose chart
52+
chart.options.chart = {
53+
"polar": True,
54+
"type": "column",
55+
"width": 3600,
56+
"height": 3600,
57+
"backgroundColor": PAGE_BG,
58+
"style": {"color": INK},
59+
}
2960

3061
# Title
3162
chart.options.title = {
32-
"text": "rose-basic · highcharts · pyplots.ai",
33-
"style": {"fontSize": "48px", "fontWeight": "bold"},
63+
"text": "rose-basic · highcharts · anyplot.ai",
64+
"style": {"fontSize": "48px", "fontWeight": "bold", "color": INK},
3465
}
3566

3667
# Subtitle for context
37-
chart.options.subtitle = {"text": "Monthly Rainfall (mm)", "style": {"fontSize": "32px"}}
68+
chart.options.subtitle = {"text": "Monthly Rainfall (mm)", "style": {"fontSize": "32px", "color": INK_SOFT}}
3869

3970
# X-axis (categories around the circle)
4071
chart.options.x_axis = {
4172
"categories": months,
4273
"tickmarkPlacement": "on",
4374
"lineWidth": 0,
44-
"labels": {"style": {"fontSize": "28px"}},
75+
"labels": {"style": {"fontSize": "28px", "color": INK_SOFT}},
76+
"gridLineColor": GRID,
4577
}
4678

47-
# Y-axis (radial - values extend from center)
79+
# Y-axis (radial values extend from center)
4880
chart.options.y_axis = {
4981
"min": 0,
5082
"gridLineInterpolation": "polygon",
5183
"lineWidth": 0,
52-
"labels": {"style": {"fontSize": "24px"}},
53-
"title": {"text": "Rainfall (mm)", "style": {"fontSize": "28px"}},
84+
"labels": {"format": "{value} mm", "style": {"fontSize": "22px", "color": INK_SOFT}},
85+
"title": {"text": "Rainfall (mm)", "style": {"fontSize": "28px", "color": INK}},
86+
"gridLineColor": GRID,
5487
}
5588

89+
# Pane — startAngle 0 places Jan at 12 o'clock; larger pane fills square canvas
90+
chart.options.pane = {"size": "88%", "startAngle": 0}
91+
5692
# Plot options for the rose/polar column
5793
chart.options.plot_options = {
58-
"column": {"pointPadding": 0, "groupPadding": 0, "borderWidth": 2, "borderColor": "#ffffff"},
59-
"series": {"dataLabels": {"enabled": True, "format": "{y}", "style": {"fontSize": "20px", "fontWeight": "normal"}}},
94+
"column": {"pointPadding": 0, "groupPadding": 0, "borderWidth": 2, "borderColor": PAGE_BG},
95+
"series": {
96+
"dataLabels": {
97+
"enabled": True,
98+
"format": "{y}",
99+
"style": {"fontSize": "26px", "fontWeight": "normal", "color": INK_SOFT},
100+
}
101+
},
60102
}
61103

62-
# Pane configuration for polar chart
63-
chart.options.pane = {"size": "85%", "startAngle": -15}
104+
# Rich tooltip using Highcharts pointFormat — highlights the per-point color swatch
105+
chart.options.tooltip = {
106+
"headerFormat": "<span style='font-size:24px'><b>{point.key}</b></span><br/>",
107+
"pointFormat": "<span style='color:{point.color}'>●</span> Rainfall: <b>{point.y} mm</b>",
108+
"backgroundColor": ELEVATED_BG,
109+
"style": {"color": INK, "fontSize": "22px"},
110+
"borderColor": INK_SOFT,
111+
}
64112

65-
# Legend configuration
66-
chart.options.legend = {"enabled": True, "itemStyle": {"fontSize": "28px"}}
113+
# Disable legend — redundant for a single-series chart
114+
chart.options.legend = {"enabled": False}
67115

68-
# Create series with Python Blue color
116+
# Series with per-point value-proportional colors (colorByPoint via data objects)
69117
series = ColumnSeries()
70118
series.name = "Rainfall"
71-
series.data = rainfall
72-
series.color = "#306998"
119+
series.data = [{"y": v, "color": c} for v, c in zip(rainfall, colors, strict=True)]
73120

74121
chart.add_series(series)
75122

76-
# Download Highcharts JS and Highcharts More (for polar charts)
77-
highcharts_url = "https://code.highcharts.com/highcharts.js"
78-
highcharts_more_url = "https://code.highcharts.com/highcharts-more.js"
123+
# Download Highcharts JS and Highcharts More (required for polar charts)
124+
highcharts_url = "https://cdn.jsdelivr.net/npm/highcharts@latest/highcharts.js"
125+
highcharts_more_url = "https://cdn.jsdelivr.net/npm/highcharts@latest/highcharts-more.js"
79126

80127
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
81128
highcharts_js = response.read().decode("utf-8")
@@ -92,34 +139,32 @@
92139
<script>{highcharts_js}</script>
93140
<script>{highcharts_more_js}</script>
94141
</head>
95-
<body style="margin:0;">
96-
<div id="container" style="width: 4800px; height: 2700px;"></div>
142+
<body style="margin:0; background:{PAGE_BG};">
143+
<div id="container" style="width: 3600px; height: 3600px;"></div>
97144
<script>{html_str}</script>
98145
</body>
99146
</html>"""
100147

101-
# Write temp HTML
102-
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
148+
# Save HTML artifact for the site
149+
with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f:
103150
f.write(html_content)
104-
temp_path = f.name
105151

106-
# Save HTML for interactive version
107-
with open("plot.html", "w", encoding="utf-8") as f:
152+
# Write temp HTML and take screenshot for the PNG artifact
153+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
108154
f.write(html_content)
155+
temp_path = f.name
109156

110-
# Take screenshot with headless Chrome
111157
chrome_options = Options()
112158
chrome_options.add_argument("--headless")
113159
chrome_options.add_argument("--no-sandbox")
114160
chrome_options.add_argument("--disable-dev-shm-usage")
115161
chrome_options.add_argument("--disable-gpu")
116-
chrome_options.add_argument("--window-size=4800,2700")
162+
chrome_options.add_argument("--window-size=3600,3600")
117163

118164
driver = webdriver.Chrome(options=chrome_options)
119165
driver.get(f"file://{temp_path}")
120166
time.sleep(5)
121-
driver.save_screenshot("plot.png")
167+
driver.save_screenshot(f"plot-{THEME}.png")
122168
driver.quit()
123169

124-
# Clean up temp file
125170
Path(temp_path).unlink()

plots/rose-basic/implementations/python/matplotlib.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
rose-basic: Basic Rose Chart
3-
Library: matplotlib 3.10.8 | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-23
3+
Library: matplotlib 3.10.9 | Python 3.13.13
4+
Quality: 80/100 | Updated: 2026-04-30
55
"""
66

77
import matplotlib.pyplot as plt

0 commit comments

Comments
 (0)