2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "pinch-gesture-detector.h"
24 #include <dali/public-api/events/touch-point.h>
25 #include <dali/public-api/math/vector2.h>
27 #include <dali/integration-api/events/pinch-gesture-event.h>
28 #include <dali/integration-api/events/touch-event-integ.h>
31 #include <base/core-event-interface.h>
44 const unsigned int MINIMUM_TOUCH_EVENTS_REQUIRED = 4;
45 const unsigned int MINIMUM_TOUCH_EVENTS_REQUIRED_AFTER_START = 4;
47 inline float GetDistance(const TouchPoint& point1, const TouchPoint& point2)
49 Vector2 vector(point1.screen - point2.screen);
50 return vector.Length();
53 inline float GetGradient(const TouchPoint& point1, const TouchPoint& point2)
55 return (point2.screen.y - point1.screen.y)
57 (point2.screen.x - point1.screen.x);
60 inline Vector2 GetCenterPoint(const TouchPoint& point1, const TouchPoint& point2)
62 return Vector2(point1.screen + point2.screen) * 0.5f;
65 } // unnamed namespace
67 PinchGestureDetector::PinchGestureDetector(CoreEventInterface& coreEventInterface, Vector2 screenSize, float minimumPinchDistance)
68 : GestureDetector(screenSize, Gesture::Pinch),
69 mCoreEventInterface(coreEventInterface),
72 mMinimumDistanceDelta(minimumPinchDistance),
73 mStartingDistance(0.0f)
77 PinchGestureDetector::~PinchGestureDetector()
81 void PinchGestureDetector::SetMinimumPinchDistance(float distance)
83 mMinimumDistanceDelta = distance;
86 void PinchGestureDetector::SendEvent(const Integration::TouchEvent& event)
88 int pointCount = event.GetPointCount();
96 // Change state to possible as we have two touch points.
98 mTouchEvents.push_back(event);
107 // We no longer have two touch points so change state back to Clear.
109 mTouchEvents.clear();
113 const TouchPoint& currentPoint1 = event.points[0];
114 const TouchPoint& currentPoint2 = event.points[1];
116 if (currentPoint1.state == TouchPoint::Up || currentPoint2.state == TouchPoint::Up)
118 // One of our touch points has an Up event so change our state back to Clear.
120 mTouchEvents.clear();
124 mTouchEvents.push_back(event);
126 // We can only determine a pinch after a certain number of touch points have been collected.
127 if (mTouchEvents.size() >= MINIMUM_TOUCH_EVENTS_REQUIRED)
129 const TouchPoint& firstPoint1 = mTouchEvents[0].points[0];
130 const TouchPoint& firstPoint2 = mTouchEvents[0].points[1];
132 float firstDistance = GetDistance(firstPoint1, firstPoint2);
133 float currentDistance = GetDistance(currentPoint1, currentPoint2);
134 float distanceChanged = firstDistance - currentDistance;
136 // Check if distance has changed enough
137 if (fabsf(distanceChanged) > mMinimumDistanceDelta)
139 // Remove the first few events from the vector otherwise values are exaggerated
140 mTouchEvents.erase(mTouchEvents.begin(), mTouchEvents.end() - MINIMUM_TOUCH_EVENTS_REQUIRED_AFTER_START);
142 if ( !mTouchEvents.empty() )
144 mStartingDistance = GetDistance(mTouchEvents.begin()->points[0], mTouchEvents.begin()->points[1]);
146 // Send pinch started
147 SendPinch(Gesture::Started, event);
152 mTouchEvents.clear();
155 if (mState == Possible)
157 // No pinch, so restart detection
159 mTouchEvents.clear();
171 // Send pinch finished event
172 SendPinch(Gesture::Finished, event);
175 mTouchEvents.clear();
179 const TouchPoint& currentPoint1 = event.points[0];
180 const TouchPoint& currentPoint2 = event.points[1];
182 if (currentPoint1.state == TouchPoint::Up || currentPoint2.state == TouchPoint::Up)
184 mTouchEvents.push_back(event);
185 // Send pinch finished event
186 SendPinch(Gesture::Finished, event);
189 mTouchEvents.clear();
193 mTouchEvents.push_back(event);
195 if (mTouchEvents.size() >= MINIMUM_TOUCH_EVENTS_REQUIRED_AFTER_START)
197 // Send pinch continuing
198 SendPinch(Gesture::Continuing, event);
200 mTouchEvents.clear();
209 void PinchGestureDetector::Update(const Integration::GestureRequest& request)
214 void PinchGestureDetector::SendPinch(Gesture::State state, const Integration::TouchEvent& currentEvent)
216 Integration::PinchGestureEvent gesture(state);
218 if ( !mTouchEvents.empty() )
220 const Integration::TouchEvent& firstEvent = mTouchEvents[0];
222 // Assert if we have been holding TouchEvents that do not have 2 points
223 DALI_ASSERT_DEBUG( firstEvent.GetPointCount() == 2 );
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 )
230 event = *mTouchEvents.rbegin();
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] );
238 float firstDistance = GetDistance(firstPoint1, firstPoint2);
239 float currentDistance = GetDistance(currentPoint1, currentPoint2);
240 gesture.scale = currentDistance / mStartingDistance;
242 float distanceDelta = fabsf(firstDistance - currentDistance);
243 unsigned long timeDelta = currentEvent.time - firstEvent.time;
244 gesture.speed = (distanceDelta / timeDelta) * 1000.0f;
246 gesture.centerPoint = GetCenterPoint(currentPoint1, currentPoint2);
250 // Something has gone wrong, just cancel the gesture.
251 gesture.state = Gesture::Cancelled;
254 gesture.time = currentEvent.time;
256 mCoreEventInterface.QueueCoreEvent(gesture);
259 } // namespace Adaptor
261 } // namespace Internal