1

I created a sequence of points that I would like to convert into a Patch.

The goal is then to draw the patch on the left side of the y-label (see in Red in the figure), or draw it in any other part of the figure.

Although it can be accomplished with Gridspec, I would like to do it with a Patch.

import matplotlib.pyplot as plt
import numpy as np

plt.figure()
npoints = 100
td = np.linspace(np.pi*3/4, np.pi*5/4, npoints)
xd = np.cos(td)
yd = np.sin(td)
plt.plot(xd,yd)

enter image description here

EDIT1:

I am now able to make a Patch (just need to move it outside the axis):

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.path as mpath
import matplotlib.patches as mpatches

npoints = 100
td = np.linspace(np.pi*3/4, np.pi*5/4, npoints)
xd = np.cos(td)
yd = np.sin(td)

fig, ax = plt.subplots()
ax.axis([-2, 0, -1, 1])
verts=np.c_[xd,yd]
codes = np.ones(len(xd))*2 # Path.LINETO for all points except the first
codes[0] = 1 #Path.MOVETO only for the first point
path1 = mpath.Path(verts, codes)
patch = mpatches.PathPatch(path1, facecolor='none')
ax.add_patch(patch)

The result:

enter image description here

Now, I only need to move it outside the axis, maybe using a translation or scale.

I'm sure the key to do it is somewhere in this Matplotlib Transforms tutorial, more specifically, I am pretty sure the solution is using fig.transFigure.

EDIT 2: Almost there!

In order to use Figure coordinates (that are between [0,1]) I normalized the points that define the path. And instead of using ax.add_patch() that adds a patch to the axis, I use fig.add_artist() that adds the patch to the figure, over the axis.

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.path as mpath
import matplotlib.patches as mpatches

#Normalized Data
def normalize(x):
    return (x - min(x)) / (max(x) - min(x))

#plt.figure()
npoints = 100
td = np.linspace(np.pi*3/4, np.pi*5/4, npoints)
xd = np.cos(td)
yd = np.sin(td)
#plt.plot(xd,yd)

xd = normalize(xd)
yd = normalize(yd)

fig, ax = plt.subplots()
ax.axis([-2, 2, -1, 1])
verts=np.c_[xd,yd]
codes = np.ones(len(xd))*2 # Path.LINETO for all points except the first
codes[0] = 1 #Path.MOVETO only for the first point
path1 = mpath.Path(verts, codes)
patch1 = mpatches.PathPatch(path1, facecolor='none')
ax.add_patch(patch1)

patch2 = mpatches.PathPatch(path1, fc='none', ec='red', transform=fig.transFigure)
fig.add_artist(patch2)

And the result so far: enter image description here

Doing this, I just need to scale and translate the patch, maybe using Affine2D.

EDIT 3: Done!

Finally I was able to do it! I used Try and Error in the scale() and translate() parameters as I did not get what coordinate system they were using. However, it would be great to get the exact y center (0.5 in Figure coordinates).

Here is the complete code:

import numpy as np
import matplotlib.path as mpath
import matplotlib.patches as mpatches

#Normalized Data
def normalize(x):
    return (x - min(x)) / (max(x) - min(x))

npoints = 100
td = np.linspace(np.pi*3/4, np.pi*5/4, npoints)
xd = np.cos(td)
yd = np.sin(td)

xd = normalize(xd)
yd = normalize(yd)

fig, ax = plt.subplots()
ax.axis([-2, 2, -1, 1])
verts=np.c_[xd,yd]
codes = np.ones(len(xd))*2 # Path.LINETO for all points except the first
codes[0] = 1 #Path.MOVETO only for the first point
path1 = mpath.Path(verts, codes)
patch1 = mpatches.PathPatch(path1, fc='none', ec='green')
ax.add_patch(patch1) #draw inside axis

patch2 = mpatches.PathPatch(path1, fc='none', ec='C0', transform=fig.transFigure)
fig.add_artist(patch2) #this works! Draw on figure    

import matplotlib.transforms as mtransforms
tt = fig.transFigure + mtransforms.Affine2D().scale(0.02, 0.8).translate(10,45)
patch3 = mpatches.PathPatch(path1, fc='none', ec='red', transform=tt)
fig.add_artist(patch3)

And the resulting figure:

enter image description here

Pedro
  • 330
  • 2
  • 12
  • 1
    This [Path tutorial](https://matplotlib.org/stable/tutorials/advanced/path_tutorial.html) might be a good help. – Pedro Oct 31 '21 at 16:45
  • 1
    Now that I have done it, should I answer my own question or leave the solution in the question? – Pedro Nov 01 '21 at 18:01
  • Pedro, you should never update the question with the answer. Please revert the last edits and post your solution as an answer instead. (Yes, this is allowed, see See [Can I answer my own question?](https://meta.stackexchange.com/help/self-answer)) – wovano Dec 08 '21 at 16:52

1 Answers1

1

As @Pedro pointed out, most of this can be found in the tutorial that he linked. However, here is a short answer.

Basically, it's almost as if you're creating a line plot. Just specify the points you want to pass through, add them to a list and that's it.

In this example I want to pass through some points on the plot, then "lift the pen off of the paper" and continue from another point. So we create two lists - one containing the points I want to use and the second list which describes what I want to do with those points. Path.MOVETO will move your "pen" to the given point without drawing a line, so we use this to set our initial startpoint. Path.LINETO creates a straight line starting from your current pen position towards the next line in the list.

import matplotlib.pyplot as plt
from matplotlib.path import Path
import matplotlib.patches as patches

# Points you want to "pass through"
pts = [
    (0, 0),
    (0.2, 0.2),
    (0.4, 0.2),
    (0.4, 0.4),
    (0.4, 0.6),
    (0.6, 0.6),
    (0.8, 0.8)
]

# What you want to "do" with each point
codes = [
    Path.MOVETO, # inital point
    Path.LINETO,
    Path.LINETO,
    Path.LINETO,
    Path.MOVETO, # pick up the pen
    Path.LINETO,
    Path.LINETO
]

# Create path object
# https://matplotlib.org/stable/tutorials/advanced/path_tutorial.html
path = Path(pts, codes)
patch = patches.PathPatch(path, lw='2', color='r', fill=False)

# patch the path to the figure
fig, ax = plt.subplots()
ax.add_patch(patch)
plt.show()

Result of code execution:

Result of code execution

waykiki
  • 914
  • 2
  • 9
  • 19
  • 1
    Good stuff. I've thought for some time about how you could draw "behind" the axes, but I cannot figure it out. The only suggestion I have for now is that you move/center the axes somewhere in the middle of the plot, instead of keeping them in the bottom left corner. I can't paste code in comments, but check this post out, there are some good answers. I hope this will be of use to you. https://stackoverflow.com/questions/31556446/how-to-draw-axis-in-the-middle-of-the-figure – waykiki Oct 31 '21 at 18:25