plotform 0.1.0__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 (48) hide show
  1. plotform-0.1.0/CHANGELOG.md +24 -0
  2. plotform-0.1.0/LICENSE +24 -0
  3. plotform-0.1.0/MANIFEST.in +9 -0
  4. plotform-0.1.0/PKG-INFO +267 -0
  5. plotform-0.1.0/README.md +232 -0
  6. plotform-0.1.0/docs/RELEASING.md +125 -0
  7. plotform-0.1.0/docs/images/capsules.png +0 -0
  8. plotform-0.1.0/docs/images/contact-sheet.png +0 -0
  9. plotform-0.1.0/docs/images/overview.png +0 -0
  10. plotform-0.1.0/examples/geometry_check.py +70 -0
  11. plotform-0.1.0/examples/native_matplotlib.py +31 -0
  12. plotform-0.1.0/examples/showcase.py +698 -0
  13. plotform-0.1.0/pyproject.toml +68 -0
  14. plotform-0.1.0/setup.cfg +4 -0
  15. plotform-0.1.0/src/plotform/__init__.py +53 -0
  16. plotform-0.1.0/src/plotform/api.py +47 -0
  17. plotform-0.1.0/src/plotform/assets/fonts/PlotformMono-OFL.txt +93 -0
  18. plotform-0.1.0/src/plotform/assets/fonts/PlotformMono-Regular.ttf +0 -0
  19. plotform-0.1.0/src/plotform/assets/fonts/PlotformMono-TRADEMARKS.md +17 -0
  20. plotform-0.1.0/src/plotform/assets/fonts/PlotformSans-OFL.txt +93 -0
  21. plotform-0.1.0/src/plotform/assets/fonts/PlotformSans-Rounded.ttf +0 -0
  22. plotform-0.1.0/src/plotform/assets/fonts/PlotformSans-TRADEMARKS.md +17 -0
  23. plotform-0.1.0/src/plotform/assets/fonts/README.md +30 -0
  24. plotform-0.1.0/src/plotform/colorbar_utils.py +75 -0
  25. plotform-0.1.0/src/plotform/fonts.py +56 -0
  26. plotform-0.1.0/src/plotform/legend_utils.py +199 -0
  27. plotform-0.1.0/src/plotform/palette.py +103 -0
  28. plotform-0.1.0/src/plotform/rounded.py +519 -0
  29. plotform-0.1.0/src/plotform/theme.py +168 -0
  30. plotform-0.1.0/src/plotform/three_d.py +40 -0
  31. plotform-0.1.0/src/plotform.egg-info/PKG-INFO +267 -0
  32. plotform-0.1.0/src/plotform.egg-info/SOURCES.txt +46 -0
  33. plotform-0.1.0/src/plotform.egg-info/dependency_links.txt +1 -0
  34. plotform-0.1.0/src/plotform.egg-info/requires.txt +12 -0
  35. plotform-0.1.0/src/plotform.egg-info/top_level.txt +1 -0
  36. plotform-0.1.0/tests/test_api.py +44 -0
  37. plotform-0.1.0/tests/test_capsule_reference.py +57 -0
  38. plotform-0.1.0/tests/test_colorbar.py +56 -0
  39. plotform-0.1.0/tests/test_fonts.py +24 -0
  40. plotform-0.1.0/tests/test_legend.py +147 -0
  41. plotform-0.1.0/tests/test_palette.py +43 -0
  42. plotform-0.1.0/tests/test_public_api.py +44 -0
  43. plotform-0.1.0/tests/test_rounded.py +179 -0
  44. plotform-0.1.0/tests/test_theme.py +79 -0
  45. plotform-0.1.0/tools/check_sdist.py +46 -0
  46. plotform-0.1.0/tools/check_version.py +29 -0
  47. plotform-0.1.0/tools/make_doc_images.py +84 -0
  48. plotform-0.1.0/tools/prepare_fonts.py +98 -0
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ All notable changes to Plotform are documented here. The project follows
4
+ [Semantic Versioning](https://semver.org/) while it remains pre-1.0.
5
+
6
+ ## 0.1.0 — Initial public release
7
+
8
+ ### Added
9
+
10
+ - Minimalist global and scoped Matplotlib themes.
11
+ - Vivid categorical palettes and three continuous colormaps.
12
+ - Native-style `legend`, `bar`, `barh`, `colorbar`, and `annotate` helpers.
13
+ - Exact capsule annotations, circular bar caps, and separate squircle geometry.
14
+ - Solid and soft pill legends with adaptive text contrast.
15
+ - Vertical capsule colorbars with external ticks.
16
+ - Minimal 3D styling and preserved face shading.
17
+ - Bundled Plotform Sans and Plotform Mono fonts with licenses and attribution.
18
+ - A 27-chart comparison gallery, geometry reference board, and native Matplotlib example.
19
+ - CI, package build checks, and trusted-publishing release workflows.
20
+
21
+ ### Removed
22
+
23
+ - The optional compatibility layer. Use Plotform when themed output is wanted,
24
+ or ordinary Matplotlib when a script must run without the package.
plotform-0.1.0/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Econ01
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ Bundled font software is licensed separately under the SIL Open Font License.
24
+ See src/plotform/assets/fonts/ for the font licenses and attributions.
@@ -0,0 +1,9 @@
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ include pyproject.toml
5
+ recursive-include docs *.md
6
+ recursive-include docs/images *.png
7
+ recursive-include examples *.py
8
+ recursive-include tools *.py
9
+ recursive-include src/plotform/assets/fonts *.ttf *.txt *.md
@@ -0,0 +1,267 @@
1
+ Metadata-Version: 2.4
2
+ Name: plotform
3
+ Version: 0.1.0
4
+ Summary: A minimalist Matplotlib theme and plotting toolkit with customizable palettes, capsule-shaped chart elements, and bundled fonts.
5
+ Author: Econ01
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Econ01/plotform
8
+ Project-URL: Repository, https://github.com/Econ01/plotform
9
+ Project-URL: Issues, https://github.com/Econ01/plotform/issues
10
+ Project-URL: Changelog, https://github.com/Econ01/plotform/blob/main/CHANGELOG.md
11
+ Keywords: matplotlib,plotting,visualization,theme,capsule,publication
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Visualization
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: matplotlib>=3.8
26
+ Requires-Dist: numpy>=1.24
27
+ Provides-Extra: dev
28
+ Requires-Dist: build>=1.2; extra == "dev"
29
+ Requires-Dist: fonttools>=4.50; extra == "dev"
30
+ Requires-Dist: pillow>=9; extra == "dev"
31
+ Requires-Dist: pytest>=8; extra == "dev"
32
+ Requires-Dist: ruff>=0.6; extra == "dev"
33
+ Requires-Dist: tomli>=2; python_version < "3.11" and extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # Plotform
37
+
38
+ [![CI](https://github.com/Econ01/plotform/actions/workflows/ci.yml/badge.svg)](https://github.com/Econ01/plotform/actions/workflows/ci.yml)
39
+ [![PyPI](https://img.shields.io/pypi/v/plotform.svg)](https://pypi.org/project/plotform/)
40
+ [![Python](https://img.shields.io/pypi/pyversions/plotform.svg)](https://pypi.org/project/plotform/)
41
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
42
+
43
+ **A minimalist Matplotlib theme and plotting toolkit with customizable palettes, capsule-shaped chart elements, and bundled fonts.**
44
+
45
+ Plotform combines a vivid, minimalist visual style with native-style helpers for pill legends, circularly rounded bars, capsule annotations, vertical capsule colorbars, and clean 3D plots. Fonts are bundled, so system font installation is not required.
46
+
47
+ [![Plotform examples showing a line chart, rounded bars, a heatmap, and a 3D scatter plot](https://raw.githubusercontent.com/Econ01/plotform/main/docs/images/overview.png)](https://github.com/Econ01/plotform/tree/main/examples)
48
+
49
+ ## Requirements
50
+
51
+ - Python 3.10 or newer
52
+ - Matplotlib 3.8 or newer
53
+ - NumPy 1.24 or newer
54
+
55
+ ## Installation
56
+
57
+ ```bash
58
+ python -m pip install plotform
59
+ ```
60
+
61
+ For development from a source checkout:
62
+
63
+ ```bash
64
+ python -m pip install -e ".[dev]"
65
+ ```
66
+
67
+ ## Quick start
68
+
69
+ ```python
70
+ import matplotlib.pyplot as plt
71
+ import plotform as tm
72
+
73
+ tm.apply_theme()
74
+
75
+ fig, ax = plt.subplots()
76
+ ax.plot([1, 2, 3], [2, 4, 3], label="Example")
77
+ ax.set(title="A minimalist chart", xlabel="Time", ylabel="Signal")
78
+ tm.legend(ax)
79
+
80
+ fig.savefig("chart.png", dpi=800)
81
+ fig.savefig("chart.pdf")
82
+ ```
83
+
84
+ `apply_theme()` resets Matplotlib's style before applying Plotform and changes process-global settings. Call it once before creating figures. To preserve and later restore another style, use the scoped form:
85
+
86
+ ```python
87
+ with tm.theme():
88
+ fig, ax = plt.subplots()
89
+ ax.plot([1, 2, 3], [2, 4, 3], label="Example")
90
+ tm.legend(ax)
91
+ fig.savefig("chart.png")
92
+ # Previous rcParams are restored here.
93
+ ```
94
+
95
+ ## Plain Matplotlib, without Plotform
96
+
97
+ Scripts that must run without the package do not need a compatibility layer. Use native Matplotlib directly:
98
+
99
+ ```python
100
+ import matplotlib.pyplot as plt
101
+
102
+ fig, ax = plt.subplots()
103
+ ax.bar([0, 1, 2], [3, 5, 4], label="Example")
104
+ ax.legend()
105
+ fig.savefig("plot.png")
106
+ ```
107
+
108
+ `examples/native_matplotlib.py` is a complete instructor-friendly example. Plotform is an opt-in enhancement, not a requirement for these scripts.
109
+
110
+ ## Native-style helpers
111
+
112
+ | Helper | Purpose |
113
+ | --- | --- |
114
+ | `tm.legend(ax)` | Solid or soft pill legends with adaptive text contrast |
115
+ | `tm.bar(x, height, ax=ax)` | Vertical bars with circular caps |
116
+ | `tm.barh(y, width, ax=ax)` | Horizontal bars with circular caps |
117
+ | `tm.colorbar(image, ax=ax)` | Vertical capsule colorbar with external ticks |
118
+ | `tm.annotate(text, xy, ax=ax)` | Exact capsule annotation |
119
+ | `tm.style_3d(ax)` | Minimal 3D panes, grids, ticks, and viewing angle |
120
+
121
+ `legend()` uses the current axes when `ax` is omitted. `colorbar()` uses the current figure and axes. Advanced geometry remains available through `plotform.rounded`; font utilities remain available through `plotform.fonts`.
122
+
123
+ ## Fonts
124
+
125
+ - **Plotform Sans**, derived from Google Sans Flex with `ROND=100` and default weight 400.
126
+ - **Plotform Mono**, derived from Google Sans Code at its default weight.
127
+ - DejaVu fallbacks for mathematical notation and missing glyphs.
128
+
129
+ The package registers its bundled fonts directly with Matplotlib. No operating-system installation or cache clearing is needed. Font licenses and attribution are included under `src/plotform/assets/fonts/`.
130
+
131
+ ## Colors
132
+
133
+ The default vivid categorical cycle is:
134
+
135
+ | Order | Color | Hex |
136
+ | --- | --- | --- |
137
+ | 1 | Teal | `#008080` |
138
+ | 2 | Amber | `#E69F00` |
139
+ | 3 | Blue | `#0072B2` |
140
+ | 4 | Coral/vermillion | `#D55E00` |
141
+ | 5 | Purple | `#6A51A3` |
142
+ | 6 | Green | `#4C9A2A` |
143
+ | 7 | Rose | `#CC79A7` |
144
+ | 8 | Dark slate | `#4B5563` |
145
+
146
+ Extend the cycle deterministically:
147
+
148
+ ```python
149
+ tm.apply_theme(n_colors=14)
150
+ colors = tm.get_palette(14)
151
+ ```
152
+
153
+ Axes, labels, and ticks are black by default while grids remain light. For muted-gray decorations:
154
+
155
+ ```python
156
+ tm.apply_theme(gray_decorations=True)
157
+ ```
158
+
159
+ Three continuous colormaps are registered:
160
+
161
+ ```python
162
+ ax.imshow(values, cmap=tm.SCALAR_CMAP) # Full-range scalar default
163
+ ax.imshow(values, cmap=tm.DIVERGING_CMAP) # Signed/diverging data
164
+ ax.imshow(values, cmap=tm.SEQUENTIAL_CMAP) # Optional pale brand-teal map
165
+ ```
166
+
167
+ `SCALAR_CMAP` follows navy → blue → teal → green → amber, avoiding near-white endpoints. Use fully opaque scalar markers and `depthshade=False` for mplot3d scatter plots. Use `shade=True` for `bar3d` to preserve face lighting.
168
+
169
+ ## Pill legends
170
+
171
+ Solid category-color fills use adaptive white or black text, targeting at least 4.5:1 contrast. Marker and line handles are explicitly hidden so they cannot overlap labels.
172
+
173
+ ```python
174
+ tm.legend(ax, ncols=3)
175
+ ```
176
+
177
+ For auxiliary uncertainty regions, use an explicitly tinted entry:
178
+
179
+ ```python
180
+ tm.legend(ax, styles={"95% interval": "soft"})
181
+ ```
182
+
183
+ For dense plots, place the legend outside or add a white group capsule:
184
+
185
+ ```python
186
+ tm.legend(ax, outside=True)
187
+ tm.legend(ax, backdrop=True, backdrop_alpha=0.88)
188
+ ```
189
+
190
+ ## Rounded bars and annotations
191
+
192
+ ```python
193
+ import numpy as np
194
+
195
+ x = np.arange(4)
196
+
197
+ # Exact semicircular cap with a square baseline.
198
+ tm.bar(x, [3, 5, -2, 4], radius="full", ends="free", ax=ax)
199
+
200
+ # Complete vertical capsule.
201
+ tm.bar(x, [3, 5, -2, 4], radius="full", ends="both", ax=ax)
202
+
203
+ # Clamp bars too short for an exact full capsule.
204
+ tm.bar(x, [3, 5, -2, 4], radius="fit", ends="both", ax=ax)
205
+
206
+ # Numeric circular corner radius.
207
+ tm.bar(x, [3, 5, -2, 4], radius=0.16, ax=ax)
208
+ ```
209
+
210
+ Exact full mode raises a clear error when the display geometry cannot contain the requested semicircle. Use `radius="fit"` to opt into clamping instead.
211
+
212
+ Annotations use exact stadium geometry:
213
+
214
+ ```python
215
+ tm.annotate("New", (0.15, 0.85), ax=ax, transform=ax.transAxes)
216
+ ```
217
+
218
+ `plotform.rounded` also exposes `capsule_path`, `CapsuleBox`, and separate squircle primitives for advanced use.
219
+
220
+ [![Exact horizontal and vertical capsule geometry](https://raw.githubusercontent.com/Econ01/plotform/main/docs/images/capsules.png)](https://github.com/Econ01/plotform/blob/main/examples/geometry_check.py)
221
+
222
+ ## Capsule colorbars
223
+
224
+ ```python
225
+ image = ax.imshow(values, cmap=tm.SCALAR_CMAP)
226
+ colorbar = tm.colorbar(image, ax=ax)
227
+ ```
228
+
229
+ The visible gradient is clipped to an exact vertical capsule in display space. Ticks and labels remain outside. Image, contour, streamplot, scatter, and surface mappables are supported. Standard rectangular `fig.colorbar()` output remains available.
230
+
231
+ ## 3D plots
232
+
233
+ ```python
234
+ fig = plt.figure()
235
+ ax = fig.add_subplot(projection="3d")
236
+ tm.style_3d(ax)
237
+ ax.plot_surface(xx, yy, zz, cmap=tm.DIVERGING_CMAP, shade=True)
238
+ ```
239
+
240
+ `style_3d()` clears panes and applies consistent grids, ticks, and viewing geometry. Pass `gray_decorations=True` for muted decorations.
241
+
242
+ ## Visual showcase
243
+
244
+ ```bash
245
+ python examples/showcase.py
246
+ python examples/geometry_check.py
247
+ ```
248
+
249
+ The showcase creates 27 default-versus-Plotform comparisons at 800 DPI, lightweight previews, selected PDF/SVG exports, and a contact sheet under `artifacts/showcase/`. The geometry script creates exact capsule reference boards under `artifacts/`.
250
+
251
+ [View the complete 27-chart contact sheet](https://github.com/Econ01/plotform/blob/main/docs/images/contact-sheet.png) or regenerate the documentation images with `python tools/make_doc_images.py`.
252
+
253
+ ## Development
254
+
255
+ ```bash
256
+ python -m pip install -e ".[dev]"
257
+ pytest
258
+ ruff check .
259
+ ruff format --check .
260
+ python -m build
261
+ ```
262
+
263
+ See `CHANGELOG.md` for release history and `docs/RELEASING.md` for the GitHub, TestPyPI, and PyPI release process.
264
+
265
+ ## License
266
+
267
+ Code is MIT licensed. Bundled font software remains under the SIL Open Font License; see the font asset notices for details.
@@ -0,0 +1,232 @@
1
+ # Plotform
2
+
3
+ [![CI](https://github.com/Econ01/plotform/actions/workflows/ci.yml/badge.svg)](https://github.com/Econ01/plotform/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/plotform.svg)](https://pypi.org/project/plotform/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/plotform.svg)](https://pypi.org/project/plotform/)
6
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7
+
8
+ **A minimalist Matplotlib theme and plotting toolkit with customizable palettes, capsule-shaped chart elements, and bundled fonts.**
9
+
10
+ Plotform combines a vivid, minimalist visual style with native-style helpers for pill legends, circularly rounded bars, capsule annotations, vertical capsule colorbars, and clean 3D plots. Fonts are bundled, so system font installation is not required.
11
+
12
+ [![Plotform examples showing a line chart, rounded bars, a heatmap, and a 3D scatter plot](https://raw.githubusercontent.com/Econ01/plotform/main/docs/images/overview.png)](https://github.com/Econ01/plotform/tree/main/examples)
13
+
14
+ ## Requirements
15
+
16
+ - Python 3.10 or newer
17
+ - Matplotlib 3.8 or newer
18
+ - NumPy 1.24 or newer
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ python -m pip install plotform
24
+ ```
25
+
26
+ For development from a source checkout:
27
+
28
+ ```bash
29
+ python -m pip install -e ".[dev]"
30
+ ```
31
+
32
+ ## Quick start
33
+
34
+ ```python
35
+ import matplotlib.pyplot as plt
36
+ import plotform as tm
37
+
38
+ tm.apply_theme()
39
+
40
+ fig, ax = plt.subplots()
41
+ ax.plot([1, 2, 3], [2, 4, 3], label="Example")
42
+ ax.set(title="A minimalist chart", xlabel="Time", ylabel="Signal")
43
+ tm.legend(ax)
44
+
45
+ fig.savefig("chart.png", dpi=800)
46
+ fig.savefig("chart.pdf")
47
+ ```
48
+
49
+ `apply_theme()` resets Matplotlib's style before applying Plotform and changes process-global settings. Call it once before creating figures. To preserve and later restore another style, use the scoped form:
50
+
51
+ ```python
52
+ with tm.theme():
53
+ fig, ax = plt.subplots()
54
+ ax.plot([1, 2, 3], [2, 4, 3], label="Example")
55
+ tm.legend(ax)
56
+ fig.savefig("chart.png")
57
+ # Previous rcParams are restored here.
58
+ ```
59
+
60
+ ## Plain Matplotlib, without Plotform
61
+
62
+ Scripts that must run without the package do not need a compatibility layer. Use native Matplotlib directly:
63
+
64
+ ```python
65
+ import matplotlib.pyplot as plt
66
+
67
+ fig, ax = plt.subplots()
68
+ ax.bar([0, 1, 2], [3, 5, 4], label="Example")
69
+ ax.legend()
70
+ fig.savefig("plot.png")
71
+ ```
72
+
73
+ `examples/native_matplotlib.py` is a complete instructor-friendly example. Plotform is an opt-in enhancement, not a requirement for these scripts.
74
+
75
+ ## Native-style helpers
76
+
77
+ | Helper | Purpose |
78
+ | --- | --- |
79
+ | `tm.legend(ax)` | Solid or soft pill legends with adaptive text contrast |
80
+ | `tm.bar(x, height, ax=ax)` | Vertical bars with circular caps |
81
+ | `tm.barh(y, width, ax=ax)` | Horizontal bars with circular caps |
82
+ | `tm.colorbar(image, ax=ax)` | Vertical capsule colorbar with external ticks |
83
+ | `tm.annotate(text, xy, ax=ax)` | Exact capsule annotation |
84
+ | `tm.style_3d(ax)` | Minimal 3D panes, grids, ticks, and viewing angle |
85
+
86
+ `legend()` uses the current axes when `ax` is omitted. `colorbar()` uses the current figure and axes. Advanced geometry remains available through `plotform.rounded`; font utilities remain available through `plotform.fonts`.
87
+
88
+ ## Fonts
89
+
90
+ - **Plotform Sans**, derived from Google Sans Flex with `ROND=100` and default weight 400.
91
+ - **Plotform Mono**, derived from Google Sans Code at its default weight.
92
+ - DejaVu fallbacks for mathematical notation and missing glyphs.
93
+
94
+ The package registers its bundled fonts directly with Matplotlib. No operating-system installation or cache clearing is needed. Font licenses and attribution are included under `src/plotform/assets/fonts/`.
95
+
96
+ ## Colors
97
+
98
+ The default vivid categorical cycle is:
99
+
100
+ | Order | Color | Hex |
101
+ | --- | --- | --- |
102
+ | 1 | Teal | `#008080` |
103
+ | 2 | Amber | `#E69F00` |
104
+ | 3 | Blue | `#0072B2` |
105
+ | 4 | Coral/vermillion | `#D55E00` |
106
+ | 5 | Purple | `#6A51A3` |
107
+ | 6 | Green | `#4C9A2A` |
108
+ | 7 | Rose | `#CC79A7` |
109
+ | 8 | Dark slate | `#4B5563` |
110
+
111
+ Extend the cycle deterministically:
112
+
113
+ ```python
114
+ tm.apply_theme(n_colors=14)
115
+ colors = tm.get_palette(14)
116
+ ```
117
+
118
+ Axes, labels, and ticks are black by default while grids remain light. For muted-gray decorations:
119
+
120
+ ```python
121
+ tm.apply_theme(gray_decorations=True)
122
+ ```
123
+
124
+ Three continuous colormaps are registered:
125
+
126
+ ```python
127
+ ax.imshow(values, cmap=tm.SCALAR_CMAP) # Full-range scalar default
128
+ ax.imshow(values, cmap=tm.DIVERGING_CMAP) # Signed/diverging data
129
+ ax.imshow(values, cmap=tm.SEQUENTIAL_CMAP) # Optional pale brand-teal map
130
+ ```
131
+
132
+ `SCALAR_CMAP` follows navy → blue → teal → green → amber, avoiding near-white endpoints. Use fully opaque scalar markers and `depthshade=False` for mplot3d scatter plots. Use `shade=True` for `bar3d` to preserve face lighting.
133
+
134
+ ## Pill legends
135
+
136
+ Solid category-color fills use adaptive white or black text, targeting at least 4.5:1 contrast. Marker and line handles are explicitly hidden so they cannot overlap labels.
137
+
138
+ ```python
139
+ tm.legend(ax, ncols=3)
140
+ ```
141
+
142
+ For auxiliary uncertainty regions, use an explicitly tinted entry:
143
+
144
+ ```python
145
+ tm.legend(ax, styles={"95% interval": "soft"})
146
+ ```
147
+
148
+ For dense plots, place the legend outside or add a white group capsule:
149
+
150
+ ```python
151
+ tm.legend(ax, outside=True)
152
+ tm.legend(ax, backdrop=True, backdrop_alpha=0.88)
153
+ ```
154
+
155
+ ## Rounded bars and annotations
156
+
157
+ ```python
158
+ import numpy as np
159
+
160
+ x = np.arange(4)
161
+
162
+ # Exact semicircular cap with a square baseline.
163
+ tm.bar(x, [3, 5, -2, 4], radius="full", ends="free", ax=ax)
164
+
165
+ # Complete vertical capsule.
166
+ tm.bar(x, [3, 5, -2, 4], radius="full", ends="both", ax=ax)
167
+
168
+ # Clamp bars too short for an exact full capsule.
169
+ tm.bar(x, [3, 5, -2, 4], radius="fit", ends="both", ax=ax)
170
+
171
+ # Numeric circular corner radius.
172
+ tm.bar(x, [3, 5, -2, 4], radius=0.16, ax=ax)
173
+ ```
174
+
175
+ Exact full mode raises a clear error when the display geometry cannot contain the requested semicircle. Use `radius="fit"` to opt into clamping instead.
176
+
177
+ Annotations use exact stadium geometry:
178
+
179
+ ```python
180
+ tm.annotate("New", (0.15, 0.85), ax=ax, transform=ax.transAxes)
181
+ ```
182
+
183
+ `plotform.rounded` also exposes `capsule_path`, `CapsuleBox`, and separate squircle primitives for advanced use.
184
+
185
+ [![Exact horizontal and vertical capsule geometry](https://raw.githubusercontent.com/Econ01/plotform/main/docs/images/capsules.png)](https://github.com/Econ01/plotform/blob/main/examples/geometry_check.py)
186
+
187
+ ## Capsule colorbars
188
+
189
+ ```python
190
+ image = ax.imshow(values, cmap=tm.SCALAR_CMAP)
191
+ colorbar = tm.colorbar(image, ax=ax)
192
+ ```
193
+
194
+ The visible gradient is clipped to an exact vertical capsule in display space. Ticks and labels remain outside. Image, contour, streamplot, scatter, and surface mappables are supported. Standard rectangular `fig.colorbar()` output remains available.
195
+
196
+ ## 3D plots
197
+
198
+ ```python
199
+ fig = plt.figure()
200
+ ax = fig.add_subplot(projection="3d")
201
+ tm.style_3d(ax)
202
+ ax.plot_surface(xx, yy, zz, cmap=tm.DIVERGING_CMAP, shade=True)
203
+ ```
204
+
205
+ `style_3d()` clears panes and applies consistent grids, ticks, and viewing geometry. Pass `gray_decorations=True` for muted decorations.
206
+
207
+ ## Visual showcase
208
+
209
+ ```bash
210
+ python examples/showcase.py
211
+ python examples/geometry_check.py
212
+ ```
213
+
214
+ The showcase creates 27 default-versus-Plotform comparisons at 800 DPI, lightweight previews, selected PDF/SVG exports, and a contact sheet under `artifacts/showcase/`. The geometry script creates exact capsule reference boards under `artifacts/`.
215
+
216
+ [View the complete 27-chart contact sheet](https://github.com/Econ01/plotform/blob/main/docs/images/contact-sheet.png) or regenerate the documentation images with `python tools/make_doc_images.py`.
217
+
218
+ ## Development
219
+
220
+ ```bash
221
+ python -m pip install -e ".[dev]"
222
+ pytest
223
+ ruff check .
224
+ ruff format --check .
225
+ python -m build
226
+ ```
227
+
228
+ See `CHANGELOG.md` for release history and `docs/RELEASING.md` for the GitHub, TestPyPI, and PyPI release process.
229
+
230
+ ## License
231
+
232
+ Code is MIT licensed. Bundled font software remains under the SIL Open Font License; see the font asset notices for details.
@@ -0,0 +1,125 @@
1
+ # Releasing Plotform
2
+
3
+ This project uses GitHub Actions and PyPI **trusted publishing** (OIDC). Do not
4
+ store long-lived PyPI tokens in the repository.
5
+
6
+ ## 1. Validate the source
7
+
8
+ Before creating a release:
9
+
10
+ ```bash
11
+ pytest
12
+ ruff check .
13
+ ruff format --check .
14
+ python -m build
15
+ python tools/check_sdist.py
16
+ ```
17
+
18
+ Install the wheel into a clean environment and run a small import and plotting
19
+ smoke test. The CI workflow must also be green.
20
+
21
+ ## 2. Push the release branch
22
+
23
+ The repository remote is `https://github.com/Econ01/plotform.git`.
24
+
25
+ If using the `release/0.1.0` branch:
26
+
27
+ ```bash
28
+ git push -u origin release/0.1.0
29
+ ```
30
+
31
+ Open a pull request into `main`, review the diff and CI checks, and merge when
32
+ ready. A direct push to `main` is also possible for a solo project, but the pull
33
+ request provides a useful release review.
34
+
35
+ ## 3. Create GitHub environments
36
+
37
+ In **Settings → Environments**, create:
38
+
39
+ - `testpypi`
40
+ - `pypi`
41
+
42
+ For the first production release, require manual approval on the `pypi`
43
+ environment so publication cannot happen accidentally.
44
+
45
+ ## 4. Configure TestPyPI trusted publishing
46
+
47
+ Sign in to TestPyPI and add a **pending trusted publisher** with exactly:
48
+
49
+ - PyPI project name: `plotform`
50
+ - Owner: `Econ01`
51
+ - Repository name: `plotform`
52
+ - Workflow name: `release.yml`
53
+ - Environment name: `testpypi`
54
+
55
+ If a TestPyPI project already exists, add the publisher under its publishing
56
+ settings instead.
57
+
58
+ ## 5. Configure production PyPI trusted publishing
59
+
60
+ On production PyPI, add the same publisher identity, except:
61
+
62
+ - Environment name: `pypi`
63
+
64
+ The workflow filename and environment names must match exactly. GitHub Actions
65
+ uses these values when requesting an OIDC token.
66
+
67
+ ## 6. Publish to TestPyPI
68
+
69
+ In the GitHub repository, open **Actions → Release → Run workflow** and run the
70
+ workflow on the release branch. A manual workflow run publishes only to
71
+ TestPyPI; production publication happens through a GitHub release.
72
+
73
+ Then create a clean environment and install:
74
+
75
+ ```bash
76
+ python -m pip install \
77
+ --index-url https://test.pypi.org/simple/ \
78
+ --extra-index-url https://pypi.org/simple/ \
79
+ plotform
80
+ ```
81
+
82
+ The extra index supplies dependencies such as Matplotlib and NumPy that may not
83
+ be available on TestPyPI.
84
+
85
+ Verify:
86
+
87
+ ```python
88
+ import plotform as tm
89
+
90
+ assert tm.__version__ == "0.1.0"
91
+ tm.apply_theme()
92
+ ```
93
+
94
+ Run a small plotting example as well.
95
+
96
+ ## 7. Create the production release
97
+
98
+ After TestPyPI validation:
99
+
100
+ 1. Ensure `pyproject.toml` contains version `0.1.0`.
101
+ 2. Update `CHANGELOG.md` if needed.
102
+ 3. Create and push tag `v0.1.0`:
103
+
104
+ ```bash
105
+ git tag -a v0.1.0 -m "Plotform 0.1.0"
106
+ git push origin v0.1.0
107
+ ```
108
+
109
+ 4. In GitHub, choose **Releases → Draft a new release**, select `v0.1.0`, add
110
+ the changelog notes, and publish.
111
+ 5. The Release workflow checks the tag, builds the distributions, and publishes
112
+ to production PyPI through the `pypi` environment.
113
+ 6. Approve the production environment if protection is enabled.
114
+
115
+ ## 8. Verify the public installation
116
+
117
+ In another clean environment:
118
+
119
+ ```bash
120
+ python -m pip install plotform
121
+ python -c "import plotform; print(plotform.__version__)"
122
+ ```
123
+
124
+ Finally, add the repository description, topics, and PyPI link in GitHub's About
125
+ section. The README already contains CI, PyPI, Python, and license badges.
Binary file
Binary file