## Four (Count 'Em) Methods for Drawing Arcs!

### In a Liberty BASIC Graphics Window or GraphicBox

#### Featuring programming by Alyce Watson, David Drake, Tom Nally and Andy Amaya

### compiled by Tomas J. Nally

#### Steelweaver52@aol.com

| Watson's Method |

| Drake's Method |

| Nally's Method |

| Amaya's Method |

### "I'm Trying to Draw an Arc..."

In April of 2002, "lazman2001" left a modest request in the LBExp (Liberty BASIC Expert) newsgroup on Yahoo:

    I'm trying to draw an arc in a graphics window.
    LB doesn't have an arc command. How would I draw
    an arc with the following information.

    X and Y center point
    Radius
    starting angle
    ending angle

    Any ideas?
    ~laz

Any ideas you ask? If there is one thing that the helpful and creative LB community has, it's ideas! Our five or six newsgroups are spilling over with ideas! We can hardly contain them! Even our ideas have ideas!

To Laz's satisfaction, users of the LBExp group posted three effective ways for drawing arcs within a day of his request. All three of those code samples are posted below, with permission of the authors.

But programming geometry is too much fun, and can't be confined by time. Conforums group member Andy Amaya has recently produced another arc drawing routine, which we also feature here with his permission.

To effectively use any of the methods, it's important to understand what input is required by each routine. I'll leave that up to the readers to identify the input as the four methods are presented below.

| Top |

### Alyce Calls the Windows API

Screenshot: Alyce's arc generator

Few among us can command the Windows API as effectively as Alyce Watson, and she responded to Laz' request using the tools written by the Microsoft engineers.

As you examine the code below, note that the Windows API arc function has nine arguements. These arguements are identified in the comments at the end of the routine.

In addition to the fact that The Alyce method is API-based, it differs from the proceeding two methods in another respect: the arcs output by the routine can be portions of ellipses, not just circles.

To use this method in your programs, copy the code shown below, and paste it into your favorite LB editor.

    NoMainWin
    WindowWidth=600:WindowHeight=400

    Open "Arc" For graphics_nsb_nf As #1
    Print #1, "trapclose [quit]"
    Print #1, "down; fill yellow;size 5"
    Print #1, "color blue"

    hwDC=GetDC(hWnd(#1))

    Call Arc hwDC,10,10,350,250,40,100,300,200

    'to flush graphics drawn via api:
    Print #1, "getbmp arc 0 0 500 300"
    Print #1, "drawbmp arc 0 0;flush"

    Wait

    [quit]
    Call ReleaseDC hWnd(#1), hwDC
    Close #1:End

    Sub Arc hDC,x1,y1,x2,y2,x3,y3,x4,y4
    CallDLL #gdi32, "Arc",hDC As long,x1 As long,y1 As long,_
    x2 As long,y2 As long,x3 As long,y3 As long,_
    x4 As long,y4 As long,results As boolean
    End Sub

    Function GetDC(hWnd)
    CallDLL #user32, "GetDC",hWnd As long,GetDC As long
    End Function

    Sub ReleaseDC hWnd, hDC
    CallDLL#user32,"ReleaseDC",hWnd As long,_
    hDC As long,result As long
    End Sub

    ' BOOL Arc(
    ' HDC hdc, // handle to device context
    ' Int nLeftRect, // x-coordinate of bounding rectangle’s upper-left corner
    ' Int nTopRect, // y-coordinate of bounding rectangle’s upper-left corner
    ' Int nRightRect, // x-coordinate of bounding rectangle’s lower-right corner
    ' Int nBottomRect,// y-coordinate of bounding rectangle’s lower-right corner
    ' Int nXStartArc, // first radial ending point
    ' Int nYStartArc, // first radial ending point
    ' Int nXEndArc, // second radial ending point
    ' Int nYEndArc // second radial ending point

| Top |

### D^2 Writes a Sub

Screenshot: Drake's arc generator

About the same time that Alyce was posting her message, David Drake was uploading his own method.

David wrote a `Sub`, to which the user would pass the following six arguments:

- startAngle
- endAngle
- segments
- centerX
- centerY
- radius

In David's routine, the zero angle is "due east" of the arc center, and the arcs are drawn counterclockwise.

I made one small adjustment to David's routine. His program requires that the starting and ending angles of the arc be in units of radians. I added a couple of variables that allow the user to specify the start and end angles in degrees, after which the angles are converted to radians for use by the sub.

The user may identify these variables as he studies the code below for use in his own program.

    'This demo has an arc maker as a SUB
    'By David Drake
    'With Minor adjustments by Nally, May 2003

    'window stuff
    nomainwin
    WindowWidth = 500
    WindowHeight = 400
    open "Drawing Arcs - By David Drake" for graphics as #1
    print #1, "trapclose [quit]"
    print #1, "color black;backcolor white"
    print #1, "down"

    'Parameters of arc

    startAngleD =  90  ' Start angle given in degrees
                       ' but converted to radians below

    startAngle  = (startAngleD / 360) * (2 * 3.14159)

    endAngleD  =  270  ' End angle given in degrees
                       ' but converted to radians below

    endAngle   = (endAngleD / 360) * (2 * 3.14159)

    segments   = 180   ' Segments in arc

    centerX    = 150   ' Center of arc X (pixels)
    centerY    = 200   ' Center of arc Y (pixels)
    radius     = 100   ' Radious of arc (pixels)

    call arc startAngle, endAngle, segments, centerX, centerY, radius

    wait
    [quit]
    close #1

    'Subroutine that draws arc
    sub arc startAngle, endAngle, segments, centerX, centerY, radius

        'endAngle = int(endAngle*100)
        resolution = (endAngle-startAngle)/segments ' Calculates
                                                    ' steps in arc based on segments
        x = centerX+radius*cos(startAngle) ' Calculate start point of arc (X)
        y = centerY+radius*sin(startAngle) ' Calculate start point of arc (Y)
        print #1, "place ";x;" ";y         ' Place starting point of arc

        for angle = startAngle to endAngle step resolution ' Sweep arc
            x = centerX+radius*cos(angle) ' Calculate arc point (X)
            y = centerY+radius*sin(angle) ' Calculate arc point (Y)
            print #1, "goto ";x;" ";y     ' Draw line to next point on arc
        next angle

    end sub

| Top |

### Nally Writes a Function

Screenshot: Nally's Arc Generator

Screenshot: Stars made by calling GenArc()

My own version of arc-drawing turned out to be quite similar to David Drake's in the use of geometry. Our routines even have exactly the same input, though the input is sent to the routine in slightly different order.

One difference in my routine is that I used a user-defined `Function` instead of a `Sub`. (I had no reason for choosing a `Function` as my tool, other than the fact that I had never used a `Sub` before, and knew a function would do the job.) To call my function, the programmer would need a line that looks something like this:

    A = GenArc(Xc, Yc, R, StartD, EndD, NLS)

If you look at the code of the function below, you will see that the function will draw an arc, while the variable `GenArc` will never be assigned a value. Consequently, the variable `A` is never assigned a value either. This is an atypical use of a user-defined `Function`, but it does work.

One unintended capability of my arc-drawing function (and probably of David Drake's, too) is the ability to draw objects that look like stars. This capability arises because of the fact the function divides the arc into a series of small, adjacent "subarcs", then connects the endpoints of these subarcs with line segments. But if the arc to be drawn is a large multiple of 360 degrees (7200 degrees, say), and if the number of line segments used to draw the arc is a prime number (47, say), then the line segments will criss-cross the circle of the arc connecting the endpoints of the 47 subarcs. A star--like one of those shown on the right--is the result.

As seen below, the function itself is called `GenArc()`. But included in the zip archive of this newsletter is more complete BASIC program called `GenArc01.bas` which provides a simple GUI to allow the user to test out the function by adjusting the input one value at a time.

See the `GenArc()` function below.

    Function GenArc(Xc, Yc, R, StartD, EndD, NLS)
        pi = 4*atn(1)
        StartR = (StartD/360)*(2*pi)
        EndR   = (EndD/360)*(2*pi)
        ArcElement = (EndR - StartR) / NLS

        xplot(0) = Xc + R * cos(StartR)
        yplot(0) = Yc - R * sin(StartR)

        For i = 1 to NLS
            xplot(i) = Xc + R * cos(StartR + i*ArcElement)
            yplot(i) = Yc - R * sin(StartR + i*ArcElement)
        Next i

        'Draw a small cross at the arc center

        print #main.Gbox1, "line " + str$(Xc - 5) + " " + str$(Yc)+ " " + str$(Xc + 5) + " " + str$(Yc)
        print #main.Gbox1, "line " + str$(Xc) + " " + str$(Yc + 5)+ " "  + str$(Xc) + " " + str$(Yc - 5)

        'Plot the Arc

        For i = 1 to NLS
            ArcString$ = Str$(xplot(i-1)) + " " + _
                         Str$(yplot(i-1)) + " " + _
                         Str$(xplot(i)) + " " + _
                         Str$(yplot(i))
            print #main.Gbox1, "line " + ArcString$
        next i

    End Function

| Top |

### Andy Amaya's Artistic Arcs

Screenshot: Amaya's arc generator

Just as I started compiling this article on drawing arcs about a week ago, Andy Amaya posted this interesting message on the Conforums message board:

    Hi All,

    Have recently uploaded two graphics demos
    to the FTP site for this forum (at bottom
    of forum home page).

    arcSUB demo.bas - shows how to implement
    drawing arcs (parts of a circle) in LB using
    dots (single plotted points) or using lines
    to connect the dots. Also demonstrates how
    to move sprites in perfect circles.

Though sometimes less is more, in this case more is more, so we decided to ask Andy's permission to include his creative work in this article. He agreed.

The input to Andy's `Sub` is quite similar in substance to the input in Drake's `Sub` and in Nally's `Function`. There are at least two differences, however. In Andy's routine, one does not specify the number of "subarcs" in the arc. Rather, one sends the `incSize` to the `Sub`, which is the spacing between the points of the arc.

The second difference involves the inclusion of an additional parameter in the input sent to the `Sub`. This parameter is `mode`. A `mode` of 0 instructs the `Sub` to build the arc with dots, while a `mode` of 1 instructs the `Sub` to build the arc using lines.

All we present below is the `Sub` itself. However, in the zip archive of the newsletter is a cool application, `Arc-Sub demo.bas`, in which Andy artfully uses his `Sub` to draw designs.

Please note this, however. You should install Andy's `Arc-Sub demo.bas` in the same folder in which Liberty BASIC is installed. This is because Andy's program calls sprite and background graphics that are part of the Liberty BASIC distribution. These commands look for these graphics in a particular folder. Example:

    LOADBMP "smile1","sprites\smiley2.bmp"

Have fun using `Arc-Sub demo.bas`! It's worth running a few times even if you have no need to learn how to draw arcs!

Meanwhile, Andy's `Sub arc` is shown below.

    '*********************************   SUB arc  *******************************
    '** Parameters are:                                                        **
    '** CenterX coord, CenterY coord, Radius, Angle1, Angle2, incSize, mode    **
    '** incSize determines spacing between points, mode: 0 = dots, 1 = lines   **
    '**                                                                        **
    '** All parameters are numeric, compiler error occurs IF strings are used. **
    '**                                                                        **
    '** Includes logic to allow plotting from 180 to > 360 degrees. Avoiding   **
    '** having to call routine twice to plot from 180 to 360 then calling again**
    '** from 0 to 179. To cross 360/0 boundary, make call with 2nd angle       **
    '** greater than 360 and less than or equal to 540.                        **
    '** For Example: make call with angle1=180, angle2=539 (clockwise) or      **
    '** angle1=539, angle2=180 (counter-clockwise).                            **
    '**                                                                        **
    '** Source: Developed this routine FOR use with TRS-80 mode1 I graphics    **
    '****************************************************************************
    SUB arc centerX,centerY,radius,angle1,angle2,incSize,mode
        'check angles: make sure they are within acceptable range
        IF angle1 < 0 OR angle1 > 540 OR angle2 < 0 OR angle2 > 540 THEN [badParm]

        'Determine 'STEP' direction and magnitude used in FOR..NEXT loop to plot
        IF angle1 < angle2 THEN inc = incSize ELSE inc = -1 * incSize

        'one degree of arc resolution, otherwise it's a single point
        IF angle1 <> angle2 THEN            'plot arc

            'use mode=0 to plot single dots, use mode=1 to connect dots with lines
            IF mode = 0 THEN
                FOR x = angle1 TO angle2 STEP inc
                    IF x > 360 THEN rx = x - 360 ELSE rx = x
                    arcX=dx(rx) * radius + centerX
                    arcY=dy(rx) * radius + centerY
                    'change to handle name of graphic window used in your program
                    PRINT #1.g,"SET ";arcX;" ";arcY
                NEXT x
            ELSE
                IF angle1 > 360 THEN rx = angle1 - 360 ELSE rx = angle1
                x1 = dx(rx) * radius + centerX
                y1 = dy(rx) * radius + centerY
                FOR x = angle1+inc TO angle2 STEP inc
                    IF x > 360 THEN rx = x - 360 ELSE rx = x
                    arcX = dx(rx) * radius + centerX
                    arcY = dy(rx) * radius + centerY
                    'change to handle name of graphic window used in your program
                    PRINT #1.g,"LINE ";x1;" ";y1;" ";arcX;" ";arcY
                    x1 = arcX
                    y1 = arcY
                NEXT x
            END IF
        ELSE
            'angles are the same, so just plot a single point indicated by angle
            PRINT #1.g,"SET ";arcX;" ";arcY
        END IF
    [badParm]
    END SUB

| Top |