2 * Copyright (c) 2022 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/rotation-gesture/rotation-gesture-recognizer.h>
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>
39 inline float GetAngle(const Integration::Point& point1, const Integration::Point& point2)
41 const Vector2& point1ScreenPosition = point1.GetScreenPosition();
42 const Vector2& point2ScreenPosition = point2.GetScreenPosition();
43 return atan2(point2ScreenPosition.y - point1ScreenPosition.y, point2ScreenPosition.x - point1ScreenPosition.x);
46 inline Vector2 GetCenterPoint(const Integration::Point& point1, const Integration::Point& point2)
48 return Vector2(point1.GetScreenPosition() + point2.GetScreenPosition()) * 0.5f;
51 } // unnamed namespace
53 RotationGestureRecognizer::RotationGestureRecognizer(Observer& observer, uint32_t minimumTouchEvents, uint32_t minimumTouchEventsAfterStart)
54 : GestureRecognizer(GestureType::ROTATION),
59 mMinimumTouchEvents(minimumTouchEvents),
60 mMinimumTouchEventsAfterStart(minimumTouchEventsAfterStart)
64 void RotationGestureRecognizer::SendEvent(const Integration::TouchEvent& event)
66 int pointCount = event.GetPointCount();
67 GestureRecognizerPtr ptr(this); // To keep us from being destroyed during the life-time of this method
75 // Change state to possible as we have two touch points.
77 mTouchEvents.push_back(event);
86 // We no longer have two touch points so change state back to CLEAR.
92 const Integration::Point& currentPoint1 = event.points[0];
93 const Integration::Point& currentPoint2 = event.points[1];
95 if(currentPoint1.GetState() == PointState::UP || currentPoint2.GetState() == PointState::UP || currentPoint1.GetState() == PointState::INTERRUPTED)
97 // One of our touch points has an Up event so change our state back to CLEAR.
103 mTouchEvents.push_back(event);
105 // We can only determine a rotation after a certain number of touch points have been collected.
106 if(mTouchEvents.size() >= mMinimumTouchEvents)
108 // Remove the first few events from the vector otherwise values are exaggerated
109 mTouchEvents.erase(mTouchEvents.begin(), mTouchEvents.end() - mMinimumTouchEvents);
111 if(!mTouchEvents.empty())
113 mStartingAngle = GetAngle(mTouchEvents.begin()->points[0], mTouchEvents.begin()->points[1]);
115 // Send rotation started
116 SendRotation(GestureState::STARTED, event);
121 mTouchEvents.clear();
123 if(mState == POSSIBLE)
125 // No rotation, so restart detection
127 mTouchEvents.clear();
137 if(event.points[0].GetState() == PointState::INTERRUPTED)
139 // System interruption occurred, rotation should be cancelled
140 mTouchEvents.clear();
141 SendRotation(GestureState::CANCELLED, event);
143 mTouchEvents.clear();
145 else if(pointCount != 2)
147 // Send rotation finished event
148 SendRotation(GestureState::FINISHED, event);
151 mTouchEvents.clear();
155 const Integration::Point& currentPoint1 = event.points[0];
156 const Integration::Point& currentPoint2 = event.points[1];
158 if((currentPoint1.GetState() == PointState::UP) ||
159 (currentPoint2.GetState() == PointState::UP))
161 mTouchEvents.push_back(event);
162 // Send rotation finished event
163 SendRotation(GestureState::FINISHED, event);
166 mTouchEvents.clear();
170 mTouchEvents.push_back(event);
172 if(mTouchEvents.size() >= mMinimumTouchEventsAfterStart)
174 // Send rotation continuing
175 SendRotation(GestureState::CONTINUING, event);
177 mTouchEvents.clear();
186 void RotationGestureRecognizer::SetMinimumTouchEvents(uint32_t value)
188 mMinimumTouchEvents = value;
191 void RotationGestureRecognizer::SetMinimumTouchEventsAfterStart(uint32_t value)
193 mMinimumTouchEventsAfterStart = value;
196 void RotationGestureRecognizer::SendRotation(GestureState state, const Integration::TouchEvent& currentEvent)
198 RotationGestureEvent gesture(state);
199 if(!mTouchEvents.empty())
201 // Assert if we have been holding TouchEvents that do not have 2 points
202 DALI_ASSERT_DEBUG(mTouchEvents[0].GetPointCount() == 2);
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)
209 event = *mTouchEvents.rbegin();
212 const Integration::Point& currentPoint1(event.points[0]);
213 const Integration::Point& currentPoint2(event.points[1]);
215 gesture.rotation = GetAngle(currentPoint1, currentPoint2) - mStartingAngle;
216 gesture.centerPoint = GetCenterPoint(currentPoint1, currentPoint2);
220 // Something has gone wrong, just cancel the gesture.
221 gesture.state = GestureState::CANCELLED;
224 gesture.time = currentEvent.time;
225 gesture.sourceType = mSourceType;
226 gesture.sourceData = mSourceData;
230 // Create another handle so the recognizer cannot be destroyed during process function
231 GestureRecognizerPtr recognizerHandle = this;
233 mObserver.Process(*mScene, gesture);
237 } // namespace Internal