TouchEventVisualizer.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using bbiwarg.Recognition.TouchRecognition;
  9. using bbiwarg.Utility;
  10. using bbiwarg.Input.InputHandling;
  11. namespace bbiwarg.Output.DebugOutput
  12. {
  13. /// <summary>
  14. /// Provides an image showing touch events.
  15. /// </summary>
  16. class TouchEventVisualizer
  17. {
  18. /// <summary>
  19. /// used to prevent running <see cref="handleNewFrameData"/> and <see cref="getOutputImage"/> simultaneously from different threads
  20. /// </summary>
  21. private Object sync;
  22. /// <summary>
  23. /// used to store the relative time at which the touch events occured
  24. /// </summary>
  25. private Stopwatch timer;
  26. /// <summary>
  27. /// all touch events starting at the last TouchDown, indexed by a unique id
  28. /// </summary>
  29. private Dictionary<int, List<Vector2D>> activeTouches;
  30. /// <summary>
  31. /// old touch events which are stored for a limited time, indexed by a unique id
  32. /// </summary>
  33. private Dictionary<int, List<Vector2D>> oldTouches;
  34. /// <summary>
  35. /// relative times of last frame updates which are stored for a limited time, indexed by a unique id
  36. /// </summary>
  37. private Dictionary<int, long> lastUpdates;
  38. /// <summary>
  39. /// the next free id, used as a unique id for the dictionaries
  40. /// </summary>
  41. private int nextFreeID;
  42. /// <summary>
  43. /// Creates a TouchEventVisualizer.
  44. /// </summary>
  45. public TouchEventVisualizer()
  46. {
  47. sync = new object();
  48. reset();
  49. }
  50. /// <summary>
  51. /// Resets the touch events and the timer.
  52. /// </summary>
  53. public void reset()
  54. {
  55. timer = new Stopwatch();
  56. timer.Start();
  57. nextFreeID = 1;
  58. activeTouches = new Dictionary<int, List<Vector2D>>();
  59. oldTouches = new Dictionary<int, List<Vector2D>>();
  60. lastUpdates = new Dictionary<int, long>();
  61. }
  62. /// <summary>
  63. /// Handles the event that a new frame is finished processing by updating the touch events.
  64. /// </summary>
  65. /// <param name="sender">event sender</param>
  66. /// <param name="e">event arguments</param>
  67. public void handleNewFrameData(object sender, NewProcessedFrameEventArgs e)
  68. {
  69. FrameData frameData = e.FrameData;
  70. lock (frameData) lock (sync)
  71. {
  72. if (frameData.ResetFlag)
  73. reset();
  74. foreach (TouchEvent te in frameData.TouchEvents)
  75. {
  76. switch (te.Type)
  77. {
  78. case TouchEventType.Down:
  79. activeTouches.Add(te.Touch.TrackID, new List<Vector2D>());
  80. activeTouches[te.Touch.TrackID].Add(te.Touch.RelativePosition);
  81. break;
  82. case TouchEventType.Move:
  83. activeTouches[te.Touch.TrackID].Add(te.Touch.RelativePosition);
  84. break;
  85. case TouchEventType.Up:
  86. activeTouches[te.Touch.TrackID].Add(te.Touch.RelativePosition);
  87. oldTouches.Add(nextFreeID, activeTouches[te.Touch.TrackID]);
  88. lastUpdates.Add(nextFreeID, timer.ElapsedMilliseconds);
  89. activeTouches.Remove(te.Touch.TrackID);
  90. nextFreeID++;
  91. break;
  92. }
  93. }
  94. }
  95. }
  96. /// <summary>
  97. /// Returns an output image showing the touch events.
  98. /// </summary>
  99. /// <param name="imageSize">output image size</param>
  100. /// <param name="numRows">number of rows in the palm grid</param>
  101. /// <param name="numColumns">number columns in the palm grid</param>
  102. /// <returns>image showing touch events</returns>
  103. public OutputImage getOutputImage(ImageSize imageSize, int numRows, int numColumns)
  104. {
  105. lock (sync)
  106. {
  107. long currentTime = timer.ElapsedMilliseconds;
  108. removeOldPositions(currentTime - Parameters.TouchEventVisualizerFadeOutTime);
  109. OutputImage outputImage = new OutputImage(imageSize);
  110. int imageWidth = imageSize.Width;
  111. int imageHeight = imageSize.Height;
  112. // border
  113. outputImage.drawBorder(Parameters.TouchEventVisualizerGridColor);
  114. // draw grid
  115. int widthPerColumn = imageWidth / numColumns;
  116. int heightPerRow = imageHeight / numRows;
  117. // find active blocks
  118. bool[,] activeBlocks = new bool[numRows, numColumns];
  119. if (numRows * numColumns > 1)
  120. {
  121. foreach (List<Vector2D> positions in activeTouches.Values)
  122. {
  123. Vector2D lastPosition = positions.Last();
  124. int activeRow = (int)Math.Min(lastPosition.Y * numRows, numRows - 1);
  125. int activeCol = (int)Math.Min(lastPosition.X * numColumns, numColumns - 1);
  126. activeBlocks[activeRow, activeCol] = true;
  127. }
  128. }
  129. // draw blocks
  130. int index = 1;
  131. for (int row = 0; row < numRows; row++)
  132. {
  133. for (int col = 0; col < numColumns; col++)
  134. {
  135. if (activeBlocks[row, col])
  136. outputImage.fillRectangle(new Rectangle(col * widthPerColumn, row * heightPerRow, widthPerColumn, heightPerRow), Parameters.TouchEventVisualizerActiveBlockColor);
  137. int x = (int)((col + 0.5f) * widthPerColumn) - 5;
  138. int y = (int)((row + 0.5f) * heightPerRow) + 5;
  139. outputImage.drawText(new Point(x, y), index.ToString(), Parameters.TouchEventVisualizerTextColor);
  140. index++;
  141. }
  142. }
  143. // draw grid
  144. for (int i = 0; i <= numColumns; i++)
  145. outputImage.drawLineSegment(new LineSegment2D(new Vector2D(i * widthPerColumn, 0), new Vector2D(i * widthPerColumn, imageHeight - 1)), Parameters.TouchEventVisualizerGridColor);
  146. for (int i = 0; i <= numRows; i++)
  147. outputImage.drawLineSegment(new LineSegment2D(new Vector2D(0, i * heightPerRow), new Vector2D(imageWidth - 1, i * heightPerRow)), Parameters.TouchEventVisualizerGridColor);
  148. // draw active touches
  149. foreach (List<Vector2D> positions in activeTouches.Values)
  150. outputImage.drawTouchGesture(positions, imageSize.MaxPixel);
  151. // draw old touches (fade out)
  152. foreach (int id in oldTouches.Keys)
  153. {
  154. List<Vector2D> positions = oldTouches[id];
  155. long lastUpdate = lastUpdates[id];
  156. float opacity = 1 - ((currentTime - lastUpdate) / (float)Parameters.TouchEventVisualizerFadeOutTime);
  157. outputImage.drawTouchGesture(positions, imageSize.MaxPixel, opacity);
  158. }
  159. return outputImage;
  160. }
  161. }
  162. /// <summary>
  163. /// Removes old touch events and update times.
  164. /// </summary>
  165. /// <param name="breakTime">every touch event which occured before breakTime is removed</param>
  166. private void removeOldPositions(long breakTime)
  167. {
  168. List<int> ids = new List<int>(lastUpdates.Keys);
  169. for (int i = ids.Count - 1; i >= 0; i--)
  170. {
  171. int id = ids[i];
  172. if (breakTime > lastUpdates[id])
  173. {
  174. oldTouches.Remove(id);
  175. lastUpdates.Remove(id);
  176. }
  177. }
  178. }
  179. }
  180. }