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, int sliderPos, int sliderMax, int sliderCurr) { 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 List> activeBlocks = getActiveBlocks(numRows, numColumns, sliderPos, sliderMax, sliderCurr); if (numRows * numColumns > 1) { for (int i = 0; i < activeTouches.Values.Count; i++) { List positions = activeTouches.Values.ElementAt(i); Vector2D lastPosition = positions.Last(); int activeRow = (int)Math.Min(lastPosition.Y * numRows, numRows - 1); int activeCol = (int)Math.Min(lastPosition.X * activeBlocks.ElementAt(activeRow).Count, activeBlocks.ElementAt(activeRow).Count - 1); if (activeRow != sliderPos) activeBlocks.ElementAt(activeRow)[activeCol] = true; if (activeRow == sliderPos) { if (Parameters.PalmSliderLastTouched != -1) { int temp = Parameters.PalmSliderCurr + (activeCol - Parameters.PalmSliderLastTouched); if (temp < 0) Parameters.PalmSliderCurr = 0; if (temp > Parameters.PalmSliderMax) Parameters.PalmSliderCurr = Parameters.PalmSliderMax; else Parameters.PalmSliderCurr = temp; Parameters.PalmSliderLastTouched = activeCol; } else if (Parameters.PalmSliderLastTouched == -1) { Parameters.PalmSliderLastTouched = activeCol; } } else { Parameters.PalmSliderLastTouched = -1; } } if (activeTouches.Values.Count < 1) { Parameters.PalmSliderLastTouched = -1; } } if(sliderPos > -1 && sliderPos < numRows) { for (int i = 0; i < Parameters.PalmSliderCurr; i++) { activeBlocks.ElementAt(sliderPos)[i] = true; } } Parameters.ActiveTouches = activeBlocks; // draw blocks int index = 1; for (int row = 0; row < numRows; row++) { for (int col = 0; col < activeBlocks.ElementAt(row).Count; col++) { if (activeBlocks.ElementAt(row)[col]) outputImage.fillRectangle(new Rectangle(col * imageWidth/activeBlocks.ElementAt(row).Count, row * heightPerRow, imageWidth / activeBlocks.ElementAt(row).Count, heightPerRow), Parameters.TouchEventVisualizerActiveBlockColor); if(row != sliderPos) { int x = (int)((col + 0.5f) * imageWidth / activeBlocks.ElementAt(row).Count) - 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 < activeBlocks.Count; i++) { for(int j = 0; j < activeBlocks.ElementAt(i).Count; j++) { outputImage.drawLineSegment(new LineSegment2D( new Vector2D(j * imageWidth/activeBlocks.ElementAt(i).Count, i * heightPerRow), new Vector2D(j * imageWidth / activeBlocks.ElementAt(i).Count, (i + 1) * heightPerRow -1)), Parameters.TouchEventVisualizerGridColor); } } //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; } } private List> getActiveBlocks(int numRows, int numColumns, int sliderPos, int sliderMax, int sliderCurr) { List> res = new List>(); for(int i = 0; i < numRows; i++) { List temp = new List(); if (i == sliderPos) { for (int j = 0; j < sliderMax; j++) temp.Add(false); } else { for (int j = 0; j < numColumns; j++) temp.Add(false); } res.Add(temp); } return res; } /// /// 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); } } } } }