PageRenderTime 143ms CodeModel.GetById 115ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 1ms

/src/org/mt4j/input/inputProcessors/componentProcessors/rotateProcessor/RotateProcessor.java

http://mt4j.googlecode.com/
Java | 468 lines | 266 code | 82 blank | 120 comment | 45 complexity | 41945272f45691541544d66099e0fe47 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.rotateProcessor;
 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.inputData.MTFingerInputEvt;
 26import org.mt4j.input.inputProcessors.IInputProcessor;
 27import org.mt4j.input.inputProcessors.MTGestureEvent;
 28import org.mt4j.input.inputProcessors.componentProcessors.AbstractComponentProcessor;
 29import org.mt4j.input.inputProcessors.componentProcessors.AbstractCursorProcessor;
 30import org.mt4j.util.math.Vector3D;
 31
 32import processing.core.PApplet;
 33
 34/**
 35 * The Class RotateProcessor. Rotation multitouch gesture.
 36 * Fires RotateEvent gesture events.
 37 * @author Christopher Ruff
 38 */
 39public class RotateProcessor extends AbstractCursorProcessor {
 40
 41	/** The applet. */
 42	private PApplet applet;
 43	
 44	/** The rc. */
 45	private RotationContext rc;
 46	
 47	/** The drag plane normal. */
 48	private Vector3D dragPlaneNormal;
 49	
 50	
 51	public RotateProcessor(PApplet graphicsContext){
 52		this(graphicsContext, false);
 53	}
 54	
 55	/**
 56	 * Instantiates a new rotate processor.
 57	 * 
 58	 * @param graphicsContext the graphics context
 59	 */
 60	public RotateProcessor(PApplet graphicsContext, boolean stopEventPropagation){
 61		super(stopEventPropagation);
 62		this.applet = graphicsContext;
 63		this.dragPlaneNormal = new Vector3D(0,0,1);
 64		this.setLockPriority(2);
 65	}
 66	
 67	
 68	
 69	@Override
 70	public void cursorStarted(InputCursor newCursor, AbstractCursorInputEvt positionEvent) {
 71		IMTComponent3D comp = positionEvent.getTarget();
 72		logger.debug(this.getName() + " INPUT_STARTED, Cursor: " + newCursor.getId());
 73		
 74		List<InputCursor> alreadyLockedCursors = getLockedCursors();
 75		if (alreadyLockedCursors.size() >= 2){ //gesture with 2 fingers already in progress
 76			//TODO get the 2 cursors which are most far away from each other
 77			InputCursor firstCursor = alreadyLockedCursors.get(0);
 78			InputCursor secondCursor = alreadyLockedCursors.get(1);
 79			//Check if the new finger is more far away, so use that one
 80			if (isCursorDistanceGreater(firstCursor, secondCursor, newCursor) && canLock(firstCursor, newCursor)){
 81				RotationContext newContext = new RotationContext(firstCursor, newCursor, comp);
 82				if (!newContext.isGestureAborted()){ //Check if we could start gesture (ie. if fingers on component)
 83					rc = newContext;
 84					//Lock new Second cursor
 85					this.getLock(firstCursor, newCursor);
 86					this.unLock(secondCursor);
 87					logger.debug(this.getName() + " we could lock cursors: " + firstCursor.getId() +", and more far away cursor " + newCursor.getId());
 88				}else{
 89					logger.debug(this.getName() + " we could NOT exchange new second cursor - cursor not on component: " + firstCursor.getId() +", " + secondCursor.getId());
 90				}
 91			}else{
 92				logger.debug(this.getName() + " has already enough cursors for this gesture and the new cursors distance to the first one ist greater (or we dont have the priority to lock it) - adding to unused ID:" + newCursor.getId());
 93			}
 94		}else{ //no gesture in progress yet
 95			List<InputCursor> availableCursors = getFreeComponentCursors();
 96			logger.debug(this.getName() + " Available cursors: " + availableCursors.size());
 97			if (availableCursors.size() >= 2){
 98				InputCursor otherCursor = getFarthestFreeComponentCursorTo(newCursor);
 99//				logger.debug(this.getName() + " already had 1 unused cursor - we can try start gesture! used Cursor ID:" + otherCursor.getId() + " and new cursor ID:" + newCursor.getId());
100				
101				if (this.canLock(otherCursor, newCursor)){ //TODO remove check, since alreday checked in getAvailableComponentCursors()?
102					rc = new RotationContext(otherCursor, newCursor, comp);
103					if (!rc.isGestureAborted()){
104						this.getLock(otherCursor, newCursor);
105						logger.debug(this.getName() + " we could lock both cursors!");
106						this.fireGestureEvent(new RotateEvent(this, MTGestureEvent.GESTURE_STARTED, positionEvent.getCurrentTarget(), otherCursor, newCursor, Vector3D.ZERO_VECTOR, rc.getRotationPoint(), 0f));
107					}else{
108						logger.debug(this.getName() + " gesture aborted, probably at least 1 finger not on component!");
109						rc = null;
110					}
111				}else{
112					logger.debug(this.getName() + " we could NOT lock both cursors!");
113				}
114			}else{
115				logger.debug(this.getName() + " still missing a cursor to start gesture");
116			}
117		}
118	}
119
120
121	@Override
122	public void cursorUpdated(InputCursor m, AbstractCursorInputEvt positionEvent) {
123		List<InputCursor> alreadyLockedCursors = getLockedCursors();
124		if (rc != null && alreadyLockedCursors.size() == 2 && alreadyLockedCursors.contains(m)){
125			float rotationAngleDegrees = rc.updateAndGetRotationAngle(m);
126			this.fireGestureEvent(new RotateEvent(this, MTGestureEvent.GESTURE_UPDATED, positionEvent.getCurrentTarget(), rc.getPinFingerCursor(), rc.getRotateFingerCursor(), Vector3D.ZERO_VECTOR, rc.getRotationPoint(), rotationAngleDegrees));
127		}
128	}
129
130	
131	@Override
132	public void cursorEnded(InputCursor c, AbstractCursorInputEvt positionEvent) {
133		IMTComponent3D comp = positionEvent.getTarget();
134		logger.debug(this.getName() + " INPUT_ENDED RECIEVED - CURSOR: " + c.getId());
135		logger.debug("Rotate ended -> Active cursors: " + getCurrentComponentCursors().size() + " Available cursors: " + getFreeComponentCursors().size() +  " Locked cursors: " + getLockedCursors().size());
136		if (getLockedCursors().contains(c)){
137			InputCursor firstCursor = rc.getFirstCursor(); 
138			InputCursor secondCursor = rc.getSecondCursor();
139			if (firstCursor.equals(c) || secondCursor.equals(c)){ //The leaving cursor was used by the processor
140				InputCursor leftOverCursor = firstCursor.equals(c) ? secondCursor : firstCursor;
141				InputCursor futureCursor = getFarthestFreeCursorTo(leftOverCursor);
142				if (futureCursor != null){ //already checked in getFartherstAvailableCursor() if we can lock it
143					RotationContext newContext = new RotationContext(futureCursor, leftOverCursor, comp);
144					if (!newContext.isGestureAborted()){
145						rc = newContext;
146						this.getLock(leftOverCursor, futureCursor);
147						logger.debug(this.getName() + " continue with different cursors (ID: " + futureCursor.getId() + ")" + " " + "(ID: " + leftOverCursor.getId() + ")");
148					}else{ //couldnt start gesture - cursor's not on component 
149						this.endGesture(leftOverCursor, positionEvent, firstCursor, secondCursor);
150					}
151				}else{ //we cant use another cursor  - End gesture
152					this.endGesture(leftOverCursor, positionEvent, firstCursor, secondCursor);
153				}
154				this.unLock(c); 
155			}
156		}
157	}
158	
159	private void endGesture(InputCursor leftOverCursor, AbstractCursorInputEvt positionEvent, InputCursor firstCursor, InputCursor secondCursor){
160		this.unLock(leftOverCursor);
161		this.fireGestureEvent(new RotateEvent(this, MTGestureEvent.GESTURE_ENDED, positionEvent.getCurrentTarget(), firstCursor, secondCursor, Vector3D.ZERO_VECTOR, rc.getRotationPoint(), 0));
162		this.rc = null;
163	}
164	
165
166	@Override
167	public void cursorLocked(InputCursor c, IInputProcessor lockingAnalyzer) {
168		if (lockingAnalyzer instanceof AbstractComponentProcessor){
169			logger.debug(this.getName() + " Recieved CURSOR LOCKED by (" + ((AbstractComponentProcessor)lockingAnalyzer).getName()  + ") - cursor ID: " + c.getId());
170		}else{
171			logger.debug(this.getName() + " Recieved CURSOR LOCKED by higher priority signal - cursor ID: " + c.getId());
172		}
173		
174		if (rc != null && (rc.getFirstCursor().equals(c) || rc.getSecondCursor().equals(c))){
175			//TODO do we have to unlock the 2nd cursor, besides "c" ??
176			this.unLockAllCursors();
177			//FIXME TEST
178			this.fireGestureEvent(new RotateEvent(this, MTGestureEvent.GESTURE_CANCELED, c.getCurrentTarget(), rc.getFirstCursor(), rc.getSecondCursor(), Vector3D.ZERO_VECTOR, rc.getRotationPoint(), 0));
179			rc = null;
180			logger.debug(this.getName() + " cursor:" + c.getId() + " CURSOR LOCKED. Was an active cursor in this gesture!");
181		}
182	}
183
184	
185	@Override
186	public void cursorUnlocked(InputCursor c) {
187		logger.debug(this.getName() + " Recieved UNLOCKED signal for cursor ID: " + c.getId());
188		
189		if (getLockedCursors().size() >= 2){ //we dont need the unlocked cursor, gesture still in progress
190			return;
191		}
192		
193		//Dont we have to unlock the cursors if there could still 1 be locked?
194		//-> actually we always lock 2 cursors at once so should never be only 1 locked, but just for safety..
195		this.unLockAllCursors();
196
197		List<InputCursor> availableCursors = getFreeComponentCursors();
198		if (availableCursors.size() >= 2){ //we can try to resume the gesture
199			InputCursor firstCursor = availableCursors.get(0);
200			InputCursor secondCursor = getFarthestFreeComponentCursorTo(firstCursor);
201
202			//See if we can obtain a lock on both cursors
203			IMTComponent3D comp = firstCursor.getFirstEvent().getTarget();
204			RotationContext newContext = new RotationContext(firstCursor, secondCursor, comp);
205			if (!newContext.isGestureAborted()){ //Check if we could start gesture (i.e. if fingers on component)
206				rc = newContext;
207				this.getLock(firstCursor, secondCursor);
208				//FIXME TEST
209				this.fireGestureEvent(new RotateEvent(this, MTGestureEvent.GESTURE_RESUMED, c.getCurrentTarget(), firstCursor, secondCursor, Vector3D.ZERO_VECTOR, rc.getRotationPoint(), 0f));
210				logger.debug(this.getName() + " we could lock cursors: " + firstCursor.getId() +", " + secondCursor.getId());
211			}else{
212				rc = null;
213				logger.debug(this.getName() + " we could NOT resume gesture - cursors not on component: " + firstCursor.getId() +", " + secondCursor.getId());
214			}
215		}
216	}
217	
218	
219	
220	
221	
222	/**
223	 * The Class RotationContext.
224	 */
225	private class RotationContext {
226
227		/** The pin finger start. */
228		private Vector3D pinFingerStart;
229
230		/** The pin finger last. */
231		private Vector3D pinFingerLast;
232
233		/** The pin finger new. */
234		private Vector3D pinFingerNew;
235
236		/** The rotate finger start. */
237		private Vector3D rotateFingerStart;
238
239		/** The rotate finger last. */
240		private Vector3D rotateFingerLast;
241
242		/** The rotate finger new. */
243		private Vector3D rotateFingerNew;
244
245		/** The last rotation vect. */
246		private Vector3D lastRotationVect;
247
248		/** The object. */
249		private IMTComponent3D object;
250
251		/** The rotation point. */
252		private Vector3D rotationPoint;
253
254		/** The pin finger cursor. */
255		private InputCursor pinFingerCursor; 
256
257		/** The rotate finger cursor. */
258		private InputCursor rotateFingerCursor; 
259
260		private boolean gestureAborted;
261
262		/**
263		 * Instantiates a new rotation context.
264		 * 
265		 * @param pinFingerCursor the pin finger cursor
266		 * @param rotateFingerCursor the rotate finger cursor
267		 * @param object the object
268		 */
269		public RotationContext(InputCursor pinFingerCursor, InputCursor rotateFingerCursor, IMTComponent3D object){
270			this.pinFingerCursor = pinFingerCursor;
271			this.rotateFingerCursor = rotateFingerCursor;
272
273//			Vector3D interPoint = getIntersection(applet, object, pinFingerCursor);
274			Vector3D interPoint = getIntersection(applet, pinFingerCursor.getCurrentEvent().getCurrentTarget(), pinFingerCursor);
275			if (interPoint !=null)
276				pinFingerNew = interPoint;
277			else{
278				logger.warn(getName() + " Pinfinger NEW = NULL");
279				pinFingerNew = new Vector3D();
280				gestureAborted = true;
281			}
282
283			//Use lastEvent when resuming with another cursor that started long ago
284//			Vector3D interPointRot = getIntersection(applet, object, rotateFingerCursor);
285			Vector3D interPointRot = getIntersection(applet, object, rotateFingerCursor);
286
287			if (interPointRot !=null)
288				rotateFingerStart = interPointRot;
289			else{
290				logger.warn(getName() + " rotateFingerStart = NULL");
291				rotateFingerStart = new Vector3D();
292				//TODO ABORT THE Rotation HERE!
293				gestureAborted = true;
294			}
295
296			this.pinFingerStart = pinFingerNew.getCopy(); 
297
298			this.pinFingerLast	= pinFingerStart.getCopy(); 
299
300			this.rotateFingerLast	= rotateFingerStart.getCopy();
301			this.rotateFingerNew	= rotateFingerStart.getCopy();	
302
303			this.object = object;
304
305			this.rotationPoint = pinFingerNew.getCopy();
306
307			//Get the rotation vector for reference for the next rotation
308			this.lastRotationVect = rotateFingerStart.getSubtracted(pinFingerNew);
309
310			//FIXME REMOVE!
311//			dragPlaneNormal = ((MTPolygon)object).getNormal();
312//			logger.debug("DragNormal: " + dragPlaneNormal);
313		}
314
315		/**
316		 * Update and get rotation angle.
317		 * 
318		 * @param moveCursor the move cursor
319		 * 
320		 * @return the float
321		 */
322		public float updateAndGetRotationAngle(InputCursor moveCursor) {
323//			/*
324			float newAngleRad;
325			float newAngleDegrees;
326
327			//save the current pinfinger location as the old one
328			this.pinFingerLast = this.pinFingerNew;
329			//save the current pinfinger location as the old one
330			this.rotateFingerLast = this.rotateFingerNew;
331
332			//Check which finger moved and has to be updated
333			if (moveCursor.equals(pinFingerCursor)){
334				updatePinFinger();
335			}
336			else if (moveCursor.equals(rotateFingerCursor)){
337				updateRotateFinger();
338			}
339
340			//FIXME REMOVE!
341//			dragPlaneNormal = ((MTPolygon)object).getNormal();
342//			logger.debug("DragNormal: " + dragPlaneNormal);
343
344			//TODO drop Z values after that?
345			//calculate the vector between the rotation finger vectors
346			Vector3D currentRotationVect = rotateFingerNew.getSubtracted(pinFingerNew).normalizeLocal(); //TEST normalize rotation vector
347
348			//calculate the angle between the rotaion finger vectors
349			newAngleRad 	= Vector3D.angleBetween(lastRotationVect, currentRotationVect);
350			newAngleDegrees = (float)Math.toDegrees(newAngleRad); 
351
352			//FIXME EXPERIMENTAL BECAUSE ANGLEBETWEEN GIVES ROTATIONS SOMETIMES WHEN BOTH VECTORS ARE EQUAL!?
353			if (rotateFingerLast.equalsVector(rotateFingerNew) && pinFingerLast.equalsVector(pinFingerNew)){
354				//logger.debug("Angle gleich lassen");
355				newAngleDegrees = 0.0f;
356			}else{
357				//logger.debug("Neuer Angle: " + newAngleDegrees);
358			}
359
360//			logger.debug("lastRotVect: " + lastRotationVect + " currentROtationVect: " + currentRotationVect + " Deg: " + newAngleDegrees);
361
362			Vector3D cross = lastRotationVect.getCross(currentRotationVect);
363			//Get the direction of rotation
364			if (cross.getZ() < 0){
365				newAngleDegrees*=-1;
366			}
367
368			//Check if the current and last rotation vectors are equal or not 
369			if (!Float.isNaN(newAngleDegrees)/*!String.valueOf(newAngleDegrees).equalsIgnoreCase("NaN")*/){
370				//if (newAngleDegrees != Float.NaN){ //if the vectors are equal rotationangle is NAN?
371				lastRotationVect = currentRotationVect;
372				return newAngleDegrees;
373			}else{
374				//lastRotationVect = currentRotationVect;
375				return 0;
376			}
377//			*/
378//			return 0;
379		}
380
381
382		/**
383		 * Update rotate finger.
384		 */
385		private void updateRotateFinger(){
386			if (object == null || object.getViewingCamera() == null){ //IF component was destroyed while gesture still active
387				this.gestureAborted = true;
388				return ;
389			}
390			Vector3D newRotateFingerPos = getPlaneIntersection(applet, dragPlaneNormal, rotateFingerStart.getCopy(), rotateFingerCursor);
391			//Update the field
392			if (newRotateFingerPos != null){
393				this.rotateFingerNew = newRotateFingerPos;
394				this.rotationPoint = pinFingerNew; 
395			}else{
396				logger.error(getName() + " new newRotateFinger Pos = null at update");
397			}
398		}
399
400
401		/**
402		 * Update pin finger.
403		 */
404		private void updatePinFinger(){  
405			if (object == null){ //IF component was destroyed while gesture still active
406				this.gestureAborted = true;
407				return;
408			}
409			Vector3D newPinFingerPos = getPlaneIntersection(applet, dragPlaneNormal, pinFingerStart.getCopy(), pinFingerCursor);
410			
411			if (newPinFingerPos != null){
412				// Update pinfinger with new position
413				this.pinFingerNew = newPinFingerPos;
414				//Set the Rotation finger as the rotation point because the pinfinger was moved
415				this.rotationPoint = rotateFingerNew; //REMOVE!!? made because of scale
416			}else{
417				logger.error(getName() + " new PinFinger Pos = null at update");
418			}
419		}
420
421
422		/**
423		 * Gets the rotation point.
424		 * 
425		 * @return the rotation point
426		 */
427		public Vector3D getRotationPoint() {
428			return rotationPoint;
429		}
430
431		/**
432		 * Gets the pin finger cursor.
433		 * 
434		 * @return the pin finger cursor
435		 */
436		public InputCursor getPinFingerCursor() {
437			return pinFingerCursor;
438		}
439
440		/**
441		 * Gets the rotate finger cursor.
442		 * 
443		 * @return the rotate finger cursor
444		 */
445		public InputCursor getRotateFingerCursor() {
446			return rotateFingerCursor;
447		}
448
449		public boolean isGestureAborted() {
450			return gestureAborted;
451		}
452		
453		public InputCursor getFirstCursor(){
454			return this.pinFingerCursor;
455		}
456		
457		public InputCursor getSecondCursor(){
458			return this.rotateFingerCursor;
459		}
460
461	}
462	
463	@Override
464	public String getName() {
465		return "Rotate Processor";
466	}
467
468}