home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 10 / 10.iso / l / l391 / 2.ddi / CHART.BA$ / CHART.bin
Encoding:
Text File  |  1992-08-19  |  140.0 KB  |  4,649 lines

  1. ' ------------------------------------------------------------------------
  2. ' Visual Basic for MS-DOS Presentation Graphics ToolKit
  3. '
  4. ' The Presentation Graphics ToolKit (CHART.BAS, CHARTASM.ASM,
  5. ' FONT.BAS, and FONTASM.ASM) provides support for adding
  6. ' charts, graphs, and fonts to your programs.
  7. '
  8. ' To use the Presentation Graphics routines in your
  9. ' program either include CHART.BAS and FONT.BAS in
  10. ' your program or use the supplied library (CHART.LIB)
  11. ' and Quick Library (CHART.QLB) and call the appropriate
  12. ' procedures.  An Alternate Math version of the
  13. ' Presentation Graphics Toolkit (for use with programs
  14. ' compiled /FPA) can be created as follows:
  15. '     BC /X /E /O /FPa font.bas;
  16. '     BC /X /E /O /FPa Chart.bas;
  17. '     DEL Charta.lib
  18. '     LIB Charta.Lib + Chart.Obj + Font.Obj + ChartAsm.Obj + FontAsm.Obj + vbdos.lib;
  19. '
  20. ' The charting routines cannot be used without the
  21. ' font support routines.
  22. '
  23. ' Note, if you include CHART.BAS and FONT.BAS in your
  24. ' program, you will also have to create and use a
  25. ' library and Quick Library containing the low-level
  26. ' support routines found in CHARTASM.ASM and FONTASM.ASM.
  27. ' This is done as follows:
  28. '       LIB chartasm.lib + chartasm + fontasm + VBDOS.LIB;
  29. '       LINK /Q chartasm.lib, chartasm.qlb,, VBDOSQLB.LIB;
  30. '
  31. ' Copyright (C) 1982-1992 Microsoft Corporation
  32. '
  33. ' You have a royalty-free right to use, modify, reproduce
  34. ' and distribute the sample applications and toolkits provided with
  35. ' Visual Basic for MS-DOS (and/or any modified version)
  36. ' in any way you find useful, provided that you agree that
  37. ' Microsoft has no warranty, obligations or liability for
  38. ' any of the sample applications or toolkits.
  39. ' ------------------------------------------------------------------------
  40.  
  41. ' CHART.BI contains all of the TYPE definitions and SUB declarations
  42. ' that are accessible to the library user as well as CONST definitions for
  43. ' some routine parameters and error messages:
  44. '$INCLUDE: 'CHART.BI'
  45.  
  46. ' FONT.BI contains all of the TYPE definitions and SUB declarations
  47. ' required for graphics text:
  48. '$INCLUDE: 'FONT.BI'
  49.  
  50. CONST cTicSize = .02            ' Percent of axis length to use for tic length
  51. CONST cMaxChars = 255           ' Maximum ASCII value allowed for character
  52. CONST cBarWid = .8              ' Percent of category width to use for bar
  53. CONST cPiVal = 3.141592         ' A value for PI
  54. CONST cFalse = 0                ' Logical false
  55. CONST cTrue = NOT cFalse        ' Logical true
  56.  
  57. ' Below are TYPE definitions local to this module:
  58.  
  59. ' TYPE for recording information on title spacing:
  60. TYPE TitleLayout
  61.     Top         AS INTEGER        ' Space above first title
  62.     TitleOne    AS INTEGER        ' Height of first title
  63.     Middle      AS INTEGER        ' Space between first and second titles
  64.     TitleTwo    AS INTEGER        ' Height of second title
  65.     Bottom      AS INTEGER        ' Space below second title
  66.     TotalSize   AS INTEGER        ' Sum of all the above
  67. END TYPE
  68.  
  69. ' TYPE for recording information on the legend layout:
  70. TYPE LegendLayout
  71.     NumCol      AS INTEGER        ' Number of columns in legend
  72.     NumRow      AS INTEGER        ' Number of rows in legend
  73.     SymbolSize  AS INTEGER        ' Height of symbol
  74.     LabelOffset AS INTEGER        ' Space between start of symbol and label
  75.     RowSpacing  AS INTEGER        ' Space between tops of rows
  76.     ColSpacing  AS INTEGER        ' Spacing between beginnings of columns
  77.     HorizBorder AS INTEGER        ' Top and bottom border
  78.     VertBorder  AS INTEGER        ' Left and right border
  79. END TYPE
  80.  
  81. ' TYPE for a group of global parameters:
  82. TYPE GlobalParams
  83.     SysFlag     AS INTEGER        ' cYes means Analyze call is from system
  84.     Initialized AS INTEGER        ' cYes means clInitChart has been called
  85.   
  86.     PaletteScrn AS INTEGER        ' Screen mode for which palette is set
  87.     PaletteBits AS INTEGER        ' Bits per pixel for current screen mode
  88.     PaletteSet  AS INTEGER        ' cYes means palette has been initialized
  89.     White       AS INTEGER        ' White attribute in current screen mode
  90.  
  91.     Aspect      AS SINGLE         ' Current screen aspect
  92.     MaxXPix     AS INTEGER        ' Screen size along X axis
  93.     MaxYPix     AS INTEGER        ' Screen size along Y axis
  94.     MaxColor    AS INTEGER        ' Maximum color number for current screen
  95.  
  96.     ChartWid    AS INTEGER        ' Width of chart window
  97.     ChartHgt    AS INTEGER        ' Height of chart window
  98.     CwX1        AS INTEGER        ' Left side of chart window
  99.     CwY1        AS INTEGER        ' Top edge of chart window
  100.     CwX2        AS INTEGER        ' Right side of chart window
  101.     CwY2        AS INTEGER        ' Bottom edge of chart window
  102.  
  103.     XStagger    AS INTEGER        ' Boolean, true if category labels overflow
  104.     ValLenX     AS INTEGER        ' Maximum length of value labels on X-axis
  105.     ValLenY     AS INTEGER        ' Maximum length of value labels on Y-axis
  106.  
  107.     NVals       AS INTEGER        ' Number of data values in data series
  108.     NSeries     AS INTEGER        ' Number of series of data
  109.     MSeries     AS INTEGER        ' If multiple-series chart then cYes, else
  110.                                             ' cNo
  111.     XMode       AS INTEGER        ' Axis mode of x axis
  112.     YMode       AS INTEGER        ' Axis mode of y axis
  113. END TYPE
  114.  
  115. ' FUNCTION and SUB declarations for procedures local to this module:
  116.  
  117. DECLARE FUNCTION clBuildBitP$ (Bits%, c%, InP$)
  118. DECLARE FUNCTION clBuildPlaneP$ (Bits%, c%, InP$)
  119. DECLARE FUNCTION clColorMaskL% (Bits%, Colr%)
  120. DECLARE FUNCTION clGetStyle% (StyleNum%)
  121. DECLARE FUNCTION clMaxVal (A, B)
  122. DECLARE FUNCTION clMap2Pal% (N%)
  123. DECLARE FUNCTION clMap2Attrib% (N%)
  124. DECLARE FUNCTION clMaxStrLen% (Txt$(), First%, Last%)
  125. DECLARE FUNCTION clVal2Str$ (X, Places%, Format%)
  126.  
  127. DECLARE SUB clAdjustScale (Axis AS AxisType)
  128. DECLARE SUB clAnalyzeC (Cat$(), N%, SLabels$(), First%, Last%)
  129. DECLARE SUB clAnalyzeS (N%, SLabels$(), First%, Last%)
  130. DECLARE SUB clBuildPalette (ScrnMode%, Bits%)
  131. DECLARE SUB clChkInit ()
  132. DECLARE SUB clChkFonts ()
  133. DECLARE SUB clChkForErrors (Env AS ChartEnvironment, TypeMin%, TypeMax%, N%, First%, Last%)
  134. DECLARE SUB clChkChartWindow (Env AS ChartEnvironment)
  135. DECLARE SUB clChkPalettes (c%(), s%(), P$(), Char%(), B%())
  136. DECLARE SUB clClearError ()
  137. DECLARE SUB clColorMaskH (Bits%, Colr%, CMask%())
  138. DECLARE SUB clDrawAxes (Cat$())
  139. DECLARE SUB clDrawDataWindow ()
  140. DECLARE SUB clDrawChartWindow ()
  141. DECLARE SUB clDrawTitles ()
  142. DECLARE SUB clDrawLegend (SeriesLabel$(), First AS INTEGER, Last AS INTEGER)
  143. DECLARE SUB clDrawBarData ()
  144. DECLARE SUB clDrawColumnData ()
  145. DECLARE SUB clDrawLineData ()
  146. DECLARE SUB clDrawPieData (value(), Expl%(), N%)
  147. DECLARE SUB clDrawScatterData ()
  148. DECLARE SUB clFilter (A AS AxisType, AxisMode%, D1(), D2(), N%)
  149. DECLARE SUB clFilterMS (A AS AxisType, AxisMode%, D1(), D2(), N%, First%, Last%)
  150. DECLARE SUB clFlagSystem ()
  151. DECLARE SUB clFormatTics (A AS AxisType)
  152. DECLARE SUB clHPrint (X%, Y%, Txt$)
  153. DECLARE SUB clInitChart ()
  154. DECLARE SUB clInitStdStruc ()
  155. DECLARE SUB clLabelXTics (Axis AS AxisType, Cat$(), TicX, TicTotX%, TicY, YBoundry%)
  156. DECLARE SUB clLabelYTics (Axis AS AxisType, Cat$(), TicX, TicY, TicTotY%)
  157. DECLARE SUB clLayoutTitle (TL AS ANY, T1 AS ANY, T2 AS ANY)
  158. DECLARE SUB clPrintTitle (TitleVar AS TitleType, Y%)
  159. DECLARE SUB clRenderBar (X1, Y1, X2, Y2, c%)
  160. DECLARE SUB clRenderWindow (W AS RegionType)
  161. DECLARE SUB clScaleAxis (A AS AxisType, AxisMode%, D1())
  162. DECLARE SUB clSelectChartWindow ()
  163. DECLARE SUB clSelectRelWindow (W AS RegionType)
  164. DECLARE SUB clSetAxisModes ()
  165. DECLARE SUB clSetChartFont (N AS INTEGER)
  166. DECLARE SUB clSetError (ErrNo AS INTEGER)
  167. DECLARE SUB clSetCharColor (N%)
  168. DECLARE SUB clSetGlobalParams ()
  169. DECLARE SUB clSizeDataWindow (Cat$())
  170. DECLARE SUB clLayoutLegend (SeriesLabel$(), First%, Last%)
  171. DECLARE SUB clSpaceTics ()
  172. DECLARE SUB clSpaceTicsA (A AS AxisType, AxisMode%, AxisLen%, TicWid%)
  173. DECLARE SUB clTitleXAxis (A AS AxisType, X1%, X2%, YBoundry%)
  174. DECLARE SUB clTitleYAxis (A AS AxisType, Y1%, Y2%)
  175. DECLARE SUB clUnFlagSystem ()
  176. DECLARE SUB clVPrint (X%, Y%, Txt$)
  177.  
  178.  
  179. ' Variable definitions local to this module:
  180.  
  181. DIM PaletteC%(0 TO cPalLen)            ' List of colors     for drawing data
  182. DIM PaletteS%(0 TO cPalLen)            ' List of styles     for drawing data
  183. DIM PaletteP$(0 TO cPalLen)            ' List of patterns   for drawing data
  184. DIM PaletteCh%(0 TO cPalLen)           ' List of plot chars for drawing data
  185. DIM PaletteB%(0 TO cPalLen)            ' List of patterns   for borders
  186.  
  187. DIM StdChars%(0 TO cPalLen)            ' Holds default plot characters
  188.  
  189. DIM DAxis         AS AxisType          ' Default axis settings
  190. DIM DWindow       AS RegionType        ' Default window settings
  191. DIM DLegend       AS LegendType        ' Default legend settings
  192. DIM DTitle        AS TitleType         ' Default title settings
  193.  
  194. DIM XTitleLayout  AS TitleLayout       ' X-axis layout information
  195. DIM YTitleLayout  AS TitleLayout       ' Y-axis layout information
  196. DIM TTitleLayout  AS TitleLayout       ' Main/Sub layout information
  197.  
  198. DIM LLayout       AS LegendLayout      ' Legend layout information
  199.  
  200. DIM GFI           AS FontInfo          ' Global font information
  201. DIM GE            AS ChartEnvironment  ' An internal global chart environment
  202. DIM GP            AS GlobalParams      ' Holds a number of global parameters
  203.                                                     ' used in the charting routines.  See
  204.                                                     ' TYPE definition for details.
  205.  
  206. '$DYNAMIC
  207. DIM V1(1, 1), V2(1, 1)                 ' Internal dynamic data arrays.
  208. '$STATIC
  209.  
  210.  
  211. ' Module level code:
  212.  
  213. ' This error trap is set in the ChartScreen routine and will
  214. ' be evoked if an invalid screen mode is used:
  215. ScreenErr:
  216.     clSetError cBadScreen
  217.     RESUME NEXT
  218.  
  219. ' This error trap should catch all errors that arise in using
  220. ' the charting library that are not expected:
  221. UnexpectedErr:
  222.     clSetError cCLUnexpectedOff + ERR
  223.     RESUME NEXT
  224.  
  225. '=== AnalyzeChart - Sets up scales and data window sizes
  226. '
  227. '  Arguments:
  228. '     Env        - A ChartEnvironment variable
  229. '
  230. '     Cat$(1)    - One-dimensional array of category labels
  231. '
  232. '     Value(1)   - One-dimensional array of values to chart
  233. '
  234. '     N%         - The number of data values in data series
  235. '
  236. '  Return Values:
  237. '     Scale and Data-Window values are changed as appropriate.
  238. '
  239. '=================================================================
  240. SUB AnalyzeChart (Env AS ChartEnvironment, Cat$(), value(), N AS INTEGER)
  241.  
  242. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  243. SHARED V1()
  244. REDIM V1(1 TO N%, 1 TO 1)
  245. DIM Dum$(1 TO 1)
  246.  
  247.     ' Check initialization and fonts:
  248.     clClearError
  249.     clChkInit
  250.     clChkFonts
  251.     IF ChartErr >= 100 THEN EXIT SUB
  252.   
  253.     ' Set a global flag to indicate that this isn't a multiple-series chart:
  254.     GP.MSeries = cNo
  255.  
  256.     ' Check for obvious parameter and ChartEnvironment errors:
  257.     clChkForErrors Env, 1, 3, N, 0, 0
  258.     IF ChartErr > 100 THEN EXIT SUB
  259.  
  260.     ' Make a copy of the user's ChartEnvironment variable to the library's
  261.     ' global environment variable:
  262.     GE = Env
  263.  
  264.     ' Set the correct axis modes for the type of chart specified in the
  265.     ' chart environment:
  266.     clSetAxisModes
  267.  
  268.     ' Transfer the input data to the dynamic working data array.  Do this
  269.     ' for each axis because, depending on the chart type, either one may be
  270.     ' the value axis.  The Filter routine automatically ignores the call if
  271.     ' the axis is a category axis:
  272.     clFilter GE.XAxis, GP.XMode, value(), V1(), N
  273.     clFilter GE.YAxis, GP.YMode, value(), V1(), N
  274.  
  275.     ' Analyze the data for scale-maximum and -minimum and set the scale-
  276.     ' factor, etc. depending on the options set in the chart environment:
  277.     clAnalyzeC Cat$(), N, Dum$(), 1, 1
  278.   
  279.     ' Copy the global chart environment back to the user's ChartEnvironment
  280.     ' variable so that the settings that were calculated by the library are
  281.     ' accessible.  Then, if this routine wasn't called by the library itself,
  282.     ' in the course of drawing a bar, column or line chart, deallocate the
  283.     ' working data array:
  284.     Env = GE
  285.     IF GP.SysFlag = cNo THEN ERASE V1
  286.  
  287. END SUB
  288.  
  289. '=== AnalyzeChartMS - Analyzes multiple-series data for scale/window size.
  290. '
  291. '  Arguments:
  292. '     Env             - ChartEnvironment variable
  293. '
  294. '     Cat$(1)         - One-dimensional array of category labels
  295. '
  296. '     Value(2)        - Two-dimensional array of values to chart.  First
  297. '                       dimension (rows) represents different values within
  298. '                       a series.  Second dimension (columns) represents
  299. '                       different series.
  300. '
  301. '     N%              - Number of values (beginning with 1) to chart per
  302. '                       series.
  303. '
  304. '     First%          - First series to analyze
  305. '
  306. '     Last%           - Last series to analyze
  307. '
  308. '     SeriesLabel$(1) - Labels for the different series
  309. '
  310. '  Return Values:
  311. '     Various settings in the Env variable are altered in accordance with
  312. '     the analysis.
  313. '
  314. '=================================================================
  315. SUB AnalyzeChartMS (Env AS ChartEnvironment, Cat$(), value() AS SINGLE, N AS INTEGER, First AS INTEGER, Last AS INTEGER, SeriesLabel$())
  316.  
  317. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  318. SHARED V1()
  319. REDIM V1(1 TO N, 1 TO Last - First + 1)
  320.  
  321.     ' Check initialization and fonts:
  322.     clClearError
  323.     clChkInit
  324.     clChkFonts
  325.     IF ChartErr >= 100 THEN EXIT SUB
  326.  
  327.     ' Set a global flag to indicate that this is a multiple-series chart:
  328.     GP.MSeries = cYes
  329.  
  330.     ' Check for obvious parameter and ChartEnvironment errors:
  331.     clChkForErrors Env, 1, 3, N, 0, 0
  332.     IF ChartErr > 100 THEN EXIT SUB
  333.  
  334.     ' Make a copy of the user's ChartEnvironment variable to the library's
  335.     ' global environment variable:
  336.     GE = Env
  337.  
  338.     ' Set the correct axis modes for the type of chart specified in the
  339.     ' chart environment:
  340.     clSetAxisModes
  341.  
  342.     ' Transfer the input data to the dynamic working data array.  Do this
  343.     ' for each axis because, depending on the chart type, either one may be
  344.     ' the value axis.  The Filter routine automatically ignores the call if
  345.     ' the axis is a category axis:
  346.     clFilterMS GE.XAxis, GP.XMode, value(), V1(), N, First, Last
  347.     clFilterMS GE.YAxis, GP.YMode, value(), V1(), N, First, Last
  348.  
  349.     ' Analyze the data for scale maximums and minimums and set the scale
  350.     ' factor, etc. depending on the options set in the chart environment:
  351.     clAnalyzeC Cat$(), N, SeriesLabel$(), First, Last
  352.  
  353.     ' Copy the global chart environment back to the user's ChartEnvironment
  354.     ' variable so that the settings that were calculated by the library are
  355.     ' accessible.  Then, if this routine wasn't called by the library itself,
  356.     ' in the course of drawing a bar, column or line chart, deallocate the
  357.     ' working data array:
  358.     Env = GE
  359.     IF GP.SysFlag = cNo THEN ERASE V1
  360.  
  361. END SUB
  362.  
  363. '=== AnalyzePie - Analyzes data for a pie chart
  364. '
  365. '  Arguments:
  366. '     Env      - A ChartEnvironment variable
  367. '
  368. '     Cat$()   - One-dimensional array of category names
  369. '
  370. '     Value()  - One-dimensional array of values to chart
  371. '
  372. '     Expl()   - One dimensional array of flags indicating whether slices
  373. '                are to be "exploded" (0 means no, 1 means yes).
  374. '                Ignored if Env.ChartStyle <> 1.
  375. '
  376. '     N        - The number of values to chart
  377. '
  378. '  Return Values:
  379. '     None.
  380. '
  381. '=================================================================
  382. SUB AnalyzePie (Env AS ChartEnvironment, Cat$(), value() AS SINGLE, Expl() AS INTEGER, N AS INTEGER)
  383. SHARED GE AS ChartEnvironment
  384. SHARED GP AS GlobalParams
  385. SHARED TTitleLayout AS TitleLayout
  386. SHARED XTitleLayout AS TitleLayout
  387. SHARED YTitleLayout AS TitleLayout
  388. SHARED V1()
  389. DIM EmptyTitle AS TitleType
  390.  
  391.     ' Check initialization and fonts:
  392.     clClearError
  393.     clChkInit
  394.     clChkFonts
  395.     IF ChartErr >= 100 THEN EXIT SUB
  396.  
  397.     ' This is a multiple series chart (a pie chart is treated as a
  398.     ' multiple series chart with each series having one value):
  399.     GP.MSeries = cYes
  400.     GP.NSeries = N
  401.  
  402.     ' Check for obvious parameter and ChartEnvironment errors:
  403.     clChkForErrors Env, cPie, cPie, 2, 1, N
  404.     IF ChartErr > 100 THEN EXIT SUB
  405.  
  406.     ' Make a copy of the user's ChartEnvironment variable to the library's
  407.     ' global environment variable:
  408.     GE = Env
  409.  
  410.     ' Set the correct axis modes for the type of chart specified in the
  411.     ' chart environment:
  412.     clSetAxisModes
  413.  
  414.     ' Set global parameters and layout main title:
  415.     clSetGlobalParams
  416.   
  417.     ' Layout titles (ignore X and Y axis titles):
  418.     clLayoutTitle TTitleLayout, GE.MainTitle, GE.SubTitle
  419.     EmptyTitle.Title = ""
  420.     clLayoutTitle XTitleLayout, EmptyTitle, EmptyTitle
  421.     clLayoutTitle YTitleLayout, EmptyTitle, EmptyTitle
  422.  
  423.     ' Calculate the size for LegendWindow and DataWindow:
  424.     clLayoutLegend Cat$(), 1, N
  425.     IF ChartErr > 100 THEN EXIT SUB
  426.     clSizeDataWindow Cat$()
  427.     IF ChartErr > 100 THEN EXIT SUB
  428.  
  429.     ' Copy the global chart environment back to the user's ChartEnvironment
  430.     ' variable so that the settings that were calculated by the library are
  431.     ' accessible.  Then, if this routine wasn't called by the library itself,
  432.     ' in the course of drawing a pie chart, deallocate the working data array:
  433.     Env = GE
  434.  
  435. END SUB
  436.  
  437. '=== AnalyzeScatter - Sets up scales and data-window sizes for scatter chart
  438. '
  439. '  Arguments:
  440. '     Env        - A ChartEnvironment variable
  441. '
  442. '     ValX(1)    - One-dimensional array of values for X axis
  443. '
  444. '     ValY(1)    - One-dimensional array of values for Y axis
  445. '
  446. '     N%         - The number of data values in data series
  447. '
  448. '  Return Values:
  449. '     Scale and data-window values are changed as appropriate.
  450. '
  451. '=================================================================
  452. SUB AnalyzeScatter (Env AS ChartEnvironment, ValX() AS SINGLE, ValY() AS SINGLE, N AS INTEGER)
  453.  
  454. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  455. SHARED V1(), V2()
  456. REDIM V1(1 TO N, 1 TO 1), V2(1 TO N, 1 TO 1)
  457. DIM Dum$(1 TO 1)
  458.  
  459.     ' Check initialization and fonts:
  460.     clClearError
  461.     clChkInit
  462.     clChkFonts
  463.     IF ChartErr >= 100 THEN EXIT SUB
  464.  
  465.     ' Set a global flag to indicate that this isn't a multiple-series chart:
  466.     GP.MSeries = cNo
  467.  
  468.     ' Check for obvious parameter and ChartEnvironment errors:
  469.     clChkForErrors Env, 4, 4, N%, 0, 0
  470.     IF ChartErr > 100 THEN EXIT SUB
  471.  
  472.     ' Make a copy of the user's ChartEnvironment variable to the library's
  473.     ' global environment variable:
  474.     GE = Env
  475.  
  476.     ' Set the correct axis modes for the type of chart specified in the
  477.     ' chart environment:
  478.     clSetAxisModes
  479.  
  480.     ' Transfer the input data to the dynamic working data arrays (one
  481.     ' for each axis):
  482.     clFilter GE.XAxis, GP.XMode, ValX(), V1(), N
  483.     clFilter GE.YAxis, GP.YMode, ValY(), V2(), N
  484.  
  485.     ' Analyze the data for scale-maximum and -minimum and set the scale-
  486.     ' factor, etc. depending on the options set in the chart environment:
  487.     clAnalyzeS N, Dum$(), 1, 1
  488.  
  489.     ' Copy the global chart environment back to the user's ChartEnvironment
  490.     ' variable so that the settings that were calculated by the library are
  491.     ' accessible.  Then, if this routine wasn't called by the library itself,
  492.     ' in the course of drawing a scatter chart, deallocate the working
  493.     ' data arrays:
  494.     Env = GE
  495.     IF GP.SysFlag = cNo THEN ERASE V1, V2
  496.  
  497. END SUB
  498.  
  499. '=== AnalyzeScatterMS - Analyzes multiple-series data for scale/window size
  500. '
  501. '  Arguments:
  502. '     Env             - A ChartEnvironment variable
  503. '
  504. '     ValX(2)         - Two-dimensional array of values for X axis.  First
  505. '                       dimension (rows) represents different values within
  506. '                       a series.  Second dimension (columns) represents
  507. '                       different series.
  508. '
  509. '     ValY(2)         - Two-dimensional array of values for Y axis.  Above
  510. '                       comments apply
  511. '
  512. '     N%              - Number of values (beginning with 1) to chart per
  513. '                       series
  514. '
  515. '     First%          - First series to analyze
  516. '
  517. '     Last%           - Last series to analyze
  518. '
  519. '     SeriesLabel$(1) - Labels for the different series
  520. '
  521. '  Return Values:
  522. '     Various settings in the Env variable are altered in accordance with
  523. '     the analysis.
  524. '
  525. '=================================================================
  526. SUB AnalyzeScatterMS (Env AS ChartEnvironment, ValX() AS SINGLE, ValY() AS SINGLE, N AS INTEGER, First AS INTEGER, Last AS INTEGER, SeriesLabel$())
  527.  
  528. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  529. SHARED V1(), V2()
  530. REDIM V1(1 TO N, 1 TO Last - First + 1), V2(1 TO N, 1 TO Last - First + 1)
  531. DIM Dum$(1 TO 1)
  532.  
  533.     ' Check initialization and fonts:
  534.     clClearError
  535.     clChkInit
  536.     clChkFonts
  537.     IF ChartErr >= 100 THEN EXIT SUB
  538.  
  539.     ' Set a global flag to indicate that this is a multiple-series chart:
  540.     GP.MSeries = cYes
  541.  
  542.     ' Check for obvious parameter and ChartEnvironment errors:
  543.     clChkForErrors Env, 4, 4, N%, 0, 0
  544.     IF ChartErr > 100 THEN EXIT SUB
  545.  
  546.     ' Make a copy of the user's ChartEnvironment variable to the library's
  547.     ' global environment variable:
  548.     GE = Env
  549.  
  550.     ' Set the correct axis modes for the type of chart specified in the
  551.     ' chart environment:
  552.     clSetAxisModes
  553.  
  554.     ' Transfer the input data to the dynamic working data arrays (one
  555.     ' for each axis):
  556.     clFilterMS GE.XAxis, GP.XMode, ValX(), V1(), N, First, Last
  557.     clFilterMS GE.YAxis, GP.YMode, ValY(), V2(), N, First, Last
  558.  
  559.     ' Analyze the data for scale-maximum and -minimum and set the scale-
  560.     ' factor, etc. depending on the options set in the chart environment:
  561.     clAnalyzeS N, SeriesLabel$(), First%, Last%
  562.  
  563.     ' Copy the global chart environment back to the user's ChartEnvironment
  564.     ' variable so that the settings that were calculated by the library are
  565.     ' accessible.  Then, if this routine wasn't called by the library itself,
  566.     ' in the course of drawing a scatter chart, deallocate the working
  567.     ' data arrays:
  568.     Env = GE
  569.     IF GP.SysFlag = cNo THEN ERASE V1, V2
  570.  
  571. END SUB
  572.  
  573. '=== Chart - Draws a single-series category/value chart
  574. '
  575. '  Arguments:
  576. '     Env        - A ChartEnvironment variable
  577. '
  578. '     Cat$(1)    - One-dimensional array of category labels
  579. '
  580. '     Value(1)   - One-dimensional array of values to plot
  581. '
  582. '     N          - The number of data values in data series
  583. '
  584. '  Return Values:
  585. '     Some elements of the Env variable are altered by plotting routines
  586. '
  587. '  Remarks:
  588. '     This routine takes all of the parameters set in the Env variable
  589. '     and draws a single-series chart of type Bar, Column, or Line
  590. '     depending on the chart type specified in the Env variable.
  591. '
  592. '=================================================================
  593. SUB Chart (Env AS ChartEnvironment, Cat$(), value() AS SINGLE, N AS INTEGER)
  594.  
  595. SHARED V1()
  596.  
  597.     ' Analyze data for scale and window settings:
  598.     clFlagSystem
  599.     AnalyzeChart Env, Cat$(), value(), N
  600.     clUnFlagSystem
  601.     IF ChartErr < 100 THEN
  602.  
  603.         ' Draw the different elements of the chart:
  604.         clDrawChartWindow
  605.         clDrawTitles
  606.         clDrawDataWindow
  607.         clDrawAxes Cat$()
  608.   
  609.         ' Call appropriate Draw...Data routine for chart type:
  610.         SELECT CASE Env.ChartType
  611.             CASE 1: clDrawBarData
  612.             CASE 2: clDrawColumnData
  613.             CASE 3: clDrawLineData
  614.         END SELECT
  615.  
  616.     END IF
  617.  
  618.     ' Deallocate the data array:
  619.     ERASE V1
  620.  
  621. END SUB
  622.  
  623. '=== ChartMS - Draws a multiple-series category/value chart
  624. '
  625. '  Arguments:
  626. '     Env               - A ChartEnvironment variable
  627. '
  628. '     Cat$(1)           - A one-dimensional array of category names for the
  629. '                         different data values
  630. '
  631. '     Value(2)          - A two-dimensional array of values--one column for
  632. '                         each series of data
  633. '
  634. '     N%                - The number of data points in each series of data
  635. '
  636. '     First%            - The first series to be plotted
  637. '
  638. '     Last%             - The last series to be plotted
  639. '
  640. '     SeriesLabel$(1)   - Labels used for each series in the legend
  641. '
  642. '  Return Values:
  643. '     Some elements of the Env variable are altered by plotting routines
  644. '
  645. '  Remarks:
  646. '     This routine takes all of the parameters set in the Env variable
  647. '     and draws a multiple-series chart of type Bar, Column, or Line
  648. '     depending on the chart type specified in the Env variable.
  649. '
  650. '=================================================================
  651. SUB ChartMS (Env AS ChartEnvironment, Cat$(), value() AS SINGLE, N AS INTEGER, First AS INTEGER, Last AS INTEGER, SeriesLabel$())
  652.  
  653. SHARED V1()
  654.  
  655.     ' Analyze data for scale settings:
  656.     clFlagSystem
  657.     AnalyzeChartMS Env, Cat$(), value(), N, First, Last, SeriesLabel$()
  658.     clUnFlagSystem
  659.     IF ChartErr < 100 THEN
  660.  
  661.         ' Draw the different elements of the chart:
  662.         clDrawChartWindow
  663.         clDrawTitles
  664.         clDrawDataWindow
  665.         clDrawAxes Cat$()
  666.  
  667.         ' Call appropriate Draw...DataMS routine for chart type:
  668.         SELECT CASE Env.ChartType
  669.             CASE 1: clDrawBarData
  670.             CASE 2: clDrawColumnData
  671.             CASE 3: clDrawLineData
  672.         END SELECT
  673.  
  674.         ' Lastly, add the legend:
  675.         clDrawLegend SeriesLabel$(), First, Last
  676.  
  677.     END IF
  678.  
  679.     ' Deallocate the data array:
  680.     ERASE V1
  681.  
  682. END SUB
  683.  
  684. '=== ChartPie - Draws a pie chart
  685. '
  686. '  Arguments:
  687. '     Env      - A ChartEnvironment variable
  688. '
  689. '     Cat$()   - One-dimensional array of category names
  690. '
  691. '     Value()  - One-dimensional array of values to chart
  692. '
  693. '     Expl%()  - One-dimensional array of flags indicating whether slices
  694. '                are to be "exploded" or not (0 means no, 1 means yes),
  695. '                ignored if ChartStyle <> 1
  696. '
  697. '     N%       - The number of values to chart
  698. '
  699. '  Return Values:
  700. '     No return values
  701. '
  702. '=================================================================
  703. SUB ChartPie (Env AS ChartEnvironment, Cat$(), value() AS SINGLE, Expl() AS INTEGER, N AS INTEGER)
  704. SHARED GP AS GlobalParams
  705.     ' Set the global system flag to tell the AnalyzePie routine that it
  706.     ' is being called by the system and not the user:
  707.     clFlagSystem
  708.  
  709.     ' Calculate the size of the Data- and Legend-window:
  710.     AnalyzePie Env, Cat$(), value(), Expl(), N
  711.  
  712.     ' Remove the system flag:
  713.     clUnFlagSystem
  714.  
  715.     ' If there were no errors during analysis draw the chart:
  716.     IF ChartErr < 100 THEN
  717.  
  718.         ' Draw the different chart elements:
  719.         clDrawChartWindow
  720.         clDrawTitles
  721.         clDrawDataWindow
  722.         clDrawPieData value(), Expl(), N
  723.         IF ChartErr <> 0 THEN EXIT SUB
  724.         clDrawLegend Cat$(), 1, N
  725.  
  726.     END IF
  727.  
  728. END SUB
  729.  
  730. '=== ChartScatter - Draws a single-series scatter chart
  731. '
  732. '  Arguments:
  733. '     Env      - A ChartEnvironment variable
  734. '
  735. '     ValX(1)  - One-dimensional array of values for X axis
  736. '
  737. '     ValY(1)  - One-dimensional array of values for Y axis
  738. '
  739. '     N%       - The number of values to chart
  740. '
  741. '
  742. '  Return Values:
  743. '     Some elements of Env variable may be changed by drawing routines
  744. '
  745. '  Remarks:
  746. '     ChartScatter should be called when a chart with two value axes is
  747. '     desired
  748. '
  749. '=================================================================
  750. SUB ChartScatter (Env AS ChartEnvironment, ValX() AS SINGLE, ValY() AS SINGLE, N AS INTEGER)
  751. DIM Dum$(1 TO 1)
  752. SHARED V1(), V2()
  753.  
  754.     ' Set the global system flag to tell the AnalyzeScatter routine that it
  755.     ' is being called by the system and not the user:
  756.     clFlagSystem
  757.   
  758.     ' Calculate the scale maximums and minimums and scale factor. Also
  759.     ' calculate the sizes for the Data- and Legend-windows:
  760.     AnalyzeScatter Env, ValX(), ValY(), N
  761.  
  762.     ' Remove the system flag:
  763.     clUnFlagSystem
  764.  
  765.     ' If there were no errors during analysis draw the chart:
  766.     IF ChartErr < 100 THEN
  767.  
  768.         ' Draw the different elements of the chart:
  769.         clDrawChartWindow
  770.         clDrawTitles
  771.         clDrawDataWindow
  772.         clDrawAxes Dum$()
  773.         clDrawScatterData
  774.  
  775.     END IF
  776.  
  777.     ' Deallocate the dynamic working data arrays:
  778.     ERASE V1, V2
  779.  
  780. END SUB
  781.  
  782. '=== ChartScatterMS - Draws a multiple-series scatter chart
  783. '
  784. '  Arguments:
  785. '     Env            - A ChartEnvironment variable
  786. '
  787. '     ValX(2)        - Two-dimensional array of values for X axis
  788. '
  789. '     ValY(2)        - Two-dimensional array of values for Y axis
  790. '
  791. '     N%             - The number of values in each series
  792. '
  793. '     First%         - First series to chart (first column)
  794. '
  795. '     Last%          - Last series to chart (last column)
  796. '
  797. '     SeriesLabel$() - Label used for each series in legend
  798. '
  799. '
  800. '  Return Values:
  801. '     Some elements in Env variable may be changed by drawing routines
  802. '
  803. '  Remarks:
  804. '     A scatter chart uses two value axes so it must have values for both
  805. '     the X and Y axes (ValX(), ValY()).  The first dimension denotes
  806. '     the different values within a series.  The second dimension specifies
  807. '     different data series (e.g. ValX(4,3) would represent the fourth value
  808. '     in the third series of data).
  809. '
  810. '=================================================================
  811. SUB ChartScatterMS (Env AS ChartEnvironment, ValX() AS SINGLE, ValY() AS SINGLE, N AS INTEGER, First AS INTEGER, Last AS INTEGER, SeriesLabel$())
  812. DIM Dum$(1 TO 1)
  813. SHARED V1(), V2()
  814.  
  815.     ' Set the global system flag to tell the AnalyzeScatterMS routine that it
  816.     ' is being called by the system and not the user:
  817.     clFlagSystem
  818.  
  819.     ' Calculate the scale maximums and minimums and scale factor. Also
  820.     ' calculate the sizes for the Data- and Legend-windows:
  821.     AnalyzeScatterMS Env, ValX(), ValY(), N, First, Last, SeriesLabel$()
  822.  
  823.     ' Remove the system flag:
  824.     clUnFlagSystem
  825.  
  826.     ' If there were no errors during analysis draw the chart:
  827.     IF ChartErr < 100 THEN
  828.  
  829.         ' Draw the different elements of the chart:
  830.         clDrawChartWindow
  831.         clDrawTitles
  832.         clDrawDataWindow
  833.         clDrawAxes Dum$()
  834.         clDrawScatterData
  835.         clDrawLegend SeriesLabel$(), First, Last
  836.  
  837.     END IF
  838.  
  839.     ' Deallocate the dynamic working data arrays:
  840.     ERASE V1, V2
  841.  
  842. END SUB
  843.  
  844. '=== ChartScreen - Sets the SCREEN mode and default palettes
  845. '
  846. '  Arguments:
  847. '     N%    - A valid BASIC graphic mode, or mode 0
  848. '
  849. '  Return Values:
  850. '     All palettes may be altered
  851. '
  852. '=================================================================
  853. SUB ChartScreen (N AS INTEGER)
  854. SHARED GP AS GlobalParams
  855.  
  856.     ' Check initialization and fonts:
  857.     clClearError
  858.     clChkInit
  859.  
  860.     ' Set up branch to error processor and attempt to set the specified
  861.     ' screen mode and draw to it:
  862.     ON ERROR GOTO ScreenErr
  863.     SCREEN N
  864.     IF N <> 0 THEN PRESET (0, 0)
  865.     ON ERROR GOTO UnexpectedErr
  866.  
  867.     ' If the above PRESET failed, then the TestScreen error processor will
  868.     ' have set the ChartErr error variable to a nonzero value.  If the last
  869.     ' call to ChartScreen used the same mode, GP.PaletteScrn will equal N; and
  870.     ' there is no need to rebuild palettes.  In either case there is no need
  871.     ' to do anything else, so exit:
  872.     IF ChartErr <> 0 OR (GP.PaletteScrn = N AND GP.PaletteSet) THEN EXIT SUB
  873.   
  874.     ' This is a new screen mode so use the SELECT CASE statement below
  875.     ' to handle it.  It sets the number of bits per pixel for a screen
  876.     ' mode so that the palettes can be built properly:
  877.     SELECT CASE N
  878.  
  879.         ' Screen mode 0 is not a graphics mode and is included mainly for
  880.         ' completeness.  The actual screen mode has been set above, so exit:
  881.         CASE 0:
  882.             EXIT SUB
  883.  
  884.         CASE 1:  Bits% = 2
  885.         CASE 2:  Bits% = 1
  886.         CASE 3:  Bits% = 1
  887.         CASE 4:  Bits% = 1
  888.         CASE 7:  Bits% = 4
  889.         CASE 8:  Bits% = 4
  890.         CASE 9:
  891.                     ' For screen mode 9, assume a 256K EGA and try setting
  892.                     ' a color to 63.  If that fails, assume it is a 64K EGA
  893.                     ' (the number of bit planes is four for 256K and two for
  894.                     ' 64K):
  895.                     Bits% = 4
  896.                     ON ERROR GOTO ScreenErr
  897.                     clClearError
  898.                     COLOR 15
  899.                     IF ChartErr <> 0 THEN Bits% = 2
  900.                     clClearError
  901.                     ON ERROR GOTO UnexpectedErr
  902.  
  903.         CASE 10: Bits% = 2
  904.         CASE 11: Bits% = 1
  905.         CASE 12: Bits% = 4
  906.         CASE 13: Bits% = 8
  907.  
  908.         ' If none of the above match then a valid screen mode was specified;
  909.         ' however the mode is un-supported so set error and exit:
  910.         CASE ELSE: clSetError cBadScreen
  911.                       EXIT SUB
  912.     END SELECT
  913.  
  914.     ' The screen aspect is 4/3 * MaxY/MaxX:
  915.     VIEW
  916.     WINDOW (0, 0)-(1, 1)
  917.     GP.MaxXPix% = PMAP(1, 0) + 1
  918.     GP.MaxYPix% = PMAP(0, 1) + 1
  919.     GP.Aspect = 1.33333 * (GP.MaxYPix% - 1) / (GP.MaxXPix% - 1)
  920.     WINDOW
  921.  
  922.     ' The number of colors available:
  923.     GP.MaxColor = 2 ^ Bits% - 1
  924.   
  925.     ' Specify which color to use for white:
  926.     SELECT CASE N
  927.         CASE 13: GP.White = 15
  928.         CASE ELSE: GP.White = GP.MaxColor
  929.     END SELECT
  930.  
  931.     ' Build palette for this screen mode:
  932.     clBuildPalette N, Bits%
  933.  
  934. END SUB
  935.  
  936. '=== clAdjustScale - Calculates scaling factor for an axis and adjusts max-min
  937. '                  as appropriate for scale factor and log base if log axis:
  938. '
  939. '  Arguments:
  940. '     Axis  -  AxisType variable describing axis to be scaled.
  941. '
  942. '  Return Values:
  943. '     May set the ScaleFactor and ScaleTitle elements and alter
  944. '     ScaleMin and ScaleMax elements of the Axis variable.
  945. '
  946. '=================================================================
  947. SUB clAdjustScale (Axis AS AxisType)
  948.  
  949.     ' Don't try to scale a log axis:
  950.     IF Axis.RangeType = cLog THEN
  951.  
  952.         Axis.ScaleFactor = 1
  953.         Axis.ScaleTitle.Title = "Log" + STR$(Axis.LogBase)
  954.  
  955.     ' For a linear axis, choose a scale factor up to Trillions depending
  956.     ' on the size of the axis limits:
  957.     ELSE
  958.  
  959.         ' Choose the largest ABS from Max and Min for the axis:
  960.         IF ABS(Axis.ScaleMax) > ABS(Axis.ScaleMin) THEN
  961.             Max = ABS(Axis.ScaleMax)
  962.         ELSE
  963.             Max = ABS(Axis.ScaleMin)
  964.         END IF
  965.  
  966.         ' Find out power of three by which to scale:
  967.         Power% = INT((LOG(Max) / LOG(10)) / 3)
  968.  
  969.         ' And, choose the correct title to go with it:
  970.         SELECT CASE Power%
  971.             CASE -4:     Axis.ScaleTitle.Title = "Trillionths"
  972.             CASE -3:     Axis.ScaleTitle.Title = "Billionths"
  973.             CASE -2:     Axis.ScaleTitle.Title = "Millionths"
  974.             CASE -1:     Axis.ScaleTitle.Title = "Thousandths"
  975.             CASE 0:     Axis.ScaleTitle.Title = ""
  976.             CASE 1:     Axis.ScaleTitle.Title = "Thousands"
  977.             CASE 2:     Axis.ScaleTitle.Title = "Millions"
  978.             CASE 3:     Axis.ScaleTitle.Title = "Billions"
  979.             CASE 4:     Axis.ScaleTitle.Title = "Trillions"
  980.             CASE ELSE:  Axis.ScaleTitle.Title = "10^" + LTRIM$(STR$(Power% * 3))
  981.         END SELECT
  982.  
  983.         ' Calculate the actual scale factor:
  984.         Axis.ScaleFactor = 10 ^ (3 * Power%)
  985.  
  986.         ' Finally, scale Max and Min by ScaleFactor:
  987.         Axis.ScaleMin = Axis.ScaleMin / Axis.ScaleFactor
  988.         Axis.ScaleMax = Axis.ScaleMax / Axis.ScaleFactor
  989.  
  990.     END IF
  991.  
  992. END SUB
  993.  
  994. '=== clAnalyzeC - Does analysis of category/value data
  995. '
  996. '  Arguments:
  997. '     Cat$(1)     -  List of category names
  998. '
  999. '     N%          -  Number of data values per series
  1000. '
  1001. '     SLabels$    -  Labels for the different data series
  1002. '
  1003. '     First%      -  First series to chart
  1004. '
  1005. '     Last%       -  Last series to chart
  1006. '
  1007. '  Return Values:
  1008. '     Some values in GE are altered.
  1009. '
  1010. '=================================================================
  1011. SUB clAnalyzeC (Cat$(), N%, SLabels$(), First%, Last%)
  1012. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  1013. SHARED TTitleLayout AS TitleLayout
  1014. SHARED XTitleLayout AS TitleLayout
  1015. SHARED YTitleLayout AS TitleLayout
  1016. SHARED V1()
  1017.  
  1018.     ' Save the number of values and the number of series in the chart in
  1019.     ' the global parameter variables:
  1020.     GP.NVals = N%
  1021.     GP.NSeries = Last% - First% + 1
  1022.  
  1023.     ' Analyze data for scale-maximim and -minimum and scale-factor:
  1024.     clScaleAxis GE.XAxis, GP.XMode, V1()
  1025.     IF ChartErr > 100 THEN EXIT SUB
  1026.  
  1027.     clScaleAxis GE.YAxis, GP.YMode, V1()
  1028.     IF ChartErr > 100 THEN EXIT SUB
  1029.  
  1030.     ' Format tic labels (needed for sizing routines) and set global
  1031.     ' parameters (again used by sizing and other routines):
  1032.     clFormatTics GE.XAxis
  1033.     clFormatTics GE.YAxis
  1034.     clSetGlobalParams
  1035.  
  1036.     ' Layout Titles
  1037.     clLayoutTitle TTitleLayout, GE.MainTitle, GE.SubTitle
  1038.     clLayoutTitle YTitleLayout, GE.YAxis.AxisTitle, GE.YAxis.ScaleTitle
  1039.     clLayoutTitle XTitleLayout, GE.XAxis.AxisTitle, GE.XAxis.ScaleTitle
  1040.  
  1041.     ' If this is a multiple-series chart, calculate the legend size:
  1042.     IF GP.MSeries = cYes THEN clLayoutLegend SLabels$(), First%, Last%
  1043.     IF ChartErr > 100 THEN EXIT SUB
  1044.  
  1045.     ' Calculate the data-window size:
  1046.     clSizeDataWindow Cat$()
  1047.     IF ChartErr > 100 THEN EXIT SUB
  1048.  
  1049.     ' Finally, figure out the distance between tic marks:
  1050.     clSpaceTics
  1051.  
  1052. END SUB
  1053.  
  1054. '=== clAnalyzeS - Does actual analysis of scatter data
  1055. '
  1056. '  Arguments:
  1057. '     N%          -  Number of values per data series
  1058. '
  1059. '     SLabels$(1) -  Labels for the data series
  1060. '
  1061. '     First%      -  First series to analyze
  1062. '
  1063. '     Last%       -  Last series to analyze
  1064. '
  1065. '  Return Values:
  1066. '     Values in GE are altered.
  1067. '
  1068. '=================================================================
  1069. SUB clAnalyzeS (N%, SLabels$(), First%, Last%)
  1070. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  1071. SHARED TTitleLayout AS TitleLayout
  1072. SHARED XTitleLayout AS TitleLayout
  1073. SHARED YTitleLayout AS TitleLayout
  1074. SHARED V1(), V2()
  1075. DIM Dum$(1 TO 1)
  1076.  
  1077.     ' Save the number of values and the number of series in the chart in
  1078.     ' the global parameter variables:
  1079.     GP.NVals = N%
  1080.     GP.NSeries = Last% - First% + 1
  1081.  
  1082.     ' Analyze data for scale-maximim and -minimum and scale-factor:
  1083.     clScaleAxis GE.XAxis, GP.XMode, V1()
  1084.     IF ChartErr > 100 THEN EXIT SUB
  1085.  
  1086.     clScaleAxis GE.YAxis, GP.YMode, V2()
  1087.     IF ChartErr > 100 THEN EXIT SUB
  1088.  
  1089.     ' Format tic labels (needed for sizing routines) and set global
  1090.     ' parameters (again used by sizing and other routines):
  1091.     clFormatTics GE.XAxis
  1092.     clFormatTics GE.YAxis
  1093.     clSetGlobalParams
  1094.   
  1095.     ' Layout Titles
  1096.     clLayoutTitle TTitleLayout, GE.MainTitle, GE.SubTitle
  1097.     clLayoutTitle YTitleLayout, GE.YAxis.AxisTitle, GE.YAxis.ScaleTitle
  1098.     clLayoutTitle XTitleLayout, GE.XAxis.AxisTitle, GE.XAxis.ScaleTitle
  1099.  
  1100.     ' If this is a multiple-series chart, calculate the legend size:
  1101.     IF GP.MSeries = cYes THEN clLayoutLegend SLabels$(), First%, Last%
  1102.     IF ChartErr > 100 THEN EXIT SUB
  1103.  
  1104.     ' Calculate the data window size:
  1105.     clSizeDataWindow Dum$()
  1106.     IF ChartErr > 100 THEN EXIT SUB
  1107.  
  1108.     ' Finally, figure out the distance between tic marks:
  1109.     clSpaceTics
  1110.  
  1111. END SUB
  1112.  
  1113. '=== clBuildBitP$ - Builds a pattern tile for a one bit-plane screen mode
  1114. '
  1115. '  Arguments:
  1116. '     Bits%    =  Number of bits per pixel in this screen mode
  1117. '
  1118. '     C%       =  The color used to make the pattern.
  1119. '
  1120. '     InP$     =  Reference pattern
  1121. '
  1122. '  Return Values:
  1123. '     Returns the specified pattern in specified color.
  1124. '
  1125. '  Remarks:
  1126. '     In screen modes where a pixel on the screen is represented by 1 or
  1127. '     more bits that are adjacent in memory, a byte of memory represents
  1128. '     one or more pixels depending on the number of bits per pixel the
  1129. '     mode uses (e.g. screen mode 1 uses 2 bits per pixel so each byte
  1130. '     contains 4 pixels).  To make a pattern tile in a specific color
  1131. '     you first decide which pixels should be on and which ones off.
  1132. '     Then, you set the corresponding two-bit pixels in the tile bytes
  1133. '     to the value of the color you want the pattern to be.  This routine
  1134. '     does this semi-automatically.  First it inputs a reference pattern that
  1135. '     contains the pattern defined in the highest color available for a
  1136. '     screen mode (all bits in a pixel set to one).  Then a color mask byte
  1137. '     is prepared with each pixel set to the color that was specified as
  1138. '     input to the routine.  When these two components (reference pattern
  1139. '     and color mask) are combined using a logical "AND" any pixel in the
  1140. '     reference pattern that was black (all zero) will remain black and any
  1141. '     pixel that was white will be of the input color.  The nice feature of
  1142. '     this scheme is that you can use one pattern set for any color
  1143. '     available for the screen mode.
  1144. '
  1145. '     Example: Screen mode 1; 2 bits per pixel; to build a pattern
  1146. '              with pixels alternating on and off in color 2:
  1147. '
  1148. '     Reference pattern:   11 00 11 00    (8 bits = 1 byte)
  1149. '     Color mask:          10 10 10 10    (each pixel set to color 2)
  1150. '                         -------------
  1151. '     Result of "AND"      10 00 10 00    (pattern in color 2)
  1152. '
  1153. '=================================================================
  1154. FUNCTION clBuildBitP$ (Bits%, c%, InP$)
  1155.  
  1156.     ' First get color mask to match this color and pixel size (bits per pixel):
  1157.     CMask% = clColorMaskL%(Bits%, c%)
  1158.   
  1159.     ' Initialize the output pattern to empty then combine the color
  1160.     ' mask with each byte in the input tile using a logical "AND":
  1161.     OutP$ = ""
  1162.     FOR i% = 1 TO LEN(InP$)
  1163.         NxtCH% = CMask% AND ASC(MID$(InP$, i%, 1))
  1164.         OutP$ = OutP$ + CHR$(NxtCH%)
  1165.     NEXT i%
  1166.  
  1167.     ' Return the completed pattern:
  1168.     clBuildBitP$ = OutP$
  1169.  
  1170. END FUNCTION
  1171.  
  1172. '=== clBuildPalette - Builds the five chart palettes
  1173. '
  1174. '  Arguments:
  1175. '     N           -  Screen mode for which to build palettes
  1176. '
  1177. '  Return Values:
  1178. '     Values in chart palettes set to standard ones for this mode
  1179. '
  1180. '  Remarks:
  1181. '     The following code sets up the palettes that are referenced when the
  1182. '     different chart elements are drawn.  See the charting library
  1183. '     documentation for a complete description of how these palettes are
  1184. '     used in drawing different portions of a chart.
  1185. '
  1186. '=================================================================
  1187. SUB clBuildPalette (ScrnMode AS INTEGER, Bits AS INTEGER)
  1188. SHARED PaletteC%(), PaletteS%(), PaletteP$(), PaletteCh%(), PaletteB%()
  1189. SHARED StdChars%()
  1190. SHARED GP AS GlobalParams
  1191.  
  1192.     ' Flag palette set and record the screen mode:
  1193.     GP.PaletteSet = cYes
  1194.     GP.PaletteScrn = ScrnMode
  1195.     GP.PaletteBits = Bits
  1196.  
  1197.     ' The first palettes to set are the character palette and the border
  1198.     ' style palette:
  1199.     PaletteCh%(0) = 0
  1200.     PaletteB%(0) = &HFFFF
  1201.     FOR i% = 1 TO cPalLen
  1202.         PaletteCh%(i%) = StdChars%(i%)
  1203.         PaletteB%(i%) = clGetStyle(i%)
  1204.     NEXT i%
  1205.  
  1206.     ' The next palette to set is the color palette, which is made up of
  1207.     ' a list of 10 (maybe repeating) colors.  Begin by setting the first
  1208.     ' two colors.  The first color (position 0) is always black and the
  1209.     ' second color is always white (or whatever the maximum color number
  1210.     ' is mapped to in the graphics-card palette).  Cycle through setting
  1211.     ' other colors.  They will be entered in order starting with color 1
  1212.     ' until the maximum number of colors is reached or the palette is filled
  1213.     ' (size governed by the cPalLen CONST).  If the maximum color is reached
  1214.     ' before the palette is filled then repeat the cycle again excluding
  1215.     ' color 0, and so on, until the color palette is filled:
  1216.  
  1217.     PaletteC%(0) = 0        ' Black
  1218.     PaletteC%(1) = GP.White ' White
  1219.  
  1220.     FOR i% = 2 TO cPalLen
  1221.         MappedI% = ((i% - 2) MOD GP.MaxColor) + 1
  1222.         PaletteC%(i%) = MappedI%
  1223.     NEXT i%
  1224.  
  1225.     ' Setting the line styles is almost the inverse of setting the colors
  1226.     ' in that each color within a cycle has the same line style.  When a
  1227.     ' new cycle of colors begins, though, the line style changes to
  1228.     ' differentiate the new cycle from previous ones.  The line style
  1229.     ' begins as &HFFFF or a solid line:
  1230.  
  1231.     ' The pattern component of the palette contains fill patterns for use in
  1232.     ' filling bars and pie slices.  Fill patterns are "bit" oriented whereas
  1233.     ' line styles are "pixel" oriented.  What this means is that a fill
  1234.     ' pattern of CHR$(&HFF) will be white regardless of what the current
  1235.     ' color is.  If you know that each pixel on the screen is represented by
  1236.     ' 2 bits in RAM and you want a solid fill with color 2, the corresponding
  1237.     ' definition would be CHR$(&HAA) (in binary 10 10 10 10 -- notice, four
  1238.     ' pixels of two bits each set to 2).  The following code automatically
  1239.     ' takes a fill pattern defined in terms of pixels, and by masking it
  1240.     ' with the current color generates the same fill pattern in the
  1241.     ' specified color.  Start with solid black (color 0):
  1242.  
  1243.     PaletteS%(0) = &HFFFF
  1244.     PaletteP$(0) = CHR$(0)
  1245.  
  1246.     FOR i% = 1 TO cPalLen
  1247.  
  1248.         ' The cycle number starts at one and is incremented each time
  1249.         ' the maximum number of colors for the current screen mode is reached:
  1250.         Cycle% = ((i% - 1) \ GP.MaxColor) + 1
  1251.  
  1252.         ' Set the style palette from the standard styles (which have
  1253.         ' previously been placed in the border palette):
  1254.         PaletteS%(i%) = PaletteB%(Cycle%)
  1255.       
  1256.         ' Get the default pattern and put it into the palette:
  1257.         SELECT CASE ScrnMode
  1258.     
  1259.             ' One bit plane modes:
  1260.             CASE 1, 2, 11, 13: RefPattern$ = GetPattern$(Bits, Cycle%)
  1261.           
  1262.             ' Multiple bit plane modes:
  1263.             CASE ELSE: RefPattern$ = GetPattern$(1, Cycle%)
  1264.  
  1265.         END SELECT
  1266.         PaletteP$(i%) = MakeChartPattern$(RefPattern$, PaletteC%(i%), 0)
  1267.      
  1268.     NEXT i%
  1269.  
  1270. END SUB
  1271.  
  1272. '=== clBuildPlaneP$ - Builds a pattern tile for multiple bit-plane screen modes
  1273. '
  1274. '  Arguments:
  1275. '     Bits%    =  Number of planes in this screen mode
  1276. '
  1277. '     C%       =  The color used to make the pattern
  1278. '
  1279. '     InP$     =  Reference pattern
  1280. '
  1281. '  Return Values:
  1282. '     Returns the specified pattern in specified color
  1283. '
  1284. '  Remarks:
  1285. '     PAINT tiles are different for screen modes that use 2 or more
  1286. '     bit-planes than for the modes that use only one (see remarks for
  1287. '     clBuildBitP$()).  When bit-planes are used each pixel requires only
  1288. '     one bit per byte, but, there needs to be one byte for each bit-
  1289. '     plane.  The process for building a pattern from a reference pattern
  1290. '     and color mask are logically the same as in the one bit-plane modes
  1291. '     the only difference is that a color mask requires several bytes
  1292. '     (one for each bit-plane) rather than one.
  1293. '
  1294. '  Example: Screen mode 9 with 2 bit planes; pattern with alternating
  1295. '           pixels on and off; color 2:
  1296. '
  1297. '           Reference pattern:   1 0 1 0 1 0 1 0
  1298. '           Color mask:          0 0 0 0 0 0 0 0   (plane 1)
  1299. '                                1 1 1 1 1 1 1 1   (plane 2)
  1300. '                               -----------------
  1301. '           Result of "AND"      0 0 0 0 0 0 0 0   (plane 1)
  1302. '                                1 0 1 0 1 0 1 0   (plane 2)
  1303. '
  1304. '
  1305. '=================================================================
  1306. FUNCTION clBuildPlaneP$ (Bits%, c%, InP$)
  1307. DIM CMask%(1 TO 4)
  1308.  
  1309.     ' First get color mask to match this color and pixel size (bits per pixel):
  1310.     clColorMaskH Bits%, c%, CMask%()
  1311.  
  1312.     ' Initialize the output pattern to empty then combine the color
  1313.     ' mask with each byte in the input tile using a logical "AND":
  1314.     OutP$ = ""
  1315.     FOR TileByte% = 1 TO LEN(InP$)
  1316.         RefTile% = ASC(MID$(InP$, TileByte%, 1))
  1317.  
  1318.         ' Combine each bit-plane in the color mask with the pattern byte:
  1319.         FOR Plane% = 1 TO Bits%
  1320.             OutP$ = OutP$ + CHR$(RefTile% AND CMask%(Plane%))
  1321.         NEXT Plane%
  1322.     NEXT TileByte%
  1323.  
  1324.     ' Return the completed pattern:
  1325.     clBuildPlaneP$ = OutP$
  1326.   
  1327. END FUNCTION
  1328.  
  1329. '=== clChkChartWindow - Makes sure the chart window is valid
  1330. '
  1331. '  Arguments:
  1332. '     Env   -  A ChartEnvironment variable
  1333. '
  1334. '  Return Values:
  1335. '     Changes global parameters for chart window
  1336. '
  1337. '  Remarks:
  1338. '     This routine forces the chart window to be valid.  If the input
  1339. '     values are invalid a full screen is chosen.  The valid chart window
  1340. '     is stored in the global parameter set and used by other charting
  1341. '     routines.  The last valid screen set by ChartScreen is used as
  1342. '     reference.
  1343. '
  1344. '=================================================================
  1345. SUB clChkChartWindow (Env AS ChartEnvironment)
  1346. SHARED GP AS GlobalParams
  1347.  
  1348.     ' Make sure X1 < X2:
  1349.     IF Env.ChartWindow.X1 < Env.ChartWindow.X2 THEN
  1350.         GP.CwX1 = Env.ChartWindow.X1
  1351.         GP.CwX2 = Env.ChartWindow.X2
  1352.     ELSE
  1353.         GP.CwX1 = Env.ChartWindow.X2
  1354.         GP.CwX2 = Env.ChartWindow.X1
  1355.     END IF
  1356.  
  1357.     ' Make sure Y1 < Y2:
  1358.     IF Env.ChartWindow.Y1 < Env.ChartWindow.Y2 THEN
  1359.         GP.CwY1 = Env.ChartWindow.Y1
  1360.         GP.CwY2 = Env.ChartWindow.Y2
  1361.     ELSE
  1362.         GP.CwY1 = Env.ChartWindow.Y2
  1363.         GP.CwY2 = Env.ChartWindow.Y1
  1364.     END IF
  1365.  
  1366.     ' If the X coordinates of the chart window are invalid,
  1367.     ' set them to full screen:
  1368.     IF GP.CwX1 < 0 OR GP.CwX2 >= GP.MaxXPix OR GP.CwX1 = GP.CwX2 THEN
  1369.         GP.CwX1 = 0
  1370.         GP.CwX2 = GP.MaxXPix - 1
  1371.     END IF
  1372.  
  1373.     ' If the Y coordinates of the chart window are invalid,
  1374.     ' set them to full screen:
  1375.     IF GP.CwY1 < 0 OR GP.CwY2 >= GP.MaxYPix OR GP.CwY1 = GP.CwY2 THEN
  1376.         GP.CwY1 = 0
  1377.         GP.CwY2 = GP.MaxYPix - 1
  1378.     END IF
  1379.  
  1380.     ' Set chart height and width for use later:
  1381.     GP.ChartWid = GP.CwX2 - GP.CwX1 + 1
  1382.     GP.ChartHgt = GP.CwY2 - GP.CwY1 + 1
  1383.  
  1384.     ' Put the valid coordinates in Env:
  1385.     Env.ChartWindow.X1 = GP.CwX1
  1386.     Env.ChartWindow.Y1 = GP.CwY1
  1387.     Env.ChartWindow.X2 = GP.CwX2
  1388.     Env.ChartWindow.Y2 = GP.CwY2
  1389.  
  1390. END SUB
  1391.  
  1392. '=== clChkFonts - Checks that there is at least one loaded font
  1393. '
  1394. '  Arguments:
  1395. '     none
  1396. '
  1397. '  Return Values:
  1398. '     Chart error set if no room for a font
  1399. '
  1400. '=================================================================
  1401. SUB clChkFonts ()
  1402.  
  1403.     ' See if a font is loaded:
  1404.     GetTotalFonts Reg%, LoadVar%
  1405.   
  1406.     ' If not then find out the maximum number of fonts allowed and if
  1407.     ' there's room, then load the default font:
  1408.     IF LoadVar% <= 0 THEN
  1409.         GetMaxFonts MReg%, MLoad%
  1410.         IF Reg% < MReg% AND LoadVar% < MLoad% THEN
  1411.             DefaultFont Segment%, Offset%
  1412.             FontNum% = RegisterMemFont(Segment%, Offset%)
  1413.             FontNum% = LoadFont("N" + STR$(LoadVar% + 1))
  1414.       
  1415.         ' If there's no room, then set an error:
  1416.         ELSE
  1417.             clSetError cNoFontSpace
  1418.         END IF
  1419.     END IF
  1420. END SUB
  1421.  
  1422. '=== CheckForErrors - Checks for and tries to fix a variety of errors
  1423. '
  1424. '  Arguments:
  1425. '     Env      -  ChartEnvironment variable
  1426. '
  1427. '     TypeMin% -  Minimum allowable ChartType
  1428. '
  1429. '     TypeMax% -  Maximum allowable ChartType
  1430. '
  1431. '     N%       -  Number of data values per series
  1432. '
  1433. '     First%   -  Column of data representing first series
  1434. '
  1435. '     Last%    -  Column of data representing last series
  1436. '
  1437. '  Return Values:
  1438. '     This routine is the main one that checks for errors of input in
  1439. '     the ChartEnvironment variable and routine parameters.
  1440. '
  1441. '=================================================================
  1442. SUB clChkForErrors (Env AS ChartEnvironment, TypeMin%, TypeMax%, N%, First%, Last%)
  1443.  
  1444.     ' Clear any previous error:
  1445.     clClearError
  1446.  
  1447.     ' Check for correct chart type:
  1448.     IF Env.ChartType < TypeMin% OR Env.ChartType > TypeMax% THEN
  1449.         clSetError cBadType
  1450.         EXIT SUB
  1451.     END IF
  1452.  
  1453.     ' Check for valid chart style:
  1454.     IF Env.ChartStyle < 1 OR Env.ChartStyle > 2 THEN
  1455.         clSetError cBadStyle
  1456.         Env.ChartStyle = 1
  1457.     END IF
  1458.  
  1459.     ' The following things are not relevant for pie charts:
  1460.     IF Env.ChartType <> cPie THEN
  1461.  
  1462.         ' Check LogBase for the X axis (default to 10):
  1463.         IF Env.XAxis.RangeType = cLog AND Env.XAxis.LogBase <= 0 THEN
  1464.             clSetError cBadLogBase
  1465.             Env.XAxis.LogBase = 10
  1466.         END IF
  1467.  
  1468.         ' Check LogBase for the Y axis (default to 10):
  1469.         IF Env.YAxis.RangeType = cLog AND Env.YAxis.LogBase <= 0 THEN
  1470.             clSetError cBadLogBase
  1471.             Env.YAxis.LogBase = 10
  1472.         END IF
  1473.  
  1474.         ' Check X axis ScaleFactor:
  1475.         IF Env.XAxis.AutoScale <> cYes AND Env.XAxis.ScaleFactor = 0 THEN
  1476.             clSetError cBadScaleFactor
  1477.             Env.XAxis.ScaleFactor = 1
  1478.         END IF
  1479.  
  1480.         ' Check Y axis ScaleFactor:
  1481.         IF Env.YAxis.AutoScale <> cYes AND Env.YAxis.ScaleFactor = 0 THEN
  1482.             clSetError cBadScaleFactor
  1483.             Env.YAxis.ScaleFactor = 1
  1484.         END IF
  1485.     END IF
  1486.  
  1487.     ' Make sure N > 0:
  1488.     IF N% <= 0 THEN
  1489.         clSetError cTooSmallN
  1490.         EXIT SUB
  1491.     END IF
  1492.  
  1493.     ' Check that First series <= Last one:
  1494.     IF First% > Last% THEN
  1495.         clSetError cTooFewSeries
  1496.         EXIT SUB
  1497.     END IF
  1498.  
  1499.     ' Force ChartWindow to be valid:
  1500.     clChkChartWindow Env
  1501.  
  1502. END SUB
  1503.  
  1504. '=== clChkInit - Check that chartlib has been initialized
  1505. '
  1506. '  Arguments:
  1507. '     none
  1508. '
  1509. '  Return Values:
  1510. '     none
  1511. '
  1512. '=================================================================
  1513. SUB clChkInit ()
  1514. SHARED GP AS GlobalParams
  1515.  
  1516.     IF NOT GP.Initialized THEN clInitChart
  1517.  
  1518. END SUB
  1519.  
  1520. '=== clChkPalettes - Makes sure that palettes are dimensioned correctly
  1521. '
  1522. '  Arguments:
  1523. '     C%()     -  Color palette array
  1524. '
  1525. '     S%()     -  Style palette array
  1526. '
  1527. '     P$()     -  Pattern palette array
  1528. '
  1529. '     Char%()  -  Plot character palette array
  1530. '
  1531. '     B%()     -  Border pattern palette array
  1532. '
  1533. '  Return Values:
  1534. '     Chart error may be set to cBadPalette
  1535. '
  1536. '=================================================================
  1537. SUB clChkPalettes (c() AS INTEGER, s() AS INTEGER, P$(), Char() AS INTEGER, B() AS INTEGER)
  1538.  
  1539.     ' Check each palette array to be sure it is dimensioned from 0
  1540.     ' to cPalLen:
  1541.     FOR i% = 1 TO 5
  1542.         SELECT CASE i%
  1543.             CASE 1:  L% = LBOUND(c, 1): U% = UBOUND(c, 1)
  1544.             CASE 2:  L% = LBOUND(s, 1): U% = UBOUND(s, 1)
  1545.             CASE 3:  L% = LBOUND(P$, 1): U% = UBOUND(P$, 1)
  1546.             CASE 4:  L% = LBOUND(Char, 1): U% = UBOUND(Char, 1)
  1547.             CASE 5:  L% = LBOUND(B, 1): U% = UBOUND(B, 1)
  1548.         END SELECT
  1549.  
  1550.         ' If incorrectly dimensioned then set error:
  1551.         IF (L% <> 0) OR (U% <> cPalLen) THEN
  1552.             clSetError cBadPalette
  1553.             EXIT SUB
  1554.         END IF
  1555.     NEXT i%
  1556.  
  1557. END SUB
  1558.  
  1559. '=== clClearError - Clears ChartErr, the ChartLib error variable
  1560. '
  1561. '  Arguments:
  1562. '     None
  1563. '
  1564. '  Return Values:
  1565. '     Sets ChartErr to 0
  1566. '
  1567. '=================================================================
  1568. SUB clClearError ()
  1569.  
  1570.     ChartErr = 0
  1571.  
  1572. END SUB
  1573.  
  1574. '=== clColorMaskH% - Function to generate a byte with each pixel set to
  1575. '                  some color - for high-res modes (7,8,9,10)
  1576. '
  1577. '  Arguments:
  1578. '     Bits%    -  Number of bits per pixel in current screen mode
  1579. '
  1580. '     Colr%    -  Color to make the mask
  1581. '
  1582. '     CMask%() -  One dimensional array to place mask values in
  1583. '
  1584. '  Return Values:
  1585. '     Screen modes 7, 8, 9 and 10 use bit planes.  Rather than using
  1586. '     adjacent bits in one byte to determine a color, they use bits
  1587. '     "stacked" on top of each other in different bytes.  This routine
  1588. '     generates one byte of a particular color by setting the different
  1589. '     levels of the stack to &H00 and &HFF to represent eight pixels
  1590. '     of a particular color.
  1591. '
  1592. '=================================================================
  1593. SUB clColorMaskH (Bits%, Colr%, CMask%())
  1594.  
  1595.     ' Copy the color to a local variable:
  1596.     RefColor% = Colr%
  1597.  
  1598.     ' Bits% is the number of bit planes, set a mask for each one:
  1599.     FOR i% = 1 TO Bits%
  1600.  
  1601.         ' Check rightmost bit in color, if it is set to 1 then this plane is
  1602.         ' "on" (it equals &HFF):
  1603.         IF RefColor% MOD 2 <> 0 THEN
  1604.             CMask%(i%) = &HFF
  1605.  
  1606.         ' If the bit is 0, the plane is off (it equals &H0):
  1607.         ELSE
  1608.             CMask%(i%) = &H0
  1609.         END IF
  1610.  
  1611.         ' Shift the reference color right by one bit:
  1612.         RefColor% = RefColor% \ 2
  1613.     NEXT i%
  1614.             
  1615. END SUB
  1616.  
  1617. '=== clColorMaskL% - Function to generate a byte with each pixel set to
  1618. '                 some color.
  1619. '
  1620. '  Arguments:
  1621. '     Bits%    -  Number of bits per pixel in current screen mode
  1622. '
  1623. '     Colr%    -  Color to make the mask
  1624. '
  1625. '  Return Values:
  1626. '     Returns integer with low byte that contains definitions for
  1627. '     pixels of specified color.
  1628. '
  1629. '=================================================================
  1630. FUNCTION clColorMaskL% (Bits%, Colr%)
  1631.  
  1632.     ' Initialize the mask to zero:
  1633.     M% = 0
  1634.  
  1635.     ' Multiplying a number by (2 ^ Bits%) will shift it left by "Bits%" bits:
  1636.     LShift% = 2 ^ Bits%
  1637.  
  1638.     ' Create a byte in which each pixel (of "Bits%" bits) is set to
  1639.     ' Colr%.  This is done by setting the mask to "Colr%" then shifting
  1640.     ' it left by "Bits%" and repeating until the byte is full:
  1641.     FOR i% = 0 TO 7 \ Bits%
  1642.         M% = M% * LShift% + Colr%
  1643.     NEXT i%
  1644.  
  1645.     ' Return the mask as the value of the function:
  1646.     clColorMaskL% = M% MOD 256
  1647.  
  1648. END FUNCTION
  1649.  
  1650. '=== clDrawAxes - Draws the axes for a chart
  1651. '
  1652. '  Arguments:
  1653. '     Cat$(1)  -  One-dimensional array or category names for use in
  1654. '                 labeling the category axis (ignored if category
  1655. '                 axis not used)
  1656. '
  1657. '  Return Values:
  1658. '     No return values
  1659. '
  1660. '=================================================================
  1661. SUB clDrawAxes (Cat$())
  1662. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  1663. SHARED GFI AS FontInfo
  1664. SHARED PaletteC%(), PaletteB%()
  1665.  
  1666.     ' Use temporary variables to refer to axis limits:
  1667.     X1 = GE.XAxis.ScaleMin
  1668.     X2 = GE.XAxis.ScaleMax
  1669.     Y1 = GE.YAxis.ScaleMin
  1670.     Y2 = GE.YAxis.ScaleMax
  1671.  
  1672.     ' To draw the tic/grid lines it is necessary to know where the line
  1673.     ' starts and ends.  If tic marks are specified (by setting
  1674.     ' the "labeled" flag in the axis definition) then the
  1675.     ' tic lines start "ticwidth" below or to the left of the X and
  1676.     ' Y axis respectively.  If grid lines are specified then the
  1677.     ' tic/grid line ends at ScaleMax for the respective axis.  The
  1678.     ' case statements below calculate where the tic/grid lines start
  1679.     ' based on the above criteria.
  1680.  
  1681.     ' Check for tic marks first (X Axis):
  1682.     SELECT CASE GE.XAxis.Labeled
  1683.         CASE cNo:  XTicMinY = Y1
  1684.         CASE ELSE
  1685.             XTicMinY = Y1 - cTicSize * (Y2 - Y1)
  1686.             IF GP.XStagger = cYes THEN
  1687.                 clSetChartFont GE.XAxis.TicFont
  1688.                 XTicDropY = GFI.PixHeight * (Y2 - Y1) / (GE.DataWindow.Y2 - GE.DataWindow.Y1)
  1689.             ELSE
  1690.                 XTicDropY = 0
  1691.             END IF
  1692.     END SELECT
  1693.  
  1694.     ' (Y Axis):
  1695.     SELECT CASE GE.YAxis.Labeled
  1696.         CASE cNo:  YTicMinX = X1
  1697.         CASE ELSE: YTicMinX = X1 - cTicSize * (X2 - X1)
  1698.     END SELECT
  1699.  
  1700.     ' Now for the other end of the tic/grid lines check for
  1701.     ' the grid flag (X axis):
  1702.     SELECT CASE GE.XAxis.grid
  1703.         CASE cNo:  XTicMaxY = Y1
  1704.         CASE ELSE: XTicMaxY = Y2
  1705.     END SELECT
  1706.  
  1707.     ' (Y Axis):
  1708.     SELECT CASE GE.YAxis.grid
  1709.         CASE cNo:  YTicMaxX = X1
  1710.         CASE ELSE: YTicMaxX = X2
  1711.     END SELECT
  1712.  
  1713.     ' Now that the beginning and end of the tic/grid lines has been
  1714.     ' calculated, it is necessary to figure out where they fall along the
  1715.     ' axes.  This depends on the type of axis: category or value.  On a
  1716.     ' category axis the tic/grid lines should fall in the middle of each
  1717.     ' bar set.  This is calculated by adding 1/2 of TicInterval to
  1718.     ' the beginning of the axis.  On a value axis the tic/grid line
  1719.     ' falls at the beginning of the axis.  It is also necessary to know
  1720.     ' the total number of tics per axis.  The following CASE statements
  1721.     ' calculate this.  Once the first tic/grid location on an axis is
  1722.     ' calculated the others can be calculated as they are drawn by adding
  1723.     ' TicInterval each time to the position of the previous tic mark:
  1724.  
  1725.     ' Location of the first (leftmost) tic/grid line on the X axis:
  1726.     TicTotX% = CINT((X2 - X1) / GE.XAxis.TicInterval)
  1727.     SELECT CASE GP.XMode
  1728.         CASE cCategory: TicX = X1 + GE.XAxis.TicInterval / 2
  1729.         CASE ELSE
  1730.             TicX = X1
  1731.             TicTotX% = TicTotX% + 1
  1732.     END SELECT
  1733.  
  1734.     ' Location of the first (top) tic/grid line on the Y axis:
  1735.     TicTotY% = CINT((Y2 - Y1) / GE.YAxis.TicInterval)
  1736.     SELECT CASE GP.YMode
  1737.         CASE cCategory: TicY = Y1 + GE.YAxis.TicInterval / 2
  1738.         CASE ELSE
  1739.             TicY = Y1
  1740.             TicTotY% = TicTotY% + 1
  1741.     END SELECT
  1742.  
  1743.     ' Now it's time to draw the axes; first the X then the Y axis.
  1744.     ' There's a small complexity that has to be dealt with first, though.
  1745.     ' The tic/grid lines are specified in "world" coordinates since that
  1746.     ' is easier to calculate but the current VIEW (the DataWindow) would
  1747.     ' clip them since tic marks (and also labels) lie outside of that
  1748.     ' region.  The solution is to extrapolate the DataWindow "world" to the
  1749.     ' ChartWindow region and set our VIEW to the ChartWindow.  This will
  1750.     ' clip labels if they are too long and try to go outside the Chart
  1751.     ' Window but still allow use of world coordinates for specifying
  1752.     ' locations.  To extrapolate the world coordinates to the ChartWindow,
  1753.     ' PMAP can be used.  This works since PMAP can take pixel coordinates
  1754.     ' outside of the current VIEW and map them to the appropriate world
  1755.     ' coordinates.  The DataWindow coordinates (calculated in the routine
  1756.     ' clSizeDataWindow) are expressed relative to the ChartWindow so
  1757.     ' it can be somewhat complicated trying to understand what to use with
  1758.     ' PMAP.  If you draw a picture of it things will appear more straight
  1759.     ' forward.
  1760.   
  1761.     ' To make sure that bars and columns aren't drawn over the axis lines
  1762.     ' temporarily move the left DataWindow border left by one and the bottom
  1763.     ' border down by one pixel:
  1764.     GE.DataWindow.X1 = GE.DataWindow.X1 - 1
  1765.     GE.DataWindow.Y2 = GE.DataWindow.Y2 + 1
  1766.  
  1767.     ' Select the DataWindow view and assign the "world" to it:
  1768.     clSelectRelWindow GE.DataWindow
  1769.     WINDOW (X1, Y1)-(X2, Y2)
  1770.     GTextWindow X1, Y1, X2, Y2, cFalse
  1771.  
  1772.     ' Next, use PMAP to extrapolate to ChartWindow:
  1773.     WorldX1 = PMAP(-GE.DataWindow.X1, 2)
  1774.     WorldX2 = PMAP(GP.ChartWid - 1 - GE.DataWindow.X1, 2)
  1775.  
  1776.     WorldY1 = PMAP(GP.ChartHgt - 1 - GE.DataWindow.Y1, 3)
  1777.     WorldY2 = PMAP(-GE.DataWindow.Y1, 3)
  1778.   
  1779.     ' Reset the DataWindow borders back to their original settings:
  1780.     GE.DataWindow.X1 = GE.DataWindow.X1 + 1
  1781.     GE.DataWindow.Y2 = GE.DataWindow.Y2 - 1
  1782.  
  1783.     ' Finally, select the ChartWindow VIEW and apply the extrapolated
  1784.     ' window to it:
  1785.     clSelectChartWindow
  1786.     WINDOW (WorldX1, WorldY1)-(WorldX2, WorldY2)
  1787.     GTextWindow WorldX1, WorldY1, WorldX2, WorldY2, cFalse
  1788.  
  1789.      ' Draw the X and Y axes (one pixel to left and bottom of window):
  1790.     CX% = PaletteC%(clMap2Pal%(GE.XAxis.AxisColor))  ' Color of X axis
  1791.     CY% = PaletteC%(clMap2Pal%(GE.YAxis.AxisColor))  ' Color of Y axis
  1792.  
  1793.     SX% = PaletteB%(clMap2Pal%(GE.XAxis.GridStyle)) ' Line styles; X grid
  1794.     SY% = PaletteB%(clMap2Pal%(GE.YAxis.GridStyle)) ' Line styles; Y grid
  1795.  
  1796.     LINE (X1, Y1)-(X2, Y1), CX%
  1797.     LINE (X1, Y1)-(X1, Y2), CY%
  1798.  
  1799.     ' X-Axis...Draw styled grid line then solid tic mark:
  1800.     TicLoc = TicX
  1801.     Stagger% = cFalse
  1802.     FOR i% = 1 TO TicTotX%
  1803.         LINE (TicLoc, Y1)-(TicLoc, XTicMaxY), CY%, , SX%
  1804.         IF Stagger% THEN
  1805.             LINE (TicLoc, XTicMinY - XTicDropY)-(TicLoc, Y1), CX%
  1806.             Stagger% = cFalse
  1807.         ELSE
  1808.             LINE (TicLoc, XTicMinY)-(TicLoc, Y1), CX%
  1809.             Stagger% = cTrue
  1810.         END IF
  1811.         TicLoc = TicLoc + GE.XAxis.TicInterval
  1812.     NEXT i%
  1813.  
  1814.     ' Y-Axis...Draw styled grid line then solid tic mark:
  1815.     TicLoc = TicY
  1816.     FOR i% = 1 TO TicTotY%
  1817.         LINE (X1, TicLoc)-(YTicMaxX, TicLoc), CX%, , SY%
  1818.         LINE (YTicMinX, TicLoc)-(X1, TicLoc), CY%
  1819.         TicLoc = TicLoc + GE.YAxis.TicInterval
  1820.     NEXT i%
  1821.  
  1822.     ' Label X tic marks and print titles:
  1823.     clLabelXTics GE.XAxis, Cat$(), TicX, TicTotX%, XTicMinY, YBoundry%
  1824.     clTitleXAxis GE.XAxis, GE.DataWindow.X1, GE.DataWindow.X2, YBoundry%
  1825.  
  1826.     ' Label Y tic marks and print titles:
  1827.     clLabelYTics GE.YAxis, Cat$(), YTicMinX, TicY, TicTotY%
  1828.     clTitleYAxis GE.YAxis, GE.DataWindow.Y1, GE.DataWindow.Y2
  1829.  
  1830. END SUB
  1831.  
  1832. '=== clDrawBarData - Draws data portion of multi-series bar chart
  1833. '
  1834. '  Arguments:
  1835. '     None
  1836. '
  1837. '  Return Values:
  1838. '     None
  1839. '
  1840. '=================================================================
  1841. SUB clDrawBarData ()
  1842. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  1843. SHARED PaletteC%()
  1844. SHARED V1()
  1845.  
  1846.     ' Set the VIEW to the DataWindow:
  1847.     clSelectRelWindow GE.DataWindow
  1848.  
  1849.     ' Set the WINDOW to match:
  1850.     WINDOW (GE.XAxis.ScaleMin, GE.YAxis.ScaleMin)-(GE.XAxis.ScaleMax, GE.YAxis.ScaleMax)
  1851.     GTextWindow GE.XAxis.ScaleMin, GE.YAxis.ScaleMin, GE.XAxis.ScaleMax, GE.YAxis.ScaleMax, cFalse
  1852.   
  1853.     ' If this is a linear axis then determine where the bars should grow from:
  1854.     IF GE.XAxis.RangeType = cLinear THEN
  1855.  
  1856.         ' If the scale minimum and maximum are on opposite sides of zero
  1857.         ' set the bar starting point to zero:
  1858.         IF GE.XAxis.ScaleMin < 0 AND GE.XAxis.ScaleMax > 0 THEN
  1859.             BarMin = 0
  1860.  
  1861.         ' If the axis range is all negative the the bars should grow from
  1862.         ' the right to the left so make the bar starting point the scale
  1863.         ' maximum:
  1864.         ELSEIF GE.XAxis.ScaleMin < 0 THEN
  1865.             BarMin = GE.XAxis.ScaleMax
  1866.  
  1867.         ' The axis range is all positive so the bar starting point is the
  1868.         ' scale minimum:
  1869.         ELSE
  1870.             BarMin = GE.XAxis.ScaleMin
  1871.         END IF
  1872.       
  1873.     ' The bar starting point for log axes should always be the scale minimum
  1874.     ' since only positive numbers are represented on a log axis (even though
  1875.     ' the log of small numbers is negative):
  1876.     ELSE
  1877.         BarMin = GE.XAxis.ScaleMin
  1878.     END IF
  1879.  
  1880.     ' Calculate the width of a bar.  Divide by the number of
  1881.     ' series if it's a plain (not stacked) chart:
  1882.     BarWid = GE.YAxis.TicInterval * cBarWid
  1883.     IF GE.ChartStyle = cPlain THEN BarWid = BarWid / GP.NSeries
  1884.  
  1885.     ' Calculate the beginning Y value of first bar then loop drawing
  1886.     ' all the bars:
  1887.     SpaceWid = GE.YAxis.TicInterval * (1 - cBarWid)
  1888.     StartLoc = GE.YAxis.ScaleMax - SpaceWid / 2
  1889.  
  1890.     FOR i% = 1 TO GP.NVals
  1891.  
  1892.         ' Reset sum variables for positive and negative stacked bars:
  1893.         RSumPos = 0
  1894.         RSumNeg = 0
  1895.  
  1896.         ' Reset the bar starting points:
  1897.         BarStartPos = BarMin
  1898.         BarStartNeg = BarMin
  1899.  
  1900.         ' Reset starting Y location of this bar set:
  1901.         BarLoc = StartLoc
  1902.  
  1903.         ' Now, chart the different series for this category:
  1904.         FOR J% = 1 TO GP.NSeries
  1905.  
  1906.             ' Get the value to chart from the data array:
  1907.             V = V1(i%, J%)
  1908.  
  1909.             ' If the value isn't a missing one then try to chart it:
  1910.             IF V <> cMissingValue THEN
  1911.  
  1912.                 ' If the X-axis has the AutoScale flag set then divide
  1913.                 ' the value by the axis' ScaleFactor variable:
  1914.                 IF GE.XAxis.AutoScale = cYes THEN V = V / GE.XAxis.ScaleFactor
  1915.  
  1916.                 ' If this is a plain chart then calculate the bar's location
  1917.                 ' and draw it:
  1918.                 IF GE.ChartStyle = cPlain THEN
  1919.  
  1920.                     BarLoc = StartLoc - (J% - 1) * BarWid
  1921.                     clRenderBar BarMin, BarLoc, V, BarLoc - BarWid, J%
  1922.  
  1923.                 ' If the bars should be stacked then draw either a positive or
  1924.                 ' negative portion of a bar depending on whether the data value
  1925.                 ' is positive or negative:
  1926.                 ELSE
  1927.  
  1928.                     ' If the value is positive:
  1929.                     IF V > 0 THEN
  1930.  
  1931.                         ' Add the value to the current sum for the bar and draw
  1932.                         ' the bar from the top of the last portion:
  1933.                         RSumPos = RSumPos + V
  1934.                         clRenderBar BarStartPos, BarLoc, RSumPos, BarLoc - BarWid, J%
  1935.                         BarStartPos = RSumPos
  1936.  
  1937.                     ' If the value is negative:
  1938.                     ELSE
  1939.  
  1940.                         ' Add the value to the current sum for the bar and draw
  1941.                         ' the bar from the bottom of the last portion:
  1942.                         RSumNeg = RSumNeg + V
  1943.                         clRenderBar BarStartNeg, BarLoc, RSumNeg, BarLoc - BarWid, J%
  1944.                         BarStartNeg = RSumNeg
  1945.  
  1946.                     END IF
  1947.                 END IF
  1948.             END IF
  1949.  
  1950.         NEXT J%
  1951.  
  1952.         ' Update the bar cluster's starting location:
  1953.         StartLoc = StartLoc - GE.YAxis.TicInterval
  1954.  
  1955.     NEXT i%
  1956.  
  1957.     ' If BarMin isn't the axis minimum then draw a reference line:
  1958.     IF BarMin <> GE.XAxis.ScaleMin THEN
  1959.         LINE (BarMin, GE.YAxis.ScaleMin)-(BarMin, GE.YAxis.ScaleMax), PaletteC%(clMap2Pal%(GE.YAxis.AxisColor))
  1960.     END IF
  1961.  
  1962. END SUB
  1963.  
  1964. '=== clDrawChartWindow - Draws the Chart window
  1965. '
  1966. '  Arguments:
  1967. '     None
  1968. '
  1969. '  Return Values:
  1970. '     None
  1971. '
  1972. '  Remarks:
  1973. '     This routine erases any previous viewport
  1974. '
  1975. '=================================================================
  1976. SUB clDrawChartWindow ()
  1977. SHARED GE AS ChartEnvironment
  1978.  
  1979.     ' Define viewport then render window:
  1980.     clSelectChartWindow
  1981.     clRenderWindow GE.ChartWindow
  1982.  
  1983. END SUB
  1984.  
  1985. '=== clDrawColumnData - Draws data portion of MS Column chart
  1986. '
  1987. '  Arguments:
  1988. '     None
  1989. '
  1990. '  Return Values:
  1991. '     None
  1992. '
  1993. '=================================================================
  1994. SUB clDrawColumnData ()
  1995. SHARED GP AS GlobalParams, GE AS ChartEnvironment
  1996. SHARED PaletteC%(), V1()
  1997.  
  1998.     ' First, set the VIEW to DataWindow:
  1999.     clSelectRelWindow GE.DataWindow
  2000.  
  2001.     ' Set the WINDOW to match:
  2002.     WINDOW (GE.XAxis.ScaleMin, GE.YAxis.ScaleMin)-(GE.XAxis.ScaleMax, GE.YAxis.ScaleMax)
  2003.     GTextWindow GE.XAxis.ScaleMin, GE.YAxis.ScaleMin, GE.XAxis.ScaleMax, GE.YAxis.ScaleMax, cFalse
  2004.  
  2005.     ' If this is a linear axis then determine where the bars should grow from:
  2006.     IF GE.YAxis.RangeType = cLinear THEN
  2007.  
  2008.         ' Draw 0 reference line if the scale minimum and maximum are on
  2009.         ' opposite sides of zero.  Also set the bar starting point to zero
  2010.         ' so that bars grow from the zero line:
  2011.         IF GE.YAxis.ScaleMin < 0 AND GE.YAxis.ScaleMax > 0 THEN
  2012.             BarMin = 0
  2013.  
  2014.         ' If the axis range is all negative the the bars should grow from
  2015.         ' the right to the left so make the bar starting point the scale
  2016.         ' maximum:
  2017.         ELSEIF GE.YAxis.ScaleMin < 0 THEN
  2018.             BarMin = GE.YAxis.ScaleMax
  2019.  
  2020.         ' The axis range is all positive so the bar starting point is the
  2021.         ' scale minimum:
  2022.         ELSE
  2023.             BarMin = GE.YAxis.ScaleMin
  2024.         END IF
  2025.       
  2026.     ' The bar starting point for log axes should always be the scale minimum
  2027.     ' since only positive numbers are represented on a log axis (even though
  2028.     ' the log of small numbers is negative):
  2029.     ELSE
  2030.         BarMin = GE.YAxis.ScaleMin
  2031.     END IF
  2032.       
  2033.     ' Calculate the width of a bar.  Divide by the number of
  2034.     ' series if it's a plain (not stacked) chart:
  2035.     BarWid = GE.XAxis.TicInterval * cBarWid
  2036.     IF GE.ChartStyle = cPlain THEN BarWid = BarWid / GP.NSeries
  2037.  
  2038.     ' calculate the beginning X value of first bar and loop, drawing all
  2039.     ' the bars:
  2040.     SpaceWid = GE.XAxis.TicInterval * (1 - cBarWid)
  2041.     StartLoc = GE.XAxis.ScaleMin + SpaceWid / 2
  2042.  
  2043.     FOR i% = 1 TO GP.NVals
  2044.  
  2045.         ' Reset sum variables for positive and negative stacked bars:
  2046.         RSumPos = 0
  2047.         RSumNeg = 0
  2048.  
  2049.         BarStartPos = BarMin
  2050.         BarStartNeg = BarMin
  2051.  
  2052.         ' Reset starting Y location of this bar set:
  2053.         BarLoc = StartLoc
  2054.  
  2055.         ' Now, go across the rows charting the different series for
  2056.         ' this category:
  2057.         FOR J% = 1 TO GP.NSeries
  2058.  
  2059.             ' Get the value to chart from the data array:
  2060.             V = V1(i%, J%)
  2061.  
  2062.             ' If the value isn't a missing one then try to chart it:
  2063.             IF V <> cMissingValue THEN
  2064.  
  2065.                 ' If the Y-axis has the AutoScale flag set then divide
  2066.                 ' the value by the axis' ScaleFactor variable:
  2067.                 IF GE.YAxis.AutoScale = cYes THEN V = V / GE.YAxis.ScaleFactor
  2068.  
  2069.                 ' If this is a plain chart then calculate the bar's location
  2070.                 ' and draw it:
  2071.                 IF GE.ChartStyle = cPlain THEN
  2072.  
  2073.                     BarLoc = StartLoc + (J% - 1) * BarWid
  2074.                     clRenderBar BarLoc, BarMin, BarLoc + BarWid, V, J%
  2075.  
  2076.                 ' If the bars should be stacked then draw either a positive or
  2077.                 ' negative portion of a bar depending on whether the data value
  2078.                 ' is positive or negative:
  2079.                 ELSE
  2080.  
  2081.                     ' If the value is positive:
  2082.                     IF V > 0 THEN
  2083.  
  2084.                         ' Add the value to the current sum for the bar and draw
  2085.                         ' the bar from the top of the last portion:
  2086.                         RSumPos = RSumPos + V
  2087.                         clRenderBar BarLoc, BarStartPos, BarLoc + BarWid, RSumPos, J%
  2088.                         BarStartPos = RSumPos
  2089.  
  2090.                     ' If the value is negative:
  2091.                     ELSE
  2092.  
  2093.                         ' Add the value to the current sum for the bar and draw
  2094.                         ' the bar from the bottom of the last portion:
  2095.                         RSumNeg = RSumNeg + V
  2096.                         clRenderBar BarLoc, BarStartNeg, BarLoc + BarWid, RSumNeg, J%
  2097.                         BarStartNeg = RSumNeg
  2098.  
  2099.                     END IF
  2100.                 END IF
  2101.             END IF
  2102.  
  2103.         NEXT J%
  2104.  
  2105.         ' Update the bar cluster's starting location:
  2106.         StartLoc = StartLoc + GE.XAxis.TicInterval
  2107.  
  2108.     NEXT i%
  2109.  
  2110.     ' If BarMin isn't the axis minimum then draw a reference line:
  2111.     IF BarMin <> GE.YAxis.ScaleMin THEN
  2112.         LINE (GE.XAxis.ScaleMin, BarMin)-(GE.XAxis.ScaleMax, BarMin), PaletteC%(clMap2Pal%(GE.XAxis.AxisColor))
  2113.     END IF
  2114.  
  2115. END SUB
  2116.  
  2117. '=== clDrawDataWindow - Draws the Data window
  2118. '
  2119. '  Arguments:
  2120. '     None
  2121. '
  2122. '  Return Values:
  2123. '     None
  2124. '
  2125. '  Remarks:
  2126. '     This routine erases any previous viewport or window specification.
  2127. '
  2128. '=================================================================
  2129. SUB clDrawDataWindow ()
  2130. SHARED GE AS ChartEnvironment
  2131.  
  2132.     ' Define viewport then render window:
  2133.     clSelectRelWindow GE.DataWindow
  2134.     clRenderWindow GE.DataWindow
  2135.  
  2136. END SUB
  2137.  
  2138. '=== clDrawLegend - Draws a legend
  2139. '
  2140. '  Arguments:
  2141. '     SeriesLabel$(1)   - Array of labels for the legend
  2142. '
  2143. '     First%            - Label number corresponding to first series
  2144. '
  2145. '     Last%             - Label number corresponding to last series
  2146. '
  2147. '  Return Values:
  2148. '     None.
  2149. '
  2150. '=================================================================
  2151. SUB clDrawLegend (SeriesLabel$(), First AS INTEGER, Last AS INTEGER)
  2152.  
  2153. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  2154. SHARED PaletteC%(), PaletteP$(), PaletteCh%()
  2155. SHARED GFI AS FontInfo
  2156. SHARED LLayout AS LegendLayout
  2157.  
  2158.     ' If legend flag is No then exit:
  2159.     IF GE.Legend.Legend = cNo THEN EXIT SUB
  2160.  
  2161.     ' Select and render the legend window:
  2162.     clSelectRelWindow GE.Legend.LegendWindow
  2163.     clRenderWindow GE.Legend.LegendWindow
  2164.     WINDOW
  2165.     GTextWindow 0, 0, 0, 0, cFalse
  2166.  
  2167.     ' Start with the first label, set the Y position of the first line
  2168.     ' of labels and loop through all the rows in the legend:
  2169.     clSetChartFont GE.Legend.TextFont
  2170.     LabelNum% = First
  2171.     YPos% = LLayout.HorizBorder
  2172.     FOR i% = 1 TO LLayout.NumRow
  2173.  
  2174.         ' Set position of beginning of row:
  2175.         XPos% = LLayout.VertBorder
  2176.  
  2177.         FOR J% = 1 TO LLayout.NumCol
  2178.  
  2179.             ' Map the label number to a valid palette reference:
  2180.             MJ% = clMap2Pal%(LabelNum% - First + 1)
  2181.  
  2182.             ' Depending on ChartType draw either a filled box or the
  2183.             ' plot character used for plotting:
  2184.             XStep% = LLayout.SymbolSize / GP.Aspect
  2185.             SELECT CASE GE.ChartType
  2186.  
  2187.                 CASE cBar, cColumn, cPie:
  2188.                     LINE (XPos%, YPos%)-STEP(XStep%, LLayout.SymbolSize), 0, BF
  2189.                     LINE (XPos%, YPos%)-STEP(XStep%, LLayout.SymbolSize), 1, B
  2190.                     PAINT (XPos% + 1, YPos% + 1), PaletteP$(MJ%), 1
  2191.                     LINE (XPos%, YPos%)-STEP(XStep%, LLayout.SymbolSize), PaletteC%(MJ%), B
  2192.  
  2193.                 CASE cLine, cScatter:
  2194.                     clSetCharColor MJ%
  2195.                     PlotChr$ = CHR$(PaletteCh%(MJ%))
  2196.                     clHPrint XPos% + XStep% - GFI.AvgWidth, YPos% - GFI.Leading, PlotChr$
  2197.  
  2198.             END SELECT
  2199.  
  2200.             ' Print the label for this entry in the legend:
  2201.             clSetCharColor GE.Legend.TextColor
  2202.             clHPrint XPos% + LLayout.LabelOffset, YPos% - GFI.Leading, SeriesLabel$(LabelNum%)
  2203.  
  2204.             ' Increment the label count and check count has finished:
  2205.             LabelNum% = LabelNum% + 1
  2206.             IF LabelNum% > Last THEN EXIT SUB
  2207.  
  2208.             ' Move over to the next column:
  2209.             XPos% = XPos% + LLayout.ColSpacing
  2210.  
  2211.         NEXT J%
  2212.  
  2213.         ' Move position to the next row:
  2214.         YPos% = YPos% + LLayout.RowSpacing
  2215.  
  2216.     NEXT i%
  2217.  
  2218. END SUB
  2219.  
  2220. '=== clDrawLineData - Draws data portion line chart
  2221. '
  2222. '  Arguments:
  2223. '     None
  2224. '
  2225. '  Return Values:
  2226. '     None
  2227. '
  2228. '=================================================================
  2229. SUB clDrawLineData ()
  2230. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  2231. SHARED PaletteC%(), PaletteS%(), PaletteCh%()
  2232. SHARED GFI AS FontInfo
  2233. SHARED V1()
  2234.  
  2235.     ' First, set the appropriate font and make text horizontal:
  2236.     clSetChartFont GE.DataFont
  2237.     SetGTextDir 0
  2238.   
  2239.     ' Then, set the view to DataWindow:
  2240.     clSelectRelWindow GE.DataWindow
  2241.  
  2242.     ' Set the window to match:
  2243.     WINDOW (GE.XAxis.ScaleMin, GE.YAxis.ScaleMin)-(GE.XAxis.ScaleMax, GE.YAxis.ScaleMax)
  2244.     GTextWindow GE.XAxis.ScaleMin, GE.YAxis.ScaleMin, GE.XAxis.ScaleMax, GE.YAxis.ScaleMax, cFalse
  2245.  
  2246.     ' Loop through the series:
  2247.     FOR J% = 1 TO GP.NSeries
  2248.  
  2249.         ' Map the series number into a valid palette reference:
  2250.         MJ% = clMap2Pal%(J%)
  2251.  
  2252.         ' Calculate starting X location of first point and set
  2253.         ' last value to missing (since this is the first value in the
  2254.         ' series the last value wasn't there):
  2255.         StartLoc = GE.XAxis.ScaleMin + GE.XAxis.TicInterval / 2
  2256.         LastMissing% = cYes
  2257.  
  2258.         FOR i% = 1 TO GP.NVals
  2259.  
  2260.             ' Get a value from the data array:
  2261.             V = V1(i%, J%)
  2262.  
  2263.             ' If the value is missing, set the LastMissing flag to Yes and
  2264.             ' go to the next value:
  2265.             IF V = cMissingValue THEN
  2266.                 LastMissing% = cYes
  2267.  
  2268.             ' If the value is not missing then try to chart it:
  2269.             ELSE
  2270.  
  2271.                 ' Scale the value (and convert it to a log if this is a
  2272.                 ' Log axis):
  2273.                 IF GE.YAxis.AutoScale = cYes THEN V = V / GE.YAxis.ScaleFactor
  2274.  
  2275.                 ' If the style dictates lines and the last point wasn't
  2276.                 ' missing then draw a line between the last point and this one:
  2277.                 IF GE.ChartStyle = cLines AND LastMissing% <> cYes THEN
  2278.                     LINE -(StartLoc, V), PaletteC%(MJ%), , PaletteS%(MJ%)
  2279.                 END IF
  2280.  
  2281.                 ' Position and print character:
  2282.                 CX% = PMAP(StartLoc, 0) - GetGTextLen(CHR$(PaletteCh%(MJ%))) / 2
  2283.                 CY% = PMAP(V, 1) - GFI.Ascent / 2
  2284.                 clSetCharColor MJ%
  2285.                 clHPrint CX%, CY%, CHR$(PaletteCh%(MJ%))
  2286.  
  2287.                 PSET (StartLoc, V), POINT(StartLoc, V)
  2288.  
  2289.                 LastMissing% = cNo
  2290.             END IF
  2291.  
  2292.             ' Move to next category position:
  2293.             StartLoc = StartLoc + GE.XAxis.TicInterval
  2294.         NEXT i%
  2295.     NEXT J%
  2296.  
  2297. END SUB
  2298.  
  2299. '=== clDrawPieData - Draws data part of a pie chart
  2300. '
  2301. '  Arguments:
  2302. '     Value(1)    -  One-dimensional array of data values
  2303. '
  2304. '     Expl(1)     -  One-dimensional array of explode flags (1=explode, 0=no)
  2305. '
  2306. '     N%          -  The number of data values to plot
  2307. '  Return Values:
  2308. '     None
  2309. '
  2310. '=================================================================
  2311. SUB clDrawPieData (value() AS SINGLE, Expl() AS INTEGER, N AS INTEGER)
  2312. SHARED GE AS ChartEnvironment
  2313. SHARED GP AS GlobalParams
  2314. SHARED GFI AS FontInfo
  2315. SHARED PaletteC%(), PaletteP$()
  2316.  
  2317.     ' Set the font to use for percent labels:
  2318.     clSetChartFont GE.DataFont
  2319.  
  2320.     ' Set up some reference variables:
  2321.     Pi2 = 2 * cPiVal                  ' 2*PI for radians conversions
  2322.     MinAngle = Pi2 / 120              ' Smallest wedge to try to paint
  2323.     A1 = -.0000001                    ' Starting and ending angle (set
  2324.     A2 = A1                           ' to very small negative to get
  2325.                                                  ' radius line for first wedge)
  2326.  
  2327.     ' Size the pie.
  2328.     ' Choose the point in the middle of the data window for the pie center:
  2329.     WINDOW (0, 0)-(1, 1)
  2330.     X = PMAP(.5, 0)                  ' Distance: left to center
  2331.     Y = PMAP(.5, 1)                  ' Distance: bottom to center
  2332.     WINDOW                           ' Now, use physical coordinates (pixels)
  2333.     GTextWindow 0, 0, 0, 0, cFalse
  2334.  
  2335.     ' Adjust radii for percent labels if required:
  2336.     clSetChartFont GE.DataFont
  2337.     IF GE.ChartStyle = cPercent THEN
  2338.         RadiusX = (X - 6 * GFI.AvgWidth) * GP.Aspect
  2339.         RadiusY = Y - 2 * GFI.PixHeight
  2340.     ELSE
  2341.         RadiusX = X * GP.Aspect
  2342.         RadiusY = Y
  2343.     END IF
  2344.  
  2345.     ' Pick the smallest radius (adjusted for screen aspect) then reduce
  2346.     ' it by 10% so the pie isn't too close to the window border:
  2347.     IF RadiusX < RadiusY THEN
  2348.         Radius = RadiusX
  2349.     ELSE
  2350.         Radius = RadiusY
  2351.     END IF
  2352.     Radius = (.9 * Radius) / GP.Aspect
  2353.  
  2354.     ' If radius is too small then error:
  2355.     IF Radius <= 0 THEN
  2356.         clSetError cBadDataWindow
  2357.         EXIT SUB
  2358.     END IF
  2359.  
  2360.     ' Find the sum of the data values (use double precision Sum variable to
  2361.     ' protect against overflow if summing large data values):
  2362.     Sum# = 0
  2363.     FOR i% = 1 TO GP.NSeries
  2364.         IF value(i%) > 0 THEN Sum# = Sum# + value(i%)
  2365.     NEXT i%
  2366.  
  2367.     ' Loop through drawing and painting the wedges:
  2368.     FOR i% = 1 TO N
  2369.  
  2370.         ' Map I% to a valid palette reference:
  2371.         MappedI% = clMap2Pal(i%)
  2372.  
  2373.         ' Draw wedges for positive values only:
  2374.         IF value(i%) > 0 THEN
  2375.  
  2376.             ' Calculate wedge percent and wedge ending angle:
  2377.             Percent = value(i%) / Sum#
  2378.             A2 = A1 - Percent * Pi2
  2379.  
  2380.             ' This locates the angle through the center of the pie wedge and
  2381.             ' calculates X and Y components of the vector headed in that
  2382.             ' direction:
  2383.             Bisect = (A1 + A2) / 2
  2384.             BisectX = Radius * COS(Bisect)
  2385.             BisectY = Radius * SIN(Bisect) * GP.Aspect
  2386.  
  2387.             ' If the piece is exploded then offset it 1/10th of a radius
  2388.             ' along the bisecting angle calculated above:
  2389.             IF Expl(i%) <> 0 THEN
  2390.                 CX = X + .1 * BisectX
  2391.                 CY = Y + .1 * BisectY
  2392.             ELSE
  2393.                 CX = X
  2394.                 CY = Y
  2395.             END IF
  2396.  
  2397.             ' If the angle is large enough, paint the wedge (if wedges of
  2398.             ' smaller angles are painted, the "paint" will sometimes spill out):
  2399.             IF (A1 - A2) > MinAngle THEN
  2400.                 PX = CX + .8 * BisectX
  2401.                 PY = CY + .8 * BisectY
  2402.  
  2403.                 ' Outline the wedge in color 1 and paint it black.
  2404.                 CIRCLE (CX, CY), Radius, 1, A1, A2, GP.Aspect
  2405.                 PAINT (PX, PY), 0, 1
  2406.                 ' Paint with the appropriate pattern:
  2407.                 PAINT (PX, PY), PaletteP$(MappedI%), 1
  2408.             END IF
  2409.             ' draw the wedge in the correct color:
  2410.             CIRCLE (CX, CY), Radius, PaletteC%(MappedI%), A1, A2, GP.Aspect
  2411.       
  2412.             ' Label pie wedge with percent if appropriate:
  2413.             IF GE.ChartStyle = cPercent THEN
  2414.                 Label$ = clVal2Str$(Percent * 100, 1, 1) + "%"
  2415.                 LabelX% = CX + BisectX + (GFI.AvgWidth * COS(Bisect))
  2416.                 LabelY% = CY + BisectY + (GFI.AvgWidth * SIN(Bisect)) * GP.Aspect
  2417.  
  2418.                 ' Adjust label location for the quadrant:
  2419.                 Quadrant% = FIX((ABS(Bisect / Pi2)) * 4)
  2420.                 IF Quadrant% = 0 OR Quadrant% = 1 THEN
  2421.                     LabelY% = LabelY% - GFI.Ascent
  2422.                 END IF
  2423.                 IF Quadrant% = 1 OR Quadrant% = 2 THEN
  2424.                     LabelX% = LabelX% - GetGTextLen(Label$)
  2425.                 END IF
  2426.  
  2427.                 clSetCharColor GE.Legend.TextColor
  2428.                 clHPrint LabelX%, LabelY%, Label$
  2429.             END IF
  2430.         END IF
  2431.  
  2432.         ' Set the beginning of next wedge to the end of this one:
  2433.         A1 = A2
  2434.  
  2435.     NEXT i%
  2436.  
  2437. END SUB
  2438.  
  2439. '=== clDrawScatterData - Draws data portion of Scatter chart
  2440. '
  2441. '  Arguments:
  2442. '     None
  2443. '
  2444. '  Return Values:
  2445. '     None
  2446. '
  2447. '=================================================================
  2448. SUB clDrawScatterData ()
  2449. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  2450. SHARED PaletteC%(), PaletteS%(), PaletteCh%()
  2451. SHARED GFI AS FontInfo
  2452. SHARED V1(), V2()
  2453.  
  2454.     ' Select the chart font and make text output horizontal:
  2455.     clSetChartFont GE.DataFont
  2456.     SetGTextDir 0
  2457.   
  2458.     ' Now, loop through all the points charting them:
  2459.     FOR Series% = 1 TO GP.NSeries
  2460.  
  2461.         ' Set LastMissing flag to Yes for first point in series:
  2462.         LastMissing% = cYes
  2463.         MS% = clMap2Pal%(Series%)
  2464.  
  2465.         ' Loop through all the points, charting them:
  2466.         FOR DataPoint% = 1 TO GP.NVals
  2467.  
  2468.             ' Get the X-value and Y-values from the data arrays:
  2469.             VX = V1(DataPoint%, Series%)
  2470.             VY = V2(DataPoint%, Series%)
  2471.  
  2472.             ' If either of the values to chart is missing set LastMissing
  2473.             ' flag to Yes to indicate a missing point and go to the next point:
  2474.             IF VX = cMissingValue OR VY = cMissingValue THEN
  2475.                 LastMissing% = cYes
  2476.  
  2477.             ELSE
  2478.  
  2479.                 ' Otherwise, scale the X and Y values if AutoScale is set for
  2480.                 ' their respective axes:
  2481.                 IF GE.XAxis.AutoScale = cYes THEN VX = VX / GE.XAxis.ScaleFactor
  2482.                 IF GE.YAxis.AutoScale = cYes THEN VY = VY / GE.YAxis.ScaleFactor
  2483.  
  2484.                 ' If this is a lined chart and the last point wasn't missing,
  2485.                 ' then draw a line from last point to the current point:
  2486.                 IF GE.ChartStyle = cLines AND LastMissing% <> cYes THEN
  2487.                     LINE -(VX, VY), PaletteC%(MS%), , PaletteS%(MS%)
  2488.                 END IF
  2489.  
  2490.                 ' In any case draw the plot character.  Start by getting the
  2491.                 ' screen coordinates of the character relative to the point
  2492.                 ' just charted:
  2493.                 CX% = PMAP(VX, 0) - GetGTextLen(CHR$(PaletteCh%(MS%))) / 2
  2494.                 CY% = PMAP(VY, 1) - GFI.Ascent / 2
  2495.  
  2496.                 ' Now, set the character color and print it:
  2497.                 clSetCharColor MS%
  2498.                 clHPrint CX%, CY%, CHR$(PaletteCh%(MS%))
  2499.  
  2500.                 ' Finally, reset the graphics cursor, since printing the
  2501.                 ' character changed it:
  2502.                 PSET (VX, VY), POINT(VX, VY)
  2503.  
  2504.                 LastMissing% = cNo
  2505.             END IF
  2506.  
  2507.         NEXT DataPoint%
  2508.     NEXT Series%
  2509. END SUB
  2510.  
  2511. '=== clDrawTitles - Draws the main and subtitles on a chart
  2512. '
  2513. '  Arguments:
  2514. '     None
  2515. '
  2516. '  Return Values:
  2517. '     None
  2518. '
  2519. '=================================================================
  2520. SUB clDrawTitles ()
  2521. SHARED GE AS ChartEnvironment
  2522. SHARED TTitleLayout AS TitleLayout
  2523.  
  2524.     ' Bottom of main title line is 1-1/2 character heights from the
  2525.     ' top of the chart window:
  2526.     YPos% = TTitleLayout.Top
  2527.     clPrintTitle GE.MainTitle, YPos%
  2528.  
  2529.     ' Add 1.5 * character height to y position for subtitle line position:
  2530.     YPos% = YPos% + TTitleLayout.TitleOne + TTitleLayout.Middle
  2531.     clPrintTitle GE.SubTitle, YPos%
  2532.  
  2533. END SUB
  2534.  
  2535. '=== clFilter - Filters input data into dynamic working data array
  2536. '
  2537. '  Arguments:
  2538. '     Axis     -  An AxisType variable
  2539. '
  2540. '     AxisMode%-  Mode for this axis
  2541. '
  2542. '     D1(1)    -  One-dimensional array of input data
  2543. '
  2544. '     D2(2)    -  Two-dimensional array for filtered data
  2545. '
  2546. '     N%       -  The number of values to transfer
  2547. '
  2548. '  Return Values:
  2549. '     Alters values in D2()
  2550. '
  2551. '=================================================================
  2552. SUB clFilter (Axis AS AxisType, AxisMode%, D1(), D2(), N%)
  2553.  
  2554.     ' If the axis is a category one then exit:
  2555.     IF AxisMode% = cCategory THEN EXIT SUB
  2556.  
  2557.     ' Transfer the data from the input data array to the working data
  2558.     ' array:
  2559.     FOR i% = 1 TO N%
  2560.         D2(i%, 1) = D1(i%)
  2561.     NEXT i%
  2562.  
  2563.     ' Call FilterMS to go through the data again scaling it and taking
  2564.     ' logs depending on the settings for this axis:
  2565.     clFilterMS Axis, AxisMode%, D2(), D2(), N%, 1, 1
  2566.  
  2567. END SUB
  2568.  
  2569. '=== clFilterMS - Filters two-dimensional input data into the dynamic working
  2570. '               data array
  2571. '
  2572. '  Arguments:
  2573. '     Axis     -  An AxisType variable
  2574. '
  2575. '     AxisMode%-  Axis mode for the axis
  2576. '
  2577. '     D1(2)    -  Two-dimensional array of input data
  2578. '
  2579. '     D2(2)    -  Two-dimensional array for filtered data
  2580. '
  2581. '     N%       -  The number of values to transfer
  2582. '
  2583. '     First%   -  First data series to filter
  2584. '
  2585. '     Last%    -  Last data series to filter
  2586. '
  2587. '  Return Values:
  2588. '     Alters values in D2()
  2589. '
  2590. '=================================================================
  2591. SUB clFilterMS (Axis AS AxisType, AxisMode%, D1(), D2(), N%, First%, Last%)
  2592.  
  2593.     ' If the axis is a category axis then exit:
  2594.     IF AxisMode% = cCategory THEN EXIT SUB
  2595.  
  2596.     ' If this isn't an autoscale axis, use the scale factor from the
  2597.     ' environment.  If it is an autoscale axis don't scale at all now
  2598.     ' it will be done when the data is drawn on the screen:
  2599.     IF Axis.AutoScale = cNo THEN
  2600.         ScaleFactor = Axis.ScaleFactor
  2601.     ELSE
  2602.         ScaleFactor = 1
  2603.     END IF
  2604.  
  2605.     ' If this a log axis calculate the log base:
  2606.     IF AxisMode% = cLog THEN LogRef = LOG(Axis.LogBase)
  2607.  
  2608.     ' Loop through the data series:
  2609.     FOR J% = First% TO Last%
  2610.  
  2611.         ' Loop through the values within the series:
  2612.         FOR i% = 1 TO N%
  2613.  
  2614.             ' Get a data value and if it isn't missing, then scale it:
  2615.             V = D1(i%, J%)
  2616.             IF V <> cMissingValue THEN V = V / ScaleFactor
  2617.  
  2618.             ' If the axis is a log axis, then if the value is greater than
  2619.             ' it is safe to take it's log.  Otherwise, set the data value to
  2620.             ' missing:
  2621.             IF Axis.RangeType = cLog THEN
  2622.                  IF V > 0 THEN
  2623.                     V = LOG(V) / LogRef
  2624.                 ELSE
  2625.                     V = cMissingValue
  2626.                 END IF
  2627.             END IF
  2628.  
  2629.             ' Place the value in the output data array:
  2630.             D2(i%, J% - First% + 1) = V
  2631.  
  2632.         NEXT i%
  2633.  
  2634.     NEXT J%
  2635.  
  2636. END SUB
  2637.  
  2638. '=== clFlagSystem - Sets GP.SysFlag to cYes
  2639. '
  2640. '  Arguments:
  2641. '     None
  2642. '
  2643. '  Return Values:
  2644. '     Alters the value of GP.SysFlag
  2645. '
  2646. '=================================================================
  2647. SUB clFlagSystem ()
  2648. SHARED GP AS GlobalParams
  2649.  
  2650.     GP.SysFlag = cYes
  2651.  
  2652. END SUB
  2653.  
  2654. '=== clFormatTics - Figures out tic label format and TicDecimals.
  2655. '
  2656. '  Arguments:
  2657. '     Axis     -  AxisType variable for which to format tics.
  2658. '
  2659. '  Return Values:
  2660. '     The TicFormat and Decimals elements may be changed for an axis
  2661. '     if AutoTic is cYes.
  2662. '
  2663. '=================================================================
  2664. SUB clFormatTics (Axis AS AxisType)
  2665.  
  2666.     ' If AutoScale isn't Yes then exit
  2667.     IF Axis.AutoScale <> cYes THEN EXIT SUB
  2668.  
  2669.     ' If the size of the largest value is bigger than seven decimal
  2670.     ' places then set TicFormat to exponential.  Otherwise, set it
  2671.     ' to normal:
  2672.     IF ABS(Axis.ScaleMin) >= 10 ^ 8 OR ABS(Axis.ScaleMax) >= 10 ^ 8 THEN
  2673.         Axis.TicFormat = cExpFormat
  2674.     ELSE
  2675.         Axis.TicFormat = cNormFormat
  2676.     END IF
  2677.  
  2678.     ' Pick the largest of the scale max and min (in absolute value) and
  2679.     ' use that to decide how many decimals to use when displaying the tic
  2680.     ' labels:
  2681.     Range = ABS(Axis.ScaleMax)
  2682.     IF ABS(Axis.ScaleMin) > Range THEN Range = ABS(Axis.ScaleMin)
  2683.     IF Range < 10 THEN
  2684.         TicResolution = -INT(-ABS(LOG(Range) / LOG(10!))) + 1
  2685.         IF TicResolution > 9 THEN TicResolution = 9
  2686.         Axis.TicDecimals = TicResolution
  2687.     ELSE
  2688.         Axis.TicDecimals = 0
  2689.     END IF
  2690.  
  2691. END SUB
  2692.  
  2693. '=== clGetStyle - Returns a predefined line-style definition
  2694. '
  2695. '  Arguments:
  2696. '     StyleNum%   -  A number identifying the entry to return
  2697. '
  2698. '  Return Values:
  2699. '     Returns the line-style for the specified style number
  2700. '
  2701. '=================================================================
  2702. FUNCTION clGetStyle% (StyleNum%)
  2703.  
  2704.     SELECT CASE StyleNum%
  2705.         CASE 1: Style% = &HFFFF
  2706.         CASE 2: Style% = &HF0F0
  2707.         CASE 3: Style% = &HF060
  2708.         CASE 4: Style% = &HCCCC
  2709.         CASE 5: Style% = &HC8C8
  2710.         CASE 6: Style% = &HEEEE
  2711.         CASE 7: Style% = &HEAEA
  2712.         CASE 8: Style% = &HF6DE
  2713.         CASE 9: Style% = &HF6F6
  2714.         CASE 10: Style% = &HF56A
  2715.         CASE 11: Style% = &HCECE
  2716.         CASE 12: Style% = &HA8A8
  2717.         CASE 13: Style% = &HAAAA
  2718.         CASE 14: Style% = &HE4E4
  2719.         CASE 15: Style% = &HC88C
  2720.     END SELECT
  2721.     clGetStyle% = Style%
  2722.  
  2723. END FUNCTION
  2724.  
  2725. '=== clHPrint - Prints text Horizontally on the screen
  2726. '
  2727. '  Arguments:
  2728. '     X     -  X position for the lower left of the first character to be
  2729. '              printed (in absolute screen coordinates)
  2730. '
  2731. '     Y     -  Y position for the lower left of the first character to be
  2732. '              printed (in absolute screen coordinates)
  2733. '
  2734. '     Txt$  -  Text to print
  2735. '
  2736. '  Return Values:
  2737. '     None
  2738. '
  2739. '=================================================================
  2740. SUB clHPrint (X%, Y%, Txt$)
  2741.  
  2742.     ' Map the input coordinates relative to the current viewport:
  2743.     X = PMAP(X%, 2)
  2744.     Y = PMAP(Y%, 3)
  2745.   
  2746.     ' Output the text horizontally:
  2747.     SetGTextDir 0
  2748.     TextLen% = OutGText(X, Y, Txt$)
  2749.  
  2750. END SUB
  2751.  
  2752. '=== clInitChart - Initializes the charting library.
  2753. '
  2754. '  Arguments:
  2755. '     None
  2756. '
  2757. '  Return Values:
  2758. '     None
  2759. '
  2760. '  Remarks:
  2761. '     This routine initializes some default data structures and is
  2762. '     called automatically by charting routines if the variable
  2763. '     GP.Initialized is cNo (or zero).
  2764. '
  2765. '=================================================================
  2766. SUB clInitChart ()
  2767. SHARED StdChars%(), GP AS GlobalParams
  2768.  
  2769.     ' Clear any previous errors
  2770.     clClearError
  2771.   
  2772.     ON ERROR GOTO UnexpectedErr
  2773.  
  2774.     ' Initialize PaletteSet to no so palettes will be initialized properly
  2775.     ' when ChartScreen is called:
  2776.     GP.PaletteSet = cNo
  2777.  
  2778.     ' Set up the list of plotting characters:
  2779.     PlotChars$ = "*ox=+/:&#@%![$^"
  2780.     StdChars%(0) = 0
  2781.     FOR i% = 1 TO cPalLen
  2782.         StdChars%(i%) = ASC(MID$(PlotChars$, i%, 1))
  2783.     NEXT i%
  2784.  
  2785.     ' Initialize standard structures for title, axis, window and legend:
  2786.     clInitStdStruc
  2787.  
  2788.     GP.Initialized = cYes
  2789.  
  2790. END SUB
  2791.  
  2792. '=== clInitStdStruc - Initializes structures for standard titles, axes, etc.
  2793. '
  2794. '  Arguments:
  2795. '     None
  2796. '
  2797. '  Return Values:
  2798. '     None
  2799. '
  2800. '=================================================================
  2801. SUB clInitStdStruc ()
  2802. SHARED DAxis AS AxisType, DWindow AS RegionType
  2803. SHARED DLegend AS LegendType, DTitle AS TitleType
  2804.   
  2805. ' Set up default components of the default chart
  2806. ' environment; start with default title:
  2807.  
  2808. ' Default title definition:
  2809. DTitle.Title = ""                ' Title text is blank
  2810. DTitle.TitleFont = 1             ' Title font is first one
  2811. DTitle.TitleColor = 1            ' Title color is white
  2812. DTitle.Justify = cCenter         ' Center justified
  2813.  
  2814. ' Default axis definition:
  2815. DAxis.grid = cNo                 ' No grid
  2816. DAxis.GridStyle = 1              ' Solid lines for grid
  2817. DAxis.AxisTitle = DTitle         ' Use above to initialize axis title
  2818. DAxis.AxisColor = 1              ' Axis color is white
  2819. DAxis.Labeled = cYes             ' Label and tic axis
  2820. DAxis.RangeType = cLinear        ' Linear axis
  2821. DAxis.LogBase = 10               ' Logs to base 10
  2822. DAxis.AutoScale = cYes           ' Automatically scale numbers if needed
  2823. DAxis.ScaleTitle = DTitle        ' Scale title
  2824. DAxis.TicFont = 1                ' Tic font is first one
  2825. DAxis.TicDecimals = 0            ' No decimals
  2826.  
  2827. ' Default window definition:
  2828. DWindow.Background = 0           ' Black background
  2829. DWindow.Border = cNo             ' Window will have no border
  2830. DWindow.BorderColor = 1          ' Make the borders white
  2831. DWindow.BorderStyle = 1          ' Solid-line borders
  2832.  
  2833. ' Default legend definition:
  2834. DLegend.Legend = cYes            ' Draw a legend if multi-series chart
  2835. DLegend.Place = cRight           ' On the right side
  2836. DLegend.TextColor = 1            ' Legend text is white on black
  2837. DLegend.TextFont = 1             ' Legend text font is first one
  2838. DLegend.AutoSize = cYes          ' Figure out size automatically
  2839. DLegend.LegendWindow = DWindow   ' Use the default window specification
  2840.  
  2841. END SUB
  2842.  
  2843. '=== clLabelXTics - Labels tic marks for X axis
  2844. '
  2845. '  Arguments:
  2846. '     Axis     -  An AxisType variable containing axis specification
  2847. '
  2848. '     Cat$(1)  -  One-dimensional array of category labels.  Ignored
  2849. '                 if axis not category axis
  2850. '
  2851. '     TicX     -  X coordinate of first tic mark
  2852. '
  2853. '     TicY     -  Y coordinate of tic tip (portion away from axis)
  2854. '
  2855. '     YBoundry% -  Y coordinate of bottom of tic labels
  2856. '
  2857. '  Return Values:
  2858. '     None
  2859. '
  2860. '=================================================================
  2861. SUB clLabelXTics (Axis AS AxisType, Cat$(), TicX, TicTotX%, TicY, YBoundry%)
  2862. SHARED GFI AS FontInfo
  2863. SHARED GP AS GlobalParams
  2864. SHARED GE AS ChartEnvironment
  2865.  
  2866.     ' If this axis isn't supposed to be labeled then exit:
  2867.     IF Axis.Labeled <> cYes THEN EXIT SUB
  2868.  
  2869.     ' Set the appropriate color, font, and orientation for tic labels:
  2870.     clSetCharColor Axis.AxisColor
  2871.     clSetChartFont Axis.TicFont
  2872.     SetGTextDir 0
  2873.   
  2874.     ' The Y coordinate of the labels will be a constant .5 character
  2875.     ' heights below the end of the tic marks (TicY):
  2876.     Y% = PMAP(TicY, 1) + (GFI.Ascent - GFI.Leading) / 2
  2877.     IF GP.XStagger = cYes THEN
  2878.         YDrop% = (3 * GFI.Ascent - GFI.Leading) / 2
  2879.     ELSE
  2880.         YDrop% = 0
  2881.     END IF
  2882.     YBoundry% = Y% + YDrop% + GFI.PixHeight
  2883.  
  2884.     ' Loop through and write labels
  2885.     TX = TicX
  2886.     CatNum% = 1
  2887.     Stagger% = cFalse
  2888.     FOR i% = 1 TO TicTotX%
  2889.  
  2890.         ' The label depends on axis mode (category, value):
  2891.         SELECT CASE GP.XMode
  2892.             CASE cCategory: Txt$ = Cat$(CatNum%)
  2893.             CASE ELSE:      Txt$ = clVal2Str$(TX, Axis.TicDecimals, Axis.TicFormat)
  2894.         END SELECT
  2895.         TxtLen% = GetGTextLen(Txt$)
  2896.         IF GP.XMode = cCategory THEN
  2897.             MaxLen% = 2 * (GE.DataWindow.X2 - GE.DataWindow.X1) / GP.NVals - GFI.AvgWidth
  2898.             IF MaxLen% < 0 THEN MaxLen% = 0
  2899.             DO UNTIL TxtLen% <= MaxLen%
  2900.                 Txt$ = LEFT$(Txt$, LEN(Txt$) - 1)
  2901.                 TxtLen% = GetGTextLen(Txt$)
  2902.             LOOP
  2903.         END IF
  2904.  
  2905.         ' Center the label under the tic mark and print it:
  2906.         X% = PMAP(TX, 0) - (TxtLen%) / 2
  2907.  
  2908.         IF Stagger% THEN
  2909.             clHPrint X%, Y% + YDrop%, Txt$
  2910.             Stagger% = cFalse
  2911.         ELSE
  2912.             clHPrint X%, Y%, Txt$
  2913.             Stagger% = cTrue
  2914.         END IF
  2915.  
  2916.         ' Move to the next tic mark:
  2917.         TX = TX + Axis.TicInterval
  2918.         CatNum% = CatNum% + 1
  2919.     NEXT i%
  2920.  
  2921. END SUB
  2922.  
  2923. '=== clLabelYTics - Labels tic marks and draws Y axis title
  2924. '
  2925. '  Arguments:
  2926. '     Axis     -  An AxisType variable containing axis specification
  2927. '
  2928. '     Cat$(1)  -  One-dimensional array of category labels.  Ignored
  2929. '                 if axis not category axis
  2930. '
  2931. '     TicX     -  X coordinate of first tic's tip (away from axis)
  2932. '
  2933. '     TicY     -  Y coordinate of first tic
  2934. '
  2935. '  Return Values:
  2936. '     None
  2937. '
  2938. '=================================================================
  2939. SUB clLabelYTics (Axis AS AxisType, Cat$(), TicX, TicY, TicTotY%)
  2940. SHARED GFI AS FontInfo
  2941. SHARED GP AS GlobalParams
  2942.  
  2943.     ' If axis isn't supposed to be labeled then exit:
  2944.     IF Axis.Labeled <> cYes THEN EXIT SUB
  2945.  
  2946.     ' Set the appropriate color, font, and orientation for tic labels:
  2947.     clSetCharColor Axis.AxisColor
  2948.     clSetChartFont Axis.TicFont
  2949.     SetGTextDir 0
  2950.  
  2951.     ' Loop through and write labels
  2952.     TY = TicY
  2953.     CatNum% = 1
  2954.     FOR i% = 1 TO TicTotY%
  2955.  
  2956.         ' The label depends on axis mode (category, value):
  2957.         SELECT CASE GP.YMode
  2958.             CASE cCategory: Txt$ = Cat$(GP.NVals - CatNum% + 1)
  2959.             CASE ELSE:      Txt$ = clVal2Str$(TY, Axis.TicDecimals, Axis.TicFormat)
  2960.         END SELECT
  2961.         TxtLen% = GetGTextLen(Txt$)
  2962.  
  2963.         ' Space the label 1/2 character width to the left of the tic
  2964.         ' mark and center it vertically on the tic mark (round vertical
  2965.         ' location to the next highest integer):
  2966.         X% = PMAP(TicX, 0) - TxtLen% - (.5 * GFI.MaxWidth)
  2967.         Y% = -INT(-(PMAP(TY, 1) - (GFI.Ascent + GFI.Leading) / 2))
  2968.  
  2969.         ' Print the label:
  2970.         clHPrint X%, Y%, Txt$
  2971.  
  2972.         ' Go to the next tic mark:
  2973.         TY = TY + Axis.TicInterval
  2974.         CatNum% = CatNum% + 1
  2975.     NEXT i%
  2976.  
  2977. END SUB
  2978.  
  2979. '=== clLayoutLegend - Calculates size of the legend
  2980. '
  2981. '  Arguments:
  2982. '     SeriesLabel$(1) - The labels used in the legend
  2983. '
  2984. '     First%   - The first series (label) to process
  2985. '
  2986. '     Last%    - The last series (label) to process
  2987. '
  2988. '  Return Values:
  2989. '     The coordinates in the legend window portion of Env are altered
  2990. '
  2991. '  Remarks:
  2992. '     Sizing the legend window requires finding out where it goes (right
  2993. '     or bottom) and determining how big the labels are and how big
  2994. '     the legend needs to be to hold them.
  2995. '
  2996. '=================================================================
  2997. SUB clLayoutLegend (SeriesLabel$(), First%, Last%)
  2998. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  2999. SHARED GFI AS FontInfo
  3000. SHARED LLayout AS LegendLayout
  3001. SHARED TTitleLayout AS TitleLayout
  3002. DIM W AS RegionType
  3003.  
  3004.     ' If "no legend" is specified, then exit:
  3005.     IF GE.Legend.Legend = cNo THEN EXIT SUB
  3006.  
  3007.     ' This may be an auto legend or not, but, in either case we're
  3008.     ' going to need the following information:
  3009.     clSetChartFont GE.Legend.TextFont
  3010.  
  3011.     LLayout.SymbolSize = GFI.Ascent - GFI.Leading - 1
  3012.     LLayout.HorizBorder = GFI.Ascent
  3013.     LLayout.VertBorder = GFI.AvgWidth
  3014.     LLayout.RowSpacing = 1.75 * (LLayout.SymbolSize + 1)
  3015.     LLayout.LabelOffset = LLayout.SymbolSize / GP.Aspect + GFI.AvgWidth
  3016.  
  3017.     'RowLeading% = LLayout.RowSpacing - LLayout.SymbolSize
  3018.     RowLeading% = .75 * LLayout.SymbolSize + 1.75
  3019.  
  3020.     ColWid% = clMaxStrLen(SeriesLabel$(), First%, Last%) + LLayout.LabelOffset
  3021.     LLayout.ColSpacing = ColWid% + GFI.AvgWidth
  3022.  
  3023.     ' If this isn't an autosize legend:
  3024.     IF GE.Legend.AutoSize = cNo THEN
  3025.  
  3026.         ' Check the legend coordinates supplied by the user to make
  3027.         ' sure that they are valid.  If they are, exit:
  3028.         W = GE.Legend.LegendWindow
  3029.         LWid% = W.X2 - W.X1
  3030.         LHgt% = W.Y2 - W.Y1
  3031.         IF LWid% > 0 AND LHgt% > 0 THEN
  3032.  
  3033.             ' Calculate the number of columns and rows of labels that will
  3034.             ' fit in the legend:
  3035.             NumCol% = INT((LWid% - LLayout.VertBorder) / (LLayout.ColSpacing))
  3036.             IF NumCol% <= 0 THEN NumCol% = 1
  3037.             IF NumCol% > GP.NSeries THEN NumCol% = GP.NSeries
  3038.             NumRow% = -INT(-GP.NSeries / NumCol%)
  3039.             LLayout.NumRow = NumRow%
  3040.             LLayout.NumCol = NumCol%
  3041.  
  3042.             ' Re-calculate the column and row spacing:
  3043.             LLayout.ColSpacing = INT((LWid% - LLayout.VertBorder) / NumCol%)
  3044.             LLayout.RowSpacing = INT((LHgt% - 2 * LLayout.HorizBorder + RowLeading%) / NumRow%)
  3045.  
  3046.             EXIT SUB
  3047.  
  3048.         ' If invalid legend coordinates are discovered set an error and
  3049.         ' go on to calculate new ones:
  3050.         ELSE
  3051.  
  3052.           clSetError cBadLegendWindow
  3053.  
  3054.         END IF
  3055.     END IF
  3056.  
  3057.     ' Do remaining calculations according to the legend placement specified
  3058.     ' (right, bottom, overlay):
  3059.     SELECT CASE GE.Legend.Place
  3060.  
  3061.         CASE cRight, cOverlay:
  3062.  
  3063.             ' Leave room at top for chart titles:
  3064.             Top% = TTitleLayout.TotalSize
  3065.  
  3066.             ' Figure out the maximum number of legend rows that will
  3067.             ' fit in the amount of space you have left for the legend
  3068.             ' height.  Then, see how many columns are needed.  Once
  3069.             ' the number of columns is set refigure how many rows are
  3070.             ' required:
  3071.             NumRow% = INT((GP.ChartHgt - Top% - 2 * LLayout.HorizBorder) / LLayout.RowSpacing)
  3072.             IF NumRow% > GP.NSeries THEN NumRow% = GP.NSeries
  3073.             NumCol% = -INT(-GP.NSeries / NumRow%)
  3074.             NumRow% = -INT(-GP.NSeries / NumCol%)
  3075.  
  3076.             ' Set the width and height:
  3077.             LWid% = NumCol% * LLayout.ColSpacing - GFI.AvgWidth + 2 * LLayout.VertBorder
  3078.             LHgt% = (NumRow% * LLayout.RowSpacing - RowLeading% + 2 * LLayout.HorizBorder)
  3079.  
  3080.             ' Place the legend one character width from right and even with
  3081.             ' what will be the top of the data window:
  3082.             LLft% = GP.ChartWid - 1 - LWid% - GFI.AvgWidth
  3083.             LTop% = Top%
  3084.  
  3085.         CASE cBottom:
  3086.  
  3087.             ' The number of label columns that will fit (using the same
  3088.             ' procedure as above except figure columns first):
  3089.             NumCol% = INT((GP.ChartWid - 2 * LLayout.HorizBorder) / LLayout.ColSpacing)
  3090.             IF NumCol% > GP.NSeries THEN NumCol% = GP.NSeries
  3091.             NumRow% = -INT(-GP.NSeries / NumCol%)
  3092.             NumCol% = -INT(-GP.NSeries / NumRow%)
  3093.  
  3094.             ' Set the width and height:
  3095.             LWid% = NumCol% * LLayout.ColSpacing - GFI.AvgWidth + 2 * LLayout.VertBorder
  3096.             LHgt% = (NumRow% * LLayout.RowSpacing - RowLeading% + 2 * LLayout.HorizBorder)
  3097.  
  3098.             ' Center the legend horizontally one character from the bottom:
  3099.             LLft% = (GP.ChartWid - 1 - LWid%) / 2
  3100.             LTop% = GP.ChartHgt - 1 - LHgt% - GFI.Ascent
  3101.  
  3102.     END SELECT
  3103.  
  3104.     ' Record legend columns and rows:
  3105.     LLayout.NumRow = NumRow%
  3106.     LLayout.NumCol = NumCol%
  3107.  
  3108.     ' Finally, place the legend coordinates in GE:
  3109.     GE.Legend.LegendWindow.X1 = LLft%
  3110.     GE.Legend.LegendWindow.Y1 = LTop%
  3111.     GE.Legend.LegendWindow.X2 = LLft% + LWid%
  3112.     GE.Legend.LegendWindow.Y2 = LTop% + LHgt%
  3113.  
  3114.     ' If, after all this, the legend window is invalid, set error:
  3115.     IF LLft% < 0 OR LTop% < 0 OR LWid% <= 0 OR LHgt% <= 0 THEN
  3116.         clSetError cBadLegendWindow
  3117.     END IF
  3118.  
  3119. END SUB
  3120.  
  3121. '=== clLayoutTitle - Figures out title layouts for Top, X-axis and
  3122. '                      Y-axis titles
  3123. '
  3124. '  Arguments:
  3125. '     TL    -  Layout variable into which to place titles
  3126. '
  3127. '     T1    -  First title
  3128. '
  3129. '     T2    -  Second Title
  3130. '
  3131. '  Return Values:
  3132. '     none
  3133. '
  3134. '=================================================================
  3135. SUB clLayoutTitle (TL AS TitleLayout, T1 AS TitleType, T2 AS TitleType)
  3136. SHARED GFI AS FontInfo
  3137.  
  3138.     ' Set the title heights initially to 0:
  3139.     TL.TitleOne = 0
  3140.     TL.TitleTwo = 0
  3141.   
  3142.     ' If the first title is set then get its height:
  3143.     Total% = 0
  3144.     IF LTRIM$(T1.Title) <> "" THEN
  3145.         clSetChartFont T1.TitleFont
  3146.         TL.TitleOne = GFI.PixHeight
  3147.         Total% = Total% + 1
  3148.     END IF
  3149.   
  3150.     ' If the second title is set then get it's height:
  3151.     IF LTRIM$(T2.Title) <> "" THEN
  3152.         clSetChartFont T2.TitleFont
  3153.         TL.TitleTwo = GFI.PixHeight
  3154.         Lead2% = GFI.Leading
  3155.         Total% = Total% + 1
  3156.     END IF
  3157.   
  3158.     ' Set the "leading" values for label spacing depending on how many
  3159.     ' of the titles were non-blank:
  3160.     TotalHeight% = TL.TitleOne + TL.TitleTwo
  3161.     SELECT CASE Total%
  3162.         CASE 0:
  3163.             TL.Top = 8
  3164.             TL.Middle = 0
  3165.             TL.Bottom = 4
  3166.  
  3167.         CASE 1:
  3168.             TL.Top = 8 + TotalHeight% / 8
  3169.             TL.Middle = 0
  3170.             TL.Bottom = TL.Top
  3171.       
  3172.         CASE 2:
  3173.             TL.Top = 8 + TotalHeight% / 8
  3174.             TL.Middle = 0: IF Lead2% = 0 THEN TL.Middle = TL.TitleOne / 2
  3175.             TL.Bottom = TL.Top
  3176.     END SELECT
  3177.  
  3178.     TL.TotalSize = TL.Top + TL.TitleOne + TL.Middle + TL.TitleTwo + TL.Bottom
  3179.   
  3180. END SUB
  3181.  
  3182. '=== clMap2Attrib% - Maps an integer to a screen attribute for current
  3183. '                    screen mode
  3184. '
  3185. '  Arguments:
  3186. '     N% - The number to map
  3187. '
  3188. '  Return Values:
  3189. '     The function returns:
  3190. '        0 is mapped to 0, all other numbers are mapped to the range
  3191. '        1 to GP.MaxColor
  3192. '
  3193. '=================================================================
  3194. FUNCTION clMap2Attrib% (N%)
  3195. SHARED GP AS GlobalParams
  3196.  
  3197.     AbsN% = ABS(N%)
  3198.     IF AbsN% = 0 THEN
  3199.         clMap2Attrib% = AbsN%
  3200.     ELSE
  3201.         clMap2Attrib% = (AbsN% - 1) MOD GP.MaxColor + 1
  3202.     END IF
  3203.  
  3204. END FUNCTION
  3205.  
  3206. '=== clMap2Pal% - Maps an integer into a palette reference
  3207. '
  3208. '  Arguments:
  3209. '     N% - The number to map
  3210. '
  3211. '  Return Values:
  3212. '     The function returns (N%-1) MOD cPalLen + 1
  3213. '
  3214. '  Remarks:
  3215. '     This FUNCTION is used in almost every reference to a palette to ensure
  3216. '     that an invalid number doesn't cause a reference outside of a palette
  3217. '     array (and thus crash the library).  This FUNCTION maps the first
  3218. '     cPalLen values to themselves. Numbers above cPalLen are mapped to
  3219. '     the values 2..cPalLen.
  3220. '
  3221. '=================================================================
  3222. FUNCTION clMap2Pal% (N%)
  3223.  
  3224.     AbsN% = ABS(N%)
  3225.     IF AbsN% > cPalLen THEN
  3226.         clMap2Pal% = (AbsN% - 2) MOD (cPalLen - 1) + 2
  3227.     ELSE
  3228.         clMap2Pal% = AbsN%
  3229.     END IF
  3230.  
  3231. END FUNCTION
  3232.  
  3233. '=== clMaxStrLen% - Finds the length of the longest string in a list
  3234. '
  3235. '  Arguments:
  3236. '     Txt$(1)  -  One-dimensional array of strings to search
  3237. '
  3238. '     First%   -  First string to consider
  3239. '
  3240. '     Last%    -  Last string to consider
  3241. '
  3242. '  Return Values:
  3243. '     This FUNCTION returns the length of the longest string
  3244. '
  3245. '=================================================================
  3246. FUNCTION clMaxStrLen% (Txt$(), First%, Last%)
  3247.  
  3248.     ' Set Max to 0 then loop through each label updating Max if the
  3249.     ' label is longer:
  3250.     Max% = 0
  3251.     FOR Row% = First% TO Last%
  3252.         L% = GetGTextLen(Txt$(Row%))
  3253.         IF L% > Max% THEN Max% = L%
  3254.     NEXT Row%
  3255.  
  3256.     ' Return Max as the value of the FUNCTION:
  3257.     clMaxStrLen% = Max%
  3258.  
  3259. END FUNCTION
  3260.  
  3261. '=== clMaxVal - Returns the maximum of two numbers
  3262. '
  3263. '  Arguments:
  3264. '     A  -  The first number
  3265. '
  3266. '     B  -  The second number
  3267. '
  3268. '  Return Values:
  3269. '     The function returns the maximum of the two values
  3270. '
  3271. '=================================================================
  3272. FUNCTION clMaxVal (A, B)
  3273.  
  3274.     IF A > B THEN clMaxVal = A ELSE clMaxVal = B
  3275.  
  3276. END FUNCTION
  3277.  
  3278. '=== clPrintTitle - Prints title correctly justified and colored
  3279. '
  3280. '  Arguments:
  3281. '     TitleVar - A TitleType variable containing specifications for the
  3282. '                title to be printed
  3283. '
  3284. '     Y%       - Vertical position in window for bottom of line
  3285. '
  3286. '  Return Values:
  3287. '     None
  3288. '
  3289. '=================================================================
  3290. SUB clPrintTitle (TitleVar AS TitleType, Y%)
  3291. SHARED GFI AS FontInfo, GP AS GlobalParams
  3292.  
  3293.     ' Calculate width of the title text:
  3294.     clSetChartFont TitleVar.TitleFont
  3295.  
  3296.     Txt$ = RTRIM$(TitleVar.Title)
  3297.     TxtLen% = GetGTextLen(Txt$)
  3298.     IF TxtLen% = 0 THEN EXIT SUB
  3299.  
  3300.     ' Calculate horizontal position depending on justification style
  3301.     SELECT CASE TitleVar.Justify
  3302.  
  3303.         CASE cCenter: X% = (GP.ChartWid - 1 - (TxtLen%)) / 2
  3304.         CASE cRight:  X% = GP.ChartWid - 1 - TxtLen% - GFI.AvgWidth
  3305.         CASE ELSE:    X% = GFI.AvgWidth
  3306.  
  3307.     END SELECT
  3308.  
  3309.     ' Set color of text and print it:
  3310.     clSetCharColor TitleVar.TitleColor
  3311.     clHPrint X%, Y%, Txt$
  3312.  
  3313. END SUB
  3314.  
  3315. '=== clRenderBar - Renders a bar for a bar or column chart
  3316. '
  3317. '  Arguments:
  3318. '     X1    -  Left side of bar (in data world coordinates)
  3319. '
  3320. '     Y1    -  Top of bar (in data world coordinates)
  3321. '
  3322. '     X2    -  Right side of bar (in data world coordinates)
  3323. '
  3324. '     Y2    -  Bottom of bar (in data world coordinates)
  3325. '
  3326. '     C%    -  Palette entry number to use for border color and fill pattern
  3327. '
  3328. '  Return Values:
  3329. '     None
  3330. '
  3331. '=================================================================
  3332. SUB clRenderBar (X1, Y1, X2, Y2, c%)
  3333. SHARED PaletteC%(), PaletteP$()
  3334.  
  3335.     ' First clear out space for the bar by drawing a bar in black:
  3336.     LINE (X1, Y1)-(X2, Y2), 0, BF
  3337.  
  3338.     ' Put a border around the bar and fill with pattern:
  3339.     MC% = clMap2Pal%(c%)
  3340.  
  3341.     LINE (X1, Y1)-(X2, Y2), 1, B
  3342.     PAINT ((X1 + X2) / 2, (Y1 + Y2) / 2), PaletteP$(MC%), 1
  3343.     LINE (X1, Y1)-(X2, Y2), PaletteC%(MC%), B
  3344.  
  3345. END SUB
  3346.  
  3347. '=== clRenderWindow - Renders a window on the screen
  3348. '
  3349. '  Arguments:
  3350. '     W - A RegionType variable
  3351. '
  3352. '  Return Values:
  3353. '     None
  3354. '
  3355. '  Remarks:
  3356. '     This routine assumes that the viewport is set to the borders of
  3357. '     the window to be rendered
  3358. '
  3359. '=================================================================
  3360. SUB clRenderWindow (W AS RegionType)
  3361. SHARED PaletteC%(), PaletteB%()
  3362.  
  3363.     ' Set window since the size of the viewport is unknown and draw
  3364.     ' a filled box of the background color specified by the window
  3365.     ' definition:
  3366.     WINDOW (0, 0)-(1, 1)
  3367.     LINE (0, 0)-(1, 1), PaletteC%(clMap2Pal%(W.Background)), BF
  3368.  
  3369.     ' Draw a border if specified:
  3370.     IF W.Border = cYes THEN
  3371.         LINE (0, 0)-(1, 1), PaletteC%(clMap2Pal%(W.BorderColor)), B, PaletteB%(clMap2Pal%(W.BorderStyle))
  3372.     END IF
  3373.  
  3374. END SUB
  3375.  
  3376. '=== clScaleAxis - Calculates minimum, maximum and scale factor for an axis
  3377. '
  3378. '  Arguments:
  3379. '     A        - An AxisType variable
  3380. '
  3381. '     AxisMode%- cCategory or cValue
  3382. '
  3383. '     D1(2)    - Two-dimensional array of values to be scaled
  3384. '
  3385. '  Return Values:
  3386. '     ScaleMin, ScaleMax, ScaleFactor, and ScaleTitle elements in
  3387. '     axis variable will be altered if it is a category axis or
  3388. '     AutoScale is Yes.
  3389. '
  3390. '=================================================================
  3391. SUB clScaleAxis (Axis AS AxisType, AxisMode%, D1())
  3392. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  3393.  
  3394.     ' If this is a category axis then ignore all the flags and force
  3395.     ' scale parameters to those needed by charting routines:
  3396.     IF AxisMode% = cCategory THEN
  3397.         Axis.ScaleMin = 0
  3398.         Axis.ScaleMax = 1
  3399.         Axis.ScaleFactor = 1
  3400.         Axis.ScaleTitle.Title = ""
  3401.         EXIT SUB
  3402.     END IF
  3403.  
  3404.     ' If AutoScale isn't Yes then exit:
  3405.     IF Axis.AutoScale <> cYes THEN EXIT SUB
  3406.  
  3407.     ' AutoScale was specified, calculate the different scale variables
  3408.     ' Set maximum and minimum to defaults.
  3409.  
  3410.     ' Initialize the value- and row-minimum and maximum values to zero:
  3411.     VMin = 0
  3412.     VMax = 0
  3413.  
  3414.     RMin = 0
  3415.     RMax = 0
  3416.  
  3417.     ' Compare data values for minimum and maximum:
  3418.     FOR Row% = 1 TO GP.NVals
  3419.  
  3420.         ' Initialize positive and negative sum variables:
  3421.         RSumPos = 0
  3422.         RSumNeg = 0
  3423.  
  3424.         ' Evaluate the value from this row in each series:
  3425.         FOR Column% = 1 TO GP.NSeries
  3426.  
  3427.             ' Get the value from the data array:
  3428.             V = D1(Row%, Column%)
  3429.  
  3430.             ' Process values that aren't missing only:
  3431.             IF V <> cMissingValue THEN
  3432.  
  3433.                 ' Add positive values to positive sum and negative ones to
  3434.                 ' negative sum:
  3435.                 IF V > 0 THEN RSumPos = RSumPos + V
  3436.                 IF V < 0 THEN RSumNeg = RSumNeg + V
  3437.  
  3438.                 ' Compare the value against current maximum and minimum and
  3439.                 ' replace them if appropriate:
  3440.                 IF V < VMin THEN VMin = V
  3441.                 IF V > VMax THEN VMax = V
  3442.  
  3443.             END IF
  3444.  
  3445.         NEXT Column%
  3446.  
  3447.         ' Compare the positive and negative sums for this row with the
  3448.         ' current row maximum and minimum and replace them if appropriate:
  3449.         IF RSumNeg < RMin THEN RMin = RSumNeg
  3450.         IF RSumPos > RMax THEN RMax = RSumPos
  3451.  
  3452.     NEXT Row%
  3453.  
  3454.     ' If the chart style is one, meaning that the data isn't stacked for
  3455.     ' bar and column charts, or it is a line or scatter chart then the scale
  3456.     ' minimum and maximum are the minimum and maximum values found.
  3457.     ' Each value is adjusted so the data is not drawn on or beyond the
  3458.     ' border of the data window:
  3459.     IF GE.ChartStyle = 1 OR GE.ChartType = cLine OR GE.ChartType = cScatter THEN
  3460.         IF VMin < 0 THEN
  3461.             Axis.ScaleMin = VMin - .01 * (VMax - VMin)
  3462.         END IF
  3463.         IF VMax > 0 THEN
  3464.             Axis.ScaleMax = VMax + .01 * (VMax - VMin)
  3465.         END IF
  3466.  
  3467.     ' Otherwise, the scale minimum and maximum are the minimum and maximum
  3468.     ' sums of the data for each row:
  3469.     ELSE
  3470.         IF RMin < 0 THEN
  3471.             Axis.ScaleMin = RMin - .01 * (RMax - RMin)
  3472.         END IF
  3473.         IF RMax > 0 THEN
  3474.             Axis.ScaleMax = RMax + .01 * (RMax - RMin)
  3475.         END IF
  3476.     END IF
  3477.  
  3478.     ' If no data then force range to be non-zero:
  3479.     IF Axis.ScaleMin = Axis.ScaleMax THEN Axis.ScaleMax = 1
  3480.  
  3481.     ' Adjust the scale limits by ScaleFactor if required:
  3482.     clAdjustScale Axis
  3483.  
  3484. END SUB
  3485.  
  3486. '=== clSelectChartFont - Selects a font to use and gets info about it
  3487. '
  3488. '  Arguments:
  3489. '     N%    -  Font number to use
  3490. '
  3491. '  Return Values:
  3492. '     none
  3493. '
  3494. '=================================================================
  3495. SUB clSelectChartFont (N%)
  3496. SHARED GFI AS FontInfo
  3497.   
  3498.     ' Select the font and get information about it:
  3499.     SelectFont N%
  3500.     GetFontInfo GFI
  3501. END SUB
  3502.  
  3503. '=== clSelectChartWindow - Sets viewport to chart window
  3504. '
  3505. '  Arguments:
  3506. '     Env         - A ChartEnvironment variable
  3507. '
  3508. '  Return Values:
  3509. '     None
  3510. '
  3511. '  Remarks:
  3512. '     This routine erases any previous viewport
  3513. '
  3514. '=================================================================
  3515. SUB clSelectChartWindow ()
  3516. SHARED GP AS GlobalParams
  3517.  
  3518.     ' Set viewport to chart window:
  3519.     VIEW (GP.CwX1, GP.CwY1)-(GP.CwX2, GP.CwY2)
  3520.  
  3521. END SUB
  3522.  
  3523. '=== clSelectRelWindow - Sets viewport to window relative to chart window
  3524. '
  3525. '  Arguments:
  3526. '     Env   - A ChartEnvironment variable
  3527. '
  3528. '     W     - RegionType variable of window to set
  3529. '
  3530. '  Return Values:
  3531. '     None
  3532. '
  3533. '  Remarks:
  3534. '     This routine erases any previous viewport
  3535. '
  3536. '=================================================================
  3537. SUB clSelectRelWindow (W AS RegionType)
  3538. SHARED GP AS GlobalParams
  3539.  
  3540.     ' New viewport is defined relative to the current one:
  3541.     VIEW (GP.CwX1 + W.X1, GP.CwY1 + W.Y1)-(GP.CwX1 + W.X2, GP.CwY1 + W.Y2)
  3542.  
  3543. END SUB
  3544.  
  3545. '=== clSetAxisModes - Sets axis modes for X- and Y-axis according to
  3546. '                   ChartType
  3547. '
  3548. '  Arguments:
  3549. '     None
  3550. '
  3551. '  Return Values:
  3552. '     Alters XAxis and YAxis axis modes
  3553. '
  3554. '=================================================================
  3555. SUB clSetAxisModes ()
  3556. SHARED GE AS ChartEnvironment
  3557. SHARED GP AS GlobalParams
  3558.  
  3559.     SELECT CASE GE.ChartType
  3560.  
  3561.         CASE cBar:
  3562.             GP.XMode = cValue
  3563.             GP.YMode = cCategory
  3564.  
  3565.         CASE cColumn, cLine:
  3566.             GP.XMode = cCategory
  3567.             GP.YMode = cValue
  3568.  
  3569.         CASE cScatter:
  3570.             GP.XMode = cValue
  3571.             GP.YMode = cValue
  3572.  
  3573.         CASE cPie:
  3574.             GP.XMode = cCategory
  3575.             GP.YMode = cCategory
  3576.  
  3577.     END SELECT
  3578.  
  3579. END SUB
  3580.  
  3581. '=== clSetCharColor - Sets color for DRAW characters
  3582. '
  3583. '  Arguments:
  3584. '     N%    -  Color number
  3585. '
  3586. '  Return Values:
  3587. '     None
  3588. '
  3589. '=================================================================
  3590. SUB clSetCharColor (N%)
  3591. SHARED PaletteC%()
  3592.  
  3593.     ' Check for valid color number then set color if correct:
  3594.     SetGTextColor PaletteC%(clMap2Pal%(N%))
  3595.  
  3596. END SUB
  3597.  
  3598. '=== clSetChartFont - Selects the specified font
  3599. '
  3600. '  Arguments:
  3601. '     N%    -  Number of loaded font to select
  3602. '
  3603. '  Return Values:
  3604. '     none
  3605. '
  3606. '=================================================================
  3607. SUB clSetChartFont (N AS INTEGER)
  3608. SHARED GFI AS FontInfo
  3609.  
  3610.     ' Select font and get information on it:
  3611.     SelectFont N%
  3612.     GetFontInfo GFI
  3613.  
  3614. END SUB
  3615.  
  3616. '=== clSetError - Sets the ChartLib error variable
  3617. '
  3618. '  Arguments:
  3619. '     ErrNo    - The error number to set
  3620. '
  3621. '  Return Values:
  3622. '     Sets ChartErr to ErrNo
  3623. '
  3624. '=================================================================
  3625. SUB clSetError (ErrNo AS INTEGER)
  3626.  
  3627.     ChartErr = ErrNo
  3628.  
  3629. END SUB
  3630.  
  3631. '=== clSetGlobalParams - Sets some global parameters that other routines use
  3632. '
  3633. '  Arguments:
  3634. '     None
  3635. '
  3636. '  Return Values:
  3637. '     GP.ValLenX and GP.ValLenY are altered
  3638. '
  3639. '=================================================================
  3640. SUB clSetGlobalParams ()
  3641. SHARED GP AS GlobalParams, GE AS ChartEnvironment
  3642.  
  3643.     ' Figure out longest label on X axis:
  3644.     clSetChartFont GE.XAxis.TicFont
  3645.     SF = GE.XAxis.ScaleMin
  3646.     Len1 = GetGTextLen(clVal2Str$(SF, GE.XAxis.TicDecimals, GE.XAxis.TicFormat))
  3647.     SF = GE.XAxis.ScaleMax
  3648.     Len2 = GetGTextLen(clVal2Str$(SF, GE.XAxis.TicDecimals, GE.XAxis.TicFormat))
  3649.     GP.ValLenX = clMaxVal(Len1, Len2)
  3650.  
  3651.     ' Figure out longest label on Y axis:
  3652.     clSetChartFont GE.YAxis.TicFont
  3653.     SF = GE.YAxis.ScaleMin
  3654.     Len1 = GetGTextLen(clVal2Str$(SF, GE.YAxis.TicDecimals, GE.YAxis.TicFormat))
  3655.     SF = GE.YAxis.ScaleMax
  3656.     Len2 = GetGTextLen(clVal2Str$(SF, GE.YAxis.TicDecimals, GE.YAxis.TicFormat))
  3657.     GP.ValLenY = clMaxVal(Len1, Len2)
  3658.  
  3659. END SUB
  3660.  
  3661. '=== clSizeDataWindow - Calculates general data window size
  3662. '
  3663. '  Arguments:
  3664. '     Cat$(1)  - One-dimensional array of category labels (only
  3665. '                used if one of the axes is a category one)
  3666. '
  3667. '  Return Values:
  3668. '     The X1, Y1, X2, Y2 elements of the GE variable will be
  3669. '     set to the data window coordinates
  3670. '
  3671. '=================================================================
  3672. SUB clSizeDataWindow (Cat$())
  3673. SHARED GE AS ChartEnvironment
  3674. SHARED GP AS GlobalParams
  3675. SHARED GFI AS FontInfo
  3676. SHARED TTitleLayout AS TitleLayout
  3677. SHARED XTitleLayout AS TitleLayout
  3678. SHARED YTitleLayout AS TitleLayout
  3679.  
  3680.     ' *** TOP
  3681.     ' Adjust the top of the data window:
  3682.     DTop% = TTitleLayout.TotalSize
  3683.  
  3684.     ' *** LEFT
  3685.     ' Do left side:
  3686.     DLeft% = YTitleLayout.TotalSize
  3687.  
  3688.     ' Add room for axis labels if the axis is labeled and not a pie chart:
  3689.     IF GE.ChartType <> cPie THEN
  3690.         IF GE.YAxis.Labeled = cYes THEN
  3691.  
  3692.             ' Get the correct font:
  3693.             clSetChartFont GE.YAxis.TicFont
  3694.  
  3695.             ' If it is a category axis then add longest category label:
  3696.             IF GP.YMode = cCategory THEN
  3697.                 DLeft% = DLeft% + clMaxStrLen%(Cat$(), 1, GP.NVals) + .5 * GFI.MaxWidth
  3698.  
  3699.             ' If it a value axis just add characters for label (plus 1/2 for
  3700.             ' spacing):
  3701.             ELSE
  3702.                 DLeft% = DLeft% + GP.ValLenY + (.5 * GFI.MaxWidth)
  3703.             END IF
  3704.  
  3705.         ELSEIF GP.XMode = cValue AND GE.XAxis.Labeled = cYes THEN
  3706.  
  3707.             ' Then space over 1/2 of the leftmost label on the X Axis if it's
  3708.             ' a value axis; if it's a category axis assume the label will be
  3709.             ' correct:
  3710.             DLeft% = DLeft% + GP.ValLenX \ 2
  3711.         END IF
  3712.     END IF
  3713.  
  3714.     ' *** RIGHT
  3715.     ' For the right, space over 8 pixels from the right:
  3716.     DRight% = 12
  3717.  
  3718.     ' Then space over 1/2 of the rightmost label on the X Axis if it's
  3719.     ' a value axis; if it's a category axis assume the label will be
  3720.     ' correct:
  3721.     IF GP.XMode = cValue AND GE.XAxis.Labeled = cYes THEN
  3722.         DRight% = DRight% + (GP.ValLenX) \ 2
  3723.     END IF
  3724.  
  3725.     DRight% = GP.ChartWid - DRight%
  3726.  
  3727.     ' *** YTIC MARKS
  3728.     ' Finally, adjust the window coordinates for tic marks (if it's not a
  3729.     ' pie chart):
  3730.     IF GE.ChartType <> cPie THEN
  3731.         IF GE.YAxis.Labeled = cYes THEN
  3732.             DLeft% = DRight% - (DRight% - DLeft%) / (1 + cTicSize)
  3733.         END IF
  3734.     END IF
  3735.  
  3736.     ' *** LEGEND
  3737.     ' Account for the legend if its on the right:
  3738.     IF GE.Legend.Legend = cYes AND GP.MSeries = cYes THEN
  3739.         IF GE.Legend.Place = cRight THEN
  3740.             A% = GE.Legend.LegendWindow.X1
  3741.             DRight% = DRight% - ABS(GP.ChartWid - A%)
  3742.         END IF
  3743.     END IF
  3744.  
  3745.     ' Now we have DLeft%, DRight% we can check if the labels fit on the
  3746.     ' X axis or if we need to put them on two rows:
  3747.     GP.XStagger = cFalse
  3748.     IF GP.XMode = cCategory AND GE.ChartType <> cPie THEN
  3749.         clSetChartFont GE.XAxis.TicFont
  3750.         TicInterval% = (DRight% - DLeft%) \ GP.NVals
  3751.         IF clMaxStrLen%(Cat$(), 1, GP.NVals) + .5 * GFI.MaxWidth > TicInterval% THEN
  3752.             GP.XStagger = cTrue
  3753.         END IF
  3754.     END IF
  3755.  
  3756.     ' If we do have to stagger, check if there is enough space to the
  3757.     ' left and right for long categories.  Make adjustments as necessary:
  3758.     IF GP.XStagger THEN
  3759.         LenLeft% = GetGTextLen%(Cat$(1)) + GFI.AvgWidth
  3760.         LenRight% = GetGTextLen%(Cat$(GP.NVals)) + GFI.AvgWidth
  3761.         SizeRight% = cTrue
  3762.         SizeLeft% = cTrue
  3763.         OldRight% = DRight%
  3764.         OldLeft% = DLeft%
  3765.         DO WHILE SizeRight% OR SizeLeft%
  3766.             IF LenRight% - TicInterval% > 2 * (GP.ChartWid - DRight%) AND 2 * (GP.ChartWid - DRight%) < TicInterval% THEN
  3767.                 SizeRight% = cTrue
  3768.             ELSE
  3769.                 SizeRight% = cFalse
  3770.             END IF
  3771.             IF SizeRight% THEN
  3772.                 TicInterval% = (2 * (GP.ChartWid - DLeft%) - LenRight%) \ (2 * GP.NVals - 1)
  3773.                 IF LenRight% > 2 * TicInterval% THEN
  3774.                     TicInterval% = (GP.ChartWid - DLeft%) / (GP.NVals + .5)
  3775.                 END IF
  3776.                 DRight% = DLeft% + GP.NVals * TicInterval%
  3777.             END IF
  3778.             IF LenLeft% - TicInterval% > 2 * DLeft% AND 2 * DLeft% < TicInterval% THEN
  3779.                 SizeLeft% = cTrue
  3780.             ELSE
  3781.                 SizeLeft% = cFalse
  3782.             END IF
  3783.             IF SizeLeft% THEN
  3784.                 TicInterval% = (2 * DRight% - LenLeft%) \ (2 * GP.NVals - 1)
  3785.                 IF LenLeft% > 2 * TicInterval% THEN
  3786.                     TicInterval% = DRight% / (GP.NVals + .5)
  3787.                 END IF
  3788.                 DLeft% = DRight% - GP.NVals * TicInterval%
  3789.             END IF
  3790.  
  3791.             ' Make sure we haven't gone too far on either side:
  3792.             IF DRight% > OldRight% THEN
  3793.                 DRight% = OldRight%
  3794.             END IF
  3795.             IF DLeft% < OldLeft% THEN
  3796.                 DLeft% = OldLeft%
  3797.             END IF
  3798.  
  3799.             ' Check if there has been a change, if not, we are done:
  3800.             IF ABS(ChangeRight% - DRight%) + ABS(ChangeLeft% - DLeft%) > 0 THEN
  3801.                 EXIT DO
  3802.             ELSE
  3803.                 ChangeRight% = DRight%
  3804.                 ChangeLeft% = DLeft%
  3805.             END IF
  3806.         LOOP
  3807.     END IF
  3808.  
  3809.     ' *** BOTTOM
  3810.     DBot% = XTitleLayout.TotalSize
  3811.  
  3812.     ' If axis is labeled (and not a pie chart), add row for tic
  3813.     ' labels + 1/2 row spacing:
  3814.     IF GE.XAxis.Labeled = cYes AND GE.ChartType <> cPie THEN
  3815.         IF GP.XStagger = cTrue THEN
  3816.             DBot% = DBot% + 3 * GFI.PixHeight
  3817.         ELSE
  3818.             DBot% = DBot% + 1.5 * GFI.PixHeight
  3819.         END IF
  3820.     END IF
  3821.  
  3822.     ' Make the setting relative to the chart window:
  3823.     DBot% = GP.ChartHgt - 1 - DBot%
  3824.  
  3825.  
  3826.     ' *** XTIC MARKS
  3827.     ' Finally, adjust the window coordinates for tic marks (if it's not a
  3828.     ' pie chart):
  3829.     IF GE.ChartType <> cPie THEN
  3830.         IF GE.XAxis.Labeled = cYes THEN
  3831.             DBot% = DTop% + (DBot% - DTop%) / (1 + cTicSize)
  3832.         END IF
  3833.  
  3834.     END IF
  3835.  
  3836.     ' *** LEGEND
  3837.     ' Account for the legend if its on the bottom:
  3838.     IF GE.Legend.Legend = cYes AND GP.MSeries = cYes THEN
  3839.         IF GE.Legend.Place = cBottom THEN
  3840.             A% = GE.Legend.LegendWindow.Y1
  3841.             DBot% = DBot% - ABS(GP.ChartHgt - A%)
  3842.         END IF
  3843.     END IF
  3844.  
  3845.     ' Install values in the DataWindow definition:
  3846.     GE.DataWindow.X1 = DLeft%
  3847.     GE.DataWindow.X2 = DRight%
  3848.     GE.DataWindow.Y1 = DTop%
  3849.     GE.DataWindow.Y2 = DBot%
  3850.  
  3851.     ' If the window is invalid then set error:
  3852.     IF DLeft% >= DRight% OR DTop% >= DBot% THEN
  3853.         clSetError cBadDataWindow
  3854.     END IF
  3855.  
  3856. END SUB
  3857.  
  3858. '=== clSpaceTics - Calculates TicInterval
  3859. '
  3860. '  Arguments:
  3861. '     None
  3862. '
  3863. '  Return Values:
  3864. '     The TicInterval will be altered
  3865. '
  3866. '  Remarks:
  3867. '     The TicInterval is the distance between tic marks in WORLD
  3868. '     coordinates (i.e. the coordinates your data are in)
  3869. '
  3870. '=================================================================
  3871. SUB clSpaceTics ()
  3872. SHARED GE AS ChartEnvironment, GP AS GlobalParams
  3873. SHARED GFI AS FontInfo
  3874.  
  3875.     ' X-Axis:
  3876.     ' Calculate the length of the axis and of the longest tic label.  Then,
  3877.     ' use that information to calculate the number of tics that will fit:
  3878.     clSetChartFont GE.XAxis.TicFont
  3879.     AxisLen% = GE.DataWindow.X2 - GE.DataWindow.X1 + 1
  3880.     TicWid% = GP.ValLenX + GFI.MaxWidth
  3881.     clSpaceTicsA GE.XAxis, GP.XMode, AxisLen%, TicWid%
  3882.  
  3883.     ' Y-Axis:
  3884.     ' Same procedure as above:
  3885.     clSetChartFont GE.YAxis.TicFont
  3886.     AxisLen% = GE.DataWindow.Y2 - GE.DataWindow.Y1 + 1
  3887.     TicWid% = 2 * GFI.Ascent
  3888.     clSpaceTicsA GE.YAxis, GP.YMode, AxisLen%, TicWid%
  3889.  
  3890. END SUB
  3891.  
  3892. '=== clSpaceTicsA - Figures out TicInterval for an axis
  3893. '
  3894. '  Arguments:
  3895. '     Axis     -  An AxisType variable to space tics for
  3896. '
  3897. '     AxisMode%-  cCategory or cValue
  3898. '
  3899. '     AxisLen% -  Length of the axis in pixels
  3900. '
  3901. '  Return Values:
  3902. '     The TicInterval value may be changed for an axis
  3903. '
  3904. '  Remarks:
  3905. '     The TicInterval is the distance between tic marks in adjusted world
  3906. '     coordinates (i.e. the coordinates your data are in scaled by
  3907. '     ScaleFactor and adjusted by LogBase if it is a log axis).
  3908. '
  3909. '=================================================================
  3910. SUB clSpaceTicsA (Axis AS AxisType, AxisMode%, AxisLen%, TicWid%)
  3911. SHARED GP AS GlobalParams
  3912.  
  3913.     ' If this is a category axis the tic interval is 1
  3914.     ' divided by the number-of-categories:
  3915.     IF AxisMode% = cCategory THEN
  3916.         Axis.TicInterval = 1 / GP.NVals
  3917.         EXIT SUB
  3918.     END IF
  3919.  
  3920.     ' Otherwise, if we're supposed to scale this axis then the tic interval
  3921.     ' depends on how many will fit and some aesthetic considerations:
  3922.     IF Axis.AutoScale = cYes THEN
  3923.       
  3924.         ' Figure which is bigger in absolute value between scale maximum
  3925.         ' and minimum:
  3926.         MaxRange = ABS(Axis.ScaleMax)
  3927.         IF ABS(Axis.ScaleMin) > MaxRange THEN MaxRange = ABS(Axis.ScaleMin)
  3928.  
  3929.         ' Calculate the maximum number of tic marks that will fit:
  3930.         MaxTics% = INT(AxisLen% / TicWid%)
  3931.  
  3932.         ' If the maximum number of tics is one or less set the tic
  3933.         ' interval to the axis range and the number of tics to one:
  3934.         IF MaxTics% <= 1 THEN
  3935.             NumTics% = 1
  3936.             TicInterval = Axis.ScaleMax - Axis.ScaleMin
  3937.       
  3938.         ELSE
  3939.             ' Guess that the tic interval is equal to 1/10th of the order
  3940.             ' of magnitude of the largest of the scale max or min:
  3941.             TicInterval = .1 * 10 ^ INT(LOG(MaxRange) / LOG(10!))
  3942.  
  3943.             ' If this doesn't result in too many tic marks then OK. Otherwise
  3944.             ' multiply the tic interval by 2 and 5 alternatively until the
  3945.             ' number of tic marks falls into the acceptable range.
  3946.             NextStep% = 2
  3947.             ScaleRange = Axis.ScaleMax - Axis.ScaleMin
  3948.             DO
  3949.                 NumTics% = -INT(-ScaleRange / TicInterval)
  3950.                 IF (NumTics% <= MaxTics%) THEN EXIT DO
  3951.                 TicInterval = TicInterval * NextStep%
  3952.                 NextStep% = 7 - NextStep%
  3953.             LOOP UNTIL NumTics% <= MaxTics%
  3954.         END IF
  3955.  
  3956.         ' Set Axis.TicInterval and adjust scale maximum and minimum:
  3957.         Axis.TicInterval = TicInterval
  3958.         IF ABS(TicInterval) < 1 THEN
  3959.             Axis.TicDecimals = -INT(-ABS(LOG(1.1 * TicInterval) / LOG(10!)))
  3960.         END IF
  3961.  
  3962.         Axis.ScaleMax = -INT(-Axis.ScaleMax / TicInterval) * TicInterval
  3963.         Axis.ScaleMin = INT(Axis.ScaleMin / TicInterval) * TicInterval
  3964.     END IF
  3965.   
  3966. END SUB
  3967.  
  3968. '=== clTitleXAxis - Draws titles on X axis (AxisTitle and ScaleTitle)
  3969. '
  3970. '  Arguments:
  3971. '     Axis  -  AxisType variable describing axis
  3972. '
  3973. '     X1%   -  Left of DataWindow
  3974. '
  3975. '     X2%   -  Right of DataWindow
  3976. '
  3977. '     YBoundry%   -  Top boundry of title block
  3978. '
  3979. '=================================================================
  3980. SUB clTitleXAxis (Axis AS AxisType, X1%, X2%, YBoundry%)
  3981. SHARED GFI AS FontInfo
  3982. SHARED XTitleLayout AS TitleLayout
  3983.  
  3984.     CH% = GFI.PixHeight
  3985.     CW% = GFI.MaxWidth
  3986.  
  3987.     ' Set position of first title:
  3988.     Y% = YBoundry% + XTitleLayout.Top
  3989.  
  3990.     ' Loop through the two titles (AxisTitle and ScaleTitle), printing
  3991.     ' them if they aren't blank:
  3992.     FOR i% = 1 TO 2
  3993.  
  3994.         ' Get the test, color, and justification for the title to be printed:
  3995.         SELECT CASE i%
  3996.  
  3997.             CASE 1:  ' AxisTitle
  3998.                 Txt$ = Axis.AxisTitle.Title
  3999.                 c% = Axis.AxisTitle.TitleColor
  4000.                 J% = Axis.AxisTitle.Justify
  4001.                 F% = Axis.AxisTitle.TitleFont
  4002.                 Lead% = XTitleLayout.Middle
  4003.                 
  4004.             CASE 2:  ' ScaleTitle
  4005.                 Txt$ = Axis.ScaleTitle.Title
  4006.                 c% = Axis.ScaleTitle.TitleColor
  4007.                 J% = Axis.ScaleTitle.Justify
  4008.                 F% = Axis.ScaleTitle.TitleFont
  4009.                 Lead% = XTitleLayout.Bottom
  4010.  
  4011.         END SELECT
  4012.         clSetChartFont F%
  4013.         Txt$ = RTRIM$(Txt$)
  4014.         TxtLen% = GetGTextLen(Txt$)
  4015.  
  4016.         ' If the title isn't all blank:
  4017.         IF TxtLen% <> 0 THEN
  4018.  
  4019.             ' Set the title's color:
  4020.             clSetCharColor c%
  4021.  
  4022.             ' Calculate x position of title's first character depending on
  4023.             ' the justification flag:
  4024.             SELECT CASE J%
  4025.                 CASE cLeft:   X% = X1%
  4026.                 CASE cCenter: X% = ((X1% + X2%) - TxtLen%) / 2
  4027.                 CASE ELSE:    X% = X2% - TxtLen%
  4028.             END SELECT
  4029.  
  4030.             ' Write out the text:
  4031.             clHPrint X%, Y%, Txt$
  4032.  
  4033.             ' Move down to the next title position:
  4034.             Y% = Y% + GFI.PixHeight + XTitleLayout.Middle
  4035.  
  4036.         END IF
  4037.  
  4038.     NEXT i%
  4039.  
  4040. END SUB
  4041.  
  4042. '=== clTitleYAxis - Draws titles on Y axis (AxisTitle and ScaleTitle)
  4043. '
  4044. '  Arguments:
  4045. '     Axis  -  AxisType variable describing axis
  4046. '
  4047. '     Y1%   -  Top of DataWindow
  4048. '
  4049. '     Y2%   -  Bottom of DataWindow
  4050. '
  4051. '  Return Values:
  4052. '
  4053. '=================================================================
  4054. STATIC SUB clTitleYAxis (Axis AS AxisType, Y1%, Y2%)
  4055. SHARED GFI AS FontInfo
  4056. SHARED YTitleLayout AS TitleLayout
  4057.  
  4058.   
  4059.     ' Set position for first title:
  4060.     X% = YTitleLayout.Top
  4061.  
  4062.     ' Loop through the two titles (AxisTitle and ScaleTitle), printing
  4063.     ' them if they aren't blank:
  4064.     FOR i% = 1 TO 2
  4065.  
  4066.         ' Get the test, color, and justification for the title to be printed:
  4067.         SELECT CASE i%
  4068.  
  4069.             CASE 1:  ' AxisTitle
  4070.                 Txt$ = Axis.AxisTitle.Title
  4071.                 c% = Axis.AxisTitle.TitleColor
  4072.                 J% = Axis.AxisTitle.Justify
  4073.                 F% = Axis.AxisTitle.TitleFont
  4074.                 Lead% = YTitleLayout.TitleOne + YTitleLayout.Middle
  4075.  
  4076.             CASE 2:  ' ScaleTitle
  4077.                 Txt$ = Axis.ScaleTitle.Title
  4078.                 c% = Axis.ScaleTitle.TitleColor
  4079.                 J% = Axis.ScaleTitle.Justify
  4080.                 F% = Axis.ScaleTitle.TitleFont
  4081.                 Lead% = 0
  4082.  
  4083.         END SELECT
  4084.         clSetChartFont F%
  4085.         Txt$ = RTRIM$(Txt$)
  4086.         TxtLen% = GetGTextLen(Txt$)
  4087.  
  4088.         IF TxtLen% <> 0 THEN
  4089.  
  4090.             ' Set title's color:
  4091.             clSetCharColor c%
  4092.  
  4093.             ' Calculate y position of title's first character depending on
  4094.             ' the justification flag:
  4095.             SELECT CASE J%
  4096.                 CASE cLeft:   Y% = Y2%
  4097.                 CASE cCenter: Y% = ((Y1% + Y2%) + TxtLen%) / 2
  4098.                 CASE ELSE:    Y% = Y1% + (TxtLen% - 1)
  4099.             END SELECT
  4100.  
  4101.             ' Write out the text:
  4102.             clVPrint X%, Y%, Txt$
  4103.  
  4104.             ' Move to next title position:
  4105.             X% = X% + Lead%
  4106.  
  4107.         END IF
  4108.  
  4109.     NEXT i%
  4110.  
  4111. END SUB
  4112.  
  4113. '=== clUnFlagSystem - Sets GP.SysFlag to cNo
  4114. '
  4115. '  Arguments:
  4116. '     None
  4117. '
  4118. '  Return Values:
  4119. '     Alters the value of GP.SysFlag
  4120. '
  4121. '=================================================================
  4122. SUB clUnFlagSystem ()
  4123. SHARED GP AS GlobalParams
  4124.  
  4125.     GP.SysFlag = cNo
  4126.  
  4127. END SUB
  4128.  
  4129. '=== clVal2Str$ - Converts a single precision value to a string
  4130. '
  4131. '  Arguments:
  4132. '     X        -  The value to convert
  4133. '
  4134. '     Places%  -  The number of places after the decimal to produce
  4135. '
  4136. '     Format%  -  1 For normal, other than 1 for exponential
  4137. '
  4138. '  Return Values:
  4139. '     Returns a string representation of the input number
  4140. '
  4141. '=================================================================
  4142. FUNCTION clVal2Str$ (X, Places%, Format%)
  4143.  
  4144.     ' Make a local copy of the value:
  4145.     XX = ABS(X)
  4146.  
  4147.     ' Force format to exponential if that is specified or number is
  4148.     ' bigger than a long integer will hold (2^31-1):
  4149.     IF Format% <> cNormFormat OR XX >= 2 ^ 31 THEN
  4150.  
  4151.         ' For exponential format calculate the exponent that will make
  4152.         ' one decimal to left of decimal place.  This is done by truncating
  4153.         ' the log (base 10) of XX:
  4154.         IF XX = 0 THEN ExpX = 0 ELSE ExpX = INT(LOG(XX) / LOG(10))
  4155.         XX = XX / (10 ^ ExpX)
  4156.  
  4157.         ' If no decimals are specified then a number of 9.5x will be
  4158.         ' rounded up to 10 leaving two places to left of decimal so check
  4159.         ' for that and if that occurs divide number by 10 and add 1 to exponent:
  4160.         IF Places% <= 0 AND CLNG(XX) > 9 THEN
  4161.             XX = XX / 10
  4162.             ExpX = ExpX + 1
  4163.         END IF
  4164.  
  4165.     END IF
  4166.  
  4167.     ' If no decimal places are specified then generate a rounded integer:
  4168.     IF Places% <= 0 THEN
  4169.         ValStr$ = LTRIM$(STR$(CLNG(XX)))
  4170.  
  4171.     ' If decimal places are called for, round number to requisite number of
  4172.     ' decimals and generate string:
  4173.     ELSE
  4174.  
  4175.         ' Limit places after decimal to six:
  4176.         DP% = Places%
  4177.         IF DP% > 6 THEN DP% = 6
  4178.         RF% = 10 ^ DP%
  4179.  
  4180.         ' Figure out integer portion:
  4181.         IntX = FIX(XX)
  4182.  
  4183.         ' Round the fractional part to correct number of decimals.  If
  4184.         ' the fraction carries to the 1's place in the rounding
  4185.         ' adjust IntX by adding 1:
  4186.         FracX = CLNG((1 + XX - IntX) * RF%)
  4187.         IF FracX >= 2 * RF% THEN
  4188.             IntX = IntX + 1
  4189.         END IF
  4190.  
  4191.         'Finally, generate the output string:
  4192.         ValStr$ = LTRIM$(STR$(IntX)) + "." + MID$(STR$(FracX), 3)
  4193.  
  4194.     END IF
  4195.  
  4196.     ' Add exponent ending if format is exponent:
  4197.     IF Format% <> cNormFormat OR ABS(X) > 2 ^ 31 THEN
  4198.         ValStr$ = ValStr$ + "E"
  4199.         IF ExpX >= 0 THEN ValStr$ = ValStr$ + "+"
  4200.         ValStr$ = ValStr$ + LTRIM$(STR$(ExpX))
  4201.     END IF
  4202.  
  4203.     ' Add minus sign if appropriate:
  4204.     IF X < 0 AND VAL(ValStr$) <> 0 THEN ValStr$ = "-" + ValStr$
  4205.     clVal2Str$ = ValStr$
  4206.  
  4207. END FUNCTION
  4208.  
  4209. '=== clVPrint - Prints text vertically on the screen
  4210. '
  4211. '  Arguments:
  4212. '     X     -  X position of lower left of first char (in absolute screen
  4213. '              coordinates)
  4214. '
  4215. '     Y     -  Y position of lower left of first char (in absolute screen
  4216. '              coordinates)
  4217. '
  4218. '     Txt$  -  Text to print
  4219. '
  4220. '  Return Values:
  4221. '     None
  4222. '
  4223. '=================================================================
  4224. SUB clVPrint (X%, Y%, Txt$)
  4225.  
  4226.     ' Map the input coordinates relative to the current viewport:
  4227.     X = PMAP(X%, 2)
  4228.     Y = PMAP(Y%, 3)
  4229.  
  4230.     ' Print text out vertically:
  4231.     SetGTextDir 1
  4232.     TextLen% = OutGText(X, Y, Txt$)
  4233.     SetGTextDir 0
  4234.   
  4235. END SUB
  4236.  
  4237. '=== DefaultChart - Sets up the ChartEnvironment variable to generate a
  4238. '                   default chart of the type and style specified
  4239. '
  4240. '  Arguments:
  4241. '     Env        - A ChartEnvironment variable
  4242. '
  4243. '     ChartType  - The chart type desired: 1=Bar, 2=Column, 3=Line,
  4244. '                  4=Scatter, 5=Pie
  4245. '
  4246. '     ChartStyle - The chart style (depends on type, see README file)
  4247. '
  4248. '
  4249. '  Return Values:
  4250. '     Elements of Env variable are set to default values
  4251. '
  4252. '  Remarks:
  4253. '     This subprogram should be called to initialize the ChartEnvironment
  4254. '     variable before a charting routine is called.
  4255. '
  4256. '=================================================================
  4257. SUB DefaultChart (Env AS ChartEnvironment, ChartType AS INTEGER, ChartStyle AS INTEGER)
  4258.  
  4259. SHARED DTitle AS TitleType, DWindow AS RegionType
  4260. SHARED DAxis AS AxisType, DLegend AS LegendType
  4261.  
  4262.     ' Clear any previous chart errors:
  4263.     clClearError
  4264.  
  4265.     ' Check initialization:
  4266.     clChkInit
  4267.  
  4268.   ' Put type in environment:
  4269.     IF ChartType < 1 OR ChartType > 5 THEN
  4270.         clSetError cBadType
  4271.         EXIT SUB
  4272.     END IF
  4273.     Env.ChartType = ChartType
  4274.  
  4275.     ' Put chart style in environment:
  4276.     IF ChartStyle < 1 OR ChartStyle > 2 THEN
  4277.         clSetError cBadStyle
  4278.         ChartStyle = 1
  4279.     END IF
  4280.     Env.ChartStyle = ChartStyle
  4281.  
  4282.     ' Set elements of chart to default:
  4283.     Env.DataFont = 1
  4284.  
  4285.     Env.MainTitle = DTitle
  4286.     Env.SubTitle = DTitle
  4287.  
  4288.     Env.ChartWindow = DWindow           ' Chart window is default window
  4289.     Env.ChartWindow.Border = cYes       ' with a border.
  4290.  
  4291.     Env.DataWindow = DWindow
  4292.  
  4293.     Env.XAxis = DAxis
  4294.     Env.YAxis = DAxis
  4295.  
  4296.     Env.Legend = DLegend
  4297.  
  4298. END SUB
  4299.  
  4300. '=== GetPaletteDef - Changes an entry in the internal palette
  4301. '
  4302. '  Arguments:
  4303. '     C%()     -  Color palette array
  4304. '
  4305. '     S%()     -  Style palette array
  4306. '
  4307. '     P$()     -  Pattern palette array
  4308. '
  4309. '     Char%()  -  Plot character palette array
  4310. '
  4311. '     B%()     -  Border style palette array
  4312. '
  4313. '  Return Values:
  4314. '     Chart error may be set
  4315. '
  4316. '=================================================================
  4317. SUB GetPaletteDef (c() AS INTEGER, s() AS INTEGER, P$(), Char() AS INTEGER, B() AS INTEGER)
  4318. SHARED GP AS GlobalParams
  4319. SHARED PaletteC%(), PaletteS%(), PaletteP$(), PaletteCh%(), PaletteB%()
  4320.  
  4321.     ' Reset any outstanding errors:
  4322.     clClearError
  4323.  
  4324.     ' Make sure palettes have been initialized:
  4325.     IF NOT GP.PaletteSet THEN
  4326.         clSetError cPalettesNotSet
  4327.         EXIT SUB
  4328.     END IF
  4329.  
  4330.     ' Make sure the user's palettes are the correct size:
  4331.     clChkPalettes c(), s(), P$(), Char(), B()
  4332.     IF (ChartErr <> 0) THEN EXIT SUB
  4333.  
  4334.     ' Replace the palette values with input variables (making sure that
  4335.     ' the color and character numbers are in range):
  4336.     FOR N% = 0 TO cPalLen
  4337.         c(N%) = PaletteC%(N%)
  4338.         s(N%) = PaletteS%(N%)
  4339.         P$(N%) = PaletteP$(N%)
  4340.         Char(N%) = PaletteCh%(N%)
  4341.         B(N%) = PaletteB%(N%)
  4342.     NEXT N%
  4343.  
  4344. END SUB
  4345.  
  4346. '=== GetPattern - Returns a pattern from among 3 pattern palettes
  4347. '
  4348. '  Arguments:
  4349. '     Bits%       -  The number of bits per pixel for the pattern
  4350. '
  4351. '     PatternNum% -  The pattern number to return
  4352. '
  4353. '  Return Values:
  4354. '     Returns a pattern tile from the list below.
  4355. '
  4356. '  Remarks:
  4357. '     Below are three pattern sets.  There is a set of patterns for one, two
  4358. '     and eight bit-per-pixel screens.
  4359. '
  4360. '=================================================================
  4361. FUNCTION GetPattern$ (Bits%, PatternNum%)
  4362.  
  4363.     SELECT CASE Bits%
  4364.  
  4365.         ' One bit-per-pixel patterns:
  4366.         CASE 1:
  4367.             SELECT CASE PatternNum%
  4368.                 CASE 1: P$ = CHR$(&HFF)
  4369.                 CASE 2: P$ = CHR$(&H55) + CHR$(&HAA)
  4370.                 CASE 3: P$ = CHR$(&H33) + CHR$(&HCC)
  4371.                 CASE 4: P$ = CHR$(&H0) + CHR$(&HE7)
  4372.                 CASE 5: P$ = CHR$(&H7F) + CHR$(&HBF) + CHR$(&HDF) + CHR$(&HEF) + CHR$(&HF7) + CHR$(&HFB) + CHR$(&HFD) + CHR$(&HFE)
  4373.                 CASE 6: P$ = CHR$(&H7E) + CHR$(&HBD) + CHR$(&HDB) + CHR$(&HE7) + CHR$(&HE7) + CHR$(&HDB) + CHR$(&HBD) + CHR$(&H7E)
  4374.                 CASE 7: P$ = CHR$(&HFE) + CHR$(&HFD) + CHR$(&HFB) + CHR$(&HF7) + CHR$(&HEF) + CHR$(&HDF) + CHR$(&HBF) + CHR$(&H7F)
  4375.                 CASE 8: P$ = CHR$(&H33) + CHR$(&HCC) + CHR$(&HCC) + CHR$(&H33)
  4376.                 CASE 9: P$ = CHR$(&H0) + CHR$(&HFD) + CHR$(&H0) + CHR$(&HF7) + CHR$(&H0) + CHR$(&HDF) + CHR$(&H0) + CHR$(&H7F)
  4377.                 CASE 10: P$ = CHR$(&HF) + CHR$(&H87) + CHR$(&HC3) + CHR$(&HE1) + CHR$(&HF0) + CHR$(&H78) + CHR$(&H3C) + CHR$(&H1E)
  4378.                 CASE 11: P$ = CHR$(&HA8) + CHR$(&H51) + CHR$(&HA2) + CHR$(&H45) + CHR$(&H8A) + CHR$(&H15) + CHR$(&H2A) + CHR$(&H54)
  4379.                 CASE 12: P$ = CHR$(&HAA) + CHR$(&H55) + CHR$(&H0) + CHR$(&H0) + CHR$(&HAA) + CHR$(&H55) + CHR$(&H0) + CHR$(&H0)
  4380.                 CASE 13: P$ = CHR$(&H2A) + CHR$(&H15) + CHR$(&H8A) + CHR$(&H45) + CHR$(&HA2) + CHR$(&H51) + CHR$(&HA8) + CHR$(&H54)
  4381.                 CASE 14: P$ = CHR$(&H88) + CHR$(&H0) + CHR$(&H22) + CHR$(&H0) + CHR$(&H88) + CHR$(&H0) + CHR$(&H22) + CHR$(&H0)
  4382.                 CASE 15: P$ = CHR$(&HFF) + CHR$(&H0) + CHR$(&HFF) + CHR$(&H0) + CHR$(&HFF) + CHR$(&H0) + CHR$(&HFF) + CHR$(&H0)
  4383.             END SELECT
  4384.  
  4385.         ' Two bit-per-pixel patterns:
  4386.         CASE 2:
  4387.             SELECT CASE PatternNum%
  4388.                 CASE 1: P$ = CHR$(&HFF)
  4389.                 CASE 2: P$ = CHR$(&HCC) + CHR$(&H33)
  4390.                 CASE 3: P$ = CHR$(&HF0) + CHR$(&H3C) + CHR$(&HF) + CHR$(&HC3)
  4391.                 CASE 4: P$ = CHR$(&HF0) + CHR$(&HF)
  4392.                 CASE 5: P$ = CHR$(&H3) + CHR$(&HC) + CHR$(&H30) + CHR$(&HC0)
  4393.                 CASE 6: P$ = CHR$(&HFF) + CHR$(&HC)
  4394.                 CASE 7: P$ = CHR$(&HF0) + CHR$(&HF0) + CHR$(&HF) + CHR$(&HF)
  4395.                 CASE 8: P$ = CHR$(&HFF) + CHR$(&HC) + CHR$(&H30) + CHR$(&HC0)
  4396.                 CASE 9: P$ = CHR$(&HC0) + CHR$(&H30) + CHR$(&HC) + CHR$(&H3)
  4397.                 CASE 10: P$ = CHR$(&HC0) + CHR$(&HC)
  4398.                 CASE 11: P$ = CHR$(&HCC) + CHR$(&HCC) + CHR$(&H33) + CHR$(&H33)
  4399.                 CASE 12: P$ = CHR$(&HCC) + CHR$(&HCC) + CHR$(&H0) + CHR$(&H0)
  4400.                 CASE 13: P$ = CHR$(&HFF) + CHR$(&H33) + CHR$(&H33)
  4401.                 CASE 14: P$ = CHR$(&HFF) + CHR$(&H0)
  4402.                 CASE 15: P$ = CHR$(&HCC) + CHR$(&H30) + CHR$(&H0)
  4403.             END SELECT
  4404.  
  4405.         ' Eight bit-per-pixel patterns:
  4406.         CASE 8:
  4407.             P$ = CHR$(&HFF)
  4408.  
  4409.     END SELECT
  4410.  
  4411.     ' Return the pattern as the value of the function:
  4412.     GetPattern$ = P$
  4413.  
  4414. END FUNCTION
  4415.  
  4416. '=== LabelChartH - Prints horizontal text on a chart
  4417. '
  4418. '  Arguments:
  4419. '     Env        - A ChartEnvironment variable
  4420. '
  4421. '     X          - Horizontal position of text relative to the left of
  4422. '                  the Chart window (in pixels)
  4423. '
  4424. '     Y          - Vertical position of text relative to the top of
  4425. '                  the Chart window (in pixels)
  4426. '
  4427. '     Font%      - Font number to use for the text
  4428. '
  4429. '     TxtColor   - Color number (in internal color palette) for text
  4430. '
  4431. '     TxtString$ - String variable containing text to print
  4432. '
  4433. '  Return Values:
  4434. '     None
  4435. '
  4436. '=================================================================
  4437. SUB LabelChartH (Env AS ChartEnvironment, X AS INTEGER, Y AS INTEGER, font AS INTEGER, TxtColor AS INTEGER, TxtString$)
  4438.  
  4439.     ' Reset any outstanding errors:
  4440.     clClearError
  4441.  
  4442.     ' Check initialization and fonts:
  4443.     clChkInit
  4444.     clChkFonts
  4445.     IF ChartErr >= 100 THEN EXIT SUB
  4446.  
  4447.     ' Select ChartWindow as reference viewport:
  4448.     clSelectChartWindow
  4449.  
  4450.     ' Select font and set color:
  4451.     SelectFont font
  4452.     clSetCharColor TxtColor
  4453.  
  4454.     ' Call internal print routine to print text:
  4455.     clHPrint X, Y, TxtString$
  4456.  
  4457. END SUB
  4458.  
  4459. '=== LabelChartV - Prints vertical text on a chart
  4460. '
  4461. '  Arguments:
  4462. '     Env        - A ChartEnvironment variable
  4463. '
  4464. '     X          - Horizontal position of text relative to the left of
  4465. '                  the Chart window (in pixels)
  4466. '
  4467. '     Y          - Vertical position of text relative to the top of
  4468. '                  the Chart window (in pixels)
  4469. '
  4470. '     Font%      - Font number to use for the text
  4471. '
  4472. '     TxtColor   - Color number (in internal color palette) for text
  4473. '
  4474. '     TxtString$ - String variable containing text to print
  4475. '
  4476. '  Return Values:
  4477. '     None
  4478. '
  4479. '=================================================================
  4480. SUB LabelChartV (Env AS ChartEnvironment, X AS INTEGER, Y AS INTEGER, font AS INTEGER, TxtColor AS INTEGER, TxtString$)
  4481.  
  4482.     ' Reset any outstanding errors:
  4483.     clClearError
  4484.  
  4485.     ' Check initialization and fonts:
  4486.     clChkInit
  4487.     clChkFonts
  4488.     IF ChartErr >= 100 THEN EXIT SUB
  4489.  
  4490.     ' Select ChartWindow as reference viewport:
  4491.     clSelectChartWindow
  4492.  
  4493.     ' Select font and set color:
  4494.     SelectFont font%
  4495.     clSetCharColor TxtColor
  4496.  
  4497.     ' Call internal print routine to print text:
  4498.     clVPrint X, Y, TxtString$
  4499.  
  4500. END SUB
  4501.  
  4502. '=== MakeChartPattern$ - Makes a pattern given reference pattern and
  4503. '                        foreground and background colors
  4504. '
  4505. '  Arguments:
  4506. '     RefPattern$ -  Reference pattern
  4507. '
  4508. '     FG%         -  Foreground color
  4509. '
  4510. '     BG%         -  Background color
  4511. '
  4512. '  Return Values:
  4513. '     Returns a pattern in standard PAINT format
  4514. '     Sets error cBadScreen if ChartScreen hasn't been called
  4515. '
  4516. '=================================================================
  4517. FUNCTION MakeChartPattern$ (RefPattern$, FG AS INTEGER, BG AS INTEGER)
  4518. SHARED GP AS GlobalParams
  4519.  
  4520.     ' Reset any outstanding errors:
  4521.     clClearError
  4522.  
  4523.     ' Check initialization:
  4524.     clChkInit
  4525.     IF ChartErr >= 100 THEN EXIT FUNCTION
  4526.     IF NOT GP.PaletteSet THEN
  4527.         clSetError cBadScreen
  4528.         EXIT FUNCTION
  4529.     END IF
  4530.  
  4531.     FGColor% = clMap2Attrib%(FG%)
  4532.     BGColor% = clMap2Attrib%(BG%)
  4533.  
  4534.     ' Screens 1, 2, 11 and 13 are 1 bit plane modes and require one method
  4535.     ' of generating pattern tiles.  The other modes supported are multiple
  4536.     ' bit plane modes and require another method of generating pattern
  4537.     ' tiles.  Select the appropriate method for this screen mode:
  4538.     SELECT CASE GP.PaletteScrn
  4539.         
  4540.         ' One bit plane modes:
  4541.         CASE 1, 2, 11, 13: SinglePlane% = cTrue
  4542.         CASE ELSE: SinglePlane% = cFalse
  4543.   
  4544.     END SELECT
  4545.  
  4546.     ' Do foreground part of pattern:
  4547.     IF SinglePlane% THEN
  4548.             FGPattern$ = clBuildBitP$(GP.PaletteBits, FGColor%, RefPattern$)
  4549.     ELSE
  4550.             FGPattern$ = clBuildPlaneP$(GP.PaletteBits, FGColor%, RefPattern$)
  4551.     END IF
  4552.  
  4553.     ' Do background part of pattern (if background color is black then
  4554.     ' the pattern is just the foreground pattern):
  4555.     IF BGColor% = 0 THEN
  4556.         Pattern$ = FGPattern$
  4557.  
  4558.     ELSE
  4559.         ' Background reference pattern is inverted foreground pattern:
  4560.         BGPattern$ = ""
  4561.         FOR i% = 1 TO LEN(RefPattern$)
  4562.             BGPattern$ = BGPattern$ + CHR$(ASC(MID$(RefPattern$, i%, 1)) XOR &HFF)
  4563.         NEXT i%
  4564.       
  4565.         ' Build the corresponding PAINT style pattern:
  4566.         IF SinglePlane% THEN
  4567.                 BGPattern$ = clBuildBitP$(GP.PaletteBits, BGColor%, BGPattern$)
  4568.         ELSE
  4569.                 BGPattern$ = clBuildPlaneP$(GP.PaletteBits, BGColor%, BGPattern$)
  4570.         END IF
  4571.  
  4572.         ' Put foreground and background patterns back together:
  4573.         Pattern$ = ""
  4574.         FOR i% = 1 TO LEN(FGPattern$)
  4575.             Pattern$ = Pattern$ + CHR$(ASC(MID$(FGPattern$, i%, 1)) OR ASC(MID$(BGPattern$, i%, 1)))
  4576.         NEXT i%
  4577.  
  4578.     END IF
  4579.  
  4580.     MakeChartPattern$ = Pattern$
  4581.  
  4582. END FUNCTION
  4583.  
  4584. '=== ResetPaletteDef - Resets charting palettes for last screen
  4585. '                      mode set with ChartScreen.
  4586. '
  4587. '=================================================================
  4588. SUB ResetPaletteDef ()
  4589. SHARED GP AS GlobalParams
  4590.  
  4591.     ' Clear outstanding errors:
  4592.     clClearError
  4593.  
  4594.     ' Check initialization:
  4595.     clChkInit
  4596.  
  4597.     ' Make sure that ChartScreen has been called at least once:
  4598.     IF NOT GP.PaletteSet THEN
  4599.         clSetError cBadScreen
  4600.         EXIT SUB
  4601.     END IF
  4602.  
  4603.     ' Now rebuild the palette with the last set screen mode:
  4604.     clBuildPalette GP.PaletteScrn, GP.PaletteBits
  4605.  
  4606. END SUB
  4607.  
  4608. '=== SetPaletteDef - Changes an entry in the internal palette
  4609. '
  4610. '  Arguments:
  4611. '     C%()     -  Color palette array
  4612. '
  4613. '     S%()     -  Style palette array
  4614. '
  4615. '     P$()     -  Pattern palette array
  4616. '
  4617. '     Char%()  -  Plot character palette array
  4618. '
  4619. '     B%()     -  Border style palette array
  4620. '
  4621. '  Return Values:
  4622. '     Internal chart palettes may be modified or ChartErr set
  4623. '
  4624. '=================================================================
  4625. SUB SetPaletteDef (c() AS INTEGER, s() AS INTEGER, P$(), Char() AS INTEGER, B() AS INTEGER)
  4626. SHARED PaletteC%(), PaletteS%(), PaletteP$(), PaletteCh%(), PaletteB%()
  4627.  
  4628.     ' Reset any outstanding errors and check that palettes are dimesioned
  4629.     ' correctly:
  4630.     clClearError
  4631.     clChkPalettes c(), s(), P$(), Char(), B()
  4632.     IF (ChartErr <> 0) THEN EXIT SUB
  4633.   
  4634.     ' Check initialization:
  4635.     clChkInit
  4636.  
  4637.     ' Replace the palette values with input variables (making sure that
  4638.     ' the color and character numbers are in range):
  4639.     FOR N% = 0 TO cPalLen
  4640.         PaletteC%(N%) = clMap2Attrib%(c%(N%))
  4641.         PaletteS%(N%) = s(N%)
  4642.         PaletteP$(N%) = P$(N%)
  4643.         PaletteCh%(N%) = ABS(Char(N%)) MOD (cMaxChars + 1)
  4644.         PaletteB%(N%) = B(N%)
  4645.     NEXT N%
  4646.  
  4647. END SUB
  4648.  
  4649.