/***********************************************************************
* mt4j Copyright (c) 2008 - 2009 Christopher Ruff, Fraunhofer-Gesellschaft All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j.input.inputProcessors.componentProcessors;
import java.util.ArrayList;
import java.util.List;
import org.mt4j.components.interfaces.IMTComponent3D;
import org.mt4j.input.inputData.ActiveCursorPool;
import org.mt4j.input.inputData.InputCursor;
import org.mt4j.input.inputData.MTFingerInputEvt;
import org.mt4j.input.inputData.MTInputEvent;
import org.mt4j.input.inputProcessors.IInputProcessor;
import org.mt4j.util.math.Tools3D;
import org.mt4j.util.math.Vector3D;
import processing.core.PApplet;
public abstract class AbstractCursorProcessor extends AbstractComponentProcessor{
private List<InputCursor> activeCursors;
/** The lock priority. */
private int lockPriority;
private ArrayList<InputCursor> activeCursorsWithEndedOnes;
public AbstractCursorProcessor(){
activeCursors = new ArrayList<InputCursor>();
activeCursorsWithEndedOnes = new ArrayList<InputCursor>();
this.lockPriority = 1;
}
@Override
public boolean isInterestedIn(MTInputEvent inputEvt) {
// return inputEvt instanceof AbstractCursorInputEvt;
return inputEvt instanceof MTFingerInputEvt
&& inputEvt.hasTarget();
}
@Override
public void preProcess(MTInputEvent inputEvent) {
super.preProcess(inputEvent);
MTFingerInputEvt posEvt = (MTFingerInputEvt)inputEvent;
InputCursor c = posEvt.getCursor();
switch (posEvt.getId()) {
case MTFingerInputEvt.INPUT_DETECTED:
activeCursors.add(c);
activeCursorsWithEndedOnes.add(c);
c.registerForLocking(this);
break;
case MTFingerInputEvt.INPUT_UPDATED:
break;
case MTFingerInputEvt.INPUT_ENDED:
activeCursors.remove(c);
c.unregisterForLocking(this);
break;
default:
break;
}
}
@Override
protected void processInputEvtImpl(MTInputEvent inputEvent) {
// AbstractCursorInputEvt posEvt = (AbstractCursorInputEvt)inputEvent;
MTFingerInputEvt posEvt = (MTFingerInputEvt)inputEvent;
InputCursor c = posEvt.getCursor();
switch (posEvt.getId()) {
case MTFingerInputEvt.INPUT_DETECTED:
// activeCursors.add(c);
// c.registerGeneralInterest(this);
cursorStarted(c, posEvt);
break;
case MTFingerInputEvt.INPUT_UPDATED:
cursorUpdated(c, posEvt);
break;
case MTFingerInputEvt.INPUT_ENDED:
// activeCursors.remove(c);
cursorEnded(c, posEvt);
// if (c.isLockedBy(this)){
unLock(c);
// }
// c.unregisterGeneralInterest(this);
activeCursorsWithEndedOnes.remove(c);
break;
default:
break;
}
}
/**
* Gets all active cursors which started on this component.
* It is not check whether this input processor could lock any of them.
* User <code>getAvailableComponentCursors()</code> instead for that.
*
* @return the active component cursors
*/
public List<InputCursor> getCurrentComponentCursors(){
return this.activeCursors;
}
public InputCursor[] getCurrentComponentCursorsArray(){
return this.activeCursors.toArray(new InputCursor[getCurrentComponentCursors().size()]);
}
//////////////////////////////////////////////////////////
//TODO check at getAvailable/fartherst etc cursor if cursor on component!? -> not usable if not! -> intersect cursor target? -> but what about canvas which has no intersection -> hardcode?
//TODO allow to subscribe to other application cursors??
//TODO method to get the 2 cursors which are farthest away from each other
//TODO getNearestAvailableCursorTo()
//TODO caution: if cursorLocked() is invoked we cant check if gesture used the cursor by checking
//if the cursor was in getLockedCurosor list because its already removed
//exclude INPUT_ENDED?
//FIXME this will also be cursors from scenes the component isnt even in!
//TODO we should only listen to InputRetargeter from the component's scene, and only get the onces which arent already
//targeted at this component..
public InputCursor[] getAllActiveApplicationCursors(){
return ActiveCursorPool.getInstance().getActiveCursors();
}
//TODO dont include the input_ended cursors!?
/**
* Returns all component cursors that are not yet locked but could be locked
* by this input processor.
* @return the free component cursors array
*/
public InputCursor[] getFreeComponentCursorsArray(){
List<InputCursor> freeCursors = getFreeComponentCursors();
return freeCursors.toArray(new InputCursor[freeCursors.size()]);
}
//TODO should we also check if the cursors are on the component? / input_ended?
//TODO dont include the input_ended cursors!?
/**
* Returns all component cursors that are not yet locked but could be locked
* by this input processor.
* @return the free component cursors
*/
public List<InputCursor> getFreeComponentCursors(){
List<InputCursor> activeCursorsOnComp = this.getCurrentComponentCursors();
List<InputCursor> freeCursors = new ArrayList<InputCursor>();
for (InputCursor inputCursor : activeCursorsOnComp) {
if (!inputCursor.isLockedBy(this) && inputCursor.canLock(this)){
freeCursors.add(inputCursor);
}
}
// return freeCursors.toArray(new InputCursor[freeCursors.size()]);
return freeCursors;
}
/**
* Return all the component cursors that this component processor has successfully locked.
* @return the locked cursors array
*/
public InputCursor[] getLockedCursorsArray(){
List<InputCursor> locked = getLockedCursors();
return locked.toArray(new InputCursor[locked.size()]);
}
/**
* Return all the component cursors that this component processor has successfully locked.
* @return the locked cursors
*/
public List<InputCursor> getLockedCursors(){
List<InputCursor> activeCursorsOnCompWithENDED = this.activeCursorsWithEndedOnes;
List<InputCursor> lockedCursors = new ArrayList<InputCursor>();
for (InputCursor inputCursor : activeCursorsOnCompWithENDED) {
if (inputCursor.isLockedBy(this)){
lockedCursors.add(inputCursor);
}
}
return lockedCursors;
}
/**
* Releases all cursors that this component input processor currently holds a lock on.
*/
public void unLockAllCursors(){
//FIXME we should also unlock the cursors that have input_ended, so that processors with lower priority can start the gesture and end it correctly aferwards
// List<InputCursor> activeCursorsOnComp = acp.getActiveComponentCursors();
List<InputCursor> activeCursorsOnCompWithENDED = this.activeCursorsWithEndedOnes;
for (InputCursor inputCursor : activeCursorsOnCompWithENDED) {
if (inputCursor.isLockedBy(this)){
unLock(inputCursor);
}
}
}
/**
* Returns the most far away component cursor to the specified cursor.
* Only returns a cursor if it is free to use and could be locked by this processor.
*
* @param cursor the cursor
* @return the farthest free component cursor to
*/
public InputCursor getFarthestFreeComponentCursorTo(InputCursor cursor){
return getFarthestFreeCursorTo(cursor, new InputCursor[]{});
}
/**
* Returns the most far away component cursor to the specified cursor.
* Only returns a cursor if it is free to use and could be locked by this processor.
*
* @param cursor the cursor
* @param excludedFromSearch the excluded from search
* @return the farthest free cursor to
*/
public InputCursor getFarthestFreeCursorTo(InputCursor cursor, InputCursor... excludedFromSearch){
float currDist = Float.MIN_VALUE;
InputCursor fartherstCursor = null;
Vector3D cursorPos = cursor.getPosition();
for (InputCursor currentCursor : this.getCurrentComponentCursors()) {
if (currentCursor.equals(cursor) || !currentCursor.canLock(this) || currentCursor.isLockedBy(this))
continue;
for (InputCursor excludedCursor : excludedFromSearch) {
if (currentCursor.equals(excludedCursor)){
continue;
}
}
float distanceToCurrentCursor = currentCursor.getPosition().distance2D(cursorPos);
if (distanceToCurrentCursor >= currDist || distanceToCurrentCursor == 0.0f){
currDist = distanceToCurrentCursor;
fartherstCursor = currentCursor;
}
}
return fartherstCursor;
}
/**
* Checks if the distance between a reference cursor and a cursor is greater than the distance to another cursor.
*
* @param reference the reference
* @param oldCursor the old cursor
* @param newCursor the new cursor
* @return true, if is cursor distance greater
*/
public boolean isCursorDistanceGreater(InputCursor reference, InputCursor oldCursor, InputCursor newCursor){
// float distanceToOldCursor = reference.getPosition().distance2D(oldCursor.getPosition());
// float distanceToNewCursor = reference.getPosition().distance2D(newCursor.getPosition());
// return distanceToNewCursor > distanceToOldCursor;
return getDistance(reference, newCursor) > getDistance(reference, oldCursor);
}
/**
* Gets the distance between two cursors.
*
* @param a the a
* @param b the b
* @return the distance
*/
public float getDistance(InputCursor a, InputCursor b){
return a.getPosition().distance2D(b.getPosition());
}
/**
* Gets the intersection point of a cursor and a specified component.
* Can return null if the cursor doesent intersect the component.
*
* @param app the app
* @param component the component
* @param c the c
* @return the intersection
*/
public Vector3D getIntersection(PApplet app, IMTComponent3D component, InputCursor c){
return component.getIntersectionGlobal(Tools3D.getCameraPickRay(app, component, c));
}
///////////////////////////////////////////////////////
/**
* Gets the input cursor locking priority.
*
* @return the input cursor locking priority
*/
public int getLockPriority() {
return lockPriority;
}
/**
* Sets the input cursor locking priority.
*
* @param gesturePriority the new input cursor locking priority
*/
public void setLockPriority(int gesturePriority) {
this.lockPriority = gesturePriority;
}
/**
* Checks if this input processor would have the
* sufficient priority to lock the specified input cursors.
*
* @param cursors the cursors
*
* @return true, if successful
*/
protected boolean canLock(InputCursor... cursors){
int locked = 0;
for (int i = 0; i < cursors.length; i++) {
InputCursor m = cursors[i];
if (m.canLock(this)){
locked++;
}
}
return locked == cursors.length;
}
/**
* Locks the cursor with this processor if the processors lock priority
* is higher or equal than the current lock priority of this cursor.
*
* @param cursors the cursors
*
* @return true, if all specified cursors could get locked
*/
protected boolean getLock(InputCursor... cursors){
int locked = 0;
for (int i = 0; i < cursors.length; i++) {
InputCursor m = cursors[i];
if (m.getLock(this)){
locked++;
}
}
return locked == cursors.length;
}
/**
* Unlocks the specified cursors if they are not longer used by this processor.
* If the priority by which the cursors are locked changes by that,
* the <code>cursorUnlocked</code> method is invoked on processors
* with a lower priority who by that get a chance to lock this cursor again.
*
* @param cursors the cursors
*/
protected void unLock(InputCursor... cursors){
for (int i = 0; i < cursors.length; i++) {
InputCursor inputCursor = cursors[i];
inputCursor.unlock(this);
}
}
@Override
public int compareTo(AbstractComponentProcessor o) {
if (o instanceof AbstractCursorProcessor) {
AbstractCursorProcessor o2 = (AbstractCursorProcessor) o;
if (this.getLockPriority() < o2.getLockPriority()){
return -1;
}else if (this.getLockPriority() > o2.getLockPriority()){
return 1;
}else{
if (!this.equals(o2)
&& this.getLockPriority() == o2.getLockPriority()
){
return -1;
}
return 0;
}
}else{
return 1;
}
}
/**
* This method is called if a input processor with a higher locking-priority than this one sucessfully
* locked the specified cursor. If this cursor was used in this input processor, we have to stop using it until it
* is unlocked by the other processor!
*
* @param cursor the cursor
* @param lockingprocessor the locking processor
*/
abstract public void cursorLocked(InputCursor cursor, IInputProcessor lockingprocessor);
/**
* This method is called if a input processor with a higher locking-priority than this one removes his lock on the specified
* cursor (i.e. because the conditions for continuing the gesture aren't met anymore). This gives this input processor the chance to
* see if it can use the cursor and try to lock it again.
*
* @param cursor the cursor
*/
abstract public void cursorUnlocked(InputCursor cursor);
/**
* Called when a new cursor has been detected.
* @param inputCursor
* @param currentEvent
*/
abstract public void cursorStarted(InputCursor inputCursor, MTFingerInputEvt currentEvent);
/**
* Called when a cursor has been updated with a new input event.
* @param inputCursor
* @param currentEvent
*/
abstract public void cursorUpdated(InputCursor inputCursor, MTFingerInputEvt currentEvent);
/**
* Called when a cursor has been removed.
* @param inputCursor
* @param currentEvent
*/
abstract public void cursorEnded(InputCursor inputCursor, MTFingerInputEvt currentEvent);
}