/*  slice.c */


/*
 * Vis5D system for visualizing five dimensional gridded data sets.
 * Copyright (C) 1990 - 2000 Bill Hibbard, Johan Kellum, Brian Paul,
 * Dave Santek, and Andre Battaiola.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * As a special exception to the terms of the GNU General Public
 * License, you are permitted to link Vis5D with (and distribute the
 * resulting source and executables) the LUI library (copyright by
 * Stellar Computer Inc. and licensed for distribution with Vis5D),
 * the McIDAS library, and/or the NetCDF library, where those
 * libraries are governed by the terms of their own licenses.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "../config.h"

/* Functions for selecting & moving 2-D slices. */


#include <stdio.h>
#include <math.h>
#include "api.h"
#include "gui.h"
#include "globals.h"


/* useful macros: */

#define CROSS( c, a, b )  { c[0] =  a[1]*b[2]-a[2]*b[1]; \
                            c[1] = -a[0]*b[2]+a[2]*b[0]; \
                            c[2] =  a[0]*b[1]-a[1]*b[0]; \
                          }

#define MAGNITUDE( a )    sqrt( a[0]*a[0] + a[1]*a[1] + a[2]*a[2] )

#define CLAMP(VAL,MIN,MAX)   ( (VAL<MIN) ? MIN : ((VAL>MAX) ? MAX : VAL) )


#define DISTANCE( x1, y1, x2, y2 )   sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) )



/* for horizontal slices:
     corner 0 = North East
     corner 1 = South East
     corner 2 = South West
     corner 3 = North West
*/
static void get_col_from_lon( int dindex, float lon, int type, float *col);
static void get_row_from_lat(int dindex, float lat, int type, float *row);





/*
 * Calculate the distance from the cursor to the nearest corner of
 * the given hslice.
 * Input:  curx, cury - cursor coords in pixels
 *         time, var - which timestep and variable
 *         level - position of slice in grid coords [0..Nl-1].
 * Output:  corner - number of nearest corner in [0..3]
 * Returned:  distance from cursor to corner.
 */
static float distance_to_hslice( int index, int curx, int cury,
                                 int time, int var, float level, int *corner )
{
   float cx, cy, px, py;
   float dist, neardist, p[3];
   float r, c;
   float flevel = (float) level;
   int i;
   int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;
   /** INDEX = display index **/
 
   vis5d_get_sizePRIME(index, &Nr, &Nc, &Nl, &LowLev,  &WindNl, &WindLow);

   neardist = 1000000.0;  /* any large value */

   cx = (float) curx;
   cy = (float) cury;

   /* Find distance to each corner of the slice */
   for (i=0;i<4;i++) {
      switch (i) {
         case 0:  r = 0.0;  c = (float) (Nc-1);  break;
         case 1:  r = (float) (Nr-1);  c = (float) (Nc-1);  break;
         case 2:  r = (float) (Nr-1);  c = 0.0;  break;
         case 3:  r = 0.0;  c = 0.0;  break;
      }
      vis5d_gridPRIME_to_xyzPRIME(index, time, var, r, c,
                                  flevel, &p[0], &p[1], &p[2] );
      vis5d_project(index, p, &px, &py );

      dist = (px-cx)*(px-cx) + (py-cy)*(py-cy);
      if (dist<=neardist) {
         neardist = dist;
         *corner = i;
      }
   }

   return neardist;
}

static float distance_to_hclip( int index, int curx, int cury,
                                int num, float level, int *corner)
{
   float cx, cy, px, py;
   float dist, neardist, p[3];
   float r, c;
   int i;
   int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;

   vis5d_get_sizePRIME(index, &Nr, &Nc, &Nl, &LowLev,  &WindNl, &WindLow);
   neardist = 1000000.0;  /* any large value */

   cx = (float) curx;
   cy = (float) cury;

   /* Find distance to each corner of the plane */
   for (i=0;i<4;i++) {
      switch (i) {
         case 0:  r = 0.0;  c = (float) (Nc-1);  break;
         case 1:  r = (float) (Nr-1);  c = (float) (Nc-1);  break;
         case 2:  r = (float) (Nr-1);  c = 0.0;  break;
         case 3:  r = 0.0;  c = 0.0;  break;
      }
      vis5d_gridPRIME_to_xyzPRIME(index, 0, 0, r, c,
                                  level, &p[0], &p[1], &p[2] );
      vis5d_project(index, p, &px, &py );

      dist = (px-cx)*(px-cx) + (py-cy)*(py-cy);
      if (dist<=neardist) {
         neardist = dist;
         *corner = i;
      }
   }

   return neardist;
}
   
static float distance_to_vclip( int index, int curx, int cury, int num,
                                float r1, float c1, float r2, float c2, int *corner)
{
   float cx, cy, px, py;   
   float dist, neardist, p[3];   
   float l, r, c;   
   int curved;
   int i;   
   int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;

   vis5d_get_sizePRIME(index, &Nr, &Nc, &Nl, &LowLev,  &WindNl, &WindLow);
   neardist = 1000000.0;  /* any large value */

   cx = (float) curx;
   cy = (float) cury;

   /* find nearest corner */
   for (i=0;i<4;i++) {
      switch (i) {
          case 0:  r = r1;  c = c1;  l = (float) (Nl-1+LowLev);  break;
          case 1:  r = r2;  c = c2;  l = (float) (Nl-1+LowLev);  break;
          case 2:  r = r1;  c = c1;  l = (float) LowLev;  break;
          case 3:  r = r2;  c = c2;  l = (float) LowLev;  break;
      }
      /* convert r,c,l to graphics coord p */
      vis5d_gridPRIME_to_xyzPRIME(index, 0,0, r, c, l, &p[0], &p[1], &p[2] );
      vis5d_project(index, p, &px, &py );

      dist = (px-cx)*(px-cx) + (py-cy)*(py-cy);
      if (dist<neardist) {
         neardist = dist;
         *corner = i;
      }
   }

   vis5d_get_curved(index, &curved);
   if (curved==0) {
      /* try to find a closer top or bottom edge midpoint */
      for (i=4;i<6;i++) {
         c = ( c1 + c2 ) / 2.0;
         r = ( r1 + r2 ) / 2.0;
         if (i==4) {
            l = (float) (Nl-1+LowLev);
         }
         else {
            l = (float) LowLev;
         }
         vis5d_gridPRIME_to_xyzPRIME(index, 0,0, r, c, l, &p[0], &p[1], &p[2] );
         vis5d_project(index, p, &px, &py );
         dist = (px-cx)*(px-cx) + (py-cy)*(py-cy);
         if (dist<neardist) {
            neardist = dist;
            *corner = i;
         }
      }
   }

   return neardist;
}


/*
 * Calculate the distance from the cursor to the nearest corner of
 * the given vslice.
 * Input:  curx, cuy - cursor coords in pixels
 *         time, var - which timestep and variable
 *         r1,c1,r2,c2 - position of slice in grid coords
 * Output:  corner - number of nearest corner or edge in 0..5
 * Returned:  distance from cursor to corner.
 */
static float distance_to_vslice( int index, int curx, int cury,
                                 int time, int var,
                                 float r1, float c1, float r2, float c2,
                                 int *corner )
{
   float cx, cy;
   float px, py;
   float r, c, l;
   float dist, neardist, p[3];
   int i;
   int curved;
   int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;
   /** INDEX = display index **/
 
   vis5d_get_sizePRIME(index, &Nr, &Nc, &Nl, &LowLev,  &WindNl, &WindLow);

   neardist = 1000000.0;  /* any large value */

   cx = (float) curx;
   cy = (float) cury;

   /* find nearest corner */
   for (i=0;i<4;i++) {
      switch (i) {
          case 0:  r = r1;  c = c1;  l = (float) (Nl-1+LowLev);  break;
          case 1:  r = r2;  c = c2;  l = (float) (Nl-1+LowLev);  break;
          case 2:  r = r1;  c = c1;  l = (float) LowLev;  break;
          case 3:  r = r2;  c = c2;  l = (float) LowLev;  break;
      }
      /* convert r,c,l to graphics coord p */
      vis5d_gridPRIME_to_xyzPRIME(index, time, var, r, c, l, &p[0], &p[1], &p[2] );
      vis5d_project(index, p, &px, &py );

      dist = (px-cx)*(px-cx) + (py-cy)*(py-cy);
      if (dist<neardist) {
         neardist = dist;
         *corner = i;
      }
   }

   vis5d_get_curved(index, &curved);
   if (curved==0) {
      /* try to find a closer top or bottom edge midpoint */
      for (i=4;i<6;i++) {
         c = ( c1 + c2 ) / 2.0;
         r = ( r1 + r2 ) / 2.0;
         if (i==4) {
            l = (float) (Nl-1+LowLev);
         }
         else {
            l = (float) LowLev;
         }
         vis5d_gridPRIME_to_xyzPRIME(index, time, var, r, c, l, &p[0], &p[1], &p[2] );
         vis5d_project(index, p, &px, &py );
         dist = (px-cx)*(px-cx) + (py-cy)*(py-cy);
         if (dist<neardist) {
            neardist = dist;
            *corner = i;
         }
      }
   }

   return neardist;
}

int update_linked_vpos_sliders( int vindex, int dindex, int type, int num, float level)
{
   int *next_vindex, *next_type, *next_var;
   int cur_dindex, cur_vindex, cur_type, cur_var;
   GuiContext gtx = get_gui_gtx(dindex);

   cur_vindex = vindex;
   cur_type = type;
   cur_var = num;

   /**************************************/
   /* loop through all the linked slices */
   /**************************************/
   while(1){
      /**********************************************/
      /* check which slice it is and exit if needed */
      /**********************************************/
      if (gtx->group_index > 0){
         if(!vis5d_get_group_graphic_link(cur_vindex,cur_type,cur_var,
                                          &next_vindex,&next_type,&next_var)){
            return 0;
         }
      }
      else{
         if(!vis5d_get_slice_link(cur_vindex,cur_type,cur_var,
                                  &next_vindex,&next_type,&next_var)){
            return 0;
         }
      }

         
      cur_vindex = *next_vindex;
      cur_type = *next_type;
      cur_var = *next_var;
      if (vindex == cur_vindex && type == cur_type && num == cur_var){
         return 1;
      }
      vis5d_get_ctx_display_index( cur_vindex, &cur_dindex);
      gtx = get_gui_gtx( cur_dindex );
      
      if (type == VIS5D_HSLICE){
         if (gtx->cur_hslice == num){
            mod_vpos_slider( cur_dindex, gtx->hslice_pos_slider, num, 0, level);
         }
      }
      if (type == VIS5D_CHSLICE ){
         if (cb_chvar[cur_dindex] == num){
            mod_vpos_slider( cur_dindex, gtx->chslice_pos_slider, num, 0, level);
         }
      }
      if (type == VIS5D_HWIND){
         if (gtx->cur_hwind == num){
            mod_vpos_slider( cur_dindex, gtx->hwind_pos_slider, num, 1, level);
         }
      }
      if (type == VIS5D_HSTREAM){
         if (gtx->cur_hstream == num){
            mod_vpos_slider( cur_dindex, gtx->hwind_pos_slider, num, 1, level);
         }
      }
   }
   return 1;
}


      




int move_linked_hslices( int vindex, int dindex, int type, int num, float level)
{
   int *next_vindex, *next_type, *next_var;
   int cur_dindex, cur_vindex, cur_type, cur_var;
   int time, curtime, numtimes;
   float interval, low, high, density, scale;
   float hgt, lev, l;
   GuiContext gtx = get_gui_gtx(dindex);


   cur_vindex = vindex;
   cur_type = type;
   cur_var = num;

   vis5d_gridlevelPRIME_to_height( dindex, 0, num, level, &hgt);

   /**************************************/
   /* loop through all the linked slices */
   /**************************************/
   while(1){
      /**********************************************/
      /* check which slice it is and exit if needed */
      /**********************************************/
      if(gtx->group_index > 0){
         if(!vis5d_get_group_graphic_link(cur_vindex,cur_type,cur_var,
                                  &next_vindex,&next_type,&next_var)){
            return 0;
         }
      }
      else{
         if(!vis5d_get_slice_link(cur_vindex,cur_type,cur_var,
                                  &next_vindex,&next_type,&next_var)){
            return 0;
         }
      }

      cur_vindex = *next_vindex;
      cur_type = *next_type;
      cur_var = *next_var;
      if (vindex == cur_vindex && type == cur_type && num == cur_var){
         return 1;
      }
      vis5d_get_ctx_display_index( cur_vindex, &cur_dindex);
      vis5d_height_to_gridlevelPRIME( cur_dindex, 0, cur_var, hgt, &lev);

      /****************************/
      /* get, set, and make slice */
      /****************************/
      if (cur_type == VIS5D_HSLICE){
         vis5d_get_hslice(cur_vindex, cur_var, &interval, &low, &high, &l);
         vis5d_set_hslice(cur_vindex, cur_var, interval, low, high, lev);
         vis5d_get_ctx_numtimes(cur_vindex, &numtimes);
         vis5d_get_ctx_timestep(cur_vindex, &curtime);
         for (time = 0; time < numtimes; time++){
            vis5d_make_hslice( cur_vindex, time, cur_var, time==curtime);
         }
      }
      else if (cur_type == VIS5D_CHSLICE){
         vis5d_set_chslice(cur_vindex, cur_var, lev);
         vis5d_get_ctx_numtimes(cur_vindex, &numtimes);
         vis5d_get_ctx_timestep(cur_vindex, &curtime);
         for (time = 0; time < numtimes; time++){
            vis5d_make_chslice( cur_vindex, time, cur_var, time==curtime);
         }
      }
      else if (cur_type == VIS5D_HWIND){
         vis5d_get_hwindslice(cur_dindex, cur_var, &density, &scale, &l);
         vis5d_set_hwindslice(cur_dindex, cur_var, density, scale, lev);
         vis5d_get_dtx_numtimes(cur_dindex, &numtimes);
         vis5d_get_dtx_timestep(cur_dindex, &curtime);
         for (time = 0; time < numtimes; time++){
            vis5d_make_hwindslice( cur_dindex, time, cur_var, time==curtime);
         }
      }
      else if (cur_type == VIS5D_HSTREAM){
         vis5d_get_hstreamslice(cur_dindex, cur_var, &density, &l);
         vis5d_set_hstreamslice(cur_dindex, cur_var, density, lev);
         vis5d_get_dtx_numtimes(cur_dindex, &numtimes);
         vis5d_get_dtx_timestep(cur_dindex, &curtime);
         for (time = 0; time < numtimes; time++){
            vis5d_make_hstreamslice( cur_dindex, time, cur_var, time==curtime);
         }
      }
   }
   return 1;
}




       
      

      
int move_linked_vslices( int vindex, int dindex, int type, int num, int corner,
                         float row0, float col0, float row1, float col1)
{
   int *next_vindex, *next_type, *next_var;
   int cur_dindex, cur_vindex, cur_type, cur_var;
   int Nr, Nc, Nl, Nr0, Nc0, Nl0, LowLev, WindNl, WindLow;
   int time, curtime, numtimes;
   float interval, low, high, density, scale;
   float lat1, lat0, lon1, lon0;
   float r0, r1, c0, c1;
   GuiContext gtx = get_gui_gtx(dindex);

   cur_vindex = vindex;
   cur_type = type;
   cur_var = num;

   vis5d_rowcolPRIME_to_latlon( dindex, 0, 0, row0, col0, &lat0, &lon0);
   vis5d_rowcolPRIME_to_latlon( dindex, 0, 0, row1, col1, &lat1, &lon1);
   vis5d_get_sizePRIME( dindex, &Nr0, &Nc0, &Nl0, &LowLev,  &WindNl, &WindLow);

   /**************************************/
   /* loop through all the linked slices */
   /**************************************/
   while(1){
      /**********************************************/
      /* check which slice it is and exit if needed */
      /**********************************************/
      if (gtx->group_index > 0){
         if(!vis5d_get_group_graphic_link(cur_vindex,cur_type,cur_var,
                                          &next_vindex,&next_type,&next_var)){
            return 0;
         }
      }
      else{
         if(!vis5d_get_slice_link(cur_vindex,cur_type,cur_var,
                                  &next_vindex,&next_type,&next_var)){
            return 0;
         }
      }

      cur_vindex = *next_vindex;
      cur_type = *next_type;
      cur_var = *next_var;

      if (vindex == cur_vindex && type == cur_type && num == cur_var){
         return 1;
      }

      vis5d_get_ctx_display_index( cur_vindex, &cur_dindex);
      vis5d_get_sizePRIME( cur_dindex, &Nr, &Nc, &Nl, &LowLev,  &WindNl, &WindLow);

      /**************************************/      
      /* get the appropriate r0, c0, r1, c1 */
      /**************************************/
      if (cur_type == VIS5D_VSLICE){
         vis5d_get_vslice(cur_vindex, cur_var, &interval, &low, &high,
                          &r0, &c0, &r1, &c1);
      }
      else if (cur_type == VIS5D_CVSLICE){
         vis5d_get_cvslice(cur_vindex, cur_var,
                          &r0, &c0, &r1, &c1);
      }
      else if (cur_type == VIS5D_VWIND){
         vis5d_get_vwindslice(cur_dindex, cur_var, &density, &scale,
                          &r0, &c0, &r1, &c1);
      }
      else if (cur_type == VIS5D_VSTREAM){
         vis5d_get_vstreamslice(cur_dindex, cur_var, &density, 
                          &r0, &c0, &r1, &c1);
      }


      /************************************************/
      /* get appropriate row and col from lat and lon */
      /************************************************/  
      if (corner==0 || corner==2){
         if (row0 == 0){
            get_col_from_lon( cur_dindex, lon0, 0, &c0);
            r0 = 0.0;
         }
         else if (col0 == 0){
            get_row_from_lat( cur_dindex, lat0, 0, &r0);
            c0 = 0.0;
         }
         else if (row0 == Nr0-1){
            get_col_from_lon( cur_dindex, lon0, 1,  &c0);
            r0 = (float)(Nr-1);
         }
         else if(col0 == Nc0-1){
            get_row_from_lat( cur_dindex, lat0, 1, &r0);
            c0 = (float)(Nc-1);
         }
      }
      else if(corner==1 || corner==3){
         if (row1 == 0){
            get_col_from_lon( cur_dindex, lon1, 0, &c1);
            r1 = 0.0;
         }
         else if (col1 == 0){
            get_row_from_lat( cur_dindex, lat1, 0, &r1);
            c1 = 0.0;
         }
         else if (row1 == Nr0-1){
            get_col_from_lon( cur_dindex, lon1, 1,  &c1);
            r1 = (float)(Nr-1);
         }
         else if(col1 == Nc0-1){
            get_row_from_lat( cur_dindex, lat1, 1, &r1);
            c1 = (float)(Nc-1);
         }
      }
      else if(corner==4 || corner==5){
         if (row0 == 0){
            get_col_from_lon( cur_dindex, lon0, 0, &c0);
            r0 = 0.0;
         }
         else if (col0 == 0){
            get_row_from_lat( cur_dindex, lat0, 0, &r0);
            c0 = 0.0;
         }
         else if (row0 == Nr0-1){
            get_col_from_lon( cur_dindex, lon0, 1,  &c0);
            r0 = (float)(Nr-1);
         }
         else if(col0 == Nc0-1){
            get_row_from_lat( cur_dindex, lat0, 1, &r0);
            c0 = (float)(Nc-1);
         }
         if (row1 == 0){
            get_col_from_lon( cur_dindex, lon1, 0, &c1);
            r1 = 0.0;
         }
         else if (col1 == 0){
            get_row_from_lat( cur_dindex, lat1, 0, &r1);
            c1 = 0.0;
         }
         else if (row1 == Nr0-1){
            get_col_from_lon( cur_dindex, lon1, 1,  &c1);
            r1 = (float)(Nr-1);
         }
         else if(col1 == Nc0-1){
            get_row_from_lat( cur_dindex, lat1, 1, &r1);
            c1 = (float)(Nc-1);
         }
      }
      else{
         printf("error in linked slice moving\n");
         return 0;
      }


      /*******************************/      
      /* now set and make the slices */
      /*******************************/
      if (cur_type == VIS5D_VSLICE){
         vis5d_set_vslice(cur_vindex, cur_var, interval, low, high,
                          r0, c0, r1, c1);
         vis5d_get_ctx_numtimes(cur_vindex, &numtimes);
         vis5d_get_ctx_timestep( cur_vindex, &curtime);
         for (time = 0; time < numtimes; time++){
            vis5d_make_vslice(cur_vindex, time, cur_var, time==curtime);
         }
      }
      else if (cur_type == VIS5D_CVSLICE){
         vis5d_set_cvslice(cur_vindex, cur_var,
                          r0, c0, r1, c1);
         vis5d_get_ctx_numtimes(cur_vindex, &numtimes);
         vis5d_get_ctx_timestep( cur_vindex, &curtime);
         for (time = 0; time < numtimes; time++){
            vis5d_make_cvslice(cur_vindex, time, cur_var, time==curtime);
         }
      }
      else if (cur_type == VIS5D_VWIND){
         vis5d_set_vwindslice(cur_dindex, cur_var, density, scale,
                          r0, c0, r1, c1);
         vis5d_get_dtx_numtimes(cur_dindex, &numtimes);
         vis5d_get_dtx_timestep(cur_dindex, &curtime);
         for (time = 0; time < numtimes; time++){
            vis5d_make_vwindslice(cur_dindex, time, cur_var, time==curtime);
         }
      }
      else if (cur_type == VIS5D_VSTREAM){
         vis5d_set_vstreamslice(cur_dindex, cur_var, density,
                          r0, c0, r1, c1);
         vis5d_get_dtx_numtimes(cur_dindex, &numtimes);
         vis5d_get_dtx_timestep(cur_dindex, &curtime);
         for (time = 0; time < numtimes; time++){
            vis5d_make_vstreamslice(cur_dindex, time, cur_var, time==curtime);
         }
      }
   }
   return 1;
}






/*
 * Given a cursor position, find the slice to be selected for moving.
 * Input:  curx,cury - the cursor position in pixels (0,0) = upper-left
 *         time - which timestep
 *         contype   - pointer to int
 *         contour   - pointer to int
 *         corner    - pointer to int
 * Output:  contype - either HSLICE, VSLICE, CHSLICE, or CVSLICE.
 *          contour - the number of the nearest contour (which variable)
 *          corner - the number of the nearest corner in [0..3] or
 *                   if a vslice edge midpoint has been selected 4, or 5
 * Returned:  1 - a contour has been identified.
 *            0 - a contour was not identified.
 */
static int find_nearest_slice( int index, int curx, int cury, int time,
                               int *contype, int *contour, int *corner, int *thecontext )
{
   float dist, neardist;
   int var, corn, w;
   int nearctx, yo;
   float interval, low, high, level;
   float row0, col0, row1, col1;
   float density, scale;
   int Uvar, Vvar, Wvar, Uvar2, Vvar2, Wvar2, TrajU, TrajV, TrajW;
   int NumVars;
   int Nr, Nc, Nl[MAXVARS], LowLev[MAXVARS], MaxNl, MaxNlVar, WindNl, WindLow;
   int howmany, whichones[VIS5D_MAX_CONTEXTS];
   int owner[9];
   /** INDEX = display index **/

   vis5d_get_num_of_ctxs_in_display( index, &howmany, whichones);
   neardist = 15.0 * 15.0;  /* 15 pixel radius */
   *contype = *contour = *corner = *thecontext = -1;
   nearctx = -1;
   for (yo = 0; yo < howmany; yo ++){
      int vindex = whichones[yo];

      vis5d_get_ctx_numvars( vindex, &NumVars );
      vis5d_get_size(vindex, &Nr, &Nc, Nl, LowLev, &MaxNl,  &MaxNlVar, &WindNl, &WindLow);

      /* try horizontal contour slices */
      for (var=0;var<NumVars;var++) {
         if (vis5d_enable_graphics(vindex, VIS5D_HSLICE, var, VIS5D_GET)) {
            vis5d_get_hslice(vindex, var, &interval, &low, &high, &level);
            dist = distance_to_hslice(index, curx, cury, time, var, level, &corn );
            if (dist<neardist) {
               *thecontext = vindex;
               neardist = dist;
               *contype = VIS5D_HSLICE;
               *contour = var;
               *corner = corn;
            }
         }
      }

      /* try horizontal color slices */
      for (var=0;var<NumVars;var++) {
         if (vis5d_enable_graphics(vindex, VIS5D_CHSLICE, var, VIS5D_GET)) {
            vis5d_get_chslice(vindex, var, &level);
            dist = distance_to_hslice(index, curx, cury, time, var, level, &corn );
            if (dist<neardist) {
               *thecontext = vindex;
               neardist = dist;
               *contype = VIS5D_CHSLICE;
               *contour = var;
               *corner = corn;
            }
         }
      }

      /* try vertical contour slices */
      for (var=0;var<NumVars;var++) {
         if (vis5d_enable_graphics(vindex, VIS5D_VSLICE, var, VIS5D_GET)) {
            vis5d_get_vslice(vindex, var, &interval,
                                  &low, &high, &row0, &col0, &row1, &col1);
            dist = distance_to_vslice(index, curx, cury,  time, var,
                                      row0, col0, row1, col1, &corn );
            if (dist<neardist) {
               neardist = dist;
               *thecontext = vindex;
               *contype = VIS5D_VSLICE;
               *contour = var;
               *corner = corn;
            }
         }
      }

      /* try vertical colored slices */
      for (var=0;var<NumVars;var++) {
         if (vis5d_enable_graphics(vindex, VIS5D_CVSLICE, var, VIS5D_GET)) {
            vis5d_get_cvslice(vindex, var, &row0, &col0, &row1, &col1);
            dist = distance_to_vslice(index, curx, cury,  time, var,
                                      row0, col0, row1, col1, &corn );
            if (dist<neardist) {
               neardist = dist;
               *contype = VIS5D_CVSLICE;
               *thecontext = vindex;
               *contour = var;
               *corner = corn;
            }
         }
      }

      vis5d_get_wind_vars(index, &owner[0],&Uvar, &owner[1], &Vvar, &owner[2], &Wvar,
                          &owner[3], &Uvar2, &owner[4], &Vvar2, &owner[5], &Wvar2,
                          &owner[6], &TrajU, &owner[7], &TrajV, &owner[8], &TrajW);

      /* try horizontal wind slices */
      for (w=0;w<VIS5D_WIND_SLICES;w++) {
         int wner = w == 0 ? owner[0] : owner[3];
         if (wner == vindex && vis5d_enable_graphics(vindex, VIS5D_HWIND, w, VIS5D_GET)) {
            int uvar = w == 0 ? Uvar : Uvar2;
            vis5d_get_hwindslice(index, w, &density, &scale, &level);
            dist = distance_to_hslice(index, curx, cury, time, uvar, level, &corn );
            if (dist<neardist) {
               neardist = dist;
               *contype = VIS5D_HWIND;
               *thecontext = vindex;
               *contour = w;
               *corner = corn;
            }
         }
      }

      /* try vertical wind slice */
      for (w=0;w<VIS5D_WIND_SLICES;w++) {
         int wner = w == 0 ? owner[0] : owner[3];
         if (wner == vindex && vis5d_enable_graphics(vindex, VIS5D_VWIND, w, VIS5D_GET)) {
            int uvar = w == 0 ? Uvar : Uvar2;
            vis5d_get_vwindslice(index, w, &density, &scale, &row0, &col0, &row1, &col1);
            dist = distance_to_vslice(index, curx, cury, time, uvar,
                                      row0, col0, row1, col1, &corn );
            if (dist<neardist) {
               neardist = dist;
               *contype = VIS5D_VWIND;
               *contour = w;
               *thecontext = vindex;
               *corner = corn;
            }
         }
      }

      /* try horizontal stream slices */
      for (w=0;w<VIS5D_WIND_SLICES;w++) {
         int wner = w == 0 ? owner[0] : owner[3];
         if (wner == vindex && vis5d_enable_graphics(vindex, VIS5D_HSTREAM, w, VIS5D_GET)) {
            int uvar = w == 0 ? Uvar : Uvar2;
            vis5d_get_hstreamslice(index, w, &density, &level);
            dist = distance_to_hslice(index, curx, cury, time, uvar, level, &corn );
            if (dist<neardist) {
               neardist = dist;
               *contype = VIS5D_HSTREAM;
               *contour = w;
               *thecontext = vindex;
               *corner = corn;
            }
         }
      }

      /* try vertical stream slice */
      for (w=0;w<VIS5D_WIND_SLICES;w++) {
         int wner = w == 0 ? owner[0] : owner[3];         
         if (wner == vindex && vis5d_enable_graphics(vindex, VIS5D_VSTREAM, w, VIS5D_GET)) {
            int uvar = w == 0 ? Uvar : Uvar2;
            vis5d_get_vstreamslice(index, w, &density, &row0, &col0, &row1, &col1);
            dist = distance_to_vslice(index, curx, cury, time, uvar,
                                      row0, col0, row1, col1, &corn );
            if (dist<neardist) {
               neardist = dist;
               *contype = VIS5D_VSTREAM;
               *thecontext = vindex;
               *contour = w;
               *corner = corn;
            }
         }
      }
   }
   /** ALL DONE!  contype, contour, corner have the results */
   if (*contype!=-1) {
      return 1;
   }
   else
      return 0;
}

static int find_nearest_clip_slice( int index, int curx, int cury,
                                    int *planetype, int *planenum, int *corner)
{
   float dist, neardist;
   int i, corn, current;
   float level, r1, c1, r2, c2;

   neardist = 15.0 * 15.0;  /* 15 pixel radius */
   *planetype = *planenum = *corner = -1;

   /*try horizontal clipping planes*/
   for (i=0; i < 2; i++){
      vis5d_get_hclip( index, i, &level);
      vis5d_get_clip_mode( index, i, &current);
      if (current==1){
         dist = distance_to_hclip( index, curx, cury, i, level, &corn);
         if (dist < neardist){
            neardist = dist;
            *planetype = 0;
            *planenum = i;
            *corner = corn;
         }
      }
   }
   for (i=0; i < 4; i++){
      vis5d_get_vclip( index, i, &r1, &c1, &r2, &c2);
      vis5d_get_clip_mode( index, i+2, &current);
      if (current==1){
         dist = distance_to_vclip( index, curx, cury, i, r1, c1, r2, c2, &corn);
         if (dist < neardist){
            neardist = dist;
            *planetype = 1;
            *planenum = i;
            *corner = corn;
         }
      }
   }
   if (*planetype != -1){
      return 1;
   }
   else{
      return 0;
   }
}

/*** point_nearest_line ***********************************************
   Given two lines 'a' and 'b' find the point along 'a' which is
   closest to 'b'.  The result is in the form of a parameter 't' along
   line 'a'.
   Input:  a, da - point on 'a' and direction of 'a'
           b, db - point on 'b' and direction of 'b'
   Return:  t, such that a+t*da = the nearest point.
**********************************************************************/
static float point_nearest_line( float a[3], float da[3],
                                 float b[3], float db[3] )
{
   float dc[3], mc;
   float m[3][3], det, t;

   /* find point on 'a' closest to 'b' */
   CROSS( dc, da, db );   /* dc = da X db */
   mc = MAGNITUDE( dc );
   if (mc<=0.0001) {
      /* a is nearly parallel to b */
      return 0.0;
   }
   else {
      m[0][0] = b[0]-a[0];  m[0][1] = b[1]-a[1];  m[0][2] = b[2]-a[2];
      m[1][0] = db[0];      m[1][1] = db[1];      m[1][2] = db[2];
      m[2][0] = dc[0];      m[2][1] = dc[1];      m[2][2] = dc[2];

      det = m[0][0] * (m[1][1]*m[2][2]-m[1][2]*m[2][1])
          - m[0][1] * (m[1][0]*m[2][2]-m[1][2]*m[2][0])
           + m[0][2] * (m[1][0]*m[2][1]-m[1][1]*m[2][0]);

      t = det / (mc*mc);
      return t;
   }
}



/*** move_hslice ******************************************************
   Move a horizontal slice according to the cursor position.
   Input:  curx, cury - cursor position in pixels
           level - pointer to current slice level
           corner - which corner is 'grabbed'
   Output:  level - modified to change position.
   Returned:  0 = no movement
              1 = slice was moved.
**********************************************************************/
static int move_hslice( int index, int curx, int cury,
                        int time, int var,
                        float *level, int corner )
{
   float cx,cy;
   float a[3], da[3];
   float b[3], db[3];
   float t;
   float topl, botl, aa[3];
   float r, c;
   float maxlevel, minlevel;
   int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;
   int vNr, vNc, vNl[MAXVARS], vLowLev[MAXVARS], vMaxNl, vMaxNlVar, vWindNl, vWindLow;

   /** INDEX = vis5d_context index **/ 
   int dindex;

   vis5d_get_ctx_display_index( index, &dindex);
   vis5d_get_sizePRIME(dindex, &Nr, &Nc, &Nl, &LowLev,  &WindNl, &WindLow);
   vis5d_get_size( index, &vNr, &vNc, vNl, vLowLev, &vMaxNl, &vMaxNlVar, &vWindNl, &vWindLow);
   if (vNl[var]==1) {
      /* Special case: hslice of a 2-D grid can be at any level*/
      maxlevel = (float) (Nl-1);
      minlevel = 0.0;
   }
   else {
      maxlevel = (float) (Nl-1+LowLev);
      minlevel = (float) LowLev;
   }
   /* "unproject" cursor position to a line 'b' in 3-D graphics coordinates */
   cx = (float) curx;
   cy = (float) cury;
   vis5d_unproject( dindex, cx, cy, b, db );
   /* let 'a'+'da' be the line corresponding to the edge of the box
    * along which the slice is being dragged.
    */
   switch (corner) {
      case 0:  r = 0.0;  c = (float) (Nc-1);  break;
      case 1:  r = (float) (Nr-1);  c = (float) (Nc-1);  break;
      case 2:  r = (float) (Nr-1);  c = 0.0;  break;
      case 3:  r = 0.0;  c = 0.0;  break;
   }
   topl = maxlevel;
   botl = minlevel;
   vis5d_gridPRIME_to_xyzPRIME(dindex, time, var, r, c, botl, &a[0], &a[1], &a[2] );
   vis5d_gridPRIME_to_xyzPRIME(dindex, time, var, r, c, topl, &aa[0], &aa[1], &aa[2] );
   da[0] = aa[0] - a[0];
   da[1] = aa[1] - a[1];
   da[2] = aa[2] - a[2];


   /* find point on 'a' closest to 'b' */
   t = point_nearest_line( a, da, b, db );
   if (t==0.0) {
      return 0;
   }
   else {
      float x, y, z, row, col;
      /* convert "closest" z value back to a grid level */
      x = a[0] + t * da[0];
      y = a[1] + t * da[1];
      z = a[2] + t * da[2];
      vis5d_xyzPRIME_to_gridPRIME(dindex, time, var, x, y, z, &row, &col, level );
      return 1;
   }

}

static int move_hclip( int index, int num, int curx, int cury,
                       float  *level, int corner)
{
   int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;
   float cx,cy;
   float a[3], da[3];
   float b[3], db[3];
   float t;
   float topl, botl, aa[3];
   float r, c;
   float maxlevel, minlevel;

   vis5d_get_sizePRIME(index, &Nr, &Nc, &Nl, &LowLev,  &WindNl, &WindLow);

   maxlevel = (float) (Nl-1+LowLev);
   minlevel = (float) LowLev;

   /* "unproject" cursor position to a line 'b' in 3-D graphics coordinates */
   cx = (float) curx;
   cy = (float) cury;
   vis5d_unproject( index, cx, cy, b, db );
   /* let 'a'+'da' be the line corresponding to the edge of the box
    * along which the slice is being dragged.
    */
   switch (corner) {
      case 0:  r = 0.0;  c = (float) (Nc-1);  break;
      case 1:  r = (float) (Nr-1);  c = (float) (Nc-1);  break;
      case 2:  r = (float) (Nr-1);  c = 0.0;  break;
      case 3:  r = 0.0;  c = 0.0;  break;
   }
   topl = maxlevel;
   botl = minlevel;
   vis5d_gridPRIME_to_xyzPRIME(index, 0,0, r, c, botl, &a[0], &a[1], &a[2] );
   vis5d_gridPRIME_to_xyzPRIME(index, 0,0, r, c, topl, &aa[0], &aa[1], &aa[2] );
   da[0] = aa[0] - a[0];
   da[1] = aa[1] - a[1];
   da[2] = aa[2] - a[2];
      
   /* find point on 'a' closest to 'b' */
   t = point_nearest_line( a, da, b, db );
   if (t==0.0) {
      return 0;
   }
   else {
      float x, y, z, row, col;
      /* convert "closest" z value back to a grid level */
      x = a[0] + t * da[0];
      y = a[1] + t * da[1];
      z = a[2] + t * da[2];
      vis5d_xyzPRIME_to_gridPRIME(index, 0,0, x, y, z, &row, &col, level );
      return 1;
   }
}

static int move_vclip( int index, int num, int curx, float cury, float *r1, float *c1,
                       float *r2, float *c2, int  corner)
{
   float cx, cy;
   float b[3], db[3];
   float p[3], px, py;
   float nr, nc;
   int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;

   vis5d_get_sizePRIME(index, &Nr, &Nc, &Nl, &LowLev,  &WindNl, &WindLow);

   nr = (float) (Nr-1);
   nc = (float) (Nc-1);

   cx = (float) curx;
   cy = (float) cury;
   vis5d_unproject( index, cx, cy, b, db );

   if (corner<4) {
      /** dragging slice by a corner **/
      float dist, neardist, neart, t, l;
      float newcol, newrow;
      int edge, nearedge;

      if (corner<2) {
         l = (float) (Nl-1+LowLev);
      }
      else {
         l = (float) LowLev;
      }

      /* find the point on one of the top or bottom box edges which */
      /* is closest to the cursor */
      neardist = 10000.0;
      for (edge=0;edge<4;edge++) {
         float r0, c0, r1, c1, a[3], da[3], aa[3];

         switch (edge) {
            case 0:
               /* north */
               r0 = 0.0;  c0 = 0.0;
               r1 = 0.0;  c1 = (float) (Nc-1);
               break;
            case 1:
               /* east */
               r0 = 0.0;  c0 = (float) (Nc-1);
               r1 = (float) (Nr-1);  c1 = c0;
               break;
            case 2:
               /* south */
               r0 = (float) (Nr-1);  c0 = 0.0;
               r1 = (float) (Nr-1);  c1 = (float) (Nc-1);
               break;
            case 3:
               /* west */
               r0 = 0.0;  c0 = 0.0;
               r1 = (float) (Nr-1);  c1 = 0.0;
               break;
         }
         vis5d_gridPRIME_to_xyzPRIME( index, 0,0, r0, c0, l, &a[0], &a[1], &a[2] );
         vis5d_gridPRIME_to_xyzPRIME( index, 0,0, r1, c1, l, &aa[0], &aa[1], &aa[2] );

         da[0] = aa[0] - a[0];
         da[1] = aa[1] - a[1];
         da[2] = aa[2] - a[2];
         t = point_nearest_line( a, da, b, db );
         p[0] = a[0] + da[0]*t;
         p[1] = a[1] + da[1]*t;
         p[2] = a[2] + da[2]*t;
         vis5d_project( index, p, &px, &py );
         dist = sqrt( (px-cx)*(px-cx) + (py-cy)*(py-cy) );
         if (dist==0.0)  return 0;
         if (dist<neardist) {
            neardist = dist;
            nearedge = edge;
            neart = CLAMP( t, 0.0, 1.0 );
         }
      }
      /* compute new row and column for corner of the slice */
      switch (nearedge) {
         case 0:
            /* north */
            newcol = nc*neart;
            newrow = 0.0;
            break;
         case 1:
            /* east */
            newcol = nc;
            newrow = nr*neart;
            break;
         case 2:
            /* south */
            newcol = nc*neart;
            newrow = nr;
            break;
         case 3:
            /* west */
            newcol = 0.0;
            newrow = nr*neart;
            break;
      }

      if (corner%2==0) {
         /* make sure the corners won't be too close */
         if (DISTANCE( *r2, *c2, newrow, newcol) > 2.0) {
            *r1 = newrow;
            *c1 = newcol;
         }
      }
      else {
         /* make sure the corners won't be too close */
         if (DISTANCE( *r1, *c1, newrow, newcol) > 2.0) {
            *r2 = newrow;
            *c2 = newcol;
         }
      }
      return 1;

   }
   else {
      /** dragging slice by midpoint of edge **/

      float prow, pcol, plev, drow, dcol;
      float newr1, newc1, newr2, newc2;
      float x, y, t;


      if (corner==4) {
         /* find intersection 'p', of 'b' with top plane of box */
         vis5d_gridPRIME_to_xyzPRIME(index, 0,0, 1.0, 1.0, (float) (Nl-1+LowLev),
                           &x, &y, &t);
         t = (t - b[2]) / db[2];
      }
      else {
         /* find intersection 'p', of 'b' with bottom plane of box */
         vis5d_gridPRIME_to_xyzPRIME(index, 0,0, 1.0, 1.0, (float) LowLev, &x, &y, &t);
         t = (t - b[2]) / db[2];
      }
      p[0] = b[0] + t*db[0];
      p[1] = b[1] + t*db[1];
      p[2] = b[2] + t*db[2];

      /* convert p to row and columns */
      vis5d_xyzPRIME_to_gridPRIME( index, 0,0, p[0], p[1], p[2], &prow, &pcol, &plev );

      /* let d = 2-D vector parallel to current slice */
      drow = *r2 - *r1;
      dcol = *c2 - *c1;

      if (drow==0.0) {
         /* east/west slice */
         newc1 = *c1;
         newc2 = *c2;
         newr1 = newr2 = prow;
      }
      else if (dcol==0.0) {
         /* north/south slice */
         newr1 = *r1;
         newr2 = *r2;
         newc1 = newc2 = pcol;
      }
      else {
         /* diagonal slice */
         /* recompute r1,c1, r2,c2 such that they fall on the line */
         /* defined by point (prow,pcol) and direction (drow,dcol) */
         t = -prow / drow;
         newr1 = 0.0;
         newc1 = pcol + dcol*t;

         t = (nr-prow) / drow;
         newr2 = Nr-1.0;
         newc2 = pcol + dcol*t;
         /* trim */
         if (newc1<0.0) {
            t = -pcol / dcol;
            newr1 = prow + drow*t;
            newc1 = 0.0;
         }
         if (newc1>(float)(Nc-1)) {
            t = (nc-pcol) / dcol;
            newr1 = prow + drow*t;
            newc1 = (float) (Nc-1);
         }
         if (newc2<0.0) {
            t = -pcol / dcol;
            newr2 = prow + drow*t;
            newc2 = 0.0;
         }
         if (newc2>(float)(Nc-1)) {
            t = (nc-pcol) / dcol;
            newr2 = prow + drow*t;
            newc2 = (float) (Nc-1);
         }
      }

      newc1 = CLAMP( newc1, 0.0, nc );
      newc2 = CLAMP( newc2, 0.0, nc );
      newr1 = CLAMP( newr1, 0.0, nr );
      newr2 = CLAMP( newr2, 0.0, nr );

#ifdef LEAVEOUT
      if ((newc1==0.0 && newc2==0.0) || (newc1==nc && newc2==nc)) {
         if (newr1<nr/2.0)
           newr1 = 0.0;
         else
           newr1 = nr;
         newr2 = nr - newr1;
      }
      if ((newr1==0.0 && newr2==0.0) || (newr1==nc && newr2==nc)) {
         if (newc1<nc/2.0)
           newc1 = 0.0;
         else
           newc1 = nc;
         newc2 = nc - newc1;
      }
#endif
      /* make sure the corners won't be too close */
      if (DISTANCE( newr1, newc1, newr2, newc2 ) > 2.0) {
         if (drow < 0.0){
            *r1 = newr2;
            *c1 = newc2;
            *r2 = newr1;
            *c2 = newc1;
         }
         else{
            *r1 = newr1;
            *c1 = newc1;
            *r2 = newr2;
            *c2 = newc2;
         }
      }
      return 1;
   }
}

/* this will get the top/bottom col for a given lon */
/* type = 0     Top/North edge of box */
/* type = 1     Bottom/South edge of box */
static void get_col_from_lon( int dindex, float lon, int type, float *col)
{
   float projargs[100];
   int proj;
   float longitude, longitude1, longitude2, junk;
 
   *col = 0.0;
   vis5d_get_dtx_projection( dindex, &proj, projargs);
   if (proj == PROJ_GENERIC || proj == PROJ_LINEAR ||
       proj == PROJ_CYLINDRICAL || proj == PROJ_SPHERICAL){
      int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;
      
      vis5d_get_sizePRIME(dindex, &Nr, &Nc, &Nl,
                          &LowLev,  &WindNl, &WindLow);

      vis5d_rowcolPRIME_to_latlon( dindex, 0, 0, 0.0, 0.0,
                                   &junk, &longitude1);
      vis5d_rowcolPRIME_to_latlon( dindex, 0, 0, 0.0, Nc-1,
                                   &junk, &longitude2);
      *col = ((((float)(Nc-1))/
             (longitude2-longitude1))*(lon-longitude1));
   }
   else{
      float num, left, right, countdown, done, mpoint, edge;
      int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;
      vis5d_get_sizePRIME(dindex, &Nr, &Nc, &Nl,
                       &LowLev,  &WindNl, &WindLow);
      /* do it iteratively!! */
      /* north edge */
      left = 0.0;
      right = (float)(Nc-1);
      countdown = 20;
      done = 0;
      if (type==0){
         edge = 0.0;
      }
      else{
         edge = (float)(Nr-1);
      } 
      while (!done && countdown >= 0){
         mpoint = (right + left)/2.0;
         *col = mpoint;
         vis5d_rowcolPRIME_to_latlon( dindex, 0, 0, edge,
                            mpoint, &junk, &longitude);
         if (fabs(lon-longitude) < 0.001){
            done = 1;
         }
         else if (longitude > lon){
            countdown--;
            left = mpoint;
         }
         else{
            countdown--;
            right = mpoint;
         }
      }
   }
}
         
/* type = 0 west edge of box */
/* type = 1 east side of box */                                          
static void get_row_from_lat(int dindex, float lat, int type, float *row)
{
   float projargs[100];
   int proj;
   float latitude, latitude1, latitude2, junk;

   *row = 0.0;
   vis5d_get_dtx_projection( dindex, &proj, projargs);
   if (proj == PROJ_GENERIC || proj == PROJ_LINEAR ||
       proj == PROJ_CYLINDRICAL || proj == PROJ_SPHERICAL){
      int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;

      vis5d_get_sizePRIME(dindex, &Nr, &Nc, &Nl,
                          &LowLev,  &WindNl, &WindLow);

      vis5d_rowcolPRIME_to_latlon( dindex, 0, 0, 0.0, 0.0,
                                   &latitude1, &junk);
      vis5d_rowcolPRIME_to_latlon( dindex, 0, 0, Nr-1, 0.0,
                                   &latitude2, &junk);
      *row = ((((float)(Nr-1))/
             (latitude2-latitude1))*(lat-latitude1));
   }
   else{
      float num, top, bottom, countdown, done, mpoint, edge;
      int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;
      vis5d_get_sizePRIME(dindex, &Nr, &Nc, &Nl,
                       &LowLev,  &WindNl, &WindLow);
      /* do it iteratively!! */
      top = 0.0;
      bottom = (float)(Nr-1);
      countdown = 20;
      done = 0;
      if (type==0){
         edge = 0.0;
      }
      else{
         edge = (float)(Nc-1);
      }
      while (!done && countdown >= 0){
         mpoint = (top+bottom)/2.0;
         *row = mpoint;
         vis5d_rowcolPRIME_to_latlon( dindex, 0, 0, mpoint,
                            edge, &latitude, &junk);
         if (fabs(lat-latitude) < 0.001){
            done = 1;
         }
         else if (latitude > lat ){
            countdown--;
            top = mpoint;
         }
         else{
            countdown--;
            bottom = mpoint;
         }
      }
   }
}





 
/*
 * Move a vertical slice according to the cursor position.
 * Input:  curx, cury - cursor position in pixels
 *         time, var - which timestep and variable.
 *         r1,c1,r2,c2 - pointers to current slice position values
 *         corner - which corner is 'grabbed'
 * Output:  r1,c1,r2,c2 - possibly modified slice position.
 * Returned:  0 = no movement
 *            1 = slice was moved.
 */
static int move_vslice( int index, int curx, int cury,
                        int time, int var,
                        float *r1, float *c1, float *r2, float *c2,
                        int corner )
{
   float cx, cy;
   float b[3], db[3];
   float p[3], px, py;
   float nr, nc;
   int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;
   int vNr, vNc, vNl[MAXVARS], vLowLev[MAXVARS], vMaxNl, vMaxNlVar, vWindNl, vWindLow;

   /** INDEX = v5d_context index **/
   int dindex;

   vis5d_get_ctx_display_index( index, &dindex); 
   vis5d_get_sizePRIME(dindex, &Nr, &Nc, &Nl, &LowLev,  &WindNl, &WindLow);
   vis5d_get_size( index, &vNr, &vNc, vNl, vLowLev, &vMaxNl, &vMaxNlVar, &vWindNl, &vWindLow);

   nr = (float) (Nr-1);
   nc = (float) (Nc-1);

   cx = (float) curx;
   cy = (float) cury;
   vis5d_unproject( dindex, cx, cy, b, db );

   if (corner<4) {
      /** dragging slice by a corner **/
      float dist, neardist, neart, t, l;
      float newcol, newrow;
      int edge, nearedge;

      if (corner<2) {
         l = (float) (Nl-1+LowLev);
      }
      else {
         l = (float) LowLev;
      }

      /* find the point on one of the top or bottom box edges which */
      /* is closest to the cursor */
      neardist = 10000.0;
      for (edge=0;edge<4;edge++) {
         float r0, c0, r1, c1, a[3], da[3], aa[3];

         switch (edge) {
            case 0:
               /* north */
               r0 = 0.0;  c0 = 0.0;
               r1 = 0.0;  c1 = (float) (Nc-1);
               break;
            case 1:
               /* east */
               r0 = 0.0;  c0 = (float) (Nc-1);
               r1 = (float) (Nr-1);  c1 = c0;
               break;
            case 2:
               /* south */
               r0 = (float) (Nr-1);  c0 = 0.0;
               r1 = (float) (Nr-1);  c1 = (float) (Nc-1);
               break;
            case 3:
               /* west */
               r0 = 0.0;  c0 = 0.0;
               r1 = (float) (Nr-1);  c1 = 0.0;
               break;
         }
         vis5d_gridPRIME_to_xyzPRIME( dindex, time, var, r0, c0, l, &a[0], &a[1], &a[2] );
         vis5d_gridPRIME_to_xyzPRIME( dindex, time, var, r1, c1, l, &aa[0], &aa[1], &aa[2] );

         da[0] = aa[0] - a[0];
         da[1] = aa[1] - a[1];
         da[2] = aa[2] - a[2];
         t = point_nearest_line( a, da, b, db );
         p[0] = a[0] + da[0]*t;
         p[1] = a[1] + da[1]*t;
         p[2] = a[2] + da[2]*t;
         vis5d_project( dindex, p, &px, &py );
         dist = sqrt( (px-cx)*(px-cx) + (py-cy)*(py-cy) );
         if (dist==0.0)  return 0;
         if (dist<neardist) {
            neardist = dist;
            nearedge = edge;
            neart = CLAMP( t, 0.0, 1.0 );
         }
      }

      /* compute new row and column for corner of the slice */
      switch (nearedge) {
         case 0:
            /* north */
            newcol = nc*neart;
            newrow = 0.0;
            break;
         case 1:
            /* east */
            newcol = nc;
            newrow = nr*neart;
            break;
         case 2:
            /* south */
            newcol = nc*neart;
            newrow = nr;
            break;
         case 3:
            /* west */
            newcol = 0.0;
            newrow = nr*neart;
            break;
      }

      if (corner%2==0) {
         /* make sure the corners won't be too close */
         if (DISTANCE( *r2, *c2, newrow, newcol) > 2.0) {
            *r1 = newrow;
            *c1 = newcol;
         }
      }
      else {
         /* make sure the corners won't be too close */
         if (DISTANCE( *r1, *c1, newrow, newcol) > 2.0) {
            *r2 = newrow;
            *c2 = newcol;
         }
      }
      return 1;

   }
   else {
      /** dragging slice by midpoint of edge **/

      float prow, pcol, plev, drow, dcol;
      float newr1, newc1, newr2, newc2;
      float x, y, t;


      if (corner==4) {
         /* find intersection 'p', of 'b' with top plane of box */
         vis5d_gridPRIME_to_xyzPRIME(dindex, time, var, 1.0, 1.0, (float) (Nl-1+LowLev),
                           &x, &y, &t);
         t = (t - b[2]) / db[2];
      }
      else {
         /* find intersection 'p', of 'b' with bottom plane of box */
         vis5d_gridPRIME_to_xyzPRIME(dindex, time, var, 1.0, 1.0, (float) LowLev, &x, &y, &t);
         t = (t - b[2]) / db[2];
      }
      p[0] = b[0] + t*db[0];
      p[1] = b[1] + t*db[1];
      p[2] = b[2] + t*db[2];

      /* convert p to row and columns */
      vis5d_xyzPRIME_to_gridPRIME( dindex, time, var, p[0], p[1], p[2], &prow, &pcol, &plev );

      /* let d = 2-D vector parallel to current slice */
      drow = *r2 - *r1;
      dcol = *c2 - *c1;

      if (drow==0.0) {
         /* east/west slice */
         newc1 = *c1;
         newc2 = *c2;
         newr1 = newr2 = prow;
      }
      else if (dcol==0.0) {
         /* north/south slice */
         newr1 = *r1;
         newr2 = *r2;
         newc1 = newc2 = pcol;
      }
      else {
         /* diagonal slice */
         /* recompute r1,c1, r2,c2 such that they fall on the line */
         /* defined by point (prow,pcol) and direction (drow,dcol) */
         t = -prow / drow;
         newr1 = 0.0;
         newc1 = pcol + dcol*t;

         t = (nr-prow) / drow;
         newr2 = Nr-1.0;
         newc2 = pcol + dcol*t;

         /* trim */
         if (newc1<0.0) {
            t = -pcol / dcol;
            newr1 = prow + drow*t;
            newc1 = 0.0;
         }
         if (newc1>(float)(Nc-1)) {
            t = (nc-pcol) / dcol;
            newr1 = prow + drow*t;
            newc1 = (float) (Nc-1);
         }
         if (newc2<0.0) {
            t = -pcol / dcol;
            newr2 = prow + drow*t;
            newc2 = 0.0;
         }
         if (newc2>(float)(Nc-1)) {
            t = (nc-pcol) / dcol;
            newr2 = prow + drow*t;
            newc2 = (float) (Nc-1);
         }

         /* MJK 12.15.98 begin */
         drow = *r1 - *r2;
         if (drow < 0.0) drow = -drow;
         dcol = *c1 - *c2;
         if (dcol < 0.0) dcol = -dcol;
         if (drow >= dcol)
         {
             if (((*r1 < *r2) && (newr1 > newr2)) ||
                 ((*r1 > *r2) && (newr1 < newr2))) drow = -1.0;
         }
         else
         {
             if (((*c1 < *c2) && (newc1 > newc2)) ||
                 ((*c1 > *c2) && (newc1 < newc2))) drow = -1.0;
         }
         if (drow < 0.0)
         {
             x = newr1, newr1 = newr2, newr2 = x;
             y = newc1, newc1 = newc2, newc2 = y;
         }
         /* MJK 12.15.98 end */
      }

      newc1 = CLAMP( newc1, 0.0, nc );
      newc2 = CLAMP( newc2, 0.0, nc );
      newr1 = CLAMP( newr1, 0.0, nr );
      newr2 = CLAMP( newr2, 0.0, nr );
#ifdef LEAVEOUT
      if ((newc1==0.0 && newc2==0.0) || (newc1==nc && newc2==nc)) {
         if (newr1<nr/2.0)
           newr1 = 0.0;
         else
           newr1 = nr;
         newr2 = nr - newr1;
      }
      if ((newr1==0.0 && newr2==0.0) || (newr1==nc && newr2==nc)) {
         if (newc1<nc/2.0)
           newc1 = 0.0;
         else
           newc1 = nc;
         newc2 = nc - newc1;
      }
#endif
      /* make sure the corners won't be too close */
      if (DISTANCE( newr1, newc1, newr2, newc2 ) > 2.0) {
         *r1 = newr1;
         *c1 = newc1;
         *r2 = newr2;
         *c2 = newc2;
      }
      return 1;
   }
}



/*** move_slice ****************************************************
   Move the position of a contour.  This function is called to
   respond to a user action.
   Input:  curx, cury - the cursor position in pixels (0,0) = upper-left.
           dindex     - display context index
           vindex     - vis5d context index
           contype    - the type of contour to move (HSLICE, CVSLICE,etc)
           time, var  - which display timestep and ctx variable 
           corner     - the corner of the contour to use as reference.
           recalc     - non-zero if slice should be recalculated now.
**********************************************************************/
static void move_slice( int dindex, int vindex, int curx, int cury,
                        int contype, int time, int var, int corner,
                        int recalc , int grp_index)
{
   GuiContext gtx = get_gui_gtx(dindex);
   float interval0, low0, high0, level;
   float interval, low, high;
   float row0, col0, row1, col1;
   float density, scale;
   float lat0, lon0, lat1, lon1, hgt;
   float r0, c0, c1, r1;
   float junk, junk1, junk2, junk3, lev;
   int Uvar, Vvar, Wvar, Uvar2, Vvar2, Wvar2, TrajU, TrajV, TrajW;
   int Uvarowner, Vvarowner, Wvarowner, Uvar2owner, Vvar2owner, Wvar2owner;
   int TrajUowner, TrajVowner, TrajWowner;
   int it, numtimes, dpynumtimes, curtime, dpycurtime;
   int dhowmany, dwhichones[VIS5D_MAX_CONTEXTS];
   int chowmany, cwhichones[VIS5D_MAX_CONTEXTS];
   int dyo, cyo, good;
   char aname[20];

   vis5d_get_ctx_numtimes( vindex, &numtimes );
   vis5d_get_dtx_numtimes( dindex, &dpynumtimes );
   vis5d_get_dtx_timestep( dindex, &dpycurtime );
   vis5d_get_ctx_timestep( vindex, &curtime);


   if (contype==VIS5D_HSLICE) {
      vis5d_get_hslice(vindex, var, &interval, &low, &high, &level);
      if (move_hslice( vindex, curx, cury, time, var, &level, corner ) && recalc) {
         vis5d_set_hslice(vindex, var, interval, low, high, level);
         
         /* MJK 12.15.98 */
         update_vpos_slider (vindex, contype, var, level);


         for (it=0;it<numtimes;it++) {
            vis5d_make_hslice(vindex, it, var, it==curtime);
         }
         update_linked_vpos_sliders( vindex, dindex, contype, var, level);
         move_linked_hslices( vindex, dindex, VIS5D_HSLICE, var, level);
      }
   }
   else if (contype==VIS5D_CHSLICE) {
      vis5d_get_chslice(vindex, var, &level);
      if (move_hslice( vindex, curx, cury, time, var, &level, corner ) && recalc) {
         vis5d_set_chslice(vindex, var, level);

         /* MJK 12.15.98 */
         update_vpos_slider (vindex, contype, var, level);


         for (it=0;it<numtimes;it++) {
            vis5d_make_chslice(vindex, it, var, it==curtime);
         }
         update_linked_vpos_sliders( vindex, dindex, contype, var, level);
         move_linked_hslices( vindex, dindex, VIS5D_CHSLICE, var, level);
      }
   }
   else if (contype==VIS5D_VSLICE) {
      vis5d_get_vslice(vindex, var, &interval, &low, &high, &row0, &col0, &row1, &col1);
      if (move_vslice( vindex, curx, cury, time, var, &row0, &col0, &row1, &col1, corner)
          && recalc) {
         vis5d_set_vslice(vindex, var, interval, low, high, row0, col0, row1, col1);
         for (it=0;it<numtimes;it++) {
            vis5d_make_vslice(vindex, it, var, it==curtime);
         }
         move_linked_vslices( vindex, dindex, VIS5D_VSLICE, var, corner,
                              row0, col0, row1, col1);
      }         
   }
   else if (contype==VIS5D_CVSLICE) {
      vis5d_get_cvslice(vindex, var, &row0, &col0, &row1, &col1);
      if (move_vslice( vindex, curx, cury, time, var, &row0, &col0, &row1, &col1, corner)
          && recalc) {
         vis5d_set_cvslice(vindex, var, row0, col0, row1, col1);
         for (it=0;it<numtimes;it++) {
            vis5d_make_cvslice(vindex, it, var, it==curtime);
         }
         move_linked_vslices( vindex, dindex, VIS5D_CVSLICE, var, corner,
                              row0, col0, row1, col1);
      }
   }
   else if (contype==VIS5D_HWIND) {
      int uvar;
      vis5d_get_wind_vars(dindex, &Uvarowner, &Uvar, &Vvarowner, &Vvar, &Wvarowner, &Wvar,
                          &Uvar2owner, &Uvar2, &Vvar2owner, &Vvar2, &Wvar2owner, 
                          &Wvar2, &TrajUowner, &TrajU, &TrajVowner, &TrajV, &TrajWowner, &TrajW);

      uvar = var == 0 ? Uvar : Uvar2;
      vis5d_get_hwindslice(dindex, var, &density, &scale, &level);
      if (move_hslice( vindex, curx, cury, time, uvar, &level, corner) && recalc) {
         vis5d_set_hwindslice(dindex, var, density, scale, level);

         /* MJK 12.15.98 */
         update_vpos_slider (vindex, contype, var, level);

         for (it=0;it<dpynumtimes;it++) {
            vis5d_make_hwindslice(dindex, it, var, it==dpycurtime);
         }
         update_linked_vpos_sliders( vindex, dindex, contype, var, level);
         move_linked_hslices( vindex, dindex, VIS5D_HWIND, var, level);
      }
   }
   else if (contype==VIS5D_VWIND) {
      int uvar;
      vis5d_get_wind_vars(dindex, &Uvarowner, &Uvar, &Vvarowner, &Vvar, &Wvarowner, &Wvar,
                          &Uvar2owner, &Uvar2, &Vvar2owner, &Vvar2, &Wvar2owner, 
                          &Wvar2, &TrajUowner, &TrajU, &TrajVowner, &TrajV, &TrajWowner, &TrajW);

      uvar = var == 0 ? Uvar : Uvar2;
      vis5d_get_vwindslice(dindex, var, &density, &scale, &row0, &col0, &row1, &col1);
      if (move_vslice( vindex, curx, cury, time, uvar,
                       &row0, &col0, &row1, &col1, corner) && recalc) {
         vis5d_set_vwindslice(dindex, var, density, scale, row0, col0, row1, col1);
         for (it=0;it<dpynumtimes;it++) {
            vis5d_make_vwindslice(dindex, it, var, it==dpycurtime);
         }
         move_linked_vslices( vindex, dindex, VIS5D_VWIND, var, corner,        
                              row0, col0, row1, col1);   
      }
   }
   else if (contype==VIS5D_HSTREAM) {
      int uvar;
      vis5d_get_wind_vars(dindex, &Uvarowner, &Uvar, &Vvarowner, &Vvar, &Wvarowner, &Wvar,
                          &Uvar2owner, &Uvar2, &Vvar2owner, &Vvar2, &Wvar2owner, 
                          &Wvar2, &TrajUowner, &TrajU, &TrajVowner, &TrajV, &TrajWowner, &TrajW);

      uvar = var == 0 ? Uvar : Uvar2;
      vis5d_get_hstreamslice(dindex, var, &density, &level);
      if (move_hslice( vindex, curx, cury, time, uvar, &level, corner) && recalc) {
         vis5d_set_hstreamslice(dindex, var, density, level);

         /* MJK 12.15.98 */         
         update_vpos_slider (vindex, contype, var, level);         

         for (it=0;it<dpynumtimes;it++) {
            vis5d_make_hstreamslice(dindex, it, var, it==dpycurtime);
         }
         update_linked_vpos_sliders( vindex, dindex, contype, var, level);
         move_linked_hslices( vindex, dindex, VIS5D_HWIND, var, level);
      }
   }
   else if (contype==VIS5D_VSTREAM) {
      int uvar;
      vis5d_get_wind_vars(dindex, &Uvarowner, &Uvar, &Vvarowner, &Vvar, &Wvarowner, &Wvar,
                          &Uvar2owner, &Uvar2, &Vvar2owner, &Vvar2, &Wvar2owner, 
                          &Wvar2, &TrajUowner, &TrajU, &TrajVowner, &TrajV, &TrajWowner, &TrajW);

      uvar = var == 0 ? Uvar : Uvar2;
      vis5d_get_vstreamslice(dindex, var, &density, &row0, &col0, &row1, &col1);
      if (move_vslice( vindex, curx, cury, time, uvar,
                       &row0, &col0, &row1, &col1, corner) && recalc) {
         vis5d_set_vstreamslice(dindex, var, density, row0, col0, row1, col1);
         for (it=0;it<dpynumtimes;it++) {
            vis5d_make_vstreamslice(dindex, it, var, it==dpycurtime);
         }
         move_linked_vslices( vindex, dindex, VIS5D_VSTREAM, var, corner,                   
                              row0, col0, row1, col1);
      }
   } 
   if (grp_index >0){
      vis5d_invalidate_grp_frames(grp_index);
   }
   else{
      vis5d_invalidate_dtx_frames(dindex);
   }
}

static void move_plane( int index, int curx, int cury, 
                        int planetype, int planenum, int corner)
{
   float level, r1, c1, r2, c2;
   int current;
   /* horiz plane */
   if (planetype==0){
      vis5d_get_hclip( index, planenum, &level);
      vis5d_get_clip_mode( index, planenum, &current);
      if (move_hclip( index, planenum, curx, cury, &level, corner) && current){
         vis5d_set_hclip( index, planenum, level);
      }
   }
   else if (planetype==1){
      vis5d_get_vclip( index, planenum, &r1, &c1, &r2, &c2);
      vis5d_get_clip_mode( index, planenum+2, &current);
      if (move_vclip( index, planenum, curx, cury, &r1, &c1, &r2, &c2, corner) && current){
         vis5d_set_vclip( index, planenum, r1, c1, r2, c2);
      }
   }
   vis5d_invalidate_dtx_frames(index);
}           


/*
 * This function is called whenever an input event occurs in the
 * main viewing window while in 'slice moving mode'.
 * Input:  event - the X event
 *         time - current timestep
 * Return:  1 = event was used to move a slice
 *          0 = event was not used
 */
int slice_event( int index, XEvent event, int time )
{
   int result;           /* value returned */
   static int flag;      /* has a contour been selected? */
   static int contype;    /* the type of contour selected */
   static int contour;    /* the contour selectd */
   static int corner;     /* the contour corner selected */
   static int otherindex = -1; /* the index of the vis5d_ctx */
   static int planetype;
   static int planenum;
   int clipmode;
   int recalc;

   GuiContext gtx = get_gui_gtx(index);
   /* INDEX = display index */

   /* MJK 12.15.98 begin */
   static float prev_r1, prev_c1, prev_r2, prev_c2;


   if ((flag) && (contype == VIS5D_VSLICE))
   {
      float     cur_r1, cur_c1, cur_r2, cur_c2, x1, x2, x3;

      vis5d_get_vslice (otherindex, contour, &x1, &x2, &x3,
                        &cur_r1, &cur_c1, &cur_r2, &cur_c2);
      if ((cur_r1 != prev_r1) || (cur_c1 != prev_c1) ||
          (cur_r2 != prev_r2) || (cur_c2 != prev_c2))
      {
         corner = (corner == 0) ? 1 : (corner == 1) ? 0 : corner;
      }
   }
   /* MJK 12.15.98 end */



   result = 0;
   clipmode = vis5d_graphics_mode(index, VIS5D_CLIP, VIS5D_GET);

   if (event.type==ButtonPress && event.xbutton.button==Button3) {
      /* The right mouse button has been pressed. */
      /* Determine which slice is closest to the current cursor position. */
      if (clipmode){
         flag = find_nearest_clip_slice( index, event.xbutton.x, event.xbutton.y,
                                         &planetype, &planenum, &corner);
         otherindex = 1;
      }
      else{
         flag = find_nearest_slice(index, event.xbutton.x, event.xbutton.y, time,
                                    &contype, &contour, &corner, &otherindex );
      }
      if (otherindex == -1){
         flag = -1;
         result = -1;
      }
      else {
         result = 1;
      }
   }

   if ( (event.type==MotionNotify || event.type==ButtonPress)
       /*&& event.xbutton.button==Button3*/ && flag==1) {
      /* The cursor has been moved while the right mouse button */
      /* is pressed.  Update the position of the slice. */
#ifdef SINGLE_TASK
      /* If we don't have parallelism, recompute only if not animating */
      recalc = (gtx->GoTime==0) ? 1 : 0;
#else
      /* If we have parallelism, recompute whether animating or not */
      recalc = 1;
#endif

      if (clipmode){
         move_plane( index, event.xbutton.x, event.xbutton.y,
                     planetype, planenum, corner);
      }
      else{
         move_slice( index, otherindex, event.xbutton.x, event.xbutton.y,
                  contype, time, contour, corner, recalc, gtx->group_index );
      }

      /* MJK 12.15.98 begin */
      if (contype == VIS5D_VSLICE)
      {
         float  x1, x2, x3;

         vis5d_get_vslice (otherindex, contour, &x1, &x2, &x3,
                           &prev_r1, &prev_c1, &prev_r2, &prev_c2);
      }
      /* MJK 12.15.98 end */


      result = 1;
   }

   if (event.type==ButtonRelease && event.xbutton.button==Button3 &&
       otherindex >= 0) {
      /* The right mouse button has been released. */
      if (clipmode){
         move_plane( index, event.xbutton.x, event.xbutton.y,
                     planetype, planenum, corner);
      }
      else{
         move_slice( index, otherindex, event.xbutton.x, event.xbutton.y,
                     contype, time, contour, corner, 1, gtx->group_index );
      }
      flag = 0;
      result = 1;
   }

   return result;
}




syntax highlighted by Code2HTML, v. 0.9.1