utils.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. from __future__ import division
  2. from time import sleep
  3. from threading import Thread
  4. from numpy import array, diag, dot, cos, sin, asarray, column_stack, c_
  5. from gtk import Window
  6. from src import RectangularArea
  7. class BoundingBoxArea(RectangularArea):
  8. def __init__(self, x, y, points):
  9. super(BoundingBoxArea, self).__init__(x, y, 0, 0)
  10. self.points = array(points).T
  11. self.update_bounds()
  12. def translate_points(self, tx, ty):
  13. self.points += [[tx], [ty]]
  14. def scale_points(self, scale, cx, cy):
  15. self.translate_points(-cx, -cy)
  16. self.points = dot(diag([scale, scale]), self.points)
  17. self.translate_points(cx, cy)
  18. def rotate_points(self, angle, cx, cy):
  19. cosa = cos(angle)
  20. sina = sin(angle)
  21. mat = array([[cosa, -sina], [sina, cosa]])
  22. self.translate_points(-cx, -cy)
  23. self.points = dot(mat, self.points)
  24. self.translate_points(cx, cy)
  25. def contains(self, x, y):
  26. ox, oy = self.get_position()
  27. return inside_shape((x - ox, y - oy), self.points)
  28. def update_bounds(self):
  29. min_x, min_y = self.points.min(1)
  30. max_x, max_y = self.points.max(1)
  31. width = max_x - min_x
  32. height = max_y - min_y
  33. if min_x or min_y:
  34. self.width = width
  35. self.height = height
  36. self.translate_points(-min_x, -min_y)
  37. self.translate(min_x, min_y)
  38. elif width != self.width or height != self.height:
  39. self.set_size(width, height)
  40. FLICK_UPDATE_RATE = 30
  41. class Flick(object):
  42. def __init__(self, callback, seconds, start_amount=1.0):
  43. self.callback = callback
  44. self.amount = start_amount
  45. self.iterations = int(seconds * FLICK_UPDATE_RATE)
  46. self.reduce_rate = start_amount / self.iterations
  47. def iteration(self):
  48. self.callback(self.amount)
  49. self.amount -= self.reduce_rate
  50. self.iterations -= 1
  51. return self.is_not_done()
  52. def is_not_done(self):
  53. return self.iterations > 0
  54. def is_done(self):
  55. return self.iterations <= 0
  56. class FlickThread(Thread):
  57. def __init__(self):
  58. super(FlickThread, self).__init__()
  59. self.flicks = []
  60. def run(self):
  61. while True:
  62. self.flicks = filter(Flick.iteration, self.flicks)
  63. sleep(1 / FLICK_UPDATE_RATE)
  64. def add(self, flick):
  65. self.flicks.append(flick)
  66. def inside_shape(p, verts):
  67. """Test whether the point p is inside the specified shape.
  68. The shape is specified by 'verts' and 'edges'
  69. Arguments:
  70. p - the 2d point
  71. verts - (N,2) array of points
  72. Returns:
  73. - True/False based on result of in/out test.
  74. Uses the 'ray to infinity' even-odd test.
  75. Let the ray be the horizontal ray starting at p and going to +inf in x.
  76. """
  77. x, y = p
  78. inside = False
  79. for i in range(-1, verts.shape[1] - 1):
  80. x0, y0 = verts[:,i]
  81. x1, y1 = verts[:,i + 1]
  82. # Check for horizontal line - another horz line can't intersect it
  83. # Check if both verts to the left, bottom or top of ray
  84. if (y0 == y1) or (x0 < x and x1 < x) or (y0 < y and y1 < y) \
  85. or (y0 > y and y1 > y):
  86. continue
  87. # Compute x intersection value
  88. xisect = x0 + (x1 - x0) * (y - y0) / (y1 - y0)
  89. print xisect, x
  90. if xisect >= x:
  91. inside = not inside
  92. return inside
  93. class GtkEventWindow(Window):
  94. def __init__(self, width=0, height=0):
  95. Window.__init__(self)
  96. self.area = RectangularArea(0, 0, width, height)
  97. self.connect('configure-event', self.sync_area)
  98. def get_area(self):
  99. return self.area
  100. def on_update(self, handler):
  101. self.area.on_update(handler)
  102. def sync_area(self, win, event):
  103. """Synchronize root multi-touch area with GTK window."""
  104. self.area.width = event.width
  105. self.area.height = event.height
  106. self.area.set_position(*event.get_coords())