0

I know there are several other questions and articles on the internet, but they wern't quite enough. My problem is, that right now, i'm calculating the points of a sphere by stacking circles with a ascending/descending radius, made with bresenham's circle drawing algorithm. This is my code, but it should'n be important.

    def drawCircle(self, xyz, r): #Draws circle with radius "r" from midpoint "xyz". Note: Circle is parallel to x-axis
        xc, yc, zc = xyz
        coords = []
        
        def drawC(xc, yc, zc, x, y):
            coords.append((xc+x, yc+y, zc))
            coords.append((xc-x, yc+y, zc))
            coords.append((xc+x, yc-y, zc))
            coords.append((xc-x, yc-y, zc))
            coords.append((xc+y, yc+x, zc))
            coords.append((xc-y, yc+x, zc))
            coords.append((xc+y, yc-x, zc))
            coords.append((xc-y, yc-x, zc))
            
        x = 0
        y = r
        d = 3 - 2 * r
        drawC(xc, yc, zc, x, y) 
        while y >= x:
            x += 1
            if (d > 0):
                y -= 1 
                d = d + 4 * (x - y) + 10 
            else:
                d = d + 4 * x + 6
            drawC(xc, yc, zc, x, y) 
        
        for c in coords:
            self.drawPixel(c)
        return coords


    def drawSphere(self,xyz,r): #Draws sphere. Not quite functional yet
        x, y, z = xyz
        for sr in range(-1,r):
            self.drawCircle((x,y,z-r+sr),sr)
            
        index = list(range(-1,r))
        for sr in index:
            self.drawCircle((x,y,z+sr),index[-sr])

When I render one of these spheres this happens:

one

If I increase the thickness to 2 layers

two

and to 3 layers:

th

There are holes. :( The thiccer the sphere has less holes, but is much slower to create. Is there a way to have hole-less sphere?

  • 1
    why using circles instead of sphere? If you are in screen coordinates then you know the `x,y` range and just compute `z` ...so 2 nested `for` loops for `x,y` in range `<-r+r>` then single `if` inside circle `(x^2 + y^2 <= r^2)` and then compute `z` from `x,y` using `z = +/- sqrt(r^2 - x^2 - y^2)` and render pixel with added offset for sphere center... This will be much faster and without holes and no bresenham... see [Drawing 3D sphere in C/C++](https://stackoverflow.com/a/25135125/2521214) – Spektre Dec 06 '20 at 08:41

2 Answers2

1

I would not proceed by drawing circles. Instead I would fill the disk by scanning the bounding box of the projected disk (this is a square), then computing R² - (X-Xc)² - (Y-Yc)² and drawing when this quantity is positive.

If you need the Z coordinate, this is the square root of the above quantity.


You can evaluate it very quickly in an incremental way, noting that

R² - (X+1-Xc)² - (Y-Yc)² = R² - (X-Xc)² - (Y-Yc)² + 2(X-Xc) + 1 = 
R² - (X-Xc)² - (Y-Yc)² + 2X + 1-2Xc`
0

So it looks like you are drawing just the edge of each circle.

One way to eliminate the holes is to fill in the area inside each circle. You can pick a point anywhere on the circle, then pick the two points directly next to it and fill in a triangle between them. Then, move the second point to the opposite side of the third and draw a new triangle.

Or, you could pick one axis of the circle (x or y) and take the two points that lie on x=some value for every x. You already have opposing points when you call drawC(), so just plug them into draw_line below:

def fill_line(x1, x2, y, z):
    max = max(x1,x2)
    min = min(x1,x2)
    for i in range(min, max):
        coords.append(i, y, z)

You could also iterate over the square each circle is in and find the points within it:

def fill_circle(xy, r, z):
    for i in range(xy[0]-r, xy[0]+r):
        for j in range(xy[1]-r, xy[1]+r):
            if (i - xy[0])**2 + (j - xy[1])**2 < r**2:
                coords.append(i,j,z)

If you get holes between the layers when you fill in the circles, try also drawing circles on another axis.

thshea
  • 1,048
  • 6
  • 18
  • How would I implement, drawing the circle on a nother axis? Currently the circle is parallel to the x-axis. What if I wanted it to be parallel to the z-axis? – DinosaurMoritz Dec 06 '20 at 01:59
  • 1
    The easiest way is to change what variables you iterate over in your loop. Since a sphere is symmetric on all axes, you can just swap x and y, or x and z, everywhere in your code. Since you iterate over x and y, it is parallel to those axes. To make it parallel to the z axis, just swap x and z or y and z. – thshea Dec 06 '20 at 02:02