A pie chart showing proportions of categorical data as slices of a circle. Each slice represents a category's contribution to the whole, with the angle proportional to its value. Essential for visualizing parts-to-whole relationships in a simple, intuitive format.

""" anyplot.ai
pie-basic: Basic Pie Chart
Library: bokeh 3.9.0 | Python 3.13.13
Quality: 89/100 | Updated: 2026-05-28
"""
import io
import math
import os
import time
from pathlib import Path
from bokeh.io import output_file, save
from bokeh.models import ColumnDataSource, Label, Legend, LegendItem
from bokeh.plotting import figure
from PIL import Image
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# Theme
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"
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
IMPRINT_PALETTE = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477", "#99B314"]
# Data - Cloud infrastructure market share (2024)
categories = ["AWS", "Azure", "Google Cloud", "Alibaba", "Others"]
values = [33, 23, 11, 4, 29]
# First 4 use canonical Imprint palette order; "Others" uses semantic muted anchor
colors = [IMPRINT_PALETTE[0], IMPRINT_PALETTE[1], IMPRINT_PALETTE[2], IMPRINT_PALETTE[3], INK_MUTED]
# Compute angles and percentages
total = sum(values)
percentages = [v / total * 100 for v in values]
angles = [v / total * 2 * math.pi for v in values]
# Start/end angles (clockwise from top)
start_angles = []
end_angles = []
current = math.pi / 2
for a in angles:
start_angles.append(current)
current -= a
end_angles.append(current)
mid_angles = [(s + e) / 2 for s, e in zip(start_angles, end_angles, strict=True)]
# Explode the largest slice (AWS) for emphasis
explode_idx = 0
explode_r = 0.06
offsets_x = [0.0] * len(categories)
offsets_y = [0.0] * len(categories)
offsets_x[explode_idx] = explode_r * math.cos(mid_angles[explode_idx])
offsets_y[explode_idx] = explode_r * math.sin(mid_angles[explode_idx])
# Title sizing — scale down only if longer than 67-char baseline
title = "pie-basic · python · bokeh · anyplot.ai"
title_pt = round(50 * (67 / len(title))) if len(title) > 67 else 50
# Figure — 2400×2400 square (canonical for symmetric plots like pie charts)
W, H = 2400, 2400
radius = 0.82
p = figure(
width=W,
height=H,
title=title,
x_range=(-1.2, 1.2),
y_range=(-1.05, 1.1),
toolbar_location=None,
min_border_bottom=200,
min_border_left=60,
min_border_top=130,
min_border_right=60,
)
# Wedges
source = ColumnDataSource(
data={"x": offsets_x, "y": offsets_y, "start": start_angles, "end": end_angles, "color": colors}
)
renderers = p.wedge(
x="x",
y="y",
radius=radius,
start_angle="start",
end_angle="end",
direction="clock",
fill_color="color",
line_color=PAGE_BG,
line_width=4,
source=source,
)
# Percentage labels — near-white on all palette colors (all medium-dark)
# except ANYPLOT_MUTED in dark theme which is lighter and needs dark text
slice_label_colors = ["#F0EFE8"] * 4 + ["#1A1A17" if THEME == "dark" else "#F0EFE8"]
label_r = radius * 0.62
for i in range(len(categories)):
lx = label_r * math.cos(mid_angles[i]) + offsets_x[i]
ly = label_r * math.sin(mid_angles[i]) + offsets_y[i]
p.add_layout(
Label(
x=lx,
y=ly,
text=f"{percentages[i]:.0f}%",
text_font_size="34pt",
text_color=slice_label_colors[i],
text_font_style="bold",
text_align="center",
text_baseline="middle",
)
)
# Legend with category names and percentages
legend_items = [
LegendItem(label=f"{categories[i]} ({percentages[i]:.0f}%)", renderers=[renderers], index=i)
for i in range(len(categories))
]
legend = Legend(
items=legend_items,
location="center",
orientation="horizontal",
label_text_font_size="34pt",
label_text_color=INK_SOFT,
glyph_width=40,
glyph_height=40,
spacing=30,
padding=20,
background_fill_color=ELEVATED_BG,
background_fill_alpha=0.9,
border_line_color=INK_SOFT,
)
p.add_layout(legend, "below")
# Styling
p.title.text_font_size = f"{title_pt}pt"
p.title.align = "center"
p.title.text_color = INK
p.axis.visible = False
p.grid.visible = False
p.outline_line_color = PAGE_BG
p.background_fill_color = PAGE_BG
p.border_fill_color = PAGE_BG
# Save HTML (interactive catalog artifact)
output_file(f"plot-{THEME}.html")
save(p)
# Screenshot with headless Chrome — window is H+200 tall so bokeh canvas fills
# exactly W×H; PIL crops to the target rect before saving.
opts = Options()
for arg in (
"--headless=new",
"--no-sandbox",
"--disable-dev-shm-usage",
"--disable-gpu",
f"--window-size={W},{H + 200}",
"--hide-scrollbars",
"--force-device-scale-factor=1",
):
opts.add_argument(arg)
driver = webdriver.Chrome(options=opts)
driver.set_window_size(W, H + 200)
driver.get(f"file://{Path(f'plot-{THEME}.html').resolve()}")
time.sleep(3)
raw = driver.get_screenshot_as_png()
driver.quit()
Image.open(io.BytesIO(raw)).crop((0, 0, W, H)).save(f"plot-{THEME}.png")
Part of Basic Pie Chart on anyplot.ai.