The circles come out OK but they don't look as nice as the ones that PureBasic draws. I tried compensating with custom smoothing "thresholds" (commented out) in an effort to help the smaller circles match PureBasic's look and hoped that (1-1.0/r) would be sufficient for larger circles to drop the last pixel along the edge. But it still looks kind of bad.
All of the custom thresholds besides the default have been commented out. You can uncomment them to see how they look for comparison.
The example code will draw two circles. The one on the left is from this drawCircle2i() and the one on the right is PureBasic's Circle(). There is a button on the window you can click which will increase the radius by 1 each time it's clicked and redraw the circles.
There are other algorithms to draw circles but it would be interesting to know if this method can be improved.
Circle drawing code:
Code: Select all
Procedure drawCircle2i(x, y, radius, color.l)
Protected r=radius+1 ;/ Radius does not include the center pixel
Protected d=r*2 ;/ Diameter : (r<<1)
Protected rr=r*r ;/ r^2
Protected xi, yi ;/ Integer 0 to r along x / y
Protected dx, dy ;/ Delta x / y for pythagorean (xi - r)^2 and (yi - r)^2
Protected dcx2, dcy2 ;/ Delta from center axis * 2
Protected centerX=r
Protected centerY=r
Protected threshold.f ;/ Smooth the last pixel along the radius
threshold.f=1
;/ Manually tweak threshold for small circles
Select radius
Case 1
Plot(1,0,color)
Plot(0,1,color)
Plot(1,1,color)
Plot(2,1,color)
Plot(1,2,color)
ProcedureReturn
; Case 4
; threshold.f=0.75
; Case 5, 13
; threshold.f=0.9
; Case 6, 7, 8
; threshold.f=0.88
; Case 16, 17
; threshold.f=0.93
; Case 21
; threshold.f=0.94
; Case 22, 23, 24, 25, 26, 27
; threshold.f=0.95
; Case 28
; threshold.f=0.96
; Case 29, 30
; threshold.f=0.9623
; Case 33
; threshold.f=0.965
; Case 37
; threshold.f=0.983
; Default
threshold.f=1-1.0/r
EndSelect
;/ The circle is shifted -1 along x and y to account for
;/ a single pixel appearing at each pole. The pixel is
;/ accounted for by dx+dy '<' rr instead of '<='
;/ to by drawing pixels which are within the radius but
;/ not those which lay on it exactly.
x-1
y-1
For yi=0 To r
dy=(r-yi)*(r-yi)
dcy2=(centerY-yi)*2
For xi=0 To r
dx=(r-xi)*(r-xi)
;/ Point is on or outside the circle
If Not dx+dy<rr
Continue
EndIf
;/ Distance from center 0-1
distance.f=(dx+dy)/rr
;/ Edge threshold (smoothing)
If distance.f>threshold.f
Continue
EndIf
dcx2=(centerX-xi)*2
;/ Vertical axis
If centerX=xi
Plot(x+xi,y+yi,color)
Plot(x+xi,y+yi+dcy2,color)
;/ Horizontal axis
ElseIf centerY=yi
Plot(x+xi,y+yi,color)
Plot(x+xi+dcx2,y+yi,color)
Else
; Top left quadrant
Plot(x+xi,y+yi,color)
;/ Top right quadrant
Plot(x+xi+dcx2,y+yi,color)
;/ Buttom left quadrant
Plot(x+xi,y+yi+dcy2,color)
;/ Bottom right quadrant
Plot(x+xi+dcx2,y+yi+dcy2,color)
EndIf
Next xi
Next yi
EndProcedure
Code: Select all
window=OpenWindow(#PB_Any,0,0,320,320,"")
CreateImage(#Image,320,320,32,#PB_Image_Transparent)
button=ButtonGadget(#PB_Any,65,50,20,20,"")
radius=29
diameter=radius*2
outline=5
r=radius
ImageGadget(#ImageGadget,0,0,320,320,ImageID(#Image))
StartDrawing(ImageOutput(#Image))
DrawingMode(#PB_2DDrawing_AlphaBlend)
drawCircle2i(1,1,radius,RGBA(0,0,0,255))
Circle(radius*2+4+radius,radius+1,radius,RGBA(0,0,0,255))
offsetX=50
offsetYn=9
offsetXY=51
offsetYXn=10
StopDrawing()
SetGadgetState(#ImageGadget,ImageID(#Image))
Repeat
event=WaitWindowEvent(1)
If event=#PB_Event_Gadget And EventGadget()=button
radius+1
FreeImage(#Image)
CreateImage(#Image,320,320,32,#PB_Image_Transparent)
ImageGadget(#ImageGadget,0,0,320,320,ImageID(#Image))
StartDrawing(ImageOutput(#Image))
DrawingMode(#PB_2DDrawing_AlphaBlend)
drawCircle2i(1,1,radius,RGBA(0,0,0,255))
Circle(radius*2+4+radius,radius+1,radius,RGBA(0,0,0,255))
StopDrawing()
SetGadgetState(#ImageGadget,ImageID(#Image))
EndIf
Until event=#PB_Event_CloseWindow