2

I need to plot half-ellipses (or to be precise, half-elliptical disks, hence I can't use matplotlib to plot an elliptical arc).

I found OpenCV can do this with this syntax: cv2.Ellipse(img, center, axes, angle, start_angle, end_angle, color, thickness=1, lineType=8, shift=0), but there is one thing that really bothers me. I want to use standard x and y coordinates, not pixels. Also, I will need to plot half ellipses with non-integer radius, and it seem OpenCV can't do that (for the same reason of having pixel coordinates).

So, I need a code that can do what OpenCV does (with the same angle + start_angle + end_angle structure), but without requiring me to work in pixels.

Gabriel
  • 40,504
  • 73
  • 230
  • 404
odnerpmocon
  • 282
  • 1
  • 4
  • 17

2 Answers2

4

You can use matplotlib arc is you didn't want them filled in. For a filled arc, you can use the accepted solution here where a generic patch is defined and combine with the matplotlib ellipse example,

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy.random as rnd
import numpy as np

def arc_patch(xy, width, height, theta1=0., theta2=180., resolution=50, **kwargs):

    # generate the points
    theta = np.linspace(np.radians(theta1), np.radians(theta2), resolution)
    points = np.vstack((width*np.cos(theta)  + xy[0], 
                        height*np.sin(theta) + xy[1]))
    # build the polygon and add it to the axes
    poly = mpatches.Polygon(points.T, closed=True, **kwargs)

    return poly

NUM = 10
arcs = []
for i in range(NUM):
    r = rnd.rand()*360.
    arcs.append(arc_patch(xy=rnd.rand(2)*10, width=1., 
                height=1., theta1=r, theta2=r+180.))

# axis settings
fig, ax = plt.subplots(1,1)
for a in arcs:
    ax.add_artist(a)
    a.set_clip_box(ax.bbox)
    a.set_alpha(0.5)
    a.set_facecolor(rnd.rand(3))

ax.set_xlim(0, 10)
ax.set_ylim(0, 10)

plt.show()

Which looks like, enter image description here

Community
  • 1
  • 1
Ed Smith
  • 12,716
  • 2
  • 43
  • 55
  • FYI, `matplotlib.patches.Arc` can make filled half-ellipses. – Syrtis Major Mar 14 '17 at 14:02
  • Do you have a reference for this @Syrtis Major? The official docs for `matplotlib.patches.Arc` here: http://matplotlib.org/api/patches_api.html#module-matplotlib.patches explicitly says "Because it performs various optimizations, it can not be filled." – Ed Smith Mar 17 '17 at 10:01
  • Ah, I didn't notice the docs, but just tried it (see answer below). I'm not sure why, maybe the docs is a bit outdated? – Syrtis Major Mar 17 '17 at 12:32
  • I just realize that `Arc`s can not fill themselves indeed, but the `PatchCollection` can fill them. That's why my solution works. – Syrtis Major Apr 01 '17 at 06:05
3

Use matplotlib.patches.Arc can make half-ellipses, just specify the keywords theta1=0.0, theta2=180.0 (or 90 to 270). I've written a wrapper function named arcs for making a scatter plot of Arcs. It uses the PatchCollection, should have better performance and enable colorbar. You can find it at gist (link).

Here's an example:

a = np.arange(11)
arcs(a, a, w=4, h=a, rot=a*30, theta1=0.0, theta2=180.0, 
     c=a, alpha=0.5, edgecolor='none')
plt.colorbar()

enter image description here


A brief implementation of arcs is posted below for completeness as Ed Smith suggested.

def arcs(x, y, w, h, rot=0.0, theta1=0.0, theta2=360.0,
         c='b', **kwargs):
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.patches import Arc
    from matplotlib.collections import PatchCollection

    if np.isscalar(c):
        kwargs.setdefault('color', c)
        c = None

    zipped = np.broadcast(x, y, w, h, rot, theta1, theta2)
    patches = [Arc((x_, y_), w_, h_, rot_, t1_, t2_)
               for x_, y_, w_, h_, rot_, t1_, t2_ in zipped]
    collection = PatchCollection(patches, **kwargs)

    if c is not None:
        c = np.broadcast_to(c, zipped.shape).ravel()
        collection.set_array(c)

    ax = plt.gca()
    ax.add_collection(collection)
    return collection

The full version can be found at gist (link).

Syrtis Major
  • 3,791
  • 1
  • 30
  • 40
  • Your solution looks good. Is it possible to extract out the minimal code for an example of using PatchCollections so as to give a self contained answer here? – Ed Smith Apr 03 '17 at 15:50