---
name: zyplib-vis
description: Use when an agent needs to create, refine, or review figures with `zyplib.vis`. Trigger when the user asks to plot EEG signals, PSD/STFT/CWT/DWT results, draw common statistical or ML charts, arrange multi-panel Matplotlib figures, unify plotting styles, add annotations/highlights/comparison markers, build palette-aware figures, or use zyplib's built-in visualization helpers instead of writing raw Matplotlib from scratch.
---

# zyplib-vis

Use `zyplib.vis` as a two-level visualization toolkit: top-level `plot_xxx` functions for complete figures, and `mpl_utils` for composable draw/layout/style helpers.

Assume the caller is using a released/importable `zyplib` package. Prefer the public API re-exported from `zyplib.vis`; only deep-import submodules when you need module-specific behavior, or when the needed helper exists in a submodule but is not re-exported from `zyplib.vis`.

## Read the source in this order

- First locate the installed/importable `zyplib` package root.

  ```bash
  python -c "import zyplib; from pathlib import Path; print(Path(zyplib.__file__).resolve().parent)"
  ```

- Then read `<zyplib>/vis/` in this order.
  - Start with `__init__.py` to see the public API surface and the default style side effect.
  - Then read `plot_common.py`, `plot_eeg.py`, `plot_ml.py`, and `plot_misc.py` to understand the task-oriented top-level plotting entries.
  - Then read `mpl_utils/__init__.py`, followed by `draw.py`, `layout.py`, and `style.py`.
  - Read `topomap.py`, `color_palette.py`, and `connectivity.py` only when the task is specifically about topography, palette design, or connectivity plots.

## Understand the package structure

Think in this order:

```text
zyplib.vis
├── Level 1: complete plot entry points
│   ├── plot_common.py   -> line/bar/scatter/distribution/heatmap/...
│   ├── plot_eeg.py      -> signals, PSD, STFT, CWT, DWT
│   ├── plot_ml.py       -> PCA, TSNE, confusion matrix, training curves, ...
│   └── plot_misc.py     -> specialized task plots
├── Level 2: mpl_utils composable helpers
│   ├── draw.py          -> annotations, highlights, comparison marks, inset zoom
│   ├── layout.py        -> grids, mosaic layout, save helpers
│   └── style.py         -> axis/figure styling, rc/style contexts, color cycle
└── Specialized modules outside the main two-level split
    ├── topomap.py       -> EEG topography helpers built on MNE
    ├── color_palette.py -> palette definitions and color-generation helpers
    └── connectivity.py  -> connectivity-specific visualization helpers
```

The important mental model is:

1. Use `plot_xxx` when you want zyplib to own the whole plotting flow.
2. Use `mpl_utils` when you already have Matplotlib axes/figures and want reusable enhancement blocks.
3. Reach for `topomap.py`, `color_palette.py`, and `connectivity.py` when the task is domain-specific rather than part of the generic `plot_xxx` + `mpl_utils` split.

## Level 1: `plot_xxx` owns the complete plotting layer

`plot_common.py`, `plot_eeg.py`, `plot_ml.py`, and `plot_misc.py` contain the main task-oriented plotting APIs.

Use this layer when the request is something like:

- "plot a line/bar/scatter/distribution/heatmap"
- "plot EEG signals / PSD / STFT / CWT / DWT"
- "plot PCA / TSNE / confusion matrix / training curve"

Key characteristics:

- These functions are the main entry point for making a full chart from task data.
- They usually accept `ax=None`, create a figure when needed, and return `fig, ax` for further edits.
- They usually handle `show`, `save_path`, and `auto_close` through the shared `_prepare_ax` / `_finalize_figure` lifecycle.
- They may accept convenience fields such as `title`, `xlabel`, `ylabel`, or labels, but that does **not** make them the main style layer.
- Consistent styling should still be centralized separately when possible.

Treat them as the "complete plot" layer, not as raw drawing primitives.

Important nuance:

- Most generic `plot_xxx` functions follow the shared lifecycle contract clearly.
- Some specialized modules, such as `topomap.py`, are more domain-specific wrappers and do **not** follow the exact same `fig, ax` contract.
- So the correct rule is: `plot_xxx` is the default complete layer, not an absolute guarantee that every visualization helper in `zyplib.vis` looks identical.

When you plan to keep editing the figure after calling a `plot_xxx` function, control the lifecycle explicitly:

- Prefer `show=False` while you are still decorating the figure.
- Keep `auto_close=False` if more post-processing is coming.
- Then apply `style_ax`, `style_figure`, or your own Matplotlib logic afterward.

## Level 2: `mpl_utils` is the composable Matplotlib toolbox

`mpl_utils` is not the main plotting entry. It is the reusable tool layer that helps you assemble, polish, and annotate Matplotlib figures.

### `draw.py`: local visual enhancements

Use when the figure already exists and you need targeted additions:

- `draw_reference_lines` - vertical/horizontal reference lines
- `draw_highlight` - highlighted ranges or regions
- `draw_annotate_arrow` / `draw_text` - annotation helpers
- `draw_error_regions` - error bands / uncertainty areas
- `draw_comparison_lines` - significance/comparison brackets
- `draw_inset_zoom` - inset zoom panel
- `create_broken_axes` - broken-axis layout helper

This layer is for "decorate an existing plot", not "decide the whole chart structure".

### `layout.py`: figure and subplot organization

Use when the main issue is layout rather than data drawing:

- `make_grids` - regular subplot grids
- `make_figure_layout` - named mosaic layouts
- `save_figure` - standardized save helper
- `adjust_subplot_spacing` / `adjust_figure_margins` - layout polishing

This layer is especially useful when one user request implies a multi-panel figure, report-style canvas, or repeated subplot workflow.

### `style.py`: consistent styling and rc control

Use when the main issue is visual consistency:

- `style_ax` - style one axis
- `style_figure` - style a whole figure after plotting
- `MplStyleContext` - temporary style/rc context
- `use_color_cycle` - temporary or global color cycle override
- `use_font_family` / `use_font_size` - font-related rc helpers
- `register_mpl_styles` - load built-in `.mplstyle` files into Matplotlib

Important rule: context-based style changes must happen **before** creating the figure or axes they are supposed to affect.

That is especially true for:

- `MplStyleContext(...)`
- `use_color_cycle(..., policy='context')`
- other rc/style contexts that alter Matplotlib defaults

## Independent modules worth knowing

These modules are part of `zyplib.vis`, but they are not just aliases for the two main layers.

### `topomap.py`

- Use for EEG topography / scalp maps.
- Main helpers: `topmap`, `pxx_topomap`.
- Built on MNE (`mne.viz.plot_topomap`).
- Treat this as a domain-specific visualization entry, not a generic `plot_common.py` style plot helper.

### `color_palette.py`

- Use when the task is really about palette selection, palette generation, or previewing color schemes.
- Includes predefined publication-friendly schemes such as `TABLEAU_10`, `SEABORN_DEEP_10`, `VIRIDIS_10`, `CIVIDIS_10`, and `COOLWARM_10`.
- Pairs naturally with `mpl_utils.use_color_cycle`.
- Think of it as the color vocabulary layer that supports the style layer.

### `connectivity.py`

- Use for EEG/connectivity-specific visualizations rather than generic charts.
- Exposes connectivity-related helpers such as `plot_connectivity_circle` and matrix reshaping utilities.
- This is a specialized domain module, not a substitute for `plot_line` / `plot_heatmap` / `mpl_utils`.

## Choose the right entry

Use this decision map:

- Need a complete standard chart quickly -> start with `zyplib.vis.plot_xxx`
- Need to arrange many subplots or a non-trivial panel layout -> use `make_grids` or `make_figure_layout`
- Need to apply consistent titles/labels/grid/spines/tick/legend rules after plotting -> use `style_ax` or `style_figure`
- Need local annotations/highlights/error bands/comparison brackets/inset zoom -> use `draw_*`
- Need a dedicated EEG topomap -> use `topomap.py`
- Need palette design or palette preview -> use `color_palette.py`
- Need connectivity-specific visualization -> use `connectivity.py`

Prefer public re-exports from `zyplib.vis` first, for example:

```python
import zyplib.vis as zvis

fig, ax = zvis.plot_line(...)
zvis.style_ax(ax, ...)
```

Only deep-import the submodule when that module itself is the thing you need to reason about, or when the helper is not exposed at the top level.

## Recommended styling workflow

This is the preferred style for writing `zyplib.vis` plotting code.

### Recommended sequence

1. Import `zyplib` at the top of the script, and import `zyplib.vis` early.
2. Define style globally near the top, or wrap the plotting block in a style context.
3. Keep the middle section focused on plotting logic.
4. Apply final axis/figure polishing with `style_ax` / `style_figure` if needed.
5. Only inline local style parameters when the figure really has special local exceptions.

Important precision:

- Importing `zyplib` alone does **not** apply the `zyplib.vis` default style.
- The built-in Matplotlib style registration happens when `zyplib.vis` is imported.
- So the recommended pattern is: import `zyplib`, then import `zyplib.vis` early enough that style defaults are active before plotting.

### Best-practice pattern

```python
import zyplib
import zyplib.vis as zvis

# Option A: rely on the default `zyp-default` applied by `zyplib.vis`
# Option B: add a small global/context override here, before creating figures
with zvis.MplStyleContext(rc={'font.size': 13}):
    fig, ax = zvis.plot_line(x=x, y=y, show=False, auto_close=False)
    zvis.style_ax(ax, title='Example', xlabel='Time', ylabel='Value')
```

The point is not "never pass style kwargs". The point is to keep code readable:

- Good default: style policy first, plotting logic second
- Good default: one place sets the overall look
- Good default: local plot calls focus on data and structure

Avoid this becoming a mixed style soup:

- plotting logic
- inline line color tweaks
- inline font changes
- inline grid/spine tweaks
- inline legend formatting

all scattered across the middle of the plotting code

Inline style is still acceptable when:

- one subplot really is a local exception
- a comparison figure needs one-off emphasis
- the figure is complex enough that a single global style policy is not sufficient

But that should be the exception, not the default style of writing.

## Built-in styles and defaults

`zyplib.vis` has an import-time style side effect.

The current behavior is:

1. `zyplib.vis.__init__` checks `config.LOAD_DEFAULT_MPL_STYLE`
2. the default value is `True`
3. when enabled, `register_mpl_styles()` loads `.mplstyle` files from `mpl_utils/styles/` and its subfolders
4. if `zyp-default` is available, `plt.style.use('zyp-default')` is applied automatically

So when you import `zyplib.vis`, you should assume the default visual baseline may already be active.

Built-in styles currently include at least:

- `zyp-default`
- `zyp`
- `science`
- `scatter`
- journal presets under `styles/journals/`, such as `ieee` and `nature`

### `zyp-default`: the default baseline you should know

Key defaults currently include:

- color cycle: Tableau-like 10-color palette (`#4E79A7`, `#F28E2B`, `#E15759`, ...)
- font family: `sans-serif`, with Chinese-friendly fallbacks such as `HarmonyOS Sans SC`, `Microsoft YaHei`, `Noto Sans`
- base font size: `12.5`
- axes spines: top/right hidden by default
- grid: enabled by default, light alpha (`0.3`) and thin width (`0.5`)
- ticks: inward by default (`xtick.direction: in`, `ytick.direction: in`)
- legend: frame off by default
- lines: linewidth `1.2`, markersize `4`
- figure/save defaults: `figure.dpi=100`, `savefig.dpi=300`, `savefig.bbox=tight`, white figure background

This means a lot of figures will already look "publication-ish" without extra styling.

### Other built-in styles are heavier

Be careful with `zyp` and `science`:

- they enable `text.usetex: True`
- they are more paper-style and more opinionated
- they may require a working LaTeX environment

So do not switch to them casually in automation unless the environment is ready for that requirement.

## Finish with these checks

Before finishing a `zyplib.vis` task, check these points:

1. Did I start from the correct layer: `plot_xxx` for full plots, `mpl_utils` for enhancement, or a specialized module for domain-specific visuals?
2. Did I import `zyplib.vis` early enough for built-in style registration to happen before plotting?
3. If I used a style context or color-cycle context, did I create the figure inside that context?
4. Is the plotting code mostly about plotting logic, with style policy defined up front rather than scattered inline?
5. If I used inline styling heavily, is it because the figure genuinely needed local exceptions?
6. If the figure still needs post-processing, did I keep `show` / `auto_close` under control so the figure remains editable?
7. Am I using the public `zyplib.vis` API where possible instead of unnecessary deep imports?
