Drawing Empty Circles

Monkey Forums/Monkey Programming/Drawing Empty Circles

APC(Posted 2014) [#1]
In Monkey DrawCirle, DrawEllipse or DrawOval functions fill the shapes.
I need to draw empty circles and I did the following code:
The only problem it is the performance.
Is there another way to draw empty circles in Monkey?

 Method Circle:Void(x:Float, y:Float, w:Float, h:Float)
    For Local phi:Float = 0.0 Until 360.0
        DrawPoint(x + Cos(phi) * w, y - Sin(phi) * h)
    Next
End



GfK(Posted 2014) [#2]
Maybe something like:
Method Circle:Void(x:Float, y:Float, w:Float, h:Float)
    For Local phi:Float = 0.0 Until 360.0 Step 10
        DrawLine(x + Cos(phi-10) * w, y - Sin(phi-10) * h, x + Cos(phi) * w, y - Sin(phi) * h )
    Next
End
Untested, but you're basically drawing 36 short lines, instead of 360 points.


muddy_shoes(Posted 2014) [#3]
http://en.wikipedia.org/wiki/Midpoint_circle_algorithm


APC(Posted 2014) [#4]
Guys thank you , It improved the performance


Gerry Quinn(Posted 2014) [#5]
The simplest thing is to make a Polygon function that draws a polygon of N points, starting at a certain angle. Then for a circle, you just use lots of points.

Of course if the background is unimportant, you can DrawCircle twice in different colours, with the second one having a smaller radius.


Danilo(Posted 2014) [#6]
Translated my old PureBasic code for Bresenham circle and ellipse:
Strict

Import mojo

Function Circle:Void(x:Int, y:Int, radius:Int)
    Local temp_x:Int = 0
    Local d:Int      = 3 - 2 * radius
    
    While temp_x <= radius
        DrawPoint(x+temp_x, y+radius) ' part 1
        DrawPoint(x-temp_x, y+radius)
        DrawPoint(x+radius, y+temp_x) ' part 2
        DrawPoint(x-radius, y+temp_x)
        DrawPoint(x+radius, y-temp_x) ' part 3
        DrawPoint(x-radius, y-temp_x)
        DrawPoint(x-temp_x, y-radius) ' part 4
        DrawPoint(x+temp_x, y-radius)
        
        If d < 0
            d += 4 * temp_x + 6
        Else
            d += 4 * (temp_x - radius) + 10
            radius -= 1
        Endif
        temp_x += 1
    Wend
End

Function Ellipse:Void(x:Int, y:Int, radius_x:Int, radius_y:Int)
    Local temp_x:Int         = 0
    Local temp_y:Int         = radius_y
    Local temp_radius_x1:Int = radius_x * radius_x
    Local temp_radius_y1:Int = radius_y * radius_y
    Local temp_radius_x2:Int = temp_radius_x1 * 2
    Local temp_radius_y2:Int = temp_radius_y1 * 2
    Local temp_fx:Int        = 0
    Local temp_fy:Int        = temp_radius_x2 * radius_y
    Local p:Int              = temp_radius_y1 - temp_radius_x1*radius_y + Int(0.25 * temp_radius_x1)
    
    If radius_x = radius_y
        Circle(x,y,radius_x)
        Return
    Endif
    
    While temp_fx <= temp_fy
        DrawPoint(x+temp_x, y+temp_y)
        DrawPoint(x-temp_x, y+temp_y)
        DrawPoint(x-temp_x, y-temp_y)
        DrawPoint(x+temp_x, y-temp_y)
        
        temp_x += 1
        temp_fx += temp_radius_y2
        If p < 0
            p += temp_fx + temp_radius_y1
        Else
            temp_y  -= 1
            temp_fy -= temp_radius_x2
            p       += temp_fx + temp_radius_y1 - temp_fy
        Endif
    Wend

    DrawPoint(x+temp_x, y+temp_y)
    DrawPoint(x-temp_x, y+temp_y)
    DrawPoint(x-temp_x, y-temp_y)
    DrawPoint(x+temp_x, y-temp_y)
    
    p = temp_radius_y1 * (temp_x - 0.5) * (temp_x + 0.5) + temp_radius_x1 * (temp_y - 1)*(temp_y - 1) - temp_radius_x1 * temp_radius_y1
    
    While temp_y > 0
        temp_y -= 1
        temp_fy -= temp_radius_x2
        If p >= 0
            p -= temp_fy + temp_radius_x1
        Else
            temp_x += 1
            temp_fx += temp_radius_y2
            p += temp_fx - temp_fy + temp_radius_x1
        Endif
        
        DrawPoint(x+temp_x, y+temp_y)
        DrawPoint(x-temp_x, y+temp_y)
        DrawPoint(x-temp_x, y-temp_y)
        DrawPoint(x+temp_x, y-temp_y)
        
    Wend

End

Function RotateAt:Void(x:Float, y:Float, angle:Float)
    Translate(x, y)
    Rotate(angle)
    Translate(-x, -y)
End

Class Program Extends App

    Field rotation:Float = 0

    Method OnCreate:Int()
        SetUpdateRate 60
        Return 0
    End

    Method OnRender:Int()
        Local mid_x:Int = DeviceWidth()  * 0.5
        Local mid_y:Int = DeviceHeight() * 0.5
        Cls 128,128,128

        SetColor 0,0,0
        Circle mid_x, mid_y,40

        RotateAt(mid_x,mid_y,rotation)
        rotation += 5

        'SetColor 255,255,255
        'DrawEllipse mid_x, mid_y,100,60
        'SetColor 0,0,0
        For Local i:Int = 60 To 150 Step 5
            Ellipse mid_x, mid_y,40+i,i
        Next
        Return 0
    End
End

Function Main:Int()
    New Program
    Return 0
End



Shinkiro1(Posted 2014) [#7]
Function DrawCircleOutline:Void(x:Float, y:Float, radius:Float, detail:Int = -1)
	If detail < 0
		detail = radius / 2.0
	ElseIf detail < 3
		detail = 3
	ElseIf detail > MAX_VERTS
		detail = MAX_VERTS
	End

	Local angleStep:Float = 360.0 / detail
	Local angle:Float
	Local offsetX:Float
	Local offsetY:float
	Local first:Bool = True
	Local firstX:Float
	Local firstY:float
	Local thisX:Float
	Local thisY:float
	Local lastX:Float
	Local lastY:Float

	For Local vertIndex:= 0 Until detail
		offsetX = Sin(angle) * radius
		offsetY = Cos(angle) * radius
		If first
			first = False
			firstX = x + offsetX
			firstY = y + offsetY
			lastX = firstX
			lastY = firstY
		Else
			thisX = x + offsetX
			thisY = y + offsetY
			DrawLine(lastX, lastY, thisX, thisY)
			lastX = thisX
			lastY = thisY
		EndIf
		angle += angleStep
	Next
	DrawLine(lastX, lastY, firstX, firstY)
End


You can leave the detail parameter out if you just want to draw a circle.
detail is the number of lines that will be drawn so you can draw polygons with this function too.


Midimaster(Posted 2014) [#8]
Bresenham performs in most cases faster and better, because it draws the exact amount of pixels which is necessary to close the radius.

Only on very big circles you will see advantages with a 5°-steps method. Additional you can speed up this method, because you never need to draw all 360° as you see in the Bresenham. Also the 5°-steps method will be faster if you only draw 45° and mirror all this results 8 times.


Danilo(Posted 2014) [#9]
Bresenham does not use Sin() and Cos(). If you draw 72 lines for 5° steps, each line is still made up
of points, so I think the Bresenham Circle is the simplest. The amount of points required to draw a 360° circle
shouldn't differ much. You just can't take fewer points for the same closed circle.
Could be an advantage when the lines/points get drawn with hardware acceleration, if available for a specific targets.


ziggy(Posted 2014) [#10]
And pre-creating a float array of a circle-like polygon and drawing it using DrawPoly with the given translate and scale? wouldn't this be faster taking into account that it is pre-calculated?


muddy_shoes(Posted 2014) [#11]
The only bit that's precalculated is the initial positions. You're still translating and scaling each time. The actual polygon draw might well be faster though.

I think everyone is getting a bit carried away here with theorising. As usual, Monkey is a cross-platform language. What's fast on one target may be slow on another. Also, this isn't 1990 so just thinking about CPU and avoiding trig isn't necessarily going to find the fastest method considering the way GL rendering works. Frankly, unless you're going circle crazy I doubt there's much noticeable difference between a doing some trig and getting a reasonable number of lines and doing Bresenham's on most of the targets.


Beaker(Posted 2014) [#12]
On HTML5 I created a native line circle function using the built in arc method. Seems very nippy.


ziggy(Posted 2014) [#13]
I think everyone is getting a bit carried away here with theorising.
Yes! That's fun!