TouchGesture.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /*
  2. * Copyright 2010 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include <algorithm>
  8. #include "include/core/SkMatrix.h"
  9. #include "include/core/SkTime.h"
  10. #include "tools/viewer/TouchGesture.h"
  11. #define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER true
  12. static const SkScalar MAX_FLING_SPEED = SkIntToScalar(1500);
  13. static SkScalar pin_max_fling(SkScalar speed) {
  14. if (speed > MAX_FLING_SPEED) {
  15. speed = MAX_FLING_SPEED;
  16. }
  17. return speed;
  18. }
  19. static double getseconds() {
  20. return SkTime::GetMSecs() * 0.001;
  21. }
  22. // returns +1 or -1, depending on the sign of x
  23. // returns +1 if z is zero
  24. static SkScalar SkScalarSignNonZero(SkScalar x) {
  25. SkScalar sign = SK_Scalar1;
  26. if (x < 0) {
  27. sign = -sign;
  28. }
  29. return sign;
  30. }
  31. static void unit_axis_align(SkVector* unit) {
  32. const SkScalar TOLERANCE = SkDoubleToScalar(0.15);
  33. if (SkScalarAbs(unit->fX) < TOLERANCE) {
  34. unit->fX = 0;
  35. unit->fY = SkScalarSignNonZero(unit->fY);
  36. } else if (SkScalarAbs(unit->fY) < TOLERANCE) {
  37. unit->fX = SkScalarSignNonZero(unit->fX);
  38. unit->fY = 0;
  39. }
  40. }
  41. void TouchGesture::FlingState::reset(float sx, float sy) {
  42. fActive = true;
  43. fDirection.set(sx, sy);
  44. fSpeed0 = SkPoint::Normalize(&fDirection);
  45. fSpeed0 = pin_max_fling(fSpeed0);
  46. fTime0 = getseconds();
  47. unit_axis_align(&fDirection);
  48. // printf("---- speed %g dir %g %g\n", fSpeed0, fDirection.fX, fDirection.fY);
  49. }
  50. bool TouchGesture::FlingState::evaluateMatrix(SkMatrix* matrix) {
  51. if (!fActive) {
  52. return false;
  53. }
  54. const float t = (float)(getseconds() - fTime0);
  55. const float MIN_SPEED = 2;
  56. const float K0 = 5;
  57. const float K1 = 0.02f;
  58. const float speed = fSpeed0 * (sk_float_exp(- K0 * t) - K1);
  59. if (speed <= MIN_SPEED) {
  60. fActive = false;
  61. return false;
  62. }
  63. float dist = (fSpeed0 - speed) / K0;
  64. // printf("---- time %g speed %g dist %g\n", t, speed, dist);
  65. float tx = fDirection.fX * dist;
  66. float ty = fDirection.fY * dist;
  67. if (DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER) {
  68. tx = (float)sk_float_round2int(tx);
  69. ty = (float)sk_float_round2int(ty);
  70. }
  71. matrix->setTranslate(tx, ty);
  72. // printf("---- evaluate (%g %g)\n", tx, ty);
  73. return true;
  74. }
  75. ///////////////////////////////////////////////////////////////////////////////
  76. static const SkMSec MAX_DBL_TAP_INTERVAL = 300;
  77. static const float MAX_DBL_TAP_DISTANCE = 100;
  78. static const float MAX_JITTER_RADIUS = 2;
  79. // if true, then ignore the touch-move, 'cause its probably just jitter
  80. static bool close_enough_for_jitter(float x0, float y0, float x1, float y1) {
  81. return sk_float_abs(x0 - x1) <= MAX_JITTER_RADIUS &&
  82. sk_float_abs(y0 - y1) <= MAX_JITTER_RADIUS;
  83. }
  84. ///////////////////////////////////////////////////////////////////////////////
  85. TouchGesture::TouchGesture() {
  86. this->reset();
  87. }
  88. TouchGesture::~TouchGesture() {
  89. }
  90. void TouchGesture::resetTouchState() {
  91. fIsTransLimited = false;
  92. fTouches.reset();
  93. fState = kEmpty_State;
  94. fLocalM.reset();
  95. fLastUpMillis = SkTime::GetMSecs() - 2*MAX_DBL_TAP_INTERVAL;
  96. fLastUpP.set(0, 0);
  97. }
  98. void TouchGesture::reset() {
  99. fGlobalM.reset();
  100. this->resetTouchState();
  101. }
  102. void TouchGesture::flushLocalM() {
  103. fGlobalM.postConcat(fLocalM);
  104. fLocalM.reset();
  105. }
  106. const SkMatrix& TouchGesture::localM() {
  107. if (fFlinger.isActive()) {
  108. if (!fFlinger.evaluateMatrix(&fLocalM)) {
  109. this->flushLocalM();
  110. }
  111. }
  112. return fLocalM;
  113. }
  114. void TouchGesture::appendNewRec(void* owner, float x, float y) {
  115. Rec* rec = fTouches.append();
  116. rec->fOwner = owner;
  117. rec->fStartX = rec->fPrevX = rec->fLastX = x;
  118. rec->fStartY = rec->fPrevY = rec->fLastY = y;
  119. rec->fLastT = rec->fPrevT = static_cast<float>(SkTime::GetSecs());
  120. }
  121. void TouchGesture::touchBegin(void* owner, float x, float y) {
  122. // SkDebugf("--- %d touchBegin %p %g %g\n", fTouches.count(), owner, x, y);
  123. int index = this->findRec(owner);
  124. if (index >= 0) {
  125. this->flushLocalM();
  126. fTouches.removeShuffle(index);
  127. SkDebugf("---- already exists, removing\n");
  128. }
  129. if (fTouches.count() == 2) {
  130. return;
  131. }
  132. this->flushLocalM();
  133. fFlinger.stop();
  134. this->appendNewRec(owner, x, y);
  135. switch (fTouches.count()) {
  136. case 1:
  137. fState = kTranslate_State;
  138. break;
  139. case 2:
  140. fState = kZoom_State;
  141. break;
  142. default:
  143. break;
  144. }
  145. }
  146. int TouchGesture::findRec(void* owner) const {
  147. for (int i = 0; i < fTouches.count(); i++) {
  148. if (owner == fTouches[i].fOwner) {
  149. return i;
  150. }
  151. }
  152. return -1;
  153. }
  154. static SkScalar center(float pos0, float pos1) {
  155. return (pos0 + pos1) * 0.5f;
  156. }
  157. static const float MAX_ZOOM_SCALE = 4;
  158. static const float MIN_ZOOM_SCALE = 0.25f;
  159. float TouchGesture::limitTotalZoom(float scale) const {
  160. // this query works 'cause we know that we're square-scale w/ no skew/rotation
  161. const float curr = SkScalarToFloat(fGlobalM[0]);
  162. if (scale > 1 && curr * scale > MAX_ZOOM_SCALE) {
  163. scale = MAX_ZOOM_SCALE / curr;
  164. } else if (scale < 1 && curr * scale < MIN_ZOOM_SCALE) {
  165. scale = MIN_ZOOM_SCALE / curr;
  166. }
  167. return scale;
  168. }
  169. void TouchGesture::touchMoved(void* owner, float x, float y) {
  170. // SkDebugf("--- %d touchMoved %p %g %g\n", fTouches.count(), owner, x, y);
  171. if (kEmpty_State == fState) {
  172. return;
  173. }
  174. int index = this->findRec(owner);
  175. if (index < 0) {
  176. SkDebugf("---- ignoring move without begin\n");
  177. return;
  178. }
  179. Rec& rec = fTouches[index];
  180. // not sure how valuable this is
  181. if (fTouches.count() == 2) {
  182. if (close_enough_for_jitter(rec.fLastX, rec.fLastY, x, y)) {
  183. // SkDebugf("--- drop touchMove, within jitter tolerance %g %g\n", rec.fLastX - x, rec.fLastY - y);
  184. return;
  185. }
  186. }
  187. rec.fPrevX = rec.fLastX; rec.fLastX = x;
  188. rec.fPrevY = rec.fLastY; rec.fLastY = y;
  189. rec.fPrevT = rec.fLastT;
  190. rec.fLastT = static_cast<float>(SkTime::GetSecs());
  191. switch (fTouches.count()) {
  192. case 1: {
  193. float dx = rec.fLastX - rec.fStartX;
  194. float dy = rec.fLastY - rec.fStartY;
  195. dx = (float)sk_float_round2int(dx);
  196. dy = (float)sk_float_round2int(dy);
  197. fLocalM.setTranslate(dx, dy);
  198. } break;
  199. case 2: {
  200. SkASSERT(kZoom_State == fState);
  201. const Rec& rec0 = fTouches[0];
  202. const Rec& rec1 = fTouches[1];
  203. float scale = this->computePinch(rec0, rec1);
  204. scale = this->limitTotalZoom(scale);
  205. fLocalM.setTranslate(-center(rec0.fStartX, rec1.fStartX),
  206. -center(rec0.fStartY, rec1.fStartY));
  207. fLocalM.postScale(scale, scale);
  208. fLocalM.postTranslate(center(rec0.fLastX, rec1.fLastX),
  209. center(rec0.fLastY, rec1.fLastY));
  210. } break;
  211. default:
  212. break;
  213. }
  214. }
  215. void TouchGesture::touchEnd(void* owner) {
  216. // SkDebugf("--- %d touchEnd %p\n", fTouches.count(), owner);
  217. int index = this->findRec(owner);
  218. if (index < 0) {
  219. SkDebugf("--- not found\n");
  220. return;
  221. }
  222. const Rec& rec = fTouches[index];
  223. if (this->handleDblTap(rec.fLastX, rec.fLastY)) {
  224. return;
  225. }
  226. // count() reflects the number before we removed the owner
  227. switch (fTouches.count()) {
  228. case 1: {
  229. this->flushLocalM();
  230. float dx = rec.fLastX - rec.fPrevX;
  231. float dy = rec.fLastY - rec.fPrevY;
  232. float dur = rec.fLastT - rec.fPrevT;
  233. if (dur > 0) {
  234. fFlinger.reset(dx / dur, dy / dur);
  235. }
  236. fState = kEmpty_State;
  237. } break;
  238. case 2:
  239. this->flushLocalM();
  240. SkASSERT(kZoom_State == fState);
  241. fState = kEmpty_State;
  242. break;
  243. default:
  244. SkASSERT(kZoom_State == fState);
  245. break;
  246. }
  247. fTouches.removeShuffle(index);
  248. limitTrans();
  249. }
  250. bool TouchGesture::isFling(SkPoint* dir) {
  251. if (fFlinger.isActive()) {
  252. SkScalar speed;
  253. fFlinger.get(dir, &speed);
  254. if (speed > 1000) {
  255. return true;
  256. }
  257. }
  258. return false;
  259. }
  260. float TouchGesture::computePinch(const Rec& rec0, const Rec& rec1) {
  261. double dx = rec0.fStartX - rec1.fStartX;
  262. double dy = rec0.fStartY - rec1.fStartY;
  263. double dist0 = sqrt(dx*dx + dy*dy);
  264. dx = rec0.fLastX - rec1.fLastX;
  265. dy = rec0.fLastY - rec1.fLastY;
  266. double dist1 = sqrt(dx*dx + dy*dy);
  267. double scale = dist1 / dist0;
  268. return (float)scale;
  269. }
  270. bool TouchGesture::handleDblTap(float x, float y) {
  271. bool found = false;
  272. double now = SkTime::GetMSecs();
  273. if (now - fLastUpMillis <= MAX_DBL_TAP_INTERVAL) {
  274. if (SkPoint::Length(fLastUpP.fX - x,
  275. fLastUpP.fY - y) <= MAX_DBL_TAP_DISTANCE) {
  276. fFlinger.stop();
  277. fLocalM.reset();
  278. fGlobalM.reset();
  279. fTouches.reset();
  280. fState = kEmpty_State;
  281. found = true;
  282. }
  283. }
  284. fLastUpMillis = now;
  285. fLastUpP.set(x, y);
  286. return found;
  287. }
  288. void TouchGesture::setTransLimit(const SkRect& contentRect, const SkRect& windowRect,
  289. const SkMatrix& preTouchMatrix) {
  290. fIsTransLimited = true;
  291. fContentRect = contentRect;
  292. fWindowRect = windowRect;
  293. fPreTouchM = preTouchMatrix;
  294. }
  295. void TouchGesture::limitTrans() {
  296. if (!fIsTransLimited) {
  297. return;
  298. }
  299. SkRect scaledContent = fContentRect;
  300. fPreTouchM.mapRect(&scaledContent);
  301. fGlobalM.mapRect(&scaledContent);
  302. const SkScalar ZERO = 0;
  303. fGlobalM.postTranslate(ZERO, std::min(ZERO, fWindowRect.fBottom - scaledContent.fTop));
  304. fGlobalM.postTranslate(ZERO, std::max(ZERO, fWindowRect.fTop - scaledContent.fBottom));
  305. fGlobalM.postTranslate(std::min(ZERO, fWindowRect.fRight - scaledContent.fLeft), ZERO);
  306. fGlobalM.postTranslate(std::max(ZERO, fWindowRect.fLeft - scaledContent.fRight), ZERO);
  307. }