summaryrefslogtreecommitdiff
path: root/blt/src/bltGrLine.c
diff options
context:
space:
mode:
Diffstat (limited to 'blt/src/bltGrLine.c')
-rw-r--r--blt/src/bltGrLine.c5091
1 files changed, 5091 insertions, 0 deletions
diff --git a/blt/src/bltGrLine.c b/blt/src/bltGrLine.c
new file mode 100644
index 00000000000..103d4eddbf0
--- /dev/null
+++ b/blt/src/bltGrLine.c
@@ -0,0 +1,5091 @@
+/*
+ * bltGrLine.c --
+ *
+ * This module implements line graph and stripchart elements for
+ * the BLT graph widget.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+#include "bltGraph.h"
+#include "bltChain.h"
+#include <X11/Xutil.h>
+
+#include "bltGrElem.h"
+
+#define COLOR_DEFAULT (XColor *)1
+#define PATTERN_SOLID ((Pixmap)1)
+
+#define PEN_INCREASING 1 /* Draw line segments for only those
+ * data points whose abscissas are
+ * monotonically increasing in
+ * order */
+#define PEN_DECREASING 2 /* Lines will be drawn between only
+ * those points whose abscissas are
+ * decreasing in order */
+
+#define PEN_BOTH_DIRECTIONS (PEN_INCREASING | PEN_DECREASING)
+ /* Lines will be drawn between points regardless of the ordering of
+ * the abscissas */
+
+#define BROKEN_TRACE(dir,last,next) \
+ (((((dir) & PEN_DECREASING) == 0) && ((next) < (last))) || \
+ ((((dir) & PEN_INCREASING) == 0) && ((next) > (last))))
+
+#define DRAW_SYMBOL(linePtr) \
+ (((linePtr)->symbolCounter % (linePtr)->symbolInterval) == 0)
+
+typedef enum {
+ PEN_SMOOTH_NONE, /* Line segments */
+ PEN_SMOOTH_STEP, /* Step-and-hold */
+ PEN_SMOOTH_NATURAL, /* Natural cubic spline */
+ PEN_SMOOTH_QUADRATIC, /* Quadratic spline */
+ PEN_SMOOTH_CATROM, /* Catrom parametric spline */
+ PEN_SMOOTH_LAST /* Sentinel */
+} Smoothing;
+
+typedef struct {
+ char *name;
+ Smoothing value;
+} SmoothingInfo;
+
+static SmoothingInfo smoothingInfo[] = {
+ { "linear", PEN_SMOOTH_NONE },
+ { "step", PEN_SMOOTH_STEP },
+ { "natural", PEN_SMOOTH_NATURAL },
+ { "cubic", PEN_SMOOTH_NATURAL },
+ { "quadratic", PEN_SMOOTH_QUADRATIC },
+ { "catrom", PEN_SMOOTH_CATROM },
+ { (char *)NULL, PEN_SMOOTH_LAST }
+};
+
+
+typedef struct {
+ Point2D *screenPts; /* Array of transformed coordinates */
+ int nScreenPts; /* Number of coordinates */
+ int *dataToStyle; /* Index of pen styles */
+ int *indices; /* Maps segments/traces to data points */
+
+} MapInfo;
+
+/*
+ * Symbol types for line elements
+ */
+typedef enum {
+ SYMBOL_NONE,
+ SYMBOL_SQUARE,
+ SYMBOL_CIRCLE,
+ SYMBOL_DIAMOND,
+ SYMBOL_PLUS,
+ SYMBOL_CROSS,
+ SYMBOL_SPLUS,
+ SYMBOL_SCROSS,
+ SYMBOL_TRIANGLE,
+ SYMBOL_ARROW,
+ SYMBOL_BITMAP
+} SymbolType;
+
+typedef struct {
+ SymbolType type; /* Type of symbol to be drawn/printed */
+
+ int size; /* Requested size of symbol in pixels */
+
+ XColor *outlineColor; /* Outline color */
+
+ int outlineWidth; /* Width of the outline */
+
+ GC outlineGC; /* Outline graphics context */
+
+ XColor *fillColor; /* Normal fill color */
+
+ GC fillGC; /* Fill graphics context */
+
+ /* The last two fields are used only for bitmap symbols. */
+
+ Pixmap bitmap; /* Bitmap to determine foreground/background
+ * pixels of the symbol */
+
+ Pixmap mask; /* Bitmap representing the transparent
+ * pixels of the symbol */
+
+} Symbol;
+
+typedef struct {
+ int start; /* Index into the X-Y coordinate
+ * arrays indicating where trace
+ * starts. */
+
+ int nScreenPts; /* Number of points in the continuous
+ * trace */
+
+ Point2D *screenPts; /* Array of screen coordinates
+ * (malloc-ed) representing the
+ * trace. */
+
+ int *symbolToData; /* Reverse mapping of screen
+ * coordinate indices back to their
+ * data coordinates */
+} Trace;
+
+typedef struct {
+ char *name; /* Name of pen style. If the pen was
+ * statically allocated the name will
+ * be NULL. */
+
+ Tk_Uid classUid; /* Type of pen */
+
+ char *typeId; /* String token identifying the type
+ * of pen */
+
+ unsigned int flags; /* Indicates if the pen element is
+ * active or normal */
+
+ int refCount; /* Reference count for elements using
+ * this pen. */
+ Blt_HashEntry *hashPtr;
+
+ Tk_ConfigSpec *configSpecs; /* Configuration specifications */
+
+ PenConfigureProc *configProc;
+ PenDestroyProc *destroyProc;
+
+ /* Symbol attributes. */
+ Symbol symbol; /* Element symbol type */
+
+ /* Trace attributes. */
+ int traceWidth; /* Width of the line segments. If
+ * lineWidth is 0, no line will be
+ * drawn, only symbols. */
+
+ Blt_Dashes traceDashes; /* Dash on-off list value */
+
+ XColor *traceColor; /* Line segment color */
+
+ XColor *traceOffColor; /* Line segment dash gap color */
+
+ GC traceGC; /* Line segment graphics context */
+
+ /* Error bar attributes. */
+ int errorShow; /* Describes which error bars to
+ * display: none, x, y, or * both. */
+
+ int errorWidth; /* Width of the error bar segments. */
+
+ XColor *errorColor; /* Color of the error bar. */
+
+ GC errorGC; /* Error bar graphics context. */
+
+ /* Show value attributes. */
+ int valueShow; /* Indicates whether to display data
+ * value. Values are x, y, both, or
+ * none. */
+ char *valueFormat; /* A printf format string. */
+
+ TextStyle valueStyle; /* Text attributes (color, font,
+ * rotation, etc.) of the value. */
+
+} LinePen;
+
+typedef struct {
+ AxisRange weight; /* Weight range where this pen is valid. */
+
+ LinePen *penPtr; /* Pen used to draw symbols, traces, error
+ * bars, segments, etc. */
+
+ Segment2D *xErrorBars; /* Point to start of this pen's X-error bar
+ * segments in the element's array. */
+ Segment2D *yErrorBars; /* Point to start of this pen's Y-error bar
+ * segments in the element's array. */
+ int xErrorBarCnt; /* # of error bars for this pen. */
+ int yErrorBarCnt; /* # of error bars for this pen. */
+
+ int symbolSize; /* Size of the pen's symbol scaled to the
+ * current graph size. */
+
+ /* Graph specific data. */
+
+ Point2D *symbolPts; /* Points to start of array for this pen. */
+
+ int nSymbolPts; /* # of points for this pen. */
+
+ /* The last two fields are used only for stripcharts. */
+
+ Segment2D *strips; /* Points to start of the line segments
+ * for this pen. */
+
+ int nStrips; /* # of line segments for this pen. */
+
+} LinePenStyle;
+
+typedef struct {
+ char *name; /* Identifier used to refer the
+ * element. Used in the "insert",
+ * "delete", or "show", operations. */
+
+ Tk_Uid classUid; /* Type of element */
+
+ Graph *graphPtr; /* Graph widget of element*/
+
+ unsigned int flags; /* Indicates if the entire element is
+ * active, or if coordinates need to
+ * be calculated */
+
+ char **tags;
+
+ int hidden; /* If non-zero, don't display the
+ * element. */
+
+ Blt_HashEntry *hashPtr;
+
+ char *label; /* Label displayed in legend */
+
+ int labelRelief; /* Relief of label in legend. */
+
+ Axis2D axes;
+
+ ElemVector x, y, w; /* Contains array of numeric values */
+
+ ElemVector xError; /* Relative/symmetric X error values. */
+ ElemVector yError; /* Relative/symmetric Y error values. */
+ ElemVector xHigh, xLow; /* Absolute/asymmetric X-coordinate high/low
+ error values. */
+ ElemVector yHigh, yLow; /* Absolute/asymmetric Y-coordinate high/low
+ error values. */
+
+ int *reqActive; /* Array of indices (malloc-ed) that
+ * indicate the data points are active
+ * (drawn with "active" colors). */
+
+ int nReqActive; /* Number of active data points.
+ * Special case: if < 0 then all data
+ * points are drawn active. */
+
+ ElementProcs *procsPtr;
+ Tk_ConfigSpec *configSpecs; /* Configuration specifications */
+
+ Segment2D *xErrorBars; /* Point to start of this pen's X-error bar
+ * segments in the element's array. */
+ Segment2D *yErrorBars; /* Point to start of this pen's Y-error bar
+ * segments in the element's array. */
+ int xErrorBarCnt; /* # of error bars for this pen. */
+ int yErrorBarCnt; /* # of error bars for this pen. */
+
+ int *xErrorToData; /* Maps individual error bar segments back
+ * to the data point associated with it. */
+ int *yErrorToData; /* Maps individual error bar segments back
+ * to the data point associated with it. */
+
+ LinePen *activePenPtr; /* Pen to draw "active" elements. */
+ LinePen *normalPenPtr; /* Pen to draw elements normally. */
+ Blt_Chain *palette; /* Array of pen styles: pens are associated
+ * with specific ranges of data.*/
+
+ /* Symbol scaling */
+ int scaleSymbols; /* If non-zero, the symbols will scale
+ * in size as the graph is zoomed
+ * in/out. */
+
+ double xRange, yRange; /* Initial X-axis and Y-axis ranges:
+ * used to scale the size of element's
+ * symbol. */
+
+ /*
+ * Line specific configurable attributes
+ */
+ LinePen builtinPen;
+
+ /* Line smoothing */
+ Smoothing reqSmooth; /* Requested smoothing function to use
+ * for connecting the data points */
+
+ Smoothing smooth; /* Smoothing function used. */
+
+ double rTolerance; /* Tolerance to reduce the number of
+ * points displayed. */
+ /*
+ * Drawing related data structures.
+ */
+
+ /* Area-under-curve fill attributes. */
+ XColor *fillFgColor;
+ XColor *fillBgColor;
+ GC fillGC;
+
+ Blt_Tile fillTile; /* Tile for fill area. */
+ Pixmap fillStipple; /* Stipple for fill area. */
+
+ int nFillPts;
+ Point2D *fillPts; /* Array of points used to draw
+ * polygon to fill area under the
+ * curve */
+
+ /* Symbol points */
+ Point2D *symbolPts; /* Holds the screen coordinates of all
+ * the data points for the element. */
+ int nSymbolPts; /* Number of points */
+
+ int *symbolToData; /* Contains indices of data points.
+ * It's first used to map pens to the
+ * visible points to sort them by pen
+ * style, and later to find data
+ * points from the index of a visible
+ * point. */
+
+ /* Active symbol points */
+ Point2D *activePts; /* Array of indices representing the
+ * "active" points. */
+ int nActivePts; /* Number of indices in the above array. */
+
+ int *activeToData; /* Contains indices of data points.
+ * It's first used to map pens to the
+ * visible points to sort them by pen
+ * style, and later to find data
+ * points from the index of a visible
+ * point. */
+
+ int reqMaxSymbols;
+ int symbolInterval;
+ int symbolCounter;
+
+ /* X-Y graph-specific fields */
+
+ int penDir; /* Indicates if a change in the pen
+ * direction should be considered a
+ * retrace (line segment is not
+ * drawn). */
+
+ Blt_Chain *chainPtr; /* List of traces (a trace is a series
+ * of contiguous line segments). New
+ * traces are generated when either
+ * the next segment changes the pen
+ * direction, or the end point is
+ * clipped by the plotting area. */
+
+ /* Stripchart-specific fields */
+
+ Segment2D *strips; /* Holds the the line segments of the
+ * element trace. The segments are
+ * grouped by pen style. */
+ int nStrips; /* Number of line segments to be drawn. */
+ int *stripToData; /* Pen to visible line segment mapping. */
+
+} Line;
+
+static Tk_OptionParseProc StringToPattern;
+static Tk_OptionPrintProc PatternToString;
+static Tk_OptionParseProc StringToSmooth;
+static Tk_OptionPrintProc SmoothToString;
+extern Tk_OptionParseProc Blt_StringToStyles;
+extern Tk_OptionPrintProc Blt_StylesToString;
+static Tk_OptionParseProc StringToPenDir;
+static Tk_OptionPrintProc PenDirToString;
+static Tk_OptionParseProc StringToSymbol;
+static Tk_OptionPrintProc SymbolToString;
+
+static Tk_CustomOption patternOption =
+{
+ StringToPattern, PatternToString, (ClientData)0
+};
+static Tk_CustomOption smoothOption =
+{
+ StringToSmooth, SmoothToString, (ClientData)0
+};
+static Tk_CustomOption stylesOption =
+{
+ Blt_StringToStyles, Blt_StylesToString, (ClientData)sizeof(LinePenStyle)
+};
+static Tk_CustomOption penDirOption =
+{
+ StringToPenDir, PenDirToString, (ClientData)0
+};
+static Tk_CustomOption symbolOption =
+{
+ StringToSymbol, SymbolToString, (ClientData)0
+};
+extern Tk_CustomOption bltColorOption;
+extern Tk_CustomOption bltDashesOption;
+extern Tk_CustomOption bltDataOption;
+extern Tk_CustomOption bltDataPairsOption;
+extern Tk_CustomOption bltDistanceOption;
+extern Tk_CustomOption bltListOption;
+extern Tk_CustomOption bltLinePenOption;
+extern Tk_CustomOption bltShadowOption;
+extern Tk_CustomOption bltXAxisOption;
+extern Tk_CustomOption bltYAxisOption;
+extern Tk_CustomOption bltTileOption;
+extern Tk_CustomOption bltFillOption;
+
+#define DEF_LINE_ACTIVE_PEN "activeLine"
+#define DEF_LINE_AXIS_X "x"
+#define DEF_LINE_AXIS_Y "y"
+#define DEF_LINE_DASHES (char *)NULL
+#define DEF_LINE_DATA (char *)NULL
+#define DEF_LINE_FILL_COLOR "defcolor"
+#define DEF_LINE_FILL_MONO "defcolor"
+#define DEF_LINE_HIDE "no"
+#define DEF_LINE_LABEL (char *)NULL
+#define DEF_LINE_LABEL_RELIEF "flat"
+#define DEF_LINE_MAX_SYMBOLS "0"
+#define DEF_LINE_OFFDASH_COLOR (char *)NULL
+#define DEF_LINE_OFFDASH_MONO (char *)NULL
+#define DEF_LINE_OUTLINE_COLOR "defcolor"
+#define DEF_LINE_OUTLINE_MONO "defcolor"
+#define DEF_LINE_OUTLINE_WIDTH "1"
+#define DEF_LINE_PATTERN (char *)NULL
+#define DEF_LINE_PATTERN_BG "white"
+#define DEF_LINE_PATTERN_FG "black"
+#define DEF_LINE_PATTERN_TILE (char *)NULL
+#define DEF_LINE_PEN_COLOR RGB_NAVYBLUE
+#define DEF_LINE_PEN_DIRECTION "both"
+#define DEF_LINE_PEN_MONO RGB_BLACK
+#define DEF_LINE_PEN_WIDTH "1"
+#define DEF_LINE_PIXELS "0.125i"
+#define DEF_LINE_REDUCE "0.0"
+#define DEF_LINE_SCALE_SYMBOLS "yes"
+#define DEF_LINE_SMOOTH "linear"
+#define DEF_LINE_STIPPLE (char *)NULL
+#define DEF_LINE_STYLES ""
+#define DEF_LINE_SYMBOL "circle"
+#define DEF_LINE_TAGS "all"
+#define DEF_LINE_X_DATA (char *)NULL
+#define DEF_LINE_Y_DATA (char *)NULL
+
+#define DEF_LINE_ERRORBAR_COLOR "defcolor"
+#define DEF_LINE_ERRORBAR_WIDTH "1"
+#define DEF_LINE_SHOW_ERRORBARS "both"
+
+#define DEF_PEN_ACTIVE_COLOR RGB_BLUE
+#define DEF_PEN_ACTIVE_MONO RGB_BLACK
+#define DEF_PEN_DASHES (char *)NULL
+#define DEF_PEN_FILL_COLOR "defcolor"
+#define DEF_PEN_FILL_MONO "defcolor"
+#define DEF_PEN_LINE_WIDTH "1"
+#define DEF_PEN_NORMAL_COLOR RGB_NAVYBLUE
+#define DEF_PEN_NORMAL_MONO RGB_BLACK
+#define DEF_PEN_OFFDASH_COLOR (char *)NULL
+#define DEF_PEN_OFFDASH_MONO (char *)NULL
+#define DEF_PEN_OUTLINE_COLOR "defcolor"
+#define DEF_PEN_OUTLINE_MONO "defcolor"
+#define DEF_PEN_OUTLINE_WIDTH "1"
+#define DEF_PEN_PIXELS "0.125i"
+#define DEF_PEN_SYMBOL "circle"
+#define DEF_PEN_TYPE "line"
+#define DEF_PEN_VALUE_ANCHOR "s"
+#define DEF_PEN_VALUE_COLOR RGB_BLACK
+#define DEF_PEN_VALUE_FONT STD_FONT_SMALL
+#define DEF_PEN_VALUE_FORMAT "%g"
+#define DEF_PEN_VALUE_ROTATE (char *)NULL
+#define DEF_PEN_VALUE_SHADOW (char *)NULL
+#define DEF_PEN_SHOW_VALUES "no"
+
+static Tk_ConfigSpec lineElemConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
+ DEF_LINE_ACTIVE_PEN, Tk_Offset(Line, activePenPtr),
+ TK_CONFIG_NULL_OK, &bltLinePenOption},
+ {TK_CONFIG_CUSTOM, "-areapattern", "areaPattern", "AreaPattern",
+ DEF_LINE_PATTERN, Tk_Offset(Line, fillStipple),
+ TK_CONFIG_NULL_OK, &patternOption},
+ {TK_CONFIG_COLOR, "-areaforeground", "areaForeground", "areaForeground",
+ DEF_LINE_PATTERN_FG, Tk_Offset(Line, fillFgColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-areabackground", "areaBackground", "areaBackground",
+ DEF_LINE_PATTERN_BG, Tk_Offset(Line, fillBgColor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_CUSTOM, "-areatile", "areaTile", "AreaTile",
+ DEF_LINE_PATTERN_TILE, Tk_Offset(Line, fillTile),
+ TK_CONFIG_NULL_OK, &bltTileOption},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_LINE_TAGS, Tk_Offset(Line, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_LINE_PEN_COLOR, Tk_Offset(Line, builtinPen.traceColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_LINE_PEN_MONO, Tk_Offset(Line, builtinPen.traceColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_LINE_DASHES, Tk_Offset(Line, builtinPen.traceDashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_CUSTOM, "-data", "data", "Data",
+ DEF_LINE_DATA, 0, 0, &bltDataPairsOption},
+ {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_LINE_ERRORBAR_COLOR, Tk_Offset(Line, builtinPen.errorColor),
+ 0, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_LINE_ERRORBAR_WIDTH, Tk_Offset(Line, builtinPen.errorWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_LINE_FILL_COLOR, Tk_Offset(Line, builtinPen.symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_LINE_FILL_MONO, Tk_Offset(Line, builtinPen.symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_LINE_HIDE, Tk_Offset(Line, hidden), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-label", "label", "Label",
+ (char *)NULL, Tk_Offset(Line, label), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_RELIEF, "-labelrelief", "labelRelief", "LabelRelief",
+ DEF_LINE_LABEL_RELIEF, Tk_Offset(Line, labelRelief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ DEF_LINE_PEN_WIDTH, Tk_Offset(Line, builtinPen.traceWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_LINE_AXIS_X, Tk_Offset(Line, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_LINE_AXIS_Y, Tk_Offset(Line, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_CUSTOM, "-maxsymbols", "maxSymbols", "MaxSymbols",
+ DEF_LINE_MAX_SYMBOLS, Tk_Offset(Line, reqMaxSymbols),
+ GRAPH | STRIPCHART | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_LINE_OFFDASH_COLOR, Tk_Offset(Line, builtinPen.traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_LINE_OFFDASH_MONO, Tk_Offset(Line, builtinPen.traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_LINE_OUTLINE_COLOR, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+ TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_LINE_OUTLINE_MONO, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+ TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outlinewidth", "outlineWidth", "OutlineWidth",
+ DEF_LINE_OUTLINE_WIDTH, Tk_Offset(Line, builtinPen.symbol.outlineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-pen", "pen", "Pen",
+ (char *)NULL, Tk_Offset(Line, normalPenPtr),
+ TK_CONFIG_NULL_OK, &bltLinePenOption},
+ {TK_CONFIG_CUSTOM, "-pixels", "pixels", "Pixels",
+ DEF_LINE_PIXELS, Tk_Offset(Line, builtinPen.symbol.size),
+ GRAPH | STRIPCHART, &bltDistanceOption},
+ {TK_CONFIG_DOUBLE, "-reduce", "reduce", "Reduce",
+ DEF_LINE_REDUCE, Tk_Offset(Line, rTolerance),
+ GRAPH | STRIPCHART | TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-scalesymbols", "scaleSymbols", "ScaleSymbols",
+ DEF_LINE_SCALE_SYMBOLS, Tk_Offset(Line, scaleSymbols),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_LINE_SHOW_ERRORBARS, Tk_Offset(Line, builtinPen.errorShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Tk_Offset(Line, builtinPen.valueShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-smooth", "smooth", "Smooth",
+ DEF_LINE_SMOOTH, Tk_Offset(Line, reqSmooth),
+ TK_CONFIG_DONT_SET_DEFAULT, &smoothOption},
+ {TK_CONFIG_CUSTOM, "-styles", "styles", "Styles",
+ DEF_LINE_STYLES, Tk_Offset(Line, palette),
+ TK_CONFIG_NULL_OK, &stylesOption},
+ {TK_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol",
+ DEF_LINE_SYMBOL, Tk_Offset(Line, builtinPen.symbol),
+ TK_CONFIG_DONT_SET_DEFAULT, &symbolOption},
+ {TK_CONFIG_CUSTOM, "-trace", "trace", "Trace",
+ DEF_LINE_PEN_DIRECTION, Tk_Offset(Line, penDir),
+ TK_CONFIG_DONT_SET_DEFAULT, &penDirOption},
+ {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR,
+ Tk_Offset(Line, builtinPen.valueStyle.anchor), 0},
+ {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR, Tk_Offset(Line, builtinPen.valueStyle.color), 0},
+ {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT, Tk_Offset(Line, builtinPen.valueStyle.font), 0},
+ {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Tk_Offset(Line, builtinPen.valueFormat),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+ DEF_PEN_VALUE_ROTATE, Tk_Offset(Line, builtinPen.valueStyle.theta), 0},
+ {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+ DEF_PEN_VALUE_SHADOW, Tk_Offset(Line, builtinPen.valueStyle.shadow),
+ 0, &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-weights", "weights", "Weights",
+ (char *)NULL, Tk_Offset(Line, w), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-x", "xData", "XData",
+ (char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xdata", "xData", "XData",
+ (char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xerror", "xError", "XError",
+ (char *)NULL, Tk_Offset(Line, xError), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh",
+ (char *)NULL, Tk_Offset(Line, xHigh), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xlow", "xLow", "XLow",
+ (char *)NULL, Tk_Offset(Line, xLow), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-y", "yData", "YData",
+ (char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-ydata", "yData", "YData",
+ (char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-yerror", "yError", "YError",
+ (char *)NULL, Tk_Offset(Line, yError), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh",
+ (char *)NULL, Tk_Offset(Line, yHigh), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-ylow", "yLow", "YLow",
+ (char *)NULL, Tk_Offset(Line, yLow), 0, &bltDataOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+
+static Tk_ConfigSpec stripElemConfigSpecs[] =
+{
+ {TK_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
+ DEF_LINE_ACTIVE_PEN, Tk_Offset(Line, activePenPtr),
+ TK_CONFIG_NULL_OK, &bltLinePenOption},
+ {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
+ DEF_LINE_TAGS, Tk_Offset(Line, tags),
+ TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_LINE_PEN_COLOR, Tk_Offset(Line, builtinPen.traceColor),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_LINE_PEN_MONO, Tk_Offset(Line, builtinPen.traceColor),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_LINE_DASHES, Tk_Offset(Line, builtinPen.traceDashes),
+ TK_CONFIG_NULL_OK, &bltDashesOption},
+ {TK_CONFIG_CUSTOM, "-data", "data", "Data",
+ DEF_LINE_DATA, 0, 0, &bltDataPairsOption},
+ {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_LINE_ERRORBAR_COLOR, Tk_Offset(Line, builtinPen.errorColor),
+ 0, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_LINE_ERRORBAR_WIDTH, Tk_Offset(Line, builtinPen.errorWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_LINE_FILL_COLOR, Tk_Offset(Line, builtinPen.symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_LINE_FILL_MONO, Tk_Offset(Line, builtinPen.symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
+ DEF_LINE_HIDE, Tk_Offset(Line, hidden), TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_STRING, "-label", "label", "Label",
+ (char *)NULL, Tk_Offset(Line, label), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_RELIEF, "-labelrelief", "labelRelief", "LabelRelief",
+ DEF_LINE_LABEL_RELIEF, Tk_Offset(Line, labelRelief),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ DEF_LINE_PEN_WIDTH, Tk_Offset(Line, builtinPen.traceWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
+ DEF_LINE_AXIS_X, Tk_Offset(Line, axes.x), 0, &bltXAxisOption},
+ {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
+ DEF_LINE_AXIS_Y, Tk_Offset(Line, axes.y), 0, &bltYAxisOption},
+ {TK_CONFIG_CUSTOM, "-maxsymbols", "maxSymbols", "MaxSymbols",
+ DEF_LINE_MAX_SYMBOLS, Tk_Offset(Line, reqMaxSymbols),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_LINE_OFFDASH_COLOR, Tk_Offset(Line, builtinPen.traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_LINE_OFFDASH_MONO, Tk_Offset(Line, builtinPen.traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_LINE_OUTLINE_COLOR, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+ TK_CONFIG_COLOR_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_LINE_OUTLINE_MONO, Tk_Offset(Line, builtinPen.symbol.outlineColor),
+ TK_CONFIG_MONO_ONLY, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outlinewidth", "outlineWidth", "OutlineWidth",
+ DEF_LINE_OUTLINE_WIDTH, Tk_Offset(Line, builtinPen.symbol.outlineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-pen", "pen", "Pen",
+ (char *)NULL, Tk_Offset(Line, normalPenPtr),
+ TK_CONFIG_NULL_OK, &bltLinePenOption},
+ {TK_CONFIG_CUSTOM, "-pixels", "pixels", "Pixels",
+ DEF_LINE_PIXELS, Tk_Offset(Line, builtinPen.symbol.size), 0,
+ &bltDistanceOption},
+ {TK_CONFIG_BOOLEAN, "-scalesymbols", "scaleSymbols", "ScaleSymbols",
+ DEF_LINE_SCALE_SYMBOLS, Tk_Offset(Line, scaleSymbols),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_LINE_SHOW_ERRORBARS, Tk_Offset(Line, builtinPen.errorShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Tk_Offset(Line, builtinPen.valueShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-smooth", "smooth", "Smooth",
+ DEF_LINE_SMOOTH, Tk_Offset(Line, reqSmooth),
+ TK_CONFIG_DONT_SET_DEFAULT, &smoothOption},
+ {TK_CONFIG_CUSTOM, "-styles", "styles", "Styles",
+ DEF_LINE_STYLES, Tk_Offset(Line, palette),
+ TK_CONFIG_NULL_OK, &stylesOption},
+ {TK_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol",
+ DEF_LINE_SYMBOL, Tk_Offset(Line, builtinPen.symbol),
+ TK_CONFIG_DONT_SET_DEFAULT, &symbolOption},
+ {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR,
+ Tk_Offset(Line, builtinPen.valueStyle.anchor), 0},
+ {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR, Tk_Offset(Line, builtinPen.valueStyle.color), 0},
+ {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT, Tk_Offset(Line, builtinPen.valueStyle.font), 0},
+ {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Tk_Offset(Line, builtinPen.valueFormat),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+ DEF_PEN_VALUE_ROTATE, Tk_Offset(Line, builtinPen.valueStyle.theta), 0},
+ {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+ DEF_PEN_VALUE_SHADOW, Tk_Offset(Line, builtinPen.valueStyle.shadow), 0,
+ &bltShadowOption},
+ {TK_CONFIG_CUSTOM, "-weights", "weights", "Weights",
+ (char *)NULL, Tk_Offset(Line, w), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-x", "xData", "XData",
+ (char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xdata", "xData", "XData",
+ (char *)NULL, Tk_Offset(Line, x), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-y", "yData", "YData",
+ (char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xerror", "xError", "XError", (char *)NULL,
+ Tk_Offset(Line, xError), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-ydata", "yData", "YData",
+ (char *)NULL, Tk_Offset(Line, y), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-yerror", "yError", "YError", (char *)NULL,
+ Tk_Offset(Line, yError), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh", (char *)NULL,
+ Tk_Offset(Line, xHigh), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-xlow", "xLow", "XLow", (char *)NULL,
+ Tk_Offset(Line, xLow), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh", (char *)NULL,
+ Tk_Offset(Line, xHigh), 0, &bltDataOption},
+ {TK_CONFIG_CUSTOM, "-ylow", "yLow", "YLow", (char *)NULL,
+ Tk_Offset(Line, yLow), 0, &bltDataOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+static Tk_ConfigSpec linePenConfigSpecs[] =
+{
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_PEN_ACTIVE_COLOR, Tk_Offset(LinePen, traceColor),
+ TK_CONFIG_COLOR_ONLY | ACTIVE_PEN},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_PEN_ACTIVE_MONO, Tk_Offset(LinePen, traceColor),
+ TK_CONFIG_MONO_ONLY | ACTIVE_PEN},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_PEN_NORMAL_COLOR, Tk_Offset(LinePen, traceColor),
+ TK_CONFIG_COLOR_ONLY | NORMAL_PEN},
+ {TK_CONFIG_COLOR, "-color", "color", "Color",
+ DEF_PEN_NORMAL_MONO, Tk_Offset(LinePen, traceColor),
+ TK_CONFIG_MONO_ONLY | NORMAL_PEN},
+ {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
+ DEF_PEN_DASHES, Tk_Offset(LinePen, traceDashes),
+ TK_CONFIG_NULL_OK | ALL_PENS, &bltDashesOption},
+ {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
+ DEF_LINE_ERRORBAR_COLOR, Tk_Offset(LinePen, errorColor),
+ ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
+ DEF_LINE_ERRORBAR_WIDTH, Tk_Offset(LinePen, errorWidth),
+ ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_PEN_FILL_COLOR, Tk_Offset(LinePen, symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill",
+ DEF_PEN_FILL_MONO, Tk_Offset(LinePen, symbol.fillColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth",
+ (char *)NULL, Tk_Offset(LinePen, traceWidth),
+ ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_PEN_OFFDASH_COLOR, Tk_Offset(LinePen, traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-offdash", "offDash", "OffDash",
+ DEF_PEN_OFFDASH_MONO, Tk_Offset(LinePen, traceOffColor),
+ TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_PEN_OUTLINE_COLOR, Tk_Offset(LinePen, symbol.outlineColor),
+ TK_CONFIG_COLOR_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline",
+ DEF_PEN_OUTLINE_MONO, Tk_Offset(LinePen, symbol.outlineColor),
+ TK_CONFIG_MONO_ONLY | ALL_PENS, &bltColorOption},
+ {TK_CONFIG_CUSTOM, "-outlinewidth", "outlineWidth", "OutlineWidth",
+ DEF_PEN_OUTLINE_WIDTH, Tk_Offset(LinePen, symbol.outlineWidth),
+ TK_CONFIG_DONT_SET_DEFAULT | ALL_PENS, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-pixels", "pixels", "Pixels",
+ DEF_PEN_PIXELS, Tk_Offset(LinePen, symbol.size),
+ ALL_PENS, &bltDistanceOption},
+ {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
+ DEF_LINE_SHOW_ERRORBARS, Tk_Offset(LinePen, errorShow),
+ TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
+ DEF_PEN_SHOW_VALUES, Tk_Offset(LinePen, valueShow),
+ ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
+ {TK_CONFIG_CUSTOM, "-symbol", "symbol", "Symbol",
+ DEF_PEN_SYMBOL, Tk_Offset(LinePen, symbol),
+ TK_CONFIG_DONT_SET_DEFAULT | ALL_PENS, &symbolOption},
+ {TK_CONFIG_STRING, "-type", (char *)NULL, (char *)NULL,
+ DEF_PEN_TYPE, Tk_Offset(Pen, typeId), ALL_PENS | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
+ DEF_PEN_VALUE_ANCHOR, Tk_Offset(LinePen, valueStyle.anchor), ALL_PENS},
+ {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
+ DEF_PEN_VALUE_COLOR, Tk_Offset(LinePen, valueStyle.color), ALL_PENS},
+ {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
+ DEF_PEN_VALUE_FONT, Tk_Offset(LinePen, valueStyle.font), ALL_PENS},
+ {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
+ DEF_PEN_VALUE_FORMAT, Tk_Offset(LinePen, valueFormat),
+ ALL_PENS | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
+ DEF_PEN_VALUE_ROTATE, Tk_Offset(LinePen, valueStyle.theta), ALL_PENS},
+ {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
+ DEF_PEN_VALUE_SHADOW, Tk_Offset(LinePen, valueStyle.shadow),
+ ALL_PENS, &bltShadowOption},
+ {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
+};
+
+typedef double (DistanceProc) _ANSI_ARGS_((int x, int y, Point2D *p,
+ Point2D *q, Point2D *t));
+
+/* Forward declarations */
+#ifdef __STDC__
+static PenConfigureProc ConfigurePen;
+static PenDestroyProc DestroyPen;
+static ElementClosestProc ClosestLine;
+static ElementConfigProc ConfigureLine;
+static ElementDestroyProc DestroyLine;
+static ElementDrawProc DrawActiveLine;
+static ElementDrawProc DrawNormalLine;
+static ElementDrawSymbolProc DrawSymbol;
+static ElementExtentsProc GetLineExtents;
+static ElementToPostScriptProc ActiveLineToPostScript;
+static ElementToPostScriptProc NormalLineToPostScript;
+static ElementSymbolToPostScriptProc SymbolToPostScript;
+static ElementMapProc MapLine;
+static DistanceProc DistanceToY;
+static DistanceProc DistanceToX;
+static DistanceProc DistanceToLine;
+static Blt_TileChangedProc TileChangedProc;
+#endif /* __STDC__ */
+
+
+INLINE static int
+Round(x)
+ register double x;
+{
+ return (int) (x + ((x < 0.0) ? -0.5 : 0.5));
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * Custom configuration option (parse and print) routines
+ * ----------------------------------------------------------------------
+ */
+
+static int
+StringToBitmap(interp, tkwin, symbolPtr, string)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ Symbol *symbolPtr;
+ char *string;
+{
+ Pixmap bitmap, mask;
+ char **elemArr;
+ int nElems;
+ int result;
+
+ if (Tcl_SplitList(interp, string, &nElems, &elemArr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ bitmap = mask = None;
+
+ if (nElems > 2) {
+ Tcl_AppendResult(interp, "too many elements in bitmap list \"", string,
+ "\": should be \"bitmap mask\"", (char *)NULL);
+ result = TCL_ERROR;
+ goto error;
+ }
+ bitmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(elemArr[0]));
+ if (bitmap == None) {
+ result = TCL_BREAK;
+ Tcl_ResetResult(interp);
+ goto error;
+ }
+ if ((nElems > 1) && (elemArr[1][0] != '\0')) {
+ mask = Tk_GetBitmap(interp, tkwin, Tk_GetUid(elemArr[1]));
+ if (mask == None) {
+ Tk_FreeBitmap(Tk_Display(tkwin), bitmap);
+ result = TCL_ERROR;
+ goto error;
+ }
+ }
+ Blt_Free(elemArr);
+ if (symbolPtr->bitmap != None) {
+ Tk_FreeBitmap(Tk_Display(tkwin), symbolPtr->bitmap);
+ }
+ symbolPtr->bitmap = bitmap;
+ if (symbolPtr->mask != None) {
+ Tk_FreeBitmap(Tk_Display(tkwin), symbolPtr->mask);
+ }
+ symbolPtr->mask = mask;
+ return TCL_OK;
+ error:
+ Blt_Free(elemArr);
+ return result;
+}
+
+/*ARGSUSED*/
+static char *
+PatternToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin;
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of field in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Pixmap stipple = *(Pixmap *)(widgRec + offset);
+
+ if (stipple == None) {
+ return "";
+ }
+ if (stipple == PATTERN_SOLID) {
+ return "solid";
+ }
+ return Tk_NameOfBitmap(Tk_Display(tkwin), stipple);
+}
+
+/*ARGSUSED*/
+static int
+StringToPattern(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing field */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of field in record */
+{
+ Pixmap *stipplePtr = (Pixmap *)(widgRec + offset);
+ Pixmap stipple;
+
+ if (string == NULL) {
+ stipple = None;
+ } else if (strcmp(string, "solid") == 0) {
+ stipple = PATTERN_SOLID;
+ } else {
+ stipple = Tk_GetBitmap(interp, tkwin, Tk_GetUid(string));
+ if (stipple == None) {
+ return TCL_ERROR;
+ }
+ }
+ if ((*stipplePtr != None) && (*stipplePtr != PATTERN_SOLID)) {
+ Tk_FreeBitmap(Tk_Display(tkwin), *stipplePtr);
+ }
+ *stipplePtr = stipple;
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfSymbol --
+ *
+ * Converts the symbol value into its string representation.
+ *
+ * Results:
+ * The static string representing the symbol type is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfSymbol(symbolPtr)
+ Symbol *symbolPtr;
+{
+ switch (symbolPtr->type) {
+ case SYMBOL_NONE:
+ return "none";
+ case SYMBOL_SQUARE:
+ return "square";
+ case SYMBOL_CIRCLE:
+ return "circle";
+ case SYMBOL_DIAMOND:
+ return "diamond";
+ case SYMBOL_PLUS:
+ return "plus";
+ case SYMBOL_CROSS:
+ return "cross";
+ case SYMBOL_SPLUS:
+ return "splus";
+ case SYMBOL_SCROSS:
+ return "scross";
+ case SYMBOL_TRIANGLE:
+ return "triangle";
+ case SYMBOL_ARROW:
+ return "arrow";
+ case SYMBOL_BITMAP:
+ return "bitmap";
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSymbol --
+ *
+ * Convert the string representation of a line style or symbol name
+ * into its numeric form.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The symbol type is
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToSymbol(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing symbol type */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of symbol type field in record */
+{
+ Symbol *symbolPtr = (Symbol *)(widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if (c == '\0') {
+ symbolPtr->type = SYMBOL_NONE;
+ } else if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
+ symbolPtr->type = SYMBOL_NONE;
+ } else if ((c == 'c') && (length > 1) &&
+ (strncmp(string, "circle", length) == 0)) {
+ symbolPtr->type = SYMBOL_CIRCLE;
+ } else if ((c == 's') && (length > 1) &&
+ (strncmp(string, "square", length) == 0)) {
+ symbolPtr->type = SYMBOL_SQUARE;
+ } else if ((c == 'd') && (strncmp(string, "diamond", length) == 0)) {
+ symbolPtr->type = SYMBOL_DIAMOND;
+ } else if ((c == 'p') && (strncmp(string, "plus", length) == 0)) {
+ symbolPtr->type = SYMBOL_PLUS;
+ } else if ((c == 'c') && (length > 1) &&
+ (strncmp(string, "cross", length) == 0)) {
+ symbolPtr->type = SYMBOL_CROSS;
+ } else if ((c == 's') && (length > 1) &&
+ (strncmp(string, "splus", length) == 0)) {
+ symbolPtr->type = SYMBOL_SPLUS;
+ } else if ((c == 's') && (length > 1) &&
+ (strncmp(string, "scross", length) == 0)) {
+ symbolPtr->type = SYMBOL_SCROSS;
+ } else if ((c == 't') && (strncmp(string, "triangle", length) == 0)) {
+ symbolPtr->type = SYMBOL_TRIANGLE;
+ } else if ((c == 'a') && (strncmp(string, "arrow", length) == 0)) {
+ symbolPtr->type = SYMBOL_ARROW;
+ } else {
+ int result;
+
+ result = StringToBitmap(interp, tkwin, symbolPtr, string);
+ if (result != TCL_OK) {
+ if (result != TCL_ERROR) {
+ Tcl_AppendResult(interp, "bad symbol \"", string,
+"\": should be \"none\", \"circle\", \"square\", \"diamond\", \"plus\", \
+\"cross\", \"splus\", \"scross\", \"triangle\", \"arrow\" \
+or the name of a bitmap", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ symbolPtr->type = SYMBOL_BITMAP;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SymbolToString --
+ *
+ * Convert the symbol value into a string.
+ *
+ * Results:
+ * The string representing the symbol type or line style is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SymbolToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin;
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of symbol type field in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ Symbol *symbolPtr = (Symbol *)(widgRec + offset);
+ char *result;
+
+ if (symbolPtr->type == SYMBOL_BITMAP) {
+ Tcl_DString dString;
+
+ Tcl_DStringInit(&dString);
+ Tcl_DStringAppendElement(&dString,
+ Tk_NameOfBitmap(Tk_Display(tkwin), symbolPtr->bitmap));
+ Tcl_DStringAppendElement(&dString, (symbolPtr->mask == None) ? "" :
+ Tk_NameOfBitmap(Tk_Display(tkwin), symbolPtr->mask));
+ result = Blt_Strdup(Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ *freeProcPtr = (Tcl_FreeProc *)Blt_Free;
+ } else {
+ result = NameOfSymbol(symbolPtr);
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfSmooth --
+ *
+ * Converts the smooth value into its string representation.
+ *
+ * Results:
+ * The static string representing the smooth type is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfSmooth(value)
+ Smoothing value;
+{
+ if ((value < 0) || (value >= PEN_SMOOTH_LAST)) {
+ return "unknown smooth value";
+ }
+ return smoothingInfo[value].name;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSmooth --
+ *
+ * Convert the string representation of a line style or smooth name
+ * into its numeric form.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The smooth type is
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToSmooth(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing smooth type */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of smooth type field in record */
+{
+ Smoothing *valuePtr = (Smoothing *)(widgRec + offset);
+ register SmoothingInfo *siPtr;
+
+ for (siPtr = smoothingInfo; siPtr->name != NULL; siPtr++) {
+ if (strcmp(string, siPtr->name) == 0) {
+ *valuePtr = siPtr->value;
+ return TCL_OK;
+ }
+ }
+ Tcl_AppendResult(interp, "bad smooth value \"", string, "\": should be \
+linear, step, natural, or quadratic", (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SmoothToString --
+ *
+ * Convert the smooth value into a string.
+ *
+ * Results:
+ * The string representing the smooth type or line style is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+SmoothToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of smooth type field in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int smooth = *(int *)(widgRec + offset);
+
+ return NameOfSmooth(smooth);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToPenDir --
+ *
+ * Convert the string representation of a line style or symbol name
+ * into its numeric form.
+ *
+ * Results:
+ * The return value is a standard Tcl result. The symbol type is
+ * written into the widget record.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+StringToPenDir(clientData, interp, tkwin, string, widgRec, offset)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Interpreter to send results back to */
+ Tk_Window tkwin; /* Not used. */
+ char *string; /* String representing pen direction */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of pen direction field in record */
+{
+ int *penDirPtr = (int *)(widgRec + offset);
+ unsigned int length;
+ char c;
+
+ c = string[0];
+ length = strlen(string);
+ if ((c == 'i') && (strncmp(string, "increasing", length) == 0)) {
+ *penDirPtr = PEN_INCREASING;
+ } else if ((c == 'd') && (strncmp(string, "decreasing", length) == 0)) {
+ *penDirPtr = PEN_DECREASING;
+ } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
+ *penDirPtr = PEN_BOTH_DIRECTIONS;
+ } else {
+ Tcl_AppendResult(interp, "bad trace value \"", string,
+ "\" : should be \"increasing\", \"decreasing\", or \"both\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NameOfPenDir --
+ *
+ * Convert the pen direction into a string.
+ *
+ * Results:
+ * The static string representing the pen direction is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static char *
+NameOfPenDir(penDir)
+ int penDir; /* Direction for pen drawing between points */
+{
+ switch (penDir) {
+ case PEN_INCREASING:
+ return "increasing";
+ case PEN_DECREASING:
+ return "decreasing";
+ case PEN_BOTH_DIRECTIONS:
+ return "both";
+ default:
+ return "unknown trace direction";
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PenDirToString --
+ *
+ * Convert the pen direction into a string.
+ *
+ * Results:
+ * The string representing the pen drawing direction is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static char *
+PenDirToString(clientData, tkwin, widgRec, offset, freeProcPtr)
+ ClientData clientData; /* Not used. */
+ Tk_Window tkwin; /* Not used. */
+ char *widgRec; /* Element information record */
+ int offset; /* Offset of pen direction field in record */
+ Tcl_FreeProc **freeProcPtr; /* Not used. */
+{
+ int penDir = *(int *)(widgRec + offset);
+
+ return NameOfPenDir(penDir);
+}
+
+
+/*
+ * Clear the number of points and segments, in case there are no
+ * segments or points
+ */
+static void
+ClearPalette(palette)
+ Blt_Chain *palette;
+{
+ register LinePenStyle *stylePtr;
+ Blt_ChainLink *linkPtr;
+
+ for (linkPtr = Blt_ChainFirstLink(palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->nStrips = stylePtr->nSymbolPts = 0;
+ stylePtr->xErrorBarCnt = stylePtr->yErrorBarCnt = 0;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigurePen --
+ *
+ * Sets up the appropriate configuration parameters in the GC.
+ * It is assumed the parameters have been previously set by
+ * a call to Tk_ConfigureWidget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information such as line width, line style, color
+ * etc. get set in a new GC.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigurePen(graphPtr, penPtr)
+ Graph *graphPtr;
+ Pen *penPtr;
+{
+ LinePen *lpPtr = (LinePen *)penPtr;
+ unsigned long gcMask;
+ GC newGC;
+ XGCValues gcValues;
+ XColor *colorPtr;
+
+ Blt_ResetTextStyle(graphPtr->tkwin, &(lpPtr->valueStyle));
+ /*
+ * Set the outline GC for this pen: GCForeground is outline color.
+ * GCBackground is the fill color (only used for bitmap symbols).
+ */
+ gcMask = (GCLineWidth | GCForeground);
+ colorPtr = lpPtr->symbol.outlineColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ gcValues.foreground = colorPtr->pixel;
+ if (lpPtr->symbol.type == SYMBOL_BITMAP) {
+ colorPtr = lpPtr->symbol.fillColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ /*
+ * Set a clip mask if either
+ * 1) no background color was designated or
+ * 2) a masking bitmap was specified.
+ *
+ * These aren't necessarily the bitmaps we'll be using for
+ * clipping. But this makes it unlikely that anyone else will
+ * be sharing this GC when we set the clip origin (at the time
+ * the bitmap is drawn).
+ */
+ if (colorPtr != NULL) {
+ gcValues.background = colorPtr->pixel;
+ gcMask |= GCBackground;
+ if (lpPtr->symbol.mask != None) {
+ gcValues.clip_mask = lpPtr->symbol.mask;
+ gcMask |= GCClipMask;
+ }
+ } else {
+ gcValues.clip_mask = lpPtr->symbol.bitmap;
+ gcMask |= GCClipMask;
+ }
+ }
+ gcValues.line_width = LineWidth(lpPtr->symbol.outlineWidth);
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lpPtr->symbol.outlineGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->symbol.outlineGC);
+ }
+ lpPtr->symbol.outlineGC = newGC;
+
+ /* Fills for symbols: GCForeground is fill color */
+
+ gcMask = (GCLineWidth | GCForeground);
+ colorPtr = lpPtr->symbol.fillColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ newGC = NULL;
+ if (colorPtr != NULL) {
+ gcValues.foreground = colorPtr->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ }
+ if (lpPtr->symbol.fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->symbol.fillGC);
+ }
+ lpPtr->symbol.fillGC = newGC;
+
+ /* Line segments */
+
+ gcMask = (GCLineWidth | GCForeground | GCLineStyle | GCCapStyle |
+ GCJoinStyle);
+ gcValues.cap_style = CapButt;
+ gcValues.join_style = JoinRound;
+ gcValues.line_style = LineSolid;
+ gcValues.line_width = LineWidth(lpPtr->traceWidth);
+
+ colorPtr = lpPtr->traceOffColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ if (colorPtr != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = colorPtr->pixel;
+ }
+ gcValues.foreground = lpPtr->traceColor->pixel;
+ if (LineIsDashed(lpPtr->traceDashes)) {
+ gcValues.line_width = lpPtr->traceWidth;
+ gcValues.line_style =
+ (colorPtr == NULL) ? LineOnOffDash : LineDoubleDash;
+ }
+ newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lpPtr->traceGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, lpPtr->traceGC);
+ }
+ if (LineIsDashed(lpPtr->traceDashes)) {
+ lpPtr->traceDashes.offset = lpPtr->traceDashes.values[0] / 2;
+ Blt_SetDashes(graphPtr->display, newGC, &(lpPtr->traceDashes));
+ }
+ lpPtr->traceGC = newGC;
+ gcMask = (GCLineWidth | GCForeground);
+ colorPtr = lpPtr->symbol.fillColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+
+ colorPtr = lpPtr->errorColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = lpPtr->traceColor;
+ }
+ gcValues.line_width = LineWidth(lpPtr->errorWidth);
+ gcValues.foreground = colorPtr->pixel;
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (lpPtr->errorGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->errorGC);
+ }
+ lpPtr->errorGC = newGC;
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyPen --
+ *
+ * Release memory and resources allocated for the style.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the pen style is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DestroyPen(graphPtr, penPtr)
+ Graph *graphPtr;
+ Pen *penPtr;
+{
+ LinePen *lpPtr = (LinePen *)penPtr;
+
+ Blt_FreeTextStyle(graphPtr->display, &(lpPtr->valueStyle));
+ if (lpPtr->symbol.outlineGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->symbol.outlineGC);
+ }
+ if (lpPtr->symbol.fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->symbol.fillGC);
+ }
+ if (lpPtr->errorGC != NULL) {
+ Tk_FreeGC(graphPtr->display, lpPtr->errorGC);
+ }
+ if (lpPtr->traceGC != NULL) {
+ Blt_FreePrivateGC(graphPtr->display, lpPtr->traceGC);
+ }
+ if (lpPtr->symbol.bitmap != None) {
+ Tk_FreeBitmap(graphPtr->display, lpPtr->symbol.bitmap);
+ lpPtr->symbol.bitmap = None;
+ }
+ if (lpPtr->symbol.mask != None) {
+ Tk_FreeBitmap(graphPtr->display, lpPtr->symbol.mask);
+ lpPtr->symbol.mask = None;
+ }
+}
+
+
+static void
+InitPen(penPtr)
+ LinePen *penPtr;
+{
+ Blt_InitTextStyle(&(penPtr->valueStyle));
+ penPtr->symbol.outlineWidth = 1;
+ penPtr->traceWidth = 1;
+ penPtr->symbol.type = SYMBOL_CIRCLE;
+ penPtr->symbol.bitmap = None;
+ penPtr->symbol.mask = None;
+ penPtr->symbol.outlineColor = COLOR_DEFAULT;
+ penPtr->symbol.fillColor = COLOR_DEFAULT;
+ penPtr->configSpecs = linePenConfigSpecs;
+ penPtr->configProc = ConfigurePen;
+ penPtr->destroyProc = DestroyPen;
+ penPtr->flags = NORMAL_PEN;
+ penPtr->errorWidth = 1;
+ penPtr->valueShow = SHOW_NONE;
+ penPtr->errorShow = SHOW_BOTH;
+ penPtr->name = "";
+}
+
+Pen *
+Blt_LinePen(penName)
+ char *penName;
+{
+ LinePen *penPtr;
+
+ penPtr = Blt_Calloc(1, sizeof(LinePen));
+ assert(penPtr);
+ InitPen(penPtr);
+ penPtr->name = Blt_Strdup(penName);
+ if (strcmp(penName, "activeLine") == 0) {
+ penPtr->flags = ACTIVE_PEN;
+ }
+ return (Pen *) penPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * In this section, the routines deal with building and filling
+ * the element's data structures with transformed screen
+ * coordinates. They are triggered from TranformLine which is
+ * called whenever the data or coordinates axes have changed and
+ * new screen coordinates need to be calculated.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScaleSymbol --
+ *
+ * Returns the scaled size for the line element. Scaling depends
+ * upon when the base line ranges for the element were set and
+ * the current range of the graph.
+ *
+ * Results:
+ * The new size of the symbol, after considering how much the
+ * graph has been scaled, is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ScaleSymbol(elemPtr, normalSize)
+ Element *elemPtr;
+ int normalSize;
+{
+ int maxSize;
+ double scale;
+ int newSize;
+
+ scale = 1.0;
+ if (elemPtr->scaleSymbols) {
+ double xRange, yRange;
+
+ xRange = (elemPtr->axes.x->max - elemPtr->axes.x->min);
+ yRange = (elemPtr->axes.y->max - elemPtr->axes.y->min);
+ if (elemPtr->flags & SCALE_SYMBOL) {
+ /* Save the ranges as a baseline for future scaling. */
+ elemPtr->xRange = xRange;
+ elemPtr->yRange = yRange;
+ elemPtr->flags &= ~SCALE_SYMBOL;
+ } else {
+ double xScale, yScale;
+
+ /* Scale the symbol by the smallest change in the X or Y axes */
+ xScale = elemPtr->xRange / xRange;
+ yScale = elemPtr->yRange / yRange;
+ scale = MIN(xScale, yScale);
+ }
+ }
+ newSize = Round(normalSize * scale);
+
+ /*
+ * Don't let the size of symbols go unbounded. Both X and Win32
+ * drawing routines assume coordinates to be a signed short int.
+ */
+ maxSize = (int)MIN(elemPtr->graphPtr->hRange, elemPtr->graphPtr->vRange);
+ if (newSize > maxSize) {
+ newSize = maxSize;
+ }
+
+ /* Make the symbol size odd so that its center is a single pixel. */
+ newSize |= 0x01;
+ return newSize;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetScreenPoints --
+ *
+ * Generates a coordinate array of transformed screen coordinates
+ * from the data points.
+ *
+ * Results:
+ * The transformed screen coordinates are returned.
+ *
+ * Side effects:
+ * Memory is allocated for the coordinate array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GetScreenPoints(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ double *x, *y;
+ register int i, n;
+ register int count;
+ register Point2D *screenPts;
+ register int *indices;
+
+ n = NumberOfPoints(linePtr);
+ x = linePtr->x.valueArr;
+ y = linePtr->y.valueArr;
+ screenPts = Blt_Malloc(sizeof(Point2D) * n);
+ assert(screenPts);
+ indices = Blt_Malloc(sizeof(int) * n);
+ assert(indices);
+
+ count = 0; /* Count the valid screen coordinates */
+ for (i = 0; i < n; i++) {
+ if ((finite(x[i])) && (finite(y[i]))) {
+ screenPts[count] =
+ Blt_Map2D(graphPtr, x[i], y[i], &(linePtr->axes));
+ indices[count] = i;
+ count++;
+ }
+ }
+ mapPtr->screenPts = screenPts;
+ mapPtr->nScreenPts = count;
+ mapPtr->indices = indices;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReducePoints --
+ *
+ * Generates a coordinate array of transformed screen coordinates
+ * from the data points.
+ *
+ * Results:
+ * The transformed screen coordinates are returned.
+ *
+ * Side effects:
+ * Memory is allocated for the coordinate array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ReducePoints(mapPtr, tolerance)
+ MapInfo *mapPtr;
+ double tolerance;
+{
+ register int i, k, n;
+ Point2D *screenPts;
+ int *indices, *simple;
+
+ simple = Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+ indices = Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+ screenPts = Blt_Malloc(sizeof(Point2D) * mapPtr->nScreenPts);
+ n = Blt_SimplifyLine(mapPtr->screenPts, 0, mapPtr->nScreenPts - 1,
+ tolerance, simple);
+ for (i = 0; i < n; i++) {
+ k = simple[i];
+ screenPts[i] = mapPtr->screenPts[k];
+ indices[i] = mapPtr->indices[k];
+ }
+#ifdef notdef
+ if (n < mapPtr->nScreenPts) {
+ fprintf(stderr, "reduced from %d to %d\n", mapPtr->nScreenPts, n);
+ }
+#endif
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->indices);
+ Blt_Free(simple);
+ mapPtr->screenPts = screenPts;
+ mapPtr->indices = indices;
+ mapPtr->nScreenPts = n;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GenerateSteps --
+ *
+ * Resets the coordinate and pen index arrays adding new points
+ * for step-and-hold type smoothing.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The temporary arrays for screen coordinates and pen indices
+ * are updated.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GenerateSteps(mapPtr)
+ MapInfo *mapPtr;
+{
+ int newSize;
+ register int i, count;
+ Point2D *screenPts;
+ int *indices;
+
+ newSize = ((mapPtr->nScreenPts - 1) * 2) + 1;
+ screenPts = Blt_Malloc(newSize * sizeof(Point2D));
+ assert(screenPts);
+ indices = Blt_Malloc(sizeof(int) * newSize);
+ assert(indices);
+
+ screenPts[0] = mapPtr->screenPts[0];
+ indices[0] = 0;
+
+ count = 1;
+ for (i = 1; i < mapPtr->nScreenPts; i++) {
+ screenPts[count + 1] = mapPtr->screenPts[i];
+
+ /* Hold last y-coordinate, use new x-coordinate */
+ screenPts[count].x = screenPts[count + 1].x;
+ screenPts[count].y = screenPts[count - 1].y;
+
+ /* Use the same style for both the hold and the step points */
+ indices[count] = indices[count + 1] = mapPtr->indices[i];
+ count += 2;
+ }
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->indices);
+ mapPtr->indices = indices;
+ mapPtr->screenPts = screenPts;
+ mapPtr->nScreenPts = newSize;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GenerateSpline --
+ *
+ * Computes a spline based upon the data points, returning a new
+ * (larger) coordinate array or points.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The temporary arrays for screen coordinates and data indices
+ * are updated based upon spline.
+ *
+ * FIXME: Can't interpolate knots along the Y-axis. Need to break
+ * up point array into interchangable X and Y vectors earlier.
+ * Pass extents (left/right or top/bottom) as parameters.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GenerateSpline(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ int extra;
+ register int i, j, count;
+ Point2D *origPts, *intpPts;
+ int *indices;
+ int nIntpPts, nOrigPts;
+ int result;
+ int x;
+
+ nOrigPts = mapPtr->nScreenPts;
+ origPts = mapPtr->screenPts;
+ assert(mapPtr->nScreenPts > 0);
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ if (origPts[j].x <= origPts[i].x) {
+ return; /* Points are not monotonically increasing */
+ }
+ }
+ if (((origPts[0].x > (double)graphPtr->right)) ||
+ ((origPts[mapPtr->nScreenPts - 1].x < (double)graphPtr->left))) {
+ return; /* All points are clipped */
+ }
+ /*
+ * The spline is computed in screen coordinates instead of data
+ * points so that we can select the abscissas of the interpolated
+ * points from each pixel horizontally across the plotting area.
+ */
+ extra = (graphPtr->right - graphPtr->left) + 1;
+ if (extra < 1) {
+ return;
+ }
+ nIntpPts = nOrigPts + extra + 1;
+ intpPts = Blt_Malloc(nIntpPts * sizeof(Point2D));
+ assert(intpPts);
+
+ indices = Blt_Malloc(sizeof(int) * nIntpPts);
+ assert(indices);
+
+ /* Populate the x2 array with both the original X-coordinates and
+ * extra X-coordinates for each horizontal pixel that the line
+ * segment contains. */
+ count = 0;
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+
+ /* Add the original x-coordinate */
+ intpPts[count].x = origPts[i].x;
+
+ /* Include the starting offset of the point in the offset array */
+ indices[count] = mapPtr->indices[i];
+ count++;
+
+ /* Is any part of the interval (line segment) in the plotting
+ * area? */
+ if ((origPts[j].x >= (double)graphPtr->left) ||
+ (origPts[i].x <= (double)graphPtr->right)) {
+ int last;
+
+ x = (int)(origPts[i].x + 1.0);
+
+ /*
+ * Since the line segment may be partially clipped on the
+ * left or right side, the points to interpolate are
+ * always interior to the plotting area.
+ *
+ * left right
+ * x1----|--------------------------|---x2
+ *
+ * Pick the max of the starting X-coordinate and the
+ * left edge and the min of the last X-coordinate and
+ * the right edge.
+ */
+ x = MAX(x, graphPtr->left);
+ last = (int)MIN(origPts[j].x, graphPtr->right);
+
+ /* Add the extra x-coordinates to the interval. */
+ while (x < last) {
+ indices[count] = mapPtr->indices[i];
+ intpPts[count++].x = (double)x;
+ x++;
+ }
+ }
+ }
+ nIntpPts = count;
+ result = FALSE;
+ if (linePtr->smooth == PEN_SMOOTH_NATURAL) {
+ result = Blt_NaturalSpline(origPts, nOrigPts, intpPts, nIntpPts);
+ } else if (linePtr->smooth == PEN_SMOOTH_QUADRATIC) {
+ result = Blt_QuadraticSpline(origPts, nOrigPts, intpPts, nIntpPts);
+ }
+ if (!result) {
+ /* The spline interpolation failed. We'll fallback to the
+ * current coordinates and do no smoothing (standard line
+ * segments). */
+ linePtr->smooth = PEN_SMOOTH_NONE;
+ Blt_Free(intpPts);
+ Blt_Free(indices);
+ } else {
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->indices);
+ mapPtr->indices = indices;
+ mapPtr->screenPts = intpPts;
+ mapPtr->nScreenPts = nIntpPts;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GenerateParametricSpline --
+ *
+ * Computes a spline based upon the data points, returning a new
+ * (larger) coordinate array or points.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The temporary arrays for screen coordinates and data indices
+ * are updated based upon spline.
+ *
+ * FIXME: Can't interpolate knots along the Y-axis. Need to break
+ * up point array into interchangable X and Y vectors earlier.
+ * Pass extents (left/right or top/bottom) as parameters.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GenerateParametricSpline(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ register int i, j, count;
+ Point2D *origPts, *intpPts;
+ int *indices;
+ int nIntpPts, nOrigPts;
+ int result;
+ double dist;
+ Point2D p, q;
+ Extents2D exts;
+
+ nOrigPts = mapPtr->nScreenPts;
+ origPts = mapPtr->screenPts;
+ assert(mapPtr->nScreenPts > 0);
+
+ /* Populate the x2 array with both the original X-coordinates and
+ * extra X-coordinates for each horizontal pixel that the line
+ * segment contains. */
+ count = 1;
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ dist = hypot(origPts[j].x - origPts[i].x, origPts[j].y - origPts[i].y);
+ count += 1 + (int)(dist * 0.5);
+ }
+ nIntpPts = count;
+ intpPts = Blt_Malloc(nIntpPts * sizeof(Point2D));
+ assert(intpPts);
+
+ indices = Blt_Malloc(sizeof(int) * nIntpPts);
+ assert(indices);
+
+ Blt_GraphExtents(graphPtr, &exts);
+ count = 0;
+ for (i = 0, j = 1; j < nOrigPts; i++, j++) {
+ dist = hypot(origPts[j].x - origPts[i].x, origPts[j].y - origPts[i].y);
+ /* Add the original x-coordinate */
+ intpPts[count].x = (double)i;
+ intpPts[count].y = 0.0;
+
+ /* Include the starting offset of the point in the offset array */
+ indices[count] = mapPtr->indices[i];
+ count++;
+
+ p = origPts[i];
+ q = origPts[j];
+
+ /* Is any part of the interval (line segment) in the plotting
+ * area? */
+
+ if (Blt_LineRectClip(&exts, &p, &q)) {
+ double distP, distQ;
+
+ distP = hypot(p.x - origPts[i].x, p.y - origPts[i].y);
+ distQ = hypot(q.x - origPts[i].x, q.y - origPts[i].y);
+ distP += 2.0;
+ while(distP <= distQ) {
+ /* Point is indicated by its interval and parameter t. */
+ intpPts[count].x = (double)i;
+ intpPts[count].y = distP / dist;
+ indices[count] = mapPtr->indices[i];
+ count++;
+ distP += 2.0;
+ }
+ }
+ }
+ intpPts[count].x = (double)i;
+ intpPts[count].y = 0.0;
+ indices[count] = mapPtr->indices[i];
+ count++;
+ nIntpPts = count;
+ result = FALSE;
+ if (linePtr->smooth == PEN_SMOOTH_NATURAL) {
+ result = Blt_NaturalParametricSpline(origPts, nOrigPts, &exts, FALSE,
+ intpPts, nIntpPts);
+ } else if (linePtr->smooth == PEN_SMOOTH_CATROM) {
+ result = Blt_CatromParametricSpline(origPts, nOrigPts, intpPts,
+ nIntpPts);
+ }
+ if (!result) {
+ /* The spline interpolation failed. We'll fallback to the
+ * current coordinates and do no smoothing (standard line
+ * segments). */
+ linePtr->smooth = PEN_SMOOTH_NONE;
+ Blt_Free(intpPts);
+ Blt_Free(indices);
+ } else {
+ Blt_Free(mapPtr->screenPts);
+ Blt_Free(mapPtr->indices);
+ mapPtr->indices = indices;
+ mapPtr->screenPts = intpPts;
+ mapPtr->nScreenPts = nIntpPts;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapSymbols --
+ *
+ * Creates two arrays of points and pen indices, filled with
+ * the screen coordinates of the visible
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the index array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapSymbols(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ Extents2D exts;
+ Point2D *symbolPts;
+ int *indices;
+ register int i, count;
+ register Point2D *pointPtr, *endPtr;
+
+ symbolPts = Blt_Malloc(sizeof(Point2D) * mapPtr->nScreenPts);
+ assert(symbolPts);
+
+ indices = Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+ assert(indices);
+
+ Blt_GraphExtents(graphPtr, &exts);
+ count = 0; /* Count the number of visible points */
+ i = 0;
+
+ for (pointPtr = mapPtr->screenPts, endPtr = pointPtr + mapPtr->nScreenPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (PointInRegion(&exts, pointPtr->x, pointPtr->y)) {
+ symbolPts[count].x = pointPtr->x;
+ symbolPts[count].y = pointPtr->y;
+ indices[count] = mapPtr->indices[i];
+ count++;
+ }
+ i++;
+ }
+ linePtr->symbolPts = symbolPts;
+ linePtr->nSymbolPts = count;
+ linePtr->symbolToData = indices;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapActiveSymbols --
+ *
+ * Creates an array of points of the active graph coordinates.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is freed and allocated for the active point array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapActiveSymbols(graphPtr, linePtr)
+ Graph *graphPtr;
+ Line *linePtr;
+{
+ Extents2D exts;
+ double x, y;
+ int count;
+ Point2D *activePts;
+ register int i;
+ int pointIndex;
+ int nPoints;
+ int *activeToData;
+
+ if (linePtr->activePts != NULL) {
+ Blt_Free(linePtr->activePts);
+ linePtr->activePts = NULL;
+ }
+ if (linePtr->activeToData != NULL) {
+ Blt_Free(linePtr->activeToData);
+ linePtr->activeToData = NULL;
+ }
+ Blt_GraphExtents(graphPtr, &exts);
+ activePts = Blt_Malloc(sizeof(Point2D) * linePtr->nReqActive);
+ assert(activePts);
+ activeToData = Blt_Malloc(sizeof(int) * linePtr->nReqActive);
+ nPoints = NumberOfPoints(linePtr);
+ count = 0; /* Count the visible active points */
+ for (i = 0; i < linePtr->nReqActive; i++) {
+ pointIndex = linePtr->reqActive[i];
+ if (pointIndex >= nPoints) {
+ continue; /* Index not available */
+ }
+ x = linePtr->x.valueArr[pointIndex];
+ y = linePtr->y.valueArr[pointIndex];
+ activePts[count] = Blt_Map2D(graphPtr, x, y, &(linePtr->axes));
+ activeToData[count] = pointIndex;
+ if (PointInRegion(&exts, activePts[count].x, activePts[count].y)) {
+ count++;
+ }
+ }
+ if (count > 0) {
+ linePtr->activePts = activePts;
+ linePtr->activeToData = activeToData;
+ } else {
+ /* No active points were visible. */
+ Blt_Free(activePts);
+ Blt_Free(activeToData);
+ }
+ linePtr->nActivePts = count;
+ linePtr->flags &= ~ACTIVE_PENDING;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapStrip --
+ *
+ * Creates an array of line segments of the graph coordinates.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is allocated for the line segment array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapStrip(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ Extents2D exts;
+ Segment2D *strips;
+ int *indices, *indexPtr;
+ register Point2D *endPtr, *pointPtr;
+ register Segment2D *segPtr;
+ register int count;
+
+ indices = Blt_Malloc(sizeof(int) * mapPtr->nScreenPts);
+ assert(indices);
+
+ /*
+ * Create array to hold points for line segments (not polyline
+ * coordinates). So allocate twice the number of points.
+ */
+ segPtr = strips = Blt_Malloc(mapPtr->nScreenPts * sizeof(Segment2D));
+ assert(strips);
+
+ Blt_GraphExtents(graphPtr, &exts);
+ count = 0; /* Count the number of segments. */
+ indexPtr = mapPtr->indices;
+ for (pointPtr = mapPtr->screenPts,
+ endPtr = mapPtr->screenPts + (mapPtr->nScreenPts - 1);
+ pointPtr < endPtr; pointPtr++, indexPtr++) {
+ segPtr->p = pointPtr[0];
+ segPtr->q = pointPtr[1];
+ if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) {
+ segPtr++;
+ indices[count] = *indexPtr;
+ count++;
+ }
+ }
+ linePtr->stripToData = indices;
+ linePtr->nStrips = count;
+ linePtr->strips = strips;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MergePens --
+ *
+ * Reorders the both arrays of points and segments to merge pens.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The old arrays are freed and new ones allocated containing
+ * the reordered points and segments.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MergePens(linePtr, dataToStyle)
+ Line *linePtr;
+ PenStyle **dataToStyle;
+{
+ LinePenStyle *stylePtr;
+ register int i;
+ Blt_ChainLink *linkPtr;
+
+ if (Blt_ChainGetLength(linePtr->palette) < 2) {
+ linkPtr = Blt_ChainFirstLink(linePtr->palette);
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->nStrips = linePtr->nStrips;
+ stylePtr->strips = linePtr->strips;
+ stylePtr->nSymbolPts = linePtr->nSymbolPts;
+ stylePtr->symbolPts = linePtr->symbolPts;
+ stylePtr->xErrorBarCnt = linePtr->xErrorBarCnt;
+ stylePtr->yErrorBarCnt = linePtr->yErrorBarCnt;
+ stylePtr->xErrorBars = linePtr->xErrorBars;
+ stylePtr->yErrorBars = linePtr->yErrorBars;
+ return;
+ }
+
+ /* We have more than one style. Group line segments and points of
+ * like pen styles. */
+
+ if (linePtr->nStrips > 0) {
+ Segment2D *strips;
+ int *stripToData;
+ register Segment2D *segPtr;
+ register int *indexPtr;
+ int dataIndex;
+
+ strips = Blt_Malloc(linePtr->nStrips * sizeof(Segment2D));
+ stripToData = Blt_Malloc(linePtr->nStrips * sizeof(int));
+ assert(strips && stripToData);
+ segPtr = strips, indexPtr = stripToData;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->strips = segPtr;
+ for (i = 0; i < linePtr->nStrips; i++) {
+ dataIndex = linePtr->stripToData[i];
+ if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+ *segPtr++ = linePtr->strips[i];
+ *indexPtr++ = dataIndex;
+ }
+ }
+ stylePtr->nStrips = segPtr - stylePtr->strips;
+ }
+ Blt_Free(linePtr->strips);
+ linePtr->strips = strips;
+ Blt_Free(linePtr->stripToData);
+ linePtr->stripToData = stripToData;
+ }
+ if (linePtr->nSymbolPts > 0) {
+ int *indexPtr;
+ register Point2D *symbolPts, *pointPtr;
+ register int *symbolToData;
+ int dataIndex;
+
+ symbolPts = Blt_Malloc(linePtr->nSymbolPts * sizeof(Point2D));
+ symbolToData = Blt_Malloc(linePtr->nSymbolPts * sizeof(int));
+ assert(symbolPts && symbolToData);
+ pointPtr = symbolPts, indexPtr = symbolToData;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->symbolPts = pointPtr;
+ for (i = 0; i < linePtr->nSymbolPts; i++) {
+ dataIndex = linePtr->symbolToData[i];
+ if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+ *pointPtr++ = linePtr->symbolPts[i];
+ *indexPtr++ = dataIndex;
+ }
+ }
+ stylePtr->nSymbolPts = pointPtr - stylePtr->symbolPts;
+ }
+ Blt_Free(linePtr->symbolPts);
+ linePtr->symbolPts = symbolPts;
+ Blt_Free(linePtr->symbolToData);
+ linePtr->symbolToData = symbolToData;
+ }
+ if (linePtr->xErrorBarCnt > 0) {
+ Segment2D *xErrorBars, *segPtr;
+ int *xErrorToData, *indexPtr;
+ int dataIndex;
+
+ xErrorBars = Blt_Malloc(linePtr->xErrorBarCnt * sizeof(Segment2D));
+ xErrorToData = Blt_Malloc(linePtr->xErrorBarCnt * sizeof(int));
+ assert(xErrorBars);
+ segPtr = xErrorBars, indexPtr = xErrorToData;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->xErrorBars = segPtr;
+ for (i = 0; i < linePtr->xErrorBarCnt; i++) {
+ dataIndex = linePtr->xErrorToData[i];
+ if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+ *segPtr++ = linePtr->xErrorBars[i];
+ *indexPtr++ = dataIndex;
+ }
+ }
+ stylePtr->xErrorBarCnt = segPtr - stylePtr->xErrorBars;
+ }
+ Blt_Free(linePtr->xErrorBars);
+ linePtr->xErrorBars = xErrorBars;
+ Blt_Free(linePtr->xErrorToData);
+ linePtr->xErrorToData = xErrorToData;
+ }
+ if (linePtr->yErrorBarCnt > 0) {
+ Segment2D *errorBars, *segPtr;
+ int *errorToData, *indexPtr;
+ int dataIndex;
+
+ errorBars = Blt_Malloc(linePtr->yErrorBarCnt * sizeof(Segment2D));
+ errorToData = Blt_Malloc(linePtr->yErrorBarCnt * sizeof(int));
+ assert(errorBars);
+ segPtr = errorBars, indexPtr = errorToData;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->yErrorBars = segPtr;
+ for (i = 0; i < linePtr->yErrorBarCnt; i++) {
+ dataIndex = linePtr->yErrorToData[i];
+ if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
+ *segPtr++ = linePtr->yErrorBars[i];
+ *indexPtr++ = dataIndex;
+ }
+ }
+ stylePtr->yErrorBarCnt = segPtr - stylePtr->yErrorBars;
+ }
+ Blt_Free(linePtr->yErrorBars);
+ linePtr->yErrorBars = errorBars;
+ Blt_Free(linePtr->yErrorToData);
+ linePtr->yErrorToData = errorToData;
+ }
+}
+
+#define CLIP_TOP (1<<0)
+#define CLIP_BOTTOM (1<<1)
+#define CLIP_RIGHT (1<<2)
+#define CLIP_LEFT (1<<3)
+
+INLINE static int
+OutCode(extsPtr, p)
+ Extents2D *extsPtr;
+ Point2D *p;
+{
+ int code;
+
+ code = 0;
+ if (p->x > extsPtr->right) {
+ code |= CLIP_RIGHT;
+ } else if (p->x < extsPtr->left) {
+ code |= CLIP_LEFT;
+ }
+ if (p->y > extsPtr->bottom) {
+ code |= CLIP_BOTTOM;
+ } else if (p->y < extsPtr->top) {
+ code |= CLIP_TOP;
+ }
+ return code;
+}
+
+static int
+ClipSegment(extsPtr, code1, code2, p, q)
+ Extents2D *extsPtr;
+ register int code1, code2;
+ register Point2D *p, *q;
+{
+ int inside, outside;
+
+ inside = ((code1 | code2) == 0);
+ outside = ((code1 & code2) != 0);
+
+ /*
+ * In the worst case, we'll clip the line segment against each of
+ * the four sides of the bounding rectangle.
+ */
+ while ((!outside) && (!inside)) {
+ if (code1 == 0) {
+ Point2D *tmp;
+ int code;
+
+ /* Swap pointers and out codes */
+ tmp = p, p = q, q = tmp;
+ code = code1, code1 = code2, code2 = code;
+ }
+ if (code1 & CLIP_LEFT) {
+ p->y += (q->y - p->y) *
+ (extsPtr->left - p->x) / (q->x - p->x);
+ p->x = extsPtr->left;
+ } else if (code1 & CLIP_RIGHT) {
+ p->y += (q->y - p->y) *
+ (extsPtr->right - p->x) / (q->x - p->x);
+ p->x = extsPtr->right;
+ } else if (code1 & CLIP_BOTTOM) {
+ p->x += (q->x - p->x) *
+ (extsPtr->bottom - p->y) / (q->y - p->y);
+ p->y = extsPtr->bottom;
+ } else if (code1 & CLIP_TOP) {
+ p->x += (q->x - p->x) *
+ (extsPtr->top - p->y) / (q->y - p->y);
+ p->y = extsPtr->top;
+ }
+ code1 = OutCode(extsPtr, p);
+
+ inside = ((code1 | code2) == 0);
+ outside = ((code1 & code2) != 0);
+ }
+ return (!inside);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SaveTrace --
+ *
+ * Creates a new trace and inserts it into the line's
+ * list of traces.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+SaveTrace(linePtr, start, length, mapPtr)
+ Line *linePtr;
+ int start; /* Starting index of the trace in data point
+ * array. Used to figure out closest point */
+ int length; /* Number of points forming the trace */
+ MapInfo *mapPtr;
+{
+ Trace *tracePtr;
+ Point2D *screenPts;
+ int *indices;
+ register int i, j;
+
+ tracePtr = Blt_Malloc(sizeof(Trace));
+ assert(tracePtr);
+ screenPts = Blt_Malloc(sizeof(Point2D) * length);
+ assert(screenPts);
+ indices = Blt_Malloc(sizeof(int) * length);
+ assert(indices);
+
+ /* Copy the screen coordinates of the trace into the point array */
+
+ if (mapPtr->indices != NULL) {
+ for (i = 0, j = start; i < length; i++, j++) {
+ screenPts[i].x = mapPtr->screenPts[j].x;
+ screenPts[i].y = mapPtr->screenPts[j].y;
+ indices[i] = mapPtr->indices[j];
+ }
+ } else {
+ for (i = 0, j = start; i < length; i++, j++) {
+ screenPts[i].x = mapPtr->screenPts[j].x;
+ screenPts[i].y = mapPtr->screenPts[j].y;
+ indices[i] = j;
+ }
+ }
+ tracePtr->nScreenPts = length;
+ tracePtr->screenPts = screenPts;
+ tracePtr->symbolToData = indices;
+ tracePtr->start = start;
+ if (linePtr->chainPtr == NULL) {
+ linePtr->chainPtr = Blt_ChainCreate();
+ }
+ Blt_ChainAppend(linePtr->chainPtr, tracePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeTraces --
+ *
+ * Deletes all the traces for the line.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+FreeTraces(linePtr)
+ Line *linePtr;
+{
+ Blt_ChainLink *linkPtr;
+ Trace *tracePtr;
+
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+ Blt_Free(tracePtr->symbolToData);
+ Blt_Free(tracePtr->screenPts);
+ Blt_Free(tracePtr);
+ }
+ Blt_ChainDestroy(linePtr->chainPtr);
+ linePtr->chainPtr = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapTraces --
+ *
+ * Creates an array of line segments of the graph coordinates.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is allocated for the line segment array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapTraces(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ int start, count;
+ int code1, code2;
+ Point2D *p, *q;
+ Point2D s;
+ Extents2D exts;
+ register int i;
+ int broken, offscreen;
+
+ Blt_GraphExtents(graphPtr, &exts);
+ count = 1;
+ code1 = OutCode(&exts, mapPtr->screenPts);
+ p = mapPtr->screenPts;
+ q = p + 1;
+ for (i = 1; i < mapPtr->nScreenPts; i++, p++, q++) {
+ code2 = OutCode(&exts, q);
+ if (code2 != 0) {
+ /* Save the coordinates of the last point, before clipping */
+ s = *q;
+ }
+ broken = BROKEN_TRACE(linePtr->penDir, p->x, q->x);
+ offscreen = ClipSegment(&exts, code1, code2, p, q);
+ if (broken || offscreen) {
+
+ /*
+ * The last line segment is either totally clipped by the plotting
+ * area or the x-direction is wrong, breaking the trace. Either
+ * way, save information about the last trace (if one exists),
+ * discarding the current line segment
+ */
+
+ if (count > 1) {
+ start = i - count;
+ SaveTrace(linePtr, start, count, mapPtr);
+ count = 1;
+ }
+ } else {
+ count++; /* Add the point to the trace. */
+ if (code2 != 0) {
+
+ /*
+ * If the last point is clipped, this means that the trace is
+ * broken after this point. Restore the original coordinate
+ * (before clipping) after saving the trace.
+ */
+
+ start = i - (count - 1);
+ SaveTrace(linePtr, start, count, mapPtr);
+ mapPtr->screenPts[i] = s;
+ count = 1;
+ }
+ }
+ code1 = code2;
+ }
+ if (count > 1) {
+ start = i - count;
+ SaveTrace(linePtr, start, count, mapPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapFillArea --
+ *
+ * Creates an array of points that represent a polygon that fills
+ * the area under the element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is allocated for the polygon point array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapFillArea(graphPtr, linePtr, mapPtr)
+ Graph *graphPtr;
+ Line *linePtr;
+ MapInfo *mapPtr;
+{
+ Point2D *origPts, *clipPts;
+ Extents2D exts;
+ double maxY;
+ register int i, n;
+
+ if (linePtr->fillPts != NULL) {
+ Blt_Free(linePtr->fillPts);
+ linePtr->fillPts = NULL;
+ linePtr->nFillPts = 0;
+ }
+ if (mapPtr->nScreenPts < 3) {
+ return;
+ }
+ n = mapPtr->nScreenPts + 3;
+ Blt_GraphExtents(graphPtr, &exts);
+
+ maxY = (double)graphPtr->bottom;
+ origPts = Blt_Malloc(sizeof(Point2D) * n);
+ for (i = 0; i < mapPtr->nScreenPts; i++) {
+ origPts[i].x = mapPtr->screenPts[i].x + 1;
+ origPts[i].y = mapPtr->screenPts[i].y;
+ if (origPts[i].y > maxY) {
+ maxY = origPts[i].y;
+ }
+ }
+ /* Add edges to make (if necessary) the polygon fill to the bottom
+ * of plotting window */
+ origPts[i].x = origPts[i - 1].x;
+ origPts[i].y = maxY;
+ i++;
+ origPts[i].x = origPts[0].x;
+ origPts[i].y = maxY;
+ i++;
+ origPts[i] = origPts[0];
+
+ clipPts = Blt_Malloc(sizeof(Point2D) * n * 3);
+ assert(clipPts);
+ n = Blt_PolyRectClip(&exts, origPts, n - 1, clipPts);
+
+ Blt_Free(origPts);
+ if (n < 3) {
+ Blt_Free(clipPts);
+ } else {
+ linePtr->fillPts = clipPts;
+ linePtr->nFillPts = n;
+ }
+}
+
+static void
+ResetLine(linePtr)
+ Line *linePtr;
+{
+ FreeTraces(linePtr);
+ ClearPalette(linePtr->palette);
+ if (linePtr->symbolPts != NULL) {
+ Blt_Free(linePtr->symbolPts);
+ }
+ if (linePtr->symbolToData != NULL) {
+ Blt_Free(linePtr->symbolToData);
+ }
+ if (linePtr->strips != NULL) {
+ Blt_Free(linePtr->strips);
+ }
+ if (linePtr->stripToData != NULL) {
+ Blt_Free(linePtr->stripToData);
+ }
+ if (linePtr->activePts != NULL) {
+ Blt_Free(linePtr->activePts);
+ }
+ if (linePtr->activeToData != NULL) {
+ Blt_Free(linePtr->activeToData);
+ }
+ if (linePtr->xErrorBars != NULL) {
+ Blt_Free(linePtr->xErrorBars);
+ }
+ if (linePtr->xErrorToData != NULL) {
+ Blt_Free(linePtr->xErrorToData);
+ }
+ if (linePtr->yErrorBars != NULL) {
+ Blt_Free(linePtr->yErrorBars);
+ }
+ if (linePtr->yErrorToData != NULL) {
+ Blt_Free(linePtr->yErrorToData);
+ }
+ linePtr->xErrorBars = linePtr->yErrorBars = linePtr->strips = NULL;
+ linePtr->symbolPts = linePtr->activePts = NULL;
+ linePtr->stripToData = linePtr->symbolToData = linePtr->xErrorToData =
+ linePtr->yErrorToData = linePtr->activeToData = NULL;
+ linePtr->nActivePts = linePtr->nSymbolPts = linePtr->nStrips =
+ linePtr->xErrorBarCnt = linePtr->yErrorBarCnt = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MapLine --
+ *
+ * Calculates the actual window coordinates of the line element.
+ * The window coordinates are saved in an allocated point array.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Memory is (re)allocated for the point array.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+MapLine(graphPtr, elemPtr)
+ Graph *graphPtr; /* Graph widget record */
+ Element *elemPtr; /* Element component record */
+{
+ Line *linePtr = (Line *)elemPtr;
+ MapInfo mapInfo;
+ int nPoints;
+ PenStyle **dataToStyle;
+ Blt_ChainLink *linkPtr;
+ LinePenStyle *stylePtr;
+
+ ResetLine(linePtr);
+ nPoints = NumberOfPoints(linePtr);
+ if (nPoints < 1) {
+ return; /* No data points */
+ }
+ GetScreenPoints(graphPtr, linePtr, &mapInfo);
+ MapSymbols(graphPtr, linePtr, &mapInfo);
+
+ if ((linePtr->flags & ACTIVE_PENDING) && (linePtr->nReqActive > 0)) {
+ MapActiveSymbols(graphPtr, linePtr);
+ }
+ /*
+ * Map connecting line segments if they are to be displayed.
+ */
+ if ((nPoints > 1) && ((graphPtr->classUid == bltStripElementUid) ||
+ (linePtr->builtinPen.traceWidth > 0))) {
+ linePtr->smooth = linePtr->reqSmooth;
+
+ /*
+ * Do smoothing if necessary. This can extend the coordinate array,
+ * so both mapInfo.points and mapInfo.nPoints may change.
+ */
+
+ switch (linePtr->smooth) {
+ case PEN_SMOOTH_STEP:
+ GenerateSteps(&mapInfo);
+ break;
+
+ case PEN_SMOOTH_NATURAL:
+ case PEN_SMOOTH_QUADRATIC:
+ if (mapInfo.nScreenPts < 3) {
+ /* Can't interpolate with less than three points. */
+ linePtr->smooth = PEN_SMOOTH_NONE;
+ } else {
+ GenerateSpline(graphPtr, linePtr, &mapInfo);
+ }
+ break;
+
+ case PEN_SMOOTH_CATROM:
+ if (mapInfo.nScreenPts < 3) {
+ /* Can't interpolate with less than three points. */
+ linePtr->smooth = PEN_SMOOTH_NONE;
+ } else {
+ GenerateParametricSpline(graphPtr, linePtr, &mapInfo);
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (linePtr->rTolerance > 0.0) {
+ ReducePoints(&mapInfo, linePtr->rTolerance);
+ }
+ if ((linePtr->fillTile != NULL) || (linePtr->fillStipple != None)) {
+ MapFillArea(graphPtr, linePtr, &mapInfo);
+ }
+ if (graphPtr->classUid == bltStripElementUid) {
+ MapStrip(graphPtr, linePtr, &mapInfo);
+ } else {
+ MapTraces(graphPtr, linePtr, &mapInfo);
+ }
+ }
+ Blt_Free(mapInfo.screenPts);
+ Blt_Free(mapInfo.indices);
+
+ /* Set the symbol size of all the pen styles. */
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->symbolSize =
+ ScaleSymbol(elemPtr, stylePtr->penPtr->symbol.size);
+ }
+ dataToStyle = Blt_StyleMap((Element *)linePtr);
+ if (((linePtr->yHigh.nValues > 0) && (linePtr->yLow.nValues > 0)) ||
+ ((linePtr->xHigh.nValues > 0) && (linePtr->xLow.nValues > 0)) ||
+ (linePtr->xError.nValues > 0) || (linePtr->yError.nValues > 0)) {
+ Blt_MapErrorBars(graphPtr, (Element *)linePtr, dataToStyle);
+ }
+ MergePens(linePtr, dataToStyle);
+ Blt_Free(dataToStyle);
+}
+
+static double
+DistanceToLine(x, y, p, q, t)
+ int x, y; /* Sample X-Y coordinate. */
+ Point2D *p, *q; /* End points of the line segment. */
+ Point2D *t; /* (out) Point on line segment. */
+{
+ double right, left, top, bottom;
+
+ *t = Blt_GetProjection(x, y, p, q);
+ if (p->x > q->x) {
+ right = p->x, left = q->x;
+ } else {
+ left = p->x, right = q->x;
+ }
+ if (p->y > q->y) {
+ bottom = p->y, top = q->y;
+ } else {
+ top = p->y, bottom = q->y;
+ }
+ if (t->x > right) {
+ t->x = right;
+ } else if (t->x < left) {
+ t->x = left;
+ }
+ if (t->y > bottom) {
+ t->y = bottom;
+ } else if (t->y < top) {
+ t->y = top;
+ }
+ return hypot((t->x - x), (t->y - y));
+}
+
+static double
+DistanceToX(x, y, p, q, t)
+ int x, y; /* Search X-Y coordinate. */
+ Point2D *p, *q; /* End points of the line segment. */
+ Point2D *t; /* (out) Point on line segment. */
+{
+ double dx, dy;
+ double dist;
+
+ if (p->x > q->x) {
+ if ((x > p->x) || (x < q->x)) {
+ return DBL_MAX; /* X-coordinate outside line segment. */
+ }
+ } else {
+ if ((x > q->x) || (x < p->x)) {
+ return DBL_MAX; /* X-coordinate outside line segment. */
+ }
+ }
+ dx = p->x - q->x;
+ dy = p->y - q->y;
+ t->x = (double)x;
+ if (FABS(dx) < DBL_EPSILON) {
+ double d1, d2;
+ /* Same X-coordinate indicates a vertical line. Pick the
+ * closest end point. */
+ d1 = p->y - y;
+ d2 = q->y - y;
+ if (FABS(d1) < FABS(d2)) {
+ t->y = p->y, dist = d1;
+ } else {
+ t->y = q->y, dist = d2;
+ }
+ } else if (FABS(dy) < DBL_EPSILON) {
+ /* Horizontal line. */
+ t->y = p->y, dist = p->y - y;
+ } else {
+ double m, b;
+
+ m = dy / dx;
+ b = p->y - (m * p->x);
+ t->y = (x * m) + b;
+ dist = y - t->y;
+ }
+ return FABS(dist);
+}
+
+static double
+DistanceToY(x, y, p, q, t)
+ int x, y; /* Search X-Y coordinate. */
+ Point2D *p, *q; /* End points of the line segment. */
+ Point2D *t; /* (out) Point on line segment. */
+{
+ double dx, dy;
+ double dist;
+
+ /* FIXME: provide a distance without overriding a valid */
+ if (p->y > q->y) {
+ if ((y > p->y) || (y < q->y)) {
+ return DBL_MAX;
+ }
+ } else {
+ if ((y > q->y) || (y < p->y)) {
+ return DBL_MAX;
+ }
+ }
+ dx = p->x - q->x;
+ dy = p->y - q->y;
+ t->y = y;
+ if (FABS(dy) < DBL_EPSILON) {
+ double d1, d2;
+
+ /* Save Y-coordinate indicates an horizontal line. Pick the
+ * closest end point. */
+ d1 = p->x - x;
+ d2 = q->x - x;
+ if (FABS(d1) < FABS(d2)) {
+ t->x = p->x, dist = d1;
+ } else {
+ t->x = q->x, dist = d2;
+ }
+ } else if (FABS(dx) < DBL_EPSILON) {
+ /* Vertical line. */
+ t->x = p->x, dist = p->x - x;
+ } else {
+ double m, b;
+
+ m = dy / dx;
+ b = p->y - (m * p->x);
+ t->x = (y - b) / m;
+ dist = x - t->x;
+ }
+ return FABS(dist);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestTrace --
+ *
+ * Find the line segment closest to the given window coordinate
+ * in the element.
+ *
+ * Results:
+ * If a new minimum distance is found, the information regarding
+ * it is returned via searchPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ClosestTrace(graphPtr, linePtr, searchPtr, distProc)
+ Graph *graphPtr; /* Graph widget record */
+ Line *linePtr; /* Line element record */
+ ClosestSearch *searchPtr; /* Info about closest point in element */
+ DistanceProc *distProc;
+{
+ Blt_ChainLink *linkPtr;
+ Point2D closest, b;
+ Trace *tracePtr;
+ double dist, minDist;
+ register Point2D *pointPtr, *endPtr;
+ int i;
+
+ i = -1; /* Suppress compiler warning. */
+ minDist = searchPtr->dist;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+ for (pointPtr = tracePtr->screenPts,
+ endPtr = tracePtr->screenPts + (tracePtr->nScreenPts - 1);
+ pointPtr < endPtr; pointPtr++) {
+ dist = (*distProc)(searchPtr->x, searchPtr->y, pointPtr,
+ pointPtr + 1, &b);
+ if (dist < minDist) {
+ closest = b;
+ i = tracePtr->symbolToData[pointPtr - tracePtr->screenPts];
+ minDist = dist;
+ }
+ }
+ }
+ if (minDist < searchPtr->dist) {
+ searchPtr->dist = minDist;
+ searchPtr->elemPtr = (Element *)linePtr;
+ searchPtr->index = i;
+ searchPtr->point = Blt_InvMap2D(graphPtr, closest.x, closest.y,
+ &(linePtr->axes));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestStrip --
+ *
+ * Find the line segment closest to the given window coordinate
+ * in the element.
+ *
+ * Results:
+ * If a new minimum distance is found, the information regarding
+ * it is returned via searchPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ClosestStrip(graphPtr, linePtr, searchPtr, distProc)
+ Graph *graphPtr; /* Graph widget record */
+ Line *linePtr; /* Line element record */
+ ClosestSearch *searchPtr; /* Info about closest point in element */
+ DistanceProc *distProc;
+{
+ Point2D closest, b;
+ double dist, minDist;
+ int i;
+ register Segment2D *segPtr, *endPtr;
+ int count;
+
+ i = 0;
+ minDist = searchPtr->dist;
+ count = 0;
+ for (segPtr = linePtr->strips, endPtr = segPtr + linePtr->nStrips;
+ segPtr < endPtr; segPtr++) {
+ dist = (*distProc)(searchPtr->x, searchPtr->y,
+ &(segPtr->p), &(segPtr->q), &b);
+ if (dist < minDist) {
+ closest = b;
+ i = linePtr->stripToData[count];
+ minDist = dist;
+ }
+ count++;
+ }
+ if (minDist < searchPtr->dist) {
+ searchPtr->dist = minDist;
+ searchPtr->elemPtr = (Element *)linePtr;
+ searchPtr->index = i;
+ searchPtr->point = Blt_InvMap2D(graphPtr, closest.x, closest.y,
+ &(linePtr->axes));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestPoint --
+ *
+ * Find the element whose data point is closest to the given screen
+ * coordinate.
+ *
+ * Results:
+ * If a new minimum distance is found, the information regarding
+ * it is returned via searchPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ClosestPoint(linePtr, searchPtr)
+ Line *linePtr; /* Line element that we are looking at */
+ ClosestSearch *searchPtr; /* Assorted information related to searching
+ * for the closest point */
+{
+ double dist, minDist;
+ double dx, dy;
+ int count, i;
+ register Point2D *endPtr, *pointPtr;
+
+ minDist = searchPtr->dist;
+ i = 0;
+
+ /*
+ * Instead of testing each data point in graph coordinates, look at
+ * the array of mapped screen coordinates. The advantages are
+ * 1) only examine points that are visible (unclipped), and
+ * 2) the computed distance is already in screen coordinates.
+ */
+ count = 0;
+ for (pointPtr = linePtr->symbolPts,
+ endPtr = linePtr->symbolPts + linePtr->nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ dx = (double)(searchPtr->x - pointPtr->x);
+ dy = (double)(searchPtr->y - pointPtr->y);
+ if (searchPtr->along == SEARCH_BOTH) {
+ dist = hypot(dx, dy);
+ } else if (searchPtr->along == SEARCH_X) {
+ dist = dx;
+ } else if (searchPtr->along == SEARCH_Y) {
+ dist = dy;
+ } else {
+ /* This can't happen */
+ continue;
+ }
+ if (dist < minDist) {
+ i = linePtr->symbolToData[count];
+ minDist = dist;
+ }
+ count++;
+ }
+ if (minDist < searchPtr->dist) {
+ searchPtr->elemPtr = (Element *)linePtr;
+ searchPtr->dist = minDist;
+ searchPtr->index = i;
+ searchPtr->point.x = linePtr->x.valueArr[i];
+ searchPtr->point.y = linePtr->y.valueArr[i];
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetLineExtents --
+ *
+ * Retrieves the range of the line element
+ *
+ * Results:
+ * Returns the number of data points in the element.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+GetLineExtents(elemPtr, extsPtr)
+ Element *elemPtr;
+ Extents2D *extsPtr;
+{
+ int nPoints;
+
+ extsPtr->top = extsPtr->left = DBL_MAX;
+ extsPtr->bottom = extsPtr->right = -DBL_MAX;
+
+ nPoints = NumberOfPoints(elemPtr);
+ if (nPoints < 1) {
+ return;
+ }
+ extsPtr->right = elemPtr->x.max;
+ if ((elemPtr->x.min <= 0.0) && (elemPtr->axes.x->logScale)) {
+ extsPtr->left = Blt_FindElemVectorMinimum(&elemPtr->x, DBL_MIN);
+ } else {
+ extsPtr->left = elemPtr->x.min;
+ }
+ extsPtr->bottom = elemPtr->y.max;
+ if ((elemPtr->y.min <= 0.0) && (elemPtr->axes.y->logScale)) {
+ extsPtr->top = Blt_FindElemVectorMinimum(&elemPtr->y, DBL_MIN);
+ } else {
+ extsPtr->top = elemPtr->y.min;
+ }
+
+ /* Correct the data limits for error bars */
+
+ if (elemPtr->xError.nValues > 0) {
+ register int i;
+ double x;
+
+ nPoints = MIN(elemPtr->xError.nValues, nPoints);
+ for (i = 0; i < nPoints; i++) {
+ x = elemPtr->x.valueArr[i] + elemPtr->xError.valueArr[i];
+ if (x > extsPtr->right) {
+ extsPtr->right = x;
+ }
+ x = elemPtr->x.valueArr[i] - elemPtr->xError.valueArr[i];
+ if (elemPtr->axes.x->logScale) {
+ if (x < 0.0) {
+ x = -x; /* Mirror negative values, instead
+ * of ignoring them. */
+ }
+ if ((x > DBL_MIN) && (x < extsPtr->left)) {
+ extsPtr->left = x;
+ }
+ } else if (x < extsPtr->left) {
+ extsPtr->left = x;
+ }
+ }
+ } else {
+ if ((elemPtr->xHigh.nValues > 0) &&
+ (elemPtr->xHigh.max > extsPtr->right)) {
+ extsPtr->right = elemPtr->xHigh.max;
+ }
+ if (elemPtr->xLow.nValues > 0) {
+ double left;
+
+ if ((elemPtr->xLow.min <= 0.0) &&
+ (elemPtr->axes.x->logScale)) {
+ left = Blt_FindElemVectorMinimum(&elemPtr->xLow, DBL_MIN);
+ } else {
+ left = elemPtr->xLow.min;
+ }
+ if (left < extsPtr->left) {
+ extsPtr->left = left;
+ }
+ }
+ }
+
+ if (elemPtr->yError.nValues > 0) {
+ register int i;
+ double y;
+
+ nPoints = MIN(elemPtr->yError.nValues, nPoints);
+ for (i = 0; i < nPoints; i++) {
+ y = elemPtr->y.valueArr[i] + elemPtr->yError.valueArr[i];
+ if (y > extsPtr->bottom) {
+ extsPtr->bottom = y;
+ }
+ y = elemPtr->y.valueArr[i] - elemPtr->yError.valueArr[i];
+ if (elemPtr->axes.y->logScale) {
+ if (y < 0.0) {
+ y = -y; /* Mirror negative values, instead
+ * of ignoring them. */
+ }
+ if ((y > DBL_MIN) && (y < extsPtr->left)) {
+ extsPtr->top = y;
+ }
+ } else if (y < extsPtr->top) {
+ extsPtr->top = y;
+ }
+ }
+ } else {
+ if ((elemPtr->yHigh.nValues > 0) &&
+ (elemPtr->yHigh.max > extsPtr->bottom)) {
+ extsPtr->bottom = elemPtr->yHigh.max;
+ }
+ if (elemPtr->yLow.nValues > 0) {
+ double top;
+
+ if ((elemPtr->yLow.min <= 0.0) &&
+ (elemPtr->axes.y->logScale)) {
+ top = Blt_FindElemVectorMinimum(&elemPtr->yLow, DBL_MIN);
+ } else {
+ top = elemPtr->yLow.min;
+ }
+ if (top < extsPtr->top) {
+ extsPtr->top = top;
+ }
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TileChangedProc
+ *
+ * Rebuilds the designated GC with the new tile pixmap.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+TileChangedProc(clientData, tile)
+ ClientData clientData;
+ Blt_Tile tile; /* Not used. */
+{
+ Line *linePtr = clientData;
+ Graph *graphPtr;
+
+ graphPtr = linePtr->graphPtr;
+ if (graphPtr->tkwin != NULL) {
+ graphPtr->flags |= REDRAW_WORLD;
+ Blt_EventuallyRedrawGraph(graphPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureLine --
+ *
+ * Sets up the appropriate configuration parameters in the GC.
+ * It is assumed the parameters have been previously set by
+ * a call to Tk_ConfigureWidget.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information such as line width, line style, color
+ * etc. get set in a new GC.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ConfigureLine(graphPtr, elemPtr)
+ Graph *graphPtr;
+ Element *elemPtr;
+{
+ Line *linePtr = (Line *)elemPtr;
+ unsigned long gcMask;
+ XGCValues gcValues;
+ GC newGC;
+ Blt_ChainLink *linkPtr;
+
+ if (ConfigurePen(graphPtr, (Pen *)&(linePtr->builtinPen)) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Point to the static normal/active pens if no external pens have
+ * been selected.
+ */
+ if (linePtr->normalPenPtr == NULL) {
+ linePtr->normalPenPtr = &(linePtr->builtinPen);
+ }
+ linkPtr = Blt_ChainFirstLink(linePtr->palette);
+ if (linkPtr != NULL) {
+ LinePenStyle *stylePtr;
+
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ stylePtr->penPtr = linePtr->normalPenPtr;
+ }
+ if (linePtr->fillTile != NULL) {
+ Blt_SetTileChangedProc(linePtr->fillTile, TileChangedProc, linePtr);
+ }
+ /*
+ * Set the outline GC for this pen: GCForeground is outline color.
+ * GCBackground is the fill color (only used for bitmap symbols).
+ */
+ gcMask = 0;
+ if (linePtr->fillFgColor != NULL) {
+ gcMask |= GCForeground;
+ gcValues.foreground = linePtr->fillFgColor->pixel;
+ }
+ if (linePtr->fillBgColor != NULL) {
+ gcMask |= GCBackground;
+ gcValues.background = linePtr->fillBgColor->pixel;
+ }
+ if ((linePtr->fillStipple != None) &&
+ (linePtr->fillStipple != PATTERN_SOLID)) {
+ gcMask |= (GCStipple | GCFillStyle);
+ gcValues.stipple = linePtr->fillStipple;
+ gcValues.fill_style = (linePtr->fillBgColor == NULL)
+ ? FillStippled : FillOpaqueStippled;
+ }
+ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
+ if (linePtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, linePtr->fillGC);
+ }
+ linePtr->fillGC = newGC;
+
+ if (Blt_ConfigModified(linePtr->configSpecs, "-scalesymbols",
+ (char *)NULL)) {
+ linePtr->flags |= (MAP_ITEM | SCALE_SYMBOL);
+ }
+ if (Blt_ConfigModified(linePtr->configSpecs, "-pixels", "-trace", "-*data",
+ "-smooth", "-map*", "-label", "-hide", "-x", "-y", (char *)NULL)) {
+ linePtr->flags |= MAP_ITEM;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClosestLine --
+ *
+ * Find the closest point or line segment (if interpolated) to
+ * the given window coordinate in the line element.
+ *
+ * Results:
+ * Returns the distance of the closest point among other
+ * information.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ClosestLine(graphPtr, elemPtr, searchPtr)
+ Graph *graphPtr; /* Graph widget record */
+ Element *elemPtr; /* Element to examine */
+ ClosestSearch *searchPtr; /* Info about closest point in element */
+{
+ Line *linePtr = (Line *)elemPtr;
+ int mode;
+
+ mode = searchPtr->mode;
+ if (mode == SEARCH_AUTO) {
+ LinePen *penPtr = linePtr->normalPenPtr;
+
+ mode = SEARCH_POINTS;
+ if ((NumberOfPoints(linePtr) > 1) && (penPtr->traceWidth > 0)) {
+ mode = SEARCH_TRACES;
+ }
+ }
+ if (mode == SEARCH_POINTS) {
+ ClosestPoint(linePtr, searchPtr);
+ } else {
+ DistanceProc *distProc;
+ int found;
+
+ if (searchPtr->along == SEARCH_X) {
+ distProc = DistanceToX;
+ } else if (searchPtr->along == SEARCH_Y) {
+ distProc = DistanceToY;
+ } else {
+ distProc = DistanceToLine;
+ }
+ if (elemPtr->classUid == bltStripElementUid) {
+ found = ClosestStrip(graphPtr, linePtr, searchPtr, distProc);
+ } else {
+ found = ClosestTrace(graphPtr, linePtr, searchPtr, distProc);
+ }
+ if ((!found) && (searchPtr->along != SEARCH_BOTH)) {
+ ClosestPoint(linePtr, searchPtr);
+ }
+ }
+}
+
+/*
+ * XDrawLines() points: XMaxRequestSize(dpy) - 3
+ * XFillPolygon() points: XMaxRequestSize(dpy) - 4
+ * XDrawSegments() segments: (XMaxRequestSize(dpy) - 3) / 2
+ * XDrawRectangles() rectangles: (XMaxRequestSize(dpy) - 3) / 2
+ * XFillRectangles() rectangles: (XMaxRequestSize(dpy) - 3) / 2
+ * XDrawArcs() or XFillArcs() arcs: (XMaxRequestSize(dpy) - 3) / 3
+ */
+
+#define MAX_DRAWLINES(d) (Blt_MaxRequestSize(d) - 3)
+#define MAX_DRAWPOLYGON(d) (Blt_MaxRequestSize(d) - 4)
+#define MAX_DRAWSEGMENTS(d) ((Blt_MaxRequestSize(d) - 3) / 2)
+#define MAX_DRAWRECTANGLES(d) ((Blt_MaxRequestSize(d) - 3) / 2)
+#define MAX_DRAWARCS(d) ((Blt_MaxRequestSize(d) - 3) / 3)
+
+#ifdef WIN32
+
+static void
+DrawCircles(
+ Display *display,
+ Drawable drawable,
+ Line *linePtr,
+ LinePen *penPtr,
+ int nSymbolPts,
+ Point2D *symbolPts,
+ int radius)
+{
+ HBRUSH brush, oldBrush;
+ HPEN pen, oldPen;
+ HDC dc;
+ TkWinDCState state;
+ register Point2D *pointPtr, *endPtr;
+
+ if (drawable == None) {
+ return; /* Huh? */
+ }
+ if ((penPtr->symbol.fillGC == NULL) &&
+ (penPtr->symbol.outlineWidth == 0)) {
+ return;
+ }
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+ /* SetROP2(dc, tkpWinRopModes[penPtr->symbol.fillGC->function]); */
+ if (penPtr->symbol.fillGC != NULL) {
+ brush = CreateSolidBrush(penPtr->symbol.fillGC->foreground);
+ } else {
+ brush = GetStockBrush(NULL_BRUSH);
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ pen = Blt_GCToPen(dc, penPtr->symbol.outlineGC);
+ } else {
+ pen = GetStockPen(NULL_PEN);
+ }
+ oldPen = SelectPen(dc, pen);
+ oldBrush = SelectBrush(dc, brush);
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ Ellipse(dc, (int)pointPtr->x - radius, (int)pointPtr->y - radius,
+ (int)pointPtr->x + radius + 1, (int)pointPtr->y + radius + 1);
+ }
+ DeleteBrush(SelectBrush(dc, oldBrush));
+ DeletePen(SelectPen(dc, oldPen));
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+#else
+
+static void
+DrawCircles(display, drawable, linePtr, penPtr, nSymbolPts, symbolPts, radius)
+ Display *display;
+ Drawable drawable;
+ Line *linePtr;
+ LinePen *penPtr;
+ int nSymbolPts;
+ Point2D *symbolPts;
+ int radius;
+{
+ register int i;
+ XArc *arcArr; /* Array of arcs (circle) */
+ register XArc *arcPtr;
+ int reqSize, nArcs;
+ int s;
+ int count;
+ register Point2D *pointPtr, *endPtr;
+
+ s = radius + radius;
+ arcArr = Blt_Malloc(nSymbolPts * sizeof(XArc));
+ arcPtr = arcArr;
+
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ arcPtr->x = (short int)pointPtr->x - radius;
+ arcPtr->y = (short int)pointPtr->y - radius;
+ arcPtr->width = arcPtr->height = (unsigned short)s;
+ arcPtr->angle1 = 0;
+ arcPtr->angle2 = 23040;
+ arcPtr++, count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ arcPtr->x = (short int)pointPtr->x - radius;
+ arcPtr->y = (short int)pointPtr->y - radius;
+ arcPtr->width = arcPtr->height = (unsigned short)s;
+ arcPtr->angle1 = 0;
+ arcPtr->angle2 = 23040;
+ arcPtr++;
+ }
+ }
+ reqSize = MAX_DRAWARCS(display);
+ for (i = 0; i < count; i += reqSize) {
+ nArcs = ((i + reqSize) > count) ? (count - i) : reqSize;
+ if (penPtr->symbol.fillGC != NULL) {
+ XFillArcs(display, drawable, penPtr->symbol.fillGC, arcArr + i,
+ nArcs);
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ XDrawArcs(display, drawable, penPtr->symbol.outlineGC, arcArr + i,
+ nArcs);
+ }
+ }
+ Blt_Free(arcArr);
+}
+
+#endif
+
+static void
+DrawSquares(display, drawable, linePtr, penPtr, nSymbolPts, symbolPts, r)
+ Display *display;
+ Drawable drawable;
+ Line *linePtr;
+ LinePen *penPtr;
+ int nSymbolPts;
+ register Point2D *symbolPts;
+ int r;
+{
+ XRectangle *rectArr;
+ register Point2D *pointPtr, *endPtr;
+ register XRectangle *rectPtr;
+ int reqSize, nRects;
+ int s;
+ register int i;
+ int count;
+
+ s = r + r;
+ rectArr = Blt_Malloc(nSymbolPts * sizeof(XRectangle));
+ rectPtr = rectArr;
+
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ rectPtr->x = (short int)(pointPtr->x - r);
+ rectPtr->y = (short int)(pointPtr->y - r);
+ rectPtr->width = rectPtr->height = (unsigned short)s;
+ rectPtr++, count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ rectPtr->x = (short int)(pointPtr->x - r);
+ rectPtr->y = (short int)(pointPtr->y - r);
+ rectPtr->width = rectPtr->height = (unsigned short)s;
+ rectPtr++;
+ }
+ }
+ reqSize = MAX_DRAWRECTANGLES(display);
+ for (i = 0; i < count; i += reqSize) {
+ nRects = ((i + reqSize) > count) ? (count - i) : reqSize;
+ if (penPtr->symbol.fillGC != NULL) {
+ XFillRectangles(display, drawable, penPtr->symbol.fillGC,
+ rectArr + i, nRects);
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ XDrawRectangles(display, drawable, penPtr->symbol.outlineGC,
+ rectArr + i, nRects);
+ }
+ }
+ Blt_Free(rectArr);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawSymbols --
+ *
+ * Draw the symbols centered at the each given x,y coordinate
+ * in the array of points.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws a symbol at each coordinate given. If active,
+ * only those coordinates which are currently active are
+ * drawn.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawSymbols(graphPtr, drawable, linePtr, penPtr, size, nSymbolPts, symbolPts)
+ Graph *graphPtr; /* Graph widget record */
+ Drawable drawable; /* Pixmap or window to draw into */
+ Line *linePtr;
+ LinePen *penPtr;
+ int size; /* Size of element */
+ int nSymbolPts; /* Number of coordinates in array */
+ Point2D *symbolPts; /* Array of x,y coordinates for line */
+{
+ XPoint pattern[13]; /* Template for polygon symbols */
+ int r1, r2;
+ register int i, n;
+ int count;
+ register Point2D *pointPtr, *endPtr;
+#define SQRT_PI 1.77245385090552
+#define S_RATIO 0.886226925452758
+
+ if (size < 3) {
+ if (penPtr->symbol.fillGC != NULL) {
+ XPoint *pointArr;
+
+ pointArr = Blt_Malloc(nSymbolPts * sizeof(XPoint));
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ pointArr[count].x = (short int)pointPtr->x;
+ pointArr[count].y = (short int)pointPtr->y;
+ count++;
+ }
+ XDrawPoints(graphPtr->display, drawable, penPtr->symbol.fillGC,
+ pointArr, nSymbolPts, CoordModeOrigin);
+ Blt_Free(pointArr);
+ }
+ return;
+ }
+ r1 = (int)ceil(size * 0.5);
+ r2 = (int)ceil(size * S_RATIO * 0.5);
+
+ switch (penPtr->symbol.type) {
+ case SYMBOL_NONE:
+ break;
+
+ case SYMBOL_SQUARE:
+ DrawSquares(graphPtr->display, drawable, linePtr, penPtr, nSymbolPts,
+ symbolPts, r2);
+ break;
+
+ case SYMBOL_CIRCLE:
+ DrawCircles(graphPtr->display, drawable, linePtr, penPtr, nSymbolPts,
+ symbolPts, r1);
+ break;
+
+ case SYMBOL_SPLUS:
+ case SYMBOL_SCROSS:
+ {
+ XSegment *segArr; /* Array of line segments (splus, scross) */
+ register XSegment *segPtr;
+ int reqSize, nSegs, chunk;
+
+ if (penPtr->symbol.type == SYMBOL_SCROSS) {
+ r2 = Round(r2 * M_SQRT1_2);
+ pattern[3].y = pattern[2].x = pattern[0].x = pattern[0].y = -r2;
+ pattern[3].x = pattern[2].y = pattern[1].y = pattern[1].x = r2;
+ } else {
+ pattern[0].y = pattern[1].y = pattern[2].x = pattern[3].x = 0;
+ pattern[0].x = pattern[2].y = -r2;
+ pattern[1].x = pattern[3].y = r2;
+ }
+ segArr = Blt_Malloc(nSymbolPts * 2 * sizeof(XSegment));
+ segPtr = segArr;
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ segPtr->x1 = pattern[0].x + (short int)pointPtr->x;
+ segPtr->y1 = pattern[0].y + (short int)pointPtr->y;
+ segPtr->x2 = pattern[1].x + (short int)pointPtr->x;
+ segPtr->y2 = pattern[1].y + (short int)pointPtr->y;
+ segPtr++;
+ segPtr->x1 = pattern[2].x + (short int)pointPtr->x;
+ segPtr->y1 = pattern[2].y + (short int)pointPtr->y;
+ segPtr->x2 = pattern[3].x + (short int)pointPtr->x;
+ segPtr->y2 = pattern[3].y + (short int)pointPtr->y;
+ segPtr++;
+ count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ segPtr->x1 = pattern[0].x + (short int)pointPtr->x;
+ segPtr->y1 = pattern[0].y + (short int)pointPtr->y;
+ segPtr->x2 = pattern[1].x + (short int)pointPtr->x;
+ segPtr->y2 = pattern[1].y + (short int)pointPtr->y;
+ segPtr++;
+ segPtr->x1 = pattern[2].x + (short int)pointPtr->x;
+ segPtr->y1 = pattern[2].y + (short int)pointPtr->y;
+ segPtr->x2 = pattern[3].x + (short int)pointPtr->x;
+ segPtr->y2 = pattern[3].y + (short int)pointPtr->y;
+ segPtr++;
+ }
+ }
+ nSegs = count * 2;
+ /* Always draw skinny symbols regardless of the outline width */
+ reqSize = MAX_DRAWSEGMENTS(graphPtr->display);
+ for (i = 0; i < nSegs; i += reqSize) {
+ chunk = ((i + reqSize) > nSegs) ? (nSegs - i) : reqSize;
+ XDrawSegments(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, segArr + i, chunk);
+ }
+ Blt_Free(segArr);
+ }
+ break;
+
+ case SYMBOL_PLUS:
+ case SYMBOL_CROSS:
+ {
+ XPoint *polygon;
+ register XPoint *pntPtr;
+ int d; /* Small delta for cross/plus thickness */
+
+ d = (r2 / 3);
+
+ /*
+ *
+ * 2 3 The plus/cross symbol is a closed polygon
+ * of 12 points. The diagram to the left
+ * 0,12 1 4 5 represents the positions of the points
+ * x,y which are computed below. The extra
+ * 11 10 7 6 (thirteenth) point connects the first and
+ * last points.
+ * 9 8
+ */
+
+ pattern[0].x = pattern[11].x = pattern[12].x = -r2;
+ pattern[2].x = pattern[1].x = pattern[10].x = pattern[9].x = -d;
+ pattern[3].x = pattern[4].x = pattern[7].x = pattern[8].x = d;
+ pattern[5].x = pattern[6].x = r2;
+ pattern[2].y = pattern[3].y = -r2;
+ pattern[0].y = pattern[1].y = pattern[4].y = pattern[5].y =
+ pattern[12].y = -d;
+ pattern[11].y = pattern[10].y = pattern[7].y = pattern[6].y = d;
+ pattern[9].y = pattern[8].y = r2;
+
+ if (penPtr->symbol.type == SYMBOL_CROSS) {
+ double dx, dy;
+
+ /* For the cross symbol, rotate the points by 45 degrees. */
+ for (n = 0; n < 12; n++) {
+ dx = (double)pattern[n].x * M_SQRT1_2;
+ dy = (double)pattern[n].y * M_SQRT1_2;
+ pattern[n].x = Round(dx - dy);
+ pattern[n].y = Round(dx + dy);
+ }
+ pattern[12] = pattern[0];
+ }
+ polygon = Blt_Malloc(nSymbolPts * 13 * sizeof(XPoint));
+ pntPtr = polygon;
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ for (n = 0; n < 13; n++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ pntPtr++;
+ }
+ count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ for (n = 0; n < 13; n++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ pntPtr++;
+ }
+ }
+ }
+ if (penPtr->symbol.fillGC != NULL) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 13) {
+ XFillPolygon(graphPtr->display, drawable,
+ penPtr->symbol.fillGC, pntPtr, 13, Complex,
+ CoordModeOrigin);
+ }
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 13) {
+ XDrawLines(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, pntPtr, 13,
+ CoordModeOrigin);
+ }
+ }
+ Blt_Free(polygon);
+ }
+ break;
+
+ case SYMBOL_DIAMOND:
+ {
+ XPoint *polygon;
+ register XPoint *pntPtr;
+
+ /*
+ *
+ * The plus symbol is a closed polygon
+ * 1 of 4 points. The diagram to the left
+ * represents the positions of the points
+ * 0,4 x,y 2 which are computed below. The extra
+ * (fifth) point connects the first and
+ * 3 last points.
+ *
+ */
+ pattern[1].y = pattern[0].x = -r1;
+ pattern[2].y = pattern[3].x = pattern[0].y = pattern[1].x = 0;
+ pattern[3].y = pattern[2].x = r1;
+ pattern[4] = pattern[0];
+
+ polygon = Blt_Malloc(nSymbolPts * 5 * sizeof(XPoint));
+ pntPtr = polygon;
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ for (n = 0; n < 5; n++, pntPtr++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ }
+ count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ for (n = 0; n < 5; n++, pntPtr++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ }
+ }
+ }
+ if (penPtr->symbol.fillGC != NULL) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 5) {
+ XFillPolygon(graphPtr->display, drawable,
+ penPtr->symbol.fillGC, pntPtr, 5, Convex,
+ CoordModeOrigin);
+
+ }
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 5) {
+ XDrawLines(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, pntPtr, 5,
+ CoordModeOrigin);
+ }
+ }
+ Blt_Free(polygon);
+ }
+ break;
+
+ case SYMBOL_TRIANGLE:
+ case SYMBOL_ARROW:
+ {
+ XPoint *polygon;
+ register XPoint *pntPtr;
+ double b;
+ int b2, h1, h2;
+#define H_RATIO 1.1663402261671607
+#define B_RATIO 1.3467736870885982
+#define TAN30 0.57735026918962573
+#define COS30 0.86602540378443871
+
+ b = Round(size * B_RATIO * 0.7);
+ b2 = Round(b * 0.5);
+ h2 = Round(TAN30 * b2);
+ h1 = Round(b2 / COS30);
+ /*
+ *
+ * The triangle symbol is a closed polygon
+ * 0,3 of 3 points. The diagram to the left
+ * represents the positions of the points
+ * x,y which are computed below. The extra
+ * (fourth) point connects the first and
+ * 2 1 last points.
+ *
+ */
+
+ if (penPtr->symbol.type == SYMBOL_ARROW) {
+ pattern[3].x = pattern[0].x = 0;
+ pattern[3].y = pattern[0].y = h1;
+ pattern[1].x = b2;
+ pattern[2].y = pattern[1].y = -h2;
+ pattern[2].x = -b2;
+ } else {
+ pattern[3].x = pattern[0].x = 0;
+ pattern[3].y = pattern[0].y = -h1;
+ pattern[1].x = b2;
+ pattern[2].y = pattern[1].y = h2;
+ pattern[2].x = -b2;
+ }
+ polygon = Blt_Malloc(nSymbolPts * 4 * sizeof(XPoint));
+ pntPtr = polygon;
+ if (linePtr->symbolInterval > 0) {
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ for (n = 0; n < 4; n++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ pntPtr++;
+ }
+ count++;
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ count = nSymbolPts;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ for (n = 0; n < 4; n++) {
+ pntPtr->x = pattern[n].x + (short int)pointPtr->x;
+ pntPtr->y = pattern[n].y + (short int)pointPtr->y;
+ pntPtr++;
+ }
+ }
+ }
+ if (penPtr->symbol.fillGC != NULL) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 4) {
+ XFillPolygon(graphPtr->display, drawable,
+ penPtr->symbol.fillGC, pntPtr, 4, Convex,
+ CoordModeOrigin);
+ }
+ }
+ if (penPtr->symbol.outlineWidth > 0) {
+ for (pntPtr = polygon, i = 0; i < count; i++, pntPtr += 4) {
+ XDrawLines(graphPtr->display, drawable,
+ penPtr->symbol.outlineGC, pntPtr, 4,
+ CoordModeOrigin);
+ }
+ }
+ Blt_Free(polygon);
+ }
+ break;
+ case SYMBOL_BITMAP:
+ {
+ Pixmap bitmap, mask;
+ int width, height, bmWidth, bmHeight;
+ double scale, sx, sy;
+ int dx, dy;
+ register int x, y;
+
+ Tk_SizeOfBitmap(graphPtr->display, penPtr->symbol.bitmap,
+ &width, &height);
+ mask = None;
+
+ /*
+ * Compute the size of the scaled bitmap. Stretch the
+ * bitmap to fit a nxn bounding box.
+ */
+ sx = (double)size / (double)width;
+ sy = (double)size / (double)height;
+ scale = MIN(sx, sy);
+ bmWidth = (int)(width * scale);
+ bmHeight = (int)(height * scale);
+
+ XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC, None);
+ if (penPtr->symbol.mask != None) {
+ mask = Blt_ScaleBitmap(graphPtr->tkwin, penPtr->symbol.mask,
+ width, height, bmWidth, bmHeight);
+ XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC,
+ mask);
+ }
+ bitmap = Blt_ScaleBitmap(graphPtr->tkwin, penPtr->symbol.bitmap,
+ width, height, bmWidth, bmHeight);
+ if (penPtr->symbol.fillGC == NULL) {
+ XSetClipMask(graphPtr->display, penPtr->symbol.outlineGC,
+ bitmap);
+ }
+ dx = bmWidth / 2;
+ dy = bmHeight / 2;
+ if (linePtr->symbolInterval > 0) {
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ if (DRAW_SYMBOL(linePtr)) {
+ x = (int)pointPtr->x - dx;
+ y = (int)pointPtr->y - dy;
+ if ((penPtr->symbol.fillGC == NULL) || (mask != None)) {
+ XSetClipOrigin(graphPtr->display,
+ penPtr->symbol.outlineGC, x, y);
+ }
+ XCopyPlane(graphPtr->display, bitmap, drawable,
+ penPtr->symbol.outlineGC, 0, 0, bmWidth, bmHeight,
+ x, y, 1);
+ }
+ linePtr->symbolCounter++;
+ }
+ } else {
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ x = (int)pointPtr->x - dx;
+ y = (int)pointPtr->y - dy;
+ if ((penPtr->symbol.fillGC == NULL) || (mask != None)) {
+ XSetClipOrigin(graphPtr->display,
+ penPtr->symbol.outlineGC, x, y);
+ }
+ XCopyPlane(graphPtr->display, bitmap, drawable,
+ penPtr->symbol.outlineGC, 0, 0, bmWidth, bmHeight,
+ x, y, 1);
+ }
+ }
+ Tk_FreePixmap(graphPtr->display, bitmap);
+ if (mask != None) {
+ Tk_FreePixmap(graphPtr->display, mask);
+ }
+ }
+ break;
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * DrawSymbol --
+ *
+ * Draw the symbol centered at the each given x,y coordinate.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws a symbol at the coordinate given.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+DrawSymbol(graphPtr, drawable, elemPtr, x, y, size)
+ Graph *graphPtr; /* Graph widget record */
+ Drawable drawable; /* Pixmap or window to draw into */
+ Element *elemPtr; /* Line element information */
+ int x, y; /* Center position of symbol */
+ int size; /* Size of symbol. */
+{
+ Line *linePtr = (Line *)elemPtr;
+ LinePen *penPtr = linePtr->normalPenPtr;
+
+ if (penPtr->traceWidth > 0) {
+ /*
+ * Draw an extra line offset by one pixel from the previous to
+ * give a thicker appearance. This is only for the legend
+ * entry. This routine is never called for drawing the actual
+ * line segments.
+ */
+ XDrawLine(graphPtr->display, drawable, penPtr->traceGC, x - size,
+ y, x + size, y);
+ XDrawLine(graphPtr->display, drawable, penPtr->traceGC, x - size,
+ y + 1, x + size, y + 1);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ Point2D point;
+
+ point.x = x, point.y = y;
+ DrawSymbols(graphPtr, drawable, linePtr, linePtr->normalPenPtr, size,
+ 1, &point);
+ }
+}
+
+#ifdef WIN32
+
+static void
+DrawTraces(
+ Graph *graphPtr,
+ Drawable drawable, /* Pixmap or window to draw into */
+ Line *linePtr,
+ LinePen *penPtr)
+{
+ Blt_ChainLink *linkPtr;
+ Trace *tracePtr;
+ int size;
+ int n;
+ register int i;
+ int start, extra;
+
+ size = ((Blt_MaxRequestSize(graphPtr->display) * 4) / sizeof(XPoint)) - 2;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+ /*
+ * If the trace has to be split into separate XDrawLines
+ * calls, then the end point of the current trace is also the
+ * starting point of the new split.
+ */
+ start = extra = 0;
+ for (i = tracePtr->nScreenPts; i > 0; i -= size) {
+ n = MIN(i, size);
+ Blt_DrawPoint2DLine(graphPtr->display, drawable, penPtr->traceGC,
+ tracePtr->screenPts + start, n + extra);
+ start += (n - 1);
+ extra = 1;
+ }
+ }
+}
+
+#else
+
+static void
+DrawTraces(graphPtr, drawable, linePtr, penPtr)
+ Graph *graphPtr;
+ Drawable drawable; /* Pixmap or window to draw into */
+ Line *linePtr;
+ LinePen *penPtr;
+{
+ Blt_ChainLink *linkPtr;
+ Trace *tracePtr;
+ int size;
+ int n;
+ register int i;
+ int start, extra;
+ XPoint *pointArr;
+ register Point2D *pointPtr, *endPtr;
+ int count;
+
+ size = ((Blt_MaxRequestSize(graphPtr->display) * 4) / sizeof(XPoint)) - 2;
+
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+
+ /*
+ * If the trace has to be split into separate XDrawLines
+ * calls, then the end point of the current trace is also the
+ * starting point of the new split.
+ */
+ start = extra = 0;
+ pointArr = Blt_Malloc((tracePtr->nScreenPts + 1) * sizeof(XPoint));
+ for (i = tracePtr->nScreenPts; i > 0; i -= size) {
+ n = MIN(i, size);
+ count = 0;
+ for (pointPtr = tracePtr->screenPts + start,
+ endPtr = tracePtr->screenPts + (n + extra);
+ pointPtr < endPtr; pointPtr++) {
+ pointArr[count].x = (short int)pointPtr->x;
+ pointArr[count].y = (short int)pointPtr->y;
+ count++;
+ }
+ XDrawLines(graphPtr->display, drawable, penPtr->traceGC,
+ pointArr, n + extra, CoordModeOrigin);
+ start += (n - 1);
+ extra = 1;
+ }
+ Blt_Free(pointArr);
+ }
+}
+#endif /* WIN32 */
+
+static void
+DrawValues(graphPtr, drawable, linePtr, penPtr, nSymbolPts, symbolPts,
+ pointToData)
+ Graph *graphPtr;
+ Drawable drawable;
+ Line *linePtr;
+ LinePen *penPtr;
+ int nSymbolPts;
+ Point2D *symbolPts;
+ int *pointToData;
+{
+ Point2D *pointPtr, *endPtr;
+ int count;
+ char string[TCL_DOUBLE_SPACE * 2 + 2];
+ char *fmt;
+ double x, y;
+
+ fmt = penPtr->valueFormat;
+ if (fmt == NULL) {
+ fmt = "%g";
+ }
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ x = linePtr->x.valueArr[pointToData[count]];
+ y = linePtr->y.valueArr[pointToData[count]];
+ count++;
+ if (penPtr->valueShow == SHOW_X) {
+ sprintf(string, fmt, x);
+ } else if (penPtr->valueShow == SHOW_Y) {
+ sprintf(string, fmt, y);
+ } else if (penPtr->valueShow == SHOW_BOTH) {
+ sprintf(string, fmt, x);
+ strcat(string, ",");
+ sprintf(string + strlen(string), fmt, y);
+ }
+ Blt_DrawText(graphPtr->tkwin, drawable, string, &(penPtr->valueStyle),
+ (int)pointPtr->x, (int)pointPtr->y);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawActiveLine --
+ *
+ * Draws the connected line(s) representing the element. If the
+ * line is made up of non-line symbols and the line width
+ * parameter has been set (linewidth > 0), the element will also
+ * be drawn as a line (with the linewidth requested). The line
+ * may consist of separate line segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * X drawing commands are output.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawActiveLine(graphPtr, drawable, elemPtr)
+ Graph *graphPtr; /* Graph widget record */
+ Drawable drawable; /* Pixmap or window to draw into */
+ Element *elemPtr; /* Element to be drawn */
+{
+ Line *linePtr = (Line *)elemPtr;
+ LinePen *penPtr = linePtr->activePenPtr;
+ int symbolSize;
+
+ if (penPtr == NULL) {
+ return;
+ }
+ symbolSize = ScaleSymbol(elemPtr, linePtr->activePenPtr->symbol.size);
+
+ /*
+ * nReqActive
+ * > 0 Some points are active. Uses activeArr.
+ * < 0 All points are active.
+ * == 0 No points are active.
+ */
+ if (linePtr->nReqActive > 0) {
+ if (linePtr->flags & ACTIVE_PENDING) {
+ MapActiveSymbols(graphPtr, linePtr);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ DrawSymbols(graphPtr, drawable, linePtr, penPtr, symbolSize,
+ linePtr->nActivePts, linePtr->activePts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, linePtr, penPtr,
+ linePtr->nActivePts, linePtr->activePts,
+ linePtr->activeToData);
+ }
+ } else if (linePtr->nReqActive < 0) {
+ if (penPtr->traceWidth > 0) {
+ if (linePtr->nStrips > 0) {
+ Blt_DrawSegments2D(graphPtr->display, drawable,
+ penPtr->traceGC, linePtr->strips, linePtr->nStrips);
+ } else if (Blt_ChainGetLength(linePtr->chainPtr) > 0) {
+ DrawTraces(graphPtr, drawable, linePtr, penPtr);
+ }
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ DrawSymbols(graphPtr, drawable, linePtr, penPtr, symbolSize,
+ linePtr->nSymbolPts, linePtr->symbolPts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, linePtr, penPtr,
+ linePtr->nSymbolPts, linePtr->symbolPts,
+ linePtr->symbolToData);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawNormalLine --
+ *
+ * Draws the connected line(s) representing the element. If the
+ * line is made up of non-line symbols and the line width parameter
+ * has been set (linewidth > 0), the element will also be drawn as
+ * a line (with the linewidth requested). The line may consist of
+ * separate line segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * X drawing commands are output.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawNormalLine(graphPtr, drawable, elemPtr)
+ Graph *graphPtr; /* Graph widget record */
+ Drawable drawable; /* Pixmap or window to draw into */
+ Element *elemPtr; /* Element to be drawn */
+{
+ Line *linePtr = (Line *)elemPtr;
+ LinePen *penPtr;
+ Blt_ChainLink *linkPtr;
+ register LinePenStyle *stylePtr;
+ unsigned int count;
+
+ /* Fill area under the curve */
+ if (linePtr->fillPts != NULL) {
+ XPoint *pointArr;
+ Point2D *endPtr, *pointPtr;
+
+ pointArr = Blt_Malloc(sizeof(XPoint) * linePtr->nFillPts);
+ count = 0;
+ for(pointPtr = linePtr->fillPts,
+ endPtr = linePtr->fillPts + linePtr->nFillPts;
+ pointPtr < endPtr; pointPtr++) {
+ pointArr[count].x = (short int)pointPtr->x;
+ pointArr[count].y = (short int)pointPtr->y;
+ count++;
+ }
+ if (linePtr->fillTile != NULL) {
+ Blt_SetTileOrigin(graphPtr->tkwin, linePtr->fillTile, 0, 0);
+ Blt_TilePolygon(graphPtr->tkwin, drawable, linePtr->fillTile,
+ pointArr, linePtr->nFillPts);
+ } else if (linePtr->fillStipple != None) {
+ XFillPolygon(graphPtr->display, drawable, linePtr->fillGC,
+ pointArr, linePtr->nFillPts, Complex, CoordModeOrigin);
+ }
+ Blt_Free(pointArr);
+ }
+
+ /* Lines: stripchart segments or graph traces. */
+
+ if (linePtr->nStrips > 0) {
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ penPtr = stylePtr->penPtr;
+ if ((stylePtr->nStrips > 0) && (penPtr->errorWidth > 0)) {
+ Blt_DrawSegments2D(graphPtr->display, drawable,
+ penPtr->traceGC, stylePtr->strips, stylePtr->nStrips);
+ }
+ }
+ } else if ((Blt_ChainGetLength(linePtr->chainPtr) > 0) &&
+ (linePtr->normalPenPtr->traceWidth > 0)) {
+ DrawTraces(graphPtr, drawable, linePtr, linePtr->normalPenPtr);
+ }
+
+ if (linePtr->reqMaxSymbols > 0) {
+ int total;
+
+ total = 0;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ total += stylePtr->nSymbolPts;
+ }
+ linePtr->symbolInterval = total / linePtr->reqMaxSymbols;
+ linePtr->symbolCounter = 0;
+ }
+
+ /* Symbols, error bars, values. */
+
+ count = 0;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ penPtr = stylePtr->penPtr;
+ if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorShow & SHOW_X)) {
+ Blt_DrawSegments2D(graphPtr->display, drawable, penPtr->errorGC,
+ stylePtr->xErrorBars, stylePtr->xErrorBarCnt);
+ }
+ if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorShow & SHOW_Y)) {
+ Blt_DrawSegments2D(graphPtr->display, drawable, penPtr->errorGC,
+ stylePtr->yErrorBars, stylePtr->yErrorBarCnt);
+ }
+ if ((stylePtr->nSymbolPts > 0) &&
+ (penPtr->symbol.type != SYMBOL_NONE)) {
+ DrawSymbols(graphPtr, drawable, linePtr, penPtr,
+ stylePtr->symbolSize, stylePtr->nSymbolPts,
+ stylePtr->symbolPts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ DrawValues(graphPtr, drawable, linePtr, penPtr,
+ stylePtr->nSymbolPts, stylePtr->symbolPts,
+ linePtr->symbolToData + count);
+ }
+ count += stylePtr->nSymbolPts;
+ }
+ linePtr->symbolInterval = 0;
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * GetSymbolPostScriptInfo --
+ *
+ * Set up the PostScript environment with the macros and
+ * attributes needed to draw the symbols of the element.
+ *
+ * Results:
+ * None.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+GetSymbolPostScriptInfo(graphPtr, psToken, penPtr, size)
+ Graph *graphPtr;
+ PsToken psToken;
+ LinePen *penPtr;
+ int size;
+{
+ XColor *outlineColor, *fillColor, *defaultColor;
+
+ /* Set line and foreground attributes */
+ outlineColor = penPtr->symbol.outlineColor;
+ fillColor = penPtr->symbol.fillColor;
+ defaultColor = penPtr->traceColor;
+
+ if (fillColor == COLOR_DEFAULT) {
+ fillColor = defaultColor;
+ }
+ if (outlineColor == COLOR_DEFAULT) {
+ outlineColor = defaultColor;
+ }
+ if (penPtr->symbol.type == SYMBOL_NONE) {
+ Blt_LineAttributesToPostScript(psToken, defaultColor,
+ penPtr->traceWidth + 2, &(penPtr->traceDashes),
+ CapButt, JoinMiter);
+ } else {
+ Blt_LineWidthToPostScript(psToken, penPtr->symbol.outlineWidth);
+ Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+ }
+
+ /*
+ * Build a PostScript procedure to draw the symbols. For bitmaps,
+ * paint both the bitmap and its mask. Otherwise fill and stroke
+ * the path formed already.
+ */
+ Blt_AppendToPostScript(psToken, "\n/DrawSymbolProc {\n", (char *)NULL);
+ switch (penPtr->symbol.type) {
+ case SYMBOL_NONE:
+ break; /* Do nothing */
+ case SYMBOL_BITMAP:
+ {
+ int width, height;
+ double sx, sy, scale;
+
+ /*
+ * Compute how much to scale the bitmap. Don't let the
+ * scaled bitmap exceed the bounding square for the
+ * symbol.
+ */
+ Tk_SizeOfBitmap(graphPtr->display, penPtr->symbol.bitmap,
+ &width, &height);
+ sx = (double)size / (double)width;
+ sy = (double)size / (double)height;
+ scale = MIN(sx, sy);
+
+ if ((penPtr->symbol.mask != None) && (fillColor != NULL)) {
+ Blt_AppendToPostScript(psToken,
+ "\n % Bitmap mask is \"",
+ Tk_NameOfBitmap(graphPtr->display, penPtr->symbol.mask),
+ "\"\n\n ", (char *)NULL);
+ Blt_BackgroundToPostScript(psToken, fillColor);
+ Blt_BitmapToPostScript(psToken, graphPtr->display,
+ penPtr->symbol.mask, scale, scale);
+ }
+ Blt_AppendToPostScript(psToken,
+ "\n % Bitmap symbol is \"",
+ Tk_NameOfBitmap(graphPtr->display, penPtr->symbol.bitmap),
+ "\"\n\n ", (char *)NULL);
+ Blt_ForegroundToPostScript(psToken, outlineColor);
+ Blt_BitmapToPostScript(psToken, graphPtr->display,
+ penPtr->symbol.bitmap, scale, scale);
+ }
+ break;
+ default:
+ Blt_AppendToPostScript(psToken, " gsave\n", (char *)NULL);
+ if (fillColor != NULL) {
+ Blt_AppendToPostScript(psToken, " ", (char *)NULL);
+ Blt_BackgroundToPostScript(psToken, fillColor);
+ Blt_AppendToPostScript(psToken, " Fill\n", (char *)NULL);
+ }
+ if ((outlineColor != NULL) && (penPtr->symbol.outlineWidth > 0)) {
+ Blt_AppendToPostScript(psToken, " ", (char *)NULL);
+ Blt_ForegroundToPostScript(psToken, outlineColor);
+ Blt_AppendToPostScript(psToken, " stroke\n", (char *)NULL);
+ }
+ Blt_AppendToPostScript(psToken, " grestore\n", (char *)NULL);
+ break;
+ }
+ Blt_AppendToPostScript(psToken, "} def\n\n", (char *)NULL);
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * SymbolsToPostScript --
+ *
+ * Draw a symbol centered at the given x,y window coordinate
+ * based upon the element symbol type and size.
+ *
+ * Results:
+ * None.
+ *
+ * Problems:
+ * Most notable is the round-off errors generated when
+ * calculating the centered position of the symbol.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+SymbolsToPostScript(graphPtr, psToken, penPtr, size, nSymbolPts, symbolPts)
+ Graph *graphPtr;
+ PsToken psToken;
+ LinePen *penPtr;
+ int size;
+ int nSymbolPts;
+ Point2D *symbolPts;
+{
+ double symbolSize;
+ register Point2D *pointPtr, *endPtr;
+ static char *symbolMacros[] =
+ {
+ "Li", "Sq", "Ci", "Di", "Pl", "Cr", "Sp", "Sc", "Tr", "Ar", "Bm",
+ (char *)NULL,
+ };
+ GetSymbolPostScriptInfo(graphPtr, psToken, penPtr, size);
+
+ symbolSize = (double)size;
+ switch (penPtr->symbol.type) {
+ case SYMBOL_SQUARE:
+ case SYMBOL_CROSS:
+ case SYMBOL_PLUS:
+ case SYMBOL_SCROSS:
+ case SYMBOL_SPLUS:
+ symbolSize = (double)Round(size * S_RATIO);
+ break;
+ case SYMBOL_TRIANGLE:
+ case SYMBOL_ARROW:
+ symbolSize = (double)Round(size * 0.7);
+ break;
+ case SYMBOL_DIAMOND:
+ symbolSize = (double)Round(size * M_SQRT1_2);
+ break;
+
+ default:
+ break;
+ }
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ Blt_FormatToPostScript(psToken, "%g %g %g %s\n", pointPtr->x,
+ pointPtr->y, symbolSize, symbolMacros[penPtr->symbol.type]);
+ }
+}
+
+/*
+ * -----------------------------------------------------------------
+ *
+ * SymbolToPostScript --
+ *
+ * Draw the symbol centered at the each given x,y coordinate.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Draws a symbol at the coordinate given.
+ *
+ * -----------------------------------------------------------------
+ */
+static void
+SymbolToPostScript(graphPtr, psToken, elemPtr, x, y, size)
+ Graph *graphPtr; /* Graph widget record */
+ PsToken psToken;
+ Element *elemPtr; /* Line element information */
+ double x, y; /* Center position of symbol */
+ int size; /* Size of element */
+{
+ Line *linePtr = (Line *)elemPtr;
+ LinePen *penPtr = linePtr->normalPenPtr;
+
+ if (penPtr->traceWidth > 0) {
+ /*
+ * Draw an extra line offset by one pixel from the previous to
+ * give a thicker appearance. This is only for the legend
+ * entry. This routine is never called for drawing the actual
+ * line segments.
+ */
+ Blt_LineAttributesToPostScript(psToken, penPtr->traceColor,
+ penPtr->traceWidth + 2, &(penPtr->traceDashes), CapButt, JoinMiter);
+ Blt_FormatToPostScript(psToken, "%g %g %d Li\n", x, y, size + size);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ Point2D point;
+
+ point.x = x, point.y = y;
+ SymbolsToPostScript(graphPtr, psToken, penPtr, size, 1, &point);
+ }
+}
+
+
+static void
+SetLineAttributes(psToken, penPtr)
+ PsToken psToken;
+ LinePen *penPtr;
+{
+ /* Set the attributes of the line (color, dashes, linewidth) */
+ Blt_LineAttributesToPostScript(psToken, penPtr->traceColor,
+ penPtr->traceWidth, &(penPtr->traceDashes), CapButt, JoinMiter);
+ if ((LineIsDashed(penPtr->traceDashes)) &&
+ (penPtr->traceOffColor != NULL)) {
+ Blt_AppendToPostScript(psToken, "/DashesProc {\n gsave\n ",
+ (char *)NULL);
+ Blt_BackgroundToPostScript(psToken, penPtr->traceOffColor);
+ Blt_AppendToPostScript(psToken, " ", (char *)NULL);
+ Blt_LineDashesToPostScript(psToken, (Blt_Dashes *)NULL);
+ Blt_AppendToPostScript(psToken, "stroke\n grestore\n} def\n",
+ (char *)NULL);
+ } else {
+ Blt_AppendToPostScript(psToken, "/DashesProc {} def\n", (char *)NULL);
+ }
+}
+
+static void
+TracesToPostScript(psToken, linePtr, penPtr)
+ PsToken psToken;
+ Line *linePtr;
+ LinePen *penPtr;
+{
+ Blt_ChainLink *linkPtr;
+ Trace *tracePtr;
+ register Point2D *pointPtr, *endPtr;
+ int count;
+
+ SetLineAttributes(psToken, penPtr);
+ for (linkPtr = Blt_ChainFirstLink(linePtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+ if (tracePtr->nScreenPts <= 0) {
+ continue;
+ }
+#define PS_MAXPATH 1500 /* Maximum number of components in a PostScript
+ * (level 1) path. */
+ pointPtr = tracePtr->screenPts;
+ Blt_FormatToPostScript(psToken, " newpath %g %g moveto\n",
+ pointPtr->x,
+ pointPtr->y);
+ pointPtr++;
+ count = 0;
+ for (endPtr = tracePtr->screenPts + (tracePtr->nScreenPts - 1);
+ pointPtr < endPtr; pointPtr++) {
+ Blt_FormatToPostScript(psToken, " %g %g lineto\n",
+ pointPtr->x,
+ pointPtr->y);
+ if ((count % PS_MAXPATH) == 0) {
+ Blt_FormatToPostScript(psToken,
+ "DashesProc stroke\n newpath %g %g moveto\n",
+ pointPtr->x,
+ pointPtr->y);
+ }
+ count++;
+ }
+ Blt_FormatToPostScript(psToken, " %g %g lineto\n",
+ pointPtr->x,
+ pointPtr->y);
+ Blt_AppendToPostScript(psToken, "DashesProc stroke\n", (char *)NULL);
+ }
+}
+
+
+static void
+ValuesToPostScript(psToken, linePtr, penPtr, nSymbolPts, symbolPts,
+ pointToData)
+ PsToken psToken;
+ Line *linePtr;
+ LinePen *penPtr;
+ int nSymbolPts;
+ Point2D *symbolPts;
+ int *pointToData;
+{
+ Point2D *pointPtr, *endPtr;
+ int count;
+ char string[TCL_DOUBLE_SPACE * 2 + 2];
+ char *fmt;
+ double x, y;
+
+ fmt = penPtr->valueFormat;
+ if (fmt == NULL) {
+ fmt = "%g";
+ }
+ count = 0;
+ for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts;
+ pointPtr < endPtr; pointPtr++) {
+ x = linePtr->x.valueArr[pointToData[count]];
+ y = linePtr->y.valueArr[pointToData[count]];
+ count++;
+ if (penPtr->valueShow == SHOW_X) {
+ sprintf(string, fmt, x);
+ } else if (penPtr->valueShow == SHOW_Y) {
+ sprintf(string, fmt, y);
+ } else if (penPtr->valueShow == SHOW_BOTH) {
+ sprintf(string, fmt, x);
+ strcat(string, ",");
+ sprintf(string + strlen(string), fmt, y);
+ }
+ Blt_TextToPostScript(psToken, string, &(penPtr->valueStyle),
+ pointPtr->x, pointPtr->y);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ActiveLineToPostScript --
+ *
+ * Generates PostScript commands to draw as "active" the points
+ * (symbols) and or line segments (trace) representing the
+ * element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript pen width, dashes, and color settings are changed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+ActiveLineToPostScript(graphPtr, psToken, elemPtr)
+ Graph *graphPtr;
+ PsToken psToken;
+ Element *elemPtr;
+{
+ Line *linePtr = (Line *)elemPtr;
+ LinePen *penPtr = linePtr->activePenPtr;
+ int symbolSize;
+
+ if (penPtr == NULL) {
+ return;
+ }
+ symbolSize = ScaleSymbol(elemPtr, penPtr->symbol.size);
+ if (linePtr->nReqActive > 0) {
+ if (linePtr->flags & ACTIVE_PENDING) {
+ MapActiveSymbols(graphPtr, linePtr);
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ SymbolsToPostScript(graphPtr, psToken, penPtr, symbolSize,
+ linePtr->nActivePts, linePtr->activePts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(psToken, linePtr, penPtr, linePtr->nActivePts,
+ linePtr->activePts, linePtr->activeToData);
+ }
+ } else if (linePtr->nReqActive < 0) {
+ if (penPtr->traceWidth > 0) {
+ if (linePtr->nStrips > 0) {
+ SetLineAttributes(psToken, penPtr);
+ Blt_Segments2DToPostScript(psToken, linePtr->strips,
+ linePtr->nStrips);
+ }
+ if (Blt_ChainGetLength(linePtr->chainPtr) > 0) {
+ TracesToPostScript(psToken, linePtr, (LinePen *)penPtr);
+ }
+ }
+ if (penPtr->symbol.type != SYMBOL_NONE) {
+ SymbolsToPostScript(graphPtr, psToken, penPtr, symbolSize,
+ linePtr->nSymbolPts, linePtr->symbolPts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(psToken, linePtr, penPtr, linePtr->nSymbolPts,
+ linePtr->symbolPts, linePtr->symbolToData);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NormalLineToPostScript --
+ *
+ * Similar to the DrawLine procedure, prints PostScript related
+ * commands to form the connected line(s) representing the element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * PostScript pen width, dashes, and color settings are changed.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+NormalLineToPostScript(graphPtr, psToken, elemPtr)
+ Graph *graphPtr;
+ PsToken psToken;
+ Element *elemPtr;
+{
+ Line *linePtr = (Line *)elemPtr;
+ register LinePenStyle *stylePtr;
+ Blt_ChainLink *linkPtr;
+ LinePen *penPtr;
+ unsigned int count;
+ XColor *colorPtr;
+
+ /* Draw fill area */
+ if (linePtr->fillPts != NULL) {
+ /* Create a path to use for both the polygon and its outline. */
+ Blt_PathToPostScript(psToken, linePtr->fillPts, linePtr->nFillPts);
+ Blt_AppendToPostScript(psToken, "closepath\n", (char *)NULL);
+
+ /* If the background fill color was specified, draw the
+ * polygon in a solid fashion with that color. */
+ if (linePtr->fillBgColor != NULL) {
+ Blt_BackgroundToPostScript(psToken, linePtr->fillBgColor);
+ Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+ }
+ Blt_ForegroundToPostScript(psToken, linePtr->fillFgColor);
+ if (linePtr->fillTile != NULL) {
+ /* TBA: Transparent tiling is the hard part. */
+ } else if ((linePtr->fillStipple != None) &&
+ (linePtr->fillStipple != PATTERN_SOLID)) {
+ /* Draw the stipple in the foreground color. */
+ Blt_StippleToPostScript(psToken, graphPtr->display,
+ linePtr->fillStipple);
+ } else {
+ Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
+ }
+ }
+ /* Draw lines */
+ if (linePtr->nStrips > 0) {
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ penPtr = stylePtr->penPtr;
+ if ((stylePtr->nStrips > 0) && (penPtr->traceWidth > 0)) {
+ SetLineAttributes(psToken, penPtr);
+ Blt_Segments2DToPostScript(psToken, stylePtr->strips,
+ stylePtr->nStrips);
+ }
+ }
+ } else if ((Blt_ChainGetLength(linePtr->chainPtr) > 0) &&
+ (linePtr->normalPenPtr->traceWidth > 0)) {
+ TracesToPostScript(psToken, linePtr, linePtr->normalPenPtr);
+ }
+
+ /* Draw symbols, error bars, values. */
+
+ count = 0;
+ for (linkPtr = Blt_ChainFirstLink(linePtr->palette); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ stylePtr = Blt_ChainGetValue(linkPtr);
+ penPtr = stylePtr->penPtr;
+ colorPtr = penPtr->errorColor;
+ if (colorPtr == COLOR_DEFAULT) {
+ colorPtr = penPtr->traceColor;
+ }
+ if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorShow & SHOW_X)) {
+ Blt_LineAttributesToPostScript(psToken, colorPtr,
+ penPtr->errorWidth, NULL, CapButt, JoinMiter);
+ Blt_Segments2DToPostScript(psToken, stylePtr->xErrorBars,
+ stylePtr->xErrorBarCnt);
+ }
+ if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorShow & SHOW_Y)) {
+ Blt_LineAttributesToPostScript(psToken, colorPtr,
+ penPtr->errorWidth, NULL, CapButt, JoinMiter);
+ Blt_Segments2DToPostScript(psToken, stylePtr->yErrorBars,
+ stylePtr->yErrorBarCnt);
+ }
+ if ((stylePtr->nSymbolPts > 0) &&
+ (stylePtr->penPtr->symbol.type != SYMBOL_NONE)) {
+ SymbolsToPostScript(graphPtr, psToken, penPtr,
+ stylePtr->symbolSize, stylePtr->nSymbolPts,
+ stylePtr->symbolPts);
+ }
+ if (penPtr->valueShow != SHOW_NONE) {
+ ValuesToPostScript(psToken, linePtr, penPtr,
+ stylePtr->nSymbolPts, stylePtr->symbolPts,
+ linePtr->symbolToData + count);
+ }
+ count += stylePtr->nSymbolPts;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyLine --
+ *
+ * Release memory and resources allocated for the line element.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the line element is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+#define FreeVector(v) \
+ if ((v).clientId != NULL) { \
+ Blt_FreeVectorId((v).clientId); \
+ } else if ((v).valueArr != NULL) { \
+ Blt_Free((v).valueArr); \
+ }
+
+static void
+DestroyLine(graphPtr, elemPtr)
+ Graph *graphPtr;
+ Element *elemPtr;
+{
+ Line *linePtr = (Line *)elemPtr;
+
+ if (linePtr->normalPenPtr != &(linePtr->builtinPen)) {
+ Blt_FreePen(graphPtr, (Pen *)linePtr->normalPenPtr);
+ }
+ DestroyPen(graphPtr, (Pen *)&(linePtr->builtinPen));
+ if (linePtr->activePenPtr != NULL) {
+ Blt_FreePen(graphPtr, (Pen *)linePtr->activePenPtr);
+ }
+
+ FreeVector(linePtr->w);
+ FreeVector(linePtr->x);
+ FreeVector(linePtr->xHigh);
+ FreeVector(linePtr->xLow);
+ FreeVector(linePtr->xError);
+ FreeVector(linePtr->y);
+ FreeVector(linePtr->yHigh);
+ FreeVector(linePtr->yLow);
+ FreeVector(linePtr->yError);
+
+ ResetLine(linePtr);
+ if (linePtr->palette != NULL) {
+ Blt_FreePalette(graphPtr, linePtr->palette);
+ Blt_ChainDestroy(linePtr->palette);
+ }
+ if (linePtr->tags != NULL) {
+ Blt_Free(linePtr->tags);
+ }
+ if (linePtr->reqActive != NULL) {
+ Blt_Free(linePtr->reqActive);
+ }
+ if (linePtr->fillPts != NULL) {
+ Blt_Free(linePtr->fillPts);
+ }
+ if (linePtr->fillTile != NULL) {
+ Blt_FreeTile(linePtr->fillTile);
+ }
+ if ((linePtr->fillStipple != None) &&
+ (linePtr->fillStipple != PATTERN_SOLID)) {
+ Tk_FreeBitmap(graphPtr->display, linePtr->fillStipple);
+ }
+ if (linePtr->fillGC != NULL) {
+ Tk_FreeGC(graphPtr->display, linePtr->fillGC);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_LineElement --
+ *
+ * Allocate memory and initialize methods for the new line element.
+ *
+ * Results:
+ * The pointer to the newly allocated element structure is returned.
+ *
+ * Side effects:
+ * Memory is allocated for the line element structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static ElementProcs lineProcs =
+{
+ ClosestLine, /* Finds the closest element/data point */
+ ConfigureLine, /* Configures the element. */
+ DestroyLine, /* Destroys the element. */
+ DrawActiveLine, /* Draws active element */
+ DrawNormalLine, /* Draws normal element */
+ DrawSymbol, /* Draws the element symbol. */
+ GetLineExtents, /* Find the extents of the element's data. */
+ ActiveLineToPostScript, /* Prints active element. */
+ NormalLineToPostScript, /* Prints normal element. */
+ SymbolToPostScript, /* Prints the line's symbol. */
+ MapLine /* Compute element's screen coordinates. */
+};
+
+Element *
+Blt_LineElement(graphPtr, name, classUid)
+ Graph *graphPtr;
+ char *name;
+ Tk_Uid classUid;
+{
+ register Line *linePtr;
+
+ linePtr = Blt_Calloc(1, sizeof(Line));
+ assert(linePtr);
+ linePtr->procsPtr = &lineProcs;
+ if (classUid == bltLineElementUid) {
+ linePtr->configSpecs = lineElemConfigSpecs;
+ } else {
+ linePtr->configSpecs = stripElemConfigSpecs;
+ }
+ linePtr->penDir = PEN_BOTH_DIRECTIONS;
+ linePtr->reqSmooth = PEN_SMOOTH_NONE;
+ linePtr->flags = SCALE_SYMBOL;
+ linePtr->normalPenPtr = &(linePtr->builtinPen);
+ linePtr->labelRelief = TK_RELIEF_FLAT;
+ linePtr->fillStipple = None;
+
+ /* By default an element's name and label are the same. */
+ linePtr->label = Blt_Strdup(name);
+ linePtr->name = Blt_Strdup(name);
+ linePtr->graphPtr = graphPtr;
+ linePtr->hidden = FALSE;
+ linePtr->classUid = classUid;
+ InitPen(linePtr->normalPenPtr);
+ linePtr->palette = Blt_ChainCreate();
+ return (Element *)linePtr;
+}