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.
Files changed (73) hide show
  1. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/.github/workflows/ci.yml +2 -2
  2. rsplotlib-0.2.6/.github/workflows/rust.yml +61 -0
  3. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/Cargo.lock +58 -3
  4. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/Cargo.toml +3 -2
  5. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/PKG-INFO +2 -2
  6. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/README.md +13 -10
  7. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/README_zh.md +1 -1
  8. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/build_wheel.sh +33 -0
  9. rsplotlib-0.2.6/ci/smoke_test.py +17 -0
  10. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/pyproject.toml +1 -2
  11. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/__init__.py +1 -1
  12. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/_font_resolver.py +1 -1
  13. rsplotlib-0.2.6/python/rsplotlib/colors.py +147 -0
  14. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/core/api.py +48 -15
  15. rsplotlib-0.2.6/python/rsplotlib/dates.py +85 -0
  16. rsplotlib-0.2.6/python/rsplotlib/pyplot.py +3016 -0
  17. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/utils/_rcparams.py +7 -5
  18. rsplotlib-0.2.6/src/core/colormap.rs +559 -0
  19. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/core/colors.rs +8 -0
  20. rsplotlib-0.2.6/src/core/elements.rs +232 -0
  21. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/core/marker.rs +22 -5
  22. rsplotlib-0.2.6/src/figure/axes.rs +3304 -0
  23. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axes_bounds.rs +82 -111
  24. rsplotlib-0.2.6/src/figure/axes_colorbar.rs +529 -0
  25. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axes_grid.rs +7 -2
  26. rsplotlib-0.2.6/src/figure/axes_legend.rs +580 -0
  27. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axes_mesh.rs +92 -2
  28. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axes_render_elements.rs +902 -351
  29. rsplotlib-0.2.6/src/figure/axes_secondary.rs +388 -0
  30. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axes_title.rs +49 -34
  31. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/axis.rs +32 -0
  32. rsplotlib-0.2.6/src/figure/figure.rs +2137 -0
  33. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/figure/mod.rs +2 -0
  34. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/lib.rs +84 -30
  35. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/utils/font_stack.rs +89 -10
  36. rsplotlib-0.2.6/src/utils/mathtext.rs +1097 -0
  37. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/utils/mod.rs +1 -0
  38. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/utils/pyfuncs.rs +527 -66
  39. rsplotlib-0.1.9/ci/smoke_test.py +0 -74
  40. rsplotlib-0.1.9/python/rsplotlib/pyplot.py +0 -1194
  41. rsplotlib-0.1.9/src/core/colormap.rs +0 -119
  42. rsplotlib-0.1.9/src/core/elements.rs +0 -180
  43. rsplotlib-0.1.9/src/figure/axes.rs +0 -2085
  44. rsplotlib-0.1.9/src/figure/axes_legend.rs +0 -225
  45. rsplotlib-0.1.9/src/figure/figure.rs +0 -1176
  46. rsplotlib-0.1.9/text_utils +0 -0
  47. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/.github/workflows/release.yml +0 -0
  48. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/.gitignore +0 -0
  49. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/LICENSE +0 -0
  50. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/core/__init__.py +0 -0
  51. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/figure/__init__.py +0 -0
  52. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/figure/_defaults.py +0 -0
  53. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/gridspec.py +0 -0
  54. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/layout/__init__.py +0 -0
  55. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/layout/gridspec.py +0 -0
  56. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/pylab.py +0 -0
  57. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/ticker.py +0 -0
  58. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/ticks/__init__.py +0 -0
  59. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/ticks/ticker.py +0 -0
  60. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/utils/__init__.py +0 -0
  61. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/utils/_font_resolver.py +0 -0
  62. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/python/rsplotlib/utils/style.py +0 -0
  63. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/rust-toolchain.toml +0 -0
  64. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/core/mod.rs +0 -0
  65. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/layout/gridspec.rs +0 -0
  66. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/layout/mod.rs +0 -0
  67. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/ticks/mod.rs +0 -0
  68. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/ticks/ticker.rs +0 -0
  69. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/utils/font_resolver.rs +0 -0
  70. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/src/utils/style.rs +0 -0
  71. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/tutorial.css +0 -0
  72. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/tutorial.html +0 -0
  73. {rsplotlib-0.1.9 → rsplotlib-0.2.6}/tutorial.js +0 -0
@@ -2,9 +2,9 @@ name: CI
2
2
 
3
3
  on:
4
4
  push:
5
- branches: [main, master]
5
+ branches: [main]
6
6
  pull_request:
7
- branches: [main, master]
7
+ branches: [main]
8
8
 
9
9
  # 同一分支/PR 的新推送会取消仍在进行的旧运行,节省 CI 时间。
10
10
  concurrency:
@@ -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.1.9"
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.22"
432
+ version = "1.0.23"
378
433
  source = "registry+https://github.com/rust-lang/crates.io-index"
379
- checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
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.1.9"
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.1.9
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
  [![Python](https://img.shields.io/badge/Python-3.10%2B-blue)](https://www.python.org/)
31
- [![Rust](https://img.shields.io/badge/Rust-2021-orange)](https://www.rust-lang.org/)
31
+ [![Rust](https://img.shields.io/badge/Rust-2024-orange)](https://www.rust-lang.org/)
32
32
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
33
33
  [![PyO3](https://img.shields.io/badge/PyO3-0.29-2c2d72)](https://pyo3.rs/)
34
34
  [![plotters](https://img.shields.io/badge/plotters-0.3-7d5cff)](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
  [![Python](https://img.shields.io/badge/Python-3.10%2B-blue)](https://www.python.org/)
6
- [![Rust](https://img.shields.io/badge/Rust-2021-orange)](https://www.rust-lang.org/)
6
+ [![Rust](https://img.shields.io/badge/Rust-2024-orange)](https://www.rust-lang.org/)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
  [![PyO3](https://img.shields.io/badge/PyO3-0.29-2c2d72)](https://pyo3.rs/)
9
9
  [![plotters](https://img.shields.io/badge/plotters-0.3-7d5cff)](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. If provided, an arrow is automatically drawn from this position to `xy`
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 and arrow color, default `'black'`
517
- - `arrowprops` (dict, optional): Arrow properties dictionary. Supports:
518
- - `arrowstyle`: Arrow style (e.g., `'->'`, `'-|>'`)
519
- - `arrowsize`: Arrow relative size
520
- - `arrowstyle` (str, optional): Arrow style independent of `arrowprops`
521
- - `arrowsize` (float, optional): Arrow size independent of `arrowprops`
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
  [![Python](https://img.shields.io/badge/Python-3.10%2B-blue)](https://www.python.org/)
6
- [![Rust](https://img.shields.io/badge/Rust-2021-orange)](https://www.rust-lang.org/)
6
+ [![Rust](https://img.shields.io/badge/Rust-2024-orange)](https://www.rust-lang.org/)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
  [![PyO3](https://img.shields.io/badge/PyO3-0.29-2c2d72)](https://pyo3.rs/)
9
9
  [![plotters](https://img.shields.io/badge/plotters-0.3-7d5cff)](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.1.9"
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"
@@ -13,7 +13,7 @@ MaxNLocator = ticker.MaxNLocator
13
13
  MultipleLocator = ticker.MultipleLocator
14
14
  AutoMinorLocator = ticker.AutoMinorLocator
15
15
 
16
- __version__ = "0.1.9"
16
+ __version__ = "0.2.6"
17
17
  # 从内部 Rust 模块导出字体注册函数
18
18
 
19
19
  __all__ = list(_api_all) + [
@@ -164,4 +164,4 @@ def apply_rcparams_font() -> Optional[str]:
164
164
  return path
165
165
  except Exception:
166
166
  continue
167
- return None
167
+ return None
@@ -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, linewidth,
69
- lw, c, ls, markersize, markeredgewidth, solid_capstyle,
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, density=False, label=None, alpha=0.7, color=None):
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: 区间数 (默认: 10)
124
- density: 是否归一化 (默认: False)
125
- label: 图例标签 (默认: None)
126
- alpha: 透明度 (默认: 0.7)
127
- color: 颜色 (默认: None)
128
- """
129
- return _rsplotlib.hist(_to_list_recursive(x), bins, density, label, alpha, color)
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):