/*
 * Copyright (C) 1997-2009, R3vis Corporation.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
 *
 * Original Contributor:
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *   http://www.r3vis.com/
 * Additional Contributor(s):
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: rmvsignal.c,v 1.8 2007/07/22 17:37:17 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.8 $
 * $Log: rmvsignal.c,v $
 * Revision 1.8  2007/07/22 17:37:17  wes
 * Adding usrData and usrCallback parameters to all rmv routines
 *
 * Revision 1.7  2005/06/08 18:33:18  wes
 * Code cleanup to eliminate compiler warnings.
 *
 * Revision 1.6  2005/04/27 03:35:32  wes
 * Free up memory at the end of rmvI2ScatterGeom
 *
 * Revision 1.5  2005/02/19 16:09:13  wes
 * Distro sync and consolidation.
 *
 * Revision 1.4  2005/01/23 17:11:02  wes
 * Copyright updated to 2005.
 *
 * Revision 1.3  2004/01/17 04:09:26  wes
 * Updated copyright line for 2004.
 *
 * Revision 1.2  2003/02/02 02:07:23  wes
 * Updated copyright to 2003.
 *
 * Revision 1.1.1.1  2003/01/28 02:15:23  wes
 * Manual rebuild of rm150 repository.
 *
 * Revision 1.5  2003/01/16 22:21:20  wes
 * Updated all source files to reflect new organization of header files:
 * all header files formerly located in include/rmaux, include/rmi, include/rmv
 * are now located in include/rm.
 *
 * Revision 1.4  2002/04/30 19:40:37  wes
 * Updated copyright dates.
 *
 * Revision 1.3  2001/03/31 17:10:08  wes
 * v1.4.0-alpha-2 checkin.
 *
 * Revision 1.2  2000/04/20 16:17:45  wes
 * JDB modifications: code rearrangement, additional docs.
 *
 * Revision 1.1  2000/04/17 00:05:24  wes
 * Lots of documentation updates, courtesy of jdb.
 *
 * Revision 1.2  2000/02/29 23:43:59  wes
 * Compile warning cleanups.
 *
 * Revision 1.1.1.1  2000/02/28 21:29:40  wes
 * OpenRM 1.2 Checkin
 *
 * Revision 1.1.1.1  2000/02/28 17:18:48  wes
 * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
 *
 */


#include <rm/rm.h>
#include <rm/rmv.h>
#include "rmvprivt.h"

/* public documentation needs work in this file jdb 03/26/00 */

static RMvertex2D Xmarkerverts[4] =
{
  {0.35355339, 0.35355339}, {-0.35355339, -0.35355339},
  {-0.35355339, 0.35355339}, {0.35355339, -0.35355339}
};

static RMvertex2D Ntriangleverts[6] =
{
  {0.0, 0.5}, {-0.4330127, -0.25},
  {-0.4330127,-0.25}, {0.4330127, -0.25},
  {0.4330127, -0.25}, {0.0, 0.5}
};

static RMvertex2D Striangleverts[6] =
{
  {0.0, -0.5}, {-0.4330127, 0.25},
  {-0.4330127, 0.25}, {0.4330127, 0.25},
  {0.4330127, 0.25}, {0.0, -0.5}
};

static RMvertex2D bowtieverts[8] =
{
  {0.5, 0.5}, {-0.5, -0.5},
  {-0.5, -0.5}, {-0.5, 0.5},
  {-0.5, 0.5}, {0.5, -0.5},
  {0.5, -0.5}, {0.5, 0.5}
};

static RMvertex2D squareverts[8] =
{
  {0.5, 0.5}, {-0.5, 0.5},
  {-0.5, 0.5}, {-0.5, -0.5},
  {-0.5, -0.5}, {0.5, -0.5},
  {0.5, -0.5}, {0.5, 0.5}
};

static RMvertex2D diamondverts[8] =
{
  {0.0, 1.0},{-0.5, 0.0},
  {-.5, 0.0}, {0.0, -1.0},
  {0.0, -1.0}, {0.5, 0.0},
  {0.5, 0.0}, {0.0, 1.0}
};

static RMvertex2D crossverts[4] =
{
  {0.5, 0.0}, {-0.5, 0.0},
  {0.0, 0.5}, {0.0, -0.5}
};

static RMvertex2D ntrianglefilledverts[3] =
{
  {0.0, 0.5}, {-0.4330127, -0.25}, {0.4330127, -0.25}
};

typedef struct RMV2DMarkerTools /* PRIVATE */
{
    int         nv;
    int         gl_begin_flag;
    RMvertex2D *shape;
} RMV2DMarkerTools;

static RMV2DMarkerTools
rmv2DMarkerTools[8] = {
    {8, GL_LINES, squareverts},			/* the 2D square marker */
    {4, GL_LINES, crossverts},			/* the cross marker */
    {4, GL_LINES, Xmarkerverts},		/* the X marker */
    {6, GL_LINES, Ntriangleverts},		/* an equilateral triangle pointing north */
    {8, GL_LINES, bowtieverts} ,
    {6, GL_LINES, Striangleverts},		/* an equilateral triangle pointing south */
    {8, GL_LINES, diamondverts},		/* the 2D diamond marker */
    {3, GL_TRIANGLES, ntrianglefilledverts}	/* filled north-pointing triangle */
};

/* PRIVATE declarations */
void private_rmv2DGetBarScale (RMvertex2D (*appgridfunc)(int i, void *usrData), void *appGridUsrData, RMvertex2D *base_point, int i, float scale, int scale_mode, int axis_offset_enum, float *dx, float *dy);

/*
 * ----------------------------------------------------
 * @Name rmvI2AreaFill
 @pstart
 RMenum rmvI2AreaFill (RMvertex2D (*appgridfunc)(int i, void *usrData),
                       void *appGridUsrData,
	               float (*appdatafunc)(int i, void *usrData),
		       void *appDataUsrData,
		       float (*appdata2func)(int i, void *usrData),
		       void *appData2UsrData,
		       RMvisMap *vmap,
		       int axis_offset_enum,
		       int npts,
		       float zeroval,
		       RMnode *n)
 @pend

 @astart
 RMvertex2D (*appgridfunc)(int i, void *appGridUsrData) - a handle to a
    caller-supplied function that returns an RMvertex2D (x, y)
    corresponding to the grid point (i) (input).
	          
 void *appGridUsrData - (input, optional) you can pass in a void * to
    user data; this void * will be provided as a parameter to the user-defined
    grid evalution callback. If you don't want to pass in a void * to any
    user data for the grid evaluation callback, pass in a NULL for this
    parameter.

 float (*appdatafunc)(int i, void *usrData) - a handle to a
    caller-supplied function that returns a float, which is the scalar
    value at the grid point (i) (input).

 void *appDataUsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    data field evaluation callback. If you don't want to pass in a void *
    to any user data for the primary data field grid evaluation callback,
    pass in a NULL for this parameter.
			   
 float (*appdata2func)(int i, void *usrData) -- (input, optional)
    A handle to a caller-supplied function that returns a float, which is the
    scalar value at the grid point (i) of the optional secondary data field.
    RMV uses the float returned by appdata2func in conjunction with the
    RMvismap to compute vertex color.

 void *appData2UsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    secondary data field evaluation callback. If you don't want to pass in
    a void * to any user data for the secondary data field evaluation
    callback, pass in a NULL for this parameter.
    
 RMvisMap *vmap - a handle to an RMvisMap object (input).
		  
 int axis_offset_enum - an integer specifying in which axis to offset
    the area fill.  Must be one of RMV_XAXIS_OFFSET, RMV_YAXIS_OFFSET
    (input).

 int npts - an integer specifying the number of data points (input).
		       
 float zeroval - a float specifying the zero line within the data
    range.  The "area fill" will be computed relative to this zero
    line (input).
		       
 RMnode *n - a handle to an RMnode (modified).
 @aend

 @dstart

 Creates an "area filled" plot from data.  The area filled is a single
 color, the RMnode default color at render time.  The area outline
 plot can also be colored via the secondary data function and the
 RMvismap.  

 Upon success, RM_CHILL is returned and the area outline plot
 primitives are added to the RMnode.  Otherwise, RM_WHACKED is
 returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmvI2AreaFill (RMvertex2D (*appgridfunc)(int i, void *usrData),
	       void *appGridUsrData,
	       float (*appdatafunc)(int i, void *usrData),
	       void *appDataUsrData,
	       float (*appdata2func)(int i, void *usrData),
	       void *appData2UsrData,
	       RMvisMap *vmap,
	       int axis_offset_enum,
	       int npts,
	       float zeroval,
	       RMnode *n)
{
    int          i, j;
    int          new_size;
    float       *xcoords, *ycoords, *data, *data2;
    float       *new_data = NULL, *new_x = NULL, *new_y = NULL, *new_data2 = NULL;
    RMvertex2D  *v;
    RMcolor4D   *c;
    RMprimitive *t;

    /* error check on functions, etc. */
    {
	int s1, s2, s3, s4;

	s1 = RM_ASSERT((void *)n, "rmvI2AreaFill error: NULL RMnode for return parameter");
	s2 = RM_ASSERT((void *)appgridfunc, "rmvI2AreaFill error: NULL app grid callback");
	s3 = RM_ASSERT((void *)appdatafunc, "rmvI2AreaFill error: NULL app data callback ");
	s4 = RM_CHILL;
	if (!(((vmap != NULL) && (appdata2func != NULL)) || ((vmap == NULL) && (appdata2func == NULL))))
	    s4 = RM_ASSERT((void *)NULL, "rmvI2AreaFill error: the vismap and secondary data callback function must BOTH be NULL or defined.");

	if ((s1 ==  RM_WHACKED) || (s2 == RM_WHACKED) || (s3 == RM_WHACKED) || (s4 == RM_WHACKED))
	    return(RM_WHACKED);
    }
    
    /* need a way to check that both vmap & app2datafunc are NULL or not */
    t = rmPrimitiveNew(RM_QUADMESH);
    
    xcoords = (float *)malloc(sizeof(float) * npts);
    ycoords = (float *)malloc(sizeof(float) * npts);
    data = (float *)malloc(sizeof(float) * npts);
    
    if (vmap != NULL)
	data2 = (float *)malloc(sizeof(float) * npts);
    else
	data2 = NULL;

    /* gather the grid from the app. this is necessary since we're going
     * to potentially modify the grid underneath the caller, placing new
     * grid points at where the data crosses the zerovalue
     */
    
    for (i = 0; i < npts; i++)
    {
	RMvertex2D t;
	
	t = (*appgridfunc)(i, appGridUsrData);

	xcoords[i] = t.x;
	ycoords[i] = t.y;
	data[i] = (*appdatafunc)(i, appDataUsrData);

	if (data2)
	    data2[i] = (*appdata2func)(i, appData2UsrData);
    }
    private_rmvInsertZeroCrossings(data, npts, xcoords, ycoords, NULL, data2, &new_data, &new_x, &new_y, NULL, &new_size, &new_data2, zeroval);

    if (data2)
	c = rmColor4DNew(new_size * 2);
    else
	c = NULL;

    v = rmVertex2DNew(new_size * 2);	
    
    for (i = 0, j = 0; i < new_size; i++)
    {
	v[j].x = new_x[i];
	v[j].y = new_y[i];

	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    v[j].y += zeroval;
	else 
	    if (axis_offset_enum == RMV_XAXIS_OFFSET)
		v[j].x += zeroval;
	
	if (data2)
	{
	    int k = rmVismapIndexFromData(vmap, new_data2[i]);

	    rmVismapGetColor4D(vmap, k, (c + j));
	}
	j++;
    }

    for (i = 0; i < new_size; i++)
    {
	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    v[j].y = new_y[i] + new_data[i];
	else
	    v[j].y = new_y[i];
	    
	if (axis_offset_enum == RMV_XAXIS_OFFSET)
	    v[j].x = new_x[i] + new_data[i];
	else
	    v[j].x = new_x[i];

	if (data2)
	{
	    int k = rmVismapIndexFromData(vmap, new_data2[i]);

	    rmVismapGetColor4D(vmap, k, (c + j));
	}
	j++;
    }
    rmPrimitiveSetVertex2D(t, (new_size * 2), v, RM_COPY_DATA, NULL);

    rmPrimitiveSetQmeshDims(t, new_size, 2);
    
    if (c)
    {
        rmPrimitiveSetColor4D(t, (new_size * 2), c, RM_COPY_DATA, NULL);
	rmColor4DDelete(c);
    }
	
    /* now, add the new primitive onto the node */
    rmNodeAddPrimitive(n, t);

    rmVertex2DDelete(v);
    free((void *)xcoords);
    free((void *)ycoords);
    free((void *)data);
    if (data2)
	free((void *)data2);

    if (new_data)
	free((void *)new_data);

    if (new_data2)
	free((void *)new_data2);

    if (new_x)
	free((void *)new_x);

    if (new_y)
	free((void *)new_y);

    return(RM_CHILL);
}
    

/*
 * ----------------------------------------------------
 * @Name rmvI2AreaOutline
 @pstart
 RMenum rmvI2AreaOutline (RMvertex2D (*appgridfunc)(int i, void *usrData),
                          void *appGridUsrData,
		          float (*appdatafunc)(int i, void *usrData),
			  void *appDataUsrData,
			  float (*appdata2func)(int i, void *usrData),
			  void *appData2UsrData,
			  RMvisMap *map,
			  int axis_offset_enum,
			  int npts,
			  float zerocrossing,
			  RMenum linewidth_enum,
			  RMenum linestyle_enum,
			  RMnode *n)
 @pend

 @astart
 RMvertex2D (*appgridfunc)(int i, void *appGridUsrData) - a handle to a
    caller-supplied function that returns an RMvertex2D (x, y)
    corresponding to the grid point (i) (input).
	          
 void *appGridUsrData - (input, optional) you can pass in a void * to
    user data; this void * will be provided as a parameter to the user-defined
    grid evalution callback. If you don't want to pass in a void * to any
    user data for the grid evaluation callback, pass in a NULL for this
    parameter.

 float (*appdatafunc)(int i, void *usrData) - a handle to a
    caller-supplied function that returns a float, which is the scalar
    value at the grid point (i) (input).

 void *appDataUsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    data field evaluation callback. If you don't want to pass in a void *
    to any user data for the primary data field grid evaluation callback,
    pass in a NULL for this parameter.
			   
 float (*appdata2func)(int i, void *usrData) -- (input, optional)
    A handle to a caller-supplied function that returns a float, which is the
    scalar value at the grid point (i) of the optional secondary data field.
    RMV uses the float returned by appdata2func in conjunction with the
    RMvismap to compute vertex color.

 void *appData2UsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    secondary data field evaluation callback. If you don't want to pass in
    a void * to any user data for the secondary data field evaluation
    callback, pass in a NULL for this parameter.
    
 RMvisMap *vmap - a handle to an RMvisMap object (input).
		  
 int axis_offset_enum - an integer specifying in which axis to offset
    the area outline.  Must be one of RMV_XAXIS_OFFSET,
    RMV_YAXIS_OFFSET (input).

 int npts - an integer specifying the number of data points (input).
		       
 float zerocrossing - a float specifying the zero line within the data
    range.  The "area outline" will be computed relative to this zero
    line (input).
		       
 RMenum linewidth_enum - an RMenum specifying the line width.  Must be
    one of RM_LINEWIDTH_NARROW, RM_LINEWIDTH_MEDIUM,
    RM_LINEWIDTH_HEAVY, or RM_LINEWIDTH_[1..8] (input).

 RMenum linestyle_enum - an RMenum specifying the line style.  Must
    be one of RM_LINES_SOLID, RM_LINES_DASHED, RM_LINES_DOTTED,
    RM_LINES_DOT_DASH, or RM_LINES_DASH_DASH_DOT (input).

 RMnode *n - a handle to an RMnode (modified).
 @aend

 @dstart

 Creates an "area outline" plot from data.  The area outline is a
 single color, the RMnode default color at render time.  The area
 outline plot can also be colored via the secondary data function and
 the RMvismap.  Line segments connects the zeroval value with the
 first and last data point.  

 Upon success, RM_CHILL is returned and the area outline plot
 primitives are added to the RMnode.  Otherwise, RM_WHACKED is
 returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmvI2AreaOutline (RMvertex2D (*appgridfunc)(int i, void *usrData),
		  void *appGridUsrData,
		  float (*appdatafunc)(int i, void *usrData),
		  void *appDataUsrData,
		  float (*appdata2func)(int i, void *usrData),
		  void *appData2UsrData,
		  RMvisMap *map,
		  int axis_offset_enum,
		  int npts,
		  float zerocrossing,
		  RMenum linewidth_enum,
		  RMenum linestyle_enum,
		  RMnode *n)
{
    int          i, j;
    int          npts_in_polyline;
    float        xoff = 0.0, yoff = 0.0;
    RMprimitive *t;
    RMvertex2D  *v;
    RMcolor4D   *c;

    /* error check on functions, etc. */
    {
	int s1, s2, s3, s4;

	s1 = RM_ASSERT((void *)n, "rmvI2AreaOutline error: NULL RMnode for return parameter");
	s2 = RM_ASSERT((void *)appgridfunc, "rmvI2AreaOutline error: NULL app grid callback");
	s3 = RM_ASSERT((void *)appdatafunc, "rmvI2AreaOutline error: NULL app data callback ");
	s4 = RM_CHILL;
	if (!(((map != NULL) && (appdata2func != NULL)) || ((map == NULL) && (appdata2func == NULL))))
	    s4 = RM_ASSERT((void *)NULL, "rmvI2AreaOutline error: the vismap and secondary data callback function must BOTH be NULL or defined.");

	if ((s1 ==  RM_WHACKED) || (s2 == RM_WHACKED) || (s3 == RM_WHACKED) || (s4 == RM_WHACKED))
	    return(RM_WHACKED);
    }
    xoff = (axis_offset_enum == RMV_XAXIS_OFFSET) ? zerocrossing : 0.0;
    yoff = (axis_offset_enum == RMV_YAXIS_OFFSET) ? zerocrossing : 0.0;

    npts_in_polyline = (npts * 2) + 1;

    v = rmVertex2DNew(npts_in_polyline);
    
    t = rmPrimitiveNew(RM_LINE_STRIP);

    if (map != NULL)
	c = rmColor4DNew(npts_in_polyline);
    else
	c = NULL;

    for (i = 0, j = 0; i < npts; i++)
    {
	RMvertex2D t;

	t = (*appgridfunc)(i, appGridUsrData);
	
        v[j].x = t.x + xoff;
	v[j].y = t.y + yoff;
	
	if (c != NULL)
	{
	    int   k;
	    float d2;
	    
	    d2 = (*appdata2func)(i, appData2UsrData);
	    k = rmVismapIndexFromData(map, d2);
	    rmVismapGetColor4D(map, k, (c + j));
	}
	j++;
    }

    for (i = (npts - 1); i >= 0; i--)
    {
	float td;

	td = (*appdatafunc)(i, appDataUsrData);

	v[j].x = v[i].x - xoff;
	v[j].y = v[i].y - yoff;

	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    v[j].y += td;
	    
	if (axis_offset_enum == RMV_XAXIS_OFFSET)
	    v[j].x += td;

	if (c != NULL)
	{
	    int   k;
	    float d2;

	    d2 = (*appdata2func)(i, appData2UsrData);
	    k = rmVismapIndexFromData(map, d2);
	    rmVismapGetColor4D(map, k, (c + j));
	}
	j++;
    }
    i = 0;
    v[j].x = v[0].x;
    v[j].y = v[0].y;

    if (c != NULL)
    {
	int   k;
	float d2;

	d2 = (*appdata2func)(i, appData2UsrData);
	k = rmVismapIndexFromData(map, d2);
	rmVismapGetColor4D(map, k, (c + j));
    }
    rmPrimitiveSetVertex2D(t, npts_in_polyline, v, RM_COPY_DATA, NULL);

    if (c != NULL)
    {
        rmPrimitiveSetColor4D(t, npts_in_polyline, c, RM_COPY_DATA, NULL);
	rmColor4DDelete(c);
    }
	
    /* now, add the new primitive onto the node */
    rmNodeAddPrimitive(n, t);
    rmVertex2DDelete(v);

    /* apply the user-requested linewidth/linestyles to the RMnode */
    rmNodeSetLineWidth(n, linewidth_enum);
    rmNodeSetLineStyle(n, linestyle_enum);

    return(RM_CHILL);
}
    

/*
 * ----------------------------------------------------
 * @Name rmvI2BarFilled
 @pstart
 RMenum rmvI2BarFilled (RMvertex2D (*appgridfunc)(int i, void *usrData),
                        void *appGridUsrData,
	                float (*appdatafunc)(int i, void *usrData),
			void *appDataUsrData,
		        float (*appdata2func)(int i, void *usrData),
			void *appData2UsrData,
		        RMvisMap *vmap,
		        int axis_offset_enum,
		        int npts,
		        float scale,
		        int scale_enum,
		        RMnode *n)
 @pend

 @astart
 RMvertex2D (*appgridfunc)(int i, void *appGridUsrData) - a handle to a
    caller-supplied function that returns an RMvertex2D (x, y)
    corresponding to the grid point (i) (input).
	          
 void *appGridUsrData - (input, optional) you can pass in a void * to
    user data; this void * will be provided as a parameter to the user-defined
    grid evalution callback. If you don't want to pass in a void * to any
    user data for the grid evaluation callback, pass in a NULL for this
    parameter.

 float (*appdatafunc)(int i, void *usrData) - a handle to a
    caller-supplied function that returns a float, which is the scalar
    value at the grid point (i) (input).

 void *appDataUsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    data field evaluation callback. If you don't want to pass in a void *
    to any user data for the primary data field grid evaluation callback,
    pass in a NULL for this parameter.
			   
 float (*appdata2func)(int i, void *usrData) -- (input, optional)
    A handle to a caller-supplied function that returns a float, which is the
    scalar value at the grid point (i) of the optional secondary data field.
    RMV uses the float returned by appdata2func in conjunction with the
    RMvismap to compute vertex color.

 void *appData2UsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    secondary data field evaluation callback. If you don't want to pass in
    a void * to any user data for the secondary data field evaluation
    callback, pass in a NULL for this parameter.
    
 RMvisMap *vmap - a handle to an RMvisMap object (input).
		  
 int axis_offset_enum - an integer specifying in which axis to offset
    the filled bar.  Must be one of RMV_XAXIS_OFFSET, RMV_YAXIS_OFFSET
    (input).

 int npts - an integer specifying the number of data points (input).
		       
 float scale - a float specfiying the scaling factor for the bars.
    When the scaling mode is RMV_SCALE_ABSOLUTE, this parameter
    dictates the size of each bar directly.  When the mode is
    RMV_SCALE_RELATIVE, the product of this parameter and the adjacent
    grid spacing determines the size of the bars (input).
			   
 int scaling_enum - an integer specifying the scaling type.  Must be
    one of RMV_SCALE_ABSOLUTE OR RMV_SCALE_RELATIVE (input).
			  
 RMnode *n - a handle to an RMnode (modified).
 @aend

 @dstart

 Creates a traditional bar plot from data, with the data shown as
 filled bars.  The bars are a single color, the RMnode default color
 at render time.  The bar plot can also be colored via the secondary
 data function and the RMvismap.  

 Upon success, RM_CHILL is returned and the filled bar plot primitives
 are added to the RMnode.  Otherwise, RM_WHACKED is returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmvI2BarFilled (RMvertex2D (*appgridfunc)(int i, void *usrData),
		void *appGridUsrData,
	        float (*appdatafunc)(int i, void *usrData),
		void *appDataUsrData,
	        float (*appdata2func)(int i, void *usrData),
		void *appData2UsrData,
	        RMvisMap *vmap,
	        int axis_offset_enum,
	        int npts,
	        float scale,
	        int scale_enum,
	        RMnode *n)
{
    int          i, j;
    float        dx, dy;
    RMprimitive *t;
    RMvertex2D  *v;
    RMcolor4D   *c;

    /* error check on functions, etc. */
    {
	int s1, s2, s3, s4;

	s1 = RM_ASSERT((void *)n, "rmvI2BarFilled error: NULL RMnode for return parameter");
	s2 = RM_ASSERT((void *)appgridfunc, "rmvI2BarFilled error: NULL app grid callback");
	s3 = RM_ASSERT((void *)appdatafunc, "rmvI2BarFilled error: NULL app data callback ");
	s4 = RM_CHILL;
	if (!(((vmap != NULL) && (appdata2func != NULL)) || ((vmap == NULL) && (appdata2func == NULL))))
	    s4 = RM_ASSERT((void *)NULL, "rmvI2BarFilled error: the vismap and secondary data callback function must BOTH be NULL or defined.");

	if ((s1 ==  RM_WHACKED) || (s2 == RM_WHACKED) || (s3 == RM_WHACKED) || (s4 == RM_WHACKED))
	    return(RM_WHACKED);
    }
    
    v = rmVertex2DNew(npts * 4);
    t = rmPrimitiveNew(RM_QUADS);

    if (vmap != NULL)
	c = rmColor4DNew(npts * 4); /* per-vertex color */
    else
	c = NULL;

    for (i = 0, j = 0; i < npts; i++)
    {
	float      data;
	RMvertex2D bar_top, bar_base;
	RMcolor4D  quadColor;

	bar_base = (*appgridfunc)(i, appGridUsrData);
	bar_top = bar_base;
	data = (*appdatafunc)(i, appDataUsrData);
	
	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    bar_top.y += data;
	if (axis_offset_enum == RMV_XAXIS_OFFSET)
	    bar_top.x += data;

	private_rmv2DGetBarScale(appgridfunc, appGridUsrData, &bar_base, i, scale, scale_enum, axis_offset_enum, &dx, &dy);

	if (c != NULL)
        {
	    int   j;
	    float d2;

	    d2 = (*appdata2func)(i, appData2UsrData);
	    j = rmVismapIndexFromData(vmap, d2);
	    rmVismapGetColor4D(vmap, j, &quadColor);
	}
	
	v[j].x = bar_base.x - dx;
	v[j].y = bar_base.y - dy;

	if (c != NULL)
	    c[j] = quadColor;
	j++;
	
	v[j].x = bar_base.x + dx;
	v[j].y = bar_base.y + dy;
	if (c != NULL)
	    c[j] = quadColor;
	j++;
       
	v[j].x = bar_top.x + dx;
	v[j].y = bar_top.y + dy;
	if (c != NULL)
	    c[j] = quadColor;
	j++;
       
	v[j].x = bar_top.x - dx;
	v[j].y = bar_top.y - dy;
	if (c != NULL)
	    c[j] = quadColor;
	j++;
	
    }
    rmPrimitiveSetVertex2D(t, (npts * 4), v, RM_COPY_DATA,NULL);
    free(v);

    if (c)
    {
        rmPrimitiveSetColor4D(t, npts*4, c, RM_COPY_DATA, NULL);
	rmColor4DDelete(c);
    }
	
    /* now, add the new primitive onto the node */
    rmNodeAddPrimitive(n, t);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmvI2BarOutline
 @pstart
 RMenum rmvI2BarOutline (RMvertex2D (*appgridfunc)(int i, void *usrData),
                         void *appGridUsrData,
		         float (*appdatafunc)(int i, void *usrData),
			 void *appDataUsrData,
			 float (*appdata2func)(int i, void *usrData),
			 void *appData2UsrData,
			 RMvisMap *vmap,
			 int axis_offset_enum,
			 int npts,
			 float scale,
			 int scale_enum,
			 RMenum linewidth,
			 RMenum linestyle,
			 RMnode *n)
 @pend

 @astart
 RMvertex2D (*appgridfunc)(int i, void *appGridUsrData) - a handle to a
    caller-supplied function that returns an RMvertex2D (x, y)
    corresponding to the grid point (i) (input).
	          
 void *appGridUsrData - (input, optional) you can pass in a void * to
    user data; this void * will be provided as a parameter to the user-defined
    grid evalution callback. If you don't want to pass in a void * to any
    user data for the grid evaluation callback, pass in a NULL for this
    parameter.

 float (*appdatafunc)(int i, void *usrData) - a handle to a
    caller-supplied function that returns a float, which is the scalar
    value at the grid point (i) (input).

 void *appDataUsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    data field evaluation callback. If you don't want to pass in a void *
    to any user data for the primary data field grid evaluation callback,
    pass in a NULL for this parameter.
			   
 float (*appdata2func)(int i, void *usrData) -- (input, optional)
    A handle to a caller-supplied function that returns a float, which is the
    scalar value at the grid point (i) of the optional secondary data field.
    RMV uses the float returned by appdata2func in conjunction with the
    RMvismap to compute vertex color.

 void *appData2UsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    secondary data field evaluation callback. If you don't want to pass in
    a void * to any user data for the secondary data field evaluation
    callback, pass in a NULL for this parameter.
    
 RMvisMap *vmap - a handle to an optional RMvisMap object (input).
		  
 int axis_offset_enum - an integer specifying in which axis to offset
    the bar outline.  Must be one of RMV_XAXIS_OFFSET,
    RMV_YAXIS_OFFSET (input).

 int npts - an integer specifying the number of data points (input).
		       
 float scale - a float specfiying the scaling factor for the bars.
    When the scaling mode is RMV_SCALE_ABSOLUTE, this parameter
    dictates the width of each bar directly.  When the mode is
    RMV_SCALE_RELATIVE, the product of this parameter and the adjacent
    grid spacing determines the width of the bars (input).
			   
 int scaling_enum - an integer specifying the scaling type.  Must be
    one of RMV_SCALE_ABSOLUTE OR RMV_SCALE_RELATIVE (input).
			  
 RMenum linewidth_enum - an RMenum specifying the line width.  Must be
    one of RM_LINEWIDTH_NARROW, RM_LINEWIDTH_MEDIUM,
    RM_LINEWIDTH_HEAVY, or RM_LINEWIDTH_[1..8] (input).

 RMenum linestyle_enum - an RMenum specifying the line style.  Must
    be one of RM_LINES_SOLID, RM_LINES_DASHED, RM_LINES_DOTTED,
    RM_LINES_DOT_DASH, or RM_LINES_DASH_DASH_DOT (input).

 RMnode *n - a handle to an RMnode (modified).
 @aend

 @dstart

 Creates a traditional bar plot from data, with the data shown as
 outline bars.  The bar outlines are a single color, the RMnode
 default color at render time.  The bar plot can also be colored via
 the secondary data function and the RMvismap.  

 Upon success, RM_CHILL is returned and the outline bar plot
 primitives are added to the RMnode.  Otherwise, RM_WHACKED is
 returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmvI2BarOutline (RMvertex2D (*appgridfunc)(int i, void *usrData),
		 void *appGridUsrData,
		 float (*appdatafunc)(int i, void *usrData),
		 void *appDataUsrData,
		 float (*appdata2func)(int i, void *usrData),
		 void *appData2UsrData,
		 RMvisMap *vmap,
		 int axis_offset_enum,
		 int npts,
		 float scale,
		 int scale_enum,
		 RMenum linewidth,
		 RMenum linestyle,
		 RMnode *n)
{
    int          i, j;
    float        dx, dy;
    RMprimitive *t;
    RMvertex2D  *v;
    RMcolor4D   *c = NULL;
    RMcolor4D    rgb;
    RMvertex2D   p1, p2;

    /* error check on functions, etc. */
    {
	int s1, s2, s3, s4;

	s1 = RM_ASSERT((void *)n, "rmvI2BarOutline error: NULL RMnode for return parameter");
	s2 = RM_ASSERT((void *)appgridfunc, "rmvI2BarOutline error: NULL app grid callback");
	s3 = RM_ASSERT((void *)appdatafunc, "rmvI2BarOutline error: NULL app data callback ");
	s4 = RM_CHILL;
	if (!(((vmap != NULL) && (appdata2func != NULL)) || ((vmap == NULL) && (appdata2func == NULL))))
	    s4 = RM_ASSERT((void *)NULL, "rmvI2BarOutline error: the vismap and secondary data callback function must BOTH be NULL or defined.");
	
	if ((s1 ==  RM_WHACKED) || (s2 == RM_WHACKED) || (s3 == RM_WHACKED) || (s4 == RM_WHACKED))
	    return(RM_WHACKED);
    }
    
    v = rmVertex2DNew(npts * 8);	/* 8 lines/point, 2 verts/line */
    
    t = rmPrimitiveNew(RM_LINES);

    if (vmap != NULL)
	c = rmColor4DNew(npts * 8);
    else
	c = NULL;

    for (i = 0, j = 0; i < npts; i++)
    {
	float data;

	p1 = (*appgridfunc)(i, appGridUsrData);
	p2 = p1;
	data = (*appdatafunc)(i, appDataUsrData);
	
	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    p2.y += data;
	if (axis_offset_enum == RMV_XAXIS_OFFSET)
	    p2.x += data;

	private_rmv2DGetBarScale(appgridfunc, appGridUsrData,  &p1, i, scale, scale_enum, axis_offset_enum, &dx, &dy);

       if (c)
       {
	   int   k, ii;
	   float d2;

	   d2 = (*appdata2func)(i, appData2UsrData);
	   k = rmVismapIndexFromData(vmap, d2);
	   rmVismapGetColor4D(vmap, k, &rgb);

	   k = j;

	   for (ii = 0; ii < 8; ii++)
	       c[k + ii] = rgb;

#if 0	   
	   VCOPY(rgb, (c + k));
	   VCOPY(rgb, (c + 1 + k));
	   
	   VCOPY(rgb, (c + 2 + k));
	   VCOPY(rgb, (c + 3 + k));
	   
	   VCOPY(rgb, (c + 4 + k));
	   VCOPY(rgb, (c + 5 + k));
	   
	   VCOPY(rgb, (c + 6 + k));
	   VCOPY(rgb, (c + 7 + k));
#endif
       }
       /* seg 1 */
       v[j].x = p1.x - dx;
       v[j].y = p1.y - dy;
       j++;
       
       v[j].x = p1.x + dx;
       v[j].y = p1.y + dy;
       j++;

       /* seg 2 */
       v[j].x = p1.x + dx;
       v[j].y = p1.y + dy;
       j++;
       
       v[j].x = p2.x + dx;
       v[j].y = p2.y + dy;
       j++;

       /* seg 3 */
       v[j].x = p2.x + dx;
       v[j].y = p2.y + dy;
       j++;
       
       v[j].x = p2.x - dx;
       v[j].y = p2.y - dy;
       j++;

       /* seg 4 */
       v[j].x = p2.x - dx;
       v[j].y = p2.y - dy;
       j++;
       
       /* seg 5 */
       v[j].x = p1.x - dx;
       v[j].y = p1.y - dy;
       j++;
    }

    rmPrimitiveSetVertex2D(t, (npts * 8), v, RM_COPY_DATA, NULL);

    if (c)
    {
        rmPrimitiveSetColor4D(t, (npts * 8), c, RM_COPY_DATA, NULL);
	rmColor4DDelete(c);
    }

    /* set the linestyle and weights */
    rmNodeSetLineStyle(n, linestyle);
    rmNodeSetLineWidth(n, linewidth);
    
    /* now, add the new primitive onto the node */
    rmNodeAddPrimitive(n, t);
    rmVertex2DDelete(v);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmvI2Impulse
 @pstart
 RMenum rmvI2Impulse (RMvertex2D (*appgridfunc)(int i, void *usrData),
                      void *appGridUsrData,
	              float (*appdatafunc)(int i, void *usrData),
		      void *appDataUsrData,
		      float (*appdata2func)(int i, void *usrData),
		      void *appData2UsrData,
		      RMvisMap *vmap,
		      int axis_offset_enum,
		      int npts,
		      RMenum linewidth_enum,
		      RMenum linestyle_enum,
		      RMnode *n)
 @pend

 @astart
 RMvertex2D (*appgridfunc)(int i, void *appGridUsrData) - a handle to a
    caller-supplied function that returns an RMvertex2D (x, y)
    corresponding to the grid point (i) (input).
	          
 void *appGridUsrData - (input, optional) you can pass in a void * to
    user data; this void * will be provided as a parameter to the user-defined
    grid evalution callback. If you don't want to pass in a void * to any
    user data for the grid evaluation callback, pass in a NULL for this
    parameter.

 float (*appdatafunc)(int i, void *usrData) - a handle to a
    caller-supplied function that returns a float, which is the scalar
    value at the grid point (i) (input).

 void *appDataUsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    data field evaluation callback. If you don't want to pass in a void *
    to any user data for the primary data field grid evaluation callback,
    pass in a NULL for this parameter.
			   
 float (*appdata2func)(int i, void *usrData) -- (input, optional)
    A handle to a caller-supplied function that returns a float, which is the
    scalar value at the grid point (i) of the optional secondary data field.
    RMV uses the float returned by appdata2func in conjunction with the
    RMvismap to compute vertex color.

 void *appData2UsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    secondary data field evaluation callback. If you don't want to pass in
    a void * to any user data for the secondary data field evaluation
    callback, pass in a NULL for this parameter.
    
 RMvisMap *vmap - a handle to an optional RMvisMap object (input).
		  
 int axis_offset_enum - an integer specifying in which axis to offset
    the impulse spike.  Must be one of RMV_XAXIS_OFFSET,
    RMV_YAXIS_OFFSET (input).

 int npts - an integer specifying the number of data points (input).
		       
 RMenum linewidth_enum - an RMenum specifying the line width.  Must be
    one of RM_LINEWIDTH_NARROW, RM_LINEWIDTH_MEDIUM,
    RM_LINEWIDTH_HEAVY, or RM_LINEWIDTH_[1..8] (input).

 RMenum linestyle_enum - an RMenum specifying the line style.  Must
    be one of RM_LINES_SOLID, RM_LINES_DASHED, RM_LINES_DOTTED,
    RM_LINES_DOT_DASH, or RM_LINES_DASH_DASH_DOT (input).

 RMnode *n - a handle to an RMnode (modified).
 @aend

 @dstart

 Creates an impulse plot from data, with the data shown as horizontal
 or vertical spikes.  The impulse plot is a single color, the RMnode
 default color at render time.  The impulse spikes can also be colored
 via the secondary data function and the RMvismap.  The ith impulse
 spike is a combination of the grid point, the primary data function,
 and the axis offset chosen.  

 Upon success, RM_CHILL is returned and the impulse plot primitives
 are added to the RMnode.  Otherwise, RM_WHACKED is returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmvI2Impulse (RMvertex2D (*appgridfunc)(int i, void *usrData),
	      void *appGridUsrData,
	      float (*appdatafunc)(int i, void *usrData),
	      void *appDataUsrData,
	      float (*appdata2func)(int i, void *usrData),
	      void *appData2UsrData,
	      RMvisMap *vmap,
	      int axis_offset_enum,
	      int npts,
	      RMenum linewidth_enum,
	      RMenum linestyle_enum,
	      RMnode *n)
{
    int          i, j;
    RMprimitive *t;
    RMvertex2D  *v;
    RMcolor4D   *c;

    /* error check on functions, etc. */
    {
	int s1, s2, s3, s4;

	s1 = RM_ASSERT((void *)n, "rmvI2Impulse error: NULL RMnode for return parameter");
	s2 = RM_ASSERT((void *)appgridfunc, "rmvI2Impulse error: NULL app grid callback");
	s3 = RM_ASSERT((void *)appdatafunc, "rmvI2Impulse error: NULL app data callback ");
	s4 = RM_CHILL;
	if (!(((vmap != NULL) && (appdata2func != NULL)) || ((vmap == NULL) && (appdata2func == NULL))))
	    s4 = RM_ASSERT((void *)NULL, "rmvI2Impulse error: the vismap and secondary data callback function must BOTH be NULL or defined.");

	if ((s1 ==  RM_WHACKED) || (s2 == RM_WHACKED) || (s3 == RM_WHACKED) || (s4 == RM_WHACKED))
	    return(RM_WHACKED);
    }

    v = rmVertex2DNew(npts * 2);
    
    t = rmPrimitiveNew(RM_LINES);

    if (vmap != NULL)
        c = rmColor4DNew(npts * 2);
    else
	c = NULL;

    for (i = 0, j = 0; i < npts; i++)
    {
	float data;
	
	v[j] = (*appgridfunc)(i, appGridUsrData);
	v[j + 1] = v[j];
	data = (*appdatafunc)(i, appDataUsrData);

	if (c)
	{
	    int   k;
	    float d2;

	    d2 = (*appdata2func)(i, appData2UsrData);
	    k = rmVismapIndexFromData(vmap, d2);
	    rmVismapGetColor4D(vmap, k, (c + j));
	}
	j++;

	if (axis_offset_enum == RMV_XAXIS_OFFSET)
	    v[j].x += data;
	
	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    v[j].y += data;

	if (c)
	    c[j] = c[j - 1];

	j++;
    }

    /* set the linestyle and weights */
    rmNodeSetLineStyle(n, linestyle_enum);
    rmNodeSetLineWidth(n, linewidth_enum);
    
    rmPrimitiveSetVertex2D(t, (npts * 2), v, RM_COPY_DATA, NULL);

    if (c)
    {
        rmPrimitiveSetColor4D(t, (npts * 2), c, RM_COPY_DATA, NULL);
	rmColor4DDelete(c);
    }
	
    /* now, add the new primitive onto the node */
    rmNodeAddPrimitive(n, t);
    rmVertex2DDelete(v);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmvI2Polyline
 @pstart
 RMenum rmvI2Polyline (RMvertex2D (*appgridfunc)(int i, void *usrData),
                       void *appGridUsrData,
	               float (*appdatafunc)(int i, void *usrData),
		       void *appDataUsrData,
		       float (*appdata2func)(int i, void *usrData),
		       void *appData2UsrData,
		       RMvisMap *vmap,
		       int axis_offset_enum,
		       int npts,
		       RMenum linewidth_enum,
		       RMenum linestyle_enum,
		       RMnode *n)
 @pend

 @astart
 RMvertex2D (*appgridfunc)(int i, void *appGridUsrData) - a handle to a
    caller-supplied function that returns an RMvertex2D (x, y)
    corresponding to the grid point (i) (input).
	          
 void *appGridUsrData - (input, optional) you can pass in a void * to
    user data; this void * will be provided as a parameter to the user-defined
    grid evalution callback. If you don't want to pass in a void * to any
    user data for the grid evaluation callback, pass in a NULL for this
    parameter.

 float (*appdatafunc)(int i, void *usrData) - a handle to a
    caller-supplied function that returns a float, which is the scalar
    value at the grid point (i) (input).

 void *appDataUsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    data field evaluation callback. If you don't want to pass in a void *
    to any user data for the primary data field grid evaluation callback,
    pass in a NULL for this parameter.
			   
 float (*appdata2func)(int i, void *usrData) -- (input, optional)
    A handle to a caller-supplied function that returns a float, which is the
    scalar value at the grid point (i) of the optional secondary data field.
    RMV uses the float returned by appdata2func in conjunction with the
    RMvismap to compute vertex color.

 void *appData2UsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    secondary data field evaluation callback. If you don't want to pass in
    a void * to any user data for the secondary data field evaluation
    callback, pass in a NULL for this parameter.
    
 RMvisMap *vmap - a handle to an RMvisMap object (input).
		  
 int axis_offset_enum - an integer specifying in which axis to offset
    the polyline.  Must be one of RMV_XAXIS_OFFSET, RMV_YAXIS_OFFSET
    (input).

 int npts - an integer specifying the number of data points (input).
		       
 RMenum linewidth_enum - an RMenum specifying the line width.  Must be
    one of RM_LINEWIDTH_NARROW, RM_LINEWIDTH_MEDIUM,
    RM_LINEWIDTH_HEAVY, or RM_LINEWIDTH_[1..8] (input).

 RMenum linestyle_enum - an RMenum specifying the line style.  Must
    be one of RM_LINES_SOLID, RM_LINES_DASHED, RM_LINES_DOTTED,
    RM_LINES_DOT_DASH, or RM_LINES_DASH_DASH_DOT (input).

 RMnode *n - a handle to an RMnode (modified).
 @aend

 @dstart

 Creates a polyine x/y plot from data.  The polyline is a single
 color, the RMnode default color at render time.  The polyline can
 also be colored via the secondary data function and the RMvismap.
 The ith vertex of the polyline is a combination of the grid point,
 the primary data function, and the axis offset chosen.  

 Upon success, RM_CHILL is returned and the polyline primitives are
 added to the RMnode.  Otherwise, RM_WHACKED is returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmvI2Polyline (RMvertex2D (*appgridfunc)(int i, void *usrData),
	       void *appGridUsrData,
	       float (*appdatafunc)(int i, void *usrData),
	       void *appDataUsrData,
	       float (*appdata2func)(int i, void *usrData),
	       void *appData2UsrData,
	       RMvisMap *vmap,
	       int axis_offset_enum,
	       int npts,
	       RMenum linewidth_enum,
	       RMenum linestyle_enum,
	       RMnode *n)

{
    int          i;
    RMprimitive *t;
    RMvertex2D  *v;
    RMcolor4D   *c;

    /* error check on functions, etc. */
    {
	int s1, s2, s3, s4;

	s1 = RM_ASSERT((void *)n, "rmvI2Polyline error: NULL RMnode for return parameter");
	s2 = RM_ASSERT((void *)appgridfunc, "rmvI2Polyline error: NULL app grid callback");
	s3 = RM_ASSERT((void *)appdatafunc, "rmvI2Polyline error: NULL app data callback ");
	s4 = RM_CHILL;
	if (!(((vmap != NULL) && (appdata2func != NULL)) || ((vmap == NULL) && (appdata2func == NULL))))
	    s4 = RM_ASSERT((void *)NULL, "rmvI2Polyline error: the vismap and secondary data callback function must BOTH be NULL or defined.");
	
	if ((s1 ==  RM_WHACKED) || (s2 == RM_WHACKED) || (s3 == RM_WHACKED) || (s4 == RM_WHACKED))
	    return(RM_WHACKED);
    }
    
    v = rmVertex2DNew(npts);
    
    t = rmPrimitiveNew(RM_LINE_STRIP);

    if (vmap != NULL)
	c = rmColor4DNew(npts);
    else
	c = NULL;

    for (i = 0; i < npts; i++)
    {
	float data;
	
	v[i] = (*appgridfunc)(i, appGridUsrData);
	data = (*appdatafunc)(i, appDataUsrData);
	
	if (axis_offset_enum == RMV_XAXIS_OFFSET)
	    v[i].x += data;

	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    v[i].y += data;
	
	if (c)
	{
	    int   j;
	    float d2;

	    d2 = (*appdata2func)(i, appData2UsrData);
	    j = rmVismapIndexFromData(vmap, d2);
	    rmVismapGetColor4D(vmap, j, (c + i));
	}

    }

    /* set the linestyle and weights */
    rmNodeSetLineStyle(n,linestyle_enum);
    rmNodeSetLineWidth(n,linewidth_enum);
    
    rmPrimitiveSetVertex2D(t, npts, v, RM_COPY_DATA, NULL);

    if (c)
    {
        rmPrimitiveSetColor4D(t, npts, c, RM_COPY_DATA, NULL);
	rmColor4DDelete(c);
    }
	
    /* now, add the new primitive onto the node */
    rmNodeAddPrimitive(n, t);
    rmVertex2DDelete(v);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmvI2ScatterGeom
 @pstart
 RMenum rmvI2ScatterGeom (RMvertex2D (*appgridfunc)(int i, void *usrData),
		          float (*appdatafunc)(int i, void *usrData),
			  float (*appdata2func)(int i, void *usrData),
			  RMvisMap *vmap,
			  int axis_offset_enum,
			  int npts,
			  float scale,
			  int scaling_enum,
			  int marker_enum,
			  RMnode *n)
 @pend

 @astart
 RMvertex2D (*appgridfunc)(int i, void *appGridUsrData) - a handle to a
    caller-supplied function that returns an RMvertex2D (x, y)
    corresponding to the grid point (i) (input).
	          
 void *appGridUsrData - (input, optional) you can pass in a void * to
    user data; this void * will be provided as a parameter to the user-defined
    grid evalution callback. If you don't want to pass in a void * to any
    user data for the grid evaluation callback, pass in a NULL for this
    parameter.

 float (*appdatafunc)(int i, void *usrData) - a handle to a
    caller-supplied function that returns a float, which is the scalar
    value at the grid point (i) (input).

 void *appDataUsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    data field evaluation callback. If you don't want to pass in a void *
    to any user data for the primary data field grid evaluation callback,
    pass in a NULL for this parameter.
			   
 float (*appdata2func)(int i, void *usrData) -- (input, optional)
    A handle to a caller-supplied function that returns a float, which is the
    scalar value at the grid point (i) of the optional secondary data field.
    RMV uses the float returned by appdata2func in conjunction with the
    RMvismap to compute vertex color.

 void *appData2UsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    secondary data field evaluation callback. If you don't want to pass in
    a void * to any user data for the secondary data field evaluation
    callback, pass in a NULL for this parameter.
    
 RMvisMap *vmap - a handle to an optional RMvisMap object (input).
		  
 int axis_offset_enum - an integer specifying in which axis to offset
    the scatterplot.  Must be one of RMV_XAXIS_OFFSET,
    RMV_YAXIS_OFFSET (input).

 int npts - an integer specifying the number of data points (input).
		       
 float scale - a float specifying the scaling factor for the markers.
    When the scaling mode is RMV_SCALE_ABSOLUTE, this parameter
    dictates the size of each marker directly.  When the mode is
    RMV_SCALE_RELATIVE, the product of this parameter and the adjacent
    grid spacing determines the size of the marker (input).
			   
 int scaling_enum - an integer specifying the scaling type.  Must be
    one of RMV_SCALE_ABSOLUTE OR RMV_SCALE_RELATIVE (input).
			  
 int marker_enum - an integer specifying the geometry to use for
    the markers.  Must be one of RMV_2DMARKER_[SQUARE, CROSS, X,
    NORTHTRIANGLE, BOWTIE, SOUTHTRIANGLE, DIAMOND,
    NORTHTRIANGLE_FILLED] (input).

 RMnode *n - a handle to an RMnode (modified).
 @aend

 @dstart

 Creates a scatterplot from data, with each position represented with
 a geometric marker.  The scatterplot is a single color, the RMnode
 default color at render time.  The geometry markers can also be
 colored via the secondary data function and the RMvismap.  

 Upon success, RM_CHILL is returned and the scatterplot geometry
 primitives are added to the RMnode.  Otherwise, RM_WHACKED is
 returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmvI2ScatterGeom (RMvertex2D (*appgridfunc)(int i, void *usrData),
		  void *appGridUsrData,
		  float (*appdatafunc)(int i, void *usrData),
		  void *appDataUsrData,
		  float (*appdata2func)(int i, void *usrData),
		  void *appData2UsrData,
		  RMvisMap *vmap,
		  int axis_offset_enum,
		  int npts,
		  float scale,
		  int scaling_enum,
		  int marker_enum,
		  RMnode *n)
{
    int                 i;
    float               fscalex, fscaley;
    float              *s;
    RMprimitive        *t;
    RMvertex2D         *v;
    RMcolor4D          *c;
    RMvertex2D          p1;
    RMinternalMarker2D *m;

    /*
     * we create one marker for the whole dataset.  the marker can
     * be at varying scales.
     *
     * june 22 1997 - all markers are the same size.
     */

    /* error check on functions, etc. */
    {
	int s1,s2,s3,s4;

	s1 = RM_ASSERT((void *)n,"rmvI2ScatterGeom error: NULL RMnode for return parameter");
	s2 = RM_ASSERT((void *)appgridfunc, "rmvI2ScatterGeom error: NULL app grid callback");
	s3 = RM_ASSERT((void *)appdatafunc, "rmvI2ScatterGeom error: NULL app data callback ");
	s4 = RM_CHILL;
	if (!(((vmap != NULL) && (appdata2func != NULL)) || ((vmap == NULL) && (appdata2func == NULL))))
	    s4 = RM_ASSERT((void *)NULL, "rmvI2ScatterGeom error: the vismap and secondary data callback function must BOTH be NULL or defined.");

	if ((s1 ==  RM_WHACKED) || (s2 == RM_WHACKED) || (s3 == RM_WHACKED) || (s4 == RM_WHACKED))
	    return(RM_WHACKED);
    }
    
    v = rmVertex2DNew(npts);
    s = rmFloatNew(npts);

    if (vmap != NULL)
        c = rmColor4DNew(npts);
    else
	c = NULL;
    
    t = rmPrimitiveNew(RM_MARKERS2D);

    for (i = 0; i < npts; i++)
    {
	float data;
	
	p1 = (*appgridfunc)(i, appGridUsrData);
	data = (*appdatafunc)(i, appDataUsrData);
	
	private_rmv2DGetBarScale(appgridfunc, appGridUsrData,  &p1, i, scale, scaling_enum, axis_offset_enum, &fscalex, &fscaley);
	
	s[i] = (fscalex > fscaley) ? fscalex : fscaley;
	s[i] *= 2.0;		/* bar style scaling is scaled by 0.5, need to scale up */
	
	if (axis_offset_enum == RMV_XAXIS_OFFSET)
	    p1.x += data;

	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    p1.y += data;

	V2COPY(&p1, (v + i));

	if (c)
	{
	    int   k;
	    float d2;

	    d2 = (*appdata2func)(i, appData2UsrData);
	    k = rmVismapIndexFromData(vmap, d2);
	    rmVismapGetColor4D(vmap, k, (c + i));
	}
    }
    m = rmInternalMarker2DNew(rmv2DMarkerTools[marker_enum].nv, rmv2DMarkerTools[marker_enum].gl_begin_flag, rmv2DMarkerTools[marker_enum].shape);
    rmPrimitiveSetVertex2D(t, npts, v, RM_COPY_DATA, NULL);

    if (c)
    {
        rmPrimitiveSetColor4D(t, npts, c, RM_COPY_DATA, NULL);
	rmColor4DDelete(c);
    }

    rmPrimitiveSetMarkerScale(t, npts, s, RM_COPY_DATA, NULL);
    rmPrimitiveSetMarkerPrims(t, 1, &m);

    rmInternalMarker2DDelete(m); /* thanks Alex, 3/2005 */

    /* now, add the new primitive onto the node */
    rmNodeAddPrimitive(n, t);
    rmVertex2DDelete(v);
    rmFloatDelete(s);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmvI2ScatterGlyph
 @pstart
 RMenum rmvI2ScatterGlyph (RMvertex2D (*appgridfunc)(int i, void *usrData),
                           void *appGridUsrData,
		           float (*appdatafunc)(int i, void *usrData),
			   void *appDataUsrData,
			   float (*appdata2func)(int i, void *usrData),
			   void *appData2UsrData,
			   RMvisMap *vmap,
			   int axis_offset_enum,
			   int npts,
			   int size_enum,
			   int marker_enum,
			   RMnode *n)
 @pend

 @astart
 RMvertex2D (*appgridfunc)(int i, void *appGridUsrData) - a handle to a
    caller-supplied function that returns an RMvertex2D (x, y)
    corresponding to the grid point (i) (input).
	          
 void *appGridUsrData - (input, optional) you can pass in a void * to
    user data; this void * will be provided as a parameter to the user-defined
    grid evalution callback. If you don't want to pass in a void * to any
    user data for the grid evaluation callback, pass in a NULL for this
    parameter.

 float (*appdatafunc)(int i, void *usrData) - a handle to a
    caller-supplied function that returns a float, which is the scalar
    value at the grid point (i) (input).

 void *appDataUsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    data field evaluation callback. If you don't want to pass in a void *
    to any user data for the primary data field grid evaluation callback,
    pass in a NULL for this parameter.
			   
 float (*appdata2func)(int i, void *usrData) -- (input, optional)
    A handle to a caller-supplied function that returns a float, which is the
    scalar value at the grid point (i) of the optional secondary data field.
    RMV uses the float returned by appdata2func in conjunction with the
    RMvismap to compute vertex color.

 void *appData2UsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    secondary data field evaluation callback. If you don't want to pass in
    a void * to any user data for the secondary data field evaluation
    callback, pass in a NULL for this parameter.
    
 RMvisMap *vmap - a handle to an optional RMvisMap object (input).
		  
 int axis_offset_enum - an integer specifying in which axis to offset
    the scatterplot.  Must be one of RMV_XAXIS_OFFSET,
    RMV_YAXIS_OFFSET (input).

 int npts - an integer specifying the number of data points (input).
		       
 int size_enum - an integer specifying the size of the markers.  Must
    be one of RM_FONT_XXS, RM_FONT_XS, RM_FONT_S, RM_FONT_M,
    RM_FONT_L, RM_FONT_XL, RM_FONT_XXL, specifying markers from
    extra-extra small to extra-large (input).

 int marker_enum - an integer specifying the glyph to use for the
    markers.  Must be one of RMV_ZAPFMARKER_[STAR_FILLED,
    STAR_UNFILLED, ASTERIX_FILLED, ASTERIX_UNFILLED, CIRCLE_FILLED,
    CIRCLE_UNFILLED, SQUARE_FILLED, SQUARE_UNFILLED,
    UPTRIANGLE_FILLED, DOWNTRIANGLE_FILLED, CLUBS_FILLED,
    DIAMONDS_FILLED, HEARTS_FILLED, SPADES_FILLED] (input).
			   
 RMnode *n - a handle to an RMnode (modified).
 @aend

 @dstart

 Creates a scatterplot from data, with each position represented with
 a glyph from the Zapf-Dingbats font.  Note that the precise size of
 the markers onscreen is implementation dependent.  The scatterplot is
 a single color, the RMnode default color at render time.  The glyph
 markers can also be colored via the secondary data function and the
 RMvismap.  

 Upon success, RM_CHILL is returned and the scatterplot glyph
 primitives are added to the RMnode.  Otherwise, RM_WHACKED is
 returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmvI2ScatterGlyph (RMvertex2D (*appgridfunc)(int i, void *usrData),
		   void *appGridUsrData,
		   float (*appdatafunc)(int i, void *usrData),
		   void *appDataUsrData,
		   float (*appdata2func)(int i, void *usrData),
		   void *appData2UsrData,
		   RMvisMap *vmap,
		   int axis_offset_enum,
		   int npts,
		   int size_enum,
		   int marker_enum,
		   RMnode *n)
{
    int          i;
    int         *index_list;
    RMprimitive *t;
    RMvertex2D  *v;
    RMcolor4D   *c;

    /* error check on functions, etc. */
    {
	int s1, s2, s3, s4;

	s1 = RM_ASSERT((void *)n, "rmvI2ScatterGlyph error: NULL RMnode for return parameter");
	s2 = RM_ASSERT((void *)appgridfunc, "rmvI2ScatterGlyph error: NULL app grid callback");
	s3 = RM_ASSERT((void *)appdatafunc, "rmvI2ScatterGlyph error: NULL app data callback ");
	s4 = RM_CHILL;
	if (!(((vmap != NULL) && (appdata2func != NULL)) || ((vmap == NULL) && (appdata2func == NULL))))
	    s4 = RM_ASSERT((void *)NULL, "rmvI2ScatterGlyph error: the vismap and secondary data callback function must BOTH be NULL or defined.");

	if ((s1 ==  RM_WHACKED) || (s2 == RM_WHACKED) || (s3 == RM_WHACKED) || (s4 == RM_WHACKED))
	    return(RM_WHACKED);
    }
    
    v = rmVertex2DNew(npts);

    t = rmPrimitiveNew(RM_INDEXED_TEXT);

    if (vmap != NULL)
	c = rmColor4DNew(npts);
    else
	c = NULL;

    /* first, build the list of x,y points */
    for (i = 0; i < npts; i++)
    {
	float data;
	
	v[i] = (*appgridfunc)(i, appGridUsrData);
	data = (*appdatafunc)(i, appDataUsrData);
	
	if (axis_offset_enum == RMV_XAXIS_OFFSET)
	    v[i].x += data;

	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    v[i].y += data;

	if (c)
	{
	    int k;
	    float d2;

	    d2 = (*appdata2func)(i, appData2UsrData);
	    k = rmVismapIndexFromData(vmap, d2);
	    rmVismapGetColor4D(vmap, k, (c + i));
	}
    }

    /* next, create a single bitmap for the requested marker type */
    switch (marker_enum)
	{
	    
	case RMV_ZAPFMARKER_STAR_FILLED:
	case RMV_ZAPFMARKER_STAR_UNFILLED:
	case RMV_ZAPFMARKER_ASTERIX_FILLED :
	case RMV_ZAPFMARKER_ASTERIX_UNFILLED:
	case RMV_ZAPFMARKER_CIRCLE_FILLED:
	case RMV_ZAPFMARKER_CIRCLE_UNFILLED:
	case RMV_ZAPFMARKER_SQUARE_FILLED:
	case RMV_ZAPFMARKER_SQUARE_UNFILLED:
	case RMV_ZAPFMARKER_UPTRIANGLE_FILLED:
	case RMV_ZAPFMARKER_DOWNTRIANGLE_FILLED:
	case RMV_ZAPFMARKER_CLUBS_FILLED:
	case RMV_ZAPFMARKER_DIAMONDS_FILLED:
	case RMV_ZAPFMARKER_HEARTS_FILLED:
	case RMV_ZAPFMARKER_SPADES_FILLED:
	    {
		char *c2[1];
		char  c[2];
		
		c[0] = (char)marker_enum;
		c[1] = '\0';
		c2[0] = c;
		
		rmPrimitiveSetText(t, 1, c2);
	    }
	break;
	
	default:
	    fprintf(stderr," bogus glyph marker enum. \n");
	    break;
	}

    /* build a list of int's, the same length as the number of points
     *  that we have.  these int's will contain indices into the
     *  bitmap cache for the RM_TEXTCHARS primitive. 
     */
    index_list = (int *)malloc(sizeof(int) * npts);

    /*
     * set them all to zero - this is the same as going through
     * one by one and setting a[i] = 0.  the entire primitive is
     * going to use the text glyph cached at index 0 (that's just
     * how this particular vis routine is written).
     */
    memset(index_list, 0, sizeof(int) * npts);

    /* set the index list */
    rmPrimitiveSetIndices(t, npts, index_list, RM_COPY_DATA,NULL);
    rmPrimitiveSetVertex2D(t, npts, v, RM_COPY_DATA, NULL);

    if (c)
    {
        rmPrimitiveSetColor4D(t, npts, c, RM_COPY_DATA, NULL);
	rmColor4DDelete(c);
    }
    
    /* set justification to center/center for dingbat glyphs */
    {
	RMtextProps *tp;
	tp = rmTextPropsNew();
	rmTextPropsSetAttribs(tp, RM_FONT_DINGBATS, size_enum, RM_FALSE, RM_FALSE, RM_CENTER, RM_CENTER);
	rmNodeSetSceneTextProps(n, tp);
	rmTextPropsDelete(tp);	/* RM makes a copy */
    }

    /* now, add the new primitive onto the node */
    rmNodeAddPrimitive(n, t); 
    rmVertex2DDelete(v);
    free((void *)index_list);

    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmvI2Step
 @pstart
 RMenum rmvI2Step (RMvertex2D (*appgridfunc)(int i, void *usrData),
                   void *appGridUsrData,
	           float (*appdatafunc)(int i, void *usrData),
		   void *appDataUsrData,
		   float (*appdata2func)(int i, void *usrData),
		   void *appData2UsrData,
		   RMvisMap *vmap,
		   int axis_offset_enum,
		   int npts,
		   float zerocrossing,
		   RMenum linewidth_enum,
		   RMenum linestyle_enum,
		   RMnode *n)
 @pend

 @astart
 RMvertex2D (*appgridfunc)(int i, void *appGridUsrData) - a handle to a
    caller-supplied function that returns an RMvertex2D (x, y)
    corresponding to the grid point (i) (input).
	          
 void *appGridUsrData - (input, optional) you can pass in a void * to
    user data; this void * will be provided as a parameter to the user-defined
    grid evalution callback. If you don't want to pass in a void * to any
    user data for the grid evaluation callback, pass in a NULL for this
    parameter.

 float (*appdatafunc)(int i, void *usrData) - a handle to a
    caller-supplied function that returns a float, which is the scalar
    value at the grid point (i) (input).

 void *appDataUsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    data field evaluation callback. If you don't want to pass in a void *
    to any user data for the primary data field grid evaluation callback,
    pass in a NULL for this parameter.
			   
 float (*appdata2func)(int i, void *usrData) -- (input, optional)
    A handle to a caller-supplied function that returns a float, which is the
    scalar value at the grid point (i) of the optional secondary data field.
    RMV uses the float returned by appdata2func in conjunction with the
    RMvismap to compute vertex color.

 void *appData2UsrData - (optional, input) you can pass in a void * to user
    data; this void * will be provided as a parameter to the user-defined
    secondary data field evaluation callback. If you don't want to pass in
    a void * to any user data for the secondary data field evaluation
    callback, pass in a NULL for this parameter.
    
 RMvisMap *vmap - a handle to an optional RMvisMap object (input).
		  
 int axis_offset_enum - an integer specifying in which axis to offset
    the stairstep.  Must be one of RMV_XAXIS_OFFSET,
    RMV_YAXIS_OFFSET (input).

 int npts - an integer specifying the number of data points (input).
		       
 float zerocrossing - a float specifying the zero line within the data
    range.  The "area fill" will be computed relative to this zero
    line (input).
		   
 RMenum linewidth_enum - an RMenum specifying the line width.  Must be
    one of RM_LINEWIDTH_NARROW, RM_LINEWIDTH_MEDIUM,
    RM_LINEWIDTH_HEAVY, or RM_LINEWIDTH_[1..8] (input).

 RMenum linestyle_enum - an RMenum specifying the line style.  Must
    be one of RM_LINES_SOLID, RM_LINES_DASHED, RM_LINES_DOTTED,
    RM_LINES_DOT_DASH, or RM_LINES_DASH_DASH_DOT (input).

 RMnode *n - a handle to an RMnode (modified).
 @aend

 @dstart

 Creates a stairstep plot from data.  The plot is a single color, the
 RMnode default color at render time.  The stairsteps can also be
 colored via the secondary data function and the RMvismap.  A line
 segment connects the zerocrossing value with the first and last data
 point.  

 Upon success, RM_CHILL is returned and the stairstep primitives are
 added to the RMnode.  Otherwise, RM_WHACKED is returned.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmvI2Step (RMvertex2D (*appgridfunc)(int i, void *usrData),
	   void *appGridUsrData,
	   float (*appdatafunc)(int i, void *usrData),
	   void *appDataUsrData,
	   float (*appdata2func)(int i, void *usrData),
	   void *appData2UsrData,
	   RMvisMap *vmap,
	   int axis_offset_enum,
	   int npts,
	   float zerocrossing,
	   RMenum linewidth_enum,
	   RMenum linestyle_enum,
	   RMnode *n)
{
    int          i, j;
    float        xoff = 0.0, yoff = 0.0;
    float        dx, dy;
    RMprimitive *prim;
    RMvertex2D  *v;
    RMvertex2D   t;
    RMcolor4D   *c;

    /*
     * todo: this routine doesn't make very efficient use of the app
     * callbacks. a lot of info could be reused.
     */

    /* error check on functions, etc. */
    {
	int s1, s2, s3, s4;

	s1 = RM_ASSERT((void *)n, "rmvI2Step error: NULL RMnode for return parameter");
	s2 = RM_ASSERT((void *)appgridfunc, "rmvI2Step error: NULL app grid callback");
	s3 = RM_ASSERT((void *)appdatafunc, "rmvI2Step error: NULL app data callback ");
	s4 = RM_CHILL;
	if (!(((vmap != NULL) && (appdata2func != NULL)) || ((vmap == NULL) && (appdata2func == NULL))))
	    s4 = RM_ASSERT((void *)NULL, "rmvI2Step error: the vismap and secondary data callback function must BOTH be NULL or defined.");

	if ((s1 ==  RM_WHACKED) || (s2 == RM_WHACKED) || (s3 == RM_WHACKED) || (s4 == RM_WHACKED))
	    return(RM_WHACKED);
    }
    
    xoff = (axis_offset_enum == RMV_XAXIS_OFFSET) ? zerocrossing : 0.0;
    yoff = (axis_offset_enum == RMV_YAXIS_OFFSET) ? zerocrossing : 0.0;

    v = rmVertex2DNew((npts * 2) + 2);
    
    prim = rmPrimitiveNew(RM_LINE_STRIP);

    if (vmap != NULL)
	c = rmColor4DNew((npts * 2) + 2);
    else
	c = NULL;

    /* do the 1st anchor point */
    i = 0;
    t = (*appgridfunc)(i, appGridUsrData);
    private_rmv2DGetBarScale(appgridfunc, appGridUsrData,  &t, 0, 1.0F, RMV_SCALE_RELATIVE, axis_offset_enum, &dx, &dy);

    if (axis_offset_enum == RMV_YAXIS_OFFSET)
        dx *= 1.0;
    else
        dx = -xoff;

    if (axis_offset_enum == RMV_XAXIS_OFFSET)
        dy *= 1.0;
    else
        dy = -yoff;

    j = 0;
    
    v[j].x = t.x - dx;
    v[j].y = t.y - dy;

    if (c)
    {
        int   k;
	float d2;
	
	d2 = (*appdata2func)(0, appData2UsrData);
	k = rmVismapIndexFromData(vmap, d2);
	rmVismapGetColor4D(vmap, k, (c));
    }
    
    j++;
    for (i = 0; i < npts; i++)
    {
	float data;
	
	v[j] = (*appgridfunc)(i, appGridUsrData);
	data = (*appdatafunc)(i, appDataUsrData);
	private_rmv2DGetBarScale(appgridfunc, appGridUsrData,  (v + j), i, 1.0F, RMV_SCALE_RELATIVE, axis_offset_enum, &dx, &dy);

	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    dx *= 1.0;
	else
	    dx = 0.0;

	if (axis_offset_enum == RMV_XAXIS_OFFSET)
	    dy *= 1.0;
	else
	    dy = 0.0;

	if (axis_offset_enum == RMV_XAXIS_OFFSET)
	    v[j].x += data;
	else
	    v[j].x -= dx;

	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    v[j].y += data;
	else
	    v[j].y -= dy;
	
	if (c)
	{
	    int   k;
	    float d2;

	    d2 =(*appdata2func)(i, appData2UsrData);
	    k = rmVismapIndexFromData(vmap, d2);
	    rmVismapGetColor4D(vmap, k, (c + j));
	}

	j++;
	v[j] = (*appgridfunc)(i, appGridUsrData);

	if (axis_offset_enum == RMV_XAXIS_OFFSET)
	    v[j].x += data;
	else
	    v[j].x += dx;

	if (axis_offset_enum == RMV_YAXIS_OFFSET)
	    v[j].y += data;
	else
	    v[j].y += dy;

	if (c)
	{
	    int   k;
	    float d2;

	    d2 = (*appdatafunc)(i, appDataUsrData);
	    k = rmVismapIndexFromData(vmap, d2);
	    rmVismapGetColor4D(vmap, k, (c + j));
	}
	j++;
    }

    /* do the final anchor point */
    v[j] = (*appgridfunc)(i - 1, appGridUsrData);
    v[j].x += dx;
    v[j].x += (axis_offset_enum == RMV_XAXIS_OFFSET) ? xoff : 0.0;
    v[j].y += dy;
    v[j].y += (axis_offset_enum == RMV_XAXIS_OFFSET) ? yoff : 0.0;

    if (c)
    {
        int   k;
	float d2;

	d2 = (*appdata2func)(i - 1, appData2UsrData);
	k = rmVismapIndexFromData(vmap, d2);
	rmVismapGetColor4D(vmap, k, (c + j));
    }
    
    /* set the linestyle and weights */
    rmNodeSetLineStyle(n, linestyle_enum);
    rmNodeSetLineWidth(n, linewidth_enum);
    
    rmPrimitiveSetVertex2D(prim, (npts * 2) + 2, v, RM_COPY_DATA, NULL);

    if (c)
    {
        rmPrimitiveSetColor4D(prim, (npts * 2) + 2,c, RM_COPY_DATA, NULL);
	rmColor4DDelete(c);
    }
	
    /* now, add the new primitive onto the node */
    rmNodeAddPrimitive(n,prim);
    rmVertex2DDelete(v);

    return(RM_CHILL);
}


/* PRIVATE */
void
private_rmv2DGetBarScale (RMvertex2D (*appgridfunc)(int i, void *usrData),
			  void *appGridUsrData,
			  RMvertex2D *base_point,
			  int i,
			  float scale,
			  int scale_mode,
			  int axis_offset_enum,
			  float *dx,
			  float *dy)
{
   switch (scale_mode)
      {
      case RMV_SCALE_ABSOLUTE:
	 {
	    if (axis_offset_enum == RMV_YAXIS_OFFSET)
	       {
		  *dx = scale * 0.5F;
		  *dy = 0.0F;
	       }
	    else if (axis_offset_enum == RMV_XAXIS_OFFSET)
	       {
		  *dx = 0.0F;
		  *dy = scale * 0.5F;
	       }
	    else
	       {
		  *dx = *dy = scale;
	       }
	    break;
	 }

      case RMV_SCALE_RELATIVE:
	 {
	    float      deltax, deltay;
	    RMvertex2D next_point;
	    
	    if (i == 0)
	       {
		  next_point = (*appgridfunc)(i + 1, appGridUsrData);
		  deltax = next_point.x - base_point->x;
		  deltay = next_point.y - base_point->y;
	       }
	    else
	       {
		  next_point = (*appgridfunc)(i - 1, appGridUsrData);
		  deltax = base_point->x - next_point.x;
		  deltay = base_point->y - next_point.y;
	       }

	    switch (axis_offset_enum)
	       {
	       case RMV_XAXIS_OFFSET:
		  *dx = 0.0F;
		  *dy = deltay * scale * 0.5F;
		  break;

	       case RMV_YAXIS_OFFSET:
		  *dx = deltax * scale * 0.5F;
		  *dy = 0.0F;
		  break;

	       case RMV_NOAXIS_OFFSET:
		  *dx = *dy = scale;
		  break;

	       default: /* bogus axis offset enum */
		  break;
	       }
	    break;
	 }

      default: /* bogus scaling mode */
	 break;
      }
}
/* EOF */
