
/*
 * bltGrBar.c --
 *
 *	This module implements barchart elements for the BLT graph widget.
 *
 * Copyright 1993-1998 Lucent Technologies, Inc.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby
 * granted, provided that the above copyright notice appear in all
 * copies and that both that the copyright notice and warranty
 * disclaimer appear in supporting documentation, and that the names
 * of Lucent Technologies any of their entities not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.
 *
 * Lucent Technologies disclaims all warranties with regard to this
 * software, including all implied warranties of merchantability and
 * fitness.  In no event shall Lucent Technologies be liable for any
 * special, indirect or consequential damages or any damages
 * whatsoever resulting from loss of use, data or profits, whether in
 * an action of contract, negligence or other tortuous action, arising
 * out of or in connection with the use or performance of this
 * software.
 */

#include "bltGraph.h"
#include <X11/Xutil.h>

#include "bltGrElem.h"

typedef struct {
    char *name;			/* Pen style identifier.  If NULL pen
				 * was statically allocated. */
    Blt_Uid classUid;		/* Type of pen */
    char *typeId;		/* String token identifying the type of pen */
    unsigned int flags;		/* Indicates if the pen element is active or
				 * normal */
    int refCount;		/* Reference count for elements using
				 * this pen. */
    Blt_HashEntry *hashPtr;
    Tk_ConfigSpec *specsPtr;	/* Configuration specifications */

    PenConfigureProc *configProc;
    PenDestroyProc *destroyProc;

    XColor *fgColor;		/* Foreground color of bar */
    Tk_3DBorder border;		/* 3D border and background color */
    int borderWidth;		/* 3D border width of bar */
    int relief;			/* Relief of the bar */
    Pixmap stipple;		/* Stipple */
    GC gc;			/* Graphics context */

    /* Error bar attributes. */
    int errorBarShow;		/* Describes which error bars to
				 * display: none, x, y, or * both. */

    int errorBarLineWidth;	/* Width of the error bar segments. */

    int errorBarCapWidth;
    XColor *errorBarColor;	/* Color of the error bar. */

    GC errorBarGC;		/* Error bar graphics context. */

    /* Show value attributes. */
    int valueShow;		/* Indicates whether to display data value.
				 * Values are x, y, or none. */

    char *valueFormat;		/* A printf format string. */
    TextStyle valueStyle;	/* Text attributes (color, font,
				 * rotation, etc.) of the value. */
    
} BarPen;

typedef struct {
    Weight weight;		/* Weight range where this pen is valid. */

    BarPen *penPtr;		/* Pen to draw */

    Segment2D *xErrorBars;	/* Point to start of this pen's X-error bar 
				 * segments in the element's array. */

    Segment2D *yErrorBars;	/* Point to start of this pen's Y-error bar 
				 * segments in the element's array. */
    int xErrorBarCnt;		/* # of error bars for this pen. */

    int yErrorBarCnt;		/* # of error bars for this pen. */

    int errorBarCapWidth;	/* Length of the cap ends on each
				 * error bar. */

    int symbolSize;		/* Size of the pen's symbol scaled to the
				 * current graph size. */

    /* Bar chart specific data. */
    XRectangle *rectangles;	/* Indicates starting location in bar
				 * array for this pen. */
    int nRects;			/* Number of bar segments for this pen. */

} BarPenStyle;

typedef struct {
    char *name;			/* Identifier to refer the
				 * element. Used in the "insert",
				 * "delete", or "show", commands. */

    Blt_Uid classUid;		/* Type of element; either 
				 * bltBarElementUid, bltLineElementUid, or
				 * bltStripElementUid. */

    Graph *graphPtr;		/* Graph widget of element*/
    unsigned int flags;		/* Indicates if the entire element is
				 * active, or if coordinates need to
				 * be calculated */
    char **tags;
    int hidden;			/* If non-zero, don't display the element. */

    Blt_HashEntry *hashPtr;
    char *label;		/* Label displayed in legend */
    int labelRelief;		/* Relief of label in legend. */

    Axis2D axes;
    ElemVector x, y, w;		/* Contains array of numeric values */

    ElemVector xError;		/* Relative/symmetric X error values. */
    ElemVector yError;		/* Relative/symmetric Y error values. */
    ElemVector xHigh, xLow;	/* Absolute/asymmetric X-coordinate high/low
				   error values. */
    ElemVector yHigh, yLow;	/* Absolute/asymmetric Y-coordinate high/low
				   error values. */

    int *activeIndices;		/* Array of indices (malloc-ed) that
				 * indicate the data points have been
				 * selected as active (drawn with
				 * "active" colors). */

    int nActiveIndices;		/* Number of active data points. Special
				 * case: if nActiveIndices < 0 and the
				 * active bit is set in "flags", then all
				 * data points are drawn active. */

    ElementProcs *procsPtr;	/* Class information for bar elements */
    Tk_ConfigSpec *specsPtr;	/* Configuration specifications */

    Segment2D *xErrorBars;	/* Point to start of this pen's X-error bar 
				 * segments in the element's array. */
    Segment2D *yErrorBars;	/* Point to start of this pen's Y-error bar 
				 * segments in the element's array. */
    int xErrorBarCnt;		/* # of error bars for this pen. */
    int yErrorBarCnt;		/* # of error bars for this pen. */

    int *xErrorToData;		/* Maps individual error bar segments back
				 * to the data point associated with it. */
    int *yErrorToData;		/* Maps individual error bar segments back
				 * to the data point associated with it. */

    int errorBarCapWidth;	/* Length of cap on error bars */

    BarPen *activePenPtr;	/* Standard Pens */
    BarPen *normalPenPtr;

    Blt_Chain *palette;		/* Chain of pen style information. */

    /* Symbol scaling */
    int scaleSymbols;		/* If non-zero, the symbols will scale
				 * in size as the graph is zoomed
				 * in/out.  */

    double xRange, yRange;	/* Initial X-axis and Y-axis ranges:
				 * used to scale the size of element's
				 * symbol. */
    int state;
    /*
     * Bar specific attributes
     */
    BarPen builtinPen;

    int *rectToData;
    XRectangle *rectangles;	/* Array of rectangles comprising the bar
				 * segments of the element. */
    int nRects;			/* # of visible bar segments for element */

    int padX;			/* Spacing on either side of bar */
    double barWidth;
    int nActive;

    XRectangle *activeRects;
    int *activeToData;
} Bar;

extern Tk_CustomOption bltBarPenOption;
extern Tk_CustomOption bltDataOption;
extern Tk_CustomOption bltDataPairsOption;
extern Tk_CustomOption bltDistanceOption;
extern Tk_CustomOption bltListOption;
extern Tk_CustomOption bltXAxisOption;
extern Tk_CustomOption bltYAxisOption;
extern Tk_CustomOption bltShadowOption;
extern Tk_CustomOption bltFillOption;
extern Tk_CustomOption bltColorOption;
extern Tk_CustomOption bltStateOption;

extern Tk_OptionParseProc Blt_StringToStyles;
extern Tk_OptionPrintProc Blt_StylesToString;
static Tk_OptionParseProc StringToBarMode;
static Tk_OptionPrintProc BarModeToString;

static Tk_CustomOption stylesOption =
{
    Blt_StringToStyles, Blt_StylesToString, (ClientData)sizeof(BarPenStyle)
};

Tk_CustomOption bltBarModeOption =
{
    StringToBarMode, BarModeToString, (ClientData)0
};

#define DEF_BAR_ACTIVE_PEN		"activeBar"
#define DEF_BAR_AXIS_X			"x"
#define DEF_BAR_AXIS_Y			"y"
#define DEF_BAR_BACKGROUND		"navyblue"
#define DEF_BAR_BG_MONO			BLACK
#define DEF_BAR_BORDERWIDTH		"2"
#define DEF_BAR_DATA			(char *)NULL
#define DEF_BAR_ERRORBAR_COLOR		"defcolor"
#define DEF_BAR_ERRORBAR_LINE_WIDTH	"1"
#define DEF_BAR_ERRORBAR_CAP_WIDTH	"1"
#define DEF_BAR_FOREGROUND		"blue"
#define DEF_BAR_FG_MONO			WHITE
#define DEF_BAR_HIDE			"no"
#define DEF_BAR_LABEL			(char *)NULL
#define DEF_BAR_LABEL_RELIEF		"flat"
#define DEF_BAR_NORMAL_STIPPLE		""
#define DEF_BAR_RELIEF			"raised"
#define DEF_BAR_SHOW_ERRORBARS		"both"
#define DEF_BAR_STATE			"normal"
#define DEF_BAR_STYLES			""
#define DEF_BAR_TAGS			"all"
#define DEF_BAR_WIDTH			"0.0"
#define DEF_BAR_DATA			(char *)NULL

#define DEF_PEN_ACTIVE_BACKGROUND		"red"
#define DEF_PEN_ACTIVE_BG_MONO		WHITE
#define DEF_PEN_ACTIVE_FOREGROUND     	"pink"
#define DEF_PEN_ACTIVE_FG_MONO		BLACK
#define DEF_PEN_BORDERWIDTH		"2"
#define DEF_PEN_NORMAL_BACKGROUND	"navyblue"
#define DEF_PEN_NORMAL_BG_MONO		BLACK
#define DEF_PEN_NORMAL_FOREGROUND	"blue"
#define DEF_PEN_NORMAL_FG_MONO		WHITE
#define DEF_PEN_RELIEF			"raised"
#define DEF_PEN_STIPPLE			""
#define DEF_PEN_TYPE			"bar"
#define	DEF_PEN_VALUE_ANCHOR		"s"
#define	DEF_PEN_VALUE_COLOR		RGB_BLACK
#define	DEF_PEN_VALUE_FONT		STD_FONT_SMALL
#define	DEF_PEN_VALUE_FORMAT		"%g"
#define	DEF_PEN_VALUE_ROTATE		(char *)NULL
#define	DEF_PEN_VALUE_SHADOW		(char *)NULL
#define DEF_PEN_SHOW_VALUES		"no"

static Tk_ConfigSpec barPenConfigSpecs[] =
{
    {TK_CONFIG_BORDER, "-background", "background", "Background",
	DEF_PEN_ACTIVE_BACKGROUND, Tk_Offset(BarPen, border),
	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY | ACTIVE_PEN},
    {TK_CONFIG_BORDER, "-background", "background", "Background",
	DEF_PEN_ACTIVE_BACKGROUND, Tk_Offset(BarPen, border),
	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY | ACTIVE_PEN},
    {TK_CONFIG_BORDER, "-background", "background", "Background",
	DEF_PEN_NORMAL_BACKGROUND, Tk_Offset(BarPen, border),
	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY | NORMAL_PEN},
    {TK_CONFIG_BORDER, "-background", "background", "Background",
	DEF_PEN_NORMAL_BACKGROUND, Tk_Offset(BarPen, border),
	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY | NORMAL_PEN},
    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
	(char *)NULL, 0, ALL_PENS},
    {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
	(char *)NULL, 0, ALL_PENS},
    {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
	DEF_PEN_BORDERWIDTH, Tk_Offset(BarPen, borderWidth), ALL_PENS,
	&bltDistanceOption},
    {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
	DEF_BAR_ERRORBAR_COLOR, Tk_Offset(BarPen, errorBarColor), 
	ALL_PENS, &bltColorOption},
    {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
	DEF_BAR_ERRORBAR_LINE_WIDTH, Tk_Offset(BarPen, errorBarLineWidth),
        ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
    {TK_CONFIG_CUSTOM, "-errorbarcap", "errorBarCap", "ErrorBarCap", 
	DEF_BAR_ERRORBAR_CAP_WIDTH, Tk_Offset(BarPen, errorBarCapWidth),
        ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL,
	(char *)NULL, 0, ALL_PENS},
    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
	DEF_PEN_ACTIVE_FOREGROUND, Tk_Offset(BarPen, fgColor),
	ACTIVE_PEN | TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
	DEF_PEN_ACTIVE_FOREGROUND, Tk_Offset(BarPen, fgColor),
	ACTIVE_PEN |  TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
	DEF_PEN_NORMAL_FOREGROUND, Tk_Offset(BarPen, fgColor),
	NORMAL_PEN |  TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
	DEF_PEN_NORMAL_FOREGROUND, Tk_Offset(BarPen, fgColor),
	NORMAL_PEN |  TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
	DEF_PEN_RELIEF, Tk_Offset(BarPen, relief), ALL_PENS},
    {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
	DEF_BAR_SHOW_ERRORBARS, Tk_Offset(BarPen, errorBarShow),
	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
    {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
	DEF_PEN_SHOW_VALUES, Tk_Offset(BarPen, valueShow),
        ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
    {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
	DEF_PEN_STIPPLE, Tk_Offset(BarPen, stipple),
	ALL_PENS | TK_CONFIG_NULL_OK},
    {TK_CONFIG_STRING, "-type", (char *)NULL, (char *)NULL,
	DEF_PEN_TYPE, Tk_Offset(BarPen, typeId), ALL_PENS | TK_CONFIG_NULL_OK},
    {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
	DEF_PEN_VALUE_ANCHOR, Tk_Offset(BarPen, valueStyle.anchor), ALL_PENS},
    {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
	DEF_PEN_VALUE_COLOR, Tk_Offset(BarPen, valueStyle.color), ALL_PENS},
    {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
	DEF_PEN_VALUE_FONT, Tk_Offset(BarPen, valueStyle.font), ALL_PENS},
    {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
	DEF_PEN_VALUE_FORMAT, Tk_Offset(BarPen, valueFormat),
	ALL_PENS | TK_CONFIG_NULL_OK},
    {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
	DEF_PEN_VALUE_ROTATE, Tk_Offset(BarPen, valueStyle.theta), ALL_PENS},
    {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
	DEF_PEN_VALUE_SHADOW, Tk_Offset(BarPen, valueStyle.shadow),
	ALL_PENS, &bltShadowOption},
    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
};


static Tk_ConfigSpec barElemConfigSpecs[] =
{
    {TK_CONFIG_CUSTOM, "-activepen", "activePen", "ActivePen",
	DEF_BAR_ACTIVE_PEN, Tk_Offset(Bar, activePenPtr),
	TK_CONFIG_NULL_OK, &bltBarPenOption},
    {TK_CONFIG_BORDER, "-background", "background", "Background",
	DEF_BAR_BACKGROUND, Tk_Offset(Bar, builtinPen.border),
	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_BORDER, "-background", "background", "Background",
	DEF_BAR_BACKGROUND, Tk_Offset(Bar, builtinPen.border),
	TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_DOUBLE, "-barwidth", "barWidth", "BarWidth",
	DEF_BAR_WIDTH, Tk_Offset(Bar, barWidth),
	TK_CONFIG_DONT_SET_DEFAULT},
    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
	(char *)NULL, 0, 0},
    {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
	(char *)NULL, 0, 0},
    {TK_CONFIG_CUSTOM, "-bindtags", "bindTags", "BindTags",
	DEF_BAR_TAGS, Tk_Offset(Bar, tags),
	TK_CONFIG_NULL_OK, &bltListOption},
    {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth",
	DEF_BAR_BORDERWIDTH, Tk_Offset(Bar, builtinPen.borderWidth),
	0, &bltDistanceOption},
    {TK_CONFIG_CUSTOM, "-errorbarcolor", "errorBarColor", "ErrorBarColor",
	DEF_BAR_ERRORBAR_COLOR, Tk_Offset(Bar, builtinPen.errorBarColor), 
	0, &bltColorOption},
    {TK_CONFIG_CUSTOM, "-errorbarwidth", "errorBarWidth", "ErrorBarWidth",
	DEF_BAR_ERRORBAR_LINE_WIDTH, 
	Tk_Offset(Bar, builtinPen.errorBarLineWidth),
        TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
    {TK_CONFIG_CUSTOM, "-errorbarcap", "errorBarCap", "ErrorBarCap", 
	DEF_BAR_ERRORBAR_CAP_WIDTH, Tk_Offset(Bar, builtinPen.errorBarCapWidth),
        ALL_PENS | TK_CONFIG_DONT_SET_DEFAULT, &bltDistanceOption},
    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
    {TK_CONFIG_CUSTOM, "-data", "data", "Data",
	(char *)NULL, 0, 0, &bltDataPairsOption},
    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
	DEF_BAR_FOREGROUND, Tk_Offset(Bar, builtinPen.fgColor),
	TK_CONFIG_NULL_OK | TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
	DEF_BAR_FOREGROUND, Tk_Offset(Bar, builtinPen.fgColor),
        TK_CONFIG_NULL_OK | TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_STRING, "-label", "label", "Label",
	DEF_BAR_LABEL, Tk_Offset(Bar, label), TK_CONFIG_NULL_OK},
    {TK_CONFIG_RELIEF, "-labelrelief", "labelRelief", "LabelRelief",
	DEF_BAR_LABEL_RELIEF, Tk_Offset(Bar, labelRelief),
	TK_CONFIG_DONT_SET_DEFAULT},
    {TK_CONFIG_BOOLEAN, "-hide", "hide", "Hide",
	DEF_BAR_HIDE, Tk_Offset(Bar, hidden),
	TK_CONFIG_DONT_SET_DEFAULT},
    {TK_CONFIG_CUSTOM, "-mapx", "mapX", "MapX",
	DEF_BAR_AXIS_X, Tk_Offset(Bar, axes.x), 0, &bltXAxisOption},
    {TK_CONFIG_CUSTOM, "-mapy", "mapY", "MapY",
	DEF_BAR_AXIS_Y, Tk_Offset(Bar, axes.y), 0, &bltYAxisOption},
    {TK_CONFIG_CUSTOM, "-pen", "pen", "Pen",
	(char *)NULL, Tk_Offset(Bar, normalPenPtr),
	TK_CONFIG_NULL_OK, &bltBarPenOption},
    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
	DEF_BAR_RELIEF, Tk_Offset(Bar, builtinPen.relief), 0},
    {TK_CONFIG_CUSTOM, "-showerrorbars", "showErrorBars", "ShowErrorBars",
	DEF_BAR_SHOW_ERRORBARS, Tk_Offset(Bar, builtinPen.errorBarShow),
	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
    {TK_CONFIG_CUSTOM, "-showvalues", "showValues", "ShowValues",
	DEF_PEN_SHOW_VALUES, Tk_Offset(Bar, builtinPen.valueShow),
	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
    {TK_CONFIG_CUSTOM, "-state", "state", "State",
	DEF_BAR_STATE, Tk_Offset(Bar, state), 
	TK_CONFIG_DONT_SET_DEFAULT, &bltStateOption},
    {TK_CONFIG_BITMAP, "-stipple", "stipple", "Stipple",
	DEF_BAR_NORMAL_STIPPLE, Tk_Offset(Bar, builtinPen.stipple),
	TK_CONFIG_NULL_OK},
    {TK_CONFIG_CUSTOM, "-styles", "styles", "Styles",
	DEF_BAR_STYLES, Tk_Offset(Bar, palette), 
        TK_CONFIG_NULL_OK, &stylesOption},
    {TK_CONFIG_ANCHOR, "-valueanchor", "valueAnchor", "ValueAnchor",
	DEF_PEN_VALUE_ANCHOR, Tk_Offset(Bar, builtinPen.valueStyle.anchor), 0},
    {TK_CONFIG_COLOR, "-valuecolor", "valueColor", "ValueColor",
	DEF_PEN_VALUE_COLOR, Tk_Offset(Bar, builtinPen.valueStyle.color), 0},
    {TK_CONFIG_FONT, "-valuefont", "valueFont", "ValueFont",
	DEF_PEN_VALUE_FONT, Tk_Offset(Bar, builtinPen.valueStyle.font), 0},
    {TK_CONFIG_STRING, "-valueformat", "valueFormat", "ValueFormat",
	DEF_PEN_VALUE_FORMAT, Tk_Offset(Bar, builtinPen.valueFormat),
        TK_CONFIG_NULL_OK},
    {TK_CONFIG_DOUBLE, "-valuerotate", "valueRotate", "ValueRotate",
	DEF_PEN_VALUE_ROTATE, Tk_Offset(Bar, builtinPen.valueStyle.theta), 0},
    {TK_CONFIG_CUSTOM, "-valueshadow", "valueShadow", "ValueShadow",
	DEF_PEN_VALUE_SHADOW, Tk_Offset(Bar, builtinPen.valueStyle.shadow),
	0, &bltShadowOption},

    {TK_CONFIG_CUSTOM, "-weights", "weights", "Weights",
	(char *)NULL, Tk_Offset(Bar, w), 0, &bltDataOption},
    {TK_CONFIG_CUSTOM, "-x", "xdata", "Xdata",
	DEF_BAR_DATA, Tk_Offset(Bar, x), 0, &bltDataOption},
    {TK_CONFIG_CUSTOM, "-y", "ydata", "Ydata",
	DEF_BAR_DATA, Tk_Offset(Bar, y), 0, &bltDataOption},
    {TK_CONFIG_CUSTOM, "-xdata", "xdata", "Xdata",
	DEF_BAR_DATA, Tk_Offset(Bar, x), 0, &bltDataOption},
    {TK_CONFIG_CUSTOM, "-ydata", "ydata", "Ydata",
	DEF_BAR_DATA, Tk_Offset(Bar, y), 0, &bltDataOption},
    {TK_CONFIG_CUSTOM, "-xerror", "xError", "XError",
	DEF_BAR_DATA, Tk_Offset(Bar, xError), 0, &bltDataOption},
    {TK_CONFIG_CUSTOM, "-xhigh", "xHigh", "XHigh",
	DEF_BAR_DATA, Tk_Offset(Bar, xHigh), 0, &bltDataOption},
    {TK_CONFIG_CUSTOM, "-xlow", "xLow", "XLow",
	DEF_BAR_DATA, Tk_Offset(Bar, xLow), 0, &bltDataOption},
    {TK_CONFIG_CUSTOM, "-yerror", "yError", "YError",
	DEF_BAR_DATA, Tk_Offset(Bar, yError), 0, &bltDataOption},
    {TK_CONFIG_CUSTOM, "-yhigh", "yHigh", "YHigh",
	DEF_BAR_DATA, Tk_Offset(Bar, yHigh), 0, &bltDataOption},
    {TK_CONFIG_CUSTOM, "-ylow", "yLow", "YLow",
	DEF_BAR_DATA, Tk_Offset(Bar, yLow), 0, &bltDataOption},
    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
};

/* Forward declarations */
static PenConfigureProc ConfigurePen;
static PenDestroyProc DestroyPen;
static ElementClosestProc ClosestBar;
static ElementConfigProc ConfigureBar;
static ElementDestroyProc DestroyBar;
static ElementDrawProc DrawActiveBar;
static ElementDrawProc DrawNormalBar;
static ElementDrawSymbolProc DrawSymbol;
static ElementExtentsProc GetBarExtents;
static ElementToPostScriptProc ActiveBarToPostScript;
static ElementToPostScriptProc NormalBarToPostScript;
static ElementSymbolToPostScriptProc SymbolToPostScript;
static ElementMapProc MapBar;

INLINE static int
Round(x)
    register double x;
{
    return (int) (x + ((x < 0.0) ? -0.5 : 0.5));
}

/*
 * ----------------------------------------------------------------------
 * Custom option parse and print procedures
 * ----------------------------------------------------------------------
 */

/*
 * ----------------------------------------------------------------------
 *
 * NameOfBarMode --
 *
 *	Converts the integer representing the mode style into a string.
 *
 * ----------------------------------------------------------------------
 */
static char *
NameOfBarMode(mode)
    BarMode mode;
{
    switch (mode) {
    case MODE_INFRONT:
	return "infront";
    case MODE_OVERLAP:
	return "overlap";
    case MODE_STACKED:
	return "stacked";
    case MODE_ALIGNED:
	return "aligned";
    default:
	return "unknown mode value";
    }
}

/*
 * ----------------------------------------------------------------------
 *
 * StringToMode --
 *
 *	Converts the mode string into its numeric representation.
 *
 *	Valid mode strings are:
 *
 *      "infront"   Draw a full bar at each point in the element.
 *
 * 	"stacked"   Stack bar segments vertically. Each stack is defined
 *		    by each ordinate at a particular abscissa. The height
 *		    of each segment is represented by the sum the previous
 *		    ordinates.
 *
 *	"aligned"   Align bar segments as smaller slices one next to
 *		    the other.  Like "stacks", aligned segments are
 *		    defined by each ordinate at a particular abscissa.
 *
 * Results:
 *	A standard Tcl result.
 *
 * ----------------------------------------------------------------------
 */
/*ARGSUSED*/
static int
StringToBarMode(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;		/* Mode style string */
    char *widgRec;		/* Cubicle structure record */
    int offset;			/* Offset of style in record */
{
    BarMode *modePtr = (BarMode *)(widgRec + offset);
    unsigned int length;
    char c;

    c = string[0];
    length = strlen(string);
    if ((c == 'n') && (strncmp(string, "normal", length) == 0)) {
	*modePtr = MODE_INFRONT;
    } else if ((c == 'i') && (strncmp(string, "infront", length) == 0)) {
	*modePtr = MODE_INFRONT;
    } else if ((c == 's') && (strncmp(string, "stacked", length) == 0)) {
	*modePtr = MODE_STACKED;
    } else if ((c == 'a') && (strncmp(string, "aligned", length) == 0)) {
	*modePtr = MODE_ALIGNED;
    } else if ((c == 'o') && (strncmp(string, "overlap", length) == 0)) {
	*modePtr = MODE_OVERLAP;
    } else {
	Tcl_AppendResult(interp, "bad mode argument \"", string,
	    "\": should be \"infront\", \"stacked\", \"overlap\", or \"aligned\"",
	    (char *)NULL);
	return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 * ----------------------------------------------------------------------
 *
 * BarModeToString --
 *
 *	Returns the mode style string based upon the mode flags.
 *
 * Results:
 *	The mode style string is returned.
 *
 * ----------------------------------------------------------------------
 */
/*ARGSUSED*/
static char *
BarModeToString(clientData, tkwin, widgRec, offset, freeProcPtr)
    ClientData clientData;	/* Not used. */
    Tk_Window tkwin;		/* Not used. */
    char *widgRec;		/* Row/column structure record */
    int offset;			/* Offset of mode in Partition record */
    Tcl_FreeProc **freeProcPtr;	/* Not used. */
{
    BarMode mode = *(BarMode *)(widgRec + offset);

    return NameOfBarMode(mode);
}


/* 
 * Zero out the style's number of rectangles and errorbars. 
 */
static void
ClearPalette(palette)
    Blt_Chain *palette;
{
    register BarPenStyle *stylePtr;
    Blt_ChainLink *linkPtr;

    for (linkPtr = Blt_ChainFirstLink(palette); linkPtr != NULL;
	 linkPtr = Blt_ChainNextLink(linkPtr)) {
	stylePtr = Blt_ChainGetValue(linkPtr);
	stylePtr->xErrorBarCnt = stylePtr->yErrorBarCnt = stylePtr->nRects = 0;
    }
}

static int
ConfigurePen(graphPtr, penPtr)
    Graph *graphPtr;
    Pen *penPtr;
{
    BarPen *bpPtr = (BarPen *)penPtr;
    XGCValues gcValues;
    unsigned long gcMask;
    int fillStyle;
    GC newGC;
    long defColor;

    Blt_ResetTextStyle(graphPtr->tkwin, &(bpPtr->valueStyle));
    gcMask = GCForeground;
    if (bpPtr->fgColor != NULL) {
	defColor = bpPtr->fgColor->pixel;
	gcValues.foreground = bpPtr->fgColor->pixel;
    } else if (bpPtr->border != NULL) {
	defColor = Tk_3DBorderColor(bpPtr->border)->pixel;
	gcValues.foreground = Tk_3DBorderColor(bpPtr->border)->pixel;
    } else {
	defColor = BlackPixel(graphPtr->display, 
			      Tk_ScreenNumber(graphPtr->tkwin));
    }
    if ((bpPtr->fgColor != NULL) && (bpPtr->border != NULL)) {
	gcMask |= GCBackground;
	gcValues.background = Tk_3DBorderColor(bpPtr->border)->pixel;
	fillStyle = FillOpaqueStippled;
    } else {
	fillStyle = FillStippled;
    }
    if (bpPtr->stipple != None) {
	gcValues.stipple = bpPtr->stipple;
	gcValues.fill_style = fillStyle;
	gcMask |= (GCStipple | GCFillStyle);
    }
    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
    if (bpPtr->gc != NULL) {
	Tk_FreeGC(graphPtr->display, bpPtr->gc);
    }
    bpPtr->gc = newGC;

    gcMask = GCForeground | GCLineWidth;
    if (bpPtr->errorBarColor == COLOR_DEFAULT) {
	gcValues.foreground = defColor;
    } else {
	gcValues.foreground = bpPtr->errorBarColor->pixel;
    }
    gcValues.line_width = LineWidth(bpPtr->errorBarLineWidth);
    newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
    if (bpPtr->errorBarGC != NULL) {
	Tk_FreeGC(graphPtr->display, bpPtr->errorBarGC);
    }
    bpPtr->errorBarGC = newGC;

    return TCL_OK;
}

static void
DestroyPen(graphPtr, penPtr)
    Graph *graphPtr;
    Pen *penPtr;
{
    BarPen *bpPtr = (BarPen *)penPtr;

    Blt_FreeTextStyle(graphPtr->display, &(bpPtr->valueStyle));
    if (bpPtr->gc != NULL) {
	Tk_FreeGC(graphPtr->display, bpPtr->gc);
    }
    if (bpPtr->errorBarGC != NULL) {
	Tk_FreeGC(graphPtr->display, bpPtr->errorBarGC);
    }
}

static void
InitPen(penPtr)
    BarPen *penPtr;
{
    Blt_InitTextStyle(&(penPtr->valueStyle));
    penPtr->specsPtr = barPenConfigSpecs;
    penPtr->configProc = ConfigurePen;
    penPtr->destroyProc = DestroyPen;
    penPtr->relief = TK_RELIEF_RAISED;
    penPtr->flags = NORMAL_PEN;
    penPtr->errorBarShow = SHOW_BOTH;
    penPtr->valueShow = SHOW_NONE;
    penPtr->borderWidth = 2;
}

Pen *
Blt_BarPen(penName)
    char *penName;
{
    BarPen *penPtr;

    penPtr = Blt_Calloc(1, sizeof(BarPen));
    assert(penPtr);
    InitPen(penPtr);
    penPtr->name = Blt_Strdup(penName);
    if (strcmp(penName, "activeBar") == 0) {
	penPtr->flags = ACTIVE_PEN;
    }
    return (Pen *) penPtr;
}

/*
 * ----------------------------------------------------------------------
 *
 * CheckStacks --
 *
 *	Check that the data limits are not superseded by the heights
 *	of stacked bar segments.  The heights are calculated by
 *	Blt_ComputeStacks.
 *
 * Results:
 *	If the y-axis limits need to be adjusted for stacked segments,
 *	*minPtr* or *maxPtr* are updated.
 *
 * Side effects:
 *	Autoscaling of the y-axis is affected.
 *
 * ----------------------------------------------------------------------
 */
static void
CheckStacks(graphPtr, pairPtr, minPtr, maxPtr)
    Graph *graphPtr;
    Axis2D *pairPtr;
    double *minPtr, *maxPtr;	/* Current minimum maximum for y-axis */
{
    FreqInfo *infoPtr;
    register int i;

    if ((graphPtr->mode != MODE_STACKED) || (graphPtr->nStacks == 0)) {
	return;
    }
    infoPtr = graphPtr->freqArr;
    for (i = 0; i < graphPtr->nStacks; i++) {
	if ((infoPtr->axes.x == pairPtr->x) && 
	    (infoPtr->axes.y == pairPtr->y)) {
	    /*

	     * Check if any of the y-values (because of stacking) are
	     * greater than the current limits of the graph.
	     */
	    if (infoPtr->sum < 0.0) {
		if (*minPtr > infoPtr->sum) {
		    *minPtr = infoPtr->sum;
		}
	    } else {
		if (*maxPtr < infoPtr->sum) {
		    *maxPtr = infoPtr->sum;
		}
	    }
	}
	infoPtr++;
    }
}

/*
 * ----------------------------------------------------------------------
 *
 * ConfigureBar --
 *
 *	Sets up the appropriate configuration parameters in the GC.
 *      It is assumed the parameters have been previously set by
 *	a call to Tk_ConfigureWidget.
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then interp->result contains an error message.
 *
 * Side effects:
 *	Configuration information such as bar foreground/background
 *	color and stipple etc. get set in a new GC.
 *
 * ----------------------------------------------------------------------
 */
/*ARGSUSED*/
static int
ConfigureBar(graphPtr, elemPtr)
    Graph *graphPtr;
    register Element *elemPtr;
{
    Bar *barPtr = (Bar *)elemPtr;
    Blt_ChainLink *linkPtr;

    if (ConfigurePen(graphPtr, (Pen *)&(barPtr->builtinPen)) != TCL_OK) {
	return TCL_ERROR;
    }
    /*
     * Point to the static normal pen if no external pens have
     * been selected.
     */
    if (barPtr->normalPenPtr == NULL) {
	barPtr->normalPenPtr = &(barPtr->builtinPen);
    }
    linkPtr = Blt_ChainFirstLink(barPtr->palette);
    if (linkPtr != NULL) {
	BarPenStyle *stylePtr;

	stylePtr = Blt_ChainGetValue(linkPtr);
	stylePtr->penPtr = barPtr->normalPenPtr;
    }
    if (Blt_ConfigModified(barPtr->specsPtr, graphPtr->interp, "-barwidth", "-*data",
	    "-map*", "-label", "-hide", "-x", "-y", (char *)NULL)) {
	barPtr->flags |= MAP_ITEM;
    }
    return TCL_OK;
}

static void
GetBarExtents(elemPtr, extsPtr)
    Element *elemPtr;
    Extents2D *extsPtr;
{
    Graph *graphPtr = elemPtr->graphPtr;
    Bar *barPtr = (Bar *)elemPtr;
    double middle, barWidth;
    int nPoints;

    extsPtr->top = extsPtr->left = DBL_MAX;
    extsPtr->bottom = extsPtr->right = -DBL_MAX;

    nPoints = NumberOfPoints(barPtr);
    if (nPoints < 1) {
	return;			/* No data points */
    }
    barWidth = graphPtr->barWidth;
    if (barPtr->barWidth > 0.0) {
	barWidth = barPtr->barWidth;
    }
    middle = barWidth * 0.5;
    extsPtr->left = barPtr->x.min - middle;
    extsPtr->right = barPtr->x.max + middle;

    extsPtr->top = barPtr->y.min;
    extsPtr->bottom = barPtr->y.max;
    if (extsPtr->bottom < graphPtr->baseline) {
	extsPtr->bottom = graphPtr->baseline;
    }
    /*
     * Handle "stacked" bar elements specially.
     *
     * If element is stacked, the sum of its ordinates may be outside
     * the minimum/maximum limits of the element's data points.
     */
    if ((graphPtr->mode == MODE_STACKED) && (graphPtr->nStacks > 0)) {
	CheckStacks(graphPtr, &(elemPtr->axes), &(extsPtr->top), 
		&(extsPtr->bottom));
    }
    /* Warning: You get what you deserve if the x-axis is logScale */
    if (elemPtr->axes.x->logScale) {
	extsPtr->left = Blt_FindElemVectorMinimum(&(barPtr->x), DBL_MIN) + 
	    middle;
    }
    /* Fix y-min limits for barchart */
    if (elemPtr->axes.y->logScale) {
 	if ((extsPtr->top <= 0.0) || (extsPtr->top > 1.0)) {
	    extsPtr->top = 1.0;
	}
    } else {
	if (extsPtr->top > 0.0) {
	    extsPtr->top = 0.0;
	}
    }
    /* Correct the extents for error bars if they exist. */
    if (elemPtr->xError.nValues > 0) {
	register int i;
	double x;
	
	/* Correct the data limits for error bars */
	nPoints = MIN(elemPtr->xError.nValues, nPoints);
	for (i = 0; i < nPoints; i++) {
	    x = elemPtr->x.valueArr[i] + elemPtr->xError.valueArr[i];
	    if (x > extsPtr->right) {
		extsPtr->right = x;
	    }
	    x = elemPtr->x.valueArr[i] - elemPtr->xError.valueArr[i];
	    if (elemPtr->axes.x->logScale) {
		if (x < 0.0) {
		    x = -x;	/* Mirror negative values, instead
				 * of ignoring them. */
		}
		if ((x > DBL_MIN) && (x < extsPtr->left)) {
		    extsPtr->left = x;
		}
	    } else if (x < extsPtr->left) {
		extsPtr->left = x;
	    }
	}		     
    } else {
	if ((elemPtr->xHigh.nValues > 0) && 
	    (elemPtr->xHigh.max > extsPtr->right)) {
	    extsPtr->right = elemPtr->xHigh.max;
	}
	if (elemPtr->xLow.nValues > 0) {
	    double left;
	    
	    if ((elemPtr->xLow.min <= 0.0) && 
		(elemPtr->axes.x->logScale)) {
		left = Blt_FindElemVectorMinimum(&elemPtr->xLow, DBL_MIN);
	    } else {
		left = elemPtr->xLow.min;
	    }
	    if (left < extsPtr->left) {
		extsPtr->left = left;
	    }
	}
    }
    if (elemPtr->yError.nValues > 0) {
	register int i;
	double y;
	
	nPoints = MIN(elemPtr->yError.nValues, nPoints);
	for (i = 0; i < nPoints; i++) {
	    y = elemPtr->y.valueArr[i] + elemPtr->yError.valueArr[i];
	    if (y > extsPtr->bottom) {
		extsPtr->bottom = y;
	    }
	    y = elemPtr->y.valueArr[i] - elemPtr->yError.valueArr[i];
	    if (elemPtr->axes.y->logScale) {
		if (y < 0.0) {
		    y = -y;	/* Mirror negative values, instead
				 * of ignoring them. */
		}
		if ((y > DBL_MIN) && (y < extsPtr->left)) {
		    extsPtr->top = y;
		}
	    } else if (y < extsPtr->top) {
		extsPtr->top = y;
	    }
	}		     
    } else {
	if ((elemPtr->yHigh.nValues > 0) && 
	    (elemPtr->yHigh.max > extsPtr->bottom)) {
	    extsPtr->bottom = elemPtr->yHigh.max;
	}
	if (elemPtr->yLow.nValues > 0) {
	    double top;
	    
	    if ((elemPtr->yLow.min <= 0.0) && 
		(elemPtr->axes.y->logScale)) {
		top = Blt_FindElemVectorMinimum(&elemPtr->yLow, DBL_MIN);
	    } else {
		top = elemPtr->yLow.min;
	    }
	    if (top < extsPtr->top) {
		extsPtr->top = top;
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------
 *
 * ClosestBar --
 *
 *	Find the bar segment closest to the window coordinates	point
 *	specified.
 *
 *	Note:  This does not return the height of the stacked segment
 *	       (in graph coordinates) properly.
 *
 * Results:
 *	Returns 1 if the point is width any bar segment, otherwise 0.
 *
 * ----------------------------------------------------------------------
 */
/*ARGSUSED*/
static void
ClosestBar(graphPtr, elemPtr, searchPtr)
    Graph *graphPtr;		/* Graph widget record */
    Element *elemPtr;		/* Bar element */
    ClosestSearch *searchPtr;	/* Info of closest point in element */
{
    Bar *barPtr = (Bar *)elemPtr;
    Point2D *pointPtr, *endPtr;
    Point2D t, outline[5];
    XRectangle *rectPtr;
    double left, right, top, bottom;
    double minDist, dist;
    int imin;
    register int i;

    minDist = searchPtr->dist;
    imin = 0;
    
    rectPtr = barPtr->rectangles;
    for (i = 0; i < barPtr->nRects; i++) {
	if (PointInRectangle(rectPtr, searchPtr->x, searchPtr->y)) {
	    imin = barPtr->rectToData[i];
	    minDist = 0.0;
	    break;
	}
	left = rectPtr->x, top = rectPtr->y;
	right = (double)(rectPtr->x + rectPtr->width);
	bottom = (double)(rectPtr->y + rectPtr->height);
	outline[4].x = outline[3].x = outline[0].x = left;
	outline[4].y = outline[1].y = outline[0].y = top;
	outline[2].x = outline[1].x = right;
	outline[3].y = outline[2].y = bottom;

	for (pointPtr = outline, endPtr = outline + 4; pointPtr < endPtr; 
	     pointPtr++) {
	    t = Blt_GetProjection(searchPtr->x, searchPtr->y,
				  pointPtr, pointPtr + 1);
	    if (t.x > right) {
		t.x = right;
	    } else if (t.x < left) {
		t.x = left;
	    }
	    if (t.y > bottom) {
		t.y = bottom;
	    } else if (t.y < top) {
		t.y = top;
	    }
	    dist = hypot((t.x - searchPtr->x), (t.y - searchPtr->y));
	    if (dist < minDist) {
		minDist = dist;
		imin = barPtr->rectToData[i];
	    }
	}
	rectPtr++;
    }
    if (minDist < searchPtr->dist) {
	searchPtr->elemPtr = (Element *)elemPtr;
	searchPtr->dist = minDist;
	searchPtr->index = imin;
	searchPtr->point.x = (double)barPtr->x.valueArr[imin];
	searchPtr->point.y = (double)barPtr->y.valueArr[imin];
    }
}

/*
 *----------------------------------------------------------------------
 *
 * MergePens --
 *
 *	Reorders the both arrays of points and errorbars to merge pens.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The old arrays are freed and new ones allocated containing
 *	the reordered points and errorbars.
 *
 *----------------------------------------------------------------------
 */
static void
MergePens(barPtr, dataToStyle)
    Bar *barPtr;
    PenStyle **dataToStyle;
{
    BarPenStyle *stylePtr;
    Blt_ChainLink *linkPtr;

    if (Blt_ChainGetLength(barPtr->palette) < 2) {
	linkPtr = Blt_ChainFirstLink(barPtr->palette);
	stylePtr = Blt_ChainGetValue(linkPtr);
	stylePtr->nRects = barPtr->nRects;
	stylePtr->rectangles = barPtr->rectangles;
	stylePtr->symbolSize = barPtr->rectangles->width / 2;
	stylePtr->xErrorBarCnt = barPtr->xErrorBarCnt;
	stylePtr->xErrorBars = barPtr->xErrorBars;
	stylePtr->yErrorBarCnt = barPtr->yErrorBarCnt;
	stylePtr->yErrorBars = barPtr->yErrorBars;
	return;
    }
    /* We have more than one style. Group bar segments of like pen
     * styles together.  */

    if (barPtr->nRects > 0) {
	XRectangle *rectangles;
	int *rectToData;
	int dataIndex;
	register XRectangle *rectPtr;
	register int *indexPtr;
	register int i;

	rectangles = Blt_Malloc(barPtr->nRects * sizeof(XRectangle));
	rectToData = Blt_Malloc(barPtr->nRects * sizeof(int));
	assert(rectangles && rectToData);

	rectPtr = rectangles, indexPtr = rectToData;
	for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
	     linkPtr = Blt_ChainNextLink(linkPtr)) {
	    stylePtr = Blt_ChainGetValue(linkPtr);
	    stylePtr->symbolSize = rectPtr->width / 2;
	    stylePtr->rectangles = rectPtr;
	    for (i = 0; i < barPtr->nRects; i++) {
		dataIndex = barPtr->rectToData[i];
		if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
		    *rectPtr++ = barPtr->rectangles[i];
		    *indexPtr++ = dataIndex;
		}
	    }
	    stylePtr->nRects = rectPtr - stylePtr->rectangles;
	}
	Blt_Free(barPtr->rectangles);
	barPtr->rectangles = rectangles;
	Blt_Free(barPtr->rectToData);
	barPtr->rectToData = rectToData;
    }
    if (barPtr->xErrorBarCnt > 0) {
	Segment2D *errorBars, *segPtr;
	int *errorToData, *indexPtr;
	int dataIndex;
	register int i;

	errorBars = Blt_Malloc(barPtr->xErrorBarCnt * sizeof(Segment2D));
	errorToData = Blt_Malloc(barPtr->xErrorBarCnt * sizeof(int));
	assert(errorBars);
	segPtr = errorBars, indexPtr = errorToData;
	for (linkPtr = Blt_ChainFirstLink(barPtr->palette); 
	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	    stylePtr = Blt_ChainGetValue(linkPtr);
	    stylePtr->xErrorBars = segPtr;
	    for (i = 0; i < barPtr->xErrorBarCnt; i++) {
		dataIndex = barPtr->xErrorToData[i];
		if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
		    *segPtr++ = barPtr->xErrorBars[i];
		    *indexPtr++ = dataIndex;
		}
	    }
	    stylePtr->xErrorBarCnt = segPtr - stylePtr->xErrorBars;
	}
	Blt_Free(barPtr->xErrorBars);
	barPtr->xErrorBars = errorBars;
	Blt_Free(barPtr->xErrorToData);
	barPtr->xErrorToData = errorToData;
    }
    if (barPtr->yErrorBarCnt > 0) {
	Segment2D *errorBars, *segPtr;
	int *errorToData, *indexPtr;
	int dataIndex;
	register int i;

	errorBars = Blt_Malloc(barPtr->yErrorBarCnt * sizeof(Segment2D));
	errorToData = Blt_Malloc(barPtr->yErrorBarCnt * sizeof(int));
	assert(errorBars);
	segPtr = errorBars, indexPtr = errorToData;
	for (linkPtr = Blt_ChainFirstLink(barPtr->palette); 
	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	    stylePtr = Blt_ChainGetValue(linkPtr);
	    stylePtr->yErrorBars = segPtr;
	    for (i = 0; i < barPtr->yErrorBarCnt; i++) {
		dataIndex = barPtr->yErrorToData[i];
		if (dataToStyle[dataIndex] == (PenStyle *)stylePtr) {
		    *segPtr++ = barPtr->yErrorBars[i];
		    *indexPtr++ = dataIndex;
		}
	    }
	    stylePtr->yErrorBarCnt = segPtr - stylePtr->yErrorBars;
	}
	Blt_Free(barPtr->yErrorBars);
	barPtr->yErrorBars = errorBars;
	Blt_Free(barPtr->yErrorToData);
	barPtr->yErrorToData = errorToData;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * MapActiveBars --
 *
 *	Creates an array of points of the active graph coordinates.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Memory is freed and allocated for the active point array.
 *
 *----------------------------------------------------------------------
 */
static void
MapActiveBars(barPtr)
    Bar *barPtr;
{
    if (barPtr->activeRects != NULL) {
	Blt_Free(barPtr->activeRects);
	barPtr->activeRects = NULL;
    }
    if (barPtr->activeToData != NULL) {
	Blt_Free(barPtr->activeToData);
	barPtr->activeToData = NULL;
    }
    barPtr->nActive = 0;

    if (barPtr->nActiveIndices > 0) {
	XRectangle *activeRects;
	int *activeToData;
	register int i, n;
	register int count;

	activeRects = Blt_Malloc(sizeof(XRectangle) * barPtr->nActiveIndices);
	assert(activeRects);
	activeToData = Blt_Malloc(sizeof(int) * barPtr->nActiveIndices);
	assert(activeToData);
	count = 0;
	for (i = 0; i < barPtr->nRects; i++) {
	    for (n = 0; n < barPtr->nActiveIndices; n++) {
		if (barPtr->rectToData[i] == barPtr->activeIndices[n]) {
		    activeRects[count] = barPtr->rectangles[i];
		    activeToData[count] = i;
		    count++;
		}
	    }
	}
	barPtr->nActive = count;
	barPtr->activeRects = activeRects;
	barPtr->activeToData = activeToData;
    }
    barPtr->flags &= ~ACTIVE_PENDING;
}

static void
ResetBar(barPtr)
    Bar *barPtr;
{
    /* Release any storage associated with the display of the bar */
    ClearPalette(barPtr->palette);
    if (barPtr->activeRects != NULL) {
	Blt_Free(barPtr->activeRects);
    }
    if (barPtr->activeToData != NULL) {
	Blt_Free(barPtr->activeToData);
    }
    if (barPtr->xErrorBars != NULL) {
	Blt_Free(barPtr->xErrorBars);
    }
    if (barPtr->xErrorToData != NULL) {
	Blt_Free(barPtr->xErrorToData);
    }
    if (barPtr->yErrorBars != NULL) {
	Blt_Free(barPtr->yErrorBars);
    }
    if (barPtr->yErrorToData != NULL) {
	Blt_Free(barPtr->yErrorToData);
    }
    if (barPtr->rectangles != NULL) {
	Blt_Free(barPtr->rectangles);
    }
    if (barPtr->rectToData != NULL) {
	Blt_Free(barPtr->rectToData);
    }
    barPtr->activeToData = barPtr->xErrorToData = barPtr->yErrorToData = 
	barPtr->rectToData = NULL;
    barPtr->activeRects = barPtr->rectangles = NULL;
    barPtr->xErrorBars = barPtr->yErrorBars = NULL;
    barPtr->nActive = barPtr->xErrorBarCnt = barPtr->yErrorBarCnt = 
	barPtr->nRects = 0;
}

/*
 * ----------------------------------------------------------------------
 *
 * MapBar --
 *
 *	Calculates the actual window coordinates of the bar element.
 *	The window coordinates are saved in the bar element structure.
 *
 * Results:
 *	None.
 *
 * Notes:
 *	A bar can have multiple segments (more than one x,y pairs).
 *	In this case, the bar can be represented as either a set of
 *	non-contiguous bars or a single multi-segmented (stacked) bar.
 *
 *	The x-axis layout for a barchart may be presented in one of
 *	two ways.  If abscissas are used, the bars are placed at those
 *	coordinates.  Otherwise, the range will represent the number
 *	of values.
 *
 * ----------------------------------------------------------------------
 */
static void
MapBar(graphPtr, elemPtr)
    Graph *graphPtr;
    Element *elemPtr;
{
    Bar *barPtr = (Bar *)elemPtr;
    FreqKey key;
    PenStyle **dataToStyle;
    Point2D c1, c2;		/* Two opposite corners of the rectangle
				 * in graph coordinates. */
    double *x, *y;
    double barWidth, barOffset;
    double baseline;
    double dx, dy;
    int *rectToData;		/* Maps rectangles to data point indices */
    int height;
    int invertBar;
    int nPoints, count;
    register XRectangle *rectPtr, *rectangles;
    register int i;
    int size;
    Blt_ChainLink *linkPtr;
    BarPenStyle *stylePtr;

    ResetBar(barPtr);
    nPoints = NumberOfPoints(barPtr);
    if (nPoints < 1) {
	return;			/* No data points */
    }
    barWidth = graphPtr->barWidth;
    if (barPtr->barWidth > 0.0) {
	barWidth = barPtr->barWidth;
    }
    baseline = (barPtr->axes.y->logScale) ? 1.0 : graphPtr->baseline;
    barOffset = barWidth * 0.5;

    /*
     * Create an array of rectangles representing the screen coordinates
     * of all the segments in the bar.
     */
    rectPtr = rectangles = Blt_Malloc(nPoints * sizeof(XRectangle));
    assert(rectangles);
    rectToData = Blt_Calloc(nPoints, sizeof(int));
    assert(rectToData);

    x = barPtr->x.valueArr, y = barPtr->y.valueArr;
    count = 0;
    for (i = 0; i < nPoints; i++) {
	if (((x[i] - barWidth) > barPtr->axes.x->axisRange.max) ||
	    ((x[i] + barWidth) < barPtr->axes.x->axisRange.min)) {
	    continue;		/* Abscissa is out of range of the x-axis */
	}
	c1.x = x[i] - barOffset;
	c1.y = y[i];
	c2.x = c1.x + barWidth;
	c2.y = baseline;

	/*
	 * If the mode is "aligned" or "stacked" we need to adjust the
	 * x or y coordinates of the two corners.
	 */

	if ((graphPtr->nStacks > 0) && (graphPtr->mode != MODE_INFRONT)) {
	    Blt_HashEntry *hPtr;

	    key.value = x[i];
	    key.axes = barPtr->axes;
	    hPtr = Blt_FindHashEntry(&(graphPtr->freqTable), (char *)&key);
	    if (hPtr != NULL) {
		FreqInfo *infoPtr;
		double slice, width;

		infoPtr = (FreqInfo *)Blt_GetHashValue(hPtr);
		switch (graphPtr->mode) {
		case MODE_STACKED:
		    c2.y = infoPtr->lastY;
		    c1.y += c2.y;
		    infoPtr->lastY = c1.y;
		    break;

		case MODE_ALIGNED:
		    infoPtr->count++;
		    slice = barWidth / (double)infoPtr->freq;
		    c1.x += slice * (infoPtr->freq - infoPtr->count);
		    c2.x = c1.x + slice;
		    break;

		case MODE_OVERLAP:
		    infoPtr->count++;
		    slice = barWidth / (double)(infoPtr->freq * 2);
		    width = slice * (infoPtr->freq + 1);
		    c1.x += slice * (infoPtr->freq - infoPtr->count);
		    c2.x = c1.x + width;
		    break;
		case MODE_INFRONT:
		    break;
		}
	    }
	}
	invertBar = FALSE;
	if (c1.y < c2.y) {
	    double temp;

	    /* Handle negative bar values by swapping ordinates */
	    temp = c1.y, c1.y = c2.y, c2.y = temp;
	    invertBar = TRUE;
	}
	/*
	 * Get the two corners of the bar segment and compute the rectangle
	 */
	c1 = Blt_Map2D(graphPtr, c1.x, c1.y, &barPtr->axes);
	c2 = Blt_Map2D(graphPtr, c2.x, c2.y, &barPtr->axes);

	/* Bound the bars vertically by the size of the graph window */
	if (c1.y < 0.0) {
	    c1.y = 0.0;
	} else if (c1.y > (double)graphPtr->height) {
	    c1.y = (double)graphPtr->height;
	}
	if (c2.y < 0.0) {
	    c2.y = 0.0;
	} else if (c2.y > (double)graphPtr->height) {
	    c2.y = (double)graphPtr->height;
	}
	dx = c1.x - c2.x;
	dy = c1.y - c2.y;
	height = (int)Round(FABS(dy));
	if (invertBar) {
	    rectPtr->y = (short int)MIN(c1.y, c2.y);
	} else {
	    rectPtr->y = (short int)(MAX(c1.y, c2.y)) - height;
	}
	rectPtr->x = (short int)MIN(c1.x, c2.x);
	rectPtr->width = (short int)Round(FABS(dx)) + 1;
	if (rectPtr->width < 1) {
	    rectPtr->width = 1;
	}
	rectPtr->height = height + 1;
	if (rectPtr->height < 1) {
	    rectPtr->height = 1;
	}
	rectToData[count] = i;	/* Save the data index corresponding to the
				 * rectangle */
	rectPtr++;
	count++;
    }
    barPtr->nRects = count;
    barPtr->rectangles = rectangles;
    barPtr->rectToData = rectToData;
    if (barPtr->nActiveIndices > 0) {
	MapActiveBars(barPtr);
    }
	
    size = 20;
    if (count > 0) {
	size = rectangles->width;
    }
    /* Set the symbol size of all the pen styles. */
    for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
	 linkPtr = Blt_ChainNextLink(linkPtr)) {
	stylePtr = Blt_ChainGetValue(linkPtr);
	stylePtr->symbolSize = size;
	stylePtr->errorBarCapWidth = (stylePtr->penPtr->errorBarCapWidth > 0) 
	    ? stylePtr->penPtr->errorBarCapWidth : (int)(size * 0.6666666);
	stylePtr->errorBarCapWidth /= 2;
    }
    dataToStyle = Blt_StyleMap((Element *)barPtr);
    if (((barPtr->yHigh.nValues > 0) && (barPtr->yLow.nValues > 0)) ||
	((barPtr->xHigh.nValues > 0) && (barPtr->xLow.nValues > 0)) ||
	(barPtr->xError.nValues > 0) || (barPtr->yError.nValues > 0)) {
	Blt_MapErrorBars(graphPtr, (Element *)barPtr, dataToStyle);
    }
    MergePens(barPtr, dataToStyle);
    Blt_Free(dataToStyle);
}

/*
 * -----------------------------------------------------------------
 *
 * DrawSymbol --
 *
 * 	Draw a symbol centered at the given x,y window coordinate
 *	based upon the element symbol type and size.
 *
 * Results:
 *	None.
 *
 * Problems:
 *	Most notable is the round-off errors generated when
 *	calculating the centered position of the symbol.
 * -----------------------------------------------------------------
 */
/*ARGSUSED*/
static void
DrawSymbol(graphPtr, drawable, elemPtr, x, y, size)
    Graph *graphPtr;
    Drawable drawable;		/* Pixmap or window to draw into */
    Element *elemPtr;
    int x, y;
    int size;
{
    BarPen *penPtr = ((Bar *)elemPtr)->normalPenPtr;
    int radius;

    if ((penPtr->border == NULL) && (penPtr->fgColor == NULL)) {
	return;
    }
    radius = (size / 2);
    size--;

    x -= radius;
    y -= radius;
    XSetTSOrigin(graphPtr->display, penPtr->gc, x, y);
    XFillRectangle(graphPtr->display, drawable, penPtr->gc, x, y, 
		   size, size);
    XSetTSOrigin(graphPtr->display, penPtr->gc, 0, 0);
}

/*
 * -----------------------------------------------------------------
 *
 * DrawBarSegments --
 *
 * 	Draws each of the rectangular segments for the element.
 *
 * Results:
 *	None.
 *
 * -----------------------------------------------------------------
 */
static void
DrawBarSegments(
    Graph *graphPtr,
    Drawable drawable,		/* Pixmap or window to draw into */
    BarPen *penPtr,
    XRectangle *rectangles,
    int nRects)
{
    register XRectangle *rectPtr;

    if ((penPtr->border == NULL) && (penPtr->fgColor == NULL)) {
	return;
    }
    XFillRectangles(graphPtr->display, drawable, penPtr->gc, rectangles, 
		    nRects);
    if ((penPtr->border != NULL) && (penPtr->borderWidth > 0) && 
	(penPtr->relief != TK_RELIEF_FLAT)) {
	XRectangle *endPtr;

	for (rectPtr = rectangles, endPtr = rectangles + nRects; 
	     rectPtr < endPtr; rectPtr++) {
	    Blt_Draw3DRectangle(graphPtr->tkwin, drawable, penPtr->border,
		rectPtr->x, rectPtr->y, rectPtr->width, rectPtr->height,
		penPtr->borderWidth, penPtr->relief);
	}
    }
}

/*
 * -----------------------------------------------------------------
 *
 * DrawBarValues --
 *
 * 	Draws the numeric value of the bar.
 *
 * Results:
 *	None.
 *
 * -----------------------------------------------------------------
 */
static void
DrawBarValues(
    Graph *graphPtr, 
    Drawable drawable, 
    Bar *barPtr,
    BarPen *penPtr,
    XRectangle *rectangles,
    int nRects,
    int *rectToData)
{
    XRectangle *rectPtr, *endPtr;
    int count;
    char *fmt;
    char string[TCL_DOUBLE_SPACE * 2 + 2];
    double x, y;
    Point2D anchorPos;
    
    count = 0;
    fmt = penPtr->valueFormat;
    if (fmt == NULL) {
	fmt = "%g";
    }
    for (rectPtr = rectangles, endPtr = rectangles + nRects; rectPtr < endPtr; 
	 rectPtr++) {
	x = barPtr->x.valueArr[rectToData[count]];
	y = barPtr->y.valueArr[rectToData[count]];
	count++;
	if (penPtr->valueShow == SHOW_X) {
	    sprintf(string, fmt, x); 
	} else if (penPtr->valueShow == SHOW_Y) {
	    sprintf(string, fmt, y); 
	} else if (penPtr->valueShow == SHOW_BOTH) {
	    sprintf(string, fmt, x);
	    strcat(string, ",");
	    sprintf(string + strlen(string), fmt, y);
	}
	if (graphPtr->inverted) {
	    anchorPos.y = rectPtr->y + rectPtr->height * 0.5;
	    anchorPos.x = rectPtr->x + rectPtr->width;
	    if (y < graphPtr->baseline) {
		anchorPos.x -= rectPtr->width;
	    } 
	} else {
	    anchorPos.x = rectPtr->x + rectPtr->width * 0.5;
	    anchorPos.y = rectPtr->y;
	    if (y < graphPtr->baseline) {			
		anchorPos.y += rectPtr->height;
	    }
	}
	Blt_DrawText(graphPtr->tkwin, drawable, string, &(penPtr->valueStyle), 
		     (int)anchorPos.x, (int)anchorPos.y);
    }
}


/*
 * ----------------------------------------------------------------------
 *
 * DrawNormalBar --
 *
 *	Draws the rectangle representing the bar element.  If the
 *	relief option is set to "raised" or "sunken" and the bar
 *	borderwidth is set (borderwidth > 0), a 3D border is drawn
 *	around the bar.
 *
 *	Don't draw bars that aren't visible (i.e. within the limits
 *	of the axis).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	X drawing commands are output.
 *
 * ----------------------------------------------------------------------
 */
static void
DrawNormalBar(graphPtr, drawable, elemPtr)
    Graph *graphPtr;
    Drawable drawable;
    Element *elemPtr;
{
    Bar *barPtr = (Bar *)elemPtr;
    int count;
    Blt_ChainLink *linkPtr;
    register BarPenStyle *stylePtr;
    BarPen *penPtr;

    count = 0;
    for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
	 linkPtr = Blt_ChainNextLink(linkPtr)) {
	stylePtr = Blt_ChainGetValue(linkPtr);
	penPtr = stylePtr->penPtr;
	if (stylePtr->nRects > 0) {
	    DrawBarSegments(graphPtr, drawable, penPtr, stylePtr->rectangles, 
		stylePtr->nRects);
	}
	if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_X)) {
	    Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC, 
		       stylePtr->xErrorBars, stylePtr->xErrorBarCnt);
	}
	if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_Y)) {
	    Blt_Draw2DSegments(graphPtr->display, drawable, penPtr->errorBarGC, 
		       stylePtr->yErrorBars, stylePtr->yErrorBarCnt);
	}
	if (penPtr->valueShow != SHOW_NONE) {
	    DrawBarValues(graphPtr, drawable, barPtr, penPtr, 
			stylePtr->rectangles, stylePtr->nRects, 
			barPtr->rectToData + count);
	}
	count += stylePtr->nRects;
    }
}

/*
 * ----------------------------------------------------------------------
 *
 * DrawActiveBar --
 *
 *	Draws rectangles representing the active segments of the
 *	bar element.  If the -relief option is set (other than "flat")
 *	and the borderwidth is greater than 0, a 3D border is drawn
 *	around the each bar segment.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	X drawing commands are output.
 *
 * ----------------------------------------------------------------------
 */
static void
DrawActiveBar(graphPtr, drawable, elemPtr)
    Graph *graphPtr;
    Drawable drawable;
    Element *elemPtr;
{
    Bar *barPtr = (Bar *)elemPtr;

    if (barPtr->activePenPtr != NULL) {
	BarPen *penPtr = barPtr->activePenPtr;

	if (barPtr->nActiveIndices > 0) {
	    if (barPtr->flags & ACTIVE_PENDING) {
		MapActiveBars(barPtr);
	    }
	    DrawBarSegments(graphPtr, drawable, penPtr, barPtr->activeRects, 
			 barPtr->nActive);
	    if (penPtr->valueShow != SHOW_NONE) {
		DrawBarValues(graphPtr, drawable, barPtr, penPtr, 
			   barPtr->activeRects, barPtr->nActive, 
			   barPtr->activeToData);
	    }
	} else if (barPtr->nActiveIndices < 0) {
	    DrawBarSegments(graphPtr, drawable, penPtr, barPtr->rectangles, 
			 barPtr->nRects);
	    if (penPtr->valueShow != SHOW_NONE) {
		DrawBarValues(graphPtr, drawable, barPtr, penPtr, 
			barPtr->rectangles, barPtr->nRects, barPtr->rectToData);
	    }
	}
    }
}

/*
 * -----------------------------------------------------------------
 *
 * SymbolToPostScript --
 *
 * 	Draw a symbol centered at the given x,y window coordinate
 *	based upon the element symbol type and size.
 *
 * Results:
 *	None.
 *
 * Problems:
 *	Most notable is the round-off errors generated when
 *	calculating the centered position of the symbol.
 *
 * -----------------------------------------------------------------
 */
/*ARGSUSED*/
static void
SymbolToPostScript(graphPtr, psToken, elemPtr, x, y, size)
    Graph *graphPtr;
    PsToken psToken;
    Element *elemPtr;
    int size;
    double x, y;
{
    Bar *barPtr = (Bar *)elemPtr;
    BarPen *bpPtr = barPtr->normalPenPtr;

    if ((bpPtr->border == NULL) && (bpPtr->fgColor == NULL)) {
	return;
    }
    /*
     * Build a PostScript procedure to draw the fill and outline of
     * the symbol after the path of the symbol shape has been formed
     */
    Blt_AppendToPostScript(psToken, "\n",
	"/DrawSymbolProc {\n",
	"  gsave\n    ", (char *)NULL);
    if (bpPtr->stipple != None) {
	if (bpPtr->border != NULL) {
	    Blt_BackgroundToPostScript(psToken,Tk_3DBorderColor(bpPtr->border));
	    Blt_AppendToPostScript(psToken, "    Fill\n    ", (char *)NULL);
	}
	if (bpPtr->fgColor != NULL) {
	    Blt_ForegroundToPostScript(psToken, bpPtr->fgColor);
	} else {
	    Blt_ForegroundToPostScript(psToken,Tk_3DBorderColor(bpPtr->border));
	}
	Blt_StippleToPostScript(psToken, graphPtr->display, bpPtr->stipple);
    } else if (bpPtr->fgColor != NULL) {
	Blt_ForegroundToPostScript(psToken, bpPtr->fgColor);
	Blt_AppendToPostScript(psToken, "    fill\n", (char *)NULL);
    }
    Blt_AppendToPostScript(psToken, "  grestore\n", (char *)NULL);
    Blt_AppendToPostScript(psToken, "} def\n\n", (char *)NULL);
    Blt_FormatToPostScript(psToken, "%g %g %d Sq\n", x, y, size);
}

static void
SegmentsToPostScript(graphPtr, psToken, penPtr, rectPtr, nRects)
    Graph *graphPtr;
    PsToken psToken;
    BarPen *penPtr;
    register XRectangle *rectPtr;
    int nRects;
{
    XRectangle *endPtr;

    if ((penPtr->border == NULL) && (penPtr->fgColor == NULL)) {
	return;
    }
    for (endPtr = rectPtr + nRects; rectPtr < endPtr; rectPtr++) {
	if ((rectPtr->width < 1) || (rectPtr->height < 1)) {
	    continue;
	}
	if (penPtr->stipple != None) {
	    Blt_RegionToPostScript(psToken, 
		(double)rectPtr->x, (double)rectPtr->y,
		(int)rectPtr->width - 1, (int)rectPtr->height - 1);
	    if (penPtr->border != NULL) {
		Blt_BackgroundToPostScript(psToken, 
			Tk_3DBorderColor(penPtr->border));
		Blt_AppendToPostScript(psToken, "Fill\n", (char *)NULL);
	    }
	    if (penPtr->fgColor != NULL) {
		Blt_ForegroundToPostScript(psToken, penPtr->fgColor);
	    } else {
		Blt_ForegroundToPostScript(psToken, 
					   Tk_3DBorderColor(penPtr->border));
	    }
	    Blt_StippleToPostScript(psToken, graphPtr->display, 
				    penPtr->stipple);
	} else if (penPtr->fgColor != NULL) {
	    Blt_ForegroundToPostScript(psToken, penPtr->fgColor);
	    Blt_RectangleToPostScript(psToken, 
		(double)rectPtr->x, (double)rectPtr->y,
		(int)rectPtr->width - 1, (int)rectPtr->height - 1);
	}
	if ((penPtr->border != NULL) && (penPtr->borderWidth > 0) && 
	    (penPtr->relief != TK_RELIEF_FLAT)) {
	    Blt_Draw3DRectangleToPostScript(psToken, penPtr->border, 
		(double)rectPtr->x, (double)rectPtr->y, 
		(int)rectPtr->width, (int)rectPtr->height,
		penPtr->borderWidth, penPtr->relief);
	}
    }
}

static void
BarValuesToPostScript(
    Graph *graphPtr,
    PsToken psToken,
    Bar *barPtr,
    BarPen *penPtr,
    XRectangle *rectangles,
    int nRects,
    int *rectToData)
{
    XRectangle *rectPtr, *endPtr;
    int count;
    char *fmt;
    char string[TCL_DOUBLE_SPACE * 2 + 2];
    double x, y;
    Point2D anchorPos;
    
    count = 0;
    fmt = penPtr->valueFormat;
    if (fmt == NULL) {
	fmt = "%g";
    }
    for (rectPtr = rectangles, endPtr = rectangles + nRects; rectPtr < endPtr; 
	 rectPtr++) {
	x = barPtr->x.valueArr[rectToData[count]];
	y = barPtr->y.valueArr[rectToData[count]];
	count++;
	if (penPtr->valueShow == SHOW_X) {
	    sprintf(string, fmt, x); 
	} else if (penPtr->valueShow == SHOW_Y) {
	    sprintf(string, fmt, y); 
	} else if (penPtr->valueShow == SHOW_BOTH) {
	    sprintf(string, fmt, x);
	    strcat(string, ",");
	    sprintf(string + strlen(string), fmt, y);
	}
	if (graphPtr->inverted) {
	    anchorPos.y = rectPtr->y + rectPtr->height * 0.5;
	    anchorPos.x = rectPtr->x + rectPtr->width;
	    if (y < graphPtr->baseline) {
		anchorPos.x -= rectPtr->width;
	    } 
	} else {
	    anchorPos.x = rectPtr->x + rectPtr->width * 0.5;
	    anchorPos.y = rectPtr->y;
	    if (y < graphPtr->baseline) {			
		anchorPos.y += rectPtr->height;
	    }
	}
	Blt_TextToPostScript(psToken, string, &(penPtr->valueStyle), 
		     anchorPos.x, anchorPos.y);
    }
}

/*
 * ----------------------------------------------------------------------
 *
 * ActiveBarToPostScript --
 *
 *	Similar to the NormalBarToPostScript procedure, generates
 *	PostScript commands to display the rectangles representing the
 *	active bar segments of the element.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	PostScript pen width, dashes, and color settings are changed.
 *
 * ----------------------------------------------------------------------
 */
/*ARGSUSED*/
static void
ActiveBarToPostScript(graphPtr, psToken, elemPtr)
    Graph *graphPtr;
    PsToken psToken;
    Element *elemPtr;
{
    Bar *barPtr = (Bar *)elemPtr;

    if (barPtr->activePenPtr != NULL) {
	BarPen *penPtr = barPtr->activePenPtr;
	
	if (barPtr->nActiveIndices > 0) {
	    if (barPtr->flags & ACTIVE_PENDING) {
		MapActiveBars(barPtr);
	    }
	    SegmentsToPostScript(graphPtr, psToken, penPtr,
				 barPtr->activeRects, barPtr->nActive);
	    if (penPtr->valueShow != SHOW_NONE) {
		BarValuesToPostScript(graphPtr, psToken, barPtr, penPtr, 
		   barPtr->activeRects, barPtr->nActive, barPtr->activeToData);
	    }
	} else if (barPtr->nActiveIndices < 0) {
	    SegmentsToPostScript(graphPtr, psToken, penPtr, 
				 barPtr->rectangles, barPtr->nRects);
	    if (penPtr->valueShow != SHOW_NONE) {
		BarValuesToPostScript(graphPtr, psToken, barPtr, penPtr, 
		   barPtr->rectangles, barPtr->nRects, barPtr->rectToData);
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------
 *
 * NormalBarToPostScript --
 *
 *	Generates PostScript commands to form the rectangles
 *	representing the segments of the bar element.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	PostScript pen width, dashes, and color settings are changed.
 *
 * ----------------------------------------------------------------------
 */
/*ARGSUSED*/
static void
NormalBarToPostScript(graphPtr, psToken, elemPtr)
    Graph *graphPtr;
    PsToken psToken;
    Element *elemPtr;
{
    Bar *barPtr = (Bar *)elemPtr;
    Blt_ChainLink *linkPtr;
    register BarPenStyle *stylePtr;
    int count;
    BarPen *penPtr;
    XColor *colorPtr;

    count = 0;
    for (linkPtr = Blt_ChainFirstLink(barPtr->palette); linkPtr != NULL;
	 linkPtr = Blt_ChainNextLink(linkPtr)) {
	stylePtr = Blt_ChainGetValue(linkPtr);
	penPtr = stylePtr->penPtr;
	if (stylePtr->nRects > 0) {
	    SegmentsToPostScript(graphPtr, psToken, penPtr, 
		stylePtr->rectangles, stylePtr->nRects);
	}
	colorPtr = penPtr->errorBarColor;
	if (colorPtr == COLOR_DEFAULT) {
	    colorPtr = penPtr->fgColor;
	}
	if ((stylePtr->xErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_X)) {
	    Blt_LineAttributesToPostScript(psToken, colorPtr, 
		penPtr->errorBarLineWidth, NULL, CapButt, JoinMiter);
	    Blt_2DSegmentsToPostScript(psToken, stylePtr->xErrorBars,
		stylePtr->xErrorBarCnt);
	}
	if ((stylePtr->yErrorBarCnt > 0) && (penPtr->errorBarShow & SHOW_Y)) {
	    Blt_LineAttributesToPostScript(psToken, colorPtr, 
		penPtr->errorBarLineWidth, NULL, CapButt, JoinMiter);
	    Blt_2DSegmentsToPostScript(psToken, stylePtr->yErrorBars,
		stylePtr->yErrorBarCnt);
	}
	if (penPtr->valueShow != SHOW_NONE) {
	    BarValuesToPostScript(graphPtr, psToken, barPtr, penPtr, 
		stylePtr->rectangles, stylePtr->nRects, 
		barPtr->rectToData + count);
	}
	count += stylePtr->nRects;
    }
}

/*
 * ----------------------------------------------------------------------
 *
 * DestroyBar --
 *
 *	Release memory and resources allocated for the bar element.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Everything associated with the bar element is freed up.
 *
 * ----------------------------------------------------------------------
 */
#define FreeElemVector(v) \
    if ((v).clientId != NULL) { \
	Blt_FreeVectorId((v).clientId); \
    } else if ((v).valueArr != NULL) { \
	Blt_Free((v).valueArr); \
    } 

static void
DestroyBar(graphPtr, elemPtr)
    Graph *graphPtr;
    Element *elemPtr;
{
    Bar *barPtr = (Bar *)elemPtr;

    if (barPtr->normalPenPtr != &(barPtr->builtinPen)) {
	Blt_FreePen(graphPtr, (Pen *)barPtr->normalPenPtr);
    }
    DestroyPen(graphPtr, (Pen *)&(barPtr->builtinPen));
    if (barPtr->activePenPtr != NULL) {
	Blt_FreePen(graphPtr, (Pen *)barPtr->activePenPtr);
    }
    FreeElemVector(barPtr->x);
    FreeElemVector(barPtr->y);
    FreeElemVector(barPtr->w);
    FreeElemVector(barPtr->xHigh);
    FreeElemVector(barPtr->xLow);
    FreeElemVector(barPtr->xError);
    FreeElemVector(barPtr->yHigh);
    FreeElemVector(barPtr->yLow);
    FreeElemVector(barPtr->yError);

    ResetBar(barPtr);
    if (barPtr->activeIndices != NULL) {
	Blt_Free(barPtr->activeIndices);
    }
    if (barPtr->palette != NULL) {
	Blt_FreePalette(graphPtr, barPtr->palette);
	Blt_ChainDestroy(barPtr->palette);
    }
    if (barPtr->tags != NULL) {
	Blt_Free(barPtr->tags);
    }
}

/*
 * ----------------------------------------------------------------------
 *
 * Blt_BarElement --
 *
 *	Allocate memory and initialize methods for the new bar element.
 *
 * Results:
 *	The pointer to the newly allocated element structure is returned.
 *
 * Side effects:
 *	Memory is allocated for the bar element structure.
 *
 * ----------------------------------------------------------------------
 */

static ElementProcs barProcs =
{
    ClosestBar,
    ConfigureBar,
    DestroyBar,
    DrawActiveBar,
    DrawNormalBar,
    DrawSymbol,
    GetBarExtents,
    ActiveBarToPostScript,
    NormalBarToPostScript,
    SymbolToPostScript,
    MapBar,
};


Element *
Blt_BarElement(graphPtr, name, type)
    Graph *graphPtr;
    char *name;
    Blt_Uid type;
{
    register Bar *barPtr;

    barPtr = Blt_Calloc(1, sizeof(Bar));
    assert(barPtr);
    barPtr->normalPenPtr = &(barPtr->builtinPen);
    barPtr->procsPtr = &barProcs;
    barPtr->specsPtr = barElemConfigSpecs;
    barPtr->labelRelief = TK_RELIEF_FLAT;
    barPtr->classUid = type;
    /* By default, an element's name and label are the same. */
    barPtr->label = Blt_Strdup(name);
    barPtr->name = Blt_Strdup(name);

    barPtr->graphPtr = graphPtr;
    barPtr->hidden = FALSE;

    InitPen(barPtr->normalPenPtr);
    barPtr->palette = Blt_ChainCreate();
    return (Element *)barPtr;
}

/*
 * ----------------------------------------------------------------------
 *
 * Blt_InitFreqTable --
 *
 *	Generate a table of abscissa frequencies.  Duplicate
 *	x-coordinates (depending upon the bar drawing mode) indicate
 *	that something special should be done with each bar segment
 *	mapped to the same abscissa (i.e. it should be stacked,
 *	aligned, or overlay-ed with other segments)
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Memory is allocated for the bar element structure.
 *
 * ----------------------------------------------------------------------
 */
void
Blt_InitFreqTable(graphPtr)
    Graph *graphPtr;
{
    register Element *elemPtr;
    Blt_ChainLink *linkPtr;
    Blt_HashEntry *hPtr;
    Blt_HashSearch cursor;
    Bar *barPtr;
    int isNew, count;
    int nStacks, nSegs;
    int nPoints;
    FreqKey key;
    Blt_HashTable freqTable;
    register int i;
    double *xArr;
    /*
     * Free resources associated with a previous frequency table. This
     * includes the array of frequency information and the table itself
     */
    if (graphPtr->freqArr != NULL) {
	Blt_Free(graphPtr->freqArr);
	graphPtr->freqArr = NULL;
    }
    if (graphPtr->nStacks > 0) {
	Blt_DeleteHashTable(&(graphPtr->freqTable));
	graphPtr->nStacks = 0;
    }
    if (graphPtr->mode == MODE_INFRONT) {
	return;			/* No frequency table is needed for
				 * "infront" mode */
    }
    Blt_InitHashTable(&(graphPtr->freqTable), sizeof(FreqKey) / sizeof(int));

    /*
     * Initialize a hash table and fill it with unique abscissas.
     * Keep track of the frequency of each x-coordinate and how many
     * abscissas have duplicate mappings.
     */
    Blt_InitHashTable(&freqTable, sizeof(FreqKey) / sizeof(int));
    nSegs = nStacks = 0;
    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	elemPtr = Blt_ChainGetValue(linkPtr);
	if ((elemPtr->hidden) || (elemPtr->classUid != bltBarElementUid)) {
	    continue;
	}
	nSegs++;
	barPtr = (Bar *)elemPtr;
	xArr = barPtr->x.valueArr;
	nPoints = NumberOfPoints(barPtr);
	for (i = 0; i < nPoints; i++) {
	    key.value = xArr[i];
	    key.axes = barPtr->axes;
	    hPtr = Blt_CreateHashEntry(&freqTable, (char *)&key, &isNew);
	    assert(hPtr != NULL);
	    if (isNew) {
		count = 1;
	    } else {
		count = (int)Blt_GetHashValue(hPtr);
		if (count == 1) {
		    nStacks++;
		}
		count++;
	    }
	    Blt_SetHashValue(hPtr, (ClientData)count);
	}
    }
    if (nSegs == 0) {
	return;			/* No bar elements to be displayed */
    }
    if (nStacks > 0) {
	FreqInfo *infoPtr;
	FreqKey *keyPtr;
	Blt_HashEntry *h2Ptr;

	graphPtr->freqArr = Blt_Calloc(nStacks, sizeof(FreqInfo));
	assert(graphPtr->freqArr);
	infoPtr = graphPtr->freqArr;
	for (hPtr = Blt_FirstHashEntry(&freqTable, &cursor); hPtr != NULL;
	    hPtr = Blt_NextHashEntry(&cursor)) {
	    count = (int)Blt_GetHashValue(hPtr);
	    keyPtr = (FreqKey *)Blt_GetHashKey(&freqTable, hPtr);
	    if (count > 1) {
		h2Ptr = Blt_CreateHashEntry(&(graphPtr->freqTable),
		    (char *)keyPtr, &isNew);
		count = (int)Blt_GetHashValue(hPtr);
		infoPtr->freq = count;
		infoPtr->axes = keyPtr->axes;
		Blt_SetHashValue(h2Ptr, infoPtr);
		infoPtr++;
	    }
	}
    }
    Blt_DeleteHashTable(&freqTable);
    graphPtr->nStacks = nStacks;
}

/*
 * ----------------------------------------------------------------------
 *
 * Blt_ComputeStacks --
 *
 *	Determine the height of each stack of bar segments.  A stack
 *	is created by designating two or more points with the same
 *	abscissa.  Each ordinate defines the height of a segment in
 *	the stack.  This procedure simply looks at all the data points
 *	summing the heights of each stacked segment. The sum is saved
 *	in the frequency information table.  This value will be used
 *	to calculate the y-axis limits (data limits aren't sufficient).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The heights of each stack is computed. CheckStacks will
 *	use this information to adjust the y-axis limits if necessary.
 *
 * ----------------------------------------------------------------------
 */
void
Blt_ComputeStacks(graphPtr)
    Graph *graphPtr;
{
    Element *elemPtr;
    Bar *barPtr;
    FreqKey key;
    Blt_ChainLink *linkPtr;
    Blt_HashEntry *hPtr;
    int nPoints;
    register int i;
    register FreqInfo *infoPtr;
    double *xArr, *yArr;

    if ((graphPtr->mode != MODE_STACKED) || (graphPtr->nStacks == 0)) {
	return;
    }
    /* Reset the sums for all duplicate values to zero. */

    infoPtr = graphPtr->freqArr;
    for (i = 0; i < graphPtr->nStacks; i++) {
	infoPtr->sum = 0.0;
	infoPtr++;
    }

    /* Look at each bar point, adding the ordinates of duplicate abscissas */

    for (linkPtr = Blt_ChainFirstLink(graphPtr->elements.displayList);
	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
	elemPtr = Blt_ChainGetValue(linkPtr);
	if ((elemPtr->hidden) || (elemPtr->classUid != bltBarElementUid)) {
	    continue;
	}
	barPtr = (Bar *)elemPtr;
	xArr = barPtr->x.valueArr;
	yArr = barPtr->y.valueArr;
	nPoints = NumberOfPoints(barPtr);
	for (i = 0; i < nPoints; i++) {
	    key.value = xArr[i];
	    key.axes = barPtr->axes;
	    hPtr = Blt_FindHashEntry(&(graphPtr->freqTable), (char *)&key);
	    if (hPtr == NULL) {
		continue;
	    }
	    infoPtr = (FreqInfo *)Blt_GetHashValue(hPtr);
	    infoPtr->sum += yArr[i];
	}
    }
}

void
Blt_ResetStacks(graphPtr)
    Graph *graphPtr;
{
    register FreqInfo *infoPtr, *endPtr;

    for (infoPtr = graphPtr->freqArr, 
	     endPtr = graphPtr->freqArr + graphPtr->nStacks;
	 infoPtr < endPtr; infoPtr++) {
	infoPtr->lastY = 0.0;
	infoPtr->count = 0;
    }
}

