Commit 9e95bdd7 authored by UVA Multi-touch's avatar UVA Multi-touch

Some bugfixes in MultiTouchListener.

- TUIO server now has a separate verbose level.
- For practical reasons, active touch points are now stored in a list instead of a dictionary.
- Fixed AttributeError for some unitialized attributes.
parent 98d85613
......@@ -3,7 +3,7 @@ import time
from threading import Thread
from math import atan2, pi
from tuio_server import TuiListener
from tuio_server import TuioServer2D
from logger import Logger
from events import TapEvent, FlickEvent, RotateEvent, PinchEvent, PanEvent
......@@ -25,8 +25,8 @@ def add(a, b):
class TouchPoint(object):
def __init__(self, sid, x, y):
self.sid = sid
self.x = x
self.y = y
self.px = self.x = x
self.py = self.y = y
def update(self, x, y):
self.px = self.x
......@@ -35,7 +35,10 @@ class TouchPoint(object):
self.y = y
def distance_to(self, other_x, other_y):
return distance(self.x, self.y, other_x, other_y)
return distance((self.x, self.y), (other_x, other_y))
def distance_to_prev(self):
return self.distance_to(self.px, self.py)
def init_gesture_data(self, cx, cy):
self.pinch = self.old_pinch = self.distance_to(cx, cy)
......@@ -62,7 +65,6 @@ class TouchPoint(object):
# Heuristic constants
# TODO: Encapsulate DPI resolution in distance heuristics
SUPPORTED_GESTURES = ('tap', 'pan', 'flick', 'rotate', 'pinch')
TUIO_ADDRESS = ('localhost', 3333)
DOUBLE_TAP_DISTANCE_THRESHOLD = 30
FLICK_VELOCITY_TRESHOLD = 20
TAP_INTERVAL = .200
......@@ -71,24 +73,31 @@ MAX_MULTI_DRAG_DISTANCE = 100
class MultiTouchListener(Logger):
def __init__(self, verbose=0, update_rate=60, **kwargs):
def __init__(self, update_rate=60, verbose=0, tuio_verbose=0, **kwargs):
super(MultiTouchListener, self).__init__(**kwargs)
self.verbose = verbose
self.tuio_verbose = tuio_verbose
self.last_tap = 0
self.update_rate = update_rate
self.points_changed = False
self.handlers = {}
# Session id's pointing to point coordinates
self.points = {}
self.points = []
self.taps_down = []
self.taps = []
self.centroid = (0, 0)
def update_centroid(self):
self.old_centroid = self.centroid
cx, cy = zip(*[(p.x, p.y) for p in self.points])
l = len(self.points)
if not l:
self.centroid = (0, 0)
return
cx, cy = zip(*[(p.x, p.y) for p in self.points])
self.centroid = (reduce(add, cx, 0) / l, reduce(add, cy, 0) / l)
def analyze(self):
......@@ -148,7 +157,7 @@ class MultiTouchListener(Logger):
rotation += da
p.set_pinch(distance(p.x, p.y, cx, cy))
p.set_pinch(p.distance_to(cx, cy))
pinch += p.pinch_diff()
if rotation:
......@@ -166,7 +175,8 @@ class MultiTouchListener(Logger):
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]
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:
......@@ -180,10 +190,11 @@ class MultiTouchListener(Logger):
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)
if self.find_point(sid):
raise ValueError('Point with session id "%s" already exists.' % sid)
self.points[sid] = p = TouchPoint(sid, x, y)
p = TouchPoint(sid, x, y)
self.points.append(p)
self.update_centroid()
# Detect multi-point gestures
......@@ -191,24 +202,32 @@ class MultiTouchListener(Logger):
p.init_gesture_data(*self.centroid)
if len(self.points) == 2:
first_p = self.points[0]
first_p.init_gesture_data(*self.centroid)
self.points[0].init_gesture_data(*self.centroid)
self.taps_down.append(p)
self.last_tap = time.time()
self.points_changed = True
def find_point(self, sid, index=False):
for i, p in enumerate(self.points):
if p.sid == sid:
return (i, p) if index else p
if index:
return -1, None
def point_up(self, sid):
if sid not in self.points:
i, p = self.find_point(sid, index=True)
if not p:
raise KeyError('No point with session id "%s".' % sid)
p = self.points[sid]
del self.points[sid]
del self.points[i]
# Tap/flick detection
if p in self.taps_down:
# Detect if Flick based on movement
if distance(p.x, p.y, p.px, p.py) > FLICK_VELOCITY_TRESHOLD:
if p.distance_to_prev() > FLICK_VELOCITY_TRESHOLD:
self.trigger(FlickEvent(p.px, p.py, (p.dx(), p.dy())))
else:
if time.time() - self.last_tap < TAP_INTERVAL:
......@@ -219,18 +238,12 @@ class MultiTouchListener(Logger):
self.points_changed = True
def point_move(self, sid, x, y):
self.points[sid].update(x, y)
self.find_point(sid).update(x, y)
self.points_changed = True
def _tuio_handler(self, addr, tags, data, source):
# TODO: Call self.point_{down,up,move}
pass
def _tuio_server(self):
server = OSCServer(TUIO_ADDRESS)
server.addDefaultHandlers()
server.addMsgHandler('/tuio', self._tuio_handler)
server.serve_forever()
server = TuioServer2D(self, verbose=self.tuio_verbose)
server.start()
def start(self):
"""
......@@ -275,6 +288,6 @@ if __name__ == '__main__':
def tap(event):
print 'tap:', event
loop = MultiTouchListener(verbose=2)
loop = MultiTouchListener(verbose=2, tuio_verbose=1)
loop.bind('tap', tap)
loop.start()
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