TouchEventVisualizer.cs 8.1 KB

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