File size: 3,772 Bytes
1ea7ba6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
"""

figstyle.py β€” one shared, publication-grade figure design system.



Import this at the top of every plot script:



    import figstyle

    figstyle.apply()

    ...

    figstyle.save(fig, "outputs/shortcut_matrix")   # writes .pdf AND .png (600 dpi)



Design goals (Transactions/CEA-grade):

  * Colorblind-safe (Okabe-Ito categorical + cividis sequential β€” both survive

    grayscale printing, unlike the old red/green).

  * Embedded TrueType fonts in PDF (pdf.fonttype=42) so no Type-3 AQC warnings.

  * White background, uniform typography, vector PDF + 600-dpi PNG fallback.



Nothing here needs a GPU; it is pure matplotlib configuration.

"""
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt


# ── Okabe-Ito colorblind-safe categorical palette ────────────────────────────
OKABE_ITO = {
    "black":        "#000000",
    "orange":       "#E69F00",
    "sky":          "#56B4E9",
    "green":        "#009E73",
    "yellow":       "#F0E442",
    "blue":         "#0072B2",
    "vermillion":   "#D55E00",
    "purple":       "#CC79A7",
    "grey":         "#8C8C8C",
}

# ── Semantic roles for THIS paper (kept consistent across every figure) ───────
# SS = Spice_Spectrum = in-the-wild (the robust/hero source)
# IN = Indian_Spices  = studio
PALETTE = {
    "wild":          OKABE_ITO["blue"],        # SS-trained / in-the-wild
    "studio":        OKABE_ITO["orange"],      # Indian-trained / studio
    "within":        OKABE_ITO["green"],       # within-source (good)
    "cross_free":    OKABE_ITO["sky"],         # the free cross direction (wild->studio)
    "cross_broken":  OKABE_ITO["vermillion"],  # the collapse (studio->wild)
    "chance":        OKABE_ITO["grey"],        # random baseline line
}

# Sequential map for the accuracy heatmap: colorblind- AND grayscale-safe.
MATRIX_CMAP = "cividis"


def apply():
    """Set global rcParams. Call once at the top of a plot script."""
    plt.rcParams.update({
        "pdf.fonttype":       42,     # embed TrueType (no Type-3)
        "ps.fonttype":        42,
        "svg.fonttype":       "none",
        "font.family":        "sans-serif",
        "font.sans-serif":    ["DejaVu Sans", "Arial", "Helvetica"],
        "font.size":          11,
        "axes.titlesize":     12,
        "axes.labelsize":     11,
        "axes.labelweight":   "bold",
        "xtick.labelsize":    10,
        "ytick.labelsize":    10,
        "legend.fontsize":    9,
        "figure.facecolor":   "white",
        "axes.facecolor":     "white",
        "savefig.facecolor":  "white",
        "axes.spines.top":    False,
        "axes.spines.right":  False,
        "axes.grid":          False,
        "figure.dpi":         120,
    })


def save(fig, stem, dpi_png: int = 600, also_tiff: bool = False):
    """Save `fig` to `<stem>.pdf` (vector) and `<stem>.png` (600 dpi), white bg.



    `stem` may include a directory; parents are created. Returns the list of paths.

    """
    stem = Path(stem)
    stem.parent.mkdir(parents=True, exist_ok=True)
    out = []
    pdf = stem.with_suffix(".pdf")
    png = stem.with_suffix(".png")
    fig.savefig(pdf, bbox_inches="tight", facecolor="white")
    fig.savefig(png, dpi=dpi_png, bbox_inches="tight", facecolor="white")
    out += [pdf, png]
    if also_tiff:
        tif = stem.with_suffix(".tiff")
        fig.savefig(tif, dpi=300, bbox_inches="tight", facecolor="white",
                    pil_kwargs={"compression": "tiff_lzw"})
        out.append(tif)
    plt.close(fig)
    for p in out:
        print(f"  saved {p}")
    return out