conibo Posted August 26, 2013 Share Posted August 26, 2013 (edited) This is Bresenham's circle drawing algorithm for SCAR 3.22. I rediscovered it myself; only after writing it did I look it up on Wikipedia. I reinvented the wheel, kind of. I got the idea from my calculus 2 classes and from Bresenham's line drawing algorithm, which I still don't understand completely. program New; const WHITE = 16777215; BLACK = 0; RED = 209910; BMPSIZE = 499; COSPIFOURTH = 0.7071068; var bmp,qq,y,m,k,j: integer; procedure drawevencircle2(offsetx,offsety,radius:integer); //radius includes the outer pixel var radiussquared,Y0,pixelstodo,count: integer; temp1,temp2,temp3,temp4: integer; begin pixelstodo:=round(radius*COSPIFOURTH); radiussquared:= radius*radius; radiussquared:=radiussquared + radius div 2; Y0:=radius; for count:=0 to pixelstodo do begin temp1:=offsetx+count+1; temp2:=offsety-Y0; temp3:=offsetx-count; temp4:=offsety+Y0+1; fastsetpixel(bmp, temp1,temp2 , RED);//second octant fastsetpixel(bmp, temp3,temp2 , RED);//third octant fastsetpixel(bmp, temp1,temp4 , RED);//seventh octant temp1:= offsetx-Y0; temp2:=offsety+count+1; fastsetpixel(bmp, temp3,temp4 , RED);//sixth octant temp3:=offsety-count; temp4:= offsetx+Y0+1; fastsetpixel(bmp, temp1, temp2, RED);//fifth octant fastsetpixel(bmp, temp1, temp3, RED);//fourth octant fastsetpixel(bmp, temp4, temp2, RED);//eighth octant fastsetpixel(bmp, temp4, temp3, RED);//first octant if((Y0*Y0+count*count)>radiussquared) then Y0:=Y0-1; radiussquared:=radiussquared-1; end; end; procedure drawevencircleopt(offsetx,offsety,radius:integer); //radius includes the outer pixel var radiussquared,Y0,pixelstodo,count,Y0squared,countsquared: integer; temp1,temp2,temp3,temp4: integer; begin pixelstodo:=round(radius*COSPIFOURTH); radiussquared:= radius*radius; Y0squared:=radiussquared; radiussquared:=radiussquared + radius div 2; Y0:=radius; countsquared:=0; for count:=0 to pixelstodo do begin temp1:=offsetx+count+1; temp2:=offsety-Y0; temp3:=offsetx-count; temp4:=offsety+Y0+1; fastsetpixel(bmp, temp1,temp2 , WHITE);//second octant fastsetpixel(bmp, temp3,temp2 , WHITE);//third octant fastsetpixel(bmp, temp1,temp4 , WHITE);//seventh octant temp1:= offsetx-Y0; temp2:=offsety+count+1; fastsetpixel(bmp, temp3,temp4 , WHITE);//sixth octant temp3:=offsety-count; temp4:= offsetx+Y0+1; fastsetpixel(bmp, temp1, temp2, WHITE);//fifth octant fastsetpixel(bmp, temp1, temp3, WHITE);//fourth octant fastsetpixel(bmp, temp4, temp2, WHITE);//eighth octant fastsetpixel(bmp, temp4, temp3, WHITE);//first octant if((Y0squared+countsquared)>radiussquared) then begin Y0squared:=Y0squared-(Y0*2)+1; Y0:=Y0-1; end; countsquared:=countsquared+(count*2)+1; radiussquared:=radiussquared-1; end; end; begin displayDebugImgWindow(BMPSIZE+1,BMPSIZE+1); wait(100); qq := GetDebugCanvas; bmp := BitmapFromString(BMPSIZE, BMPSIZE, ''); y:=getbitmapcanvas(bmp); m:=1; j:=getsystemtime; for k:=1 to 20 do begin m:=m+2+2*k; drawevencircle2(m,22,k); end; for k:=4 to 200 do begin drawevencircle2(250,250,k); k:=k+2; end; writeln(getsystemtime-j); m:=1; j:=getsystemtime; for k:=1 to 20 do begin m:=m+2+2*k; drawevencircleopt(m,22,k); end; for k:=4 to 200 do begin drawevencircleopt(250,250,k); k:=k+2; end; writeln(getsystemtime-j); SafeCopyCanvas(y, qq, 0, 0, BMPSIZE, BMPSIZE, 0, 0, BMPSIZE, BMPSIZE); freebitmap(bmp); end. In this program, ironically, the optimized version is consistently slower than the non-optimized one. EDIT: I messed up the squaring code for the optimized version earlier. It is correct now. Edited August 26, 2013 by conibo 1 Quote Link to comment Share on other sites More sharing options...
Janilabo Posted August 26, 2013 Share Posted August 26, 2013 (edited) This is Bresenham's circle drawing algorithm for SCAR 3.22. I rediscovered it myself; only after writing it did I look it up on Wikipedia.I reinvented the wheel, kind of. I got the idea from my calculus 2 classes and from Bresenham's line drawing algorithm, which I still don't understand completely. program New; const WHITE = 16777215; BLACK = 0; RED = 209910; BMPSIZE = 499; COSPIFOURTH = 0.7071068; var bmp,qq,y,m,k,j: integer; procedure drawevencircle2(offsetx,offsety,radius:integer); //radius includes the outer pixel var radiussquared,Y0,pixelstodo,count: integer; temp1,temp2,temp3,temp4: integer; begin pixelstodo:=round(radius*COSPIFOURTH); radiussquared:= radius*radius; radiussquared:=radiussquared + radius div 2; Y0:=radius; for count:=0 to pixelstodo do begin temp1:=offsetx+count+1; temp2:=offsety-Y0; temp3:=offsetx-count; temp4:=offsety+Y0+1; fastsetpixel(bmp, temp1,temp2 , RED);//second octant fastsetpixel(bmp, temp3,temp2 , RED);//third octant fastsetpixel(bmp, temp1,temp4 , RED);//seventh octant temp1:= offsetx-Y0; temp2:=offsety+count+1; fastsetpixel(bmp, temp3,temp4 , RED);//sixth octant temp3:=offsety-count; temp4:= offsetx+Y0+1; fastsetpixel(bmp, temp1, temp2, RED);//fifth octant fastsetpixel(bmp, temp1, temp3, RED);//fourth octant fastsetpixel(bmp, temp4, temp2, RED);//eighth octant fastsetpixel(bmp, temp4, temp3, RED);//first octant if((Y0*Y0+count*count)>radiussquared) then Y0:=Y0-1; radiussquared:=radiussquared-1; end; end; procedure drawevencircleopt(offsetx,offsety,radius:integer); //radius includes the outer pixel var radiussquared,Y0,pixelstodo,count,Y0squared,countsquared: integer; temp1,temp2,temp3,temp4: integer; begin pixelstodo:=round(radius*COSPIFOURTH); radiussquared:= radius*radius; Y0squared:=radiussquared; radiussquared:=radiussquared + radius div 2; Y0:=radius; countsquared:=0; for count:=0 to pixelstodo do begin countsquared:=countsquared+(count*2)+1; temp1:=offsetx+count+1; temp2:=offsety-Y0; temp3:=offsetx-count; temp4:=offsety+Y0+1; fastsetpixel(bmp, temp1,temp2 , WHITE);//second octant fastsetpixel(bmp, temp3,temp2 , WHITE);//third octant fastsetpixel(bmp, temp1,temp4 , WHITE);//seventh octant temp1:= offsetx-Y0; temp2:=offsety+count+1; fastsetpixel(bmp, temp3,temp4 , WHITE);//sixth octant temp3:=offsety-count; temp4:= offsetx+Y0+1; fastsetpixel(bmp, temp1, temp2, WHITE);//fifth octant fastsetpixel(bmp, temp1, temp3, WHITE);//fourth octant fastsetpixel(bmp, temp4, temp2, WHITE);//eighth octant fastsetpixel(bmp, temp4, temp3, WHITE);//first octant if((Y0squared+countsquared)>radiussquared) then begin Y0:=Y0-1; Y0squared:=Y0squared-(Y0*2)+1; end; radiussquared:=radiussquared-1; end; end; begin displayDebugImgWindow(BMPSIZE+1,BMPSIZE+1); wait(100); qq := GetDebugCanvas; bmp := BitmapFromString(BMPSIZE, BMPSIZE, ''); y:=getbitmapcanvas(bmp); m:=1; j:=getsystemtime; for k:=1 to 20 do begin m:=m+2+2*k; drawevencircle2(m,22,k); end; for k:=4 to 200 do begin drawevencircle2(250,250,k); k:=k+2; end; writeln(getsystemtime-j); m:=1; j:=getsystemtime; for k:=1 to 20 do begin m:=m+2+2*k; drawevencircleopt(m,22,k); end; for k:=4 to 200 do begin drawevencircleopt(250,250,k); k:=k+2; end; writeln(getsystemtime-j); SafeCopyCanvas(y, qq, 0, 0, BMPSIZE, BMPSIZE, 0, 0, BMPSIZE, BMPSIZE); freebitmap(bmp); end. In this program, ironically, the optimized version is consistently slower than the non-optimized one. Nice work conibo!Interesting code you have wrote there. By the way, here is the 3.35+ version of it (tested with 3.38 & 3.39): const WHITE = 16777215; BLACK = 0; RED = 209910; BMPSIZE = 499; COSPIFOURTH = 0.7071068; var bmp: TSCARBitmap; m, k, j: Integer; //radius includes the outer pixel procedure DrawEvenCircle2(offsetx, offsety, radius: Integer); var radiussquared, Y0, pixelstodo, count: Integer; temp1, temp2, temp3, temp4: Integer; begin pixelstodo := Round(radius * COSPIFOURTH); radiussquared := (radius * radius); radiussquared := (radiussquared + (radius div 2)); Y0 := radius; for count := 0 to pixelstodo do begin temp1 := ((offsetx + count) + 1); temp2 := (offsety - Y0); temp3 := (offsetx - count); temp4 := ((offsety + Y0) + 1); bmp.Pixels[temp1, temp2] := RED;//second octant bmp.Pixels[temp3, temp2] := RED;//third octant bmp.Pixels[temp1, temp4] := RED;//seventh octant temp1 := (offsetx - Y0); temp2 := (offsety + count + 1); bmp.Pixels[temp3, temp4] := RED;//sixth octant temp3 := (offsety - count); temp4 := ((offsetx + Y0) + 1); bmp.Pixels[temp1, temp2] := RED;//fifth octant bmp.Pixels[temp1, temp3] := RED;//fourth octant bmp.Pixels[temp4, temp2] := RED;//eighth octant bmp.Pixels[temp4, temp3] := RED;//first octant if (((Y0 * Y0) + (count * count)) > radiussquared) then Y0 := (Y0 - 1); radiussquared := (radiussquared - 1); end; end; //radius includes the outer pixel procedure DrawEvenCircleopt(offsetx, offsety, radius: Integer); var radiussquared, Y0, pixelstodo, count, Y0squared, countsquared: Integer; temp1, temp2, temp3, temp4: Integer; begin pixelstodo := Round(radius * COSPIFOURTH); radiussquared := (radius * radius); Y0squared := radiussquared; radiussquared := (radiussquared + (radius div 2)); Y0 := radius; countsquared := 0; for count := 0 to pixelstodo do begin temp1 := (offsetx + count + 1); temp2 := (offsety - Y0); temp3 := (offsetx - count); temp4 := ((offsety + Y0) + 1); bmp.Pixels[temp1, temp2] := WHITE;//second octant bmp.Pixels[temp3, temp2] := WHITE;//third octant bmp.Pixels[temp1, temp4] := WHITE;//seventh octant temp1 := (offsetx - Y0); temp2 := ((offsety + count) + 1); bmp.Pixels[temp3, temp4] := WHITE;//sixth octant temp3 := (offsety - count); temp4 := ((offsetx + Y0) + 1); bmp.Pixels[temp1, temp2] := WHITE;//fifth octant bmp.Pixels[temp1, temp3] := WHITE;//fourth octant bmp.Pixels[temp4, temp2] := WHITE;//eighth octant bmp.Pixels[temp4, temp3] := WHITE;//first octant if ((Y0squared + countsquared) > radiussquared) then begin Y0squared := (Y0squared - (Y0 * 2) + 1); Y0 := (Y0 - 1); end; countsquared := (countsquared + (count * 2) + 1); radiussquared := (radiussquared - 1); end; end; begin DisplayDebugImgWindow((BMPSIZE + 1), (BMPSIZE + 1)); Wait(100); bmp := TSCARBitmap.Create(''); bmp.SetSize(BMPSIZE, BMPSIZE); m := 1; j := GetSystemTime; for k := 1 to 20 do begin m := ((m + 2) + (2 * k)); DrawEvenCircle2(m, 22, k); end; for k := 4 to 200 do begin DrawEvenCircle2(250, 250, k); k := (k + 2); end; WriteLn(IntToStr(GetSystemTime - j) + ' ms.'); m := 1; j := GetSystemTime; for k := 1 to 20 do begin m := ((m + 2) + (2 * k)); DrawEvenCircleopt(m, 22, k); end; for k := 4 to 200 do begin DrawEvenCircleopt(250, 250, k); k := (k + 2); end; WriteLn(IntToStr(GetSystemTime - j) + ' ms.'); DebugBitmap(bmp); bmp.Free; end. Once again, good job conibo! Thanks for sharing your code with the community man. Edited August 26, 2013 by Janilabo Included squaring fixes by conibo Quote Link to comment Share on other sites More sharing options...
slacky Posted August 27, 2013 Share Posted August 27, 2013 (edited) What you got there is a bad implementation of Bresenham's circle, and the use of a constant which from what I see is unsuited (COSPIFOURTH), and it destroys small circles from what I can tell. Not tryin' to be rude, but just stating the obvious. Now this is a proper implementation of Bresenham's circle: program new; { Creates all the points needed to define a simple circle } function Circle(C: TPoint; Radius:Integer): TPointArray; var err,x,y,h: Integer; begin x := radius; y := 0; err := 1-x; h := 0; while(x >= y) do begin h := h + 8; SetLength(Result, h); Result[h-1] := Point(C.x + x, C.y + y); Result[h-2] := Point(C.x - x, C.y + y); Result[h-3] := Point(C.x + x, C.y - y); Result[h-4] := Point(C.x - x, C.y - y); Result[h-5] := Point(C.x + y, C.y + x); Result[h-6] := Point(C.x - y, C.y + x); Result[h-7] := Point(C.x + y, C.y - x); Result[h-8] := Point(C.x - y, C.y - x); Inc(y); if(err < 0) then err := err + (2*y+1) else begin Dec(x); err := err + (2*(y-x+1)); end; end; end; var i: Integer; TPA: TPointArray; bmp: TSCARBitmap; begin bmp := TSCARBitmap.Create(''); bmp.SetSize(300, 300); for i:=1 to 12 do begin tpa := Circle(Point(150,150), Round(Sqr(i))); bmp.SetPixels(TPA, RandomRange(100000,16000000)); end; DebugBitmap(bmp); bmp.Free; end. But it's nice to see people contributing, so don't let me scare you away just cuz I can be an ass!! :-) Edited November 27, 2013 by slacky Uppdated - Support SCAR 3.4 Quote Link to comment Share on other sites More sharing options...
FHannes Posted August 27, 2013 Share Posted August 27, 2013 I like how slacky's circles have colors Quote Link to comment Share on other sites More sharing options...
conibo Posted August 27, 2013 Author Share Posted August 27, 2013 What you got there is a bad implementation of Bresenham's circle, and the use of a constant which from what I see is unsuited (COSPIFOURTH), and it destroys small circles from what I can tell. My algorithm as posted doesn't really distort the smaller circles; it just assumes the center of the circle is between pixels, not on a pixel. Thus, "drawevencircle". Radius times the cosine of one fourth of pi is the number of pixels in an octant, which is what I want to draw. All that's required to turn the algorithm into a near-perfect Bresenham clone is changing the following lines temp1:=offsetx+count+1; temp4:=offsety+Y0+1; temp2:=offsety+count+1; temp4:= offsetx+Y0+1; with these, respectively: temp1:=offsetx+count; temp4:=offsety+Y0; temp2:=offsety+count; temp4:= offsetx+Y0; Quote Link to comment Share on other sites More sharing options...
slacky Posted August 28, 2013 Share Posted August 28, 2013 When small circles turns out as squares then the circle is not a circle, so it's distorted. I was aware that you used a square of four pixels, a .5px center, but I don't see the reason. And my circles have colors! I win! Quote Link to comment Share on other sites More sharing options...
conibo Posted August 28, 2013 Author Share Posted August 28, 2013 I just thought of an optimization for my algorithm that would probably have been obvious to the more experienced programmers: merging Y0squared and countsquared into one variable. For Scar 3.38, modified from the code graciously posted by Janilabo: procedure DrawEvenCircleopt(offsetx, offsety, radius: Integer); var radiussquared, Y0, pixelstodo, count, Y0squaredpluscountsquared: Integer; temp1, temp2, temp3, temp4: Integer; begin pixelstodo := Round(radius * COSPIFOURTH); radiussquared := (radius * radius); Y0squaredpluscountsquared := radiussquared; radiussquared := (radiussquared + (radius div 2)); Y0 := radius; for count := 0 to pixelstodo do begin temp1 := (offsetx + count); temp2 := (offsety - Y0); temp3 := (offsetx - count); temp4 := (offsety + Y0); bmp.Pixels[temp1, temp2] := WHITE;//second octant bmp.Pixels[temp3, temp2] := WHITE;//third octant bmp.Pixels[temp1, temp4] := WHITE;//seventh octant temp1 := (offsetx - Y0); temp2 := (offsety + count); bmp.Pixels[temp3, temp4] := WHITE;//sixth octant temp3 := (offsety - count); temp4 := (offsetx + Y0); bmp.Pixels[temp1, temp2] := WHITE;//fifth octant bmp.Pixels[temp1, temp3] := WHITE;//fourth octant bmp.Pixels[temp4, temp2] := WHITE;//eighth octant bmp.Pixels[temp4, temp3] := WHITE;//first octant if ((Y0squaredpluscountsquared) > radiussquared) then begin Y0squaredpluscountsquared := (Y0squaredpluscountsquared - (Y0 * 2) + 1); Y0 := (Y0 - 1); end; Y0squaredpluscountsquared := (Y0squaredpluscountsquared + (count * 2) + 1); radiussquared := (radiussquared - 1); end; end; And for Scar 3.22: procedure drawevencircleopt(offsetx,offsety,radius:integer); var radiussquared,Y0,pixelstodo,count,Y0squaredpluscountsquared: integer; temp1,temp2,temp3,temp4: integer; begin pixelstodo:=round(radius*COSPIFOURTH); radiussquared:= radius*radius; Y0squaredpluscountsquared:=radiussquared; radiussquared:=radiussquared + radius div 2; Y0:=radius; for count:=0 to pixelstodo do begin temp1:=offsetx+count; temp2:=offsety-Y0; temp3:=offsetx-count; temp4:=offsety+Y0; fastsetpixel(bmp, temp1,temp2 , WHITE);//second octant fastsetpixel(bmp, temp3,temp2 , WHITE);//third octant fastsetpixel(bmp, temp1,temp4 , WHITE);//seventh octant temp1:= offsetx-Y0; temp2:=offsety+count; fastsetpixel(bmp, temp3,temp4 , WHITE);//sixth octant temp3:=offsety-count; temp4:= offsetx+Y0; fastsetpixel(bmp, temp1, temp2, WHITE);//fifth octant fastsetpixel(bmp, temp1, temp3, WHITE);//fourth octant fastsetpixel(bmp, temp4, temp2, WHITE);//eighth octant fastsetpixel(bmp, temp4, temp3, WHITE);//first octant if((Y0squaredpluscountsquared)>radiussquared) then begin Y0squaredpluscountsquared:=Y0squaredpluscountsquared-(Y0*2)+1; Y0:=Y0-1; end; radiussquared:=radiussquared-1; Y0squaredpluscountsquared:=Y0squaredpluscountsquared+(count*2)+1; end; end; Quote Link to comment Share on other sites More sharing options...