 /*
  * Khoros: $Id: tiepoints.c,v 1.1 1991/05/10 15:59:17 khoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: tiepoints.c,v 1.1 1991/05/10 15:59:17 khoros Exp $";
#endif

 /*
  * $Log: tiepoints.c,v $
 * Revision 1.1  1991/05/10  15:59:17  khoros
 * Initial revision
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 *            Copyright 1990 University of New Mexico
 *
 *  Permission to use, copy, modify, distribute, and sell this
 *  software and its documentation for any purpose is hereby
 *  granted without fee, provided that the above copyright
 *  notice appear in all copies and that both that copyright
 *  notice and this permission notice appear in supporting docu-
 *  mentation, and that the name of UNM not be used in advertis-
 *  ing or publicity pertaining to distribution of the software
 *  without specific, written prior permission.  UNM makes no
 *  representations about the suitability 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 DAMAGES WHATSOEVER
 *  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 PERFORMANCE
 *  OF THIS SOFTWARE.
 *
 *----------------------------------------------------------------------
 */
#include "unmcopyright.h"
#include "warpimage.h"

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>			Initialization Routines               <<<<
   >>>>                                                       <<<<
   >>>>			add_src_tiepoint()		      <<<<
   >>>>			add_dest_tiepoint()		      <<<<
   >>>>			delete_tiepoint()		      <<<<
   >>>>			delete_all_tiepoints()		      <<<<
   >>>>			display_tiept()		              <<<<
   >>>>			save_pixel_values()		      <<<<
   >>>>			restore_pixel_values()		      <<<<
   >>>>			refresh_workspace()		      <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  */

/************************************************************
*
*  MODULE NAME: add_src_tiepoint()
*
*      PURPOSE: add_src_tiepoint is the event handler that adds
*		a tiepoint onto the source image
*
*        INPUT: widget     -  the widget for the event
*               clientData -  not used
*               event      -  the event
*
*       OUTPUT: the green source tiepoint appears on the source image
*
*   WRITTEN BY: Danielle Argiro & Tom Sauer
*
************************************************************/


void add_src_tiepoint(widget, clientData, event, dispatch)
Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
	int x, y, i; 
	TiePointNode *tiept; 
        Widget   raster;
        Arg      args[MaxArgs];
        Position xoffset, yoffset;
	char *error1 = "Please select a destination tiepoint before choosing a new source tiepoint";
        char *id, *src_contents, *dest_contents, temp[MaxLength];

	if (src_image->image == NULL) return;
	if ((tiepoint_mode == SourceAndDest) && (dest_image->image == NULL))
		return;

	if (rubberbanding == True) return;
	if (point_and_clicking == True) return;
	if ((tiepoint_started == True) && (tiepoint_mode == SourceAndDest))
	{
	    xvf_error_wait(error1, "add_src_tiepoint", NULL);
	    return;
	}

	x = event->xbutton.x;
        y = event->xbutton.y;

        if (src_zoom->raster == widget)
	{
	   xvw_get_zoom_position(widget, src_zoom, &x, &y);
	   if (x == -1 || y == -1)
	     return;
	}

	if (tiepoints == NULL)
	{
	    tiepoints = (TiePointNode *) calloc(1, sizeof(TiePointNode));
	    if (tiepoints == NULL)
	    {
		xvf_error_wait("Cannot calloc room for tiepoints list",
			       "add_src_tiepoints", NULL);
	 	return;
	    }
	    tp_count = 0;
	    tiept = tiepoints;
	}
	else
	{
	   tiept = tiepoints; 
	   while (tiept->next != NULL) 
		tiept = tiept->next;

	   tiept->next = (TiePointNode *) calloc(1, sizeof(TiePointNode));
           if (tiept->next == NULL)
           {
                xvf_error_wait("Ran out of room for tiepoints list",
                               "add_src_tiepoints", NULL);
                return;
            }
	   tiept = tiept->next;
	}

        i = 0;
        XtSetArg(args[i], XtNxoffset, &xoffset);             i++;
        XtSetArg(args[i], XtNyoffset, &yoffset);             i++;
        XtGetValues(src_image->raster, args, i);


	if (src_zoom->raster == widget)
	{
	   tiept->src_x = x;
	   tiept->src_y = y;
	}
	else
	{
	   tiept->src_x = x + xoffset;
	   tiept->src_y = y + yoffset;
	}
	tiepoint_started = true;
	tp_count++;
	tiept->id = tp_count; 
	tiept->next = NULL;

        save_pixel_values(src_image, tiept->src_old_pixel, 
			  tiept->src_x, tiept->src_y);
	        
	if (tiepoint_mode == SourceAndDest)
	    display_tiept(src_image->ximage, src_image->pixmap, 
			 tiept->src_x, tiept->src_y, red);
	else
	    display_tiept(src_image->ximage, src_image->pixmap, 
			 tiept->src_x, tiept->src_y, green);

        refresh_workspace(src_image->raster, src_image->ximage, src_zoom);

	if ((tiepoint_mode == SourceOnly) && (view_toplevel != NULL))
        {
            sprintf(temp, "%d", tiept->id);
            id = xvf_strcpy(temp);
            sprintf(temp, "%d,%d", tiept->src_x, tiept->src_y);
            src_contents = xvf_strcpy(temp);
            sprintf(temp, "%d,%d", tiept->dest_x, tiept->dest_y);
            dest_contents = xvf_strcpy(temp);
            tiept_label_offset = add_tiepoint_label(viewback, id,
                                        src_contents, dest_contents,
                                        tiept->offset, &tiept->src, 
					&tiept->dest);
	    tiept->tiept_label = tiept_label_offset;
        }

	/* no other event handler should receive this button press event */
	*dispatch = False;
}

/************************************************************
*
*  MODULE NAME: add_dest_tiepoint()
*
*      PURPOSE: add_dest_tiepoint is the event handler that adds
*		a tiepoint onto the destination image
*
*        INPUT: widget     -  the widget for the event
*               clientData -  not used
*               event      -  the event
*
*       OUTPUT: the green destination tiepoint appears on the destination image,
*		and the red source tiepoint is changed to green.
*
*   WRITTEN BY: Danielle Argiro & Tom Sauer
*
************************************************************/


void add_dest_tiepoint(widget, clientData, event, dispatch)
Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
        Widget   raster;
        Arg      args[MaxArgs];
        Position xoffset, yoffset;
	TiePointNode *tiept;
	int x, y, i;
        char *id, *src_contents, *dest_contents, temp[MaxLength];

	if (dest_image->image == NULL) return;

    	if (rubberbanding == True) return;
    	if (tiepoint_started != True) return;
	if (point_and_clicking == True) return;

	x = event->xbutton.x;
        y = event->xbutton.y;

	if (dest_zoom->raster == widget)
	{
	   xvw_get_zoom_position(widget, dest_zoom, &x, &y);
	   if (x == -1 || y == -1)
	     return;
	}

	tiept = tiepoints;
	while (tiept->next != NULL)
	  tiept = tiept->next;

	i = 0;
        XtSetArg(args[i], XtNxoffset, &xoffset);             i++;
        XtSetArg(args[i], XtNyoffset, &yoffset);             i++;
        XtGetValues(dest_image->raster, args, i);

	if (dest_zoom->raster == widget)
	{
	   tiept->dest_x = x;
	   tiept->dest_y = y;
	}
	else
	{
	   tiept->dest_x = x + xoffset;
	   tiept->dest_y = y + yoffset;
	}
        tiept->offset = tiept_label_offset;

	/* add green tiepoint to destination widget */
        save_pixel_values(dest_image, tiept->dest_old_pixel, 
			  tiept->dest_x, tiept->dest_y);
	display_tiept(dest_image->ximage, dest_image->pixmap, 
		      tiept->dest_x, tiept->dest_y, green);
        refresh_workspace(dest_image->raster, dest_image->ximage, dest_zoom);

	/* change tiepoint on source image from red to green */
	i = 0;
        XtSetArg(args[i], XtNxoffset, &xoffset);             i++;
        XtSetArg(args[i], XtNyoffset, &yoffset);             i++;
        XtGetValues(src_image->raster, args, i);

	display_tiept(src_image->ximage, src_image->pixmap, 
		     tiept->src_x, tiept->src_y, green);
        refresh_workspace(src_image->raster, src_image->ximage, src_zoom);

	tiepoint_started = false;

	if (view_toplevel != NULL)
        {
            sprintf(temp, "%d", tiept->id);
            id = xvf_strcpy(temp);
            sprintf(temp, "%d,%d", tiept->src_x, tiept->src_y);
            src_contents = xvf_strcpy(temp);
            sprintf(temp, "%d,%d", tiept->dest_x, tiept->dest_y);
            dest_contents = xvf_strcpy(temp);
            tiept_label_offset = add_tiepoint_label(viewback, id,
                                        src_contents, dest_contents,
                                        tiept->offset, &tiept->src, 
					&tiept->dest);
	    tiept->tiept_label = tiept_label_offset;
        }
	*dispatch = False;
}


/************************************************************
*
*  MODULE NAME: delete_tiepoint()
*
*      PURPOSE: delete_src_tiepoint is the event handler that adds
*		a tiepoint onto the source image
*
*        INPUT: widget     -  the widget for the event
*               clientData -  not used
*               event      -  the event
*
*       OUTPUT: the green source tiepoint disappears on the source image
*
*   WRITTEN BY: Danielle Argiro & Tom Sauer
*
************************************************************/

void delete_tiepoint(widget, clientData, event, dispatch)
Widget  widget;
caddr_t clientData;
XEvent  *event;
Boolean *dispatch;
{
	TiePointNode *tiept, *last;
	int x, y, i; 
	int done = False;
        Arg      args[MaxArgs];
        Position xoffset = 0, yoffset = 0;

	if (src_image->image == NULL) 
		return;

    	if (rubberbanding == True) return;
	if (point_and_clicking == True) return;

	x = event->xbutton.x;
        y = event->xbutton.y;


	if (src_zoom->raster == widget)
	{
	   xvw_get_zoom_position(widget, src_zoom, &x, &y);
	   if (x == -1 || y == -1)
	     return;
	}
	else if (src_image->raster == widget)
	{
	      i = 0;
              XtSetArg(args[i], XtNxoffset, &xoffset);             i++;
              XtSetArg(args[i], XtNyoffset, &yoffset);             i++;
              XtGetValues(src_image->raster, args, i);
        }
	else if (tiepoint_mode == SourceAndDest)
	{
	    if (dest_zoom->raster == widget)
	    {
	        xvw_get_zoom_position(widget, dest_zoom, &x, &y);
                if (x == -1 || y == -1)
                   return;
	    }
	    else if (dest_image->raster == widget)
	    {
	      i = 0;
              XtSetArg(args[i], XtNxoffset, &xoffset);             i++;
              XtSetArg(args[i], XtNyoffset, &yoffset);             i++;
              XtGetValues(dest_image->raster, args, i);
	    }
	}

	last = tiept = tiepoints;
	while ((tiept != NULL) && !done)
	{
	    if (check_hot_spot(x, y, (tiept->src_x - xoffset), 
				     (tiept->src_y - yoffset))) 
		done = True;
	    else if (check_hot_spot(x, y, (tiept->dest_x - xoffset), 
			                  (tiept->dest_y - yoffset))) 
		done = True;
	    else 
	    {
	        last = tiept;
	 	tiept = tiept->next;
	    }
	}
	if (!done) 
		return;

        restore_pixel_values(src_image, tiept->src_old_pixel, 
			     tiept->src_x, tiept->src_y);
        refresh_workspace(src_image->raster, src_image->ximage, src_zoom);

	if (tiepoint_mode == SourceAndDest)
	{
            restore_pixel_values(dest_image, tiept->dest_old_pixel, 
			         tiept->dest_x, tiept->dest_y);
            refresh_workspace(dest_image->raster, dest_image->ximage, dest_zoom);
	}

	if (last == tiept) 
	{
		tiepoints = tiepoints->next;
		if (view_toplevel != NULL)
		{
		    XtDestroyWidget(last->tiept_label);
		    last->offset = NULL;
		}
		free(last);
		if (tiepoints == NULL)
		    tiept_label_offset = NULL;
        }
	else
	{
	     last->next = tiept->next;
	     if (view_toplevel != NULL)
		 XtDestroyWidget(tiept->tiept_label);
	     free(tiept);
	}


	*dispatch = False;

	
}


/************************************************************
*
*  MODULE NAME: delete_all_tiepoints
*
*      PURPOSE: deletes all tiepoints
*
*        INPUT: None
*
*       OUTPUT: None
*
*   WRITTEN BY: Tom Sauer
*
************************************************************/

delete_all_tiepoints()
{
	TiePointNode *tiept, *last;

	if (src_image->image == NULL)
		return;
    	if (rubberbanding == True) return;
	if (point_and_clicking == True) return;
	if (tiepoints == NULL) return;

	tiept = tiepoints;
	while ((tiept != NULL))
	{
           restore_pixel_values(src_image, tiept->src_old_pixel, 
			        tiept->src_x, tiept->src_y);
	   if (tiepoint_mode == SourceAndDest)
                restore_pixel_values(dest_image, tiept->dest_old_pixel, 
			        tiept->dest_x, tiept->dest_y);
           last = tiept;
	   tiept = tiept->next;
	   if (last->tiept_label != NULL) XtDestroyWidget(last->tiept_label);
	   free(last->offset);
	   free(last->tiept_label);
	   free(last);
	}
	tiept_label_offset = NULL;

	tiepoints = NULL;

        refresh_workspace(src_image->raster, src_image->ximage, src_zoom);

	if (tiepoint_mode == SourceAndDest)
           refresh_workspace(dest_image->raster, dest_image->ximage, dest_zoom);
}


/************************************************************
*
*  MODULE NAME: display_tiept
*
*      PURPOSE: displays a tiepoint onto the XImage at location
*		specified, in color given.
*
*        INPUT: ximage - the XImage onto which tiept is to be displayed
*		pixmap - the X pixmap onto which tiept is to be displayed
*		x, y   - the (x,y) location of the tiept
*		color  - the color in which the tiept is to be displayed
*
*       OUTPUT: The tiept id displayed.
*
*   WRITTEN BY: Danielle Argiro & Tom Sauer
*
************************************************************/

display_tiept(ximage, pixmap, x, y, color)
XImage *ximage;
int    x, y;
Pixel  color;
{
	XPutPixel(ximage, x,   y,   color);
	XPutPixel(ximage, x+1, y,   color);
	XPutPixel(ximage, x-1, y,   color);
	XPutPixel(ximage, x,   y+1, color);
	XPutPixel(ximage, x,   y-1, color);
	XPutPixel(ximage, x+1, y+1, color);
	XPutPixel(ximage, x+1, y-1, color);
	XPutPixel(ximage, x-1, y+1, color);
	XPutPixel(ximage, x-1, y-1, color);
	if (pixmap != None)
	{
	   XSetForeground(display, gc, color);
	   XFillRectangle(display, pixmap, gc, x-1, y-1, 3, 3);
	}
}

/************************************************************
*
*  MODULE NAME: save_pixel_values
*
*      PURPOSE: saves the pixel values that are to be over-written
*		by a tiepoint, for later restoration if that tiepoint
*		happens to be deleted.
*
*        INPUT: xvdisplay - the DisplayStructure for the image to change
*		old_pixel - array in which to store values
*		(x, y) - the pixel position where the tiepoint will
*			 soon be placed (the center of the 3x3 grid of
*			 pixel values to be saved).
*
*       OUTPUT: none
*
*   WRITTEN BY: Danielle Argiro & Tom Sauer
*
************************************************************/
save_pixel_values(xvdisplay, old_pixel, x, y)
DisplayStructure       *xvdisplay;
Pixel  old_pixel[3][3];
int x, y;
{
	int i, j, xpos, ypos;

	ypos = y - 1;
	for (i = 0; i< 3; i++)
	{
	    xpos = x - 1;
            for (j = 0; j < 3; j++)
	    {
	 	old_pixel[i][j] = XGetPixel(xvdisplay->ximage, xpos, ypos);
		xpos++;
	    }
	    ypos++;
	}
	        
}


/************************************************************
*
*  MODULE NAME: restore_pixel_values
*
*      PURPOSE: restores the pixel values that are were over-written
*		by a tiepoint, when that tiepoint is deleted.
*
*        INPUT: ximage - the XImage to which to restore the values
*		old_pixel - array from which to get values
*		(x, y) - the pixel position where the tiepoint will
*			 soon be deleted (the center of the 3x3 grid of
*			 pixel values to be restored).
*
*       OUTPUT: none
*
*   WRITTEN BY: Danielle Argiro & Tom Sauer
*
************************************************************/

restore_pixel_values(xvdisplay, old_pixel, x, y)
DisplayStructure   *xvdisplay;
Pixel  old_pixel[3][3];
int x, y;
{
	int i, j, xpos, ypos;

	ypos  = y - 1;

	for (i = 0; i < 3; i++)
	{
	    xpos  = x - 1;
            for (j = 0; j < 3; j++)
	    {
	        XPutPixel(xvdisplay->ximage, xpos, ypos, old_pixel[i][j]);
	        if (xvdisplay->pixmap != None)
	        {
	           XSetForeground(display, gc, old_pixel[i][j]);
	           XFillRectangle(display, xvdisplay->pixmap, gc, 
				  xpos, ypos, 1, 1);
	        }
	  	xpos++;
	    }
	    ypos++;
	}
}


/************************************************************
*
*  MODULE NAME: refresh_workspace
*
*      PURPOSE: refreshes the workspace, including the source image,
*		the destination image, the source zoom box, and the
*		destination zoom box.
*
*        INPUT: raster - the widget to refresh
*		ximage - the widgets corresponding ximage
*		zoom - the widgets corresponding zoom structure
*
*       OUTPUT: none
*
*   WRITTEN BY: Danielle Argiro & Tom Sauer
*
************************************************************/
refresh_workspace(raster, ximage, zoom)
Widget raster;
XImage *ximage;
ZoomStructure *zoom;
{
	Position xoffset, yoffset;
	int i, width, height;
	Arg args[MaxArgs];

	
	i =0;
	XtSetArg(args[i], XtNxoffset, &xoffset); i++;
	XtSetArg(args[i], XtNyoffset, &yoffset); i++;
	XtSetArg(args[i], XtNwidth, &width); i++;
	XtSetArg(args[i], XtNheight, &height); i++;

	XtGetValues(raster, args, i);

	XPutImage(display, XtWindow(raster), gc, 
		  ximage, xoffset, yoffset, 0, 0, 
		  width, height);

	refresh_zoom(zoom);

	if (zoom->mode == RubberBand)
	    XDrawRectangle(display, XtWindow(raster), gc_invert,
                           zoom->min_x - xoffset, zoom->min_y - yoffset,
			   zoom->xoffset, zoom->yoffset);
}


/************************************************************
*
*  MODULE NAME: add_tiepoint()
*
*      PURPOSE: add_tiepoints new tiepoints read in
*
*        INPUT: src_tps - a 2d array of x,y source tiept locations
*               tgt_tps - a 2d array of x,y target tiept locations
*		num_points - the number of tiepoint pairs
*		type - specifies SourceOnly or SourceAndDest
*
*       OUTPUT: builds the tiepoint linked list
*
*   WRITTEN BY: Tom Sauer
*
************************************************************/


add_tiepoint(src_tps, tgt_tps, num_points, type)
int **src_tps, **tgt_tps;
int type, num_points;
{
	int i;
	TiePointNode *tiept; 
        char *id, *src_contents, *dest_contents, temp[MaxLength];


	tiepoints = (TiePointNode *) calloc(1, sizeof(TiePointNode));
	if (tiepoints == NULL)
	{
	    xvf_error_wait("Cannot calloc room for tiepoints list",
	                   "add_src_tiepoints", NULL);
 	    return;
	}
	tiepoints->next = NULL;
	tiept = tiepoints;

	for (i = 1; i < num_points; i++)
	{
	     tiept->next = (TiePointNode *) calloc(1, sizeof(TiePointNode));
	     if (tiept->next == NULL)
	     {
	         xvf_error_wait("Cannot calloc room for tiepoints list",
			       "add_src_tiepoints", NULL);
 	         return;
	     }
	     tiept = tiept->next;
	     tiept->next = NULL;
	}

	tiept = tiepoints;
	tp_count = 0;
	for (i = 0; i < num_points; i++)
	{
	    tp_count++;
	    tiept->id = tp_count; 
	    tiept->src_x = src_tps[0][i];
	    tiept->src_y = src_tps[1][i];
            save_pixel_values(src_image, tiept->src_old_pixel, 
			     tiept->src_x, tiept->src_y);
	    display_tiept(src_image->ximage, src_image->pixmap, 
			         tiept->src_x, tiept->src_y, green);
	    if (type == SourceAndDest)
	    {
	       tiept->dest_x = tgt_tps[0][i];
	       tiept->dest_y = tgt_tps[1][i];
               save_pixel_values(dest_image, tiept->dest_old_pixel, 
			     tiept->dest_x, tiept->dest_y);
	       display_tiept(dest_image->ximage, dest_image->pixmap, 
			         tiept->dest_x, tiept->dest_y, green);
	    }
	    tiept = tiept->next;
	}

	tiept = tiepoints;
	if (view_toplevel != NULL)
        {
	   while(tiept != NULL)
	   {
              sprintf(temp, "%d", tiept->id);
              id = xvf_strcpy(temp);
              sprintf(temp, "%d,%d", tiept->src_x, tiept->src_y);
              src_contents = xvf_strcpy(temp);
              sprintf(temp, "%d,%d", tiept->dest_x, tiept->dest_y);
              dest_contents = xvf_strcpy(temp);
              tiept_label_offset = add_tiepoint_label(viewback, id,
                                          src_contents, dest_contents,
                                          tiept->offset, &tiept->src, 
					  &tiept->dest);
	      tiept->tiept_label = tiept_label_offset;
	      tiept = tiept->next;
	  }
        }
}
