diff --git a/plots/alluvial-basic/implementations/python/matplotlib.py b/plots/alluvial-basic/implementations/python/matplotlib.py index 7b432c7ae9..d0ce39bad1 100644 --- a/plots/alluvial-basic/implementations/python/matplotlib.py +++ b/plots/alluvial-basic/implementations/python/matplotlib.py @@ -20,11 +20,11 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette — use positions 1→N in order -OKABE_ITO = [ +IMPRINT = [ "#009E73", # 1: bluish green (brand) - "#D55E00", # 2: vermillion - "#0072B2", # 3: blue - "#CC79A7", # 4: reddish purple + "#C475FD", # 2: vermillion + "#4467A3", # 3: blue + "#BD8233", # 4: reddish purple ] # Data: Voter migration across 4 election cycles @@ -32,7 +32,7 @@ time_points = ["2012", "2016", "2020", "2024"] categories = ["Party A", "Party B", "Party C", "Independent"] -colors = {cat: OKABE_ITO[i] for i, cat in enumerate(categories)} +colors = {cat: IMPRINT[i] for i, cat in enumerate(categories)} # Node values at each time point (thousands of voters) node_values = { diff --git a/plots/andrews-curves/implementations/python/matplotlib.py b/plots/andrews-curves/implementations/python/matplotlib.py index 02a5c73db4..7b038a731d 100644 --- a/plots/andrews-curves/implementations/python/matplotlib.py +++ b/plots/andrews-curves/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3"] # Data np.random.seed(42) @@ -55,11 +55,11 @@ # Plot Andrews curves for each observation for i in range(len(curves)): - ax.plot(t, curves[i], color=OKABE_ITO[y[i]], alpha=0.4, linewidth=2.5) + ax.plot(t, curves[i], color=IMPRINT[y[i]], alpha=0.4, linewidth=2.5) # Create legend with sample lines for idx, species in enumerate(species_names): - ax.plot([], [], color=OKABE_ITO[idx], linewidth=3, label=species, alpha=0.4) + ax.plot([], [], color=IMPRINT[idx], linewidth=3, label=species, alpha=0.4) # Style ax.set_xlabel("t (radians)", fontsize=20, color=INK) diff --git a/plots/area-cumulative-flow/implementations/python/matplotlib.py b/plots/area-cumulative-flow/implementations/python/matplotlib.py index 361264cc47..69e895cbc5 100644 --- a/plots/area-cumulative-flow/implementations/python/matplotlib.py +++ b/plots/area-cumulative-flow/implementations/python/matplotlib.py @@ -28,7 +28,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Okabe-Ito positions 1-5 (bottom to top: Done → Backlog) -COLORS = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00"] +COLORS = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"] # Data: 90-day software Kanban board simulation np.random.seed(42) diff --git a/plots/area-stacked-confidence/implementations/python/matplotlib.py b/plots/area-stacked-confidence/implementations/python/matplotlib.py index 05a98c744a..52790a87d2 100644 --- a/plots/area-stacked-confidence/implementations/python/matplotlib.py +++ b/plots/area-stacked-confidence/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data: quarterly energy consumption by source with measurement uncertainty np.random.seed(42) @@ -73,34 +73,34 @@ # Plot stacked areas from bottom to top with enhanced styling # Solar (bottom layer) -ax.fill_between(quarters, 0, solar_stack, color=OKABE_ITO[0], alpha=0.85, label="Solar", zorder=3) -ax.fill_between(quarters, solar_lower, solar_upper, color=OKABE_ITO[0], alpha=0.25, linewidth=0, zorder=2) -ax.plot(quarters, solar_lower, color=OKABE_ITO[0], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) -ax.plot(quarters, solar_upper, color=OKABE_ITO[0], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) +ax.fill_between(quarters, 0, solar_stack, color=IMPRINT[0], alpha=0.85, label="Solar", zorder=3) +ax.fill_between(quarters, solar_lower, solar_upper, color=IMPRINT[0], alpha=0.25, linewidth=0, zorder=2) +ax.plot(quarters, solar_lower, color=IMPRINT[0], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) +ax.plot(quarters, solar_upper, color=IMPRINT[0], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) # Wind (second layer) -ax.fill_between(quarters, solar_stack, wind_stack, color=OKABE_ITO[1], alpha=0.85, label="Wind", zorder=3) -ax.fill_between(quarters, wind_lower, wind_upper, color=OKABE_ITO[1], alpha=0.25, linewidth=0, zorder=2) -ax.plot(quarters, wind_lower, color=OKABE_ITO[1], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) -ax.plot(quarters, wind_upper, color=OKABE_ITO[1], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) +ax.fill_between(quarters, solar_stack, wind_stack, color=IMPRINT[1], alpha=0.85, label="Wind", zorder=3) +ax.fill_between(quarters, wind_lower, wind_upper, color=IMPRINT[1], alpha=0.25, linewidth=0, zorder=2) +ax.plot(quarters, wind_lower, color=IMPRINT[1], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) +ax.plot(quarters, wind_upper, color=IMPRINT[1], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) # Hydro (third layer) -ax.fill_between(quarters, wind_stack, hydro_stack, color=OKABE_ITO[2], alpha=0.85, label="Hydro", zorder=3) -ax.fill_between(quarters, hydro_lower, hydro_upper, color=OKABE_ITO[2], alpha=0.25, linewidth=0, zorder=2) -ax.plot(quarters, hydro_lower, color=OKABE_ITO[2], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) -ax.plot(quarters, hydro_upper, color=OKABE_ITO[2], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) +ax.fill_between(quarters, wind_stack, hydro_stack, color=IMPRINT[2], alpha=0.85, label="Hydro", zorder=3) +ax.fill_between(quarters, hydro_lower, hydro_upper, color=IMPRINT[2], alpha=0.25, linewidth=0, zorder=2) +ax.plot(quarters, hydro_lower, color=IMPRINT[2], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) +ax.plot(quarters, hydro_upper, color=IMPRINT[2], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) # Natural Gas (top layer) -ax.fill_between(quarters, hydro_stack, gas_stack, color=OKABE_ITO[3], alpha=0.85, label="Natural Gas", zorder=3) -ax.fill_between(quarters, gas_lower, gas_upper, color=OKABE_ITO[3], alpha=0.25, linewidth=0, zorder=2) -ax.plot(quarters, gas_lower, color=OKABE_ITO[3], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) -ax.plot(quarters, gas_upper, color=OKABE_ITO[3], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) +ax.fill_between(quarters, hydro_stack, gas_stack, color=IMPRINT[3], alpha=0.85, label="Natural Gas", zorder=3) +ax.fill_between(quarters, gas_lower, gas_upper, color=IMPRINT[3], alpha=0.25, linewidth=0, zorder=2) +ax.plot(quarters, gas_lower, color=IMPRINT[3], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) +ax.plot(quarters, gas_upper, color=IMPRINT[3], linewidth=0.8, alpha=0.4, linestyle=":", zorder=1) # Add prominent center lines for clarity and visual hierarchy -ax.plot(quarters, solar_stack, color=OKABE_ITO[0], linewidth=3, alpha=1.0, zorder=4, solid_capstyle="round") -ax.plot(quarters, wind_stack, color=OKABE_ITO[1], linewidth=3, alpha=1.0, zorder=4, solid_capstyle="round") -ax.plot(quarters, hydro_stack, color=OKABE_ITO[2], linewidth=3, alpha=1.0, zorder=4, solid_capstyle="round") -ax.plot(quarters, gas_stack, color=OKABE_ITO[3], linewidth=3, alpha=1.0, zorder=4, solid_capstyle="round") +ax.plot(quarters, solar_stack, color=IMPRINT[0], linewidth=3, alpha=1.0, zorder=4, solid_capstyle="round") +ax.plot(quarters, wind_stack, color=IMPRINT[1], linewidth=3, alpha=1.0, zorder=4, solid_capstyle="round") +ax.plot(quarters, hydro_stack, color=IMPRINT[2], linewidth=3, alpha=1.0, zorder=4, solid_capstyle="round") +ax.plot(quarters, gas_stack, color=IMPRINT[3], linewidth=3, alpha=1.0, zorder=4, solid_capstyle="round") # Styling ax.set_xlabel("Quarter", fontsize=20, color=INK) diff --git a/plots/area-stacked-percent/implementations/python/matplotlib.py b/plots/area-stacked-percent/implementations/python/matplotlib.py index a6f8483430..d72923f825 100644 --- a/plots/area-stacked-percent/implementations/python/matplotlib.py +++ b/plots/area-stacked-percent/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"] # Data - Renewable energy evolution (2014-2024) years = np.arange(2014, 2025) @@ -41,7 +41,7 @@ labels = ["Solar", "Wind", "Hydro", "Natural Gas", "Coal"] # Create stacked area -ax.stackplot(years, data_percent, labels=labels, colors=OKABE_ITO, alpha=0.85, edgecolor=PAGE_BG, linewidth=1.5) +ax.stackplot(years, data_percent, labels=labels, colors=IMPRINT, alpha=0.85, edgecolor=PAGE_BG, linewidth=1.5) # Styling ax.set_xlabel("Year (2014–2024)", fontsize=20, color=INK) diff --git a/plots/area-stacked/implementations/python/matplotlib.py b/plots/area-stacked/implementations/python/matplotlib.py index 51807968b1..494c6f8ce6 100644 --- a/plots/area-stacked/implementations/python/matplotlib.py +++ b/plots/area-stacked/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (use positions 1→N) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data: Monthly website traffic sources over 24 months np.random.seed(42) @@ -44,7 +44,7 @@ fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) ax.set_facecolor(PAGE_BG) -ax.stackplot(months, data, labels=categories, colors=OKABE_ITO, alpha=0.85) +ax.stackplot(months, data, labels=categories, colors=IMPRINT, alpha=0.85) # X-axis formatting (show as months) tick_positions = [1, 6, 12, 18, 24] diff --git a/plots/bar-diverging/implementations/python/matplotlib.py b/plots/bar-diverging/implementations/python/matplotlib.py index 5eead35e9b..993fdb9cf3 100644 --- a/plots/bar-diverging/implementations/python/matplotlib.py +++ b/plots/bar-diverging/implementations/python/matplotlib.py @@ -43,8 +43,8 @@ values_sorted = values[sorted_indices] # Create colors using Okabe-Ito palette -# #009E73 (position 1) for positive, #D55E00 (position 2) for negative -colors = ["#009E73" if v >= 0 else "#D55E00" for v in values_sorted] +# #009E73 (position 1) for positive, #C475FD (position 2) for negative +colors = ["#009E73" if v >= 0 else "#C475FD" for v in values_sorted] # Plot fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) @@ -87,7 +87,7 @@ # Add legend legend_elements = [ Patch(facecolor="#009E73", edgecolor=INK_SOFT, label="Positive (Satisfied)"), - Patch(facecolor="#D55E00", edgecolor=INK_SOFT, label="Negative (Dissatisfied)"), + Patch(facecolor="#C475FD", edgecolor=INK_SOFT, label="Negative (Dissatisfied)"), ] leg = ax.legend(handles=legend_elements, loc="lower right", fontsize=16) if leg: diff --git a/plots/bar-grouped/implementations/python/matplotlib.py b/plots/bar-grouped/implementations/python/matplotlib.py index c7dc14dd62..07e5e5e7f4 100644 --- a/plots/bar-grouped/implementations/python/matplotlib.py +++ b/plots/bar-grouped/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Okabe-Ito palette (position 1 is always #009E73) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3"] # Data: Quarterly sales by product line (in thousands USD) categories = ["Q1", "Q2", "Q3", "Q4"] @@ -45,7 +45,7 @@ max_values_per_category = {cat: max(sales_data[group][i] for group in groups) for i, cat in enumerate(categories)} bars = [] -for i, (group, color) in enumerate(zip(groups, OKABE_ITO, strict=True)): +for i, (group, color) in enumerate(zip(groups, IMPRINT, strict=True)): bar = ax.bar( x + offsets[i], sales_data[group], bar_width, label=group, color=color, edgecolor=INK_SOFT, linewidth=1.5 ) diff --git a/plots/bar-race-animated/implementations/python/matplotlib.py b/plots/bar-race-animated/implementations/python/matplotlib.py index b62e2e2922..d8b614f69f 100644 --- a/plots/bar-race-animated/implementations/python/matplotlib.py +++ b/plots/bar-race-animated/implementations/python/matplotlib.py @@ -18,11 +18,11 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477"] # Data: Tech Company Market Cap ($B) — year-end snapshots (2019–2024) companies = ["Alphabet", "Amazon", "Apple", "Meta", "Microsoft", "Nvidia", "Tesla"] -company_colors = {c: OKABE_ITO[i] for i, c in enumerate(companies)} +company_colors = {c: IMPRINT[i] for i, c in enumerate(companies)} market_cap = { "Alphabet": [920, 1200, 1960, 1140, 1800, 2150], diff --git a/plots/bar-spine/implementations/python/matplotlib.py b/plots/bar-spine/implementations/python/matplotlib.py index 553cd74de5..8b8fb38f2b 100644 --- a/plots/bar-spine/implementations/python/matplotlib.py +++ b/plots/bar-spine/implementations/python/matplotlib.py @@ -48,7 +48,7 @@ # Colors: Okabe-Ito positions 1 and 2 COLOR_SURVIVED = "#009E73" -COLOR_DIED = "#D55E00" +COLOR_DIED = "#C475FD" # Plot fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) diff --git a/plots/bar-stacked-labeled/implementations/python/matplotlib.py b/plots/bar-stacked-labeled/implementations/python/matplotlib.py index ab6c2a01dd..df2ff21efa 100644 --- a/plots/bar-stacked-labeled/implementations/python/matplotlib.py +++ b/plots/bar-stacked-labeled/implementations/python/matplotlib.py @@ -26,7 +26,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Okabe-Ito palette (positions 1-4) -COLORS = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +COLORS = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data - Quarterly revenue by product category (in millions $) np.random.seed(42) diff --git a/plots/bar-stacked-percent/implementations/python/matplotlib.py b/plots/bar-stacked-percent/implementations/python/matplotlib.py index 97aca6f3db..2fa53eb67d 100644 --- a/plots/bar-stacked-percent/implementations/python/matplotlib.py +++ b/plots/bar-stacked-percent/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (first series is ALWAYS position 1) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"] # Data: Energy mix by country (percentage of total electricity generation) categories = ["Germany", "France", "UK", "Spain", "Italy", "Poland"] @@ -49,7 +49,7 @@ bottom = np.zeros(len(categories)) # Create stacked bars -for i, (component, color) in enumerate(zip(components, OKABE_ITO, strict=True)): +for i, (component, color) in enumerate(zip(components, IMPRINT, strict=True)): bars = ax.bar( x, percentages[:, i], bar_width, bottom=bottom, label=component, color=color, edgecolor=PAGE_BG, linewidth=1.5 ) diff --git a/plots/bar-stacked/implementations/python/matplotlib.py b/plots/bar-stacked/implementations/python/matplotlib.py index dcb98886f7..57807c5273 100644 --- a/plots/bar-stacked/implementations/python/matplotlib.py +++ b/plots/bar-stacked/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (positions 1-4) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data: Quarterly revenue by product category (in millions USD) categories = ["Q1", "Q2", "Q3", "Q4"] @@ -39,7 +39,7 @@ # Create stacked bars bottom = np.zeros(len(categories)) for i, (product, values) in enumerate(zip(products, [software, hardware, services, support], strict=True)): - ax.bar(x, values, bar_width, label=product, bottom=bottom, color=OKABE_ITO[i], edgecolor=PAGE_BG, linewidth=1.5) + ax.bar(x, values, bar_width, label=product, bottom=bottom, color=IMPRINT[i], edgecolor=PAGE_BG, linewidth=1.5) bottom += values # Add total labels above each stacked bar diff --git a/plots/bland-altman-basic/implementations/python/matplotlib.py b/plots/bland-altman-basic/implementations/python/matplotlib.py index d3be83ea72..8e8fa7caa0 100644 --- a/plots/bland-altman-basic/implementations/python/matplotlib.py +++ b/plots/bland-altman-basic/implementations/python/matplotlib.py @@ -17,7 +17,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" # Okabe-Ito position 1 — first categorical series -ACCENT2 = "#D55E00" # Okabe-Ito position 2 — for limits of agreement lines +ACCENT2 = "#C475FD" # Okabe-Ito position 2 — for limits of agreement lines # Data: Simulated blood pressure readings from two sphygmomanometers np.random.seed(42) diff --git a/plots/box-grouped/implementations/python/matplotlib.py b/plots/box-grouped/implementations/python/matplotlib.py index 7dd222b850..bb08e43e59 100644 --- a/plots/box-grouped/implementations/python/matplotlib.py +++ b/plots/box-grouped/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (positions 1-3 for three subcategories) -COLORS = ["#009E73", "#D55E00", "#0072B2"] +COLORS = ["#009E73", "#C475FD", "#4467A3"] # Data - Employee performance scores across departments and experience levels np.random.seed(42) diff --git a/plots/box-notched/implementations/python/matplotlib.py b/plots/box-notched/implementations/python/matplotlib.py index 46b4a26c34..ce10406774 100644 --- a/plots/box-notched/implementations/python/matplotlib.py +++ b/plots/box-notched/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette - first series is always brand green -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"] # Data - Test success rates across different test suites np.random.seed(42) @@ -68,7 +68,7 @@ ) # Apply colors to boxes -for patch, color in zip(bp["boxes"], OKABE_ITO, strict=True): +for patch, color in zip(bp["boxes"], IMPRINT, strict=True): patch.set_facecolor(color) patch.set_alpha(0.75) patch.set_edgecolor(INK_SOFT) diff --git a/plots/calibration-curve/implementations/python/matplotlib.py b/plots/calibration-curve/implementations/python/matplotlib.py index 707ea96590..722cbfe9be 100644 --- a/plots/calibration-curve/implementations/python/matplotlib.py +++ b/plots/calibration-curve/implementations/python/matplotlib.py @@ -18,8 +18,8 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette - canonical order -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] -BRAND = OKABE_ITO[0] # #009E73 +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] +BRAND = IMPRINT[0] # #009E73 # Data np.random.seed(42) @@ -97,13 +97,13 @@ # Well-calibrated ax1.fill_between( - prob_pred_cal, prob_true_cal - prob_std_cal, prob_true_cal + prob_std_cal, alpha=0.2, color=OKABE_ITO[0] + prob_pred_cal, prob_true_cal - prob_std_cal, prob_true_cal + prob_std_cal, alpha=0.2, color=IMPRINT[0] ) ax1.plot( prob_pred_cal, prob_true_cal, "o-", - color=OKABE_ITO[0], + color=IMPRINT[0], linewidth=3, markersize=10, label=f"Well-Calibrated (Brier: {brier_cal:.3f})", @@ -111,13 +111,13 @@ # Overconfident ax1.fill_between( - prob_pred_over, prob_true_over - prob_std_over, prob_true_over + prob_std_over, alpha=0.2, color=OKABE_ITO[1] + prob_pred_over, prob_true_over - prob_std_over, prob_true_over + prob_std_over, alpha=0.2, color=IMPRINT[1] ) ax1.plot( prob_pred_over, prob_true_over, "s-", - color=OKABE_ITO[1], + color=IMPRINT[1], linewidth=3, markersize=10, label=f"Overconfident (Brier: {brier_over:.3f})", @@ -125,13 +125,13 @@ # Underconfident ax1.fill_between( - prob_pred_under, prob_true_under - prob_std_under, prob_true_under + prob_std_under, alpha=0.2, color=OKABE_ITO[2] + prob_pred_under, prob_true_under - prob_std_under, prob_true_under + prob_std_under, alpha=0.2, color=IMPRINT[2] ) ax1.plot( prob_pred_under, prob_true_under, "^-", - color=OKABE_ITO[2], + color=IMPRINT[2], linewidth=3, markersize=10, label=f"Underconfident (Brier: {brier_under:.3f})", @@ -159,13 +159,13 @@ # Histogram ax2.hist( - y_prob_calibrated, bins=20, alpha=0.6, color=OKABE_ITO[0], label="Well-Calibrated", edgecolor=PAGE_BG, linewidth=0.5 + y_prob_calibrated, bins=20, alpha=0.6, color=IMPRINT[0], label="Well-Calibrated", edgecolor=PAGE_BG, linewidth=0.5 ) ax2.hist( y_prob_overconfident, bins=20, alpha=0.6, - color=OKABE_ITO[1], + color=IMPRINT[1], label="Overconfident", edgecolor=PAGE_BG, linewidth=0.5, @@ -174,7 +174,7 @@ y_prob_underconfident, bins=20, alpha=0.6, - color=OKABE_ITO[2], + color=IMPRINT[2], label="Underconfident", edgecolor=PAGE_BG, linewidth=0.5, diff --git a/plots/candlestick-volume/implementations/python/matplotlib.py b/plots/candlestick-volume/implementations/python/matplotlib.py index 293ed87650..b297b32c35 100644 --- a/plots/candlestick-volume/implementations/python/matplotlib.py +++ b/plots/candlestick-volume/implementations/python/matplotlib.py @@ -22,7 +22,7 @@ # Okabe-Ito palette UP_COLOR = "#009E73" # Brand green (up days) -DOWN_COLOR = "#D55E00" # Vermillion (down days) +DOWN_COLOR = "#C475FD" # Vermillion (down days) # Data - Generate realistic 60 trading days of OHLC data with volume np.random.seed(42) diff --git a/plots/cat-box-strip/implementations/python/matplotlib.py b/plots/cat-box-strip/implementations/python/matplotlib.py index c72fde9aee..1748d6fc86 100644 --- a/plots/cat-box-strip/implementations/python/matplotlib.py +++ b/plots/cat-box-strip/implementations/python/matplotlib.py @@ -54,7 +54,7 @@ widths=0.5, patch_artist=True, boxprops={"facecolor": BRAND, "alpha": 0.4, "linewidth": 2, "edgecolor": INK_SOFT}, - medianprops={"color": "#E69F00", "linewidth": 3}, + medianprops={"color": "#AE3030", "linewidth": 3}, whiskerprops={"color": INK_SOFT, "linewidth": 2}, capprops={"color": INK_SOFT, "linewidth": 2}, flierprops={ diff --git a/plots/circlepacking-basic/implementations/python/matplotlib.py b/plots/circlepacking-basic/implementations/python/matplotlib.py index 9f9b88e284..d3102f0639 100644 --- a/plots/circlepacking-basic/implementations/python/matplotlib.py +++ b/plots/circlepacking-basic/implementations/python/matplotlib.py @@ -64,8 +64,8 @@ # Depth-based colors using Okabe-Ito palette depth_colors = { 0: BRAND, # Root: brand green - 1: "#D55E00", # Departments: vermillion - 2: "#0072B2", # Teams: blue + 1: "#C475FD", # Departments: vermillion + 2: "#4467A3", # Teams: blue } # Circle packing layout diff --git a/plots/circos-basic/implementations/python/matplotlib.py b/plots/circos-basic/implementations/python/matplotlib.py index 2123c18438..9437a9f839 100644 --- a/plots/circos-basic/implementations/python/matplotlib.py +++ b/plots/circos-basic/implementations/python/matplotlib.py @@ -25,14 +25,14 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (positions 1-7, plus position 8 for neutral) -OKABE_ITO = [ +IMPRINT = [ "#009E73", # 1: bluish green (brand) - "#D55E00", # 2: vermillion - "#0072B2", # 3: blue - "#CC79A7", # 4: reddish purple - "#E69F00", # 5: orange - "#56B4E9", # 6: sky blue - "#F0E442", # 7: yellow + "#C475FD", # 2: vermillion + "#4467A3", # 3: blue + "#BD8233", # 4: reddish purple + "#AE3030", # 5: orange + "#2ABCCD", # 6: sky blue + "#954477", # 7: yellow ] # Data: Genomic chromosome interactions @@ -108,8 +108,8 @@ x = np.concatenate([x_outer, x_inner]) y = np.concatenate([y_outer, y_inner]) - color_idx = i % len(OKABE_ITO) - ax.fill(x, y, color=OKABE_ITO[color_idx], alpha=0.85, edgecolor=PAGE_BG, linewidth=1.5) + color_idx = i % len(IMPRINT) + ax.fill(x, y, color=IMPRINT[color_idx], alpha=0.85, edgecolor=PAGE_BG, linewidth=1.5) # Add segment label mid_angle = np.radians((start + end) / 2) @@ -137,8 +137,8 @@ x = np.concatenate([x_outer, x_inner]) y = np.concatenate([y_outer, y_inner]) - color_idx = i % len(OKABE_ITO) - ax.fill(x, y, color=OKABE_ITO[color_idx], alpha=0.5, edgecolor="none") + color_idx = i % len(IMPRINT) + ax.fill(x, y, color=IMPRINT[color_idx], alpha=0.5, edgecolor="none") # Draw connections (ribbons for synteny blocks) max_value = max(c[2] for c in connections) @@ -191,8 +191,8 @@ codes = [Path.MOVETO, Path.CURVE3, Path.CURVE3, Path.LINETO, Path.CURVE3, Path.CURVE3, Path.CLOSEPOLY] path = Path(verts, codes) - color_idx = idx1 % len(OKABE_ITO) - patch = mpatches.PathPatch(path, facecolor=OKABE_ITO[color_idx], alpha=0.4, edgecolor="none") + color_idx = idx1 % len(IMPRINT) + patch = mpatches.PathPatch(path, facecolor=IMPRINT[color_idx], alpha=0.4, edgecolor="none") ax.add_patch(patch) # Title @@ -204,7 +204,7 @@ # Legend (outside the plot) legend_elements = [ - mpatches.Patch(facecolor=OKABE_ITO[i % len(OKABE_ITO)], label=chromosomes[i], alpha=0.85) for i in range(n_chroms) + mpatches.Patch(facecolor=IMPRINT[i % len(IMPRINT)], label=chromosomes[i], alpha=0.85) for i in range(n_chroms) ] leg = ax.legend( handles=legend_elements, diff --git a/plots/contour-decision-boundary/implementations/python/matplotlib.py b/plots/contour-decision-boundary/implementations/python/matplotlib.py index c93760bbdb..135765043b 100644 --- a/plots/contour-decision-boundary/implementations/python/matplotlib.py +++ b/plots/contour-decision-boundary/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477"] # Data - generate synthetic two-moon classification data np.random.seed(42) @@ -43,7 +43,7 @@ ax.set_facecolor(PAGE_BG) # Plot decision regions with contourf -ax.contourf(xx, yy, Z, alpha=0.3, colors=[OKABE_ITO[0], OKABE_ITO[1]], levels=[-0.5, 0.5, 1.5]) +ax.contourf(xx, yy, Z, alpha=0.3, colors=[IMPRINT[0], IMPRINT[1]], levels=[-0.5, 0.5, 1.5]) # Add decision boundary line ax.contour(xx, yy, Z, colors=INK_SOFT, linewidths=2, levels=[0.5]) @@ -59,7 +59,7 @@ ax.scatter( X[class_0_correct, 0], X[class_0_correct, 1], - c=OKABE_ITO[0], + c=IMPRINT[0], s=150, alpha=0.9, edgecolors=PAGE_BG, @@ -71,7 +71,7 @@ ax.scatter( X[class_1_correct, 0], X[class_1_correct, 1], - c=OKABE_ITO[1], + c=IMPRINT[1], s=150, alpha=0.9, edgecolors=PAGE_BG, @@ -89,7 +89,7 @@ ax.scatter( X[class_0_incorrect, 0], X[class_0_incorrect, 1], - c=OKABE_ITO[0], + c=IMPRINT[0], s=200, alpha=0.9, edgecolors=INK_SOFT, @@ -102,7 +102,7 @@ ax.scatter( X[class_1_incorrect, 0], X[class_1_incorrect, 1], - c=OKABE_ITO[1], + c=IMPRINT[1], s=200, alpha=0.9, edgecolors=INK_SOFT, diff --git a/plots/dashboard-metrics-tiles/implementations/python/matplotlib.py b/plots/dashboard-metrics-tiles/implementations/python/matplotlib.py index a3c4d91fd8..41b5277d93 100644 --- a/plots/dashboard-metrics-tiles/implementations/python/matplotlib.py +++ b/plots/dashboard-metrics-tiles/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Status colors — semantic alarm indicators using Okabe-Ito positions 1, 5, 2 -STATUS_COLORS = {"good": "#009E73", "warning": "#E69F00", "critical": "#D55E00"} +STATUS_COLORS = {"good": "#009E73", "warning": "#AE3030", "critical": "#C475FD"} STATUS_LABELS = {"good": "GOOD", "warning": "WARNING", "critical": "CRITICAL"} # Data diff --git a/plots/dashboard-synchronized-crosshair/implementations/python/matplotlib.py b/plots/dashboard-synchronized-crosshair/implementations/python/matplotlib.py index 1843b877f2..003006ae14 100644 --- a/plots/dashboard-synchronized-crosshair/implementations/python/matplotlib.py +++ b/plots/dashboard-synchronized-crosshair/implementations/python/matplotlib.py @@ -26,8 +26,8 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" BRAND = "#009E73" # anyplot palette position 1 — always first series -COLOR_2 = "#9418DB" # position 2 — RSI line -COLOR_RED = "#B71D27" # position 3 — semantic: down / overbought +COLOR_2 = "#C475FD" # position 2 — RSI line +COLOR_RED = "#AE3030" # position 3 — semantic: down / overbought # Data — stock-like time series: price, volume, RSI np.random.seed(42) diff --git a/plots/datamatrix-basic/implementations/python/matplotlib.py b/plots/datamatrix-basic/implementations/python/matplotlib.py index f6a93a5bd2..8b9d3e2a02 100644 --- a/plots/datamatrix-basic/implementations/python/matplotlib.py +++ b/plots/datamatrix-basic/implementations/python/matplotlib.py @@ -28,8 +28,8 @@ # Zone colors — Okabe-Ito, theme-constant (data encoding) COLOR_FINDER = "#009E73" -COLOR_TIMING = "#0072B2" -COLOR_DATA = "#E69F00" +COLOR_TIMING = "#4467A3" +COLOR_DATA = "#AE3030" # Data — 20×20 Data Matrix for electronics component traceability np.random.seed(42) diff --git a/plots/dendrogram-radial/implementations/python/matplotlib.py b/plots/dendrogram-radial/implementations/python/matplotlib.py index ffaea53a55..9dbd4b1209 100644 --- a/plots/dendrogram-radial/implementations/python/matplotlib.py +++ b/plots/dendrogram-radial/implementations/python/matplotlib.py @@ -20,7 +20,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"] family_names = ["Asteraceae", "Rosaceae", "Fabaceae", "Poaceae", "Lamiaceae"] # Abbreviated species labels: As01-As10, Ro01-Ro10, Fa01-Fa10, Po01-Po10, La01-La10 @@ -53,7 +53,7 @@ def link_color_func(node_id): c = node_cluster.get(node_id, -1) - return OKABE_ITO[c - 1] if c != -1 else INK_SOFT + return IMPRINT[c - 1] if c != -1 else INK_SOFT # Compute dendrogram layout with colored links @@ -63,7 +63,7 @@ def link_color_func(node_id): link_colors = dendro["color_list"] leaves = dendro["leaves"] -leaf_colors = [OKABE_ITO[cluster_labels[leaf] - 1] for leaf in leaves] +leaf_colors = [IMPRINT[cluster_labels[leaf] - 1] for leaf in leaves] # Radial coordinate helpers — inner_r pads the root away from center x_max = n_species * 10.0 @@ -141,7 +141,7 @@ def to_xy(x, y): # Legend legend_elements = [ - Line2D([0], [0], marker="o", linestyle="None", markerfacecolor=OKABE_ITO[i], markersize=14, label=family_names[i]) + Line2D([0], [0], marker="o", linestyle="None", markerfacecolor=IMPRINT[i], markersize=14, label=family_names[i]) for i in range(k_families) ] leg = ax.legend( diff --git a/plots/donut-basic/implementations/python/matplotlib.py b/plots/donut-basic/implementations/python/matplotlib.py index c8dc749e3e..8c10754e3c 100644 --- a/plots/donut-basic/implementations/python/matplotlib.py +++ b/plots/donut-basic/implementations/python/matplotlib.py @@ -16,7 +16,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (first segment is always the brand green) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"] # Data - Annual budget allocation by department (USD thousands) categories = ["Engineering", "Marketing", "Operations", "Sales", "Support"] @@ -32,7 +32,7 @@ labels=categories, autopct="%1.1f%%", startangle=90, - colors=OKABE_ITO, + colors=IMPRINT, wedgeprops={"width": 0.42, "edgecolor": PAGE_BG, "linewidth": 3}, pctdistance=0.78, labeldistance=1.08, diff --git a/plots/donut-nested/implementations/python/matplotlib.py b/plots/donut-nested/implementations/python/matplotlib.py index ecd90ff592..47d9505175 100644 --- a/plots/donut-nested/implementations/python/matplotlib.py +++ b/plots/donut-nested/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (positions 1-4 for departments) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data - Budget allocation: departments (inner) and expense categories (outer) departments = ["Engineering", "Marketing", "Operations", "Sales"] @@ -48,11 +48,11 @@ # Color palette - use Okabe-Ito base with varying opacity per subcategory dept_colors = {} for i, dept in enumerate(departments): - base_color = OKABE_ITO[i] + base_color = IMPRINT[i] num_cats = len(categories[dept]) dept_colors[dept] = [base_color] * num_cats -inner_colors = [OKABE_ITO[i] for i in range(len(departments))] +inner_colors = [IMPRINT[i] for i in range(len(departments))] outer_colors = [] for dept in departments: outer_colors.extend(dept_colors[dept]) @@ -102,7 +102,7 @@ for i, dept in enumerate(departments): for cat in categories[dept]: legend_elements.append( - plt.Rectangle((0, 0), 1, 1, facecolor=OKABE_ITO[i], edgecolor=INK_SOFT, label=f"{dept}: {cat}") + plt.Rectangle((0, 0), 1, 1, facecolor=IMPRINT[i], edgecolor=INK_SOFT, label=f"{dept}: {cat}") ) ax.legend( diff --git a/plots/dot-matrix-proportional/implementations/python/matplotlib.py b/plots/dot-matrix-proportional/implementations/python/matplotlib.py index 3c3de5a87a..c31b8bc947 100644 --- a/plots/dot-matrix-proportional/implementations/python/matplotlib.py +++ b/plots/dot-matrix-proportional/implementations/python/matplotlib.py @@ -17,7 +17,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3"] # Data: remote work preference survey, n=100 employees categories = ["Hybrid", "Fully Remote", "In-Office"] @@ -35,7 +35,7 @@ xs = [idx % n_cols for idx in range(total)] ys = [n_rows - 1 - idx // n_cols for idx in range(total)] -dot_colors = [OKABE_ITO[c] for c in dot_cats] +dot_colors = [IMPRINT[c] for c in dot_cats] # Landscape figure: dot grid occupies a square central column; legend sits below fig, ax = plt.subplots(figsize=(16, 10), facecolor=PAGE_BG) @@ -53,7 +53,7 @@ # Legend — three items in one row, centred below the grid handles = [ - mpatches.Patch(color=OKABE_ITO[i], label=f"{categories[i]} — {counts[i]} / {total}") + mpatches.Patch(color=IMPRINT[i], label=f"{categories[i]} — {counts[i]} / {total}") for i in range(len(categories)) ] leg = ax.legend( diff --git a/plots/drawdown-basic/implementations/python/matplotlib.py b/plots/drawdown-basic/implementations/python/matplotlib.py index ed1334d67f..c270772799 100644 --- a/plots/drawdown-basic/implementations/python/matplotlib.py +++ b/plots/drawdown-basic/implementations/python/matplotlib.py @@ -22,7 +22,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Semantic palette: red for drawdown (loss), green for recovery (gain) -DRAWDOWN_COLOR = "#B71D27" # anyplot palette position 3 +DRAWDOWN_COLOR = "#AE3030" # anyplot palette position 3 RECOVERY_COLOR = "#009E73" # anyplot palette position 1 # Data — 2 years of simulated daily portfolio values with multiple drawdown cycles diff --git a/plots/dumbbell-basic/implementations/python/matplotlib.py b/plots/dumbbell-basic/implementations/python/matplotlib.py index 51f9431c60..9294f0499f 100644 --- a/plots/dumbbell-basic/implementations/python/matplotlib.py +++ b/plots/dumbbell-basic/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ RULE = (0.10, 0.10, 0.09, 0.18) if THEME == "light" else (0.94, 0.94, 0.91, 0.22) BEFORE_COLOR = "#009E73" # Okabe-Ito 1 — first series -AFTER_COLOR = "#D55E00" # Okabe-Ito 2 +AFTER_COLOR = "#C475FD" # Okabe-Ito 2 # Data: Employee satisfaction scores before and after workplace policy changes categories = ["Engineering", "Marketing", "Sales", "HR", "Finance", "Operations", "Customer Support", "Product"] diff --git a/plots/facet-grid/implementations/python/matplotlib.py b/plots/facet-grid/implementations/python/matplotlib.py index 263af25d8e..7fd74cebdf 100644 --- a/plots/facet-grid/implementations/python/matplotlib.py +++ b/plots/facet-grid/implementations/python/matplotlib.py @@ -29,11 +29,11 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Okabe-Ito palette - first series is always #009E73 -OKABE_ITO = [ +IMPRINT = [ "#009E73", # brand green - "#D55E00", # vermillion - "#0072B2", # blue - "#CC79A7", # reddish purple + "#C475FD", # vermillion + "#4467A3", # blue + "#BD8233", # reddish purple ] # Data @@ -68,7 +68,7 @@ ) # Color map: regions to Okabe-Ito palette -region_colors = {region: OKABE_ITO[i] for i, region in enumerate(regions)} +region_colors = {region: IMPRINT[i] for i, region in enumerate(regions)} # Create scatter plots in each facet with trend lines for i, region in enumerate(regions): diff --git a/plots/forest-basic/implementations/python/matplotlib.py b/plots/forest-basic/implementations/python/matplotlib.py index 2160f08763..4e9b54bae9 100644 --- a/plots/forest-basic/implementations/python/matplotlib.py +++ b/plots/forest-basic/implementations/python/matplotlib.py @@ -22,7 +22,7 @@ # Okabe-Ito palette OKABE_ITO_1 = "#009E73" # Primary data color (brand green) -OKABE_ITO_2 = "#D55E00" # Secondary +OKABE_ITO_2 = "#C475FD" # Secondary # Data: Meta-analysis of RCTs comparing treatment efficacy (standardized mean difference) studies = [ diff --git a/plots/frequency-polygon-basic/implementations/python/matplotlib.py b/plots/frequency-polygon-basic/implementations/python/matplotlib.py index 87cf2f387e..6f71a07880 100644 --- a/plots/frequency-polygon-basic/implementations/python/matplotlib.py +++ b/plots/frequency-polygon-basic/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (positions 1-3) -COLORS = ["#009E73", "#D55E00", "#0072B2"] +COLORS = ["#009E73", "#C475FD", "#4467A3"] # Data - Heights by age group np.random.seed(42) diff --git a/plots/funnel-basic/implementations/python/matplotlib.py b/plots/funnel-basic/implementations/python/matplotlib.py index ab6663cf85..253479fe1f 100644 --- a/plots/funnel-basic/implementations/python/matplotlib.py +++ b/plots/funnel-basic/implementations/python/matplotlib.py @@ -17,8 +17,8 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette — first stage is brand green (#009E73) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00"] -# Light orange #E69F00 needs dark text; pick INK so the label also +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"] +# Light orange #AE3030 needs dark text; pick INK so the label also # stays legible where it overflows the narrow bottom segment. TEXT_ON_FILL = ["white", "white", "white", "white", INK] @@ -48,7 +48,7 @@ y = np.array([y_top, y_bot]) x_left = np.array([-w_top / 2, -w_bot / 2]) x_right = np.array([w_top / 2, w_bot / 2]) - ax.fill_betweenx(y, x_left, x_right, facecolor=OKABE_ITO[i], edgecolor=PAGE_BG, linewidth=2) + ax.fill_betweenx(y, x_left, x_right, facecolor=IMPRINT[i], edgecolor=PAGE_BG, linewidth=2) y_mid = (y_top + y_bot) / 2 pct = (values[i] / max_value) * 100 diff --git a/plots/gain-curve/implementations/python/matplotlib.py b/plots/gain-curve/implementations/python/matplotlib.py index 7e00429386..4bb4ed428d 100644 --- a/plots/gain-curve/implementations/python/matplotlib.py +++ b/plots/gain-curve/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" BRAND = "#009E73" # Okabe-Ito position 1 -SECONDARY = "#D55E00" # Okabe-Ito position 2 +SECONDARY = "#C475FD" # Okabe-Ito position 2 NEUTRAL = INK_MUTED # Data: Generate synthetic classification data (customer response model) diff --git a/plots/gantt-basic/implementations/python/matplotlib.py b/plots/gantt-basic/implementations/python/matplotlib.py index 27a81e618a..d56bfe04e6 100644 --- a/plots/gantt-basic/implementations/python/matplotlib.py +++ b/plots/gantt-basic/implementations/python/matplotlib.py @@ -21,7 +21,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Okabe-Ito palette for categories -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD"] # Data - Software Development Project tasks = [ @@ -66,10 +66,10 @@ # Category to color mapping using Okabe-Ito palette category_colors = { - "Planning": OKABE_ITO[0], - "Development": OKABE_ITO[1], - "Testing": OKABE_ITO[2], - "Deployment": OKABE_ITO[3], + "Planning": IMPRINT[0], + "Development": IMPRINT[1], + "Testing": IMPRINT[2], + "Deployment": IMPRINT[3], } # Plot diff --git a/plots/gauge-basic/implementations/python/matplotlib.py b/plots/gauge-basic/implementations/python/matplotlib.py index 7b386d1ec8..2edd35e0b8 100644 --- a/plots/gauge-basic/implementations/python/matplotlib.py +++ b/plots/gauge-basic/implementations/python/matplotlib.py @@ -20,8 +20,8 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Okabe-Ito zone colors (colorblind-safe red/yellow/green) -ZONE_BAD = "#D55E00" # vermillion -ZONE_WARN = "#E69F00" # orange +ZONE_BAD = "#C475FD" # vermillion +ZONE_WARN = "#AE3030" # orange ZONE_GOOD = "#009E73" # bluish green (brand) # Data diff --git a/plots/heatmap-adjacency/implementations/python/matplotlib.py b/plots/heatmap-adjacency/implementations/python/matplotlib.py index eb0a85fdf7..c6588be0b5 100644 --- a/plots/heatmap-adjacency/implementations/python/matplotlib.py +++ b/plots/heatmap-adjacency/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" -COMM_COLORS = ["#009E73", "#D55E00", "#0072B2"] # Okabe-Ito positions 1–3 +COMM_COLORS = ["#009E73", "#C475FD", "#4467A3"] # Okabe-Ito positions 1–3 # Data: research collaboration network with 3 communities np.random.seed(42) diff --git a/plots/heatmap-geographic/implementations/python/matplotlib.py b/plots/heatmap-geographic/implementations/python/matplotlib.py index c0f110c0d9..60738a3820 100644 --- a/plots/heatmap-geographic/implementations/python/matplotlib.py +++ b/plots/heatmap-geographic/implementations/python/matplotlib.py @@ -125,7 +125,7 @@ ) # Sensor locations (slightly more visible than before) -ax.scatter(longitudes, latitudes, s=25, c="#0072B2", alpha=0.45, edgecolors="none", label="Sensor Locations") +ax.scatter(longitudes, latitudes, s=25, c="#4467A3", alpha=0.45, edgecolors="none", label="Sensor Locations") # Colorbar cbar = plt.colorbar(im, ax=ax, shrink=0.8, pad=0.02) diff --git a/plots/hierarchy-toggle-view/implementations/python/matplotlib.py b/plots/hierarchy-toggle-view/implementations/python/matplotlib.py index 32b6b950b6..ff764c86db 100644 --- a/plots/hierarchy-toggle-view/implementations/python/matplotlib.py +++ b/plots/hierarchy-toggle-view/implementations/python/matplotlib.py @@ -17,7 +17,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" -COLORS = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +COLORS = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data: Corporate Annual Budget ($M) budget = { diff --git a/plots/histogram-cumulative/implementations/python/matplotlib.py b/plots/histogram-cumulative/implementations/python/matplotlib.py index 533b2a09db..a1d3ebace3 100644 --- a/plots/histogram-cumulative/implementations/python/matplotlib.py +++ b/plots/histogram-cumulative/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" BRAND = "#009E73" # Okabe-Ito position 1 -ACCENT = "#D55E00" # Okabe-Ito position 2 for reference lines +ACCENT = "#C475FD" # Okabe-Ito position 2 for reference lines # Data - exam scores with realistic distribution np.random.seed(42) diff --git a/plots/histogram-density/implementations/python/matplotlib.py b/plots/histogram-density/implementations/python/matplotlib.py index d8e94a33d5..fc3f7bb65a 100644 --- a/plots/histogram-density/implementations/python/matplotlib.py +++ b/plots/histogram-density/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ # Okabe-Ito palette BRAND = "#009E73" # First categorical series -ACCENT = "#D55E00" # Second color for overlay +ACCENT = "#C475FD" # Second color for overlay # Data: Generate realistic test score data with a normal distribution np.random.seed(42) diff --git a/plots/histogram-kde/implementations/python/matplotlib.py b/plots/histogram-kde/implementations/python/matplotlib.py index aef4f0290a..4fb706ce13 100644 --- a/plots/histogram-kde/implementations/python/matplotlib.py +++ b/plots/histogram-kde/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" -ACCENT = "#D55E00" +ACCENT = "#C475FD" # Data - simulate stock daily returns (realistic financial data) np.random.seed(42) diff --git a/plots/histogram-overlapping/implementations/python/matplotlib.py b/plots/histogram-overlapping/implementations/python/matplotlib.py index 1d38842393..81762416a5 100644 --- a/plots/histogram-overlapping/implementations/python/matplotlib.py +++ b/plots/histogram-overlapping/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito colors (first series is always #009E73) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3"] # Data - Comparing salary distributions across three departments np.random.seed(42) @@ -40,9 +40,9 @@ bins = np.linspace(20000, 150000, 35) # Plot overlapping histograms with transparency -ax.hist(engineering, bins=bins, alpha=0.5, label="Engineering", color=OKABE_ITO[0], edgecolor=INK_SOFT, linewidth=1.5) -ax.hist(marketing, bins=bins, alpha=0.5, label="Marketing", color=OKABE_ITO[1], edgecolor=INK_SOFT, linewidth=1.5) -ax.hist(sales, bins=bins, alpha=0.5, label="Sales", color=OKABE_ITO[2], edgecolor=INK_SOFT, linewidth=1.5) +ax.hist(engineering, bins=bins, alpha=0.5, label="Engineering", color=IMPRINT[0], edgecolor=INK_SOFT, linewidth=1.5) +ax.hist(marketing, bins=bins, alpha=0.5, label="Marketing", color=IMPRINT[1], edgecolor=INK_SOFT, linewidth=1.5) +ax.hist(sales, bins=bins, alpha=0.5, label="Sales", color=IMPRINT[2], edgecolor=INK_SOFT, linewidth=1.5) # Labels and styling ax.set_xlabel("Annual Salary ($)", fontsize=20, color=INK) diff --git a/plots/histogram-returns-distribution/implementations/python/matplotlib.py b/plots/histogram-returns-distribution/implementations/python/matplotlib.py index 579e611214..9905022927 100644 --- a/plots/histogram-returns-distribution/implementations/python/matplotlib.py +++ b/plots/histogram-returns-distribution/implementations/python/matplotlib.py @@ -21,8 +21,8 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" BRAND = "#009E73" # Okabe-Ito pos 1 — main histogram bars -TAIL_COLOR = "#D55E00" # Okabe-Ito pos 2 — tail regions -CURVE_COLOR = "#0072B2" # Okabe-Ito pos 3 — normal distribution curve +TAIL_COLOR = "#C475FD" # Okabe-Ito pos 2 — tail regions +CURVE_COLOR = "#4467A3" # Okabe-Ito pos 3 — normal distribution curve # Data — simulate daily stock returns with slight fat tails (t-distribution df=8) np.random.seed(42) diff --git a/plots/histogram-stacked/implementations/python/matplotlib.py b/plots/histogram-stacked/implementations/python/matplotlib.py index 3dd2a45ce7..bc6e380c5e 100644 --- a/plots/histogram-stacked/implementations/python/matplotlib.py +++ b/plots/histogram-stacked/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette - using first 3 colors in canonical order -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3"] # Data - Response times (ms) for three different server regions np.random.seed(42) @@ -41,7 +41,7 @@ [us_east, europe, asia], bins=20, stacked=True, - color=OKABE_ITO, + color=IMPRINT, label=labels, edgecolor=PAGE_BG, linewidth=0.5, diff --git a/plots/histogram-stepwise/implementations/python/matplotlib.py b/plots/histogram-stepwise/implementations/python/matplotlib.py index eea063e82a..88df5f17b9 100644 --- a/plots/histogram-stepwise/implementations/python/matplotlib.py +++ b/plots/histogram-stepwise/implementations/python/matplotlib.py @@ -25,7 +25,7 @@ # Okabe-Ito palette BRAND = "#009E73" # Position 1 -ACCENT = "#D55E00" # Position 2 +ACCENT = "#C475FD" # Position 2 # Data - Two distributions with distinct shapes np.random.seed(42) diff --git a/plots/hive-basic/implementations/python/matplotlib.py b/plots/hive-basic/implementations/python/matplotlib.py index 7963402230..cf7d774d24 100644 --- a/plots/hive-basic/implementations/python/matplotlib.py +++ b/plots/hive-basic/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette for axes (first 3 colors for 3 categories) -AXIS_COLORS = ["#009E73", "#D55E00", "#0072B2"] +AXIS_COLORS = ["#009E73", "#C475FD", "#4467A3"] # Create software module dependency network data np.random.seed(42) diff --git a/plots/horizon-basic/implementations/python/matplotlib.py b/plots/horizon-basic/implementations/python/matplotlib.py index 59344c0961..a9006b1283 100644 --- a/plots/horizon-basic/implementations/python/matplotlib.py +++ b/plots/horizon-basic/implementations/python/matplotlib.py @@ -46,7 +46,7 @@ # Color bases with theme-adaptive styling pos_base = mcolors.to_rgb("#009E73") # Okabe-Ito brand green for positive -neg_base = mcolors.to_rgb("#D55E00") # Okabe-Ito vermillion for negative +neg_base = mcolors.to_rgb("#C475FD") # Okabe-Ito vermillion for negative # Create figure fig, axes = plt.subplots(n_series, 1, figsize=(16, 9), sharex=True, facecolor=PAGE_BG) diff --git a/plots/ice-basic/implementations/python/matplotlib.py b/plots/ice-basic/implementations/python/matplotlib.py index fdb17b9f89..dabc03b87e 100644 --- a/plots/ice-basic/implementations/python/matplotlib.py +++ b/plots/ice-basic/implementations/python/matplotlib.py @@ -20,7 +20,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" BRAND = "#009E73" # Okabe-Ito position 1 — ICE individual lines -ACCENT = "#D55E00" # Okabe-Ito position 2 — PDP average line +ACCENT = "#C475FD" # Okabe-Ito position 2 — PDP average line # Data: synthetic housing dataset np.random.seed(42) diff --git a/plots/indicator-bollinger/implementations/python/matplotlib.py b/plots/indicator-bollinger/implementations/python/matplotlib.py index b0b1abdccf..65316420fe 100644 --- a/plots/indicator-bollinger/implementations/python/matplotlib.py +++ b/plots/indicator-bollinger/implementations/python/matplotlib.py @@ -20,8 +20,8 @@ # Okabe-Ito palette - first color is always brand green BRAND = "#009E73" -SECONDARY = "#D55E00" -TERTIARY = "#0072B2" +SECONDARY = "#C475FD" +TERTIARY = "#4467A3" # Data - Generate realistic stock price data with Bollinger Bands np.random.seed(42) diff --git a/plots/indicator-ema/implementations/python/matplotlib.py b/plots/indicator-ema/implementations/python/matplotlib.py index e291dd5705..e42a954b2d 100644 --- a/plots/indicator-ema/implementations/python/matplotlib.py +++ b/plots/indicator-ema/implementations/python/matplotlib.py @@ -22,9 +22,9 @@ # Okabe-Ito colors for EMAs EMA_SHORT_COLOR = "#009E73" # Okabe-Ito pos 1 — 12-day EMA -EMA_LONG_COLOR = "#D55E00" # Okabe-Ito pos 2 — 26-day EMA -GOLDEN_COLOR = "#E69F00" # Okabe-Ito pos 5 — bullish crossover -DEATH_COLOR = "#CC79A7" # Okabe-Ito pos 4 — bearish crossover +EMA_LONG_COLOR = "#C475FD" # Okabe-Ito pos 2 — 26-day EMA +GOLDEN_COLOR = "#AE3030" # Okabe-Ito pos 5 — bullish crossover +DEATH_COLOR = "#BD8233" # Okabe-Ito pos 4 — bearish crossover # Data np.random.seed(42) diff --git a/plots/indicator-macd/implementations/python/matplotlib.py b/plots/indicator-macd/implementations/python/matplotlib.py index 7db22ba626..59ef870768 100644 --- a/plots/indicator-macd/implementations/python/matplotlib.py +++ b/plots/indicator-macd/implementations/python/matplotlib.py @@ -23,10 +23,10 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette -MACD_COLOR = "#0072B2" # Blue (position 3) -SIGNAL_COLOR = "#E69F00" # Orange (position 5) +MACD_COLOR = "#4467A3" # Blue (position 3) +SIGNAL_COLOR = "#AE3030" # Orange (position 5) POSITIVE_COLOR = "#009E73" # Green (position 1) -NEGATIVE_COLOR = "#D55E00" # Red-orange (position 2) +NEGATIVE_COLOR = "#C475FD" # Red-orange (position 2) # Generate synthetic stock price data and calculate MACD np.random.seed(42) diff --git a/plots/indicator-rsi/implementations/python/matplotlib.py b/plots/indicator-rsi/implementations/python/matplotlib.py index fd75211045..de5904d7bd 100644 --- a/plots/indicator-rsi/implementations/python/matplotlib.py +++ b/plots/indicator-rsi/implementations/python/matplotlib.py @@ -66,10 +66,10 @@ ax.set_facecolor(PAGE_BG) # Shade overbought zone (70-100) -ax.fill_between(df["date"], 70, 100, alpha=0.12, color="#D55E00") +ax.fill_between(df["date"], 70, 100, alpha=0.12, color="#C475FD") # Shade oversold zone (0-30) -ax.fill_between(df["date"], 0, 30, alpha=0.12, color="#0072B2") +ax.fill_between(df["date"], 0, 30, alpha=0.12, color="#4467A3") # Plot RSI line using brand color ax.plot(df["date"], df["rsi"], color=BRAND, linewidth=3, label="RSI (14-period)") diff --git a/plots/indicator-sma/implementations/python/matplotlib.py b/plots/indicator-sma/implementations/python/matplotlib.py index 8241f96e32..971f166cab 100644 --- a/plots/indicator-sma/implementations/python/matplotlib.py +++ b/plots/indicator-sma/implementations/python/matplotlib.py @@ -21,9 +21,9 @@ # Okabe-Ito palette — canonical order PRICE_COLOR = "#009E73" # position 1 — price line (first series) -SMA20_COLOR = "#D55E00" # position 2 -SMA50_COLOR = "#0072B2" # position 3 -SMA200_COLOR = "#CC79A7" # position 4 +SMA20_COLOR = "#C475FD" # position 2 +SMA50_COLOR = "#4467A3" # position 3 +SMA200_COLOR = "#BD8233" # position 4 # Data - realistic stock price data with trend and volatility np.random.seed(42) diff --git a/plots/kagi-basic/implementations/python/matplotlib.py b/plots/kagi-basic/implementations/python/matplotlib.py index f7c8322e0e..e360710845 100644 --- a/plots/kagi-basic/implementations/python/matplotlib.py +++ b/plots/kagi-basic/implementations/python/matplotlib.py @@ -21,7 +21,7 @@ # Data colors (Okabe-Ito palette, theme-independent) COLOR_YANG = "#009E73" # Okabe-Ito position 1 (green for bullish) -COLOR_YIN = "#D55E00" # Okabe-Ito position 2 (red for bearish) +COLOR_YIN = "#C475FD" # Okabe-Ito position 2 (red for bearish) # Generate synthetic stock price data np.random.seed(42) diff --git a/plots/learning-curve-basic/implementations/python/matplotlib.py b/plots/learning-curve-basic/implementations/python/matplotlib.py index ac333659b9..40573f3ad9 100644 --- a/plots/learning-curve-basic/implementations/python/matplotlib.py +++ b/plots/learning-curve-basic/implementations/python/matplotlib.py @@ -16,7 +16,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" -ACCENT = "#D55E00" +ACCENT = "#C475FD" # Data - Simulate learning curve for a model np.random.seed(42) diff --git a/plots/lift-curve/implementations/python/matplotlib.py b/plots/lift-curve/implementations/python/matplotlib.py index d66d1c55fb..806d13afbc 100644 --- a/plots/lift-curve/implementations/python/matplotlib.py +++ b/plots/lift-curve/implementations/python/matplotlib.py @@ -17,7 +17,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" # Okabe-Ito position 1 -ACCENT = "#E69F00" # Okabe-Ito position 5 for reference line +ACCENT = "#AE3030" # Okabe-Ito position 5 for reference line # Data - Simulate realistic customer response model predictions np.random.seed(42) diff --git a/plots/line-annotated-events/implementations/python/matplotlib.py b/plots/line-annotated-events/implementations/python/matplotlib.py index 2081a82907..016ee40f67 100644 --- a/plots/line-annotated-events/implementations/python/matplotlib.py +++ b/plots/line-annotated-events/implementations/python/matplotlib.py @@ -44,7 +44,7 @@ ax.plot(dates, prices, linewidth=3, color=BRAND, label="Stock Price", zorder=2) # Event markers with alternating heights -event_color = "#D55E00" if THEME == "light" else "#56B4E9" +event_color = "#C475FD" if THEME == "light" else "#2ABCCD" heights = [0.85, 0.70, 0.85, 0.70, 0.85] for i, (event_date, event_label) in enumerate(events): diff --git a/plots/line-confidence/implementations/python/matplotlib.py b/plots/line-confidence/implementations/python/matplotlib.py index f0b9e90aa9..56b9cc19cf 100644 --- a/plots/line-confidence/implementations/python/matplotlib.py +++ b/plots/line-confidence/implementations/python/matplotlib.py @@ -41,8 +41,8 @@ ax.set_facecolor(PAGE_BG) # Shaded confidence band (contrasting color with semi-transparent alpha) -# Use sky blue (#56B4E9) for the band to contrast with green line -ax.fill_between(days, y_lower, y_upper, alpha=0.25, color="#56B4E9", label="95% Confidence Interval") +# Use sky blue (#2ABCCD) for the band to contrast with green line +ax.fill_between(days, y_lower, y_upper, alpha=0.25, color="#2ABCCD", label="95% Confidence Interval") # Central trend line (prominent, brand green) ax.plot(days, y, color="#009E73", linewidth=3, label="Forecast Mean") diff --git a/plots/line-loss-training/implementations/python/matplotlib.py b/plots/line-loss-training/implementations/python/matplotlib.py index d4e89f90ea..768fe3c15d 100644 --- a/plots/line-loss-training/implementations/python/matplotlib.py +++ b/plots/line-loss-training/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ # Okabe-Ito colors TRAINING_COLOR = "#009E73" -VALIDATION_COLOR = "#D55E00" +VALIDATION_COLOR = "#C475FD" # Data np.random.seed(42) diff --git a/plots/line-multi/implementations/python/matplotlib.py b/plots/line-multi/implementations/python/matplotlib.py index eaa6b67219..fd1d0a54e0 100644 --- a/plots/line-multi/implementations/python/matplotlib.py +++ b/plots/line-multi/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Okabe-Ito palette -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3"] # Data: Monthly sales (in thousands) for 3 product lines over 12 months np.random.seed(42) @@ -46,7 +46,7 @@ ax.plot( months, product_a, - color=OKABE_ITO[0], + color=IMPRINT[0], linewidth=3, marker="o", markersize=10, @@ -56,7 +56,7 @@ ax.plot( months, product_b, - color=OKABE_ITO[1], + color=IMPRINT[1], linewidth=3, marker="s", markersize=10, @@ -66,7 +66,7 @@ ax.plot( months, product_c, - color=OKABE_ITO[2], + color=IMPRINT[2], linewidth=3, marker="^", markersize=10, diff --git a/plots/line-stepwise/implementations/python/matplotlib.py b/plots/line-stepwise/implementations/python/matplotlib.py index caaf575f16..941bd9dc32 100644 --- a/plots/line-stepwise/implementations/python/matplotlib.py +++ b/plots/line-stepwise/implementations/python/matplotlib.py @@ -20,7 +20,7 @@ # Okabe-Ito palette (first series is always brand green) BRAND = "#009E73" -SECONDARY = "#D55E00" +SECONDARY = "#C475FD" # Data: Server capacity levels over monitoring period np.random.seed(42) diff --git a/plots/line-stock-comparison/implementations/python/matplotlib.py b/plots/line-stock-comparison/implementations/python/matplotlib.py index 4e10bc713e..c4426c0989 100644 --- a/plots/line-stock-comparison/implementations/python/matplotlib.py +++ b/plots/line-stock-comparison/implementations/python/matplotlib.py @@ -29,7 +29,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # anyplot palette — canonical order -ANYPLOT_PALETTE = ["#009E73", "#9418DB", "#B71D27", "#16B8F3"] +IMPRINT = ["#009E73", "#C475FD", "#AE3030", "#4467A3"] # Data — simulated daily stock prices for ~1 year (252 trading days) np.random.seed(42) @@ -53,7 +53,7 @@ fig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG) ax.set_facecolor(PAGE_BG) -for (symbol, values), color in zip(rebased.items(), ANYPLOT_PALETTE, strict=False): +for (symbol, values), color in zip(rebased.items(), IMPRINT, strict=False): # SPY is the benchmark — thicker line for emphasis and visual hierarchy lw = 3.0 if symbol == "SPY" else 2.0 ax.plot(dates, values, label=symbol, color=color, linewidth=lw) @@ -63,7 +63,7 @@ # Annotate SPY as benchmark to create visual hierarchy and storytelling focal point spy_final = rebased["SPY"][-1] -spy_color = ANYPLOT_PALETTE[3] # SPY is 4th series (#16B8F3) +spy_color = IMPRINT[3] # SPY is 4th series (#4467A3) ax.annotate( "Benchmark", xy=(dates[-1], spy_final), diff --git a/plots/line-styled/implementations/python/matplotlib.py b/plots/line-styled/implementations/python/matplotlib.py index 08bb0e2fc0..454ae284d5 100644 --- a/plots/line-styled/implementations/python/matplotlib.py +++ b/plots/line-styled/implementations/python/matplotlib.py @@ -17,7 +17,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette -COLORS = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +COLORS = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data - CPU performance benchmarks over time np.random.seed(42) diff --git a/plots/line-timeseries-rolling/implementations/python/matplotlib.py b/plots/line-timeseries-rolling/implementations/python/matplotlib.py index 3466ad60c5..38ee72698d 100644 --- a/plots/line-timeseries-rolling/implementations/python/matplotlib.py +++ b/plots/line-timeseries-rolling/implementations/python/matplotlib.py @@ -40,7 +40,7 @@ ax.set_facecolor(PAGE_BG) # Raw data - thin, semi-transparent line (secondary Okabe-Ito color) -ax.plot(df["date"], df["temperature"], linewidth=1, alpha=0.4, color="#D55E00", label="Daily Temperature") +ax.plot(df["date"], df["temperature"], linewidth=1, alpha=0.4, color="#C475FD", label="Daily Temperature") # Rolling average - prominent smooth line (brand green) ax.plot(df["date"], df["rolling_avg"], linewidth=3.5, color="#009E73", label="7-Day Rolling Average") diff --git a/plots/linked-views-selection/implementations/python/matplotlib.py b/plots/linked-views-selection/implementations/python/matplotlib.py index c92d1dc889..ec64ec1166 100644 --- a/plots/linked-views-selection/implementations/python/matplotlib.py +++ b/plots/linked-views-selection/implementations/python/matplotlib.py @@ -21,8 +21,8 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] -BRAND = OKABE_ITO[0] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] +BRAND = IMPRINT[0] # Data np.random.seed(42) diff --git a/plots/logistic-regression/implementations/python/matplotlib.py b/plots/logistic-regression/implementations/python/matplotlib.py index 7a54a73ea6..17226c3f7b 100644 --- a/plots/logistic-regression/implementations/python/matplotlib.py +++ b/plots/logistic-regression/implementations/python/matplotlib.py @@ -25,7 +25,7 @@ # Okabe-Ito palette (first series is brand green) BRAND = "#009E73" -SECONDARY = "#D55E00" +SECONDARY = "#C475FD" # Data - Credit risk scoring: probability of loan approval based on credit score np.random.seed(42) diff --git a/plots/manhattan-gwas/implementations/python/matplotlib.py b/plots/manhattan-gwas/implementations/python/matplotlib.py index 4321149853..beca93a199 100644 --- a/plots/manhattan-gwas/implementations/python/matplotlib.py +++ b/plots/manhattan-gwas/implementations/python/matplotlib.py @@ -20,7 +20,7 @@ BRAND = "#009E73" # Okabe-Ito palette for chromosome alternation -CHROM_COLORS = ["#009E73", "#D55E00"] # Brand green and vermillion, alternating +CHROM_COLORS = ["#009E73", "#C475FD"] # Brand green and vermillion, alternating # Data - Simulate GWAS results for 22 chromosomes np.random.seed(42) diff --git a/plots/map-drilldown-geographic/implementations/python/matplotlib.py b/plots/map-drilldown-geographic/implementations/python/matplotlib.py index ab475f5ec7..5d2f75c919 100644 --- a/plots/map-drilldown-geographic/implementations/python/matplotlib.py +++ b/plots/map-drilldown-geographic/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" BOX_TEXT = "#F0EFE8" # light text on dark anyplot_seq boxes (good contrast on both ends) -HIGHLIGHT = "#9418DB" # anyplot palette position 2 — selected-region indicator +HIGHLIGHT = "#C475FD" # anyplot palette position 2 — selected-region indicator # anyplot sequential colormap: green → dark azure (single-polarity continuous) anyplot_seq = LinearSegmentedColormap.from_list("anyplot_seq", ["#009E73", "#003D94"]) diff --git a/plots/map-marker-clustered/implementations/python/matplotlib.py b/plots/map-marker-clustered/implementations/python/matplotlib.py index 8db713226c..913e7f49e9 100644 --- a/plots/map-marker-clustered/implementations/python/matplotlib.py +++ b/plots/map-marker-clustered/implementations/python/matplotlib.py @@ -26,7 +26,7 @@ # anyplot categorical palette (positions 1-3) cat_names = ["Retail", "Grocery", "Electronics"] -cat_colors = {"Retail": "#009E73", "Grocery": "#9418DB", "Electronics": "#B71D27"} +cat_colors = {"Retail": "#009E73", "Grocery": "#C475FD", "Electronics": "#AE3030"} # Data: European store locations clustered by city np.random.seed(42) diff --git a/plots/map-projections/implementations/python/matplotlib.py b/plots/map-projections/implementations/python/matplotlib.py index 2959b4a46e..9c9ebea0b5 100644 --- a/plots/map-projections/implementations/python/matplotlib.py +++ b/plots/map-projections/implementations/python/matplotlib.py @@ -29,7 +29,7 @@ # Map colors (theme-adaptive; semantic: land/ocean are not data series) LAND_COLOR = "#E8E4D9" if THEME == "light" else "#3A3830" OCEAN_COLOR = "#A8D5E5" if THEME == "light" else "#1A3348" -TISSOT_COLOR = "#B71D27" # anyplot palette position 3 (red) +TISSOT_COLOR = "#AE3030" # anyplot palette position 3 (red) # Projections to compare projections = [ diff --git a/plots/map-route-path/implementations/python/matplotlib.py b/plots/map-route-path/implementations/python/matplotlib.py index d137ab228f..f94870895e 100644 --- a/plots/map-route-path/implementations/python/matplotlib.py +++ b/plots/map-route-path/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ # Okabe-Ito positions for start/end markers START_COLOR = "#009E73" # position 1 — green -END_COLOR = "#D55E00" # position 2 — vermillion +END_COLOR = "#C475FD" # position 2 — vermillion # Data: Simulated hiking trail GPS track (San Francisco coastal path) np.random.seed(42) diff --git a/plots/marimekko-basic/implementations/python/matplotlib.py b/plots/marimekko-basic/implementations/python/matplotlib.py index fd55caf25b..11b9a317e1 100644 --- a/plots/marimekko-basic/implementations/python/matplotlib.py +++ b/plots/marimekko-basic/implementations/python/matplotlib.py @@ -34,8 +34,8 @@ ) # Okabe-Ito palette (positions 1-4 in canonical order) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] -# Smart label contrast: white on dark segments, ink on lighter #CC79A7 +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] +# Smart label contrast: white on dark segments, ink on lighter #BD8233 label_colors = ["white", "white", "white", INK] # Calculate bar widths (proportional to column totals) @@ -48,7 +48,7 @@ fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) ax.set_facecolor(PAGE_BG) -for i, (product, color, label_color) in enumerate(zip(products, OKABE_ITO, label_colors, strict=True)): +for i, (product, color, label_color) in enumerate(zip(products, IMPRINT, label_colors, strict=True)): heights = values[i] / column_totals bottoms = values[:i].sum(axis=0) / column_totals if i > 0 else np.zeros(len(regions)) diff --git a/plots/maze-circular/implementations/python/matplotlib.py b/plots/maze-circular/implementations/python/matplotlib.py index f7d570429d..21dc62588f 100644 --- a/plots/maze-circular/implementations/python/matplotlib.py +++ b/plots/maze-circular/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" GOAL_COLOR = "#009E73" # Okabe-Ito position 1 -ENTRY_COLOR = "#D55E00" # Okabe-Ito position 2 +ENTRY_COLOR = "#C475FD" # Okabe-Ito position 2 # Data — Fibonacci sector progression for naturally varied corridor widths np.random.seed(13) diff --git a/plots/mosaic-categorical/implementations/python/matplotlib.py b/plots/mosaic-categorical/implementations/python/matplotlib.py index 67a4cbbb86..5f1183c578 100644 --- a/plots/mosaic-categorical/implementations/python/matplotlib.py +++ b/plots/mosaic-categorical/implementations/python/matplotlib.py @@ -23,7 +23,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette — first series always #009E73 -CELL_COLORS = {"Survived": "#009E73", "Did Not Survive": "#D55E00"} +CELL_COLORS = {"Survived": "#009E73", "Did Not Survive": "#C475FD"} # Data: Titanic passenger survival by class (realistic proportions) counts = { diff --git a/plots/network-basic/implementations/python/matplotlib.py b/plots/network-basic/implementations/python/matplotlib.py index afb8ad9c49..8e74b6b8d0 100644 --- a/plots/network-basic/implementations/python/matplotlib.py +++ b/plots/network-basic/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette for 4 departments -GROUP_COLORS = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +GROUP_COLORS = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] GROUP_NAMES = ["Engineering", "Research", "Marketing", "Design"] # Data: social network of 20 people across 4 company departments @@ -141,7 +141,7 @@ fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) ax.set_facecolor(PAGE_BG) -BRIDGE_COLOR = "#E69F00" # Okabe-Ito amber — distinct cross-department highlight +BRIDGE_COLOR = "#AE3030" # Okabe-Ito amber — distinct cross-department highlight # Draw curved edges using FancyArrowPatch for src, tgt in edges: diff --git a/plots/network-bipartite/implementations/python/matplotlib.py b/plots/network-bipartite/implementations/python/matplotlib.py index ba48dfc885..aae4138c3a 100644 --- a/plots/network-bipartite/implementations/python/matplotlib.py +++ b/plots/network-bipartite/implementations/python/matplotlib.py @@ -17,7 +17,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" # Okabe-Ito pos 1 — researchers -ACCENT = "#D55E00" # Okabe-Ito pos 2 — papers +ACCENT = "#C475FD" # Okabe-Ito pos 2 — papers # Data — researcher-paper affiliation network (bibliometrics) np.random.seed(42) diff --git a/plots/network-directed/implementations/python/matplotlib.py b/plots/network-directed/implementations/python/matplotlib.py index e1314931c5..a04bb5b54e 100644 --- a/plots/network-directed/implementations/python/matplotlib.py +++ b/plots/network-directed/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette for group colors (first 4 positions) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data: Software package dependencies (arrows show import direction) np.random.seed(42) @@ -68,7 +68,7 @@ ] # Group colors (Okabe-Ito) -group_colors = {"core": OKABE_ITO[0], "api": OKABE_ITO[1], "utils": OKABE_ITO[2], "tests": OKABE_ITO[3]} +group_colors = {"core": IMPRINT[0], "api": IMPRINT[1], "utils": IMPRINT[2], "tests": IMPRINT[3]} # Plot fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) diff --git a/plots/network-force-directed/implementations/python/matplotlib.py b/plots/network-force-directed/implementations/python/matplotlib.py index f128d701b6..8b91b0bcee 100644 --- a/plots/network-force-directed/implementations/python/matplotlib.py +++ b/plots/network-force-directed/implementations/python/matplotlib.py @@ -20,7 +20,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Okabe-Ito palette — first series is always #009E73 -COMMUNITY_COLORS = ["#009E73", "#D55E00", "#0072B2"] +COMMUNITY_COLORS = ["#009E73", "#C475FD", "#4467A3"] COMMUNITY_NAMES = ["Engineering", "Marketing", "Sales"] # Data: a 50-person company social network with 3 departments diff --git a/plots/network-hierarchical/implementations/python/matplotlib.py b/plots/network-hierarchical/implementations/python/matplotlib.py index dc244d798c..1baf32322c 100644 --- a/plots/network-hierarchical/implementations/python/matplotlib.py +++ b/plots/network-hierarchical/implementations/python/matplotlib.py @@ -25,7 +25,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Okabe-Ito palette -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data: Software Module Hierarchy (24 nodes, 4 levels) np.random.seed(42) @@ -106,10 +106,10 @@ # Use Okabe-Ito palette for levels level_colors = { - 0: OKABE_ITO[0], # Brand green (#009E73) - 1: OKABE_ITO[1], # Vermillion (#D55E00) - 2: OKABE_ITO[2], # Blue (#0072B2) - 3: OKABE_ITO[3], # Reddish purple (#CC79A7) + 0: IMPRINT[0], # Brand green (#009E73) + 1: IMPRINT[1], # Vermillion (#C475FD) + 2: IMPRINT[2], # Blue (#4467A3) + 3: IMPRINT[3], # Reddish purple (#BD8233) } level_names = ["Root Module", "Core Modules", "Sub-modules", "Leaf Modules"] diff --git a/plots/network-transport-static/implementations/python/matplotlib.py b/plots/network-transport-static/implementations/python/matplotlib.py index 935bbd6189..943f17ff34 100644 --- a/plots/network-transport-static/implementations/python/matplotlib.py +++ b/plots/network-transport-static/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette for route types -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477"] # Seed for reproducibility np.random.seed(42) @@ -192,7 +192,7 @@ curve = -0.2 # Get route color from Okabe-Ito palette based on type - color = OKABE_ITO[route["type"]] + color = IMPRINT[route["type"]] # Draw arrow arrow = mpatches.FancyArrowPatch( @@ -272,9 +272,9 @@ # Create legend legend_elements = [ - mpatches.Patch(facecolor=OKABE_ITO[0], edgecolor=OKABE_ITO[0], label="Regional Express (RE)"), - mpatches.Patch(facecolor=OKABE_ITO[1], edgecolor=OKABE_ITO[1], label="Airport Service (AIR)"), - mpatches.Patch(facecolor=OKABE_ITO[2], edgecolor=OKABE_ITO[2], label="S-Bahn (S)"), + mpatches.Patch(facecolor=IMPRINT[0], edgecolor=IMPRINT[0], label="Regional Express (RE)"), + mpatches.Patch(facecolor=IMPRINT[1], edgecolor=IMPRINT[1], label="Airport Service (AIR)"), + mpatches.Patch(facecolor=IMPRINT[2], edgecolor=IMPRINT[2], label="S-Bahn (S)"), ] leg = ax.legend(handles=legend_elements, loc="upper right", fontsize=16, framealpha=0.95) if leg: diff --git a/plots/network-weighted/implementations/python/matplotlib.py b/plots/network-weighted/implementations/python/matplotlib.py index c5f208ac25..9efd9340f5 100644 --- a/plots/network-weighted/implementations/python/matplotlib.py +++ b/plots/network-weighted/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito categorical palette (positions 1-4) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data: Trade network between countries (billions USD) np.random.seed(42) @@ -128,7 +128,7 @@ # Region names and group mapping region_names = ["Americas", "Europe", "Asia", "Oceania"] -group_colors = {i: OKABE_ITO[i] for i in range(4)} +group_colors = {i: IMPRINT[i] for i in range(4)} # Create plot fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) @@ -185,7 +185,7 @@ # Region legend legend_handles = [] for i, region in enumerate(region_names): - handle = ax.scatter([], [], s=400, c=OKABE_ITO[i], edgecolors=PAGE_BG, linewidths=2, label=region) + handle = ax.scatter([], [], s=400, c=IMPRINT[i], edgecolors=PAGE_BG, linewidths=2, label=region) legend_handles.append(handle) leg = ax.legend( diff --git a/plots/ohlc-bar/implementations/python/matplotlib.py b/plots/ohlc-bar/implementations/python/matplotlib.py index ff0c764bab..9806e37714 100644 --- a/plots/ohlc-bar/implementations/python/matplotlib.py +++ b/plots/ohlc-bar/implementations/python/matplotlib.py @@ -22,7 +22,7 @@ # Okabe-Ito palette COLOR_UP = "#009E73" # Brand green for up bars -COLOR_DOWN = "#D55E00" # Vermillion for down bars +COLOR_DOWN = "#C475FD" # Vermillion for down bars # Data - Generate 45 trading days of synthetic stock OHLC data np.random.seed(42) diff --git a/plots/parallel-categories-basic/implementations/python/matplotlib.py b/plots/parallel-categories-basic/implementations/python/matplotlib.py index 9694d7a17e..d13043e92b 100644 --- a/plots/parallel-categories-basic/implementations/python/matplotlib.py +++ b/plots/parallel-categories-basic/implementations/python/matplotlib.py @@ -39,7 +39,7 @@ } # Okabe-Ito palette for channel colors -colors = {"Online": "#009E73", "Store": "#D55E00", "Mobile": "#0072B2"} +colors = {"Online": "#009E73", "Store": "#C475FD", "Mobile": "#4467A3"} # Create figure fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) diff --git a/plots/parliament-basic/implementations/python/matplotlib.py b/plots/parliament-basic/implementations/python/matplotlib.py index 792a14ad83..a5d0c26804 100644 --- a/plots/parliament-basic/implementations/python/matplotlib.py +++ b/plots/parliament-basic/implementations/python/matplotlib.py @@ -17,7 +17,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (canonical order, starting with brand green) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477"] # Data - fictional parliament with 6 parties (400 total seats) parties = [ @@ -29,7 +29,7 @@ "Progressive Front", ] seats = [98, 87, 76, 65, 48, 26] -colors = OKABE_ITO[: len(parties)] +colors = IMPRINT[: len(parties)] total_seats = sum(seats) # Parliament layout parameters diff --git a/plots/pdp-basic/implementations/python/matplotlib.py b/plots/pdp-basic/implementations/python/matplotlib.py index b6501e395f..a36e8fa29a 100644 --- a/plots/pdp-basic/implementations/python/matplotlib.py +++ b/plots/pdp-basic/implementations/python/matplotlib.py @@ -20,7 +20,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" # Okabe-Ito position 1 -ACCENT = "#E69F00" # Okabe-Ito position 5 for rug plot +ACCENT = "#AE3030" # Okabe-Ito position 5 for rug plot # Data: Train a gradient boosting model and compute partial dependence np.random.seed(42) diff --git a/plots/phase-diagram/implementations/python/matplotlib.py b/plots/phase-diagram/implementations/python/matplotlib.py index 2f7377683e..dbb65b3763 100644 --- a/plots/phase-diagram/implementations/python/matplotlib.py +++ b/plots/phase-diagram/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ # Okabe-Ito palette BRAND = "#009E73" -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Damped harmonic oscillator: m*x'' + c*x' + k*x = 0 # Using underdamped solution: x(t) = A*exp(-gamma*t)*cos(omega_d*t + phi) @@ -50,10 +50,10 @@ # Plot each trajectory for i, (x, v, A, _phi) in enumerate(trajectories): # Plot trajectory line - ax.plot(x, v, color=OKABE_ITO[i], linewidth=2.5, alpha=0.8, label=f"Trajectory {i + 1} (A={A:.1f})") + ax.plot(x, v, color=IMPRINT[i], linewidth=2.5, alpha=0.8, label=f"Trajectory {i + 1} (A={A:.1f})") # Mark start point with larger marker - ax.scatter(x[0], v[0], s=250, color=OKABE_ITO[i], edgecolor=PAGE_BG, linewidth=2, zorder=5, marker="o") + ax.scatter(x[0], v[0], s=250, color=IMPRINT[i], edgecolor=PAGE_BG, linewidth=2, zorder=5, marker="o") # Add arrows to show direction along trajectory n_points = len(x) @@ -68,7 +68,7 @@ "", xy=(x[idx + 10], v[idx + 10]), xytext=(x[idx], v[idx]), - arrowprops={"arrowstyle": "->", "color": OKABE_ITO[i], "lw": 2.5, "mutation_scale": 20}, + arrowprops={"arrowstyle": "->", "color": IMPRINT[i], "lw": 2.5, "mutation_scale": 20}, ) # Mark the equilibrium point (stable fixed point at origin) diff --git a/plots/point-and-figure-basic/implementations/python/matplotlib.py b/plots/point-and-figure-basic/implementations/python/matplotlib.py index d7067cbb2a..52ff41bddb 100644 --- a/plots/point-and-figure-basic/implementations/python/matplotlib.py +++ b/plots/point-and-figure-basic/implementations/python/matplotlib.py @@ -27,7 +27,7 @@ # Okabe-Ito: green for rising (X), vermillion for falling (O) X_COLOR = "#009E73" -O_COLOR = "#D55E00" +O_COLOR = "#C475FD" # Data: synthetic stock price with upward drift np.random.seed(42) diff --git a/plots/polar-bar/implementations/python/matplotlib.py b/plots/polar-bar/implementations/python/matplotlib.py index 514f033253..7e0d349242 100644 --- a/plots/polar-bar/implementations/python/matplotlib.py +++ b/plots/polar-bar/implementations/python/matplotlib.py @@ -19,8 +19,8 @@ # Okabe-Ito palette (positions 1, 2, 3 for three series) BRAND = "#009E73" # Position 1 (bluish green) -ACCENT1 = "#D55E00" # Position 2 (vermillion) -ACCENT2 = "#0072B2" # Position 3 (blue) +ACCENT1 = "#C475FD" # Position 2 (vermillion) +ACCENT2 = "#4467A3" # Position 3 (blue) # Data - Wind direction frequencies (8 compass directions) np.random.seed(42) diff --git a/plots/polar-line/implementations/python/matplotlib.py b/plots/polar-line/implementations/python/matplotlib.py index 4cfbb9804e..780cbf3d79 100644 --- a/plots/polar-line/implementations/python/matplotlib.py +++ b/plots/polar-line/implementations/python/matplotlib.py @@ -22,7 +22,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" -ACCENT = "#D55E00" +ACCENT = "#C475FD" # Data - Simulating hourly temperature patterns for two seasons np.random.seed(42) diff --git a/plots/polar-scatter/implementations/python/matplotlib.py b/plots/polar-scatter/implementations/python/matplotlib.py index 846c8d057a..56766b1c45 100644 --- a/plots/polar-scatter/implementations/python/matplotlib.py +++ b/plots/polar-scatter/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette -COLORS = ["#009E73", "#D55E00", "#0072B2"] +COLORS = ["#009E73", "#C475FD", "#4467A3"] # Data - Wind measurement data with prevailing directions np.random.seed(42) diff --git a/plots/qq-basic/implementations/python/matplotlib.py b/plots/qq-basic/implementations/python/matplotlib.py index a67b4ec28c..6994413dd3 100644 --- a/plots/qq-basic/implementations/python/matplotlib.py +++ b/plots/qq-basic/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" # Okabe-Ito position 1 — scatter points -REF_COLOR = "#D55E00" # Okabe-Ito position 2 — reference line +REF_COLOR = "#C475FD" # Okabe-Ito position 2 — reference line # Data: systolic blood pressure (mmHg) from a mixed patient cohort # Bimodal mixture of normotensive (n=75) and hypertensive (n=25) patients diff --git a/plots/radar-basic/implementations/python/matplotlib.py b/plots/radar-basic/implementations/python/matplotlib.py index 6c272d8874..e5460ad225 100644 --- a/plots/radar-basic/implementations/python/matplotlib.py +++ b/plots/radar-basic/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" SERIES_1 = "#009E73" # Okabe-Ito position 1 -SERIES_2 = "#D55E00" # Okabe-Ito position 2 +SERIES_2 = "#C475FD" # Okabe-Ito position 2 # Data - Employee performance comparison across six competencies categories = ["Communication", "Technical Skills", "Teamwork", "Problem Solving", "Leadership", "Creativity"] diff --git a/plots/radar-multi/implementations/python/matplotlib.py b/plots/radar-multi/implementations/python/matplotlib.py index d9d777e8d3..40a0e55ecc 100644 --- a/plots/radar-multi/implementations/python/matplotlib.py +++ b/plots/radar-multi/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (first three series) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3"] # Data: Product comparison across key attributes categories = ["Performance", "Battery Life", "Camera", "Display", "Build Quality", "Value"] @@ -42,8 +42,8 @@ # Plot each product for idx, (product, values) in enumerate(products.items()): values_closed = values + values[:1] # Close the polygon - ax.plot(angles, values_closed, "o-", linewidth=3, label=product, color=OKABE_ITO[idx], markersize=10) - ax.fill(angles, values_closed, alpha=0.25, color=OKABE_ITO[idx]) + ax.plot(angles, values_closed, "o-", linewidth=3, label=product, color=IMPRINT[idx], markersize=10) + ax.fill(angles, values_closed, alpha=0.25, color=IMPRINT[idx]) # Set category labels at each axis ax.set_xticks(angles[:-1]) diff --git a/plots/renko-basic/implementations/python/matplotlib.py b/plots/renko-basic/implementations/python/matplotlib.py index 1b6487288d..ffbf46806e 100644 --- a/plots/renko-basic/implementations/python/matplotlib.py +++ b/plots/renko-basic/implementations/python/matplotlib.py @@ -20,7 +20,7 @@ # Okabe-Ito palette BULLISH = "#009E73" # Green for upward -BEARISH = "#D55E00" # Vermillion (reddish) for downward +BEARISH = "#C475FD" # Vermillion (reddish) for downward # Generate synthetic price data np.random.seed(42) diff --git a/plots/roc-curve/implementations/python/matplotlib.py b/plots/roc-curve/implementations/python/matplotlib.py index a80e37035c..68f381d28f 100644 --- a/plots/roc-curve/implementations/python/matplotlib.py +++ b/plots/roc-curve/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477"] # Data np.random.seed(42) @@ -45,14 +45,14 @@ fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) ax.set_facecolor(PAGE_BG) -ax.plot(fpr1, tpr1, color=OKABE_ITO[0], linewidth=3.5, label=f"Random Forest (AUC = {auc1:.2f})") -ax.plot(fpr2, tpr2, color=OKABE_ITO[1], linewidth=3.5, label=f"Logistic Regression (AUC = {auc2:.2f})") -ax.plot(fpr3, tpr3, color=OKABE_ITO[2], linewidth=3.5, label=f"Decision Tree (AUC = {auc3:.2f})") +ax.plot(fpr1, tpr1, color=IMPRINT[0], linewidth=3.5, label=f"Random Forest (AUC = {auc1:.2f})") +ax.plot(fpr2, tpr2, color=IMPRINT[1], linewidth=3.5, label=f"Logistic Regression (AUC = {auc2:.2f})") +ax.plot(fpr3, tpr3, color=IMPRINT[2], linewidth=3.5, label=f"Decision Tree (AUC = {auc3:.2f})") ax.plot([0, 1], [0, 1], color=INK_SOFT, linewidth=2.5, linestyle="--", label="Random Classifier (AUC = 0.50)") -ax.fill_between(fpr1, tpr1, alpha=0.12, color=OKABE_ITO[0]) -ax.fill_between(fpr2, tpr2, alpha=0.08, color=OKABE_ITO[1]) +ax.fill_between(fpr1, tpr1, alpha=0.12, color=IMPRINT[0]) +ax.fill_between(fpr2, tpr2, alpha=0.08, color=IMPRINT[1]) # Style ax.set_xlabel("False Positive Rate", fontsize=20, color=INK) diff --git a/plots/scatter-animated-controls/implementations/python/matplotlib.py b/plots/scatter-animated-controls/implementations/python/matplotlib.py index fd6e2e8db5..5a89c1cccb 100644 --- a/plots/scatter-animated-controls/implementations/python/matplotlib.py +++ b/plots/scatter-animated-controls/implementations/python/matplotlib.py @@ -16,7 +16,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" -ACCENT = "#D55E00" +ACCENT = "#C475FD" np.random.seed(42) countries = 15 diff --git a/plots/scatter-categorical/implementations/python/matplotlib.py b/plots/scatter-categorical/implementations/python/matplotlib.py index 574334fac1..e16a3d5b8f 100644 --- a/plots/scatter-categorical/implementations/python/matplotlib.py +++ b/plots/scatter-categorical/implementations/python/matplotlib.py @@ -18,11 +18,11 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (canonical order) -OKABE_ITO = [ +IMPRINT = [ "#009E73", # bluish green — ALWAYS first series - "#D55E00", # vermillion - "#0072B2", # blue - "#CC79A7", # reddish purple + "#C475FD", # vermillion + "#4467A3", # blue + "#BD8233", # reddish purple ] # Data — plant species with distinct petal characteristics @@ -50,9 +50,9 @@ # Plot each category with distinct colors and markers scatter_data = [ - (species_a_x, species_a_y, OKABE_ITO[0], markers[0]), - (species_b_x, species_b_y, OKABE_ITO[1], markers[1]), - (species_c_x, species_c_y, OKABE_ITO[2], markers[2]), + (species_a_x, species_a_y, IMPRINT[0], markers[0]), + (species_b_x, species_b_y, IMPRINT[1], markers[1]), + (species_c_x, species_c_y, IMPRINT[2], markers[2]), ] for i, (x, y, color, marker) in enumerate(scatter_data): diff --git a/plots/scatter-embedding/implementations/python/matplotlib.py b/plots/scatter-embedding/implementations/python/matplotlib.py index 717bd970b0..4c4d0208f4 100644 --- a/plots/scatter-embedding/implementations/python/matplotlib.py +++ b/plots/scatter-embedding/implementations/python/matplotlib.py @@ -23,7 +23,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477"] CLUSTER_LABELS = ["Finance", "Healthcare", "Technology", "Sports", "Politics", "Science", "Entertainment"] @@ -37,7 +37,7 @@ fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) ax.set_facecolor(PAGE_BG) -for i, (label, color) in enumerate(zip(CLUSTER_LABELS, OKABE_ITO, strict=False)): +for i, (label, color) in enumerate(zip(CLUSTER_LABELS, IMPRINT, strict=False)): mask = labels == i ax.scatter(X_2d[mask, 0], X_2d[mask, 1], c=color, s=90, alpha=0.65, edgecolors=PAGE_BG, linewidth=0.5, label=label) diff --git a/plots/scatter-map-geographic/implementations/python/matplotlib.py b/plots/scatter-map-geographic/implementations/python/matplotlib.py index 48426e36d5..f350d62ecf 100644 --- a/plots/scatter-map-geographic/implementations/python/matplotlib.py +++ b/plots/scatter-map-geographic/implementations/python/matplotlib.py @@ -20,13 +20,13 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Okabe-Ito palette — use positions 1→N in canonical order -OKABE_ITO = [ +IMPRINT = [ "#009E73", # 1: bluish green (ALWAYS first series) - "#D55E00", # 2: vermillion - "#0072B2", # 3: blue - "#CC79A7", # 4: reddish purple - "#E69F00", # 5: orange - "#56B4E9", # 6: sky blue + "#C475FD", # 2: vermillion + "#4467A3", # 3: blue + "#BD8233", # 4: reddish purple + "#AE3030", # 5: orange + "#2ABCCD", # 6: sky blue ] # Data - Major world cities with population and region @@ -69,7 +69,7 @@ # Map regions to Okabe-Ito colors unique_regions = ["Asia", "South America", "North America", "Africa", "Europe", "Oceania"] -region_colors = {region: OKABE_ITO[i] for i, region in enumerate(unique_regions)} +region_colors = {region: IMPRINT[i] for i, region in enumerate(unique_regions)} colors = [region_colors[r] for r in regions] # Scale sizes based on population @@ -480,7 +480,7 @@ size_labels = ["10M", "20M", "35M"] size_handles = [] for val, label in zip(size_values, size_labels, strict=True): - handle = ax.scatter([], [], c=OKABE_ITO[0], s=val * 22, label=label, edgecolors=PAGE_BG, linewidths=1.5, alpha=0.8) + handle = ax.scatter([], [], c=IMPRINT[0], s=val * 22, label=label, edgecolors=PAGE_BG, linewidths=1.5, alpha=0.8) size_handles.append(handle) size_legend = ax.legend( diff --git a/plots/scatter-matrix-interactive/implementations/python/matplotlib.py b/plots/scatter-matrix-interactive/implementations/python/matplotlib.py index cd149bf072..bdd2a05d72 100644 --- a/plots/scatter-matrix-interactive/implementations/python/matplotlib.py +++ b/plots/scatter-matrix-interactive/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Okabe-Ito palette (positions 1-3) -COLORS = ["#009E73", "#D55E00", "#0072B2"] +COLORS = ["#009E73", "#C475FD", "#4467A3"] # Data - Iris dataset (4 numeric variables, 150 samples) iris = load_iris() diff --git a/plots/scatter-matrix/implementations/python/matplotlib.py b/plots/scatter-matrix/implementations/python/matplotlib.py index fbb25ff7ea..32391e546e 100644 --- a/plots/scatter-matrix/implementations/python/matplotlib.py +++ b/plots/scatter-matrix/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (3 colors for 3 species) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3"] # Data: Iris-like flower measurements (4 variables, 3 species) np.random.seed(42) @@ -61,13 +61,13 @@ if i == j: # Diagonal: histograms with enhanced visual hierarchy - for species_idx, (_species, color) in enumerate(zip(species_names, OKABE_ITO, strict=True)): + for species_idx, (_species, color) in enumerate(zip(species_names, IMPRINT, strict=True)): mask = species_indices == species_idx species_data = data_arrays[i][mask] ax.hist(species_data, bins=12, alpha=0.85, color=color, edgecolor=INK_SOFT, linewidth=1.0) else: # Off-diagonal: scatter plots with enhanced marker definition - for species_idx, (_species, color) in enumerate(zip(species_names, OKABE_ITO, strict=True)): + for species_idx, (_species, color) in enumerate(zip(species_names, IMPRINT, strict=True)): mask = species_indices == species_idx ax.scatter( data_arrays[j][mask], @@ -121,7 +121,7 @@ markeredgecolor=PAGE_BG, markeredgewidth=0.5, ) - for species, color in zip(species_names, OKABE_ITO, strict=True) + for species, color in zip(species_names, IMPRINT, strict=True) ] leg = fig.legend( handles=legend_elements, loc="center right", fontsize=16, frameon=True, fancybox=False, edgecolor=INK_SOFT diff --git a/plots/scatter-regression-linear/implementations/python/matplotlib.py b/plots/scatter-regression-linear/implementations/python/matplotlib.py index f21823de1a..b52d9fc13b 100644 --- a/plots/scatter-regression-linear/implementations/python/matplotlib.py +++ b/plots/scatter-regression-linear/implementations/python/matplotlib.py @@ -17,7 +17,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" # Okabe-Ito position 1 -SECONDARY = "#D55E00" # Okabe-Ito position 2 for regression line +SECONDARY = "#C475FD" # Okabe-Ito position 2 for regression line # Data: Study hours vs exam scores (realistic educational context) np.random.seed(42) diff --git a/plots/scatter-regression-lowess/implementations/python/matplotlib.py b/plots/scatter-regression-lowess/implementations/python/matplotlib.py index d14d61eb99..c4ea2db97e 100644 --- a/plots/scatter-regression-lowess/implementations/python/matplotlib.py +++ b/plots/scatter-regression-lowess/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" -ACCENT = "#D55E00" +ACCENT = "#C475FD" # Data - tree height vs age with realistic growth pattern (power law) np.random.seed(42) diff --git a/plots/scatter-regression-polynomial/implementations/python/matplotlib.py b/plots/scatter-regression-polynomial/implementations/python/matplotlib.py index 65986e5470..b490b90784 100644 --- a/plots/scatter-regression-polynomial/implementations/python/matplotlib.py +++ b/plots/scatter-regression-polynomial/implementations/python/matplotlib.py @@ -17,7 +17,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" # Okabe-Ito position 1 — ALWAYS first series -SECONDARY = "#D55E00" # Okabe-Ito position 2 +SECONDARY = "#C475FD" # Okabe-Ito position 2 # Data - Modeling diminishing returns (economics example) np.random.seed(42) diff --git a/plots/scatter-text/implementations/python/matplotlib.py b/plots/scatter-text/implementations/python/matplotlib.py index e2a91d64df..29564ec957 100644 --- a/plots/scatter-text/implementations/python/matplotlib.py +++ b/plots/scatter-text/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito categorical palette -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data: Programming languages positioned by paradigm similarity # (simulating dimensionality reduction output) @@ -160,7 +160,7 @@ "systems", ] -category_to_color = {"dynamic": OKABE_ITO[0], "functional": OKABE_ITO[1], "systems": OKABE_ITO[2], "jvm": OKABE_ITO[3]} +category_to_color = {"dynamic": IMPRINT[0], "functional": IMPRINT[1], "systems": IMPRINT[2], "jvm": IMPRINT[3]} color_list = [category_to_color[cat] for cat in categories] @@ -195,10 +195,10 @@ # Legend legend_elements = [ - Patch(facecolor=OKABE_ITO[0], label="Dynamic/Scripting"), - Patch(facecolor=OKABE_ITO[1], label="Functional"), - Patch(facecolor=OKABE_ITO[2], label="Systems"), - Patch(facecolor=OKABE_ITO[3], label="JVM-based"), + Patch(facecolor=IMPRINT[0], label="Dynamic/Scripting"), + Patch(facecolor=IMPRINT[1], label="Functional"), + Patch(facecolor=IMPRINT[2], label="Systems"), + Patch(facecolor=IMPRINT[3], label="JVM-based"), ] leg = ax.legend(handles=legend_elements, loc="upper right", fontsize=16, framealpha=0.9) if leg: diff --git a/plots/shap-waterfall/implementations/python/matplotlib.py b/plots/shap-waterfall/implementations/python/matplotlib.py index 40f892f427..647f4f082c 100644 --- a/plots/shap-waterfall/implementations/python/matplotlib.py +++ b/plots/shap-waterfall/implementations/python/matplotlib.py @@ -20,8 +20,8 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" # Semantic direction colors — Okabe-Ito positions 2 and 3 -COLOR_POS = "#D55E00" # vermillion: positive SHAP (pushes prediction up) -COLOR_NEG = "#0072B2" # blue: negative SHAP (pushes prediction down) +COLOR_POS = "#C475FD" # vermillion: positive SHAP (pushes prediction up) +COLOR_NEG = "#4467A3" # blue: negative SHAP (pushes prediction down) # Data — credit loan approval model, individual applicant explanation # Features ordered by absolute SHAP magnitude, largest first (top of chart) diff --git a/plots/silhouette-basic/implementations/python/matplotlib.py b/plots/silhouette-basic/implementations/python/matplotlib.py index e9d85f0f60..4934b8e383 100644 --- a/plots/silhouette-basic/implementations/python/matplotlib.py +++ b/plots/silhouette-basic/implementations/python/matplotlib.py @@ -21,7 +21,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette for clusters -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477"] # Data - use iris dataset for realistic clustering example iris = load_iris() @@ -54,8 +54,8 @@ np.arange(y_lower, y_upper), 0, ith_cluster_silhouette_values, - facecolor=OKABE_ITO[i % len(OKABE_ITO)], - edgecolor=OKABE_ITO[i % len(OKABE_ITO)], + facecolor=IMPRINT[i % len(IMPRINT)], + edgecolor=IMPRINT[i % len(IMPRINT)], alpha=0.8, ) diff --git a/plots/skewt-logp-atmospheric/implementations/python/matplotlib.py b/plots/skewt-logp-atmospheric/implementations/python/matplotlib.py index 08e977a8ed..f5fa2fd693 100644 --- a/plots/skewt-logp-atmospheric/implementations/python/matplotlib.py +++ b/plots/skewt-logp-atmospheric/implementations/python/matplotlib.py @@ -25,9 +25,9 @@ # Okabe-Ito — data series TEMP_COLOR = "#009E73" # temperature — position 1 -DEWPOINT_COLOR = "#D55E00" # dewpoint — position 2 -CAPE_COLOR = "#E69F00" # CAPE shading — position 5 -CIN_COLOR = "#0072B2" # CIN shading — position 3 +DEWPOINT_COLOR = "#C475FD" # dewpoint — position 2 +CAPE_COLOR = "#AE3030" # CAPE shading — position 5 +CIN_COLOR = "#4467A3" # CIN shading — position 3 # Data — simulated mid-latitude radiosonde sounding (surface to upper troposphere) np.random.seed(42) diff --git a/plots/slope-basic/implementations/python/matplotlib.py b/plots/slope-basic/implementations/python/matplotlib.py index 5ac2eb0c05..a41dbd1077 100644 --- a/plots/slope-basic/implementations/python/matplotlib.py +++ b/plots/slope-basic/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" COLOR_INC = "#009E73" # Okabe-Ito pos 1 — increase (brand green) -COLOR_DEC = "#D55E00" # Okabe-Ito pos 2 — decrease (vermillion) +COLOR_DEC = "#C475FD" # Okabe-Ito pos 2 — decrease (vermillion) # Data products = ["Product A", "Product B", "Product C", "Product D", "Product E", "Product F", "Product G", "Product H"] diff --git a/plots/smith-chart-basic/implementations/python/matplotlib.py b/plots/smith-chart-basic/implementations/python/matplotlib.py index c77077b1f2..4fa0ecee87 100644 --- a/plots/smith-chart-basic/implementations/python/matplotlib.py +++ b/plots/smith-chart-basic/implementations/python/matplotlib.py @@ -25,8 +25,8 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" -OI_2 = "#D55E00" # Okabe-Ito position 2 — VSWR circles -OI_3 = "#0072B2" # Okabe-Ito position 3 — boundary / matched condition +OI_2 = "#C475FD" # Okabe-Ito position 2 — VSWR circles +OI_3 = "#4467A3" # Okabe-Ito position 3 — boundary / matched condition # Data — antenna impedance sweep 1–6 GHz Z0 = 50 diff --git a/plots/sn-curve-basic/implementations/python/matplotlib.py b/plots/sn-curve-basic/implementations/python/matplotlib.py index e452d079c2..27e62537e1 100644 --- a/plots/sn-curve-basic/implementations/python/matplotlib.py +++ b/plots/sn-curve-basic/implementations/python/matplotlib.py @@ -20,10 +20,10 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" BRAND = "#009E73" # Okabe-Ito #1 — test data points -COLOR_FIT = "#D55E00" # Okabe-Ito #2 — Basquin fit line -COLOR_ULT = "#0072B2" # Okabe-Ito #3 — Ultimate Strength -COLOR_YLD = "#CC79A7" # Okabe-Ito #4 — Yield Strength -COLOR_END = "#E69F00" # Okabe-Ito #5 — Endurance Limit +COLOR_FIT = "#C475FD" # Okabe-Ito #2 — Basquin fit line +COLOR_ULT = "#4467A3" # Okabe-Ito #3 — Ultimate Strength +COLOR_YLD = "#BD8233" # Okabe-Ito #4 — Yield Strength +COLOR_END = "#AE3030" # Okabe-Ito #5 — Endurance Limit # Data: Simulated fatigue test results for structural steel specimens np.random.seed(42) diff --git a/plots/span-basic/implementations/python/matplotlib.py b/plots/span-basic/implementations/python/matplotlib.py index ca90f2e7bd..eea99ed567 100644 --- a/plots/span-basic/implementations/python/matplotlib.py +++ b/plots/span-basic/implementations/python/matplotlib.py @@ -19,8 +19,8 @@ INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" BRAND = "#009E73" # Okabe-Ito position 1 — first series -C2 = "#D55E00" # Okabe-Ito position 2 -C3 = "#0072B2" # Okabe-Ito position 3 +C2 = "#C475FD" # Okabe-Ito position 2 +C3 = "#4467A3" # Okabe-Ito position 3 # Data — stock prices with a simulated recession dip np.random.seed(42) diff --git a/plots/spectrum-basic/implementations/python/matplotlib.py b/plots/spectrum-basic/implementations/python/matplotlib.py index 300824b486..4c074fda8d 100644 --- a/plots/spectrum-basic/implementations/python/matplotlib.py +++ b/plots/spectrum-basic/implementations/python/matplotlib.py @@ -16,7 +16,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" BRAND = "#009E73" # Okabe-Ito position 1 -ACCENT = "#D55E00" # Okabe-Ito position 2 for annotations +ACCENT = "#C475FD" # Okabe-Ito position 2 for annotations # Generate synthetic signal with multiple frequency components np.random.seed(42) diff --git a/plots/streamgraph-basic/implementations/python/matplotlib.py b/plots/streamgraph-basic/implementations/python/matplotlib.py index 8a262e42d5..90e28faa84 100644 --- a/plots/streamgraph-basic/implementations/python/matplotlib.py +++ b/plots/streamgraph-basic/implementations/python/matplotlib.py @@ -17,7 +17,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD"] np.random.seed(42) @@ -70,7 +70,7 @@ fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) ax.set_facecolor(PAGE_BG) -ax.stackplot(months_fine, data_smooth, labels=categories, colors=OKABE_ITO, baseline="wiggle", alpha=0.85) +ax.stackplot(months_fine, data_smooth, labels=categories, colors=IMPRINT, baseline="wiggle", alpha=0.85) ax.set_xlabel("Month (Jan 2023 – Dec 2024)", fontsize=20, color=INK) ax.set_title("streamgraph-basic · matplotlib · anyplot.ai", fontsize=24, color=INK, fontweight="medium") diff --git a/plots/strip-basic/implementations/python/matplotlib.py b/plots/strip-basic/implementations/python/matplotlib.py index e631ed835f..6e19b4525a 100644 --- a/plots/strip-basic/implementations/python/matplotlib.py +++ b/plots/strip-basic/implementations/python/matplotlib.py @@ -17,7 +17,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data — employee satisfaction scores by department (1–10 scale) np.random.seed(42) @@ -39,7 +39,7 @@ for i, dept in enumerate(departments): scores = dept_scores[dept] jitter = np.random.uniform(-0.2, 0.2, len(scores)) - ax.scatter(i + jitter, scores, s=200, alpha=0.6, color=OKABE_ITO[i], edgecolors=PAGE_BG, linewidth=0.5) + ax.scatter(i + jitter, scores, s=200, alpha=0.6, color=IMPRINT[i], edgecolors=PAGE_BG, linewidth=0.5) # Mean reference lines for i, dept in enumerate(departments): diff --git a/plots/subplot-mosaic/implementations/python/matplotlib.py b/plots/subplot-mosaic/implementations/python/matplotlib.py index e77011e341..890d6f6e94 100644 --- a/plots/subplot-mosaic/implementations/python/matplotlib.py +++ b/plots/subplot-mosaic/implementations/python/matplotlib.py @@ -20,9 +20,9 @@ # Okabe-Ito palette BRAND = "#009E73" # Position 1 - always first series -OI_2 = "#D55E00" -OI_3 = "#0072B2" -OI_4 = "#CC79A7" +OI_2 = "#C475FD" +OI_3 = "#4467A3" +OI_4 = "#BD8233" # Data np.random.seed(42) diff --git a/plots/sunburst-basic/implementations/python/matplotlib.py b/plots/sunburst-basic/implementations/python/matplotlib.py index bae70288d9..da4ba993a3 100644 --- a/plots/sunburst-basic/implementations/python/matplotlib.py +++ b/plots/sunburst-basic/implementations/python/matplotlib.py @@ -16,8 +16,8 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" # Okabe-Ito base colors for level-1 departments (positions 1–3) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] -OKABE_RGB = [tuple(int(c[j : j + 2], 16) / 255 for j in (1, 3, 5)) for c in OKABE_ITO] +IMPRINT = ["#009E73", "#C475FD", "#4467A3"] +OKABE_RGB = [tuple(int(c[j : j + 2], 16) / 255 for j in (1, 3, 5)) for c in IMPRINT] # Data: Company budget breakdown ($thousands) data = { @@ -39,7 +39,7 @@ dept_total = sum(sum(projs.values()) for projs in teams.values()) level1_names.append(dept) level1_values.append(dept_total) - level1_colors.append(OKABE_ITO[i]) + level1_colors.append(IMPRINT[i]) r, g, b = OKABE_RGB[i] team_items = list(teams.items()) diff --git a/plots/swarm-basic/implementations/python/matplotlib.py b/plots/swarm-basic/implementations/python/matplotlib.py index fcca3d32e4..5f72fd0815 100644 --- a/plots/swarm-basic/implementations/python/matplotlib.py +++ b/plots/swarm-basic/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette — 4 departments -COLORS = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +COLORS = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data - Employee performance scores by department np.random.seed(42) diff --git a/plots/timeline-basic/implementations/python/matplotlib.py b/plots/timeline-basic/implementations/python/matplotlib.py index 91bf7fff51..ed3b21f579 100644 --- a/plots/timeline-basic/implementations/python/matplotlib.py +++ b/plots/timeline-basic/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] np.random.seed(42) events = pd.DataFrame( @@ -50,10 +50,10 @@ ) category_colors = { - "Planning": OKABE_ITO[0], - "Development": OKABE_ITO[1], - "Testing": OKABE_ITO[2], - "Release": OKABE_ITO[3], + "Planning": IMPRINT[0], + "Development": IMPRINT[1], + "Testing": IMPRINT[2], + "Release": IMPRINT[3], } colors = [category_colors[cat] for cat in events["category"]] diff --git a/plots/timeseries-decomposition/implementations/python/matplotlib.py b/plots/timeseries-decomposition/implementations/python/matplotlib.py index a4aebba575..f07ee4fbae 100644 --- a/plots/timeseries-decomposition/implementations/python/matplotlib.py +++ b/plots/timeseries-decomposition/implementations/python/matplotlib.py @@ -20,11 +20,11 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette -OKABE_ITO = [ +IMPRINT = [ "#009E73", # bluish green (brand — first series) - "#D55E00", # vermillion - "#0072B2", # blue - "#CC79A7", # reddish purple + "#C475FD", # vermillion + "#4467A3", # blue + "#BD8233", # reddish purple ] # Data - Monthly retail sales over 6 years (72 months = 6 full annual cycles) @@ -51,7 +51,7 @@ fig, axes = plt.subplots(4, 1, figsize=(16, 12), sharex=True, facecolor=PAGE_BG) # Original series -axes[0].plot(dates, ts.values, color=OKABE_ITO[0], linewidth=2.5) +axes[0].plot(dates, ts.values, color=IMPRINT[0], linewidth=2.5) axes[0].set_facecolor(PAGE_BG) axes[0].set_ylabel("Original (Sales USD)", fontsize=20, color=INK) axes[0].tick_params(axis="y", labelsize=16, colors=INK_SOFT) @@ -61,21 +61,21 @@ ) # Trend component -axes[1].plot(dates, decomposition.trend, color=OKABE_ITO[1], linewidth=2.5) +axes[1].plot(dates, decomposition.trend, color=IMPRINT[1], linewidth=2.5) axes[1].set_facecolor(PAGE_BG) axes[1].set_ylabel("Trend (Sales USD)", fontsize=20, color=INK) axes[1].tick_params(axis="y", labelsize=16, colors=INK_SOFT) axes[1].grid(True, alpha=0.15, linewidth=0.8, color=INK) # Seasonal component -axes[2].plot(dates, decomposition.seasonal, color=OKABE_ITO[2], linewidth=2.5) +axes[2].plot(dates, decomposition.seasonal, color=IMPRINT[2], linewidth=2.5) axes[2].set_facecolor(PAGE_BG) axes[2].set_ylabel("Seasonal (Sales USD)", fontsize=20, color=INK) axes[2].tick_params(axis="y", labelsize=16, colors=INK_SOFT) axes[2].grid(True, alpha=0.15, linewidth=0.8, color=INK) # Residual component -axes[3].plot(dates, decomposition.resid, color=OKABE_ITO[3], linewidth=2.5) +axes[3].plot(dates, decomposition.resid, color=IMPRINT[3], linewidth=2.5) axes[3].axhline(y=0, color=INK_SOFT, linestyle="-", linewidth=1, alpha=0.3) axes[3].set_facecolor(PAGE_BG) axes[3].set_ylabel("Residual (Sales USD)", fontsize=20, color=INK) diff --git a/plots/timeseries-forecast-uncertainty/implementations/python/matplotlib.py b/plots/timeseries-forecast-uncertainty/implementations/python/matplotlib.py index 5e00c50691..20e2e785aa 100644 --- a/plots/timeseries-forecast-uncertainty/implementations/python/matplotlib.py +++ b/plots/timeseries-forecast-uncertainty/implementations/python/matplotlib.py @@ -22,8 +22,8 @@ # Okabe-Ito — pos 1 (historical), pos 2 (forecast), pos 6 (CI bands) HISTORICAL_COLOR = "#009E73" -FORECAST_COLOR = "#D55E00" -CI_COLOR = "#56B4E9" +FORECAST_COLOR = "#C475FD" +CI_COLOR = "#2ABCCD" # Data — monthly retail sales, 3-year history + 12-month ARIMA-style forecast np.random.seed(42) diff --git a/plots/treemap-basic/implementations/python/matplotlib.py b/plots/treemap-basic/implementations/python/matplotlib.py index e6685faa75..a7da603e23 100644 --- a/plots/treemap-basic/implementations/python/matplotlib.py +++ b/plots/treemap-basic/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette for categories -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"] # Data - Budget allocation by department and project data = [ @@ -44,7 +44,7 @@ # Category to color mapping (using Okabe-Ito palette) unique_categories = ["Engineering", "Sales", "Marketing", "Operations", "HR"] -category_colors = {cat: OKABE_ITO[i % len(OKABE_ITO)] for i, cat in enumerate(unique_categories)} +category_colors = {cat: IMPRINT[i % len(IMPRINT)] for i, cat in enumerate(unique_categories)} # Normalize values to fill a 160x90 area (matching figsize aspect ratio) total = sum(values) diff --git a/plots/upset-basic/implementations/python/matplotlib.py b/plots/upset-basic/implementations/python/matplotlib.py index 0181dd1150..bc6f49de55 100644 --- a/plots/upset-basic/implementations/python/matplotlib.py +++ b/plots/upset-basic/implementations/python/matplotlib.py @@ -25,7 +25,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" BRAND = "#009E73" -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD"] # Data: bug reports spanning software system modules np.random.seed(42) @@ -71,7 +71,7 @@ ax_sets = fig.add_subplot(gs[1, 0]) # ── Intersection bars (top right) ────────────────────────────────────────────── -bar_colors = [OKABE_ITO[min(d - 1, len(OKABE_ITO) - 1)] for d in degrees] +bar_colors = [IMPRINT[min(d - 1, len(IMPRINT) - 1)] for d in degrees] ax_bars.bar(range(n_ints), int_counts, color=bar_colors, width=0.65, zorder=2) for i, (cnt, _col) in enumerate(zip(int_counts, bar_colors, strict=False)): ax_bars.text( @@ -99,7 +99,7 @@ ax_bars.spines["left"].set_color(INK_SOFT) # Degree legend inside intersection bars area -patches = [mpatches.Patch(color=OKABE_ITO[min(d - 1, 5)], label=f"Degree {d}") for d in sorted(set(degrees))] +patches = [mpatches.Patch(color=IMPRINT[min(d - 1, 5)], label=f"Degree {d}") for d in sorted(set(degrees))] leg = ax_bars.legend( handles=patches, fontsize=13, @@ -136,7 +136,7 @@ # Active dots + connecting lines for col_j, (sig, deg) in enumerate(zip(int_sigs, degrees, strict=False)): - color = OKABE_ITO[min(deg - 1, len(OKABE_ITO) - 1)] + color = IMPRINT[min(deg - 1, len(IMPRINT) - 1)] active_rows = [i for i, v in enumerate(sig) if v] if len(active_rows) > 1: ax_matrix.plot( diff --git a/plots/venn-basic/implementations/python/matplotlib.py b/plots/venn-basic/implementations/python/matplotlib.py index d5a9d79062..78d0fb5701 100644 --- a/plots/venn-basic/implementations/python/matplotlib.py +++ b/plots/venn-basic/implementations/python/matplotlib.py @@ -16,7 +16,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3"] # Data: Developer survey - programming language proficiency # venn3 expects: (Abc, aBc, ABc, abC, AbC, aBC, ABC) @@ -38,7 +38,7 @@ set_labels=("Python", "JavaScript", "SQL"), ax=ax, alpha=0.6, - set_colors=OKABE_ITO, + set_colors=IMPRINT, ) # Style circle labels (set names) diff --git a/plots/venn-labeled-items/implementations/python/matplotlib.py b/plots/venn-labeled-items/implementations/python/matplotlib.py index e897dedd68..8db064c4d8 100644 --- a/plots/venn-labeled-items/implementations/python/matplotlib.py +++ b/plots/venn-labeled-items/implementations/python/matplotlib.py @@ -19,8 +19,8 @@ # Okabe-Ito categorical (canonical positions 1, 2, 3) COLOR_A = "#009E73" # brand green — ALWAYS first series -COLOR_B = "#D55E00" # vermillion -COLOR_C = "#0072B2" # blue +COLOR_B = "#C475FD" # vermillion +COLOR_C = "#4467A3" # blue # Data: three Chartgeist-style traits RADIUS = 1.5 diff --git a/plots/violin-grouped-swarm/implementations/python/matplotlib.py b/plots/violin-grouped-swarm/implementations/python/matplotlib.py index b8329e8c4e..3812dc1ebb 100644 --- a/plots/violin-grouped-swarm/implementations/python/matplotlib.py +++ b/plots/violin-grouped-swarm/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (first two colors for the two groups) -COLORS = ["#009E73", "#D55E00"] +COLORS = ["#009E73", "#C475FD"] # Data - Response times across task types and expertise levels np.random.seed(42) diff --git a/plots/violin-split/implementations/python/matplotlib.py b/plots/violin-split/implementations/python/matplotlib.py index dd98716294..1d68a9fc52 100644 --- a/plots/violin-split/implementations/python/matplotlib.py +++ b/plots/violin-split/implementations/python/matplotlib.py @@ -20,7 +20,7 @@ # Okabe-Ito palette COLOR_CONTROL = "#009E73" # Position 1 - brand green -COLOR_TREATMENT = "#D55E00" # Position 2 - vermillion +COLOR_TREATMENT = "#C475FD" # Position 2 - vermillion # Data - Test score distributions for control vs treatment groups across school levels np.random.seed(42) diff --git a/plots/violin-swarm/implementations/python/matplotlib.py b/plots/violin-swarm/implementations/python/matplotlib.py index f3f77c3d02..1d21e34912 100644 --- a/plots/violin-swarm/implementations/python/matplotlib.py +++ b/plots/violin-swarm/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ # Okabe-Ito palette - first series is always #009E73 BRAND = "#009E73" # violin fill -ACCENT = "#D55E00" # swarm points +ACCENT = "#C475FD" # swarm points # Data - Reaction times (ms) across 4 experimental conditions np.random.seed(42) diff --git a/plots/volcano-basic/implementations/python/matplotlib.py b/plots/volcano-basic/implementations/python/matplotlib.py index b9aa7d9923..fc657d9ec8 100644 --- a/plots/volcano-basic/implementations/python/matplotlib.py +++ b/plots/volcano-basic/implementations/python/matplotlib.py @@ -17,8 +17,8 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette for significance categories -COLOR_DOWNREG = "#0072B2" # Blue -COLOR_UPREG = "#D55E00" # Vermillion +COLOR_DOWNREG = "#4467A3" # Blue +COLOR_UPREG = "#C475FD" # Vermillion COLOR_NONSIG = "#888888" # Neutral gray # Data - simulated differential expression results diff --git a/plots/voronoi-basic/implementations/python/matplotlib.py b/plots/voronoi-basic/implementations/python/matplotlib.py index a768645632..1729271c3f 100644 --- a/plots/voronoi-basic/implementations/python/matplotlib.py +++ b/plots/voronoi-basic/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette for Voronoi regions -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477"] # Data - Generate seed points for Voronoi diagram np.random.seed(42) @@ -64,7 +64,7 @@ polygon[:, 1] = np.clip(polygon[:, 1], y_min, y_max) polygons.append(polygon) - poly_colors.append(OKABE_ITO[idx % len(OKABE_ITO)]) + poly_colors.append(IMPRINT[idx % len(IMPRINT)]) # Draw all Voronoi regions at once collection = PolyCollection(polygons, facecolors=poly_colors, edgecolors=INK_SOFT, linewidths=2.5, alpha=0.6) diff --git a/plots/waffle-basic/implementations/python/matplotlib.py b/plots/waffle-basic/implementations/python/matplotlib.py index 417e141671..71fe4ad68a 100644 --- a/plots/waffle-basic/implementations/python/matplotlib.py +++ b/plots/waffle-basic/implementations/python/matplotlib.py @@ -19,7 +19,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette (canonical order) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"] # Data - Survey responses (sums to 100%) categories = ["Strongly Agree", "Agree", "Neutral", "Disagree"] @@ -53,7 +53,7 @@ square_size, square_size, boxstyle="round,pad=0.02,rounding_size=0.1", - facecolor=OKABE_ITO[category_idx], + facecolor=IMPRINT[category_idx], edgecolor=PAGE_BG, linewidth=1.5, ) @@ -67,7 +67,7 @@ # Create legend with percentage labels legend_patches = [ - mpatches.Patch(color=OKABE_ITO[i], label=f"{categories[i]} ({values[i]}%)") for i in range(len(categories)) + mpatches.Patch(color=IMPRINT[i], label=f"{categories[i]} ({values[i]}%)") for i in range(len(categories)) ] leg = ax.legend( handles=legend_patches, loc="center left", bbox_to_anchor=(1.02, 0.5), fontsize=18, frameon=True, fancybox=True diff --git a/plots/waterfall-basic/implementations/python/matplotlib.py b/plots/waterfall-basic/implementations/python/matplotlib.py index e79b5a90d5..dbf16343a5 100644 --- a/plots/waterfall-basic/implementations/python/matplotlib.py +++ b/plots/waterfall-basic/implementations/python/matplotlib.py @@ -20,8 +20,8 @@ # Okabe-Ito palette for waterfall categories INCREASE_COLOR = "#009E73" # Brand green (position 1) -DECREASE_COLOR = "#D55E00" # Vermillion (position 2) -TOTAL_COLOR = "#0072B2" # Blue (position 3) +DECREASE_COLOR = "#C475FD" # Vermillion (position 2) +TOTAL_COLOR = "#4467A3" # Blue (position 3) # Data: Project Budget Allocation (from initial budget to final allocation) # Different scenario from the original (avoiding Altair match) diff --git a/plots/windrose-basic/implementations/python/matplotlib.py b/plots/windrose-basic/implementations/python/matplotlib.py index 9c7ba3b75e..defa09a2fe 100644 --- a/plots/windrose-basic/implementations/python/matplotlib.py +++ b/plots/windrose-basic/implementations/python/matplotlib.py @@ -22,7 +22,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette for wind speed bins (starting with brand green) -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD"] # Data - Simulated annual wind measurements (8760 hourly readings) np.random.seed(42) @@ -91,7 +91,7 @@ freq_matrix[:, j], width=bar_width, bottom=bottoms, - color=OKABE_ITO[j], + color=IMPRINT[j], edgecolor=PAGE_BG, linewidth=0.5, label=f"{speed_labels[j]} m/s", diff --git a/plots/wordcloud-basic/implementations/python/matplotlib.py b/plots/wordcloud-basic/implementations/python/matplotlib.py index c1b2f99e01..60fbcf33d9 100644 --- a/plots/wordcloud-basic/implementations/python/matplotlib.py +++ b/plots/wordcloud-basic/implementations/python/matplotlib.py @@ -18,7 +18,7 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" # Okabe-Ito palette -OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"] +IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477"] # Data - Tech industry survey responses about most valued skills word_frequencies = { @@ -67,7 +67,7 @@ # Create word cloud with Okabe-Ito color palette and proper backgrounds def color_func(word, font_size, position, orientation, random_state=None, **kwargs): - return OKABE_ITO[hash(word) % len(OKABE_ITO)] + return IMPRINT[hash(word) % len(IMPRINT)] wc = WordCloud( diff --git a/pyproject.toml b/pyproject.toml index 60cda3bcc8..df40fd1c14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -150,7 +150,6 @@ ignore = [ "plots/*/implementations/python/plotnine.py" = ["E402"] "plots/*/implementations/python/bokeh.py" = ["E402"] "plots/*/implementations/python/seaborn.py" = ["E402"] -"plots/*/implementations/python/plotly.py" = ["E402"] [tool.ruff.format] quote-style = "double" diff --git a/scripts/migrate_render_and_upload.py b/scripts/migrate_render_and_upload.py new file mode 100644 index 0000000000..906db1ba80 --- /dev/null +++ b/scripts/migrate_render_and_upload.py @@ -0,0 +1,470 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.13" +# dependencies = [ +# "click>=8.1", +# "pyyaml>=6.0", +# "rich>=13.0", +# ] +# /// +"""Stage B of the imprint migration: re-render light+dark images locally and +overwrite the GCS production bucket. + +For every (spec, language, library) listed in ``.migration-list.json`` (or +discovered from a freshly-applied Stage A), this script: + +1. Renders ``plot-light.png`` and ``plot-dark.png`` in ``.regen-preview/{library}/`` + by running the impl with ``ANYPLOT_THEME`` set to each theme. +2. Optimizes each PNG in place and emits responsive variants (400/800/1200 px, + PNG + WebP) via ``core.images``. +3. Uploads the bundle to ``gs://anyplot-images/staging/{spec}/{language}/{library}/``. +4. Promotes staging → ``gs://anyplot-images/plots/{spec}/{language}/{library}/`` + with public-read ACL, then deletes staging. + +Resumable via ``.migration-progress.json`` — Ctrl-C and re-run to continue from +where you stopped. Skips items already marked ``done``. + +Self-contained: does NOT depend on the regen module, so it works for R and +Julia impls too (the regen module is Python-only). +""" + +from __future__ import annotations + +import json +import os +import shutil +import subprocess +import sys +import time +from dataclasses import dataclass +from pathlib import Path + +import click +from rich.console import Console +from rich.progress import Progress, SpinnerColumn, TextColumn, TimeElapsedColumn + + +REPO_ROOT = Path(__file__).resolve().parent.parent +PROGRESS_FILE = REPO_ROOT / ".migration-progress.json" + +# Use the project's venv directly. The script's own inline-deps venv (click, +# pyyaml, rich) does not have matplotlib/bokeh/etc., so `uv run python` would +# pick the wrong interpreter. The project venv is created via `uv sync --extra plotting`. +PROJECT_PYTHON = REPO_ROOT / ".venv" / "bin" / "python" + +GCS_BUCKET = "anyplot-images" +CACHE_HEADER = "Cache-Control:public, max-age=604800" + +FILE_EXT_BY_LANGUAGE: dict[str, str] = {"python": ".py", "r": ".R", "julia": ".jl"} + +# Libraries that need a display server (Selenium-driven Chrome for PNG export). +SELENIUM_LIBS = {"bokeh", "highcharts"} + + +# ───────────────────────────────────────────────────────────────────────────── +# Path helpers +# ───────────────────────────────────────────────────────────────────────────── + + +def _impl_path(spec: str, language: str, library: str) -> Path: + return REPO_ROOT / "plots" / spec / "implementations" / language / f"{library}{FILE_EXT_BY_LANGUAGE[language]}" + + +def _preview_dir(spec: str, language: str, library: str) -> Path: + return REPO_ROOT / "plots" / spec / "implementations" / language / ".regen-preview" / library + + +def _staging_path(spec: str, language: str, library: str) -> str: + return f"gs://{GCS_BUCKET}/staging/{spec}/{language}/{library}" + + +def _production_path(spec: str, language: str, library: str) -> str: + return f"gs://{GCS_BUCKET}/plots/{spec}/{language}/{library}" + + +# ───────────────────────────────────────────────────────────────────────────── +# Pre-flight checks +# ───────────────────────────────────────────────────────────────────────────── + + +@dataclass +class PreFlight: + python_ok: bool + r_ok: bool + julia_ok: bool + chrome_ok: bool + chromedriver_ok: bool + gcloud_ok: bool + notes: list[str] + + +def _check_command(cmd: list[str]) -> bool: + try: + subprocess.run(cmd, capture_output=True, check=True, timeout=10) + return True + except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired): + return False + + +PYTHON_LIB_IMPORTS: dict[str, str] = { + "matplotlib": "matplotlib", + "seaborn": "seaborn", + "plotnine": "plotnine", + "plotly": "plotly", + "bokeh": "bokeh", + "altair": "altair", + "pygal": "pygal", + "highcharts": "highcharts_core", + "letsplot": "lets_plot", +} + + +def preflight(python_libs: set[str], needs_r: bool, needs_julia: bool, needs_selenium: bool) -> PreFlight: + notes: list[str] = [] + + if not PROJECT_PYTHON.is_file(): + python_ok = False + notes.append(f"Project venv missing at {PROJECT_PYTHON} → run: uv sync --extra plotting") + elif not python_libs: + python_ok = True + else: + modules = sorted({PYTHON_LIB_IMPORTS[lib] for lib in python_libs if lib in PYTHON_LIB_IMPORTS}) + python_ok = _check_command([str(PROJECT_PYTHON), "-c", "import " + ", ".join(modules)]) + if not python_ok: + notes.append(f"Project venv lacks one of: {', '.join(modules)} → run: uv sync --extra plotting") + + r_ok = _check_command(["R", "--version"]) if needs_r else True + if needs_r and not r_ok: + notes.append("R not installed → sudo apt-get install r-base r-base-dev, then install ggplot2 + ragg + tidyr + dplyr (see plan)") + + julia_ok = _check_command(["julia", "--version"]) if needs_julia else True + if needs_julia and not julia_ok: + notes.append("Julia not installed → download from julialang.org, then julia --project=. -e 'using Pkg; Pkg.instantiate()'") + + chrome_ok = True + chromedriver_ok = True + if needs_selenium: + chrome_ok = _check_command(["google-chrome", "--version"]) or _check_command(["chromium", "--version"]) + chromedriver_ok = _check_command(["chromedriver", "--version"]) + if not chrome_ok: + notes.append("Chrome/Chromium missing (needed for bokeh + highcharts PNG export)") + if not chromedriver_ok: + notes.append("chromedriver missing (needed for bokeh + highcharts PNG export)") + + gcloud_ok = _check_command(["gcloud", "auth", "list", "--format=value(account)"]) + if not gcloud_ok: + notes.append("gcloud not authenticated → gcloud auth login") + + return PreFlight( + python_ok=python_ok, + r_ok=r_ok, + julia_ok=julia_ok, + chrome_ok=chrome_ok, + chromedriver_ok=chromedriver_ok, + gcloud_ok=gcloud_ok, + notes=notes, + ) + + +# ───────────────────────────────────────────────────────────────────────────── +# Render +# ───────────────────────────────────────────────────────────────────────────── + + +def _render_one_theme(impl: Path, preview: Path, language: str, theme: str) -> None: + env = os.environ.copy() + env["ANYPLOT_THEME"] = theme + env["MPLBACKEND"] = "Agg" + + if language == "python": + cmd = [str(PROJECT_PYTHON), "-P", str(impl.resolve())] + elif language == "r": + cmd = ["Rscript", str(impl.resolve())] + elif language == "julia": + cmd = ["julia", f"--project={REPO_ROOT}", str(impl.resolve())] + else: + raise ValueError(f"Unsupported language: {language}") + + # xvfb-run gives bokeh/highcharts a display when Chrome is headless-only. + try: + subprocess.run(["xvfb-run", *cmd], cwd=preview, env=env, check=True, capture_output=True, timeout=180) + except FileNotFoundError: + subprocess.run(cmd, cwd=preview, env=env, check=True, capture_output=True, timeout=180) + + +def render(spec: str, language: str, library: str) -> tuple[Path, Path]: + impl = _impl_path(spec, language, library) + if not impl.is_file(): + raise FileNotFoundError(f"impl not found: {impl}") + + preview = _preview_dir(spec, language, library) + if preview.exists(): + shutil.rmtree(preview) + preview.mkdir(parents=True) + + for theme in ("light", "dark"): + _render_one_theme(impl, preview, language, theme) + + light_png = preview / "plot-light.png" + dark_png = preview / "plot-dark.png" + if not light_png.is_file() or not dark_png.is_file(): + raise RuntimeError(f"render incomplete in {preview} — missing light or dark PNG") + return light_png, dark_png + + +# ───────────────────────────────────────────────────────────────────────────── +# Optimize + responsive variants +# ───────────────────────────────────────────────────────────────────────────── + + +def _process_image(png: Path) -> None: + subprocess.run( + [str(PROJECT_PYTHON), "-m", "core.images", "process", str(png), str(png)], + cwd=REPO_ROOT, + check=True, + capture_output=True, + ) + subprocess.run( + [str(PROJECT_PYTHON), "-m", "core.images", "responsive", str(png), str(png.parent) + "/"], + cwd=REPO_ROOT, + check=True, + capture_output=True, + ) + + +# ───────────────────────────────────────────────────────────────────────────── +# GCS upload + promote +# ───────────────────────────────────────────────────────────────────────────── + + +def _gsutil(args: list[str], check: bool = True) -> subprocess.CompletedProcess: + return subprocess.run(["gsutil", *args], check=check, capture_output=True, text=True) + + +def upload_to_staging(preview: Path, staging: str) -> None: + targets: list[Path] = [] + for pat in ("plot-light*.png", "plot-light*.webp", "plot-dark*.png", "plot-dark*.webp"): + targets.extend(sorted(preview.glob(pat))) + if not targets: + raise FileNotFoundError(f"no rendered images in {preview}") + _gsutil(["-m", "-h", CACHE_HEADER, "cp", *(str(p) for p in targets), f"{staging}/"]) + _gsutil(["-m", "acl", "ch", "-u", "AllUsers:R", f"{staging}/plot-light*", f"{staging}/plot-dark*"], check=False) + + for theme in ("light", "dark"): + html = preview / f"plot-{theme}.html" + if html.is_file(): + _gsutil(["-h", CACHE_HEADER, "cp", str(html), f"{staging}/plot-{theme}.html"]) + _gsutil(["acl", "ch", "-u", "AllUsers:R", f"{staging}/plot-{theme}.html"], check=False) + + +def promote_to_production(staging: str, production: str) -> None: + # Copy then make public; finally clean up staging. + _gsutil(["-m", "-h", CACHE_HEADER, "cp", "-r", f"{staging}/*", f"{production}/"]) + _gsutil(["-m", "acl", "ch", "-r", "-u", "AllUsers:R", f"{production}/"], check=False) + _gsutil(["-m", "rm", "-r", f"{staging}/"], check=False) + + +# ───────────────────────────────────────────────────────────────────────────── +# Progress persistence +# ───────────────────────────────────────────────────────────────────────────── + + +def _load_progress() -> dict[str, str]: + if PROGRESS_FILE.is_file(): + return json.loads(PROGRESS_FILE.read_text()) + return {} + + +def _save_progress(progress: dict[str, str]) -> None: + PROGRESS_FILE.write_text(json.dumps(progress, indent=2, sort_keys=True)) + + +def _key(spec: str, language: str, library: str) -> str: + return f"{spec}/{language}/{library}" + + +# ───────────────────────────────────────────────────────────────────────────── +# Target discovery — files that were just changed by Stage A +# ───────────────────────────────────────────────────────────────────────────── + + +def _discover_targets_from_git(library_filter: str) -> list[tuple[str, str, str]]: + """Find (spec, language, library) from git's working tree — files modified vs HEAD.""" + out = subprocess.run( + ["git", "diff", "--name-only", "HEAD"], + cwd=REPO_ROOT, + capture_output=True, + text=True, + check=True, + ) + targets: list[tuple[str, str, str]] = [] + for line in out.stdout.splitlines(): + # Expected: plots/{spec}/implementations/{language}/{library}.{ext} + parts = line.split("/") + if len(parts) != 5 or parts[0] != "plots" or parts[2] != "implementations": + continue + spec = parts[1] + language = parts[3] + if language not in FILE_EXT_BY_LANGUAGE: + continue + library = Path(parts[4]).stem + if library_filter != "all" and library != library_filter: + continue + targets.append((spec, language, library)) + return targets + + +# ───────────────────────────────────────────────────────────────────────────── +# Per-target driver +# ───────────────────────────────────────────────────────────────────────────── + + +def process_one(spec: str, language: str, library: str, *, dry_run: bool, console: Console) -> str: + """Returns one of: done | error: | skipped (dry-run).""" + preview = _preview_dir(spec, language, library) + + try: + light, dark = render(spec, language, library) + except Exception as e: + return f"error: render failed: {type(e).__name__}: {e}" + + if dry_run: + console.print(f" [dim]dry-run[/dim] rendered {light.relative_to(REPO_ROOT)} + {dark.relative_to(REPO_ROOT)}") + return "skipped (dry-run)" + + try: + _process_image(light) + _process_image(dark) + except subprocess.CalledProcessError as e: + return f"error: image processing failed: {e}" + + staging = _staging_path(spec, language, library) + production = _production_path(spec, language, library) + try: + upload_to_staging(preview, staging) + promote_to_production(staging, production) + except subprocess.CalledProcessError as e: + return f"error: GCS upload failed: {e}" + + # Clean preview dir to save disk over a 1000+ file run. + shutil.rmtree(preview, ignore_errors=True) + return "done" + + +# ───────────────────────────────────────────────────────────────────────────── +# CLI +# ───────────────────────────────────────────────────────────────────────────── + + +@click.command() +@click.option( + "--library", + default="all", + help="Process one library or 'all'. Filters discovered targets.", +) +@click.option("--spec", default=None, help="Restrict to one spec (for testing).") +@click.option( + "--check-only", + is_flag=True, + help="Run pre-flight checks and exit. Does not render or upload.", +) +@click.option( + "--dry-run", + is_flag=True, + help="Render locally but do NOT optimize, upload, or promote.", +) +@click.option( + "--skip-on-error", + is_flag=True, + help="Continue past per-target failures instead of stopping.", +) +@click.option( + "--resume", + is_flag=True, + help="Skip items already marked 'done' in .migration-progress.json.", +) +def main(library: str, spec: str | None, check_only: bool, dry_run: bool, skip_on_error: bool, resume: bool) -> None: + """Re-render and upload light/dark images for migrated impls.""" + console = Console() + + # Discover targets first so we know which langs to pre-flight. + targets = _discover_targets_from_git(library) + if spec: + targets = [t for t in targets if t[0] == spec] + + if not targets and not check_only: + console.print("[yellow]No modified impl files in working tree.[/yellow] " + "Apply Stage A first (or pass --spec for one-off testing).") + if not spec: + sys.exit(1) + # If --spec given, fall through with a synthetic target. + # Spec given but not changed → run a single target inferred from library filter. + if library != "all": + for lang, libs in (("python", ["matplotlib", "seaborn", "plotnine", "plotly", "bokeh", "altair", "pygal", "highcharts", "letsplot"]), + ("r", ["ggplot2"]), + ("julia", ["makie"])): + if library in libs: + targets = [(spec, lang, library)] + break + + needs_r = any(t[1] == "r" for t in targets) + needs_julia = any(t[1] == "julia" for t in targets) + needs_selenium = any(t[2] in SELENIUM_LIBS for t in targets) + python_libs = {t[2] for t in targets if t[1] == "python"} + + pf = preflight(python_libs=python_libs, needs_r=needs_r, needs_julia=needs_julia, needs_selenium=needs_selenium) + if pf.notes: + console.print("[bold]Pre-flight checks:[/bold]") + for n in pf.notes: + console.print(f" [red]✗[/red] {n}") + if check_only or not (pf.python_ok and pf.r_ok and pf.julia_ok and pf.gcloud_ok): + console.print() + console.print("[red]Fix the pre-flight issues above before continuing.[/red]") + sys.exit(2 if not check_only else 0) + else: + console.print("[green]All pre-flight checks passed.[/green]") + if check_only: + sys.exit(0) + + progress_data = _load_progress() if resume else {} + progress_data.setdefault("__started__", time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())) + + console.print(f"\nProcessing {len(targets)} target(s) " + f"(library={library}, spec={spec or 'all'}, dry_run={dry_run})") + + failures: list[tuple[str, str]] = [] + with Progress( + SpinnerColumn(), + TextColumn("{task.description}"), + TimeElapsedColumn(), + console=console, + transient=False, + ) as bar: + task = bar.add_task("Migrating", total=len(targets)) + for spec_id, language, lib in targets: + key = _key(spec_id, language, lib) + if resume and progress_data.get(key) == "done": + bar.advance(task) + continue + bar.update(task, description=f"Migrating {key}") + result = process_one(spec_id, language, lib, dry_run=dry_run, console=console) + progress_data[key] = result + _save_progress(progress_data) + if result.startswith("error"): + failures.append((key, result)) + if not skip_on_error: + console.print(f"\n[red]Aborting on first error: {key} → {result}[/red]") + console.print("Re-run with --skip-on-error to continue past failures.") + sys.exit(1) + bar.advance(task) + + console.print(f"\n[bold]Done.[/bold] {len(targets) - len(failures)} succeeded, {len(failures)} failed.") + if failures: + console.print("[yellow]Failures:[/yellow]") + for k, msg in failures[:20]: + console.print(f" • {k}: {msg}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/scripts/migrate_to_imprint.py b/scripts/migrate_to_imprint.py new file mode 100644 index 0000000000..49e321cc1c --- /dev/null +++ b/scripts/migrate_to_imprint.py @@ -0,0 +1,298 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.13" +# dependencies = [ +# "click>=8.1", +# "pyyaml>=6.0", +# "rich>=13.0", +# ] +# /// +"""One-shot positional hex migration: Okabe-Ito / variant-D → imprint. + +Walks every existing impl file under ``plots/*/implementations/{python,r,julia}/``, +keeps only those whose metadata YAML has BOTH ``preview_url_light`` and +``preview_url_dark``, and rewrites old palette hex literals in place using +a positional slot-to-slot mapping. Idempotent: files that already contain +only imprint hex are left untouched. + +This is intentionally NOT integrated with the regen module — it's a +self-contained one-time fix that bypasses the AI review cycle to unify +the homepage palette quickly. +""" + +from __future__ import annotations + +import re +import sys +from collections import Counter, defaultdict +from dataclasses import dataclass, field +from pathlib import Path + +import click +import yaml +from rich.console import Console +from rich.table import Table + + +REPO_ROOT = Path(__file__).resolve().parent.parent + +# Positional slot mappings. Order = slot index in the old palette. +# Keys are lowercased so the regex match (also lowercased) hits them. +OKABE_TO_IMPRINT: dict[str, str] = { + "#009e73": "#009E73", + "#d55e00": "#C475FD", + "#0072b2": "#4467A3", + "#cc79a7": "#BD8233", + "#e69f00": "#AE3030", + "#56b4e9": "#2ABCCD", + "#f0e442": "#954477", +} + +VARIANT_D_TO_IMPRINT: dict[str, str] = { + "#009e73": "#009E73", + "#9418db": "#C475FD", + "#b71d27": "#AE3030", + "#16b8f3": "#4467A3", + "#99b314": "#99B314", + "#d359a7": "#954477", + "#ba843e": "#BD8233", +} + +# Combined map. Both palettes share #009E73, and Okabe doesn't include the +# variant-D hex codes (and vice versa), so they merge cleanly. +COMBINED: dict[str, str] = {**OKABE_TO_IMPRINT, **VARIANT_D_TO_IMPRINT} + +# Hex codes we expect to find — lowercased, with leading '#'. +OLD_HEXES: set[str] = {k for k in COMBINED if COMBINED[k].lower() != k} + +# Library names by language (subdir = language). +LIBRARIES_BY_LANGUAGE: dict[str, list[str]] = { + "python": ["matplotlib", "seaborn", "plotnine", "plotly", "bokeh", "altair", "pygal", "highcharts", "letsplot"], + "r": ["ggplot2"], + "julia": ["makie"], +} +FILE_EXT_BY_LANGUAGE: dict[str, str] = {"python": ".py", "r": ".R", "julia": ".jl"} + + +@dataclass +class Stats: + changed: int = 0 + skipped_already_imprint: int = 0 + skipped_no_dark: int = 0 + skipped_no_metadata: int = 0 + warnings: list[str] = field(default_factory=list) + changed_files: list[Path] = field(default_factory=list) + + +# Match any #RRGGBB literal (3-byte form). The substitution callback decides +# whether to replace based on the lowercased value. +HEX_RE = re.compile(r"#[0-9A-Fa-f]{6}") + + +def _substitute(text: str) -> tuple[str, int]: + """Apply combined positional map. Returns (new_text, replacement_count).""" + count = 0 + + def repl(match: re.Match[str]) -> str: + nonlocal count + original = match.group(0) + lower = original.lower() + new = COMBINED.get(lower) + if new is None or new == original: + return original + # Only count a real change (don't count #009E73 → #009E73 no-ops). + if new.lower() != lower: + count += 1 + return new + # Same hex but with different case — normalize case anyway, but don't + # count as a substantive change. + return new if new != original else original + + new_text = HEX_RE.sub(repl, text) + return new_text, count + + +def _rename_palette_constant(text: str) -> str: + """Rename common old palette variable names to IMPRINT (Python, R, Julia). + + Only renames identifiers that look like a standalone palette constant + (whole-word match, all-caps name). Conservative — does not touch + inline comments or string literals. + """ + # Whole-word, ASCII identifier boundary on both sides. + for old in ("OKABE_ITO", "ANYPLOT_PALETTE"): + text = re.sub(rf"\b{old}\b", "IMPRINT", text) + return text + + +def _metadata_path(spec_dir: Path, language: str, library: str) -> Path: + return spec_dir / "metadata" / language / f"{library}.yaml" + + +def _impl_path(spec_dir: Path, language: str, library: str) -> Path: + return spec_dir / "implementations" / language / f"{library}{FILE_EXT_BY_LANGUAGE[language]}" + + +def _has_dual_theme(meta_path: Path) -> bool: + """True iff metadata YAML has both preview_url_light AND preview_url_dark non-empty.""" + if not meta_path.is_file(): + return False + try: + data = yaml.safe_load(meta_path.read_text()) or {} + except yaml.YAMLError: + return False + return bool(data.get("preview_url_light")) and bool(data.get("preview_url_dark")) + + +def _process_file( + impl_path: Path, + meta_path: Path, + *, + dry_run: bool, + stats: Stats, +) -> None: + if not _has_dual_theme(meta_path): + # No dark variant on record → skip per scope rule. + if not meta_path.is_file(): + stats.skipped_no_metadata += 1 + else: + stats.skipped_no_dark += 1 + return + + original = impl_path.read_text() + lower = original.lower() + + has_old = any(h in lower for h in OLD_HEXES) + if not has_old: + stats.skipped_already_imprint += 1 + return + + # Detect mixed-palette case (file has hex from BOTH old palettes). + okabe_only = OLD_HEXES & set(OKABE_TO_IMPRINT) - {"#009e73"} + variant_only = OLD_HEXES & set(VARIANT_D_TO_IMPRINT) - {"#009e73", "#99b314"} + has_okabe = any(h in lower for h in okabe_only) + has_variant = any(h in lower for h in variant_only) + if has_okabe and has_variant: + stats.warnings.append(f"mixed palettes (Okabe + variant-D) in {impl_path.relative_to(REPO_ROOT)}") + + new_text, count = _substitute(original) + new_text = _rename_palette_constant(new_text) + + if new_text == original: + # Should not happen given has_old==True, but guard anyway. + stats.skipped_already_imprint += 1 + return + + if not dry_run: + impl_path.write_text(new_text) + stats.changed += 1 + stats.changed_files.append(impl_path) + + +def _iter_targets( + library_filter: str, + spec_filter: str | None, +) -> list[tuple[Path, str, str]]: + """Yield (spec_dir, language, library) for every existing impl matching filters.""" + plots_root = REPO_ROOT / "plots" + if not plots_root.is_dir(): + raise click.ClickException(f"plots/ not found at {plots_root}") + + targets: list[tuple[Path, str, str]] = [] + spec_dirs = sorted(plots_root.iterdir()) + for spec_dir in spec_dirs: + if not spec_dir.is_dir(): + continue + if spec_filter and spec_dir.name != spec_filter: + continue + for language, libs in LIBRARIES_BY_LANGUAGE.items(): + for library in libs: + if library_filter != "all" and library != library_filter: + continue + impl = _impl_path(spec_dir, language, library) + if impl.is_file(): + targets.append((spec_dir, language, library)) + return targets + + +def _summarize(stats_by_lib: dict[str, Stats], console: Console, dry_run: bool) -> None: + table = Table(title=("DRY RUN — no files written" if dry_run else "Migration applied")) + table.add_column("Library", style="bold") + table.add_column("Changed", justify="right", style="green") + table.add_column("Already imprint", justify="right", style="cyan") + table.add_column("No dark variant", justify="right", style="yellow") + table.add_column("No metadata", justify="right", style="red") + + totals = Counter() + for lib in sorted(stats_by_lib): + s = stats_by_lib[lib] + table.add_row( + lib, + str(s.changed), + str(s.skipped_already_imprint), + str(s.skipped_no_dark), + str(s.skipped_no_metadata), + ) + totals["changed"] += s.changed + totals["skipped_already_imprint"] += s.skipped_already_imprint + totals["skipped_no_dark"] += s.skipped_no_dark + totals["skipped_no_metadata"] += s.skipped_no_metadata + table.add_section() + table.add_row( + "[bold]TOTAL[/bold]", + f"[bold]{totals['changed']}[/bold]", + str(totals["skipped_already_imprint"]), + str(totals["skipped_no_dark"]), + str(totals["skipped_no_metadata"]), + ) + console.print(table) + + warns = [w for s in stats_by_lib.values() for w in s.warnings] + if warns: + console.print(f"\n[yellow]Warnings ({len(warns)}):[/yellow]") + for w in warns[:20]: + console.print(f" • {w}") + if len(warns) > 20: + console.print(f" … and {len(warns) - 20} more") + + +@click.command() +@click.option( + "--library", + default="all", + help="Restrict to one library (matplotlib, seaborn, ..., ggplot2, makie) or 'all'.", +) +@click.option("--spec", default=None, help="Restrict to one spec id (for testing).") +@click.option("--dry-run", is_flag=True, help="Report what would change; do not write files.") +@click.option("--list-changed", is_flag=True, help="After summary, list every changed file path (full report).") +def main(library: str, spec: str | None, dry_run: bool, list_changed: bool) -> None: + """Migrate Okabe-Ito / variant-D hex codes to imprint, in place.""" + console = Console(stderr=True) + + valid_libs = {lib for libs in LIBRARIES_BY_LANGUAGE.values() for lib in libs} | {"all"} + if library not in valid_libs: + raise click.ClickException(f"Unknown library: {library}. Valid: {sorted(valid_libs)}") + + targets = _iter_targets(library, spec) + if not targets: + console.print(f"[red]No targets matched library={library} spec={spec}[/red]") + sys.exit(1) + + console.print(f"Scanning {len(targets)} impl files (library={library}, spec={spec or 'all'})…") + + stats_by_lib: dict[str, Stats] = defaultdict(Stats) + for spec_dir, language, lib in targets: + impl = _impl_path(spec_dir, language, lib) + meta = _metadata_path(spec_dir, language, lib) + _process_file(impl, meta, dry_run=dry_run, stats=stats_by_lib[lib]) + + _summarize(stats_by_lib, console, dry_run=dry_run) + + if list_changed: + all_changed = [p for s in stats_by_lib.values() for p in s.changed_files] + for p in sorted(all_changed): + print(p.relative_to(REPO_ROOT)) + + +if __name__ == "__main__": + main()