plotnine 0.15.0.dev3__py3-none-any.whl → 0.15.2__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 (139) hide show
  1. plotnine/__init__.py +2 -0
  2. plotnine/_mpl/layout_manager/_engine.py +1 -1
  3. plotnine/_mpl/layout_manager/_layout_items.py +126 -41
  4. plotnine/_mpl/layout_manager/_layout_tree.py +712 -314
  5. plotnine/_mpl/layout_manager/_spaces.py +305 -101
  6. plotnine/_mpl/patches.py +70 -34
  7. plotnine/_mpl/text.py +144 -63
  8. plotnine/_mpl/utils.py +1 -1
  9. plotnine/_utils/__init__.py +50 -107
  10. plotnine/_utils/context.py +78 -2
  11. plotnine/_utils/ipython.py +35 -51
  12. plotnine/_utils/quarto.py +26 -0
  13. plotnine/_utils/yippie.py +115 -0
  14. plotnine/composition/__init__.py +11 -0
  15. plotnine/composition/_beside.py +55 -0
  16. plotnine/composition/_compose.py +471 -0
  17. plotnine/composition/_plot_spacer.py +60 -0
  18. plotnine/composition/_stack.py +55 -0
  19. plotnine/coords/coord.py +3 -3
  20. plotnine/data/__init__.py +31 -0
  21. plotnine/data/anscombe-quartet.csv +45 -0
  22. plotnine/doctools.py +4 -4
  23. plotnine/facets/facet.py +4 -4
  24. plotnine/facets/strips.py +17 -28
  25. plotnine/geoms/annotate.py +13 -13
  26. plotnine/geoms/annotation_logticks.py +7 -8
  27. plotnine/geoms/annotation_stripes.py +6 -6
  28. plotnine/geoms/geom.py +60 -27
  29. plotnine/geoms/geom_abline.py +3 -2
  30. plotnine/geoms/geom_area.py +2 -2
  31. plotnine/geoms/geom_bar.py +1 -0
  32. plotnine/geoms/geom_bin_2d.py +6 -2
  33. plotnine/geoms/geom_blank.py +0 -3
  34. plotnine/geoms/geom_boxplot.py +8 -4
  35. plotnine/geoms/geom_col.py +2 -2
  36. plotnine/geoms/geom_count.py +6 -2
  37. plotnine/geoms/geom_crossbar.py +3 -3
  38. plotnine/geoms/geom_density_2d.py +6 -2
  39. plotnine/geoms/geom_dotplot.py +2 -2
  40. plotnine/geoms/geom_errorbar.py +2 -2
  41. plotnine/geoms/geom_errorbarh.py +2 -2
  42. plotnine/geoms/geom_histogram.py +1 -1
  43. plotnine/geoms/geom_hline.py +3 -2
  44. plotnine/geoms/geom_linerange.py +2 -2
  45. plotnine/geoms/geom_map.py +5 -5
  46. plotnine/geoms/geom_path.py +11 -12
  47. plotnine/geoms/geom_point.py +4 -5
  48. plotnine/geoms/geom_pointdensity.py +4 -0
  49. plotnine/geoms/geom_pointrange.py +3 -5
  50. plotnine/geoms/geom_polygon.py +2 -3
  51. plotnine/geoms/geom_qq.py +4 -0
  52. plotnine/geoms/geom_qq_line.py +4 -0
  53. plotnine/geoms/geom_quantile.py +4 -0
  54. plotnine/geoms/geom_raster.py +4 -5
  55. plotnine/geoms/geom_rect.py +3 -4
  56. plotnine/geoms/geom_ribbon.py +7 -7
  57. plotnine/geoms/geom_rug.py +1 -1
  58. plotnine/geoms/geom_segment.py +2 -2
  59. plotnine/geoms/geom_sina.py +3 -3
  60. plotnine/geoms/geom_smooth.py +7 -3
  61. plotnine/geoms/geom_step.py +2 -2
  62. plotnine/geoms/geom_text.py +2 -3
  63. plotnine/geoms/geom_violin.py +8 -5
  64. plotnine/geoms/geom_vline.py +3 -2
  65. plotnine/ggplot.py +64 -85
  66. plotnine/guides/guide.py +7 -10
  67. plotnine/guides/guide_colorbar.py +3 -3
  68. plotnine/guides/guide_legend.py +3 -3
  69. plotnine/guides/guides.py +6 -6
  70. plotnine/helpers.py +49 -0
  71. plotnine/iapi.py +28 -5
  72. plotnine/labels.py +3 -3
  73. plotnine/layer.py +36 -19
  74. plotnine/mapping/_atomic.py +178 -0
  75. plotnine/mapping/_env.py +13 -2
  76. plotnine/mapping/_eval_environment.py +1 -1
  77. plotnine/mapping/aes.py +85 -49
  78. plotnine/scales/__init__.py +2 -0
  79. plotnine/scales/limits.py +7 -7
  80. plotnine/scales/scale.py +3 -3
  81. plotnine/scales/scale_color.py +82 -18
  82. plotnine/scales/scale_continuous.py +6 -4
  83. plotnine/scales/scale_datetime.py +28 -14
  84. plotnine/scales/scale_discrete.py +1 -1
  85. plotnine/scales/scale_identity.py +21 -2
  86. plotnine/scales/scale_manual.py +8 -2
  87. plotnine/scales/scale_xy.py +2 -2
  88. plotnine/stats/binning.py +4 -1
  89. plotnine/stats/smoothers.py +23 -36
  90. plotnine/stats/stat.py +20 -32
  91. plotnine/stats/stat_bin.py +6 -5
  92. plotnine/stats/stat_bin_2d.py +11 -9
  93. plotnine/stats/stat_bindot.py +13 -16
  94. plotnine/stats/stat_boxplot.py +6 -6
  95. plotnine/stats/stat_count.py +6 -9
  96. plotnine/stats/stat_density.py +7 -10
  97. plotnine/stats/stat_density_2d.py +12 -8
  98. plotnine/stats/stat_ecdf.py +7 -6
  99. plotnine/stats/stat_ellipse.py +9 -6
  100. plotnine/stats/stat_function.py +10 -8
  101. plotnine/stats/stat_hull.py +6 -3
  102. plotnine/stats/stat_identity.py +5 -2
  103. plotnine/stats/stat_pointdensity.py +5 -7
  104. plotnine/stats/stat_qq.py +46 -20
  105. plotnine/stats/stat_qq_line.py +16 -11
  106. plotnine/stats/stat_quantile.py +15 -9
  107. plotnine/stats/stat_sina.py +13 -15
  108. plotnine/stats/stat_smooth.py +8 -10
  109. plotnine/stats/stat_sum.py +5 -2
  110. plotnine/stats/stat_summary.py +7 -10
  111. plotnine/stats/stat_summary_bin.py +11 -14
  112. plotnine/stats/stat_unique.py +5 -2
  113. plotnine/stats/stat_ydensity.py +8 -11
  114. plotnine/themes/elements/__init__.py +2 -1
  115. plotnine/themes/elements/element_line.py +17 -9
  116. plotnine/themes/elements/margin.py +64 -1
  117. plotnine/themes/theme.py +9 -1
  118. plotnine/themes/theme_538.py +0 -1
  119. plotnine/themes/theme_bw.py +0 -1
  120. plotnine/themes/theme_dark.py +0 -1
  121. plotnine/themes/theme_gray.py +6 -5
  122. plotnine/themes/theme_light.py +1 -1
  123. plotnine/themes/theme_matplotlib.py +5 -5
  124. plotnine/themes/theme_seaborn.py +7 -4
  125. plotnine/themes/theme_void.py +9 -8
  126. plotnine/themes/theme_xkcd.py +0 -1
  127. plotnine/themes/themeable.py +109 -31
  128. plotnine/typing.py +17 -6
  129. plotnine/watermark.py +3 -3
  130. {plotnine-0.15.0.dev3.dist-info → plotnine-0.15.2.dist-info}/METADATA +13 -6
  131. plotnine-0.15.2.dist-info/RECORD +221 -0
  132. {plotnine-0.15.0.dev3.dist-info → plotnine-0.15.2.dist-info}/WHEEL +1 -1
  133. plotnine/plot_composition/__init__.py +0 -10
  134. plotnine/plot_composition/_compose.py +0 -436
  135. plotnine/plot_composition/_spacer.py +0 -32
  136. plotnine-0.15.0.dev3.dist-info/RECORD +0 -215
  137. /plotnine/{plot_composition → composition}/_plotspec.py +0 -0
  138. {plotnine-0.15.0.dev3.dist-info → plotnine-0.15.2.dist-info}/licenses/LICENSE +0 -0
  139. {plotnine-0.15.0.dev3.dist-info → plotnine-0.15.2.dist-info}/top_level.txt +0 -0
@@ -3,11 +3,11 @@ from __future__ import annotations
3
3
  import abc
4
4
  from dataclasses import dataclass
5
5
  from functools import cached_property
6
- from typing import TYPE_CHECKING
6
+ from typing import TYPE_CHECKING, cast
7
7
 
8
8
  import numpy as np
9
9
 
10
- from plotnine.plot_composition import OR
10
+ from plotnine.composition import Beside
11
11
 
12
12
  from ._spaces import LayoutSpaces
13
13
 
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
16
16
 
17
17
  from plotnine import ggplot
18
18
  from plotnine._mpl.gridspec import p9GridSpec
19
- from plotnine.plot_composition import Compose
19
+ from plotnine.composition import Compose
20
20
 
21
21
 
22
22
  @dataclass
@@ -80,141 +80,181 @@ class LayoutTree:
80
80
  represents.
81
81
  """
82
82
 
83
- @cached_property
84
- def sub_compositions(self) -> list[LayoutTree]:
83
+ @staticmethod
84
+ def create(
85
+ cmp: Compose,
86
+ lookup_spaces: dict[ggplot, LayoutSpaces],
87
+ ) -> LayoutTree:
85
88
  """
86
- LayoutTrees of the direct sub compositions of this one
89
+ Create a LayoutTree for this composition
90
+
91
+ Parameters
92
+ ----------
93
+ cmp :
94
+ Composition
95
+ lookup_spaces :
96
+ A table to lookup the LayoutSpaces for each plot.
97
+
98
+ Notes
99
+ -----
100
+ LayoutTree works by modifying the `.gridspec` of the compositions,
101
+ and the `LayoutSpaces` of the plots.
87
102
  """
88
- return [item for item in self.nodes if isinstance(item, LayoutTree)]
103
+ from plotnine import ggplot
89
104
 
90
- @cached_property
91
- @abc.abstractmethod
92
- def lefts(self) -> Sequence[float]:
105
+ nodes: list[LayoutSpaces | LayoutTree] = []
106
+ for item in cmp:
107
+ if isinstance(item, ggplot):
108
+ nodes.append(lookup_spaces[item])
109
+ else:
110
+ nodes.append(LayoutTree.create(item, lookup_spaces))
111
+
112
+ if isinstance(cmp, Beside):
113
+ return ColumnsTree(cmp.gridspec, nodes)
114
+ else:
115
+ return RowsTree(cmp.gridspec, nodes)
116
+
117
+ def harmonise(self):
93
118
  """
94
- Left values [figure space] of nodes in this tree
119
+ Align and resize plots in composition to look good
95
120
  """
121
+ self.align_axis_titles()
122
+ self.align()
123
+ self.resize()
96
124
 
97
125
  @abc.abstractmethod
98
- def set_left_alignment_margin(self, value: float):
126
+ def align(self):
99
127
  """
100
- Set a margin to align the left of the panels in this composition
128
+ Align all the edges in this composition & contained compositions
101
129
 
102
- In figure dimenstions
130
+ This function mutates the layout spaces, specifically the
131
+ margin_alignments along the sides of the plot.
103
132
  """
104
133
 
105
- @cached_property
106
134
  @abc.abstractmethod
107
- def bottoms(self) -> Sequence[float]:
108
- """
109
- Bottom values [figure space] of nodes in this tree
135
+ def resize(self):
110
136
  """
137
+ Resize panels and the entire plots
111
138
 
112
- @abc.abstractmethod
113
- def set_bottom_alignment_margin(self, value: float):
139
+ This function mutates the composition gridspecs; specifically the
140
+ width_ratios and height_ratios.
114
141
  """
115
- Set a margin to align the bottom of the panels in this composition
116
142
 
117
- In figure dimenstions
143
+ def align_sub_compositions(self):
144
+ """
145
+ Align the compositions contained in this one
118
146
  """
147
+ # Recurse into the contained compositions
148
+ for tree in self.sub_compositions:
149
+ tree.align()
119
150
 
120
- @cached_property
121
151
  @abc.abstractmethod
122
- def tops(self) -> Sequence[float]:
152
+ def align_axis_titles(self):
123
153
  """
124
- Top values [figure space] of nodes in this tree
154
+ Align the axis titles along the composing dimension
155
+
156
+ Since the alignment value used to for this purpose is one of
157
+ the fields in the _side_space, it affects the space created
158
+ for the panel.
159
+
160
+ We could align the titles within self.align but we would have
161
+ to store the value outside the _side_space and pick it up when
162
+ setting the position of the texts!
125
163
  """
126
164
 
127
- @abc.abstractmethod
128
- def set_top_alignment_margin(self, value: float):
165
+ def resize_sub_compositions(self):
129
166
  """
130
- Set a margin to align the top of the panels in this composition
167
+ Resize panels in the compositions contained in this one
168
+ """
169
+ for tree in self.sub_compositions:
170
+ tree.resize()
131
171
 
132
- In figure dimenstions
172
+ @cached_property
173
+ def sub_compositions(self) -> list[LayoutTree]:
133
174
  """
175
+ LayoutTrees of the direct sub compositions of this one
176
+ """
177
+ return [item for item in self.nodes if isinstance(item, LayoutTree)]
134
178
 
135
179
  @cached_property
136
180
  @abc.abstractmethod
137
- def rights(self) -> Sequence[float]:
181
+ def panel_lefts(self) -> Sequence[float]:
138
182
  """
139
- Right values [figure space] of nodes in this tree
183
+ Left values [figure space] of nodes in this tree
140
184
  """
141
185
 
186
+ @cached_property
142
187
  @abc.abstractmethod
143
- def set_right_alignment_margin(self, value: float):
188
+ def panel_rights(self) -> Sequence[float]:
144
189
  """
145
- Set a margin to align the right of the panels in this composition
146
-
147
- In figure dimenstions
190
+ Right values [figure space] of nodes in this tree
148
191
  """
149
192
 
193
+ @cached_property
150
194
  @abc.abstractmethod
151
- def align(self):
195
+ def panel_bottoms(self) -> Sequence[float]:
152
196
  """
153
- Align all the edges in this composition & contained compositions
154
-
155
- This function mutates the layout spaces, specifically the
156
- alignment_margins along the sides of the plot.
197
+ Bottom values [figure space] of nodes in this tree
157
198
  """
158
199
 
159
- def align_sub_compositions(self):
200
+ @cached_property
201
+ @abc.abstractmethod
202
+ def panel_tops(self) -> Sequence[float]:
160
203
  """
161
- Align the compositions contained in this one
204
+ Top values [figure space] of nodes in this tree
162
205
  """
163
- # Recurse into the contained compositions
164
- for tree in self.sub_compositions:
165
- tree.align()
166
206
 
167
207
  @property
168
- def bottoms_align(self) -> bool:
208
+ def panel_lefts_align(self) -> bool:
169
209
  """
170
- Return True if panel bottoms for the nodes are aligned
210
+ Return True if panel lefts for the nodes are aligned
171
211
  """
172
- arr = np.array(self.bottoms)
212
+ arr = np.array(self.panel_lefts)
173
213
  return all(arr == arr[0])
174
214
 
175
215
  @property
176
- def lefts_align(self) -> bool:
216
+ def panel_rights_align(self) -> bool:
177
217
  """
178
- Return True if panel lefts for the nodes are aligned
218
+ Return True if panel rights for the nodes are aligned
179
219
  """
180
- arr = np.array(self.lefts)
220
+ arr = np.array(self.panel_rights)
181
221
  return all(arr == arr[0])
182
222
 
183
223
  @property
184
- def tops_align(self) -> bool:
224
+ def panel_bottoms_align(self) -> bool:
185
225
  """
186
- Return True if panel tops for the nodes are aligned
226
+ Return True if panel bottoms for the nodes are aligned
187
227
  """
188
- arr = np.array(self.tops)
228
+ arr = np.array(self.panel_bottoms)
189
229
  return all(arr == arr[0])
190
230
 
191
231
  @property
192
- def rights_align(self) -> bool:
232
+ def panel_tops_align(self) -> bool:
193
233
  """
194
- Return True if panel rights for the nodes are aligned
234
+ Return True if panel tops for the nodes are aligned
195
235
  """
196
- arr = np.array(self.rights)
236
+ arr = np.array(self.panel_tops)
197
237
  return all(arr == arr[0])
198
238
 
199
239
  @property
200
240
  @abc.abstractmethod
201
241
  def panel_width(self) -> float:
202
242
  """
203
- A representative for width for panels of the nodes
243
+ A representative width for panels of the nodes
204
244
  """
205
245
 
206
246
  @property
207
247
  @abc.abstractmethod
208
248
  def panel_height(self) -> float:
209
249
  """
210
- A representative for height for panels of the nodes
250
+ A representative height for panels of the nodes
211
251
  """
212
252
 
213
253
  @property
214
254
  @abc.abstractmethod
215
255
  def plot_width(self) -> float:
216
256
  """
217
- A representative for width for plots of the nodes
257
+ A representative width for plots of the nodes
218
258
  """
219
259
 
220
260
  @property
@@ -252,174 +292,295 @@ class LayoutTree:
252
292
  """
253
293
  return [node.panel_height for node in self.nodes]
254
294
 
255
- def resize(self):
295
+ @cached_property
296
+ @abc.abstractmethod
297
+ def left_tag_width(self) -> float:
298
+ """
299
+ A representative width [figure space] for the left tags of the nodes
256
300
  """
257
- Resize panels and the entire plots
258
301
 
259
- This function mutates the composition gridspecs; specifically the
260
- width_ratios and height_ratios.
302
+ @cached_property
303
+ @abc.abstractmethod
304
+ def right_tag_width(self) -> float:
305
+ """
306
+ A representative width [figure space] for the right tags of the nodes
261
307
  """
262
308
 
263
- def resize_sub_compositions(self):
309
+ @cached_property
310
+ @abc.abstractmethod
311
+ def bottom_tag_height(self) -> float:
264
312
  """
265
- Resize panels in the compositions contained in this one
313
+ A representative height [figure space] for the top tags of the nodes
266
314
  """
267
- for tree in self.sub_compositions:
268
- tree.resize()
269
315
 
270
- @staticmethod
271
- def create(
272
- cmp: Compose,
273
- lookup_spaces: dict[ggplot, LayoutSpaces],
274
- ) -> LayoutTree:
316
+ @cached_property
317
+ @abc.abstractmethod
318
+ def top_tag_height(self) -> float:
319
+ """
320
+ A representative height [figure space] for the top tags of the nodes
275
321
  """
276
- Create a LayoutTree for this composition
277
322
 
278
- Parameters
279
- ----------
280
- cmp :
281
- Composition
282
- lookup_spaces :
283
- A table to lookup the LayoutSpaces for each plot.
323
+ @cached_property
324
+ def left_tag_widths(self) -> list[float]:
325
+ """
326
+ The widths of the left tags in this tree
327
+ """
328
+ return [node.left_tag_width for node in self.nodes]
284
329
 
285
- Notes
286
- -----
287
- LayoutTree works by modifying the `.gridspec` of the compositions,
288
- and the `LayoutSpaces` of the plots.
330
+ @cached_property
331
+ def right_tag_widths(self) -> list[float]:
289
332
  """
290
- from plotnine import ggplot
333
+ The widths of the right tags in this tree
334
+ """
335
+ return [node.right_tag_width for node in self.nodes]
291
336
 
292
- nodes: list[LayoutSpaces | LayoutTree] = []
293
- for item in cmp:
294
- if isinstance(item, ggplot):
295
- nodes.append(lookup_spaces[item])
296
- else:
297
- nodes.append(LayoutTree.create(item, lookup_spaces))
337
+ @cached_property
338
+ def bottom_tag_heights(self) -> list[float]:
339
+ """
340
+ The heights of the bottom tags in this tree
341
+ """
342
+ return [node.bottom_tag_height for node in self.nodes]
298
343
 
299
- if isinstance(cmp, OR):
300
- return ColumnsTree(cmp.gridspec, nodes)
301
- else:
302
- return RowsTree(cmp.gridspec, nodes)
344
+ @cached_property
345
+ def top_tag_heights(self) -> list[float]:
346
+ """
347
+ The heights of the top tags in this tree
348
+ """
349
+ return [node.top_tag_height for node in self.nodes]
303
350
 
304
- def harmonise(self):
351
+ @property
352
+ def left_tags_align(self) -> bool:
305
353
  """
306
- Align and resize plots in composition to look good
354
+ Return True if the left tags for the nodes are aligned
307
355
  """
308
- self.align()
309
- self.resize()
356
+ arr = np.array(self.left_tag_widths)
357
+ return all(arr == arr[0])
310
358
 
359
+ @property
360
+ def right_tags_align(self) -> bool:
361
+ """
362
+ Return True if the right tags for the nodes are aligned
363
+ """
364
+ arr = np.array(self.right_tag_widths)
365
+ return all(arr == arr[0])
311
366
 
312
- @dataclass
313
- class ColumnsTree(LayoutTree):
314
- """
315
- Tree with columns at the outermost level
367
+ @property
368
+ def bottom_tags_align(self) -> bool:
369
+ """
370
+ Return True if the bottom tags for the nodes are aligned
371
+ """
372
+ arr = np.array(self.bottom_tag_heights)
373
+ return all(arr == arr[0])
316
374
 
317
- e.g. p1 | (p2 / p3)
375
+ @property
376
+ def top_tags_align(self) -> bool:
377
+ """
378
+ Return True if the top tags for the nodes are aligned
379
+ """
380
+ arr = np.array(self.top_tag_heights)
381
+ return all(arr == arr[0])
318
382
 
383
+ @property
384
+ def left_axis_titles_align(self) -> bool:
385
+ """
386
+ Return True if the left axis titles align
387
+ """
388
+ arr = np.array(self.left_axis_title_clearances)
389
+ return all(arr == arr[0])
319
390
 
320
- -------------------
321
- | | |
322
- | | |
323
- | | |
324
- | | |
325
- | |---------|
326
- | | |
327
- | | |
328
- | | |
329
- | | |
330
- -------------------
331
- """
391
+ @property
392
+ def bottom_axis_titles_align(self) -> bool:
393
+ """
394
+ Return True if the bottom axis titles align
395
+ """
396
+ arr = np.array(self.bottom_axis_title_clearances)
397
+ return all(arr == arr[0])
332
398
 
333
- def align(self):
334
- self.align_tops()
335
- self.align_bottoms()
336
- self.align_sub_compositions()
399
+ @cached_property
400
+ @abc.abstractmethod
401
+ def left_axis_title_clearance(self) -> float:
402
+ """
403
+ Distance between the left y-axis title and the panel
404
+ """
337
405
 
338
406
  @cached_property
339
- def lefts(self):
340
- left_item = self.nodes[0]
341
- if isinstance(left_item, LayoutSpaces):
342
- return [left_item.l.left]
343
- else:
344
- return left_item.lefts
407
+ @abc.abstractmethod
408
+ def bottom_axis_title_clearance(self) -> float:
409
+ """
410
+ Distance between the left x-axis title and the panel
411
+ """
345
412
 
346
- def set_left_alignment_margin(self, value: float):
347
- left_item = self.nodes[0]
348
- if isinstance(left_item, LayoutSpaces):
349
- left_item.l.alignment_margin = value
350
- else:
351
- left_item.set_left_alignment_margin(value)
413
+ @cached_property
414
+ def left_axis_title_clearances(self) -> list[float]:
415
+ """
416
+ Distances between the left y-axis titles and the panels
417
+ """
418
+ return [node.left_axis_title_clearance for node in self.nodes]
352
419
 
353
420
  @cached_property
354
- def bottoms(self):
355
- values = []
356
- for item in self.nodes:
357
- if isinstance(item, LayoutSpaces):
358
- values.append(item.b.bottom)
359
- else:
360
- values.append(max(item.bottoms))
361
- return values
421
+ def bottom_axis_title_clearances(self) -> list[float]:
422
+ """
423
+ Distances between the bottom x-axis titles and the panels
424
+ """
425
+ return [node.bottom_axis_title_clearance for node in self.nodes]
362
426
 
363
- def align_bottoms(self):
427
+ @abc.abstractmethod
428
+ def set_left_margin_alignment(self, value: float):
364
429
  """
365
- Align the immediate bottom edges this composition
430
+ Set a margin to align the left of the panels in this composition
366
431
 
367
- ----------- -----------
368
- | | | | | |
369
- | | | | | |
370
- | | | -> | | |
371
- | |#####| |#####|#####|
372
- |#####| | | | |
373
- ----------- -----------
432
+ In figure dimenstions
374
433
  """
375
- # If panels are aligned and have a non-zero alignment_margin,
376
- # aligning them again will set that value to zero and undoes
377
- # the alignment.
378
- if self.bottoms_align:
379
- return
380
434
 
381
- values = max(self.bottoms) - np.array(self.bottoms)
382
- for item, value in zip(self.nodes, values):
383
- if isinstance(item, LayoutSpaces):
384
- item.b.alignment_margin = value
385
- else:
386
- item.set_bottom_alignment_margin(value)
435
+ @abc.abstractmethod
436
+ def set_right_margin_alignment(self, value: float):
437
+ """
438
+ Set a margin to align the right of the panels in this composition
387
439
 
388
- del self.bottoms
440
+ In figure dimenstions
441
+ """
389
442
 
390
- def set_bottom_alignment_margin(self, value: float):
391
- for item in self.nodes:
392
- if isinstance(item, LayoutSpaces):
393
- item.b.alignment_margin = value
394
- else:
395
- item.set_bottom_alignment_margin(value)
443
+ @abc.abstractmethod
444
+ def set_bottom_margin_alignment(self, value: float):
445
+ """
446
+ Set a margin to align the bottom of the panels in this composition
396
447
 
397
- @cached_property
398
- def rights(self):
399
- right_item = self.nodes[-1]
400
- if isinstance(right_item, LayoutSpaces):
401
- return [right_item.r.right]
402
- else:
403
- return right_item.rights
448
+ In figure dimenstions
449
+ """
404
450
 
405
- def set_right_alignment_margin(self, value: float):
406
- right_item = self.nodes[-1]
407
- if isinstance(right_item, LayoutSpaces):
408
- right_item.r.alignment_margin = value
409
- else:
410
- right_item.set_right_alignment_margin(value)
451
+ @abc.abstractmethod
452
+ def set_top_margin_alignment(self, value: float):
453
+ """
454
+ Set a margin to align the top of the panels in this composition
411
455
 
412
- @cached_property
413
- def tops(self):
414
- values = []
415
- for item in self.nodes:
456
+ In figure dimenstions
457
+ """
458
+
459
+ @abc.abstractmethod
460
+ def set_left_tag_alignment(self, value: float):
461
+ """
462
+ Set the space to align the left tags in this composition
463
+
464
+ In figure dimenstions
465
+ """
466
+
467
+ @abc.abstractmethod
468
+ def set_right_tag_alignment(self, value: float):
469
+ """
470
+ Set the space to align the right tags in this composition
471
+
472
+ In figure dimenstions
473
+ """
474
+
475
+ @abc.abstractmethod
476
+ def set_bottom_tag_alignment(self, value: float):
477
+ """
478
+ Set the space to align the bottom tags in this composition
479
+
480
+ In figure dimenstions
481
+ """
482
+
483
+ @abc.abstractmethod
484
+ def set_top_tag_alignment(self, value: float):
485
+ """
486
+ Set the space to align the top tags in this composition
487
+
488
+ In figure dimenstions
489
+ """
490
+
491
+ @abc.abstractmethod
492
+ def set_left_axis_title_alignment(self, value: float):
493
+ """
494
+ Set the space to align the left axis titles in this composition
495
+
496
+ In figure dimenstions
497
+ """
498
+
499
+ @abc.abstractmethod
500
+ def set_bottom_axis_title_alignment(self, value: float):
501
+ """
502
+ Set the space to align the bottom axis titles in this composition
503
+
504
+ In figure dimenstions
505
+ """
506
+
507
+
508
+ @dataclass
509
+ class ColumnsTree(LayoutTree):
510
+ """
511
+ Tree with columns at the outermost level
512
+
513
+ e.g. p1 | (p2 / p3)
514
+
515
+
516
+ -------------------
517
+ | | |
518
+ | | |
519
+ | | |
520
+ | | |
521
+ | |---------|
522
+ | | |
523
+ | | |
524
+ | | |
525
+ | | |
526
+ -------------------
527
+ """
528
+
529
+ def align(self):
530
+ self.align_top_tags()
531
+ self.align_bottom_tags()
532
+ self.align_panel_tops()
533
+ self.align_panel_bottoms()
534
+ self.align_sub_compositions()
535
+
536
+ def align_axis_titles(self):
537
+ self.align_bottom_axis_titles()
538
+ for tree in self.sub_compositions:
539
+ tree.align_axis_titles()
540
+
541
+ def resize(self):
542
+ """
543
+ Resize the widths of gridspec so that panels have equal widths
544
+ """
545
+ # The new width of each panel is the average width of all
546
+ # the panels plus all the space to the left and right
547
+ # of the panels.
548
+ plot_widths = np.array(self.plot_widths)
549
+ panel_widths = np.array(self.panel_widths)
550
+ non_panel_space = plot_widths - panel_widths
551
+ new_plot_widths = panel_widths.mean() + non_panel_space
552
+ width_ratios = new_plot_widths / new_plot_widths.min()
553
+ self.gridspec.set_width_ratios(width_ratios)
554
+ self.resize_sub_compositions()
555
+
556
+ def align_panel_bottoms(self):
557
+ """
558
+ Align the immediate bottom edges this composition
559
+
560
+ ----------- -----------
561
+ | | | | | |
562
+ | | | | | |
563
+ | | | -> | | |
564
+ | |#####| |#####|#####|
565
+ |#####| | | | |
566
+ ----------- -----------
567
+ """
568
+ # If panels are aligned and have a non-zero margin_alignment,
569
+ # aligning them again will set that value to zero and undoes
570
+ # the alignment.
571
+ if self.panel_bottoms_align:
572
+ return
573
+
574
+ values = max(self.panel_bottoms) - np.array(self.panel_bottoms)
575
+ for item, value in zip(self.nodes, values):
416
576
  if isinstance(item, LayoutSpaces):
417
- values.append(item.t.top)
577
+ item.b.margin_alignment = value
418
578
  else:
419
- values.append(min(item.tops))
420
- return values
579
+ item.set_bottom_margin_alignment(value)
580
+
581
+ del self.panel_bottoms
421
582
 
422
- def align_tops(self):
583
+ def align_panel_tops(self):
423
584
  """
424
585
  Align the immediate top edges in this composition
425
586
 
@@ -431,67 +592,194 @@ class ColumnsTree(LayoutTree):
431
592
  | | | | | |
432
593
  ----------- -----------
433
594
  """
434
- if self.tops_align:
595
+ if self.panel_tops_align:
596
+ return
597
+
598
+ values = np.array(self.panel_tops) - min(self.panel_tops)
599
+ for item, value in zip(self.nodes, values):
600
+ if isinstance(item, LayoutSpaces):
601
+ item.t.margin_alignment = value
602
+ else:
603
+ item.set_top_margin_alignment(value)
604
+
605
+ del self.panel_tops
606
+
607
+ def align_bottom_tags(self):
608
+ if self.bottom_tags_align:
435
609
  return
436
610
 
437
- values = np.array(self.tops) - min(self.tops)
611
+ values = cast(
612
+ "Sequence[float]",
613
+ max(self.bottom_tag_heights) - np.array(self.bottom_tag_heights),
614
+ )
438
615
  for item, value in zip(self.nodes, values):
439
616
  if isinstance(item, LayoutSpaces):
440
- item.t.alignment_margin = value
617
+ item.l.tag_alignment = value
441
618
  else:
442
- item.set_top_alignment_margin(value)
619
+ item.set_bottom_tag_alignment(value)
620
+
621
+ def align_top_tags(self):
622
+ if self.top_tags_align:
623
+ return
624
+
625
+ values = cast(
626
+ "Sequence[float]",
627
+ max(self.top_tag_heights) - np.array(self.top_tag_heights),
628
+ )
629
+ for item, value in zip(self.nodes, values):
630
+ if isinstance(item, LayoutSpaces):
631
+ item.t.tag_alignment = value
632
+ else:
633
+ item.set_top_tag_alignment(value)
634
+
635
+ def align_bottom_axis_titles(self):
636
+ if self.bottom_axis_titles_align:
637
+ pass
638
+
639
+ values = max(self.bottom_axis_title_clearances) - np.array(
640
+ self.bottom_axis_title_clearances
641
+ )
642
+ # We ignore 0 values since they can undo values
643
+ # set to align this composition with an outer one.
644
+ for item, value in zip(self.nodes, values):
645
+ if value == 0:
646
+ continue
647
+ if isinstance(item, LayoutSpaces):
648
+ item.b.axis_title_alignment = value
649
+ else:
650
+ item.set_bottom_axis_title_alignment(value)
651
+
652
+ @cached_property
653
+ def panel_lefts(self):
654
+ left_item = self.nodes[0]
655
+ if isinstance(left_item, LayoutSpaces):
656
+ return [left_item.l.panel_left]
657
+ else:
658
+ return left_item.panel_lefts
443
659
 
444
- del self.tops
660
+ @cached_property
661
+ def panel_rights(self):
662
+ right_item = self.nodes[-1]
663
+ if isinstance(right_item, LayoutSpaces):
664
+ return [right_item.r.panel_right]
665
+ else:
666
+ return right_item.panel_rights
445
667
 
446
- def set_top_alignment_margin(self, value: float):
668
+ @cached_property
669
+ def panel_bottoms(self):
670
+ values = []
447
671
  for item in self.nodes:
448
672
  if isinstance(item, LayoutSpaces):
449
- item.t.alignment_margin = value
673
+ values.append(item.b.panel_bottom)
450
674
  else:
451
- item.set_top_alignment_margin(value)
675
+ values.append(max(item.panel_bottoms))
676
+ return values
677
+
678
+ @cached_property
679
+ def panel_tops(self):
680
+ values = []
681
+ for item in self.nodes:
682
+ if isinstance(item, LayoutSpaces):
683
+ values.append(item.t.panel_top)
684
+ else:
685
+ values.append(min(item.panel_tops))
686
+ return values
452
687
 
453
688
  @property
454
689
  def panel_width(self) -> float:
455
- """
456
- A representative for width for panels of the nodes
457
- """
458
690
  return sum(self.panel_widths)
459
691
 
460
692
  @property
461
693
  def panel_height(self) -> float:
462
- """
463
- A representative for height for panels of the nodes
464
- """
465
694
  return float(np.mean(self.panel_heights))
466
695
 
467
696
  @property
468
697
  def plot_width(self) -> float:
469
- """
470
- A representative for width for plots of the nodes
471
- """
472
698
  return sum(self.plot_widths)
473
699
 
474
700
  @property
475
701
  def plot_height(self) -> float:
476
- """
477
- A representative for height for plots of the nodes
478
- """
479
702
  return max(self.plot_heights)
480
703
 
481
- def resize(self):
482
- """
483
- Resize the widths of gridspec so that panels have equal widths
484
- """
485
- # The new width of each panel is the average width of all
486
- # the panels plus all the space to the left and right
487
- # of the panels.
488
- plot_widths = np.array(self.plot_widths)
489
- panel_widths = np.array(self.panel_widths)
490
- non_panel_space = plot_widths - panel_widths
491
- new_plot_widths = panel_widths.mean() + non_panel_space
492
- width_ratios = new_plot_widths / new_plot_widths.min()
493
- self.gridspec.set_width_ratios(width_ratios)
494
- self.resize_sub_compositions()
704
+ @cached_property
705
+ def left_tag_width(self) -> float:
706
+ return self.left_tag_widths[0]
707
+
708
+ @cached_property
709
+ def right_tag_width(self) -> float:
710
+ return self.right_tag_widths[-1]
711
+
712
+ @cached_property
713
+ def bottom_tag_height(self) -> float:
714
+ return max(self.bottom_tag_heights)
715
+
716
+ @cached_property
717
+ def top_tag_height(self) -> float:
718
+ return max(self.top_tag_heights)
719
+
720
+ @cached_property
721
+ def left_axis_title_clearance(self) -> float:
722
+ return self.left_axis_title_clearances[0]
723
+
724
+ @cached_property
725
+ def bottom_axis_title_clearance(self) -> float:
726
+ return max(self.bottom_axis_title_clearances)
727
+
728
+ def set_left_margin_alignment(self, value: float):
729
+ left_item = self.nodes[0]
730
+ if isinstance(left_item, LayoutSpaces):
731
+ left_item.l.margin_alignment = value
732
+ else:
733
+ left_item.set_left_margin_alignment(value)
734
+
735
+ def set_right_margin_alignment(self, value: float):
736
+ right_item = self.nodes[-1]
737
+ if isinstance(right_item, LayoutSpaces):
738
+ right_item.r.margin_alignment = value
739
+ else:
740
+ right_item.set_right_margin_alignment(value)
741
+
742
+ def set_bottom_margin_alignment(self, value: float):
743
+ for item in self.nodes:
744
+ if isinstance(item, LayoutSpaces):
745
+ item.b.margin_alignment = value
746
+ else:
747
+ item.set_bottom_margin_alignment(value)
748
+
749
+ def set_top_margin_alignment(self, value: float):
750
+ for item in self.nodes:
751
+ if isinstance(item, LayoutSpaces):
752
+ item.t.margin_alignment = value
753
+ else:
754
+ item.set_top_margin_alignment(value)
755
+
756
+ def set_bottom_tag_alignment(self, value: float):
757
+ for item in self.nodes:
758
+ if isinstance(item, LayoutSpaces):
759
+ item.l.tag_alignment = value
760
+ else:
761
+ item.set_bottom_tag_alignment(value)
762
+
763
+ def set_top_tag_alignment(self, value: float):
764
+ for item in self.nodes:
765
+ if isinstance(item, LayoutSpaces):
766
+ item.t.tag_alignment = value
767
+ else:
768
+ item.set_top_tag_alignment(value)
769
+
770
+ def set_bottom_axis_title_alignment(self, value: float):
771
+ for item in self.nodes:
772
+ if isinstance(item, LayoutSpaces):
773
+ item.b.axis_title_alignment = value
774
+ else:
775
+ item.set_bottom_axis_title_alignment(value)
776
+
777
+ def set_left_axis_title_alignment(self, value: float):
778
+ left_item = self.nodes[0]
779
+ if isinstance(left_item, LayoutSpaces):
780
+ left_item.l.axis_title_alignment = value
781
+ else:
782
+ left_item.set_left_axis_title_alignment(value)
495
783
 
496
784
 
497
785
  @dataclass
@@ -513,21 +801,34 @@ class RowsTree(LayoutTree):
513
801
  """
514
802
 
515
803
  def align(self):
516
- self.align_lefts()
517
- self.align_rights()
804
+ self.align_left_tags()
805
+ self.align_right_tags()
806
+ self.align_panel_lefts()
807
+ self.align_panel_rights()
518
808
  self.align_sub_compositions()
519
809
 
520
- @cached_property
521
- def lefts(self):
522
- values = []
523
- for item in self.nodes:
524
- if isinstance(item, LayoutSpaces):
525
- values.append(item.l.left)
526
- else:
527
- values.append(max(item.lefts))
528
- return values
810
+ def align_axis_titles(self):
811
+ self.align_left_axis_titles()
812
+ for tree in self.sub_compositions:
813
+ tree.align_axis_titles()
814
+
815
+ def resize(self):
816
+ """
817
+ Resize the heights of gridspec so that panels have equal heights
529
818
 
530
- def align_lefts(self):
819
+ This method resizes (recursively) the contained compositions
820
+ """
821
+ # The new height of each panel is the average width of all
822
+ # the panels plus all the space above and below the panels.
823
+ plot_heights = np.array(self.plot_heights)
824
+ panel_heights = np.array(self.panel_heights)
825
+ non_panel_space = plot_heights - panel_heights
826
+ new_plot_heights = panel_heights.mean() + non_panel_space
827
+ height_ratios = new_plot_heights / new_plot_heights.max()
828
+ self.gridspec.set_height_ratios(height_ratios)
829
+ self.resize_sub_compositions()
830
+
831
+ def align_panel_lefts(self):
531
832
  """
532
833
  Align the immediate left edges in this composition
533
834
 
@@ -541,51 +842,19 @@ class RowsTree(LayoutTree):
541
842
  | # | | # |
542
843
  ----------- -----------
543
844
  """
544
- if self.lefts_align:
845
+ if self.panel_lefts_align:
545
846
  return
546
847
 
547
- values = max(self.lefts) - np.array(self.lefts)
848
+ values = max(self.panel_lefts) - np.array(self.panel_lefts)
548
849
  for item, value in zip(self.nodes, values):
549
850
  if isinstance(item, LayoutSpaces):
550
- item.l.alignment_margin = value
851
+ item.l.margin_alignment = value
551
852
  else:
552
- item.set_left_alignment_margin(value)
553
-
554
- del self.lefts
555
-
556
- def set_left_alignment_margin(self, value: float):
557
- for item in self.nodes:
558
- if isinstance(item, LayoutSpaces):
559
- item.l.alignment_margin = value
560
- else:
561
- item.set_left_alignment_margin(value)
562
-
563
- @cached_property
564
- def bottoms(self):
565
- bottom_item = self.nodes[-1]
566
- if isinstance(bottom_item, LayoutSpaces):
567
- return [bottom_item.b.bottom]
568
- else:
569
- return bottom_item.bottoms
570
-
571
- def set_bottom_alignment_margin(self, value: float):
572
- bottom_item = self.nodes[-1]
573
- if isinstance(bottom_item, LayoutSpaces):
574
- bottom_item.b.alignment_margin = value
575
- else:
576
- bottom_item.set_bottom_alignment_margin(value)
853
+ item.set_left_margin_alignment(value)
577
854
 
578
- @cached_property
579
- def rights(self):
580
- values = []
581
- for item in self.nodes:
582
- if isinstance(item, LayoutSpaces):
583
- values.append(item.r.right)
584
- else:
585
- values.append(min(item.rights))
586
- return values
855
+ del self.panel_lefts
587
856
 
588
- def align_rights(self):
857
+ def align_panel_rights(self):
589
858
  """
590
859
  Align the immediate right edges in this composition
591
860
 
@@ -599,80 +868,209 @@ class RowsTree(LayoutTree):
599
868
  | #| | # |
600
869
  ----------- -----------
601
870
  """
602
- if self.rights_align:
871
+ if self.panel_rights_align:
603
872
  return
604
873
 
605
- values = np.array(self.rights) - min(self.rights)
874
+ values = np.array(self.panel_rights) - min(self.panel_rights)
606
875
  for item, value in zip(self.nodes, values):
607
876
  if isinstance(item, LayoutSpaces):
608
- item.r.alignment_margin = value
877
+ item.r.margin_alignment = value
609
878
  else:
610
- item.set_right_alignment_margin(value)
879
+ item.set_right_margin_alignment(value)
880
+
881
+ del self.panel_rights
611
882
 
612
- del self.rights
883
+ def align_left_tags(self):
884
+ """
885
+ Make all the left tags takeup the same amount of space
886
+
887
+
888
+ Given
889
+
890
+ V
891
+ ------------------------------------
892
+ | plot_margin | tag | artists |
893
+ |------------------------------------|
894
+ | plot_margin | A long tag | artists |
895
+ ------------------------------------
896
+
897
+ V
898
+ ------------------------------------
899
+ | plot_margin | #######tag | artists |
900
+ |------------------------------------|
901
+ | plot_margin | A long tag | artists |
902
+ ------------------------------------
903
+ """
904
+ if self.left_tags_align:
905
+ return
906
+
907
+ values = cast(
908
+ "Sequence[float]",
909
+ max(self.left_tag_widths) - np.array(self.left_tag_widths),
910
+ )
911
+ for item, value in zip(self.nodes, values):
912
+ if isinstance(item, LayoutSpaces):
913
+ item.l.tag_alignment = value
914
+ else:
915
+ item.set_left_tag_alignment(value)
613
916
 
614
- def set_right_alignment_margin(self, value: float):
917
+ def align_right_tags(self):
918
+ if self.right_tags_align:
919
+ return
920
+
921
+ values = cast(
922
+ "Sequence[float]",
923
+ max(self.right_tag_widths) - np.array(self.right_tag_widths),
924
+ )
925
+ for item, value in zip(self.nodes, values):
926
+ if isinstance(item, LayoutSpaces):
927
+ item.r.tag_alignment = value
928
+ else:
929
+ item.set_right_tag_alignment(value)
930
+
931
+ def align_left_axis_titles(self):
932
+ if self.left_axis_titles_align:
933
+ pass
934
+
935
+ values = max(self.left_axis_title_clearances) - np.array(
936
+ self.left_axis_title_clearances
937
+ )
938
+ for item, value in zip(self.nodes, values):
939
+ if value == 0:
940
+ continue
941
+ if isinstance(item, LayoutSpaces):
942
+ item.l.axis_title_alignment = value
943
+ else:
944
+ item.set_left_axis_title_alignment(value)
945
+
946
+ @cached_property
947
+ def panel_lefts(self):
948
+ values = []
615
949
  for item in self.nodes:
616
950
  if isinstance(item, LayoutSpaces):
617
- item.r.alignment_margin = value
951
+ values.append(item.l.panel_left)
618
952
  else:
619
- item.set_right_alignment_margin(value)
953
+ values.append(max(item.panel_lefts))
954
+ return values
620
955
 
621
956
  @cached_property
622
- def tops(self):
623
- top_item = self.nodes[0]
624
- if isinstance(top_item, LayoutSpaces):
625
- return [top_item.t.top]
957
+ def panel_rights(self):
958
+ values = []
959
+ for item in self.nodes:
960
+ if isinstance(item, LayoutSpaces):
961
+ values.append(item.r.panel_right)
962
+ else:
963
+ values.append(min(item.panel_rights))
964
+ return values
965
+
966
+ @cached_property
967
+ def panel_bottoms(self):
968
+ bottom_item = self.nodes[-1]
969
+ if isinstance(bottom_item, LayoutSpaces):
970
+ return [bottom_item.b.panel_bottom]
626
971
  else:
627
- return top_item.tops
972
+ return bottom_item.panel_bottoms
628
973
 
629
- def set_top_alignment_margin(self, value: float):
974
+ @cached_property
975
+ def panel_tops(self):
630
976
  top_item = self.nodes[0]
631
977
  if isinstance(top_item, LayoutSpaces):
632
- top_item.t.alignment_margin = value
978
+ return [top_item.t.panel_top]
633
979
  else:
634
- top_item.set_top_alignment_margin(value)
980
+ return top_item.panel_tops
635
981
 
636
982
  @property
637
983
  def panel_width(self) -> float:
638
- """
639
- A representative for width for panels of the nodes
640
- """
641
984
  return float(np.mean(self.panel_widths))
642
985
 
643
986
  @property
644
987
  def panel_height(self) -> float:
645
- """
646
- A representative for height for panels of the nodes
647
- """
648
988
  return sum(self.panel_heights)
649
989
 
650
990
  @property
651
991
  def plot_width(self) -> float:
652
- """
653
- A representative for width for plots of the nodes
654
- """
655
992
  return max(self.plot_widths)
656
993
 
657
994
  @property
658
995
  def plot_height(self) -> float:
659
- """
660
- A representative for height for plots of the nodes
661
- """
662
996
  return sum(self.plot_heights)
663
997
 
664
- def resize(self):
665
- """
666
- Resize the heights of gridspec so that panels have equal heights
998
+ @cached_property
999
+ def left_tag_width(self) -> float:
1000
+ return max(self.left_tag_widths)
667
1001
 
668
- This method resizes (recursively) the contained compositions
669
- """
670
- # The new height of each panel is the average width of all
671
- # the panels plus all the space above and below the panels.
672
- plot_heights = np.array(self.plot_heights)
673
- panel_heights = np.array(self.panel_heights)
674
- non_panel_space = plot_heights - panel_heights
675
- new_plot_heights = panel_heights.mean() + non_panel_space
676
- height_ratios = new_plot_heights / new_plot_heights.max()
677
- self.gridspec.set_height_ratios(height_ratios)
678
- self.resize_sub_compositions()
1002
+ @cached_property
1003
+ def right_tag_width(self) -> float:
1004
+ return max(self.right_tag_widths)
1005
+
1006
+ @cached_property
1007
+ def top_tag_height(self) -> float:
1008
+ return self.top_tag_heights[0]
1009
+
1010
+ @cached_property
1011
+ def bottom_tag_height(self) -> float:
1012
+ return self.bottom_tag_heights[-1]
1013
+
1014
+ @cached_property
1015
+ def left_axis_title_clearance(self) -> float:
1016
+ return max(self.left_axis_title_clearances)
1017
+
1018
+ @cached_property
1019
+ def bottom_axis_title_clearance(self) -> float:
1020
+ return self.bottom_axis_title_clearances[-1]
1021
+
1022
+ def set_left_margin_alignment(self, value: float):
1023
+ for item in self.nodes:
1024
+ if isinstance(item, LayoutSpaces):
1025
+ item.l.margin_alignment = value
1026
+ else:
1027
+ item.set_left_margin_alignment(value)
1028
+
1029
+ def set_right_margin_alignment(self, value: float):
1030
+ for item in self.nodes:
1031
+ if isinstance(item, LayoutSpaces):
1032
+ item.r.margin_alignment = value
1033
+ else:
1034
+ item.set_right_margin_alignment(value)
1035
+
1036
+ def set_bottom_margin_alignment(self, value: float):
1037
+ bottom_item = self.nodes[-1]
1038
+ if isinstance(bottom_item, LayoutSpaces):
1039
+ bottom_item.b.margin_alignment = value
1040
+ else:
1041
+ bottom_item.set_bottom_margin_alignment(value)
1042
+
1043
+ def set_top_margin_alignment(self, value: float):
1044
+ top_item = self.nodes[0]
1045
+ if isinstance(top_item, LayoutSpaces):
1046
+ top_item.t.margin_alignment = value
1047
+ else:
1048
+ top_item.set_top_margin_alignment(value)
1049
+
1050
+ def set_left_tag_alignment(self, value: float):
1051
+ for item in self.nodes:
1052
+ if isinstance(item, LayoutSpaces):
1053
+ item.l.tag_alignment = value
1054
+ else:
1055
+ item.set_left_tag_alignment(value)
1056
+
1057
+ def set_right_tag_alignment(self, value: float):
1058
+ for item in self.nodes:
1059
+ if isinstance(item, LayoutSpaces):
1060
+ item.r.tag_alignment = value
1061
+ else:
1062
+ item.set_right_tag_alignment(value)
1063
+
1064
+ def set_left_axis_title_alignment(self, value: float):
1065
+ for item in self.nodes:
1066
+ if isinstance(item, LayoutSpaces):
1067
+ item.l.axis_title_alignment = value
1068
+ else:
1069
+ item.set_left_axis_title_alignment(value)
1070
+
1071
+ def set_bottom_axis_title_alignment(self, value: float):
1072
+ bottom_item = self.nodes[-1]
1073
+ if isinstance(bottom_item, LayoutSpaces):
1074
+ bottom_item.b.axis_title_alignment = value
1075
+ else:
1076
+ bottom_item.set_bottom_axis_title_alignment(value)