0

I want to use in matplotlib.patches.Arc the clip_path parameter, but do not succeed. Next is just an example, where I want to see not the complete orange arc but only the partial orange arc between y-axis and the red circle by using the clip_path parameter, but do no understand how to define the clip_path parameters. Thanks.

import math as m
import matplotlib.pyplot as plt
import matplotlib.patches as pat

plt.figure(figsize=(10,10),dpi=300)

ag=10
plt.axis([-ag,ag,-ag,ag])  
plt.grid(True)

circle1 = plt.Circle((0, 2.5), 7, color='r',fill=False)
plt.gcf().gca().add_artist(circle1)   

myarc=pat.Arc((0,0),25,18,angle=0,theta1=0,theta2=355,color="orange")
plt.gcf().gca().add_artist(myarc) 

plt.savefig("myarc.png")
plt.show()

This is what I got:

enter image description here

Just a further remark: With next modification of theta1 and theta2 angle I get what I need, but for this the two intersections need to be determined first. My intention is to avoid these calculations and just draw an ellipse and defining two clipping paths (the red circle and the y-axis).

myarc=pat.Arc((0,0),25,18,angle=0,theta1=110,theta2=152,color="orange")

Stefan
  • 1
  • 2
  • [Find the intersection of two curves given by (x, y) data with high precision in Python](https://stackoverflow.com/questions/42464334/find-the-intersection-of-two-curves-given-by-x-y-data-with-high-precision-in) I find this answer to be very helpful – r-beginners Aug 16 '20 at 14:15
  • Thank you, this will work, determine the intersections and just draw the partial elliptical arc. But would like to avoid these calculations and let python find the intersections by defining the clip_path, if this works... – Stefan Aug 16 '20 at 15:49
  • Matplotlib's clipping only seems to let you keep the inside, Probably you need a library such as [shapely](https://pypi.org/project/Shapely/) for more elaborate clipping. – JohanC Aug 16 '20 at 16:09
  • @JohanC Thank you, I will check it. To get the orange arc inside the red circle, do you know the command syntax for this in the clip_path(???) option? I failed so far with defining any function in the clip_path. – Stefan Aug 17 '20 at 08:17

2 Answers2

2

To clip the arc by the circle, you can use myarc.set_clip_path(circle1). It is important that both the arc and the circle are previously added to the plot (ax.add_artist()). Note that clipping by the borders of the axes happens automatically.

To create more complicated clipping, the shapely is probably handier.

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

fig, ax = plt.subplots(figsize=(10, 6))

circle1 = plt.Circle((0, 2.5), 7, color='r', fill=False)
ax.add_artist(circle1)

myarc = mpatches.Arc((0, 0), 25, 18, angle=0, theta1=0, theta2=355, color="orange", lw=5)
ax.add_artist(myarc)
myarc.set_clip_path(circle1)

ag = 10
ax.set_xlim(-ag, ag)
ax.set_ylim(-ag, ag)
plt.grid(True)
ax.set_aspect('equal') # set the aspect ratio so circles look like circles
plt.show()

resulting plot

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • Sorry to bother you again, I copied your code lines, executed in Spyder 3.8 and got a plot with almost the complete orange ellipse, just on the right side beyond the grid the ellipse was cut off. Verified the copied code lines, see no difference... – Stefan Aug 17 '20 at 12:09
  • Did you first add both the circle and the arc to the same `ax`? `set_clip_path` only has the effect after the elements have been added. Maybe you could edit your question and add your new code? – JohanC Aug 17 '20 at 12:39
  • Used your code above, copied it, tried ax.add_patch(myarc) as well, get always the red circle and the orange ellipse in and outside the red circle – Stefan Aug 17 '20 at 22:25
  • Maybe you're not working with a recent matplotlib version? – JohanC Aug 17 '20 at 22:34
  • just to provide the current status, I am still troubleshooting. My installation is Spyder 4.1.4, Python 3.8.3 64-bit | Qt 5.9.7 | PyQt5 5.9.2 | Windows 10 , matplotlib 3.2.2 and update check says, that Spyder is up to date. – Stefan Aug 19 '20 at 17:46
  • Well, after Reset Spyder to factory defaults I get now the same plot as shown in your answer. Thank you for your patience! – Stefan Aug 19 '20 at 18:24
0

By using the steps of answer (1) I got the wanted result without the need to calculate all the intersections. Steps:

  1. Defining and Plotting series of curves

  2. Defining clipping areas by using clip_path option (e.g. circles or shaping an area by concatenating 1D-arrays through mathematical function results)

  3. Using clip_path to get rid of unwanted portion of curves

    # Import python Modules
    import math as m
    import matplotlib.pyplot as plt
    import numpy as np
    from matplotlib.patches import Polygon
    
    ## constants for the circles
    Loc=37      # in degree
    StepAl=3    # in degree
    StepAz=10   # in degree
    rAequ=6.3   # Radius
    rWkSt=9.6   # Radius
    Ze=3.14     # Distance
    
    ## red AlCircles, in total 31
    AlCircle=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    i=0
    while i<=30:
        AlCircle[i]=[0,0,0,0]
        i=i+1   
    # Calculating the parameters of the AlCircles
    i=0
    while i<=30:
        AlCircle[i][0]=rAequ*m.tan((-Loc+i*StepAl)/2/180*m.pi)        # lower y-Value 
        AlCircle[i][1]=rAequ*m.tan((-Loc+180-i*StepAl)/2/180*m.pi)    # upper y-Value
        AlCircle[i][2]=(AlCircle[i][1]-AlCircle[i][0])/2    # Radius
        AlCircle[i][3]=AlCircle[i][0]+AlCircle[i][2]        # Center
        i=i+1
    
    ## green AzCircles, in total 18
    DZ=rAequ/m.cos(Loc/180*m.pi)    # Distance
    
    AzCircle=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    i=0
    while i<=17:
        AzCircle[i]=[0,0]
        i=i+1   
    # Calculating the parameters of the AzCircles
    i=1
    while i<=17:
        AzCircle[i][0]=DZ*m.tan((-90+i*StepAz)/180*m.pi)  # distance Center to y-Axis
        AzCircle[i][1]=rAequ/m.cos(Loc/180*m.pi)/m.cos((-90+i*StepAz)/180*m.pi)    # Radius of AzCircles
        i=i+1   
    
    ### Generating Plots
    plt.figure(figsize=(10,10),dpi=100) 
    ag=rWkSt
    
    plt.axis([-ag,ag,-ag,ag])  
    plt.grid(True)
    
    # Plotting blue Circle
    circle0=plt.Circle((0,0),rWkSt,color='b',fill=False)
    plt.gcf().gca().add_artist(circle0)
    
    # Plotting red AlCircles
    i=0
    while i<=30:
        # defining Cliparea1
        myCliparea1=plt.Circle((0,0),rWkSt,color="b",ls="dotted",fill=False)
        plt.gcf().gca().add_artist(myCliparea1)
    
        # calculating AlCircles and plotting
        circle1=plt.Circle((0,AlCircle[i][3]),AlCircle[i][2],color='r',fill=False)
        plt.gcf().gca().add_artist(circle1)    
    
        circle1.set_clip_path(myCliparea1)   # performing clipping
    
        i=i+1
    
    # Plotting green AzCircles
    i=1
    while i<=17:   # nur bis 17
        xA=9.072582     # precalculated Intersection for f1(x) and f2(x)  
    
        # f1(x) for lower clipping area border line
        x1=np.arange(-xA,+xA,0.1)
        y1=(-1)*np.sqrt(AlCircle[0][2]**2-x1**2)+AlCircle[0][3]
    
        # f2(x) for upper clipping area border line
        x2=np.arange(xA,-xA,-0.1)
        y2=(+1)*np.sqrt(rWkSt**2-x2**2)
    
        # building clipping area
        x3=np.concatenate((x1,x2))
        y3=np.concatenate((y1,y2))
        poly = Polygon(np.column_stack([x3, y3]), animated=True, color="aqua", fill=False)
        plt.gcf().gca().add_artist(poly)       # plotting of clipping area
    
        # calculating AzCircles and plotting
        circle2=plt.Circle((-AzCircle[i][0],Ze-DZ),AzCircle[i][1],color='g',fill=False)
        plt.gcf().gca().add_artist(circle2)     
    
        circle2.set_clip_path(poly)    # performing clipping
    
        i=i+1    
    
    
    plt.savefig("myPlot.png")
    plt.show()
    

myPlot

image

swatchai
  • 17,400
  • 3
  • 39
  • 58
Stefan
  • 1
  • 2