Metadata-Version: 2.5
Name: stockchart
Version: 0.2.0
Summary: A creative plotting library that draws data as stockings pulled up over legs. 一个用丝袜与腿表示数据的创意绘图库。
Project-URL: Homepage, https://github.com/xie-huaaaan/stockchart
Project-URL: Repository, https://github.com/xie-huaaaan/stockchart
Project-URL: Issues, https://github.com/xie-huaaaan/stockchart/issues
Project-URL: Documentation, https://github.com/xie-huaaaan/stockchart/blob/main/docs/api/index.md
Author: xie-huaaaan
License-Expression: MPL-2.0 AND LicenseRef-ARR
License-File: ASSETS-LICENSE
License-File: LICENSE
Keywords: charting-library,charts,creative-coding,data-visualization,infographics,matplotlib,plotting,skia
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Scientific/Engineering :: Visualization
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: numpy>=2.5
Requires-Dist: pillow>=13.0
Requires-Dist: skia-python>=144.1
Description-Content-Type: text/markdown

# Stock Chart (丝袜统计图)

[GitHub: xie-huaaaan/stockchart](https://github.com/xie-huaaaan/stockchart)



**A creative plotting library that draws data as stockings pulled up over a leg.**

![A grouped stockchart: two series over three months](https://raw.githubusercontent.com/xie-huaaaan/stockchart/main/docs/images/hero.png)

`stockchart` is a playful alternative to the bar chart. Each value becomes the height of a stocking pulled up over a leg: a larger value means more stocking, and zero is a bare leg. It is made for infographics, slides, posters, and anywhere a chart should be memorable rather than conventional.

- **Values → stocking height.** Grouped, paired, and positive/negative mirror charts are supported.
- **19 built-in styles.** Sheer, opaque tights, stripes, knit, fishnet, polka dots, hearts, lace trims, and more.
- **Data-science friendly.** Lists, NumPy arrays, dictionaries, and pandas `DataFrame`/`Series`. pandas is optional and never imported by the library itself.
- **Native high-resolution output.** Stocking textures and lighting are generated at the target pixel density, not upscaled from a bitmap.
- **Composable by design.** Start with one-line `bar()`; drop down to pixel-level placement when you need a custom layout.
- **No matplotlib.** Text and lines are rendered with Skia, including a per-character font fallback for Chinese, Japanese, and Korean.

<table>
  <tr>
    <td width="50%" align="center">
      <img src="https://raw.githubusercontent.com/xie-huaaaan/stockchart/main/docs/images/showcase-grouped.png" alt="A grouped chart comparing planned and actual weekend hours"><br>
      <sub>Grouped chart — <code>black_sheer</code> (welt) and <code>white_sheer</code> (lace)</sub>
    </td>
    <td width="50%" align="center">
      <img src="https://raw.githubusercontent.com/xie-huaaaan/stockchart/main/docs/images/showcase-single.png" alt="A single-series chart with negative values, all legs upright"><br>
      <sub>Single series with negative values — every leg stays upright (<code>heart_white</code>)</sub>
    </td>
  </tr>
  <tr>
    <td width="62%" align="center">
      <img src="https://raw.githubusercontent.com/xie-huaaaan/stockchart/main/docs/images/showcase-styles.png" width="95%" alt="Seven legs wearing seven different stocking styles"><br>
      <sub>A sweep of presets — 5 fabric types, 4 top edges</sub>
    </td>
    <td width="38%" align="center">
      <img src="https://raw.githubusercontent.com/xie-huaaaan/stockchart/main/docs/images/showcase-mirror.png" width="62%" alt="A mirror chart with positive and negative values"><br>
      <sub>Mirror chart — positive and negative values</sub>
    </td>
  </tr>
</table>

## Installation

```bash
pip install stockchart
```

or with [uv](https://docs.astral.sh/uv/):

```bash
uv add stockchart
```

Requires Python 3.12 or newer. Runtime dependencies are `skia-python`, `numpy`, and `pillow`.

## Quick start

```python
from stockchart import bar

bar(
    [[320, 210], [450, 280], [510, 190]],
    labels=["Jan", "Feb", "Mar"],
    series_labels=["This site", "Neighbor"],
    title="Monthly report",
    xlabel="Month",
    ylabel="Active users",
    style=["black_sheer", "white_sheer"],
    value_labels=True,
    seed=2,
).save("chart.png")
```

The result is the image at the top of this README. `bar()` maps the data, chooses leg assets, lays out groups, and draws the axes. It returns a `BarPlot` that can be saved directly or adjusted further.

A mirror chart for positive and negative values:

```python
from stockchart import mirror_bar

mirror_bar(
    [4.2, -2.8, 6.1, -5.5, 3.4, -1.9],
    labels=["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
    title="Daily change",
    style="gray_sheer",
    value_labels=True,
    seed=2,
).save("mirror.png")
```

## Data input

Pass the data structure you already have:

| Input | How it is interpreted |
| --- | --- |
| 1-D list / tuple / sequence | One series |
| 2-D sequence `(groups, series)` | Grouped data |
| `numpy.ndarray` | 1-D or 2-D arrays |
| `dict` of scalars | Keys become x-axis labels, one series |
| `dict` of equal-length sequences | Keys become series names and a legend is generated |
| pandas `DataFrame` | Index → x labels, columns → series names |
| pandas `Series` | Index → x labels, one series |
| `PlotData` | Already-normalized data |

For example, a DataFrame gets its labels and legend automatically:

```python
import pandas as pd
from stockchart import bar

df = pd.DataFrame(
    {"This site": [320, 450, 510], "Neighbor": [210, 280, 190]},
    index=["Jan", "Feb", "Mar"],
)
bar(df, title="Monthly report", style=["black_sheer", "white_sheer"]).save("chart.png")
```

Explicit `labels=` and `series_labels=` override anything inferred from the data. Custom containers can be registered with `register_data_type`; see the [data input reference](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api/data-input.md).

## Stocking styles

Pass a preset name, or a list of names to cycle through styles:

```python
bar(
    [5, 6, 7, 8, 6, 9, 4],
    labels=["Sheer", "Lace", "Stripe", "Knit", "Fishnet", "Dots", "Tights"],
    title="One of each",
    style=[
        "nude_sheer",
        "white_sheer",
        "stripe_rainbow",
        "knit_cream",
        "fishnet_black",
        "polka_black",
        "black_pantyhose",
    ],
    value_labels=True,
    seed=3,
)
```

The 19 presets cover sheer stockings, opaque tights, stripes, ribbed knit, fishnet, polka dots, hearts, lace trims, and more. For direct control, pass a `StockingStyle` built from `Plain`, `Striped`, `Ribbed`, `Fishnet`, or `Dotted` fabrics with a `Bare`, `Welt`, `Lace`, or `Fade` top edge. The full style table is in the [rendering reference](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api/rendering.md).

## Using external assets

Load transparent PNG or SVG files with `load_leg()`, collect them in `LegLibrary([...])`, and pass `library=` to a chart. You may also include built-in legs from `DEFAULT_LEG_LIBRARY`.

```python
from stockchart import LegLibrary, bar, load_leg

library = LegLibrary([load_leg("left.png"), load_leg("right.svg")])
bar([3, 5, 4], library=library, paired="none", seed=2).save("custom.png")
```

Use the same design height for assets in a library, preferably **720px high** to match the built-in style and layout defaults. A PNG's pixel dimensions become its design dimensions: a 1440px-high PNG is imported as 1440 design pixels, not automatically treated as a 2x version of a 720px asset. Use `.save(..., scale=2)` for higher output resolution. Other design heights require adjusting lengths such as welts, stripes, mesh cells, and layout gaps. Prepare assets with transparent backgrounds and the foot at the bottom, close to the image edge. See [practical advice for external assets](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api/leg-assets.md#practical-advice-for-external-assets) for preparation, sizing, and style examples.

## Titles, axis names, and options

```python
bar(
    [[320, 210], [450, 280], [510, 190]],
    labels=["Jan", "Feb", "Mar"],
    title="Monthly report",  # centered over the plot by default
    xlabel="Month",
    ylabel="Active users",
    title_align="left",  # restore a left-aligned title
    grid=True,
    axis_style="handdrawn",
    legend_loc="bottom",
)
```

Chart-level parameters stay in the signature; lower-level options such as `grid`, `axis_style`, `legend_loc`, `legend_ncol`, `x_label_rotation`, `bar_gap`, `group_gap`, and `font` are passed through a typed option bag. See the [`ChartOptions` table](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api/charts.md).

## API layers

The library is deliberately layered, so you can choose how much control you want:

| Layer | What it gives you | Entry points |
| --- | --- | --- |
| L0 assets | Leg metadata and built-in artwork | `Leg`, `LegLibrary`, `load_leg` |
| L1 primitives | One stocking, text, hand-drawn lines | `render_stocking`, `StockingStyle`, `STYLES`, `draw_text`, `sketch_*` |
| L2 free-form plot | Canvas, baseline, arbitrary leg placement | `BarPlot`, `Scale`, `Canvas` |
| L3 layout helpers | Uniform spacing and automatic leg selection | `add_bars`, `add_grouped_bars`, `add_paired_bars` |
| L4 one-liners | Data normalization and fully automatic layout | `bar`, `mirror_bar` |

For a more hands-on layout:

```python
from stockchart import BarPlot, Scale

plot = BarPlot(1200)
plot.add_grouped_bars([[300], [500], [400]], style="black_sheer")
plot.add_x_labels(["A", "B", "C"])
plot.add_y_axis(Scale((0, 500), (0, 500)))
plot.add_title("Free-form layout")
plot.save("chart.png")
```

## Output

`BarPlot` renders to PNG and can also return a PIL image or a NumPy array:

```python
plot.save("chart.png", scale=2)  # PNG at 2× pixel density
image = plot.to_pil()
array = plot.to_numpy()
```

`scale` controls output pixel density; stocking textures and lighting are regenerated at that density. SVG and PDF output are intentionally not supported: the stocking fabric is a per-pixel algorithm, so a vector version would need a separate renderer — PNG is the only output format.

## Documentation

- [API reference](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api/index.md) — one page per layer
- [Examples](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api/examples.md) — runnable snippets by scenario
- [Design notes](https://github.com/xie-huaaaan/stockchart/blob/main/docs/design.md)

## Development

```bash
git clone https://github.com/xie-huaaaan/stockchart.git
cd stockchart
uv sync
uv run python -m pytest
uv run ruff check . && uv run ruff format .
uv run python -m pyright
```

Issues and pull requests are welcome.

## Status

`0.x.x` is an early public release and API may still change in minor releases.

## License

Copyright © 2026 xie-huaaaan.

The source code is released under the [Mozilla Public License 2.0](https://github.com/xie-huaaaan/stockchart/blob/main/LICENSE) (MPL-2.0). You can use `stockchart` in closed-source projects; if you modify MPL-covered source files, those files must remain available under MPL-2.0.

The built-in leg artwork (`src/stockchart/assets/leg_*.svg` and `legs.json`) and its precomputed ink maps and vector drawing commands (`src/stockchart/_precomputed/leg_*.npz` / `leg_*.skp`) are **All Rights Reserved** and are not covered by MPL-2.0; see the [asset license](https://github.com/xie-huaaaan/stockchart/blob/main/ASSETS-LICENSE). The artwork may be used to render charts with `stockchart`, and charts you generate are your own work. The artwork itself may not be extracted or redistributed except as part of an unmodified stockchart package.

---

<details>
<summary>简体中文</summary>

**用丝袜被拉起的高度表示数据的创意绘图库。**

![分组丝袜图：三个月、两个系列](https://raw.githubusercontent.com/xie-huaaaan/stockchart/main/docs/images/hero-zh.png)

`stockchart` 是柱状图的一种有趣替代：每个数值对应一条腿上丝袜被拉起的高度，数值越大丝袜越高，0 则是一条裸腿。它适合信息图、幻灯片、海报，以及任何希望图表更有个性、而不只是“标准”的场合。

- **数值 → 丝袜高度**：支持分组、成对，以及正负值上下镜像。
- **19 种内置样式**：透肉丝袜、厚袜、条纹、针织、网袜、波点、爱心、蕾丝边等。
- **支持常见数据类型**：列表、NumPy 数组、字典、pandas `DataFrame` / `Series`。pandas 是可选依赖，库本身不会 import 它。
- **原生高分辨率输出**：丝袜纹理与光影按目标像素密度重新生成，不是把小图放大。
- **分层可组合**：一行 `bar()` 即可出图；需要自由排版时可下沉到逐像素放置。
- **不依赖 matplotlib**：文字与线条由 Skia 渲染，逐字符字体回退，中文可正常显示。

<table>
  <tr>
    <td width="50%" align="center">
      <img src="https://raw.githubusercontent.com/xie-huaaaan/stockchart/main/docs/images/showcase-grouped-zh.png" alt="分组图：周末计划与实际的小时数对比"><br>
      <sub>分组图 —— <code>black_sheer</code>（松紧袜口）与 <code>white_sheer</code>（蕾丝边）</sub>
    </td>
    <td width="50%" align="center">
      <img src="https://raw.githubusercontent.com/xie-huaaaan/stockchart/main/docs/images/showcase-single-zh.png" alt="含负值的单系列图，全部朝上的腿"><br>
      <sub>单系列含负值 —— 依旧全是朝上的腿（<code>heart_white</code>）</sub>
    </td>
  </tr>
  <tr>
    <td width="62%" align="center">
      <img src="https://raw.githubusercontent.com/xie-huaaaan/stockchart/main/docs/images/showcase-styles-zh.png" width="95%" alt="七条腿穿着七种不同的丝袜样式"><br>
      <sub>样式巡礼 —— 5 种袜身、4 种上沿</sub>
    </td>
    <td width="38%" align="center">
      <img src="https://raw.githubusercontent.com/xie-huaaaan/stockchart/main/docs/images/showcase-mirror-zh.png" width="62%" alt="正负值镜像图"><br>
      <sub>镜像图 —— 正负值上下分布</sub>
    </td>
  </tr>
</table>

## 安装

```bash
pip install stockchart
```

或使用 [uv](https://docs.astral.sh/uv/)：

```bash
uv add stockchart
```

需要 Python 3.12 或更高版本。运行时依赖只有 `skia-python`、`numpy`、`pillow`。

## 快速开始

```python
from stockchart import bar

bar(
    [[320, 210], [450, 280], [510, 190]],
    labels=["一月", "二月", "三月"],
    series_labels=["本站", "隔壁站"],
    title="每月活跃用户",
    xlabel="月份",
    ylabel="人数",
    style=["black_sheer", "white_sheer"],
    value_labels=True,
    seed=2,
).save("chart.png")
```

输出就是本文顶部那张图。`bar()` 会自动完成数值映射、选腿、分组布局与坐标轴绘制，返回一个可以直接 `.save()`、也可以继续微调的 `BarPlot`。

正负值镜像图：

```python
from stockchart import mirror_bar

mirror_bar(
    [4.2, -2.8, 6.1, -5.5, 3.4, -1.9],
    labels=["周一", "周二", "周三", "周四", "周五", "周六"],
    title="每日变化",
    style="gray_sheer",
    value_labels=True,
    seed=2,
).save("mirror.png")
```

## 数据输入

直接传入手头的数据结构即可：

| 输入 | 解释 |
| --- | --- |
| 一维列表 / 元组 / 序列 | 单系列 |
| 二维序列 `(组数, 系列数)` | 分组数据 |
| `numpy.ndarray` | 一维或二维数组 |
| 标量值字典 | 键作 x 轴标签，单系列 |
| 等长序列字典 | 键作系列名，并自动生成图例 |
| pandas `DataFrame` | index → x 标签，columns → 系列名 |
| pandas `Series` | index → x 标签，单系列 |
| `PlotData` | 已归一化的数据 |

例如 DataFrame 会自动带上标签和图例：

```python
import pandas as pd
from stockchart import bar

df = pd.DataFrame(
    {"本站": [320, 450, 510], "隔壁站": [210, 280, 190]},
    index=["一月", "二月", "三月"],
)
bar(df, title="每月活跃用户", style=["black_sheer", "white_sheer"]).save("chart.png")
```

显式传入的 `labels=` / `series_labels=` 会覆盖从数据推断出的值。自定义容器可以用 `register_data_type` 注册，详见 [数据输入参考](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api.zh-CN/data-input.md)。

## 丝袜样式

传一个预设名，或用列表让样式循环：

```python
bar(
    [5, 6, 7, 8, 6, 9, 4],
    labels=["肉丝", "白丝", "彩条", "针织", "网袜", "波点", "厚袜"],
    title="每种来一条",
    style=[
        "nude_sheer",
        "white_sheer",
        "stripe_rainbow",
        "knit_cream",
        "fishnet_black",
        "polka_black",
        "black_pantyhose",
    ],
    value_labels=True,
    seed=3,
)
```

19 个预设覆盖透肉丝袜、厚袜、条纹、针织、网袜、波点、爱心、蕾丝边等。需要更细的控制时，可以直接构造 `StockingStyle`：袜身用 `Plain` / `Striped` / `Ribbed` / `Fishnet` / `Dotted`，上沿用 `Bare` / `Welt` / `Lace` / `Fade`。完整样式表见 [渲染原语参考](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api.zh-CN/rendering.md)。

## 使用外部素材

用 `load_leg()` 导入透明 PNG 或 SVG，再通过 `LegLibrary([...])` 和 `library=` 传给图表；也可以混入 `DEFAULT_LEG_LIBRARY` 中的内置腿。

```python
from stockchart import LegLibrary, bar, load_leg

library = LegLibrary([load_leg("left.png"), load_leg("right.svg")])
bar([3, 5, 4], library=library, paired="none", seed=2).save("custom.png")
```

建议同一库中的素材使用相同设计高度，并优先准备为 **720px 高**，以适配内置样式与布局默认值。PNG 的像素尺寸就是设计尺寸；1440px 高的 PNG 导入后是 1440 个设计像素，不会被自动视为“720px 素材的 2x 版本”。要提高输出分辨率，请用 `.save(..., scale=2)`；保留其他设计高度时，需要自行调整袜口、条纹、网眼等长度和布局间距。素材应有透明背景、脚端朝下并贴近图片底部。完整的准备建议、尺寸换算和样式示例见[外部素材实操建议](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api.zh-CN/leg-assets.md#外部素材实操建议)。

## 标题、轴名与选项

```python
bar(
    [[320, 210], [450, 280], [510, 190]],
    labels=["一月", "二月", "三月"],
    series_labels=["本站", "隔壁站"],
    title="每月活跃用户",  # 默认在绘图区上方居中
    xlabel="月份",
    ylabel="人数",
    title_align="left",  # 需要时改回左对齐
    grid=True,
    axis_style="handdrawn",
    legend_loc="bottom",
)
```

图表语义参数留在函数签名里；`grid`、`axis_style`、`legend_loc`、`legend_ncol`、`x_label_rotation`、`bar_gap`、`group_gap`、`font` 等下层选项通过类型化的选项包透传。完整列表见 [`ChartOptions` 表](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api.zh-CN/charts.md)。

## API 分层

库里刻意分成五层，按需要的控制力选择即可：

| 层 | 提供什么 | 入口 |
| --- | --- | --- |
| L0 素材 | 腿素材的元数据与内置图形 | `Leg`、`LegLibrary`、`load_leg` |
| L1 渲染原语 | 单条丝袜、文字、手绘线条 | `render_stocking`、`StockingStyle`、`STYLES`、`draw_text`、`sketch_*` |
| L2 自由柱状图 | 画布、基线、任意位置贴腿 | `BarPlot`、`Scale`、`Canvas` |
| L3 布局封装 | 统一间距、自动选腿 | `add_bars`、`add_grouped_bars`、`add_paired_bars` |
| L4 一行出图 | 数据归一 + 全自动布局 | `bar`、`mirror_bar` |

例如 L2/L3 可以自己摆放每一条腿：

```python
from stockchart import BarPlot, Scale

plot = BarPlot(1200)
plot.add_grouped_bars([[300], [500], [400]], style="black_sheer")
plot.add_x_labels(["A", "B", "C"])
plot.add_y_axis(Scale((0, 500), (0, 500)))
plot.add_title("自由排版")
plot.save("chart.png")
```

## 输出

`BarPlot` 输出 PNG，也可以直接拿 PIL Image 或 NumPy 数组：

```python
plot.save("chart.png", scale=2)  # 2 倍像素密度的 PNG
image = plot.to_pil()
array = plot.to_numpy()
```

`scale` 控制输出像素密度；丝袜纹理与光影会按该密度重新生成。库不提供 SVG / PDF 输出：袜身是逐像素算法，转矢量需要另写渲染器，输出只有 PNG。

## 文档

- [API 参考](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api.zh-CN/index.md)——每层一页
- [示例](https://github.com/xie-huaaaan/stockchart/blob/main/docs/api.zh-CN/examples.md)——按场景组织的可运行代码
- [设计说明](https://github.com/xie-huaaaan/stockchart/blob/main/docs/design.zh-CN.md)

## 开发

```bash
git clone https://github.com/xie-huaaaan/stockchart.git
cd stockchart
uv sync
uv run python -m pytest
uv run ruff check . && uv run ruff format .
uv run python -m pyright
```

欢迎提 issue 或 PR。

## 状态

`0.x.x` 是早期公开版本， API 仍可能在小版本中调整。

## 许可证

Copyright © 2026 xie-huaaaan。

源代码以 [Mozilla Public License 2.0](https://github.com/xie-huaaaan/stockchart/blob/main/LICENSE)（MPL-2.0）发布。你可以在闭源项目中使用 `stockchart`；如果修改了受 MPL 覆盖的源文件，这些文件需要继续以 MPL-2.0 提供。

内置腿部素材（`src/stockchart/assets/leg_*.svg` 与 `legs.json`）及其预制墨线与矢量绘图指令（`src/stockchart/_precomputed/leg_*.npz` / `leg_*.skp`）为 **All Rights Reserved（保留所有权利）**，不在 MPL-2.0 覆盖范围内，详见 [素材许可](https://github.com/xie-huaaaan/stockchart/blob/main/ASSETS-LICENSE)。素材可用于通过 `stockchart` 渲染图表，你生成的图表归你所有；素材本身除作为未修改的完整软件包的一部分外，不得单独提取或再分发。

</details>
