Reputation: 7929
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?
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?
Upvotes: 3
Views: 1991
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