figrecipe 0.6.0__py3-none-any.whl → 0.7.4__py3-none-any.whl

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 (177) hide show
  1. figrecipe/__init__.py +106 -973
  2. figrecipe/_api/__init__.py +48 -0
  3. figrecipe/_api/_extract.py +108 -0
  4. figrecipe/_api/_notebook.py +61 -0
  5. figrecipe/_api/_panel.py +46 -0
  6. figrecipe/_api/_save.py +191 -0
  7. figrecipe/_api/_seaborn_proxy.py +34 -0
  8. figrecipe/_api/_style_manager.py +153 -0
  9. figrecipe/_api/_subplots.py +333 -0
  10. figrecipe/_api/_validate.py +82 -0
  11. figrecipe/_dev/__init__.py +2 -93
  12. figrecipe/_dev/_plotters.py +76 -0
  13. figrecipe/_dev/_run_demos.py +56 -0
  14. figrecipe/_dev/demo_plotters/__init__.py +35 -166
  15. figrecipe/_dev/demo_plotters/_categories.py +81 -0
  16. figrecipe/_dev/demo_plotters/_figure_creators.py +119 -0
  17. figrecipe/_dev/demo_plotters/_helpers.py +31 -0
  18. figrecipe/_dev/demo_plotters/_registry.py +50 -0
  19. figrecipe/_dev/demo_plotters/bar_categorical/__init__.py +4 -0
  20. figrecipe/_dev/demo_plotters/contour_surface/__init__.py +4 -0
  21. figrecipe/_dev/demo_plotters/distribution/__init__.py +4 -0
  22. figrecipe/_dev/demo_plotters/image_matrix/__init__.py +4 -0
  23. figrecipe/_dev/demo_plotters/line_curve/__init__.py +4 -0
  24. figrecipe/_dev/demo_plotters/{plot_plot.py → line_curve/plot_plot.py} +3 -2
  25. figrecipe/_dev/demo_plotters/scatter_points/__init__.py +4 -0
  26. figrecipe/_dev/demo_plotters/special/__init__.py +4 -0
  27. figrecipe/_dev/demo_plotters/{plot_pie.py → special/plot_pie.py} +5 -1
  28. figrecipe/_dev/demo_plotters/spectral_signal/__init__.py +4 -0
  29. figrecipe/_dev/demo_plotters/vector_flow/__init__.py +4 -0
  30. figrecipe/_editor/__init__.py +57 -9
  31. figrecipe/_editor/_bbox/__init__.py +43 -0
  32. figrecipe/_editor/_bbox/_collections.py +177 -0
  33. figrecipe/_editor/_bbox/_elements.py +159 -0
  34. figrecipe/_editor/_bbox/_extract.py +256 -0
  35. figrecipe/_editor/_bbox/_extract_axes.py +370 -0
  36. figrecipe/_editor/_bbox/_extract_text.py +342 -0
  37. figrecipe/_editor/_bbox/_lines.py +173 -0
  38. figrecipe/_editor/_bbox/_transforms.py +146 -0
  39. figrecipe/_editor/_flask_app.py +68 -1039
  40. figrecipe/_editor/_helpers.py +242 -0
  41. figrecipe/_editor/_hitmap/__init__.py +76 -0
  42. figrecipe/_editor/_hitmap/_artists/__init__.py +21 -0
  43. figrecipe/_editor/_hitmap/_artists/_collections.py +345 -0
  44. figrecipe/_editor/_hitmap/_artists/_images.py +68 -0
  45. figrecipe/_editor/_hitmap/_artists/_lines.py +107 -0
  46. figrecipe/_editor/_hitmap/_artists/_patches.py +163 -0
  47. figrecipe/_editor/_hitmap/_artists/_text.py +190 -0
  48. figrecipe/_editor/_hitmap/_colors.py +181 -0
  49. figrecipe/_editor/_hitmap/_detect.py +137 -0
  50. figrecipe/_editor/_hitmap/_restore.py +154 -0
  51. figrecipe/_editor/_hitmap_main.py +182 -0
  52. figrecipe/_editor/_preferences.py +135 -0
  53. figrecipe/_editor/_render_overrides.py +480 -0
  54. figrecipe/_editor/_renderer.py +35 -185
  55. figrecipe/_editor/_routes_axis.py +453 -0
  56. figrecipe/_editor/_routes_core.py +284 -0
  57. figrecipe/_editor/_routes_element.py +317 -0
  58. figrecipe/_editor/_routes_style.py +223 -0
  59. figrecipe/_editor/_templates/__init__.py +78 -1
  60. figrecipe/_editor/_templates/_html.py +109 -13
  61. figrecipe/_editor/_templates/_scripts/__init__.py +120 -0
  62. figrecipe/_editor/_templates/_scripts/_api.py +228 -0
  63. figrecipe/_editor/_templates/_scripts/_colors.py +485 -0
  64. figrecipe/_editor/_templates/_scripts/_core.py +436 -0
  65. figrecipe/_editor/_templates/_scripts/_debug_snapshot.py +186 -0
  66. figrecipe/_editor/_templates/_scripts/_element_editor.py +310 -0
  67. figrecipe/_editor/_templates/_scripts/_files.py +195 -0
  68. figrecipe/_editor/_templates/_scripts/_hitmap.py +509 -0
  69. figrecipe/_editor/_templates/_scripts/_inspector.py +315 -0
  70. figrecipe/_editor/_templates/_scripts/_labels.py +464 -0
  71. figrecipe/_editor/_templates/_scripts/_legend_drag.py +265 -0
  72. figrecipe/_editor/_templates/_scripts/_modals.py +226 -0
  73. figrecipe/_editor/_templates/_scripts/_overlays.py +292 -0
  74. figrecipe/_editor/_templates/_scripts/_panel_drag.py +334 -0
  75. figrecipe/_editor/_templates/_scripts/_panel_position.py +279 -0
  76. figrecipe/_editor/_templates/_scripts/_selection.py +237 -0
  77. figrecipe/_editor/_templates/_scripts/_tabs.py +89 -0
  78. figrecipe/_editor/_templates/_scripts/_view_mode.py +107 -0
  79. figrecipe/_editor/_templates/_scripts/_zoom.py +179 -0
  80. figrecipe/_editor/_templates/_styles/__init__.py +69 -0
  81. figrecipe/_editor/_templates/_styles/_base.py +64 -0
  82. figrecipe/_editor/_templates/_styles/_buttons.py +206 -0
  83. figrecipe/_editor/_templates/_styles/_color_input.py +123 -0
  84. figrecipe/_editor/_templates/_styles/_controls.py +265 -0
  85. figrecipe/_editor/_templates/_styles/_dynamic_props.py +144 -0
  86. figrecipe/_editor/_templates/_styles/_forms.py +126 -0
  87. figrecipe/_editor/_templates/_styles/_hitmap.py +184 -0
  88. figrecipe/_editor/_templates/_styles/_inspector.py +90 -0
  89. figrecipe/_editor/_templates/_styles/_labels.py +118 -0
  90. figrecipe/_editor/_templates/_styles/_modals.py +98 -0
  91. figrecipe/_editor/_templates/_styles/_overlays.py +130 -0
  92. figrecipe/_editor/_templates/_styles/_preview.py +225 -0
  93. figrecipe/_editor/_templates/_styles/_selection.py +73 -0
  94. figrecipe/_params/_DECORATION_METHODS.py +6 -0
  95. figrecipe/_recorder.py +35 -106
  96. figrecipe/_recorder_utils.py +124 -0
  97. figrecipe/_reproducer/__init__.py +18 -0
  98. figrecipe/_reproducer/_core.py +498 -0
  99. figrecipe/_reproducer/_custom_plots.py +279 -0
  100. figrecipe/_reproducer/_seaborn.py +100 -0
  101. figrecipe/_reproducer/_violin.py +186 -0
  102. figrecipe/_signatures/_kwargs.py +273 -0
  103. figrecipe/_signatures/_loader.py +21 -423
  104. figrecipe/_signatures/_parsing.py +147 -0
  105. figrecipe/_wrappers/_axes.py +119 -910
  106. figrecipe/_wrappers/_axes_helpers.py +136 -0
  107. figrecipe/_wrappers/_axes_plots.py +418 -0
  108. figrecipe/_wrappers/_axes_seaborn.py +157 -0
  109. figrecipe/_wrappers/_figure.py +162 -0
  110. figrecipe/_wrappers/_panel_labels.py +127 -0
  111. figrecipe/_wrappers/_plot_helpers.py +143 -0
  112. figrecipe/_wrappers/_violin_helpers.py +180 -0
  113. figrecipe/styles/__init__.py +8 -6
  114. figrecipe/styles/_dotdict.py +72 -0
  115. figrecipe/styles/_finalize.py +134 -0
  116. figrecipe/styles/_fonts.py +77 -0
  117. figrecipe/styles/_kwargs_converter.py +178 -0
  118. figrecipe/styles/_plot_styles.py +209 -0
  119. figrecipe/styles/_style_applier.py +32 -478
  120. figrecipe/styles/_style_loader.py +16 -192
  121. figrecipe/styles/_themes.py +151 -0
  122. figrecipe/styles/presets/MATPLOTLIB.yaml +2 -1
  123. figrecipe/styles/presets/SCITEX.yaml +29 -24
  124. {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/METADATA +37 -2
  125. figrecipe-0.7.4.dist-info/RECORD +188 -0
  126. figrecipe/_editor/_bbox.py +0 -978
  127. figrecipe/_editor/_hitmap.py +0 -937
  128. figrecipe/_editor/_templates/_scripts.py +0 -2778
  129. figrecipe/_editor/_templates/_styles.py +0 -1326
  130. figrecipe/_reproducer.py +0 -975
  131. figrecipe-0.6.0.dist-info/RECORD +0 -90
  132. /figrecipe/_dev/demo_plotters/{plot_bar.py → bar_categorical/plot_bar.py} +0 -0
  133. /figrecipe/_dev/demo_plotters/{plot_barh.py → bar_categorical/plot_barh.py} +0 -0
  134. /figrecipe/_dev/demo_plotters/{plot_contour.py → contour_surface/plot_contour.py} +0 -0
  135. /figrecipe/_dev/demo_plotters/{plot_contourf.py → contour_surface/plot_contourf.py} +0 -0
  136. /figrecipe/_dev/demo_plotters/{plot_tricontour.py → contour_surface/plot_tricontour.py} +0 -0
  137. /figrecipe/_dev/demo_plotters/{plot_tricontourf.py → contour_surface/plot_tricontourf.py} +0 -0
  138. /figrecipe/_dev/demo_plotters/{plot_tripcolor.py → contour_surface/plot_tripcolor.py} +0 -0
  139. /figrecipe/_dev/demo_plotters/{plot_triplot.py → contour_surface/plot_triplot.py} +0 -0
  140. /figrecipe/_dev/demo_plotters/{plot_boxplot.py → distribution/plot_boxplot.py} +0 -0
  141. /figrecipe/_dev/demo_plotters/{plot_ecdf.py → distribution/plot_ecdf.py} +0 -0
  142. /figrecipe/_dev/demo_plotters/{plot_hist.py → distribution/plot_hist.py} +0 -0
  143. /figrecipe/_dev/demo_plotters/{plot_hist2d.py → distribution/plot_hist2d.py} +0 -0
  144. /figrecipe/_dev/demo_plotters/{plot_violinplot.py → distribution/plot_violinplot.py} +0 -0
  145. /figrecipe/_dev/demo_plotters/{plot_hexbin.py → image_matrix/plot_hexbin.py} +0 -0
  146. /figrecipe/_dev/demo_plotters/{plot_imshow.py → image_matrix/plot_imshow.py} +0 -0
  147. /figrecipe/_dev/demo_plotters/{plot_matshow.py → image_matrix/plot_matshow.py} +0 -0
  148. /figrecipe/_dev/demo_plotters/{plot_pcolor.py → image_matrix/plot_pcolor.py} +0 -0
  149. /figrecipe/_dev/demo_plotters/{plot_pcolormesh.py → image_matrix/plot_pcolormesh.py} +0 -0
  150. /figrecipe/_dev/demo_plotters/{plot_spy.py → image_matrix/plot_spy.py} +0 -0
  151. /figrecipe/_dev/demo_plotters/{plot_errorbar.py → line_curve/plot_errorbar.py} +0 -0
  152. /figrecipe/_dev/demo_plotters/{plot_fill.py → line_curve/plot_fill.py} +0 -0
  153. /figrecipe/_dev/demo_plotters/{plot_fill_between.py → line_curve/plot_fill_between.py} +0 -0
  154. /figrecipe/_dev/demo_plotters/{plot_fill_betweenx.py → line_curve/plot_fill_betweenx.py} +0 -0
  155. /figrecipe/_dev/demo_plotters/{plot_stackplot.py → line_curve/plot_stackplot.py} +0 -0
  156. /figrecipe/_dev/demo_plotters/{plot_stairs.py → line_curve/plot_stairs.py} +0 -0
  157. /figrecipe/_dev/demo_plotters/{plot_step.py → line_curve/plot_step.py} +0 -0
  158. /figrecipe/_dev/demo_plotters/{plot_scatter.py → scatter_points/plot_scatter.py} +0 -0
  159. /figrecipe/_dev/demo_plotters/{plot_eventplot.py → special/plot_eventplot.py} +0 -0
  160. /figrecipe/_dev/demo_plotters/{plot_loglog.py → special/plot_loglog.py} +0 -0
  161. /figrecipe/_dev/demo_plotters/{plot_semilogx.py → special/plot_semilogx.py} +0 -0
  162. /figrecipe/_dev/demo_plotters/{plot_semilogy.py → special/plot_semilogy.py} +0 -0
  163. /figrecipe/_dev/demo_plotters/{plot_stem.py → special/plot_stem.py} +0 -0
  164. /figrecipe/_dev/demo_plotters/{plot_acorr.py → spectral_signal/plot_acorr.py} +0 -0
  165. /figrecipe/_dev/demo_plotters/{plot_angle_spectrum.py → spectral_signal/plot_angle_spectrum.py} +0 -0
  166. /figrecipe/_dev/demo_plotters/{plot_cohere.py → spectral_signal/plot_cohere.py} +0 -0
  167. /figrecipe/_dev/demo_plotters/{plot_csd.py → spectral_signal/plot_csd.py} +0 -0
  168. /figrecipe/_dev/demo_plotters/{plot_magnitude_spectrum.py → spectral_signal/plot_magnitude_spectrum.py} +0 -0
  169. /figrecipe/_dev/demo_plotters/{plot_phase_spectrum.py → spectral_signal/plot_phase_spectrum.py} +0 -0
  170. /figrecipe/_dev/demo_plotters/{plot_psd.py → spectral_signal/plot_psd.py} +0 -0
  171. /figrecipe/_dev/demo_plotters/{plot_specgram.py → spectral_signal/plot_specgram.py} +0 -0
  172. /figrecipe/_dev/demo_plotters/{plot_xcorr.py → spectral_signal/plot_xcorr.py} +0 -0
  173. /figrecipe/_dev/demo_plotters/{plot_barbs.py → vector_flow/plot_barbs.py} +0 -0
  174. /figrecipe/_dev/demo_plotters/{plot_quiver.py → vector_flow/plot_quiver.py} +0 -0
  175. /figrecipe/_dev/demo_plotters/{plot_streamplot.py → vector_flow/plot_streamplot.py} +0 -0
  176. {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/WHEEL +0 -0
  177. {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/licenses/LICENSE +0 -0
@@ -9,6 +9,11 @@ Usage
9
9
  >>> # List all available plot types
10
10
  >>> print(list(REGISTRY.keys()))
11
11
  >>>
12
+ >>> # List by category
13
+ >>> from figrecipe._dev.demo_plotters import CATEGORIES
14
+ >>> for cat, plots in CATEGORIES.items():
15
+ ... print(f"{cat}: {plots}")
16
+ >>>
12
17
  >>> # Create a single figure with all plot types
13
18
  >>> fig, axes = create_all_plots_figure(fr)
14
19
  >>>
@@ -16,180 +21,44 @@ Usage
16
21
  >>> fig, ax = REGISTRY['plot'](fr, rng, ax)
17
22
  """
18
23
 
19
- import importlib
20
- import math
21
- from pathlib import Path
22
- from typing import Callable, Dict, Optional, Tuple
23
-
24
- import numpy as np
25
-
26
- # Registry: plot_name -> plotter function
27
- REGISTRY: Dict[str, Callable] = {}
28
-
29
- # Get all plot_*.py files in this directory
30
- _demo_dir = Path(__file__).parent
31
- _demo_files = sorted(_demo_dir.glob("plot_*.py"))
32
-
33
- # Import all demo functions into registry
34
- for _file in _demo_files:
35
- _module_name = _file.stem # e.g., plot_scatter
36
- _plot_name = _module_name.replace("plot_", "") # e.g., scatter
37
- _func_name = _module_name # e.g., plot_scatter
38
- try:
39
- _module = importlib.import_module(f".{_module_name}", package=__name__)
40
- if hasattr(_module, _func_name):
41
- REGISTRY[_plot_name] = getattr(_module, _func_name)
42
- except ImportError as e:
43
- print(f"Warning: Could not import {_module_name}: {e}")
44
-
45
-
46
- def list_plots():
47
- """List all available plot types.
48
-
49
- Returns
50
- -------
51
- list of str
52
- Names of available plot types.
53
- """
54
- return list(REGISTRY.keys())
55
-
56
-
57
- def get_plotter(name: str) -> Optional[Callable]:
58
- """Get plotter function by name.
59
-
60
- Parameters
61
- ----------
62
- name : str
63
- Plot type name (e.g., 'scatter', 'bar', 'hist').
64
-
65
- Returns
66
- -------
67
- callable or None
68
- Plotter function or None if not found.
69
- """
70
- return REGISTRY.get(name)
71
-
72
-
73
- def create_all_plots_figure(plt, seed: int = 42) -> Tuple:
74
- """Create a single figure with all supported plot types.
75
-
76
- Parameters
77
- ----------
78
- plt : module
79
- figrecipe module (import figrecipe as fr).
80
- seed : int
81
- Random seed for reproducibility.
82
-
83
- Returns
84
- -------
85
- tuple
86
- (fig, axes) where fig is RecordingFigure and axes is array of RecordingAxes.
87
- """
88
- rng = np.random.default_rng(seed)
89
-
90
- n_plots = len(REGISTRY)
91
- ncols = 7
92
- nrows = math.ceil(n_plots / ncols)
93
-
94
- fig, axes = plt.subplots(nrows=nrows, ncols=ncols)
95
- axes_flat = axes.flatten()
96
-
97
- results = {}
98
- for i, (name, plotter) in enumerate(REGISTRY.items()):
99
- ax = axes_flat[i]
100
- try:
101
- plotter(plt, rng, ax=ax)
102
- results[name] = {"success": True, "error": None}
103
- except Exception as e:
104
- ax.set_title(f"{name}\n(ERROR)")
105
- ax.text(
106
- 0.5,
107
- 0.5,
108
- str(e)[:50],
109
- ha="center",
110
- va="center",
111
- transform=ax.transAxes,
112
- fontsize=6,
113
- wrap=True,
114
- )
115
- results[name] = {"success": False, "error": str(e)}
116
-
117
- # Hide unused axes
118
- for i in range(n_plots, len(axes_flat)):
119
- axes_flat[i].set_visible(False)
120
-
121
- # Add panel labels
122
- panel_labels = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" * 2 # 52 labels
123
- for i, ax in enumerate(axes_flat[:n_plots]):
124
- if i < len(panel_labels):
125
- plt.panel_label(ax, panel_labels[i])
126
-
127
- fig.suptitle(f"All {n_plots} Supported Plot Types")
128
-
129
- return fig, axes, results
130
-
131
-
132
- def run_individual_demos(plt, output_dir=None, show=False):
133
- """Run all demo plotters individually and optionally save outputs.
134
-
135
- Parameters
136
- ----------
137
- plt : module
138
- figrecipe or matplotlib.pyplot module.
139
- output_dir : Path, optional
140
- Directory to save output images.
141
- show : bool
142
- Whether to show figures.
143
-
144
- Returns
145
- -------
146
- dict
147
- Results for each demo: {name: {'success': bool, 'error': str or None}}
148
- """
149
- rng = np.random.default_rng(42)
150
- results = {}
151
-
152
- if output_dir:
153
- output_dir = Path(output_dir)
154
- output_dir.mkdir(parents=True, exist_ok=True)
155
-
156
- for name, plotter in REGISTRY.items():
157
- try:
158
- fig, ax = plotter(plt, rng)
159
- if output_dir:
160
- out_path = output_dir / f"plot_{name}.png"
161
- if hasattr(fig, "fig"):
162
- fig.fig.savefig(out_path, dpi=100, bbox_inches="tight")
163
- else:
164
- fig.savefig(out_path, dpi=100, bbox_inches="tight")
165
- if show:
166
- plt.show()
167
- else:
168
- if hasattr(fig, "fig"):
169
- plt.close(fig.fig)
170
- else:
171
- plt.close(fig)
172
- results[name] = {"success": True, "error": None}
173
- except Exception as e:
174
- results[name] = {"success": False, "error": str(e)}
175
-
176
- return results
177
-
24
+ from ._categories import CATEGORIES, CATEGORY_DISPLAY_NAMES, REPRESENTATIVES
25
+ from ._figure_creators import (
26
+ create_all_plots_figure,
27
+ run_all_demos,
28
+ run_individual_demos,
29
+ )
30
+ from ._helpers import (
31
+ get_plotter,
32
+ get_representative_plots,
33
+ list_plots,
34
+ list_plots_by_category,
35
+ )
36
+ from ._registry import REGISTRY
178
37
 
179
38
  # Legacy exports for backwards compatibility
180
- __all__ = [f"plot_{name}" for name in REGISTRY.keys()]
39
+ __all__ = [
40
+ "REGISTRY",
41
+ "CATEGORIES",
42
+ "CATEGORY_DISPLAY_NAMES",
43
+ "REPRESENTATIVES",
44
+ "list_plots",
45
+ "list_plots_by_category",
46
+ "get_representative_plots",
47
+ "get_plotter",
48
+ "create_all_plots_figure",
49
+ "run_individual_demos",
50
+ "run_all_demos",
51
+ ]
52
+
53
+ # Legacy: export plot_* functions to globals
181
54
  for _name, _func in REGISTRY.items():
182
55
  globals()[f"plot_{_name}"] = _func
56
+ __all__.append(f"plot_{_name}")
183
57
 
184
58
 
185
59
  def list_demos():
186
60
  """Legacy: List all available demo functions."""
187
- return __all__
188
-
189
-
190
- def run_all_demos(plt, output_dir=None, show=False):
191
- """Legacy: Alias for run_individual_demos."""
192
- return run_individual_demos(plt, output_dir, show)
61
+ return [f"plot_{name}" for name in REGISTRY.keys()]
193
62
 
194
63
 
195
64
  # EOF
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Plot categories for organized display."""
4
+
5
+ from typing import Dict, List
6
+
7
+ from ._registry import REGISTRY
8
+
9
+ # Plot categories for organized display
10
+ # Keys match directory names in demo_plotters/
11
+ _CATEGORIES_RAW: Dict[str, List[str]] = {
12
+ "line_curve": [
13
+ "plot",
14
+ "step",
15
+ "fill",
16
+ "fill_between",
17
+ "fill_betweenx",
18
+ "errorbar",
19
+ "stackplot",
20
+ "stairs",
21
+ ],
22
+ "scatter_points": ["scatter"],
23
+ "bar_categorical": ["bar", "barh"],
24
+ "distribution": ["hist", "hist2d", "boxplot", "violinplot", "ecdf"],
25
+ "image_matrix": ["imshow", "matshow", "pcolor", "pcolormesh", "hexbin", "spy"],
26
+ "contour_surface": [
27
+ "contour",
28
+ "contourf",
29
+ "tricontour",
30
+ "tricontourf",
31
+ "tripcolor",
32
+ "triplot",
33
+ ],
34
+ "spectral_signal": [
35
+ "specgram",
36
+ "psd",
37
+ "csd",
38
+ "cohere",
39
+ "angle_spectrum",
40
+ "magnitude_spectrum",
41
+ "phase_spectrum",
42
+ "acorr",
43
+ "xcorr",
44
+ ],
45
+ "vector_flow": ["quiver", "barbs", "streamplot"],
46
+ "special": ["pie", "stem", "eventplot", "loglog", "semilogx", "semilogy"],
47
+ }
48
+
49
+ # Display names for categories (for UI/documentation)
50
+ CATEGORY_DISPLAY_NAMES: Dict[str, str] = {
51
+ "line_curve": "Line & Curve",
52
+ "scatter_points": "Scatter & Points",
53
+ "bar_categorical": "Bar & Categorical",
54
+ "distribution": "Distribution",
55
+ "image_matrix": "2D Image & Matrix",
56
+ "contour_surface": "Contour & Surface",
57
+ "spectral_signal": "Spectral & Signal",
58
+ "vector_flow": "Vector & Flow",
59
+ "special": "Special",
60
+ }
61
+
62
+ # Filter categories to only include available plots
63
+ CATEGORIES: Dict[str, List[str]] = {
64
+ cat: [p for p in plots if p in REGISTRY] for cat, plots in _CATEGORIES_RAW.items()
65
+ }
66
+ CATEGORIES = {cat: plots for cat, plots in CATEGORIES.items() if plots}
67
+
68
+ # Representative plots (one per category)
69
+ REPRESENTATIVES: Dict[str, str] = {
70
+ "line_curve": "plot",
71
+ "scatter_points": "scatter",
72
+ "bar_categorical": "bar",
73
+ "distribution": "hist",
74
+ "image_matrix": "imshow",
75
+ "contour_surface": "contourf",
76
+ "spectral_signal": "specgram",
77
+ "vector_flow": "quiver",
78
+ "special": "pie",
79
+ }
80
+
81
+ # EOF
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Figure creation functions for demo plotters."""
4
+
5
+ import math
6
+ from pathlib import Path
7
+ from typing import Dict, Tuple
8
+
9
+ import numpy as np
10
+
11
+ from ._registry import REGISTRY
12
+
13
+
14
+ def create_all_plots_figure(plt, seed: int = 42) -> Tuple:
15
+ """Create a single figure with all supported plot types.
16
+
17
+ Parameters
18
+ ----------
19
+ plt : module
20
+ figrecipe module (import figrecipe as fr).
21
+ seed : int
22
+ Random seed for reproducibility.
23
+
24
+ Returns
25
+ -------
26
+ tuple
27
+ (fig, axes, results) where results maps name to success/error.
28
+ """
29
+ rng = np.random.default_rng(seed)
30
+
31
+ n_plots = len(REGISTRY)
32
+ ncols = 7
33
+ nrows = math.ceil(n_plots / ncols)
34
+
35
+ fig, axes = plt.subplots(nrows=nrows, ncols=ncols)
36
+ axes_flat = axes.flatten()
37
+
38
+ results = {}
39
+ for i, (name, plotter) in enumerate(REGISTRY.items()):
40
+ ax = axes_flat[i]
41
+ try:
42
+ plotter(plt, rng, ax=ax)
43
+ results[name] = {"success": True, "error": None}
44
+ except Exception as e:
45
+ ax.set_title(f"{name}\n(ERROR)")
46
+ ax.text(
47
+ 0.5,
48
+ 0.5,
49
+ str(e)[:50],
50
+ ha="center",
51
+ va="center",
52
+ transform=ax.transAxes,
53
+ fontsize=6,
54
+ wrap=True,
55
+ )
56
+ results[name] = {"success": False, "error": str(e)}
57
+
58
+ # Hide unused axes
59
+ for i in range(n_plots, len(axes_flat)):
60
+ axes_flat[i].set_visible(False)
61
+
62
+ # Add panel labels
63
+ panel_labels = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" * 2
64
+ for i, ax in enumerate(axes_flat[:n_plots]):
65
+ if i < len(panel_labels):
66
+ plt.panel_label(ax, panel_labels[i])
67
+
68
+ fig.suptitle(f"All {n_plots} Supported Plot Types")
69
+
70
+ return fig, axes, results
71
+
72
+
73
+ def run_individual_demos(plt, output_dir=None, show=False) -> Dict:
74
+ """Run all demo plotters individually and optionally save outputs.
75
+
76
+ Parameters
77
+ ----------
78
+ plt : module
79
+ figrecipe or matplotlib.pyplot module.
80
+ output_dir : Path, optional
81
+ Directory to save output images.
82
+ show : bool
83
+ Whether to show figures.
84
+
85
+ Returns
86
+ -------
87
+ dict
88
+ Results for each demo: {name: {'success': bool, 'error': str or None}}
89
+ """
90
+ rng = np.random.default_rng(42)
91
+ results = {}
92
+
93
+ if output_dir:
94
+ output_dir = Path(output_dir)
95
+ output_dir.mkdir(parents=True, exist_ok=True)
96
+
97
+ for name, plotter in REGISTRY.items():
98
+ try:
99
+ fig, ax = plotter(plt, rng)
100
+ if output_dir:
101
+ out_path = output_dir / f"plot_{name}.png"
102
+ mpl_fig = fig.fig if hasattr(fig, "fig") else fig
103
+ mpl_fig.savefig(out_path, dpi=100, bbox_inches="tight")
104
+ if show:
105
+ plt.show()
106
+ else:
107
+ mpl_fig = fig.fig if hasattr(fig, "fig") else fig
108
+ plt.close(mpl_fig)
109
+ results[name] = {"success": True, "error": None}
110
+ except Exception as e:
111
+ results[name] = {"success": False, "error": str(e)}
112
+
113
+ return results
114
+
115
+
116
+ # Legacy alias
117
+ run_all_demos = run_individual_demos
118
+
119
+ # EOF
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Helper functions for demo plotters."""
4
+
5
+ from typing import Callable, Dict, List, Optional
6
+
7
+ from ._categories import CATEGORIES, REPRESENTATIVES
8
+ from ._registry import REGISTRY
9
+
10
+
11
+ def list_plots() -> List[str]:
12
+ """List all available plot types."""
13
+ return list(REGISTRY.keys())
14
+
15
+
16
+ def list_plots_by_category() -> Dict[str, List[str]]:
17
+ """List all available plot types organized by category."""
18
+ return CATEGORIES.copy()
19
+
20
+
21
+ def get_representative_plots() -> List[str]:
22
+ """Get one representative plot from each category for demos."""
23
+ return [p for p in REPRESENTATIVES.values() if p in REGISTRY]
24
+
25
+
26
+ def get_plotter(name: str) -> Optional[Callable]:
27
+ """Get plotter function by name."""
28
+ return REGISTRY.get(name)
29
+
30
+
31
+ # EOF
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Registry for demo plotter functions."""
4
+
5
+ import importlib
6
+ from pathlib import Path
7
+ from typing import Callable, Dict
8
+
9
+ # Registry: plot_name -> plotter function
10
+ REGISTRY: Dict[str, Callable] = {}
11
+
12
+ # Get demo directory
13
+ _demo_dir = Path(__file__).parent
14
+
15
+ # Category subdirectories
16
+ _category_dirs = [
17
+ "line_curve",
18
+ "scatter_points",
19
+ "bar_categorical",
20
+ "distribution",
21
+ "image_matrix",
22
+ "contour_surface",
23
+ "spectral_signal",
24
+ "vector_flow",
25
+ "special",
26
+ ]
27
+
28
+ # Collect all plot_*.py files from subdirectories
29
+ _demo_files = []
30
+ for _cat_dir in _category_dirs:
31
+ _cat_path = _demo_dir / _cat_dir
32
+ if _cat_path.is_dir():
33
+ _demo_files.extend(sorted(_cat_path.glob("plot_*.py")))
34
+
35
+ # Import all demo functions into registry
36
+ for _file in _demo_files:
37
+ _module_name = _file.stem # e.g., plot_scatter
38
+ _plot_name = _module_name.replace("plot_", "") # e.g., scatter
39
+ _func_name = _module_name # e.g., plot_scatter
40
+ _cat_dir = _file.parent.name # e.g., scatter_points
41
+ try:
42
+ _module = importlib.import_module(
43
+ f".{_cat_dir}.{_module_name}", package=__name__.rsplit(".", 1)[0]
44
+ )
45
+ if hasattr(_module, _func_name):
46
+ REGISTRY[_plot_name] = getattr(_module, _func_name)
47
+ except ImportError as e:
48
+ print(f"Warning: Could not import {_cat_dir}.{_module_name}: {e}")
49
+
50
+ # EOF
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Demo plotters for bar_categorical category."""
4
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Demo plotters for contour_surface category."""
4
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Demo plotters for distribution category."""
4
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Demo plotters for image_matrix category."""
4
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Demo plotters for line_curve category."""
4
+
@@ -16,8 +16,9 @@ def plot_plot(plt, rng, ax=None):
16
16
  fig = ax.get_figure() if hasattr(ax, "get_figure") else ax.fig
17
17
 
18
18
  x = np.linspace(0, 2 * np.pi, 100)
19
- ax.plot(x, np.sin(x), id="sin")
20
- ax.plot(x, np.cos(x), id="cos")
19
+ ax.plot(x, np.sin(x), label="sin", id="sin")
20
+ ax.plot(x, np.cos(x), label="cos", id="cos")
21
+ ax.legend()
21
22
  ax.set_xlabel("X")
22
23
  ax.set_ylabel("Y")
23
24
  ax.set_title("plot")
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Demo plotters for scatter_points category."""
4
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Demo plotters for special category."""
4
+
@@ -2,6 +2,8 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  """pie: pie chart demo."""
4
4
 
5
+ import matplotlib.pyplot as mpl_plt
6
+
5
7
 
6
8
  def plot_pie(plt, rng, ax=None):
7
9
  """Pie chart demo.
@@ -15,7 +17,9 @@ def plot_pie(plt, rng, ax=None):
15
17
 
16
18
  sizes = [35, 25, 20, 20]
17
19
  labels = ["A", "B", "C", "D"]
18
- ax.pie(sizes, labels=labels, id="pie")
20
+ # Get colors from current style's color cycle
21
+ colors = mpl_plt.rcParams["axes.prop_cycle"].by_key()["color"][:4]
22
+ ax.pie(sizes, labels=labels, colors=colors, id="pie")
19
23
  ax.set_title("pie")
20
24
  return fig, ax
21
25
 
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Demo plotters for spectral_signal category."""
4
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Demo plotters for vector_flow category."""
4
+