 /*
  * Khoros: $Id$
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id$";
#endif

 /*
  * $Log$
  */ 

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1991, University of New Mexico.  All rights reserved.
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as to the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including, for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *---------------------------------------------------------------------
 */

#include "unmcopyright.h"        /* Copyright 1991 by UNM */

/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 >>>>
 >>>>  		        Scatter Plot Routines
 >>>>
 >>>>			create_plot_display()
 >>>>			draw_plot()
 >>>>			draw_plot_axes()
 >>>>			change_plot_col()
 >>>>			change_plot_function()
 >>>>			perform_plot_function()
 >>>>			add_cluster_from_plot()
 >>>>			update_plot_position()
 >>>>			update_current_class()
 >>>>			highlight_cursor_position()
 >>>>			draw_single_marker()
 >>>>			update_plot_focus()
 >>>>
 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/

#include "spectrum.h"
#include "xvgraphics.h"


#define PlotWidth  400
#define PlotHeight 400

/****************************************************************
*
* Routine Name: create_plot_display
*      Purpose: creates the scatterplot display 
*		& sets xvgraphics parameters
*	 Input: parent - backplane widget
*       Output: none
*   Written By: Danielle Argiro
*
****************************************************************/
create_plot_display(parent)
Widget parent;
{
	int i, j;
	Arg arg[MaxArgs];
	unsigned long pixel;
	Arg args[MaxArgs];
	float xmin, xmax, ymin, ymax, min, max;
	void add_cluster_from_plot();
	void update_plot_position();
	static int count;

	if (count++ > 0) return;

       /* 
        * allocate and initialize the columns of plot data 
        */
        if (spc_map != NULL)
	    init_plot_info();
	X3D_init_graphics (SpcId, X2D);

	i = 0;
	XtSetArg(arg[i],XtNborderWidth,1);	i++;
	XtSetArg(arg[i],XtNwidth,PlotWidth);	i++;
	XtSetArg(arg[i],XtNheight,PlotHeight);	i++;
        XtSetArg(arg[i],XtNdefaultDistance,4);  i++;
        XtSetArg(arg[i],XtNresizable, True);    i++;
	plot_workspace = XtCreateManagedWidget("spc_plot_workspace",
						formWidgetClass, parent,arg,i);

        /* Make the plot background black */
        i = 0;
        pixel = BlackPixel(display, DefaultScreen(display));
        XtSetArg(args[i],XtNbackground, pixel);                  i++;
        XtSetValues(plot_workspace, args, i);

	/* initialize graphics parameters */
	X2D_set_viewport(SpcId, 0.1, 0.9, 0.1, 0.9);
	X3D_set_X11(SpcId, display, NULL, plot_workspace);
	
	/* initially, display column 0 as X, and column 1 as Y */
	if (spc_map != NULL)
	{
	   if (spc_prop_plotting)
	   {
	  	find_min_max_col_data(spc_x_col, spc_unique_values, 
				      &xmin, &xmax);
                find_min_max_col_data(spc_y_col, spc_unique_values, 
				      &ymin, &ymax);
                min = MIN(xmin, ymin);
                max = MAX(xmax, ymax);
                spc_wcmin.x = min; spc_wcmin.y = min;
                spc_wcmax.x = max; spc_wcmax.y = max;
	   }
	   else
	   {
	        find_min_max_col_data(spc_x_col, spc_unique_values, 
				      &xmin, &xmax); 
	        spc_wcmin.x = xmin; spc_wcmax.x = xmax; 
	        find_min_max_col_data(spc_y_col, spc_unique_values, 
		                      &ymin, &ymax); 
	        spc_wcmin.y = ymin; spc_wcmax.y = ymax; 
	   }
	   plot_x_focus = (xmax - xmin)/2.0;
	   plot_y_focus = (ymax - ymin)/2.0;
	}


	X2D_set_wc_min_max(SpcId, spc_wcmin, spc_wcmax);
	X2D_set_clipping(SpcId, true); 

	XtInsertEventHandler(plot_workspace, ExposureMask,
                             FALSE, draw_plot, NULL, XtListHead);

	XtInsertEventHandler(plot_workspace, ButtonMotionMask, 
			     FALSE, add_cluster_from_plot, NULL, 
			     XtListHead);

	XtInsertEventHandler(plot_workspace, PointerMotionMask, 
			     FALSE, update_plot_position, NULL, 
			     XtListTail); 

	XtInsertEventHandler(plot_workspace, ButtonPressMask,
			     FALSE, update_plot_focus, NULL, XtListHead);

	XtInsertEventHandler(plot_workspace, 
			     PointerMotionMask | ExposureMask |
			     PointerMotionHintMask, FALSE, 
			     update_printclass, NULL, XtListHead);


	if (xvdisplay != NULL)
	    xvd_set_colormap(plot_workspace, xvdisplay->colormap);

	/* add position widget */
	i = 0;
        XtSetArg(args[i], XtNlabel, 
	VStrcpy("                    x                      "));i++;
        XtSetArg(args[i], XtNfromVert, plot_workspace);               i++;
        XtSetArg(args[i], XtNfromHoriz, NULL);                        i++;
	plot_pos_widget = XtCreateManagedWidget("position",
                                 labelWidgetClass, parent, args, i);
}

/****************************************************************
*
* Routine Name: draw_plot
*      Purpose: draws the scatter plot on exposure of plot window
*	 Input: x_col - column of info for X
*	        y_col - column of info for Y
*       Output: none
*   Written By: Danielle Argiro
*
****************************************************************/

void draw_plot()
{
	int 	point_index, i, colornum;
	int     x, y;
        Window  rootwin;
        unsigned int width, height, border_width, depth;
	Coord  *points, coord, zoom_wcmin, zoom_wcmax;
	double xout, yout;
	XColor *colorarray = NULL;

	if ((plot_workspace == NULL) || (spc_map == NULL)
	    || (spc_x_dcs == NULL) || (spc_y_dcs == NULL)) return;

	if (!(xvd_check_visibility(plot_workspace))) return;

	X2D_clear_window(SpcId);


	points     = (Coord *) malloc(spc_unique_values * sizeof(Coord));
	colorarray = (XColor *) malloc((spc_unique_values+1) * sizeof(XColor));

	XGetGeometry(display, XtWindow(plot_workspace), &rootwin, &x, &y,
                     &width, &height,  &border_width, &depth);
	X2D_set_window(SpcId, 0, 0, (short)width, (short)height);


	if (spc_actual_plot_zoomfactor == 1)
	{
	   draw_plot_axes(&spc_wcmin, &spc_wcmax);
           X2D_set_wc_min_max(SpcId, spc_wcmin, spc_wcmax);
	}
	else if (spc_prop_plotting) 
	{
	    draw_plot_axes(&zoom_wcmin, &zoom_wcmax);
	    compute_zoom_area(spc_actual_plot_zoomfactor, &zoom_wcmin,
                              &zoom_wcmax);
            X2D_set_wc_min_max(SpcId, zoom_wcmin, zoom_wcmax);
	}
	else
	{
	    draw_plot_axes(&zoom_wcmin, &zoom_wcmax);
            compute_zoom_area(spc_actual_plot_zoomfactor, &zoom_wcmin,
                                  &zoom_wcmax);
            X2D_set_wc_min_max(SpcId, zoom_wcmin, zoom_wcmax);
	}

        for (point_index = 0; point_index < spc_unique_values; point_index++)
        {
            coord.x = (double) spc_x_col[point_index];
            coord.y = (double) spc_y_col[point_index];
            X2D_convert_point_wc_to_dc (SpcId, coord, &xout, &yout); 
            spc_x_dcs[point_index] = (short) xout;
            spc_y_dcs[point_index] = (short) yout;
        }


	/* first color in colorarray is green for unassigned clusters */
	colornum = 0; 
	colorarray[colornum++] = PlotColors[0];

	/*
	 *  all unassigned
	 */
	if (spc_legend_lookup == NULL)
	{
	     for (i = 0; i < spc_unique_values; i++)
	     {
	     	points[i].x = spc_x_col[i];
	     	points[i].y = spc_y_col[i];
		points[i].d = 0;
	     }
	}
	else
	{
	    for (i = 0; i < spc_unique_values; i++)
	    {
	        points[i].x = spc_x_col[i];
	        points[i].y = spc_y_col[i];

		/* 
		 * unassigned - set color to green 
		 */
	    	if (spc_legend_lookup[spc_plot_rev_indices[i]] == NULL)
	    	{
		     points[i].d = 0;
	        }

		/* 
		 * current class or assigned class - 
		 * set color according to legend color for that cluster
		 */
	        else 
	        {
		    points[i].d = colornum;
		    colorarray[colornum++] = 
			xvdisplay->xcolors[spc_plot_rev_indices[i]];
	        }
	    }
	}

	/* draw plots indicating assigned, unassigned, current classes */
	X2D_draw_colormarker(SpcId, points, spc_unique_values, 
			     MarkerDiamond, colorarray, colornum);


        if ((spc_draw_plot_zoombox)&&(spc_potential_plot_zoomfactor > 1))
	{
	    compute_zoom_area(spc_potential_plot_zoomfactor, 
			      &zoom_wcmin, &zoom_wcmax);
	    coord.x = zoom_wcmin.x;  coord.y = zoom_wcmax.y;
	    X2D_draw_line(SpcId, zoom_wcmin, coord, &PlotColors[0]);
	    X2D_draw_line(SpcId, coord, zoom_wcmax, &PlotColors[0]);
	    coord.x = zoom_wcmax.x;  coord.y = zoom_wcmin.y;
	    X2D_draw_line(SpcId, zoom_wcmax, coord, &PlotColors[0]);
	    X2D_draw_line(SpcId, coord, zoom_wcmin, &PlotColors[0]);
	}

	/* cleanup */
	free(points);
	free(colorarray);
}


/****************************************************************
*
* Routine Name: draw_plot_axes
*      Purpose: draws simple axes on the scatter plot 
*	 Input: none
*       Output: none
*   Written By: Danielle Argiro
*
****************************************************************/
draw_plot_axes(wcmin, wcmax)
Coord *wcmin, *wcmax;
{
	float  width, height;
	Vector direction;
	char   temp[MaxLength];
	Coord  axesmin, axesmax, point, point1, point2;

	axesmin.x = 0; axesmax.x = 1;
	axesmin.y = 0; axesmax.y = 1;
	X2D_set_wc_min_max(SpcId, axesmin, axesmax);

	/* horizontal X axis */
	X2D_set_viewport(SpcId,  0.0, 1.0, 0.0, 0.1);
	point1.x = 0.08;  point1.y = 1.0;
	point2.x = 0.95;  point2.y = 1.0;
	X2D_draw_line(SpcId, point1, point2, &PlotColors[0]);

	/* tic at xmax */
	point1.x = 0.9;  point1.y = 1.0;
	point2.x = 0.9;  point2.y = 0.8;
	X2D_draw_line(SpcId, point1, point2, &PlotColors[0]);

        direction.x = 1.0; direction.y = 0.0; direction.z = 0.0;
	width  = 0.025;
	height = 0.3;

	/* label x min */
	point.x = 0.15;
        point.y = 0.4;
	sprintf(temp, "%g", wcmin->x);
	X2D_draw_text(SpcId, point, direction, width,
                      height, temp, VStrlen(temp), &PlotColors[0]);

	/* label x max */
	point.x = 0.9;
        point.y = 0.4;
	sprintf(temp, "%g", wcmax->x);
	X2D_draw_text(SpcId, point, direction, width,
                      height, temp, VStrlen(temp), &PlotColors[0]);



	direction.x = 0.0; direction.y = 1.0; direction.z = 0.0;
	width  = 0.3;
        height = 0.025;


	/* vertical Y axis */
	X2D_set_viewport(SpcId,  0.0, 0.1, 0.0, 1.0);
	point1.x = 1.0; point1.y = 0.08;
	point2.x = 1.0; point2.y = 0.95;
	X2D_draw_line(SpcId, point1, point2, &PlotColors[0]);

	/* tic at ymax */
	point1.x = 1.0;  point1.y = 0.9;
	point2.x = 0.8;  point2.y = 0.9;
	X2D_draw_line(SpcId, point1, point2, &PlotColors[0]);

	/* label y min */
        point.x = 0.4;
        point.y = 0.15;
        sprintf(temp, "%g", wcmin->y);
        X2D_draw_text(SpcId, point, direction, width,
                      height, temp, VStrlen(temp), &PlotColors[0]);

	/* label y max */
	point.x = 0.4;
        point.y = 0.9;
	sprintf(temp, "%g", wcmax->y);
	X2D_draw_text(SpcId, point, direction, width,
                      height, temp, VStrlen(temp), &PlotColors[0]);

	X2D_set_viewport(SpcId, 0.1, 0.9, 0.1, 0.9);
}


/********************************************************
*
*  Routine Name:  change_plot_col
*
*       Purpose:  When the user clicks on the 'Column Displayed as X' 
*		  or 'Column Displayed as Y' buttons, changes the column
*		  associated with the X or Y coordinates of the plot
*         Input:  form - pointer to the form tree 
*		  blank_index - index of Blank (-b) line in UIS
*				following Pane Action (-a) line
*		  flag - one of SpcX, SpcY
*        Output:  changes column associated w/ X or Y
*     Called By:  run_scatter()
*    Written By:  Danielle Argiro
*
********************************************************/

change_plot_col(form, blank_index, func_index, flag)
xvf_form *form;
int blank_index;
int flag;
{
	int i, j;
	char **choices, temp[MaxLength];
        XawListReturnStruct *list_return;
	float min, max;

	char *promptX = "Select map column to use as X data",
	     *promptY = "Select map column to use as Y data",
	     *label   = "  ", *prompt;

	/* appropriate prompt depending on color band */
	if (flag == SpcX) prompt = promptX;
        else if (flag == SpcY) prompt = promptY;

	/* create the list of choices of Map Columns */
	choices = create_mapcol_list();

	/* put up the list widget to get new column */
	list_return = xvf_run_list_wait(choices, spc_map_colnum, 2, 
		      prompt, label, 0, false);
	if (list_return == NULL) return;

	if (flag == SpcX) 
	{
	    j = 0;
	    for (i = xvdisplay->pixelmin; i <= xvdisplay->pixelmax; i++)
	    {
		if (xvdisplay->histogram[i] > 0)
		    spc_x_col[j++] = spc_map[list_return->list_index][i];
	    }
	    spc_unique_values = j;

	    if (spc_prop_plotting)
	    {
	        find_min_max_col_data(spc_x_col, spc_unique_values, &min, &max);
                min = MIN(min, spc_wcmin.y);
                max = MAX(max, spc_wcmax.y);
                spc_wcmin.x = min; spc_wcmax.x = max;
                spc_wcmin.y = min; spc_wcmax.y = max;
	    }
	    else
	    {
	        find_min_max_col_data(spc_x_col, spc_unique_values, &min, &max);
	        spc_wcmin.x = min; spc_wcmax.x = max;
	        spc_wcmin.y = min; spc_wcmax.y = max;
	    }
	    X2D_set_wc_min_max(SpcId, spc_wcmin, spc_wcmax);
	    plot_x_focus = (max - min)/2.0;
	    spc_actual_plot_zoomfactor = 1.0;

	}
	else if (flag == SpcY) 
	{
	    j = 0;
	    for (i = xvdisplay->pixelmin; i <= xvdisplay->pixelmax; i++)
	    {
		if (xvdisplay->histogram[i] > 0)
		    spc_y_col[j++] = spc_map[list_return->list_index][i];
	    }
	    spc_unique_values = j;

            if (spc_prop_plotting)
            {
                 find_min_max_col_data(spc_y_col,spc_unique_values, &min, &max);
                 min = MIN(spc_wcmin.x, min);
                 max = MAX(spc_wcmax.x, max);
                 spc_wcmin.y = min; spc_wcmax.y = max;
	    }
	    else
	    {
	        find_min_max_col_data(spc_y_col, spc_unique_values, &min, &max);
	        spc_wcmin.y = min; spc_wcmax.y = max;
	        X2D_set_wc_min_max(SpcId, spc_wcmin, spc_wcmax);
	    }
	    X2D_set_wc_min_max(SpcId, spc_wcmin, spc_wcmax);
	    plot_y_focus = (max - min)/2.0;
	    spc_actual_plot_zoomfactor = 1.0;
	}

	/* display the new plot */
	draw_plot();

	/* update info displayed on form with Blank selection */
	sprintf(temp, "M%d", list_return->list_index);
	xvf_change_input(form, blank_index, xvf_title_chng, temp, 10);
	sprintf(temp, "M%d", list_return->list_index);
        xvf_change_input(form, func_index, xvf_stringval_chng, temp, 10);


	free(choices);
	free(list_return);
}

/********************************************************
*
*  Routine Name:  change_plot_function
*
*       Purpose:  When the user enters a function for X &
*		  hits return, changes the X coordinates
*		  according to the function specified for X.
*         Input:  func_string - desired function provided by user
*		  flag - one of SpcX, SpcY
*        Output:  changes map associated w/ X or Y
*     Called By:  run_scatter()
*    Written By:  Danielle Argiro
*
********************************************************/

change_plot_function(func_string, flag)
char *func_string;
int flag;
{
	int  var_num;
	float float_val, min, max;
	char temp[MaxLength], *init_string;
	char **var_list, *function;

	/* parse variables MX out of function string */ 
	var_list =  variable_scan(func_string, &var_num);
	if (var_list == NULL) return;

        if (flag == SpcX) init_string = xvf_strcpy("X(");
        else if (flag == SpcY) init_string = xvf_strcpy("Y(");

	/* create syntactically correct function definition 
	   from function defintion pass to vexpr parser */
	function = create_function_def(func_string, init_string,
			 	       var_list, var_num);

	/* send function definition to vexpr parser */
	if (!(xve_eval_float(SpcId, function, &float_val, temp)))
        {
		xvf_error_wait(temp, "change_plot_function", NULL);
		return;
	}

	if (flag == SpcX) 
	{
	     spc_x_col = perform_plot_function(flag, var_list, var_num);
	     if (spc_prop_plotting)
	     {
                find_min_max_col_data(spc_x_col, spc_unique_values, &min, &max);
                min = MIN(min, spc_wcmin.y);
                max = MAX(max, spc_wcmax.y);
                spc_wcmin.x = min; spc_wcmax.x = max;
	     }
	     else
	     {
	        find_min_max_col_data(spc_x_col, spc_unique_values, &min, &max);
	        spc_wcmin.x = min; spc_wcmax.x = max;
	     }
	     X2D_set_wc_min_max(SpcId, spc_wcmin, spc_wcmax);
	     plot_x_focus = (max - min)/2.0;
	     spc_actual_plot_zoomfactor = 1.0;
	}
	else if (flag == SpcY)
	{
	     spc_y_col = perform_plot_function(flag, var_list, var_num);
             if (spc_prop_plotting)
             {
                find_min_max_col_data(spc_y_col, spc_unique_values, &min, &max);
                min = MIN(spc_wcmin.x, min);
                max = MAX(spc_wcmax.x, max);
                spc_wcmin.y = min; spc_wcmax.y = max;
	     }
	     else
	     {
	        find_min_max_col_data(spc_y_col, spc_unique_values, &min, &max);
	        spc_wcmin.y = min; spc_wcmax.y = max;
	     }
	     X2D_set_wc_min_max(SpcId, spc_wcmin, spc_wcmax);
	     plot_y_focus = (max - min)/2.0;
	     spc_actual_plot_zoomfactor = 1.0;
	}

	free(var_list);
	free(function);
}


/****************************************************************
*
* Routine Name: perform_plot_function()
*      Purpose: when the user provides a function of map columns
*		to be used as the X or Y plot coordinates, performs
*		the operation and updates the current x/y columns.
*
*        Input: flag - one of SpcX, SpcY
*		var_list - list of variables involved in function: M0, M1, etc
*		var_num  - size of var list
*       Output: resulting new column of info
*   Written By: Danielle Argiro
*
****************************************************************/

float *perform_plot_function(flag, var_list, var_num) 
int   flag;
char **var_list;
int   var_num;
{
     int i, j;
     int *cols; 
     float *new_column;
     float val;
     char temp[MaxLength];
     char *func, *init_string;

     cols = (int *) malloc(var_num * sizeof(int));
     new_column = (float *) malloc (spc_unique_values * sizeof(float));

     for(i = 0; i < var_num; i++)
     {
        sprintf(temp, var_list[i]);
        cols[i] = atoi(&temp[1]);
     }
     
     if (flag == SpcX) init_string = xvf_strcpy("X(");
     else if (flag == SpcY) init_string = xvf_strcpy("Y(");

     j = 0;
     for (i = 0; i < spc_map_rownum; i++)
     {
	  func = create_function_call(i, cols, var_num, init_string);
          xve_eval_float(SpcId,  func, &val, temp);
	  if (xvdisplay->histogram[i] > 0) new_column[j++] = val;
     }
     free(cols);
     return(new_column);
}

/****************************************************************
*
* Routine Name: add_cluster_from_plot
*      Purpose: adds a cluster from the plot, when mouse is depressed
*		and moved over a point in the plot.
*	 Input: widget     - the plot workspace 
*               clientData - the current entry
*               callData   - not used
*		dispatch   - whether to allow event to propogate
*       Output: none
*   Written By: Danielle Argiro
*
****************************************************************/

void add_cluster_from_plot(widget, clientData, event, dispatch)
Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
	int point_index, cluster;
	legend_list *legend_ptr;
	Coord coord[1];

	if (current_entry == NULL) return;
	if (event->xbutton.state == 0) return;

	if (!(identify_plot_point(event->xbutton.x, event->xbutton.y,
				  &point_index, &cluster)))
        {
	    *dispatch = false;
	    return;
	}

	cluster = spc_plot_rev_indices[point_index];

	if (spc_plot_operation == SpcAdd)
	{
	    /*
	     * only want to add classes not currently assigned
	     */
	    if (spc_legend_lookup[cluster] == NULL)
	    {
  	    	fprintf(stderr, "adding cluster %d to current class\n\n", 
			cluster);
	    	legend_ptr = current_entry->legend_ptr;
		add_cluster_to_class(legend_ptr, cluster);
	        coord[0].x = spc_x_col[point_index];
	        coord[0].y = spc_y_col[point_index];
	        X2D_draw_polymarker(SpcId, coord, 1, MarkerDiamond, 
				    &(xvdisplay->xcolors[cluster]));
                update_xcolors_from_legend();
                update_colorbox_from_legend(legend_ptr);
	    }
	}
	else if (spc_plot_operation == SpcDelete)
	{
	    if (spc_legend_lookup[cluster] != NULL)
            {
                fprintf(stderr,"deleting cluster %d from current class\n\n", 
			cluster);
                legend_ptr = current_entry->legend_ptr;
		restore_old_values(legend_ptr, cluster);
                if (!(delete_cluster_from_class(legend_ptr, cluster))) return;
	        coord[0].x = spc_x_col[point_index];
	        coord[0].y = spc_y_col[point_index];
	        X2D_draw_polymarker(SpcId, coord, 1, MarkerDiamond, 
				    &PlotColors[0]);
                update_xcolors_from_legend();
                update_colorbox_from_legend(legend_ptr);
            }
	}
}


/****************************************************************
*
* Routine Name: update_plot_position
*      Purpose: updates the mouse position as shown in the plot.
*	 Input: widget     - the plot workspace 
*               clientData - the current entry
*               callData   - not used
*		dispatch   - whether to allow event to propogate
*       Output: none
*   Written By: Danielle Argiro
*
****************************************************************/

void update_plot_position(widget, clientData, event, dispatch)
Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
	int   i, point_index, cluster;
	char  temp[MaxLength];
	Arg   arg[MaxArgs];

	if (!(identify_plot_point(event->xbutton.x,event->xbutton.y,
				  &point_index, &cluster))) 
	    return;

	/* update position widget */
	sprintf(temp, " %f  x  %f           ", 
		spc_x_col[point_index], spc_y_col[point_index]);
	i = 0;
        XtSetArg(arg[i], XtNlabel, temp);     i++;
	XtSetValues(plot_pos_widget, arg, i);

}

/****************************************************************
*
* Routine Name: update_current_class
*      Purpose: When user clicks on the 'select' button on the 
*		plot subform, puts up a list widget from which they
*		may select the current class.
*	 Input: none
*       Output: none
*   Written By: Danielle Argiro
*
****************************************************************/
update_current_class()
{
	int  dummy, listsize;
	char **classnames;
        XawListReturnStruct *class_list_return;
        legend_list *legend_ptr;
        char *prompt = "Select Current Class", 
	     *label  = "Available Classes";

        /*
         * put up list of available classes
 	 */
	classnames = create_category_list(NULL, SpcAll, &listsize, NULL, 0);
	class_list_return =  xvf_run_list_wait(classnames, listsize, 1, 
					       prompt, label, &dummy, False);
	if (class_list_return == NULL) return;
	
        /*
         * find legend ptr assoc. w/ class chosen
    	 */
	legend_ptr = find_legend_from_text(class_list_return->string);
	current_entry = legend_ptr->entry_ptr;
}


/****************************************************************
*
* Routine Name: highlight_cursor_position
*      Purpose: updates the point in the plot according to location
*		of cursor in the image.
*	 Input: widget     - the image workspace 
*               clientData - the current entry
*               callData   - not used
*		dispatch   - whether to allow event to propogate
*       Output: none
*   Written By: Danielle Argiro
*
****************************************************************/

void highlight_cursor_position(widget, clientData, event, dispatch)
Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
	static XColor *lastcolor = &PlotColors[0];
	static int lastindex = -1;
	int index;

	if ((plot_workspace == NULL) || (spc_map == NULL)
	    || (spc_x_dcs == NULL) || (spc_y_dcs == NULL)) return;

	if (!(xvd_check_visibility(plot_workspace))) return;

	index = get_cluster(widget, -1, -1);
	if (index == -1) return;

	if (lastindex != -1)
            draw_single_marker(lastindex, lastcolor);
	
	if (spc_legend_lookup == NULL)
	    lastcolor = &PlotColors[0];
	else
	{
  	   if (spc_legend_lookup[index] == NULL)
	      lastcolor = &PlotColors[0];
	   else 
	      lastcolor = &(xvdisplay->xcolors[index]);
	}

	draw_single_marker(index, &PlotColors[4]);

	lastindex = index;
}

/****************************************************************
*
* Routine Name: draw_single_marker
*      Purpose: draws a single polymarker on the plot widget
*	 Input: 
*       Output: none
*   Written By: Danielle Argiro
*
****************************************************************/
draw_single_marker(index, color)
int index;
XColor *color;
{
	Coord coord[1];
	int  point_index;

	point_index = spc_plot_indices[index];
	if (point_index == -1) return;

	coord[0].x = spc_x_col[point_index];
	coord[0].y = spc_y_col[point_index];
	X2D_draw_polymarker(SpcId, coord, 1, MarkerDiamond, color);
}

/************************************************************
*
*  MODULE NAME: update_plot_focus
*
*      PURPOSE: event handler which updates the zoom focus
*		on the plot workspace
*
*	 INPUT: widget - the widget that had the event
*		clientdata - used for the xvdisplay struct
*		event - the event that evoked this event handler
*
*       OUTPUT: none
*
*   WRITTEN BY: Mark Young
*
*************************************************************/

void update_plot_focus(widget, clientData, event, dispatch)

Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
	int point_index, cluster;

	spc_potential_plot_zoomfactor = 
		master_info.scatter_plot->scatter->zoomfactor;

	if (!(identify_plot_point(event->xbutton.x, event->xbutton.y,
				  &point_index, &cluster)))
            return;

	plot_x_focus = spc_x_col[point_index];
	plot_y_focus = spc_y_col[point_index];
	spc_draw_plot_zoombox = true;
	draw_plot();
}


/****************************************************************
*
* Routine Name: compute_zoom_area
*      Purpose: computes the zoom area given a zoom factor:
*		keeps plot focus point in perspective
*	 Input: zoomfactor - the zoom factor to zoom by
*       Output: zoom_wcmin - lower left hand corner of zoom box
*		zoom_wcmax - upper right hand corner of zoom box
*   Written By: Danielle Argiro
*
****************************************************************/
compute_zoom_area(zoomfactor, zoom_wcmin, zoom_wcmax)
float zoomfactor;
Coord *zoom_wcmin, *zoom_wcmax;
{
	float window;   /* total size of zoom box in wc's */
	float move_left;  /* percentage amount to move left from plot x focus */
	float move_down;  /* percentage amount to move down from plot y focus */

	if (spc_prop_plotting)
	{
	    window = (spc_wcmax.x - spc_wcmin.x)/zoomfactor;
	    move_left = plot_x_focus/(spc_wcmax.x - spc_wcmin.x);
	    zoom_wcmin->x = plot_x_focus - (move_left*window);
	    zoom_wcmax->x = zoom_wcmin->x + window;

	    window = (spc_wcmax.y - spc_wcmin.y)/zoomfactor;
	    move_down = plot_y_focus/(spc_wcmax.y - spc_wcmin.y);
            zoom_wcmin->y = plot_y_focus - (move_down*window);
            zoom_wcmax->y = zoom_wcmin->y + window;
	}

	else
	{
	    zoom_wcmin->x = plot_x_focus -
				(spc_wcmax.x-spc_wcmin.x)/(zoomfactor*2);
	    zoom_wcmax->x = plot_x_focus +
				(spc_wcmax.x-spc_wcmin.x)/(zoomfactor*2);
	    zoom_wcmin->y = plot_y_focus -
				(spc_wcmax.y-spc_wcmin.y)/(zoomfactor*2);
	    zoom_wcmax->y = plot_y_focus +
				(spc_wcmax.y-spc_wcmin.y)/(zoomfactor*2);
	}
	zoom_wcmin->z = 0.0;
	zoom_wcmax->z = 0.0;
}

/****************************************************************
*
* Routine Name: identify_plot_point
*      Purpose: passed an (x,y) point in device coordinates,
*               attempts to identify that point in the plot.
*
*        Input: (x, y)  coordinate in device coordinates
*       Output: point_index - index into spc_x_col, spc_y_col arrays
*		cluster     - index into spc_map arrays
*		returns True on success, False on failure.
*   Written By: Danielle Argiro
*
****************************************************************/
identify_plot_point(x, y, point_index, cluster)
int    x, y;
int   *point_index;
int   *cluster;
{
        int found = false;
	
	*cluster = 0;

        while ((*cluster < spc_map_rownum) && (!found))
        {
            *point_index = spc_plot_indices[*cluster];
            if (*point_index == -1) 
		(*cluster)++;
            else if ((ABS(x - spc_x_dcs[*point_index]) < 3) &&
                     (ABS(y - spc_y_dcs[*point_index]) < 3))
                found = true;
            else (*cluster)++;
        }
        return(found);
}


/****************************************************************
*
* Routine Name: init_plot_info
*      Purpose: allocates & initializes information assoc. w/ plot
*        Input: none
*       Output: none 
*   Written By: Danielle Argiro
*
****************************************************************/
init_plot_info()
{
	int i, j;

	j = 0;
        for (i = 0; i <= xvdisplay->pixelmax; i++)
        {
            if (xvdisplay->histogram[i] > 0) j++;
        }
        spc_unique_values = j;
     
	if (spc_x_col != NULL) free(spc_x_col);
        spc_x_col = (float *) malloc(spc_unique_values * sizeof(float));

	if (spc_y_col != NULL) free(spc_y_col);
        spc_y_col = (float *) malloc(spc_unique_values * sizeof(float));

	if (spc_plot_indices != NULL) free(spc_plot_indices);
        spc_plot_indices = (int *) malloc(spc_map_rownum * sizeof(int));

	if (spc_plot_rev_indices != NULL) free(spc_plot_rev_indices);
        spc_plot_rev_indices = (int *) malloc(spc_map_rownum * sizeof(int));
     
        for (i = 0; i < spc_map_rownum; i++)
        {
	   spc_plot_indices[i] = -1;
	   spc_plot_rev_indices[i] = -1;
        }
    
        j = 0;
        for (i = 0; i <= xvdisplay->pixelmax; i++)
        {
            if (xvdisplay->histogram[i] > 0)
            {
                spc_x_col[j] = spc_map[0][i];
                spc_y_col[j] = spc_map[1][i];
	        spc_plot_indices[i] = j;
	        spc_plot_rev_indices[j] = i;
	        j++;
            }
    	}

	if (spc_unique_values > 0)
	{
	    if (spc_x_dcs != NULL) free(spc_x_dcs);
            spc_x_dcs = (short *) malloc(spc_unique_values * sizeof(short));

	    if (spc_y_dcs != NULL) free(spc_y_dcs);
            spc_y_dcs = (short *) malloc(spc_unique_values * sizeof(short));
	}
}
