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
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Main bounding box extraction function.
5
+
6
+ This module contains the main extract_bboxes function that coordinates
7
+ all element-specific bbox extractors.
8
+ """
9
+
10
+ from typing import Any, Dict
11
+
12
+ from matplotlib.figure import Figure
13
+ from matplotlib.transforms import Bbox
14
+
15
+ from ._elements import get_element_bbox
16
+ from ._extract_axes import (
17
+ extract_collections,
18
+ extract_images,
19
+ extract_lines,
20
+ extract_patches,
21
+ )
22
+ from ._extract_text import (
23
+ extract_figure_text,
24
+ extract_legend,
25
+ extract_spines,
26
+ extract_text_elements,
27
+ )
28
+
29
+
30
+ def extract_bboxes(
31
+ fig: Figure,
32
+ img_width: int,
33
+ img_height: int,
34
+ include_points: bool = True,
35
+ ) -> Dict[str, Dict[str, Any]]:
36
+ """
37
+ Extract bounding boxes for all figure elements.
38
+
39
+ Parameters
40
+ ----------
41
+ fig : matplotlib.figure.Figure
42
+ Figure to extract bboxes from.
43
+ img_width : int
44
+ Width of the output image in pixels.
45
+ img_height : int
46
+ Height of the output image in pixels.
47
+ include_points : bool, optional
48
+ Whether to include point arrays for lines/scatter (default: True).
49
+
50
+ Returns
51
+ -------
52
+ dict
53
+ Mapping from element key to bbox info.
54
+ """
55
+ bboxes = {}
56
+
57
+ # Get renderer for bbox calculations
58
+ fig.canvas.draw()
59
+ renderer = fig.canvas.get_renderer()
60
+
61
+ # Get tight bbox for coordinate transformation
62
+ tight_bbox = fig.get_tightbbox(renderer)
63
+ if tight_bbox is None:
64
+ tight_bbox = Bbox.from_bounds(0, 0, fig.get_figwidth(), fig.get_figheight())
65
+
66
+ # bbox_inches='tight' adds pad_inches (default 0.1) around the tight bbox
67
+ pad_inches = 0.1
68
+ saved_width_inches = tight_bbox.width + 2 * pad_inches
69
+ saved_height_inches = tight_bbox.height + 2 * pad_inches
70
+
71
+ # Calculate scale factors from saved image size to pixel size
72
+ scale_x = img_width / saved_width_inches if saved_width_inches > 0 else 1
73
+ scale_y = img_height / saved_height_inches if saved_height_inches > 0 else 1
74
+
75
+ # Process each axes
76
+ for ax_idx, ax in enumerate(fig.get_axes()):
77
+ _extract_axes_bboxes(
78
+ ax,
79
+ ax_idx,
80
+ fig,
81
+ renderer,
82
+ tight_bbox,
83
+ img_width,
84
+ img_height,
85
+ scale_x,
86
+ scale_y,
87
+ pad_inches,
88
+ saved_height_inches,
89
+ include_points,
90
+ bboxes,
91
+ )
92
+
93
+ # Process figure-level text elements
94
+ extract_figure_text(
95
+ fig,
96
+ renderer,
97
+ tight_bbox,
98
+ img_width,
99
+ img_height,
100
+ scale_x,
101
+ scale_y,
102
+ pad_inches,
103
+ saved_height_inches,
104
+ bboxes,
105
+ )
106
+
107
+ # Add metadata
108
+ bboxes["_meta"] = {
109
+ "img_width": img_width,
110
+ "img_height": img_height,
111
+ "fig_width_inches": fig.get_figwidth(),
112
+ "fig_height_inches": fig.get_figheight(),
113
+ "dpi": fig.dpi,
114
+ }
115
+
116
+ return bboxes
117
+
118
+
119
+ def _extract_axes_bboxes(
120
+ ax,
121
+ ax_idx,
122
+ fig,
123
+ renderer,
124
+ tight_bbox,
125
+ img_width,
126
+ img_height,
127
+ scale_x,
128
+ scale_y,
129
+ pad_inches,
130
+ saved_height_inches,
131
+ include_points,
132
+ bboxes,
133
+ ):
134
+ """Extract bboxes for all elements within an axes."""
135
+ # Axes bounding box
136
+ ax_bbox = get_element_bbox(
137
+ ax,
138
+ fig,
139
+ renderer,
140
+ tight_bbox,
141
+ img_width,
142
+ img_height,
143
+ scale_x,
144
+ scale_y,
145
+ pad_inches,
146
+ saved_height_inches,
147
+ )
148
+ if ax_bbox:
149
+ bboxes[f"ax{ax_idx}_axes"] = {**ax_bbox, "type": "axes", "ax_index": ax_idx}
150
+
151
+ # Extract all element types
152
+ extract_lines(
153
+ ax,
154
+ ax_idx,
155
+ fig,
156
+ renderer,
157
+ tight_bbox,
158
+ img_width,
159
+ img_height,
160
+ scale_x,
161
+ scale_y,
162
+ pad_inches,
163
+ saved_height_inches,
164
+ include_points,
165
+ bboxes,
166
+ )
167
+ extract_collections(
168
+ ax,
169
+ ax_idx,
170
+ fig,
171
+ renderer,
172
+ tight_bbox,
173
+ img_width,
174
+ img_height,
175
+ scale_x,
176
+ scale_y,
177
+ pad_inches,
178
+ saved_height_inches,
179
+ include_points,
180
+ bboxes,
181
+ )
182
+ extract_patches(
183
+ ax,
184
+ ax_idx,
185
+ fig,
186
+ renderer,
187
+ tight_bbox,
188
+ img_width,
189
+ img_height,
190
+ scale_x,
191
+ scale_y,
192
+ pad_inches,
193
+ saved_height_inches,
194
+ bboxes,
195
+ )
196
+ extract_images(
197
+ ax,
198
+ ax_idx,
199
+ fig,
200
+ renderer,
201
+ tight_bbox,
202
+ img_width,
203
+ img_height,
204
+ scale_x,
205
+ scale_y,
206
+ pad_inches,
207
+ saved_height_inches,
208
+ bboxes,
209
+ )
210
+ extract_text_elements(
211
+ ax,
212
+ ax_idx,
213
+ fig,
214
+ renderer,
215
+ tight_bbox,
216
+ img_width,
217
+ img_height,
218
+ scale_x,
219
+ scale_y,
220
+ pad_inches,
221
+ saved_height_inches,
222
+ bboxes,
223
+ )
224
+ extract_legend(
225
+ ax,
226
+ ax_idx,
227
+ fig,
228
+ renderer,
229
+ tight_bbox,
230
+ img_width,
231
+ img_height,
232
+ scale_x,
233
+ scale_y,
234
+ pad_inches,
235
+ saved_height_inches,
236
+ bboxes,
237
+ )
238
+ extract_spines(
239
+ ax,
240
+ ax_idx,
241
+ fig,
242
+ renderer,
243
+ tight_bbox,
244
+ img_width,
245
+ img_height,
246
+ scale_x,
247
+ scale_y,
248
+ pad_inches,
249
+ saved_height_inches,
250
+ bboxes,
251
+ )
252
+
253
+
254
+ __all__ = ["extract_bboxes"]
255
+
256
+ # EOF
@@ -0,0 +1,370 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Axes element bbox extraction (lines, collections, patches, images)."""
4
+
5
+ from matplotlib.collections import PathCollection, PolyCollection
6
+ from matplotlib.patches import Rectangle
7
+
8
+ from ._collections import get_collection_bbox, get_patch_bbox
9
+ from ._elements import get_element_bbox
10
+ from ._lines import get_line_bbox, get_quiver_bbox
11
+
12
+
13
+ def extract_lines(
14
+ ax,
15
+ ax_idx,
16
+ fig,
17
+ renderer,
18
+ tight_bbox,
19
+ img_width,
20
+ img_height,
21
+ scale_x,
22
+ scale_y,
23
+ pad_inches,
24
+ saved_height_inches,
25
+ include_points,
26
+ bboxes,
27
+ ):
28
+ """Extract bboxes for line elements."""
29
+ for i, line in enumerate(ax.get_lines()):
30
+ if not line.get_visible():
31
+ continue
32
+ xdata = line.get_xdata()
33
+ ydata = line.get_ydata()
34
+ if len(xdata) == 0 or len(ydata) == 0:
35
+ continue
36
+ if line.axes != ax:
37
+ continue
38
+
39
+ bbox = get_line_bbox(
40
+ line,
41
+ ax,
42
+ fig,
43
+ renderer,
44
+ tight_bbox,
45
+ img_width,
46
+ img_height,
47
+ scale_x,
48
+ scale_y,
49
+ pad_inches,
50
+ saved_height_inches,
51
+ include_points,
52
+ )
53
+ if bbox:
54
+ bboxes[f"ax{ax_idx}_line{i}"] = {
55
+ **bbox,
56
+ "type": "line",
57
+ "label": line.get_label() or f"line_{i}",
58
+ "ax_index": ax_idx,
59
+ }
60
+
61
+
62
+ def extract_collections(
63
+ ax,
64
+ ax_idx,
65
+ fig,
66
+ renderer,
67
+ tight_bbox,
68
+ img_width,
69
+ img_height,
70
+ scale_x,
71
+ scale_y,
72
+ pad_inches,
73
+ saved_height_inches,
74
+ include_points,
75
+ bboxes,
76
+ ):
77
+ """Extract bboxes for collection elements (scatter, fill, quiver, etc.)."""
78
+ from matplotlib.collections import LineCollection, QuadMesh
79
+ from matplotlib.contour import QuadContourSet
80
+ from matplotlib.quiver import Quiver
81
+
82
+ scatter_idx = 0
83
+ for i, coll in enumerate(ax.collections):
84
+ if isinstance(coll, Quiver):
85
+ if not coll.get_visible():
86
+ continue
87
+ bbox = get_quiver_bbox(
88
+ coll,
89
+ ax,
90
+ fig,
91
+ renderer,
92
+ tight_bbox,
93
+ img_width,
94
+ img_height,
95
+ scale_x,
96
+ scale_y,
97
+ pad_inches,
98
+ saved_height_inches,
99
+ )
100
+ if bbox:
101
+ bboxes[f"ax{ax_idx}_quiver{i}"] = {
102
+ **bbox,
103
+ "type": "quiver",
104
+ "label": coll.get_label() or f"quiver_{i}",
105
+ "ax_index": ax_idx,
106
+ }
107
+
108
+ elif isinstance(coll, PathCollection):
109
+ if not coll.get_visible():
110
+ continue
111
+ bbox = get_collection_bbox(
112
+ coll,
113
+ ax,
114
+ fig,
115
+ renderer,
116
+ tight_bbox,
117
+ img_width,
118
+ img_height,
119
+ scale_x,
120
+ scale_y,
121
+ pad_inches,
122
+ saved_height_inches,
123
+ include_points,
124
+ )
125
+ if bbox:
126
+ bboxes[f"ax{ax_idx}_scatter{scatter_idx}"] = {
127
+ **bbox,
128
+ "type": "scatter",
129
+ "label": coll.get_label() or f"scatter_{scatter_idx}",
130
+ "ax_index": ax_idx,
131
+ }
132
+ scatter_idx += 1
133
+
134
+ elif isinstance(coll, PolyCollection):
135
+ if not coll.get_visible():
136
+ continue
137
+ bbox = get_collection_bbox(
138
+ coll,
139
+ ax,
140
+ fig,
141
+ renderer,
142
+ tight_bbox,
143
+ img_width,
144
+ img_height,
145
+ scale_x,
146
+ scale_y,
147
+ pad_inches,
148
+ saved_height_inches,
149
+ False,
150
+ )
151
+ if bbox:
152
+ bboxes[f"ax{ax_idx}_fill{i}"] = {
153
+ **bbox,
154
+ "type": "fill",
155
+ "label": coll.get_label() or f"fill_{i}",
156
+ "ax_index": ax_idx,
157
+ }
158
+
159
+ elif isinstance(coll, LineCollection):
160
+ if not coll.get_visible():
161
+ continue
162
+ bbox = get_collection_bbox(
163
+ coll,
164
+ ax,
165
+ fig,
166
+ renderer,
167
+ tight_bbox,
168
+ img_width,
169
+ img_height,
170
+ scale_x,
171
+ scale_y,
172
+ pad_inches,
173
+ saved_height_inches,
174
+ False,
175
+ )
176
+ if bbox:
177
+ bboxes[f"ax{ax_idx}_linecoll{i}"] = {
178
+ **bbox,
179
+ "type": "linecollection",
180
+ "label": coll.get_label() or f"linecoll_{i}",
181
+ "ax_index": ax_idx,
182
+ }
183
+
184
+ elif isinstance(coll, QuadMesh):
185
+ if not coll.get_visible():
186
+ continue
187
+ bbox = get_collection_bbox(
188
+ coll,
189
+ ax,
190
+ fig,
191
+ renderer,
192
+ tight_bbox,
193
+ img_width,
194
+ img_height,
195
+ scale_x,
196
+ scale_y,
197
+ pad_inches,
198
+ saved_height_inches,
199
+ False,
200
+ )
201
+ if bbox:
202
+ bboxes[f"ax{ax_idx}_quadmesh{i}"] = {
203
+ **bbox,
204
+ "type": "quadmesh",
205
+ "label": f"quadmesh_{i}",
206
+ "ax_index": ax_idx,
207
+ }
208
+
209
+ elif isinstance(coll, QuadContourSet):
210
+ bbox = get_element_bbox(
211
+ ax,
212
+ fig,
213
+ renderer,
214
+ tight_bbox,
215
+ img_width,
216
+ img_height,
217
+ scale_x,
218
+ scale_y,
219
+ pad_inches,
220
+ saved_height_inches,
221
+ )
222
+ if bbox:
223
+ bboxes[f"ax{ax_idx}_contour{i}"] = {
224
+ **bbox,
225
+ "type": "contour",
226
+ "label": f"contour_{i}",
227
+ "ax_index": ax_idx,
228
+ }
229
+
230
+
231
+ def extract_patches(
232
+ ax,
233
+ ax_idx,
234
+ fig,
235
+ renderer,
236
+ tight_bbox,
237
+ img_width,
238
+ img_height,
239
+ scale_x,
240
+ scale_y,
241
+ pad_inches,
242
+ saved_height_inches,
243
+ bboxes,
244
+ ):
245
+ """Extract bboxes for patch elements (bars, wedges, polygons)."""
246
+ from matplotlib.patches import Polygon, Wedge
247
+
248
+ bar_idx = 0
249
+ for i, patch in enumerate(ax.patches):
250
+ if isinstance(patch, Rectangle):
251
+ if not patch.get_visible():
252
+ continue
253
+ if patch.get_width() == 1.0 and patch.get_height() == 1.0:
254
+ continue
255
+ bbox = get_patch_bbox(
256
+ patch,
257
+ ax,
258
+ fig,
259
+ renderer,
260
+ tight_bbox,
261
+ img_width,
262
+ img_height,
263
+ scale_x,
264
+ scale_y,
265
+ pad_inches,
266
+ saved_height_inches,
267
+ )
268
+ if bbox:
269
+ bboxes[f"ax{ax_idx}_bar{bar_idx}"] = {
270
+ **bbox,
271
+ "type": "bar",
272
+ "label": patch.get_label() or f"bar_{bar_idx}",
273
+ "ax_index": ax_idx,
274
+ }
275
+ bar_idx += 1
276
+
277
+ elif isinstance(patch, Wedge):
278
+ if not patch.get_visible():
279
+ continue
280
+ bbox = get_patch_bbox(
281
+ patch,
282
+ ax,
283
+ fig,
284
+ renderer,
285
+ tight_bbox,
286
+ img_width,
287
+ img_height,
288
+ scale_x,
289
+ scale_y,
290
+ pad_inches,
291
+ saved_height_inches,
292
+ )
293
+ if bbox:
294
+ bboxes[f"ax{ax_idx}_wedge{i}"] = {
295
+ **bbox,
296
+ "type": "pie",
297
+ "label": patch.get_label() or f"wedge_{i}",
298
+ "ax_index": ax_idx,
299
+ }
300
+
301
+ elif isinstance(patch, Polygon):
302
+ if not patch.get_visible():
303
+ continue
304
+ bbox = get_patch_bbox(
305
+ patch,
306
+ ax,
307
+ fig,
308
+ renderer,
309
+ tight_bbox,
310
+ img_width,
311
+ img_height,
312
+ scale_x,
313
+ scale_y,
314
+ pad_inches,
315
+ saved_height_inches,
316
+ )
317
+ if bbox:
318
+ bboxes[f"ax{ax_idx}_polygon{i}"] = {
319
+ **bbox,
320
+ "type": "fill",
321
+ "label": patch.get_label() or f"fill_{i}",
322
+ "ax_index": ax_idx,
323
+ }
324
+
325
+
326
+ def extract_images(
327
+ ax,
328
+ ax_idx,
329
+ fig,
330
+ renderer,
331
+ tight_bbox,
332
+ img_width,
333
+ img_height,
334
+ scale_x,
335
+ scale_y,
336
+ pad_inches,
337
+ saved_height_inches,
338
+ bboxes,
339
+ ):
340
+ """Extract bboxes for image elements."""
341
+ from matplotlib.image import AxesImage
342
+
343
+ for i, img in enumerate(ax.images):
344
+ if isinstance(img, AxesImage):
345
+ if not img.get_visible():
346
+ continue
347
+ bbox = get_element_bbox(
348
+ img,
349
+ fig,
350
+ renderer,
351
+ tight_bbox,
352
+ img_width,
353
+ img_height,
354
+ scale_x,
355
+ scale_y,
356
+ pad_inches,
357
+ saved_height_inches,
358
+ )
359
+ if bbox:
360
+ bboxes[f"ax{ax_idx}_image{i}"] = {
361
+ **bbox,
362
+ "type": "image",
363
+ "label": img.get_label() or f"image_{i}",
364
+ "ax_index": ax_idx,
365
+ }
366
+
367
+
368
+ __all__ = ["extract_lines", "extract_collections", "extract_patches", "extract_images"]
369
+
370
+ # EOF