Jelajahi Sumber

Fixed rotation (finally).

Taddeus Kroes 13 tahun lalu
induk
melakukan
0fc618e566
1 mengubah file dengan 31 tambahan dan 42 penghapusan
  1. 31 42
      src/touch.py

+ 31 - 42
src/touch.py

@@ -36,13 +36,19 @@ GESTURE_UPDATE_RATE = 60
 DIST_THRESHOLD = 1. / max(screen_size)
 
 
-def angle_mod(angle):
-    if angle > pi:
-        angle -= 2 * pi
-    elif angle < -pi:
-        angle += 2 * pi
+def rotation(p0, p1):
+    """
+    Calculate the relative rotation from p0 to p1 around (0, 0) in radians.
+    """
+    rot = atan2(*p1) - atan2(*p0)
 
-    return angle
+    # Assert that the smallest possible rotation value is returned
+    if rot >= pi:
+        rot = 2 * pi - rot
+    elif rot <= -pi:
+        rot = -2 * pi - rot
+
+    return rot
 
 
 def distance(a, b):
@@ -114,21 +120,11 @@ class TouchPoint(object):
     def is_stationary(self):
         return self.distance_to_prev() < DIST_THRESHOLD
 
-    def angle_diff(self, cx, cy):
-        angle = atan2(cy - self.y, self.x - cx)
+    def rotation_around(self, cx, cy):
+        prev = self.px - cx, self.py - cy
+        current = self.x - cx, self.y - cy
 
-        if self.prev_angle == None:
-            delta = 0
-        elif self.prev_angle > 0 and angle < 0:
-            delta = -2 * pi - angle + self.prev_angle
-        elif self.prev_angle < 0 and angle > 0:
-            delta = 2 * pi - angle + self.prev_angle
-        else:
-            delta = angle - self.prev_angle
-
-        self.prev_angle = angle
-
-        return delta
+        return rotation(prev, current)
 
 
 class MultiTouchListener(Logger):
@@ -146,11 +142,12 @@ class MultiTouchListener(Logger):
 
         # Put centroid outside screen to prevent misinterpretation
         self.centroid = -1., -1.
+        self.rot_centroid = -1., -1.
 
         self.server = TuioServer2D(self, verbose=tuio_verbose)
 
         self.angle = self.pinch = None
-        self.prev_pinch = None
+        self.prev_dist = None
 
     def point_down(self, sid, x, y):
         """
@@ -222,51 +219,43 @@ class MultiTouchListener(Logger):
     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.
+        centroid. Pinch is the percentual change in average distance change
+        from the points to the centroid.
         """
         l = len(self.points)
 
         if l < 2 or ('pinch' not in self.handlers \
                      and 'rotate' not in self.handlers):
-            self.prev_pinch = None
+            self.prev_dist = None
             return
 
-        # Get the sum of all angle differences
         l = len(self.points)
         cx, cy = self.centroid
-        #rcx, rcy = self.rot_centroid
-        rotation = sum([p.angle_diff(cx, cy) for p in self.points]) / l
-        pinch = sum([p.distance(cx, cy) for p in self.points]) / l
+        rotation = sum([p.rotation_around(cx, cy) for p in self.points]) / l
+        dist = sum([p.distance(cx, cy) for p in self.points]) / l
 
         if rotation:
             self.trigger(Rotate(cx, cy, rotation, l))
 
-        if self.prev_pinch == None:
-            self.prev_pinch = pinch
-        else:
-            pinch -= self.prev_pinch
+        if self.prev_dist != None:
+            pinch = dist / self.prev_dist
 
             if pinch:
                 self.trigger(Pinch(cx, cy, pinch, l))
 
+        self.prev_dist = dist
+
     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:
+        if self.points:
+            self.centroid = average([p.xy for p in self.points])
+        else:
+            # There are no touch points, move the centroid out of the screen
             self.centroid = -1., -1.
-            return
-
-        self.centroid = average([p.xy for p in self.points])
-
-        # Only use stationary points for rotational centroid, if any
-        stat = [p.xy for p in filter(TouchPoint.is_stationary, self.points)]
-        self.rot_centroid = average(stat) if stat else self.centroid
 
 
     def centroid_movement(self):