/*  cursor.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"


#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <math.h>
#include "api.h"
#include "gui.h"

#define DOT( a, b )       (a[0]*b[0] + a[1]*b[1] + a[2]*b[2])

#define EPS 0.000001
  

/*
 * Verify that the cursor is in bounds.  If not, put it in bounds.
 */
void check_cursor_in_bounds( int index) /* DISPLAY INDEX */
{
  float row, col, lev;
  float x, y, z;
  int CoordFlag;
  int Nr, Nc, Nl, LowLev, MaxNlVar, WindNl, WindLow;

  vis5d_get_cursor(index, &x, &y, &z);

  vis5d_xyzPRIME_to_gridPRIME(index, -1, -1, x, y, z, &row, &col, &lev);

  CoordFlag = vis5d_graphics_mode(index, VIS5D_GRID_COORDS, VIS5D_GET);

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

  /* if cursor position is being printed in grid coordinates, snap */
  /* cursor location to discrete grid positions */
  if (CoordFlag==1) {
    row = (float) (int) (row+0.5);
    col = (float) (int) (col+0.5);
    lev = (float) (int) (lev+0.5);
  }

  if (row<0.0)  row = 0.0;
  else if (row>Nr-1)  row = Nr-1;
  if (col<0.0)  col = 0.0;
  else if (col>Nc-1)  col = Nc-1;
  if (lev<0.0)  lev = 0.0;
  else if (lev>Nl-1)  lev = Nl-1;

  vis5d_gridPRIME_to_xyzPRIME(index, -1, -1, row, col, lev, &x, &y, &z);

  vis5d_set_cursor(index, x, y, z);
  return;
}




/*** cursor_event ********************************************************
   This function receives an input event and tries to use it to update
   the cursor's position.
   Input: event - a copy of the XEvent.
   Returned:  0 = event not used.
              1 = cursor moved.
              2 = middle button pressed.
*************************************************************************/
int cursor_event( int index, XEvent event )
{
   GuiContext gtx = get_gui_gtx(index);
   int dyo, dhowmany, dwhichones[VIS5D_MAX_CONTEXTS];
   float lat, lon, hgt;
   static int pressed3 = 0;
   int result;           /* value returned */
   static int sounding = 0;
   static float zcursor;
   result = 0;

   if (event.type==ButtonPress && event.xbutton.button==Button3) {
      /* Right mouse button has been pressed. */
      pressed3 = 1;
      result = 1;
      sounding = vis5d_graphics_mode(index, VIS5D_SOUND, VIS5D_GET);
      if (sounding)  {
         float cx,cy,s[3], ds[3], a[3], da[3];
         float dsds, dads, dada, sds, ads, sda, ada, t; 
         cx = (float) event.xbutton.x;
         cy = (float) event.xbutton.y;
     
         /* unproject (cx,cy) to line in 3-D  */
          vis5d_unproject(index, cx, cy, a, da);
          vis5d_get_cursor(index, &s[0], &s[1], &s[2]);
          ds[0] = 0;
          ds[1] = 0;
          ds[2] = 1;  
        /* Find the closest point on line s, sa to line a, da */
          dsds = DOT(ds, ds);
          dads = DOT(da, ds);
          dada = DOT(da, da); 
          sds  = DOT(s,  ds);
          ads  = DOT(a,  ds);
          sda  = DOT(s,  da);
          ada  = DOT(a,  da);
          if ((dada*-dsds)-(-dads*dads)==0.000) zcursor = 0;
          else{  
             t = ((dada*(sds-ads))-(dads*(sda-ada)))/((dada*-dsds)-(-dads*dads)); 
             zcursor = s[2] + t*ds[2];
          } 
      } 
       
   }
   else  if (event.type==ButtonPress && event.xbutton.button==Button2) {
      /* middle button pressed */
      result = 2;
   }
   else if (event.type==ButtonRelease && event.xbutton.button==Button3) {
      /* Right mouse button has been released. */
      pressed3 = 0;
      result = 1;
      sounding = 0;
   }

   if ( (event.type==MotionNotify || event.type==ButtonPress) && pressed3 ) {
      float cx, cy, s[3], ds[3], a[3], da[3], dc[3], d;
      float t, x, y, z,tzmax, tzmin, zmin, zmax;
     


      sounding = vis5d_graphics_mode(index, VIS5D_SOUND, VIS5D_GET);
      if (sounding) {
         float dads, ads, sds;
         int Nr, Nc, Nl[MAXVARS], LowLev[MAXVARS], MaxNl, MaxNlVar, WindNl, WindLow;         
         float xmin, xmax, ymin, ymax, zmin, zmax;

         cx = (float) event.xbutton.x;
         cy = (float) event.xbutton.y;
         vis5d_unproject(index, cx, cy, a, da);
      
         /* intersect line a, da with plane z = zcursor  */
         vis5d_get_box_bounds( index, &xmin, &xmax, &ymin,
                               &ymax, &zmin, &zmax);
         if (zcursor < zmin) zcursor = zmin;
         if (zcursor > zmax) zcursor = zmax; 
         if (da[2] == 0.0000) t= 10.0;
         else  t = (zcursor-a[2])/da[2]; 
         x = a[0] + da[0]*t;
         y = a[1] + da[1]*t;
         z = a[2] + da[2]*t;

         if (gtx->group_index > 0){
            vis5d_xyzPRIME_to_geo(index, 0, 0, x, y, z, &lat, &lon, &hgt);
            vis5d_get_num_of_dtxs_in_group( gtx->group_index, &dhowmany, dwhichones);
            for (dyo = 0; dyo < dhowmany; dyo++){
               vis5d_geo_to_xyzPRIME( dwhichones[dyo], 0, 0, lat, lon, hgt,
                                      &x, &y, &z);
               vis5d_set_cursor(dwhichones[dyo], x, y, z);
               check_cursor_in_bounds(dwhichones[dyo]);
            }
         }
         else{ 
            vis5d_set_cursor(index, x, y, z);
            check_cursor_in_bounds(index);
         }
         result = 1;
      }

      
      else{ 
         /* move 3-D cursor */
 
         cx = (float) event.xbutton.x;
         cy = (float) event.xbutton.y;
   
         /* unproject (cx,cy) to line in 3-D */
         vis5d_unproject(index, cx, cy, a, da);
         /* vector from a to cursor pos */
         vis5d_get_cursor(index, &x, &y, &z);
         dc[0] = x - a[0];
         dc[1] = y - a[1];
         dc[2] = z - a[2];
   
         /* find component of dc on da (da already has length 1) */
         d = DOT( dc, da );
   
         /* calculate position of cursor along 'a' */
         x = a[0] + da[0]*d;
         y = a[1] + da[1]*d;
         z = a[2] + da[2]*d;
         if (gtx->group_index > 0){
            vis5d_xyzPRIME_to_geo(index, 0, 0, x, y, z, &lat, &lon, &hgt);
            vis5d_get_num_of_dtxs_in_group( gtx->group_index, &dhowmany, dwhichones);
            for (dyo = 0; dyo < dhowmany; dyo++){
               vis5d_geo_to_xyzPRIME( dwhichones[dyo], 0, 0, lat, lon, hgt,
                                      &x, &y, &z);
               vis5d_set_cursor(dwhichones[dyo], x, y, z);
               check_cursor_in_bounds(dwhichones[dyo]);
            }
         }
         else{
            vis5d_set_cursor(index, x, y, z);
            check_cursor_in_bounds(index);
         }
         result = 1;
      }      
   }

   if (event.type==KeyPress) {
      char buff[10];
      KeySym key;
      XComposeStatus compose;
      int shift_flag;
      float x, y, z;
      float row, col, lev;
      float step;

      XLookupString( &event.xkey, buff, 10, &key, &compose );
      shift_flag = event.xkey.state & ShiftMask;

      vis5d_get_cursor(index, &x, &y, &z);
      vis5d_xyzPRIME_to_gridPRIME( index, 0, 0, x, y, z, &row, &col, &lev );

      if (vis5d_graphics_mode(index, VIS5D_GRID_COORDS, VIS5D_GET)) {
         /* move by whole grid units */
         step = 1.0;
      }
      else {
         /* move by 1/10 grid units */
         step = 0.1;
      }

      switch (key) {
         case XK_Left:
            col -= step;
            result = 1;
            break;
         case XK_Right:
            col += step;
            result = 1;
            break;
         case XK_Up:
            if (shift_flag) {
               lev += step;
            }
            else {
               row -= step;
            }
            result = 1;
            break;
         case XK_Down:
            if (shift_flag) {
               lev -= step;
            }
            else {
               row += step;
            }
            result = 1;
            break;
         default:
            result = 0;
      }

      if (result==1) {
         vis5d_gridPRIME_to_xyzPRIME( index, 0, 0, row, col, lev, &x, &y, &z );
         if (gtx->group_index > 0){
            vis5d_xyzPRIME_to_geo(index, 0, 0, x, y, z, &lat, &lon, &hgt);
            vis5d_get_num_of_dtxs_in_group( gtx->group_index, &dhowmany, dwhichones);
            for (dyo = 0; dyo < dhowmany; dyo++){
               vis5d_geo_to_xyzPRIME( dwhichones[dyo], 0, 0, lat, lon, hgt,
                                      &x, &y, &z);
               vis5d_set_cursor(dwhichones[dyo], x, y, z);
               check_cursor_in_bounds(dwhichones[dyo]);
            }
         }
         else{
            vis5d_set_cursor(index, x, y, z);
            check_cursor_in_bounds(index);
         }
      }
   }

   return result;
}


syntax highlighted by Code2HTML, v. 0.9.1