Commit ea38f361 authored by Taddeus Kroes's avatar Taddeus Kroes

Finished rewriting TouchProcessor.pde.

parent 5132ece7
......@@ -15,7 +15,7 @@ def TapEvent(GestureEvent):
def FlickEvent(GestureEvent):
def __init__(self, x, y, velocity):
super(TapEvent, self).__init__('flick')
super(FlickEvent, self).__init__('flick')
self.x = x
self.y = y
self.velocity = velocity
......@@ -23,3 +23,43 @@ def FlickEvent(GestureEvent):
def __str__(self):
return '<%s (%s, %s) velocity=%s>' % \
(self.__class__.__name__, self.x, self.y, self.velocity)
def RotateEvent(GestureEvent):
def __init__(self, cx, cy, angle, n):
super(RotateEvent, self).__init__('rotate')
self.cx = cx
self.cy = cy
self.angle = angle
self.n = n
def __str__(self):
return '<%s (%s, %s) angle=%s n=%d>' % \
(self.__class__.__name__, self.x, self.y, self.angle, self.n)
def PinchEvent(GestureEvent):
def __init__(self, cx, cy, amount, n):
super(RotateEvent, self).__init__('pinch')
self.cx = cx
self.cy = cy
self.amount = amount
self.n = n
def __str__(self):
return '<%s (%s, %s) amount=%s n=%d>' % \
(self.__class__.__name__, self.x, self.y, self.amount, self.n)
def PanEvent(GestureEvent):
def __init__(self, x, y, dx, dy, n):
super(RotateEvent, self).__init__('pan')
self.x = x
self.y = y
self.dx = dx
self.dy = dy
self.n = n
def __str__(self):
return '<%s (%s, %s) amount=%s n=%d>' % \
(self.__class__.__name__, self.x, self.y, self.amount, self.n)
#!/usr/bin/env python
from events import TapEvent, FlickEvent
from events import TapEvent, FlickEvent, RotateEvent, PinchEvent, PanEvent
import time
from math import atan2
from threading import Thread
......@@ -32,8 +32,11 @@ class TouchPoint(object):
self.x = x
self.y = y
def distance_to(self, other_x, other_y):
return distance(self.x, self.y, other_x, other_y)
def init_gesture_data(self, cx, cy):
self.pinch = self.old_pinch = distance(self.x, self.y, cx, cy)
self.pinch = self.old_pinch = self.distance_to(cx, cy)
self.angle = self.old_angle = atan2(self.y - cy, self.x - cx)
def set_angle(self, angle):
......@@ -44,6 +47,9 @@ class TouchPoint(object):
self.old_pinch = self.pinch
self.pinch = pinch
def angle_diff(self):
return self.angle - self.old_angle
def dx(self):
return int(self.x - self.px)
......@@ -51,11 +57,14 @@ class TouchPoint(object):
return int(self.y - self.py)
# Heuristic constants
# TODO: Encapsulate DPI resolution in distance heuristics
SUPPORTED_GESTURES = ('tap', 'pan', 'flick', 'rotate', 'pinch')
TUIO_ADDRESS = ('localhost', 3333)
DOUBLE_TAP_DISTANCE = 30
TAP_INTERVAL = .200
TAP_TIMEOUT = .200
MAX_MULTI_DRAG_DISTANCE = 100
class MultiTouchListener(object):
......@@ -84,11 +93,10 @@ class MultiTouchListener(object):
if self.points_changed:
self.update_centroid()
# Do not try to rotate or pinch while dragging
# Do not try to rotate or pinch while panning
# This gets rid of a lot of jittery events
if not self.detect_drag():
self.detect_rotation()
self.detect_pinch()
if not self.detect_pan():
self.detect_rotation_and_pinch()
self.points_changed = False
......@@ -110,14 +118,63 @@ class MultiTouchListener(object):
self.trigger(TapEvent(*self.taps[0]))
self.taps = []
def detect_rotation(self):
if 'rotate' not in self.handlers:
return
def detect_rotation_and_pinch(self):
"""
Rotation is the average angle change between each point and the
centroid. Pinch is the average distance change from the points to the
centroid.
"""
l = len(self.points)
def detect_pinch(self):
if 'pinch' not in self.handlers:
if 'pinch' not in self.handlers or l < 2:
return
rotation = pinch = 0
for p in self.points:
p.set_angle(atan2(p.y - cy, p.x - cx))
da = p.angle_diff()
# Assert that angle is in [-PI, PI]
if da > PI:
da -= 2 * PI
elif da < PI:
da += 2 * PI
rotation += da
p.set_pinch(distance(p.x, p.y, cx, cy))
pinch += p.pinch_diff()
if rotation:
self.trigger(RotateEvent(cx, cy, rotation / l, l))
if pinch:
self.trigger(PinchEvent(cx, cy, pinch / l, l))
def detect_pan(self):
"""
Look for multi-finger drag events. Multi-drag is defined as all the
fingers moving close-ish together in the same direction.
"""
l = len(self.points)
m = MAX_MULTI_DRAG_DISTANCE
clustered = l == 1 or all([p.distance_to(*self.centroid) <= m \
for p in self.points])
directions = [(cmp(p.dx(), 0), cmp(p.dy(), 0)) for p in self.points]
if any(map(all, zip(*directions))) and clustered:
if l == 1:
p = self.points[0]
cx, cy, dx, dy = p.x, p.y, p.dx(), p.dy()
else:
cx, cy = self.centroid
old_cx, old_cy = self.old_centroid
dx, dy = cx - old_cx, cy - old_cy
self.trigger(PanEvent(cx, cy, dx, dy, l))
def point_down(self, sid, x, y):
if sid in self.points:
raise KeyError('Point with session id "%s" already exists.' % sid)
......@@ -198,7 +255,7 @@ class MultiTouchListener(object):
def bind(self, gesture, handler):
if gesture not in SUPPORTED_GESTURES:
raise ValueError('Unsopported gesture "%s".' % gesture)
raise ValueError('Unsupported gesture "%s".' % gesture)
if gesture not in self.handlers:
self.handlers[gesture] = []
......@@ -207,7 +264,10 @@ class MultiTouchListener(object):
def trigger(self, event):
if event.gesture in self.handlers:
for handler in self.handlers[event.gesture]:
h = self.handlers[event.gesture]
self.log('Event triggered: "%s" (%d handlers)' % (event, len(h)))
for handler in h:
handler(event)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment