2 * Copyright (c) 2019 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 <dali/internal/event/events/pinch-gesture-recognizer.h>
24 #include <dali/public-api/events/touch-point.h>
25 #include <dali/public-api/math/vector2.h>
27 #include <dali/internal/event/events/pinch-gesture-event.h>
28 #include <dali/integration-api/events/touch-event-integ.h>
29 #include <dali/internal/event/common/scene-impl.h>
42 const unsigned int MINIMUM_TOUCH_EVENTS_REQUIRED = 4;
43 const unsigned int MINIMUM_TOUCH_EVENTS_REQUIRED_AFTER_START = 4;
45 const float MINIMUM_DISTANCE_DELTA_DIVISOR = 85.0f;
47 inline float GetDistance(const Integration::Point& point1, const Integration::Point& point2)
49 Vector2 vector(point1.GetScreenPosition() - point2.GetScreenPosition());
50 return vector.Length();
53 inline Vector2 GetCenterPoint(const Integration::Point& point1, const Integration::Point& point2)
55 return Vector2(point1.GetScreenPosition() + point2.GetScreenPosition()) * 0.5f;
58 } // unnamed namespace
60 PinchGestureRecognizer::PinchGestureRecognizer( Observer& observer, Vector2 screenSize, float minimumPinchDistance )
61 : GestureRecognizer( screenSize, Gesture::Pinch ),
62 mObserver( observer ),
65 mStartingDistance( 0.0f )
67 SetMinimumPinchDistance( minimumPinchDistance );
70 PinchGestureRecognizer::~PinchGestureRecognizer()
74 void PinchGestureRecognizer::SetMinimumPinchDistance(float value)
76 mMinimumDistanceDelta = value >= 0.0f ? value : (mScreenSize.height / MINIMUM_DISTANCE_DELTA_DIVISOR);
79 void PinchGestureRecognizer::SendEvent(const Integration::TouchEvent& event)
81 int pointCount = event.GetPointCount();
89 // Change state to possible as we have two touch points.
91 mTouchEvents.push_back(event);
100 // We no longer have two touch points so change state back to Clear.
102 mTouchEvents.clear();
106 const Integration::Point& currentPoint1 = event.points[0];
107 const Integration::Point& currentPoint2 = event.points[1];
109 if (currentPoint1.GetState() == PointState::UP || currentPoint2.GetState() == PointState::UP)
111 // One of our touch points has an Up event so change our state back to Clear.
113 mTouchEvents.clear();
117 mTouchEvents.push_back(event);
119 // We can only determine a pinch after a certain number of touch points have been collected.
120 if (mTouchEvents.size() >= MINIMUM_TOUCH_EVENTS_REQUIRED)
122 const Integration::Point& firstPoint1 = mTouchEvents[0].points[0];
123 const Integration::Point& firstPoint2 = mTouchEvents[0].points[1];
125 float firstDistance = GetDistance(firstPoint1, firstPoint2);
126 float currentDistance = GetDistance(currentPoint1, currentPoint2);
127 float distanceChanged = firstDistance - currentDistance;
129 // Check if distance has changed enough
130 if (fabsf(distanceChanged) > mMinimumDistanceDelta)
132 // Remove the first few events from the vector otherwise values are exaggerated
133 mTouchEvents.erase(mTouchEvents.begin(), mTouchEvents.end() - MINIMUM_TOUCH_EVENTS_REQUIRED_AFTER_START);
135 if ( !mTouchEvents.empty() )
137 mStartingDistance = GetDistance(mTouchEvents.begin()->points[0], mTouchEvents.begin()->points[1]);
139 // Send pinch started
140 SendPinch(Gesture::Started, event);
145 mTouchEvents.clear();
148 if (mState == Possible)
150 // No pinch, so restart detection
152 mTouchEvents.clear();
164 // Send pinch finished event
165 SendPinch(Gesture::Finished, event);
168 mTouchEvents.clear();
172 const Integration::Point& currentPoint1 = event.points[0];
173 const Integration::Point& currentPoint2 = event.points[1];
175 if (currentPoint1.GetState() == PointState::UP || currentPoint2.GetState() == PointState::UP)
177 mTouchEvents.push_back(event);
178 // Send pinch finished event
179 SendPinch(Gesture::Finished, event);
182 mTouchEvents.clear();
186 mTouchEvents.push_back(event);
188 if (mTouchEvents.size() >= MINIMUM_TOUCH_EVENTS_REQUIRED_AFTER_START)
190 // Send pinch continuing
191 SendPinch(Gesture::Continuing, event);
193 mTouchEvents.clear();
202 void PinchGestureRecognizer::Update(const GestureRequest& request)
207 void PinchGestureRecognizer::SendPinch(Gesture::State state, const Integration::TouchEvent& currentEvent)
209 PinchGestureEvent gesture(state);
211 if ( !mTouchEvents.empty() )
213 const Integration::TouchEvent& firstEvent = mTouchEvents[0];
215 // Assert if we have been holding TouchEvents that do not have 2 points
216 DALI_ASSERT_DEBUG( firstEvent.GetPointCount() == 2 );
218 // We should use the current event in our calculations unless it does not have two points.
219 // If it does not have two points, then we should use the last point in mTouchEvents.
220 Integration::TouchEvent event( currentEvent );
221 if ( event.GetPointCount() != 2 )
223 event = *mTouchEvents.rbegin();
226 const Integration::Point& firstPoint1( firstEvent.points[0] );
227 const Integration::Point& firstPoint2( firstEvent.points[1] );
228 const Integration::Point& currentPoint1( event.points[0] );
229 const Integration::Point& currentPoint2( event.points[1] );
231 float firstDistance = GetDistance(firstPoint1, firstPoint2);
232 float currentDistance = GetDistance(currentPoint1, currentPoint2);
233 gesture.scale = currentDistance / mStartingDistance;
235 float distanceDelta = fabsf(firstDistance - currentDistance);
236 float timeDelta = static_cast<float> ( currentEvent.time - firstEvent.time );
237 gesture.speed = ( distanceDelta / timeDelta ) * 1000.0f;
239 gesture.centerPoint = GetCenterPoint(currentPoint1, currentPoint2);
243 // Something has gone wrong, just cancel the gesture.
244 gesture.state = Gesture::Cancelled;
247 gesture.time = currentEvent.time;
251 // Create another handle so the recognizer cannot be destroyed during process function
252 GestureRecognizerPtr recognizerHandle = this;
254 mObserver.Process(*mScene, gesture);
258 } // namespace Internal