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