plotnine 0.15.0a1__py3-none-any.whl → 0.15.0a3__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.
- plotnine/_mpl/layout_manager/_layout_items.py +85 -23
- plotnine/_mpl/layout_manager/_layout_tree.py +16 -6
- plotnine/_mpl/layout_manager/_spaces.py +5 -5
- plotnine/_mpl/patches.py +70 -34
- plotnine/_mpl/text.py +150 -63
- plotnine/_mpl/utils.py +1 -1
- plotnine/_utils/__init__.py +30 -2
- plotnine/doctools.py +1 -1
- plotnine/facets/strips.py +17 -28
- plotnine/geoms/annotation_logticks.py +7 -8
- plotnine/geoms/annotation_stripes.py +6 -6
- plotnine/geoms/geom.py +20 -8
- plotnine/geoms/geom_abline.py +3 -2
- plotnine/geoms/geom_blank.py +0 -3
- plotnine/geoms/geom_boxplot.py +4 -4
- plotnine/geoms/geom_crossbar.py +3 -3
- plotnine/geoms/geom_dotplot.py +1 -1
- plotnine/geoms/geom_errorbar.py +2 -2
- plotnine/geoms/geom_errorbarh.py +2 -2
- plotnine/geoms/geom_hline.py +3 -2
- plotnine/geoms/geom_linerange.py +2 -2
- plotnine/geoms/geom_map.py +3 -3
- plotnine/geoms/geom_path.py +10 -11
- plotnine/geoms/geom_point.py +4 -5
- plotnine/geoms/geom_pointrange.py +3 -5
- plotnine/geoms/geom_polygon.py +2 -3
- plotnine/geoms/geom_raster.py +4 -5
- plotnine/geoms/geom_rect.py +3 -4
- plotnine/geoms/geom_ribbon.py +7 -7
- plotnine/geoms/geom_rug.py +1 -1
- plotnine/geoms/geom_segment.py +2 -2
- plotnine/geoms/geom_smooth.py +3 -3
- plotnine/geoms/geom_step.py +2 -2
- plotnine/geoms/geom_text.py +2 -3
- plotnine/geoms/geom_violin.py +4 -5
- plotnine/geoms/geom_vline.py +3 -2
- plotnine/guides/guides.py +1 -1
- plotnine/helpers.py +49 -0
- plotnine/iapi.py +28 -5
- plotnine/layer.py +18 -12
- plotnine/mapping/_eval_environment.py +1 -1
- plotnine/scales/scale_color.py +46 -14
- plotnine/scales/scale_continuous.py +5 -4
- plotnine/scales/scale_datetime.py +28 -14
- plotnine/scales/scale_discrete.py +2 -2
- plotnine/scales/scale_identity.py +10 -2
- plotnine/scales/scale_xy.py +2 -2
- plotnine/stats/binning.py +4 -1
- plotnine/stats/smoothers.py +19 -19
- plotnine/stats/stat.py +15 -25
- plotnine/stats/stat_bin.py +2 -5
- plotnine/stats/stat_bin_2d.py +7 -9
- plotnine/stats/stat_bindot.py +6 -11
- plotnine/stats/stat_boxplot.py +5 -5
- plotnine/stats/stat_count.py +5 -9
- plotnine/stats/stat_density.py +6 -9
- plotnine/stats/stat_density_2d.py +12 -9
- plotnine/stats/stat_ecdf.py +6 -5
- plotnine/stats/stat_ellipse.py +5 -6
- plotnine/stats/stat_function.py +6 -8
- plotnine/stats/stat_hull.py +2 -3
- plotnine/stats/stat_identity.py +1 -2
- plotnine/stats/stat_pointdensity.py +4 -7
- plotnine/stats/stat_qq.py +45 -20
- plotnine/stats/stat_qq_line.py +15 -11
- plotnine/stats/stat_quantile.py +6 -7
- plotnine/stats/stat_sina.py +12 -14
- plotnine/stats/stat_smooth.py +7 -10
- plotnine/stats/stat_sum.py +1 -2
- plotnine/stats/stat_summary.py +6 -9
- plotnine/stats/stat_summary_bin.py +10 -13
- plotnine/stats/stat_unique.py +1 -2
- plotnine/stats/stat_ydensity.py +7 -10
- plotnine/themes/elements/__init__.py +2 -1
- plotnine/themes/elements/margin.py +64 -1
- plotnine/themes/theme_gray.py +5 -3
- plotnine/themes/theme_matplotlib.py +5 -4
- plotnine/themes/theme_seaborn.py +7 -4
- plotnine/themes/theme_void.py +11 -4
- plotnine/themes/themeable.py +2 -2
- plotnine/typing.py +2 -2
- {plotnine-0.15.0a1.dist-info → plotnine-0.15.0a3.dist-info}/METADATA +7 -4
- {plotnine-0.15.0a1.dist-info → plotnine-0.15.0a3.dist-info}/RECORD +86 -85
- {plotnine-0.15.0a1.dist-info → plotnine-0.15.0a3.dist-info}/WHEEL +1 -1
- {plotnine-0.15.0a1.dist-info → plotnine-0.15.0a3.dist-info}/licenses/LICENSE +0 -0
- {plotnine-0.15.0a1.dist-info → plotnine-0.15.0a3.dist-info}/top_level.txt +0 -0
|
@@ -6,6 +6,8 @@ from typing import TYPE_CHECKING, cast
|
|
|
6
6
|
|
|
7
7
|
from matplotlib.text import Text
|
|
8
8
|
|
|
9
|
+
from plotnine._mpl.patches import StripTextPatch
|
|
10
|
+
from plotnine._utils import ha_as_float, va_as_float
|
|
9
11
|
from plotnine.exceptions import PlotnineError
|
|
10
12
|
|
|
11
13
|
from ..utils import (
|
|
@@ -35,7 +37,11 @@ if TYPE_CHECKING:
|
|
|
35
37
|
from plotnine._mpl.text import StripText
|
|
36
38
|
from plotnine.iapi import legend_artists
|
|
37
39
|
from plotnine.themes.elements import margin as Margin
|
|
38
|
-
from plotnine.typing import
|
|
40
|
+
from plotnine.typing import (
|
|
41
|
+
HorizontalJustification,
|
|
42
|
+
StripPosition,
|
|
43
|
+
VerticalJustification,
|
|
44
|
+
)
|
|
39
45
|
|
|
40
46
|
from ._spaces import LayoutSpaces
|
|
41
47
|
|
|
@@ -299,9 +305,9 @@ class LayoutItems:
|
|
|
299
305
|
|
|
300
306
|
return chain(major, minor)
|
|
301
307
|
|
|
302
|
-
def
|
|
308
|
+
def strip_text_x_extra_height(self, position: StripPosition) -> float:
|
|
303
309
|
"""
|
|
304
|
-
Height taken up by the top strips
|
|
310
|
+
Height taken up by the top strips that is outside the panels
|
|
305
311
|
"""
|
|
306
312
|
if not self.strip_text_x:
|
|
307
313
|
return 0
|
|
@@ -311,11 +317,23 @@ class LayoutItems:
|
|
|
311
317
|
for st in self.strip_text_x
|
|
312
318
|
if st.patch.position == position
|
|
313
319
|
]
|
|
314
|
-
return self.calc.max_height(artists)
|
|
315
320
|
|
|
316
|
-
|
|
321
|
+
heights = []
|
|
322
|
+
|
|
323
|
+
for a in artists:
|
|
324
|
+
info = (
|
|
325
|
+
a.text.draw_info
|
|
326
|
+
if isinstance(a, StripTextPatch)
|
|
327
|
+
else a.draw_info
|
|
328
|
+
)
|
|
329
|
+
h = self.calc.height(a)
|
|
330
|
+
heights.append(max(h + h * info.strip_align, 0))
|
|
331
|
+
|
|
332
|
+
return max(heights)
|
|
333
|
+
|
|
334
|
+
def strip_text_y_extra_width(self, position: StripPosition) -> float:
|
|
317
335
|
"""
|
|
318
|
-
Width taken up by the
|
|
336
|
+
Width taken up by the top strips that is outside the panels
|
|
319
337
|
"""
|
|
320
338
|
if not self.strip_text_y:
|
|
321
339
|
return 0
|
|
@@ -325,7 +343,19 @@ class LayoutItems:
|
|
|
325
343
|
for st in self.strip_text_y
|
|
326
344
|
if st.patch.position == position
|
|
327
345
|
]
|
|
328
|
-
|
|
346
|
+
|
|
347
|
+
widths = []
|
|
348
|
+
|
|
349
|
+
for a in artists:
|
|
350
|
+
info = (
|
|
351
|
+
a.text.draw_info
|
|
352
|
+
if isinstance(a, StripTextPatch)
|
|
353
|
+
else a.draw_info
|
|
354
|
+
)
|
|
355
|
+
w = self.calc.width(a)
|
|
356
|
+
widths.append(max(w + w * info.strip_align, 0))
|
|
357
|
+
|
|
358
|
+
return max(widths)
|
|
329
359
|
|
|
330
360
|
def axis_ticks_x_max_height_at(self, location: AxesLocation) -> float:
|
|
331
361
|
"""
|
|
@@ -489,6 +519,8 @@ class LayoutItems:
|
|
|
489
519
|
|
|
490
520
|
self._adjust_axis_text_x(justify)
|
|
491
521
|
self._adjust_axis_text_y(justify)
|
|
522
|
+
self._strip_text_x_background_equal_heights()
|
|
523
|
+
self._strip_text_y_background_equal_widths()
|
|
492
524
|
|
|
493
525
|
def _adjust_axis_text_x(self, justify: TextJustifier):
|
|
494
526
|
"""
|
|
@@ -574,6 +606,36 @@ class LayoutItems:
|
|
|
574
606
|
text, ha, -axis_text_col_width, 0, width=width
|
|
575
607
|
)
|
|
576
608
|
|
|
609
|
+
def _strip_text_x_background_equal_heights(self):
|
|
610
|
+
"""
|
|
611
|
+
Make the strip_text_x_backgrounds have equal heights
|
|
612
|
+
|
|
613
|
+
The smaller heights are expanded to match the largest height
|
|
614
|
+
"""
|
|
615
|
+
if not self.strip_text_x:
|
|
616
|
+
return
|
|
617
|
+
|
|
618
|
+
heights = [self.calc.bbox(t.patch).height for t in self.strip_text_x]
|
|
619
|
+
max_height = max(heights)
|
|
620
|
+
relative_heights = [max_height / h for h in heights]
|
|
621
|
+
for text, scale in zip(self.strip_text_x, relative_heights):
|
|
622
|
+
text.patch.expand = scale
|
|
623
|
+
|
|
624
|
+
def _strip_text_y_background_equal_widths(self):
|
|
625
|
+
"""
|
|
626
|
+
Make the strip_text_y_backgrounds have equal widths
|
|
627
|
+
|
|
628
|
+
The smaller widths are expanded to match the largest width
|
|
629
|
+
"""
|
|
630
|
+
if not self.strip_text_y:
|
|
631
|
+
return
|
|
632
|
+
|
|
633
|
+
widths = [self.calc.bbox(t.patch).width for t in self.strip_text_y]
|
|
634
|
+
max_width = max(widths)
|
|
635
|
+
relative_widths = [max_width / w for w in widths]
|
|
636
|
+
for text, scale in zip(self.strip_text_y, relative_widths):
|
|
637
|
+
text.patch.expand = scale
|
|
638
|
+
|
|
577
639
|
|
|
578
640
|
def _text_is_visible(text: Text) -> bool:
|
|
579
641
|
"""
|
|
@@ -596,7 +658,7 @@ class TextJustifier:
|
|
|
596
658
|
def horizontally(
|
|
597
659
|
self,
|
|
598
660
|
text: Text,
|
|
599
|
-
ha:
|
|
661
|
+
ha: HorizontalJustification | float,
|
|
600
662
|
left: float,
|
|
601
663
|
right: float,
|
|
602
664
|
width: float | None = None,
|
|
@@ -604,8 +666,7 @@ class TextJustifier:
|
|
|
604
666
|
"""
|
|
605
667
|
Horizontally Justify text between left and right
|
|
606
668
|
"""
|
|
607
|
-
|
|
608
|
-
rel = lookup.get(ha, ha) # pyright: ignore[reportCallIssue, reportArgumentType]
|
|
669
|
+
rel = ha_as_float(ha)
|
|
609
670
|
if width is None:
|
|
610
671
|
width = self.spaces.items.calc.width(text)
|
|
611
672
|
x = rel_position(rel, width, left, right)
|
|
@@ -615,7 +676,7 @@ class TextJustifier:
|
|
|
615
676
|
def vertically(
|
|
616
677
|
self,
|
|
617
678
|
text: Text,
|
|
618
|
-
va:
|
|
679
|
+
va: VerticalJustification | float,
|
|
619
680
|
bottom: float,
|
|
620
681
|
top: float,
|
|
621
682
|
height: float | None = None,
|
|
@@ -623,14 +684,7 @@ class TextJustifier:
|
|
|
623
684
|
"""
|
|
624
685
|
Vertically Justify text between bottom and top
|
|
625
686
|
"""
|
|
626
|
-
|
|
627
|
-
"top": 1.0,
|
|
628
|
-
"center": 0.5,
|
|
629
|
-
"baseline": 0.5,
|
|
630
|
-
"center_baseline": 0.5,
|
|
631
|
-
"bottom": 0.0,
|
|
632
|
-
}
|
|
633
|
-
rel = lookup.get(va, va) # pyright: ignore[reportCallIssue, reportArgumentType]
|
|
687
|
+
rel = va_as_float(va)
|
|
634
688
|
|
|
635
689
|
if height is None:
|
|
636
690
|
height = self.spaces.items.calc.height(text)
|
|
@@ -638,13 +692,17 @@ class TextJustifier:
|
|
|
638
692
|
text.set_y(y)
|
|
639
693
|
text.set_verticalalignment("bottom")
|
|
640
694
|
|
|
641
|
-
def horizontally_across_panel(
|
|
695
|
+
def horizontally_across_panel(
|
|
696
|
+
self, text: Text, ha: HorizontalJustification | float
|
|
697
|
+
):
|
|
642
698
|
"""
|
|
643
699
|
Horizontally Justify text accross the panel(s) width
|
|
644
700
|
"""
|
|
645
701
|
self.horizontally(text, ha, self.spaces.l.left, self.spaces.r.right)
|
|
646
702
|
|
|
647
|
-
def horizontally_across_plot(
|
|
703
|
+
def horizontally_across_plot(
|
|
704
|
+
self, text: Text, ha: HorizontalJustification | float
|
|
705
|
+
):
|
|
648
706
|
"""
|
|
649
707
|
Horizontally Justify text across the plot's width
|
|
650
708
|
"""
|
|
@@ -652,13 +710,17 @@ class TextJustifier:
|
|
|
652
710
|
text, ha, self.spaces.l.plot_left, self.spaces.r.plot_right
|
|
653
711
|
)
|
|
654
712
|
|
|
655
|
-
def vertically_along_panel(
|
|
713
|
+
def vertically_along_panel(
|
|
714
|
+
self, text: Text, va: VerticalJustification | float
|
|
715
|
+
):
|
|
656
716
|
"""
|
|
657
717
|
Horizontally Justify text along the panel(s) height
|
|
658
718
|
"""
|
|
659
719
|
self.vertically(text, va, self.spaces.b.bottom, self.spaces.t.top)
|
|
660
720
|
|
|
661
|
-
def vertically_along_plot(
|
|
721
|
+
def vertically_along_plot(
|
|
722
|
+
self, text: Text, va: VerticalJustification | float
|
|
723
|
+
):
|
|
662
724
|
"""
|
|
663
725
|
Vertically Justify text along the plot's height
|
|
664
726
|
"""
|
|
@@ -3,7 +3,7 @@ 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
|
|
|
@@ -528,8 +528,9 @@ class ColumnsTree(LayoutTree):
|
|
|
528
528
|
if self.bottom_tags_align:
|
|
529
529
|
return
|
|
530
530
|
|
|
531
|
-
values =
|
|
532
|
-
|
|
531
|
+
values = cast(
|
|
532
|
+
"Sequence[float]",
|
|
533
|
+
max(self.bottom_tag_heights) - np.array(self.bottom_tag_heights),
|
|
533
534
|
)
|
|
534
535
|
for item, value in zip(self.nodes, values):
|
|
535
536
|
if isinstance(item, LayoutSpaces):
|
|
@@ -541,7 +542,10 @@ class ColumnsTree(LayoutTree):
|
|
|
541
542
|
if self.top_tags_align:
|
|
542
543
|
return
|
|
543
544
|
|
|
544
|
-
values =
|
|
545
|
+
values = cast(
|
|
546
|
+
"Sequence[float]",
|
|
547
|
+
max(self.top_tag_heights) - np.array(self.top_tag_heights),
|
|
548
|
+
)
|
|
545
549
|
for item, value in zip(self.nodes, values):
|
|
546
550
|
if isinstance(item, LayoutSpaces):
|
|
547
551
|
item.t.tag_alignment = value
|
|
@@ -776,7 +780,10 @@ class RowsTree(LayoutTree):
|
|
|
776
780
|
if self.left_tags_align:
|
|
777
781
|
return
|
|
778
782
|
|
|
779
|
-
values =
|
|
783
|
+
values = cast(
|
|
784
|
+
"Sequence[float]",
|
|
785
|
+
max(self.left_tag_widths) - np.array(self.left_tag_widths),
|
|
786
|
+
)
|
|
780
787
|
for item, value in zip(self.nodes, values):
|
|
781
788
|
if isinstance(item, LayoutSpaces):
|
|
782
789
|
item.l.tag_alignment = value
|
|
@@ -787,7 +794,10 @@ class RowsTree(LayoutTree):
|
|
|
787
794
|
if self.right_tags_align:
|
|
788
795
|
return
|
|
789
796
|
|
|
790
|
-
values =
|
|
797
|
+
values = cast(
|
|
798
|
+
"Sequence[float]",
|
|
799
|
+
max(self.right_tag_widths) - np.array(self.right_tag_widths),
|
|
800
|
+
)
|
|
791
801
|
for item, value in zip(self.nodes, values):
|
|
792
802
|
if isinstance(item, LayoutSpaces):
|
|
793
803
|
item.r.tag_alignment = value
|
|
@@ -433,7 +433,7 @@ class right_spaces(_side_spaces):
|
|
|
433
433
|
margin_alignment: float = 0
|
|
434
434
|
legend: float = 0
|
|
435
435
|
legend_box_spacing: float = 0
|
|
436
|
-
|
|
436
|
+
strip_text_y_extra_width: float = 0
|
|
437
437
|
|
|
438
438
|
def _calculate(self):
|
|
439
439
|
items = self.items
|
|
@@ -452,7 +452,7 @@ class right_spaces(_side_spaces):
|
|
|
452
452
|
self.legend = self.legend_width
|
|
453
453
|
self.legend_box_spacing = theme.getp("legend_box_spacing")
|
|
454
454
|
|
|
455
|
-
self.
|
|
455
|
+
self.strip_text_y_extra_width = items.strip_text_y_extra_width("right")
|
|
456
456
|
|
|
457
457
|
# Adjust plot_margin to make room for ylabels that protude well
|
|
458
458
|
# beyond the axes
|
|
@@ -545,7 +545,7 @@ class top_spaces(_side_spaces):
|
|
|
545
545
|
plot_subtitle_margin_bottom: float = 0
|
|
546
546
|
legend: float = 0
|
|
547
547
|
legend_box_spacing: float = 0
|
|
548
|
-
|
|
548
|
+
strip_text_x_extra_height: float = 0
|
|
549
549
|
|
|
550
550
|
def _calculate(self):
|
|
551
551
|
items = self.items
|
|
@@ -578,7 +578,7 @@ class top_spaces(_side_spaces):
|
|
|
578
578
|
self.legend = self.legend_height
|
|
579
579
|
self.legend_box_spacing = theme.getp("legend_box_spacing") * F
|
|
580
580
|
|
|
581
|
-
self.
|
|
581
|
+
self.strip_text_x_extra_height = items.strip_text_x_extra_height("top")
|
|
582
582
|
|
|
583
583
|
# Adjust plot_margin to make room for ylabels that protude well
|
|
584
584
|
# beyond the axes
|
|
@@ -1061,7 +1061,7 @@ class LayoutSpaces:
|
|
|
1061
1061
|
# Only interested in the proportion of the strip that
|
|
1062
1062
|
# does not overlap with the panel
|
|
1063
1063
|
if strip_align_x > -1:
|
|
1064
|
-
self.sh += self.t.
|
|
1064
|
+
self.sh += self.t.strip_text_x_extra_height * (1 + strip_align_x)
|
|
1065
1065
|
|
|
1066
1066
|
if facet.free["x"]:
|
|
1067
1067
|
self.sh += self.items.axis_text_x_max_height_at(
|
plotnine/_mpl/patches.py
CHANGED
|
@@ -4,12 +4,11 @@ from typing import TYPE_CHECKING
|
|
|
4
4
|
|
|
5
5
|
from matplotlib import artist
|
|
6
6
|
from matplotlib.patches import FancyBboxPatch, Rectangle
|
|
7
|
-
from matplotlib.
|
|
8
|
-
from matplotlib.transforms import Affine2D
|
|
7
|
+
from matplotlib.transforms import Bbox
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
from matplotlib.backend_bases import RendererBase
|
|
9
|
+
from plotnine._mpl.utils import rel_position
|
|
12
10
|
|
|
11
|
+
if TYPE_CHECKING:
|
|
13
12
|
from plotnine.typing import StripPosition
|
|
14
13
|
|
|
15
14
|
from .text import StripText
|
|
@@ -26,51 +25,88 @@ class StripTextPatch(FancyBboxPatch):
|
|
|
26
25
|
Strip text background box
|
|
27
26
|
"""
|
|
28
27
|
|
|
29
|
-
# The text artists that is wrapped by this box
|
|
30
28
|
text: StripText
|
|
29
|
+
"""
|
|
30
|
+
The text artists that is wrapped by this box
|
|
31
|
+
"""
|
|
32
|
+
|
|
31
33
|
position: StripPosition
|
|
32
|
-
|
|
34
|
+
"""
|
|
35
|
+
The position of the strip_text associated with this patch
|
|
36
|
+
"""
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
expand: float = 1
|
|
39
|
+
"""
|
|
40
|
+
Factor by which to expand the thickness of this patch.
|
|
36
41
|
|
|
42
|
+
This value is used by the layout manager to increase the breadth
|
|
43
|
+
of the narrower strip_backgrounds.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, text: StripText):
|
|
37
47
|
super().__init__(
|
|
38
|
-
|
|
48
|
+
# The position, width and height are determine in
|
|
49
|
+
# .get_window_extent.
|
|
50
|
+
(0, 0),
|
|
51
|
+
width=1,
|
|
52
|
+
height=1,
|
|
53
|
+
boxstyle="square, pad=0",
|
|
54
|
+
clip_on=False,
|
|
55
|
+
zorder=2.2,
|
|
39
56
|
)
|
|
40
57
|
|
|
41
58
|
self.text = text
|
|
42
59
|
self.position = text.draw_info.position
|
|
43
60
|
|
|
44
|
-
def
|
|
61
|
+
def get_window_extent(self, renderer=None):
|
|
45
62
|
"""
|
|
46
|
-
|
|
63
|
+
Location & dimensions of the box in display coordinates
|
|
47
64
|
"""
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
self.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
info = self.text.draw_info
|
|
66
|
+
m = info.margin
|
|
67
|
+
|
|
68
|
+
# bboxes in display space
|
|
69
|
+
text_bbox = self.text.get_window_extent(renderer)
|
|
70
|
+
ax_bbox = info.ax.bbox.frozen()
|
|
71
|
+
|
|
72
|
+
# line height in display space
|
|
73
|
+
line_height = self.text._line_height(renderer)
|
|
74
|
+
|
|
75
|
+
# Convert the bottom left coordinates of the patch from
|
|
76
|
+
# transAxes to display space. We are not justifying the patch
|
|
77
|
+
# within the axes so we use 0 for the lengths, this gives us
|
|
78
|
+
# a patch that starts at the edge of the axes and not one that
|
|
79
|
+
# ends at the edge
|
|
80
|
+
x0 = rel_position(info.bg_x, 0, ax_bbox.x0, ax_bbox.x1)
|
|
81
|
+
y0 = rel_position(info.bg_y, 0, ax_bbox.y0, ax_bbox.y1)
|
|
82
|
+
|
|
83
|
+
# info.bg_width and info.bg_height are in axes space
|
|
84
|
+
# so they are a scaling factor
|
|
85
|
+
if info.position == "top":
|
|
86
|
+
width = ax_bbox.width * info.bg_width
|
|
87
|
+
height = text_bbox.height + ((m.b + m.t) * line_height)
|
|
88
|
+
height *= self.expand
|
|
89
|
+
y0 += height * info.strip_align
|
|
90
|
+
else:
|
|
91
|
+
height = ax_bbox.height * info.bg_height
|
|
92
|
+
width = text_bbox.width + ((m.l + m.r) * line_height)
|
|
93
|
+
width *= self.expand
|
|
94
|
+
x0 += width * info.strip_align
|
|
95
|
+
|
|
96
|
+
return Bbox.from_bounds(x0, y0, width, height)
|
|
66
97
|
|
|
67
|
-
|
|
98
|
+
@artist.allow_rasterization
|
|
99
|
+
def draw(self, renderer):
|
|
68
100
|
"""
|
|
69
|
-
|
|
101
|
+
Draw patch
|
|
70
102
|
"""
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
103
|
+
# The geometry of the patch is determined by its rectangular bounds,
|
|
104
|
+
# this is also its "window_extent". As the extent value is in
|
|
105
|
+
# display units, we don't need a transform.
|
|
106
|
+
bbox = self.get_window_extent(renderer)
|
|
107
|
+
self.set_bounds(bbox.bounds)
|
|
108
|
+
self.set_transform(None)
|
|
109
|
+
return super().draw(renderer)
|
|
74
110
|
|
|
75
111
|
|
|
76
112
|
class InsideStrokedRectangle(Rectangle):
|