complexplorer 2.0.0__tar.gz → 3.0.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 (82) hide show
  1. complexplorer-3.0.0/PKG-INFO +222 -0
  2. complexplorer-3.0.0/README.md +167 -0
  3. complexplorer-3.0.0/complexplorer/__init__.py +119 -0
  4. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer/_version.py +1 -1
  5. complexplorer-3.0.0/complexplorer/api.py +111 -0
  6. complexplorer-3.0.0/complexplorer/cli/__init__.py +6 -0
  7. complexplorer-3.0.0/complexplorer/cli/main.py +239 -0
  8. complexplorer-3.0.0/complexplorer/core/__init__.py +73 -0
  9. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer/core/color_utils.py +114 -93
  10. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer/core/colormap.py +595 -359
  11. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer/core/domain.py +148 -234
  12. complexplorer-3.0.0/complexplorer/core/expression.py +137 -0
  13. complexplorer-3.0.0/complexplorer/core/field.py +203 -0
  14. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer/core/functions.py +76 -101
  15. complexplorer-3.0.0/complexplorer/core/presets.py +545 -0
  16. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer/core/scaling.py +151 -96
  17. complexplorer-3.0.0/complexplorer/ee/__init__.py +29 -0
  18. complexplorer-3.0.0/complexplorer/ee/transfer_function.py +284 -0
  19. complexplorer-3.0.0/complexplorer/exceptions.py +34 -0
  20. complexplorer-3.0.0/complexplorer/export/__init__.py +6 -0
  21. complexplorer-3.0.0/complexplorer/export/stl/__init__.py +19 -0
  22. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer/export/stl/mesh_repair.py +37 -78
  23. complexplorer-3.0.0/complexplorer/export/stl/ornament_generator.py +280 -0
  24. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer/export/stl/utils.py +84 -97
  25. complexplorer-3.0.0/complexplorer/gallery.py +142 -0
  26. complexplorer-3.0.0/complexplorer/mesh/__init__.py +17 -0
  27. complexplorer-3.0.0/complexplorer/mesh/builders.py +141 -0
  28. complexplorer-3.0.0/complexplorer/mesh/riemann_surface.py +188 -0
  29. complexplorer-3.0.0/complexplorer/mesh/surface.py +153 -0
  30. complexplorer-3.0.0/complexplorer/plotting/__init__.py +1 -0
  31. complexplorer-3.0.0/complexplorer/plotting/matplotlib/__init__.py +5 -0
  32. complexplorer-3.0.0/complexplorer/plotting/matplotlib/plot_2d.py +420 -0
  33. complexplorer-3.0.0/complexplorer/plotting/pyvista/__init__.py +12 -0
  34. complexplorer-3.0.0/complexplorer/plotting/pyvista/plot_3d.py +380 -0
  35. complexplorer-3.0.0/complexplorer/plotting/pyvista/riemann.py +285 -0
  36. complexplorer-3.0.0/complexplorer/plotting/pyvista/riemann_surface.py +117 -0
  37. complexplorer-3.0.0/complexplorer/plotting/pyvista/utils.py +196 -0
  38. complexplorer-3.0.0/complexplorer/py.typed +0 -0
  39. complexplorer-3.0.0/complexplorer/typing.py +110 -0
  40. complexplorer-3.0.0/complexplorer/utils/__init__.py +5 -0
  41. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer/utils/backend.py +29 -26
  42. complexplorer-3.0.0/complexplorer/utils/mesh_distortion.py +120 -0
  43. complexplorer-3.0.0/complexplorer/utils/validation.py +54 -0
  44. complexplorer-3.0.0/complexplorer.egg-info/PKG-INFO +222 -0
  45. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer.egg-info/SOURCES.txt +16 -7
  46. complexplorer-3.0.0/complexplorer.egg-info/entry_points.txt +2 -0
  47. complexplorer-3.0.0/complexplorer.egg-info/requires.txt +34 -0
  48. complexplorer-3.0.0/pyproject.toml +157 -0
  49. complexplorer-2.0.0/PKG-INFO +0 -362
  50. complexplorer-2.0.0/README.md +0 -327
  51. complexplorer-2.0.0/complexplorer/__init__.py +0 -168
  52. complexplorer-2.0.0/complexplorer/api.py +0 -210
  53. complexplorer-2.0.0/complexplorer/core/__init__.py +0 -59
  54. complexplorer-2.0.0/complexplorer/core/constants.py +0 -161
  55. complexplorer-2.0.0/complexplorer/exceptions.py +0 -259
  56. complexplorer-2.0.0/complexplorer/export/__init__.py +0 -5
  57. complexplorer-2.0.0/complexplorer/export/stl/__init__.py +0 -20
  58. complexplorer-2.0.0/complexplorer/export/stl/ornament_generator.py +0 -395
  59. complexplorer-2.0.0/complexplorer/plotting/__init__.py +0 -28
  60. complexplorer-2.0.0/complexplorer/plotting/matplotlib/__init__.py +0 -6
  61. complexplorer-2.0.0/complexplorer/plotting/matplotlib/plot_2d.py +0 -478
  62. complexplorer-2.0.0/complexplorer/plotting/matplotlib/plot_3d.py +0 -509
  63. complexplorer-2.0.0/complexplorer/plotting/pyvista/__init__.py +0 -7
  64. complexplorer-2.0.0/complexplorer/plotting/pyvista/plot_3d.py +0 -472
  65. complexplorer-2.0.0/complexplorer/plotting/pyvista/riemann.py +0 -339
  66. complexplorer-2.0.0/complexplorer/plotting/pyvista/utils.py +0 -170
  67. complexplorer-2.0.0/complexplorer/plotting/validation.py +0 -247
  68. complexplorer-2.0.0/complexplorer/special.py +0 -421
  69. complexplorer-2.0.0/complexplorer/utils/__init__.py +0 -6
  70. complexplorer-2.0.0/complexplorer/utils/color.py +0 -177
  71. complexplorer-2.0.0/complexplorer/utils/logging.py +0 -225
  72. complexplorer-2.0.0/complexplorer/utils/mesh.py +0 -234
  73. complexplorer-2.0.0/complexplorer/utils/mesh_distortion.py +0 -196
  74. complexplorer-2.0.0/complexplorer/utils/validation.py +0 -345
  75. complexplorer-2.0.0/complexplorer.egg-info/PKG-INFO +0 -362
  76. complexplorer-2.0.0/complexplorer.egg-info/requires.txt +0 -17
  77. complexplorer-2.0.0/pyproject.toml +0 -57
  78. {complexplorer-2.0.0 → complexplorer-3.0.0}/LICENSE +0 -0
  79. {complexplorer-2.0.0 → complexplorer-3.0.0}/LICENSE.art +0 -0
  80. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer.egg-info/dependency_links.txt +0 -0
  81. {complexplorer-2.0.0 → complexplorer-3.0.0}/complexplorer.egg-info/top_level.txt +0 -0
  82. {complexplorer-2.0.0 → complexplorer-3.0.0}/setup.cfg +0 -0
@@ -0,0 +1,222 @@
1
+ Metadata-Version: 2.4
2
+ Name: complexplorer
3
+ Version: 3.0.0
4
+ Summary: Complex function visualizations inspired by Elias Wegert's 'Visual Complex Functions - An Introduction with Phase Portraits'
5
+ Author-email: Igor Kuvychko <igor.kuvychko@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/kuvychko/complexplorer
8
+ Project-URL: Documentation, https://kuvychko.github.io/complexplorer/
9
+ Project-URL: Source, https://github.com/kuvychko/complexplorer
10
+ Project-URL: Changelog, https://github.com/kuvychko/complexplorer/blob/main/CHANGELOG.md
11
+ Project-URL: Release notes, https://github.com/kuvychko/complexplorer/releases
12
+ Project-URL: Bug Tracker, https://github.com/kuvychko/complexplorer/issues
13
+ Keywords: complex analysis,visualization,phase portrait,domain coloring,riemann sphere,riemann surface,stl,3d printing,pyvista
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Visualization
20
+ Classifier: Topic :: Multimedia :: Graphics
21
+ Classifier: Operating System :: OS Independent
22
+ Classifier: Framework :: Matplotlib
23
+ Requires-Python: >=3.11
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ License-File: LICENSE.art
27
+ Requires-Dist: numpy>=1.26.0
28
+ Requires-Dist: matplotlib>=3.8.0
29
+ Requires-Dist: scipy>=1.11.0
30
+ Requires-Dist: asteval>=1.0
31
+ Requires-Dist: pyvista>=0.47
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
34
+ Requires-Dist: pytest-cov>=3.0.0; extra == "dev"
35
+ Requires-Dist: pytest-timeout>=2.1.0; extra == "dev"
36
+ Requires-Dist: ruff<0.17,>=0.16.7; extra == "dev"
37
+ Requires-Dist: complexplorer[examples]; extra == "dev"
38
+ Provides-Extra: examples
39
+ Requires-Dist: nbmake>=1.5; extra == "examples"
40
+ Requires-Dist: nbconvert>=7.0; extra == "examples"
41
+ Requires-Dist: ipykernel>=6.0; extra == "examples"
42
+ Requires-Dist: colorspacious>=1.1; extra == "examples"
43
+ Requires-Dist: imageio>=2.31; extra == "examples"
44
+ Provides-Extra: docs
45
+ Requires-Dist: mkdocs-material<10,>=9.5; extra == "docs"
46
+ Requires-Dist: mkdocstrings[python]<0.31,>=0.27; extra == "docs"
47
+ Requires-Dist: mkdocs-jupyter<0.26,>=0.25; extra == "docs"
48
+ Provides-Extra: pyvista
49
+ Provides-Extra: 3d
50
+ Provides-Extra: qt
51
+ Requires-Dist: PyQt6>=6.5.0; extra == "qt"
52
+ Provides-Extra: all
53
+ Requires-Dist: complexplorer[qt]; extra == "all"
54
+ Dynamic: license-file
55
+
56
+ # Complexplorer
57
+
58
+ [![PyPI version](https://badge.fury.io/py/complexplorer.svg)](https://badge.fury.io/py/complexplorer)
59
+ [![Python](https://img.shields.io/pypi/pyversions/complexplorer.svg)](https://pypi.org/project/complexplorer/)
60
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
61
+
62
+ Complexplorer turns a complex function into something you can look at — and, if you want, hold.
63
+ It draws phase portraits, lifts them into 3D analytic landscapes, wraps them onto the Riemann
64
+ sphere, unfolds multivalued functions onto their Riemann surfaces, and exports any of it as an
65
+ STL you can print.
66
+
67
+ <p align="center">
68
+ <img src="https://raw.githubusercontent.com/kuvychko/complexplorer/main/examples/gallery/view/_tour/hero_labels_below.png" width="100%"
69
+ alt="Six panels: domain coloring, analytic landscape, Riemann relief, Riemann surface, transfer functions, and a 3D-printed ornament">
70
+ </p>
71
+
72
+ ## Install and draw something
73
+
74
+ ```bash
75
+ pip install complexplorer
76
+ ```
77
+
78
+ ```python
79
+ import complexplorer as cp
80
+
81
+ cp.plot(
82
+ cp.Rectangle(4, 4),
83
+ lambda z: (z**2 - 1) / (z**2 + 1),
84
+ cmap=cp.Phase(phase_sectors=6, auto_scale_r=True),
85
+ legend=True,
86
+ )
87
+ ```
88
+
89
+ <p align="center">
90
+ <img src="https://raw.githubusercontent.com/kuvychko/complexplorer/main/examples/gallery/view/_tour/legend_portrait.png" width="60%"
91
+ alt="Phase portrait of (z^2-1)/(z^2+1) with a phase-wheel legend inset">
92
+ </p>
93
+
94
+ Hue is the phase of `f(z)` and the shaded cells are its contour bands, so zeros and poles read as
95
+ opposite winding directions. The inset legend is the same colormap applied to the identity map,
96
+ which is what makes the picture decodable.
97
+
98
+ The same function in 3D, with `|f(z)|` as height:
99
+
100
+ ```python
101
+ cp.plot_landscape_pv(cp.Rectangle(4, 4), lambda z: (z**2 - 1) / (z**2 + 1))
102
+ ```
103
+
104
+ → [Installation and a first portrait](https://kuvychko.github.io/complexplorer/getting-started/first-portrait/)
105
+
106
+ ## What's new in 3.0
107
+
108
+ - **PyVista is the sole 3D backend**, and a required dependency. The matplotlib 3D paths are
109
+ removed; `plot_landscape_pv`, `pair_plot_landscape_pv` and `riemann_pv` replace them.
110
+ - **Riemann surfaces**: multi-sheeted covers of `z^(1/n)`, `log`, and algebraic curves
111
+ `w² = P(z)`, with branch points emerging from the geometry.
112
+ - **Thirteen colormaps**, including the perceptual families built on OkLCh and cubehelix.
113
+ - **A function catalog** (`cp.catalog`) with exact zero/pole/branch-point answer keys, and a CLI
114
+ that renders and exports from the terminal.
115
+ - **Engineering mode** (`cp.ee`): transfer functions as first-class complex callables.
116
+ - **A typed public API** with a `py.typed` marker, and colormap configuration validated at
117
+ construction.
118
+
119
+ Upgrading? The [migration guide](https://kuvychko.github.io/complexplorer/migration-3.0/) maps
120
+ every removed and renamed name to its replacement.
121
+
122
+ ## What it does
123
+
124
+ | | |
125
+ |---|---|
126
+ | **Phase portraits** | Classic and enhanced domain coloring, with a phase-wheel legend |
127
+ | **Analytic landscapes** | `\|f(z)\|` as height, phase as colour, rendered by PyVista |
128
+ | **Riemann sphere** | The compactified plane, so infinity is a place you can look at |
129
+ | **Riemann surfaces** | The multi-sheeted cover on which a multivalued function is single-valued |
130
+ | **Domains** | Rectangles, disks, annuli, and set operations on them |
131
+ | **Colormaps** | Thirteen, including perceptual families and greyscale pattern maps |
132
+ | **Modulus scaling** | Ten transfer functions from `\|f(z)\|` to height or radius |
133
+ | **Engineering mode** | `H(s)` / `H(z)` with portrait, pole-zero, Bode and Nyquist views |
134
+ | **STL export** | Modulus-scaled Riemann relief ornaments for 3D printing |
135
+ | **CLI** | `complexplorer render \| stl \| list \| gallery` |
136
+
137
+ ## A few more lines
138
+
139
+ ```python
140
+ # One-liners with sensible defaults: "2d", "3d", "riemann"
141
+ cp.quick_plot(lambda z: 1 / z, mode="riemann")
142
+
143
+ # A curated function, with its exact zeros and poles recorded
144
+ preset = cp.catalog.get("pole_flower_10")
145
+ cp.quick_plot(preset.func, **cp.PlotPresets.publication_ready())
146
+
147
+ # Transfer functions are plain callables, so every renderer accepts them
148
+ H = cp.ee.TransferFunction([1], [1, 0.2, 1])
149
+ cp.ee.transfer_portrait(H, legend=True)
150
+ cp.plot_landscape_pv(cp.Rectangle(6, 6), H)
151
+
152
+ # A printable ornament
153
+ cp.create_ornament(lambda z: z / (z**10 - 1), "flower.stl", size_mm=80)
154
+ ```
155
+
156
+ From the terminal:
157
+
158
+ ```bash
159
+ complexplorer list
160
+ complexplorer render preset:pole_flower_10 -o flower.png
161
+ complexplorer stl "z / (z**10 - 1)" --size-mm 80 -o flower.stl
162
+ ```
163
+
164
+ ## From mathematics to an object on your desk
165
+
166
+ <p align="center">
167
+ <img src="https://raw.githubusercontent.com/kuvychko/complexplorer/main/examples/gallery/view/_tour/physical_triptych.png" width="100%"
168
+ alt="Riemann relief render, untextured STL mesh, and the printed ornament">
169
+ </p>
170
+
171
+ The relief is the mathematics, the mesh is the geometry that survives losing the colour, and the
172
+ print is the object on a desk. Ten poles become ten spikes around the central zero.
173
+
174
+ → [STL export and the physical workflow](https://kuvychko.github.io/complexplorer/guide/physical-workflow/)
175
+
176
+ ## Documentation
177
+
178
+ The [documentation site](https://kuvychko.github.io/complexplorer/) has the visual tour, the
179
+ guides, and a generated API reference.
180
+
181
+ - [Reading a phase portrait](https://kuvychko.github.io/complexplorer/guide/reading-a-portrait/) —
182
+ what the colours encode, and which colormap to choose (with measured colour-vision-deficiency
183
+ behaviour)
184
+ - [3D and the Riemann sphere](https://kuvychko.github.io/complexplorer/guide/three-dimensions/) —
185
+ including headless rendering and reproducible cameras
186
+ - [Gallery](https://kuvychko.github.io/complexplorer/gallery/gallery.generated/) — every preset
187
+ and colormap, with the code that made it
188
+ - [API map](https://kuvychko.github.io/complexplorer/api/map/) — one entry point per task, and
189
+ what each returns
190
+ - [Migration guide](https://kuvychko.github.io/complexplorer/migration-3.0/) and
191
+ [changelog](https://github.com/kuvychko/complexplorer/blob/main/CHANGELOG.md)
192
+
193
+ ## A note on backends
194
+
195
+ matplotlib draws the 2D portraits and the stereographic charts. PyVista draws everything 3D and
196
+ builds the export meshes, because 3D here is a mesh, camera, lighting and export problem, which is
197
+ what PyVista is for and what matplotlib's 3D engine is not. As of 3.0 PyVista is a required
198
+ dependency, so there is no capability flag to check. The reasoning, with the measured install
199
+ footprint and import cost, is in
200
+ [the backend policy](https://kuvychko.github.io/complexplorer/development/backend-policy/).
201
+
202
+ ## Contributing
203
+
204
+ See [CONTRIBUTING.md](https://github.com/kuvychko/complexplorer/blob/main/CONTRIBUTING.md) for
205
+ setup, the checks, the spec-driven workflow, and the release runbook.
206
+
207
+ ## Citing
208
+
209
+ Citation metadata is in
210
+ [CITATION.cff](https://github.com/kuvychko/complexplorer/blob/main/CITATION.cff); GitHub's
211
+ "Cite this repository" button generates BibTeX from it.
212
+
213
+ ## Acknowledgements
214
+
215
+ Inspired by Elias Wegert's *Visual Complex Functions: An Introduction with Phase Portraits*
216
+ (Birkhäuser, 2012), which is the book to read if you want to understand what these pictures show.
217
+
218
+ ## License
219
+
220
+ MIT for the code; see [LICENSE](https://github.com/kuvychko/complexplorer/blob/main/LICENSE).
221
+ The gallery images and ornament designs are under
222
+ [LICENSE.art](https://github.com/kuvychko/complexplorer/blob/main/LICENSE.art).
@@ -0,0 +1,167 @@
1
+ # Complexplorer
2
+
3
+ [![PyPI version](https://badge.fury.io/py/complexplorer.svg)](https://badge.fury.io/py/complexplorer)
4
+ [![Python](https://img.shields.io/pypi/pyversions/complexplorer.svg)](https://pypi.org/project/complexplorer/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ Complexplorer turns a complex function into something you can look at — and, if you want, hold.
8
+ It draws phase portraits, lifts them into 3D analytic landscapes, wraps them onto the Riemann
9
+ sphere, unfolds multivalued functions onto their Riemann surfaces, and exports any of it as an
10
+ STL you can print.
11
+
12
+ <p align="center">
13
+ <img src="https://raw.githubusercontent.com/kuvychko/complexplorer/main/examples/gallery/view/_tour/hero_labels_below.png" width="100%"
14
+ alt="Six panels: domain coloring, analytic landscape, Riemann relief, Riemann surface, transfer functions, and a 3D-printed ornament">
15
+ </p>
16
+
17
+ ## Install and draw something
18
+
19
+ ```bash
20
+ pip install complexplorer
21
+ ```
22
+
23
+ ```python
24
+ import complexplorer as cp
25
+
26
+ cp.plot(
27
+ cp.Rectangle(4, 4),
28
+ lambda z: (z**2 - 1) / (z**2 + 1),
29
+ cmap=cp.Phase(phase_sectors=6, auto_scale_r=True),
30
+ legend=True,
31
+ )
32
+ ```
33
+
34
+ <p align="center">
35
+ <img src="https://raw.githubusercontent.com/kuvychko/complexplorer/main/examples/gallery/view/_tour/legend_portrait.png" width="60%"
36
+ alt="Phase portrait of (z^2-1)/(z^2+1) with a phase-wheel legend inset">
37
+ </p>
38
+
39
+ Hue is the phase of `f(z)` and the shaded cells are its contour bands, so zeros and poles read as
40
+ opposite winding directions. The inset legend is the same colormap applied to the identity map,
41
+ which is what makes the picture decodable.
42
+
43
+ The same function in 3D, with `|f(z)|` as height:
44
+
45
+ ```python
46
+ cp.plot_landscape_pv(cp.Rectangle(4, 4), lambda z: (z**2 - 1) / (z**2 + 1))
47
+ ```
48
+
49
+ → [Installation and a first portrait](https://kuvychko.github.io/complexplorer/getting-started/first-portrait/)
50
+
51
+ ## What's new in 3.0
52
+
53
+ - **PyVista is the sole 3D backend**, and a required dependency. The matplotlib 3D paths are
54
+ removed; `plot_landscape_pv`, `pair_plot_landscape_pv` and `riemann_pv` replace them.
55
+ - **Riemann surfaces**: multi-sheeted covers of `z^(1/n)`, `log`, and algebraic curves
56
+ `w² = P(z)`, with branch points emerging from the geometry.
57
+ - **Thirteen colormaps**, including the perceptual families built on OkLCh and cubehelix.
58
+ - **A function catalog** (`cp.catalog`) with exact zero/pole/branch-point answer keys, and a CLI
59
+ that renders and exports from the terminal.
60
+ - **Engineering mode** (`cp.ee`): transfer functions as first-class complex callables.
61
+ - **A typed public API** with a `py.typed` marker, and colormap configuration validated at
62
+ construction.
63
+
64
+ Upgrading? The [migration guide](https://kuvychko.github.io/complexplorer/migration-3.0/) maps
65
+ every removed and renamed name to its replacement.
66
+
67
+ ## What it does
68
+
69
+ | | |
70
+ |---|---|
71
+ | **Phase portraits** | Classic and enhanced domain coloring, with a phase-wheel legend |
72
+ | **Analytic landscapes** | `\|f(z)\|` as height, phase as colour, rendered by PyVista |
73
+ | **Riemann sphere** | The compactified plane, so infinity is a place you can look at |
74
+ | **Riemann surfaces** | The multi-sheeted cover on which a multivalued function is single-valued |
75
+ | **Domains** | Rectangles, disks, annuli, and set operations on them |
76
+ | **Colormaps** | Thirteen, including perceptual families and greyscale pattern maps |
77
+ | **Modulus scaling** | Ten transfer functions from `\|f(z)\|` to height or radius |
78
+ | **Engineering mode** | `H(s)` / `H(z)` with portrait, pole-zero, Bode and Nyquist views |
79
+ | **STL export** | Modulus-scaled Riemann relief ornaments for 3D printing |
80
+ | **CLI** | `complexplorer render \| stl \| list \| gallery` |
81
+
82
+ ## A few more lines
83
+
84
+ ```python
85
+ # One-liners with sensible defaults: "2d", "3d", "riemann"
86
+ cp.quick_plot(lambda z: 1 / z, mode="riemann")
87
+
88
+ # A curated function, with its exact zeros and poles recorded
89
+ preset = cp.catalog.get("pole_flower_10")
90
+ cp.quick_plot(preset.func, **cp.PlotPresets.publication_ready())
91
+
92
+ # Transfer functions are plain callables, so every renderer accepts them
93
+ H = cp.ee.TransferFunction([1], [1, 0.2, 1])
94
+ cp.ee.transfer_portrait(H, legend=True)
95
+ cp.plot_landscape_pv(cp.Rectangle(6, 6), H)
96
+
97
+ # A printable ornament
98
+ cp.create_ornament(lambda z: z / (z**10 - 1), "flower.stl", size_mm=80)
99
+ ```
100
+
101
+ From the terminal:
102
+
103
+ ```bash
104
+ complexplorer list
105
+ complexplorer render preset:pole_flower_10 -o flower.png
106
+ complexplorer stl "z / (z**10 - 1)" --size-mm 80 -o flower.stl
107
+ ```
108
+
109
+ ## From mathematics to an object on your desk
110
+
111
+ <p align="center">
112
+ <img src="https://raw.githubusercontent.com/kuvychko/complexplorer/main/examples/gallery/view/_tour/physical_triptych.png" width="100%"
113
+ alt="Riemann relief render, untextured STL mesh, and the printed ornament">
114
+ </p>
115
+
116
+ The relief is the mathematics, the mesh is the geometry that survives losing the colour, and the
117
+ print is the object on a desk. Ten poles become ten spikes around the central zero.
118
+
119
+ → [STL export and the physical workflow](https://kuvychko.github.io/complexplorer/guide/physical-workflow/)
120
+
121
+ ## Documentation
122
+
123
+ The [documentation site](https://kuvychko.github.io/complexplorer/) has the visual tour, the
124
+ guides, and a generated API reference.
125
+
126
+ - [Reading a phase portrait](https://kuvychko.github.io/complexplorer/guide/reading-a-portrait/) —
127
+ what the colours encode, and which colormap to choose (with measured colour-vision-deficiency
128
+ behaviour)
129
+ - [3D and the Riemann sphere](https://kuvychko.github.io/complexplorer/guide/three-dimensions/) —
130
+ including headless rendering and reproducible cameras
131
+ - [Gallery](https://kuvychko.github.io/complexplorer/gallery/gallery.generated/) — every preset
132
+ and colormap, with the code that made it
133
+ - [API map](https://kuvychko.github.io/complexplorer/api/map/) — one entry point per task, and
134
+ what each returns
135
+ - [Migration guide](https://kuvychko.github.io/complexplorer/migration-3.0/) and
136
+ [changelog](https://github.com/kuvychko/complexplorer/blob/main/CHANGELOG.md)
137
+
138
+ ## A note on backends
139
+
140
+ matplotlib draws the 2D portraits and the stereographic charts. PyVista draws everything 3D and
141
+ builds the export meshes, because 3D here is a mesh, camera, lighting and export problem, which is
142
+ what PyVista is for and what matplotlib's 3D engine is not. As of 3.0 PyVista is a required
143
+ dependency, so there is no capability flag to check. The reasoning, with the measured install
144
+ footprint and import cost, is in
145
+ [the backend policy](https://kuvychko.github.io/complexplorer/development/backend-policy/).
146
+
147
+ ## Contributing
148
+
149
+ See [CONTRIBUTING.md](https://github.com/kuvychko/complexplorer/blob/main/CONTRIBUTING.md) for
150
+ setup, the checks, the spec-driven workflow, and the release runbook.
151
+
152
+ ## Citing
153
+
154
+ Citation metadata is in
155
+ [CITATION.cff](https://github.com/kuvychko/complexplorer/blob/main/CITATION.cff); GitHub's
156
+ "Cite this repository" button generates BibTeX from it.
157
+
158
+ ## Acknowledgements
159
+
160
+ Inspired by Elias Wegert's *Visual Complex Functions: An Introduction with Phase Portraits*
161
+ (Birkhäuser, 2012), which is the book to read if you want to understand what these pictures show.
162
+
163
+ ## License
164
+
165
+ MIT for the code; see [LICENSE](https://github.com/kuvychko/complexplorer/blob/main/LICENSE).
166
+ The gallery images and ornament designs are under
167
+ [LICENSE.art](https://github.com/kuvychko/complexplorer/blob/main/LICENSE.art).
@@ -0,0 +1,119 @@
1
+ """
2
+ Complexplorer: A Python library for visualizing complex functions.
3
+
4
+ This library provides tools to create beautiful visualizations of complex-valued
5
+ functions using various color mapping techniques and plot types.
6
+ """
7
+
8
+ # Engineering mode (namespaced: cp.ee.TransferFunction, cp.ee.bode_plot, ...)
9
+ from complexplorer import ee
10
+ from complexplorer._version import __version__
11
+
12
+ # High-level API
13
+ from complexplorer.api import PlotPresets, quick_plot
14
+ from complexplorer.core.colormap import (
15
+ AnalogousWedge,
16
+ Chessboard,
17
+ Colormap,
18
+ CubehelixPhase,
19
+ DivergingWarmCool,
20
+ EarthTopographic,
21
+ FourQuadrant,
22
+ InkPaper,
23
+ Isoluminant,
24
+ LogRings,
25
+ OklabPhase,
26
+ PerceptualPastel,
27
+ Phase,
28
+ PolarChessboard,
29
+ )
30
+
31
+ # Core functionality
32
+ from complexplorer.core.domain import Annulus, CompositeDomain, Disk, Domain, Rectangle
33
+ from complexplorer.core.functions import (
34
+ inverse_stereographic,
35
+ phase,
36
+ sawtooth,
37
+ stereographic_projection,
38
+ )
39
+ from complexplorer.core.presets import FunctionPreset, catalog
40
+ from complexplorer.core.scaling import ModulusScaling, get_scaling_preset
41
+ from complexplorer.exceptions import ColormapError, ComplexplorerError, ValidationError
42
+
43
+ # PyVista is a required core dependency as of 3.0 (the sole 3D backend; also powers STL
44
+ # export), so these imports are unconditional.
45
+ from complexplorer.export.stl import OrnamentGenerator, create_ornament
46
+ from complexplorer.gallery import generate_gallery
47
+
48
+ # Plotting functions (matplotlib)
49
+ from complexplorer.plotting.matplotlib.plot_2d import (
50
+ pair_plot,
51
+ plot,
52
+ riemann_chart,
53
+ riemann_hemispheres,
54
+ )
55
+ from complexplorer.plotting.pyvista.plot_3d import pair_plot_landscape_pv, plot_landscape_pv
56
+ from complexplorer.plotting.pyvista.riemann import riemann_pv
57
+ from complexplorer.plotting.pyvista.riemann_surface import riemann_surface_pv
58
+
59
+ # Utility functions
60
+
61
+ __all__ = [
62
+ # Version
63
+ "__version__",
64
+ # Exceptions
65
+ "ComplexplorerError",
66
+ "ValidationError",
67
+ "ColormapError",
68
+ # Core classes
69
+ "Domain",
70
+ "Rectangle",
71
+ "Disk",
72
+ "Annulus",
73
+ "CompositeDomain",
74
+ "Colormap",
75
+ "Phase",
76
+ "Chessboard",
77
+ "PolarChessboard",
78
+ "LogRings",
79
+ "OklabPhase",
80
+ "PerceptualPastel",
81
+ "AnalogousWedge",
82
+ "DivergingWarmCool",
83
+ "Isoluminant",
84
+ "CubehelixPhase",
85
+ "InkPaper",
86
+ "EarthTopographic",
87
+ "FourQuadrant",
88
+ "ModulusScaling",
89
+ "get_scaling_preset",
90
+ # Function preset registry: catalog supplies functions, PlotPresets configures renders
91
+ "catalog",
92
+ "FunctionPreset",
93
+ # Gallery generator
94
+ "generate_gallery",
95
+ # Core functions
96
+ "phase",
97
+ "sawtooth",
98
+ "stereographic_projection",
99
+ "inverse_stereographic",
100
+ # Plotting functions
101
+ "plot",
102
+ "pair_plot",
103
+ "riemann_chart",
104
+ "riemann_hemispheres",
105
+ # Utilities
106
+ # High-level API
107
+ "quick_plot",
108
+ "PlotPresets",
109
+ # Engineering mode (namespaced subpackage)
110
+ "ee",
111
+ # STL export (PyVista-backed)
112
+ "OrnamentGenerator",
113
+ "create_ornament",
114
+ # PyVista 3D plotting
115
+ "plot_landscape_pv",
116
+ "pair_plot_landscape_pv",
117
+ "riemann_pv",
118
+ "riemann_surface_pv",
119
+ ]
@@ -1,3 +1,3 @@
1
1
  """Version information for complexplorer."""
2
2
 
3
- __version__ = "2.0.0"
3
+ __version__ = "3.0.0"
@@ -0,0 +1,111 @@
1
+ """High-level API for common complexplorer workflows.
2
+
3
+ This module provides convenient functions for typical use cases.
4
+ """
5
+
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from complexplorer.core.colormap import Phase
9
+ from complexplorer.core.domain import Domain, Rectangle
10
+ from complexplorer.exceptions import ValidationError
11
+
12
+ # Import plotting functions. 2D is matplotlib; 3D/Riemann are PyVista (a required core
13
+ # dependency as of 3.0 — there is no matplotlib 3D backend).
14
+ from complexplorer.plotting.matplotlib.plot_2d import plot as plot_2d
15
+ from complexplorer.typing import ComplexFunction
16
+
17
+ if TYPE_CHECKING: # import cost stays out of the runtime path
18
+ import pyvista as pv
19
+ from matplotlib.axes import Axes
20
+
21
+
22
+ def quick_plot(
23
+ func: ComplexFunction, domain: Domain | None = None, mode: str = "2d", **kwargs
24
+ ) -> "Axes | pv.Plotter | None":
25
+ """Quick visualization of a complex function.
26
+
27
+ Parameters
28
+ ----------
29
+ func : callable
30
+ Complex function to visualize
31
+ domain : Domain, optional
32
+ Domain to plot. Defaults to Rectangle(4, 4)
33
+ mode : str
34
+ Plot mode: '2d', '3d', 'riemann'
35
+ **kwargs
36
+ Additional arguments passed to plotting function
37
+
38
+ Returns
39
+ -------
40
+ Axes or Plotter object depending on mode
41
+ """
42
+ # Remember whether the caller supplied a domain: in Riemann mode a domain masks the
43
+ # sphere, so the Rectangle(4, 4) default must NOT be forwarded (default = full sphere).
44
+ domain_supplied = domain is not None
45
+ if domain is None:
46
+ domain = Rectangle(4, 4)
47
+
48
+ if "cmap" not in kwargs:
49
+ kwargs["cmap"] = Phase(phase_sectors=6, auto_scale_r=True)
50
+
51
+ # Pop the backend selector so it never leaks into the renderer. 2D is matplotlib;
52
+ # 3D/Riemann are PyVista-only (the matplotlib 3D backend was removed in 3.0).
53
+ backend = kwargs.pop("backend", None)
54
+
55
+ if mode == "2d":
56
+ return plot_2d(domain, func, **kwargs)
57
+ if mode in ("3d", "riemann"):
58
+ if backend == "matplotlib":
59
+ raise ValidationError(
60
+ "The matplotlib 3D backend was removed in 3.0; 3D/Riemann use PyVista."
61
+ )
62
+ if mode == "3d":
63
+ from complexplorer.plotting.pyvista.plot_3d import plot_landscape_pv
64
+
65
+ return plot_landscape_pv(domain, func, **kwargs)
66
+ from complexplorer.plotting.pyvista.riemann import riemann_pv
67
+
68
+ if domain_supplied:
69
+ kwargs["domain"] = domain
70
+ return riemann_pv(func, **kwargs)
71
+ raise ValidationError(f"Unknown mode: {mode}")
72
+
73
+
74
+ # Preset configurations for common use cases
75
+ class PlotPresets:
76
+ """Named plot-configuration presets (colormap + resolution bundles).
77
+
78
+ Each preset returns a plain dict of keyword arguments to spread into a
79
+ plotting entry point, e.g. ``quick_plot(f, **PlotPresets.publication_ready())``.
80
+
81
+ ``PlotPresets`` configures a render; ``catalog`` supplies a function. The registry
82
+ ``complexplorer.catalog`` holds curated *functions* (expression, domain/colormap/scaling
83
+ specs, singularity answer keys); these are the settings you draw one with.
84
+ """
85
+
86
+ @staticmethod
87
+ def publication_ready() -> dict[str, Any]:
88
+ """Settings for publication-quality figures."""
89
+ return {
90
+ "cmap": Phase(phase_sectors=12, auto_scale_r=True, scale_radius=0.8),
91
+ "resolution": 800,
92
+ }
93
+
94
+ @staticmethod
95
+ def interactive() -> dict[str, Any]:
96
+ """Settings for interactive exploration."""
97
+ return {"cmap": Phase(phase_sectors=6, auto_scale_r=True), "resolution": 400}
98
+
99
+ @staticmethod
100
+ def high_contrast() -> dict[str, Any]:
101
+ """Settings for high contrast visualization."""
102
+ return {
103
+ "cmap": Phase(phase_sectors=16, auto_scale_r=True, scale_radius=0.5),
104
+ "resolution": 600,
105
+ }
106
+
107
+
108
+ __all__ = [
109
+ "quick_plot",
110
+ "PlotPresets",
111
+ ]
@@ -0,0 +1,6 @@
1
+ """Command-line interface for Complexplorer.
2
+
3
+ The console entry point is ``complexplorer.cli.main:main``. Import the function as
4
+ ``from complexplorer.cli.main import main`` (not re-exported here, to avoid shadowing the
5
+ ``main`` submodule with the ``main`` function).
6
+ """