PageRenderTime 55ms CodeModel.GetById 28ms app.highlight 21ms RepoModel.GetById 2ms app.codeStats 0ms

/src/org/mt4j/input/inputProcessors/componentProcessors/scaleProcessor/ScaleProcessor.java

http://mt4j.googlecode.com/
Java | 407 lines | 229 code | 75 blank | 103 comment | 42 complexity | 97bb892222070505ee77a6f79377746e 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.scaleProcessor;
 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 ScaleProcessor. 2-Finger Scale multi-touch gesture.
 36 * Fires ScaleEvent gesture events.
 37 * @author Christopher Ruff
 38 */
 39public class ScaleProcessor extends AbstractCursorProcessor {
 40	
 41	/** The applet. */
 42	private PApplet applet;
 43	
 44	/** The scale context. */
 45	private ScaleContext sc;
 46	
 47	public ScaleProcessor(PApplet graphicsContext){
 48		this(graphicsContext, false);
 49	}
 50	
 51	/**
 52	 * Instantiates a new scale processor.
 53	 * 
 54	 * @param graphicsContext the graphics context
 55	 */
 56	public ScaleProcessor(PApplet graphicsContext, boolean stopPropagation){
 57		super(stopPropagation);
 58		this.applet = graphicsContext;
 59		this.setLockPriority(2);
 60	}
 61	
 62	
 63
 64	@Override
 65	public void cursorStarted(InputCursor newCursor, AbstractCursorInputEvt fEvt) {
 66		IMTComponent3D comp = fEvt.getTarget();
 67		logger.debug(this.getName() + " INPUT_STARTED, Cursor: " + newCursor.getId());
 68		
 69		List<InputCursor> alreadyLockedCursors = getLockedCursors();
 70		if (alreadyLockedCursors.size() >= 2){ //this gesture with 2 fingers already in progress
 71			//TODO get the 2 cursors which are most far away from each other
 72			InputCursor firstCursor = alreadyLockedCursors.get(0);
 73			InputCursor secondCursor = alreadyLockedCursors.get(1);
 74			if (isCursorDistanceGreater(firstCursor, secondCursor, newCursor) && canLock(firstCursor, newCursor)){
 75				ScaleContext newContext = new ScaleContext(firstCursor, newCursor, comp);
 76				if (!newContext.isGestureAborted()){ //Check if we could start gesture (ie. if fingers on component)
 77					sc = newContext;
 78					//Lock new Second cursor
 79					this.getLock(firstCursor, newCursor);
 80					this.unLock(secondCursor);
 81					logger.debug(this.getName() + " we could lock cursors: " + firstCursor.getId() +", and more far away cursor " + newCursor.getId());
 82				}else{
 83					logger.debug(this.getName() + " we could NOT exchange new second cursor - cursor not on component: " + firstCursor.getId() +", " + secondCursor.getId());
 84				}
 85			}else{
 86				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());
 87			}
 88		}else{ //no gesture in progress yet
 89			List<InputCursor> availableCursors = getFreeComponentCursors();
 90			logger.debug(this.getName() + " Available cursors: " + availableCursors.size());
 91			if (availableCursors.size() >= 2){
 92				InputCursor otherCursor = getFarthestFreeComponentCursorTo(newCursor);
 93//				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());
 94				
 95				if (this.canLock(otherCursor, newCursor)){ //TODO remove check, since alreday checked in getAvailableComponentCursors()?
 96					sc = new ScaleContext(otherCursor, newCursor, comp);
 97					if (!sc.isGestureAborted()){
 98						this.getLock(otherCursor, newCursor);
 99						logger.debug(this.getName() + " we could lock both cursors!");
100						this.fireGestureEvent(new ScaleEvent(this, MTGestureEvent.GESTURE_STARTED, fEvt.getCurrentTarget(), otherCursor, newCursor, 1, 1, 1, sc.getSecondFingerNewPos()));
101					}else{
102						logger.debug(this.getName() + " gesture aborted, probably at least 1 finger not on component!");
103						sc = null;
104					}
105				}else{
106					logger.debug(this.getName() + " we could NOT lock both cursors!");
107				}
108			}else{
109				logger.debug(this.getName() + " still missing a cursor to start gesture");
110			}
111		}
112	}
113
114
115
116	@Override
117	public void cursorUpdated(InputCursor m, AbstractCursorInputEvt fEvt) {
118		List<InputCursor> alreadyLockedCursors = getLockedCursors();
119		if (sc != null && alreadyLockedCursors.size() == 2 && alreadyLockedCursors.contains(m)){
120			float newFactor = sc.getUpdatedScaleFactor(m);
121			if (m.equals(sc.getFirstFingerCursor())){
122				this.fireGestureEvent(new ScaleEvent(this, MTGestureEvent.GESTURE_UPDATED, fEvt.getCurrentTarget(), sc.getFirstFingerCursor(), sc.getSecondFingerCursor(), newFactor, newFactor, 1, sc.getSecondFingerNewPos()));
123			}else{
124				this.fireGestureEvent(new ScaleEvent(this, MTGestureEvent.GESTURE_UPDATED, fEvt.getCurrentTarget(), sc.getFirstFingerCursor(), sc.getSecondFingerCursor(), newFactor, newFactor, 1, sc.getFirstFingerNewPos()));
125			}
126		}
127	}
128
129	
130	
131	@Override
132	public void cursorEnded(InputCursor c, AbstractCursorInputEvt fEvt) {
133		IMTComponent3D comp = fEvt.getTarget();
134		logger.debug(this.getName() + " INPUT_ENDED -> Active cursors: " + getCurrentComponentCursors().size() + " Available cursors: " + getFreeComponentCursors().size() +  " Locked cursors: " + getLockedCursors().size());
135		if (getLockedCursors().contains(c)){
136			InputCursor firstCursor = sc.getFirstFingerCursor();
137			InputCursor secondCursor = sc.getSecondFingerCursor();
138			if (firstCursor.equals(c) || secondCursor.equals(c)){ //The leaving cursor was used by the processor
139				InputCursor leftOverCursor = firstCursor.equals(c) ? secondCursor : firstCursor;
140				//TODO check if cursor is on component, else take next farthest cursor
141				InputCursor futureCursor = getFarthestFreeCursorTo(leftOverCursor);
142
143				if (futureCursor != null){ //already checked in getFartherstAvailableCursor() if we can lock it
144					ScaleContext newContext = new ScaleContext(futureCursor, leftOverCursor, comp);
145					if (!newContext.isGestureAborted()){
146						sc = newContext;
147						this.getLock(leftOverCursor, futureCursor);
148						logger.debug(this.getName() + " continue with different cursors (ID: " + futureCursor.getId() + ")" + " " + "(ID: " + leftOverCursor.getId() + ")");
149					}else{ //couldnt start gesture - cursor's not on component 
150						this.endGesture(leftOverCursor, fEvt, firstCursor, secondCursor);
151					}
152				}else{ //we cant use another cursor  - End gesture
153					this.endGesture(leftOverCursor, fEvt, firstCursor, secondCursor);
154				}
155				this.unLock(c); 
156			}
157		}
158	}
159
160	private void endGesture(InputCursor leftOverCursor, AbstractCursorInputEvt fEvt, InputCursor firstCursor, InputCursor secondCursor){
161		this.unLock(leftOverCursor);
162		this.fireGestureEvent(new ScaleEvent(this, MTGestureEvent.GESTURE_ENDED, fEvt.getCurrentTarget(), firstCursor, secondCursor, 1, 1, 1, sc.getFirstFingerNewPos()));
163		this.sc = null;
164	}
165	
166	
167	@Override
168	public void cursorLocked(InputCursor c, IInputProcessor lockingAnalyzer) {
169		if (lockingAnalyzer instanceof AbstractComponentProcessor){
170			logger.debug(this.getName() + " Recieved MOTION LOCKED by (" + ((AbstractComponentProcessor)lockingAnalyzer).getName()  + ") - cursor ID: " + c.getId());
171		}else{
172			logger.debug(this.getName() + " Recieved MOTION LOCKED by higher priority signal - cursor ID: " + c.getId());
173		}
174		
175		if (sc != null && (sc.getFirstFingerCursor().equals(c) || sc.getSecondFingerCursor().equals(c))){ //Check if gesture was in progress
176			this.unLockAllCursors();
177			//FIXME TEST
178			this.fireGestureEvent(new ScaleEvent(this, MTGestureEvent.GESTURE_CANCELED, c.getCurrentTarget(), sc.getFirstFingerCursor(), sc.getSecondFingerCursor(), 1, 1, 1, sc.getFirstFingerNewPos()));
179			sc = 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			ScaleContext newContext = new ScaleContext(firstCursor, secondCursor, comp);
205			if (!newContext.isGestureAborted()){ //Check if we could start gesture (ie. if fingers on component)
206				sc = newContext;
207				this.getLock(firstCursor, secondCursor);
208				//FIXME TEST
209				this.fireGestureEvent(new ScaleEvent(this, MTGestureEvent.GESTURE_RESUMED, c.getCurrentTarget(), firstCursor, secondCursor, 1, 1, 1, sc.getSecondFingerNewPos()));
210				logger.debug(this.getName() + " we could lock cursors: " + firstCursor.getId() +", " + secondCursor.getId());
211			}else{
212				sc = 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 ScaleContext.
224	 */
225	private class ScaleContext {
226		
227		/** The first finger cursor. */
228		private InputCursor firstFingerCursor; 
229		
230		/** The second finger cursor. */
231		private InputCursor secondFingerCursor; 
232		
233		/** The object. */
234		private IMTComponent3D object;
235		
236		/** The first finger new pos. */
237		private Vector3D firstFingerNewPos;
238		
239		/** The second finger new pos. */
240		private Vector3D secondFingerNewPos;
241		
242		/** The second finger start pos. */
243		private Vector3D secondFingerStartPos;
244		
245		/** The last scale distance. */
246		private float lastScaleDistance;
247		
248		/** The scale plane normal. */
249		private Vector3D scalePlaneNormal;
250		
251		/** The first finger start pos. */
252		private Vector3D firstFingerStartPos;
253
254		private boolean gestureAborted;
255		
256		
257		/**
258		 * Instantiates a new scale context.
259		 * 
260		 * @param firstFingerCursor the first finger cursor
261		 * @param secondFingerCursor the second finger cursor
262		 * @param object the object
263		 */
264		public ScaleContext(InputCursor firstFingerCursor, InputCursor secondFingerCursor, IMTComponent3D object) {
265			super();
266			this.firstFingerCursor = firstFingerCursor;
267			this.secondFingerCursor = secondFingerCursor;
268			this.object = object;
269			
270			//irgendwo vorher checken ob der 1. finger ?berhaupt noch ?ber dem obj ist? ist nur sicher der fall wenn mit 1 finger gedraggt wird..
271			Vector3D interPoint = getIntersection(applet, object, firstFingerCursor);
272			if (interPoint !=null)
273				firstFingerNewPos = interPoint;
274			else{
275				logger.warn(getName() + " firstFingerNewPos NEW = NULL");
276				this.firstFingerNewPos	= new Vector3D();
277				gestureAborted = true;
278			}
279			
280			Vector3D scndInterPoint = getIntersection(applet, object, secondFingerCursor);
281			if (scndInterPoint !=null)
282				secondFingerNewPos = scndInterPoint;
283			else{
284				logger.warn(getName() + " secondFingerNewPos NEW = NULL");
285				secondFingerNewPos = new Vector3D();
286				gestureAborted = true;
287			}
288			
289			firstFingerStartPos = firstFingerNewPos.getCopy();
290			secondFingerStartPos = secondFingerNewPos.getCopy();
291			
292			this.lastScaleDistance = Vector3D.distance(firstFingerNewPos, secondFingerNewPos);
293			 //Prevent scaling to 0 if both fingers are on the same position at scalstart
294			if (lastScaleDistance == 0.0)
295				lastScaleDistance = 1.0f;
296			
297			//TODO settable? get from objects? camera orthogonal?
298			scalePlaneNormal = new Vector3D(0,0,1);
299			/*
300			newFingerMiddlePos = getMiddlePointBetweenFingers();
301			oldFingerMiddlePos = newFingerMiddlePos.getCopy();
302			*/
303		}
304		
305		public boolean isGestureAborted() {
306			return gestureAborted;
307		}
308
309		/**
310		 * Gets the second finger cursor.
311		 * 
312		 * @return the second finger cursor
313		 */
314		public InputCursor getSecondFingerCursor() {
315			return this.secondFingerCursor;
316		}
317
318		/**
319		 * Gets the first finger cursor.
320		 * 
321		 * @return the first finger cursor
322		 */
323		public InputCursor getFirstFingerCursor() {
324			return this.firstFingerCursor;
325		}
326
327		
328		/**
329		 * Gets the updated scale factor.
330		 * 
331		 * @param m the m
332		 * 
333		 * @return the updated scale factor
334		 */
335		public float getUpdatedScaleFactor(InputCursor m){
336			if (object == null || object.getViewingCamera() == null){ //IF component was destroyed while gesture still active
337				this.gestureAborted = true;
338				return 1;
339			}
340			
341			//FIXME REMOVE!!
342//			scalePlaneNormal = ((MTPolygon)object).getNormal();
343//			logger.debug("scalePlaneNormal: " + scalePlaneNormal);
344//			/*
345			if (m.equals(firstFingerCursor)){ ///FIRST FINGER MOVED!
346				Vector3D newFirstFingerPos = getPlaneIntersection(applet, scalePlaneNormal, firstFingerStartPos.getCopy(), firstFingerCursor);
347				
348				//Update the field
349				if (newFirstFingerPos != null)
350					this.firstFingerNewPos = newFirstFingerPos;
351			
352			}else if (m.equals(secondFingerCursor)){ ///SECOND FINGER MOVED!
353				Vector3D newSecondFingerPos = getPlaneIntersection(applet, scalePlaneNormal, secondFingerStartPos.getCopy(), secondFingerCursor);
354				
355//				//TODO dragplane aus den beiden fingern ableiten -> wenn obj schr?g im raum, dragplane entsprechend
356//				Vector3D newSecondFingerPos = ToolsIntersection.getRayPlaneIntersection(new Ray(rayStartPoint, newPointInRayDir), scalePlaneNormal, secondFingerStartPos.getCopy());
357				
358				//Update the field
359				if (newSecondFingerPos != null)
360					this.secondFingerNewPos = newSecondFingerPos;
361			}
362			
363			//IF THE FINGERS ARE ON THE SAME POSITION RETURN 1 SO THAT NOT SCALING IS DONE
364			//ELSE THE OBJECTS WILL DISAPPEAR, scaled to 0
365			if (firstFingerNewPos.equalsVector(secondFingerNewPos))
366				return 1.0f;
367			
368			float newScaleDistance = Vector3D.distance(firstFingerNewPos, secondFingerNewPos);
369			
370			float newScaleFactor = newScaleDistance/lastScaleDistance;
371			
372			lastScaleDistance = newScaleDistance;
373			
374			return newScaleFactor;
375//			*/
376//			return 1;
377		}
378		
379
380		/**
381		 * Gets the first finger new pos.
382		 * 
383		 * @return the first finger new pos
384		 */
385		public Vector3D getFirstFingerNewPos() {
386			return firstFingerNewPos;
387		}
388
389		/**
390		 * Gets the second finger new pos.
391		 * 
392		 * @return the second finger new pos
393		 */
394		public Vector3D getSecondFingerNewPos() {
395			return secondFingerNewPos;
396		}
397	}
398	
399	
400	@Override
401	public String getName() {
402		return "Scale Processor";
403	}
404
405
406
407}