Up front: totally open to other methods that might accomplish the same thing - I thought this would be the most straightforward way to accomplish this.
Start by importing the following modules:
import matplotlib
import matplotlib.artist
import matplotlib.lines
import matplotlib.pyplot
import matplotlib.patches
import matplotlib.patheffects
import matplotlib.offsetbox
import matplotlib.transforms
Problem 1: Nested AuxTransformBoxes
Let’s say I want to create a line that is 2 inches long and rotated 45 degrees:
# Creating a 5x5 plot
fig, ax = matplotlib.pyplot.subplots(1,1, figsize=(5,5), dpi=150)
# Creating a line
line = matplotlib.lines.Line2D([0,2],[2,2])
# This box will scale and rotate
atb = matplotlib.offsetbox.AuxTransformBox(fig.dpi_scale_trans + matplotlib.transforms.Affine2D().rotate_deg(45))
# Adding the line to the box
atb.add_artist(line)
# AOB will contain the final artist, and is used for centering
aob = matplotlib.offsetbox.AnchoredOffsetbox(loc="center", child=atb, frameon=False, pad=0, borderpad=0)
# Adding the AOB to the plot
ax.add_artist(aob)
This “works”, although the line has been shifted considerably:
However, this method that uses two AuxTransformBoxes nested together does not work - you can see the line hiding at the very bottom edge of the figure, not rotated (note: I’ve turned the frame for the AnchoredOffsetBox on, in black, so you can see where it should have been drawn):
# Creating a 5x5 plot
fig, ax = matplotlib.pyplot.subplots(1,1, figsize=(5,5), dpi=150)
# Creating a line
line = matplotlib.lines.Line2D([0,2],[2,2])
# This box will ONLY scale
atb_scale = matplotlib.offsetbox.AuxTransformBox(fig.dpi_scale_trans)
# Adding the line to the box
atb_scale.add_artist(line)
# This box will ONLY rotate
atb_rotate = matplotlib.offsetbox.AuxTransformBox(matplotlib.transforms.Affine2D().rotate_deg(45))
# Adding the previous atb to this one
atb_rotate.add_artist(atb_scale)
# AOB will contain the final artist, and is used for centering
aob = matplotlib.offsetbox.AnchoredOffsetbox(loc="center", child=atb_rotate, frameon=True, pad=0, borderpad=0)
# Adding the AOB to the plot
ax.add_artist(aob)
Why would this be the case? Nested OffsetBoxes clearly work sometimes, but can you not nest AuxTransforBox
es together to interatively apply transformations?
Problem 2: Rotating Other OffsetBoxes
I was hoping to apply these AuxTransformBoxes to other types of OffsetBoxes, such as HPackers and VPackers that mix text and artists, but that doesn’t seem to work either.
This is a code snippet that stacks a text area and a line artist vertically:
# Creating a 5x5 plot
fig, ax = matplotlib.pyplot.subplots(1,1, figsize=(5,5), dpi=150)
# Creating a line
line = matplotlib.lines.Line2D([0,2],[2,2])
# This box will ONLY scale
atb = matplotlib.offsetbox.AuxTransformBox(fig.dpi_scale_trans)
# Adding the line to the box
atb.add_artist(line)
# Creating a TextArea
txt = matplotlib.offsetbox.TextArea("Example")
# This will pack the atb and txt artists together vertically
vpack = matplotlib.offsetbox.VPacker(children=[txt,atb], align="center")
# AOB will contain the final artist, and is used for centering
aob = matplotlib.offsetbox.AnchoredOffsetbox(loc="center", child=vpack, frameon=False, pad=0, borderpad=0)
# Adding the AOB to the plot
ax.add_artist(aob)
However, the same code does not work when everything is placed inside of an AuxTransformBox
that is rotated (the frame of the AnchoredOffsetbix is kept on here):
# Creating a 5x5 plot
fig, ax = matplotlib.pyplot.subplots(1,1, figsize=(5,5), dpi=150)
# Creating a line
line = matplotlib.lines.Line2D([0,2],[2,2])
# This box will ONLY scale
atb_scale = matplotlib.offsetbox.AuxTransformBox(fig.dpi_scale_trans)
# Adding the line to the box
atb_scale.add_artist(line)
# Creating a TextArea
txt = matplotlib.offsetbox.TextArea("Example")
# This will pack the atb and txt artists together vertically
vpack = matplotlib.offsetbox.VPacker(children=[txt,atb_scale], align="center")
# This box will ONLY rotate
atb_rotate = matplotlib.offsetbox.AuxTransformBox(matplotlib.transforms.Affine2D().rotate_deg(45))
# Adding the vpacker
atb_rotate.add_artist(vpack)
# AOB will contain the final artist, and is used for centering
aob = matplotlib.offsetbox.AnchoredOffsetbox(loc="center", child=atb_rotate, frameon=True, pad=0, borderpad=0)
# Adding the AOB to the plot
ax.add_artist(aob)
Again, the composite artist is found at the bottom of the screen, not respecting where the AnchoredOffsetBox tries to place it. Why would this be the case?