Просмотр исходного кода

Fixed rotation- and pinch-detection by keeping their amounts in global variables.

Taddeus Kroes 13 лет назад
Родитель
Сommit
c50325c1cf
1 измененных файлов с 87 добавлено и 90 удалено
  1. 87 90
      src/touch.py

+ 87 - 90
src/touch.py

@@ -71,6 +71,12 @@ class TouchPoint(object):
     def xy(self):
         return self.x, self.y
 
+    def dx(self):
+        return self.x - self.px
+
+    def dy(self):
+        return self.y- self.py
+
     def update(self, x, y):
         self.update_time = time.time()
         self.px = self.x
@@ -78,43 +84,21 @@ 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 distance_to_prev(self):
-        return self.distance_to(self.px, self.py)
-
-    def set_centroid(self, cx, cy):
-        self.pinch = self.distance_to(cx, cy)
-        self.angle = atan2(cy - self.y, self.x - cx)
-
-        if self.pinch != None:
-            self.old_pinch = self.pinch
-            self.old_angle = self.angle
-            self.pinch = pinch
-            self.angle = angle
-        else:
-            self.old_pinch = self.pinch = pinch
-            self.old_angle = self.angle = angle
-
-    def angle_diff(self):
-        return self.angle - self.old_angle
-
-    def pinch_diff(self):
-        return self.pinch - self.old_pinch
-
-    def dx(self):
-        return self.x - self.px
-
-    def dy(self):
-        return self.y - self.py
-
     def down_time(self):
         return time.time() - self.start_time
 
+    def angle(self, cx, cy):
+        return atan2(cy - self.y, self.x - cx)
+
+    def distance(self, other_x, other_y):
+        return distance((self.x, self.y), (other_x, other_y))
+
     def is_tap(self):
-        return self.distance_to_prev() < TAP_TIME \
-               and self.distance_to(self.start_x, self.start_y) < TAP_DISTANCE
+        return time.time() - self.update_time < TAP_TIME \
+               and self.distance(self.start_x, self.start_y) < TAP_DISTANCE
+
+    def distance_to_prev(self):
+        return self.distance(self.px, self.py)
 
     def is_stationary(self):
         return self.distance_to_prev() < DIST_THRESHOLD
@@ -134,10 +118,12 @@ class MultiTouchListener(Logger):
         self.points = []
 
         # Put centroid outside screen to prevent misinterpretation
-        self.centroid = (-1., -1.)
+        self.centroid = -1., -1.
 
         self.server = TuioServer2D(self, verbose=tuio_verbose)
 
+        self.angle = self.pinch = None
+
     def point_down(self, sid, x, y):
         """
         Called by TUIO listener when a new touch point is created, triggers a
@@ -177,7 +163,7 @@ class MultiTouchListener(Logger):
             t = time.time()
 
             if t - self.last_tap_time < TAP_TIMEOUT \
-                    and p.distance_to(*self.last_tap.xy) < DOUBLE_TAP_DISTANCE:
+                    and p.distance(*self.last_tap.xy) < DOUBLE_TAP_DISTANCE:
                 self.trigger(DoubleTap(p.x, p.y))
                 self.last_tap = None
                 self.last_tap_time = 0
@@ -200,7 +186,7 @@ class MultiTouchListener(Logger):
         """
         p = self.find_point(sid)
 
-        if p.distance_to(x, y) > DIST_THRESHOLD:
+        if p.distance(x, y) > DIST_THRESHOLD:
             p.update(x, y)
             self.trigger(MoveEvent(p))
             self.points_changed = True
@@ -215,33 +201,77 @@ class MultiTouchListener(Logger):
 
         if l < 2 or ('pinch' not in self.handlers \
                      and 'rotate' not in self.handlers):
+            self.set_state(None, None)
             return
 
+        self.calc_state()
+        rotation, pinch = self.state_diff()
         cx, cy = self.centroid
-        #rotation = pinch = 0
 
-        #for p in self.points:
-        #    da = p.angle_diff()
+        if rotation:
+            self.trigger(Rotate(cx, cy, rotation, l))
 
-        #    # Assert that angle is in [-pi, pi]
-        #    if da > pi:
-        #        da -= 2 * pi
-        #    elif da < pi:
-        #        da += 2 * pi
+        if pinch:
+            self.trigger(Pinch(cx, cy, pinch, l))
 
-        #    rotation += da
-        #    pinch += p.pinch_diff()
+    def calc_state(self):
+        l = len(self.points)
+        cx, cy = self.centroid
+        angle = reduce(add, [p.angle(cx, cy) for p in self.points]) / l
+        distance = reduce(add, [p.distance(cx, cy) for p in self.points]) / l
 
-        angles, pinches = zip(*[(p.angle_diff(), p.pinch_diff())
-                                for p in self.points])
-        rotation = reduce(add, angles)
-        pinch = reduce(add, pinches)
+        if self.angle == None:
+            self.set_state(angle, distance)
+            self.set_prev_state()
+        else:
+            self.set_prev_state()
+            self.set_state(angle, distance)
 
-        if rotation:
-            self.trigger(Rotate(cx, cy, rotation / l, l))
+    def set_state(self, angle, distance):
+        self.angle = angle
+        self.distance = distance
 
-        if pinch:
-            self.trigger(Pinch(cx, cy, pinch / l * 2, l))
+    def set_prev_state(self):
+        self.prev_angle = self.angle
+        self.prev_distance = self.distance
+
+    def state_diff(self):
+        da = self.angle - self.prev_angle
+
+        # Assert that angle is in [-pi, pi]
+        #if da > pi:
+        #    da %= 2 * pi
+        #elif da < -pi:
+        #    da %= -2 * pi
+
+        return da, self.distance / self.prev_distance
+
+    def update_centroid(self):
+        """
+        Calculate the centroid of all current touch points.
+        """
+        self.old_centroid = self.centroid
+        l = len(self.points)
+
+        # If there are no touch points, move the centroid to outside the screen
+        if not l:
+            self.centroid = -1., -1.
+            return
+
+        # Only use stationary points, if any
+        use = filter(TouchPoint.is_stationary, self.points)
+
+        if not len(use):
+            use = self.points
+
+        cx, cy = zip(*[(p.x, p.y) for p in use])
+        self.centroid = reduce(add, cx, 0) / l, reduce(add, cy, 0) / l
+
+    def centroid_movement(self):
+        cx, cy = self.centroid
+        ocx, ocy = self.old_centroid
+
+        return cx - ocx, cy - ocy
 
     def detect_pan(self):
         """
@@ -254,9 +284,9 @@ class MultiTouchListener(Logger):
             return False
 
         m = MAX_MULTI_DRAG_DISTANCE
-        clustered = l == 1 or all([p.distance_to(*self.centroid) <= m \
+        clustered = l == 1 or all([p.distance(*self.centroid) <= m \
                                    for p in self.points])
-        directions = [(cmp(p.dx(), 0), cmp(p.dy(), 0)) \
+        directions = [(cmp(p.dx(), 0), cmp(p.dy(), 0))
                       for p in self.points]
 
         if not clustered or not any(map(all, zip(*directions))):
@@ -282,15 +312,6 @@ class MultiTouchListener(Logger):
         if index:
             return -1, None
 
-    def detect_pinch(self, moved):
-        cx, cy = self.centroid
-        dist = moved.distance_to(cx, cy)
-        old_dist = distance((moved.px, moved.py), self.centroid)
-
-        if abs(dist - old_dist) > DIST_THRESHOLD:
-            self.trigger(Pinch(cx, cy, dist / old_dist,
-                                    len(self.points)))
-
     def detect_single_tap(self):
         """
         Check if a single tap event should be triggered by checking is the last
@@ -301,31 +322,6 @@ class MultiTouchListener(Logger):
             self.last_tap = None
             self.last_tap_time = 0
 
-    def update_centroid(self):
-        """
-        Calculate the centroid of all current touch points.
-        """
-        self.old_centroid = self.centroid
-        l = len(self.points)
-
-        # If there are no touch points, move the entroid to outside the screen
-        if not l:
-            self.centroid = (-1., -1.)
-            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)
-
-        # Update angle and pinch of all touch points
-        for p in self.points:
-            p.set_centroid(*self.centroid)
-
-    def centroid_movement(self):
-        cx, cy = self.centroid
-        ocx, ocy = self.old_centroid
-
-        return cx - ocx, cy - ocy
-
     def detect_gestures(self):
         """
         Detect if any gestures have occured in the past gesture frame. This
@@ -432,6 +428,7 @@ if __name__ == '__main__':
     listener.bind('single_tap', tap, 1)
     listener.bind('double_tap', tap, 2)
     listener.bind('rotate', lambda e: 0)
+    listener.bind('pinch', lambda e: 0)
 
     try:
         listener.start()