Fix conversion warnings for event refactor
[platform/core/uifw/dali-adaptor.git] / dali / internal / input / common / pinch-gesture-detector.cpp
1 /*
2  * Copyright (c) 2019 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 <dali/internal/input/common/pinch-gesture-detector.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23
24 #include <dali/public-api/events/touch-point.h>
25 #include <dali/public-api/math/vector2.h>
26
27 #include <dali/integration-api/events/pinch-gesture-event.h>
28 #include <dali/integration-api/events/touch-event-integ.h>
29
30 // INTERNAL INCLUDES
31 #include <dali/internal/system/common/core-event-interface.h>
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;
45 const unsigned int MINIMUM_TOUCH_EVENTS_REQUIRED_AFTER_START = 4;
46
47 inline float GetDistance(const Integration::Point& point1, const Integration::Point& point2)
48 {
49   Vector2 vector(point1.GetScreenPosition() - point2.GetScreenPosition());
50   return vector.Length();
51 }
52
53 inline Vector2 GetCenterPoint(const Integration::Point& point1, const Integration::Point& point2)
54 {
55   return Vector2(point1.GetScreenPosition() + point2.GetScreenPosition()) * 0.5f;
56 }
57
58 } // unnamed namespace
59
60 PinchGestureDetector::PinchGestureDetector(CoreEventInterface& coreEventInterface, Vector2 screenSize, float minimumPinchDistance)
61 : GestureDetector(screenSize, Gesture::Pinch),
62   mCoreEventInterface(coreEventInterface),
63   mState(Clear),
64   mTouchEvents(),
65   mMinimumDistanceDelta(minimumPinchDistance),
66   mStartingDistance(0.0f)
67 {
68 }
69
70 PinchGestureDetector::~PinchGestureDetector()
71 {
72 }
73
74 void PinchGestureDetector::SetMinimumPinchDistance(float distance)
75 {
76   mMinimumDistanceDelta = distance;
77 }
78
79 void PinchGestureDetector::SendEvent(const Integration::TouchEvent& event)
80 {
81   int pointCount = event.GetPointCount();
82
83   switch (mState)
84   {
85     case Clear:
86     {
87       if (pointCount == 2)
88       {
89         // Change state to possible as we have two touch points.
90         mState = Possible;
91         mTouchEvents.push_back(event);
92       }
93       break;
94     }
95
96     case Possible:
97     {
98       if (pointCount != 2)
99       {
100         // We no longer have two touch points so change state back to Clear.
101         mState = Clear;
102         mTouchEvents.clear();
103       }
104       else
105       {
106         const Integration::Point& currentPoint1 = event.points[0];
107         const Integration::Point& currentPoint2 = event.points[1];
108
109         if (currentPoint1.GetState() == PointState::UP || currentPoint2.GetState() == PointState::UP)
110         {
111           // One of our touch points has an Up event so change our state back to Clear.
112           mState = Clear;
113           mTouchEvents.clear();
114         }
115         else
116         {
117           mTouchEvents.push_back(event);
118
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)
121           {
122             const Integration::Point& firstPoint1 = mTouchEvents[0].points[0];
123             const Integration::Point& firstPoint2 = mTouchEvents[0].points[1];
124
125             float firstDistance = GetDistance(firstPoint1, firstPoint2);
126             float currentDistance = GetDistance(currentPoint1, currentPoint2);
127             float distanceChanged = firstDistance - currentDistance;
128
129             // Check if distance has changed enough
130             if (fabsf(distanceChanged) > mMinimumDistanceDelta)
131             {
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);
134
135               if ( !mTouchEvents.empty() )
136               {
137                 mStartingDistance = GetDistance(mTouchEvents.begin()->points[0], mTouchEvents.begin()->points[1]);
138
139                 // Send pinch started
140                 SendPinch(Gesture::Started, event);
141
142                 mState = Started;
143               }
144
145               mTouchEvents.clear();
146             }
147
148             if (mState == Possible)
149             {
150               // No pinch, so restart detection
151               mState = Clear;
152               mTouchEvents.clear();
153             }
154           }
155         }
156       }
157       break;
158     }
159
160     case Started:
161     {
162       if (pointCount != 2)
163       {
164         // Send pinch finished event
165         SendPinch(Gesture::Finished, event);
166
167         mState = Clear;
168         mTouchEvents.clear();
169       }
170       else
171       {
172         const Integration::Point& currentPoint1 = event.points[0];
173         const Integration::Point& currentPoint2 = event.points[1];
174
175         if (currentPoint1.GetState() == PointState::UP || currentPoint2.GetState() == PointState::UP)
176         {
177           mTouchEvents.push_back(event);
178           // Send pinch finished event
179           SendPinch(Gesture::Finished, event);
180
181           mState = Clear;
182           mTouchEvents.clear();
183         }
184         else
185         {
186           mTouchEvents.push_back(event);
187
188           if (mTouchEvents.size() >= MINIMUM_TOUCH_EVENTS_REQUIRED_AFTER_START)
189           {
190             // Send pinch continuing
191             SendPinch(Gesture::Continuing, event);
192
193             mTouchEvents.clear();
194           }
195         }
196       }
197       break;
198     }
199   }
200 }
201
202 void PinchGestureDetector::Update(const Integration::GestureRequest& request)
203 {
204   // Nothing to do.
205 }
206
207 void PinchGestureDetector::SendPinch(Gesture::State state, const Integration::TouchEvent& currentEvent)
208 {
209   Integration::PinchGestureEvent gesture(state);
210
211   if ( !mTouchEvents.empty() )
212   {
213     const Integration::TouchEvent& firstEvent = mTouchEvents[0];
214
215     // Assert if we have been holding TouchEvents that do not have 2 points
216     DALI_ASSERT_DEBUG( firstEvent.GetPointCount() == 2 );
217
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 )
222     {
223       event = *mTouchEvents.rbegin();
224     }
225
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] );
230
231     float firstDistance = GetDistance(firstPoint1, firstPoint2);
232     float currentDistance = GetDistance(currentPoint1, currentPoint2);
233     gesture.scale = currentDistance / mStartingDistance;
234
235     float distanceDelta = fabsf(firstDistance - currentDistance);
236     float timeDelta = static_cast<float> ( currentEvent.time - firstEvent.time );
237     gesture.speed = ( distanceDelta / timeDelta ) * 1000.0f;
238
239     gesture.centerPoint = GetCenterPoint(currentPoint1, currentPoint2);
240   }
241   else
242   {
243     // Something has gone wrong, just cancel the gesture.
244     gesture.state = Gesture::Cancelled;
245   }
246
247   gesture.time = currentEvent.time;
248
249   if( mScene )
250   {
251     mScene->QueueEvent(gesture);
252   }
253 }
254
255 } // namespace Adaptor
256
257 } // namespace Internal
258
259 } // namespace Dali