
/*
 * 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 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;

    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;
{
    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));
}


/*
 * ----------------------------------------------------------------------
 *
 * 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;
    }
    if (bmPtr->destBitmap == None) {
	bmPtr->destBitmap = bmPtr->srcBitmap;
    }
    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 != bmPtr->srcBitmap) {
	Tk_FreePixmap(graphPtr->display, bmPtr->destBitmap);
	bmPtr->destBitmap = bmPtr->srcBitmap;
    }
    /* 
     * 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 = bmPtr->srcBitmap;
    }
    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;

    if ((bmPtr->destBitmap == 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, bmPtr->destBitmap);
	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, bmPtr->destBitmap, 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;

    if (bmPtr->destBitmap == 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, bmPtr->destBitmap,
	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 != bmPtr->srcBitmap) {
	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, "-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) {
		Tcl_AppendResult(interp, "can't find an image \"", 
			imPtr->imageName, "\"", (char *)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.x = corner1.x + imPtr->xOffset;
	imPtr->anchorPos.y = corner1.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);
    }
}

/*
 * ----------------------------------------------------------------------
 *
 * 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, "-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 < 2) {
	return FALSE;
    }
    return Blt_PointInPolygon(samplePtr, pmPtr->screenPts, 
	pmPtr->nWorldPts + 1);
}

/*
 * ----------------------------------------------------------------------
 *
 * RegionInPolygonMarker --
 *
 * ----------------------------------------------------------------------
 */
static int
RegionInPolygonMarker(markerPtr, extsPtr, enclosed)
    Marker *markerPtr;
    Extents2D *extsPtr;
    int enclosed;
{
    PolygonMarker *pmPtr = (PolygonMarker *)markerPtr;
    
    if (pmPtr->nWorldPts >= 3) {
	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);
    }
    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;

    /* 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;
	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;
	}
    }
    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);
	if ((markerPtr->drawUnder == under) && (markerPtr->nWorldPts > 0) && 
	    (!markerPtr->hidden) && (markerPtr->state == STATE_NORMAL)) {
	    if ((*markerPtr->classPtr->pointProc) (markerPtr, &point)) {
		return markerPtr;
	    }
	}
    }
    return NULL;
}
