plotnine 0.15.3__py3-none-any.whl → 0.16.0a2__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 (61) hide show
  1. plotnine/_mpl/gridspec.py +50 -6
  2. plotnine/_mpl/layout_manager/__init__.py +2 -5
  3. plotnine/_mpl/layout_manager/_composition_layout_items.py +98 -0
  4. plotnine/_mpl/layout_manager/_composition_side_space.py +461 -0
  5. plotnine/_mpl/layout_manager/_engine.py +19 -58
  6. plotnine/_mpl/layout_manager/_grid.py +94 -0
  7. plotnine/_mpl/layout_manager/_layout_tree.py +402 -817
  8. plotnine/_mpl/layout_manager/{_layout_items.py → _plot_layout_items.py} +55 -278
  9. plotnine/_mpl/layout_manager/{_spaces.py → _plot_side_space.py} +111 -291
  10. plotnine/_mpl/layout_manager/_side_space.py +176 -0
  11. plotnine/_mpl/utils.py +259 -1
  12. plotnine/_utils/__init__.py +23 -3
  13. plotnine/_utils/context.py +9 -13
  14. plotnine/_utils/dataclasses.py +24 -0
  15. plotnine/_utils/ipython.py +4 -1
  16. plotnine/animation.py +13 -12
  17. plotnine/composition/__init__.py +6 -0
  18. plotnine/composition/_beside.py +13 -11
  19. plotnine/composition/_compose.py +263 -99
  20. plotnine/composition/_plot_annotation.py +75 -0
  21. plotnine/composition/_plot_layout.py +143 -0
  22. plotnine/composition/_plot_spacer.py +1 -1
  23. plotnine/composition/_stack.py +13 -11
  24. plotnine/composition/_types.py +28 -0
  25. plotnine/composition/_wrap.py +60 -0
  26. plotnine/facets/facet.py +9 -12
  27. plotnine/facets/facet_grid.py +2 -2
  28. plotnine/facets/facet_wrap.py +1 -1
  29. plotnine/geoms/geom.py +2 -2
  30. plotnine/geoms/geom_map.py +4 -5
  31. plotnine/geoms/geom_path.py +8 -7
  32. plotnine/geoms/geom_rug.py +6 -10
  33. plotnine/geoms/geom_text.py +5 -5
  34. plotnine/ggplot.py +63 -9
  35. plotnine/guides/guide.py +24 -6
  36. plotnine/guides/guide_colorbar.py +88 -46
  37. plotnine/guides/guide_legend.py +47 -20
  38. plotnine/guides/guides.py +2 -2
  39. plotnine/iapi.py +17 -1
  40. plotnine/scales/scale.py +1 -1
  41. plotnine/stats/binning.py +15 -43
  42. plotnine/stats/smoothers.py +7 -3
  43. plotnine/stats/stat.py +2 -2
  44. plotnine/stats/stat_density_2d.py +10 -6
  45. plotnine/stats/stat_pointdensity.py +8 -1
  46. plotnine/stats/stat_qq.py +5 -5
  47. plotnine/stats/stat_qq_line.py +6 -1
  48. plotnine/stats/stat_sina.py +19 -20
  49. plotnine/stats/stat_summary.py +4 -2
  50. plotnine/stats/stat_summary_bin.py +7 -1
  51. plotnine/themes/elements/element_line.py +2 -0
  52. plotnine/themes/elements/element_text.py +12 -1
  53. plotnine/themes/theme.py +18 -24
  54. plotnine/themes/themeable.py +17 -3
  55. plotnine/typing.py +9 -2
  56. {plotnine-0.15.3.dist-info → plotnine-0.16.0a2.dist-info}/METADATA +3 -3
  57. {plotnine-0.15.3.dist-info → plotnine-0.16.0a2.dist-info}/RECORD +60 -52
  58. {plotnine-0.15.3.dist-info → plotnine-0.16.0a2.dist-info}/WHEEL +1 -1
  59. plotnine/composition/_plotspec.py +0 -50
  60. {plotnine-0.15.3.dist-info → plotnine-0.16.0a2.dist-info}/licenses/LICENSE +0 -0
  61. {plotnine-0.15.3.dist-info → plotnine-0.16.0a2.dist-info}/top_level.txt +0 -0
@@ -1,20 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
- from dataclasses import dataclass
4
3
  from itertools import chain
5
- from typing import TYPE_CHECKING, cast
4
+ from typing import TYPE_CHECKING
6
5
 
7
6
  from matplotlib.text import Text
8
7
 
9
8
  from plotnine._mpl.patches import StripTextPatch
10
- from plotnine._utils import ha_as_float, va_as_float
11
9
  from plotnine.exceptions import PlotnineError
12
10
 
13
11
  from ..utils import (
14
- bbox_in_figure_space,
12
+ ArtistGeometry,
13
+ JustifyBoundaries,
14
+ TextJustifier,
15
15
  get_subplotspecs,
16
16
  rel_position,
17
- tight_bbox_in_figure_space,
18
17
  )
19
18
 
20
19
  if TYPE_CHECKING:
@@ -22,15 +21,12 @@ if TYPE_CHECKING:
22
21
  Any,
23
22
  Iterator,
24
23
  Literal,
25
- Sequence,
26
24
  TypeAlias,
27
25
  )
28
26
 
29
- from matplotlib.artist import Artist
30
27
  from matplotlib.axes import Axes
31
28
  from matplotlib.axis import Tick
32
- from matplotlib.backend_bases import RendererBase
33
- from matplotlib.transforms import Bbox, Transform
29
+ from matplotlib.transforms import Transform
34
30
 
35
31
  from plotnine import ggplot
36
32
  from plotnine._mpl.offsetbox import FlexibleAnchoredOffsetbox
@@ -38,12 +34,10 @@ if TYPE_CHECKING:
38
34
  from plotnine.iapi import legend_artists
39
35
  from plotnine.themes.elements import margin as Margin
40
36
  from plotnine.typing import (
41
- HorizontalJustification,
42
37
  StripPosition,
43
- VerticalJustification,
44
38
  )
45
39
 
46
- from ._spaces import LayoutSpaces
40
+ from ._plot_side_space import PlotSideSpaces
47
41
 
48
42
  AxesLocation: TypeAlias = Literal[
49
43
  "all", "first_row", "last_row", "first_col", "last_col"
@@ -64,140 +58,12 @@ if TYPE_CHECKING:
64
58
  )
65
59
 
66
60
 
67
- @dataclass
68
- class Calc:
69
- """
70
- Calculate space taken up by an artist
71
- """
72
-
73
- # fig: Figure
74
- # renderer: RendererBase
75
- plot: ggplot
76
-
77
- def __post_init__(self):
78
- self.figure = self.plot.figure
79
- self.renderer = cast("RendererBase", self.plot.figure._get_renderer()) # pyright: ignore
80
-
81
- def bbox(self, artist: Artist) -> Bbox:
82
- """
83
- Bounding box of artist in figure coordinates
84
- """
85
- return bbox_in_figure_space(artist, self.figure, self.renderer)
86
-
87
- def tight_bbox(self, artist: Artist) -> Bbox:
88
- """
89
- Bounding box of artist and its children in figure coordinates
90
- """
91
- return tight_bbox_in_figure_space(artist, self.figure, self.renderer)
92
-
93
- def width(self, artist: Artist) -> float:
94
- """
95
- Width of artist in figure space
96
- """
97
- return self.bbox(artist).width
98
-
99
- def tight_width(self, artist: Artist) -> float:
100
- """
101
- Width of artist and its children in figure space
102
- """
103
- return self.tight_bbox(artist).width
104
-
105
- def height(self, artist: Artist) -> float:
106
- """
107
- Height of artist in figure space
108
- """
109
- return self.bbox(artist).height
110
-
111
- def tight_height(self, artist: Artist) -> float:
112
- """
113
- Height of artist and its children in figure space
114
- """
115
- return self.tight_bbox(artist).height
116
-
117
- def size(self, artist: Artist) -> tuple[float, float]:
118
- """
119
- (width, height) of artist in figure space
120
- """
121
- bbox = self.bbox(artist)
122
- return (bbox.width, bbox.height)
123
-
124
- def tight_size(self, artist: Artist) -> tuple[float, float]:
125
- """
126
- (width, height) of artist and its children in figure space
127
- """
128
- bbox = self.tight_bbox(artist)
129
- return (bbox.width, bbox.height)
130
-
131
- def left_x(self, artist: Artist) -> float:
132
- """
133
- x value of the left edge of the artist
134
-
135
- ---
136
- x |
137
- ---
138
- """
139
- return self.bbox(artist).min[0]
140
-
141
- def right_x(self, artist: Artist) -> float:
142
- """
143
- x value of the left edge of the artist
144
-
145
- ---
146
- | x
147
- ---
148
- """
149
- return self.bbox(artist).max[0]
150
-
151
- def top_y(self, artist: Artist) -> float:
152
- """
153
- y value of the top edge of the artist
154
-
155
- -y-
156
- | |
157
- ---
158
- """
159
- return self.bbox(artist).max[1]
160
-
161
- def bottom_y(self, artist: Artist) -> float:
162
- """
163
- y value of the bottom edge of the artist
164
-
165
- ---
166
- | |
167
- -y-
168
- """
169
- return self.bbox(artist).min[1]
170
-
171
- def max_width(self, artists: Sequence[Artist]) -> float:
172
- """
173
- Return the maximum width of list of artists
174
- """
175
- widths = [
176
- bbox_in_figure_space(a, self.figure, self.renderer).width
177
- for a in artists
178
- ]
179
- return max(widths) if len(widths) else 0
180
-
181
- def max_height(self, artists: Sequence[Artist]) -> float:
182
- """
183
- Return the maximum height of list of artists
184
- """
185
- heights = [
186
- bbox_in_figure_space(a, self.figure, self.renderer).height
187
- for a in artists
188
- ]
189
- return max(heights) if len(heights) else 0
190
-
191
-
192
- @dataclass
193
- class LayoutItems:
61
+ class PlotLayoutItems:
194
62
  """
195
63
  Objects required to compute the layout
196
64
  """
197
65
 
198
- plot: ggplot
199
-
200
- def __post_init__(self):
66
+ def __init__(self, plot: ggplot):
201
67
  def get(name: str) -> Any:
202
68
  """
203
69
  Return themeable target or None
@@ -210,7 +76,8 @@ class LayoutItems:
210
76
  return None
211
77
  return t
212
78
 
213
- self.calc = Calc(self.plot)
79
+ self.plot = plot
80
+ self.geometry = ArtistGeometry(self.plot.figure)
214
81
 
215
82
  self.axis_title_x: Text | None = get("axis_title_x")
216
83
  self.axis_title_y: Text | None = get("axis_title_y")
@@ -326,7 +193,7 @@ class LayoutItems:
326
193
  if isinstance(a, StripTextPatch)
327
194
  else a.draw_info
328
195
  )
329
- h = self.calc.height(a)
196
+ h = self.geometry.height(a)
330
197
  heights.append(max(h + h * info.strip_align, 0))
331
198
 
332
199
  return max(heights)
@@ -352,7 +219,7 @@ class LayoutItems:
352
219
  if isinstance(a, StripTextPatch)
353
220
  else a.draw_info
354
221
  )
355
- w = self.calc.width(a)
222
+ w = self.geometry.width(a)
356
223
  widths.append(max(w + w * info.strip_align, 0))
357
224
 
358
225
  return max(widths)
@@ -362,7 +229,7 @@ class LayoutItems:
362
229
  Return maximum height[figure space] of x ticks
363
230
  """
364
231
  heights = [
365
- self.calc.tight_height(tick.tick1line)
232
+ self.geometry.tight_height(tick.tick1line)
366
233
  for ax in self._filter_axes(location)
367
234
  for tick in self.axis_ticks_x(ax)
368
235
  ]
@@ -373,7 +240,7 @@ class LayoutItems:
373
240
  Return maximum height[figure space] of x tick labels
374
241
  """
375
242
  heights = [
376
- self.calc.tight_height(label) for label in self.axis_text_x(ax)
243
+ self.geometry.tight_height(label) for label in self.axis_text_x(ax)
377
244
  ]
378
245
  return max(heights) if len(heights) else 0
379
246
 
@@ -392,7 +259,7 @@ class LayoutItems:
392
259
  Return maximum width[figure space] of y ticks
393
260
  """
394
261
  widths = [
395
- self.calc.tight_width(tick.tick1line)
262
+ self.geometry.tight_width(tick.tick1line)
396
263
  for ax in self._filter_axes(location)
397
264
  for tick in self.axis_ticks_y(ax)
398
265
  ]
@@ -403,7 +270,7 @@ class LayoutItems:
403
270
  Return maximum width[figure space] of y tick labels
404
271
  """
405
272
  widths = [
406
- self.calc.tight_width(label) for label in self.axis_text_y(ax)
273
+ self.geometry.tight_width(label) for label in self.axis_text_y(ax)
407
274
  ]
408
275
  return max(widths) if len(widths) else 0
409
276
 
@@ -423,9 +290,9 @@ class LayoutItems:
423
290
  """
424
291
  extras = []
425
292
  for ax in self._filter_axes(location):
426
- ax_top_y = self.calc.top_y(ax)
293
+ ax_top_y = self.geometry.top_y(ax)
427
294
  for label in self.axis_text_y(ax):
428
- label_top_y = self.calc.top_y(label)
295
+ label_top_y = self.geometry.top_y(label)
429
296
  extras.append(max(0, label_top_y - ax_top_y))
430
297
 
431
298
  return max(extras) if len(extras) else 0
@@ -436,9 +303,9 @@ class LayoutItems:
436
303
  """
437
304
  extras = []
438
305
  for ax in self._filter_axes(location):
439
- ax_bottom_y = self.calc.bottom_y(ax)
306
+ ax_bottom_y = self.geometry.bottom_y(ax)
440
307
  for label in self.axis_text_y(ax):
441
- label_bottom_y = self.calc.bottom_y(label)
308
+ label_bottom_y = self.geometry.bottom_y(label)
442
309
  protrusion = abs(min(label_bottom_y - ax_bottom_y, 0))
443
310
  extras.append(protrusion)
444
311
 
@@ -450,9 +317,9 @@ class LayoutItems:
450
317
  """
451
318
  extras = []
452
319
  for ax in self._filter_axes(location):
453
- ax_left_x = self.calc.left_x(ax)
320
+ ax_left_x = self.geometry.left_x(ax)
454
321
  for label in self.axis_text_x(ax):
455
- label_left_x = self.calc.left_x(label)
322
+ label_left_x = self.geometry.left_x(label)
456
323
  protrusion = abs(min(label_left_x - ax_left_x, 0))
457
324
  extras.append(protrusion)
458
325
 
@@ -464,21 +331,21 @@ class LayoutItems:
464
331
  """
465
332
  extras = []
466
333
  for ax in self._filter_axes(location):
467
- ax_right_x = self.calc.right_x(ax)
334
+ ax_right_x = self.geometry.right_x(ax)
468
335
  for label in self.axis_text_x(ax):
469
- label_right_x = self.calc.right_x(label)
336
+ label_right_x = self.geometry.right_x(label)
470
337
  extras.append(max(0, label_right_x - ax_right_x))
471
338
 
472
339
  return max(extras) if len(extras) else 0
473
340
 
474
- def _adjust_positions(self, spaces: LayoutSpaces):
341
+ def _move_artists(self, spaces: PlotSideSpaces):
475
342
  """
476
- Set the x,y position of the artists around the panels
343
+ Move the artists to their final positions
477
344
  """
478
345
  theme = self.plot.theme
479
346
  plot_title_position = theme.getp("plot_title_position", "panel")
480
347
  plot_caption_position = theme.getp("plot_caption_position", "panel")
481
- justify = TextJustifier(spaces)
348
+ justify = PlotTextJustifier(spaces)
482
349
 
483
350
  if self.plot_tag:
484
351
  set_plot_tag_position(self.plot_tag, spaces)
@@ -522,7 +389,7 @@ class LayoutItems:
522
389
  self._strip_text_x_background_equal_heights()
523
390
  self._strip_text_y_background_equal_widths()
524
391
 
525
- def _adjust_axis_text_x(self, justify: TextJustifier):
392
+ def _adjust_axis_text_x(self, justify: PlotTextJustifier):
526
393
  """
527
394
  Adjust x-axis text, justifying vertically as necessary
528
395
  """
@@ -547,13 +414,13 @@ class LayoutItems:
547
414
  )
548
415
  for text in texts:
549
416
  height = to_vertical_axis_dimensions(
550
- self.calc.tight_height(text), ax
417
+ self.geometry.tight_height(text), ax
551
418
  )
552
419
  justify.vertically(
553
420
  text, va, -axis_text_row_height, 0, height=height
554
421
  )
555
422
 
556
- def _adjust_axis_text_y(self, justify: TextJustifier):
423
+ def _adjust_axis_text_y(self, justify: PlotTextJustifier):
557
424
  """
558
425
  Adjust x-axis text, justifying horizontally as necessary
559
426
  """
@@ -600,7 +467,7 @@ class LayoutItems:
600
467
  )
601
468
  for text in texts:
602
469
  width = to_horizontal_axis_dimensions(
603
- self.calc.tight_width(text), ax
470
+ self.geometry.tight_width(text), ax
604
471
  )
605
472
  justify.horizontally(
606
473
  text, ha, -axis_text_col_width, 0, width=width
@@ -615,7 +482,9 @@ class LayoutItems:
615
482
  if not self.strip_text_x:
616
483
  return
617
484
 
618
- heights = [self.calc.bbox(t.patch).height for t in self.strip_text_x]
485
+ heights = [
486
+ self.geometry.bbox(t.patch).height for t in self.strip_text_x
487
+ ]
619
488
  max_height = max(heights)
620
489
  relative_heights = [max_height / h for h in heights]
621
490
  for text, scale in zip(self.strip_text_x, relative_heights):
@@ -630,7 +499,7 @@ class LayoutItems:
630
499
  if not self.strip_text_y:
631
500
  return
632
501
 
633
- widths = [self.calc.bbox(t.patch).width for t in self.strip_text_y]
502
+ widths = [self.geometry.bbox(t.patch).width for t in self.strip_text_y]
634
503
  max_width = max(widths)
635
504
  relative_widths = [max_width / w for w in widths]
636
505
  for text, scale in zip(self.strip_text_y, relative_widths):
@@ -644,122 +513,30 @@ def _text_is_visible(text: Text) -> bool:
644
513
  return text.get_visible() and text._text # type: ignore
645
514
 
646
515
 
647
- @dataclass
648
- class TextJustifier:
516
+ class PlotTextJustifier(TextJustifier):
649
517
  """
650
- Justify Text
651
-
652
- The justification methods reinterpret alignment values to be justification
653
- about a span.
518
+ Justify Text about a plot or it's panels
654
519
  """
655
520
 
656
- spaces: LayoutSpaces
657
-
658
- def horizontally(
659
- self,
660
- text: Text,
661
- ha: HorizontalJustification | float,
662
- left: float,
663
- right: float,
664
- width: float | None = None,
665
- ):
666
- """
667
- Horizontally Justify text between left and right
668
- """
669
- rel = ha_as_float(ha)
670
- if width is None:
671
- width = self.spaces.items.calc.width(text)
672
- x = rel_position(rel, width, left, right)
673
- text.set_x(x)
674
- text.set_horizontalalignment("left")
675
-
676
- def vertically(
677
- self,
678
- text: Text,
679
- va: VerticalJustification | float,
680
- bottom: float,
681
- top: float,
682
- height: float | None = None,
683
- ):
684
- """
685
- Vertically Justify text between bottom and top
686
- """
687
- rel = va_as_float(va)
688
-
689
- if height is None:
690
- height = self.spaces.items.calc.height(text)
691
- y = rel_position(rel, height, bottom, top)
692
- text.set_y(y)
693
- text.set_verticalalignment("bottom")
694
-
695
- def horizontally_across_panel(
696
- self, text: Text, ha: HorizontalJustification | float
697
- ):
698
- """
699
- Horizontally Justify text accross the panel(s) width
700
- """
701
- self.horizontally(
702
- text, ha, self.spaces.l.panel_left, self.spaces.r.panel_right
521
+ def __init__(self, spaces: PlotSideSpaces):
522
+ boundaries = JustifyBoundaries(
523
+ plot_left=spaces.l.plot_left,
524
+ plot_right=spaces.r.plot_right,
525
+ plot_bottom=spaces.b.plot_bottom,
526
+ plot_top=spaces.t.plot_top,
527
+ panel_left=spaces.l.panel_left,
528
+ panel_right=spaces.r.panel_right,
529
+ panel_bottom=spaces.b.panel_bottom,
530
+ panel_top=spaces.t.panel_top,
703
531
  )
704
-
705
- def horizontally_across_plot(
706
- self, text: Text, ha: HorizontalJustification | float
707
- ):
708
- """
709
- Horizontally Justify text across the plot's width
710
- """
711
- self.horizontally(
712
- text, ha, self.spaces.l.plot_left, self.spaces.r.plot_right
713
- )
714
-
715
- def vertically_along_panel(
716
- self, text: Text, va: VerticalJustification | float
717
- ):
718
- """
719
- Horizontally Justify text along the panel(s) height
720
- """
721
- self.vertically(
722
- text, va, self.spaces.b.panel_bottom, self.spaces.t.panel_top
723
- )
724
-
725
- def vertically_along_plot(
726
- self, text: Text, va: VerticalJustification | float
727
- ):
728
- """
729
- Vertically Justify text along the plot's height
730
- """
731
- self.vertically(
732
- text, va, self.spaces.b.plot_bottom, self.spaces.t.plot_top
733
- )
734
-
735
- def horizontally_about(
736
- self, text: Text, ratio: float, how: Literal["panel", "plot"]
737
- ):
738
- """
739
- Horizontally Justify text across the panel or plot
740
- """
741
- if how == "panel":
742
- self.horizontally_across_panel(text, ratio)
743
- else:
744
- self.horizontally_across_plot(text, ratio)
745
-
746
- def vertically_about(
747
- self, text: Text, ratio: float, how: Literal["panel", "plot"]
748
- ):
749
- """
750
- Vertically Justify text along the panel or plot
751
- """
752
- if how == "panel":
753
- self.vertically_along_panel(text, ratio)
754
- else:
755
- self.vertically_along_plot(text, ratio)
532
+ super().__init__(spaces.plot.figure, boundaries)
756
533
 
757
534
 
758
- def set_legends_position(legends: legend_artists, spaces: LayoutSpaces):
535
+ def set_legends_position(legends: legend_artists, spaces: PlotSideSpaces):
759
536
  """
760
537
  Place legend on the figure and justify is a required
761
538
  """
762
- panels_gs = spaces.plot.facet._panels_gridspec
539
+ panels_gs = spaces.plot._sub_gridspec
763
540
  params = panels_gs.get_subplot_params()
764
541
  transFigure = spaces.plot.figure.transFigure
765
542
 
@@ -833,12 +610,12 @@ def set_legends_position(legends: legend_artists, spaces: LayoutSpaces):
833
610
  set_position(l.box, l.position, l.justification, transPanels)
834
611
 
835
612
 
836
- def set_plot_tag_position(tag: Text, spaces: LayoutSpaces):
613
+ def set_plot_tag_position(tag: Text, spaces: PlotSideSpaces):
837
614
  """
838
615
  Set the postion of the plot_tag
839
616
  """
840
617
  theme = spaces.plot.theme
841
- panels_gs = spaces.plot.facet._panels_gridspec
618
+ panels_gs = spaces.plot._sub_gridspec
842
619
  location: TagLocation = theme.getp("plot_tag_location")
843
620
  position: TagPosition = theme.getp("plot_tag_position")
844
621
  margin = theme.get_margin("plot_tag")
@@ -866,7 +643,7 @@ def set_plot_tag_position(tag: Text, spaces: LayoutSpaces):
866
643
 
867
644
  # Calculate the position when the tag has no margins
868
645
  rel_x, rel_y = lookup[position]
869
- width, height = spaces.items.calc.size(tag)
646
+ width, height = spaces.items.geometry.size(tag)
870
647
  x = rel_position(rel_x, width, x1, x2)
871
648
  y = rel_position(rel_y, height, y1, y2)
872
649
 
@@ -894,7 +671,7 @@ def set_plot_tag_position(tag: Text, spaces: LayoutSpaces):
894
671
  tag.set_position(position)
895
672
 
896
673
 
897
- def set_plot_tag_position_in_margin(tag: Text, spaces: LayoutSpaces):
674
+ def set_plot_tag_position_in_margin(tag: Text, spaces: PlotSideSpaces):
898
675
  """
899
676
  Place the tag in an inner margin around the plot
900
677
 
@@ -932,7 +709,7 @@ def set_plot_tag_position_in_margin(tag: Text, spaces: LayoutSpaces):
932
709
  tag.set_y(y)
933
710
  tag.set_verticalalignment("bottom")
934
711
 
935
- justify = TextJustifier(spaces)
712
+ justify = PlotTextJustifier(spaces)
936
713
  if position in ("left", "right"):
937
714
  justify.vertically_along_plot(tag, va)
938
715
  elif position in ("top", "bottom"):