from __future__ import division from time import sleep from threading import Thread from numpy import array, diag, dot, cos, sin, asarray, column_stack, c_ from gtk import Window from src import RectangularArea class BoundingBoxArea(RectangularArea): def __init__(self, x, y, points): super(BoundingBoxArea, self).__init__(x, y, 0, 0) self.points = array(points).T self.update_bounds() def translate_points(self, tx, ty): self.points += [[tx], [ty]] def scale_points(self, scale, cx, cy): self.translate_points(-cx, -cy) self.points = dot(diag([scale, scale]), self.points) self.translate_points(cx, cy) def rotate_points(self, angle, cx, cy): cosa = cos(angle) sina = sin(angle) mat = array([[cosa, -sina], [sina, cosa]]) self.translate_points(-cx, -cy) self.points = dot(mat, self.points) self.translate_points(cx, cy) def contains(self, x, y): ox, oy = self.get_position() return inside_shape((x - ox, y - oy), self.points) def update_bounds(self): min_x, min_y = self.points.min(1) max_x, max_y = self.points.max(1) width = max_x - min_x height = max_y - min_y if min_x or min_y: self.width = width self.height = height self.translate_points(-min_x, -min_y) self.translate(min_x, min_y) elif width != self.width or height != self.height: self.set_size(width, height) FLICK_UPDATE_RATE = 30 class Flick(object): def __init__(self, callback, seconds, start_amount=1.0): self.callback = callback self.amount = start_amount self.iterations = int(seconds * FLICK_UPDATE_RATE) self.reduce_rate = start_amount / self.iterations def iteration(self): self.callback(self.amount) self.amount -= self.reduce_rate self.iterations -= 1 return self.is_not_done() def is_not_done(self): return self.iterations > 0 def is_done(self): return self.iterations <= 0 class FlickThread(Thread): def __init__(self): super(FlickThread, self).__init__() self.flicks = [] def run(self): while True: self.flicks = filter(Flick.iteration, self.flicks) sleep(1 / FLICK_UPDATE_RATE) def add(self, flick): self.flicks.append(flick) def inside_shape(p, verts): """Test whether the point p is inside the specified shape. The shape is specified by 'verts' and 'edges' Arguments: p - the 2d point verts - (N,2) array of points Returns: - True/False based on result of in/out test. Uses the 'ray to infinity' even-odd test. Let the ray be the horizontal ray starting at p and going to +inf in x. """ x, y = p inside = False for i in range(-1, verts.shape[1] - 1): x0, y0 = verts[:,i] x1, y1 = verts[:,i + 1] # Check for horizontal line - another horz line can't intersect it # Check if both verts to the left, bottom or top of ray if (y0 == y1) or (x0 < x and x1 < x) or (y0 < y and y1 < y) \ or (y0 > y and y1 > y): continue # Compute x intersection value xisect = x0 + (x1 - x0) * (y - y0) / (y1 - y0) if xisect >= x: inside = not inside return inside class GtkEventWindow(Window): def __init__(self, width=0, height=0): Window.__init__(self) self.area = RectangularArea(0, 0, width, height) self.connect('configure-event', self.sync_area) def get_area(self): return self.area def on_update(self, handler): self.area.on_update(handler) def sync_area(self, win, event): """Synchronize root multi-touch area with GTK window.""" self.area.width = event.width self.area.height = event.height self.area.set_position(*event.get_coords())