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