/* * 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 #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. */ Blt_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 errorBarShow; /* Describes which error bars to * display: none, x, y, or * both. */ int errorBarLineWidth; /* Width of the error bar segments. */ int errorBarCapWidth; /* Width of the cap on error bars. */ XColor *errorBarColor; /* Color of the error bar. */ GC errorBarGC; /* 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 { Weight 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 errorBarCapWidth; /* Length of the cap ends on each * error bar. */ 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. */ Blt_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 *activeIndices; /* Array of indices (malloc-ed) that * indicate the data points are active * (drawn with "active" colors). */ int nActiveIndices; /* 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. */ int errorBarCapWidth; /* Length of cap on error bars */ 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. */ int state; /* * 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 *traces; /* 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; extern Tk_CustomOption bltStateOption; #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_STATE "normal" #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_LINE_WIDTH "1" #define DEF_LINE_ERRORBAR_CAP_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.errorBarColor), 0, &bltColorOption}, {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth", DEF_LINE_ERRORBAR_LINE_WIDTH, Tk_Offset(Line, builtinPen.errorBarLineWidth), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_CUSTOM, "-errorbarcap", "errorBarCap", "ErrorBarCap", DEF_LINE_ERRORBAR_CAP_WIDTH, Tk_Offset(Line, builtinPen.errorBarCapWidth), 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), 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.errorBarShow), 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, "-state", "state", "State", DEF_LINE_STATE, Tk_Offset(Line, state), TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption}, {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.errorBarColor), 0, &bltColorOption}, {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth", DEF_LINE_ERRORBAR_LINE_WIDTH, Tk_Offset(Line, builtinPen.errorBarLineWidth), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_CUSTOM, "-errorbarcap", "errorBarCap", "ErrorBarCap", DEF_LINE_ERRORBAR_CAP_WIDTH, Tk_Offset(Line, builtinPen.errorBarCapWidth), 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.errorBarShow), 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, errorBarColor), ALL_PENS, &bltColorOption}, {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth", DEF_LINE_ERRORBAR_LINE_WIDTH, Tk_Offset(LinePen, errorBarLineWidth), ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_CUSTOM, "-errorbarcap", "errorBarCap", "ErrorBarCap", DEF_LINE_ERRORBAR_CAP_WIDTH, Tk_Offset(LinePen, errorBarCapWidth), 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, errorBarShow), 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 */ 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; #ifdef WIN32 static int tkpWinRopModes[] = { R2_BLACK, /* GXclear */ R2_MASKPEN, /* GXand */ R2_MASKPENNOT, /* GXandReverse */ R2_COPYPEN, /* GXcopy */ R2_MASKNOTPEN, /* GXandInverted */ R2_NOT, /* GXnoop */ R2_XORPEN, /* GXxor */ R2_MERGEPEN, /* GXor */ R2_NOTMERGEPEN, /* GXnor */ R2_NOTXORPEN, /* GXequiv */ R2_NOT, /* GXinvert */ R2_MERGEPENNOT, /* GXorReverse */ R2_NOTCOPYPEN, /* GXcopyInverted */ R2_MERGENOTPEN, /* GXorInverted */ R2_NOTMASKPEN, /* GXnand */ R2_WHITE /* GXset */ }; #endif 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; } if (nElems > 2) { Tcl_AppendResult(interp, "too many elements in bitmap list \"", string, "\": should be \"bitmap mask\"", (char *)NULL); result = TCL_ERROR; goto error; } mask = None; 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) || (string[0] == '\0')) { 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; /* Fill GC 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->errorBarColor; if (colorPtr == COLOR_DEFAULT) { colorPtr = lpPtr->traceColor; } gcValues.line_width = LineWidth(lpPtr->errorBarLineWidth); gcValues.foreground = colorPtr->pixel; newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues); if (lpPtr->errorBarGC != NULL) { Tk_FreeGC(graphPtr->display, lpPtr->errorBarGC); } lpPtr->errorBarGC = 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->errorBarGC != NULL) { Tk_FreeGC(graphPtr->display, lpPtr->errorBarGC); } 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->configProc = ConfigurePen; penPtr->configSpecs = linePenConfigSpecs; penPtr->destroyProc = DestroyPen; penPtr->errorBarLineWidth = 1; penPtr->errorBarShow = SHOW_BOTH; penPtr->flags = NORMAL_PEN; penPtr->name = ""; penPtr->symbol.bitmap = penPtr->symbol.mask = None; penPtr->symbol.outlineColor = penPtr->symbol.fillColor = COLOR_DEFAULT; penPtr->symbol.outlineWidth = penPtr->traceWidth = 1; penPtr->symbol.type = SYMBOL_CIRCLE; penPtr->valueShow = SHOW_NONE; } 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 */ if (graphPtr->inverted) { for (i = 0; i < n; i++) { if ((FINITE(x[i])) && (FINITE(y[i]))) { screenPts[count].x = Blt_HMap(graphPtr, linePtr->axes.y, y[i]); screenPts[count].y = Blt_VMap(graphPtr, linePtr->axes.x, x[i]); indices[count] = i; count++; } } } else { for (i = 0; i < n; i++) { if ((FINITE(x[i])) && (FINITE(y[i]))) { screenPts[count].x = Blt_HMap(graphPtr, linePtr->axes.x, x[i]); screenPts[count].y = Blt_VMap(graphPtr, linePtr->axes.y, y[i]); 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; { Extents2D exts; Point2D *origPts, *intpPts; Point2D p, q; double dist; int *indices; int nIntpPts, nOrigPts; int result; register int i, j, count; nOrigPts = mapPtr->nScreenPts; origPts = mapPtr->screenPts; assert(mapPtr->nScreenPts > 0); Blt_GraphExtents(graphPtr, &exts); /* * 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++) { p = origPts[i]; q = origPts[j]; count++; if (Blt_LineRectClip(&exts, &p, &q)) { count += (int)(hypot(q.x - p.x, q.y - p.y) * 0.5); } } nIntpPts = count; intpPts = Blt_Malloc(nIntpPts * sizeof(Point2D)); assert(intpPts); indices = Blt_Malloc(sizeof(int) * nIntpPts); assert(indices); /* * FIXME: This is just plain wrong. The spline should be computed * and evaluated in separate steps. This will mean breaking * up this routine since the catrom coefficients can be * independently computed for original data point. This * also handles the problem of allocating enough points * since evaluation is independent of the number of points * to be evalualted. The interpolated * line segments should be clipped, not the original segments. */ count = 0; for (i = 0, j = 1; j < nOrigPts; i++, j++) { p = origPts[i]; q = origPts[j]; dist = hypot(q.x - p.x, q.y - p.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++; /* 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; 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 */ for (i = 0; i < mapPtr->nScreenPts; i++) { if (PointInRegion(&exts, mapPtr->screenPts[i].x, mapPtr->screenPts[i].y)) { symbolPts[count].x = mapPtr->screenPts[i].x; symbolPts[count].y = mapPtr->screenPts[i].y; indices[count] = mapPtr->indices[i]; count++; } } 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->nActiveIndices); assert(activePts); activeToData = Blt_Malloc(sizeof(int) * linePtr->nActiveIndices); nPoints = NumberOfPoints(linePtr); count = 0; /* Count the visible active points */ for (i = 0; i < linePtr->nActiveIndices; i++) { pointIndex = linePtr->activeIndices[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; stylePtr->errorBarCapWidth = linePtr->errorBarCapWidth; 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->traces == NULL) { linePtr->traces = Blt_ChainCreate(); } Blt_ChainAppend(linePtr->traces, 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->traces); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tracePtr = Blt_ChainGetValue(linkPtr); Blt_Free(tracePtr->symbolToData); Blt_Free(tracePtr->screenPts); Blt_Free(tracePtr); } Blt_ChainDestroy(linePtr->traces); linePtr->traces = 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 size, 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->nActiveIndices > 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); size = ScaleSymbol(elemPtr, stylePtr->penPtr->symbol.size); stylePtr->symbolSize = size; stylePtr->errorBarCapWidth = (stylePtr->penPtr->errorBarCapWidth > 0) ? stylePtr->penPtr->errorBarCapWidth : (int)(size * 0.6666666); stylePtr->errorBarCapWidth /= 2; } 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; 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->traces); 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 count; int i; register Segment2D *s; i = 0; minDist = searchPtr->dist; s = linePtr->strips; for (count = 0; count < linePtr->nStrips; count++, s++) { dist = (*distProc)(searchPtr->x, searchPtr->y, &(s->p), &(s->q), &b); if (dist < minDist) { closest = b; i = linePtr->stripToData[count]; 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; } /* *---------------------------------------------------------------------- * * 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 *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. */ pointPtr = linePtr->symbolPts; for (count = 0; count < linePtr->nSymbolPts; count++, 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; } } 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", "-areapattern", (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, sizeof(XPoint)) #define MAX_DRAWPOLYGON(d) Blt_MaxRequestSize(d, sizeof(XPoint)) #define MAX_DRAWSEGMENTS(d) Blt_MaxRequestSize(d, sizeof(XSegment)) #define MAX_DRAWRECTANGLES(d) Blt_MaxRequestSize(d, sizeof(XRectangle)) #define MAX_DRAWARCS(d) Blt_MaxRequestSize(d, sizeof(XArc)) #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 *points; points = Blt_Malloc(nSymbolPts * sizeof(XPoint)); count = 0; for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts; pointPtr < endPtr; pointPtr++) { points[count].x = (short int)pointPtr->x; points[count].y = (short int)pointPtr->y; count++; } XDrawPoints(graphPtr->display, drawable, penPtr->symbol.fillGC, points, nSymbolPts, CoordModeOrigin); Blt_Free(points); } 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 *p; 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)); p = 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++) { p->x = pattern[n].x + (short int)pointPtr->x; p->y = pattern[n].y + (short int)pointPtr->y; p++; } count++; } linePtr->symbolCounter++; } } else { count = nSymbolPts; for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts; pointPtr < endPtr; pointPtr++) { for (n = 0; n < 13; n++) { p->x = pattern[n].x + (short int)pointPtr->x; p->y = pattern[n].y + (short int)pointPtr->y; p++; } } } if (penPtr->symbol.fillGC != NULL) { for (p = polygon, i = 0; i < count; i++, p += 13) { XFillPolygon(graphPtr->display, drawable, penPtr->symbol.fillGC, p, 13, Complex, CoordModeOrigin); } } if (penPtr->symbol.outlineWidth > 0) { for (p = polygon, i = 0; i < count; i++, p += 13) { XDrawLines(graphPtr->display, drawable, penPtr->symbol.outlineGC, p, 13, CoordModeOrigin); } } Blt_Free(polygon); } break; case SYMBOL_DIAMOND: { XPoint *polygon; register XPoint *p; /* * * 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)); p = 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++, p++) { p->x = pattern[n].x + (short int)pointPtr->x; p->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++, p++) { p->x = pattern[n].x + (short int)pointPtr->x; p->y = pattern[n].y + (short int)pointPtr->y; } } } if (penPtr->symbol.fillGC != NULL) { for (p = polygon, i = 0; i < count; i++, p += 5) { XFillPolygon(graphPtr->display, drawable, penPtr->symbol.fillGC, p, 5, Convex, CoordModeOrigin); } } if (penPtr->symbol.outlineWidth > 0) { for (p = polygon, i = 0; i < count; i++, p += 5) { XDrawLines(graphPtr->display, drawable, penPtr->symbol.outlineGC, p, 5, CoordModeOrigin); } } Blt_Free(polygon); } break; case SYMBOL_TRIANGLE: case SYMBOL_ARROW: { XPoint *polygon; register XPoint *p; 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)); p = 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++) { p->x = pattern[n].x + (short int)pointPtr->x; p->y = pattern[n].y + (short int)pointPtr->y; p++; } count++; } linePtr->symbolCounter++; } } else { count = nSymbolPts; for (pointPtr = symbolPts, endPtr = symbolPts + nSymbolPts; pointPtr < endPtr; pointPtr++) { for (n = 0; n < 4; n++) { p->x = pattern[n].x + (short int)pointPtr->x; p->y = pattern[n].y + (short int)pointPtr->y; p++; } } } if (penPtr->symbol.fillGC != NULL) { for (p = polygon, i = 0; i < count; i++, p += 4) { XFillPolygon(graphPtr->display, drawable, penPtr->symbol.fillGC, p, 4, Convex, CoordModeOrigin); } } if (penPtr->symbol.outlineWidth > 0) { for (p = polygon, i = 0; i < count; i++, p += 4) { XDrawLines(graphPtr->display, drawable, penPtr->symbol.outlineGC, p, 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; HBRUSH brush, oldBrush; HDC dc; HPEN pen, oldPen; POINT *points; TkWinDCState state; Trace *tracePtr; int j; int nPoints, remaining; register POINT *p; register int count; /* * Depending if the line is wide (> 1 pixel), arbitrarily break * the line in sections of 100 points. This bit of weirdness has * to do with wide geometric pens. The longer the polyline, the * slower it draws. The trade off is that we lose dash and cap * uniformity for unbearably slow polyline draws. */ if (penPtr->traceGC->line_width > 1) { nPoints = 100; } else { nPoints = Blt_MaxRequestSize(graphPtr->display, sizeof(POINT)) - 1; } points = Blt_Malloc((nPoints + 1) * sizeof(POINT)); dc = TkWinGetDrawableDC(graphPtr->display, drawable, &state); pen = Blt_GCToPen(dc, penPtr->traceGC); oldPen = SelectPen(dc, pen); brush = CreateSolidBrush(penPtr->traceGC->foreground); oldBrush = SelectBrush(dc, brush); SetROP2(dc, tkpWinRopModes[penPtr->traceGC->function]); for (linkPtr = Blt_ChainFirstLink(linePtr->traces); 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. */ /* Step 1. Convert and draw the first section of the trace. * It may contain the entire trace. */ for (p = points, count = 0; count < MIN(nPoints, tracePtr->nScreenPts); count++, p++) { p->x = (int)tracePtr->screenPts[count].x; p->y = (int)tracePtr->screenPts[count].y; } Polyline(dc, points, count); /* Step 2. Next handle any full-size chunks left. */ while ((count + nPoints) < tracePtr->nScreenPts) { /* Start with the last point of the previous trace. */ points[0].x = points[nPoints - 1].x; points[0].y = points[nPoints - 1].y; for (p = points + 1, j = 0; j < nPoints; j++, count++, p++) { p->x = (int)tracePtr->screenPts[count].x; p->y = (int)tracePtr->screenPts[count].y; } Polyline(dc, points, nPoints + 1); } /* Step 3. Convert and draw the remaining points. */ remaining = tracePtr->nScreenPts - count; if (remaining > 0) { /* Start with the last point of the previous trace. */ points[0].x = points[nPoints - 1].x; points[0].y = points[nPoints - 1].y; for (p = points + 1; count < tracePtr->nScreenPts; count++, p++) { p->x = (int)tracePtr->screenPts[count].x; p->y = (int)tracePtr->screenPts[count].y; } Polyline(dc, points, remaining + 1); } } Blt_Free(points); DeletePen(SelectPen(dc, oldPen)); DeleteBrush(SelectBrush(dc, oldBrush)); TkWinReleaseDrawableDC(drawable, dc, &state); } #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; XPoint *points; int j; int nPoints, remaining; register XPoint *p; register int count; nPoints = Blt_MaxRequestSize(graphPtr->display, sizeof(XPoint)) - 1; points = Blt_Malloc((nPoints + 1) * sizeof(XPoint)); for (linkPtr = Blt_ChainFirstLink(linePtr->traces); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { int n; 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. */ /* Step 1. Convert and draw the first section of the trace. * It may contain the entire trace. */ n = MIN(nPoints, tracePtr->nScreenPts); for (p = points, count = 0; count < n; count++, p++) { p->x = (short int)tracePtr->screenPts[count].x; p->y = (short int)tracePtr->screenPts[count].y; } XDrawLines(graphPtr->display, drawable, penPtr->traceGC, points, count, CoordModeOrigin); /* Step 2. Next handle any full-size chunks left. */ while ((count + nPoints) < tracePtr->nScreenPts) { /* Start with the last point of the previous trace. */ points[0].x = points[nPoints - 1].x; points[0].y = points[nPoints - 1].y; for (p = points + 1, j = 0; j < nPoints; j++, count++, p++) { p->x = (short int)tracePtr->screenPts[count].x; p->y = (short int)tracePtr->screenPts[count].y; } XDrawLines(graphPtr->display, drawable, penPtr->traceGC, points, nPoints + 1, CoordModeOrigin); } /* Step 3. Convert and draw the remaining points. */ remaining = tracePtr->nScreenPts - count; if (remaining > 0) { /* Start with the last point of the previous trace. */ points[0].x = points[nPoints - 1].x; points[0].y = points[nPoints - 1].y; for (p = points + 1; count < tracePtr->nScreenPts; count++, p++) { p->x = (short int)tracePtr->screenPts[count].x; p->y = (short int)tracePtr->screenPts[count].y; } XDrawLines(graphPtr->display, drawable, penPtr->traceGC, points, remaining + 1, CoordModeOrigin); } } Blt_Free(points); } #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); /* * nActiveIndices * > 0 Some points are active. Uses activeArr. * < 0 All points are active. * == 0 No points are active. */ if (linePtr->nActiveIndices > 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->nActiveIndices < 0) { if (penPtr->traceWidth > 0) { if (linePtr->nStrips > 0) { Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->traceGC, linePtr->strips, linePtr->nStrips); } else if (Blt_ChainGetLength(linePtr->traces) > 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 *points; Point2D *endPtr, *pointPtr; points = Blt_Malloc(sizeof(XPoint) * linePtr->nFillPts); count = 0; for(pointPtr = linePtr->fillPts, endPtr = linePtr->fillPts + linePtr->nFillPts; pointPtr < endPtr; pointPtr++) { points[count].x = (short int)pointPtr->x; points[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, points, linePtr->nFillPts); } else if (linePtr->fillStipple != None) { XFillPolygon(graphPtr->display, drawable, linePtr->fillGC, points, linePtr->nFillPts, Complex, CoordModeOrigin); } Blt_Free(points); } /* 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->errorBarLineWidth > 0)) { Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->traceGC, stylePtr->strips, stylePtr->nStrips); } } } else if ((Blt_ChainGetLength(linePtr->traces) > 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->errorBarShow & SHOW_X)) { Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC, stylePtr->xErrorBars, stylePtr->xErrorBarCnt); } if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_Y)) { Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC, 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: 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); } 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->traces); 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->nActiveIndices > 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->nActiveIndices < 0) { if (penPtr->traceWidth > 0) { if (linePtr->nStrips > 0) { SetLineAttributes(psToken, penPtr); Blt_2DSegmentsToPostScript(psToken, linePtr->strips, linePtr->nStrips); } if (Blt_ChainGetLength(linePtr->traces) > 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_2DSegmentsToPostScript(psToken, stylePtr->strips, stylePtr->nStrips); } } } else if ((Blt_ChainGetLength(linePtr->traces) > 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->errorBarColor; if (colorPtr == COLOR_DEFAULT) { colorPtr = penPtr->traceColor; } if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_X)) { Blt_LineAttributesToPostScript(psToken, colorPtr, penPtr->errorBarLineWidth, NULL, CapButt, JoinMiter); Blt_2DSegmentsToPostScript(psToken, stylePtr->xErrorBars, stylePtr->xErrorBarCnt); } if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_Y)) { Blt_LineAttributesToPostScript(psToken, colorPtr, penPtr->errorBarLineWidth, NULL, CapButt, JoinMiter); Blt_2DSegmentsToPostScript(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->activeIndices != NULL) { Blt_Free(linePtr->activeIndices); } 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; Blt_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; } /* By default an element's name and label are the same. */ linePtr->label = Blt_Strdup(name); linePtr->name = Blt_Strdup(name); linePtr->classUid = classUid; linePtr->flags = SCALE_SYMBOL; linePtr->graphPtr = graphPtr; linePtr->labelRelief = TK_RELIEF_FLAT; linePtr->normalPenPtr = &linePtr->builtinPen; linePtr->palette = Blt_ChainCreate(); linePtr->penDir = PEN_BOTH_DIRECTIONS; linePtr->reqSmooth = PEN_SMOOTH_NONE; InitPen(linePtr->normalPenPtr); return (Element *)linePtr; }