using BBIWARG.Input.InputHandling; using BBIWARG.Recognition.TouchRecognition; using BBIWARG.Utility; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Linq; namespace BBIWARG.Output.DebugOutput { /// /// Provides an image showing touch events. /// internal class TouchEventVisualizer { /// /// all touch events starting at the last TouchDown, indexed by a unique id /// private Dictionary> activeTouches; /// /// 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; /// /// old touch events which are stored for a limited time, indexed by a unique id /// private Dictionary> oldTouches; /// /// used to prevent running and simultaneously from different threads /// private Object sync; /// /// used to store the relative time at which the touch events occurred /// private Stopwatch timer; /// /// Creates a TouchEventVisualizer. /// public TouchEventVisualizer() { sync = new object(); reset(); } /// /// 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; } } /// /// 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; } } } } /// /// 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(); } /// /// Removes old touch events and update times. /// /// every touch event which occurred 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); } } } } }