//
// KeyboardBehaviorJ2D.java
//
/*
VisAD system for interactive analysis and visualization of numerical
data. Copyright (C) 1996 - 2017 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.java2d;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.rmi.RemoteException;
import visad.DisplayEvent;
import visad.DisplayImpl;
import visad.DisplayRenderer;
import visad.KeyboardBehavior;
import visad.MouseBehavior;
import visad.ProjectionControl;
import visad.VisADException;
/**
* KeyboardBehaviorJ2D is the VisAD class for keyboard control of
* translate (pan) and zoom in Java2D.
* @author Troy Sandblom NCAR/RAP May, 2000
* @author Don Murray (adapted to VisAD with help from Curtis)
*/
public class KeyboardBehaviorJ2D
implements KeyboardBehavior {
private ProjectionControl proj;
private DisplayRenderer displayRenderer;
private MouseBehavior mouseBehavior;
private double rotateAmount = 5.0;
private double scaleAmount = .05;
private double transAmount = .1;
/** Identifier for function to rotate positively around the Z viewing axis
(perpendicular to the screen plane) */
public static final int ROTATE_Z_POS = 7;
/** Identifier for function to rotate negatively around the Z viewing axis
(perpendicular to the screen plane) */
public static final int ROTATE_Z_NEG = 8;
/** Maximum number of functions for this behavior */
private final int MAX_FUNCTIONS = 9;
// Should someday make an object that encapsulates both and use that
// for checking.
private int[] functionKeys = new int[MAX_FUNCTIONS];
private int[] functionMods = new int[MAX_FUNCTIONS];
/**
* Construct a new keyboard behavior for the DisplayRenderer. You
* need to add the behavior to the DisplayRenderer if you want to
* use it. Default keys for manipulations are as follows:<BR>
* Translation
* <UL>
* <LI>Arrow keys - translate up, down, left, right
* </UL>
* Zoom
* <UL>
* <LI>Shift + Up arrow - zoom in (ZOOM_IN)
* <LI>Shift + Down arrow - zoom out (ZOOM_OUT)
* </UL>
* Rotate
* <UL>
* <LI>Shift + Left arrow - rotate left (ROTATE_Z_POS)
* <LI>Shift + Right arrow - rotate right (ROTATE_Z_NEG)
* </UL>
* Reset
* <UL>
* <LI>Ctrl + R key - reset to original projection (RESET)
* </UL>
* <P>
* @see DisplayRendererJ2D#addKeyboardBehavior(KeyboardBehaviorJ2D behavior)
* @param r DisplayRenderer to use.
*/
public KeyboardBehaviorJ2D(DisplayRendererJ2D r) {
displayRenderer = r;
proj = displayRenderer.getDisplay().getProjectionControl();
mouseBehavior = displayRenderer.getMouseBehavior();
// initialize array functions
mapKeyToFunction(TRANSLATE_UP, KeyEvent.VK_UP, NO_MASK);
mapKeyToFunction(TRANSLATE_DOWN, KeyEvent.VK_DOWN, NO_MASK);
mapKeyToFunction(TRANSLATE_LEFT, KeyEvent.VK_LEFT, NO_MASK);
mapKeyToFunction(TRANSLATE_RIGHT, KeyEvent.VK_RIGHT, NO_MASK);
mapKeyToFunction(ZOOM_IN, KeyEvent.VK_UP, InputEvent.SHIFT_MASK);
mapKeyToFunction(ZOOM_OUT, KeyEvent.VK_DOWN, InputEvent.SHIFT_MASK);
mapKeyToFunction(RESET, KeyEvent.VK_R, InputEvent.CTRL_MASK);
mapKeyToFunction(ROTATE_Z_POS, KeyEvent.VK_LEFT, InputEvent.SHIFT_MASK);
mapKeyToFunction(ROTATE_Z_NEG, KeyEvent.VK_RIGHT, InputEvent.SHIFT_MASK);
}
/**
* Maps key represented by keycode & modifiers to the given function.
* Each function can only have one key/modifier combination assigned
* to it at a time.
* @see java.awt.event.KeyEvent
* @see java.awt.event.InputEvent
* @param function keyboard function (ROTATE_X_POS, ZOOM_IN, etc)
* @param keycode <CODE>KeyEvent</CODE> virtual keycodes
* @param modifiers <CODE>InputEvent</CODE> key mask
*/
public void mapKeyToFunction(int function, int keycode, int modifiers) {
if (function < 0 || function >= MAX_FUNCTIONS) return;
functionKeys[function] = keycode;
functionMods[function] = modifiers;
}
/**
* Process a key event. Determines whether a meaningful key was pressed.
* @param event KeyEvent stimulus
*/
public void processKeyEvent(KeyEvent event) {
int id = event.getID();
if (id == KeyEvent.KEY_PRESSED) {
int modifiers = event.getModifiers();
int keyCode = event.getKeyCode();
// determine whether a meaningful key was pressed
for (int i=0; i<MAX_FUNCTIONS; i++) {
if (functionKeys[i] == keyCode && (modifiers == functionMods[i])) {
execFunction(i);
break;
}
}
}
// notify DisplayListeners of key event
int d_id = -1;
if (id == KeyEvent.KEY_PRESSED) d_id = DisplayEvent.KEY_PRESSED;
else if (id == KeyEvent.KEY_RELEASED) d_id = DisplayEvent.KEY_RELEASED;
if (d_id != -1) {
try {
DisplayImpl display = displayRenderer.getDisplay();
DisplayEvent e = new DisplayEvent(display, d_id, event);
display.notifyListeners(e);
}
catch (VisADException exc) { }
catch (RemoteException exc) { }
}
}
/**
* Executes the given function.
* @param function function to perform (TRANSLATE_UP, ZOOM_IN, etc)
*/
public void execFunction(int function) {
double transx = 0.0;
double transy = 0.0;
double scale = 1.0;
double anglez = 0.0;
double [] t1 = null;
double [] tstart = proj.getMatrix();
switch (function) {
case TRANSLATE_UP: // NB: down is up in Java2D compared with Java3D
transy -= transAmount;
t1 = mouseBehavior.make_matrix(
0.0, 0.0, 0.0, 1.0, transx, transy, 0.0);
break;
case TRANSLATE_DOWN:
transy += transAmount;
t1 = mouseBehavior.make_matrix(
0.0, 0.0, 0.0, 1.0, transx, transy, 0.0);
break;
case TRANSLATE_LEFT:
transx -= transAmount;
t1 = mouseBehavior.make_matrix(
0.0, 0.0, 0.0, 1.0, transx, transy, 0.0);
break;
case TRANSLATE_RIGHT:
transx += transAmount;
t1 = mouseBehavior.make_matrix(
0.0, 0.0, 0.0, 1.0, transx, transy, 0.0);
break;
case RESET:
tstart = proj.getSavedProjectionMatrix();
t1 = mouseBehavior.make_matrix(
0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0);
break;
case ZOOM_IN:
scale += scaleAmount;
t1 = mouseBehavior.make_matrix(
0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0);
break;
case ZOOM_OUT:
scale -= scaleAmount;
t1 = mouseBehavior.make_matrix(
0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0);
break;
case ROTATE_Z_NEG:
anglez -= rotateAmount;
t1 = mouseBehavior.make_matrix(
0.0, 0.0, anglez, 1.0, 0.0, 0.0, 0.0);
break;
case ROTATE_Z_POS:
anglez += rotateAmount;
t1 = mouseBehavior.make_matrix(
0.0, 0.0, anglez, 1.0, 0.0, 0.0, 0.0);
break;
default:
break;
}
if (t1 != null) {
t1 = mouseBehavior.multiply_matrix(t1, tstart);
try {
proj.setMatrix(t1);
} catch (VisADException e) {
} catch (RemoteException e) {
}
}
}
}