Skip to content

Commit 3cc576a

Browse files
committed
Add inset_element class and ggplot._insets attribute
Introduces the inset_element dataclass for placing a plot or composition as an overlay on a host plot at NPC coordinates, and the ggplot._insets list that stores them. Wiring into the draw and layout pipeline comes in later commits.
1 parent 7d85258 commit 3cc576a

3 files changed

Lines changed: 77 additions & 1 deletion

File tree

plotnine/composition/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from ._beside import Beside
22
from ._compose import Compose
3+
from ._inset_element import inset_element
34
from ._plot_annotation import plot_annotation
45
from ._plot_layout import plot_layout
56
from ._plot_spacer import plot_spacer
@@ -11,6 +12,7 @@
1112
"Stack",
1213
"Beside",
1314
"Wrap",
15+
"inset_element",
1416
"plot_annotation",
1517
"plot_layout",
1618
"plot_spacer",
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from __future__ import annotations
2+
3+
from copy import deepcopy
4+
from dataclasses import dataclass
5+
from typing import TYPE_CHECKING, Literal
6+
7+
if TYPE_CHECKING:
8+
from ..ggplot import ggplot
9+
from ._compose import Compose
10+
11+
12+
@dataclass
13+
class inset_element:
14+
"""
15+
Place a plot as an inset within another plot
16+
17+
The inset is rendered on top of the host. Adding an `inset_element`
18+
to a composition attaches it to the most recently added plot in that
19+
composition.
20+
21+
Parameters
22+
----------
23+
obj :
24+
The object to render as an inset.
25+
left, bottom, right, top :
26+
Bounding box of the inset in normalised parent coordinates,
27+
in the range ``[0, 1]``. The bottom-left corner of the region
28+
selected by `align_to` is ``(0, 0)`` and the top-right is
29+
``(1, 1)``.
30+
align_to :
31+
Which region of the host plot the bounding box is relative to:
32+
33+
- ``"panel"`` — the data area only (default).
34+
- ``"plot"`` — the panel plus axes, labels, and plot margins.
35+
- ``"full"`` — everything the host plot occupies, including
36+
any titles, captions, and legends.
37+
"""
38+
39+
obj: ggplot | Compose
40+
left: float
41+
bottom: float
42+
right: float
43+
top: float
44+
align_to: Literal["panel", "plot", "full"] = "panel"
45+
46+
def __post_init__(self):
47+
from ..ggplot import ggplot
48+
from ._compose import Compose
49+
50+
if not isinstance(self.obj, (ggplot, Compose)):
51+
raise TypeError(
52+
"inset_element requires a ggplot or Compose, got "
53+
f"{type(self.obj).__name__!r}."
54+
)
55+
56+
if not 0.0 <= self.left < self.right <= 1.0:
57+
raise ValueError(
58+
"inset_element requires 0.0 <= left < right <= 1.0, got "
59+
f"left={self.left!r}, right={self.right!r}."
60+
)
61+
62+
if not 0.0 <= self.bottom < self.top <= 1.0:
63+
raise ValueError(
64+
"inset_element requires 0.0 <= bottom < top <= 1.0, got "
65+
f"bottom={self.bottom!r}, top={self.top!r}."
66+
)
67+
68+
def __radd__(self, other: ggplot) -> ggplot:
69+
"""
70+
Attach this inset to a ggplot
71+
"""
72+
other._insets.append(deepcopy(self))
73+
return other

plotnine/ggplot.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
from plotnine import watermark
5656
from plotnine._mpl.gridspec import p9GridSpec
5757
from plotnine._mpl.layout_manager._plot_side_space import PlotSideSpaces
58-
from plotnine.composition import Compose
58+
from plotnine.composition import Compose, inset_element
5959
from plotnine.coords.coord import coord
6060
from plotnine.facets.facet import facet
6161
from plotnine.typing import DataLike, FigureFormat, MimeBundle
@@ -156,6 +156,7 @@ def __init__(
156156
self.environment = Environment.capture(1)
157157
self.layout = Layout()
158158
self.watermarks: list[watermark] = []
159+
self._insets: list[inset_element] = []
159160

160161
# build artefacts
161162
self._build_objs = NS(meta={})

0 commit comments

Comments
 (0)