NOTICE: This version of the NSF Unidata web site (archive.unidata.ucar.edu) is no longer being updated.
Current content can be found at unidata.ucar.edu.
To learn about what's going on, see About the Archive Site.
Hi Olver, It turns out you were right. This is a bug. Thanks for finding it. The fix is in the attached visad/MouseHelper.java file. Cheers, Bill ---------------------------------------------------------- Bill Hibbard, SSEC, 1225 W. Dayton St., Madison, WI 53706 hibbard@xxxxxxxxxxxxxxxxx 608-263-4427 fax: 608-263-6738 http://www.ssec.wisc.edu/~billh/vis.html
// // MouseHelper.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2002 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package visad; import java.awt.event.*; import java.rmi.*; import java.awt.*; import java.util.*; import visad.browser.Convert; /** MouseHelper is the VisAD helper class for MouseBehaviorJ3D and MouseBehaviorJ2D.<p> MouseHelper is preferred by cats everywhere.<p> */ public class MouseHelper implements RendererSourceListener { MouseBehavior behavior; /** DisplayRenderer for Display */ DisplayRenderer display_renderer; DisplayImpl display; /** ProjectionControl for Display */ private ProjectionControl proj; private double xymul; DataRenderer direct_renderer = null; /** matrix from ProjectionControl when mousePressed1 (left) */ private double[] tstart; /** screen location when mousePressed1 or mousePressed3 */ private int start_x, start_y; private double xmul, ymul; private double[] xtrans = new double[3]; private double[] ytrans = new double[3]; /** mouse in window */ private boolean mouseEntered; /** ((InputEvent) event).getModifiers() when mouse pressed */ private int mouseModifiers; /** flag for 2-D mode */ private boolean mode2D; // start of variables for table-driven mapping from mouse buttons and // keys to functons // index values for functions public static final int NONE = -1, ROTATE = 0, ZOOM = 1, TRANSLATE = 2, CURSOR_TRANSLATE = 3, CURSOR_ZOOM = 4, CURSOR_ROTATE = 5, DIRECT = 6, NFUNCTIONS = 7; // index values for mouse buttons public static final int LEFT = 0, CENTER = 1, RIGHT = 2; // actual mouse buttons pressed boolean[] actual_button = {false, false, false}; // mouse button pressed accounting for combos int virtual_button = -1; // array of enables for functions boolean[] function = {false, false, false, false, false, false, false}; // save previous function to compute function change boolean[] old_function = {false, false, false, false, false, false, false}; // enable any two mouse buttons = the third boolean enable_combos = true; // mapping from buttons/keys to function // function_map[button][CTRL][SHIFT] where // button = 0, 1, 2 // CTRL = 0, 1 // SHIFT = 0, 1 // initialize with defaults int[][][] function_map = {{{ROTATE, ZOOM}, {TRANSLATE, NONE}}, {{CURSOR_TRANSLATE, CURSOR_ZOOM}, {CURSOR_ROTATE, NONE}}, {{DIRECT, DIRECT}, {DIRECT, DIRECT}}}; // end of variables for table-driven mapping from mouse buttons // and keys to functons public MouseHelper(DisplayRenderer r, MouseBehavior b) { behavior = b; display_renderer = r; display = display_renderer.getDisplay(); proj = display.getProjectionControl(); mode2D = display_renderer.getMode2D(); // track Display's DataRenderers in case direct_renderer is removed display.addRendererSourceListener(this); function = new boolean[] {false, false, false, false, false, false, false}; enable_combos = true; } /** * Process the given event treating it as a local event. * @param event event to process. */ public void processEvent(AWTEvent event) { processEvent(event, VisADEvent.LOCAL_SOURCE); } // WLH 17 Aug 2000 private boolean first = true; /** * Process the given event, treating it as coming from a remote source * if remote flag is set. * @param event event to process. * @param remoteId id of remote source. */ public void processEvent(AWTEvent event, int remoteId) { if (behavior == null) return; // WLH 17 Aug 2000 if (first) { start_x = 0; start_y = 0; VisADRay start_ray = behavior.findRay(start_x, start_y); VisADRay start_ray_x = behavior.findRay(start_x + 1, start_y); VisADRay start_ray_y = behavior.findRay(start_x, start_y + 1); if (start_ray != null && start_ray_x != null && start_ray_y != null) { double[] tstart = proj.getMatrix(); double[] rot = new double[3]; double[] scale = new double[1]; double[] trans = new double[3]; behavior.instance_unmake_matrix(rot, scale, trans, tstart); double[] trot = behavior.make_matrix(rot[0], rot[1], rot[2], scale[0], 0.0, 0.0, 0.0); double[] xmat = behavior.make_translate( start_ray_x.position[0] - start_ray.position[0], start_ray_x.position[1] - start_ray.position[1], start_ray_x.position[2] - start_ray.position[2]); double[] ymat = behavior.make_translate( start_ray_y.position[0] - start_ray.position[0], start_ray_y.position[1] - start_ray.position[1], start_ray_y.position[2] - start_ray.position[2]); double[] xmatmul = behavior.multiply_matrix(trot, xmat); double[] ymatmul = behavior.multiply_matrix(trot, ymat); behavior.instance_unmake_matrix(rot, scale, trans, xmatmul); double xmul = trans[0]; behavior.instance_unmake_matrix(rot, scale, trans, ymatmul); double ymul = trans[1]; xymul = Math.sqrt(xmul * xmul + ymul * ymul); // System.out.println("xymul = " + xymul); first = false; } } // end if (first) if (!(event instanceof MouseEvent)) { System.out.println("MouseHelper.processStimulus: non-" + "MouseEvent"); return; } MouseEvent mouse_event = (MouseEvent) event; int event_id = event.getID(); if (event_id == MouseEvent.MOUSE_ENTERED) { mouseEntered = true; try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_ENTERED, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } return; } else if (event_id == MouseEvent.MOUSE_EXITED) { mouseEntered = false; try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_EXITED, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } return; } else if (event_id == MouseEvent.MOUSE_MOVED) { try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_MOVED, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } return; } else if (event_id == MouseEvent.MOUSE_PRESSED || event_id == MouseEvent.MOUSE_RELEASED) { int m = ((InputEvent) event).getModifiers(); int m1 = m & InputEvent.BUTTON1_MASK; int m2 = m & InputEvent.BUTTON2_MASK; int m3 = m & InputEvent.BUTTON3_MASK; int mctrl = m & InputEvent.CTRL_MASK; int mshift = m & InputEvent.SHIFT_MASK; if (event_id == MouseEvent.MOUSE_PRESSED) { display.updateBusyStatus(); if (m1 != 0) { actual_button[LEFT] = true; } if (m2 != 0) { actual_button[CENTER] = true; } if (m3 != 0) { actual_button[RIGHT] = true; } mouseModifiers = m; } else { // event_id == MouseEvent.MOUSE_RELEASED display.updateBusyStatus(); if (m1 != 0) { actual_button[LEFT] = false; } if (m2 != 0) { actual_button[CENTER] = false; } if (m3 != 0) { actual_button[RIGHT] = false; } mouseModifiers = 0; } // compute button combos int n = 0, sum = 0; for (int i=0; i<3; i++) { if (actual_button[i]) { n++; sum += i; } } if (n == 1) { virtual_button = sum; } else if (n == 2 && enable_combos) { virtual_button = 3 - sum; } else { // n == 0 || n == 3 || (n == 2 && !enable_combos) virtual_button = -1; } // compute old and new functions for (int i=0; i<NFUNCTIONS; i++) { old_function[i] = function[i]; function[i] = false; } int vctrl = (mctrl == 0) ? 0 : 1; int vshift = (mshift == 0) ? 0 : 1; int f = (virtual_button < 0) ? -1 : function_map[virtual_button][vctrl][vshift]; if (f >= 0) function[f] = true; enableFunctions((MouseEvent) event); if (event_id == MouseEvent.MOUSE_PRESSED) { try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_PRESSED, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } if (m1 != 0) { try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_PRESSED_LEFT, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } } if (m2 != 0) { try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_PRESSED_CENTER, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } } if (m3 != 0) { try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_PRESSED_RIGHT, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } } } else { // event_id == MouseEvent.MOUSE_RELEASED try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_RELEASED, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } if (m1 != 0) { try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_RELEASED_LEFT, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } } if (m2 != 0) { try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_RELEASED_CENTER, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } } if (m3 != 0) { try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_RELEASED_RIGHT, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } } } } else if (event_id == MouseEvent.MOUSE_DRAGGED) { boolean cursor = function[CURSOR_TRANSLATE] || function[CURSOR_ZOOM] || function[CURSOR_ROTATE]; boolean matrix = function[ROTATE] || function[ZOOM] || function[TRANSLATE]; if (cursor || matrix || function[DIRECT]) { display.updateBusyStatus(); Dimension d = ((MouseEvent) event).getComponent().getSize(); int current_x = ((MouseEvent) event).getX(); int current_y = ((MouseEvent) event).getY(); if (matrix) { double[] t1 = null; if (function[ZOOM]) { // current_y -> scale double scale = Math.exp((start_y-current_y) / (double) d.height); t1 = behavior.make_matrix(0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0); } if (function[TRANSLATE]) { // current_x, current_y -> translate // WLH 9 Aug 2000 double transx = xmul * (start_x - current_x); double transy = ymul * (start_y - current_y); // System.out.println("xmul = " + xmul + " ymul = " + ymul); // System.out.println("transx = " + transx + " transy = " + transy); t1 = behavior.make_translate(-transx, -transy); } if (function[ROTATE]) { if (mode2D) { double transx = xmul * (start_x - current_x); double transy = ymul * (start_y - current_y); t1 = behavior.make_translate(-transx, -transy); } else { // don't do 3-D rotation in 2-D mode double angley = - (current_x - start_x) * 100.0 / (double) d.width; double anglex = - (current_y - start_y) * 100.0 / (double) d.height; t1 = behavior.make_matrix(anglex, angley, 0.0, 1.0, 0.0, 0.0, 0.0); } } if (t1 != null) { t1 = behavior.multiply_matrix(t1, tstart); try { proj.setMatrix(t1); } catch (VisADException e) { } catch (RemoteException e) { } } } // end if (matrix) if (function[CURSOR_ZOOM]) { if (!mode2D) { // don't do cursor Z in 2-D mode // current_y -> 3-D cursor Z float diff = (start_y - current_y) * 4.0f / (float) d.height; display_renderer.drag_depth(diff); } } if (function[CURSOR_ROTATE]) { if (!mode2D) { // don't do 3-D rotation in 2-D mode double angley = - (current_x - start_x) * 100.0 / (double) d.width; double anglex = - (current_y - start_y) * 100.0 / (double) d.height; double[] t1 = behavior.make_matrix(anglex, angley, 0.0, 1.0, 0.0, 0.0, 0.0); t1 = behavior.multiply_matrix(t1, tstart); try { proj.setMatrix(t1); } catch (VisADException e) { } catch (RemoteException e) { } } } if(function[CURSOR_TRANSLATE]) { // current_x, current_y -> 3-D cursor X and Y VisADRay cursor_ray = behavior.findRay(current_x, current_y); if (cursor_ray != null) { display_renderer.drag_cursor(cursor_ray, false); } } if (function[DIRECT]) { if (direct_renderer != null) { VisADRay direct_ray = behavior.findRay(current_x, current_y); if (direct_ray != null) { direct_renderer.setLastMouseModifiers(mouseModifiers); direct_renderer.drag_direct(direct_ray, false, mouseModifiers); } } } } try { DisplayEvent e = new DisplayEvent(display, DisplayEvent.MOUSE_DRAGGED, mouse_event, remoteId); display.notifyListeners(e); } catch (VisADException e) { } catch (RemoteException e) { } } } private void enableFunctions(MouseEvent event) { if (event == null) { for (int i=0; i<NFUNCTIONS; i++) { old_function[i] = function[i]; function[i] = false; } } // compute old and new cursor and matrix enables boolean cursor = function[CURSOR_TRANSLATE] || function[CURSOR_ZOOM] || function[CURSOR_ROTATE]; boolean old_cursor = old_function[CURSOR_TRANSLATE] || old_function[CURSOR_ZOOM] || old_function[CURSOR_ROTATE]; boolean matrix = function[ROTATE] || function[ZOOM] || function[TRANSLATE]; boolean old_matrix = old_function[ROTATE] || old_function[ZOOM] || old_function[TRANSLATE]; // disable functions if (old_cursor && !cursor) { display_renderer.setCursorOn(false); } if (old_function[DIRECT] && !function[DIRECT]) { display_renderer.setDirectOn(false); if (direct_renderer != null) { direct_renderer.release_direct(); direct_renderer = null; } } // enable functions if (matrix && !old_matrix) { start_x = ((MouseEvent) event).getX(); start_y = ((MouseEvent) event).getY(); // WLH 9 Aug 2000 VisADRay start_ray = behavior.findRay(start_x, start_y); VisADRay start_ray_x = behavior.findRay(start_x + 1, start_y); VisADRay start_ray_y = behavior.findRay(start_x, start_y + 1); tstart = proj.getMatrix(); // print_matrix("tstart", tstart); double[] rot = new double[3]; double[] scale = new double[1]; double[] trans = new double[3]; behavior.instance_unmake_matrix(rot, scale, trans, tstart); double sts = scale[0]; double[] trot = behavior.make_matrix(rot[0], rot[1], rot[2], scale[0], 0.0, 0.0, 0.0); // print_matrix("trot", trot); // WLH 17 Aug 2000 double[] xmat = behavior.make_translate( start_ray_x.position[0] - start_ray.position[0], start_ray_x.position[1] - start_ray.position[1], start_ray_x.position[2] - start_ray.position[2]); double[] ymat = behavior.make_translate( start_ray_y.position[0] - start_ray.position[0], start_ray_y.position[1] - start_ray.position[1], start_ray_y.position[2] - start_ray.position[2]); double[] xmatmul = behavior.multiply_matrix(trot, xmat); double[] ymatmul = behavior.multiply_matrix(trot, ymat); /* print_matrix("xmat", xmat); print_matrix("ymat", ymat); print_matrix("xmatmul", xmatmul); print_matrix("ymatmul", ymatmul); */ behavior.instance_unmake_matrix(rot, scale, trans, xmatmul); xmul = trans[0]; behavior.instance_unmake_matrix(rot, scale, trans, ymatmul); ymul = trans[1]; // horrible hack, WLH 17 Aug 2000 if (behavior instanceof visad.java2d.MouseBehaviorJ2D) { double factor = xymul / Math.sqrt(xmul * xmul + ymul * ymul); xmul *= factor; ymul *= factor; xmul = Math.abs(xmul); ymul = -Math.abs(ymul); } /* System.out.println("xmul = " + Convert.shortString(xmul) + " ymul = " + Convert.shortString(ymul) + " scale = " + Convert.shortString(sts)); */ } // end if (matrix && !old_matrix) if (cursor && !old_cursor) { // turn cursor on whenever mouse button2 pressed display_renderer.setCursorOn(true); start_x = ((MouseEvent) event).getX(); start_y = ((MouseEvent) event).getY(); tstart = proj.getMatrix(); } if (function[CURSOR_TRANSLATE] && !old_function[CURSOR_TRANSLATE]) { VisADRay cursor_ray = behavior.findRay(start_x, start_y); if (cursor_ray != null) { display_renderer.drag_cursor(cursor_ray, true); } } if (function[CURSOR_ZOOM] && !old_function[CURSOR_ZOOM]) { if (!mode2D) { // don't do cursor Z in 2-D mode // current_y -> 3-D cursor Z VisADRay cursor_ray = behavior.cursorRay(display_renderer.getCursor()); display_renderer.depth_cursor(cursor_ray); } } if (function[DIRECT] && !old_function[DIRECT]) { if (display_renderer.anyDirects()) { int current_x = ((MouseEvent) event).getX(); int current_y = ((MouseEvent) event).getY(); VisADRay direct_ray = behavior.findRay(current_x, current_y); if (direct_ray != null) { direct_renderer = display_renderer.findDirect(direct_ray, mouseModifiers); if (direct_renderer != null) { display_renderer.setDirectOn(true); direct_renderer.setLastMouseModifiers(mouseModifiers); direct_renderer.drag_direct(direct_ray, true, mouseModifiers); } } } } } /** * Enable/disable the interpretation of any pair of mouse buttons * as the third button. * @param e enable/disable. If true (default), interpret any pair * of mouse buttons as the third button. */ public void setEnableCombos(boolean e) { enable_combos = e; enableFunctions(null); } /** * Set mapping from (button, ctrl, shift) to function. * * <pre> * map[button][ctrl][shift] = * MouseHelper.NONE for no function * MouseHelper.ROTATE for box rotate * MouseHelper.ZOOM for box zoom * MouseHelper.TRANSLATE for box translate * MouseHelper.CURSOR_TRANSLATE for cursor translate * MouseHelper.CURSOR_ZOOM for cursor on Z axis (3-D only) * MouseHelper.CURSOR_ROTATE for box rotate with cursor * MouseHelper.DIRECT for direct manipulate * where button = 0 (left), 1 (center), 2 (right) * ctrl = 0 (CTRL key not pressed), 1 (CTRL key pressed) * shift = 0 (SHIFT key not pressed), 1 (SHIFT key pressed) * * Note some direct manipulation DataRenderers test the status of * CTRL and SHIFT keys, so it is advisable that the DIRECT function * be invariant to the state of ctrl and shift in the map array. * * For example, to set the left mouse button for direct * manipulation, and the center button for box rotation * (only without shift or control): * mouse_helper.setFunctionMap(new int[][][] * {{{MouseHelper.DIRECT, MouseHelper.DIRECT}, * {MouseHelper.DIRECT, MouseHelper.DIRECT}}, * {{MouseHelper.ROTATE, MouseHelper.NONE}, * {MouseHelper.NONE, MouseHelper.NONE}}, * {{MouseHelper.NONE, MouseHelper.NONE}, * {MouseHelper.NONE, MouseHelper.NONE}}}); * </pre> * @param map map of functions. map must be int[3][2][2] * @throws VisADException bad map */ public void setFunctionMap(int[][][] map) throws VisADException { if (map == null || map.length != 3) { throw new DisplayException("bad map array"); } for (int i=0; i<3; i++) { if (map[i] == null || map[i].length != 2) { throw new DisplayException("bad map array"); } for (int j=0; j<2; j++) { if (map[i][j] == null || map[i][j].length != 2) { throw new DisplayException("bad map array"); } for (int k=0; k<2; k++) { if (map[i][j][k] >= NFUNCTIONS) { throw new DisplayException("bad map array value" + map[i][j][k]); } } } } for (int i=0; i<3; i++) { for (int j=0; j<2; j++) { for (int k=0; k<2; k++) { function_map[i][j][k] = map[i][j][k]; } } } enableFunctions(null); } /** * Print out a readable form of a matrix. Useful for * debugging. * @param title title to prepend to output. * @param m matrix to print. */ public void print_matrix(String title, double[] m) { if (behavior == null) return; double[] rot = new double[3]; double[] scale = new double[1]; double[] trans = new double[3]; behavior.instance_unmake_matrix(rot, scale, trans, m); System.out.println(title + " = (" + Convert.shortString(rot[0]) + ", " + Convert.shortString(rot[1]) + ", " + Convert.shortString(rot[2]) + "), " + Convert.shortString(scale[0]) + ", (" + Convert.shortString(trans[0]) + ", " + Convert.shortString(trans[1]) + ", " + Convert.shortString(trans[2]) + ")"); } /** * Implementation for RendererSourceListener. Notifies that the * renderer has been deleted. * @param renderer DataRenderer that was deleted. */ public void rendererDeleted(DataRenderer renderer) { if (direct_renderer != null) { if (direct_renderer == renderer || direct_renderer.equals(renderer)) { direct_renderer = null; } } } }
visad
archives: