/* * bltGrMarker.c -- * * This module implements markers 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 "bltGrElem.h" #define GETBITMAP(b) \ (((b)->destBitmap == None) ? (b)->srcBitmap : (b)->destBitmap) #define MAX_OUTLINE_POINTS 12 /* Map graph coordinates to normalized coordinates [0..1] */ #define NORMALIZE(A,x) (((x) - (A)->axisRange.min) / (A)->axisRange.range) #define DEF_MARKER_ANCHOR "center" #define DEF_MARKER_BACKGROUND RGB_WHITE #define DEF_MARKER_BG_MONO RGB_WHITE #define DEF_MARKER_BITMAP (char *)NULL #define DEF_MARKER_CAP_STYLE "butt" #define DEF_MARKER_COORDS (char *)NULL #define DEF_MARKER_DASHES (char *)NULL #define DEF_MARKER_DASH_OFFSET "0" #define DEF_MARKER_ELEMENT (char *)NULL #define DEF_MARKER_FOREGROUND RGB_BLACK #define DEF_MARKER_FG_MONO RGB_BLACK #define DEF_MARKER_FILL_COLOR RGB_RED #define DEF_MARKER_FILL_MONO RGB_WHITE #define DEF_MARKER_FONT STD_FONT #define DEF_MARKER_GAP_COLOR RGB_PINK #define DEF_MARKER_GAP_MONO RGB_BLACK #define DEF_MARKER_HEIGHT "0" #define DEF_MARKER_HIDE "no" #define DEF_MARKER_JOIN_STYLE "miter" #define DEF_MARKER_JUSTIFY "left" #define DEF_MARKER_LINE_WIDTH "1" #define DEF_MARKER_MAP_X "x" #define DEF_MARKER_MAP_Y "y" #define DEF_MARKER_NAME (char *)NULL #define DEF_MARKER_OUTLINE_COLOR RGB_BLACK #define DEF_MARKER_OUTLINE_MONO RGB_BLACK #define DEF_MARKER_PAD "4" #define DEF_MARKER_ROTATE "0.0" #define DEF_MARKER_SCALE "1.0" #define DEF_MARKER_SHADOW_COLOR (char *)NULL #define DEF_MARKER_SHADOW_MONO (char *)NULL #define DEF_MARKER_STATE "normal" #define DEF_MARKER_STIPPLE (char *)NULL #define DEF_MARKER_TEXT (char *)NULL #define DEF_MARKER_UNDER "no" #define DEF_MARKER_WIDTH "0" #define DEF_MARKER_WINDOW (char *)NULL #define DEF_MARKER_XOR "no" #define DEF_MARKER_X_OFFSET "0" #define DEF_MARKER_Y_OFFSET "0" #define DEF_MARKER_TEXT_TAGS "Text all" #define DEF_MARKER_IMAGE_TAGS "Image all" #define DEF_MARKER_BITMAP_TAGS "Bitmap all" #define DEF_MARKER_WINDOW_TAGS "Window all" #define DEF_MARKER_POLYGON_TAGS "Polygon all" #define DEF_MARKER_LINE_TAGS "Line all" static Tk_OptionParseProc StringToCoordinates; static Tk_OptionPrintProc CoordinatesToString; static Tk_CustomOption coordsOption = { StringToCoordinates, CoordinatesToString, (ClientData)0 }; extern Tk_CustomOption bltColorPairOption; extern Tk_CustomOption bltDashesOption; extern Tk_CustomOption bltDistanceOption; extern Tk_CustomOption bltListOption; extern Tk_CustomOption bltPadOption; extern Tk_CustomOption bltPositiveDistanceOption; extern Tk_CustomOption bltShadowOption; extern Tk_CustomOption bltStateOption; extern Tk_CustomOption bltXAxisOption; extern Tk_CustomOption bltYAxisOption; typedef Marker *(MarkerCreateProc) _ANSI_ARGS_((void)); typedef void (MarkerDrawProc) _ANSI_ARGS_((Marker *markerPtr, Drawable drawable)); typedef void (MarkerFreeProc) _ANSI_ARGS_((Graph *graphPtr, Marker *markerPtr)); typedef int (MarkerConfigProc) _ANSI_ARGS_((Marker *markerPtr)); typedef void (MarkerMapProc) _ANSI_ARGS_((Marker *markerPtr)); typedef void (MarkerPostScriptProc) _ANSI_ARGS_((Marker *markerPtr, PsToken psToken)); typedef int (MarkerPointProc) _ANSI_ARGS_((Marker *markerPtr, Point2D *samplePtr)); typedef int (MarkerRegionProc) _ANSI_ARGS_((Marker *markerPtr, Extents2D *extsPtr, int enclosed)); typedef struct { Tk_ConfigSpec *configSpecs; /* Marker configuration specifications */ MarkerConfigProc *configProc; MarkerDrawProc *drawProc; MarkerFreeProc *freeProc; MarkerMapProc *mapProc; MarkerPointProc *pointProc; MarkerRegionProc *regionProc; MarkerPostScriptProc *postscriptProc; } MarkerClass; /* * ------------------------------------------------------------------- * * Marker -- * * Structure defining the generic marker. In C++ parlance this * would be the base type from which all markers are derived. * * This structure corresponds with the specific types of markers. * Don't change this structure without changing the individual * marker structures of each type below. * * ------------------------------------------------------------------- */ struct MarkerStruct { char *name; /* Identifier for marker in list */ Blt_Uid classUid; /* Type of marker. */ Graph *graphPtr; /* Graph widget of marker. */ unsigned int flags; char **tags; int hidden; /* If non-zero, don't display the marker. */ Blt_HashEntry *hashPtr; Blt_ChainLink *linkPtr; Point2D *worldPts; /* Coordinate array to position marker */ int nWorldPts; /* Number of points in above array */ char *elemName; /* Element associated with marker */ Axis2D axes; int drawUnder; /* If non-zero, draw the marker * underneath any elements. This can * be a performance penalty because * the graph must be redraw entirely * each time the marker is redrawn. */ int clipped; /* Indicates if the marker is totally * clipped by the plotting area. */ int xOffset, yOffset; /* Pixel offset from graph position */ MarkerClass *classPtr; int state; }; /* * ------------------------------------------------------------------- * * TextMarker -- * * ------------------------------------------------------------------- */ typedef struct { char *name; /* Identifier for marker */ Blt_Uid classUid; /* Type of marker */ Graph *graphPtr; /* The graph this marker belongs to */ unsigned int flags; char **tags; int hidden; /* If non-zero, don't display the * marker. */ Blt_HashEntry *hashPtr; Blt_ChainLink *linkPtr; Point2D *worldPts; /* Position of marker (1 X-Y coordinate) in * world (graph) coordinates. */ int nWorldPts; /* Number of points */ char *elemName; /* Element associated with marker */ Axis2D axes; int drawUnder; /* If non-zero, draw the marker * underneath any elements. There can * be a performance because the graph * must be redraw entirely each time * this marker is redrawn. */ int clipped; /* Indicates if the marker is totally * clipped by the plotting area. */ int xOffset, yOffset; /* pixel offset from anchor */ MarkerClass *classPtr; int state; /* * Text specific fields and attributes */ #ifdef notdef char *textVarName; /* Name of variable (malloc'ed) or * NULL. If non-NULL, graph displays * the contents of this variable. */ #endif char *string; /* Text string to be display. The string * make contain newlines. */ Tk_Anchor anchor; /* Indicates how to translate the given * marker position. */ Point2D anchorPos; /* Translated anchor point. */ int width, height; /* Dimension of bounding box. */ TextStyle style; /* Text attributes (font, fg, anchor, etc) */ TextLayout *textPtr; /* Contains information about the layout * of the text. */ Point2D outline[5]; XColor *fillColor; GC fillGC; } TextMarker; static Tk_ConfigSpec textConfigSpecs[] = { {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_MARKER_ANCHOR, Tk_Offset(TextMarker, anchor), 0}, {TK_CONFIG_COLOR, "-background", "background", "MarkerBackground", (char *)NULL, Tk_Offset(TextMarker, fillColor), TK_CONFIG_NULL_OK}, {TK_CONFIG_SYNONYM, "-bg", "background", "Background", (char *)NULL, 0, 0}, {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags", DEF_MARKER_TEXT_TAGS, Tk_Offset(Marker, tags), TK_CONFIG_NULL_OK, &bltListOption}, {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts), TK_CONFIG_NULL_OK, &coordsOption}, {TK_CONFIG_STRING, "-element", "element", "Element", DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK}, {TK_CONFIG_SYNONYM, "-fg", "foreground", "Foreground", (char *)NULL, 0, 0}, {TK_CONFIG_SYNONYM, "-fill", "background", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_FONT, "-font", "font", "Font", DEF_MARKER_FONT, Tk_Offset(TextMarker, style.font), 0}, {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", DEF_MARKER_FOREGROUND, Tk_Offset(TextMarker, style.color), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", DEF_MARKER_FG_MONO, Tk_Offset(TextMarker, style.color), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify", DEF_MARKER_JUSTIFY, Tk_Offset(TextMarker, style.justify), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide", DEF_MARKER_HIDE, Tk_Offset(Marker, hidden), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption}, {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption}, {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK}, {TK_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_CUSTOM, "-padx", "padX", "PadX", DEF_MARKER_PAD, Tk_Offset(TextMarker, style.padX), TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption}, {TK_CONFIG_CUSTOM, "-pady", "padY", "PadY", DEF_MARKER_PAD, Tk_Offset(TextMarker, style.padY), TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption}, {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate", DEF_MARKER_ROTATE, Tk_Offset(TextMarker, style.theta), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow", DEF_MARKER_SHADOW_COLOR, Tk_Offset(TextMarker, style.shadow), TK_CONFIG_COLOR_ONLY, &bltShadowOption}, {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow", DEF_MARKER_SHADOW_MONO, Tk_Offset(TextMarker, style.shadow), TK_CONFIG_MONO_ONLY, &bltShadowOption}, {TK_CONFIG_CUSTOM, "-state", "state", "State", DEF_MARKER_STATE, Tk_Offset(Marker, state), TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption}, {TK_CONFIG_STRING, "-text", "text", "Text", DEF_MARKER_TEXT, Tk_Offset(TextMarker, string), TK_CONFIG_NULL_OK}, {TK_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; /* * ------------------------------------------------------------------- * * WindowMarker -- * * ------------------------------------------------------------------- */ typedef struct { char *name; /* Identifier for marker */ Blt_Uid classUid; /* Type of marker */ Graph *graphPtr; /* Graph marker belongs to */ unsigned int flags; char **tags; int hidden; /* Indicates if the marker is * currently hidden or not. */ Blt_HashEntry *hashPtr; Blt_ChainLink *linkPtr; Point2D *worldPts; /* Position of marker (1 X-Y coordinate) in * world (graph) coordinates. */ int nWorldPts; /* Number of points */ char *elemName; /* Element associated with marker */ Axis2D axes; int drawUnder; /* If non-zero, draw the marker * underneath any elements. There can * be a performance because the graph * must be redraw entirely each time * this marker is redrawn. */ int clipped; /* Indicates if the marker is totally * clipped by the plotting area. */ int xOffset, yOffset; /* Pixel offset from anchor. */ MarkerClass *classPtr; int state; /* * Window specific attributes */ char *pathName; /* Name of child widget to be displayed. */ Tk_Window tkwin; /* Window to display. */ int reqWidth, reqHeight; /* If non-zero, this overrides the size * requested by the child widget. */ Tk_Anchor anchor; /* Indicates how to translate the given * marker position. */ Point2D anchorPos; /* Translated anchor point. */ int width, height; /* Current size of the child window. */ } WindowMarker; static Tk_ConfigSpec windowConfigSpecs[] = { {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_MARKER_ANCHOR, Tk_Offset(WindowMarker, anchor), 0}, {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags", DEF_MARKER_WINDOW_TAGS, Tk_Offset(Marker, tags), TK_CONFIG_NULL_OK, &bltListOption}, {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS, Tk_Offset(WindowMarker, worldPts), TK_CONFIG_NULL_OK, &coordsOption}, {TK_CONFIG_STRING, "-element", "element", "Element", DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-height", "height", "Height", DEF_MARKER_HEIGHT, Tk_Offset(WindowMarker, reqHeight), TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption}, {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide", DEF_MARKER_HIDE, Tk_Offset(Marker, hidden), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption}, {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption}, {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-state", "state", "State", DEF_MARKER_STATE, Tk_Offset(Marker, state), TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption}, {TK_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-width", "width", "Width", DEF_MARKER_WIDTH, Tk_Offset(WindowMarker, reqWidth), TK_CONFIG_DONT_SET_DEFAULT, &bltPositiveDistanceOption}, {TK_CONFIG_STRING, "-window", "window", "Window", DEF_MARKER_WINDOW, Tk_Offset(WindowMarker, pathName), TK_CONFIG_NULL_OK}, {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; /* * ------------------------------------------------------------------- * * BitmapMarker -- * * ------------------------------------------------------------------- */ typedef struct { char *name; /* Identifier for marker */ Blt_Uid classUid; /* Type of marker */ Graph *graphPtr; /* Graph marker belongs to */ unsigned int flags; char **tags; int hidden; /* Indicates if the marker is currently * hidden or not. */ Blt_HashEntry *hashPtr; Blt_ChainLink *linkPtr; Point2D *worldPts; /* Position of marker in world (graph) * coordinates. If 2 pairs of X-Y * coordinates are specified, then the * bitmap is resized to fit this area. * Otherwise if 1 pair, then the bitmap * is positioned at the coordinate at its * normal size. */ int nWorldPts; /* Number of points */ char *elemName; /* Element associated with marker */ Axis2D axes; int drawUnder; /* If non-zero, draw the marker * underneath any elements. There can * be a performance because the graph * must be redraw entirely each time * this marker is redrawn. */ int clipped; /* Indicates if the marker is totally * clipped by the plotting area. */ int xOffset, yOffset; /* Pixel offset from origin of bitmap */ MarkerClass *classPtr; int state; /* Bitmap specific attributes */ Pixmap srcBitmap; /* Original bitmap. May be further * scaled or rotated. */ double rotate; /* Requested rotation of the bitmap */ double theta; /* Normalized rotation (0..360 * degrees) */ Tk_Anchor anchor; /* If only one X-Y coordinate is * given, indicates how to translate * the given marker position. Otherwise, * if there are two X-Y coordinates, then * this value is ignored. */ Point2D anchorPos; /* Translated anchor point. */ XColor *outlineColor; /* Foreground color */ XColor *fillColor; /* Background color */ GC gc; /* Private graphic context */ GC fillGC; /* Shared graphic context */ Pixmap destBitmap; /* Bitmap to be drawn. */ int destWidth, destHeight; /* Dimensions of the final bitmap */ Point2D outline[MAX_OUTLINE_POINTS]; /* Polygon representing the background * of the bitmap. */ int nOutlinePts; } BitmapMarker; static Tk_ConfigSpec bitmapConfigSpecs[] = { {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_MARKER_ANCHOR, Tk_Offset(BitmapMarker, anchor), 0}, {TK_CONFIG_COLOR, "-background", "background", "Background", DEF_MARKER_BACKGROUND, Tk_Offset(BitmapMarker, fillColor), TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK}, {TK_CONFIG_COLOR, "-background", "background", "Background", DEF_MARKER_BG_MONO, Tk_Offset(BitmapMarker, fillColor), TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK}, {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags", DEF_MARKER_BITMAP_TAGS, Tk_Offset(Marker, tags), TK_CONFIG_NULL_OK, &bltListOption}, {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap", DEF_MARKER_BITMAP, Tk_Offset(BitmapMarker, srcBitmap), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts), TK_CONFIG_NULL_OK, &coordsOption}, {TK_CONFIG_STRING, "-element", "element", "Element", DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK}, {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_SYNONYM, "-fill", "background", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", DEF_MARKER_FOREGROUND, Tk_Offset(BitmapMarker, outlineColor), TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK}, {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", DEF_MARKER_FG_MONO, Tk_Offset(BitmapMarker, outlineColor), TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK}, {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide", DEF_MARKER_HIDE, Tk_Offset(Marker, hidden), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption}, {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption}, {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK}, {TK_CONFIG_SYNONYM, "-outline", "foreground", (char *)NULL, (char *)NULL, 0, 0}, {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate", DEF_MARKER_ROTATE, Tk_Offset(BitmapMarker, rotate), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-state", "state", "State", DEF_MARKER_STATE, Tk_Offset(Marker, state), TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption}, {TK_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; /* * ------------------------------------------------------------------- * * ImageMarker -- * * ------------------------------------------------------------------- */ typedef struct { char *name; /* Identifier for marker */ Blt_Uid classUid; /* Type of marker */ Graph *graphPtr; /* Graph marker belongs to */ unsigned int flags; char **tags; int hidden; /* Indicates if the marker is * currently hidden or not. */ Blt_HashEntry *hashPtr; Blt_ChainLink *linkPtr; Point2D *worldPts; /* Position of marker in world (graph) * coordinates. If 2 pairs of X-Y * coordinates are specified, then the * image is resized to fit this area. * Otherwise if 1 pair, then the image * is positioned at the coordinate at * its normal size. */ int nWorldPts; /* Number of points */ char *elemName; /* Element associated with marker */ Axis2D axes; int drawUnder; /* If non-zero, draw the marker * underneath any elements. There can * be a performance because the graph * must be redraw entirely each time * this marker is redrawn. */ int clipped; /* Indicates if the marker is totally * clipped by the plotting area. */ int xOffset, yOffset; /* Pixel offset from anchor */ MarkerClass *classPtr; int state; /* Image specific attributes */ char *imageName; /* Name of image to be displayed. */ Tk_Image tkImage; /* Tk image to be displayed. */ Tk_Anchor anchor; /* Indicates how to translate the given * marker position. */ Point2D anchorPos; /* Translated anchor point. */ int width, height; /* Dimensions of the image */ Tk_Image tmpImage; Pixmap pixmap; /* Pixmap containing the scaled image */ ColorTable colorTable; /* Pointer to color table */ Blt_ColorImage srcImage; GC gc; } ImageMarker; static Tk_ConfigSpec imageConfigSpecs[] = { {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_MARKER_ANCHOR, Tk_Offset(ImageMarker, anchor), 0}, {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags", DEF_MARKER_IMAGE_TAGS, Tk_Offset(Marker, tags), TK_CONFIG_NULL_OK, &bltListOption}, {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts), TK_CONFIG_NULL_OK, &coordsOption}, {TK_CONFIG_STRING, "-element", "element", "Element", DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK}, {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide", DEF_MARKER_HIDE, Tk_Offset(Marker, hidden), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_STRING, "-image", "image", "Image", (char *)NULL, Tk_Offset(ImageMarker, imageName), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption}, {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption}, {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-state", "state", "State", DEF_MARKER_STATE, Tk_Offset(Marker, state), TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption}, {TK_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; /* * ------------------------------------------------------------------- * * LineMarker -- * * ------------------------------------------------------------------- */ typedef struct { char *name; /* Identifier for marker */ Blt_Uid classUid; /* Type is "linemarker" */ Graph *graphPtr; /* Graph marker belongs to */ unsigned int flags; char **tags; int hidden; /* Indicates if the marker is currently * hidden or not. */ Blt_HashEntry *hashPtr; Blt_ChainLink *linkPtr; Point2D *worldPts; /* Position of marker (X-Y coordinates) in * world (graph) coordinates. */ int nWorldPts; /* Number of points */ char *elemName; /* Element associated with marker */ Axis2D axes; int drawUnder; /* If non-zero, draw the marker * underneath any elements. There can * be a performance because the graph * must be redraw entirely each time * this marker is redrawn. */ int clipped; /* Indicates if the marker is totally * clipped by the plotting area. */ int xOffset, yOffset; /* Pixel offset */ MarkerClass *classPtr; int state; /* Line specific attributes */ XColor *fillColor; XColor *outlineColor; /* Foreground and background colors */ int lineWidth; /* Line width. */ int capStyle; /* Cap style. */ int joinStyle; /* Join style.*/ Blt_Dashes dashes; /* Dash list values (max 11) */ GC gc; /* Private graphic context */ Segment2D *segments; /* Malloc'ed array of points. * Represents individual line segments * (2 points per segment) comprising * the mapped line. The segments may * not necessarily be connected after * clipping. */ int nSegments; /* # segments in the above array. */ int xor; int xorState; /* State of the XOR drawing. Indicates * if the marker is currently drawn. */ } LineMarker; static Tk_ConfigSpec lineConfigSpecs[] = { {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags", DEF_MARKER_LINE_TAGS, Tk_Offset(Marker, tags), TK_CONFIG_NULL_OK, &bltListOption}, {TK_CONFIG_CAP_STYLE, "-cap", "cap", "Cap", DEF_MARKER_CAP_STYLE, Tk_Offset(LineMarker, capStyle), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts), TK_CONFIG_NULL_OK, &coordsOption}, {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes", DEF_MARKER_DASHES, Tk_Offset(LineMarker, dashes), TK_CONFIG_NULL_OK, &bltDashesOption}, {TK_CONFIG_CUSTOM, "-dashoffset", "dashOffset", "DashOffset", DEF_MARKER_DASH_OFFSET, Tk_Offset(LineMarker, dashes.offset), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_STRING, "-element", "element", "Element", DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK}, {TK_CONFIG_COLOR, "-fill", "fill", "Fill", (char *)NULL, Tk_Offset(LineMarker, fillColor), TK_CONFIG_NULL_OK}, {TK_CONFIG_JOIN_STYLE, "-join", "join", "Join", DEF_MARKER_JOIN_STYLE, Tk_Offset(LineMarker, joinStyle), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth", DEF_MARKER_LINE_WIDTH, Tk_Offset(LineMarker, lineWidth), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide", DEF_MARKER_HIDE, Tk_Offset(Marker, hidden), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption}, {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption}, {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK}, {TK_CONFIG_COLOR, "-outline", "outline", "Outline", DEF_MARKER_OUTLINE_COLOR, Tk_Offset(LineMarker, outlineColor), TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK}, {TK_CONFIG_COLOR, "-outline", "outline", "Outline", DEF_MARKER_OUTLINE_MONO, Tk_Offset(LineMarker, outlineColor), TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-state", "state", "State", DEF_MARKER_STATE, Tk_Offset(Marker, state), TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption}, {TK_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_BOOLEAN, "-xor", "xor", "Xor", DEF_MARKER_XOR, Tk_Offset(LineMarker, xor), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; /* * ------------------------------------------------------------------- * * PolygonMarker -- * * ------------------------------------------------------------------- */ typedef struct { char *name; /* Identifier for marker */ Blt_Uid classUid; /* Type of marker */ Graph *graphPtr; /* Graph marker belongs to */ unsigned int flags; char **tags; int hidden; /* Indicates if the marker is currently * hidden or not. */ Blt_HashEntry *hashPtr; Blt_ChainLink *linkPtr; Point2D *worldPts; /* Position of marker (X-Y coordinates) in * world (graph) coordinates. */ int nWorldPts; /* Number of points */ char *elemName; /* Element associated with marker */ Axis2D axes; int drawUnder; /* If non-zero, draw the marker * underneath any elements. There can * be a performance because the graph * must be redraw entirely each time * this marker is redrawn. */ int clipped; /* Indicates if the marker is totally * clipped by the plotting area. */ int xOffset, yOffset; /* Pixel offset */ MarkerClass *classPtr; int state; /* Polygon specific attributes and fields */ Point2D *screenPts; /* Array of points representing the * polygon in screen coordinates. It's * not used for drawing, but to * generate the outlinePts and fillPts * arrays that are the coordinates of * the possibly clipped outline and * filled polygon. */ ColorPair outline; ColorPair fill; Pixmap stipple; /* Stipple pattern to fill the polygon. */ int lineWidth; /* Width of polygon outline. */ int capStyle; int joinStyle; Blt_Dashes dashes; /* List of dash values. Indicates how * draw the dashed line. If no dash * values are provided, or the first value * is zero, then the line is drawn solid. */ GC outlineGC; /* Graphics context to draw the outline of * the polygon. */ GC fillGC; /* Graphics context to draw the filled * polygon. */ Point2D *fillPts; /* Malloc'ed array of points used to draw * the filled polygon. These points may * form a degenerate polygon after clipping. */ int nFillPts; /* # points in the above array. */ Segment2D *outlinePts; /* Malloc'ed array of points. * Represents individual line segments * (2 points per segment) comprising * the outline of the polygon. The * segments may not necessarily be * closed or connected after clipping. */ int nOutlinePts; /* # points in the above array. */ int xor; int xorState; /* State of the XOR drawing. Indicates * if the marker is visible. We have * to drawn it again to erase it. */ } PolygonMarker; static Tk_ConfigSpec polygonConfigSpecs[] = { {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags", DEF_MARKER_POLYGON_TAGS, Tk_Offset(Marker, tags), TK_CONFIG_NULL_OK, &bltListOption}, {TK_CONFIG_CAP_STYLE, "-cap", "cap", "Cap", DEF_MARKER_CAP_STYLE, Tk_Offset(PolygonMarker, capStyle), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-coords", "coords", "Coords", DEF_MARKER_COORDS, Tk_Offset(Marker, worldPts), TK_CONFIG_NULL_OK, &coordsOption}, {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes", DEF_MARKER_DASHES, Tk_Offset(PolygonMarker, dashes), TK_CONFIG_NULL_OK, &bltDashesOption}, {TK_CONFIG_STRING, "-element", "element", "Element", DEF_MARKER_ELEMENT, Tk_Offset(Marker, elemName), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill", DEF_MARKER_FILL_COLOR, Tk_Offset(PolygonMarker, fill), TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption}, {TK_CONFIG_CUSTOM, "-fill", "fill", "Fill", DEF_MARKER_FILL_MONO, Tk_Offset(PolygonMarker, fill), TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption}, {TK_CONFIG_JOIN_STYLE, "-join", "join", "Join", DEF_MARKER_JOIN_STYLE, Tk_Offset(PolygonMarker, joinStyle), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-linewidth", "lineWidth", "LineWidth", DEF_MARKER_LINE_WIDTH, Tk_Offset(PolygonMarker, lineWidth), TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption}, {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide", DEF_MARKER_HIDE, Tk_Offset(Marker, hidden), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX", DEF_MARKER_MAP_X, Tk_Offset(Marker, axes.x), 0, &bltXAxisOption}, {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY", DEF_MARKER_MAP_Y, Tk_Offset(Marker, axes.y), 0, &bltYAxisOption}, {TK_CONFIG_STRING, "-name", (char *)NULL, (char *)NULL, DEF_MARKER_NAME, Tk_Offset(Marker, name), TK_CONFIG_NULL_OK}, {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline", DEF_MARKER_OUTLINE_COLOR, Tk_Offset(PolygonMarker, outline), TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption}, {TK_CONFIG_CUSTOM, "-outline", "outline", "Outline", DEF_MARKER_OUTLINE_MONO, Tk_Offset(PolygonMarker, outline), TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK, &bltColorPairOption}, {TK_CONFIG_CUSTOM, "-state", "state", "State", DEF_MARKER_STATE, Tk_Offset(Marker, state), TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption}, {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple", DEF_MARKER_STIPPLE, Tk_Offset(PolygonMarker, stipple), TK_CONFIG_NULL_OK}, {TK_CONFIG_BOOLEAN, "-under", "under", "Under", DEF_MARKER_UNDER, Tk_Offset(Marker, drawUnder), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset", DEF_MARKER_X_OFFSET, Tk_Offset(Marker, xOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_BOOLEAN, "-xor", "xor", "Xor", DEF_MARKER_XOR, Tk_Offset(PolygonMarker, xor), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset", DEF_MARKER_Y_OFFSET, Tk_Offset(Marker, yOffset), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; static MarkerCreateProc CreateBitmapMarker, CreateLineMarker, CreateImageMarker, CreatePolygonMarker, CreateTextMarker, CreateWindowMarker; static MarkerDrawProc DrawBitmapMarker, DrawLineMarker, DrawImageMarker, DrawPolygonMarker, DrawTextMarker, DrawWindowMarker; static MarkerFreeProc FreeBitmapMarker, FreeLineMarker, FreeImageMarker, FreePolygonMarker, FreeTextMarker, FreeWindowMarker; static MarkerConfigProc ConfigureBitmapMarker, ConfigureLineMarker, ConfigureImageMarker, ConfigurePolygonMarker, ConfigureTextMarker, ConfigureWindowMarker; static MarkerMapProc MapBitmapMarker, MapLineMarker, MapImageMarker, MapPolygonMarker, MapTextMarker, MapWindowMarker; static MarkerPostScriptProc BitmapMarkerToPostScript, LineMarkerToPostScript, ImageMarkerToPostScript, PolygonMarkerToPostScript, TextMarkerToPostScript, WindowMarkerToPostScript; static MarkerPointProc PointInBitmapMarker, PointInLineMarker, PointInImageMarker, PointInPolygonMarker, PointInTextMarker, PointInWindowMarker; static MarkerRegionProc RegionInBitmapMarker, RegionInLineMarker, RegionInImageMarker, RegionInPolygonMarker, RegionInTextMarker, RegionInWindowMarker; static Tk_ImageChangedProc ImageChangedProc; static MarkerClass bitmapMarkerClass = { bitmapConfigSpecs, ConfigureBitmapMarker, DrawBitmapMarker, FreeBitmapMarker, MapBitmapMarker, PointInBitmapMarker, RegionInBitmapMarker, BitmapMarkerToPostScript, }; static MarkerClass imageMarkerClass = { imageConfigSpecs, ConfigureImageMarker, DrawImageMarker, FreeImageMarker, MapImageMarker, PointInImageMarker, RegionInImageMarker, ImageMarkerToPostScript, }; static MarkerClass lineMarkerClass = { lineConfigSpecs, ConfigureLineMarker, DrawLineMarker, FreeLineMarker, MapLineMarker, PointInLineMarker, RegionInLineMarker, LineMarkerToPostScript, }; static MarkerClass polygonMarkerClass = { polygonConfigSpecs, ConfigurePolygonMarker, DrawPolygonMarker, FreePolygonMarker, MapPolygonMarker, PointInPolygonMarker, RegionInPolygonMarker, PolygonMarkerToPostScript, }; static MarkerClass textMarkerClass = { textConfigSpecs, ConfigureTextMarker, DrawTextMarker, FreeTextMarker, MapTextMarker, PointInTextMarker, RegionInTextMarker, TextMarkerToPostScript, }; static MarkerClass windowMarkerClass = { windowConfigSpecs, ConfigureWindowMarker, DrawWindowMarker, FreeWindowMarker, MapWindowMarker, PointInWindowMarker, RegionInWindowMarker, WindowMarkerToPostScript, }; #ifdef notdef static MarkerClass rectangleMarkerClass = { rectangleConfigSpecs, ConfigureRectangleMarker, DrawRectangleMarker, FreeRectangleMarker, MapRectangleMarker, PointInRectangleMarker, RegionInRectangleMarker, RectangleMarkerToPostScript, }; static MarkerClass ovalMarkerClass = { ovalConfigSpecs, ConfigureOvalMarker, DrawOvalMarker, FreeOvalMarker, MapOvalMarker, PointInOvalMarker, RegionInOvalMarker, OvalMarkerToPostScript, }; #endif /* * ---------------------------------------------------------------------- * * BoxesDontOverlap -- * * Tests if the bounding box of a marker overlaps the plotting * area in any way. If so, the marker will be drawn. Just do a * min/max test on the extents of both boxes. * * Note: It's assumed that the extents of the bounding box lie * within the area. So for a 10x10 rectangle, bottom and * left would be 9. * * Results: * Returns 0 is the marker is visible in the plotting area, and * 1 otherwise (marker is clipped). * * ---------------------------------------------------------------------- */ static int BoxesDontOverlap(graphPtr, extsPtr) Graph *graphPtr; Extents2D *extsPtr; { if (graphPtr->markerClipArea == MARKERCLIPPINGAREA_PLOT){ /*if(extsPtr->right >= extsPtr->left || extsPtr->bottom >= extsPtr->top || graphPtr->right >= graphPtr->left || graphPtr->bottom >= graphPtr->top)*/ if(extsPtr->right <= extsPtr->left || extsPtr->bottom <= extsPtr->top || graphPtr->right <= graphPtr->left || graphPtr->bottom <= graphPtr->top) { return 1; } assert(extsPtr->right >= extsPtr->left); assert(extsPtr->bottom >= extsPtr->top); assert(graphPtr->right >= graphPtr->left); assert(graphPtr->bottom >= graphPtr->top); return (((double)graphPtr->right < extsPtr->left) || ((double)graphPtr->bottom < extsPtr->top) || (extsPtr->right < (double)graphPtr->left) || (extsPtr->bottom < (double)graphPtr->top)); } else { return (((double)graphPtr->width < extsPtr->left) || ((double)graphPtr->height < extsPtr->top) || (extsPtr->right < 0.0) || (extsPtr->bottom < 0.0)); } } /* * ---------------------------------------------------------------------- * * GetCoordinate -- * * Convert the expression string into a floating point value. The * only reason we use this routine instead of Blt_ExprDouble is to * handle "elastic" bounds. That is, convert the strings "-Inf", * "Inf" into -(DBL_MAX) and DBL_MAX respectively. * * Results: * The return value is a standard Tcl result. The value of the * expression is passed back via valuePtr. * * ---------------------------------------------------------------------- */ static int GetCoordinate(interp, expr, valuePtr) Tcl_Interp *interp; /* Interpreter to send results back to */ char *expr; /* Numeric expression string to parse */ double *valuePtr; /* Real-valued result of expression */ { char c; c = expr[0]; if ((c == 'I') && (strcmp(expr, "Inf") == 0)) { *valuePtr = DBL_MAX; /* Elastic upper bound */ } else if ((c == '-') && (expr[1] == 'I') && (strcmp(expr, "-Inf") == 0)) { *valuePtr = -DBL_MAX; /* Elastic lower bound */ } else if ((c == '+') && (expr[1] == 'I') && (strcmp(expr, "+Inf") == 0)) { *valuePtr = DBL_MAX; /* Elastic upper bound */ } else if (Tcl_ExprDouble(interp, expr, valuePtr) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } /* * ---------------------------------------------------------------------- * * PrintCoordinate -- * * Convert the floating point value into its string * representation. The only reason this routine is used in * instead of sprintf, is to handle the "elastic" bounds. That * is, convert the values DBL_MAX and -(DBL_MAX) into "+Inf" and * "-Inf" respectively. * * Results: * The return value is a standard Tcl result. The string of the * expression is passed back via string. * * ---------------------------------------------------------------------- */ static char * PrintCoordinate(interp, x) Tcl_Interp *interp; double x; /* Numeric value */ { if (x == DBL_MAX) { return "+Inf"; } else if (x == -DBL_MAX) { return "-Inf"; } else { static char string[TCL_DOUBLE_SPACE + 1]; Tcl_PrintDouble(interp, (double)x, string); return string; } } /* * ---------------------------------------------------------------------- * * ParseCoordinates -- * * The Tcl coordinate list is converted to their floating point * values. It will then replace the current marker coordinates. * * Since different marker types require different number of * coordinates this must be checked here. * * Results: * The return value is a standard Tcl result. * * Side effects: * If the marker coordinates are reset, the graph is eventually * redrawn with at the new marker coordinates. * * ---------------------------------------------------------------------- */ static int ParseCoordinates(interp, markerPtr, nExprs, exprArr) Tcl_Interp *interp; Marker *markerPtr; int nExprs; char **exprArr; { int nWorldPts; int minArgs, maxArgs; Point2D *worldPts; register int i; register Point2D *pointPtr; double x, y; if (nExprs == 0) { return TCL_OK; } if (nExprs & 1) { Tcl_AppendResult(interp, "odd number of marker coordinates specified", (char *)NULL); return TCL_ERROR; } if (markerPtr->classUid == bltLineMarkerUid) { minArgs = 4, maxArgs = 0; } else if (markerPtr->classUid == bltPolygonMarkerUid) { minArgs = 6, maxArgs = 0; } else if ((markerPtr->classUid == bltWindowMarkerUid) || (markerPtr->classUid == bltTextMarkerUid)) { minArgs = 2, maxArgs = 2; } else if ((markerPtr->classUid == bltImageMarkerUid) || (markerPtr->classUid == bltBitmapMarkerUid)) { minArgs = 2, maxArgs = 4; } else { Tcl_AppendResult(interp, "unknown marker type", (char *)NULL); return TCL_ERROR; } if (nExprs < minArgs) { Tcl_AppendResult(interp, "too few marker coordinates specified", (char *)NULL); return TCL_ERROR; } if ((maxArgs > 0) && (nExprs > maxArgs)) { Tcl_AppendResult(interp, "too many marker coordinates specified", (char *)NULL); return TCL_ERROR; } nWorldPts = nExprs / 2; worldPts = Blt_Malloc(nWorldPts * sizeof(Point2D)); if (worldPts == NULL) { Tcl_AppendResult(interp, "can't allocate new coordinate array", (char *)NULL); return TCL_ERROR; } /* Don't free the old coordinate array until we've parsed the new * coordinates without errors. */ pointPtr = worldPts; for (i = 0; i < nExprs; i += 2) { if ((GetCoordinate(interp, exprArr[i], &x) != TCL_OK) || (GetCoordinate(interp, exprArr[i + 1], &y) != TCL_OK)) { Blt_Free(worldPts); return TCL_ERROR; } pointPtr->x = x, pointPtr->y = y; pointPtr++; } if (markerPtr->worldPts != NULL) { Blt_Free(markerPtr->worldPts); } markerPtr->worldPts = worldPts; markerPtr->nWorldPts = nWorldPts; markerPtr->flags |= MAP_ITEM; return TCL_OK; } /* * ---------------------------------------------------------------------- * * StringToCoordinates -- * * Given a Tcl list of numeric expression representing the * element values, convert into an array of floating point * values. In addition, the minimum and maximum values are saved. * Since elastic values are allow (values which translate to the * min/max of the graph), we must try to get the non-elastic * minimum and maximum. * * Results: * The return value is a standard Tcl result. The vector is * passed back via the vecPtr. * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToCoordinates(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; /* Tcl list of numeric expressions */ char *widgRec; /* Marker record */ int offset; /* Not used. */ { Marker *markerPtr = (Marker *)widgRec; int nExprs; char **exprArr; int result; nExprs = 0; if ((string != NULL) && (Tcl_SplitList(interp, string, &nExprs, &exprArr) != TCL_OK)) { return TCL_ERROR; } if (nExprs == 0) { if (markerPtr->worldPts != NULL) { Blt_Free(markerPtr->worldPts); markerPtr->worldPts = NULL; } markerPtr->nWorldPts = 0; return TCL_OK; } result = ParseCoordinates(interp, markerPtr, nExprs, exprArr); Blt_Free(exprArr); return result; } /* * ---------------------------------------------------------------------- * * CoordinatesToString -- * * Convert the vector of floating point values into a Tcl list. * * Results: * The string representation of the vector is returned. * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * CoordinatesToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Marker record */ int offset; /* Not used. */ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { Marker *markerPtr = (Marker *)widgRec; Tcl_Interp *interp; Tcl_DString dString; char *result; register int i; register Point2D *p; if (markerPtr->nWorldPts < 1) { return ""; } interp = markerPtr->graphPtr->interp; Tcl_DStringInit(&dString); p = markerPtr->worldPts; for (i = 0; i < markerPtr->nWorldPts; i++) { Tcl_DStringAppendElement(&dString, PrintCoordinate(interp, p->x)); Tcl_DStringAppendElement(&dString, PrintCoordinate(interp, p->y)); p++; } result = Tcl_DStringValue(&dString); /* * If memory wasn't allocated for the dynamic string, do it here (it's * currently on the stack), so that Tcl can free it normally. */ if (result == dString.staticSpace) { result = Blt_Strdup(result); } *freeProcPtr = (Tcl_FreeProc *)Blt_Free; return result; } /* * ---------------------------------------------------------------------- * * HMap -- * * Map the given graph coordinate value to its axis, returning a * window position. * * Results: * Returns a floating point number representing the window * coordinate position on the given axis. * * ---------------------------------------------------------------------- */ static double HMap(graphPtr, axisPtr, x) Graph *graphPtr; Axis *axisPtr; double x; { register double norm; if (x == DBL_MAX) { norm = 1.0; } else if (x == -DBL_MAX) { norm = 0.0; } else { if (axisPtr->logScale) { if (x > 0.0) { x = log10(x); } else if (x < 0.0) { x = 0.0; } } norm = NORMALIZE(axisPtr, x); } if (axisPtr->descending) { norm = 1.0 - norm; } /* Horizontal transformation */ return ((norm * graphPtr->hRange) + graphPtr->hOffset); } /* * ---------------------------------------------------------------------- * * VMap -- * * Map the given graph coordinate value to its axis, returning a * window position. * * Results: * Returns a double precision number representing the window * coordinate position on the given axis. * * ---------------------------------------------------------------------- */ static double VMap(graphPtr, axisPtr, y) Graph *graphPtr; Axis *axisPtr; double y; { register double norm; if (y == DBL_MAX) { norm = 1.0; } else if (y == -DBL_MAX) { norm = 0.0; } else { if (axisPtr->logScale) { if (y > 0.0) { y = log10(y); } else if (y < 0.0) { y = 0.0; } } norm = NORMALIZE(axisPtr, y); } if (axisPtr->descending) { norm = 1.0 - norm; } /* Vertical transformation */ return (((1.0 - norm) * graphPtr->vRange) + graphPtr->vOffset); } /* * ---------------------------------------------------------------------- * * MapPoint -- * * Maps the given graph x,y coordinate values to a window position. * * Results: * Returns a XPoint structure containing the window coordinates * of the given graph x,y coordinate. * * ---------------------------------------------------------------------- */ static Point2D MapPoint(graphPtr, pointPtr, axesPtr) Graph *graphPtr; Point2D *pointPtr; /* Graph X-Y coordinate. */ Axis2D *axesPtr; /* Specifies which axes to use */ { Point2D result; if (graphPtr->inverted) { result.x = HMap(graphPtr, axesPtr->y, pointPtr->y); result.y = VMap(graphPtr, axesPtr->x, pointPtr->x); } else { result.x = HMap(graphPtr, axesPtr->x, pointPtr->x); result.y = VMap(graphPtr, axesPtr->y, pointPtr->y); } return result; /* Result is screen coordinate. */ } static Marker * CreateMarker(graphPtr, name, classUid) Graph *graphPtr; char *name; Blt_Uid classUid; { Marker *markerPtr; /* Create the new marker based upon the given type */ if (classUid == bltBitmapMarkerUid) { markerPtr = CreateBitmapMarker(); /* bitmap */ } else if (classUid == bltLineMarkerUid) { markerPtr = CreateLineMarker(); /* line */ } else if (classUid == bltImageMarkerUid) { markerPtr = CreateImageMarker(); /* image */ } else if (classUid == bltTextMarkerUid) { markerPtr = CreateTextMarker(); /* text */ } else if (classUid == bltPolygonMarkerUid) { markerPtr = CreatePolygonMarker(); /* polygon */ } else if (classUid == bltWindowMarkerUid) { markerPtr = CreateWindowMarker(); /* window */ } else { return NULL; } assert(markerPtr); markerPtr->graphPtr = graphPtr; markerPtr->hidden = markerPtr->drawUnder = FALSE; markerPtr->flags |= MAP_ITEM; markerPtr->name = Blt_Strdup(name); markerPtr->classUid = classUid; return markerPtr; } static void DestroyMarker(markerPtr) Marker *markerPtr; { Graph *graphPtr = markerPtr->graphPtr; if (markerPtr->drawUnder) { graphPtr->flags |= REDRAW_BACKING_STORE; } /* Free the resources allocated for the particular type of marker */ (*markerPtr->classPtr->freeProc) (graphPtr, markerPtr); if (markerPtr->worldPts != NULL) { Blt_Free(markerPtr->worldPts); } Blt_DeleteBindings(graphPtr->bindTable, markerPtr); Tk_FreeOptions(markerPtr->classPtr->configSpecs, (char *)markerPtr, graphPtr->display, 0); if (markerPtr->hashPtr != NULL) { Blt_DeleteHashEntry(&graphPtr->markers.table, markerPtr->hashPtr); } if (markerPtr->linkPtr != NULL) { Blt_ChainDeleteLink(graphPtr->markers.displayList, markerPtr->linkPtr); } if (markerPtr->name != NULL) { Blt_Free(markerPtr->name); } if (markerPtr->elemName != NULL) { Blt_Free(markerPtr->elemName); } if (markerPtr->tags != NULL) { Blt_Free(markerPtr->tags); } Blt_Free(markerPtr); } /* * ---------------------------------------------------------------------- * * ConfigureBitmapMarker -- * * This procedure is called to process an argv/argc list, plus * the Tk option database, in order to configure (or reconfigure) * a bitmap marker. * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * * Side effects: * Configuration information, such as bitmap pixmap, colors, * rotation, etc. get set for markerPtr; old resources get freed, * if there were any. The marker is eventually redisplayed. * * ---------------------------------------------------------------------- */ /* ARGSUSED */ static int ConfigureBitmapMarker(markerPtr) Marker *markerPtr; { Graph *graphPtr = markerPtr->graphPtr; BitmapMarker *bmPtr = (BitmapMarker *)markerPtr; GC newGC; XGCValues gcValues; unsigned long gcMask; if (bmPtr->srcBitmap == None) { return TCL_OK; } bmPtr->theta = FMOD(bmPtr->rotate, 360.0); if (bmPtr->theta < 0.0) { bmPtr->theta += 360.0; } gcMask = 0; if (bmPtr->outlineColor != NULL) { gcMask |= GCForeground; gcValues.foreground = bmPtr->outlineColor->pixel; } if (bmPtr->fillColor != NULL) { gcValues.background = bmPtr->fillColor->pixel; gcMask |= GCBackground; } else { gcValues.clip_mask = bmPtr->srcBitmap; gcMask |= GCClipMask; } /* Note that while this is a "shared" GC, we're going to change * the clip origin right before the bitmap is drawn anyways. This * assumes that any drawing code using this GC (with GCClipMask * set) is going to want to set the clip origin anyways. */ newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues); if (bmPtr->gc != NULL) { Tk_FreeGC(graphPtr->display, bmPtr->gc); } bmPtr->gc = newGC; /* Create background GC color */ if (bmPtr->fillColor != NULL) { gcValues.foreground = bmPtr->fillColor->pixel; newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues); if (bmPtr->fillGC != NULL) { Tk_FreeGC(graphPtr->display, bmPtr->fillGC); } bmPtr->fillGC = newGC; } bmPtr->flags |= MAP_ITEM; if (bmPtr->drawUnder) { graphPtr->flags |= REDRAW_BACKING_STORE; } Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } /* * ---------------------------------------------------------------------- * * MapBitmapMarker -- * * This procedure gets called each time the layout of the graph * changes. The x, y window coordinates of the bitmap marker are * saved in the marker structure. * * Additionly, if no background color was specified, the * GCTileStipXOrigin and GCTileStipYOrigin attributes are set in * the private GC. * * Results: * None. * * Side effects: * Window coordinates are saved and if no background color was * set, the GC stipple origins are changed to calculated window * coordinates. * * ---------------------------------------------------------------------- */ static void MapBitmapMarker(markerPtr) Marker *markerPtr; { BitmapMarker *bmPtr = (BitmapMarker *)markerPtr; Extents2D exts; Graph *graphPtr = markerPtr->graphPtr; Point2D anchorPos; Point2D corner1, corner2; int destWidth, destHeight; int srcWidth, srcHeight; register int i; if (bmPtr->srcBitmap == None) { return; } if (bmPtr->destBitmap != None) { Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap); bmPtr->destBitmap = None; } /* * Collect the coordinates. The number of coordinates will determine * the calculations to be made. * * x1 y1 A single pair of X-Y coordinates. They represent * the anchor position of the bitmap. * * x1 y1 x2 y2 Two pairs of X-Y coordinates. They represent * two opposite corners of a bounding rectangle. The * bitmap is possibly rotated and scaled to fit into * this box. * */ Tk_SizeOfBitmap(graphPtr->display, bmPtr->srcBitmap, &srcWidth, &srcHeight); corner1 = MapPoint(graphPtr, bmPtr->worldPts, &bmPtr->axes); if (bmPtr->nWorldPts > 1) { double hold; corner2 = MapPoint(graphPtr, bmPtr->worldPts + 1, &bmPtr->axes); /* Flip the corners if necessary */ if (corner1.x > corner2.x) { hold = corner1.x, corner1.x = corner2.x, corner2.x = hold; } if (corner1.y > corner2.y) { hold = corner1.y, corner1.y = corner2.y, corner2.y = hold; } } else { corner2.x = corner1.x + srcWidth - 1; corner2.y = corner1.y + srcHeight - 1; } destWidth = (int)(corner2.x - corner1.x) + 1; destHeight = (int)(corner2.y - corner1.y) + 1; if (bmPtr->nWorldPts == 1) { anchorPos = Blt_TranslatePoint(&corner1, destWidth, destHeight, bmPtr->anchor); } else { anchorPos = corner1; } anchorPos.x += bmPtr->xOffset; anchorPos.y += bmPtr->yOffset; /* Check if the bitmap sits at least partially in the plot area. */ exts.left = anchorPos.x; exts.top = anchorPos.y; exts.right = anchorPos.x + destWidth - 1; exts.bottom = anchorPos.y + destHeight - 1; bmPtr->clipped = BoxesDontOverlap(graphPtr, &exts); if (bmPtr->clipped) { return; /* Bitmap is offscreen. Don't generate * rotated or scaled bitmaps. */ } /* * Scale the bitmap if necessary. It's a little tricky because we * only want to scale what's visible on the screen, not the entire * bitmap. */ if ((bmPtr->theta != 0.0) || (destWidth != srcWidth) || (destHeight != srcHeight)) { int regionWidth, regionHeight; Region2D region; /* Indicates the portion of the scaled * bitmap that we want to display. */ double left, right, top, bottom; /* * Determine the region of the bitmap visible in the plot area. */ left = MAX(graphPtr->left, exts.left); right = MIN(graphPtr->right, exts.right); top = MAX(graphPtr->top, exts.top); bottom = MIN(graphPtr->bottom, exts.bottom); region.left = region.top = 0; if (graphPtr->left > exts.left) { region.left = (int)(graphPtr->left - exts.left); } if (graphPtr->top > exts.top) { region.top = (int)(graphPtr->top - exts.top); } regionWidth = (int)(right - left) + 1; regionHeight = (int)(bottom - top) + 1; region.right = region.left + (int)(right - left); region.bottom = region.top + (int)(bottom - top); anchorPos.x = left; anchorPos.y = top; bmPtr->destBitmap = Blt_ScaleRotateBitmapRegion(graphPtr->tkwin, bmPtr->srcBitmap, srcWidth, srcHeight, region.left, region.top, regionWidth, regionHeight, destWidth, destHeight, bmPtr->theta); bmPtr->destWidth = regionWidth; bmPtr->destHeight = regionHeight; } else { bmPtr->destWidth = srcWidth; bmPtr->destHeight = srcHeight; bmPtr->destBitmap = None; } bmPtr->anchorPos = anchorPos; { double xScale, yScale; double tx, ty; double rotWidth, rotHeight; Point2D polygon[5]; int n; /* * Compute a polygon to represent the background area of the bitmap. * This is needed for backgrounds of arbitrarily rotated bitmaps. * We also use it to print a background in PostScript. */ Blt_GetBoundingBox(srcWidth, srcHeight, bmPtr->theta, &rotWidth, &rotHeight, polygon); xScale = (double)destWidth / rotWidth; yScale = (double)destHeight / rotHeight; /* * Adjust each point of the polygon. Both scale it to the new size * and translate it to the actual screen position of the bitmap. */ tx = exts.left + destWidth * 0.5; ty = exts.top + destHeight * 0.5; for (i = 0; i < 4; i++) { polygon[i].x = (polygon[i].x * xScale) + tx; polygon[i].y = (polygon[i].y * yScale) + ty; } Blt_GraphExtents(graphPtr, &exts); n = Blt_PolyRectClip(&exts, polygon, 4, bmPtr->outline); assert(n <= MAX_OUTLINE_POINTS); if (n < 3) { memcpy(&bmPtr->outline, polygon, sizeof(Point2D) * 4); bmPtr->nOutlinePts = 4; } else { bmPtr->nOutlinePts = n; } } } /* * ---------------------------------------------------------------------- * * PointInBitmapMarker -- * * Indicates if the given point is over the bitmap marker. The * area of the bitmap is the rectangle. * * Results: * Returns 1 is the point is over the bitmap marker, 0 otherwise. * * ---------------------------------------------------------------------- */ static int PointInBitmapMarker(markerPtr, samplePtr) Marker *markerPtr; Point2D *samplePtr; { BitmapMarker *bmPtr = (BitmapMarker *)markerPtr; if (bmPtr->srcBitmap == None) { return 0; } if (bmPtr->theta != 0.0) { Point2D points[MAX_OUTLINE_POINTS]; register int i; /* * Generate the bounding polygon (isolateral) for the bitmap * and see if the point is inside of it. */ for (i = 0; i < bmPtr->nOutlinePts; i++) { points[i].x = bmPtr->outline[i].x + bmPtr->anchorPos.x; points[i].y = bmPtr->outline[i].y + bmPtr->anchorPos.y; } return Blt_PointInPolygon(samplePtr, points, bmPtr->nOutlinePts); } return ((samplePtr->x >= bmPtr->anchorPos.x) && (samplePtr->x < (bmPtr->anchorPos.x + bmPtr->destWidth)) && (samplePtr->y >= bmPtr->anchorPos.y) && (samplePtr->y < (bmPtr->anchorPos.y + bmPtr->destHeight))); } /* * ---------------------------------------------------------------------- * * RegionInBitmapMarker -- * * ---------------------------------------------------------------------- */ static int RegionInBitmapMarker(markerPtr, extsPtr, enclosed) Marker *markerPtr; Extents2D *extsPtr; int enclosed; { BitmapMarker *bmPtr = (BitmapMarker *)markerPtr; if (bmPtr->nWorldPts < 1) { return FALSE; } if (bmPtr->theta != 0.0) { Point2D points[MAX_OUTLINE_POINTS]; register int i; /* * Generate the bounding polygon (isolateral) for the bitmap * and see if the point is inside of it. */ for (i = 0; i < bmPtr->nOutlinePts; i++) { points[i].x = bmPtr->outline[i].x + bmPtr->anchorPos.x; points[i].y = bmPtr->outline[i].y + bmPtr->anchorPos.y; } return Blt_RegionInPolygon(extsPtr, points, bmPtr->nOutlinePts, enclosed); } if (enclosed) { return ((bmPtr->anchorPos.x >= extsPtr->left) && (bmPtr->anchorPos.y >= extsPtr->top) && ((bmPtr->anchorPos.x + bmPtr->destWidth) <= extsPtr->right) && ((bmPtr->anchorPos.y + bmPtr->destHeight) <= extsPtr->bottom)); } return !((bmPtr->anchorPos.x >= extsPtr->right) || (bmPtr->anchorPos.y >= extsPtr->bottom) || ((bmPtr->anchorPos.x + bmPtr->destWidth) <= extsPtr->left) || ((bmPtr->anchorPos.y + bmPtr->destHeight) <= extsPtr->top)); } /* * ---------------------------------------------------------------------- * * DrawBitmapMarker -- * * Draws the bitmap marker that have a transparent of filled * background. * * Results: * None. * * Side effects: * GC stipple origins are changed to current window coordinates. * Commands are output to X to draw the marker in its current * mode. * * ---------------------------------------------------------------------- */ static void DrawBitmapMarker(markerPtr, drawable) Marker *markerPtr; Drawable drawable; /* Pixmap or window to draw into */ { Graph *graphPtr = markerPtr->graphPtr; BitmapMarker *bmPtr = (BitmapMarker *)markerPtr; double theta; Pixmap bitmap; bitmap = GETBITMAP(bmPtr); if ((bitmap == None) || (bmPtr->destWidth < 1) || (bmPtr->destHeight < 1)) { return; } theta = FMOD(bmPtr->theta, (double)90.0); if ((bmPtr->fillColor == NULL) || (theta != 0.0)) { /* * If the bitmap is rotated and a filled background is * required, then a filled polygon is drawn before the * bitmap. */ if (bmPtr->fillColor != NULL) { int i; XPoint polygon[MAX_OUTLINE_POINTS]; for (i = 0; i < bmPtr->nOutlinePts; i++) { polygon[i].x = (short int)bmPtr->outline[i].x; polygon[i].y = (short int)bmPtr->outline[i].y; } XFillPolygon(graphPtr->display, drawable, bmPtr->fillGC, polygon, bmPtr->nOutlinePts, Convex, CoordModeOrigin); } XSetClipMask(graphPtr->display, bmPtr->gc, bitmap); XSetClipOrigin(graphPtr->display, bmPtr->gc, (int)bmPtr->anchorPos.x, (int)bmPtr->anchorPos.y); } else { XSetClipMask(graphPtr->display, bmPtr->gc, None); XSetClipOrigin(graphPtr->display, bmPtr->gc, 0, 0); } XCopyPlane(graphPtr->display, bitmap, drawable, bmPtr->gc, 0, 0, bmPtr->destWidth, bmPtr->destHeight, (int)bmPtr->anchorPos.x, (int)bmPtr->anchorPos.y, 1); } /* * ---------------------------------------------------------------------- * * BitmapMarkerToPostScript -- * * Generates PostScript to print a bitmap marker. * * Results: * None. * * ---------------------------------------------------------------------- */ static void BitmapMarkerToPostScript(markerPtr, psToken) Marker *markerPtr; /* Marker to be printed */ PsToken psToken; { Graph *graphPtr = markerPtr->graphPtr; BitmapMarker *bmPtr = (BitmapMarker *)markerPtr; Pixmap bitmap; bitmap = GETBITMAP(bmPtr); if (bitmap == None) { return; } if (bmPtr->fillColor != NULL) { Blt_BackgroundToPostScript(psToken, bmPtr->fillColor); Blt_PolygonToPostScript(psToken, bmPtr->outline, 4); } Blt_ForegroundToPostScript(psToken, bmPtr->outlineColor); Blt_FormatToPostScript(psToken, " gsave\n %g %g translate\n %d %d scale\n", bmPtr->anchorPos.x, bmPtr->anchorPos.y + bmPtr->destHeight, bmPtr->destWidth, -bmPtr->destHeight); Blt_FormatToPostScript(psToken, " %d %d true [%d 0 0 %d 0 %d] {", bmPtr->destWidth, bmPtr->destHeight, bmPtr->destWidth, -bmPtr->destHeight, bmPtr->destHeight); Blt_BitmapDataToPostScript(psToken, graphPtr->display, bitmap, bmPtr->destWidth, bmPtr->destHeight); Blt_AppendToPostScript(psToken, " } imagemask\n", "grestore\n", (char *)NULL); } /* * ---------------------------------------------------------------------- * * FreeBitmapMarker -- * * Releases the memory and attributes of the bitmap marker. * * Results: * None. * * Side effects: * Bitmap attributes (GCs, colors, bitmap, etc) get destroyed. * Memory is released, X resources are freed, and the graph is * redrawn. * * ---------------------------------------------------------------------- */ static void FreeBitmapMarker(graphPtr, markerPtr) Graph *graphPtr; Marker *markerPtr; { BitmapMarker *bmPtr = (BitmapMarker *)markerPtr; if (bmPtr->gc != NULL) { Tk_FreeGC(graphPtr->display, bmPtr->gc); } if (bmPtr->fillGC != NULL) { Tk_FreeGC(graphPtr->display, bmPtr->fillGC); } if (bmPtr->destBitmap != None) { Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap); } } /* * ---------------------------------------------------------------------- * * CreateBitmapMarker -- * * Allocate memory and initialize methods for the new bitmap marker. * * Results: * The pointer to the newly allocated marker structure is returned. * * Side effects: * Memory is allocated for the bitmap marker structure. * * ---------------------------------------------------------------------- */ static Marker * CreateBitmapMarker() { BitmapMarker *bmPtr; bmPtr = Blt_Calloc(1, sizeof(BitmapMarker)); if (bmPtr != NULL) { bmPtr->classPtr = &bitmapMarkerClass; } return (Marker *)bmPtr; } /* *---------------------------------------------------------------------- * * ImageChangedProc * * * Results: * None. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static void ImageChangedProc(clientData, x, y, width, height, imageWidth, imageHeight) ClientData clientData; int x, y, width, height; /* Not used. */ int imageWidth, imageHeight; /* Not used. */ { ImageMarker *imPtr = clientData; Tk_PhotoHandle photo; photo = Blt_FindPhoto(imPtr->graphPtr->interp, imPtr->imageName); if (photo != NULL) { if (imPtr->srcImage != NULL) { Blt_FreeColorImage(imPtr->srcImage); } /* Convert the latest incarnation of the photo image back to a * color image that we can scale. */ imPtr->srcImage = Blt_PhotoToColorImage(photo); } imPtr->graphPtr->flags |= REDRAW_BACKING_STORE; imPtr->flags |= MAP_ITEM; Blt_EventuallyRedrawGraph(imPtr->graphPtr); } /* * ---------------------------------------------------------------------- * * ConfigureImageMarker -- * * This procedure is called to process an argv/argc list, plus * the Tk option database, in order to configure (or reconfigure) * a image marker. * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * * Side effects: * Configuration information, such as image pixmap, colors, * rotation, etc. get set for markerPtr; old resources get freed, * if there were any. The marker is eventually redisplayed. * * ---------------------------------------------------------------------- */ static int ConfigureImageMarker(markerPtr) Marker *markerPtr; { ImageMarker *imPtr = (ImageMarker *)markerPtr; Graph *graphPtr = markerPtr->graphPtr; if (Blt_ConfigModified(markerPtr->classPtr->configSpecs, graphPtr->interp, "-image", (char *)NULL)) { Tcl_Interp *interp = graphPtr->interp; if (imPtr->tkImage != NULL) { Tk_FreeImage(imPtr->tkImage); imPtr->tkImage = NULL; } if (imPtr->imageName[0] != '\0') { GC newGC; Tk_PhotoHandle photo; imPtr->tkImage = Tk_GetImage(interp, graphPtr->tkwin, imPtr->imageName, ImageChangedProc, imPtr); if (imPtr->tkImage == NULL) { Blt_Free(imPtr->imageName); imPtr->imageName = NULL; return TCL_ERROR; } photo = Blt_FindPhoto(interp, imPtr->imageName); if (photo != NULL) { if (imPtr->srcImage != NULL) { Blt_FreeColorImage(imPtr->srcImage); } /* Convert the photo into a color image */ imPtr->srcImage = Blt_PhotoToColorImage(photo); } newGC = Tk_GetGC(graphPtr->tkwin, 0L, (XGCValues *)NULL); if (imPtr->gc != NULL) { Tk_FreeGC(graphPtr->display, imPtr->gc); } imPtr->gc = newGC; } } imPtr->flags |= MAP_ITEM; if (imPtr->drawUnder) { graphPtr->flags |= REDRAW_BACKING_STORE; } Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } /* * ---------------------------------------------------------------------- * * MapImageMarker -- * * This procedure gets called each time the layout of the graph * changes. The x, y window coordinates of the image marker are * saved in the marker structure. * * Additionly, if no background color was specified, the * GCTileStipXOrigin and GCTileStipYOrigin attributes are set in * the private GC. * * Results: * None. * * Side effects: * Window coordinates are saved and if no background color was * * set, the GC stipple origins are changed to calculated window * coordinates. * * ---------------------------------------------------------------------- */ static void MapImageMarker(markerPtr) Marker *markerPtr; { Extents2D exts; Graph *graphPtr; ImageMarker *imPtr; Point2D anchorPos; Point2D corner1, corner2; int scaledWidth, scaledHeight; int srcWidth, srcHeight; imPtr = (ImageMarker *)markerPtr; if (imPtr->tkImage == NULL) { return; } graphPtr = imPtr->graphPtr; corner1 = MapPoint(graphPtr, imPtr->worldPts, &imPtr->axes); if (imPtr->srcImage == NULL) { /* * Don't scale or rotate non-photo images. */ Tk_SizeOfImage(imPtr->tkImage, &srcWidth, &srcHeight); imPtr->width = srcWidth; imPtr->height = srcHeight; imPtr->anchorPos = Blt_TranslatePoint(&corner1, srcWidth, srcHeight, imPtr->anchor); imPtr->anchorPos.x += imPtr->xOffset; imPtr->anchorPos.y += imPtr->yOffset; exts.left = imPtr->anchorPos.x; exts.top = imPtr->anchorPos.y; exts.right = exts.left + srcWidth - 1; exts.bottom = exts.top + srcHeight - 1; imPtr->clipped = BoxesDontOverlap(graphPtr, &exts); return; } imPtr->width = srcWidth = Blt_ColorImageWidth(imPtr->srcImage); imPtr->height = srcHeight = Blt_ColorImageHeight(imPtr->srcImage); if ((srcWidth == 0) && (srcHeight == 0)) { imPtr->clipped = TRUE; return; /* Empty image. */ } if (imPtr->nWorldPts > 1) { double hold; corner2 = MapPoint(graphPtr, imPtr->worldPts + 1, &imPtr->axes); /* Flip the corners if necessary */ if (corner1.x > corner2.x) { hold = corner1.x, corner1.x = corner2.x, corner2.x = hold; } if (corner1.y > corner2.y) { hold = corner1.y, corner1.y = corner2.y, corner2.y = hold; } } else { corner2.x = corner1.x + srcWidth - 1; corner2.y = corner1.y + srcHeight - 1; } scaledWidth = (int)(corner2.x - corner1.x) + 1; scaledHeight = (int)(corner2.y - corner1.y) + 1; if (imPtr->nWorldPts == 1) { anchorPos = Blt_TranslatePoint(&corner1, scaledWidth, scaledHeight, imPtr->anchor); } else { anchorPos = corner1; } anchorPos.x += imPtr->xOffset; anchorPos.y += imPtr->yOffset; /* Check if the image sits at least partially in the plot area. */ exts.left = anchorPos.x; exts.top = anchorPos.y; exts.right = anchorPos.x + scaledWidth - 1; exts.bottom = anchorPos.y + scaledHeight - 1; imPtr->clipped = BoxesDontOverlap(graphPtr, &exts); if (imPtr->clipped) { return; /* Image is offscreen. Don't generate * rotated or scaled images. */ } if ((scaledWidth != srcWidth) || (scaledHeight != srcHeight)) { Tk_PhotoHandle photo; Blt_ColorImage destImage; int x, y, width, height; int left, right, top, bottom; /* Determine the region of the subimage inside of the * destination image. */ left = MAX((int)exts.left, graphPtr->left); top = MAX((int)exts.top, graphPtr->top); right = MIN((int)exts.right, graphPtr->right); bottom = MIN((int)exts.bottom, graphPtr->bottom); /* Reset image location and coordinates to that of the region */ anchorPos.x = left; anchorPos.y = top; x = y = 0; if (graphPtr->left > (int)exts.left) { x = graphPtr->left - (int)exts.left; } if (graphPtr->top > (int)exts.top) { y = graphPtr->top - (int)exts.top; } width = (int)(right - left + 1); height = (int)(bottom - top + 1); destImage = Blt_ResizeColorSubimage(imPtr->srcImage, x, y, width, height, scaledWidth, scaledHeight); #ifdef notyet /* Now convert the color image into a pixmap */ if (imPtr->pixmap != None) { Blt_FreeColorTable(imPtr->colorTable); Tk_FreePixmap(Tk_Display(graphPtr->tkwin), imPtr->pixmap); imPtr->colorTable = NULL; } imPtr->pixmap = Blt_ColorImageToPixmap(graphPtr->interp, graphPtr->tkwin, destImage, &imPtr->colorTable); #else imPtr->pixmap = None; if (imPtr->tmpImage == NULL) { imPtr->tmpImage = Blt_CreateTemporaryImage(graphPtr->interp, graphPtr->tkwin, imPtr); if (imPtr->tmpImage == NULL) { return; } } /* Put the scaled colorimage into the photo. */ photo = Blt_FindPhoto(graphPtr->interp, Blt_NameOfImage(imPtr->tmpImage)); Blt_ColorImageToPhoto(destImage, photo); #endif Blt_FreeColorImage(destImage); imPtr->width = width; imPtr->height = height; } imPtr->anchorPos = anchorPos; } /* * ---------------------------------------------------------------------- * * PointInWindowMarker -- * * Indicates if the given point is over the window marker. The * area of the window is the rectangle. * * Results: * Returns 1 is the point is over the window marker, 0 otherwise. * * ---------------------------------------------------------------------- */ static int PointInImageMarker(markerPtr, samplePtr) Marker *markerPtr; Point2D *samplePtr; { ImageMarker *imPtr = (ImageMarker *)markerPtr; return ((samplePtr->x >= imPtr->anchorPos.x) && (samplePtr->x < (imPtr->anchorPos.x + imPtr->width)) && (samplePtr->y >= imPtr->anchorPos.y) && (samplePtr->y < (imPtr->anchorPos.y + imPtr->height))); } /* * ---------------------------------------------------------------------- * * RegionInImageMarker -- * * ---------------------------------------------------------------------- */ static int RegionInImageMarker(markerPtr, extsPtr, enclosed) Marker *markerPtr; Extents2D *extsPtr; int enclosed; { ImageMarker *imPtr = (ImageMarker *)markerPtr; if (imPtr->nWorldPts < 1) { return FALSE; } if (enclosed) { return ((imPtr->anchorPos.x >= extsPtr->left) && (imPtr->anchorPos.y >= extsPtr->top) && ((imPtr->anchorPos.x + imPtr->width) <= extsPtr->right) && ((imPtr->anchorPos.y + imPtr->height) <= extsPtr->bottom)); } return !((imPtr->anchorPos.x >= extsPtr->right) || (imPtr->anchorPos.y >= extsPtr->bottom) || ((imPtr->anchorPos.x + imPtr->width) <= extsPtr->left) || ((imPtr->anchorPos.y + imPtr->height) <= extsPtr->top)); } /* * ---------------------------------------------------------------------- * * DrawImageMarker -- * * This procedure is invoked to draw a image marker. * * Results: * None. * * Side effects: * GC stipple origins are changed to current window coordinates. * Commands are output to X to draw the marker in its current mode. * * ---------------------------------------------------------------------- */ static void DrawImageMarker(markerPtr, drawable) Marker *markerPtr; Drawable drawable; /* Pixmap or window to draw into */ { ImageMarker *imPtr = (ImageMarker *)markerPtr; int width, height; if ((imPtr->tkImage == NULL) || (Tk_ImageIsDeleted(imPtr->tkImage))) { return; } if (imPtr->pixmap == None) { Pixmap pixmap; Tk_Image tkImage; tkImage = (imPtr->tmpImage != NULL) ? imPtr->tmpImage : imPtr->tkImage; Tk_SizeOfImage(tkImage, &width, &height); /* pixmap = Tk_ImageGetPhotoPixmap(tkImage); */ pixmap = None; if (pixmap == None) { /* May not be a "photo" image. */ Tk_RedrawImage(tkImage, 0, 0, width, height, drawable, (int)imPtr->anchorPos.x, (int)imPtr->anchorPos.y); } else { XCopyArea(imPtr->graphPtr->display, pixmap, drawable, imPtr->gc, 0, 0, width, height, (int)imPtr->anchorPos.x, (int)imPtr->anchorPos.y); } } else { XCopyArea(imPtr->graphPtr->display, imPtr->pixmap, drawable, imPtr->gc, 0, 0, imPtr->width, imPtr->height, (int)imPtr->anchorPos.x, (int)imPtr->anchorPos.y); } } /* * ---------------------------------------------------------------------- * * ImageMarkerToPostScript -- * * This procedure is invoked to print a image marker. * * Results: * None. * * ---------------------------------------------------------------------- */ static void ImageMarkerToPostScript(markerPtr, psToken) Marker *markerPtr; /* Marker to be printed */ PsToken psToken; { ImageMarker *imPtr = (ImageMarker *)markerPtr; char *imageName; Tk_PhotoHandle photo; if ((imPtr->tkImage == NULL) || (Tk_ImageIsDeleted(imPtr->tkImage))) { return; /* Image doesn't exist anymore */ } imageName = (imPtr->tmpImage == NULL) ? Blt_NameOfImage(imPtr->tkImage) : Blt_NameOfImage(imPtr->tmpImage); photo = Blt_FindPhoto(markerPtr->graphPtr->interp, imageName); if (photo == NULL) { return; /* Image isn't a photo image */ } Blt_PhotoToPostScript(psToken, photo, imPtr->anchorPos.x, imPtr->anchorPos.y); } /* * ---------------------------------------------------------------------- * * FreeImageMarker -- * * Destroys the structure containing the attributes of the image * marker. * * Results: * None. * * Side effects: * Image attributes (GCs, colors, image, etc) get destroyed. * Memory is released, X resources are freed, and the graph is * redrawn. * * ---------------------------------------------------------------------- */ static void FreeImageMarker(graphPtr, markerPtr) Graph *graphPtr; Marker *markerPtr; { ImageMarker *imPtr = (ImageMarker *)markerPtr; if (imPtr->pixmap != None) { Tk_FreePixmap(graphPtr->display, imPtr->pixmap); } if (imPtr->tkImage != NULL) { Tk_FreeImage(imPtr->tkImage); } if (imPtr->tmpImage != NULL) { Blt_DestroyTemporaryImage(graphPtr->interp, imPtr->tmpImage); } if (imPtr->srcImage != NULL) { Blt_FreeColorImage(imPtr->srcImage); } if (imPtr->gc != NULL) { Tk_FreeGC(graphPtr->display, imPtr->gc); } } /* * ---------------------------------------------------------------------- * * CreateImageMarker -- * * Allocate memory and initialize methods for the new image marker. * * Results: * The pointer to the newly allocated marker structure is returned. * * Side effects: * Memory is allocated for the image marker structure. * * ---------------------------------------------------------------------- */ static Marker * CreateImageMarker() { ImageMarker *imPtr; imPtr = Blt_Calloc(1, sizeof(ImageMarker)); if (imPtr != NULL) { imPtr->classPtr = &imageMarkerClass; } return (Marker *)imPtr; } /* * ---------------------------------------------------------------------- * * ConfigureTextMarker -- * * This procedure is called to process an argv/argc list, plus * the Tk option database, in order to configure (or * reconfigure) a text marker. * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * * Side effects: * Configuration information, such as text string, colors, font, * etc. get set for markerPtr; old resources get freed, if there * were any. The marker is eventually redisplayed. * * ---------------------------------------------------------------------- */ static int ConfigureTextMarker(markerPtr) Marker *markerPtr; { Graph *graphPtr = markerPtr->graphPtr; TextMarker *tmPtr = (TextMarker *)markerPtr; GC newGC; XGCValues gcValues; unsigned long gcMask; tmPtr->style.theta = FMOD(tmPtr->style.theta, 360.0); if (tmPtr->style.theta < 0.0) { tmPtr->style.theta += 360.0; } newGC = NULL; if (tmPtr->fillColor != NULL) { gcMask = GCForeground; gcValues.foreground = tmPtr->fillColor->pixel; newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues); } if (tmPtr->fillGC != NULL) { Tk_FreeGC(graphPtr->display, tmPtr->fillGC); } tmPtr->fillGC = newGC; Blt_ResetTextStyle(graphPtr->tkwin, &tmPtr->style); if (Blt_ConfigModified(tmPtr->classPtr->configSpecs, graphPtr->interp, "-text", (char *)NULL)) { if (tmPtr->textPtr != NULL) { Blt_Free(tmPtr->textPtr); tmPtr->textPtr = NULL; } tmPtr->width = tmPtr->height = 0; if (tmPtr->string != NULL) { register int i; double rotWidth, rotHeight; tmPtr->textPtr = Blt_GetTextLayout(tmPtr->string, &tmPtr->style); Blt_GetBoundingBox(tmPtr->textPtr->width, tmPtr->textPtr->height, tmPtr->style.theta, &rotWidth, &rotHeight, tmPtr->outline); tmPtr->width = ROUND(rotWidth); tmPtr->height = ROUND(rotHeight); for (i = 0; i < 4; i++) { tmPtr->outline[i].x += ROUND(rotWidth * 0.5); tmPtr->outline[i].y += ROUND(rotHeight * 0.5); } tmPtr->outline[4].x = tmPtr->outline[0].x; tmPtr->outline[4].y = tmPtr->outline[0].y; } } tmPtr->flags |= MAP_ITEM; if (tmPtr->drawUnder) { graphPtr->flags |= REDRAW_BACKING_STORE; } Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } /* * ---------------------------------------------------------------------- * * MapTextMarker -- * * Calculate the layout position for a text marker. Positional * information is saved in the marker. If the text is rotated, * a bitmap containing the text is created. * * Results: * None. * * Side effects: * If no background color has been specified, the GC stipple * origins are changed to current window coordinates. For both * rotated and non-rotated text, if any old bitmap is leftover, * it is freed. * * ---------------------------------------------------------------------- */ static void MapTextMarker(markerPtr) Marker *markerPtr; { Graph *graphPtr = markerPtr->graphPtr; TextMarker *tmPtr = (TextMarker *)markerPtr; Extents2D exts; Point2D anchorPos; if (tmPtr->string == NULL) { return; } anchorPos = MapPoint(graphPtr, tmPtr->worldPts, &tmPtr->axes); anchorPos = Blt_TranslatePoint(&anchorPos, tmPtr->width, tmPtr->height, tmPtr->anchor); anchorPos.x += tmPtr->xOffset; anchorPos.y += tmPtr->yOffset; /* * Determine the bounding box of the text and test to see if it * is at least partially contained within the plotting area. */ exts.left = anchorPos.x; exts.top = anchorPos.y; exts.right = anchorPos.x + tmPtr->width - 1; exts.bottom = anchorPos.y + tmPtr->height - 1; tmPtr->clipped = BoxesDontOverlap(graphPtr, &exts); tmPtr->anchorPos = anchorPos; } static int PointInTextMarker(markerPtr, samplePtr) Marker *markerPtr; Point2D *samplePtr; { TextMarker *tmPtr = (TextMarker *)markerPtr; if (tmPtr->string == NULL) { return 0; } if (tmPtr->style.theta != 0.0) { Point2D points[5]; register int i; /* * Figure out the bounding polygon (isolateral) for the text * and see if the point is inside of it. */ for (i = 0; i < 5; i++) { points[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x; points[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y; } return Blt_PointInPolygon(samplePtr, points, 5); } return ((samplePtr->x >= tmPtr->anchorPos.x) && (samplePtr->x < (tmPtr->anchorPos.x + tmPtr->width)) && (samplePtr->y >= tmPtr->anchorPos.y) && (samplePtr->y < (tmPtr->anchorPos.y + tmPtr->height))); } /* * ---------------------------------------------------------------------- * * RegionInTextMarker -- * * ---------------------------------------------------------------------- */ static int RegionInTextMarker(markerPtr, extsPtr, enclosed) Marker *markerPtr; Extents2D *extsPtr; int enclosed; { TextMarker *tmPtr = (TextMarker *)markerPtr; if (tmPtr->nWorldPts < 1) { return FALSE; } if (tmPtr->style.theta != 0.0) { Point2D points[5]; register int i; /* * Generate the bounding polygon (isolateral) for the bitmap * and see if the point is inside of it. */ for (i = 0; i < 4; i++) { points[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x; points[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y; } return Blt_RegionInPolygon(extsPtr, points, 4, enclosed); } if (enclosed) { return ((tmPtr->anchorPos.x >= extsPtr->left) && (tmPtr->anchorPos.y >= extsPtr->top) && ((tmPtr->anchorPos.x + tmPtr->width) <= extsPtr->right) && ((tmPtr->anchorPos.y + tmPtr->height) <= extsPtr->bottom)); } return !((tmPtr->anchorPos.x >= extsPtr->right) || (tmPtr->anchorPos.y >= extsPtr->bottom) || ((tmPtr->anchorPos.x + tmPtr->width) <= extsPtr->left) || ((tmPtr->anchorPos.y + tmPtr->height) <= extsPtr->top)); } /* * ---------------------------------------------------------------------- * * DrawTextMarker -- * * Draws the text marker on the graph. * * Results: * None. * * Side effects: * Commands are output to X to draw the marker in its current * mode. * * ---------------------------------------------------------------------- */ static void DrawTextMarker(markerPtr, drawable) Marker *markerPtr; Drawable drawable; /* Pixmap or window to draw into */ { TextMarker *tmPtr = (TextMarker *)markerPtr; Graph *graphPtr = markerPtr->graphPtr; if (tmPtr->string == NULL) { return; } if (tmPtr->fillGC != NULL) { XPoint pointArr[4]; register int i; /* * Simulate the rotated background of the bitmap by * filling a bounding polygon with the background color. */ for (i = 0; i < 4; i++) { pointArr[i].x = (short int) (tmPtr->outline[i].x + tmPtr->anchorPos.x); pointArr[i].y = (short int) (tmPtr->outline[i].y + tmPtr->anchorPos.y); } XFillPolygon(graphPtr->display, drawable, tmPtr->fillGC, pointArr, 4, Convex, CoordModeOrigin); } if (tmPtr->style.color != NULL) { Blt_DrawTextLayout(graphPtr->tkwin, drawable, tmPtr->textPtr, &tmPtr->style, (int)tmPtr->anchorPos.x, (int)tmPtr->anchorPos.y); } } /* * ---------------------------------------------------------------------- * * TextMarkerToPostScript -- * * Outputs PostScript commands to draw a text marker at a given * x,y coordinate, rotation, anchor, and font. * * Results: * None. * * Side effects: * PostScript font and color settings are changed. * * ---------------------------------------------------------------------- */ static void TextMarkerToPostScript(markerPtr, psToken) Marker *markerPtr; PsToken psToken; { TextMarker *tmPtr = (TextMarker *)markerPtr; if (tmPtr->string == NULL) { return; } if (tmPtr->fillGC != NULL) { Point2D polygon[4]; register int i; /* * Simulate the rotated background of the bitmap by * filling a bounding polygon with the background color. */ for (i = 0; i < 4; i++) { polygon[i].x = tmPtr->outline[i].x + tmPtr->anchorPos.x; polygon[i].y = tmPtr->outline[i].y + tmPtr->anchorPos.y; } Blt_BackgroundToPostScript(psToken, tmPtr->fillColor); Blt_PolygonToPostScript(psToken, polygon, 4); } Blt_TextToPostScript(psToken, tmPtr->string, &tmPtr->style, tmPtr->anchorPos.x, tmPtr->anchorPos.y); } /* * ---------------------------------------------------------------------- * * FreeTextMarker -- * * Destroys the structure containing the attributes of the text * marker. * * Results: * None. * * Side effects: * Text attributes (GCs, colors, stipple, font, etc) get destroyed. * Memory is released, X resources are freed, and the graph is * redrawn. * * ---------------------------------------------------------------------- */ static void FreeTextMarker(graphPtr, markerPtr) Graph *graphPtr; Marker *markerPtr; { TextMarker *tmPtr = (TextMarker *)markerPtr; Blt_FreeTextStyle(graphPtr->display, &tmPtr->style); if (tmPtr->textPtr != NULL) { Blt_Free(tmPtr->textPtr); } } /* * ---------------------------------------------------------------------- * * CreateTextMarker -- * * Allocate memory and initialize methods for the new text marker. * * Results: * The pointer to the newly allocated marker structure is returned. * * Side effects: * Memory is allocated for the text marker structure. * * ---------------------------------------------------------------------- */ static Marker * CreateTextMarker() { TextMarker *tmPtr; tmPtr = Blt_Calloc(1, sizeof(TextMarker)); assert(tmPtr); tmPtr->classPtr = &textMarkerClass; Blt_InitTextStyle(&tmPtr->style); tmPtr->style.anchor = TK_ANCHOR_NW; tmPtr->style.padLeft = tmPtr->style.padRight = 4; tmPtr->style.padTop = tmPtr->style.padBottom = 4; return (Marker *)tmPtr; } static void ChildEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static void ChildGeometryProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin)); static void ChildCustodyProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin)); static Tk_GeomMgr winMarkerMgrInfo = { "graph", /* Name of geometry manager used by winfo */ ChildGeometryProc, /* Procedure to for new geometry requests */ ChildCustodyProc, /* Procedure when window is taken away */ }; /* * ---------------------------------------------------------------------- * * ConfigureWindowMarker -- * * This procedure is called to process an argv/argc list, plus * the Tk option database, in order to configure (or reconfigure) * a window marker. * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * * Side effects: * Configuration information, such as window pathname, placement, * etc. get set for markerPtr; old resources get freed, if there * were any. The marker is eventually redisplayed. * * ---------------------------------------------------------------------- */ static int ConfigureWindowMarker(markerPtr) Marker *markerPtr; { Graph *graphPtr = markerPtr->graphPtr; WindowMarker *wmPtr = (WindowMarker *)markerPtr; Tk_Window tkwin; if (wmPtr->pathName == NULL) { return TCL_OK; } tkwin = Tk_NameToWindow(graphPtr->interp, wmPtr->pathName, graphPtr->tkwin); if (tkwin == NULL) { return TCL_ERROR; } if (Tk_Parent(tkwin) != graphPtr->tkwin) { Tcl_AppendResult(graphPtr->interp, "\"", wmPtr->pathName, "\" is not a child of \"", Tk_PathName(graphPtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } if (tkwin != wmPtr->tkwin) { if (wmPtr->tkwin != NULL) { Tk_DeleteEventHandler(wmPtr->tkwin, StructureNotifyMask, ChildEventProc, wmPtr); Tk_ManageGeometry(wmPtr->tkwin, (Tk_GeomMgr *) 0, (ClientData)0); Tk_UnmapWindow(wmPtr->tkwin); } Tk_CreateEventHandler(tkwin, StructureNotifyMask, ChildEventProc, wmPtr); Tk_ManageGeometry(tkwin, &winMarkerMgrInfo, wmPtr); } wmPtr->tkwin = tkwin; wmPtr->flags |= MAP_ITEM; if (wmPtr->drawUnder) { graphPtr->flags |= REDRAW_BACKING_STORE; } Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } /* * ---------------------------------------------------------------------- * * MapWindowMarker -- * * Calculate the layout position for a window marker. Positional * information is saved in the marker. * * Results: * None. * * ---------------------------------------------------------------------- */ static void MapWindowMarker(markerPtr) Marker *markerPtr; { WindowMarker *wmPtr = (WindowMarker *)markerPtr; Graph *graphPtr = markerPtr->graphPtr; Extents2D exts; int width, height; if (wmPtr->tkwin == (Tk_Window)NULL) { return; } wmPtr->anchorPos = MapPoint(graphPtr, wmPtr->worldPts, &wmPtr->axes); width = Tk_ReqWidth(wmPtr->tkwin); height = Tk_ReqHeight(wmPtr->tkwin); if (wmPtr->reqWidth > 0) { width = wmPtr->reqWidth; } if (wmPtr->reqHeight > 0) { height = wmPtr->reqHeight; } wmPtr->anchorPos = Blt_TranslatePoint(&wmPtr->anchorPos, width, height, wmPtr->anchor); wmPtr->anchorPos.x += wmPtr->xOffset; wmPtr->anchorPos.y += wmPtr->yOffset; wmPtr->width = width; wmPtr->height = height; /* * Determine the bounding box of the window and test to see if it * is at least partially contained within the plotting area. */ exts.left = wmPtr->anchorPos.x; exts.top = wmPtr->anchorPos.y; exts.right = wmPtr->anchorPos.x + wmPtr->width - 1; exts.bottom = wmPtr->anchorPos.y + wmPtr->height - 1; wmPtr->clipped = BoxesDontOverlap(graphPtr, &exts); } /* * ---------------------------------------------------------------------- * * PointInWindowMarker -- * * ---------------------------------------------------------------------- */ static int PointInWindowMarker(markerPtr, samplePtr) Marker *markerPtr; Point2D *samplePtr; { WindowMarker *wmPtr = (WindowMarker *)markerPtr; return ((samplePtr->x >= wmPtr->anchorPos.x) && (samplePtr->x < (wmPtr->anchorPos.x + wmPtr->width)) && (samplePtr->y >= wmPtr->anchorPos.y) && (samplePtr->y < (wmPtr->anchorPos.y + wmPtr->height))); } /* * ---------------------------------------------------------------------- * * RegionInWindowMarker -- * * ---------------------------------------------------------------------- */ static int RegionInWindowMarker(markerPtr, extsPtr, enclosed) Marker *markerPtr; Extents2D *extsPtr; int enclosed; { WindowMarker *wmPtr = (WindowMarker *)markerPtr; if (wmPtr->nWorldPts < 1) { return FALSE; } if (enclosed) { return ((wmPtr->anchorPos.x >= extsPtr->left) && (wmPtr->anchorPos.y >= extsPtr->top) && ((wmPtr->anchorPos.x + wmPtr->width) <= extsPtr->right) && ((wmPtr->anchorPos.y + wmPtr->height) <= extsPtr->bottom)); } return !((wmPtr->anchorPos.x >= extsPtr->right) || (wmPtr->anchorPos.y >= extsPtr->bottom) || ((wmPtr->anchorPos.x + wmPtr->width) <= extsPtr->left) || ((wmPtr->anchorPos.y + wmPtr->height) <= extsPtr->top)); } /* * ---------------------------------------------------------------------- * * DrawWindowMarker -- * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static void DrawWindowMarker(markerPtr, drawable) Marker *markerPtr; Drawable drawable; /* Pixmap or window to draw into */ { WindowMarker *wmPtr = (WindowMarker *)markerPtr; if (wmPtr->tkwin == NULL) { return; } if ((wmPtr->height != Tk_Height(wmPtr->tkwin)) || (wmPtr->width != Tk_Width(wmPtr->tkwin)) || ((int)wmPtr->anchorPos.x != Tk_X(wmPtr->tkwin)) || ((int)wmPtr->anchorPos.y != Tk_Y(wmPtr->tkwin))) { Tk_MoveResizeWindow(wmPtr->tkwin, (int)wmPtr->anchorPos.x, (int)wmPtr->anchorPos.y, wmPtr->width, wmPtr->height); } if (!Tk_IsMapped(wmPtr->tkwin)) { Tk_MapWindow(wmPtr->tkwin); } } /* * ---------------------------------------------------------------------- * * WindowMarkerToPostScript -- * * ---------------------------------------------------------------------- */ static void WindowMarkerToPostScript(markerPtr, psToken) Marker *markerPtr; PsToken psToken; { WindowMarker *wmPtr = (WindowMarker *)markerPtr; if (wmPtr->tkwin == NULL) { return; } if (Tk_IsMapped(wmPtr->tkwin)) { Blt_WindowToPostScript(psToken, wmPtr->tkwin, wmPtr->anchorPos.x, wmPtr->anchorPos.y); } } /* * ---------------------------------------------------------------------- * * FreeWindowMarker -- * * Destroys the structure containing the attributes of the window * marker. * * Results: * None. * * Side effects: * Window is destroyed and removed from the screen. * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static void FreeWindowMarker(graphPtr, markerPtr) Graph *graphPtr; Marker *markerPtr; { WindowMarker *wmPtr = (WindowMarker *)markerPtr; if (wmPtr->tkwin != NULL) { Tk_DeleteEventHandler(wmPtr->tkwin, StructureNotifyMask, ChildEventProc, wmPtr); Tk_ManageGeometry(wmPtr->tkwin, (Tk_GeomMgr *) 0, (ClientData)0); Tk_DestroyWindow(wmPtr->tkwin); } } /* * ---------------------------------------------------------------------- * * CreateWindowMarker -- * * Allocate memory and initialize methods for the new window marker. * * Results: * The pointer to the newly allocated marker structure is returned. * * Side effects: * Memory is allocated for the window marker structure. * * ---------------------------------------------------------------------- */ static Marker * CreateWindowMarker() { WindowMarker *wmPtr; wmPtr = Blt_Calloc(1, sizeof(WindowMarker)); if (wmPtr != NULL) { wmPtr->classPtr = &windowMarkerClass; } return (Marker *)wmPtr; } /* * ---------------------------------------------------------------------- * * ChildEventProc -- * * This procedure is invoked whenever StructureNotify events * occur for a window that's managed as part of a graph window * marker. This procedure's only purpose is to clean up when * windows are deleted. * * Results: * None. * * Side effects: * The window is disassociated from the window item when it is * deleted. * * ---------------------------------------------------------------------- */ static void ChildEventProc(clientData, eventPtr) ClientData clientData; /* Pointer to record describing window item. */ XEvent *eventPtr; /* Describes what just happened. */ { WindowMarker *wmPtr = clientData; if (eventPtr->type == DestroyNotify) { wmPtr->tkwin = NULL; } } /* * ---------------------------------------------------------------------- * * ChildGeometryProc -- * * This procedure is invoked whenever a window that's associated * with a window item changes its requested dimensions. * * Results: * None. * * Side effects: * The size and location on the window of the window may change, * depending on the options specified for the window item. * * ---------------------------------------------------------------------- */ /* ARGSUSED */ static void ChildGeometryProc(clientData, tkwin) ClientData clientData; /* Pointer to record for window item. */ Tk_Window tkwin; /* Window that changed its desired size. */ { WindowMarker *wmPtr = clientData; if (wmPtr->reqWidth == 0) { wmPtr->width = Tk_ReqWidth(tkwin); } if (wmPtr->reqHeight == 0) { wmPtr->height = Tk_ReqHeight(tkwin); } } /* * ---------------------------------------------------------------------- * * ChildCustodyProc -- * * This procedure is invoked when an embedded window has been * stolen by another geometry manager. The information and * memory associated with the widget is released. * * Results: * None. * * Side effects: * Arranges for the graph to be redrawn without the embedded * widget at the next idle point. * * ---------------------------------------------------------------------- */ /* ARGSUSED */ static void ChildCustodyProc(clientData, tkwin) ClientData clientData; /* Window marker to be destroyed. */ Tk_Window tkwin; /* Not used. */ { Marker *markerPtr = clientData; Graph *graphPtr; graphPtr = markerPtr->graphPtr; DestroyMarker(markerPtr); /* * Not really needed. We should get an Expose event when the * child window is unmapped. */ Blt_EventuallyRedrawGraph(graphPtr); } /* * ---------------------------------------------------------------------- * * MapLineMarker -- * * Calculate the layout position for a line marker. Positional * information is saved in the marker. The line positions are * stored in an array of points (malloc'ed). * * Results: * None. * * ---------------------------------------------------------------------- */ static void MapLineMarker(markerPtr) Marker *markerPtr; { Graph *graphPtr = markerPtr->graphPtr; LineMarker *lmPtr = (LineMarker *)markerPtr; Point2D *srcPtr, *endPtr; Segment2D *segments, *segPtr; Point2D p, q, next; Extents2D exts; lmPtr->nSegments = 0; if (lmPtr->segments != NULL) { Blt_Free(lmPtr->segments); } if (lmPtr->nWorldPts < 2) { return; /* Too few points */ } Blt_GraphExtents(graphPtr, &exts); /* * Allow twice the number of world coordinates. The line will * represented as series of line segments, not one continous * polyline. This is because clipping against the plot area may * chop the line into several disconnected segments. */ segments = Blt_Malloc(lmPtr->nWorldPts * sizeof(Segment2D)); srcPtr = lmPtr->worldPts; p = MapPoint(graphPtr, srcPtr, &lmPtr->axes); p.x += lmPtr->xOffset; p.y += lmPtr->yOffset; segPtr = segments; for (srcPtr++, endPtr = lmPtr->worldPts + lmPtr->nWorldPts; srcPtr < endPtr; srcPtr++) { next = MapPoint(graphPtr, srcPtr, &lmPtr->axes); next.x += lmPtr->xOffset; next.y += lmPtr->yOffset; q = next; if (Blt_LineRectClip(&exts, &p, &q)) { segPtr->p = p; segPtr->q = q; segPtr++; } p = next; } lmPtr->nSegments = segPtr - segments; lmPtr->segments = segments; lmPtr->clipped = (lmPtr->nSegments == 0); } static int PointInLineMarker(markerPtr, samplePtr) Marker *markerPtr; Point2D *samplePtr; { LineMarker *lmPtr = (LineMarker *)markerPtr; return Blt_PointInSegments(samplePtr, lmPtr->segments, lmPtr->nSegments, (double)lmPtr->graphPtr->halo); } /* * ---------------------------------------------------------------------- * * RegionInLineMarker -- * * ---------------------------------------------------------------------- */ static int RegionInLineMarker(markerPtr, extsPtr, enclosed) Marker *markerPtr; Extents2D *extsPtr; int enclosed; { LineMarker *lmPtr = (LineMarker *)markerPtr; if (lmPtr->nWorldPts < 2) { return FALSE; } if (enclosed) { Point2D p; Point2D *pointPtr, *endPtr; for (pointPtr = lmPtr->worldPts, endPtr = lmPtr->worldPts + lmPtr->nWorldPts; pointPtr < endPtr; pointPtr++) { p = MapPoint(lmPtr->graphPtr, pointPtr, &lmPtr->axes); if ((p.x < extsPtr->left) && (p.x > extsPtr->right) && (p.y < extsPtr->top) && (p.y > extsPtr->bottom)) { return FALSE; } } return TRUE; /* All points inside bounding box. */ } else { Point2D p, q; int count; Point2D *pointPtr, *endPtr; count = 0; for (pointPtr = lmPtr->worldPts, endPtr = lmPtr->worldPts + (lmPtr->nWorldPts - 1); pointPtr < endPtr; pointPtr++) { p = MapPoint(lmPtr->graphPtr, pointPtr, &lmPtr->axes); q = MapPoint(lmPtr->graphPtr, pointPtr + 1, &lmPtr->axes); if (Blt_LineRectClip(extsPtr, &p, &q)) { count++; } } return (count > 0); /* At least 1 segment passes through region. */ } } /* * ---------------------------------------------------------------------- * * DrawLineMarker -- * * ---------------------------------------------------------------------- */ static void DrawLineMarker(markerPtr, drawable) Marker *markerPtr; Drawable drawable; /* Pixmap or window to draw into */ { LineMarker *lmPtr = (LineMarker *)markerPtr; if (lmPtr->nSegments > 0) { Graph *graphPtr = markerPtr->graphPtr; Blt_Draw2DSegments(graphPtr->display, drawable, lmPtr->gc, lmPtr->segments, lmPtr->nSegments); if (lmPtr->xor) { /* Toggle the drawing state */ lmPtr->xorState = (lmPtr->xorState == 0); } } } /* * ---------------------------------------------------------------------- * * ConfigureLineMarker -- * * This procedure is called to process an argv/argc list, plus * the Tk option database, in order to configure (or reconfigure) * a line marker. * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * * Side effects: * Configuration information, such as line width, colors, dashes, * etc. get set for markerPtr; old resources get freed, if there * were any. The marker is eventually redisplayed. * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static int ConfigureLineMarker(markerPtr) Marker *markerPtr; { Graph *graphPtr = markerPtr->graphPtr; LineMarker *lmPtr = (LineMarker *)markerPtr; GC newGC; XGCValues gcValues; unsigned long gcMask; Drawable drawable; drawable = Tk_WindowId(graphPtr->tkwin); gcMask = (GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle); if (lmPtr->outlineColor != NULL) { gcMask |= GCForeground; gcValues.foreground = lmPtr->outlineColor->pixel; } if (lmPtr->fillColor != NULL) { gcMask |= GCBackground; gcValues.background = lmPtr->fillColor->pixel; } gcValues.cap_style = lmPtr->capStyle; gcValues.join_style = lmPtr->joinStyle; gcValues.line_width = LineWidth(lmPtr->lineWidth); gcValues.line_style = LineSolid; if (LineIsDashed(lmPtr->dashes)) { gcValues.line_style = (gcMask & GCBackground) ? LineDoubleDash : LineOnOffDash; } if (lmPtr->xor) { unsigned long pixel; gcValues.function = GXxor; gcMask |= GCFunction; if (graphPtr->plotBg == NULL) { pixel = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin)); } else { pixel = graphPtr->plotBg->pixel; } if (gcMask & GCBackground) { gcValues.background ^= pixel; } gcValues.foreground ^= pixel; if (drawable != None) { DrawLineMarker(markerPtr, drawable); } } newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues); if (lmPtr->gc != NULL) { Blt_FreePrivateGC(graphPtr->display, lmPtr->gc); } if (LineIsDashed(lmPtr->dashes)) { Blt_SetDashes(graphPtr->display, newGC, &lmPtr->dashes); } lmPtr->gc = newGC; if (lmPtr->xor) { if (drawable != None) { MapLineMarker(markerPtr); DrawLineMarker(markerPtr, drawable); } return TCL_OK; } lmPtr->flags |= MAP_ITEM; if (lmPtr->drawUnder) { graphPtr->flags |= REDRAW_BACKING_STORE; } Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } /* * ---------------------------------------------------------------------- * * LineMarkerToPostScript -- * * Prints postscript commands to display the connect line. * Dashed lines need to be handled specially, especially if a * background color is designated. * * Results: * None. * * Side effects: * PostScript output commands are saved in the interpreter * (infoPtr->interp) result field. * * ---------------------------------------------------------------------- */ static void LineMarkerToPostScript(markerPtr, psToken) Marker *markerPtr; PsToken psToken; { LineMarker *lmPtr = (LineMarker *)markerPtr; if (lmPtr->nSegments > 0) { Blt_LineAttributesToPostScript(psToken, lmPtr->outlineColor, lmPtr->lineWidth, &lmPtr->dashes, lmPtr->capStyle, lmPtr->joinStyle); if ((LineIsDashed(lmPtr->dashes)) && (lmPtr->fillColor != NULL)) { Blt_AppendToPostScript(psToken, "/DashesProc {\n gsave\n ", (char *)NULL); Blt_BackgroundToPostScript(psToken, lmPtr->fillColor); 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); } Blt_2DSegmentsToPostScript(psToken, lmPtr->segments, lmPtr->nSegments); } } /* * ---------------------------------------------------------------------- * * FreeLineMarker -- * * Destroys the structure and attributes of a line marker. * * Results: * None. * * Side effects: * Line attributes (GCs, colors, stipple, etc) get released. * Memory is deallocated, X resources are freed. * * ---------------------------------------------------------------------- */ static void FreeLineMarker(graphPtr, markerPtr) Graph *graphPtr; Marker *markerPtr; { LineMarker *lmPtr = (LineMarker *)markerPtr; if (lmPtr->gc != NULL) { Blt_FreePrivateGC(graphPtr->display, lmPtr->gc); } if (lmPtr->segments != NULL) { Blt_Free(lmPtr->segments); } } /* * ---------------------------------------------------------------------- * * CreateLineMarker -- * * Allocate memory and initialize methods for a new line marker. * * Results: * The pointer to the newly allocated marker structure is returned. * * Side effects: * Memory is allocated for the line marker structure. * * ---------------------------------------------------------------------- */ static Marker * CreateLineMarker() { LineMarker *lmPtr; lmPtr = Blt_Calloc(1, sizeof(LineMarker)); if (lmPtr != NULL) { lmPtr->classPtr = &lineMarkerClass; lmPtr->xor = FALSE; lmPtr->capStyle = CapButt; lmPtr->joinStyle = JoinMiter; } return (Marker *)lmPtr; } /* * ---------------------------------------------------------------------- * * MapPolygonMarker -- * * Calculate the layout position for a polygon marker. Positional * information is saved in the polygon in an array of points * (malloc'ed). * * Results: * None. * * ---------------------------------------------------------------------- */ static void MapPolygonMarker(markerPtr) Marker *markerPtr; { Graph *graphPtr = markerPtr->graphPtr; PolygonMarker *pmPtr = (PolygonMarker *)markerPtr; Point2D *srcPtr, *destPtr, *endPtr; Point2D *screenPts; Extents2D exts; int nScreenPts; if (pmPtr->outlinePts != NULL) { Blt_Free(pmPtr->outlinePts); pmPtr->outlinePts = NULL; pmPtr->nOutlinePts = 0; } if (pmPtr->fillPts != NULL) { Blt_Free(pmPtr->fillPts); pmPtr->fillPts = NULL; pmPtr->nFillPts = 0; } if (pmPtr->screenPts != NULL) { Blt_Free(pmPtr->screenPts); pmPtr->screenPts = NULL; } if (pmPtr->nWorldPts < 3) { return; /* Too few points */ } /* * Allocate and fill a temporary array to hold the screen * coordinates of the polygon. */ nScreenPts = pmPtr->nWorldPts + 1; screenPts = Blt_Malloc((nScreenPts + 1) * sizeof(Point2D)); endPtr = pmPtr->worldPts + pmPtr->nWorldPts; destPtr = screenPts; for (srcPtr = pmPtr->worldPts; srcPtr < endPtr; srcPtr++) { *destPtr = MapPoint(graphPtr, srcPtr, &pmPtr->axes); destPtr->x += pmPtr->xOffset; destPtr->y += pmPtr->yOffset; destPtr++; } *destPtr = screenPts[0]; Blt_GraphExtents(graphPtr, &exts); pmPtr->clipped = TRUE; if (pmPtr->fill.fgColor != NULL) { /* Polygon fill required. */ Point2D *fillPts; int n; fillPts = Blt_Malloc(sizeof(Point2D) * nScreenPts * 3); assert(fillPts); n = Blt_PolyRectClip(&exts, screenPts, pmPtr->nWorldPts, fillPts); if (n < 3) { Blt_Free(fillPts); } else { pmPtr->nFillPts = n; pmPtr->fillPts = fillPts; pmPtr->clipped = FALSE; } } if ((pmPtr->outline.fgColor != NULL) && (pmPtr->lineWidth > 0)) { Segment2D *outlinePts; register Segment2D *segPtr; /* * Generate line segments representing the polygon outline. * The resulting outline may or may not be closed from * viewport clipping. */ outlinePts = Blt_Malloc(nScreenPts * sizeof(Segment2D)); if (outlinePts == NULL) { return; /* Can't allocate point array */ } /* * Note that this assumes that the point array contains an * extra point that closes the polygon. */ segPtr = outlinePts; for (srcPtr = screenPts, endPtr = screenPts + (nScreenPts - 1); srcPtr < endPtr; srcPtr++) { segPtr->p = srcPtr[0]; segPtr->q = srcPtr[1]; if (Blt_LineRectClip(&exts, &segPtr->p, &segPtr->q)) { segPtr++; } } pmPtr->nOutlinePts = segPtr - outlinePts; pmPtr->outlinePts = outlinePts; if (pmPtr->nOutlinePts > 0) { pmPtr->clipped = FALSE; } } pmPtr->screenPts = screenPts; } static int PointInPolygonMarker(markerPtr, samplePtr) Marker *markerPtr; Point2D *samplePtr; { PolygonMarker *pmPtr = (PolygonMarker *)markerPtr; if ((pmPtr->nWorldPts >= 3) && (pmPtr->screenPts != NULL)) { return Blt_PointInPolygon(samplePtr, pmPtr->screenPts, pmPtr->nWorldPts + 1); } return FALSE; } /* * ---------------------------------------------------------------------- * * RegionInPolygonMarker -- * * ---------------------------------------------------------------------- */ static int RegionInPolygonMarker(markerPtr, extsPtr, enclosed) Marker *markerPtr; Extents2D *extsPtr; int enclosed; { PolygonMarker *pmPtr = (PolygonMarker *)markerPtr; if ((pmPtr->nWorldPts >= 3) && (pmPtr->screenPts != NULL)) { return Blt_RegionInPolygon(extsPtr, pmPtr->screenPts, pmPtr->nWorldPts, enclosed); } return FALSE; } static void DrawPolygonMarker(markerPtr, drawable) Marker *markerPtr; Drawable drawable; /* Pixmap or window to draw into */ { Graph *graphPtr = markerPtr->graphPtr; PolygonMarker *pmPtr = (PolygonMarker *)markerPtr; /* Draw polygon fill region */ if ((pmPtr->nFillPts > 0) && (pmPtr->fill.fgColor != NULL)) { XPoint *destPtr, *pointArr; Point2D *srcPtr, *endPtr; pointArr = Blt_Malloc(pmPtr->nFillPts * sizeof(XPoint)); if (pointArr == NULL) { return; } destPtr = pointArr; for (srcPtr = pmPtr->fillPts, endPtr = pmPtr->fillPts + pmPtr->nFillPts; srcPtr < endPtr; srcPtr++) { destPtr->x = (short int)srcPtr->x; destPtr->y = (short int)srcPtr->y; destPtr++; } XFillPolygon(graphPtr->display, drawable, pmPtr->fillGC, pointArr, pmPtr->nFillPts, Complex, CoordModeOrigin); Blt_Free(pointArr); } /* and then the outline */ if ((pmPtr->nOutlinePts > 0) && (pmPtr->lineWidth > 0) && (pmPtr->outline.fgColor != NULL)) { Blt_Draw2DSegments(graphPtr->display, drawable, pmPtr->outlineGC, pmPtr->outlinePts, pmPtr->nOutlinePts); } } static void PolygonMarkerToPostScript(markerPtr, psToken) Marker *markerPtr; PsToken psToken; { Graph *graphPtr = markerPtr->graphPtr; PolygonMarker *pmPtr = (PolygonMarker *)markerPtr; if (pmPtr->fill.fgColor != NULL) { /* * Options: fg bg * Draw outline only. * x Draw solid or stipple. * x x Draw solid or stipple. */ /* Create a path to use for both the polygon and its outline. */ Blt_PathToPostScript(psToken, pmPtr->fillPts, pmPtr->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 (pmPtr->fill.bgColor != NULL) { Blt_BackgroundToPostScript(psToken, pmPtr->fill.bgColor); Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL); } Blt_ForegroundToPostScript(psToken, pmPtr->fill.fgColor); if (pmPtr->stipple != None) { /* Draw the stipple in the foreground color. */ Blt_StippleToPostScript(psToken, graphPtr->display, pmPtr->stipple); } else { Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL); } } /* Draw the outline in the foreground color. */ if ((pmPtr->lineWidth > 0) && (pmPtr->outline.fgColor != NULL)) { /* Set up the line attributes. */ Blt_LineAttributesToPostScript(psToken, pmPtr->outline.fgColor, pmPtr->lineWidth, &pmPtr->dashes, pmPtr->capStyle, pmPtr->joinStyle); /* * Define on-the-fly a PostScript macro "DashesProc" that * will be executed for each call to the Polygon drawing * routine. If the line isn't dashed, simply make this an * empty definition. */ if ((pmPtr->outline.bgColor != NULL) && (LineIsDashed(pmPtr->dashes))) { Blt_AppendToPostScript(psToken, "/DashesProc {\n", "gsave\n ", (char *)NULL); Blt_BackgroundToPostScript(psToken, pmPtr->outline.bgColor); 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); } Blt_2DSegmentsToPostScript(psToken, pmPtr->outlinePts, pmPtr->nOutlinePts); } } /* * ---------------------------------------------------------------------- * * ConfigurePolygonMarker -- * * This procedure is called to process an argv/argc list, plus * the Tk option database, in order to configure (or reconfigure) * a polygon marker. * * Results: * A standard Tcl result. If TCL_ERROR is returned, then * interp->result contains an error message. * * Side effects: * Configuration information, such as polygon color, dashes, * fillstyle, etc. get set for markerPtr; old resources get * freed, if there were any. The marker is eventually * redisplayed. * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static int ConfigurePolygonMarker(markerPtr) Marker *markerPtr; { Graph *graphPtr = markerPtr->graphPtr; PolygonMarker *pmPtr = (PolygonMarker *)markerPtr; GC newGC; XGCValues gcValues; unsigned long gcMask; Drawable drawable; drawable = Tk_WindowId(graphPtr->tkwin); gcMask = (GCLineWidth | GCLineStyle); if (pmPtr->outline.fgColor != NULL) { gcMask |= GCForeground; gcValues.foreground = pmPtr->outline.fgColor->pixel; } if (pmPtr->outline.bgColor != NULL) { gcMask |= GCBackground; gcValues.background = pmPtr->outline.bgColor->pixel; } gcMask |= (GCCapStyle | GCJoinStyle); gcValues.cap_style = pmPtr->capStyle; gcValues.join_style = pmPtr->joinStyle; gcValues.line_style = LineSolid; gcValues.dash_offset = 0; gcValues.line_width = LineWidth(pmPtr->lineWidth); if (LineIsDashed(pmPtr->dashes)) { gcValues.line_style = (pmPtr->outline.bgColor == NULL) ? LineOnOffDash : LineDoubleDash; } if (pmPtr->xor) { unsigned long pixel; gcValues.function = GXxor; gcMask |= GCFunction; if (graphPtr->plotBg == NULL) { /* The graph's color option may not have been set yet */ pixel = WhitePixelOfScreen(Tk_Screen(graphPtr->tkwin)); } else { pixel = graphPtr->plotBg->pixel; } if (gcMask & GCBackground) { gcValues.background ^= pixel; } gcValues.foreground ^= pixel; if (drawable != None) { DrawPolygonMarker(markerPtr, drawable); } } newGC = Blt_GetPrivateGC(graphPtr->tkwin, gcMask, &gcValues); if (LineIsDashed(pmPtr->dashes)) { Blt_SetDashes(graphPtr->display, newGC, &pmPtr->dashes); } if (pmPtr->outlineGC != NULL) { Blt_FreePrivateGC(graphPtr->display, pmPtr->outlineGC); } pmPtr->outlineGC = newGC; gcMask = 0; if (pmPtr->fill.fgColor != NULL) { gcMask |= GCForeground; gcValues.foreground = pmPtr->fill.fgColor->pixel; } if (pmPtr->fill.bgColor != NULL) { gcMask |= GCBackground; gcValues.background = pmPtr->fill.bgColor->pixel; } if (pmPtr->stipple != None) { gcValues.stipple = pmPtr->stipple; gcValues.fill_style = (pmPtr->fill.bgColor != NULL) ? FillOpaqueStippled : FillStippled; gcMask |= (GCStipple | GCFillStyle); } newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues); if (pmPtr->fillGC != NULL) { Tk_FreeGC(graphPtr->display, pmPtr->fillGC); } pmPtr->fillGC = newGC; if ((gcMask == 0) && !(graphPtr->flags & RESET_AXES) && (pmPtr->xor)) { if (drawable != None) { MapPolygonMarker(markerPtr); DrawPolygonMarker(markerPtr, drawable); } return TCL_OK; } pmPtr->flags |= MAP_ITEM; if (pmPtr->drawUnder) { graphPtr->flags |= REDRAW_BACKING_STORE; } Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } /* * ---------------------------------------------------------------------- * * FreePolygonMarker -- * * Release memory and resources allocated for the polygon element. * * Results: * None. * * Side effects: * Everything associated with the polygon element is freed up. * * ---------------------------------------------------------------------- */ static void FreePolygonMarker(graphPtr, markerPtr) Graph *graphPtr; Marker *markerPtr; { PolygonMarker *pmPtr = (PolygonMarker *)markerPtr; if (pmPtr->fillGC != NULL) { Tk_FreeGC(graphPtr->display, pmPtr->fillGC); } if (pmPtr->outlineGC != NULL) { Blt_FreePrivateGC(graphPtr->display, pmPtr->outlineGC); } if (pmPtr->fillPts != NULL) { Blt_Free(pmPtr->fillPts); } if (pmPtr->outlinePts != NULL) { Blt_Free(pmPtr->outlinePts); } if (pmPtr->screenPts != NULL) { Blt_Free(pmPtr->screenPts); } Blt_FreeColorPair(&pmPtr->outline); Blt_FreeColorPair(&pmPtr->fill); } /* * ---------------------------------------------------------------------- * * CreatePolygonMarker -- * * Allocate memory and initialize methods for the new polygon * marker. * * Results: * The pointer to the newly allocated marker structure is * returned. * * Side effects: * Memory is allocated for the polygon marker structure. * * ---------------------------------------------------------------------- */ static Marker * CreatePolygonMarker() { PolygonMarker *pmPtr; pmPtr = Blt_Calloc(1, sizeof(PolygonMarker)); if (pmPtr != NULL) { pmPtr->classPtr = &polygonMarkerClass; pmPtr->capStyle = CapButt; pmPtr->joinStyle = JoinMiter; } return (Marker *)pmPtr; } static int NameToMarker(graphPtr, name, markerPtrPtr) Graph *graphPtr; char *name; Marker **markerPtrPtr; { Blt_HashEntry *hPtr; hPtr = Blt_FindHashEntry(&graphPtr->markers.table, name); if (hPtr != NULL) { *markerPtrPtr = (Marker *)Blt_GetHashValue(hPtr); return TCL_OK; } Tcl_AppendResult(graphPtr->interp, "can't find marker \"", name, "\" in \"", Tk_PathName(graphPtr->tkwin), (char *)NULL); return TCL_ERROR; } static int RenameMarker(graphPtr, markerPtr, oldName, newName) Graph *graphPtr; Marker *markerPtr; char *oldName, *newName; { int isNew; Blt_HashEntry *hPtr; /* Rename the marker only if no marker already exists by that name */ hPtr = Blt_CreateHashEntry(&graphPtr->markers.table, newName, &isNew); if (!isNew) { Tcl_AppendResult(graphPtr->interp, "can't rename marker: \"", newName, "\" already exists", (char *)NULL); return TCL_ERROR; } markerPtr->name = Blt_Strdup(newName); markerPtr->hashPtr = hPtr; Blt_SetHashValue(hPtr, (char *)markerPtr); /* Delete the old hash entry */ hPtr = Blt_FindHashEntry(&graphPtr->markers.table, oldName); Blt_DeleteHashEntry(&graphPtr->markers.table, hPtr); if (oldName != NULL) { Blt_Free(oldName); } return TCL_OK; } /* * ---------------------------------------------------------------------- * * NamesOp -- * * Returns a list of marker identifiers in interp->result; * * Results: * The return value is a standard Tcl result. * * ---------------------------------------------------------------------- */ static int NamesOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Marker *markerPtr; Blt_ChainLink *linkPtr; register int i; Tcl_ResetResult(interp); for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { markerPtr = Blt_ChainGetValue(linkPtr); if (argc == 3) { Tcl_AppendElement(interp, markerPtr->name); continue; } for (i = 3; i < argc; i++) { if (Tcl_StringMatch(markerPtr->name, argv[i])) { Tcl_AppendElement(interp, markerPtr->name); break; } } } return TCL_OK; } ClientData Blt_MakeMarkerTag(graphPtr, tagName) Graph *graphPtr; char *tagName; { Blt_HashEntry *hPtr; int isNew; hPtr = Blt_CreateHashEntry(&graphPtr->markers.tagTable, tagName, &isNew); assert(hPtr); return Blt_GetHashKey(&graphPtr->markers.tagTable, hPtr); } /* *---------------------------------------------------------------------- * * BindOp -- * * .g element bind elemName sequence command * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int BindOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { if (argc == 3) { Blt_HashEntry *hPtr; Blt_HashSearch cursor; char *tag; for (hPtr = Blt_FirstHashEntry(&graphPtr->markers.tagTable, &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { tag = Blt_GetHashKey(&graphPtr->markers.tagTable, hPtr); Tcl_AppendElement(interp, tag); } return TCL_OK; } return Blt_ConfigureBindings(interp, graphPtr->bindTable, Blt_MakeMarkerTag(graphPtr, argv[3]), argc - 4, argv + 4); } /* * ---------------------------------------------------------------------- * * CgetOp -- * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static int CgetOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Marker *markerPtr; if (NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) { return TCL_ERROR; } if (Tk_ConfigureValue(interp, graphPtr->tkwin, markerPtr->classPtr->configSpecs, (char *)markerPtr, argv[4], 0) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } /* * ---------------------------------------------------------------------- * * ConfigureOp -- * * Results: * The return value is a standard Tcl result. * * Side Effects: * * ---------------------------------------------------------------------- */ static int ConfigureOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Marker *markerPtr; int flags = TK_CONFIG_ARGV_ONLY; char *oldName; int nNames, nOpts; char **options; register int i; int under; /* Figure out where the option value pairs begin */ argc -= 3; argv += 3; for (i = 0; i < argc; i++) { if (argv[i][0] == '-') { break; } if (NameToMarker(graphPtr, argv[i], &markerPtr) != TCL_OK) { return TCL_ERROR; } } nNames = i; /* Number of element names specified */ nOpts = argc - i; /* Number of options specified */ options = argv + nNames; /* Start of options in argv */ for (i = 0; i < nNames; i++) { NameToMarker(graphPtr, argv[i], &markerPtr); if (nOpts == 0) { return Tk_ConfigureInfo(interp, graphPtr->tkwin, markerPtr->classPtr->configSpecs, (char *)markerPtr, (char *)NULL, flags); } else if (nOpts == 1) { return Tk_ConfigureInfo(interp, graphPtr->tkwin, markerPtr->classPtr->configSpecs, (char *)markerPtr, options[0], flags); } /* Save the old marker. */ oldName = markerPtr->name; under = markerPtr->drawUnder; if (Tk_ConfigureWidget(interp, graphPtr->tkwin, markerPtr->classPtr->configSpecs, nOpts, options, (char *)markerPtr, flags) != TCL_OK) { return TCL_ERROR; } if (oldName != markerPtr->name) { if (RenameMarker(graphPtr, markerPtr, oldName, markerPtr->name) != TCL_OK) { markerPtr->name = oldName; return TCL_ERROR; } } if ((*markerPtr->classPtr->configProc) (markerPtr) != TCL_OK) { return TCL_ERROR; } if (markerPtr->drawUnder != under) { graphPtr->flags |= REDRAW_BACKING_STORE; } } return TCL_OK; } /* * ---------------------------------------------------------------------- * * CreateOp -- * * This procedure creates and initializes a new marker. * * Results: * The return value is a pointer to a structure describing * the new element. If an error occurred, then the return * value is NULL and an error message is left in interp->result. * * Side effects: * Memory is allocated, etc. * * ---------------------------------------------------------------------- */ static int CreateOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Marker *markerPtr; Blt_HashEntry *hPtr; int isNew; Blt_Uid classUid; register int i; char *name; char string[200]; unsigned int length; char c; c = argv[3][0]; /* Create the new marker based upon the given type */ if ((c == 't') && (strcmp(argv[3], "text") == 0)) { classUid = bltTextMarkerUid; } else if ((c == 'l') && (strcmp(argv[3], "line") == 0)) { classUid = bltLineMarkerUid; } else if ((c == 'p') && (strcmp(argv[3], "polygon") == 0)) { classUid = bltPolygonMarkerUid; } else if ((c == 'i') && (strcmp(argv[3], "image") == 0)) { classUid = bltImageMarkerUid; } else if ((c == 'b') && (strcmp(argv[3], "bitmap") == 0)) { classUid = bltBitmapMarkerUid; } else if ((c == 'w') && (strcmp(argv[3], "window") == 0)) { classUid = bltWindowMarkerUid; } else { Tcl_AppendResult(interp, "unknown marker type \"", argv[3], "\": should be \"text\", \"line\", \"polygon\", \"bitmap\", \"image\", or \ \"window\"", (char *)NULL); return TCL_ERROR; } /* Scan for "-name" option. We need it for the component name */ name = NULL; for (i = 4; i < argc; i += 2) { length = strlen(argv[i]); if ((length > 1) && (strncmp(argv[i], "-name", length) == 0)) { name = argv[i + 1]; break; } } /* If no name was given for the marker, make up one. */ if (name == NULL) { sprintf(string, "marker%d", graphPtr->nextMarkerId++); name = string; } else if (name[0] == '-') { Tcl_AppendResult(interp, "name of marker \"", name, "\" can't start with a '-'", (char *)NULL); return TCL_ERROR; } markerPtr = CreateMarker(graphPtr, name, classUid); if (Blt_ConfigureWidgetComponent(interp, graphPtr->tkwin, name, markerPtr->classUid, markerPtr->classPtr->configSpecs, argc - 4, argv + 4, (char *)markerPtr, 0) != TCL_OK) { DestroyMarker(markerPtr); return TCL_ERROR; } if ((*markerPtr->classPtr->configProc) (markerPtr) != TCL_OK) { DestroyMarker(markerPtr); return TCL_ERROR; } hPtr = Blt_CreateHashEntry(&graphPtr->markers.table, name, &isNew); if (!isNew) { Marker *oldMarkerPtr; /* * Marker by the same name already exists. Delete the old * marker and it's list entry. But save the hash entry. */ oldMarkerPtr = (Marker *)Blt_GetHashValue(hPtr); oldMarkerPtr->hashPtr = NULL; DestroyMarker(oldMarkerPtr); } Blt_SetHashValue(hPtr, markerPtr); markerPtr->hashPtr = hPtr; markerPtr->linkPtr = Blt_ChainAppend(graphPtr->markers.displayList, markerPtr); if (markerPtr->drawUnder) { graphPtr->flags |= REDRAW_BACKING_STORE; } Blt_EventuallyRedrawGraph(graphPtr); Tcl_SetResult(interp, name, TCL_VOLATILE); return TCL_OK; } /* * ---------------------------------------------------------------------- * * DeleteOp -- * * Deletes the marker given by markerId. * * Results: * The return value is a standard Tcl result. * * Side Effects: * Graph will be redrawn to reflect the new display list. * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static int DeleteOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; /* Not used. */ int argc; char **argv; { Marker *markerPtr; register int i; for (i = 3; i < argc; i++) { if (NameToMarker(graphPtr, argv[i], &markerPtr) == TCL_OK) { DestroyMarker(markerPtr); } } Tcl_ResetResult(interp); Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * GetOp -- * * Find the legend entry from the given argument. The argument * can be either a screen position "@x,y" or the name of an * element. * * I don't know how useful it is to test with the name of an * element. * * Results: * A standard Tcl result. * * Side Effects: * Graph will be redrawn to reflect the new legend attributes. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int GetOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; /* Not used. */ char *argv[]; { register Marker *markerPtr; if ((argv[3][0] == 'c') && (strcmp(argv[3], "current") == 0)) { markerPtr = (Marker *)Blt_GetCurrentItem(graphPtr->bindTable); /* Report only on markers. */ if (markerPtr == NULL) { return TCL_OK; } if ((markerPtr->classUid == bltBitmapMarkerUid) || (markerPtr->classUid == bltLineMarkerUid) || (markerPtr->classUid == bltWindowMarkerUid) || (markerPtr->classUid == bltPolygonMarkerUid) || (markerPtr->classUid == bltTextMarkerUid) || (markerPtr->classUid == bltImageMarkerUid)) { Tcl_SetResult(interp, markerPtr->name, TCL_VOLATILE); } } return TCL_OK; } /* * ---------------------------------------------------------------------- * * RelinkOp -- * * Reorders the marker (given by the first name) before/after * the another marker (given by the second name) in the * marker display list. If no second name is given, the * marker is placed at the beginning/end of the list. * * Results: * A standard Tcl result. * * Side Effects: * Graph will be redrawn to reflect the new display list. * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static int RelinkOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; /* Not used. */ int argc; char **argv; { Blt_ChainLink *linkPtr, *placePtr; Marker *markerPtr; /* Find the marker to be raised or lowered. */ if (NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) { return TCL_ERROR; } /* Right now it's assumed that all markers are always in the display list. */ linkPtr = markerPtr->linkPtr; Blt_ChainUnlinkLink(graphPtr->markers.displayList, markerPtr->linkPtr); placePtr = NULL; if (argc == 5) { if (NameToMarker(graphPtr, argv[4], &markerPtr) != TCL_OK) { return TCL_ERROR; } placePtr = markerPtr->linkPtr; } /* Link the marker at its new position. */ if (argv[2][0] == 'a') { Blt_ChainLinkAfter(graphPtr->markers.displayList, linkPtr, placePtr); } else { Blt_ChainLinkBefore(graphPtr->markers.displayList, linkPtr, placePtr); } if (markerPtr->drawUnder) { graphPtr->flags |= REDRAW_BACKING_STORE; } Blt_EventuallyRedrawGraph(graphPtr); return TCL_OK; } /* * ---------------------------------------------------------------------- * * FindOp -- * * Returns if marker by a given ID currently exists. * * Results: * A standard Tcl result. * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static int FindOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Blt_ChainLink *linkPtr; Extents2D exts; Marker *markerPtr; int mode; int left, right, top, bottom; int enclosed; #define FIND_ENCLOSED (1<<0) #define FIND_OVERLAPPING (1<<1) if (strcmp(argv[3], "enclosed") == 0) { mode = FIND_ENCLOSED; } else if (strcmp(argv[3], "overlapping") == 0) { mode = FIND_OVERLAPPING; } else { Tcl_AppendResult(interp, "bad search type \"", argv[3], ": should be \"enclosed\", or \"overlapping\"", (char *)NULL); return TCL_ERROR; } if ((Tcl_GetInt(interp, argv[4], &left) != TCL_OK) || (Tcl_GetInt(interp, argv[5], &top) != TCL_OK) || (Tcl_GetInt(interp, argv[6], &right) != TCL_OK) || (Tcl_GetInt(interp, argv[7], &bottom) != TCL_OK)) { return TCL_ERROR; } if (left < right) { exts.left = (double)left; exts.right = (double)right; } else { exts.left = (double)right; exts.right = (double)left; } if (top < bottom) { exts.top = (double)top; exts.bottom = (double)bottom; } else { exts.top = (double)bottom; exts.bottom = (double)top; } enclosed = (mode == FIND_ENCLOSED); for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { markerPtr = Blt_ChainGetValue(linkPtr); if (markerPtr->hidden) { continue; } if (markerPtr->elemName != NULL) { Blt_HashEntry *hPtr; hPtr = Blt_FindHashEntry(&graphPtr->elements.table, markerPtr->elemName); if (hPtr != NULL) { Element *elemPtr; elemPtr = (Element *)Blt_GetHashValue(hPtr); if (elemPtr->hidden) { continue; } } } if ((*markerPtr->classPtr->regionProc)(markerPtr, &exts, enclosed)) { Tcl_SetResult(interp, markerPtr->name, TCL_VOLATILE); return TCL_OK; } } Tcl_SetResult(interp, "", TCL_VOLATILE); return TCL_OK; } /* * ---------------------------------------------------------------------- * * ExistsOp -- * * Returns if marker by a given ID currently exists. * * Results: * A standard Tcl result. * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static int ExistsOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Blt_HashEntry *hPtr; hPtr = Blt_FindHashEntry(&graphPtr->markers.table, argv[3]); Blt_SetBooleanResult(interp, (hPtr != NULL)); return TCL_OK; } /* * ---------------------------------------------------------------------- * * TypeOp -- * * Returns a symbolic name for the type of the marker whose ID is * given. * * Results: * A standard Tcl result. interp->result will contain the symbolic * type of the marker. * * ---------------------------------------------------------------------- */ /*ARGSUSED*/ static int TypeOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; int argc; char **argv; { Marker *markerPtr; if (NameToMarker(graphPtr, argv[3], &markerPtr) != TCL_OK) { return TCL_ERROR; } Tcl_SetResult(interp, markerPtr->classUid, TCL_STATIC); return TCL_OK; } /* Public routines */ /* * ---------------------------------------------------------------------- * * Blt_MarkerOp -- * * This procedure is invoked to process the Tcl command * that corresponds to a widget managed by this module. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * * ---------------------------------------------------------------------- */ static Blt_OpSpec markerOps[] = { {"after", 1, (Blt_Op)RelinkOp, 4, 5, "marker ?afterMarker?",}, {"before", 2, (Blt_Op)RelinkOp, 4, 5, "marker ?beforeMarker?",}, {"bind", 2, (Blt_Op)BindOp, 3, 6, "marker sequence command",}, {"cget", 2, (Blt_Op)CgetOp, 5, 5, "marker option",}, {"configure", 2, (Blt_Op)ConfigureOp, 4, 0, "marker ?marker?... ?option value?...",}, {"create", 2, (Blt_Op)CreateOp, 4, 0, "type ?option value?...",}, {"delete", 1, (Blt_Op)DeleteOp, 3, 0, "?marker?...",}, {"exists", 1, (Blt_Op)ExistsOp, 4, 4, "marker",}, {"find", 1, (Blt_Op)FindOp, 8, 8, "enclosed|overlapping x1 y1 x2 y2",}, {"get", 1, (Blt_Op)GetOp, 4, 4, "name",}, {"names", 1, (Blt_Op)NamesOp, 3, 0, "?pattern?...",}, {"type", 1, (Blt_Op)TypeOp, 4, 4, "marker",}, }; static int nMarkerOps = sizeof(markerOps) / sizeof(Blt_OpSpec); /*ARGSUSED*/ int Blt_MarkerOp(graphPtr, interp, argc, argv) Graph *graphPtr; Tcl_Interp *interp; /* Not used. */ int argc; char **argv; { Blt_Op proc; int result; proc = Blt_GetOp(interp, nMarkerOps, markerOps, BLT_OP_ARG2, argc, argv,0); if (proc == NULL) { return TCL_ERROR; } result = (*proc) (graphPtr, interp, argc, argv); return result; } /* * ------------------------------------------------------------------------- * * Blt_MarkersToPostScript -- * * ------------------------------------------------------------------------- */ void Blt_MarkersToPostScript(graphPtr, psToken, under) Graph *graphPtr; PsToken psToken; int under; { Blt_ChainLink *linkPtr; register Marker *markerPtr; for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { markerPtr = Blt_ChainGetValue(linkPtr); if ((markerPtr->classPtr->postscriptProc == NULL) || (markerPtr->nWorldPts == 0)) { continue; } if (markerPtr->drawUnder != under) { continue; } if (markerPtr->hidden) { continue; } if (markerPtr->elemName != NULL) { Blt_HashEntry *hPtr; hPtr = Blt_FindHashEntry(&graphPtr->elements.table, markerPtr->elemName); if (hPtr != NULL) { Element *elemPtr; elemPtr = (Element *)Blt_GetHashValue(hPtr); if (elemPtr->hidden) { continue; } } } Blt_AppendToPostScript(psToken, "\n% Marker \"", markerPtr->name, "\" is a ", markerPtr->classUid, " marker\n", (char *)NULL); (*markerPtr->classPtr->postscriptProc) (markerPtr, psToken); } } /* * ------------------------------------------------------------------------- * * Blt_DrawMarkers -- * * Calls the individual drawing routines (based on marker type) * for each marker in the display list. * * A marker will not be drawn if * * 1) An element linked to the marker (indicated by elemName) * is currently hidden. * * 2) No coordinates have been specified for the marker. * * 3) The marker is requesting to be drawn at a different level * (above/below the elements) from the current mode. * * 4) The marker is configured as hidden (-hide option). * * 5) The marker isn't visible in the current viewport * (i.e. clipped). * * Results: * None * * Side Effects: * Markers are drawn into the drawable (pixmap) which will eventually * be displayed in the graph window. * * ------------------------------------------------------------------------- */ void Blt_DrawMarkers(graphPtr, drawable, under) Graph *graphPtr; Drawable drawable; /* Pixmap or window to draw into */ int under; { Blt_ChainLink *linkPtr; Marker *markerPtr; for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { markerPtr = Blt_ChainGetValue(linkPtr); if ((markerPtr->nWorldPts == 0) || (markerPtr->drawUnder != under) || (markerPtr->hidden) || (markerPtr->clipped)) { continue; } if (markerPtr->elemName != NULL) { Blt_HashEntry *hPtr; /* Look up the named element and see if it's hidden */ hPtr = Blt_FindHashEntry(&graphPtr->elements.table, markerPtr->elemName); if (hPtr != NULL) { Element *elemPtr; elemPtr = (Element *)Blt_GetHashValue(hPtr); if (elemPtr->hidden) { continue; } } } (*markerPtr->classPtr->drawProc) (markerPtr, drawable); } } void Blt_MapMarkers(graphPtr) Graph *graphPtr; { Blt_ChainLink *linkPtr; Marker *markerPtr; for (linkPtr = Blt_ChainFirstLink(graphPtr->markers.displayList); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { markerPtr = Blt_ChainGetValue(linkPtr); if ((markerPtr->nWorldPts == 0) || (markerPtr->hidden)) { continue; } if ((graphPtr->flags & MAP_ALL) || (markerPtr->flags & MAP_ITEM)) { (*markerPtr->classPtr->mapProc) (markerPtr); markerPtr->flags &= ~MAP_ITEM; } } } void Blt_DestroyMarkers(graphPtr) Graph *graphPtr; { Blt_HashEntry *hPtr; Blt_HashSearch cursor; Marker *markerPtr; for (hPtr = Blt_FirstHashEntry(&graphPtr->markers.table, &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { markerPtr = (Marker *)Blt_GetHashValue(hPtr); /* * Dereferencing the pointer to the hash table prevents the * hash table entry from being automatically deleted. */ markerPtr->hashPtr = NULL; DestroyMarker(markerPtr); } Blt_DeleteHashTable(&graphPtr->markers.table); Blt_DeleteHashTable(&graphPtr->markers.tagTable); Blt_ChainDestroy(graphPtr->markers.displayList); } Marker * Blt_NearestMarker(graphPtr, x, y, under) Graph *graphPtr; int x, y; /* Screen coordinates */ int under; { Blt_ChainLink *linkPtr; Marker *markerPtr; Point2D point; point.x = (double)x; point.y = (double)y; for (linkPtr = Blt_ChainLastLink(graphPtr->markers.displayList); linkPtr != NULL; linkPtr = Blt_ChainPrevLink(linkPtr)) { markerPtr = Blt_ChainGetValue(linkPtr); /* * Don't consider markers that are pending to be mapped. Even * if the marker has already been mapped, the coordinates * could be invalid now. Better to pick no marker than the * wrong marker. */ if ((markerPtr->drawUnder == under) && (markerPtr->nWorldPts > 0) && ((markerPtr->flags & MAP_ITEM) == 0) && (!markerPtr->hidden) && (markerPtr->state == STATE_NORMAL)) { if ((*markerPtr->classPtr->pointProc) (markerPtr, &point)) { return markerPtr; } } } return NULL; }