Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

Spaceball.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr                                                                       
00003  *cr            (C) Copyright 1995-2019 The Board of Trustees of the           
00004  *cr                        University of Illinois                       
00005  *cr                         All Rights Reserved                        
00006  *cr                                                                   
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  * RCS INFORMATION:
00011  *
00012  *      $RCSfile: Spaceball.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.64 $       $Date: 2020/12/13 07:41:55 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *
00019  * The Spaceball UI object, which maintains the current state of the 
00020  * spaceball.  This code uses John Stone's spaceball library when
00021  * VMDLIBSBALL is defined, or the Spaceware library when VMDSPACEWARE 
00022  * is defined.
00023  *
00024  ***************************************************************************
00025  * TODO list:
00026  *   1) Mac code needs to either flush queued events, force-read them all
00027  *      at every screen redraw, or use an independent thread to process them
00028  *      so that the event queue never lags the screen draws.
00029  *   2) The 'animate' mode needs to use the wall-clock time since the last
00030  *      event to normalize the applied force to account for complex 
00031  *      reps slowing down the update rate.  With the exponential speed
00032  *      the animate step size can get out of hand far too easily.  The max
00033  *      step size allowed should be capped, preferably to a user-configurable
00034  *      limit.  A default step size limit of something like 50 would be a 
00035  *      good compromise. (Partially done)
00036  *   3) Orthographic mode confuses beginners because the scene doesn't
00037  *      scale when they pull objects towards the camera.  They expect 
00038  *      behavior more like what one gets with perspective.  We may want to
00039  *      provide a default behavior of scaling when orthographic projection
00040  *      is active and the user pulls towards them. (Partially done)
00041  *   4) The spaceball mode switching is too complex to be handled well
00042  *      by a two button device.  We need to map these functions to 
00043  *      keyboard keys to make it easier to use.  It would be even better if
00044  *      we briefly displayed status messages on the screen for a couple of
00045  *      seconds when modes are changed since one might not be able to see
00046  *      the text console.  This would be useful for any mode changes that
00047  *      affect the VMD control/input state that don't have some other
00048  *      visual/audio cue to observe. (e.g. the mouse pointer changing
00049  *      when mouse modes are altered).
00050  *
00051  ***************************************************************************/
00052 
00053 #include "Spaceball.h"
00054 #include "DisplayDevice.h"
00055 #include "TextEvent.h"
00056 #include "CommandQueue.h"
00057 #include "Inform.h"
00058 #include "PickList.h"
00059 #include "Animation.h"
00060 #include "VMDApp.h"
00061 #include "math.h"
00062 #include "stdlib.h" // for getenv(), abs() etc.
00063 
00064 //
00065 // 3DConnexion MacOS X driver API
00066 //
00067 #if defined(VMDTDCONNEXION) && defined(__APPLE__)
00068 #include <unistd.h>
00069 #include <Carbon/Carbon.h>
00070 #include <stdio.h>
00071 #include <stdlib.h>
00072 
00073 // 3dxware API header
00074 #include "3DconnexionClient/ConnexionClientAPI.h"
00075 
00076 extern "C" {
00077 extern OSErr InstallConnexionHandlers(ConnexionMessageHandlerProc messageHandler, ConnexionAddedHandlerProc addedHandler, ConnexionRemovedHandlerProc removedHandler) __attribute__((weak_import));
00078 }
00079 
00080 // Pascal string name of the application binary used for window 
00081 // focus handling when we don't provide an application bundle key
00082 // at callback registration time
00083 #if defined(ARCH_MACOSX)
00084 static UInt8 *executablename = (UInt8 *) "\pvmd_MACOSX";
00085 #elif defined(ARCH_MACOSXARM64)
00086 static UInt8 *executablename = (UInt8 *) "\pvmd_MACOSXARM64";
00087 #elif defined(ARCH_MACOSXX86)
00088 static UInt8 *executablename = (UInt8 *) "\pvmd_MACOSXX86";
00089 #elif defined(ARCH_MACOSXX86_64)
00090 static UInt8 *executablename = (UInt8 *) "\pvmd_MACOSXX86_64";
00091 #else
00092 #error
00093 #endif
00094 
00095 typedef struct {
00096   int enabled;   // flag indicating whether we're live or not
00097   UInt16 client; // 3DConnexion API client used for focus handling
00098   int tx;
00099   int ty;
00100   int tz;
00101   int rx; 
00102   int ry;
00103   int rz;
00104   int buttons;
00105   int eventcount;
00106 } tdx_data;
00107 
00108 // global event data structure
00109 static tdx_data tdxevent;
00110 
00111 // event handler proc
00112 // XXX note that the default single-threaded implementation
00113 //     described in the 3DConnexion documentation leaves a lot
00114 //     to be desired.  In reality, we realistically want a multithreaded
00115 //     implementation, otherwise the events queue up and get way behind
00116 //     when we draw a complex molecule.  In order to handle the device
00117 //     events separately, we can install a special event handler in 
00118 //     a second thread and have it store event data to a shared memory area.
00119 static void tdx_msghandler(io_connect_t connection, 
00120                            natural_t msgtype, void *msgarg) {
00121   ConnexionDeviceState *state = NULL;
00122   switch (msgtype) {
00123     case kConnexionMsgDeviceState:
00124       state = (ConnexionDeviceState *) msgarg;
00125       if (state->client == tdxevent.client) {
00126         switch (state->command) {
00127           case kConnexionCmdHandleAxis:
00128             tdxevent.tx +=  state->axis[0];
00129             tdxevent.ty += -state->axis[2];
00130             tdxevent.tz += -state->axis[1];
00131             tdxevent.rx +=  state->axis[3];
00132             tdxevent.ry += -state->axis[5];
00133             tdxevent.rz += -state->axis[4];
00134             tdxevent.eventcount++;
00135             break;
00136 
00137           case kConnexionCmdHandleButtons:
00138             tdxevent.buttons = state->buttons;
00139             tdxevent.eventcount++;
00140             break;
00141         }
00142       }
00143       break;
00144 
00145     default:
00146 //      printf("Unknown message type\n");
00147       break;
00148   }
00149 }
00150 
00151 static void tdx_clear() {
00152   memset(&tdxevent, 0, sizeof(tdxevent));
00153 }
00154 
00155 static int tdx_enable() {
00156   UInt16 clientID;
00157 
00158   if (InstallConnexionHandlers == NULL) {
00159     msgInfo << "No 3DConnexion driver on this system." << sendmsg;
00160     return -1;
00161   }
00162   OSErr result = InstallConnexionHandlers(tdx_msghandler, 0L, 0L);
00163   if (result != noErr) {
00164     msgInfo << "Unable to register with 3DConnexion driver." << sendmsg;
00165     return -1;
00166   }
00167 
00168 #if 1
00169   // only respond to all events when we have focus
00170   clientID = RegisterConnexionClient(0, executablename,
00171                kConnexionClientModeTakeOver, kConnexionMaskAll);
00172 #else
00173   // respond to all events whether we have focus or not
00174   clientID = RegisterConnexionClient(kConnexionClientWildcard, NULL,
00175                kConnexionClientModeTakeOver, kConnexionMaskAll);
00176 #endif
00177 
00178   tdxevent.enabled = 1;
00179   tdxevent.client = clientID;
00180 
00181   return 0;
00182 }
00183 
00184 static int tdx_detach() {
00185   if (tdxevent.enabled) {
00186     UnregisterConnexionClient(tdxevent.client);
00187     CleanupConnexionHandlers();
00188   }
00189   tdx_clear();
00190 }
00191 
00192 int tdx_getstatus(int &tx, int &ty, int &tz, int &rx, int &ry, int &rz, int &buttons) {
00193   int eventcount = tdxevent.eventcount;
00194 
00195   tx = tdxevent.tx; 
00196   ty = tdxevent.ty; 
00197   tz = tdxevent.tz; 
00198   rx = tdxevent.rx; 
00199   ry = tdxevent.ry; 
00200   rz = tdxevent.rz; 
00201   buttons = tdxevent.buttons;
00202   
00203   tdxevent.tx = 0;
00204   tdxevent.ty = 0;
00205   tdxevent.tz = 0;
00206   tdxevent.rx = 0;
00207   tdxevent.ry = 0;
00208   tdxevent.rz = 0;
00209   tdxevent.eventcount = 0;
00210   
00211   return eventcount;
00212 }
00213 
00214 #endif
00215 
00216 
00217 
00218 // constructor
00219 Spaceball::Spaceball(VMDApp *vmdapp)
00220         : UIObject(vmdapp) {
00221 
00222 #if defined(VMDTDCONNEXION) && defined(__APPLE__)
00223   // Enable input from MacOS X 3DConnexion API
00224   tdx_clear();
00225   if (tdx_enable() == 0)
00226     msgInfo << "3DConnexion SpaceNavigator enabled." << sendmsg;
00227 #endif
00228 
00229 #if defined(VMDLIBSBALL) && !defined(VMDSPACEWARE)
00230   sball=NULL; // zero it out to begin with
00231   if (getenv("VMDSPACEBALLPORT") != NULL) {
00232     msgInfo << "Opening Spaceball (direct I/O) on port: " 
00233             << getenv("VMDSPACEBALLPORT") << sendmsg;
00234     sball = sball_open(getenv("VMDSPACEBALLPORT"));
00235     if (sball == NULL) 
00236       msgErr << "Failed to open Spaceball direct I/O serial port, device disabled." 
00237              << sendmsg; 
00238   }
00239 #endif
00240 
00241   buttonDown = 0;
00242 
00243   reset();
00244 }
00245 
00246 
00247 // destructor
00248 Spaceball::~Spaceball(void) {
00249 #if defined(VMDTDCONNEXION) && defined(__APPLE__)
00250   // Disable input from MacOS X 3DConnextion API
00251   tdx_detach();
00252 #endif
00253 
00254 #if defined(VMDLIBSBALL) && !defined(VMDSPACEWARE)
00255   if (sball != NULL)
00256     sball_close(sball);
00257 #endif
00258 }
00259 
00260 
00262    
00263 // reset the spaceball to original settings
00264 void Spaceball::reset(void) {
00265   // set the default motion mode and initialize button state
00266   move_mode(NORMAL);
00267 
00268   // set global spaceball sensitivity within VMD
00269   // (this has no effect on underlying driver sensitivity settings)
00270   set_sensitivity(1.0f);
00271 
00272   // set the null region to a small value initially
00273   set_null_region(16);
00274 
00275   // set the maximum animate stride allowed to 20 by default
00276   set_max_stride(20);
00277 
00278   // set the default translation and rotation increments
00279   // these really need to be made user modifiable at runtime
00280   transInc = 1.0f / 25000.0f;
00281     rotInc = 1.0f /   200.0f;
00282   scaleInc = 1.0f / 25000.0f;
00283    animInc = 1.0f /    75.0f;
00284 }
00285 
00286 // update the display due to a command being executed.  Return whether
00287 // any action was taken on this command.
00288 // Arguments are the command type, command object, and the 
00289 // success of the command (T or F).
00290 int Spaceball::act_on_command(int type, Command *cmd) {
00291   return FALSE; // we don't take any commands presently
00292 }
00293 
00294 
00295 // check for an event, and queue it if found.  Return TRUE if an event
00296 // was generated.
00297 int Spaceball::check_event(void) {
00298   int tx, ty, tz, rx, ry, rz, buttons;
00299   int buttonchanged;
00300   int win_event=FALSE;
00301   int direct_event=FALSE;
00302   // for use in UserKeyEvent() calls
00303   DisplayDevice::EventCodes keydev=DisplayDevice::WIN_KBD;
00304 
00305   // explicitly initialize event state variables
00306   rx=ry=rz=tx=ty=tz=buttons=0;
00307 
00308 #if defined(VMDTDCONNEXION) && defined(__APPLE__)
00309   if (tdx_getstatus(tx, ty, tz, rx, ry, rz, buttons))
00310     win_event = TRUE;
00311 #else
00312   if (app->display->spaceball(&rx, &ry, &rz, &tx, &ty, &tz, &buttons)) 
00313     win_event = TRUE;
00314 #endif
00315 
00316 
00317 #if defined(VMDLIBSBALL)
00318   // combine direct spaceball events together with window-system events
00319   if (sball != NULL) {
00320     int rx2, ry2, rz2, tx2, ty2, tz2, buttons2;
00321     if (sball_getstatus(sball, &tx2, &ty2, &tz2, &rx2, &ry2, &rz2, &buttons2)) {
00322       direct_event = TRUE;
00323       rx += rx2;
00324       ry += ry2;
00325       rz += rz2;
00326       tx += tx2; 
00327       ty += ty2; 
00328       tz += tz2; 
00329       buttons |= buttons2;
00330     }
00331   }
00332 #endif
00333 
00334   if (!win_event && !direct_event)
00335     return FALSE; // no events to report
00336 
00337   // find which buttons changed state
00338   buttonchanged = buttons ^ buttonDown; 
00339 
00340   // if the user presses button 1, reset the view, a very very very
00341   // important feature to have implemented early on... ;-)
00342 #if defined(VMDLIBSBALL)  && !defined(VMDSPACEWARE)
00343   if (((buttonchanged & SBALL_BUTTON_1) && (buttons & SBALL_BUTTON_1)) ||
00344       ((buttonchanged & SBALL_BUTTON_LEFT) && (buttons & SBALL_BUTTON_LEFT))){
00345 #else 
00346 // #elif!defined(VMDLIBSBALL) && defined(VMDSPACEWARE)
00347   if ((buttonchanged & 2) && (buttons & 2)) {
00348 #endif
00349 
00350     app->scene_resetview();
00351     msgInfo << "Spaceball reset view orientation" << sendmsg;
00352   }
00353 
00354   // Toggle between the different modes
00355 #if   defined(VMDLIBSBALL)  &&  !defined(VMDSPACEWARE)
00356   if (((buttonchanged & SBALL_BUTTON_2) && (buttons & SBALL_BUTTON_2)) ||
00357       ((buttonchanged & SBALL_BUTTON_RIGHT) && (buttons & SBALL_BUTTON_RIGHT))) {
00358 #else
00359 //#elif !defined(VMDLIBSBALL) &&  defined(VMDSPACEWARE)
00360   if ((buttonchanged & 4) && (buttons & 4)) {
00361 #endif
00362 
00363     switch (moveMode) {
00364       case NORMAL:
00365         move_mode(MAXAXIS);
00366         msgInfo << "Spaceball set to dominant axis rotation/translation mode" << sendmsg;
00367         break;   
00368 
00369       case MAXAXIS:
00370         move_mode(SCALING);
00371         msgInfo << "Spaceball set to scaling mode" << sendmsg;
00372         break;   
00373 
00374       case SCALING:
00375         move_mode(ANIMATE);
00376         msgInfo << "Spaceball set to animate mode" << sendmsg;
00377         break;   
00378 
00379       case ANIMATE:
00380         move_mode(TRACKER);
00381         msgInfo << "Spaceball set to tracker mode" << sendmsg;
00382         break;   
00383 
00384       case TRACKER:
00385         move_mode(USER);
00386         msgInfo << "Spaceball set to user mode" << sendmsg;
00387         break;   
00388 
00389       default: 
00390         move_mode(NORMAL);
00391         msgInfo << "Spaceball set to rotation/translation mode" << sendmsg;
00392         break;
00393     }
00394   }
00395 
00396   // if the user presses button 3 through N, run a User command
00397 #if defined(VMDLIBSBALL)  &&  !defined(VMDSPACEWARE)
00398   if ((buttonchanged & SBALL_BUTTON_3) && (buttons & SBALL_BUTTON_3)) {
00399     runcommand(new UserKeyEvent(keydev, '3', (int) DisplayDevice::AUX));
00400   }
00401   if ((buttonchanged & SBALL_BUTTON_4) && (buttons & SBALL_BUTTON_4)) {
00402     runcommand(new UserKeyEvent(keydev, '4', (int) DisplayDevice::AUX));
00403   }
00404   if ((buttonchanged & SBALL_BUTTON_5) && (buttons & SBALL_BUTTON_5)) {
00405     runcommand(new UserKeyEvent(keydev, '5', (int) DisplayDevice::AUX));
00406   }
00407   if ((buttonchanged & SBALL_BUTTON_6) && (buttons & SBALL_BUTTON_6)) {
00408     runcommand(new UserKeyEvent(keydev, '6', (int) DisplayDevice::AUX));
00409   }
00410   if ((buttonchanged & SBALL_BUTTON_7) && (buttons & SBALL_BUTTON_7)) {
00411     runcommand(new UserKeyEvent(keydev, '7', (int) DisplayDevice::AUX));
00412   }
00413   if ((buttonchanged & SBALL_BUTTON_8) && (buttons & SBALL_BUTTON_8)) {
00414     runcommand(new UserKeyEvent(keydev, '8', (int) DisplayDevice::AUX));
00415   }
00416 //#elif !defined(VMDLIBSBALL) &&  defined(VMDSPACEWARE)
00417 #else
00418   if ((buttonchanged & 8) && (buttons & 8)) {
00419     runcommand(new UserKeyEvent(keydev, '3', (int) DisplayDevice::AUX));
00420   }
00421   if ((buttonchanged & 16) && (buttons & 16)) {
00422     runcommand(new UserKeyEvent(keydev, '4', (int) DisplayDevice::AUX));
00423   }
00424   if ((buttonchanged & 32) && (buttons & 32)) {
00425     runcommand(new UserKeyEvent(keydev, '5', (int) DisplayDevice::AUX));
00426   }
00427   if ((buttonchanged & 64) && (buttons & 64)) {
00428     runcommand(new UserKeyEvent(keydev, '6', (int) DisplayDevice::AUX));
00429   }
00430   if ((buttonchanged & 128) && (buttons & 128)) {
00431     runcommand(new UserKeyEvent(keydev, '7', (int) DisplayDevice::AUX));
00432   }
00433   if ((buttonchanged & 256) && (buttons & 256)) {
00434     runcommand(new UserKeyEvent(keydev, '8', (int) DisplayDevice::AUX));
00435   }
00436 #endif
00437 
00438   // get absolute values of axis forces for use in 
00439   // null region processing and min/max comparison tests
00440   int atx, aty, atz, arx, ary, arz;
00441   atx = abs(tx);
00442   aty = abs(ty);
00443   atz = abs(tz);
00444   arx = abs(rx);
00445   ary = abs(ry);
00446   arz = abs(rz);
00447 
00448 
00449   // perform null region processing
00450   if (atx > null_region) {
00451     tx = ((tx > 0) ? (tx - null_region) : (tx + null_region));
00452   } else {
00453     tx = 0;
00454   }
00455   if (aty > null_region) {
00456     ty = ((ty > 0) ? (ty - null_region) : (ty + null_region));
00457   } else {
00458     ty = 0;
00459   }
00460   if (atz > null_region) {
00461     tz = ((tz > 0) ? (tz - null_region) : (tz + null_region));
00462   } else {
00463     tz = 0;
00464   }
00465   if (arx > null_region) {
00466     rx = ((rx > 0) ? (rx - null_region) : (rx + null_region));
00467   } else {
00468     rx = 0;
00469   }
00470   if (ary > null_region) {
00471     ry = ((ry > 0) ? (ry - null_region) : (ry + null_region));
00472   } else {
00473     ry = 0;
00474   }
00475   if (arz > null_region) {
00476     rz = ((rz > 0) ? (rz - null_region) : (rz + null_region));
00477   } else {
00478     rz = 0;
00479   }
00480 
00481 
00482   // Ignore null motion events since some versions of the Windows 
00483   // Spaceball driver emit a constant stream of null motion event
00484   // packets which would otherwise cause continuous redraws, pegging the 
00485   // CPU and GPU at maximum load.
00486   if ((arx+ary+arz+atx+aty+atz) > 0) {
00487     float ftx = tx * sensitivity;
00488     float fty = ty * sensitivity;
00489     float ftz = tz * sensitivity;
00490     float frx = rx * sensitivity;
00491     float fry = ry * sensitivity;
00492     float frz = rz * sensitivity;
00493     char rmaxaxis = 'x';
00494     float rmaxval = 0.0f;
00495     float tmaxval = 0.0f;
00496     float tmaxvec[3] = { 0.0f, 0.0f, 0.0f };
00497     tmaxvec[0] = tmaxvec[1] = tmaxvec[2] = 0.0f;
00498 
00499     switch(moveMode) {
00500       case NORMAL:
00501         // Z-axis rotation/trans have to be negated in order to please VMD...
00502         app->scene_rotate_by(frx * rotInc, 'x');
00503         app->scene_rotate_by(fry * rotInc, 'y');
00504         app->scene_rotate_by(-frz * rotInc, 'z');
00505         if (app->display_projection_is_perspective()) {
00506           app->scene_translate_by(ftx * transInc, fty * transInc, -ftz * transInc);
00507         } else {
00508           app->scene_scale_by((1.0f + scaleInc * -ftz > 0.0f) ? 
00509                                1.0f + scaleInc * -ftz : 0.0f);
00510           app->scene_translate_by(ftx * transInc, fty * transInc, 0);
00511         }
00512  
00513         break;
00514 
00515       case MAXAXIS:
00516         // Z-axis rotation/trans have to be negated in order to please VMD...
00517         // find dominant rotation axis
00518         if (arx > ary) {
00519           if (arx > arz) {
00520             rmaxaxis = 'x';
00521             rmaxval = frx; 
00522           } else {
00523             rmaxaxis = 'z';
00524             rmaxval = -frz; 
00525           }
00526         } else {     
00527           if (ary > arz) {
00528             rmaxaxis = 'y';
00529             rmaxval = fry; 
00530           } else {
00531             rmaxaxis = 'z';
00532             rmaxval = -frz; 
00533           }
00534         }
00535 
00536         // find dominant translation axis
00537         if (atx > aty) {
00538           if (atx > atz) {
00539             tmaxval = ftx;
00540             tmaxvec[0] = ftx; 
00541           } else {
00542             tmaxval = ftz;
00543             tmaxvec[2] = ftz; 
00544           }
00545         } else {     
00546           if (aty > atz) {
00547             tmaxval = fty;
00548             tmaxvec[1] = fty; 
00549           } else {
00550             tmaxval = ftz;
00551             tmaxvec[2] = ftz; 
00552           }
00553        }
00554 
00555        // determine whether to rotate or translate
00556        if (fabs(rmaxval) > fabs(tmaxval)) {
00557          app->scene_rotate_by(rmaxval * rotInc, rmaxaxis);
00558        } else {
00559          app->scene_translate_by(tmaxvec[0] * transInc, 
00560                                  tmaxvec[1] * transInc, 
00561                                 -tmaxvec[2] * transInc);
00562        }
00563        break;
00564 
00565       case SCALING:
00566         app->scene_scale_by((1.0f + scaleInc * ftz > 0.0f) ? 
00567                              1.0f + scaleInc * ftz : 0.0f);
00568         break;
00569 
00570       case ANIMATE:
00571         // if we got a non-zero input, update the VMD animation state
00572         if (abs(ry) > 0) {
00573 #if 1
00574           // exponential input scaling
00575           float speed = fabsf(expf(fabsf((fabsf(fry) * animInc) / 1.7f))) - 1.0f;
00576 #else
00577           // linear input scaling
00578           float speed = fabsf(fry) * animInc;
00579 #endif
00580 
00581           if (speed > 0) {
00582             if (speed < 1.0) 
00583               app->animation_set_speed(speed);
00584             else
00585               app->animation_set_speed(1.0f);
00586  
00587             int stride = 1;
00588             if (fabs(speed - 1.0) > (double) maxstride)
00589               stride = maxstride;
00590             else
00591               stride = 1 + (int) fabs(speed-1.0);
00592             if (stride < 1)
00593               stride = 1; 
00594             app->animation_set_stride(stride);
00595  
00596             // -ry is turned to the right, like a typical shuttle/jog control
00597             if (fry < 0) 
00598               app->animation_set_dir(Animation::ANIM_FORWARD1);
00599             else
00600               app->animation_set_dir(Animation::ANIM_REVERSE1);
00601           } else {
00602             app->animation_set_dir(Animation::ANIM_PAUSE);
00603             app->animation_set_speed(1.0f);
00604           }
00605         } else {
00606           app->animation_set_dir(Animation::ANIM_PAUSE);
00607           app->animation_set_speed(1.0f);
00608         }
00609         break;
00610 
00611       case TRACKER:
00612         trtx = ftx;
00613         trty = fty;
00614         trtz = ftz;
00615         trrx = frx;
00616         trry = fry;
00617         trrz = frz;
00618         trbuttons = buttons;
00619         break;
00620 
00621       case USER:
00622         // inform TCL
00623         app->commandQueue->runcommand(new SpaceballEvent(ftx, fty, ftz, 
00624                                                          frx, fry, frz, 
00625                                                          buttons));
00626         break;
00627     }
00628   }
00629 
00630   // update button status for next time through
00631   buttonDown = buttons;
00632 
00633   return TRUE;
00634 }
00635 
00636 
00638 
00639 const char* Spaceball::get_mode_str(MoveMode mm) {
00640   const char* modestr;
00641 
00642   switch (mm) {
00643     default:
00644     case NORMAL:      modestr = "rotate";     break;
00645     case MAXAXIS:     modestr = "maxaxis";    break;
00646     case SCALING:     modestr = "scale";      break;
00647     case ANIMATE:     modestr = "animate";    break;
00648     case TRACKER:     modestr = "tracker";    break;
00649     case USER:        modestr = "user";       break;
00650   }
00651 
00652   return modestr;
00653 }
00654 
00655 
00656 void Spaceball::get_tracker_status(float &tx, float &ty, float &tz,
00657                                    float &rx, float &ry, float &rz, 
00658                                    int &buttons) {
00659   tx =  trtx * transInc;
00660   ty =  trty * transInc;
00661   tz = -trtz * transInc;
00662   rx =  trrx * rotInc;
00663   ry =  trry * rotInc;
00664   rz = -trrz * rotInc;
00665   buttons = trbuttons;
00666 }
00667 
00668 
00669 // set the Spaceball move mode to the given state; return success
00670 int Spaceball::move_mode(MoveMode mm) {
00671   // change the mode now
00672   moveMode = mm;
00673 
00675   if (moveMode != TRACKER) {
00676     trtx=trty=trtz=trrx=trry=trrz=0.0f; 
00677     trbuttons=0;
00678   }
00679 
00680   return TRUE; // report success
00681 }
00682 
00683 
00684 

Generated on Fri Oct 4 02:45:00 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002