/**
* Squidy Interaction Library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Squidy Interaction Library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Squidy Interaction Library. If not, see
* <http://www.gnu.org/licenses/>.
*
* 2009 Human-Computer Interaction Group, University of Konstanz.
* <http://hci.uni-konstanz.de>
*
* Please contact info@squidy-lib.de or visit our website
* <http://www.squidy-lib.de> for further information.
*/
package org.squidy.nodes;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.squidy.manager.controls.CheckBox;
import org.squidy.manager.controls.TextField;
import org.squidy.manager.data.DataConstant;
import org.squidy.manager.data.IData;
import org.squidy.manager.data.Processor;
import org.squidy.manager.data.Property;
import org.squidy.manager.data.impl.DataButton;
import org.squidy.manager.data.impl.DataDigital;
import org.squidy.manager.data.impl.DataInertial;
import org.squidy.manager.data.impl.DataKey;
import org.squidy.manager.data.impl.DataPosition2D;
import org.squidy.manager.data.impl.DataString;
import org.squidy.manager.model.AbstractNode;
import org.wiigee.device.Device;
import org.wiigee.event.AccelerationListener;
import org.wiigee.event.ButtonListener;
import org.wiigee.event.GestureEvent;
import org.wiigee.event.GestureListener;
/**
* <code>GestureRecognizerShake</code>.
*
* <pre>
* Date: Nov 21, 2008
* Time: 5:13:03 PM
* </pre>
*
* @author Nicolas Hirrle, nihirrle@htwg-konstanz.de, University of Konstanz
* @version 1.0
*/
@XmlType(name = "GestureRecognizerShake")
@Processor(name = "GestureRecognizerShake", types = { Processor.Type.FILTER }, tags = {
"gesture", "recognizer", "phidget" })
public class GestureRecognizerShake extends AbstractNode implements
GestureListener {
// Logger to log info, error, debug,... messages.
private static final Log LOG = LogFactory
.getLog(GestureRecognizerShake.class);
// ################################################################################
// BEGIN OF ADJUSTABLES
// ################################################################################
@XmlAttribute(name = "threshold")
@Property(name = "Threshold", description = "Value threshold for adding to recognizer")
@TextField
private double threshold = 0.02;
public double getThreshold() {
return threshold;
}
public void setThreshold(double threshold) {
this.threshold = threshold;
}
@XmlAttribute(name = "Loadgestures")
@Property(name = "Loadgestures")
@CheckBox
private boolean loadGestures = true;
public boolean isLoadGestures() {
return loadGestures;
}
public void setLoadGestures(boolean loadGestures) {
this.loadGestures = loadGestures;
}
@XmlAttribute(name = "InputIPhone")
@Property(name = "InputIPhone")
@CheckBox
private boolean inputIPhone = true;
public boolean isInputIPhone() {
return inputIPhone;
}
public void setInputIPhone(boolean inputIPhone) {
this.inputIPhone = inputIPhone;
}
// ################################################################################
// END OF ADJUSTABLES
// ################################################################################
private String gestureFolder = "ext/shake/gestures";
private HashMap<Integer, Gestures> gesturesLoaded;
private Device wiigee;
private DataInertial lastInertial;
private long lastIPhoneTouch;
private boolean isRecognizing = false;
private Object synch = new Object();
/*
* (non-Javadoc)
*
* @see org.squidy.manager.model.AbstractNode#onStart()
*/
@Override
public void onStart() {
if (inputIPhone)
gestureFolder = "ext/iphone/gestures";
else
gestureFolder = "ext/shake/gestures";
lastInertial = new DataInertial(GestureRecognizerShake.class, 0.5f,
0.5f, 0.5f);
gesturesLoaded = new HashMap<Integer, Gestures>();
wiigee = new Device(false);
wiigee.setTrainButton(4);
wiigee.setRecognitionButton(3);
wiigee.setCloseGestureButton(8);
wiigee.addGestureListener(this);
if (loadGestures)
loadGestures();
}
/*
* (non-Javadoc)
*
* @see org.squidy.manager.model.AbstractNode#onStop()
*/
@Override
public void onStop() {
gesturesLoaded.clear();
}
public DataPosition2D process(DataPosition2D dataPosition2D) {
if (inputIPhone) {
// synchronized (synch) {
// lastIPhoneTouch = System.currentTimeMillis();
// }
// // check first if old Recognition is running
// if (!isRecognizing) {
// startSampleRecognition();
// isRecognizing = true;
// checkWhenToStop();
// }
if (dataPosition2D.hasAttribute(iPhone.TOUCHES_BEGAN)){
if (dataPosition2D.getAttribute(iPhone.TOUCHES_BEGAN).equals("TOUCHES_BEGAN")){
startSampleRecognition();
}
}
else if (dataPosition2D.hasAttribute(iPhone.TOUCHES_ENDED)){
if (dataPosition2D.getAttribute(iPhone.TOUCHES_ENDED).equals("TOUCHES_ENDED")){
stopSampleRecognition();
}
}
}
return null;
}
// private void checkWhenToStop() {
// new Thread() {
// public void run() {
// synchronized (synch) {
// long timeNow = System.currentTimeMillis();
// while (timeNow - 50 > lastIPhoneTouch) {
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// timeNow = System.currentTimeMillis();
// }
// stopSampleRecognition();
// isRecognizing = false;
// }
// }
// }.start();
// }
/**
* @param dataInertial
* @return
*/
public IData process(DataInertial dataInertial) {
if (dataInertial.getAbsoluteValue() > (1 + threshold)
|| dataInertial.getAbsoluteValue() < (1 - threshold)) {
double[] w = { dataInertial.getX(), dataInertial.getY(),
dataInertial.getZ() };
if (inputIPhone) {
double[] tmp = { dataInertial.getX() - lastInertial.getX(),
dataInertial.getY() - lastInertial.getY(),
dataInertial.getZ() - lastInertial.getZ() };
w = tmp;
lastInertial = dataInertial.getClone();
}
wiigee.fireAccelerationEvent(w);
//DataInertial tmp = new DataInertial(GestureRecognizerShake.class, (Float) w[0], (Float)w[1], (Float)w[2]);
//return tmp;
}
return null;
}
/**
* To Recognize with the ShakeSensor do following: 1. Click Shake-button and
* hold. The Shake Sensor vibrates 2. Do gesture 3. Release Shake-button.
* The Shake Sensor vibrates
*
* @param dataButton
* @return
*/
public IData process(DataDigital dataDigital) {
if (dataDigital.hasAttribute(DataConstant.IDENTIFIER)) {
if (dataDigital.getAttribute(DataConstant.IDENTIFIER).equals(
"ShakeSK7")) {
if (dataDigital.getFlag())
startSampleRecognition();
else
stopSampleRecognition();
}
}
return dataDigital;
}
/**
* To Recognize with ShakeAcceleromter and KeyboardInput do following: 1.
* Learn gesture multiple times while holding the 't'-key on Keyboard-Input
* Window. Releasing it marks the end of one recording process; 2. Finish
* training of unique gesture by pressing 'return'-key 3. Recognize gesture
* with 'space'-bar on Keyboard-Input Window 4. To save a gesture hit 's' on
* Keyboard-Input Window and follow the instructions on the console. To be
* done.
*
* @param dataButton
* @return
*/
public IData process(DataKey dataKey) {
// keyType 84 for 't'; Hold 't' for learning a gesture. Releasing it
// marks the end of the recording process
if (dataKey.getKeyType() == 84) {
if (dataKey.getFlag())
startSampleLearning();
else
stopSampleLearning();
}
// keyType 10 for 'return-key'; finalizes learning process of a unique
// gesture
// NOTE: Gesture is not stored!
else if (dataKey.getKeyType() == 10) {
if (dataKey.getFlag()) {
closeGesture();
}
}
// keyType 32 for 'space-key'; hold to learn gesture
// switch to learning mode and sample gesture
else if (dataKey.getKeyType() == 32) {
if (dataKey.getFlag()) {
startSampleRecognition();
} else {
stopSampleRecognition();
}
}
// keyType 83 for 's-key'; press to save gesture
else if (dataKey.getKeyType() == 83 && dataKey.getFlag()) {
saveGesture();
}
return null;
}
public void gestureReceived(GestureEvent event) {
// DataGesture dataGesture = null;
DataString dataString = null;
DataKey dataKeyUp = null;
DataKey dataKeyDown = null;
DataButton dataButtonUp = null;
DataButton dataButtonDown = null;
if (event.isValid() && event.getProbability() > 0.8) {
if (gesturesLoaded.containsKey(event.getId())) {
System.out.println("New Gesture Event \""
+ gesturesLoaded.get(event.getId()).getGesture()
+ "\" with ID " + event.getId() + " and Probability "
+ event.getProbability() + " from Source "
+ event.getSource());
String gesture = gesturesLoaded.get(event.getId()).getGesture();
if (gesture.contains("right")) {
dataKeyUp = new DataKey(GestureRecognizerShake.class,
KeyEvent.VK_RIGHT, true);
dataKeyDown = new DataKey(GestureRecognizerShake.class,
KeyEvent.VK_RIGHT, false);
dataButtonUp = new DataButton(GestureRecognizerShake.class,
MouseEvent.BUTTON1, true);
dataButtonUp = new DataButton(GestureRecognizerShake.class,
MouseEvent.BUTTON1, false);
} else if (gesture.contains("left")) {
dataKeyUp = new DataKey(GestureRecognizerShake.class,
KeyEvent.VK_LEFT, true);
dataKeyDown = new DataKey(GestureRecognizerShake.class,
KeyEvent.VK_LEFT, false);
} else if (gesture.contains("circle")) {
dataKeyUp = new DataKey(GestureRecognizerShake.class,
KeyEvent.VK_F5, true);
dataKeyDown = new DataKey(GestureRecognizerShake.class,
KeyEvent.VK_F5, false);
}
dataString = new DataString(GestureRecognizerShake.class,
gesturesLoaded.get(event.getId()).getGesture());
} else {
LOG.info("New Gesture Event (not loaded) with ID "
+ event.getId() + " and Probability "
+ event.getProbability() + " from Source "
+ event.getSource());
dataString = new DataString(GestureRecognizerShake.class,
gesturesLoaded.get(event.getId()).getGesture());
}
dataString.setAttribute(DataConstant.SESSION_ID, event.getId());
if (dataString != null)
publish(dataString);
if (dataKeyUp != null)
publish(dataKeyUp);
if (dataKeyDown != null)
publish(dataKeyDown);
}
}
public void startSampleLearning() {
fireButtonPressedEvent(4);
}
public void startSampleRecognition() {
fireButtonPressedEvent(3);
}
public void closeGesture() {
fireButtonPressedEvent(8);
}
public void stopSampleLearning() {
fireButtonReleasedEvent(4);
}
public void stopSampleRecognition() {
fireButtonReleasedEvent(3);
}
/**
* Fires a button pressed event.
*
* @param button
* Integer value of the pressed button.
*/
public void fireButtonPressedEvent(int button) {
wiigee.fireButtonPressedEvent(button);
}
/**
* Fires a button released event.
*/
public void fireButtonReleasedEvent(int button) {
wiigee.fireButtonReleasedEvent(button);
}
private void loadGestures() {
new Thread() {
public void run() {
File f = new File(gestureFolder);
String[] filenames = f.list();
if (filenames == null) {
System.out.println("Failed to load gestures");
return;
} else if (filenames.length == 0) {
System.out.println("No gestures found");
return;
} else {
for (int i = 0; i < filenames.length; i++) {
int minus = filenames[i].lastIndexOf('-');
int dot = filenames[i].lastIndexOf('.');
String file = filenames[i].substring(0, dot);
if (file.isEmpty())
continue;
String gesture = filenames[i].substring(minus + 1, dot);
String idstr = filenames[i].substring(0, minus);
int id = Integer.parseInt(idstr);
Gestures g = new Gestures(id, gesture);
gesturesLoaded.put(id, g);
wiigee.loadGesture(gestureFolder + "/" + file);
System.out.println("Gesture " + gesture + " loaded");
// int minus = filenames[i].lastIndexOf('-');
// int dot = filenames[i].lastIndexOf('.');
// String file = filenames[i].substring(0, dot);
// String gesture = filenames[i].substring(0, minus);
// String idstr = filenames[i].substring(minus + 1,
// dot);
//
// int id = Integer.parseInt(idstr);
// Gestures g = new Gestures(id, gesture);
// gesturesLoaded.put(id, g);
//
// wiigee.loadGesture(gestureFolder + "/" + file);
// System.out.println("Gesture " + gesture + " loaded");
}
}
}
}.start();
}
private void saveGesture() {
new Thread() {
public void run() {
System.out.println("Do you want to save a Gesture? (y/n)");
BufferedReader console = new BufferedReader(
new InputStreamReader(System.in));
String str = new String();
try {
str = console.readLine();
if (str.equals("y")) {
System.out.println("Gesture ID: ");
String idstr = console.readLine();
int id = Integer.parseInt(idstr);
if (id < 10)
idstr = "0" + id;
System.out
.println("Gesture Name (don't use '-' and '.'): ");
BufferedReader consoleF = new BufferedReader(
new InputStreamReader(System.in));
String filename = new String();
filename = consoleF.readLine();
Gestures g = new Gestures(id, filename);
gesturesLoaded.put(g.id, g);
wiigee.saveGesture(id, gestureFolder + "/" + idstr + "-"
+ filename);
// wiigee.saveGesture(id, gestureFolder + "/" + filename
// + "-" + id);
System.out.println("Gesture " + g.getGesture()
+ " saved!");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
private static class Gestures {
public Gestures(int id, String gesture) {
this.id = id;
this.gesture = gesture;
}
private int id;
private String gesture;
public String getGesture() {
return gesture;
}
}
}