home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / TCL / BLT / BLT1.7L1 / BLT1 / blt-1.7 / src / bltGrAxis.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-22  |  73.3 KB  |  2,381 lines

  1. /*
  2.  * bltGrAxis.c --
  3.  *
  4.  *    This module implements coordinate axes for a graph
  5.  *    widget in the Tk toolkit.
  6.  *
  7.  * Copyright 1991-1994 by AT&T Bell Laboratories.
  8.  * Permission to use, copy, modify, and distribute this software
  9.  * and its documentation for any purpose and without fee is hereby
  10.  * granted, provided that the above copyright notice appear in all
  11.  * copies and that both that the copyright notice and warranty
  12.  * disclaimer appear in supporting documentation, and that the
  13.  * names of AT&T Bell Laboratories any of their entities not be used
  14.  * in advertising or publicity pertaining to distribution of the
  15.  * software without specific, written prior permission.
  16.  *
  17.  * AT&T disclaims all warranties with regard to this software, including
  18.  * all implied warranties of merchantability and fitness.  In no event
  19.  * shall AT&T be liable for any special, indirect or consequential
  20.  * damages or any damages whatsoever resulting from loss of use, data
  21.  * or profits, whether in an action of contract, negligence or other
  22.  * tortuous action, arising out of or in connection with the use or
  23.  * performance of this software.
  24.  *
  25.  */
  26.  
  27. #include <assert.h>
  28. #include "blt.h"
  29. #include "bltGraph.h"
  30. #include "bltGrElem.h"
  31. #include <ctype.h>
  32. #include <X11/Xutil.h>
  33. #include <X11/Xatom.h>
  34.  
  35. #define NTICK 5
  36.  
  37. #ifndef SHRT_MIN
  38. #define SHRT_MIN                -0x8000
  39. #endif /* SHRT_MIN */
  40. #ifndef SHRT_MAX
  41. #define SHRT_MAX                 0x7FFF
  42. #endif /* SHRT_MAX */
  43.  
  44. /*
  45.  * Round x in terms of units
  46.  */
  47. #define UROUND(x,u)    (BLT_RND((x)/(u))*(u))
  48. #define UCEIL(x,u)    (ceil((x)/(u))*(u))
  49. #define UFLOOR(x,u)    (floor((x)/(u))*(u))
  50.  
  51. #define DEF_MAJOR_TICK     0.030    /* Length of a major tick */
  52. #define DEF_MINOR_TICK     0.015    /* Length of a minor (sub)tick */
  53. #define DEF_LABEL_TICK     0.040    /* Distance from graph to start of label */
  54.  
  55. #define NUMDIGITS    15    /* Specifies the number of digits of
  56.                  * accuracy used when outputting axis
  57.                  * tick labels. */
  58. enum PositionIndices {
  59.     MAJOR_TICK, MINOR_TICK, TICK_LABEL, AXIS_LINE
  60. };
  61.  
  62. #define AVG_TICK_NUM_CHARS    16    /* Assumed average tick label size */
  63.  
  64. enum LimitIndices {
  65.     LMIN, LMAX
  66. };
  67.  
  68. /* Note: The following array is ordered according to enum AxisTypes */
  69. static char *axisNames[] =
  70. {
  71.     "x", "y", "x2", "y2"
  72. };
  73.  
  74. /* Map normalized coordinates to window coordinates */
  75. #define MAPX(X,x)        (BLT_RND((x)*(X)->scale)+(X)->offset)
  76. #define MAPY(Y,y)        ((Y)->offset-BLT_RND((y)*(Y)->scale))
  77.  
  78. /* Map graph coordinates to normalized coordinates [0..1] */
  79. #define NORM(a,x)     (((x) - (a)->min) / (a)->range)
  80.  
  81. /*
  82.  * Sun's bundled and unbundled C compilers choke on static function
  83.  * typedefs (while it can handle extern declarations) like
  84.  *
  85.  *     static Tk_OptionParseProc parseProc;
  86.  *      static Tk_OptionPrintProc printProc;
  87.  *
  88.  * As a workaround, provide forward declarations here:
  89.  */
  90. static int ParseAxisLimit _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *value, char *widgRec, int offset));
  91. static char *PrintAxisLimit _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  92.  
  93. static Tk_CustomOption MinLimitOption =
  94. {
  95.     ParseAxisLimit, PrintAxisLimit, (ClientData)LMIN,
  96. };
  97. static Tk_CustomOption MaxLimitOption =
  98. {
  99.     ParseAxisLimit, PrintAxisLimit, (ClientData)LMAX,
  100. };
  101.  
  102. /*
  103.  * ----------------------------------------------------------------------
  104.  *
  105.  * Label --
  106.  *
  107.  *     Structure containing the formatted label and the screen
  108.  *     coordinates position of the tick label (anchored at its
  109.  *     center).
  110.  *
  111.  * ----------------------------------------------------------------------
  112.  */
  113. typedef struct {
  114.     char *text;            /* Label for tick on axis */
  115.     short int x, y;        /* Window position of tick on graph */
  116. } Label;
  117.  
  118. /*
  119.  * ----------------------------------------------------------------------
  120.  *
  121.  * Axis --
  122.  *
  123.  *     Structure contains options controlling how the axis will be
  124.  *     displayed.
  125.  *
  126.  * ----------------------------------------------------------------------
  127.  */
  128.  
  129. typedef struct {
  130.     enum AxisTypes type;    /* Type of axis: X1, Y1, X2, or Y2 */
  131.     enum AxisLocations location;/* Location of the axis relative to plotting
  132.                  * surface: right, left, etc. */
  133.     int logScale;        /* If non-zero, scale values logarithmically */
  134.     int mapped;            /* If non-zero, display the axis */
  135.  
  136.     AxisDisplayProc *displayProc;
  137.     AxisPrintProc *printProc;
  138.     AxisLayoutProc *layoutProc;
  139.     AxisDestroyProc *destroyProc;
  140.  
  141.     /* User-definable fields */
  142.     char *title;        /* Axis title */
  143.     int showTicks;        /* If non-zero, display (both major
  144.                  * and minor) ticks on the axis */
  145.     int loose;            /* If non-zero, autoscale limits loosely */
  146.     int descending;        /* In non-zero, axis values are descending
  147.                  * instead of monotonically increasing. */
  148.     double limits[2];        /* Limits for scaling of axis */
  149.     double prevMin, prevMax;    /* Last limits for scaling of axis */
  150.     double reqStep;        /* Manually selected step size for
  151.                  * major ticks: If zero or less,
  152.                  * automatically calculate a "best"
  153.                  * step size based on range of
  154.                  * values. */
  155.     int reqSubTicks;        /* Manually selected # of subticks:
  156.                  * The default value is 2. */
  157.     XFontStruct *fontPtr;    /* Font used to draw tick labels. */
  158.     XColor *fgColorPtr;        /* Foreground color for ticks, labels,
  159.                  * and axis */
  160.     int lineWidth;        /* Line thickness of axis and ticks */
  161.     double theta;        /* Rotation of tick labels in degrees. */
  162.     char *formatCmd;        /* If non-NULL, indicates a Tcl proc
  163.                  * to call when formatting tick
  164.                  * labels. See the manual for its
  165.                  * usage. */
  166.     double subStep;        /* Step interval between minor ticks */
  167.     int subTicks;        /* # of minor ticks between major
  168.                  * ticks */
  169.     double step;        /* Step interval between major ticks */
  170.     int numTicks;        /* # of major ticks possible on axis:
  171.                  * Calculated by tick layout routines. */
  172.  
  173.     /* Calculated values */
  174.     unsigned int flags;
  175.  
  176.     Tk_Anchor anchor;        /* Anchor of tick labels */
  177.     int posArr[4];        /* Screen location of axis, major tick,
  178.                  * minor tick, and tick label */
  179.     XPoint titlePos;        /* Coordinates of axis title */
  180.  
  181.     Tcl_Interp *interp;
  182.     unsigned int width, height;    /* Bounding box of axis */
  183.  
  184.     double tickMin, tickMax;    /* Smallest and largest possible major
  185.                  * ticks on the plot */
  186.     int tickLength;        /* Legend of major tick on axis. */
  187.     double min, max;        /* Actual (including padding) axis limits */
  188.     double range;        /* Range of values (max-min) */
  189.     double scale;        /* Scale factor to convert values to
  190.                  * pixels */
  191.     int offset;            /* Offset of plotting region from window
  192.                  * origin */
  193.  
  194.     GC lineGC;            /* Graph context for axis lines and ticks */
  195.     GC textGC;            /* Graphic context for tick labels:
  196.                  * Must be a private GC (can't use
  197.                  * Tk_GetGC) because the FillStyle,
  198.                  * TSOrigin, and Stipple fields may
  199.                  * change when the graph is layout is
  200.                  * calculated, to accommodate rotation
  201.                  * of text. Also, note that the
  202.                  * background color is also reset when
  203.                  * the background color of the graph
  204.                  * changes. */
  205.     int numSegments;        /* Size of the above segment array */
  206.     XSegment *segArr;        /* Array of computed tick line
  207.                  * segments. Also includes the axis
  208.                  * line */
  209.     int numLabels;        /* Size of the above label array */
  210.     Label *labelArr;        /* Array of computed tick labels: See the
  211.                  * description of the Label structure. */
  212.  
  213. } Axis;
  214.  
  215. /* Axis flags: */
  216. #define AXIS_CONFIG_DIRTY    (1<<8)
  217. #define AXIS_CONFIG_USER_BIT    (1<<9)
  218. #define AXIS_CONFIG_MIN_MASK    (AXIS_CONFIG_USER_BIT << LMIN)
  219. #define AXIS_CONFIG_MAX_MASK    (AXIS_CONFIG_USER_BIT << LMAX)
  220. #define AXIS_CONFIG_MIN_SET(a)     ((a)->flags & AXIS_CONFIG_MIN_MASK)
  221. #define AXIS_CONFIG_MAX_SET(a)     ((a)->flags & AXIS_CONFIG_MAX_MASK)
  222.  
  223. #define VERTICAL_AXIS(a)    ((a)->location&1)
  224. #define HORIZONTAL_AXIS(a)        (!((a)->location&1))
  225.  
  226. #define DEF_AXIS_ALT_MAPPED     "false"
  227. #define DEF_AXIS_COMMAND    (char *)NULL
  228. #define DEF_AXIS_DESCENDING     "0"
  229. #define DEF_AXIS_FG_COLOR    BLACK
  230. #define DEF_AXIS_FG_MONO    BLACK
  231. #define DEF_AXIS_FONT         "*-Courier-Bold-R-Normal-*-100-*"
  232. #define DEF_AXIS_TICK_LENGTH    "0.1i"
  233. #define DEF_AXIS_LINE_WIDTH    "0"
  234. #define DEF_AXIS_LOG_SCALE     "0"
  235. #define DEF_AXIS_LOOSE         "0"
  236. #define DEF_AXIS_MAX        (char *)NULL
  237. #define DEF_AXIS_MIN        (char *)NULL
  238. #define DEF_AXIS_ROTATE        "0.0"
  239. #define DEF_AXIS_STD_MAPPED     "1"
  240. #define DEF_AXIS_STEPSIZE    "0.0"
  241. #define DEF_AXIS_SUBTICKS    "2"
  242. #define DEF_AXIS_TICKS        "1"
  243. #define DEF_AXIS_X_STEPSIZE_BARCHART    "1.0"
  244. #define DEF_AXIS_X_SUBTICKS_BARCHART    "0"
  245. #define DEF_AXIS_TITLE        (char *)NULL
  246.  
  247. static Tk_ConfigSpec xAxisConfigSpecs[] =
  248. {
  249.     {TK_CONFIG_COLOR, "-color", "xColor", "AxisColor",
  250.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  251.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  252.     {TK_CONFIG_COLOR, "-color", "xColor", "AxisColor",
  253.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  254.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  255.     {TK_CONFIG_STRING, "-command", "xCommand", "AxisCommand",
  256.     DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
  257.     ALL_MASK | TK_CONFIG_NULL_OK},
  258.     {TK_CONFIG_BOOLEAN, "-descending", "xDescending", "AxisDescending",
  259.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  260.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  261.     {TK_CONFIG_FONT, "-font", "xFont", "AxisFont",
  262.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  263.     {TK_CONFIG_PIXELS, "-linewidth", "xLinewidth", "AxisLinewidth",
  264.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  265.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  266.     {TK_CONFIG_BOOLEAN, "-logscale", "xLogscale", "AxisLogscale",
  267.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  268.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  269.     {TK_CONFIG_BOOLEAN, "-loose", "xLoose", "AxisLoose",
  270.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  271.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  272.     {TK_CONFIG_BOOLEAN, "-mapped", "xMapped", "AxisMapped",
  273.     DEF_AXIS_STD_MAPPED, Tk_Offset(Axis, mapped),
  274.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  275.     {TK_CONFIG_CUSTOM, "-max", "xMax", "AxisMax",
  276.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  277.     {TK_CONFIG_CUSTOM, "-min", "xMin", "AxisMin",
  278.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  279.     {TK_CONFIG_DOUBLE, "-rotate", "xRotate", "AxisRotate",
  280.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  281.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  282.     {TK_CONFIG_BOOLEAN, "-showticks", "xShowticks", "AxisShowticks",
  283.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
  284.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  285.     {TK_CONFIG_DOUBLE, "-stepsize", "xStepsize", "AxisStepsize",
  286.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep), XYGRAPH_MASK},
  287.     {TK_CONFIG_DOUBLE, "-stepsize", "xStepsize", "AxisStepsize",
  288.     DEF_AXIS_X_STEPSIZE_BARCHART, Tk_Offset(Axis, reqStep),
  289.     BARCHART_MASK},
  290.     {TK_CONFIG_INT, "-subticks", "xSubticks", "AxisSubticks",
  291.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), XYGRAPH_MASK},
  292.     {TK_CONFIG_INT, "-subticks", "xSubticks", "AxisSubticks",
  293.     DEF_AXIS_X_SUBTICKS_BARCHART, Tk_Offset(Axis, reqSubTicks),
  294.     BARCHART_MASK},
  295.     {TK_CONFIG_PIXELS, "-ticklength", "xTickLength", "AxisTickLength",
  296.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  297.     {TK_CONFIG_STRING, "-title", "xTitle", "AxisTitle",
  298.     "X", Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  299.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  300. };
  301.  
  302.  
  303. static Tk_ConfigSpec x2AxisConfigSpecs[] =
  304. {
  305.     {TK_CONFIG_COLOR, "-color", "x2Color", "AxisColor",
  306.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  307.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  308.     {TK_CONFIG_COLOR, "-color", "x2Color", "AxisColor",
  309.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  310.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  311.     {TK_CONFIG_STRING, "-command", "x2Command", "AxisCommand",
  312.     DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
  313.     ALL_MASK | TK_CONFIG_NULL_OK},
  314.     {TK_CONFIG_BOOLEAN, "-descending", "x2Descending", "AxisDescending",
  315.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  316.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  317.     {TK_CONFIG_FONT, "-font", "x2Font", "AxisFont",
  318.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  319.     {TK_CONFIG_PIXELS, "-linewidth", "x2Linewidth", "AxisLinewidth",
  320.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  321.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  322.     {TK_CONFIG_BOOLEAN, "-logscale", "x2Logscale", "AxisLogscale",
  323.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  324.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  325.     {TK_CONFIG_BOOLEAN, "-loose", "x2Loose", "AxisLoose",
  326.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  327.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  328.     {TK_CONFIG_BOOLEAN, "-mapped", "x2Mapped", "AxisMapped",
  329.     DEF_AXIS_ALT_MAPPED, Tk_Offset(Axis, mapped),
  330.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  331.     {TK_CONFIG_CUSTOM, "-max", "x2Max", "AxisMax",
  332.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  333.     {TK_CONFIG_CUSTOM, "-min", "x2Min", "AxisMin",
  334.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  335.     {TK_CONFIG_DOUBLE, "-rotate", "x2Rotate", "AxisRotate",
  336.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  337.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  338.     {TK_CONFIG_BOOLEAN, "-showticks", "x2Showticks", "AxisShowticks",
  339.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks), ALL_MASK},
  340.     {TK_CONFIG_DOUBLE, "-stepsize", "x2Stepsize", "AxisStepsize",
  341.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
  342.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  343.     {TK_CONFIG_INT, "-subticks", "x2Subticks", "AxisSubticks",
  344.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks),
  345.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  346.     {TK_CONFIG_PIXELS, "-ticklength", "x2TickLength", "AxisTickLength",
  347.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  348.     {TK_CONFIG_STRING, "-title", "x2Title", "AxisTitle",
  349.     DEF_AXIS_TITLE, Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  350.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  351. };
  352.  
  353. static Tk_ConfigSpec yAxisConfigSpecs[] =
  354. {
  355.     {TK_CONFIG_COLOR, "-color", "yColor", "AxisColor",
  356.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  357.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  358.     {TK_CONFIG_COLOR, "-color", "yColor", "AxisColor",
  359.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  360.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  361.     {TK_CONFIG_STRING, "-command", "yCommand", "AxisCommand",
  362.     (char *)NULL, Tk_Offset(Axis, formatCmd),
  363.     ALL_MASK | TK_CONFIG_NULL_OK},
  364.     {TK_CONFIG_BOOLEAN, "-descending", "yDescending", "AxisDescending",
  365.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  366.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  367.     {TK_CONFIG_FONT, "-font", "yFont", "AxisFont",
  368.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  369.     {TK_CONFIG_BOOLEAN, "-loose", "yLoose", "AxisLoose",
  370.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  371.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  372.     {TK_CONFIG_PIXELS, "-linewidth", "yLinewidth", "AxisLinewidth",
  373.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  374.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  375.     {TK_CONFIG_BOOLEAN, "-logscale", "yLogscale", "AxisLogscale",
  376.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  377.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  378.     {TK_CONFIG_BOOLEAN, "-mapped", "yMapped", "AxisMapped",
  379.     DEF_AXIS_STD_MAPPED, Tk_Offset(Axis, mapped),
  380.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  381.     {TK_CONFIG_CUSTOM, "-max", "yMax", "AxisMax",
  382.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  383.     {TK_CONFIG_CUSTOM, "-min", "yMin", "AxisMin",
  384.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  385.     {TK_CONFIG_DOUBLE, "-rotate", "yRotate", "AxisRotate",
  386.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  387.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  388.     {TK_CONFIG_BOOLEAN, "-showticks", "yShowticks", "AxisShowticks",
  389.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
  390.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  391.     {TK_CONFIG_DOUBLE, "-stepsize", "yStepsize", "AxisStepsize",
  392.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
  393.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  394.     {TK_CONFIG_INT, "-subticks", "ySubticks", "AxisSubticks",
  395.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), ALL_MASK},
  396.     {TK_CONFIG_PIXELS, "-ticklength", "yTickLength", "AxisTickLength",
  397.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  398.     {TK_CONFIG_STRING, "-title", "yTitle", "AxisTitle",
  399.     "Y", Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  400.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  401. };
  402.  
  403. static Tk_ConfigSpec y2AxisConfigSpecs[] =
  404. {
  405.     {TK_CONFIG_COLOR, "-color", "y2Color", "AxisColor",
  406.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  407.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  408.     {TK_CONFIG_COLOR, "-color", "y2Color", "AxisColor",
  409.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  410.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  411.     {TK_CONFIG_STRING, "-command", "y2Command", "AxisCommand",
  412.     DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
  413.     ALL_MASK | TK_CONFIG_NULL_OK},
  414.     {TK_CONFIG_BOOLEAN, "-descending", "y2Descending", "AxisDescending",
  415.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  416.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  417.     {TK_CONFIG_FONT, "-font", "y2Font", "AxisFont",
  418.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  419.     {TK_CONFIG_PIXELS, "-linewidth", "y2Linewidth", "AxisLinewidth",
  420.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  421.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  422.     {TK_CONFIG_BOOLEAN, "-loose", "y2Loose", "AxisLoose",
  423.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  424.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  425.     {TK_CONFIG_BOOLEAN, "-logscale", "y2Logscale", "AxisLogscale",
  426.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  427.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  428.     {TK_CONFIG_BOOLEAN, "-mapped", "y2Mapped", "AxisMapped",
  429.     DEF_AXIS_ALT_MAPPED, Tk_Offset(Axis, mapped),
  430.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  431.     {TK_CONFIG_CUSTOM, "-max", "y2Max", "AxisMax",
  432.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  433.     {TK_CONFIG_CUSTOM, "-min", "y2Min", "AxisMin",
  434.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  435.     {TK_CONFIG_DOUBLE, "-rotate", "y2Rotate", "AxisRotate",
  436.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  437.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  438.     {TK_CONFIG_BOOLEAN, "-showticks", "y2Showticks", "AxisShowticks",
  439.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
  440.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  441.     {TK_CONFIG_DOUBLE, "-stepsize", "y2Stepsize", "AxisStepsize",
  442.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
  443.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  444.     {TK_CONFIG_INT, "-subticks", "y2Subticks", "AxisSubticks",
  445.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), ALL_MASK},
  446.     {TK_CONFIG_PIXELS, "-ticklength", "y2TickLength", "AxisTickLength",
  447.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  448.     {TK_CONFIG_STRING, "-title", "y2Title", "AxisTitle",
  449.     DEF_AXIS_TITLE, Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  450.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  451. };
  452.  
  453. static Tk_ConfigSpec *axisConfigSpecs[4] =
  454. {
  455.     xAxisConfigSpecs, yAxisConfigSpecs,    /* Normal x-axis and y-axis */
  456.     x2AxisConfigSpecs, y2AxisConfigSpecs    /* Alternate x-axis and y-axis */
  457. };
  458.  
  459. /* ----------------------------------------------------------------------
  460.  * Custom option parse and print procedures
  461.  * ----------------------------------------------------------------------
  462.  */
  463. /*
  464.  * ----------------------------------------------------------------------
  465.  *
  466.  * ParseAxisLimit --
  467.  *
  468.  *    Convert the string representation of an axis limit into its
  469.  *    numeric form.
  470.  *
  471.  * Results:
  472.  *    The return value is a standard Tcl result.  The symbol type is
  473.  *    written into the widget record.
  474.  *
  475.  * ----------------------------------------------------------------------
  476.  */
  477. /*ARGSUSED*/
  478. static int
  479. ParseAxisLimit(clientData, interp, tkwin, value, widgRec, offset)
  480.     ClientData clientData;    /* Either LMIN or LMAX */
  481.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  482.     Tk_Window tkwin;        /* not used */
  483.     char *value;        /* */
  484.     char *widgRec;        /* Axis structure */
  485.     int offset;            /* Offset of limit */
  486. {
  487.     Axis *axisPtr = (Axis *)(widgRec);
  488.     int whichLimit = (int)clientData;
  489.     unsigned int mask;
  490.  
  491.     mask = (AXIS_CONFIG_USER_BIT << whichLimit);
  492.     if ((value == NULL) || (*value == '\0')) {
  493.     axisPtr->flags &= ~mask;
  494.     } else {
  495.     double newLimit;
  496.  
  497.     if (Tcl_ExprDouble(interp, value, &newLimit) != TCL_OK) {
  498.         return TCL_ERROR;
  499.     }
  500.     axisPtr->limits[whichLimit] = newLimit;
  501.     axisPtr->flags |= mask;
  502.     }
  503.     return TCL_OK;
  504. }
  505.  
  506. /*
  507.  * ----------------------------------------------------------------------
  508.  *
  509.  * PrintAxisLimit --
  510.  *
  511.  *    Convert the floating point axis limit into a string.
  512.  *
  513.  * Results:
  514.  *    The string representation of the limit is returned.
  515.  *
  516.  * ----------------------------------------------------------------------
  517.  */
  518. /*ARGSUSED*/
  519. static char *
  520. PrintAxisLimit(clientData, tkwin, widgRec, offset, freeProcPtr)
  521.     ClientData clientData;    /* Either LMIN or LMAX */
  522.     Tk_Window tkwin;        /* not used */
  523.     char *widgRec;        /* */
  524.     int offset;
  525.     Tcl_FreeProc **freeProcPtr;
  526. {
  527.     Axis *axisPtr = (Axis *)(widgRec);
  528.     int whichLimit = (int)clientData;
  529.     unsigned int mask;
  530.     char *result;
  531.  
  532.     result = "";
  533.     mask = (AXIS_CONFIG_USER_BIT << whichLimit);
  534.     if (axisPtr->flags & mask) {
  535.     char string[TCL_DOUBLE_SPACE + 1];
  536.  
  537.     Tcl_PrintDouble(axisPtr->interp, axisPtr->limits[whichLimit],
  538.         string);
  539.     result = strdup(string);
  540.     if (result == NULL) {
  541.         return "";
  542.     }
  543.     *freeProcPtr = TCL_DYNAMIC;
  544.     }
  545.     return result;
  546. }
  547.  
  548. /*
  549.  * ----------------------------------------------------------------------
  550.  *
  551.  * MakeLabel --
  552.  *
  553.  *    Converts a floating point tick value to a string representation.
  554.  *
  555.  * Results:
  556.  *    Returns a formatted label in the string buffer.
  557.  *
  558.  * Side Effects:
  559.  *    Formatted tick label will be displayed on the graph.
  560.  *
  561.  * ----------------------------------------------------------------------
  562.  */
  563. static int
  564. MakeLabel(graphPtr, axisPtr, value, string)
  565.     Graph *graphPtr;        /* Graph widget */
  566.     Axis *axisPtr;        /* Axis structure */
  567.     double value;        /* */
  568.     char string[];        /* string (length is always
  569.                  * TCL_DOUBLE_SPACE+1) containing the
  570.                  * formatted label */
  571. {
  572.     if (axisPtr->logScale) {
  573.     sprintf(string, "1E%d", BLT_RND(value));
  574.     } else {
  575.     if (axisPtr->formatCmd == NULL) {
  576.         sprintf(string, "%.*g", NUMDIGITS, value);
  577.     } else {
  578.         Tcl_PrintDouble(axisPtr->interp, value, string);
  579.     }
  580.     }
  581.     if (axisPtr->formatCmd != NULL) {
  582.     Tcl_ResetResult(axisPtr->interp);
  583.     if (Tcl_VarEval(axisPtr->interp, axisPtr->formatCmd, " ",
  584.         Tk_PathName(graphPtr->tkwin), " ",
  585.         string, (char *)NULL) != TCL_OK) {
  586.         Tk_BackgroundError(axisPtr->interp);
  587.     } else {
  588.         char *result;
  589.  
  590.         result = axisPtr->interp->result;
  591.         if (*result != '\0') {
  592.         strncpy(string, result, TCL_DOUBLE_SPACE);
  593.         string[TCL_DOUBLE_SPACE] = 0;
  594.         Tcl_ResetResult(axisPtr->interp);
  595.         }
  596.     }
  597.     }
  598.     return TCL_OK;
  599. }
  600.  
  601. /* Map graph coordinate to normalized coordinates (consider log scale) */
  602. static double
  603. Scale(axisPtr, x)
  604.     Axis *axisPtr;
  605.     double x;
  606. {
  607.     if (x == Blt_posInfinity) {
  608.     return (1.0);
  609.     } else if (x == Blt_negInfinity) {
  610.     return (0.0);
  611.     }
  612.     if (axisPtr->logScale) {
  613.     if (x > 0.0) {
  614.         x = log10(x);
  615.     } else if (x < 0.0) {
  616.         x = 0.0;
  617.     }
  618.     }
  619.     return (NORM(axisPtr, x));
  620. }
  621.  
  622. /*
  623.  * ----------------------------------------------------------------------
  624.  *
  625.  * Blt_InvTransform --
  626.  *
  627.  *    Maps the given window y-coordinate back to a graph coordinate
  628.  *    value. Called by the graph locater routine.
  629.  *
  630.  * Results:
  631.  *    Returns the graph coordinate value at the given window
  632.  *    y-coordinate.
  633.  *
  634.  * ----------------------------------------------------------------------
  635.  */
  636. double
  637. Blt_InvTransform(axis, coord)
  638.     GraphAxis *axis;
  639.     int coord;
  640. {
  641.     double norm, value;
  642.     Axis *axisPtr = (Axis *)axis;
  643.  
  644.     if (HORIZONTAL_AXIS(axisPtr)) {
  645.     coord = (coord - axisPtr->offset);
  646.     } else {
  647.     coord = (axisPtr->offset - coord);
  648.     }
  649.     norm = ((double)coord / axisPtr->scale);
  650.     if (axisPtr->descending) {
  651.     norm = 1.0 - norm;
  652.     }
  653.     value = (norm * axisPtr->range) + axisPtr->min;
  654.     if (axisPtr->logScale) {
  655.     value = BLT_EXP10(value);
  656.     }
  657.     return (value);
  658. }
  659.  
  660. /*
  661.  * ----------------------------------------------------------------------
  662.  *
  663.  * Blt_Transform --
  664.  *
  665.  *    Map the given graph coordinate value to its axis, returning a
  666.  *    window position.
  667.  *
  668.  * Results:
  669.  *    Returns the window coordinate position on the given axis.
  670.  *
  671.  * Note:
  672.  *    Since line and polygon clipping is performed by the X server,
  673.  *    we must be careful about coordinates which are outside of the
  674.  *      range of a signed short int.
  675.  *
  676.  * ----------------------------------------------------------------------
  677.  */
  678. int
  679. Blt_Transform(axis, value)
  680.     GraphAxis *axis;
  681.     double value;
  682. {
  683.     Axis *axisPtr = (Axis *)axis;
  684.     double norm;
  685.     int coord;
  686.  
  687.     norm = Scale(axisPtr, value);
  688.     if (axisPtr->descending) {
  689.     norm = 1.0 - norm;
  690.     }
  691.     coord = HORIZONTAL_AXIS(axisPtr)
  692.     ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
  693.  
  694.     /* Should really figure out a good offset value and test for that
  695.      * because we could still generate bogus numbers */
  696.     if (coord >= SHRT_MAX) {
  697.     coord = SHRT_MAX - 1000;
  698.     } else if (coord <= SHRT_MIN) {
  699.     coord = SHRT_MIN + 1000;
  700.     }
  701.     return (coord);
  702. }
  703.  
  704. /*
  705.  * ----------------------------------------------------------------------
  706.  *
  707.  * Blt_TransformPt --
  708.  *
  709.  *    Maps the given graph x,y coordinate values to a window position.
  710.  *
  711.  * Results:
  712.  *    Returns a XPoint structure containing the window coordinates of
  713.  *    the given graph x,y coordinate.
  714.  *
  715.  * ----------------------------------------------------------------------
  716.  */
  717. XPoint
  718. Blt_TransformPt(graphPtr, x, y, axisFlags)
  719.     Graph *graphPtr;
  720.     double x, y;
  721.     unsigned int axisFlags;    /* Specifies which axes to use */
  722. {
  723.     XPoint winPos;
  724.     enum AxisTypes axisType;
  725.  
  726.     axisType = (axisFlags & X1_AXIS_MASK) ? X1_AXIS : X1_AXIS;
  727.     winPos.x = Blt_Transform(graphPtr->axisArr[axisType], x);
  728.     axisType = (axisFlags & Y1_AXIS_MASK) ? Y1_AXIS : Y2_AXIS;
  729.     winPos.y = Blt_Transform(graphPtr->axisArr[axisType], y);
  730.     if (graphPtr->inverted) {    /* Swap x and y coordinates */
  731.     int coord;
  732.  
  733.     coord = winPos.y;
  734.     winPos.y = winPos.x;
  735.     winPos.x = coord;
  736.     }
  737.     return (winPos);
  738. }
  739.  
  740. /*
  741.  * ----------------------------------------------------------------------
  742.  *
  743.  * Blt_TransformDist --
  744.  *
  745.  *    Map the given graph x-coordinate value to a window position.
  746.  *
  747.  * Results:
  748.  *    Returns the window coordinate position at the given graph
  749.  *    x-coordinate.
  750.  *
  751.  * Note:
  752.  *    Since line and polygon clipping is performed by the X server,
  753.  *    we must be careful about coordinates which are outside of the
  754.  *      range of a signed short int.
  755.  *
  756.  * ----------------------------------------------------------------------
  757.  */
  758. int
  759. Blt_TransformDist(axis, value)
  760.     GraphAxis *axis;
  761.     double value;
  762. {
  763.     Axis *axisPtr = (Axis *)axis;
  764.     double norm;
  765.     int zero, coord;
  766.  
  767.     norm = Scale(axisPtr, 0.0);
  768.     zero = HORIZONTAL_AXIS(axisPtr)
  769.     ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
  770.     norm = Scale(axisPtr, value);
  771.     coord = HORIZONTAL_AXIS(axisPtr)
  772.     ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
  773.     coord -= zero;
  774.     return (BLT_ABS(coord));
  775. }
  776.  
  777. /*
  778.  * ----------------------------------------------------------------------
  779.  *
  780.  * Blt_PointOnGraph --
  781.  *
  782.  *    Determines if the window coordinates given represent a point
  783.  *    on the graph (within the bounds of the graph axes).
  784.  *
  785.  * Results:
  786.  *    Returns 1 is the point is within the bounds of the graph,
  787.  *    0 otherwise.
  788.  *
  789.  * ----------------------------------------------------------------------
  790.  */
  791. int
  792. Blt_PointOnGraph(graphPtr, pointPtr)
  793.     Graph *graphPtr;
  794.     XPoint *pointPtr;
  795. {
  796.     double norm;
  797.     Axis *axisPtr;
  798.  
  799.     /*
  800.      * It doesn't make a difference which x-axis or y-axis we use
  801.      * (the point is referenced by window coordinates). We're just
  802.      * checking if the point's within the range of axis' normalized
  803.      * coordinates [0..1].
  804.      */
  805.     axisPtr = (Axis *)graphPtr->bottomAxis;
  806.     if (axisPtr->scale == 0.0) {
  807.     return 0;        /* Axis layout hasn't been calculated yet */
  808.     }
  809.     norm = (pointPtr->x - axisPtr->offset) / axisPtr->scale;
  810.     if ((norm < 0.0) || (norm > 1.0)) {
  811.     return 0;        /* x-coordinates are off the graph */
  812.     }
  813.     axisPtr = (Axis *)graphPtr->leftAxis;
  814.     norm = (axisPtr->offset - pointPtr->y) / axisPtr->scale;
  815.     return ((norm >= 0.0) && (norm <= 1.0));
  816. }
  817.  
  818. /*
  819.  * ----------------------------------------------------------------------
  820.  *
  821.  * UpdateLimits --
  822.  *
  823.  *    Updates the min and max values for each axis as determined by
  824.  *    the data elements currently to be displayed.
  825.  *
  826.  * Results:
  827.  *    None.
  828.  *
  829.  * Side Effects:
  830.  *    Minimum, maximum data limit fields for both the X and Y axes
  831.  *    in the graph widget record are updated.
  832.  *
  833.  * ----------------------------------------------------------------------
  834.  */
  835. static void
  836. UpdateLimits(graphPtr, axisPtr)
  837.     Graph *graphPtr;
  838.     Axis *axisPtr;
  839. {
  840.     int result;
  841.  
  842.     if ((!AXIS_CONFIG_MAX_SET(axisPtr)) || (!AXIS_CONFIG_MIN_SET(axisPtr))) {
  843.     register Element *elemPtr;
  844.     Blt_ListEntry *entryPtr;
  845.     register double min, max;
  846.     double elemMin, elemMax;
  847.     double value;
  848.  
  849.     /* Find the minimum and maximum values for all the elements
  850.        displayed */
  851.     min = Blt_posInfinity, max = Blt_negInfinity;
  852.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  853.         entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  854.         elemPtr = (Element *)Blt_GetListValue(entryPtr);
  855.         if ((*elemPtr->limitsProc) (graphPtr, elemPtr,
  856.             (GraphAxis *)axisPtr, &elemMin, &elemMax) > 0) {
  857.         if (min > elemMin) {
  858.             min = elemMin;
  859.         }
  860.         if (max < elemMax) {
  861.             max = elemMax;
  862.         }
  863.         }
  864.     }
  865.     /*
  866.      * When auto-scaling, the axis limits are the bounds of the
  867.      * element data.  If no data exists, set arbitrary limits (wrt
  868.      * to log/linear scale).
  869.      */
  870.     if (min == Blt_posInfinity) {
  871.         min = (axisPtr->logScale) ? 0.001 : -10.0;
  872.     }
  873.     if (max == Blt_negInfinity) {
  874.         max = 10.0;
  875.     }
  876.     /*
  877.      * Handle situations where only one limit is set.
  878.      */
  879.     value = min;
  880.     if (AXIS_CONFIG_MIN_SET(axisPtr)) {
  881.         min = value = axisPtr->limits[LMIN];
  882.     } else if (AXIS_CONFIG_MAX_SET(axisPtr)) {
  883.         max = value = axisPtr->limits[LMAX];
  884.     }
  885.     /* If there's no range of data (min >= max), manufacture one */
  886.     if (min >= max) {
  887.         if (value == 0.0) {
  888.         min = -0.1, max = 0.1;
  889.         } else {
  890.         double x;
  891.  
  892.         x = BLT_FABS(value) * 0.1;
  893.         min = value - x;
  894.         max = value + x;
  895.         }
  896.     }
  897.     if (!AXIS_CONFIG_MIN_SET(axisPtr)) {
  898.         axisPtr->limits[LMIN] = min;
  899.     }
  900.     if (!AXIS_CONFIG_MAX_SET(axisPtr)) {
  901.         axisPtr->limits[LMAX] = max;
  902.     }
  903.     }
  904.     /* Indicate if the axis limits have changed */
  905.     result = (axisPtr->limits[LMAX] != axisPtr->prevMax) ||
  906.     (axisPtr->limits[LMIN] != axisPtr->prevMin);
  907.     /* and save the previous minimum and maximum values */
  908.     axisPtr->prevMin = axisPtr->limits[LMIN];
  909.     axisPtr->prevMax = axisPtr->limits[LMAX];
  910.     if (result) {
  911.     axisPtr->flags |= AXIS_CONFIG_DIRTY;
  912.     }
  913. }
  914.  
  915. /*
  916.  * ----------------------------------------------------------------------
  917.  *
  918.  * NiceNum --
  919.  *
  920.  *     Taken from Paul Heckbert's "Nice Numbers for Graph Labels" in
  921.  *    Graphics Gems (pp 61-63).  Finds a "nice" number approximately
  922.  *    equal to x.  Round the number if round=1, take ceiling if round=0.
  923.  *
  924.  * ----------------------------------------------------------------------
  925.  */
  926. static double
  927. NiceNum(x, round)
  928.     double x;
  929.     int round;
  930. {
  931.     double exponX;        /* exponent of x */
  932.     double fractX;        /* fractional part of x */
  933.     double nf;            /* nice, rounded fraction */
  934.  
  935.     exponX = floor(log10(x));
  936.     fractX = x / BLT_EXP10(exponX);    /* between 1 and 10 */
  937.     if (round) {
  938.     if (fractX < 1.5) {
  939.         nf = 1.;
  940.     } else if (fractX < 3.0) {
  941.         nf = 2.;
  942.     } else if (fractX < 7.0) {
  943.         nf = 5.;
  944.     } else {
  945.         nf = 10.;
  946.     }
  947.     } else if (fractX <= 1.0) {
  948.     nf = 1.;
  949.     } else if (fractX <= 2.0) {
  950.     nf = 2.;
  951.     } else if (fractX <= 5.0) {
  952.     nf = 5.0;
  953.     } else {
  954.     nf = 10.0;
  955.     }
  956.     return (nf * BLT_EXP10(exponX));
  957. }
  958.  
  959. /*
  960.  * ----------------------------------------------------------------------
  961.  *
  962.  * LogAxis --
  963.  *
  964.  *     Determine the range and units of a log scaled axis.
  965.  *
  966.  *     Unless the axis limits are specified, the axis is scaled
  967.  *     automatically, where the smallest and largest major ticks
  968.  *     encompass the range of actual data values.  When an axis
  969.  *     limit is specified, that value represents the
  970.  *     smallest(min)/largest(max) value in the displayed range of
  971.  *     values.
  972.  *
  973.  *     Both manual and automatic scaling are affected by the
  974.  *     step used.  By default, the step is the largest
  975.  *     power of ten to divide the range in more than one piece.
  976.  *
  977.  *     Automatic scaling:
  978.  *       Find the smallest number of units which contain the range of
  979.  *       values.  The minimum and maximum major tick values will be
  980.  *       represent the range of values for the axis. This greatest
  981.  *       number of major ticks possible is 10.
  982.  *
  983.  *     Manual scaling:
  984.  *       Make the minimum and maximum data values the represent the
  985.  *       range of the values for the axis.  The minimum and maximum
  986.  *       major ticks will be inclusive of this range.  This provides
  987.  *       the largest area for plotting and the expected results when
  988.  *       the axis min and max values have be set by the user (.e.g zooming).
  989.  *       The maximum number of major ticks is 20.
  990.  *
  991.  *       For log scale, there is always the possibility that the minimum
  992.  *       and maximum data values are the same magnitude.  To represent
  993.  *       the points properly, at least one full decade should be shown.
  994.  *       However, if you zoom a log scale plot, the results should be
  995.  *       predictable. Therefore, in that case, show only minor ticks.
  996.  *       Lastly, there should be an appropriate way to handle numbers <=0.
  997.  *
  998.  *          maxY
  999.  *            |    units = magnitude (of least significant digit)
  1000.  *            |    high  = largest unit tick < max axis value
  1001.  *      high _|    low   = smallest unit tick > min axis value
  1002.  *            |
  1003.  *            |    range = high - low
  1004.  *            |    # ticks = greatest factor of range/units
  1005.  *           _|
  1006.  *        U   |
  1007.  *        n   |
  1008.  *        i   |
  1009.  *        t  _|
  1010.  *            |
  1011.  *            |
  1012.  *            |
  1013.  *       low _|
  1014.  *            |
  1015.  *            |_minX________________maxX__
  1016.  *            |   |       |      |       |
  1017.  *     minY  low                        high
  1018.  *           minY
  1019.  *
  1020.  *
  1021.  *     numTicks = Number of ticks
  1022.  *     min = Minimum value of axis
  1023.  *     max = Maximum value of axis
  1024.  *     range    = Range of values (max - min)
  1025.  *
  1026.  *     If the number of decades is greater than ten, it is assumed
  1027.  *    that the full set of log-style ticks can't be drawn properly.
  1028.  *
  1029.  * Results:
  1030.  *    None
  1031.  *
  1032.  * ----------------------------------------------------------------------
  1033.  */
  1034. static void
  1035. LogAxis(axisPtr)
  1036.     Axis *axisPtr;
  1037. {
  1038.     double range;
  1039.     double min, max;
  1040.  
  1041.     min = axisPtr->limits[LMIN];
  1042.     max = axisPtr->limits[LMAX];
  1043.  
  1044.     if (min > 0.0) {
  1045.     min = floor(log10(min));
  1046.     } else {
  1047.     min = 0.0;
  1048.     }
  1049.     if (max > 0.0) {
  1050.     max = ceil(log10(max));
  1051.     } else {
  1052.     max = 1.0;
  1053.     }
  1054.     range = max - min;
  1055.     if (range > 10) {
  1056.     range = NiceNum(range, 0);
  1057.     axisPtr->step = NiceNum(range / (NTICK - 1), 1);
  1058.  
  1059.     /* Find the outer limits in terms of the step. */
  1060.     min = UFLOOR(min, axisPtr->step);
  1061.     max = UCEIL(max, axisPtr->step);
  1062.     axisPtr->numTicks = (int)((max - min) / axisPtr->step) + 1;
  1063.     axisPtr->subStep = BLT_EXP10(floor(log10(axisPtr->step)));
  1064.  
  1065.     if (axisPtr->step == axisPtr->subStep) {
  1066.         axisPtr->subTicks = 5;
  1067.         axisPtr->subStep = axisPtr->step * 0.2;
  1068.     } else {
  1069.         axisPtr->subTicks = BLT_RND(axisPtr->step / axisPtr->subStep);
  1070.     }
  1071.     } else {
  1072.     if (min == max) {
  1073.         max++;
  1074.     }
  1075.     axisPtr->numTicks = (int)((max - min) + 1);
  1076.     axisPtr->step = 1.0;
  1077.     axisPtr->subTicks = 10;
  1078.     }
  1079.     axisPtr->min = axisPtr->tickMin = min;
  1080.     axisPtr->max = axisPtr->tickMax = max;
  1081.     axisPtr->range = (max - min);
  1082. #ifdef notdef
  1083.     fprintf(stderr, "Major: %s\nRegion min=%g,max=%g\nTick min=%g,max=%g\n\
  1084. numTicks=%d, range=%g, step=%.15g\n", axisNames[axisPtr->type], min, max,
  1085.     axisPtr->tickMin, axisPtr->tickMax, axisPtr->numTicks,
  1086.     axisPtr->range, axisPtr->step);
  1087.     fprintf(stderr, "Minor numTicks=%d, step=%.15g\n\n",
  1088.     axisPtr->subTicks, axisPtr->subStep);
  1089. #endif
  1090. }
  1091.  
  1092. /*
  1093.  * ----------------------------------------------------------------------
  1094.  *
  1095.  * LinearAxis --
  1096.  *
  1097.  *     Determine the units of a linear scaled axis.
  1098.  *
  1099.  *     Unless the axis limits are specified, the axis is scaled
  1100.  *     automatically, where the smallest and largest major ticks
  1101.  *     encompass the range of actual data values.  When an axis
  1102.  *     limit is specified, that value represents the
  1103.  *     smallest(min)/largest(max) value in the displayed range of
  1104.  *     values.
  1105.  *
  1106.  *     Both manual and automatic scaling are affected by the
  1107.  *     step used.  By default, the step is the largest
  1108.  *     power of ten to divide the range in more than one piece.
  1109.  *
  1110.  *     Automatic scaling:
  1111.  *       Find the smallest number of units which contain the range of
  1112.  *       values.  The minimum and maximum major tick values will be
  1113.  *       represent the range of values for the axis. This greatest
  1114.  *       number of major ticks possible is 10.
  1115.  *
  1116.  *     Manual scaling:
  1117.  *       Make the minimum and maximum data values the represent the
  1118.  *       range of the values for the axis.  The minimum and maximum
  1119.  *       major ticks will be inclusive of this range.  This provides
  1120.  *       the largest area for plotting and the expected results when
  1121.  *       the axis min and max values have be set by the user (.e.g zooming).
  1122.  *       The maximum number of major ticks is 20.
  1123.  *
  1124.  *       For log scale, there is always the possibility that the minimum
  1125.  *       and maximum data values are the same magnitude.  To represent
  1126.  *       the points properly, at least one full decade should be shown.
  1127.  *       However, if you zoom a log scale plot, the results should be
  1128.  *       predictable. Therefore, in that case, show only minor ticks.
  1129.  *       Lastly, there should be an appropriate way to handle numbers <=0.
  1130.  *
  1131.  *          maxY
  1132.  *            |    units = magnitude (of least significant digit)
  1133.  *            |    high  = largest unit tick < max axis value
  1134.  *      high _|    low   = smallest unit tick > min axis value
  1135.  *            |
  1136.  *            |    range = high - low
  1137.  *            |    # ticks = greatest factor of range/units
  1138.  *           _|
  1139.  *        U   |
  1140.  *        n   |
  1141.  *        i   |
  1142.  *        t  _|
  1143.  *            |
  1144.  *            |
  1145.  *            |
  1146.  *       low _|
  1147.  *            |
  1148.  *            |_minX________________maxX__
  1149.  *            |   |       |      |       |
  1150.  *     minY  low                        high
  1151.  *           minY
  1152.  *
  1153.  *
  1154.  *     numTicks = Number of ticks
  1155.  *     min = Minimum value of axis
  1156.  *     max = Maximum value of axis
  1157.  *     range    = Range of values (max - min)
  1158.  *
  1159.  * Results:
  1160.  *    None.
  1161.  *
  1162.  * ----------------------------------------------------------------------
  1163.  */
  1164. static void
  1165. LinearAxis(axisPtr)
  1166.     Axis *axisPtr;
  1167. {
  1168.     double range, unit, pad;
  1169.     double min, max;
  1170.  
  1171.     min = axisPtr->limits[LMIN];
  1172.     max = axisPtr->limits[LMAX];
  1173.  
  1174.     /*
  1175.      * Calculate the major step.
  1176.      */
  1177.     range = max - min;
  1178.     if ((axisPtr->reqStep > 0.0) && (axisPtr->reqStep < range)) {
  1179.     axisPtr->step = axisPtr->reqStep;
  1180.     } else {
  1181.     range = NiceNum(range, 0);
  1182.     axisPtr->step = NiceNum(range / (NTICK - 1), 1);
  1183.     }
  1184.  
  1185.     /*
  1186.      * Find the outer tick values in terms of the major step interval.
  1187.      * Add +0.0 to preclude the possibility of an IEEE -0.0.
  1188.      */
  1189.  
  1190.     axisPtr->tickMin = UFLOOR(min, axisPtr->step) + 0.0;
  1191.     axisPtr->tickMax = UCEIL(max, axisPtr->step) + 0.0;
  1192.     range = axisPtr->tickMax - axisPtr->tickMin;
  1193.     unit = range / axisPtr->step;
  1194.     axisPtr->numTicks = BLT_RND(unit) + 1;
  1195.  
  1196.     /*
  1197.      * If the axis is "loose", the range is between the two outermost
  1198.      * ticks. Otherwise if it's "tight", the range is between the data
  1199.      * min and max.
  1200.      */
  1201.     if (axisPtr->loose) {
  1202.     axisPtr->min = axisPtr->tickMin, axisPtr->max = axisPtr->tickMax;
  1203.     } else {
  1204.     axisPtr->min = min, axisPtr->max = max;
  1205.     }
  1206.  
  1207.     /*
  1208.      * If is a limit is auto-scaled, add some padding so that the
  1209.      * symbols representing data points at the extremes aren't clipped
  1210.      * in half by the edge of the plot.  Two percent is an arbitrary
  1211.      * guess.
  1212.      */
  1213.  
  1214.     pad = (axisPtr->max - axisPtr->min) * 0.02;
  1215.     if (!AXIS_CONFIG_MIN_SET(axisPtr)) {
  1216.     axisPtr->min -= pad;
  1217.     }
  1218.     if (!AXIS_CONFIG_MAX_SET(axisPtr)) {
  1219.     axisPtr->max += pad;
  1220.     }
  1221.     axisPtr->range = axisPtr->max - axisPtr->min;
  1222.  
  1223. #ifdef notdef
  1224.     fprintf(stderr, "Major: %s\nRegion min=%g,max=%g\nTick min=%g,max=%g\n\
  1225. numTicks=%d, range=%g, step=%.15g\n", axisNames[axisPtr->type], min, max,
  1226.     axisPtr->tickMin, axisPtr->tickMax, axisPtr->numTicks,
  1227.     axisPtr->range, axisPtr->step);
  1228. #endif
  1229.  
  1230.     /* Now calculate the minor tick step and number. */
  1231.     axisPtr->subTicks = axisPtr->reqSubTicks;
  1232.     if (axisPtr->subTicks < 0) {
  1233.     axisPtr->subTicks = 0;
  1234.     }
  1235.     if (axisPtr->subTicks > 0) {
  1236.     axisPtr->subStep = axisPtr->step / axisPtr->subTicks;
  1237.     } else {
  1238.     axisPtr->subStep = axisPtr->step * 0.2;    /* Need this for layout */
  1239.     }
  1240. #ifdef notdef
  1241.     fprintf(stderr, "Minor numTicks=%d, step=%.15g\n\n",
  1242.     axisPtr->subTicks, axisPtr->subStep);
  1243. #endif
  1244. }
  1245.  
  1246. /*
  1247.  * -----------------------------------------------------------------
  1248.  *
  1249.  * SetAxisLimits  --
  1250.  *
  1251.  * -----------------------------------------------------------------
  1252.  */
  1253. static void
  1254. SetAxisLimits(graphPtr, axisPtr)
  1255.     Graph *graphPtr;
  1256.     Axis *axisPtr;
  1257. {
  1258.     UpdateLimits(graphPtr, axisPtr);
  1259.  
  1260.     /*
  1261.      * For barcharts, adjust the min or max values to include 0.0
  1262.      * if the bars are drawn along this axis.
  1263.      */
  1264.     if ((graphPtr->type == BARCHART) && (Y_AXIS(axisPtr))) {
  1265.     if (!AXIS_CONFIG_MIN_SET(axisPtr) && (axisPtr->limits[LMIN] > 0.0)) {
  1266.         axisPtr->limits[LMIN] = 0.0;
  1267.     }
  1268.     if (!AXIS_CONFIG_MAX_SET(axisPtr) && (axisPtr->limits[LMAX] < 0.0)) {
  1269.         axisPtr->limits[LMAX] = 0.0;
  1270.     }
  1271.     }
  1272.     if (axisPtr->flags & AXIS_CONFIG_DIRTY) {
  1273.     /* Calculate min/max tick (major/minor) layouts */
  1274.     if (axisPtr->logScale) {
  1275.         LogAxis(axisPtr);
  1276.     } else {
  1277.         LinearAxis(axisPtr);
  1278.     }
  1279.     axisPtr->flags &= ~AXIS_CONFIG_DIRTY;
  1280.  
  1281.     /* When any axis changes, we need to layout the entire graph. */
  1282.     graphPtr->flags |= (LAYOUT_ALL | DIRTY | REFRESH);
  1283.     }
  1284. }
  1285.  
  1286. /*
  1287.  * ----------------------------------------------------------------------
  1288.  *
  1289.  * Blt_ComputeAxes --
  1290.  *
  1291.  * Results:
  1292.  *    None.
  1293.  *
  1294.  * ----------------------------------------------------------------------
  1295.  */
  1296. void
  1297. Blt_ComputeAxes(graphPtr)
  1298.     Graph *graphPtr;
  1299. {
  1300.     register int i;
  1301.  
  1302.     for (i = 0; i < 4; i++) {
  1303.     SetAxisLimits(graphPtr, (Axis *)graphPtr->axisArr[i]);
  1304.     }
  1305. }
  1306.  
  1307. /*
  1308.  * ----------------------------------------------------------------------
  1309.  *
  1310.  * ConfigureAxis --
  1311.  *
  1312.  *    Configures axis attributes (font, line width, label, etc) and
  1313.  *    allocates a new (possibly shared) graphics context.  Line cap
  1314.  *    style is projecting.  This is for the problem of when a tick
  1315.  *    sits directly at the end point of the axis.
  1316.  *
  1317.  * Results:
  1318.  *    The return value is a standard Tcl result.
  1319.  *
  1320.  * Side Effects:
  1321.  *    Axis resources are allocated (GC, font). Axis layout is
  1322.  *    deferred until the height and width of the window are known.
  1323.  *
  1324.  * ----------------------------------------------------------------------
  1325.  */
  1326. static int
  1327. ConfigureAxis(graphPtr, axisPtr, argc, argv, flags)
  1328.     Graph *graphPtr;
  1329.     Axis *axisPtr;
  1330.     int argc;
  1331.     char *argv[];
  1332.     int flags;
  1333. {
  1334.     GC newGC;
  1335.     XGCValues gcValues;
  1336.     unsigned long gcMask;
  1337.     Tk_ConfigSpec *configSpecs;
  1338.  
  1339.     configSpecs = axisConfigSpecs[axisPtr->type];
  1340.     if (flags & TK_CONFIG_ARGV_ONLY) {
  1341.     if (argc == 0) {
  1342.         return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  1343.             configSpecs, (char *)axisPtr, (char *)NULL, flags));
  1344.     } else if (argc == 1) {
  1345.         return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  1346.             configSpecs, (char *)axisPtr, argv[0], flags));
  1347.     }
  1348.     }
  1349.     if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
  1350.         argc, argv, (char *)axisPtr, flags) != TCL_OK) {
  1351.     return TCL_ERROR;
  1352.     }
  1353.     /*
  1354.      * Check requested X and Y axis limits. Can't allow min to be
  1355.      * greater than max, or have undefined log scale limits.
  1356.      */
  1357.     if ((AXIS_CONFIG_MIN_SET(axisPtr)) && (AXIS_CONFIG_MAX_SET(axisPtr)) &&
  1358.     (axisPtr->limits[LMIN] >= axisPtr->limits[LMAX])) {
  1359.     sprintf(graphPtr->interp->result,
  1360.         "impossible %s-axis limits (min %g >= max %g)",
  1361.         axisNames[axisPtr->type], axisPtr->limits[LMIN],
  1362.         axisPtr->limits[LMAX]);
  1363.     return TCL_ERROR;
  1364.     }
  1365.     if ((axisPtr->logScale) && (AXIS_CONFIG_MIN_SET(axisPtr)) &&
  1366.     (axisPtr->limits[LMIN] <= 0.0)) {
  1367.     sprintf(graphPtr->interp->result,
  1368.         "invalid %s-axis limits (min=%g,max=%g) for log scale",
  1369.         axisNames[axisPtr->type], axisPtr->limits[LMIN],
  1370.         axisPtr->limits[LMAX]);
  1371.     return TCL_ERROR;
  1372.     }
  1373.     /*
  1374.      * Reset bogus line widths to zero. Can't allow bad line widths
  1375.      * because the layout routines compute axis and tick positions
  1376.      * with them.
  1377.      */
  1378.     if (axisPtr->lineWidth < 1) {
  1379.     axisPtr->lineWidth = 0;
  1380.     }
  1381.     /*
  1382.      * Create an unshared GC for the tick labels. The GC is private
  1383.      * because the labels may be rotated, requiring the GCStipple and
  1384.      * GCTSOffset fields to change.
  1385.      */
  1386.     gcMask = GCForeground | GCFont;
  1387.     gcValues.font = axisPtr->fontPtr->fid;
  1388.     gcValues.foreground = axisPtr->fgColorPtr->pixel;
  1389.     if (graphPtr->border != NULL) {
  1390.     gcValues.background = Tk_3DBorderColor(graphPtr->border)->pixel;
  1391.     gcMask |= GCBackground;
  1392.     }
  1393.     newGC = XCreateGC(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
  1394.     gcMask, &gcValues);
  1395.     if (axisPtr->textGC != NULL) {
  1396.     XFreeGC(graphPtr->display, axisPtr->textGC);
  1397.     }
  1398.     axisPtr->textGC = newGC;
  1399.  
  1400.     /* Create GC for axis line and ticks. */
  1401.  
  1402.     gcMask = GCForeground | GCLineWidth | GCCapStyle;
  1403.     gcValues.line_width = axisPtr->lineWidth;
  1404.     gcValues.cap_style = CapProjecting;
  1405.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  1406.     if (axisPtr->lineGC != NULL) {
  1407.     Tk_FreeGC(graphPtr->display, axisPtr->lineGC);
  1408.     }
  1409.     axisPtr->lineGC = newGC;
  1410.  
  1411.     /*
  1412.      * Don't bother to check what options have changed.  Almost every
  1413.      * axis configuration option changes the size of the plotting area
  1414.      * (except for -foreground).
  1415.      */
  1416.     graphPtr->flags |= LAYOUT_ALL;
  1417.     axisPtr->flags |= AXIS_CONFIG_DIRTY;
  1418.     SetAxisLimits(graphPtr, axisPtr);
  1419.     Blt_RedrawGraph(graphPtr);
  1420.     return TCL_OK;
  1421. }
  1422.  
  1423. /*
  1424.  * ----------------------------------------------------------------------
  1425.  *
  1426.  * DestroyAxis --
  1427.  *
  1428.  * Results:
  1429.  *    None.
  1430.  *
  1431.  * Side effects:
  1432.  *    Resources (font, color, gc, labels, etc.) associated with the
  1433.  *    axis are deallocated.
  1434.  *
  1435.  * ----------------------------------------------------------------------
  1436.  */
  1437. static void
  1438. DestroyAxis(graphPtr, axis)
  1439.     Graph *graphPtr;
  1440.     GraphAxis *axis;
  1441. {
  1442.     Axis *axisPtr = (Axis *)axis;
  1443.  
  1444.     Tk_FreeOptions(axisConfigSpecs[axisPtr->type], (char *)axisPtr,
  1445.     graphPtr->display, 0);
  1446.  
  1447.     if (axisPtr->lineGC != NULL) {
  1448.     Tk_FreeGC(graphPtr->display, axisPtr->lineGC);
  1449.     }
  1450.     if (axisPtr->textGC != NULL) {
  1451.     XFreeGC(graphPtr->display, axisPtr->textGC);
  1452.     }
  1453.     if (axisPtr->labelArr != NULL) {
  1454.     free((char *)axisPtr->labelArr);
  1455.     }
  1456.     if (axisPtr->segArr != NULL) {
  1457.     free((char *)axisPtr->segArr);
  1458.     }
  1459.     free((char *)axisPtr);
  1460. }
  1461.  
  1462. /*
  1463.  * ----------------------------------------------------------------------
  1464.  *
  1465.  * CalculateOffsets --
  1466.  *
  1467.  *    Determines the placements of the axis, major and minor ticks,
  1468.  *    and title of the axis.
  1469.  *
  1470.  * Results:
  1471.  *    None.
  1472.  *
  1473.  * ----------------------------------------------------------------------
  1474.  */
  1475. static void
  1476. CalculateOffsets(graphPtr, axisPtr)
  1477.     Graph *graphPtr;
  1478.     Axis *axisPtr;
  1479. {
  1480.     int pad;            /* Offset of axis from interior region. This
  1481.                  * includes a possible border and the axis
  1482.                  * line width. */
  1483.     unsigned int textHeight;
  1484.     int innerPos, outerPos;
  1485.     int majorOffset, minorOffset, labelOffset, titleOffset;
  1486.  
  1487.     textHeight = TEXTHEIGHT(graphPtr->fontPtr);
  1488.  
  1489.     titleOffset = graphPtr->borderWidth + textHeight;
  1490.     majorOffset = BLT_ABS(axisPtr->tickLength);
  1491.     minorOffset = BLT_RND(majorOffset * 0.5);
  1492.     labelOffset = BLT_RND(majorOffset * 1.4) + axisPtr->lineWidth / 2;
  1493.  
  1494.     /* Adjust offset for the interior border width and the line width */
  1495.     pad = graphPtr->plotBW + axisPtr->lineWidth + 2;
  1496.     if (graphPtr->plotBW > 0) {
  1497.     pad++;
  1498.     }
  1499.     if ((axisPtr->location == LEFT_AXIS) || (axisPtr->location == TOP_AXIS)) {
  1500.     majorOffset = -majorOffset;
  1501.     minorOffset = -minorOffset;
  1502.     labelOffset = -labelOffset;
  1503.     }
  1504.     /*
  1505.      * Pre-calculate the x-coordinate positions of the axis,
  1506.      * tick labels, and the individual major and minor ticks.
  1507.      */
  1508.     switch (axisPtr->location) {
  1509.     case BOTTOM_AXIS:
  1510.     innerPos = graphPtr->origin.y + pad;
  1511.     axisPtr->titlePos.x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
  1512.     axisPtr->titlePos.y = graphPtr->height - titleOffset;
  1513.     axisPtr->anchor = TK_ANCHOR_N;
  1514.     break;
  1515.  
  1516.     case LEFT_AXIS:
  1517.     innerPos = graphPtr->origin.x - pad;
  1518.     axisPtr->titlePos.x = titleOffset;
  1519.     axisPtr->titlePos.y = (graphPtr->origin.y + graphPtr->extreme.y) / 2;
  1520.     axisPtr->anchor = TK_ANCHOR_E;
  1521.     break;
  1522.  
  1523.     case TOP_AXIS:
  1524.     innerPos = graphPtr->extreme.y - pad;
  1525.     axisPtr->titlePos.x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
  1526.     axisPtr->titlePos.y = titleOffset;
  1527.     if (graphPtr->title != NULL) {
  1528.         axisPtr->titlePos.y += (2 * textHeight);
  1529.     } else {
  1530.         axisPtr->titlePos.y += (textHeight / 2);
  1531.     }
  1532.     axisPtr->anchor = TK_ANCHOR_S;
  1533.     break;
  1534.  
  1535.     case RIGHT_AXIS:
  1536.     innerPos = graphPtr->extreme.x + pad;
  1537.     axisPtr->titlePos.x = graphPtr->width -
  1538.         (graphPtr->legendPtr->width + titleOffset);
  1539.     axisPtr->titlePos.y = (graphPtr->origin.y + graphPtr->extreme.y) / 2;
  1540.     axisPtr->anchor = TK_ANCHOR_W;
  1541.     break;
  1542.  
  1543.     }
  1544.     outerPos = innerPos + majorOffset;
  1545.     axisPtr->posArr[MAJOR_TICK] = outerPos;
  1546.     axisPtr->posArr[AXIS_LINE] = innerPos;
  1547.     axisPtr->posArr[MINOR_TICK] = innerPos + minorOffset;
  1548.     axisPtr->posArr[TICK_LABEL] = innerPos + labelOffset;
  1549.     if (axisPtr->tickLength < 0) {
  1550.     axisPtr->posArr[MAJOR_TICK] = innerPos;
  1551.     axisPtr->posArr[AXIS_LINE] = outerPos;
  1552.     }
  1553. }
  1554.  
  1555. static XSegment
  1556. AxisLine(axisPtr, min, max)
  1557.     Axis *axisPtr;        /* Axis information */
  1558.     double min, max;        /* Limits of axis in graph coordinates */
  1559. {
  1560.     double normMin, normMax;
  1561.     XSegment segment;
  1562.  
  1563.     normMax = NORM(axisPtr, min);
  1564.     if (axisPtr->descending) {
  1565.     normMax = 1.0 - normMax;
  1566.     }
  1567.     normMin = NORM(axisPtr, max);
  1568.     if (axisPtr->descending) {
  1569.     normMin = 1.0 - normMin;
  1570.     }
  1571.     if (HORIZONTAL_AXIS(axisPtr)) {
  1572.     segment.y1 = segment.y2 = axisPtr->posArr[AXIS_LINE];
  1573.     segment.x1 = MAPX(axisPtr, normMin);
  1574.     segment.x2 = MAPX(axisPtr, normMax);
  1575.     } else {
  1576.     segment.x1 = segment.x2 = axisPtr->posArr[AXIS_LINE];
  1577.     segment.y1 = MAPY(axisPtr, normMin);
  1578.     segment.y2 = MAPY(axisPtr, normMax);
  1579.     }
  1580.     return (segment);
  1581. }
  1582.  
  1583.  
  1584. static XSegment
  1585. Tick(axisPtr, value, whichTick)
  1586.     Axis *axisPtr;
  1587.     double value;
  1588.     int whichTick;        /* If non-zero, create minor tick instead */
  1589. {
  1590.     double norm;
  1591.     XSegment segment;
  1592.     int tick;
  1593.  
  1594.     norm = NORM(axisPtr, value);
  1595.     if (axisPtr->descending) {
  1596.     norm = 1.0 - norm;
  1597.     }
  1598.     tick = axisPtr->posArr[whichTick];
  1599.     if (HORIZONTAL_AXIS(axisPtr)) {
  1600.     segment.y1 = axisPtr->posArr[AXIS_LINE];
  1601.     segment.y2 = tick;
  1602.     segment.x1 = segment.x2 = MAPX(axisPtr, norm);
  1603.     } else {
  1604.     segment.x1 = axisPtr->posArr[AXIS_LINE];
  1605.     segment.x2 = tick;
  1606.     segment.y1 = segment.y2 = MAPY(axisPtr, norm);
  1607.     }
  1608.     return (segment);
  1609. }
  1610.  
  1611. /*
  1612.  * -----------------------------------------------------------------
  1613.  *
  1614.  * LayoutAxis --
  1615.  *
  1616.  *    Pre-calculate the x-coordinate positions of the axis, ticks
  1617.  *    and labels to be used later when displaying the X axis.  Ticks
  1618.  *    (minor and major) will be saved in an array of XSegments so
  1619.  *    that they can be drawn in one XDrawSegments call. The strings
  1620.  *    representing the tick labels and the corresponding window
  1621.  *    positions are saved in an array of Label's.
  1622.  *
  1623.  *      Calculates the values for each major and minor tick and checks to
  1624.  *    see if they are in range (the outer ticks may be outside of the
  1625.  *    range of plotted values).
  1626.  *
  1627.  * Results:
  1628.  *    None.
  1629.  *
  1630.  * SideEffects:
  1631.  *    Line segments and tick labels saved will be used to draw
  1632.  *    the X axis.
  1633.  * ----------------------------------------------------------------- */
  1634. static void
  1635. LayoutAxis(graphPtr, axis)
  1636.     Graph *graphPtr;
  1637.     GraphAxis *axis;
  1638. {
  1639.     Axis *axisPtr = (Axis *)axis;
  1640.     XSegment *segArr;
  1641.     unsigned int arraySize;
  1642.     double min, max;
  1643.     double value, subValue;
  1644.     register int i, j;
  1645.     register int sgmts, labels;
  1646.     double epsilon;
  1647.     static float logTable[] =    /* Precomputed log10 values [1..10] */
  1648.     {
  1649.     0.0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1.0
  1650.     };
  1651.  
  1652.     CalculateOffsets(graphPtr, axisPtr);
  1653.  
  1654.     /* Save all line coordinates in an array of line segments. */
  1655.  
  1656.     arraySize = (1 + (axisPtr->numTicks * (axisPtr->subTicks + 1)));
  1657.     segArr = (XSegment *)malloc(arraySize * sizeof(XSegment));
  1658.     if (segArr == NULL) {
  1659.     return;            /* Can't allocate array of segments */
  1660.     }
  1661.     if ((axisPtr->logScale) || (axisPtr->loose) ||
  1662.     (axisPtr->limits[LMIN] == axisPtr->limits[LMAX])) {
  1663.     min = axisPtr->tickMin, max = axisPtr->tickMax;
  1664.     } else {
  1665.     min = axisPtr->limits[LMIN];
  1666.     max = axisPtr->limits[LMAX];
  1667.     }
  1668.  
  1669.     /* Axis baseline */
  1670.     segArr[0] = AxisLine(axisPtr, min, max);
  1671.  
  1672.     sgmts = 1, labels = 0;
  1673.     if (!axisPtr->showTicks) {
  1674.     goto done;        /* Only display axis line */
  1675.     }
  1676.     /* Use numbers just beyond the limits when testing for equality */
  1677.     epsilon = 2 * MIN_DBL_VALUE;
  1678.     min -= epsilon, max += epsilon;
  1679.  
  1680.     value = axisPtr->tickMin;    /* Start from smallest axis tick */
  1681.     for (i = 0; i < axisPtr->numTicks; i++) {
  1682.     subValue = value = UROUND(value, axisPtr->step);
  1683.  
  1684.     /* Minor ticks */
  1685.     for (j = 1; j < axisPtr->subTicks; j++) {
  1686.         if ((axisPtr->logScale) && (axisPtr->step == 1.0)) {
  1687.         subValue = value + (double)logTable[j];
  1688.         } else {
  1689.         subValue += axisPtr->subStep;
  1690.         }
  1691.         if ((subValue >= min) && (subValue <= max)) {
  1692.         segArr[sgmts] = Tick(axisPtr, subValue, MINOR_TICK);
  1693.         sgmts++;
  1694.         }
  1695.     }
  1696.  
  1697.     /* Major tick and label */
  1698.     if ((value >= min) && (value <= max)) {
  1699.         short int labelPos;
  1700.  
  1701.         segArr[sgmts] = Tick(axisPtr, value, MAJOR_TICK);
  1702.         labelPos = (short int)axisPtr->posArr[TICK_LABEL];
  1703.  
  1704.         /* Save tick label position */
  1705.  
  1706.         if (HORIZONTAL_AXIS(axisPtr)) {
  1707.         axisPtr->labelArr[labels].x = segArr[sgmts].x1;
  1708.         axisPtr->labelArr[labels].y = labelPos;
  1709.         } else {
  1710.         axisPtr->labelArr[labels].x = labelPos;
  1711.         axisPtr->labelArr[labels].y = segArr[sgmts].y1;
  1712.         }
  1713.         sgmts++, labels++;
  1714.     }
  1715.     value += axisPtr->step;
  1716.     }
  1717.  
  1718.   done:
  1719.  
  1720.     assert(sgmts <= arraySize);
  1721.     assert(labels <= axisPtr->numLabels);
  1722.  
  1723.     if (axisPtr->segArr != NULL) {
  1724.     free((char *)axisPtr->segArr);
  1725.     }
  1726.     axisPtr->segArr = segArr;
  1727.     axisPtr->numSegments = sgmts;
  1728. }
  1729.  
  1730. /*
  1731.  * -----------------------------------------------------------------
  1732.  *
  1733.  * DisplayAxis --
  1734.  *
  1735.  *    Draws the axis, ticks, and labels onto the canvas.
  1736.  *
  1737.  *    Initializes and passes text attribute information through
  1738.  *    TextAttributes structure.
  1739.  *
  1740.  * Results:
  1741.  *    None.
  1742.  *
  1743.  * Side Effects:
  1744.  *    Axis gets drawn on window.
  1745.  *
  1746.  * -----------------------------------------------------------------
  1747.  */
  1748.  
  1749. static float titleRot[4] =    /* Rotation for each axis title */
  1750. {
  1751.     0.0, 90.0, 0.0, 270.0
  1752. };
  1753.  
  1754. static void
  1755. DisplayAxis(graphPtr, axis, attrPtr)
  1756.     Graph *graphPtr;
  1757.     GraphAxis *axis;
  1758.     TextAttributes *attrPtr;
  1759. {
  1760.     Axis *axisPtr = (Axis *)axis;
  1761.  
  1762.     if (axisPtr->title != NULL) {
  1763.     attrPtr->theta = (double)titleRot[axisPtr->location];
  1764.     Blt_DrawText(graphPtr->display, graphPtr->canvas, axisPtr->title,
  1765.         attrPtr, axisPtr->titlePos.x, axisPtr->titlePos.y);
  1766.     }
  1767.     if (axisPtr->showTicks) {
  1768.     register int i;
  1769.     TextAttributes textAttr;
  1770.  
  1771.     /* Setup static text attribute information */
  1772.  
  1773.     textAttr.theta = axisPtr->theta;
  1774.     textAttr.anchor = axisPtr->anchor;
  1775.     textAttr.fontPtr = axisPtr->fontPtr;
  1776.     textAttr.fgColorPtr = axisPtr->fgColorPtr;
  1777.     textAttr.bgColorPtr = Tk_3DBorderColor(graphPtr->border);
  1778.     textAttr.gc = axisPtr->textGC;
  1779.  
  1780.     /* Draw the ticks labels and then the ticks and axis */
  1781.     for (i = 0; i < axisPtr->numLabels; i++) {
  1782.         Blt_DrawText(graphPtr->display, graphPtr->canvas,
  1783.         axisPtr->labelArr[i].text, &textAttr,
  1784.         axisPtr->labelArr[i].x, axisPtr->labelArr[i].y);
  1785.     }
  1786.     }
  1787.     if (axisPtr->numSegments > 0) {
  1788.     XDrawSegments(graphPtr->display, graphPtr->canvas, axisPtr->lineGC,
  1789.         axisPtr->segArr, axisPtr->numSegments);
  1790.     }
  1791. }
  1792.  
  1793. /*
  1794.  * -----------------------------------------------------------------
  1795.  *
  1796.  * PrintAxis --
  1797.  *
  1798.  *    Generates PostScript output to draw the axis, ticks, and
  1799.  *    labels.
  1800.  *
  1801.  *    Initializes and passes text attribute information through
  1802.  *    TextAttributes structure.
  1803.  *
  1804.  * Results:
  1805.  *    None.
  1806.  *
  1807.  * Side Effects:
  1808.  *    PostScript output is left in graphPtr->interp->result;
  1809.  *
  1810.  * -----------------------------------------------------------------
  1811.  */
  1812. static void
  1813. PrintAxis(graphPtr, axis, attrPtr)
  1814.     Graph *graphPtr;
  1815.     GraphAxis *axis;
  1816.     TextAttributes *attrPtr;
  1817. {
  1818.     Axis *axisPtr = (Axis *)axis;
  1819.  
  1820.     if (axisPtr->title != NULL) {
  1821.     attrPtr->theta = (double)titleRot[axisPtr->location];
  1822.     Blt_TextToPostScript(graphPtr, axisPtr->title, attrPtr,
  1823.         axisPtr->titlePos.x, axisPtr->titlePos.y);
  1824.     }
  1825.     if (axisPtr->showTicks) {
  1826.     TextAttributes textAttr;
  1827.     register int i;
  1828.  
  1829.     /* Setup static text attribute information */
  1830.  
  1831.     textAttr.theta = axisPtr->theta;
  1832.     textAttr.anchor = axisPtr->anchor;
  1833.     textAttr.fontPtr = axisPtr->fontPtr;
  1834.     textAttr.fgColorPtr = axisPtr->fgColorPtr;
  1835.     textAttr.bgColorPtr = (XColor *)NULL;
  1836.  
  1837.     for (i = 0; i < axisPtr->numLabels; i++) {
  1838.         Blt_TextToPostScript(graphPtr, axisPtr->labelArr[i].text,
  1839.         &textAttr, axisPtr->labelArr[i].x, axisPtr->labelArr[i].y);
  1840.     }
  1841.     }
  1842.     if (axisPtr->numSegments > 0) {
  1843.     Blt_SetLineAttributes(graphPtr, axisPtr->fgColorPtr,
  1844.         axisPtr->lineWidth, 0);
  1845.     Blt_SegmentsToPostScript(graphPtr, axisPtr->segArr,
  1846.         axisPtr->numSegments);
  1847.     }
  1848. }
  1849.  
  1850. static void
  1851. GetAxisGeometry(graphPtr, axisPtr)
  1852.     Graph *graphPtr;
  1853.     Axis *axisPtr;
  1854. {
  1855.     register int i;
  1856.     register int count;
  1857.     char label[TCL_DOUBLE_SPACE];
  1858.     unsigned int length;
  1859.     unsigned int arraySize, poolSize, used;
  1860.     char *pool;
  1861.     unsigned int textWidth, textHeight;
  1862.     unsigned int bbWidth, bbHeight;
  1863.     int maxWidth, maxHeight;
  1864.     double value;
  1865.     double epsilon, minAxis, maxAxis;
  1866.     Label *labelArr;
  1867.     int pad;
  1868.  
  1869.     if ((axisPtr->logScale) || (axisPtr->loose) ||
  1870.     (axisPtr->limits[LMIN] == axisPtr->limits[LMAX])) {
  1871.     minAxis = axisPtr->tickMin, maxAxis = axisPtr->tickMax;
  1872.     } else {
  1873.     minAxis = axisPtr->limits[LMIN];
  1874.     maxAxis = axisPtr->limits[LMAX];
  1875.     }
  1876.  
  1877.     /* Use numbers just beyond the limits when testing for equality */
  1878.  
  1879.     epsilon = 2 * MIN_DBL_VALUE;
  1880.     minAxis -= epsilon, maxAxis += epsilon;
  1881.  
  1882.     /* Create an array of labels with an attached of characters strings */
  1883.     arraySize = axisPtr->numTicks * sizeof(Label);
  1884.     poolSize = axisPtr->numTicks * AVG_TICK_NUM_CHARS * sizeof(char);
  1885.     labelArr = (Label *)malloc(arraySize + poolSize);
  1886.     pool = (char *)labelArr + arraySize;
  1887.  
  1888.     textHeight = TEXTHEIGHT(axisPtr->fontPtr);
  1889.  
  1890.     maxHeight = maxWidth = 0;
  1891.     used = count = 0;
  1892.     value = axisPtr->tickMin;
  1893.     for (i = 0; i < axisPtr->numTicks; i++, value += axisPtr->step) {
  1894.     value = UROUND(value, axisPtr->step);
  1895.     if ((value < minAxis) || (value > maxAxis)) {
  1896.         continue;        /* Out of range */
  1897.     }
  1898.     MakeLabel(graphPtr, axisPtr, value, label);
  1899.     length = strlen(label);
  1900.  
  1901.     /*  Resize the label array if we overflow its string pool */
  1902.     if (poolSize <= (used + length)) {
  1903.         int newSize;
  1904.         Label *newArr;
  1905.  
  1906.         newSize = poolSize + poolSize;
  1907.         while (newSize <= (used + length)) {
  1908.         newSize += newSize;
  1909.         }
  1910.         newArr = (Label *)malloc(arraySize + newSize);
  1911.         memcpy((char *)newArr, (char *)labelArr, arraySize + poolSize);
  1912.         pool = (char *)newArr + arraySize;
  1913.         free((char *)labelArr);
  1914.         poolSize = newSize;
  1915.         labelArr = newArr;
  1916.     }
  1917.     textWidth = Blt_TextStringWidth(axisPtr->fontPtr, label);
  1918.     labelArr[count].text = (pool + used);
  1919.     strcpy(labelArr[count].text, label);
  1920.     used += (length + 1);
  1921.     count++;
  1922.  
  1923.     if (axisPtr->theta == 0.0) {
  1924.         bbWidth = textWidth, bbHeight = textHeight;
  1925.     } else {
  1926.         Blt_GetBoundingBox(textWidth, textHeight, axisPtr->theta,
  1927.         &bbWidth, &bbHeight, (XPoint *)NULL);
  1928.     }
  1929.     if (bbWidth > maxWidth) {
  1930.         maxWidth = bbWidth;
  1931.     }
  1932.     if (bbHeight > maxHeight) {
  1933.         maxHeight = bbHeight;
  1934.     }
  1935.     }
  1936.     if (axisPtr->labelArr != NULL) {
  1937.     free((char *)axisPtr->labelArr);
  1938.     }
  1939.     axisPtr->labelArr = labelArr;
  1940.     axisPtr->numLabels = count;
  1941.     assert(axisPtr->numLabels <= axisPtr->numTicks);
  1942.  
  1943.     /*
  1944.      * Because the axis cap style is "CapProjecting", there's an extra
  1945.      * 1.5 linewidth to be accounted for.
  1946.      */
  1947.     pad = ((axisPtr->lineWidth * 15) / 10) + 2;
  1948.     axisPtr->width = maxWidth + pad;
  1949.     axisPtr->height = maxHeight + pad;
  1950.  
  1951.     value = BLT_ABS(axisPtr->tickLength) * 1.4;
  1952.     pad = BLT_RND(value) + graphPtr->plotBW + 1;
  1953.     if (graphPtr->plotBW > 0) {
  1954.     pad++;
  1955.     }
  1956.     if (HORIZONTAL_AXIS(axisPtr)) {
  1957.     axisPtr->height += pad;
  1958.     } else {
  1959.     axisPtr->width += pad;
  1960.     }
  1961. }
  1962.  
  1963. void
  1964. Blt_UpdateAxisBackgrounds(graphPtr, colorPtr)
  1965.     Graph *graphPtr;
  1966.     XColor *colorPtr;        /* Background color of graph margin area */
  1967. {
  1968.     Axis *axisPtr;
  1969.     register int i;
  1970.  
  1971.     for (i = 0; i < 4; i++) {
  1972.     axisPtr = (Axis *)graphPtr->axisArr[i];
  1973.     XSetBackground(Tk_Display(graphPtr->tkwin), axisPtr->textGC,
  1974.         colorPtr->pixel);
  1975.     }
  1976. }
  1977.  
  1978. /*
  1979.  * -----------------------------------------------------------------
  1980.  *
  1981.  * Blt_ComputeLayout --
  1982.  *
  1983.  *     Calculate the layout of the graph.  Based upon the data,
  1984.  *    axis limits, X and Y titles, and title height, determine
  1985.  *    the cavity left which is the plotting surface.  The first
  1986.  *    step get the data and axis limits for calculating the space
  1987.  *    needed for the top, bottom, left, and right margins.
  1988.  *
  1989.  *     1) The LEFT margin is the area from the left border to the
  1990.  *       Y axis (not including ticks). It composes the border
  1991.  *       width, the width an optional Y axis label and its padding,
  1992.  *       and the tick numeric labels. The Y axis label is rotated
  1993.  *       90 degrees so that the width is the font height.
  1994.  *
  1995.  *     2) The RIGHT margin is the area from the end of the graph
  1996.  *       to the right window border. It composes the border width,
  1997.  *       some padding, the font height (this may be dubious. It
  1998.  *       appears to provide a more even border), the max of the
  1999.  *       legend width and 1/2 max X tick number. This last part is
  2000.  *       so that the last tick label is not clipped.
  2001.  *
  2002.  *           Area Width
  2003.  *      ___________________________________________________________
  2004.  *      |          |                               |               |
  2005.  *      |          |   TOP  height of title        |               |
  2006.  *      |          |                               |               |
  2007.  *      |          |           x2 title            |               |
  2008.  *      |          |                               |               |
  2009.  *      |          |        height of x2-axis      |               |
  2010.  *      |__________|_______________________________|_______________|  A
  2011.  *      |          |                        extreme|               |  r
  2012.  *      |          |                               |               |  e
  2013.  *      |   LEFT   |                               |     RIGHT     |  a
  2014.  *      |          |                               |               |
  2015.  *      | y        |     Free area = 104%          |      y2       |  H
  2016.  *      |          |     Plotting surface = 100%   |               |  e
  2017.  *      | t        |     Tick length = 2 + 2%      |      t        |  i
  2018.  *      | i        |                               |      i        |  g
  2019.  *      | t        |                               |      t  legend|  h
  2020.  *      | l        |                               |      l   width|  t
  2021.  *      | e        |                               |      e        |
  2022.  *      |    height|                               |height         |
  2023.  *      |       of |                               | of            |
  2024.  *      |    y-axis|                               |y2-axis        |
  2025.  *      |          |                               |               |
  2026.  *      |          |origin                         |               |
  2027.  *      |__________|_______________________________|_______________|
  2028.  *      |          | (xoffset, yoffset)            |               |
  2029.  *      |          |                               |               |
  2030.  *      |          |       height of x-axis        |               |
  2031.  *      |          |                               |               |
  2032.  *      |          |   BOTTOM   x title            |               |
  2033.  *      |__________|_______________________________|_______________|
  2034.  *
  2035.  * 3) The TOP margin is the area from the top window border to the top
  2036.  *    of the graph. It composes the border width, twice the height of
  2037.  *    the title font (if one is given) and some padding between the
  2038.  *    title.
  2039.  *
  2040.  * 4) The BOTTOM margin is area from the bottom window border to the
  2041.  *    X axis (not including ticks). It composes the border width, the height
  2042.  *    an optional X axis label and its padding, the height of the font
  2043.  *    of the tick labels.
  2044.  *
  2045.  * The plotting area is between the margins which includes the X and Y axes
  2046.  * including the ticks but not the tick numeric labels. The length of
  2047.  * the ticks and its padding is 5% of the entire plotting area.  Hence the
  2048.  * entire plotting area is scaled as 105% of the width and height of the
  2049.  * area.
  2050.  *
  2051.  * The axis labels, ticks labels, title, and legend may or may not be
  2052.  * displayed which must be taken into account.
  2053.  *
  2054.  *
  2055.  * -----------------------------------------------------------------
  2056.  */
  2057. int
  2058. Blt_ComputeLayout(graphPtr)
  2059.     Graph *graphPtr;
  2060. {
  2061.     int left, right, top, bottom;
  2062.     int maxTickWidth;
  2063.     int height;
  2064.     int leftOver;
  2065.     unsigned int borderWidths;
  2066.     unsigned int lineHeight = TEXTHEIGHT(graphPtr->fontPtr);
  2067.     Axis *x1, *x2, *y1, *y2;
  2068.     unsigned int twiceHeight = (2 * lineHeight);
  2069.     unsigned int halfHeight = (lineHeight / 2);
  2070.     double value;
  2071.  
  2072.     x1 = (Axis *)graphPtr->bottomAxis;
  2073.     x2 = (Axis *)graphPtr->topAxis;
  2074.     y1 = (Axis *)graphPtr->leftAxis;
  2075.     y2 = (Axis *)graphPtr->rightAxis;
  2076.  
  2077.     top = (graphPtr->title != NULL) ? twiceHeight : halfHeight;
  2078.     left = ((y1->mapped) && (y1->title != NULL)) ? twiceHeight : halfHeight;
  2079.     bottom = ((x1->mapped) && (x1->title != NULL)) ? twiceHeight : halfHeight;
  2080.     right = ((y2->mapped) && (y2->title != NULL)) ? twiceHeight : 0;
  2081.  
  2082.     if ((x2->mapped) && (x2->title != NULL)) {
  2083.     top += twiceHeight;
  2084.     }
  2085.     GetAxisGeometry(graphPtr, x1);
  2086.     maxTickWidth = 0;
  2087.     if ((x1->mapped) && (x1->showTicks)) {
  2088.     bottom += x1->height;
  2089.     }
  2090.     GetAxisGeometry(graphPtr, x2);
  2091.     if ((x2->mapped) && (x2->showTicks)) {
  2092.     top += x2->height;
  2093.     }
  2094.     GetAxisGeometry(graphPtr, y1);
  2095.     if ((y1->mapped) && (y1->showTicks)) {
  2096.     left += y1->width + PADX;
  2097.     }
  2098.     GetAxisGeometry(graphPtr, y2);
  2099.     if ((y2->mapped) && (y2->showTicks)) {
  2100.     right += y2->width + PADX;
  2101.     }
  2102.     /* Override calculated values if user specified margins */
  2103.  
  2104.     if (graphPtr->leftMargin > 0) {
  2105.     left = graphPtr->leftMargin;
  2106.     }
  2107.     if (graphPtr->topMargin > 0) {
  2108.     top = graphPtr->topMargin;
  2109.     }
  2110.     if (graphPtr->bottomMargin > 0) {
  2111.     bottom = graphPtr->bottomMargin;
  2112.     }
  2113.     borderWidths = graphPtr->borderWidth + graphPtr->plotBW;
  2114.     height = graphPtr->height - ((2 * borderWidths) + top + bottom);
  2115.     (*graphPtr->legendPtr->geomProc) (graphPtr, height);
  2116.     if ((graphPtr->legendPtr->mapped) && (graphPtr->legendPtr->useDefault)) {
  2117.     right += graphPtr->legendPtr->width;
  2118.     } else {
  2119.     right += halfHeight;
  2120.     }
  2121.     maxTickWidth = BLT_MAX(x1->width, x2->width) / 2;
  2122.     if (right < maxTickWidth) {
  2123.     right = maxTickWidth;
  2124.     }
  2125.     if (graphPtr->rightMargin > 0) {
  2126.     right = graphPtr->rightMargin;
  2127.     }
  2128.     top += borderWidths;
  2129.     left += borderWidths;
  2130.     right += borderWidths;
  2131.     bottom += borderWidths;
  2132.  
  2133.     /* Based upon the margins, calculate the space left for the graph. */
  2134.  
  2135.     x1->offset = left;
  2136.     y1->offset = graphPtr->height - bottom;
  2137.     leftOver = graphPtr->width - (left + right);
  2138.     if (leftOver < 0) {
  2139.     return TCL_ERROR;
  2140.     }
  2141.     x1->scale = (double)leftOver;    /* Pixels per X unit */
  2142.     leftOver = graphPtr->height - (top + bottom);
  2143.     if (leftOver < 0) {
  2144.     return TCL_ERROR;
  2145.     }
  2146.     y1->scale = (double)leftOver;    /* Pixels per Y unit */
  2147.     x2->scale = x1->scale;
  2148.     x2->offset = x1->offset;
  2149.     y2->scale = y1->scale;
  2150.     y2->offset = y1->offset;
  2151.  
  2152.     /* Calculate the average symbol (formula is arbitrary) */
  2153.  
  2154.     value = log(((double)x1->scale) * y1->scale) * 0.8;
  2155.     graphPtr->avgSymSize = BLT_RND(value);
  2156.  
  2157.     graphPtr->origin.x = MAPX(x1, 0.0);
  2158.     graphPtr->origin.y = MAPY(y1, 0.0);
  2159.     graphPtr->extreme.x = MAPX(x1, 1.0);
  2160.     graphPtr->extreme.y = MAPY(y1, 1.0);
  2161.     return TCL_OK;
  2162. }
  2163.  
  2164. /*
  2165.  *--------------------------------------------------------------
  2166.  *
  2167.  * GetAxisLimits --
  2168.  *
  2169.  *    This procedure returns a string representing the axis limits
  2170.  *    of the graph.  The format of the string is { xmin ymin xmax ymax}.
  2171.  *
  2172.  * Results:
  2173.  *    Always returns TCL_OK.  The interp->result field is
  2174.  *    a list of the graph axis limits.
  2175.  *
  2176.  *--------------------------------------------------------------
  2177.  */
  2178. static int
  2179. GetAxisLimits(axisPtr, argc, argv)
  2180.     Axis *axisPtr;
  2181.     int argc;
  2182.     char **argv;
  2183.  
  2184. {
  2185.     char string[TCL_DOUBLE_SPACE + 1];
  2186.     double min, max;
  2187.  
  2188.     if (argc != 3) {
  2189.     Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
  2190.         argv[0], " ", axisNames[axisPtr->type], "axis limits\"",
  2191.         (char *)NULL);
  2192.     return TCL_ERROR;
  2193.     }
  2194.     min = axisPtr->min, max = axisPtr->max;
  2195.     if (axisPtr->logScale) {
  2196.     min = BLT_EXP10(min);
  2197.     max = BLT_EXP10(max);
  2198.     }
  2199.     Tcl_PrintDouble(axisPtr->interp, min, string);
  2200.     Tcl_AppendElement(axisPtr->interp, string);
  2201.     Tcl_PrintDouble(axisPtr->interp, max, string);
  2202.     Tcl_AppendElement(axisPtr->interp, string);
  2203.     return TCL_OK;
  2204. }
  2205.  
  2206. /*
  2207.  * ----------------------------------------------------------------------
  2208.  *
  2209.  * InvTransformCoord --
  2210.  *
  2211.  *    Maps the given window coordinate into an axis-value.
  2212.  *
  2213.  * Results:
  2214.  *    Returns a standard Tcl result.  interp->result contains
  2215.  *    the axis value. If an error occurred, TCL_ERROR is returned
  2216.  *    and interp->result will contain an error message.
  2217.  *
  2218.  * ----------------------------------------------------------------------
  2219.  */
  2220. static int
  2221. InvTransformCoord(axisPtr, argc, argv)
  2222.     Axis *axisPtr;
  2223.     int argc;
  2224.     char **argv;
  2225. {
  2226.     int coord;            /* Integer window coordinate*/
  2227.     char string[TCL_DOUBLE_SPACE + 1];
  2228.     double value;
  2229.  
  2230.     if (argc != 4) {
  2231.     Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
  2232.         argv[0], " ", axisNames[axisPtr->type],
  2233.         "axis invtransform winPos\"", (char *)NULL);
  2234.     return TCL_ERROR;
  2235.     }
  2236.     if (Tcl_GetInt(axisPtr->interp, argv[2], &coord) != TCL_OK) {
  2237.     return TCL_ERROR;
  2238.     }
  2239.     value = Blt_InvTransform((GraphAxis *)axisPtr, coord);
  2240.     Tcl_PrintDouble(axisPtr->interp, value, string);
  2241.     Tcl_AppendElement(axisPtr->interp, string);
  2242.     return TCL_OK;
  2243. }
  2244.  
  2245. /*
  2246.  * ----------------------------------------------------------------------
  2247.  *
  2248.  * TransformCoord --
  2249.  *
  2250.  *    Maps the given axis-value to a window coordinate.
  2251.  *
  2252.  * Results:
  2253.  *    Returns a standard Tcl result.  interp->result contains
  2254.  *    the window coordinate. If an error occurred, TCL_ERROR
  2255.  *    is returned and interp->result will contain an error
  2256.  *    message.
  2257.  *
  2258.  * ----------------------------------------------------------------------
  2259.  */
  2260. static int
  2261. TransformCoord(axisPtr, argc, argv)
  2262.     Axis *axisPtr;        /* Axis */
  2263.     int argc;
  2264.     char **argv;
  2265. {
  2266.     double value;
  2267.     int coord;
  2268.  
  2269.     if (argc != 4) {
  2270.     Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
  2271.         argv[0], " ", axisNames[axisPtr->type], "axis transform value\"",
  2272.         (char *)NULL);
  2273.     return TCL_ERROR;
  2274.     }
  2275.     if (Tcl_ExprDouble(axisPtr->interp, argv[2], &value) != TCL_OK) {
  2276.     return TCL_ERROR;
  2277.     }
  2278.     coord = Blt_Transform((GraphAxis *)axisPtr, value);
  2279.     sprintf(axisPtr->interp->result, "%d", coord);
  2280.     return TCL_OK;
  2281. }
  2282.  
  2283. /*
  2284.  * ----------------------------------------------------------------------
  2285.  *
  2286.  * Blt_CreateAxis --
  2287.  *
  2288.  *    Create and initialize a structure containing information to
  2289.  *     display a graph axis.
  2290.  *
  2291.  * Results:
  2292.  *    The return value is a standard Tcl result.
  2293.  *
  2294.  * ----------------------------------------------------------------------
  2295.  */
  2296. int
  2297. Blt_CreateAxis(graphPtr, type, flags)
  2298.     Graph *graphPtr;
  2299.     enum AxisTypes type;
  2300.     int flags;            /* Configuration flags */
  2301. {
  2302.     Axis *axisPtr;
  2303.     enum AxisLocations location;
  2304.  
  2305.     axisPtr = (Axis *)calloc(1, sizeof(Axis));
  2306.     if (axisPtr == NULL) {
  2307.     graphPtr->interp->result = "can't allocate axis structure";
  2308.     return TCL_ERROR;
  2309.     }
  2310.     location = (AxisLocation) type;    /* For now, there's a 1-1
  2311.                      * correspondence between axis
  2312.                      * types and locations */
  2313.     axisPtr->type = type;
  2314.     axisPtr->location = location;
  2315.     axisPtr->interp = graphPtr->interp;    /* Needed for Tcl_PrintDouble */
  2316.     axisPtr->showTicks = 1;
  2317.     axisPtr->reqSubTicks = 2;
  2318.     axisPtr->destroyProc = DestroyAxis;
  2319.     axisPtr->displayProc = DisplayAxis;
  2320.     axisPtr->layoutProc = LayoutAxis;
  2321.     axisPtr->printProc = PrintAxis;
  2322.  
  2323.     /*
  2324.      * The actual axis min and max can't be the same, so initializing
  2325.      * the previous limits to zero is Ok.
  2326.      */
  2327.     axisPtr->prevMin = axisPtr->prevMax = 0.0;
  2328.     axisPtr->mapped = ((location == BOTTOM_AXIS) || (location == LEFT_AXIS));
  2329.  
  2330.     graphPtr->axisArr[type] = (GraphAxis *)axisPtr;
  2331.     if (ConfigureAxis(graphPtr, axisPtr, 0, (char **)NULL, flags) != TCL_OK) {
  2332.     return TCL_ERROR;
  2333.     }
  2334.     return TCL_OK;
  2335. }
  2336.  
  2337. int
  2338. Blt_AxisCmd(graphPtr, axis, argc, argv, flags)
  2339.     Graph *graphPtr;
  2340.     GraphAxis *axis;
  2341.     int argc;
  2342.     char **argv;
  2343.     int flags;
  2344. {
  2345.     int result = TCL_ERROR;
  2346.     Axis *axisPtr = (Axis *)axis;
  2347.     Tcl_Interp *interp = graphPtr->interp;
  2348.     char c;
  2349.     int length;
  2350.     char *which;
  2351.  
  2352.     which = axisNames[axisPtr->type];
  2353.     if (argc < 3) {
  2354.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2355.         " ", which, "axis option ?args?\"", NULL);
  2356.     return TCL_ERROR;
  2357.     }
  2358.     c = argv[2][0];
  2359.     length = strlen(argv[2]);
  2360.  
  2361.     if ((c == 'c') && (strncmp(argv[2], "configure", length) == 0)) {
  2362.     if (argc < 3) {
  2363.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2364.         " ", which, "axis configure ?args?\"", NULL);
  2365.         return TCL_ERROR;
  2366.     }
  2367.     result = ConfigureAxis(graphPtr, axisPtr, argc - 3, argv + 3, flags);
  2368.     } else if ((c == 'l') && (strncmp(argv[2], "limits", length) == 0)) {
  2369.     result = GetAxisLimits(axisPtr, argc, argv);
  2370.     } else if ((c == 'i') && (strncmp(argv[2], "invtransform", length) == 0)) {
  2371.     result = InvTransformCoord(axisPtr, argc, argv);
  2372.     } else if ((c == 't') && (strncmp(argv[2], "transform", length) == 0)) {
  2373.     result = TransformCoord(axisPtr, argc, argv);
  2374.     } else {
  2375.     Tcl_AppendResult(interp, "bad ", which, "axis option \"", argv[2],
  2376.         "\":  should be configure or limits", (char *)NULL);
  2377.     return TCL_ERROR;
  2378.     }
  2379.     return result;
  2380. }
  2381.