Up one HP-GL Viewer. By Lee Davison. Up to top

'------------------------------------------------------------------------------------
'
' HPGL file viewer Ver 2.08 - Quick BASIC 4.5 (c) L.Davison 2007

' this was written as an excercise in coding an HPGL data viewer and is mostly a
' quick hack to get my thoughts sorted out. supports most common HP-GL and some
' HP-GL2 drawing commands and those it doesn't support probably have some commented
' out code that forms the start of what is needed. where I ran out of enthusiasm
' you'll find the note "need code"

' there is very little error or bounds checking done during the actual commands,
' chances are that most HP-GL files you try will work, many HP-GL2 files also work
' to some degree though your mileage may vary. some files may cause BASIC errors
' such ar overflow or division by zero though I've not seen those yet.

' does auto scale to fit the mode 12 (640 x 480) screen by processing the file twice,
' the first time no picture is output, but errors and warnings (if any) are generated
' and the maximum and minimum values are found. these are used on the second pass to
' offset and scale the image so it /just/ fits the 640 x 480 screen along it's longest
' dimension. the original aspect ratio /should/ be maintained.

' added in 2007 is the ability to save the screen by hitting the [S] key. this can
' take some time so be patient. the image will be saved as filename.bmp where filename
' was the name, minus any extension, of the loaded file.

' comments, suggestions, additions, offers of monetary reward etc to ..
'
' leeedavison@lycos.co.uk


'------------------------------------------------------------------------------------

CONST false = 0, true = -1			' define these
CONST absolute = true, relative = false		' for the drawmode
CONST AtoZ$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"	' "A" to "Z" characters
CONST nmrc$ = "+-.01234567890 "			' numeric characters
CONST convangle = 180 / 3.14159264		' degrees to radians to degrees

' picture frame (hard clip) limits. these should be set to what the hardware will
' physically allow

CONST minimumx = 0, minimumy = 0		' set for the table minimum
CONST maximumx = 32767, maximumy = 32767	' set for the table maximum

' calculate the angle to a point on a circle given the centre and the point. 0 degrees
' is the 12 o'clock position. this may change.

DECLARE FUNCTION CalcAngle(xcentre, ycentre, xpoint, ypoint)

' calculate the radius of a circle given the centre and a point on the circle. this
' actually calculates the linear distance between any two points

DECLARE FUNCTION CalcRadius(xcentre, ycentre, xpoint, ypoint)

'------------------------------------------------------------------------------------

DIM fo$(95)					' holds the font plot data

esc$ = CHR$(27)					' define ESCAPE
quote$ = CHR$(34)				' " as a string
separator$ = ";"				' these can end command strings
ignore$ = " " + CHR$(13) + CHR$(10)		' these are ignored

home$ = CHR$(0) + CHR$(71)			' INKEY$ for [Home] key
pgup$ = CHR$(0) + CHR$(73)			' INKEY$ for [PgUp] key
pgdn$ = CHR$(0) + CHR$(81)			' INKEY$ for [PgDn] key
eend$ = CHR$(0) + CHR$(79)			' INKEY$ for [End] key
crrt$ = CHR$(0) + CHR$(77)			' INKEY$ for [cursor right] key
crlt$ = CHR$(0) + CHR$(75)			' INKEY$ for [cursor left] key
crup$ = CHR$(0) + CHR$(72)			' INKEY$ for [cursor up] key
crdn$ = CHR$(0) + CHR$(80)			' INKEY$ for [cursor down] key

SCREEN 12					' 640 x 480 x 16

DO
  GOSUB main					' go do main processing loop

  CLS						' clear the screen
  LOCATE 2,2					' set cursor
  PRINT "Process another file [y/N]"		' set prompt

  DO						' just wait ..
    key$ = INKEY$				'   get key presses
  LOOP WHILE key$ = ""				' .. for a key press

LOOP WHILE UCASE$(key$) = "Y"			' loop if another file to do

END						' all done, bye.


'------------------------------------------------------------------------------------
'
' main loop

main:

CLS						' clear the screen

minimumxused = 32767				' set minimum and
minimumyused = 32767				' maximum used for
maximumxused = -32768				' auto scale feature
maximumyused = -32768				' (yes these are correct)

LOCATE 2,2					' set cursor
INPUT "Enter HPGL file name "; filename$	' any name, doesn't have to be *.plt
IF filename$ = "" THEN END

warningcount = 0				' no warnings yet
errorcount = 0					' no errors yet

GOSUB InitFont					' initialsie the font array

fromstring = false				' getbyte is from the file
pretend = true					' pretend to plot first time
GOSUB DoFile					' process the file

IF (errorcount OR warningcount) THEN		' if there were some errors/warnings
  COLOR 15					' white
  PRINT errorcount; "error";			' n error
  IF errorcount <> 1 THEN
    PRINT "s";					'        s
  END IF
  PRINT " and"; warningcount; "warning";	'          and m warning
  IF warningcount <> 1 THEN
    PRINT "s"					'                       s
  ELSE
    PRINT
  END IF
  LOCATE 30,1					' bottom line
  PRINT "Press any key to continue";		' indicate not just stalled
  DO						' just wait ..
  LOOP WHILE INKEY$ = ""			' .. for a key press
END IF

xsize = maximumxused - minimumxused		' x size of plot
ysize = maximumyused - minimumyused		' y size of plot
IF (ysize / xsize) > .75 THEN			' use the maximum (reduction) scale
  scale = ysize / 239				' scale is y scale (0 to 239)
ELSE
  scale = xsize / 319				' scale is x scale (0 to 319)
END IF

fromstring = false				' getbyte is from the file
pretend = false					' plot for real this time
zoom = 1					' set zoom for zoom 1
drawscale = scale / 2				' drawscale for zoom 1
drawxcentre = xsize / 2				' drawing x centre
drawycentre = ysize / 2				' drawing y centre
GOSUB CalcXYOffset				' calculate offsets

DO						' redraw loop ..

  IF redraw THEN				' only if new pan/zoom
    CLS						' clear screen for plotting
    COLOR 15					' default to white
    GOSUB DoFile				' process the file (again)
  END IF

  DO						' just wait ..
    key$ = INKEY$				' get key presses
  LOOP WHILE key$ = ""				' .. for a key press

  redraw = false				' assume no redraw
  IF key$ = home$ THEN				' [Home] key
    IF (zoom <> 1) OR (drawxcentre - (xsize / 2)) OR (drawycentre - (ysize / 2)) THEN
						' only if zoomed or not centred
      zoom = 1					' zoom for fully zoomed out
      drawscale = scale / 2			' drawscale for fully zoomed out
      drawxcentre = xsize / 2			' reset drawing x centre
      drawycentre = ysize / 2			' reset drawing y centre
      GOSUB CalcXYOffset			' calculate offsets
    END IF
  ELSEIF key$ = pgup$ THEN			' [PgUp] key
    IF zoom THEN				' if not fully zoomed out
      zoom = zoom - 1				' zoom out
      drawscale = scale / (2 ^ zoom)		' drawscale for zoom level
      GOSUB CalcXYOffset			' calculate offsets
    END IF
  ELSEIF key$ = pgdn$ THEN			' [PgDn] key
    IF drawscale > 1 THEN			' if not fully zoomed in
      zoom = zoom + 1				' zoom in
      drawscale = scale / (2 ^ zoom)		' drawscale for zoom level
      IF drawscale < 1 THEN			' check not beyond maximum zoom
        drawscale = 1				' maximum zoom allowed
      END IF
      GOSUB CalcXYOffset			' calculate offsets
    END IF
  ELSEIF key$ = eend$ THEN			' [End] key
    DO WHILE drawscale > 1			' while not fully zoomed in
      zoom = zoom + 1				' zoom in
      drawscale = scale / (2 ^ zoom)		' drawscale for zoom level
      IF drawscale < 1 THEN			' check not beyond maximum zoom
        drawscale = 1				' maximum zoom allowed
      END IF
      GOSUB CalcXYOffset			' calculate offsets
    LOOP
  ELSEIF key$ = crrt$ THEN			' [cursor right] key
    drawxcentre = drawxcentre + 159 * drawscale	' shift x centre 1/4 screen left
    GOSUB CalcXYOffset				' calculate offsets
  ELSEIF key$ = crlt$ THEN			' [cursor left] key
    drawxcentre = drawxcentre - 159 * drawscale	' shift x centre 1/4 screen right
    GOSUB CalcXYOffset				' calculate offsets
  ELSEIF key$ = crup$ THEN			' [cursor up] key
    drawycentre = drawycentre + 119 * drawscale	' shift y centre 1/4 screen down
    GOSUB CalcXYOffset				' calculate offsets
  ELSEIF key$ = crdn$ THEN			' [cursor down] key
    drawycentre = drawycentre - 119 * drawscale	' shift y centre 1/4 screen up
    GOSUB CalcXYOffset				' calculate offsets
  ELSEIF UCASE$(key$) = "S" THEN		' [S] key
    GOSUB SaveScreen				' save the screen
  END IF

LOOP WHILE key$ <> esc$				' until [ESC] is pressed

COLOR 15					' white

RETURN

'------------------------------------------------------------------------------------
'
' calculate offsets

CalcXYOffset:
  offsetx = minimumxused - 319 * drawscale + drawxcentre
						' set x offset
  offsety = minimumyused - 239 * drawscale + drawycentre
						' set y offset

  minxbound = minimumxused > offsetx		' true if violates minimum x
  maxxbound = ((maximumxused - offsetx) / drawscale) < 639
						' true if violates maximum x
  IF minxbound AND NOT maxxbound THEN
    invert = (xsize / drawscale) < 640
    GOSUB CorrectXOffset			' correct the x offset
  ELSEIF maxxbound AND NOT minxbound THEN
    invert = (xsize / drawscale) => 640
    GOSUB CorrectXOffset			' correct the x offset
  END IF

  minybound = minimumyused > offsety		' true if violates minimum y
  maxybound = ((maximumyused - offsety) / drawscale) < 479
						' true if violates maximum y
  IF minybound AND NOT maxybound THEN
    invert = (ysize / drawscale) < 480
    GOSUB CorrectYOffset			' correct the y offset
  ELSEIF maxybound AND NOT minybound THEN
    invert = (ysize / drawscale) => 480
    GOSUB CorrectYOffset			' correct the y offset
  END IF
  redraw = true					' always redraw after new offsets
RETURN


'------------------------------------------------------------------------------------
'
' correct the x offset

CorrectXOffset:
  IF invert THEN
    drawxcentre = xsize - 320 * drawscale	' shift x centre
    offsetx = maximumxused - 639 * drawscale	' set x offset
  ELSE
    drawxcentre = 319 * drawscale		' shift x centre
    offsetx = minimumxused			' set x offset
  END IF
RETURN


'------------------------------------------------------------------------------------
'
' correct the y offset

CorrectYOffset:
  IF invert THEN
    drawycentre = ysize - 240 * drawscale	' shift y centre
    offsety = maximumyused - 479 * drawscale	' set y offset
  ELSE
    drawycentre = 239 * drawscale		' shift y centre
    offsety = minimumyused			' set y offset
  END IF
RETURN


'------------------------------------------------------------------------------------
'
' process file

DoFile:
  OPEN filename$ FOR INPUT AS #1		' open the file
  GOSUB Initialise				' initialise the plot values
  GOSUB DoStream				' process the stream
  CLOSE						' close the file
RETURN


'------------------------------------------------------------------------------------
'
' process a stream of bytes until no input remains

DoStream:
  cmdstrin$ = ""				' initialise the command string
  GOSUB Getbyte					' get byte from input stream
  DO WHILE LEN(byte$)				' loop until no bytes to do
    IF atozchr THEN				' all commands are two A to Z chrs
      cmdstrin$ = cmdstrin$ + byte$		' add to command string
      cmdlength = LEN(cmdstrin$)		' get command string length
      GOSUB Getbyte				' get next byte
      IF cmdlength = 2 THEN			' if two characters
        GOSUB DoCommand				' go interpret command
        cmdstrin$ = ""				' clear the executed command
      END IF
    ELSEIF INSTR(separator$, byte$) THEN	' if separator character
      cmdstrin$ = ""				' clear command string
      GOSUB Getbyte				' get next byte
    ELSEIF INSTR(ignore$, byte$) THEN		' if ignore character
      GOSUB Getbyte				' just get the next byte
    ELSEIF byte$ = esc$ THEN			' ignore ESC sequences
      cmdstrin$ = ""				' clear the command string
      DO WHILE INSTR(ignore$, byte$) = 0	' all end ESC sequences
        GOSUB GetByte				' get a byte
      LOOP
      GOSUB Getbyte				' get next byte
    ELSE
      cmdstrin$ = ""				' clear the command string
      IF pretend THEN				' only print if not drawing
        COLOR 15				' white
        PRINT "Error : Unexpected byte "; byte$; ASC(byte$)
        errorcount = errorcount + 1
      END IF
      GOSUB Getbyte				' get next byte
    END IF
    IF INKEY$ = esc$ THEN END			' end if [ESCAPE] pressed
  LOOP
RETURN


'------------------------------------------------------------------------------------
'
' get a byte from the input stream. if the stream is empty (i.e. the whole file or
' character definition string has been read) then return a null string. set atozchr
' and numeric accordingly.

Getbyte:
  IF fromstring THEN				' reading from font string
    byte$ = MID$(fo$(lbchar), lbindex, 1)	' get next byte from string
    lbindex = lbindex + 1			' increment the index
  ELSEIF NOT EOF(1) THEN			' if no bytes from file to get
    byte$ = INPUT$(1, 1)			' get a byte from the file
  ELSE
    byte$ = ""					' else set no byte
  END IF

  IF byte$ = "" THEN				' if no byte got then
    atozchr = false				' not "A" to "Z"
    numeric = false				' not "-.01234567890 "
  ELSE						' else there was a byte got
    atozchr = (INSTR(AtoZ$, byte$) <> 0)	' true if "A" to "Z"
    numeric = (INSTR(nmrc$, byte$) <> 0)	' true if "+-.01234567890 "
  END IF
RETURN


'------------------------------------------------------------------------------------
'
' interpret commands. big mess of IF .. THEN ELSEIFs. like Topsy this just growed

DoCommand:
  IF cmdstrin$="PU" THEN			' pen up
    pendown = false				' set pen mode
    GOSUB DoCoords				' do any co-ordiantes
  ELSEIF cmdstrin$="PD" THEN			' pen down
    pendown = true				' set pen mode
    GOSUB DoCoords				' do any co-ordiantes
  ELSEIF cmdstrin$="PA" THEN			' pen absolute
    drawmode = absolute				' set draw mode
    GOSUB DoCoords				' do any co-ordiantes
  ELSEIF cmdstrin$="PR" THEN			' pen relative
    drawmode = relative				' set draw mode
    GOSUB DoCoords				' do any co-ordiantes
  ELSEIF cmdstrin$="SP" THEN			' select pen
    GOSUB GetNumber				' get pen number
    COLOR VAL(number$)				' set the colour
  ELSEIF cmdstrin$="AA" THEN			' arc absolute
    IF numeric THEN				' make sure number(s) there
      GOSUB GetArc				' get the arc parameters
      arcx = absolutex				' set arc centre x
      arcy = absolutey				' set arc centre y
      GOSUB DoArc				' go do the arc
    END IF
  ELSEIF cmdstrin$="AT" THEN			' absolute arc three point
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair				' get the centre X,Y
      interx = absolutex			' set arc intermediate x
      intery = absolutey			' set arc intermediate y
      GOSUB GetXYPair				' get the centre X,Y
      endx = absolutex				' set arc end x
      endy = absolutey				' set arc end y
      GOSUB DrawArcThreePoint			' do the arc
    END IF
  ELSEIF cmdstrin$="AR" THEN			' arc relative
    IF numeric THEN				' make sure number(s) there
      GOSUB GetArc				' get the arc parameters
      arcx = currentx + absolutex		' set arc centre x
      arcy = currenty + absolutey		' set arc centre x
      GOSUB DoArc				' go do the arc
    END IF
  ELSEIF cmdstrin$="RT" THEN			' relative arc three point
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair				' get the centre X,Y
      interx = currentx + absolutex		' set arc intermediate x
      intery = currenty + absolutey		' set arc intermediate y
      GOSUB GetXYPair				' get the centre X,Y
      endx = currenty + absolutex		' set arc end x
      endy = currenty + absolutey		' set arc end y
      GOSUB DrawArcThreePoint			' do the arc
    END IF
  ELSEIF cmdstrin$="CI" THEN			' circle
    IF numeric THEN				' make sure number(s) there
      GOSUB GetNumber				' get the radius
      radius = VAL(number$)			' set the radius
      GOSUB GetChord				' get the chord angle

      arcx = currentx				' circle centre is the ..
      arcy = currenty				' .. current pen position
      currentangle = 0				' 12'o clock
      arcangle = 360				' do a full circle

      stackpen = pendown			' save pen mode
      pendown = false				' lift pen
      stackdraw = drawmode			' save draw mode
      drawmode = absolute			' set draw mode
      absolutex = currentx			' move the pen to the ..
      absolutey = currenty + radius		' .. 12'o clock position
      GOSUB DoDraw				' move pen
      pendown = true				' always draw the circle

      GOSUB DrawArc				' go draw the arc

      pendown = false				' lift pen
      absolutex = arcx				' move pen back to the ..
      absolutey = arcy				' .. centre of the circle
      GOSUB DoDraw				' move pen
      pendown = stackpen			' restore the pen mode
      drawmode = stackdraw			' restore the draw mode
    END IF
  ELSEIF cmdstrin$="EW" THEN			' edge wedge
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair				' get wedge radius and start angle
      radius = absolutex			' set the radius
      currentangle = 90 - absolutey		' set the start angle
      GOSUB GetNumber				' get wedge arc angle
      arcangle = VAL(number$)			' set the wedge arc angle
      GOSUB GetChord				' get the chord angle
      GOSUB DoWedgeEdge				' go draw the wedge edge
    END IF
  ELSEIF cmdstrin$="WG" THEN			' fill wedge
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair				' get wedge radius and start angle
      radius = absolutex			' set the radius
      currentangle = 90 - absolutey		' set the start angle
      GOSUB GetNumber				' get wedge arc angle
      arcangle = VAL(number$)			' set the wedge arc angle
      GOSUB GetChord				' get the chord angle
      GOSUB DoWedgeEdge				' go draw the wedge edge (for now)
    END IF
  ELSEIF cmdstrin$="EA" THEN			' edge rectangle absolute
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair				' get rectangle opposite corner
      deltax = absolutex - currentx		' calculate x size
      deltay = absolutey - currenty		' calculate y size
      GOSUB DoRectEdge				' go draw rectangle edge
    END IF
  ELSEIF cmdstrin$="ER" THEN			' edge rectangle relative
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair				' get rectangle opposite corner
      deltax = absolutex			' get x size
      deltay = absolutey			' get y size
      GOSUB DoRectEdge				' go draw rectangle edge
    END IF
  ELSEIF cmdstrin$="RA" THEN			' fill rectangle absolute
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair				' get rectangle opposite corner
      deltax = absolutex - currentx		' calculate x size
      deltay = absolutey - currenty		' calculate y size
      GOSUB DoRectEdge				' go draw rectangle edge (for now)
    END IF
  ELSEIF cmdstrin$="RR" THEN			' fill rectangle relative
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair				' get rectangle opposite corner
      deltax = absolutex			' get x size
      deltay = absolutey			' get y size
      GOSUB DoRectEdge				' go draw rectangle edge (for now)
    END IF
  ELSEIF cmdstrin$="IN" THEN			' initialise graphics mode
    GOSUB Initialise
  ELSEIF cmdstrin$="DF" THEN			' set graphics defaults
    GOSUB Defaults				' note - subset of "IN"
  ELSEIF cmdstrin$="IP" THEN			' input scale points
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair				' get IP1
      IF numeric THEN				' if more numbers then
'        p1x = absolutex			' set P1 x
'        p1y = absolutey			' set P1 y
        GOSUB GetXYPair				' get P2
'        p2x = absolutex			' set P2 x
'        p2y = absolutey			' set P2 y
'      ELSE					' no P2 so relative scale
'        p2x = p1x - p2x + absolutex		' maintain P2 x relative to P1 x
'        p2y = p1y - p2y + absolutey		' maintain P2 y relative to P1 y
'        p1x = absolutex			' set P1 x
'        p1y = absolutey			' set P1 y
      END IF
'    ELSE					' no numbers so set default values
'      GOSUB IPDefault				' set default scaling points
    END IF
  ELSEIF cmdstrin$="DI" THEN			' absolute direction
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair
'      IF (absolutex <> 0) OR (absolutey <> 0) THEN
'        directionrun = absolutex
'        directionrise = absolutey
'      END IF
'    ELSE					' no numbers so set default values
'      GOSUB DIDefault				' default direction run and rise
    END IF
  ELSEIF cmdstrin$="DR" THEN			' relative direction
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair
'      IF (absolutex <> 0) OR (absolutey <> 0) THEN
'        directionrun = absolutex * (p2x - p1x) / 100
'        directionrise = absolutey * (p2y - p1y) / 100
'      END IF
'    ELSE					' no numbers so set default values
'      GOSUB DIDefault				' default direction run and rise
    END IF
  ELSEIF cmdstrin$="IW" THEN			' input window
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair				' get lower left corner x,y
'      iw1x = absolutex				' set lower left x
'      iw1y = absolutey				' set lower left y
      GOSUB GetXYPair				' get upper right corner x,y
'      iw2x = absolutex				' set upper right x
'      iw2y = absolutey				' set upper right y
'      IF iw1x > iw2x THEN			' make sure max & min x are correct
'        iw2x = iw1x
'        iw1x = absolutex
'      END IF
'      IF iw1y > iw2y THEN			' make sure max & min y are correct
'        iw2y = iw1y
'        iw1y = absolutey
'      END IF
'    ELSE					' no numbers so set default values
'      GOSUB IWDefault				' so go set defaults
    END IF
  ELSEIF cmdstrin$="SI" THEN			' absolute character size
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair				' get them
'      characterwidth = absolutex		' set width (needs range check)
'      characterheight = absolutey		' set height (needs range check)
'    ELSE					' no numbers so ..
'      GOSUB SIDefault				' .. set default values
    END IF
  ELSEIF cmdstrin$="CP" THEN			' character plot
    IF numeric THEN				' make sure number(s) there
      GOSUB GetXYPair
'    ELSE					' no numbers so set default values
'      absolutex = 0
'      absolutey = -1
    END IF
'						' need code here
  ELSEIF cmdstrin$="LT" THEN			' line type
    IF numeric THEN				' make sure number(s) there
      GOSUB GetNumber				' get it
'      linenumber = VAL(number$)		' set line type (needs range check)
      IF numeric THEN				' another number ?
        GOSUB GetNumber				' if so get it
'        linepattern = VAL(number$)		' set pattern (needs range check)
      END IF
'    ELSE					' no numbers so ..
'      GOSUB LTDefault				' .. set default
    END IF
  ELSEIF cmdstrin$="FT" THEN			' fill type
    IF numeric THEN				' make sure number(s) there
      GOSUB GetNumber				' get fill type
'      filltype = VAL(number$)			' set fill type (needs range check)
      IF numeric THEN				' if option 1 present
        GOSUB GetNumber				' get fill interval
'        fillinterval = VAL(number$)		' set fill interval
        IF numeric THEN				' if option 2 present
          GOSUB GetNumber			' get fill angle
'          fillangle = VAL(number$)		' set fill angle
        END IF
'      ELSE
'        GOSUB FTDefault			' set default fill type
      END IF
    END IF
  ELSEIF cmdstrin$="LB" THEN			' label
    stackpen = pendown				' save pen mode
    pendown = false				' lift pen
    stackdraw = drawmode			' save draw mode
    drawmode = relative				' set draw mode
    DO WHILE byte$ <> terminator$ AND LEN(byte$)
						' while not at the string end
      lbchar = ASC(byte$) - 32			' get index to font array
      IF lbchar < 0 OR lbchar > 95 THEN
        lbchar = 0				' [SPACE] if not in range
      END IF
      lbindex = 1				' initialise index
      fromstring = true				' getting bytes from the font string
      GOSUB DoStream				' process the character string
      fromstring = false			' back to the file
      absolutex = 10				' move pen to the ..
      absolutey = 0				' .. next character start
      GOSUB DoDraw				' move pen
      GOSUB Getbyte				' get the next label byte
    LOOP
    pendown = stackpen				' restore the pen mode
    drawmode = stackdraw			' restore the draw mode
    GOSUB Getbyte				' get the byte after the terminator
  ELSEIF cmdstrin$="CO" THEN			' comment string
    IF byte$ = quote$ THEN			' if there really is a comment
      DO
        GOSUB GetByte				' get bytes
      LOOP WHILE LEN(byte$) AND byte$ <> quote$	' until the end
      GOSUB GetByte				' get the next byte
    END IF
  ELSEIF cmdstrin$="DT" THEN			' define (label) terminator
    IF byte$ <>";" THEN				' make sure there's a byte there
      terminator$ = byte$			' set label terminator character
      GOSUB DTDefaultmode			' set default terminator mode only
      GOSUB GetByte				' get the next byte
      IF byte$ = "," THEN			' if second parameter
        GOSUB Getbyte				' skip the ","
        IF numeric THEN				' if really a number then
          GOSUB GetNumber			' get terminator mode
          terminatormode = VAL(number$)
						' set terminator mode
        END IF
      END IF
    ELSE
      GOSUB DTDefault				' set default character and mode
    END IF
  ELSEIF cmdstrin$="PG" THEN			' page feed
    currentx = 0				' home the pen x
    currenty = 0				' home the pen y
  ELSEIF cmdstrin$="PT" THEN			' pen thickness
    GOSUB UnsupportedCommand			' not supported
  ELSEIF cmdstrin$="SC" THEN			' scale
    GOSUB UnsupportedCommand			' not supported
  ELSEIF cmdstrin$="CA" THEN			' alternate character set
    GOSUB UnsupportedCommand			' not supported
  ELSEIF cmdstrin$="VS" THEN			' velocity set (pen speed in cm/s)
    GOSUB UnsupportedCommand			' not supported
  ELSEIF cmdstrin$="SM" THEN			' symbol mode
    GOSUB UnsupportedCommand			' not supported
    DO WHILE LEN(byte$) AND byte$ <> ";"	' until the end
      GOSUB GetByte				' get bytes
    LOOP
'  ELSEIF cmdstrin$="" THEN
'    GOSUB DumpNumber
  ELSE
    IF pretend THEN				' only print if not drawing
      COLOR 15					' white
      PRINT "Error : Unexpected command "; cmdstrin$
      errorcount = errorcount + 1
    END IF
    GOSUB DumpNumber
  END IF
RETURN


'------------------------------------------------------------------------------------
'
' do the arc

DoArc:
  currentangle = CalcAngle(arcx, arcy, currentx, currenty)
  radius = CalcRadius(arcx, arcy, currentx, currenty)
  stackdraw = drawmode				' save draw mode
  drawmode = absolute				' set draw mode
  GOSUB DrawArcDirection			' set direction & draw the arc
  drawmode = stackdraw				' restore the draw mode
RETURN


'------------------------------------------------------------------------------------
'
' calculate the values and draw the three point arc. if the points are in line then a
' single line is drawn from start to end. no check is done to see if the intermediate
' point really is between the start and end points.

DrawArcThreePoint:
  GOSUB GetChord				' get the arc chord angle

  chorddx = (interx - currentx) / 2		' dx/2 from current to intermediate
  chorddy = (intery - currenty) / 2		' dy/2 from current to intermediate

  centrex = currentx + chorddx			' chord 1 centre x
  centrey = currenty + chorddy			' chord 1 centre y

  perp1x1 = centrex - chorddy			' chord 1 perpendicular x1
  perp1y1 = centrey + chorddx			' chord 1 perpendicular y1

  perp1x2 = centrex + chorddy			' chord 1 perpendicular x2
  perp1y2 = centrey - chorddx			' chord 1 perpendicular y2

  chorddx = (endx - interx) / 2			' dx/2 from intermediate to end
  chorddy = (endy - intery) / 2			' dy/2 from intermediate to end

  centrex = interx + chorddx			' chord 2 centre x
  centrey = intery + chorddy			' chord 2 centre y

  perp2x1 = centrex - chorddy			' chord 2 perpendicular x1
  perp2y1 = centrey + chorddx			' chord 2 perpendicular y1

  perp2x2 = centrex + chorddy			' chord 2 perpendicular x2
  perp2y2 = centrey - chorddx			' chord 2 perpendicular y2

  deltaxp1 = perp1x2 - perp1x1			' dx for chord 1 perpendicular
  deltayp1 = perp1y2 - perp1y1			' dy for chord 1 perpendicular
  deltaxp2 = perp2x2 - perp2x1			' dx for chord 2 perpendicular
  deltayp2 = perp2y2 - perp2y1			' dy for chord 2 perpendicular

  deltax12 = perp1x1 - perp2x1			' start x difference
  deltay12 = perp1y1 - perp2y1			' start y difference

  unknown = (deltaxp2 * deltay12) - (deltayp2 * deltax12)
  divergence = (deltayp2 * deltaxp1) - (deltaxp2 * deltayp1)
						' zero if parallel

  IF divergence THEN				' not parallel so do arc
    unknown = unknown / divergence		' scale unknown

    arcx = perp1x1 + unknown * deltaxp1		' find x centre
    arcy = perp1y1 + unknown * deltayp1		' find y centre

    currentangle = CalcAngle(arcx, arcy, currentx, currenty)
						' angle to current point
    interangle = CalcAngle(arcx, arcy, interx, intery) - currentangle
						' angle to inter point from current
    endangle = CalcAngle(arcx, arcy, endx, endy) - currentangle
						' angle to end from current

    interangle = interangle - 360 * (interangle < 0)
						' ensure 0 to 360
    endangle = endangle - 360 * (endangle < 0)	' ensure 0 to 360

    IF interangle > endangle THEN		' if anticlockwise
      arcangle = 360 - endangle
    ELSE					' else clockwise
      arcangle = - endangle
    END IF

    radius = CalcRadius(arcx, arcy, currentx, currenty)

    stackdraw = drawmode			' save draw mode
    drawmode = absolute				' set draw mode
    GOSUB DrawArcDirection			' set direction & draw the arc
    drawmode = stackdraw			' restore the draw mode
  ELSE
    absolutex = endx				' set arc end x
    absolutey = endy				' set arc end y
    stackdraw = drawmode			' save draw mode
    drawmode = absolute				' set draw mode
    GOSUB DoDraw				' set direction & draw the line
    drawmode = stackdraw			' restore the draw mode
  END IF
RETURN


'------------------------------------------------------------------------------------
'
' most of the default settings are done as subroutines so that the code only has to
' be modified the once if the defaults are changed. this saves searching out every
' instance where the defaults can be set by changing them acc to GOSUBs and putting
' the subroutines (mostly) here. commented out code is not needed just yet
'
'------------------------------------------------------------------------------------
'
' set default scaling points P1 and P2

'IPDefault:
'  p1x = iw1x					' set P1 x default to window x min
'  p1y = iw1y					' set P1 y default to window y min
'  p2x = iw2x					' set P2 x default to window x max
'  p2y = iw2y					' set P2 y default to window y max
'RETURN


'------------------------------------------------------------------------------------
'
' set default input window (soft clip limits)

'IWDefault:
'  iw1x = minimumx				' set lower left x default
'  iw1y = minimumy				' set lower left y default
'  iw2x = maximumx				' set upper right x default
'  iw2y = maximumy				' set upper right y default
'RETURN


'------------------------------------------------------------------------------------
'
' set default label terminator character

DTDefault:
  terminator$ = CHR$(3)				' LB command terminator character
DTDefaultmode:
  terminatormode = 1				' terminator is non printing
RETURN


'------------------------------------------------------------------------------------
'
' set default direction run and rise
'
'DIDefault:
'  directionrun = 1				' default is for horizontal text
'  directionrise = 0				' default is for horizontal text
'RETURN


'------------------------------------------------------------------------------------
'
' set default pattern anchor point

'ACDefault:
'  anchorx = minimumx				' set anchor default x
'  anchory = minimumy				' set anchor default y
'RETURN


'------------------------------------------------------------------------------------
'
' set default text path

'DVDefault:
'  text path = 0				' default is for left to right text
'RETURN


'------------------------------------------------------------------------------------
'
' set default text extra space

'ESDefault:
'  eswidth = 0					' default is for no extra width
'  esheight = 0					' default is for no extra height
'RETURN


'------------------------------------------------------------------------------------
'
' set default label origin

'LODefault:
'  labelorigin = 1				' default label origin
'RETURN


'------------------------------------------------------------------------------------
'
' set default fill type

'FTDefault:
'  filltype = 1					' default fill type (solid black)
'  fillinterval = CalcRadius(p1x, p1y, p2x, p2y) / 100
'						' default fill interval is 1% P2-P1
'  fillangle = 45				' default fill angle
'RETURN


'------------------------------------------------------------------------------------
'
' set default line type

'LTDefault:
'  linetype = 32767				' default line type (solid black)
'  patternlength = CalcRadius(p1x, p1y, p2x, p2y) / 25
'						' default length is 4% P2-P1
'  linemode = 0					' default line mode
'RETURN


'------------------------------------------------------------------------------------
'
' set default scale (none)

'SCDefault:
'						' need code
'RETURN


'------------------------------------------------------------------------------------
'
' set default character size

'SIDefault:
'  characterwidth = 0.1875			' default width (cm) 75 units
'  characterheight = 0.2625			' default height (cm) 105 units
'RETURN


'------------------------------------------------------------------------------------
'
' initialise plot values, this falls through to the set plot defaults routine

Initialise:
' PU
  pendown = false				' lift the pen
' PA0,0
  drawmode = absolute				' absolute draw mode
  currentx = 0					' home the pen x
  currenty = 0					' home the pen y
'  absolutex = 0				' set x
'  absolutey = 0				' set y
'  GOSUB DoDraw					' home the pen - do this on real hw
' IP
'  GOSUB IPDefault				' set default scaling points
' WU						' not done
' PW						' not done


'------------------------------------------------------------------------------------
'
' set plot defaults

Defaults:
' AC
'  GOSUB ACDefault				' set pattern anchor default
' DI 1,0
'  GOSUB DIDefault				' default direction run and rise
' DT
  GOSUB DTDefault				' default label terminator character
' DV
'  GOSUB DVDefault				' set default text path
' ES
'  GOSUB ESDefault				' set default text extra space
' FT
'  GOSUB FTDefault				' set default fill type
' IW
'  GOSUB IWDefault				' set table window defaults
' LO 1
'  GOSUB LODefault				' default label origin
' LT
'  GOSUB LTDefault				' default line type
' PA
  drawmode = absolute				' set absolute drawmode
' SC
'  GOSUB SCDefault				' default scale (none)
' SI
'  GOSUB SIDefault				' set default character size
' AD						' not done
' CF						' not done
' LA						' not done
' PM						' not done
' RF						' not done
' SB 0						' not done
' SV						' not done
' SD						' not done
' SL						' not done
' SM						' not done
' SS						' not done
RETURN


'------------------------------------------------------------------------------------
'
' flag an unsupported command, falls through to DumpNumber - not good but doesn't
' hurt any

UnsupportedCommand:
  IF pretend THEN				' only print if not drawing
    COLOR 15					' white
    PRINT "Warning : Unsupported command "; cmdstrin$
    warningcount = warningcount + 1
  END IF


'------------------------------------------------------------------------------------
'
' get a number(s) and ignore it(them)

DumpNumber:
  DO WHILE numeric				' while the next is a number
    GOSUB GetNumber				' just read it
  LOOP
RETURN


'------------------------------------------------------------------------------------
'
' get an xy pair of co-ordinates

GetXYPair:
  GOSUB GetNumber				' get first number
  absolutex = VAL(number$)			' save to x
  GOSUB GetNumber				' get second number
  absolutey = VAL(number$)			' save to y
RETURN


'------------------------------------------------------------------------------------
'
' get x,y co-ordinate pair(s) and draw them, thsi does most of the work for the PA,
' PR, PD and PU commands

DoCoords:
  DO WHILE numeric
    GOSUB GetXYPair				' get X,Y co-ordiantes
    GOSUB DoDraw				' go draw them
  LOOP
RETURN


'------------------------------------------------------------------------------------
'
' gets a number string from the input stream. valid characters are " " and
' -.01234567890

GetNumber:
  number$ = ""					' clear the number string
  DO WHILE numeric				' while a numeric character
    number$ = number$ + byte$			' add the character to the string
    GOSUB Getbyte				' and get the next character
  LOOP
  IF NOT atozchr THEN				' if not "A" to "Z"
    GOSUB Getbyte				' skip ",;"
  END IF
RETURN


'------------------------------------------------------------------------------------
'
' get the parameters for an arc

GetArc:
  GOSUB GetXYPair				' get the centre X,Y
  GOSUB GetNumber				' get the arc angle
  arcangle = VAL(number$)			' set the arc angle


'------------------------------------------------------------------------------------
'
' get the chord angle if present, else set to the default value of 5 degrees

GetChord:
  IF numeric THEN				' if another number
    GOSUB GetNumber				' get the chord angle
    chordangle = VAL(number$)			' set the cord angle
  ELSE
    chordangle = 5				' else set the default angle
  END IF
RETURN


'------------------------------------------------------------------------------------
'
' set the arc direction and draw the arc

DrawArcDirection:
  IF arcangle < 0 THEN				' if clockwise
    chordangle = - chordangle			' invert the chord angle
  END IF


'------------------------------------------------------------------------------------
'
' draw the segments of an arc

DrawArc:
  DO WHILE ABS(arcangle) > ABS(chordangle)	' while more than chordangle to do
    currentangle = currentangle - chordangle	' reduce the remaining angle
    arcangle = arcangle - chordangle		' calculate the next point angle
    absolutex = SIN(currentangle / convangle) * radius + arcx
						' calculate the next x
    absolutey = COS(currentangle / convangle) * radius + arcy
						' calculate the next y
    GOSUB DoDraw				' and draw it
  LOOP
'  IF arcangle THEN						
    currentangle = currentangle - arcangle
    absolutex = SIN(currentangle / convangle) * radius + arcx
    absolutey = COS(currentangle / convangle) * radius + arcy
    GOSUB DoDraw
'  END IF
RETURN


'------------------------------------------------------------------------------------
'
' draws a wedge edge

DoWedgeEdge:
  arcx = currentx				' wedge centre is the ..
  arcy = currenty				' .. current pen position

  stackpen = pendown				' save pen mode
  pendown = true				' drop pen
  stackdraw = drawmode				' save draw mode
  drawmode = absolute				' set draw mode
  absolutex = SIN(currentangle / convangle) * radius + arcx
  absolutey = COS(currentangle / convangle) * radius + arcy
  GOSUB DoDraw					' move pen

  GOSUB DrawArcDirection			' set direction & draw the arc

  absolutex = arcx				' move pen back to the ..
  absolutey = arcy				' .. centre of the wedge
  GOSUB DoDraw					' move pen
  pendown = stackpen				' restore the pen mode
  drawmode = stackdraw				' restore the draw mode
RETURN


'------------------------------------------------------------------------------------
'
' draws a rectangle edge

DoRectEdge:
  stackpen = pendown				' save pen mode
  pendown = true				' drop pen
  stackdraw = drawmode				' save draw mode
  drawmode = absolute				' set draw mode

  arcx = currentx				' remember origin x
  arcy = currenty				' remember origin y

  absolutex = arcx + deltax			' horizontal edge
  absolutey = arcy				' no change
  GOSUB DoDraw					' draw edge

  absolutex = arcx + deltax			' no change
  absolutey = arcy + deltay			' vertical edge
  GOSUB DoDraw					' draw edge

  absolutex = arcx				' horizontal edge
  absolutey = arcy + deltay			' no change
  GOSUB DoDraw					' draw edge

  absolutex = arcx				' no change
  absolutey = arcy				' vertical edge
  GOSUB DoDraw					' draw edge

  pendown = stackpen				' restore the pen mode
  drawmode = stackdraw				' restore the draw mode
RETURN


'------------------------------------------------------------------------------------
'
' do draw/move to next co-ordinate pair. _ALL_ HPGL commands end up drawing straight
' lines with the pen either up or down so this is the _ONLY_ routine that does any
' actuall drawing. if this were driving a real plotter table then this would be the
' routine that generates the very basic pen+move commands that the (hopefully)
' interrupt driven lowest level routine understands.

' the bit after IF pretend THEN is used only to autoscale the output for drawing
' on the screen, a real plotter would expect the user to know what the heck they
' are doing and just plot the drawing.

DoDraw:
  oldx = currentx				' set old position x
  oldy = currenty				' set old position y

  IF drawmode THEN				' draw absolute
    currentx = absolutex			' set current x
    currenty = absolutey			' set current y
  ELSE						' draw relative
    currentx = currentx + absolutex		' set current x
    currenty = currenty + absolutey		' set current y
  ENDIF

  IF pretend THEN				' if not really drawing
    IF currentx > maximumxused THEN maximumxused = currentx
    IF currenty > maximumyused THEN maximumyused = currenty
    IF currentx < minimumxused THEN minimumxused = currentx
    IF currenty < minimumyused THEN minimumyused = currenty
  ELSEIF pendown THEN				' if pen down
    oldx = (oldx - offsetx) / drawscale		' scale x start
    oldy = (oldy - offsety) / drawscale		' scale y start
    drawx = (currentx - offsetx) / drawscale	' scale x end
    drawy = (currenty - offsety) / drawscale	' scale y end
    LINE (oldx, 479 - oldy) - (drawx, 479 - drawy)
						' draw line
  ENDIF
RETURN


'------------------------------------------------------------------------------------
'
' save screen 12 as a bitmap file

SaveScreen:
  x = INSTR(filename$, ".")			' find the "." in the filename
  IF x THEN					' if there was a "."
    filestr$ = LEFT$(filename$, x) + "bmp"	' replace the file extension with "bmp"
  ELSE						' else there was no "."
    filestr$ = filename$ + ".bmp"		' so add ".bmp" to the filename end
  END IF

  OPEN filestr$ FOR OUTPUT AS #9		' open file for .bmp output

  RESTORE SixForty				' .. to the start of the .bmp header
  FOR x = 0 TO &h75				' for each header byte
    READ y					' read it
    PRINT #9, CHR$(y);				' out to file
  NEXT

  FOR y = 479 TO 0 STEP - 1			' top to bottom
    FOR x = 0 TO 638 STEP 2			' left to right
      b = (POINT(x, y) * 16) + POINT(x + 1, y)	' get two 4 bit pixels as a byte
      PRINT #9, CHR$(b);			' save the byte to the file
    NEXT x
  NEXT y

  CLOSE #9					' close the file
RETURN


'------------------------------------------------------------------------------------
'
' bitmap file header

SixForty:
  DATA  &h42,&h4D,&h76,&h58,&h02,&h00,&h00,&h00,&h00,&h00,&h76,&h00,&h00,&h00,&h28,&h00
  DATA  &h00,&h00,&h80,&h02,&h00,&h00,&hE0,&h01,&h00,&h00,&h01,&h00,&h04,&h00,&h00,&h00
  DATA  &h00,&h00,&h00,&h58,&h02,&h00,&h00,&h00,&h00,&h00,&h00,&h00,&h00,&h00,&h00,&h00
  DATA  &h00,&h00,&h00,&h00,&h00,&h00
'         bb   gg   rr   00			'  screen 12 palette
  DATA  &h00,&h00,&h00,&h00
						'  0 — black		#000000
  DATA  &hAA,&h00,&h00,&h00
						'  1 — blue		#0000AA
  DATA  &h00,&hAA,&h00,&h00
						'  2 — green		#00AA00
  DATA  &hAA,&hAA,&h00,&h00
						'  3 — cyan		#00AAAA
  DATA  &h00,&h00,&hAA,&h00
						'  4 — red		#AA0000
  DATA  &hAA,&h00,&hAA,&h00
						'  5 — magenta		#AA00AA
  DATA  &h00,&h55,&hAA,&h00
						'  6 — brown		#AA5500
  DATA  &hAA,&hAA,&hAA,&h00
						'  7 — light grey	#AAAAAA
  DATA  &h55,&h55,&h55,&h00
						'  8 — dark grey	#555555
  DATA  &hFF,&h55,&h55,&h00
						'  9 — bright blue	#5555FF
  DATA  &h55,&hFF,&h55,&h00
						' 10 — bright green	#55FF55
  DATA  &hFF,&hFF,&h55,&h00
						' 11 — bright cyan	#55FFFF
  DATA  &h55,&h55,&hFF,&h00
						' 12 — bright red	#FF5555
  DATA  &hFF,&h55,&hFF,&h00
						' 13 — bright magenta	#FF55FF
  DATA  &h55,&hFF,&hFF,&h00
						' 14 — yellow		#FFFF55
  DATA  &hFF,&hFF,&hFF,&h00
						' 15 — bright white	#FFFFFF


'------------------------------------------------------------------------------------
'
' set up the strings that define the font for character plotting

InitFont:
  fo$(0)  = "PR40,0;"				' " "
  fo$(1)  = "PR20,0;PD;AR0,5,360;PU0,20;PD5,35;AR-5,0,180;PR5,-35;PU20,-20;"
						' "!"
  fo$(2)  = "PR10,40;PD5,15;AR-5,0,180;PR5,-15;PU20,0;PD5,15;AR-5,0,180;PR5,-15;PU10,_
-40;"
						' """
  fo$(3)  = "PR10,0;PD0,60;PU20,0;PD0,-60;PU10,20;PD-40,0;PU0,20;PD40,0;PU0,-40;"
						' "#"
  fo$(4)  = "PR20,0;PD0,60;PU20,-20;PD;AR-10,0,90;PR-20,0;AR0,-10,180;PR20,0;AR0,-10,_
-180;PR-20,0;AR0,10,-90;PU40,-20;"
						' "$"
  fo$(5)  = "PD40,60;PU-30,-15;PD;AR-5,0,360;PU30,-30;PD;AR-5,0,360;PU0,-15;"
						' "%"
  fo$(6)  = "PU40,15;PD;AR-15,0,-90;PR-10,0;AR0,15,-180;AR0,15,315;PR35.7,-34.3;PU;"
						' "&"
  fo$(7)  = "PU10,40;PD10,15;AR-5,0,180;PR0,-15;PU30,-40;"
						' "'"
  fo$(8)  = "PR25,0;PD;AR40,30,-73.74;PU15,-60;"
						' "("
  fo$(9)  = "PR15,0;PD;AR-40,30,73.74;PU25,-60;"
						' ")"
  fo$(10) = "PR0,30;PD40,0;PU-10,20;PD-20,-40;PU0,40;PD20,-40;PU10,-10;"
						' "*"
  fo$(11) = "PU0,30;PD40,0;PU-20,20;PD0,-40;PU20,-10;"
						' "+"
  fo$(12) = "PU10,-10;PD15,10;PR0,5;AR-5,0,180;PR0,-5,-5,-10;PU30,10;"
						' ","
  fo$(13) = "PU0,30;PD40,0;PU0,-30;"		' "-"
  fo$(14) = "PU20,0;PD;AR0,5,360;PU20,0;"	' "."
  fo$(15) = "PD40,60;PU0,-60;"			' "/"
  fo$(16) = "PD40,60;PU-40,-45;PD;AR15,0,90;PR10,0;AR0,15,90;PR0,30;AR-15,0,90;PR-10_
,0;AR0,-15,90;PR0,-30;PU40,-15;"
						' "0"
  fo$(17) = "PR10,0;PD20,0;PU-10,0;PD0,60;PD-10,-10;PU30,-50;"
						' "1"
  fo$(18) = "PR0,45;PD;AR15,0,-90;PR10,0;AR0,-15,-90;PR0,-5;PR-40,-40;PR40,0;PU;"
						' "2"
  fo$(19) = "PR0,15;PD;AR15,0,90;PR10,0;AR0,15,180;PR-5,0,5,0;AR0,15,180;PR-10,0;AR0_
,-15,90;PU40,-45;"
						' "3"
  fo$(20) = "PR40,20;PD-40,0;PD20,40;PU10,0;PD0,-60;PU10,0;"
						' "4"
  fo$(21) = "PR0,15;PD;AR15,0,90;PR10,0;AR0,15,90;PR0,10;AR-15,0,90;PR-10,0;AR0,-15,_
90;PR0,35;PR40,0;PU0,-60;"
						' "5"
  fo$(22) = "PR40,45;PD;AR-15,0,90;PR-10,0;AR0,-15,90;PR0,-30;AR15,0,90;PR10,0;AR0,_
15,180;PR-10,0;AR0,-15,90;PU40,-15;"
						' "6"
  fo$(23) = "PD40,60;PD-40,0;PU40,-60;"		' "7"
  fo$(24) = "PR15,30;PD;AR0,-15,180;PR10,0;AR0,15,180;AR0,15,180;PR-10,0;AR0,-15,180_
;PR10,0;PU15,-30;"
						' "8"
  fo$(25) = "PR0,15;PD;AR15,0,90;PR10,0;AR0,15,90;PR0,30;AR-15,0,90;PR-10,0;AR0,-15,_
180;PR10,0;AR0,15,90;PU0,-45;"
						' "9"
  fo$(26) = "PR20,0;PD;AR0,5,360;PU0,30;PD;AR0,5,360;PU20,-30;"
						' ":"
  fo$(27) = "PR10,-10;PD15,10;PR0,5;AR-5,0,180;PR0,-5,-5,-10;PU10,40;PD;AR0,5,360;_
PU20,-30;"
						' ";"
  fo$(28) = "PR40,60;PD-40,-30;PD40,-30;PU;"	' "<"
  fo$(29) = "PR0,20;PD40,0;PU0,20;PD-40,0;PU40,-40;"
						' "="
  fo$(30) = "PR0,60;PD40,-30;PD-40,-30;PU40,0;"	' ">"
  fo$(31) = "PR20,10;PD;AR0,-5,360;PU0,10;PD20,20;PR0,5;AR-15,0,90;PR-10,0;AR0,-15,_
90;PU40,-45;"
						' "?"
  fo$(32) = "PR30,5;PD-10,0;AR0,20,-90;PR0,10;AR20,0,-180;PR0,-10;AR-5,0,-180;PR0,17_
,0,-7;AR-10,0,180;PR0,-10;AR10,0,180;PU10,-25;"
						' "@"
  fo$(33) = "PD20,60;PR20,-60;PU-33.33,20;PD26.67,0;PU6.66,-20;"
						' "A"
  fo$(34) = "PD25,0;AR0,15,180;PR-20,0;PU20,0;PD;AR0,15,180;PR-25,0;PU5,0;PD0,-60;_
PU35,0;"
						' "B"
  fo$(35) = "PR40,45;PD;AR-15,0,90;PR-10,0;AR0,-15,90;PR0,-30;AR15,0,90;PR10,0;AR0,_
15,90;PU0,-15;"
						' "C"
  fo$(36) = "PD25,0;AR0,15,90;PR0,30;AR-15,0,90;PR-25,0;PU5,0;PD0,-60;PU35,0;"
						' "D"
  fo$(37) = "PD0,60;PD40,0;PU-10,-30;PD-30,0;PU0,-30;PD40,0;PU;"
						' "E"
  fo$(38) = "PD0,60;PD40,0;PU-10,-30;PD-30,0;PU40,-30;"
						' "F"
  fo$(39) = "PR40,45;PD;AR-15,0,90;PR-10,0;AR0,-15,90;PR0,-30;AR15,0,90;PR10,0;AR0,_
15,90;PR0,15;PR-20,0;PU20,-30;"
						' "G"
  fo$(40) = "PD0,60;PU0,-30;PD40,0;PU0,30;PD0,-60;PU;"
						' "H"
  fo$(41) = "PR10,0;PD20,0;PU-10,0;PD0,60;PU-10,0;PD20,0;PU10,-60;"
						' "I"
  fo$(42) = "PR0,15;PD;AR15,0,180;PR0,45;PU-10,0;PD20,0;PU0,-60;"
						' "J"
  fo$(43) = "PD0,60;PU0,-30;PD40,30;PU-40,-30;PD40,-30;PU;"
						' "K"
  fo$(44) = "PR0,60;PD0,-60;PD40,0;PU;"		' "L"
  fo$(45) = "PD0,60;PD20,-30;PD20,30;PD0,-60;PU;"
						' "M"
  fo$(46) = "PD0,60;PR40,-60;PR0,60;PU0,-60;"	' "N"
  fo$(47) = "PR0,15;PD;AR15,0,90;PR10,0;AR0,15,90;PR0,30;AR-15,0,90;PR-10,0;AR0,-15,_
90;PR0,-30;PU40,-15;"
						' "O"
  fo$(48) = "PR5,0;PD0,60;PU0,-30;PD20,0;AR0,15,180;PR-25,0;PU40,-60;"
						' "P"
  fo$(49) = "PR0,15;PD;AR15,0,90;PR10,0;AR0,15,90;PR0,30;AR-15,0,90;PR-10,0;AR0,-15,_
90;PR0,-30;PU20,5;PD20,-20;PU;"
						' "Q"
  fo$(50) = "PR5,0;PD0,60;PU0,-30;PD20,0;AR0,15,180;PR-25,0;PU20,-30;PD20,-30;PU;"
						' "R"
  fo$(51) = "PR0,15;PD;AR15,0,90;PR10,0;AR0,15,180;PR-10,0;AR0,15,-180;PR10,0;AR0,_
-15,-90PU0,-45;"
						' "S"
  fo$(52) = "PR20,0;PD0,60;PU-20,0;PD40,0;PU0,-60;"
						' "T"
  fo$(53) = "PR0,60;PD0,-45;AR15,0,90;PR10,0;AR0,15,90;PR0,45;PU0,-60;"
						' "U"
  fo$(54) = "PR0,60;PD20,-60;PD20,60;PU0,-60;"	' "V"
  fo$(55) = "PR0,60;PD10,-60;PR10,20;PR10,-20;PR10,60;PU0,-60;"
						' "W"
  fo$(56) = "PR0,60;PD40,-60;PU0,60;PD-40,-60;PU40,0;"
						' "X"
  fo$(57) = "PR0,60;PD20,-30;PD0,-30;PU0,30;PD20,30;PU0,-60;"
						' "Y"
  fo$(58) = "PR0,60;PD40,0;PD-40,-60;PD40,0;PU;"
						' "Z"
  fo$(59) = "PR25,0;PD-10,0;PR0,60;PD10,0;PU15,-60;"
						' "["
  fo$(60) = "PR0,60;PD40,-60;PU;"		' "\"
  fo$(61) = "PR15,0;PD10,0;PR0,60;PR-10,0;PU25,-60;"
						' "]"
  fo$(62) = "PR0,40;PD20,20;PD20,-20;PU0,-40;"	' "^"
  fo$(63) = "PR0,-10;PD40,0;PU0,10;"		' "_"
  fo$(64) = "PR30,40;PD0,15;AR-5,0,180;PR10,-15;PU10,-40;"
						' "`"
  fo$(65) = "PR5,30;PD;AR10,0,-90;PR15,0;AR0,-10,-90;PR0,-20;AR-10,0,-90;PR-20,0;AR0_
,10,-180;PR30,0;PR0,-20;PU;"
						' "a"
  fo$(66) = "PD0,60;PU0,-45;PD;AR15,0,90;PR10,0;AR0,15,90;PR0,10;AR-15,0,90;PR-10,0;_
AR0,-15,90;PU40,-25;"
						' "b"
  fo$(67) = "PR40,25;PD;AR-15,0,90;PR-10,0;AR0,-15,90;PR0,-10;AR15,0,90;PR10,0;AR0,_
15,90;PU0,-15;"
						' "c"
  fo$(68) = "PR40,25;PD;AR-15,0,90;PR-10,0;AR0,-15,90;PR0,-10;AR15,0,90;PR10,0;AR0,_
15,90;PU0,45;PD0,-60;PU;"
						' "d"
  fo$(69) = "PR0,20;PD30,0;AR0,10,180;PD-20,0;AR0,-10,90;PR0,-20;AR10,0,90;PR20,0;_
AR0,10,90;PU0,-10;"
						' "e"
  fo$(70) = "PR15,0;PD0,45;AR15,0,-90;PR5,0;PU-30,-30;PD20,0;PU15,-30;"
						' "f"
  fo$(71) = "PR40,15;PD;AR-15,0,-90;PR-10,0;AR0,15,-90;PR0,10;AR15,0,-90;PR10,0;AR0,_
-15,-90;PR0,-30;AR-15,0,-90;PR-10,0;AR0,15,-90;PU40,5;"
						' "g"
  fo$(72) = "PD0,60;PU0,-35;PDAR15,0,-90;PR10,0;AR0,-15,-90;PR0,-25;PU;"
						' "h"
  fo$(73) = "PR15,0;PD10,0;PU-5,0;PD0,40;PU0,10;PDAR0,5,360;PU20,-50;"
						' "i"
  fo$(74) = "PR5,-20;PD5,0;AR0,15,90;PD0,45;PU0,10;PDAR0,5,360;PU15,-50;"
						' "j"
  fo$(75) = "PR5,60;PD0,-60;PU0,20;PD30,20;PU-30,-20;PD30,-20;PU5,0;"
						' "k"
  fo$(76) = "PR15,0;PD;PR10,0;PU-5,0;PD0,55;AR-5,0,90;PU25,-60;"
						' "l"
  fo$(77) = "PD0,40;PU0,-10;PD;AR10,0,-180;PU0,-30;PD0,30;AR10,0,-180;PR0,-30;PU;"
						' "m"
  fo$(78) = "PD0,40;PU0,-15;PD;AR15,0,-90;PR10,0;AR0,-15,-90;PR0,-25;PU;"
						' "n"
  fo$(79) = "PR0,15;PD;AR15,0,90;PR10,0;AR0,15,90;PR0,10;AR-15,0,90;PR-10,0;AR0,-15,_
90;PR0,-10;PU40,-15;"
						' "o"
  fo$(80) = "PR0,-20;PD0,60;PU0,-25;PD;AR15,0,90;PR10,0;AR0,15,90;PR0,10;AR-15,0,90;_
PR-10,0;AR0,-15,90;PU40,-25;"
						' "p"
  fo$(81) = "PR40,25;PD;AR-15,0,90;PR-10,0;AR0,-15,90;PR0,-10;AR15,0,90;PR10,0;AR0,_
15,90;PU0,25;PD0,-60;PU0,20;"
						' "q"
  fo$(82) = "PD0,40;PU0,-15;PD;AR15,0,-90;PR10,0;AR0,-15,-90;PU0,-25;"
						' "r"
  fo$(83) = "PR0,10;PD;AR10,0,90;PR20,0;AR0,10,180;PR-20,0;AR0,10,-180;PR20,0;AR0,_
-10,-90;PU0,-30;"
						' "s"
  fo$(84) = "PU5,30;PD20,0;PU-10,30;PD0,-45;AR15,0,90;PD5,0;PU5,0;"
						' "t"
  fo$(85) = "PR0,40;PD0,-25;AR15,0,90;PR10,0;AR0,15,90;PU0,25;PD0,-40;PU;"
						' "u"
  fo$(86) = "PR0,40;PD20,-40;PD20,40;PU0,-40;"	' "v"
  fo$(87) = "PR0,40;PD10,-40;PR10,20;PR10,-20;PR10,40;PU0,-40;"
						' "w"
  fo$(88) = "PD40,40;PU-40,0;PD40,-40;PU;"	' "x"
  fo$(89) = "PR0,40;PD20,-40;PU20,40;PD-30,-60;PU30,20;"
						' "y"
  fo$(90) = "PR0,40;PD40,0;PD-40,-40;PD40,0;PU;"
						' "z"
  fo$(91) = "PR25,60;PD;AR0,-5,90;PR0,-20;PR-5,-5;PR5,-5;PR0,-20;AR5,0,90;PU15,0;"
						' "{"
  fo$(92) = "PR20,0;PD0,60;PU20,-60;"		' "|"
  fo$(93) = "PR15,0;PD;AR0,5,90;PR0,20;PR5,5;PR-5,5;PR0,20;AR-5,0,90;PU25,-60;"
						' "}"
  fo$(94) = "PR0,30;PD;AR10,0,-180;AR10,0,180;PU0,-30;"
						' "~"
  fo$(95) = "PD40,0;PD0,60;PD-40,0;PD0,-60;PU40,0;"
						' "[DEL]"
RETURN


'------------------------------------------------------------------------------------
'
' calculates the radius of an arc given the certre and a point on the arc

FUNCTION CalcRadius(xcentre, ycentre, xpoint, ypoint) STATIC

  CalcRadius = SQR((xpoint - xcentre) ^ 2 + (ypoint - ycentre) ^ 2)

END FUNCTION


'------------------------------------------------------------------------------------
'
' calculates the angle of a point on an arc from the certre of an arc

FUNCTION CalcAngle(xcentre, ycentre, xpoint, ypoint) STATIC

  xdelta = xpoint - xcentre			' calculate relative x
  ydelta = ypoint - ycentre			' calculate relative y

  IF xdelta > 0 THEN				' if we're in the right semicircle
    CalcAngle = 90 - ATN(ydelta / xdelta) * convangle
						' start at 90 degrees
  ELSEIF xdelta < 0 THEN			' if we're in the left semicircle
    CalcAngle = 270 - ATN(ydelta / xdelta) * convangle
						' start at 270 degrees
  ELSE
    CalcAngle = - 180 * (ydelta < 0)		' else 0 or 180 degrees
  END IF

END FUNCTION

'------------------------------------------------------------------------------------


Last page update: 25th February, 2007. e-mail me e-mail