/**
* Squidy 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 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. If not, see <http://www.gnu.org/licenses/>.
*
* 2006-2009 Human-Computer Interaction Group, University of Konstanz.
* <http://hci.uni-konstanz.de>
*
* Please contact info@squidy-lib.de or visit our website http://squidy-lib.de for
* further information.
*/
/**
*
*/
package org.squidy.nodes;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
import org.squidy.manager.ProcessException;
import org.squidy.manager.controls.Slider;
import org.squidy.manager.data.IData;
import org.squidy.manager.data.IDataContainer;
import org.squidy.manager.data.Processor;
import org.squidy.manager.data.Property;
import org.squidy.manager.data.Processor.Status;
import org.squidy.manager.data.impl.DataPosition2D;
import org.squidy.manager.model.AbstractNode;
import org.squidy.nodes.reactivision.TuioClient;
/**
* <code>RotationSmoother</code>.
*
* <pre>
* Date: Dec 4, 2009
* Time: 15:32:29 PM
* </pre>
*
*
* @author Nicolas Hirrle <a
* href="mailto:nihirrle@htwg-konstanz.de">nihirrle@htwg-konstanz.de</a>
* Human-Computer Interaction Group University of Konstanz
*
* @version $Id: RotationSmoother.java 772 2011-09-16 15:39:44Z raedle $
* @since 2.0.0
*/
@XmlType(name = "RotationSmoother")
@Processor(
name = "RotationSmoother",
description = "Interpolates Tokenmessages to rotate smoother",
types = { Processor.Type.FILTER },
tags = {},
status = Status.UNSTABLE
)
public class RotationSmoother extends AbstractNode {
// ################################################################################
// BEGIN OF PROPERTIES
// ################################################################################
@XmlAttribute(name = "Number of Messages to analyze")
@Property(name = "Number of Messages", description = "Number of messages (DataPosition2D) to analyze and then to interpolate Rotation if necessary", suffix = "messages")
@Slider(type = Integer.class, minimumValue = 5, maximumValue = 20, showLabels = true, showTicks = true, majorTicks = 1, minorTicks = 1, snapToTicks = false)
private int messageNumber = 10;
public int getMessageNumber() {
return messageNumber;
}
public void setMessageNumber(int messageNumber) {
this.messageNumber = messageNumber;
}
// ################################################################################
// END OF PROPERTIES
// ################################################################################
// ################################################################################
// BEGIN OF LAUNCH
// ################################################################################
/*
* (non-Javadoc)
*
* @see org.squidy.manager.model.AbstractNode#onStart()
*/
@Override
public void onStart() throws ProcessException
{
dataPackages = new Hashtable<Integer, ArrayBlockingQueue<DataPosition2D>>();
}
/*
* (non-Javadoc)
*
* @see org.squidy.manager.model.AbstractNode#onStop()
*/
@Override
public void onStop() throws ProcessException
{
dataPackages.clear();
}
// ################################################################################
// END OF LAUNCH
// ################################################################################
// ################################################################################
// BEGIN OF PROCESS
// ################################################################################
private Object lock = new Object();
private Hashtable<Integer, ArrayBlockingQueue<DataPosition2D>> dataPackages; // key = ID, Value = queue aus den 10 vorhergehenden DataPosition2D der zugeh�rigen ID
/*
* (non-Javadoc)
*
* @see
* org.squidy.manager.model.AbstractNode#beforeDataContainerProcessing
* (org.squidy.manager.data.IDataContainer)
*/
@Override
public IDataContainer preProcess(IDataContainer dataCont)
{
synchronized (lock)
{
IDataContainer dataContainer = dataCont.getClone();
IData dataTmp[] = dataContainer.getData();
ArrayList<IData> dataPublish = new ArrayList<IData>();
for (int i=0; i<dataTmp.length; i++)
{
dataPublish.add(dataTmp[i]);
}
if(dataPackages.isEmpty())
{
IData[] data = dataContainer.getData();
for (int i=0; i<data.length; i++)
{
if (data[i] instanceof DataPosition2D)
{
ArrayBlockingQueue<DataPosition2D> dataPosition2Ds = new ArrayBlockingQueue<DataPosition2D>(messageNumber);
dataPosition2Ds.add((DataPosition2D) data[i]);
int id = (Integer) data[i].getAttribute(TUIO.FIDUCIAL_ID);
dataPackages.put(id, dataPosition2Ds);
}
}
}
else
{
IData[] data = dataContainer.getData();
for (int i=0; i<data.length; i++)
{
if (data[i] instanceof DataPosition2D)
{
int id = (Integer) data[i].getAttribute(TUIO.FIDUCIAL_ID);
if(dataPackages.containsKey(id))
{
String status = (String) data[i].getAttribute(ReacTIVision.TUIO_TOKEN);
if (status == "removed")
{
dataPackages.remove(id);
}
else
{
ArrayBlockingQueue<DataPosition2D> dataPosition2Ds = dataPackages.get(id);
if (dataPosition2Ds.remainingCapacity() > 0)
dataPosition2Ds.add((DataPosition2D) data[i]);
else
{
dataPosition2Ds.remove();
dataPosition2Ds.add((DataPosition2D) data[i]);
}
}
}
else
{
ArrayBlockingQueue<DataPosition2D> dataPosition2Ds = new ArrayBlockingQueue<DataPosition2D>(messageNumber);
dataPosition2Ds.add((DataPosition2D) data[i]);
dataPackages.put(id, dataPosition2Ds);
}
}
}
// am ende
Iterator<Integer> hashIterator = dataPackages.keySet().iterator();
while (hashIterator.hasNext())
{
boolean idInContainer = false;
int id = hashIterator.next();
for (int i=0; i<data.length; i++)
{
if (data[i] instanceof DataPosition2D)
{
int idTmp = (Integer) data[i].getAttribute(TUIO.FIDUCIAL_ID);
if (id == idTmp)
{
idInContainer = true;
}
}
}
if (idInContainer == false)
{
// interpolate
DataPosition2D dataPosition2D = interpolateRotation(id);
if (dataPosition2D != null)
dataPublish.add(dataPosition2D);
}
}
}
publish(dataPublish);
}
return null;// super.beforeDataContainerProcessing(dataContainer);
}
private DataPosition2D interpolateRotation(int id)
{
ArrayBlockingQueue<DataPosition2D> dataPosition2Ds = dataPackages.get(id);
Iterator<DataPosition2D> arrayIterator = dataPosition2Ds.iterator();
while (arrayIterator.hasNext())
{
DataPosition2D dataPosition2D = arrayIterator.next();
if ((Double)dataPosition2D.getAttribute(TUIO.ROTATION_ACCELERATION) == 0)
return null; // nichts machen, da keine Rotation
}
double sumRotationAccleration = 0;
double sumAngles = 0;
arrayIterator = dataPosition2Ds.iterator();
while (arrayIterator.hasNext())
{
DataPosition2D dataPosition2D = arrayIterator.next();
double rotationAccleration = (Double)dataPosition2D.getAttribute(TUIO.ROTATION_ACCELERATION);
sumRotationAccleration = sumRotationAccleration + rotationAccleration;
Iterator<DataPosition2D> nextElement = arrayIterator;
int next = 0;
while (nextElement.hasNext())
{
if (next == 1)
break;
next++;
}
if (nextElement != arrayIterator)
{
DataPosition2D dataPosition2DNext = nextElement.next();
double difAngles = (Double) dataPosition2DNext.getAttribute(TUIO.ANGLE_A) -(Double) dataPosition2D.getAttribute(TUIO.ANGLE_A);
sumAngles = sumAngles + difAngles;
}
}
double medianRotAcc = sumRotationAccleration / dataPosition2Ds.size();
double medianAngle = sumAngles / dataPosition2Ds.size();
// get last element of array
DataPosition2D dataPosition2D = null;
arrayIterator = dataPosition2Ds.iterator();
while (arrayIterator.hasNext())
{
dataPosition2D = arrayIterator.next().getClone();
}
double angle = medianAngle + (Double) dataPosition2D.getAttribute(TUIO.ANGLE_A);//last angle
dataPosition2D.setAttribute(TUIO.ROTATION_ACCELERATION, medianRotAcc);
dataPosition2D.setAttribute(TUIO.ANGLE_A, angle);
dataPosition2D.setAttribute(ReacTIVision.TUIO_TOKEN, "updated");
return dataPosition2D;
}
// ################################################################################
// END OF PROCESS
// ################################################################################
}