/src/org/mt4j/input/inputProcessors/componentProcessors/panProcessor/PanProcessorTwoFingers.java
Java | 273 lines | 161 code | 47 blank | 65 comment | 16 complexity | 780dce8c6714d60004fbeb289dce4bc6 MD5 | raw file
1/*********************************************************************** 2 * mt4j Copyright (c) 2008 - 2009 C.Ruff, Fraunhofer-Gesellschaft All rights reserved. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * 17 ***********************************************************************/ 18package org.mt4j.input.inputProcessors.componentProcessors.panProcessor; 19 20import java.util.List; 21 22import org.mt4j.components.interfaces.IMTComponent3D; 23import org.mt4j.input.inputData.AbstractCursorInputEvt; 24import org.mt4j.input.inputData.InputCursor; 25import org.mt4j.input.inputProcessors.IInputProcessor; 26import org.mt4j.input.inputProcessors.MTGestureEvent; 27import org.mt4j.input.inputProcessors.componentProcessors.AbstractComponentProcessor; 28import org.mt4j.input.inputProcessors.componentProcessors.AbstractCursorProcessor; 29import org.mt4j.util.math.Tools3D; 30import org.mt4j.util.math.ToolsGeometry; 31import org.mt4j.util.math.Vector3D; 32 33import processing.core.PApplet; 34 35/** 36 * The Class PanProcessorTwoFingers. Multi-touch gesture processor for panning the 37 * canvas by moving the scene's camera. Should only be registered with MTCanvas components. 38 * Fires PanEvent gesture events. 39 * <br><strong>NOTE:</strong> Should be only used in combination with a MTCanvas component. 40 * @author Christopher Ruff 41 */ 42public class PanProcessorTwoFingers extends AbstractCursorProcessor { 43 44 /** The detect radius. */ 45 private float detectRadius; 46 47 /** The applet. */ 48 private PApplet applet; 49 50 /** The point in plane. */ 51 private Vector3D pointInPlane; 52 53 /** The plane normal. */ 54 private Vector3D planeNormal; 55 56 57 /** 58 * Instantiates a new pan processor two fingers. 59 * 60 * @param app the app 61 */ 62 public PanProcessorTwoFingers(PApplet app) { 63 this(app, app.width/2); 64 } 65 66 /** 67 * Instantiates a new pan processor two fingers. 68 * 69 * @param applet the applet 70 * @param panDetectRadius the pan detect radius 71 */ 72 public PanProcessorTwoFingers(PApplet applet, float panDetectRadius){ 73 this.applet = applet; 74 this.detectRadius = panDetectRadius; 75 this.pointInPlane = new Vector3D(0,0,0); 76 this.planeNormal = new Vector3D(0,0,1); 77 this.setLockPriority(2); 78 } 79 80 81 @Override 82 public void cursorStarted(InputCursor c, AbstractCursorInputEvt positionEvent) { 83 InputCursor[] locked = getLockedCursorsArray(); 84 if (locked.length >= 2){ //gesture with 2 fingers already in progress 85 logger.debug(this.getName() + " has already enough cursors for this gesture - adding to unused ID:" + c.getId()); 86 }else{//not in progress yet 87 List<InputCursor> availableCursors = getFreeComponentCursors(); 88 logger.debug(this.getName() + " Available cursors: " + availableCursors.size()); 89 90 if (availableCursors.size() >= 2){ 91 InputCursor otherCursor = (availableCursors.get(0).equals(c))? availableCursors.get(1) : availableCursors.get(0); 92 //See if we can obtain a lock on both cursors 93 if (this.canLock(otherCursor, c)){ 94 float newDistance = Vector3D.distance(otherCursor.getPosition(), c.getPosition()); 95 if (newDistance < detectRadius) { 96 this.getLock(otherCursor, c); 97 logger.debug(this.getName() + " we could lock both cursors! And fingers in zoom distance!"); 98 this.fireGestureEvent(new PanTwoFingerEvent(this, MTGestureEvent.GESTURE_STARTED, positionEvent.getCurrentTarget(), otherCursor, c, new Vector3D(0,0,0), positionEvent.getCurrentTarget().getViewingCamera())); 99 }else{ 100 logger.debug(this.getName() + " cursors not close enough to start gesture. Distance: " + newDistance); 101 } 102 }else{ 103 logger.debug(this.getName() + " we could NOT lock both cursors!"); 104 } 105 } 106 } 107 } 108 109 @Override 110 public void cursorUpdated(InputCursor c, AbstractCursorInputEvt positionEvent) { 111 List<InputCursor> locked = getLockedCursors(); 112 if (locked.contains(c)){ //in progress with this cursors 113 InputCursor firstCursor = locked.get(0); 114 InputCursor secondCursor = locked.get(1); 115 Vector3D distance = (c.equals(firstCursor))? getNewTranslation(positionEvent.getTarget(), firstCursor, secondCursor) : getNewTranslation(positionEvent.getTarget(), secondCursor, firstCursor); 116 if (c.equals(firstCursor)){ 117 this.fireGestureEvent(new PanTwoFingerEvent(this, MTGestureEvent.GESTURE_UPDATED, positionEvent.getCurrentTarget(), firstCursor, secondCursor, new Vector3D(distance.getX(),distance.getY(),0), positionEvent.getCurrentTarget().getViewingCamera())); 118 }else{ 119 this.fireGestureEvent(new PanTwoFingerEvent(this, MTGestureEvent.GESTURE_UPDATED, positionEvent.getCurrentTarget(), firstCursor, secondCursor, new Vector3D(distance.getX(),distance.getY(),0), positionEvent.getCurrentTarget().getViewingCamera())); 120 } 121 } 122 } 123 124 125 @Override 126 public void cursorEnded(InputCursor c, AbstractCursorInputEvt positionEvent) { 127 logger.debug(this.getName() + " INPUT_ENDED RECIEVED - cursor: " + c.getId()); 128 List<InputCursor> locked = getLockedCursors(); 129 if (locked.contains(c)){ 130 InputCursor leftOverCursor = (locked.get(0).equals(c))? locked.get(1) : locked.get(0); 131 InputCursor futureCursor = getFarthestFreeCursorToLimited(leftOverCursor, detectRadius); 132 if (futureCursor != null){ 133 float newDistance = Vector3D.distance(leftOverCursor.getPosition(), futureCursor.getPosition()); 134 if (newDistance < detectRadius) {//Check if other cursor is in distance 135 this.getLock(futureCursor); 136 logger.debug(this.getName() + " we could lock another cursor! (ID:" + futureCursor.getId() +")"); 137 logger.debug(this.getName() + " continue with different cursors (ID: " + futureCursor.getId() + ")" + " " + "(ID: " + leftOverCursor.getId() + ")"); 138 }else{ 139 this.endGesture(c, leftOverCursor, positionEvent.getCurrentTarget()); 140 } 141 }else{ 142 this.endGesture(c, leftOverCursor, positionEvent.getCurrentTarget()); 143 } 144 } 145 } 146 147 148 /** 149 * End gesture. 150 * 151 * @param inputEndedCursor the input ended cursor 152 * @param leftOverCursor the left over cursor 153 * @param comp the comp 154 */ 155 private void endGesture(InputCursor inputEndedCursor, InputCursor leftOverCursor, IMTComponent3D comp){ 156 this.unLock(leftOverCursor); 157 this.fireGestureEvent(new PanTwoFingerEvent(this, MTGestureEvent.GESTURE_ENDED, comp, inputEndedCursor, leftOverCursor, new Vector3D(0,0,0), comp.getViewingCamera())); 158 } 159 160 161 162 @Override 163 public void cursorLocked(InputCursor c, IInputProcessor lockingProcessor) { 164 if (lockingProcessor instanceof AbstractComponentProcessor){ 165 logger.debug(this.getName() + " Recieved cursor LOCKED by (" + ((AbstractComponentProcessor)lockingProcessor).getName() + ") - cursor ID: " + c.getId()); 166 }else{ 167 logger.debug(this.getName() + " Recieved cursor LOCKED by higher priority signal - cursor ID: " + c.getId()); 168 } 169 170 List<InputCursor> locked = getLockedCursors(); 171 if (locked.contains(c)){ 172 InputCursor leftOverCursor = (locked.get(0).equals(c))? locked.get(1) : locked.get(0); 173 this.fireGestureEvent(new PanTwoFingerEvent(this, MTGestureEvent.GESTURE_CANCELED, c.getCurrentTarget(), c, leftOverCursor, new Vector3D(0,0,0), c.getCurrentTarget().getViewingCamera())); 174 this.unLockAllCursors(); 175 logger.debug(this.getName() + " cursor:" + c.getId() + " cursor LOCKED. Was an active cursor in this gesture - we therefor have to stop this gesture!"); 176 } 177 } 178 179 180 181 @Override 182 public void cursorUnlocked(InputCursor c) { 183 logger.debug(this.getName() + " Recieved UNLOCKED signal for cursor ID: " + c.getId()); 184 185 if (getLockedCursors().size() >= 2){ //we dont need the unlocked cursor, gesture still in progress 186 return; 187 } 188 189 this.unLockAllCursors(); 190 191 List<InputCursor> availableCursors = getFreeComponentCursors(); 192 if (availableCursors.size() >= 2){ //we can try to resume the gesture 193 InputCursor firstCursor = availableCursors.get(0); 194 InputCursor secondCursor = getFarthestFreeCursorToLimited(firstCursor, detectRadius); 195 196 //See if we can obtain a lock on both cursors 197 if (this.canLock(firstCursor, secondCursor)){ 198 float newDistance = Vector3D.distance(firstCursor.getPosition(), secondCursor.getPosition()); 199 if (newDistance < detectRadius) {//Check if other cursor is in distance 200 this.getLock(firstCursor, secondCursor); 201 logger.debug(this.getName() + " we could lock cursors: " + firstCursor.getId() +", " + secondCursor.getId()); 202 logger.debug(this.getName() + " continue with different cursors (ID: " + firstCursor.getId() + ")" + " " + "(ID: " + secondCursor.getId() + ")"); 203 this.fireGestureEvent(new PanTwoFingerEvent(this, MTGestureEvent.GESTURE_RESUMED, c.getCurrentTarget(), firstCursor, secondCursor, new Vector3D(0,0,0), c.getCurrentTarget().getViewingCamera())); 204 }else{ 205 logger.debug(this.getName() + " distance was too great between cursors: " + firstCursor.getId() +", " + secondCursor.getId() + " distance: " + newDistance); 206 } 207 }else{ 208 logger.debug(this.getName() + " we could NOT lock cursors: " + firstCursor.getId() +", " + secondCursor.getId()); 209 } 210 } 211 } 212 213 214 215 216 /** 217 * Gets the new translation. 218 * 219 * @param comp the comp 220 * @param movingCursor the moving cursor 221 * @param otherCursor the other cursor 222 * 223 * @return the new translation 224 */ 225 private Vector3D getNewTranslation(IMTComponent3D comp, InputCursor movingCursor, InputCursor otherCursor){ 226 Vector3D fromFirstFinger = ToolsGeometry.getRayPlaneIntersection( 227 Tools3D.getCameraPickRay(applet, comp.getViewingCamera(), movingCursor.getPreviousEvent().getX(), movingCursor.getPreviousEvent().getY()), 228 planeNormal, 229 pointInPlane); 230 231 Vector3D fromSecondFinger = ToolsGeometry.getRayPlaneIntersection( 232 Tools3D.getCameraPickRay(applet, comp.getViewingCamera(), otherCursor.getCurrentEvent().getX(), otherCursor.getCurrentEvent().getY()), 233 planeNormal, 234 pointInPlane); 235 236 Vector3D oldMiddlePoint = getMiddlePointBetweenFingers(fromSecondFinger, fromFirstFinger); 237 238 Vector3D toFirstFinger = ToolsGeometry.getRayPlaneIntersection( 239 Tools3D.getCameraPickRay(applet, comp.getViewingCamera(), movingCursor.getCurrentEvent().getX(), movingCursor.getCurrentEvent().getY()), 240 planeNormal, 241 pointInPlane); 242 243 Vector3D newMiddlePoint = getMiddlePointBetweenFingers(toFirstFinger , fromSecondFinger); 244 Vector3D distance = newMiddlePoint.getSubtracted(oldMiddlePoint); 245 return distance; 246 } 247 248 249 /** 250 * Gets the middle point between fingers. 251 * 252 * @param firstFinger the first finger 253 * @param secondFinger the second finger 254 * 255 * @return the middle point between fingers 256 */ 257 private Vector3D getMiddlePointBetweenFingers(Vector3D firstFinger, Vector3D secondFinger){ 258 Vector3D bla = secondFinger.getSubtracted(firstFinger); //= direction vector of 1. to 2. finger 259 bla.scaleLocal(0.5f); //take the half 260 return (new Vector3D(firstFinger.getX() + bla.getX(), firstFinger.getY() + bla.getY(), firstFinger.getZ() + bla.getZ())); 261 } 262 263 264 265 @Override 266 public String getName() { 267 return "two finger pan detector"; 268 } 269 270 271 272 273}