|
@@ -0,0 +1,167 @@
|
|
|
|
|
+#!/usr/bin/env python
|
|
|
|
|
+from __future__ import division
|
|
|
|
|
+import pygame
|
|
|
|
|
+import sys
|
|
|
|
|
+import time
|
|
|
|
|
+from threading import Thread
|
|
|
|
|
+from math import degrees, cos, sin
|
|
|
|
|
+
|
|
|
|
|
+from src.server import GestureServer
|
|
|
|
|
+from src.window import FullscreenWindow
|
|
|
|
|
+from src.trackers.transform import TransformationTracker
|
|
|
|
|
+from src.trackers.tap import TapTracker
|
|
|
|
|
+from src.screen import screen_size
|
|
|
|
|
+
|
|
|
|
|
+import tests.parse_arguments
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+pygame.init()
|
|
|
|
|
+
|
|
|
|
|
+FULLSCREEN = '-f' in sys.argv[1:]
|
|
|
|
|
+
|
|
|
|
|
+# Config
|
|
|
|
|
+FINGER_RADIUS = 20
|
|
|
|
|
+CENTROID_RADIUS = 15
|
|
|
|
|
+W, H = screen_size if FULLSCREEN else (640, 480)
|
|
|
|
|
+
|
|
|
|
|
+BG_COLOR = 0, 0, 0
|
|
|
|
|
+LINE_COLOR = 128, 128, 128
|
|
|
|
|
+CIRCLE_COLOR = 255, 255, 255
|
|
|
|
|
+RECT_COLOR = 0, 200, 0
|
|
|
|
|
+RECT_POS = W / 2, H / 2
|
|
|
|
|
+RECT_SIZE = W / 6., H / 6.
|
|
|
|
|
+
|
|
|
|
|
+TAP_RADIUS = 65
|
|
|
|
|
+TAP_INCREMENT = .3
|
|
|
|
|
+
|
|
|
|
|
+BEAM_COLOR = 255, 50, 50
|
|
|
|
|
+BEAM_LENGTH = 50
|
|
|
|
|
+BEAM_WIDTH = 3
|
|
|
|
|
+BEAM_INCREMENT = .8
|
|
|
|
|
+
|
|
|
|
|
+MAX_SCALE = 10
|
|
|
|
|
+
|
|
|
|
|
+# Create canvas GUI
|
|
|
|
|
+flags = pygame.FULLSCREEN if FULLSCREEN else 0
|
|
|
|
|
+screen = pygame.display.set_mode((W, H), flags)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def coord(x, y):
|
|
|
|
|
+ w, h = screen_size
|
|
|
|
|
+ return int(round(W / w * x)), int(round(H / h * y))
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# Global state
|
|
|
|
|
+angle = 0
|
|
|
|
|
+scale = 1
|
|
|
|
|
+taps = []
|
|
|
|
|
+dtaps = []
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def update():
|
|
|
|
|
+ global taps, dtaps
|
|
|
|
|
+
|
|
|
|
|
+ # Clear previous frame
|
|
|
|
|
+ screen.fill(BG_COLOR)
|
|
|
|
|
+
|
|
|
|
|
+ # Apply scale and rotation to a fixed-size rectangle canvas
|
|
|
|
|
+ canvas = pygame.Surface(RECT_SIZE)
|
|
|
|
|
+ canvas.fill(RECT_COLOR)
|
|
|
|
|
+ transformed = pygame.transform.rotozoom(canvas, degrees(angle), scale)
|
|
|
|
|
+ rect = transformed.get_rect()
|
|
|
|
|
+ rect.center = W / 2, H / 2
|
|
|
|
|
+ screen.blit(transformed, rect)
|
|
|
|
|
+
|
|
|
|
|
+ # Draw touch points
|
|
|
|
|
+ if transform.centroid:
|
|
|
|
|
+ c = coord(*transform.centroid.xy)
|
|
|
|
|
+
|
|
|
|
|
+ for p in server.points.itervalues():
|
|
|
|
|
+ xy = coord(p.x, p.y)
|
|
|
|
|
+
|
|
|
|
|
+ # Draw line to centroid
|
|
|
|
|
+ if transform.centroid:
|
|
|
|
|
+ pygame.draw.line(screen, LINE_COLOR, xy, c, 1)
|
|
|
|
|
+
|
|
|
|
|
+ # Draw outlined circle around touch point
|
|
|
|
|
+ pygame.draw.circle(screen, CIRCLE_COLOR, xy, FINGER_RADIUS, 1)
|
|
|
|
|
+
|
|
|
|
|
+ # Fill filled circle with background color within the outline
|
|
|
|
|
+ pygame.draw.circle(screen, BG_COLOR, xy, FINGER_RADIUS - 1, 0)
|
|
|
|
|
+
|
|
|
|
|
+ # Draw filled circle around centroid
|
|
|
|
|
+ if transform.centroid:
|
|
|
|
|
+ pygame.draw.circle(screen, CIRCLE_COLOR, c, CENTROID_RADIUS)
|
|
|
|
|
+
|
|
|
|
|
+ # Draw an expanding circle around each tap event
|
|
|
|
|
+ taps = filter(lambda (xy, r): r <= TAP_RADIUS, taps)
|
|
|
|
|
+
|
|
|
|
|
+ for tap in taps:
|
|
|
|
|
+ xy, radius = tap
|
|
|
|
|
+ pygame.draw.circle(screen, CIRCLE_COLOR, xy, int(radius), 2)
|
|
|
|
|
+
|
|
|
|
|
+ # Increment radius
|
|
|
|
|
+ tap[1] += TAP_INCREMENT
|
|
|
|
|
+
|
|
|
|
|
+ # Shoot a beam from each double tap event to the left
|
|
|
|
|
+ dtaps = filter(lambda (x, y, s): 0 <= x <= W, dtaps)
|
|
|
|
|
+
|
|
|
|
|
+ for dtap in dtaps:
|
|
|
|
|
+ x, y, single = dtap
|
|
|
|
|
+
|
|
|
|
|
+ if single:
|
|
|
|
|
+ start_x = x
|
|
|
|
|
+ end_x = x + BEAM_LENGTH
|
|
|
|
|
+ dtap[0] += BEAM_INCREMENT
|
|
|
|
|
+ else:
|
|
|
|
|
+ start_x = x - BEAM_LENGTH
|
|
|
|
|
+ end_x = x
|
|
|
|
|
+ dtap[0] -= BEAM_INCREMENT
|
|
|
|
|
+
|
|
|
|
|
+ pygame.draw.line(screen, BEAM_COLOR, (start_x, y), (end_x, y), BEAM_WIDTH)
|
|
|
|
|
+
|
|
|
|
|
+ # Update canvas
|
|
|
|
|
+ pygame.display.flip()
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# Create server and fullscreen window
|
|
|
|
|
+server = GestureServer()
|
|
|
|
|
+win = FullscreenWindow(server=server)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# Bind trackers
|
|
|
|
|
+def rotate(gesture):
|
|
|
|
|
+ global angle
|
|
|
|
|
+ angle += gesture.get_angle()
|
|
|
|
|
+
|
|
|
|
|
+def pinch(gesture):
|
|
|
|
|
+ global scale
|
|
|
|
|
+ scale = min(scale * gesture.get_scale(), MAX_SCALE)
|
|
|
|
|
+
|
|
|
|
|
+transform = TransformationTracker(win)
|
|
|
|
|
+transform.bind('rotate', rotate)
|
|
|
|
|
+transform.bind('pinch', pinch)
|
|
|
|
|
+
|
|
|
|
|
+tap = TapTracker(win)
|
|
|
|
|
+tap.bind('tap', lambda g: taps.append([coord(*g.xy), FINGER_RADIUS]))
|
|
|
|
|
+tap.bind('single_tap', lambda g: dtaps.append(list(coord(*g.xy)) + [1]))
|
|
|
|
|
+tap.bind('double_tap', lambda g: dtaps.append(list(coord(*g.xy)) + [0]))
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+try:
|
|
|
|
|
+ # Start touch gesture server in separate thread
|
|
|
|
|
+ thread = Thread(target=server.start)
|
|
|
|
|
+ thread.daemon = True
|
|
|
|
|
+ thread.start()
|
|
|
|
|
+
|
|
|
|
|
+ # Start GUI event loop
|
|
|
|
|
+ def is_quit_event(e):
|
|
|
|
|
+ return e.type == pygame.QUIT \
|
|
|
|
|
+ or (e.type == pygame.KEYDOWN and e.key == ord('q'))
|
|
|
|
|
+
|
|
|
|
|
+ while not any(filter(is_quit_event, pygame.event.get())):
|
|
|
|
|
+ update()
|
|
|
|
|
+except KeyboardInterrupt:
|
|
|
|
|
+ pass
|
|
|
|
|
+finally:
|
|
|
|
|
+ server.stop()
|