2009-08-19 92 views
1

我正在研究一個iPhone應用程序,其中包含許多可以做的不同手勢輸入。目前有單指選擇/拖動,雙指滾動和兩指捏放大/縮小。我想添加兩個手指旋轉(你的手指在它們之間旋轉一個點),但我無法弄清楚如何讓它正常工作。所有其他手勢都是線性的,因此它們只是使用點或交叉產品的問題,非常多。iPhone上的雙指旋轉手勢?

我在想我必須在每個手指的前兩個點之間存儲斜率,並且如果矢量之間的角度接近90°,那麼存在旋轉的可能性。如果下一個手指移動角度也接近90°,並且一個手指上的矢量方向改變爲正向並且負向改變,那麼您將進行旋轉。問題是,我需要在這個手勢和其他手勢之間有一個非常乾淨的區別 - 上面的內容還不夠深入。

有什麼建議嗎?編輯:這是我做矢量分析的方式(與下面關於匹配像素的建議相反,請注意我在這裏使用我的Vector結構,你應該能夠猜到每個函數的作用):

//First, find the vector formed by the first touch's previous and current positions. 
struct Vector2f firstChange = getSubtractedVector([theseTouches get:0], [lastTouches get:0]); 
//We're going to store whether or not we should scroll. 
BOOL scroll = NO; 

//If there was only one touch, then we'll scroll no matter what. 
if ([theseTouches count] <= 1) 
{ 
    scroll = YES; 
} 
//Otherwise, we might scroll, scale, or rotate. 
else 
{ 
    //In the case of multiple touches, we need to test the slope between the two touches. 
    //If they're going in roughly the same direction, we should scroll. If not, zoom. 
    struct Vector2f secondChange = getSubtractedVector([theseTouches get:1], [lastTouches get:1]); 

    //Get the dot product of the two change vectors. 
    float dotChanges = getDotProduct(&firstChange, &secondChange); 

    //Get the 2D cross product of the two normalized change vectors. 
    struct Vector2f normalFirst = getNormalizedVector(&firstChange); 
    struct Vector2f normalSecond = getNormalizedVector(&secondChange); 
    float crossChanges = getCrossProduct(&normalFirst, &normalSecond); 

    //If the two vectors have a cross product that is less than cosf(30), then we know the angle between them is 30 degrees or less. 
    if (fabsf(crossChanges) <= SCROLL_MAX_CROSS && dotChanges > 0) 
    { 
     scroll = YES; 
    } 
    //Otherwise, they're in different directions so we should zoom or rotate. 
    else 
    { 
     //Store the vectors represented by the two sets of touches. 
     struct Vector2f previousDifference = getSubtractedVector([lastTouches get:1], [lastTouches get:0]); 
     struct Vector2f currentDifference = getSubtractedVector([theseTouches get:1], [theseTouches get:0]); 

     //Also find the normals of the two vectors. 
     struct Vector2f previousNormal = getNormalizedVector(&previousDifference); 
     struct Vector2f currentNormal = getNormalizedVector(&currentDifference); 

     //Find the distance between the two previous points and the two current points. 
     float previousDistance = getMagnitudeOfVector(&previousDifference); 
     float currentDistance = getMagnitudeOfVector(&currentDifference); 

     //Find the angles between the two previous points and the two current points. 
     float angleBetween = atan2(previousNormal.y,previousNormal.x) - atan2(currentNormal.y,currentNormal.x); 

     //If we had a short change in distance and the angle between touches is a big one, rotate. 
     if (fabsf(previousDistance - currentDistance) <= ROTATE_MIN_DISTANCE && fabsf(angleBetween) >= ROTATE_MAX_ANGLE) 
     { 
      if (angleBetween > 0) 
      { 
       printf("Rotate right.\n"); 
      } 
      else 
      { 
       printf("Rotate left.\n"); 
      } 
     } 
     else 
     { 
      //Get the dot product of the differences of the two points and the two vectors. 
      struct Vector2f differenceChange = getSubtracted(&secondChange, &firstChange); 
      float dotDifference = getDot(&previousDifference, &differenceChange); 
      if (dotDifference > 0) 
      { 
       printf("Zoom in.\n"); 
      } 
      else 
      { 
       printf("Zoom out.\n"); 
      } 
     } 
    } 
} 

if (scroll) 
{ 
    prinf("Scroll.\n"); 
} 

你應該注意到,如果你只是在做圖像處理或直接旋轉/縮放,那麼上述方法應該沒問題。但是,如果您像我一樣,並且您正在使用手勢來導致需要花費時間加載的內容,那麼您可能會希望避免執行此操作,直到連續激活了該手勢幾次。每個代碼與我的代碼之間的區別仍然不是完全獨立的,所以偶爾會在一堆縮放中獲得旋轉,反之亦然。

回答

2

我之前通過查找兩個手指之間的前一個和當前距離以及之前和當前線之間的角度來完成此操作。 然後,我選擇了一些經驗門檻的距離三角洲和角度θ,這對我來說工作得很好。

如果距離大於我的閾值,並且角度小於我的閾值,我縮放圖像。否則我旋轉它。 2手指滾動似乎很容易區分。

順便說一句,如果你實際存儲的值,觸摸已經存儲以前的點值。

CGPoint previousPoint1 = [self scalePoint:[touch1 previousLocationInView:nil]]; 
CGPoint previousPoint2 = [self scalePoint:[touch2 previousLocationInView:nil]]; 
CGPoint currentPoint1 = [self scalePoint:[touch1 locationInView:nil]]; 
CGPoint currentPoint2 = [self scalePoint:[touch2 locationInView:nil]]; 
+0

啊,距離是缺失的環節。我不敢相信我沒有想到這一點。我只專注於矢量的角度和方向,但距離真的是我認爲最大的區別。我已經知道以前的觸摸(用於縮放),但感謝您指出。 – Eli 2009-08-20 15:07:23

2

兩個手指,兩個移動,相反(ish)方向。什麼姿態與此相沖突?我認爲捏合/縮放比較接近,但捏合/縮放將從中心點開始移動(如果從每條線向後追蹤,線會平行並關閉),旋轉最初將具有平行線(向後追蹤),它們將彼此遠離,並且這些線將不斷地改變斜率(同時保持距離)。

編輯:你知道 - 這些都可以用相同的算法解決。

不是計算直線,而是計算每個手指下的像素。如果手指移動,平移圖像,使兩個初始像素仍然在兩根手指下。

這解決了包括滾動在內的所有雙指動作。

雙指滾動或縮放有時會看起來有點晃動,因爲它也會執行其他操作,但這是地圖應用似乎工作的方式(不包括它沒有的旋轉)。

+0

我喜歡你的像素想法,這將很好的圖像處理。不過,我正在製作一款遊戲,而旋轉手勢會將關卡旋轉到不同的視角,這需要一些資源加載等,因此不能直接區分。儘管如此,圖像案例中的好主意+1。 – Eli 2009-08-27 00:27:27