home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 9 / 09.iso / l / l196 / 3.ddi / CHRTB.BA$ / CHRTB.bin
Encoding:
Text File  |  1990-06-24  |  140.0 KB  |  4,645 lines

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