152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2010 Google Inc.
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com *
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com */
852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
10ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
1152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com#include "SkTouchGesture.h"
1252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com#include "SkMatrix.h"
1352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com#include "SkTime.h"
1452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
1552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com#define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER   true
1652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
1759f46b81f8bdd1b524f5cc43bc27603f9604c71arobertphillips@google.comstatic const SkScalar MAX_FLING_SPEED = SkIntToScalar(1500);
1852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
1959f46b81f8bdd1b524f5cc43bc27603f9604c71arobertphillips@google.comstatic SkScalar pin_max_fling(SkScalar speed) {
2052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (speed > MAX_FLING_SPEED) {
2152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        speed = MAX_FLING_SPEED;
2252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
2352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    return speed;
2452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
2552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
2652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comstatic double getseconds() {
2752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    return SkTime::GetMSecs() * 0.001;
2852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
2952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
3052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com// returns +1 or -1, depending on the sign of x
31647a804c3dd53b6743091ec97dd12111f90efec3bsalomon@google.com// returns +1 if z is zero
32647a804c3dd53b6743091ec97dd12111f90efec3bsalomon@google.comstatic SkScalar SkScalarSignNonZero(SkScalar x) {
3352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    SkScalar sign = SK_Scalar1;
3452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (x < 0) {
3552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        sign = -sign;
3652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
3752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    return sign;
3852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
3952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
4052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comstatic void unit_axis_align(SkVector* unit) {
4152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    const SkScalar TOLERANCE = SkDoubleToScalar(0.15);
4252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (SkScalarAbs(unit->fX) < TOLERANCE) {
4352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        unit->fX = 0;
44647a804c3dd53b6743091ec97dd12111f90efec3bsalomon@google.com        unit->fY = SkScalarSignNonZero(unit->fY);
4552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    } else if (SkScalarAbs(unit->fY) < TOLERANCE) {
46647a804c3dd53b6743091ec97dd12111f90efec3bsalomon@google.com        unit->fX = SkScalarSignNonZero(unit->fX);
4752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        unit->fY = 0;
4852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
4952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
5052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
5152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comvoid SkFlingState::reset(float sx, float sy) {
5252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fActive = true;
534b413c8bb123e42ca4b9c7bfa6bc2167283cb84ccommit-bot@chromium.org    fDirection.set(sx, sy);
5452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fSpeed0 = SkPoint::Normalize(&fDirection);
5552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fSpeed0 = pin_max_fling(fSpeed0);
5652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fTime0 = getseconds();
5752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
5852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    unit_axis_align(&fDirection);
5952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com//    printf("---- speed %g dir %g %g\n", fSpeed0, fDirection.fX, fDirection.fY);
6052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
6152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
6252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.combool SkFlingState::evaluateMatrix(SkMatrix* matrix) {
6352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (!fActive) {
6452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        return false;
6552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
6652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
67c31a9d6d398336ed094dafcc284282f0102ef7d2reed@google.com    const float t =  (float)(getseconds() - fTime0);
6852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    const float MIN_SPEED = 2;
69c31a9d6d398336ed094dafcc284282f0102ef7d2reed@google.com    const float K0 = 5;
70c31a9d6d398336ed094dafcc284282f0102ef7d2reed@google.com    const float K1 = 0.02f;
7152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    const float speed = fSpeed0 * (sk_float_exp(- K0 * t) - K1);
7252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (speed <= MIN_SPEED) {
7352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        fActive = false;
7452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        return false;
7552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
7652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    float dist = (fSpeed0 - speed) / K0;
7752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
7852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com//    printf("---- time %g speed %g dist %g\n", t, speed, dist);
7952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    float tx = fDirection.fX * dist;
8052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    float ty = fDirection.fY * dist;
8152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER) {
82c31a9d6d398336ed094dafcc284282f0102ef7d2reed@google.com        tx = (float)sk_float_round2int(tx);
83c31a9d6d398336ed094dafcc284282f0102ef7d2reed@google.com        ty = (float)sk_float_round2int(ty);
8452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
854b413c8bb123e42ca4b9c7bfa6bc2167283cb84ccommit-bot@chromium.org    matrix->setTranslate(tx, ty);
8652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com//    printf("---- evaluate (%g %g)\n", tx, ty);
8752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
8852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    return true;
8952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
9052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
9152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com///////////////////////////////////////////////////////////////////////////////
9252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
9352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comstatic const SkMSec MAX_DBL_TAP_INTERVAL = 300;
9452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comstatic const float MAX_DBL_TAP_DISTANCE = 100;
9552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comstatic const float MAX_JITTER_RADIUS = 2;
9652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
9752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com// if true, then ignore the touch-move, 'cause its probably just jitter
9852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comstatic bool close_enough_for_jitter(float x0, float y0, float x1, float y1) {
9952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    return  sk_float_abs(x0 - x1) <= MAX_JITTER_RADIUS &&
10052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            sk_float_abs(y0 - y1) <= MAX_JITTER_RADIUS;
10152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
10252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
10352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com///////////////////////////////////////////////////////////////////////////////
10452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
10552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comSkTouchGesture::SkTouchGesture() {
10652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    this->reset();
10752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
10852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
10952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comSkTouchGesture::~SkTouchGesture() {
11052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
11152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
11252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comvoid SkTouchGesture::reset() {
11352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fTouches.reset();
11452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fState = kEmpty_State;
11552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fLocalM.reset();
11652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fGlobalM.reset();
11752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
11852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fLastUpT = SkTime::GetMSecs() - 2*MAX_DBL_TAP_INTERVAL;
11952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fLastUpP.set(0, 0);
12052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
12152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
12252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comvoid SkTouchGesture::flushLocalM() {
12352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fGlobalM.postConcat(fLocalM);
12452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fLocalM.reset();
12552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
12652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
12752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comconst SkMatrix& SkTouchGesture::localM() {
12852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (fFlinger.isActive()) {
12952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        if (!fFlinger.evaluateMatrix(&fLocalM)) {
13052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            this->flushLocalM();
13152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        }
13252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
13352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    return fLocalM;
13452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
13552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
13652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comvoid SkTouchGesture::appendNewRec(void* owner, float x, float y) {
13752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    Rec* rec = fTouches.append();
13852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    rec->fOwner = owner;
13952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    rec->fStartX = rec->fPrevX = rec->fLastX = x;
14052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    rec->fStartY = rec->fPrevY = rec->fLastY = y;
14152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    rec->fLastT = rec->fPrevT = SkTime::GetMSecs();
14252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
14352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
14452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comvoid SkTouchGesture::touchBegin(void* owner, float x, float y) {
14538406c82b913350e55fa04af8c1941cd9b4aff52tfarina//    SkDebugf("--- %d touchBegin %p %g %g\n", fTouches.count(), owner, x, y);
14652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
14752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    int index = this->findRec(owner);
14852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (index >= 0) {
14952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        this->flushLocalM();
15052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        fTouches.removeShuffle(index);
15152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        SkDebugf("---- already exists, removing\n");
15252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
15352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
15452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (fTouches.count() == 2) {
15552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        return;
15652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
15752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
15852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    this->flushLocalM();
15952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fFlinger.stop();
16052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
16152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    this->appendNewRec(owner, x, y);
16252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
16352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    switch (fTouches.count()) {
16452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        case 1:
16552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fState = kTranslate_State;
16652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            break;
16752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        case 2:
16852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fState = kZoom_State;
16952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            break;
17052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        default:
17152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            break;
17252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
17352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
17452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
17552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comint SkTouchGesture::findRec(void* owner) const {
17652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    for (int i = 0; i < fTouches.count(); i++) {
17752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        if (owner == fTouches[i].fOwner) {
17852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            return i;
17952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        }
18052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
18152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    return -1;
18252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
18352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
18459f46b81f8bdd1b524f5cc43bc27603f9604c71arobertphillips@google.comstatic SkScalar center(float pos0, float pos1) {
1854b413c8bb123e42ca4b9c7bfa6bc2167283cb84ccommit-bot@chromium.org    return (pos0 + pos1) * 0.5f;
18652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
18752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
18852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comstatic const float MAX_ZOOM_SCALE = 4;
18952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comstatic const float MIN_ZOOM_SCALE = 0.25f;
19052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
19152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comfloat SkTouchGesture::limitTotalZoom(float scale) const {
19252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    // this query works 'cause we know that we're square-scale w/ no skew/rotation
19359f46b81f8bdd1b524f5cc43bc27603f9604c71arobertphillips@google.com    const float curr = SkScalarToFloat(fGlobalM[0]);
194d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
19552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (scale > 1 && curr * scale > MAX_ZOOM_SCALE) {
19652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        scale = MAX_ZOOM_SCALE / curr;
19752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    } else if (scale < 1 && curr * scale < MIN_ZOOM_SCALE) {
19852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        scale = MIN_ZOOM_SCALE / curr;
19952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
20052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    return scale;
20152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
20252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
20352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comvoid SkTouchGesture::touchMoved(void* owner, float x, float y) {
20438406c82b913350e55fa04af8c1941cd9b4aff52tfarina//    SkDebugf("--- %d touchMoved %p %g %g\n", fTouches.count(), owner, x, y);
20552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
206936b73424f7393994be832376287da988a52b993caryclark    if (kEmpty_State == fState) {
207936b73424f7393994be832376287da988a52b993caryclark        return;
208936b73424f7393994be832376287da988a52b993caryclark    }
20952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
21052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    int index = this->findRec(owner);
21152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (index < 0) {
21252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        // not found, so I guess we should add it...
21352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        SkDebugf("---- add missing begin\n");
21452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        this->appendNewRec(owner, x, y);
21552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        index = fTouches.count() - 1;
21652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
21752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
21852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    Rec& rec = fTouches[index];
21952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
22052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    // not sure how valuable this is
22152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (fTouches.count() == 2) {
22252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        if (close_enough_for_jitter(rec.fLastX, rec.fLastY, x, y)) {
22338406c82b913350e55fa04af8c1941cd9b4aff52tfarina//            SkDebugf("--- drop touchMove, withing jitter tolerance %g %g\n", rec.fLastX - x, rec.fLastY - y);
22452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            return;
22552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        }
22652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
22752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
22852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    rec.fPrevX = rec.fLastX; rec.fLastX = x;
22952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    rec.fPrevY = rec.fLastY; rec.fLastY = y;
23052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    rec.fPrevT = rec.fLastT; rec.fLastT = SkTime::GetMSecs();
23152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
23252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    switch (fTouches.count()) {
23352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        case 1: {
23452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            float dx = rec.fLastX - rec.fStartX;
23552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            float dy = rec.fLastY - rec.fStartY;
23652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            dx = (float)sk_float_round2int(dx);
23752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            dy = (float)sk_float_round2int(dy);
23852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fLocalM.setTranslate(dx, dy);
23952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        } break;
24052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        case 2: {
24152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            SkASSERT(kZoom_State == fState);
24252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            const Rec& rec0 = fTouches[0];
24352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            const Rec& rec1 = fTouches[1];
244d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
24552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            float scale = this->computePinch(rec0, rec1);
24652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            scale = this->limitTotalZoom(scale);
24752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
24852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fLocalM.setTranslate(-center(rec0.fStartX, rec1.fStartX),
24952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com                                 -center(rec0.fStartY, rec1.fStartY));
25052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fLocalM.postScale(scale, scale);
25152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fLocalM.postTranslate(center(rec0.fLastX, rec1.fLastX),
25252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com                                  center(rec0.fLastY, rec1.fLastY));
25352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        } break;
25452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        default:
25552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            break;
25652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
25752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
25852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
25952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comvoid SkTouchGesture::touchEnd(void* owner) {
26038406c82b913350e55fa04af8c1941cd9b4aff52tfarina//    SkDebugf("--- %d touchEnd   %p\n", fTouches.count(), owner);
26152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
26252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    int index = this->findRec(owner);
26352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (index < 0) {
26452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        SkDebugf("--- not found\n");
26552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        return;
26652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
26752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
26852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    const Rec& rec = fTouches[index];
26952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (this->handleDblTap(rec.fLastX, rec.fLastY)) {
27052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        return;
27152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
27252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
27352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    // count() reflects the number before we removed the owner
27452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    switch (fTouches.count()) {
27552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        case 1: {
27652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            this->flushLocalM();
27752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            float dx = rec.fLastX - rec.fPrevX;
27852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            float dy = rec.fLastY - rec.fPrevY;
27952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            float dur = (rec.fLastT - rec.fPrevT) * 0.001f;
28052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            if (dur > 0) {
28152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com                fFlinger.reset(dx / dur, dy / dur);
28252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            }
28352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fState = kEmpty_State;
28452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        } break;
28552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        case 2:
28652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            this->flushLocalM();
28752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            SkASSERT(kZoom_State == fState);
28852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fState = kEmpty_State;
28952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            break;
29052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        default:
29152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            SkASSERT(kZoom_State == fState);
29252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            break;
29352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
29452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
29552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fTouches.removeShuffle(index);
29652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
29752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
29852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.comfloat SkTouchGesture::computePinch(const Rec& rec0, const Rec& rec1) {
29952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    double dx = rec0.fStartX - rec1.fStartX;
30052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    double dy = rec0.fStartY - rec1.fStartY;
30152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    double dist0 = sqrt(dx*dx + dy*dy);
30252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
30352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    dx = rec0.fLastX - rec1.fLastX;
30452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    dy = rec0.fLastY - rec1.fLastY;
30552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    double dist1 = sqrt(dx*dx + dy*dy);
30652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
30752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    double scale = dist1 / dist0;
30852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    return (float)scale;
30952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
31052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
31152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.combool SkTouchGesture::handleDblTap(float x, float y) {
31252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    bool found = false;
31352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    SkMSec now = SkTime::GetMSecs();
31452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    if (now - fLastUpT <= MAX_DBL_TAP_INTERVAL) {
31552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        if (SkPoint::Length(fLastUpP.fX - x,
31652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com                            fLastUpP.fY - y) <= MAX_DBL_TAP_DISTANCE) {
31752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fFlinger.stop();
31852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fLocalM.reset();
31952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fGlobalM.reset();
32052f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fTouches.reset();
32152f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            fState = kEmpty_State;
32252f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com            found = true;
32352f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com        }
32452f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    }
32552f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com
32652f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fLastUpT = now;
32752f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    fLastUpP.set(x, y);
32852f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com    return found;
32952f57e1d11a00bdc5efb96709446546cdbc1ff9dreed@google.com}
330