/***********************************************************************
* mt4j Copyright (c) 2008 - 2009, C.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.inputSources;
import java.util.Collection;
import java.util.Iterator;
import java.util.WeakHashMap;
import org.mt4j.MTApplication;
import org.mt4j.components.MTCanvas;
import org.mt4j.components.visibleComponents.shapes.MTEllipse;
import org.mt4j.input.inputData.ActiveCursorPool;
import org.mt4j.input.inputData.InputCursor;
import org.mt4j.input.inputData.MTFingerInputEvt;
import org.mt4j.sceneManagement.ISceneChangeListener;
import org.mt4j.sceneManagement.Iscene;
import org.mt4j.sceneManagement.SceneChangeEvent;
import org.mt4j.util.MT4jSettings;
import org.mt4j.util.MTColor;
import org.mt4j.util.camera.Icamera;
import org.mt4j.util.camera.MTCamera;
import org.mt4j.util.manyMouse.ManyMouse;
import org.mt4j.util.manyMouse.ManyMouseEvent;
import org.mt4j.util.math.Vector3D;
/**
* The Class MultipleMiceInputSource.
* @author Christopher Ruff
*/
public class MultipleMiceInputSource extends AbstractInputSource {
/** The mice. */
private int mice;
/** The event. */
private ManyMouseEvent event;
/** The device to mouse info. */
private WeakHashMap<Integer, MouseInfo> deviceToMouseInfo;
/** The max screen w. */
private int maxScreenW;
/** The max screen h. */
private int maxScreenH;
/** The mt app. */
private MTApplication mtApp;
/** The default center cam. */
private Icamera defaultCenterCam;
/** The current scene. */
private Iscene currentScene;
/**
* Instantiates a new multiple mice input source.
*
* @param applet the applet
*/
public MultipleMiceInputSource(MTApplication applet) {
super(applet);
this.maxScreenW = MT4jSettings.getInstance().getWindowWidth();
this.maxScreenH = MT4jSettings.getInstance().getWindowHeight();
mice = ManyMouse.Init();
System.out.println("ManyMouse.Init() reported " + mice + ".");
for (int i = 0; i < mice; i++){
System.out.println("Mouse #" + i + ": " + ManyMouse.DeviceName(i));
}
System.out.println();
// Allocate one that PollEvent fills in so we aren't spamming the
// memory manager with throwaway objects for each event.
event = new ManyMouseEvent(); //TODO mal austesten immer neuen zu machen
deviceToMouseInfo = new WeakHashMap<Integer, MouseInfo>();
applet.registerPost(this);
applet.registerDispose(this);
defaultCenterCam = new MTCamera(applet);
currentScene = null;
}
/**
* Gets the connected mouse count.
*
* @return the connected mouse count
*/
public static int getConnectedMouseCount(){
int mice = ManyMouse.Init();
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
ManyMouse.Quit();
return mice;
}
/**
* Post.
*/
public void post(){
if (mice > 0){
while (ManyMouse.PollEvent(event)) {
// System.out.print("Mouse #");
// System.out.print(event.device);
// System.out.print(" ");
switch (event.type){
case ManyMouseEvent.ABSMOTION:
mouseMovedAbs(event);
// System.out.print("absolute motion ");
// if (event.item == 0) // x axis
// System.out.print("X axis ");
// else if (event.item == 1) // y axis
// System.out.print("Y axis ");
// else
// System.out.print("? axis "); // error?
// System.out.print(event.value);
break;
case ManyMouseEvent.RELMOTION:
mouseMovedRel(event);
// System.out.print("relative motion ");
// if (event.item == 0) // x axis
// System.out.print("X axis ");
// else if (event.item == 1) // y axis
// System.out.print("Y axis ");
// else
// System.out.print("? axis "); // error?
// System.out.print(event.value);
break;
case ManyMouseEvent.BUTTON:
// System.out.print("mouse button ");
// System.out.print(event.item);
if (event.value == 0){
// System.out.print(" up");
buttonUp(event);
}else{
buttonPressed(event);
// System.out.print(" down");
}
break;
case ManyMouseEvent.SCROLL:
/*
System.out.print("scroll wheel ");
if (event.item == 0)
{
if (event.value > 0)
System.out.print("up");
else
System.out.print("down");
}
else
{
if (event.value > 0)
System.out.print("right");
else
System.out.print("left");
}
*/
break;
case ManyMouseEvent.DISCONNECT:
System.out.print("disconnect");
System.out.println(" Device: #" + event.device);
//Try to remove the mouse and the circle of this mouse
MouseInfo deviceInfo = deviceToMouseInfo.get(event.device);
if (deviceInfo != null){
try {
if (deviceInfo.ellipse != null){
if (mtApp != null){
mtApp.getCurrentScene().getCanvas().removeChild(deviceInfo.ellipse);
}
}
this.deviceToMouseInfo.remove(event.device);
} catch (Exception e) {
e.printStackTrace();
}
}
mice--;
break;
default:
System.out.print("Unknown event: ");
System.out.print(event.type);
break;
} // switch
// System.out.println();
}
}
}
/**
* Dispose.
*/
public void dispose(){
ManyMouse.Quit();
}
/**
* Sets the mT app.
*
* @param mtApp the new mT app
*/
public void setMTApp(MTApplication mtApp){
this.mtApp = mtApp;
// this.currentCanvas = mtApp.getCurrentScene().getMainCanvas();
//Add scene change listener to mt app
this.mtApp.addSceneChangeListener(new ISceneChangeListener(){
public void processSceneChangeEvent(SceneChangeEvent sc) {
sceneChange(sc.getLastScene(), sc.getNewScene());
}
});
}
/**
* Scene change.
*
* @param lastScene the last scene
* @param newScene the new scene
*/
private void sceneChange(Iscene lastScene, Iscene newScene){
MTCanvas oldCanvas = lastScene.getCanvas();
MTCanvas newCanvas = newScene.getCanvas();
currentScene = newScene;
System.out.println("Removing multiple mice cursors from old and add to new canvas.");
Collection<MouseInfo> mouseInfos = deviceToMouseInfo.values();
for (Iterator<MouseInfo> iter = mouseInfos.iterator(); iter.hasNext();) {
MouseInfo mouseInfo = (MouseInfo) iter.next();
if (mouseInfo.ellipse != null){
float currentEllipseWidth = 6;
Vector3D v = new Vector3D(currentEllipseWidth,0,0);
v.transformDirectionVector(currentScene.getCanvas().getGlobalInverseMatrix());
float newEllipseWidth = currentEllipseWidth = v.length();
mouseInfo.ellipse.setWidthXYGlobal(newEllipseWidth*2);
try {
oldCanvas.removeChild(mouseInfo.ellipse);
} catch (Exception e) {
e.printStackTrace();
}finally{
newCanvas.addChild(mouseInfo.ellipse);
}
// mouseInfo.ellipse.setCustomAndGlobalCam(currentScene.getSceneCam(), defaultCenterCam);
mouseInfo.ellipse.attachCamera(defaultCenterCam);
}
}
}
/**
* Return the mouseinfo of the corresponding mouse device or lazily create
* the mouse info and return it if not initialized yet.
*
* @param event the event
*
* @return the or init device info
*/
private MouseInfo getOrInitDeviceInfo(ManyMouseEvent event){
int device = event.device;
MouseInfo deviceInfo = deviceToMouseInfo.get(device);
if (deviceInfo == null){
MouseInfo newMouseInfo = new MouseInfo();
newMouseInfo.device = device;
newMouseInfo.x = 0;
newMouseInfo.y = 0;
newMouseInfo.lastX = 0;
newMouseInfo.lastY = 0;
deviceToMouseInfo.put(device, newMouseInfo);
this.attachCircleToMouseInfos(newMouseInfo);
return newMouseInfo;
}else{
return deviceInfo;
}
}
/**
* Attach circle to mouse infos.
*
* @param mouseInfo the mouse info
*/
private void attachCircleToMouseInfos(MouseInfo mouseInfo){
if (this.mtApp != null){
// MTCanvas canvas = mtApp.getCurrentScene().getMainCanvas();
float currentEllipseWidth = 6;
if (currentScene != null){
Vector3D v = new Vector3D(currentEllipseWidth,0,0);
v.transformDirectionVector(currentScene.getCanvas().getGlobalInverseMatrix());
currentEllipseWidth = v.length();
}else{
}
// mouseInfo.ellipse.setWidthXYGlobal(newEllipseWidth);
MTEllipse fingerCircle = new MTEllipse(mtApp, new Vector3D(mouseInfo.x, mouseInfo.y), currentEllipseWidth, currentEllipseWidth, 10);
fingerCircle.setPickable(false);
// fingerCircle.setCustomAndGlobalCam(currentScene.getSceneCam(), defaultCenterCam);
fingerCircle.attachCamera(defaultCenterCam);
//fingerCircle.setFillColor(50, 225, 230, 200);
// fingerCircle.setNoFill(true);
fingerCircle.setFillColor(new MTColor(255,50,50, 190));
// fingerCircle.setFillColor(Tools3D.getRandom(140, 255),Tools3D.getRandom(110, 255),Tools3D.getRandom(135, 255), 150);
fingerCircle.setDrawSmooth(true);
// fingerCircle.setStrokeWeight(2);
// fingerCircle.setStrokeColor(100,100,100, 150);
fingerCircle.setNoStroke(true);
fingerCircle.setDepthBufferDisabled(true);
//Add to mouseinfo
mouseInfo.ellipse = fingerCircle;
//Add to canvas
// currentCanvas.addChild(fingerCircle);
// mtApp.getCurrentScene().getMainCanvas().addChild(fingerCircle);
}
}
/**
* Button pressed.
*
* @param event the event
*/
private void buttonPressed(ManyMouseEvent event) {
int device = event.device;
MouseInfo mouseInfo = this.getOrInitDeviceInfo(event);
mouseInfo.isButtonPressed = true;
if (mouseInfo.ellipse != null){
try {
if (mtApp != null && currentScene != null){
//TODO camera fehler manchmal?
currentScene.getCanvas().removeChild(mouseInfo.ellipse);
currentScene.getCanvas().addChild(mouseInfo.ellipse);
// //Draw circles ontop
// MTCanvas canvas = mtApp.getCurrentScene().getMainCanvas();
// canvas.removeChild(mouseInfo.ellipse);
// canvas.addChild(mouseInfo.ellipse);
}
} catch (Exception e) {
e.printStackTrace();
}
}
InputCursor m = new InputCursor();
MTFingerInputEvt touchEvt = new MTFingerInputEvt(this, mouseInfo.x, mouseInfo.y, MTFingerInputEvt.INPUT_DETECTED, m);
// m.addEvent(touchEvt);
// long motionID = m.getId();
ActiveCursorPool.getInstance().putActiveCursor(device, m);
this.enqueueInputEvent(touchEvt);
// System.out.println("Motion added on device: #" + device);
}
/**
* Mouse moved rel.
*
* @param event the event
*/
private void mouseMovedRel(ManyMouseEvent event) {
int device = event.device;
MouseInfo mouseInfo = this.getOrInitDeviceInfo(event);
//TODO warum immer halb so viel? testen ob resolution zu tun hat oder was
int advanceValue = event.value *2;
switch (event.item) {
case 0:
// System.out.print("X axis ");
int newX = mouseInfo.x + advanceValue;
if (newX > this.maxScreenW){
newX = this.maxScreenW;
advanceValue = 0;
}else if(newX < 0){
newX = 0;
advanceValue = 0;
}
mouseInfo.lastX = mouseInfo.x;
mouseInfo.x = newX;
// //Move Circle
// if (mouseInfo.ellipse != null){
// mouseInfo.ellipse.translate(new Vector3D(advanceValue, 0, 0)); //TODO nicht immer neuen vector machen
// }
break;
case 1:
// System.out.print("Y axis ");
int newY = mouseInfo.y + advanceValue;
if (newY > this.maxScreenH){
newY = this.maxScreenH;
advanceValue = 0;
}else if(newY < 0){
newY = 0;
advanceValue = 0;
}
mouseInfo.lastY = mouseInfo.y;
mouseInfo.y = newY;
// //Move Circle
// if (mouseInfo.ellipse != null){
// mouseInfo.ellipse.translate(new Vector3D(0, advanceValue, 0)); //TODO nicht immer neuen vector machen
// }
break;
default:
System.out.print("? axis "); // error?
break;
}
//Move Circle
if (mouseInfo.ellipse != null){
// mouseInfo.ellipse.translate(new Vector3D(0, advanceValue, 0)); //TODO nicht immer neuen vector machen
// mouseInfo.ellipse.setPositionParentRelative(new Vector3D(mouseInfo.x, mouseInfo.y,0));
if (currentScene!= null){
//If canvas is scaled
Vector3D dir = new Vector3D(mouseInfo.x, mouseInfo.y,0);
dir.transform(currentScene.getCanvas().getGlobalInverseMatrix());
mouseInfo.ellipse.setPositionRelativeToParent(dir);
}else{
mouseInfo.ellipse.setPositionRelativeToParent(new Vector3D(mouseInfo.x, mouseInfo.y,0));
}
}
if (mouseInfo.isButtonPressed){
InputCursor m = ActiveCursorPool.getInstance().getActiveCursorByID(device);
MTFingerInputEvt te = new MTFingerInputEvt(this, mouseInfo.x, mouseInfo.y, MTFingerInputEvt.INPUT_UPDATED, m);
// m.addEvent(te);
// System.out.println("Motion update on device: #" + device+ " X:" + mouseInfo.x + " Y:" + mouseInfo.y);
this.enqueueInputEvent(te);
}
}
/**
* Mouse moved abs.
*
* @param event2 the event2
*/
private void mouseMovedAbs(ManyMouseEvent event2) {
int device = event.device;
MouseInfo mouseInfo = this.getOrInitDeviceInfo(event);
// int advanceValue = event.value *2;
switch (event.item) {
case 0:
// System.out.print("X axis ");
mouseInfo.lastX = mouseInfo.x;
mouseInfo.x = event.value;
break;
case 1:
// System.out.print("Y axis ");
mouseInfo.lastY = mouseInfo.y;
mouseInfo.y = event.value;
break;
default:
System.out.print("? axis "); // error?
break;
}
//Move Circle
if (mouseInfo.ellipse != null){
// mouseInfo.ellipse.translate(new Vector3D(0, advanceValue, 0)); //TODO nicht immer neuen vector machen
if (currentScene!= null){
//If canvas is scaled
Vector3D dir = new Vector3D(mouseInfo.x, mouseInfo.y,0);
dir.transform(currentScene.getCanvas().getGlobalInverseMatrix());
mouseInfo.ellipse.setPositionRelativeToParent(dir);
}else{
mouseInfo.ellipse.setPositionRelativeToParent(new Vector3D(mouseInfo.x, mouseInfo.y,0));
}
}
if (mouseInfo.isButtonPressed){
InputCursor m = ActiveCursorPool.getInstance().getActiveCursorByID(device);
MTFingerInputEvt te = new MTFingerInputEvt(this, mouseInfo.x, mouseInfo.y, MTFingerInputEvt.INPUT_UPDATED, m);
// m.addEvent(te);
// System.out.println("Motion update on device: #" + device+ " X:" + mouseInfo.x + " Y:" + mouseInfo.y);
this.enqueueInputEvent(te);
}
}
/**
* Button up.
*
* @param event the event
*/
private void buttonUp(ManyMouseEvent event) {
int device = event.device;
MouseInfo mouseInfo = this.getOrInitDeviceInfo(event);
if (mouseInfo.isButtonPressed){
mouseInfo.isButtonPressed = false;
InputCursor m = ActiveCursorPool.getInstance().getActiveCursorByID(device);
// System.out.println("Motion ended on device: #" + device);
MTFingerInputEvt te;
if (m.getCurrentEvent() != null)
te = new MTFingerInputEvt(this, m.getCurrentEvent().getPosX(), m.getCurrentEvent().getPosY(), MTFingerInputEvt.INPUT_ENDED, m);
else
te = new MTFingerInputEvt(this, 0,0, MTFingerInputEvt.INPUT_ENDED, m);
// m.addEvent(te); //werden nicht hier geadded sondern synchroniesert mit dem PApplet thread in den analyzern, so dass immer nur 1
// 1 te geadded wird und dann wieder verarbeitet, dann der n�chste
ActiveCursorPool.getInstance().removeCursor(device);
this.enqueueInputEvent(te);
}
}
/**
* The Class MouseInfo.
*
* @author C.Ruff
*/
private class MouseInfo{
/** The device. */
int device;
/** The x. */
int x;
/** The y. */
int y;
/** The last x. */
int lastX;
/** The last y. */
int lastY;
/** The is button pressed. */
boolean isButtonPressed = false;
/** The ellipse. */
MTEllipse ellipse;
}
// @Override
// public boolean firesEventType(Class<? extends MTInputEvent> evtClass){
// return (evtClass == MTFingerInputEvt.class);
// }
}