Commit ea38f361 authored by Taddeus Kroes's avatar Taddeus Kroes

Finished rewriting TouchProcessor.pde.

parent 5132ece7
...@@ -15,7 +15,7 @@ def TapEvent(GestureEvent): ...@@ -15,7 +15,7 @@ def TapEvent(GestureEvent):
def FlickEvent(GestureEvent): def FlickEvent(GestureEvent):
def __init__(self, x, y, velocity): def __init__(self, x, y, velocity):
super(TapEvent, self).__init__('flick') super(FlickEvent, self).__init__('flick')
self.x = x self.x = x
self.y = y self.y = y
self.velocity = velocity self.velocity = velocity
...@@ -23,3 +23,43 @@ def FlickEvent(GestureEvent): ...@@ -23,3 +23,43 @@ def FlickEvent(GestureEvent):
def __str__(self): def __str__(self):
return '<%s (%s, %s) velocity=%s>' % \ return '<%s (%s, %s) velocity=%s>' % \
(self.__class__.__name__, self.x, self.y, self.velocity) (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 #!/usr/bin/env python
from events import TapEvent, FlickEvent from events import TapEvent, FlickEvent, RotateEvent, PinchEvent, PanEvent
import time import time
from math import atan2 from math import atan2
from threading import Thread from threading import Thread
...@@ -32,8 +32,11 @@ class TouchPoint(object): ...@@ -32,8 +32,11 @@ class TouchPoint(object):
self.x = x self.x = x
self.y = y 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): 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) self.angle = self.old_angle = atan2(self.y - cy, self.x - cx)
def set_angle(self, angle): def set_angle(self, angle):
...@@ -44,6 +47,9 @@ class TouchPoint(object): ...@@ -44,6 +47,9 @@ class TouchPoint(object):
self.old_pinch = self.pinch self.old_pinch = self.pinch
self.pinch = pinch self.pinch = pinch
def angle_diff(self):
return self.angle - self.old_angle
def dx(self): def dx(self):
return int(self.x - self.px) return int(self.x - self.px)
...@@ -51,11 +57,14 @@ class TouchPoint(object): ...@@ -51,11 +57,14 @@ class TouchPoint(object):
return int(self.y - self.py) return int(self.y - self.py)
# Heuristic constants
# TODO: Encapsulate DPI resolution in distance heuristics
SUPPORTED_GESTURES = ('tap', 'pan', 'flick', 'rotate', 'pinch') SUPPORTED_GESTURES = ('tap', 'pan', 'flick', 'rotate', 'pinch')
TUIO_ADDRESS = ('localhost', 3333) TUIO_ADDRESS = ('localhost', 3333)
DOUBLE_TAP_DISTANCE = 30 DOUBLE_TAP_DISTANCE = 30
TAP_INTERVAL = .200 TAP_INTERVAL = .200
TAP_TIMEOUT = .200 TAP_TIMEOUT = .200
MAX_MULTI_DRAG_DISTANCE = 100
class MultiTouchListener(object): class MultiTouchListener(object):
...@@ -84,11 +93,10 @@ class MultiTouchListener(object): ...@@ -84,11 +93,10 @@ class MultiTouchListener(object):
if self.points_changed: if self.points_changed:
self.update_centroid() 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 # This gets rid of a lot of jittery events
if not self.detect_drag(): if not self.detect_pan():
self.detect_rotation() self.detect_rotation_and_pinch()
self.detect_pinch()
self.points_changed = False self.points_changed = False
...@@ -110,14 +118,63 @@ class MultiTouchListener(object): ...@@ -110,14 +118,63 @@ class MultiTouchListener(object):
self.trigger(TapEvent(*self.taps[0])) self.trigger(TapEvent(*self.taps[0]))
self.taps = [] self.taps = []
def detect_rotation(self): def detect_rotation_and_pinch(self):
if 'rotate' not in self.handlers: """
return 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 or l < 2:
if 'pinch' not in self.handlers:
return 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): def point_down(self, sid, x, y):
if sid in self.points: if sid in self.points:
raise KeyError('Point with session id "%s" already exists.' % sid) raise KeyError('Point with session id "%s" already exists.' % sid)
...@@ -198,7 +255,7 @@ class MultiTouchListener(object): ...@@ -198,7 +255,7 @@ class MultiTouchListener(object):
def bind(self, gesture, handler): def bind(self, gesture, handler):
if gesture not in SUPPORTED_GESTURES: if gesture not in SUPPORTED_GESTURES:
raise ValueError('Unsopported gesture "%s".' % gesture) raise ValueError('Unsupported gesture "%s".' % gesture)
if gesture not in self.handlers: if gesture not in self.handlers:
self.handlers[gesture] = [] self.handlers[gesture] = []
...@@ -207,7 +264,10 @@ class MultiTouchListener(object): ...@@ -207,7 +264,10 @@ class MultiTouchListener(object):
def trigger(self, event): def trigger(self, event):
if event.gesture in self.handlers: 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) 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