diff options
Diffstat (limited to 'blt/src/bltGrLine.c')
-rw-r--r-- | blt/src/bltGrLine.c | 5091 |
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; +} |