May 08, 2024, 06:42:09 PM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


Drawing Arcs of a Circle

Started by GWS, June 18, 2008, 03:31:12 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

GWS

Hi folks,

I've been playing with how to draw arcs of any length up to a full circle.

This is the test program I've worked with, and the Subroutine Arc() that does the drawing ..


def w:WINDOW
def wstyle,x,y,r:int
def start,finish,colour:int

DECLARE arc(w:window,radius:int, startangle:int, endangle:int, xpos:int, ypos:int)

autodefine = "OFF"

wstyle = @SIZE|@MINBOX|@MAXBOX

WINDOW w,50,50,600,500,wstyle,0,"Window Title",main
SETWINDOWCOLOR w,RGB(0,0,50)

r = 100 :' set the radius
x = 300: y = 200 :' set the x,y co-ordinates

colour = 0xff0000 :' set the line colour ..

setlinestyle w, @lssolid, 3 :' set the line thickness ..

start = 250: finish = 90 :' set the start and finish angles ..


arc(w,r,start,finish,x,y)


CONTROL w,"B,Exit,(600-100)/2, 400, 100, 35, 0, 1"

WAITUNTIL w = 0

END

SUB main
SELECT @CLASS
case @IDCLOSEWINDOW
CLOSEWINDOW w
case @IDCONTROL
SELECT @CONTROLID
CASE 1
CLOSEWINDOW w
ENDSELECT
ENDSELECT
RETURN

sub arc(win, radius, startangle, endangle, xpos, ypos, colour)
' routine to draw an arc of a circle of given radius centered at xpos,ypos pixels in the window.
' the startangle and endangle specify the start and finish of the arc, measured in Degrees - with
' 0 degrees being visualised at 12 o'clock, 90 degrees at 3 o'clock etc ..
def pi,d2r,rad:float
def angle,span,anglestep,sx,sy:int

pi = 4 * atan(1)
d2r = pi/180

if (endangle < startangle) then endangle = endangle + 360

span = endangle - startangle
' choose a reasonable incremental step size depending on the section of the circle the arc covers ..
' a full circle is represented quite well with steps at 10 degree intervals.
anglestep = floor(10.0 * span / 360)

if (anglestep < 1) then anglestep = 1

rad = startangle * d2r

sx = xpos + radius * sin(rad): sy = ypos - radius * cos(rad)
move w, sx, sy

angle = startangle

do
rad = angle * d2r
sx = xpos + radius * sin(rad): sy = ypos - radius * cos(rad)
line w, sx, sy, colour
angle = angle + anglestep
until (angle > endangle)

return



all the best, :)

Graham
Tomorrow may be too late ..

aurelCB

Hi Graham may i ask you something,why you use autodefine "OFF"?

aurelCB

I made this change and program work?
def w:WINDOW
def wstyle,x,y,r:int
def start,finish,colour:int

DECLARE arc(w:window,radius:int, startangle:int, endangle:int, xpos:int, ypos:int)

'autodefine = "OFF"

wieirtf = @SIZE|@MINBOX|@MAXBOX

WINDOW w,50,50,600,500,wstyle,0,"Window Title",main
SETWINDOWCOLOR w,RGB(0,0,50)

r = 100 :' set the radius
x = 300: y = 200 :' set the x,y co-ordinates

colour = 0xff0000 :' set the line colour ..

setlinestyle w, @lssolid, 3 :' set the line thickness ..

start = 250: finish = 90 :' set the start and finish angles ..


arc(w,r,start,finish,x,y)


CONTROL w,"B,Exit,(600-100)/2, 400, 100, 35, 0, 1"

WAITUNTIL w = 0

END

SUB main
SELECT @CLASS
case @IDCLOSEWINDOW
CLOSEWINDOW w
case @IDCONTROL
SELECT @CONTROLID
CASE 1
CLOSEWINDOW w
ENDSELECT
ENDSELECT
RETURN

sub arc(win, radius, startangle, endangle, xpos, ypos, colour)
' routine to draw an arc of a circle of given radius centered at xpos,ypos pixels in the window.
' the startangle and endangle specify the start and finish of the arc, measured in Degrees - with
' 0 degrees being visualised at 12 o'clock, 90 degrees at 3 o'clock etc ..
def pi,d2r,rad:float
def angle,span,anglestep,sx,sy:int

pi = 4 * atan(1)
d2r = pi/180

if (endangle < startangle) then endangle = endangle + 360

span = endangle - startangle
' choose a reasonable incremental step size depending on the section of the circle the arc covers ..
' a full circle is represented quite well with steps at 10 degree intervals.
anglestep = floor(10.0 * span / 360)

if (anglestep < 1) then anglestep = 1

rad = startangle * d2r

sx = xpos + radius * sin(rad): sy = ypos - radius * cos(rad)
move w, sx, sy

angle = startangle

do
rad = angle * d2r
sx = xpos + radius * sin(rad): sy = ypos - radius * cos(rad)
line w, sx, sy, colour
angle = angle + anglestep
until (angle > endangle)

return

GWS

Hi Aurel,

The Autodefine = "OFF" statement, means that if I forget to define the type of a variable (as Int or Float for example), CBasic will refuse to compile and will warn me I've not defined the variable type.

If you don't have it, CBasic will define your variables automatically.  This can cause a problem if some variable you wanted to hold a 'Float' (ie decimal value), gets autodefined as an integer.  Then if your program loads say 2.678 into it, what you get is just 2 - the decimal part will be truncated.   This type of problem can be hard to find.

So I almost always use Autodefine = "OFF" to ensure I always define every variable in a DEF statement.  Better safe than sorry ..  :)

Graham
Tomorrow may be too late ..

GWS

Here's a test example using the Arc() subroutine to draw a rounded rectangle ..  :)

(By the way, I notice the code above has little square boxes on some lines - I think where Tabs were in the CBasic editor.  Just ignore them, if you copy the program and paste it into CBasic, they disappear again - bit of a nuisance though  ::))


def win:window
def cornerrad,border,fill,run:int
def left,top,width,height,thick:int
def twopi,th:float
def red,grn,blu:int

declare rrect(left:int,top:int,width:int,height:int,border:int,fill:int,radius:int,thick:int)
declare arc(w:window,radius:int,startangle:int,endangle:int,xpos:int,ypos:int,colour:int)

autodefine = "off"

window win,0,0,400,300,@MINBOX|@MAXBOX|@SIZE,0,"Rounded Rectangle Test",main
centerwindow win
setwindowcolor win,rgb(0,0,100)

twopi = 8 * atan(1)

starttimer win,100

width = 150
left = (400-150)/2
top = 100
height = 80
cornerrad = 15
thick = 2
fill = 0x999999
border = 0xff0000

control win,"T,CBasic,left+thick,top+cornerrad,width-2*thick,height-2*cornerrad,@cteditcenter|0x200,1"
setfont win,"Arial",20,600,@SFITALIC,1

rrect(left,top,width,height,border,fill,cornerrad,thick)

run = 1
waituntil run = 0
stoptimer win
closewindow win
end

sub main
select @class
case @idclosewindow
run=0
case @idcreate
centerwindow win
case @idtimer
rrect(left,top,width,height,border,fill,cornerrad,thick)
endselect
return

sub rrect(left,top,width,height,border,fill,radius,thick)
' draw a rounded rectangle ..

setlinestyle win,@lssolid,thick

' top,bottom, and side lines
line win,left+radius,top,left+width-radius,top,border
line win,left+radius,top+height,left+width-radius,top+height,border
line win,left,top+radius,left,top+height-radius,border
line win,left+width-1,top+radius,left+width-1,top+height-radius,border

' draw corner arcs ..
arc(win,radius,0,90,left+width-radius,top+radius,border)
arc(win,radius,90,180,left+width-radius,top+height-radius,border)
arc(win,radius,180,270,left+radius,top+height-radius,border)
arc(win,radius,270,360,left+radius,top+radius,border)

' fill in the space around the text box
floodfill win, left+radius, top+radius, fill

' slowly change the text color ..
th = th + 0.04
red = 127.5 * (1 + cos(th))
grn = 127.5 * (1 + cos(th + twopi/3))
blu = 127.5 * (1 + cos(th + 2 * twopi/3))

setcontrolcolor win,1,rgb(red,grn,blu),fill

return

sub arc(win, radius, startangle, endangle, xpos, ypos, colour)
' routine to draw an arc of a circle of given radius centered at xpos,ypos pixels in the window.
' the startangle and endangle specify the start and finish of the arc, measured in Degrees - with
' 0 degrees being visualised at 12 o'clock, 90 degrees at 3 o'clock etc ..
def pi,d2r,rad:float
def angle,span,anglestep,sx,sy:int

pi = 4 * atan(1)
d2r = pi/180

if (endangle < startangle) then endangle = endangle + 360

span = endangle - startangle
' choose a reasonable incremental step size depending on the section of the circle the arc covers ..
' a full circle is represented quite well with steps at 10 degree intervals.
anglestep = floor(10.0 * span / 360)

if (anglestep < 1) then anglestep = 1

rad = startangle * d2r

sx = xpos + radius * sin(rad): sy = ypos - radius * cos(rad)
move w, sx, sy

angle = startangle

do
rad = angle * d2r
sx = xpos + radius * sin(rad): sy = ypos - radius * cos(rad)
line w, sx, sy, colour
angle = angle + anglestep
until (angle > endangle)

return



all the best, :)

Graham
Tomorrow may be too late ..

aurelCB


Bruce Peaslee

Quote from: GWS on June 19, 2008, 01:25:54 AM
Hi Aurel,

The Autodefine = "OFF" statement, means that if I forget to define the type of a variable (as Int or Float for example), CBasic will refuse to compile and will warn me I've not defined the variable type.

If you don't have it, CBasic will define your variables automatically.  This can cause a problem if some variable you wanted to hold a 'Float' (ie decimal value), gets autodefined as an integer.  Then if your program loads say 2.678 into it, what you get is just 2 - the decimal part will be truncated.   This type of problem can be hard to find.

So I almost always use Autodefine = "OFF" to ensure I always define every variable in a DEF statement.  Better safe than sorry ..  :)

Graham

Also a hard bug to find is when you misspell a variable name. With autodefin off you get an error.
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

GWS

Yep .. I'd forgotten that one ..  :)

Graham
Tomorrow may be too late ..

LarryMc

Graham,

I played around with your program and changed it to use the DEVICE CONTEXT described recently and similiar to what I used in my gage custom controls.

Not trying to say which is better but purely as an example of what Paul was talking about.

It looks real clean in EBasic because EB has a COSD and a SIND command so everything is straight-forward in degrees.

Larrydef w:WINDOW
def wstyle,x,y,r:int
def start,finish,colour:int
def pi:float
pi = 4 * atan(1)

DECLARE "gdi32.dll",AngleArc(hdc:INT, x:INT, y:INT, dwRadius:INT, eStartAngle:float, eSweepAngle:float),INT
DECLARE "gdi32.dll", MoveToEx(hdc AS INT,x AS INT,y AS INT,lpPoint AS POINTER),INT

DECLARE arc2(w:window,radius:int, startangle:int, endangle:int, xpos:int, ypos:int,colour:int)
DECLARE cosd(ang:float)
DECLARE sind(ang:float)

autodefine = "OFF"

wstyle = @SIZE|@MINBOX|@MAXBOX

WINDOW w,50,50,600,500,wstyle,0,"Window Title",main
SETWINDOWCOLOR w,RGB(0,0,50)

r = 100 :' set the radius
x = 300: y = 200 :' set the x,y co-ordinates

colour = 0xff0000 :' set the line colour ..

setlinestyle w, @lssolid, 3 :' set the line thickness ..

start = 45: finish = 135 :' set the start and finish angles ..

arc2(w,r,start,finish,x,y,colour)


CONTROL w,"B,Exit,(600-100)/2, 400, 100, 35, 0, 1"

WAITUNTIL w = 0

END

SUB main
SELECT @CLASS
case @IDCLOSEWINDOW
CLOSEWINDOW w
case @IDCONTROL
SELECT @CONTROLID
CASE 1
CLOSEWINDOW w
ENDSELECT
ENDSELECT
RETURN

sub arc2(win:window, radius, startangle, endangle, xpos, ypos, colour)
' routine to draw an arc of a circle of given radius centered at xpos,ypos pixels in the window.
' the startangle and endangle specify the start and finish of the arc, measured in Degrees - with
' 0 degrees being visualised at 3 o'clock, 90 degrees at 12 o'clock etc ..
' (which matches what is normally found in mathematics)
def hdc:int
def ret:int
def SweepAngle:float
def eStartAngle:float
DEF ptr:POINTER

if (endangle < startangle) then endangle = endangle + 360

SweepAngle=endangle-startangle
eStartAngle=startangle

FRONTPEN w, colour
hdc = GetHDC w

MoveToEx(hdc,xpos+cosd(eStartAngle)*radius,ypos-sind(eStartAngle)*radius,ptr)

ret=AngleArc(hdc, xpos, ypos, radius, eStartAngle,SweepAngle )
ReleaseHDC w, hdc
return

sub cosd(ang:float)
def ret:float
ret = ang*pi/180
ret=cos(ret)
return ret

sub sind(ang:float)
def ret:float
ret = ang*pi/180
ret=sin(ret)
return ret
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

GWS

Thanks Larry .. COSD, SIND .. ah yes, that's in the all singing dancing Basic ..  :)

As you say, we don't have those in CBasic - all angles are in radians.  It's easy to convert into radians though.

Again, my idea as always is to keep to just the tools included in the language wherever possible.  The API routines can do clever things, but at the expense of defining them in your program - some can be quite tricky.

This 'AngleArc' for instance.  The name of API routines are case sensitive - which you have to watch out for.  You need to think about the concept of 'hdc' the 'device context' - not an easy thing for anyone just learning programming.  Then there's getting the parameters defined correctly.  Some parameters are strange types (pointers etc) and can be confusing until you are used to them.

This routine by the way - the second parameter refers to the arc 'sweep angle' not the end angle.  So if you try startangle 45 degrees, endangle 35 degrees - your example and mine not surprisingly differ.

I agree that from a math viewpoint, sweep vector angles start from 'East' and rotate anti-clockwise.  Since for this application of drawing arcs, I found the idea of a clock face with 0 degrees at 12 o'clock, and rotating clockwise like a clock, was easier to think about.

90 degrees at 3 o'clock, 180 degrees at 6 o'clock etc ..

A matter of preference I suppose.

all the best, :)

Graham

Tomorrow may be too late ..

GWS

For completeness, here's a program donated by Larry Adcock several years ago .. thanks Larry ..  :)

It looks a similar approach to the one I posted above.  I've made a couple of cosmetic changes to color the window, and to centre the 'OK' button.


' Ibasic PROGRAM demonstrating arc() funtion to draw arcs, since Ibasic CIRCLE command does not have start and end angle parameters
' Written by L a r r y   A d c o c k   Feb. 28, 2002 - Credit appreciated... Compensation appreciated even more
'
' The last parameter, arcdrawcolor, is optional... if it has not been stated in an ARC call, it will draw using the FRONTPEN color,
' but once it has been stated, all arcs will be drawn the stated color until explicitly changed
DEF win1:WINDOW
declare arc (arcwin:WINDOW, arcxcenter:DOUBLE, arcycenter:DOUBLE, arcrad:DOUBLE, arcstang:DOUBLE, arcendang:DOUBLE,  arcdrawcolor:INT)

run = 1
WINDOW win1,10,10,500,400,@size|@minbox,0, "ARC demo",myhandler
CONTROL win1,"B,OK,(500-50)/2,320,50,20,0,1"
setwindowcolor win1, 0x555555


rad=0
for aa=1 to 10
rad=rad+10

arc(win1,150,150,rad,0,90,RGB(0,255,0))
arc(win1,250,150,rad,180,270,rgb(0,0,255))

next aa

'process messages until someone closes us
WAITUNTIL run = 0
CLOSEWINDOW win1
END

'=============================================================
SUB arc (arcwin:WINDOW, arcxcenter:DOUBLE, arcycenter:DOUBLE, arcrad:DOUBLE, arcstang:DOUBLE, arcendang:DOUBLE, arcdrawcolor:INT)
def divi,facet,cw:INT
def facitang,ang,x1,y1,x2,y2:DOUBLE
'if the drawing surface has normal Windows +x right +y down
'set cw=1 for clockwise or cw=-1 for counter-clockwise
cw=-1
divi=int((arcstang-arcendang)/4)
facetang=(arcstang-arcendang)/(cw*divi)
divi=abs(divi)

ang=arcstang
x2=arcxcenter+(rad*cos(ang*0.0174532925199433))
y2=arcycenter+(rad*sin(ang*0.0174532925199433))
for facet= 1 to divi
  x1=x2
  y1=y2
  ang=ang+facetang
  x2=arcxcenter+(rad*cos(ang*0.0174532925199433))
  y2=arcycenter+(rad*sin(ang*0.0174532925199433))
  line arcwin,x1,y1,x2,y2,arcdrawcolor
next facet
RETURN
'=============================================================

'the message handler subroutine for the window
sub myhandler
select @CLASS
case @IDCLOSEWINDOW
run = 0
case @IDCONTROL
select @CONTROLID
      case 1
        run=0
endselect
endselect
return



best wishes,  :)

Graham
Tomorrow may be too late ..