rsplotlib 0.1.9__tar.gz → 0.2.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/.github/workflows/ci.yml +2 -2
- rsplotlib-0.2.6/.github/workflows/rust.yml +61 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/Cargo.lock +58 -3
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/Cargo.toml +3 -2
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/PKG-INFO +2 -2
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/README.md +13 -10
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/README_zh.md +1 -1
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/build_wheel.sh +33 -0
- rsplotlib-0.2.6/ci/smoke_test.py +17 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/pyproject.toml +1 -2
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/__init__.py +1 -1
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/_font_resolver.py +1 -1
- rsplotlib-0.2.6/python/rsplotlib/colors.py +147 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/core/api.py +48 -15
- rsplotlib-0.2.6/python/rsplotlib/dates.py +85 -0
- rsplotlib-0.2.6/python/rsplotlib/pyplot.py +3016 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/utils/_rcparams.py +7 -5
- rsplotlib-0.2.6/src/core/colormap.rs +559 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/core/colors.rs +8 -0
- rsplotlib-0.2.6/src/core/elements.rs +232 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/core/marker.rs +22 -5
- rsplotlib-0.2.6/src/figure/axes.rs +3304 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axes_bounds.rs +82 -111
- rsplotlib-0.2.6/src/figure/axes_colorbar.rs +529 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axes_grid.rs +7 -2
- rsplotlib-0.2.6/src/figure/axes_legend.rs +580 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axes_mesh.rs +92 -2
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axes_render_elements.rs +902 -351
- rsplotlib-0.2.6/src/figure/axes_secondary.rs +388 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axes_title.rs +49 -34
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axis.rs +32 -0
- rsplotlib-0.2.6/src/figure/figure.rs +2137 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/mod.rs +2 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/lib.rs +84 -30
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/utils/font_stack.rs +89 -10
- rsplotlib-0.2.6/src/utils/mathtext.rs +1097 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/utils/mod.rs +1 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/utils/pyfuncs.rs +527 -66
- rsplotlib-0.1.9/ci/smoke_test.py +0 -74
- rsplotlib-0.1.9/python/rsplotlib/pyplot.py +0 -1194
- rsplotlib-0.1.9/src/core/colormap.rs +0 -119
- rsplotlib-0.1.9/src/core/elements.rs +0 -180
- rsplotlib-0.1.9/src/figure/axes.rs +0 -2085
- rsplotlib-0.1.9/src/figure/axes_legend.rs +0 -225
- rsplotlib-0.1.9/src/figure/figure.rs +0 -1176
- rsplotlib-0.1.9/text_utils +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/.github/workflows/release.yml +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/.gitignore +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/LICENSE +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/core/__init__.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/figure/__init__.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/figure/_defaults.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/gridspec.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/layout/__init__.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/layout/gridspec.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/pylab.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/ticker.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/ticks/__init__.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/ticks/ticker.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/utils/__init__.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/utils/_font_resolver.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/utils/style.py +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/rust-toolchain.toml +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/core/mod.rs +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/layout/gridspec.rs +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/layout/mod.rs +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/ticks/mod.rs +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/ticks/ticker.rs +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/utils/font_resolver.rs +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/utils/style.rs +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/tutorial.css +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/tutorial.html +0 -0
- {rsplotlib-0.1.9 → rsplotlib-0.2.6}/tutorial.js +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: Rust Checks
|
|
2
|
+
|
|
3
|
+
# dev 分支的快速 Rust 静态检查(fmt + clippy)。
|
|
4
|
+
# 完整的 maturin 构建 / wheel / 冒烟测试由 main 分支的 ci.yml 负责。
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches: [dev]
|
|
8
|
+
pull_request:
|
|
9
|
+
branches: [dev]
|
|
10
|
+
|
|
11
|
+
# 同一分支/PR 的新推送会取消仍在进行的旧运行,节省 CI 时间。
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
env:
|
|
20
|
+
CARGO_TERM_COLOR: always
|
|
21
|
+
RUST_BACKTRACE: 1
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
checks:
|
|
25
|
+
name: fmt + clippy (${{ matrix.os }})
|
|
26
|
+
runs-on: ${{ matrix.os }}
|
|
27
|
+
strategy:
|
|
28
|
+
fail-fast: false
|
|
29
|
+
# 三大平台都跑 clippy:src/lib.rs 中的字体注册逻辑按
|
|
30
|
+
# #[cfg(target_os = "macos" / "linux" / "windows")] 分支实现,
|
|
31
|
+
# 只有在对应平台上 clippy 才会真正检查到这些代码。
|
|
32
|
+
matrix:
|
|
33
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
34
|
+
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
|
|
38
|
+
# pyo3 的 build script 需要一个 Python 解释器来探测 ABI / 版本,
|
|
39
|
+
# 否则 cargo clippy / check 无法编译(找不到 Python 配置)。
|
|
40
|
+
- uses: actions/setup-python@v5
|
|
41
|
+
with:
|
|
42
|
+
python-version: "3.13"
|
|
43
|
+
|
|
44
|
+
- name: Install Rust toolchain
|
|
45
|
+
uses: dtolnay/rust-toolchain@stable
|
|
46
|
+
with:
|
|
47
|
+
components: clippy, rustfmt
|
|
48
|
+
|
|
49
|
+
# 按操作系统自动隔离缓存,rust-cache 默认已区分 runner OS。
|
|
50
|
+
- uses: Swatinem/rust-cache@v2
|
|
51
|
+
|
|
52
|
+
# 格式检查与平台无关,只需在一个平台上跑一次。
|
|
53
|
+
- name: Check formatting
|
|
54
|
+
if: matrix.os == 'ubuntu-latest'
|
|
55
|
+
run: cargo fmt --all -- --check
|
|
56
|
+
|
|
57
|
+
# clippy 走 cargo check 模式(不做最终链接),因此无需 extension-module,
|
|
58
|
+
# 也不会去链接 libpython。不加 --target:直接用各 runner 的原生架构
|
|
59
|
+
# (修复原先在 arm64 的 macos-latest 上交叉编译 x86_64-apple-darwin 的问题)。
|
|
60
|
+
- name: Run clippy
|
|
61
|
+
run: cargo clippy --all-targets -- -D warnings
|
|
@@ -81,6 +81,37 @@ dependencies = [
|
|
|
81
81
|
"cfg-if",
|
|
82
82
|
]
|
|
83
83
|
|
|
84
|
+
[[package]]
|
|
85
|
+
name = "crossbeam-deque"
|
|
86
|
+
version = "0.8.7"
|
|
87
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
88
|
+
checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb"
|
|
89
|
+
dependencies = [
|
|
90
|
+
"crossbeam-epoch",
|
|
91
|
+
"crossbeam-utils",
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
[[package]]
|
|
95
|
+
name = "crossbeam-epoch"
|
|
96
|
+
version = "0.9.20"
|
|
97
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
98
|
+
checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
|
|
99
|
+
dependencies = [
|
|
100
|
+
"crossbeam-utils",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
[[package]]
|
|
104
|
+
name = "crossbeam-utils"
|
|
105
|
+
version = "0.8.22"
|
|
106
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
107
|
+
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
|
|
108
|
+
|
|
109
|
+
[[package]]
|
|
110
|
+
name = "either"
|
|
111
|
+
version = "1.16.0"
|
|
112
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
113
|
+
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
|
|
114
|
+
|
|
84
115
|
[[package]]
|
|
85
116
|
name = "fdeflate"
|
|
86
117
|
version = "0.3.7"
|
|
@@ -149,6 +180,9 @@ name = "jpeg-decoder"
|
|
|
149
180
|
version = "0.3.2"
|
|
150
181
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
151
182
|
checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07"
|
|
183
|
+
dependencies = [
|
|
184
|
+
"rayon",
|
|
185
|
+
]
|
|
152
186
|
|
|
153
187
|
[[package]]
|
|
154
188
|
name = "jpeg-encoder"
|
|
@@ -361,10 +395,31 @@ dependencies = [
|
|
|
361
395
|
"proc-macro2",
|
|
362
396
|
]
|
|
363
397
|
|
|
398
|
+
[[package]]
|
|
399
|
+
name = "rayon"
|
|
400
|
+
version = "1.12.0"
|
|
401
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
402
|
+
checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
|
|
403
|
+
dependencies = [
|
|
404
|
+
"either",
|
|
405
|
+
"rayon-core",
|
|
406
|
+
]
|
|
407
|
+
|
|
408
|
+
[[package]]
|
|
409
|
+
name = "rayon-core"
|
|
410
|
+
version = "1.13.0"
|
|
411
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
412
|
+
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
|
413
|
+
dependencies = [
|
|
414
|
+
"crossbeam-deque",
|
|
415
|
+
"crossbeam-utils",
|
|
416
|
+
]
|
|
417
|
+
|
|
364
418
|
[[package]]
|
|
365
419
|
name = "rsplotlib"
|
|
366
|
-
version = "0.
|
|
420
|
+
version = "0.2.6"
|
|
367
421
|
dependencies = [
|
|
422
|
+
"jpeg-decoder",
|
|
368
423
|
"jpeg-encoder",
|
|
369
424
|
"owned_ttf_parser",
|
|
370
425
|
"plotters",
|
|
@@ -374,9 +429,9 @@ dependencies = [
|
|
|
374
429
|
|
|
375
430
|
[[package]]
|
|
376
431
|
name = "rustversion"
|
|
377
|
-
version = "1.0.
|
|
432
|
+
version = "1.0.23"
|
|
378
433
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
379
|
-
checksum = "
|
|
434
|
+
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
|
380
435
|
|
|
381
436
|
[[package]]
|
|
382
437
|
name = "simd-adler32"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rsplotlib"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.6"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
|
|
@@ -12,8 +12,9 @@ crate-type = ["cdylib"]
|
|
|
12
12
|
pyo3 = "0.29.0"
|
|
13
13
|
plotters = { version = "0.3.7", default-features = false, features = ["svg_backend", "bitmap_backend", "bitmap_encoder", "ab_glyph", "line_series", "point_series"] }
|
|
14
14
|
png = "0.18.1"
|
|
15
|
-
owned_ttf_parser = "0.25"
|
|
15
|
+
owned_ttf_parser = "0.25.1"
|
|
16
16
|
jpeg-encoder = "0.7.0"
|
|
17
|
+
jpeg-decoder = "0.3.2"
|
|
17
18
|
|
|
18
19
|
[profile.release]
|
|
19
20
|
lto = "fat"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rsplotlib
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
@@ -28,7 +28,7 @@ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
|
28
28
|
> 一个由 Rust 强力驱动的高性能 Python 绘图库,提供 Matplotlib 兼容 API
|
|
29
29
|
|
|
30
30
|
[](https://www.python.org/)
|
|
31
|
-
[](https://www.rust-lang.org/)
|
|
32
32
|
[](https://opensource.org/licenses/MIT)
|
|
33
33
|
[](https://pyo3.rs/)
|
|
34
34
|
[](https://github.com/plotters-rs/plotters)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> A high-performance Python plotting library powered by Rust, with a Matplotlib-compatible API
|
|
4
4
|
|
|
5
5
|
[](https://www.python.org/)
|
|
6
|
-
[](https://www.rust-lang.org/)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
[](https://pyo3.rs/)
|
|
9
9
|
[](https://github.com/plotters-rs/plotters)
|
|
@@ -503,22 +503,25 @@ ax.axline(xy1, xy2, color=None, linestyle=None, linewidth=None)
|
|
|
503
503
|
Add arrowed text annotations.
|
|
504
504
|
|
|
505
505
|
```python
|
|
506
|
-
ax.annotate(text, xy, xytext=None, fontsize=12.0, color='black',
|
|
507
|
-
arrowprops=None, arrowstyle=None, arrowsize=1.0)
|
|
506
|
+
ax.annotate(text, xy, xytext=None, fontsize=12.0, color='black', arrowprops=None)
|
|
508
507
|
```
|
|
509
508
|
|
|
510
509
|
**Parameters:**
|
|
511
510
|
|
|
512
511
|
- `text` (str): Annotation text
|
|
513
512
|
- `xy` (tuple): Coordinates of the point being annotated `(x, y)`
|
|
514
|
-
- `xytext` (tuple, optional): Position to place the text.
|
|
513
|
+
- `xytext` (tuple, optional): Position to place the text. Defaults to `xy`
|
|
515
514
|
- `fontsize` (float): Font size, default 12.0
|
|
516
|
-
- `color` (str): Text
|
|
517
|
-
- `arrowprops` (dict, optional): Arrow properties dictionary.
|
|
518
|
-
|
|
519
|
-
- `
|
|
520
|
-
- `arrowstyle`
|
|
521
|
-
|
|
515
|
+
- `color` (str): Text color, default `'black'`
|
|
516
|
+
- `arrowprops` (dict, optional): Arrow properties dictionary. `None` (default)
|
|
517
|
+
draws no arrow; when provided (even an empty dict) an arrow is drawn from the
|
|
518
|
+
text-box edge to `xy`. Two modes, matching matplotlib:
|
|
519
|
+
- **Simple** (no `arrowstyle` key): `width`, `headwidth`, `headlength` (points)
|
|
520
|
+
and `shrink` (fraction) produce a filled arrow.
|
|
521
|
+
- **Fancy** (`arrowstyle` given): `arrowstyle` (`'-'`, `'->'`, `'<-'`, `'<->'`,
|
|
522
|
+
`'-|>'`, `'<|-'`, `'<|-|>'`, `'simple'`, `'fancy'`, `'wedge'`),
|
|
523
|
+
`mutation_scale` (head size, defaults to font size), `shrinkA` / `shrinkB`
|
|
524
|
+
(points), `linewidth` / `lw`, `color` / `ec`, `facecolor` / `fc`, `alpha`.
|
|
522
525
|
|
|
523
526
|
### hlines / vlines (Rust-level batch implementation)
|
|
524
527
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> 一个由 Rust 强力驱动的高性能 Python 绘图库,提供 Matplotlib 兼容 API
|
|
4
4
|
|
|
5
5
|
[](https://www.python.org/)
|
|
6
|
-
[](https://www.rust-lang.org/)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
[](https://pyo3.rs/)
|
|
9
9
|
[](https://github.com/plotters-rs/plotters)
|
|
@@ -31,6 +31,23 @@ if ! command -v cargo >/dev/null 2>&1; then
|
|
|
31
31
|
exit 1
|
|
32
32
|
fi
|
|
33
33
|
|
|
34
|
+
# ========== 依据 rust-toolchain.toml 准备工具链与 clippy 组件 ==========
|
|
35
|
+
# 项目用 rust-toolchain.toml 固定了 Rust channel;rustup 会自动据此选择工具链,
|
|
36
|
+
# 但构建前的 clippy 检查依赖 clippy 组件,全新环境未必已安装,这里显式补齐。
|
|
37
|
+
if [[ -f rust-toolchain.toml ]]; then
|
|
38
|
+
RUST_CHANNEL=$(grep -m1 '^channel *= *' rust-toolchain.toml | sed -E 's/.*"([^"]+)".*/\1/')
|
|
39
|
+
if [[ -n "${RUST_CHANNEL:-}" ]]; then
|
|
40
|
+
echo "Pinned Rust toolchain (rust-toolchain.toml): $RUST_CHANNEL"
|
|
41
|
+
if command -v rustup >/dev/null 2>&1; then
|
|
42
|
+
# 幂等:已安装则为空操作;离线且缺失时失败被忽略,交由后续 clippy 步骤明确报错。
|
|
43
|
+
rustup toolchain install "$RUST_CHANNEL" >/dev/null 2>&1 || true
|
|
44
|
+
rustup component add clippy --toolchain "$RUST_CHANNEL" >/dev/null 2>&1 || true
|
|
45
|
+
else
|
|
46
|
+
echo " -> rustup not found; using the cargo/clippy already on PATH." >&2
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
|
|
34
51
|
RELEASE=true
|
|
35
52
|
OUT_DIR="wheelhouse"
|
|
36
53
|
|
|
@@ -86,6 +103,22 @@ else
|
|
|
86
103
|
exit 1
|
|
87
104
|
fi
|
|
88
105
|
|
|
106
|
+
# ========== 构建前的 Fmt 静态检查(-check:任何错误都当作错误) ==========
|
|
107
|
+
echo "Running fmt checks (cargo fmt --all -- --check) ..."
|
|
108
|
+
if ! cargo fmt --all -- --check; then
|
|
109
|
+
echo "Error: fmt checks failed. Fix the warnings above before building." >&2
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
echo " -> fmt checks passed."
|
|
113
|
+
|
|
114
|
+
# ========== 构建前的 Clippy 静态检查(-D warnings:任何告警都当作错误) ==========
|
|
115
|
+
echo "Running clippy checks (cargo clippy --all-targets -- -D warnings) ..."
|
|
116
|
+
if ! cargo clippy --all-targets -- -D warnings; then
|
|
117
|
+
echo "Error: clippy checks failed. Fix the warnings above before building." >&2
|
|
118
|
+
exit 1
|
|
119
|
+
fi
|
|
120
|
+
echo " -> clippy checks passed."
|
|
121
|
+
|
|
89
122
|
BUILD_ARGS=()
|
|
90
123
|
if $RELEASE; then BUILD_ARGS+=(--release); else BUILD_ARGS+=(--debug); fi
|
|
91
124
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import rsplotlib.pyplot as plt
|
|
2
|
+
|
|
3
|
+
fig, ax = plt.subplots()
|
|
4
|
+
|
|
5
|
+
ax.plot([1, 2, 3], label=r'$\sqrt{x^2}$')
|
|
6
|
+
ax.legend()
|
|
7
|
+
ax.plot([0, 2, 3], label=r'$\sqrt{x^2}$')
|
|
8
|
+
ax.set_xlabel(r'$\Delta_i^j$', fontsize=20)
|
|
9
|
+
ax.set_ylabel(r'$\Delta_{i+1}^j$', fontsize=20)
|
|
10
|
+
ax.set_title(r'$\Delta_i^j \hspace{0.4} \mathrm{versus} \hspace{0.4} '
|
|
11
|
+
r'\Delta_{i+1}^j$', fontsize=20)
|
|
12
|
+
|
|
13
|
+
tex = r'$\mathcal{R}\prod_{i=\alpha_{i+1}}^\infty a_i\sin(2 \pi f x_i)$'
|
|
14
|
+
ax.text(1, 1.6, tex, fontsize=20, va='bottom')
|
|
15
|
+
ax.text(0.5, 2, r'$\sqrt{x^2}$', fontsize=20, va='bottom')
|
|
16
|
+
fig.tight_layout()
|
|
17
|
+
plt.savefig("ci/test_barh.png")
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rsplotlib"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.6"
|
|
8
8
|
description = "A high-performance matplotlib-compatible plotting library powered by Rust"
|
|
9
9
|
readme = "README_zh.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -29,7 +29,6 @@ classifiers = [
|
|
|
29
29
|
"Topic :: Scientific/Engineering :: Visualization",
|
|
30
30
|
]
|
|
31
31
|
keywords = ["plotting", "matplotlib", "visualization", "rust", "scientific"]
|
|
32
|
-
dependencies = []
|
|
33
32
|
|
|
34
33
|
[tool.maturin]
|
|
35
34
|
python-source = "python"
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""rsplotlib.colors - Matplotlib colors 兼容接口 (子集)。
|
|
2
|
+
|
|
3
|
+
提供归一化类 Normalize / LogNorm,用于将数据映射到 [0, 1] 供 colormap 上色。
|
|
4
|
+
当前 imshow/pcolormesh 等接受 norm 参数,Normalize/LogNorm 在此处提供兼容 API。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import math
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Normalize:
|
|
11
|
+
"""将数据线性归一化到 [0, 1],兼容 matplotlib.colors.Normalize。
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
vmin, vmax: 归一化值域端点,缺省时在首次调用时按数据自动推断。
|
|
15
|
+
clip: 是否将越界值裁剪到 [0, 1]。
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# 归一化类型标记:供 pyplot 把归一化方式(线性/对数)下沉给 Rust 上色与颜色条。
|
|
19
|
+
_norm_kind = 'linear'
|
|
20
|
+
|
|
21
|
+
def __init__(self, vmin=None, vmax=None, clip=False):
|
|
22
|
+
self.vmin = vmin
|
|
23
|
+
self.vmax = vmax
|
|
24
|
+
self.clip = clip
|
|
25
|
+
|
|
26
|
+
def autoscale_None(self, values):
|
|
27
|
+
"""当 vmin/vmax 未设置时,根据数据填充。"""
|
|
28
|
+
seq = _flatten(values)
|
|
29
|
+
if seq:
|
|
30
|
+
if self.vmin is None:
|
|
31
|
+
self.vmin = min(seq)
|
|
32
|
+
if self.vmax is None:
|
|
33
|
+
self.vmax = max(seq)
|
|
34
|
+
|
|
35
|
+
def _normalize_scalar(self, value):
|
|
36
|
+
vmin = 0.0 if self.vmin is None else float(self.vmin)
|
|
37
|
+
vmax = 1.0 if self.vmax is None else float(self.vmax)
|
|
38
|
+
if vmax == vmin:
|
|
39
|
+
return 0.0
|
|
40
|
+
t = (float(value) - vmin) / (vmax - vmin)
|
|
41
|
+
if self.clip:
|
|
42
|
+
t = max(0.0, min(1.0, t))
|
|
43
|
+
return t
|
|
44
|
+
|
|
45
|
+
def __call__(self, value, clip=None):
|
|
46
|
+
if clip is not None:
|
|
47
|
+
saved = self.clip
|
|
48
|
+
self.clip = clip
|
|
49
|
+
try:
|
|
50
|
+
return self._apply(value)
|
|
51
|
+
finally:
|
|
52
|
+
self.clip = saved
|
|
53
|
+
return self._apply(value)
|
|
54
|
+
|
|
55
|
+
def _apply(self, value):
|
|
56
|
+
if not _is_sequence(value):
|
|
57
|
+
return self._normalize_scalar(value)
|
|
58
|
+
# 循环不变量外提:vmin/vmax 的 float 转换与除数只算一次,再逐元素映射。
|
|
59
|
+
seq = _flatten(value)
|
|
60
|
+
vmin = 0.0 if self.vmin is None else float(self.vmin)
|
|
61
|
+
vmax = 1.0 if self.vmax is None else float(self.vmax)
|
|
62
|
+
if vmax == vmin:
|
|
63
|
+
return [0.0] * len(seq)
|
|
64
|
+
inv = 1.0 / (vmax - vmin)
|
|
65
|
+
if self.clip:
|
|
66
|
+
return [max(0.0, min(1.0, (v - vmin) * inv)) for v in seq]
|
|
67
|
+
return [(v - vmin) * inv for v in seq]
|
|
68
|
+
|
|
69
|
+
def __repr__(self):
|
|
70
|
+
return f'Normalize(vmin={self.vmin}, vmax={self.vmax})'
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class LogNorm(Normalize):
|
|
74
|
+
"""对数归一化,兼容 matplotlib.colors.LogNorm。
|
|
75
|
+
|
|
76
|
+
将数据按对数刻度映射到 [0, 1];非正值被视为越界。
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
_norm_kind = 'log'
|
|
80
|
+
|
|
81
|
+
def autoscale_None(self, values):
|
|
82
|
+
"""当 vmin/vmax 未设置时,用数据的最小正值 / 最大值填充(对数刻度要求正值)。"""
|
|
83
|
+
seq = [v for v in _flatten(values) if v > 0]
|
|
84
|
+
if seq:
|
|
85
|
+
if self.vmin is None:
|
|
86
|
+
self.vmin = min(seq)
|
|
87
|
+
if self.vmax is None:
|
|
88
|
+
self.vmax = max(seq)
|
|
89
|
+
|
|
90
|
+
def _normalize_scalar(self, value):
|
|
91
|
+
vmin = self.vmin
|
|
92
|
+
vmax = self.vmax
|
|
93
|
+
if vmin is None or vmax is None or vmin <= 0 or vmax <= 0:
|
|
94
|
+
return 0.0
|
|
95
|
+
v = float(value)
|
|
96
|
+
if v <= 0:
|
|
97
|
+
return 0.0
|
|
98
|
+
t = (math.log(v) - math.log(vmin)) / (math.log(vmax) - math.log(vmin))
|
|
99
|
+
if self.clip:
|
|
100
|
+
t = max(0.0, min(1.0, t))
|
|
101
|
+
return t
|
|
102
|
+
|
|
103
|
+
def _apply(self, value):
|
|
104
|
+
if not _is_sequence(value):
|
|
105
|
+
return self._normalize_scalar(value)
|
|
106
|
+
# 循环不变量外提:log(vmin) 与对数跨度只算一次,避免逐元素重复求 log。
|
|
107
|
+
seq = _flatten(value)
|
|
108
|
+
vmin = self.vmin
|
|
109
|
+
vmax = self.vmax
|
|
110
|
+
if vmin is None or vmax is None or vmin <= 0 or vmax <= 0:
|
|
111
|
+
return [0.0] * len(seq)
|
|
112
|
+
log_vmin = math.log(vmin)
|
|
113
|
+
span = math.log(vmax) - log_vmin
|
|
114
|
+
if span == 0.0:
|
|
115
|
+
return [0.0] * len(seq)
|
|
116
|
+
inv = 1.0 / span
|
|
117
|
+
if self.clip:
|
|
118
|
+
return [max(0.0, min(1.0, (math.log(v) - log_vmin) * inv)) if v > 0 else 0.0
|
|
119
|
+
for v in seq]
|
|
120
|
+
return [(math.log(v) - log_vmin) * inv if v > 0 else 0.0 for v in seq]
|
|
121
|
+
|
|
122
|
+
def __repr__(self):
|
|
123
|
+
return f'LogNorm(vmin={self.vmin}, vmax={self.vmax})'
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _is_sequence(obj):
|
|
127
|
+
if obj is None or isinstance(obj, str):
|
|
128
|
+
return False
|
|
129
|
+
return hasattr(obj, 'tolist') or hasattr(obj, '__iter__')
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _flatten(values):
|
|
133
|
+
"""递归展开嵌套序列 / rsnumpy 数组为一维浮点列表。"""
|
|
134
|
+
if values is None:
|
|
135
|
+
return []
|
|
136
|
+
if hasattr(values, 'tolist'):
|
|
137
|
+
values = values.tolist()
|
|
138
|
+
out = []
|
|
139
|
+
if isinstance(values, (list, tuple)):
|
|
140
|
+
for item in values:
|
|
141
|
+
if isinstance(item, (list, tuple)):
|
|
142
|
+
out.extend(_flatten(item))
|
|
143
|
+
else:
|
|
144
|
+
out.append(float(item))
|
|
145
|
+
else:
|
|
146
|
+
out.append(float(values))
|
|
147
|
+
return out
|
|
@@ -65,8 +65,9 @@ def plot(x, y, label=None, color=None, linestyle=None, marker=None, linewidth=No
|
|
|
65
65
|
linestyle = ls
|
|
66
66
|
return _rsplotlib.plot(
|
|
67
67
|
_to_list(x), _to_list(y),
|
|
68
|
-
label, color, linestyle, marker,
|
|
69
|
-
lw, c, ls, markersize,
|
|
68
|
+
label=label, color=color, linestyle=linestyle, marker=marker,
|
|
69
|
+
linewidth=linewidth, lw=lw, c=c, ls=ls, markersize=markersize,
|
|
70
|
+
markeredgewidth=markeredgewidth, solid_capstyle=solid_capstyle,
|
|
70
71
|
)
|
|
71
72
|
|
|
72
73
|
|
|
@@ -115,18 +116,45 @@ def barh(y, width, height=0.8, color=None, label=None):
|
|
|
115
116
|
return _rsplotlib.barh(_to_list(y), _to_list(width), height, color, label)
|
|
116
117
|
|
|
117
118
|
|
|
118
|
-
def hist(x, bins=10,
|
|
119
|
+
def hist(x, bins=10, range=None, density=False, weights=None,
|
|
120
|
+
cumulative=False, bottom=None, histtype='bar', align='mid',
|
|
121
|
+
orientation='vertical', rwidth=None, log=False, color=None,
|
|
122
|
+
facecolor=None, label=None, stacked=False, alpha=1.0):
|
|
119
123
|
"""绘制直方图
|
|
120
124
|
|
|
121
125
|
Args:
|
|
122
|
-
x: 数据
|
|
123
|
-
bins:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
x: 数据 (一维数组, 或多组数据组成的列表)
|
|
127
|
+
bins: 分箱数量 (默认 10) 或箱边界列表
|
|
128
|
+
range: 值域范围 (lo, hi)
|
|
129
|
+
density: 是否归一化为概率密度 (默认 False)
|
|
130
|
+
weights: 每个数据点的权重
|
|
131
|
+
cumulative: 是否绘制累积分布 (True/False/-1)
|
|
132
|
+
bottom: 每个柱子的起始基线
|
|
133
|
+
histtype: 'bar' | 'barstacked' | 'step' | 'stepfilled'
|
|
134
|
+
align: 'left' | 'mid' | 'right'
|
|
135
|
+
orientation: 'vertical' | 'horizontal'
|
|
136
|
+
rwidth: 柱子相对分箱宽度比例 (0~1)
|
|
137
|
+
log: 计数轴是否对数刻度
|
|
138
|
+
color / facecolor: 颜色或颜色列表
|
|
139
|
+
label: 图例标签
|
|
140
|
+
stacked: 是否堆叠
|
|
141
|
+
alpha: 透明度 (默认 1.0)
|
|
142
|
+
"""
|
|
143
|
+
if cumulative is True:
|
|
144
|
+
cum = 1
|
|
145
|
+
elif cumulative is False or cumulative is None:
|
|
146
|
+
cum = 0
|
|
147
|
+
else:
|
|
148
|
+
cum = int(cumulative)
|
|
149
|
+
weights_arg = _to_list_recursive(weights) if weights is not None else None
|
|
150
|
+
range_arg = tuple(range) if range is not None else None
|
|
151
|
+
return _rsplotlib.hist(
|
|
152
|
+
_to_list_recursive(x), bins=bins, range=range_arg, density=density,
|
|
153
|
+
weights=weights_arg, cumulative=cum, bottom=bottom, histtype=histtype,
|
|
154
|
+
align=align, orientation=orientation, rwidth=rwidth, log=log,
|
|
155
|
+
color=color, facecolor=facecolor, label=label, stacked=stacked,
|
|
156
|
+
alpha=alpha,
|
|
157
|
+
)
|
|
130
158
|
|
|
131
159
|
|
|
132
160
|
def pie(x, labels=None, colors=None, autopct=False):
|
|
@@ -217,15 +245,20 @@ def step(x, y, where_='pre', label=None, color=None, linestyle='-', linewidth=1.
|
|
|
217
245
|
return _rsplotlib.step(_to_list(x), _to_list(y), where_, label, color, linestyle, linewidth)
|
|
218
246
|
|
|
219
247
|
|
|
220
|
-
def imshow(x, cmap='gray', aspect='auto'
|
|
248
|
+
def imshow(x, cmap='gray', aspect='auto', vmin=None, vmax=None,
|
|
249
|
+
alpha=None, origin=None):
|
|
221
250
|
"""显示图像
|
|
222
251
|
|
|
223
252
|
Args:
|
|
224
|
-
x: 2D
|
|
225
|
-
cmap: 色图 (默认: 'gray', 可选: 'hot', 'cool')
|
|
253
|
+
x: 2D 标量数组 (经 cmap 上色) 或 3D RGB(A) 数组 (H, W, 3/4)
|
|
254
|
+
cmap: 色图 (默认: 'gray', 可选: 'hot', 'cool'),仅对 2D 数据生效
|
|
226
255
|
aspect: 纵横比 (默认: 'auto', 可选: 'equal')
|
|
256
|
+
vmin, vmax: 2D 数据的颜色映射值域 (缺省取数据 min/max)
|
|
257
|
+
alpha: 图像整体透明度 (0.0-1.0)
|
|
258
|
+
origin: 'upper' (默认) 或 'lower'
|
|
227
259
|
"""
|
|
228
|
-
return _rsplotlib.imshow(_to_list_recursive(x), cmap, aspect
|
|
260
|
+
return _rsplotlib.imshow(_to_list_recursive(x), cmap, aspect,
|
|
261
|
+
vmin, vmax, alpha, origin)
|
|
229
262
|
|
|
230
263
|
|
|
231
264
|
def violinplot(dataset, positions=None, widths=0.5, showmeans=False, showmedians=True):
|