FaCoffee
FaCoffee

Reputation: 7929

How to "translate" the Midpoint Circle Algorithm into matplotlib?

I need to implement the Midpoint Circle Algorithm in matplotlib, so that a circle is rasterized on a square grid of 200x200 cells. Please also refer to my other question on the subject.

Thanks to this answer, I was able to find some code which I assume works flawlessly. My only problem is that I wouldn't know how to integrate it and amend it to make sure matplotlib draws a filled circle with 1 inside and 0 outside.

This is how I implement the script with matplotlib:

import numpy
import matplotlib.pyplot as plt    
n=200 #Grid size, 4 times my visualized output in order to be able to truncate some circles
empty_lattice=numpy.zeros((n,n)) #The empty 2D grid
radius=int(numpy.random.uniform(30,90)) #Radius
xc=int(numpy.random.uniform(0,n-radius)) #X center
yc=int(numpy.random.uniform(0,n-radius)) #Y center
x=0
y=radius
d=3-2*radius
while (x<=y):
    for hor in range(0,x): #This loop is my unfortunate attempt to fill the circle with 1s
        for ver in range(0,y):
            empty_lattice[xc+x][yc+y]=1 #1st octant
            empty_lattice[xc-x][yc+y]=1 #2nd octant
            empty_lattice[xc+x][yc-y]=1 #3rd octant
            empty_lattice[xc-x][yc-y]=1 #4th octant
            empty_lattice[xc+y][yc+x]=1 #5th octant
            empty_lattice[xc-y][yc+x]=1 #6th octant
            empty_lattice[xc+y][yc-x]=1 #7th octant
            empty_lattice[xc-y][yc-x]=1 #8th octant
    if (d<0):
        d=d+4*x+6
    else:
        d=d+4*(x-y)+10
        y=y-1
    x=x+1           

Now, this is what I obtain, but you see that my circle is empty and that there is a "gap" in the horizontal line at the bottom of it. This gap occurs at each octant intersection. How can I amend my code to fill the circle and the gap?

enter image description here

EDIT

After having adopted the answer provided below, I have realized that two mirrored circles are drawn in the image below. I think this bug came with my original script and not with the answer. I only want one circle in each image. How can I get rid of this feature?

enter image description here

Upvotes: 3

Views: 1991

Answers (1)

Jeff Irwin
Jeff Irwin

Reputation: 1051

To get rid of the gap, you need to extend the horizontal range by one. In your code, that would be the line for hor in range(0,x + 1):. My code takes a different strategy.

As far as I understand it, the midpoint circle algorithm just walks around the perimeter of the circle. So I don't think it can be easily modified to fill the interior. The code below checks every point in the first octant to see if the point's distance from the center is less than or equal to the radius. If it is within the radius, then all 8 corresponding points in each octant are filled.

import numpy
import matplotlib.pyplot as plt    
n=200 #Grid size, 4 times my visualized output in order to be able to truncate some circles
empty_lattice=numpy.zeros((n,n)) #The empty 2D grid
radius=int(numpy.random.uniform(30,90)) #Radius
xc=int(numpy.random.uniform(0,n-radius)) #X center
yc=int(numpy.random.uniform(0,n-radius)) #Y center
r2 = radius ** 2
for dx in range(0, radius):
    dx2 = dx ** 2
    for dy in range(0, dx + 1):
        dy2 = dy ** 2
        if (dx2 + dy2 <= r2):
            empty_lattice[xc+dx][yc+dy]=1 #1st octant
            empty_lattice[xc-dx][yc+dy]=1 #2nd octant
            empty_lattice[xc+dx][yc-dy]=1 #3rd octant
            empty_lattice[xc-dx][yc-dy]=1 #4th octant
            empty_lattice[xc+dy][yc+dx]=1 #5th octant
            empty_lattice[xc-dy][yc+dx]=1 #6th octant
            empty_lattice[xc+dy][yc-dx]=1 #7th octant
            empty_lattice[xc-dy][yc-dx]=1 #8th octant

plt.imshow(empty_lattice)
plt.show()

If you really want to stick with the midpoint circle algorithm, you could draw the perimeter, then start a flood fill from the center point.

Edit 1:

Got rid of two unnecessary lines.

Edit 2:

The above code wraps the circle around the image edges in a modular fashion. If you don't want that, some extra conditions are needed:

            empty_lattice[xc+dx][yc+dy]=1 #1st octant
            if (xc - dx >= 0):
                empty_lattice[xc-dx][yc+dy]=1 #2nd octant
            if (yc - dy >= 0):
                empty_lattice[xc+dx][yc-dy]=1 #3rd octant
            if (xc - dx >= 0 and yc - dy >= 0):
                empty_lattice[xc-dx][yc-dy]=1 #4th octant
            if (yc + dx < n):
                empty_lattice[xc+dy][yc+dx]=1 #5th octant
            if (xc - dy >= 0):
                empty_lattice[xc-dy][yc+dx]=1 #6th octant
            if (yc - dx >= 0):
                empty_lattice[xc+dy][yc-dx]=1 #7th octant
            if (xc - dy >= 0 and yc - dx >= 0):
                empty_lattice[xc-dy][yc-dx]=1 #8th octant

Edit 3:

I realized what was wrong with your original code. Your for loop involved a variable hor, but you forgot to use hor. This code works. I'll leave it up to you to check that the indices are positive.

x=0
y=radius
d=3-2*radius
while (x<=y):
    for hor in range(0,x + 1): #This loop is my unfortunate attempt to fill the circle with 1s
##        for ver in range(0,y):
        empty_lattice[xc+hor][yc+y]=1 #1st octant
        empty_lattice[xc-hor][yc+y]=1 #2nd octant
        empty_lattice[xc+hor][yc-y]=1 #3rd octant
        empty_lattice[xc-hor][yc-y]=1 #4th octant
        empty_lattice[xc+x][yc+hor]=1 #1st octant
        empty_lattice[xc-x][yc+hor]=1 #2nd octant
        empty_lattice[xc+x][yc-hor]=1 #3rd octant
        empty_lattice[xc-x][yc-hor]=1 #4th octant
        empty_lattice[xc+hor][yc+x]=1 #5th octant
        empty_lattice[xc-hor][yc+x]=1 #6th octant
        empty_lattice[xc+hor][yc-x]=1 #7th octant
        empty_lattice[xc-hor][yc-x]=1 #8th octant
        empty_lattice[xc+y][yc+hor]=1 #5th octant
        empty_lattice[xc-y][yc+hor]=1 #6th octant
        empty_lattice[xc+y][yc-hor]=1 #7th octant
        empty_lattice[xc-y][yc-hor]=1 #8th octant
    if (d<0):
        d=d+4*x+6
    else:
        d=d+4*(x-y)+10
        y=y-1
    x=x+1  

Upvotes: 1

Related Questions