2

I need a parametric form for a matplotlib.path.Path. So I used the .vertices attribute, and it works fine except that the number of points given is too low for the use I want. Here is a code to illustrate :

import numpy as np
from matplotlib import pyplot as plt
import matplotlib.patches as mpat

fig, ax = plt.subplots()
ax.set(xlim=(-6, 6), ylim=(-6, 6))


# generate a circular path
circle = mpat.Arc((0, 0), 10, 10, theta1=20, theta2=220, color='green')
path = circle.get_transform().transform_path(circle.get_path()).cleaned().vertices[:-3]  # get_path is not enough because of the transformation, so we apply to the path the same transformation as the circle has got from the identity circle
ax.add_patch(circle)

# plot path vertices
plt.scatter(x=path[:, 0], y=path[:, 1], color='red', s=2)

shape = len(path)

plt.show()

How to increase the number of points (red) to fetch better the path (green)? Or, how can I increase the len of path?

Thanks in advance!

Adrials
  • 55
  • 7

1 Answers1

1

It's not ideal, but you can just loop over many shorter arcs, e.g.,:

import numpy as np
from matplotlib import pyplot as plt
import matplotlib.patches as mpat

fig, ax = plt.subplots()
ax.set(xlim=(-6, 6), ylim=(-6, 6))


# generate a circular path
dr = 30  # change this to control the number of points
path = np.empty((0, 2))

for i in range(20, 221, dr):
    circle = mpat.Arc((0, 0), 10, 10, theta1=i, theta2=(i + dr), color='green')
    tmppath = circle.get_transform().transform_path(circle.get_path()).cleaned().vertices[:-1]
    path = np.vstack((path, tmppath[:, 0:2]))

    ax.add_patch(circle)

# plot path vertices
plt.scatter(x=path[:, 0], y=path[:, 1], color='red', s=2)

plt.show()

Update

Another option is to hack the Arc class, so that during initialisation, the Path.arc takes in the n keyword. E.g.,

from matplotlib.patches import Path, Arc

class NewArc(Arc):
    def __init__(self, xy, width, height, angle=0.0, theta1=0.0, theta2=360.0, n=30, **kwargs):
        """
        An Arc class with the n keyword argument
        """
    
        fill = kwargs.setdefault('fill', False)
        if fill:
            raise ValueError("Arc objects can not be filled")

        super().__init__(xy, width, height, angle=angle, **kwargs)

        self.theta1 = theta1
        self.theta2 = theta2
        (self._theta1, self._theta2, self._stretched_width,
         self._stretched_height) = self._theta_stretch()

        # add in the n keyword here
        self._path = Path.arc(self._theta1, self._theta2, n=n)

The original code (without the for loop) can then be used and the number of points increased, e.g.,

from matplotlib import pyplot as plt

fig, ax = plt.subplots()
ax.set(xlim=(-6, 6), ylim=(-6, 6))

# generate a circular path using NewArc and the n keyword
circle = NewArc((0, 0), 10, 10, theta1=20, theta2=220, n=40, color='green')
path = circle.get_transform().transform_path(circle.get_path()).cleaned().vertices[:-3]
ax.add_patch(circle)

# plot path vertices
ax.scatter(x=path[:, 0], y=path[:, 1], color='red', s=2)

fig.show()
Matt Pitkin
  • 3,989
  • 1
  • 18
  • 32
  • It works in this particular case, but if `theta1 = 0.5` and `theta2 = 0.7` , or if the path is "more random" this roundabout solution is not working... Can you think of anything more "general" ? I still haven't found anything – Adrials Dec 26 '22 at 19:44
  • 1
    Another option would be to make an interpolation function from the `path`, using, e.g., [`interp1d`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html) and then get the output from over an increased number of samples. However, unless you transform things correctly, the new points would not necessarily be linearly spaced along the arc. – Matt Pitkin Dec 27 '22 at 13:16
  • I've worked out a better solution and added it to the answer – Matt Pitkin Dec 27 '22 at 13:49
  • Wow, thanks, I did not think of that! Is there a reason why the matplotlib developers didn't add this `n` parameter in optional arg to `matplotlib.patches.Arc` when it's there for `matplotlib.Path.arc`? – Adrials Dec 29 '22 at 11:27