/** * */ package cz.cuni.mff.peckam.java.origamist.gui.common; import java.awt.AWTEvent; import java.awt.Component; import java.awt.event.MouseEvent; import java.util.Enumeration; import javax.media.j3d.TransformGroup; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnBehaviorPost; import javax.vecmath.Matrix4d; import com.sun.j3d.utils.behaviors.mouse.MouseBehaviorCallback; import com.sun.j3d.utils.behaviors.mouse.MouseRotate; /** * A {@link MouseRotate} behavior that doesn't rotate around (0,0,0), but around the object's center. * * @author Martin Pecka */ public class CenteredMouseRotate extends MouseRotate { // Most of the methods and fields are just copied from MouseBehavior, because they are either default or private protected double x_angle, y_angle; protected double x_factor = .03; protected double y_factor = .03; protected MouseBehaviorCallback callback = null; /** * Creates a default mouse rotate behavior. **/ public CenteredMouseRotate() { super(); } /** * Creates a rotate behavior that uses AWT listeners and behavior * posts rather than WakeupOnAWTEvent. The behavior is added to the * specified Component. A null component can be passed to specify * the behavior should use listeners. Components can then be added to * the behavior with the addListener(Component c) method. * Note that this behavior still needs a transform * group to work on (use setTransformGroup(tg)) and the transform * group must add this behavior. * * @param flags interesting flags (wakeup conditions). */ public CenteredMouseRotate(Component c, int flags) { super(c, flags); } /** * Creates a rotate behavior that uses AWT listeners and behavior * posts rather than WakeupOnAWTEvent. The behaviors is added to * the specified Component and works on the given TransformGroup. * A null component can be passed to specify the behavior should use * listeners. Components can then be added to the behavior with the * addListener(Component c) method. * * @param c The Component to add the MouseListener and * MouseMotionListener to. * @param transformGroup The TransformGroup to operate on. */ public CenteredMouseRotate(Component c, TransformGroup transformGroup) { super(c, transformGroup); } /** * Creates a rotate behavior that uses AWT listeners and behavior * posts rather than WakeupOnAWTEvent. The behavior is added to the * specified Component. A null component can be passed to specify * the behavior should use listeners. Components can then be added * to the behavior with the addListener(Component c) method. * * @param c The Component to add the MouseListener * and MouseMotionListener to. */ public CenteredMouseRotate(Component c) { super(c); } /** * Creates a rotate behavior. * Note that this behavior still needs a transform * group to work on (use setTransformGroup(tg)) and * the transform group must add this behavior. * * @param flags interesting flags (wakeup conditions). */ public CenteredMouseRotate(int flags) { super(flags); } /** * Creates a rotate behavior given the transform group. * * @param transformGroup The transformGroup to operate on. */ public CenteredMouseRotate(TransformGroup transformGroup) { super(transformGroup); } @Override public void initialize() { super.initialize(); x_angle = 0; y_angle = 0; if ((flags & INVERT_INPUT) == INVERT_INPUT) { invert = true; x_factor *= -1; y_factor *= -1; } } @Override public double getXFactor() { return x_factor; } @Override public double getYFactor() { return y_factor; } @Override public void setFactor(double factor) { x_factor = y_factor = factor; } @Override public void setFactor(double xFactor, double yFactor) { x_factor = xFactor; y_factor = yFactor; } @Override public void processStimulus(@SuppressWarnings("rawtypes") Enumeration criteria) { WakeupCriterion wakeup; AWTEvent[] events; MouseEvent evt; while (criteria.hasMoreElements()) { wakeup = (WakeupCriterion) criteria.nextElement(); if (wakeup instanceof WakeupOnAWTEvent) { events = ((WakeupOnAWTEvent) wakeup).getAWTEvent(); if (events.length > 0) { evt = (MouseEvent) events[events.length - 1]; process(evt); } } else if (wakeup instanceof WakeupOnBehaviorPost) { while (true) { // access to the queue must be synchronized synchronized (mouseq) { if (mouseq.isEmpty()) break; evt = (MouseEvent) mouseq.remove(0); // consolidate MOUSE_DRAG events while ((evt.getID() == MouseEvent.MOUSE_DRAGGED) && !mouseq.isEmpty() && (((MouseEvent) mouseq.get(0)).getID() == MouseEvent.MOUSE_DRAGGED)) { evt = (MouseEvent) mouseq.remove(0); } } process(evt); } } } wakeupOn(mouseCriterion); } /** * Process the mouse event. * * @param evt The event to be processed. */ protected void process(MouseEvent evt) { int id; int dx, dy; processMouseEvent(evt); if (((buttonPress) && ((flags & MANUAL_WAKEUP) == 0)) || ((wakeUp) && ((flags & MANUAL_WAKEUP) != 0))) { id = evt.getID(); if ((id == MouseEvent.MOUSE_DRAGGED) && !evt.isMetaDown() && !evt.isAltDown()) { x = evt.getX(); y = evt.getY(); dx = x - x_last; dy = y - y_last; if (!reset) { x_angle = dy * y_factor; y_angle = dx * x_factor; transformX.rotX(x_angle); transformY.rotY(y_angle); transformGroup.getTransform(currXform); Matrix4d mat = new Matrix4d(); // Remember old matrix currXform.get(mat); if (invert) { currXform.mul(currXform, transformX); currXform.mul(currXform, transformY); } else { currXform.mul(transformX, currXform); currXform.mul(transformY, currXform); } // This is the only difference from MouseRotate - not setting the old transform back // currXform.setTranslation(translation); // Update xform transformGroup.setTransform(currXform); transformChanged(currXform); if (callback != null) callback.transformChanged(MouseBehaviorCallback.ROTATE, currXform); } else { reset = false; } x_last = x; y_last = y; } else if (id == MouseEvent.MOUSE_PRESSED) { x_last = evt.getX(); y_last = evt.getY(); } } } @Override public void setupCallback(MouseBehaviorCallback callback) { this.callback = callback; } }