using System;
using System.Collections.Generic;
using System.Drawing;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using bbiwarg.Recognition.TouchRecognition;
using bbiwarg.Utility;
using bbiwarg.Input.InputHandling;
namespace bbiwarg.Output.DebugOutput
{
///
/// Provides an image showing touch events.
///
class TouchEventVisualizer
{
///
/// used to prevent running and simultaneously from different threads
///
private Object sync;
///
/// used to store the relative time at which the touch events occured
///
private Stopwatch timer;
///
/// all touch events starting at the last TouchDown, indexed by a unique id
///
private Dictionary> activeTouches;
///
/// old touch events which are stored for a limited time, indexed by a unique id
///
private Dictionary> oldTouches;
///
/// relative times of last frame updates which are stored for a limited time, indexed by a unique id
///
private Dictionary lastUpdates;
///
/// the next free id, used as a unique id for the dictionaries
///
private int nextFreeID;
///
/// Creates a TouchEventVisualizer.
///
public TouchEventVisualizer()
{
sync = new object();
reset();
}
///
/// Resets the touch events and the timer.
///
public void reset()
{
timer = new Stopwatch();
timer.Start();
nextFreeID = 1;
activeTouches = new Dictionary>();
oldTouches = new Dictionary>();
lastUpdates = new Dictionary();
}
///
/// Handles the event that a new frame is finished processing by updating the touch events.
///
/// event sender
/// event arguments
public void handleNewFrameData(object sender, NewProcessedFrameEventArgs e)
{
FrameData frameData = e.FrameData;
lock (frameData) lock (sync)
{
if (frameData.ResetFlag)
reset();
foreach (TouchEvent te in frameData.TouchEvents)
{
switch (te.Type)
{
case TouchEventType.Down:
activeTouches.Add(te.Touch.TrackID, new List());
activeTouches[te.Touch.TrackID].Add(te.Touch.RelativePosition);
break;
case TouchEventType.Move:
activeTouches[te.Touch.TrackID].Add(te.Touch.RelativePosition);
break;
case TouchEventType.Up:
activeTouches[te.Touch.TrackID].Add(te.Touch.RelativePosition);
oldTouches.Add(nextFreeID, activeTouches[te.Touch.TrackID]);
lastUpdates.Add(nextFreeID, timer.ElapsedMilliseconds);
activeTouches.Remove(te.Touch.TrackID);
nextFreeID++;
break;
}
}
}
}
///
/// Returns an output image showing the touch events.
///
/// output image size
/// number of rows in the palm grid
/// number columns in the palm grid
/// image showing touch events
public OutputImage getOutputImage(ImageSize imageSize, int numRows, int numColumns)
{
lock (sync)
{
long currentTime = timer.ElapsedMilliseconds;
removeOldPositions(currentTime - Parameters.TouchEventVisualizerFadeOutTime);
OutputImage outputImage = new OutputImage(imageSize);
int imageWidth = imageSize.Width;
int imageHeight = imageSize.Height;
// border
outputImage.drawBorder(Parameters.TouchEventVisualizerGridColor);
// draw grid
int widthPerColumn = imageWidth / numColumns;
int heightPerRow = imageHeight / numRows;
// find active blocks
bool[,] activeBlocks = new bool[numRows, numColumns];
if (numRows * numColumns > 1)
{
foreach (List positions in activeTouches.Values)
{
Vector2D lastPosition = positions.Last();
int activeRow = (int)Math.Min(lastPosition.Y * numRows, numRows - 1);
int activeCol = (int)Math.Min(lastPosition.X * numColumns, numColumns - 1);
activeBlocks[activeRow, activeCol] = true;
}
}
// draw blocks
int index = 1;
for (int row = 0; row < numRows; row++)
{
for (int col = 0; col < numColumns; col++)
{
if (activeBlocks[row, col])
outputImage.fillRectangle(new Rectangle(col * widthPerColumn, row * heightPerRow, widthPerColumn, heightPerRow), Parameters.TouchEventVisualizerActiveBlockColor);
int x = (int)((col + 0.5f) * widthPerColumn) - 5;
int y = (int)((row + 0.5f) * heightPerRow) + 5;
outputImage.drawText(new Point(x,y), index.ToString(), Parameters.TouchEventVisualizerTextColor);
index++;
}
}
// draw grid
for (int i = 0; i <= numColumns; i++)
outputImage.drawLineSegment(new LineSegment2D(new Vector2D(i * widthPerColumn, 0), new Vector2D(i * widthPerColumn, imageHeight - 1)), Parameters.TouchEventVisualizerGridColor);
for (int i = 0; i <= numRows; i++)
outputImage.drawLineSegment(new LineSegment2D(new Vector2D(0, i * heightPerRow), new Vector2D(imageWidth - 1, i * heightPerRow)), Parameters.TouchEventVisualizerGridColor);
// draw active touches
foreach (List positions in activeTouches.Values)
outputImage.drawTouchGesture(positions, imageSize.MaxPixel);
// draw old touches (fade out)
foreach (int id in oldTouches.Keys)
{
List positions = oldTouches[id];
long lastUpdate = lastUpdates[id];
float opacity = 1 - ((currentTime - lastUpdate) / (float)Parameters.TouchEventVisualizerFadeOutTime);
outputImage.drawTouchGesture(positions, imageSize.MaxPixel, opacity);
}
return outputImage;
}
}
///
/// Removes old touch events and update times.
///
/// every touch event which occured before breakTime is removed
private void removeOldPositions(long breakTime)
{
List ids = new List(lastUpdates.Keys);
for (int i = ids.Count - 1; i >= 0; i--)
{
int id = ids[i];
if (breakTime > lastUpdates[id])
{
oldTouches.Remove(id);
lastUpdates.Remove(id);
}
}
}
}
}