Dali 3D User Interface Engine
pinch-gesture-detector.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 // CLASS HEADER
19 #include "pinch-gesture-detector.h"
20 
21 // EXTERNAL INCLUDES
22 #include <cmath>
23 
26 
29 
30 // INTERNAL INCLUDES
32 
33 namespace Dali
34 {
35 
36 namespace Internal
37 {
38 
39 namespace Adaptor
40 {
41 
42 namespace
43 {
44 const unsigned int MINIMUM_TOUCH_EVENTS_REQUIRED = 4;
46 
47 inline float GetDistance(const TouchPoint& point1, const TouchPoint& point2)
48 {
49  Vector2 vector(point1.screen - point2.screen);
50  return vector.Length();
51 }
52 
53 inline float GetGradient(const TouchPoint& point1, const TouchPoint& point2)
54 {
55  return (point2.screen.y - point1.screen.y)
56  /
57  (point2.screen.x - point1.screen.x);
58 }
59 
60 inline Vector2 GetCenterPoint(const TouchPoint& point1, const TouchPoint& point2)
61 {
62  return Vector2(point1.screen + point2.screen) * 0.5f;
63 }
64 
65 } // unnamed namespace
66 
67 PinchGestureDetector::PinchGestureDetector(CoreEventInterface& coreEventInterface, Vector2 screenSize, float minimumPinchDistance)
68 : GestureDetector(screenSize, Gesture::Pinch),
69  mCoreEventInterface(coreEventInterface),
70  mState(Clear),
71  mTouchEvents(),
72  mMinimumDistanceDelta(minimumPinchDistance),
73  mStartingDistance(0.0f)
74 {
75 }
76 
78 {
79 }
80 
82 {
83  mMinimumDistanceDelta = distance;
84 }
85 
86 void PinchGestureDetector::SendEvent(const Integration::TouchEvent& event)
87 {
88  int pointCount = event.GetPointCount();
89 
90  switch (mState)
91  {
92  case Clear:
93  {
94  if (pointCount == 2)
95  {
96  // Change state to possible as we have two touch points.
97  mState = Possible;
98  mTouchEvents.push_back(event);
99  }
100  break;
101  }
102 
103  case Possible:
104  {
105  if (pointCount != 2)
106  {
107  // We no longer have two touch points so change state back to Clear.
108  mState = Clear;
109  mTouchEvents.clear();
110  }
111  else
112  {
113  const TouchPoint& currentPoint1 = event.points[0];
114  const TouchPoint& currentPoint2 = event.points[1];
115 
116  if (currentPoint1.state == TouchPoint::Up || currentPoint2.state == TouchPoint::Up)
117  {
118  // One of our touch points has an Up event so change our state back to Clear.
119  mState = Clear;
120  mTouchEvents.clear();
121  }
122  else
123  {
124  mTouchEvents.push_back(event);
125 
126  // We can only determine a pinch after a certain number of touch points have been collected.
128  {
129  const TouchPoint& firstPoint1 = mTouchEvents[0].points[0];
130  const TouchPoint& firstPoint2 = mTouchEvents[0].points[1];
131 
132  float firstDistance = GetDistance(firstPoint1, firstPoint2);
133  float currentDistance = GetDistance(currentPoint1, currentPoint2);
134  float distanceChanged = firstDistance - currentDistance;
135 
136  // Check if distance has changed enough
137  if (fabsf(distanceChanged) > mMinimumDistanceDelta)
138  {
139  // Remove the first few events from the vector otherwise values are exaggerated
141 
142  if ( !mTouchEvents.empty() )
143  {
144  mStartingDistance = GetDistance(mTouchEvents.begin()->points[0], mTouchEvents.begin()->points[1]);
145 
146  // Send pinch started
147  SendPinch(Gesture::Started, event);
148 
149  mState = Started;
150  }
151 
152  mTouchEvents.clear();
153  }
154 
155  if (mState == Possible)
156  {
157  // No pinch, so restart detection
158  mState = Clear;
159  mTouchEvents.clear();
160  }
161  }
162  }
163  }
164  break;
165  }
166 
167  case Started:
168  {
169  if (pointCount != 2)
170  {
171  // Send pinch finished event
173 
174  mState = Clear;
175  mTouchEvents.clear();
176  }
177  else
178  {
179  const TouchPoint& currentPoint1 = event.points[0];
180  const TouchPoint& currentPoint2 = event.points[1];
181 
182  if (currentPoint1.state == TouchPoint::Up || currentPoint2.state == TouchPoint::Up)
183  {
184  mTouchEvents.push_back(event);
185  // Send pinch finished event
187 
188  mState = Clear;
189  mTouchEvents.clear();
190  }
191  else
192  {
193  mTouchEvents.push_back(event);
194 
196  {
197  // Send pinch continuing
199 
200  mTouchEvents.clear();
201  }
202  }
203  }
204  break;
205  }
206  }
207 }
208 
209 void PinchGestureDetector::Update(const Integration::GestureRequest& request)
210 {
211  // Nothing to do.
212 }
213 
214 void PinchGestureDetector::SendPinch(Gesture::State state, const Integration::TouchEvent& currentEvent)
215 {
216  Integration::PinchGestureEvent gesture(state);
217 
218  if ( !mTouchEvents.empty() )
219  {
220  const Integration::TouchEvent& firstEvent = mTouchEvents[0];
221 
222  // Assert if we have been holding TouchEvents that do not have 2 points
223  DALI_ASSERT_DEBUG( firstEvent.GetPointCount() == 2 );
224 
225  // We should use the current event in our calculations unless it does not have two points.
226  // If it does not have two points, then we should use the last point in mTouchEvents.
227  Integration::TouchEvent event( currentEvent );
228  if ( event.GetPointCount() != 2 )
229  {
230  event = *mTouchEvents.rbegin();
231  }
232 
233  const TouchPoint& firstPoint1( firstEvent.points[0] );
234  const TouchPoint& firstPoint2( firstEvent.points[1] );
235  const TouchPoint& currentPoint1( event.points[0] );
236  const TouchPoint& currentPoint2( event.points[1] );
237 
238  float firstDistance = GetDistance(firstPoint1, firstPoint2);
239  float currentDistance = GetDistance(currentPoint1, currentPoint2);
240  gesture.scale = currentDistance / mStartingDistance;
241 
242  float distanceDelta = fabsf(firstDistance - currentDistance);
243  unsigned long timeDelta = currentEvent.time - firstEvent.time;
244  gesture.speed = (distanceDelta / timeDelta) * 1000.0f;
245 
246  gesture.centerPoint = GetCenterPoint(currentPoint1, currentPoint2);
247  }
248  else
249  {
250  // Something has gone wrong, just cancel the gesture.
251  gesture.state = Gesture::Cancelled;
252  }
253 
254  gesture.time = currentEvent.time;
255 
257 }
258 
259 } // namespace Adaptor
260 
261 } // namespace Internal
262 
263 } // namespace Dali
Dali Docs Home
Read more about Dali