' Define type for the titles:
TYPE TitleType
   MainTitle AS STRING * 40
   XTitle AS STRING * 40
   YTitle AS STRING * 18
END TYPE

DECLARE SUB InputTitles (T AS TitleType)
DECLARE FUNCTION DrawGraph$ (T AS TitleType, Label$(), Value!(), N%)
DECLARE FUNCTION InputData% (Label$(), Value!())

' Variable declarations for titles and bar data:
DIM Titles AS TitleType, Label$(1 TO 5), Value(1 TO 5)

CONST FALSE = 0, TRUE = NOT FALSE

DO
   InputTitles Titles
   N% = InputData%(Label$(), Value())
   IF N% <> FALSE THEN
      NewGraph$ = DrawGraph$(Titles, Label$(), Value(), N%)
   END IF
LOOP WHILE NewGraph$ = "Y"

END

' ======================== DRAWGRAPH ======================
'   Draws a bar graph from the data entered in the
'   INPUTTITLES and INPUTDATA procedures.
' =========================================================
FUNCTION DrawGraph$ (T AS TitleType, Label$(), Value(), N%) STATIC

   ' Set size of graph:
   CONST GRAPHTOP = 24, GRAPHBOTTOM = 171
   CONST GRAPHLEFT = 48, GRAPHRIGHT = 624
   CONST YLENGTH = GRAPHBOTTOM - GRAPHTOP

   ' Calculate maximum and minimum values:
   YMax = 0
   YMin = 0
   FOR I% = 1 TO N%
      IF Value(I%) < YMin THEN YMin = Value(I%)
      IF Value(I%) > YMax THEN YMax = Value(I%)
   NEXT I%

   ' Calculate width of bars and space between them:
   BarWidth = (GRAPHRIGHT - GRAPHLEFT) / N%
   BarSpace = .2 * BarWidth
   BarWidth = BarWidth - BarSpace

   SCREEN 2
   CLS

   ' Draw y-axis:
   LINE (GRAPHLEFT, GRAPHTOP)-(GRAPHLEFT, GRAPHBOTTOM), 1

   ' Draw main graph title:
   Start% = 44 - (LEN(RTRIM$(T.MainTitle)) / 2)
   LOCATE 2, Start%
   PRINT RTRIM$(T.MainTitle);

   ' Annotate y-axis:
   Start% = CINT(13 - LEN(RTRIM$(T.YTitle)) / 2)
   FOR I% = 1 TO LEN(RTRIM$(T.YTitle))
      LOCATE Start% + I% - 1, 1
      PRINT MID$(T.YTitle, I%, 1);
   NEXT I%

   ' Calculate scale factor so labels aren't bigger than four digits:
   IF ABS(YMax) > ABS(YMin) THEN
      Power = YMax
   ELSE
      Power = YMin
   END IF
   Power = CINT(LOG(ABS(Power) / 100) / LOG(10))
   IF Power < 0 THEN Power = 0

   ' Scale minimum and maximum values down:
   ScaleFactor = 10 ^ Power
   YMax = CINT(YMax / ScaleFactor)
   YMin = CINT(YMin / ScaleFactor)
   ' If power isn't zero then put scale factor on chart:
   IF Power <> 0 THEN
      LOCATE 3, 2
      PRINT "x 10^"; LTRIM$(STR$(Power))
   END IF

   ' Put tic mark and number for Max point on y-axis:
   LINE (GRAPHLEFT - 3, GRAPHTOP) -STEP(3, 0)
   LOCATE 4, 2
   PRINT USING "####"; YMax

   ' Put tic mark and number for Min point on y-axis:
   LINE (GRAPHLEFT - 3, GRAPHBOTTOM) -STEP(3, 0)
   LOCATE 22, 2
   PRINT USING "####"; YMin

   YMax = YMax * ScaleFactor ' Scale minimum and maximum back
   YMin = YMin * ScaleFactor ' up for charting calculations.

   ' Annotate x-axis:
   Start% = 44 - (LEN(RTRIM$(T.XTitle)) / 2)
   LOCATE 25, Start%
   PRINT RTRIM$(T.XTitle);

   ' Calculate the pixel range for the y-axis:
   YRange = YMax - YMin

   ' Define a diagonally striped pattern:
   Tile$ = CHR$(1)+CHR$(2)+CHR$(4)+CHR$(8)+CHR$(16)+CHR$(32)+CHR$(64)+CHR$(128)

   ' Draw a zero line if appropriate:
   IF YMin < 0 THEN
      Bottom = GRAPHBOTTOM - ((-YMin) / YRange * YLENGTH)
      LOCATE INT((Bottom - 1) / 8) + 1, 5
      PRINT "0";
   ELSE
      Bottom = GRAPHBOTTOM
   END IF

   ' Draw x-axis:
   LINE (GRAPHLEFT - 3, Bottom)-(GRAPHRIGHT, Bottom)
   ' Draw bars and labels:
   Start% = GRAPHLEFT + (BarSpace / 2)
   FOR I% = 1 TO N%

      ' Draw a bar label:
      BarMid = Start% + (BarWidth / 2)
      CharMid = INT((BarMid - 1) / 8) + 1
      LOCATE 23, CharMid - INT(LEN(RTRIM$(Label$(I%))) / 2)
      PRINT Label$(I%);

      ' Draw the bar and fill it with the striped pattern:
      BarHeight = (Value(I%) / YRange) * YLENGTH
      LINE (Start%, Bottom) -STEP(BarWidth, -BarHeight), , B
      PAINT (BarMid, Bottom - (BarHeight / 2)), Tile$, 1

      Start% = Start% + BarWidth + BarSpace
   NEXT I%
   LOCATE 1, 1
   PRINT "New graph? ";
   DrawGraph$ = UCASE$(INPUT$(1))

END FUNCTION

' ======================== INPUTDATA ======================
'     Gets input for the bar labels and their values
' =========================================================
FUNCTION InputData% (Label$(), Value()) STATIC

   ' Initialize the number of data values:
   NumData% = 0

   ' Print data-entry instructions:
   CLS
   PRINT "Enter data for up to 5 bars:"
   PRINT "   * Enter the label and value for each bar."
   PRINT "   * Values can be negative."
   PRINT "   * Enter a blank label to stop."
   PRINT
   PRINT "After viewing the graph, press any key ";
   PRINT "to end the program."

   ' Accept data until blank label or 5 entries:
   Done% = FALSE
   DO
      NumData% = NumData% + 1
      PRINT
      PRINT "Bar("; LTRIM$(STR$(NumData%)); "):"
      INPUT ; "        Label? ", Label$(NumData%)

      ' Only input value if label isn't blank:
      IF Label$(NumData%) <> "" THEN
	 LOCATE , 35
	 INPUT "Value? ", Value(NumData%)

      ' If label is blank, decrement data counter
      ' and set Done flag equal to TRUE:
      ELSE
	 NumData% = NumData% - 1
	 Done% = TRUE
      END IF
   LOOP UNTIL (NumData% = 5) OR Done%

   ' Return the number of data values input:
   InputData% = NumData%

END FUNCTION

' ====================== INPUTTITLES ======================
'     Accepts input for the three different graph titles
' =========================================================
SUB InputTitles (T AS TitleType) STATIC
   SCREEN 0, 0		' Set text screen.
   DO			' Input titles.
      CLS
      INPUT "Enter main graph title: ", T.MainTitle
      INPUT "Enter x-axis title    : ", T.XTitle
      INPUT "Enter y-axis title    : ", T.YTitle

      ' Check to see if titles are OK:
      LOCATE 7, 1
      PRINT "OK (Y to continue, N to change)? ";
      LOCATE , , 1
      OK$ = UCASE$(INPUT$(1))
   LOOP UNTIL OK$ = "Y"
END SUB

