Add input source type and source data about Gesture.
[platform/core/uifw/dali-core.git] / dali / internal / event / events / rotation-gesture / rotation-gesture-recognizer.cpp
1 /*
2  * Copyright (c) 2022 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/event/events/rotation-gesture/rotation-gesture-recognizer.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23
24 // INTERNAL INCLUDES
25 #include <dali/devel-api/events/touch-point.h>
26 #include <dali/integration-api/events/touch-event-integ.h>
27 #include <dali/internal/event/common/scene-impl.h>
28 #include <dali/internal/event/events/rotation-gesture/rotation-gesture-event.h>
29 #include <dali/public-api/math/vector2.h>
30
31 // INTERNAL INCLUDES
32
33 namespace Dali
34 {
35 namespace Internal
36 {
37 namespace
38 {
39 inline float GetAngle(const Integration::Point& point1, const Integration::Point& point2)
40 {
41   const Vector2& point1ScreenPosition = point1.GetScreenPosition();
42   const Vector2& point2ScreenPosition = point2.GetScreenPosition();
43   return atan2(point2ScreenPosition.y - point1ScreenPosition.y, point2ScreenPosition.x - point1ScreenPosition.x);
44 }
45
46 inline Vector2 GetCenterPoint(const Integration::Point& point1, const Integration::Point& point2)
47 {
48   return Vector2(point1.GetScreenPosition() + point2.GetScreenPosition()) * 0.5f;
49 }
50
51 } // unnamed namespace
52
53 RotationGestureRecognizer::RotationGestureRecognizer(Observer& observer, uint32_t minimumTouchEvents, uint32_t minimumTouchEventsAfterStart)
54 : GestureRecognizer(GestureType::ROTATION),
55   mObserver(observer),
56   mState(CLEAR),
57   mTouchEvents(),
58   mStartingAngle(0.0f),
59   mMinimumTouchEvents(minimumTouchEvents),
60   mMinimumTouchEventsAfterStart(minimumTouchEventsAfterStart)
61 {
62 }
63
64 void RotationGestureRecognizer::SendEvent(const Integration::TouchEvent& event)
65 {
66   int                  pointCount = event.GetPointCount();
67   GestureRecognizerPtr ptr(this); // To keep us from being destroyed during the life-time of this method
68
69   switch(mState)
70   {
71     case CLEAR:
72     {
73       if(pointCount == 2)
74       {
75         // Change state to possible as we have two touch points.
76         mState = POSSIBLE;
77         mTouchEvents.push_back(event);
78       }
79       break;
80     }
81
82     case POSSIBLE:
83     {
84       if(pointCount != 2)
85       {
86         // We no longer have two touch points so change state back to CLEAR.
87         mState = CLEAR;
88         mTouchEvents.clear();
89       }
90       else
91       {
92         const Integration::Point& currentPoint1 = event.points[0];
93         const Integration::Point& currentPoint2 = event.points[1];
94
95         if(currentPoint1.GetState() == PointState::UP || currentPoint2.GetState() == PointState::UP || currentPoint1.GetState() == PointState::INTERRUPTED)
96         {
97           // One of our touch points has an Up event so change our state back to CLEAR.
98           mState = CLEAR;
99           mTouchEvents.clear();
100         }
101         else
102         {
103           mTouchEvents.push_back(event);
104
105           // We can only determine a rotation after a certain number of touch points have been collected.
106           if(mTouchEvents.size() >= mMinimumTouchEvents)
107           {
108             // Remove the first few events from the vector otherwise values are exaggerated
109             mTouchEvents.erase(mTouchEvents.begin(), mTouchEvents.end() - mMinimumTouchEvents);
110
111             if(!mTouchEvents.empty())
112             {
113               mStartingAngle = GetAngle(mTouchEvents.begin()->points[0], mTouchEvents.begin()->points[1]);
114
115               // Send rotation started
116               SendRotation(GestureState::STARTED, event);
117
118               mState = STARTED;
119             }
120
121             mTouchEvents.clear();
122
123             if(mState == POSSIBLE)
124             {
125               // No rotation, so restart detection
126               mState = CLEAR;
127               mTouchEvents.clear();
128             }
129           }
130         }
131       }
132       break;
133     }
134
135     case STARTED:
136     {
137       if(event.points[0].GetState() == PointState::INTERRUPTED)
138       {
139         // System interruption occurred, rotation should be cancelled
140         mTouchEvents.clear();
141         SendRotation(GestureState::CANCELLED, event);
142         mState = CLEAR;
143         mTouchEvents.clear();
144       }
145       else if(pointCount != 2)
146       {
147         // Send rotation finished event
148         SendRotation(GestureState::FINISHED, event);
149
150         mState = CLEAR;
151         mTouchEvents.clear();
152       }
153       else
154       {
155         const Integration::Point& currentPoint1 = event.points[0];
156         const Integration::Point& currentPoint2 = event.points[1];
157
158         if((currentPoint1.GetState() == PointState::UP) ||
159            (currentPoint2.GetState() == PointState::UP))
160         {
161           mTouchEvents.push_back(event);
162           // Send rotation finished event
163           SendRotation(GestureState::FINISHED, event);
164
165           mState = CLEAR;
166           mTouchEvents.clear();
167         }
168         else
169         {
170           mTouchEvents.push_back(event);
171
172           if(mTouchEvents.size() >= mMinimumTouchEventsAfterStart)
173           {
174             // Send rotation continuing
175             SendRotation(GestureState::CONTINUING, event);
176
177             mTouchEvents.clear();
178           }
179         }
180       }
181       break;
182     }
183   }
184 }
185
186 void RotationGestureRecognizer::SetMinimumTouchEvents(uint32_t value)
187 {
188   mMinimumTouchEvents = value;
189 }
190
191 void RotationGestureRecognizer::SetMinimumTouchEventsAfterStart(uint32_t value)
192 {
193   mMinimumTouchEventsAfterStart = value;
194 }
195
196 void RotationGestureRecognizer::SendRotation(GestureState state, const Integration::TouchEvent& currentEvent)
197 {
198   RotationGestureEvent gesture(state);
199   if(!mTouchEvents.empty())
200   {
201     // Assert if we have been holding TouchEvents that do not have 2 points
202     DALI_ASSERT_DEBUG(mTouchEvents[0].GetPointCount() == 2);
203
204     // We should use the current event in our calculations unless it does not have two points.
205     // If it does not have two points, then we should use the last point in mTouchEvents.
206     Integration::TouchEvent event(currentEvent);
207     if(event.GetPointCount() != 2)
208     {
209       event = *mTouchEvents.rbegin();
210     }
211
212     const Integration::Point& currentPoint1(event.points[0]);
213     const Integration::Point& currentPoint2(event.points[1]);
214
215     gesture.rotation    = GetAngle(currentPoint1, currentPoint2) - mStartingAngle;
216     gesture.centerPoint = GetCenterPoint(currentPoint1, currentPoint2);
217   }
218   else
219   {
220     // Something has gone wrong, just cancel the gesture.
221     gesture.state = GestureState::CANCELLED;
222   }
223
224   gesture.time       = currentEvent.time;
225   gesture.sourceType = mSourceType;
226   gesture.sourceData = mSourceData;
227
228   if(mScene)
229   {
230     // Create another handle so the recognizer cannot be destroyed during process function
231     GestureRecognizerPtr recognizerHandle = this;
232
233     mObserver.Process(*mScene, gesture);
234   }
235 }
236
237 } // namespace Internal
238
239 } // namespace Dali