Conversion to Apache 2.0 license
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / scrollable / scroll-view / scroll-view-wobble-effect-impl.cpp
1 /*
2  * Copyright (c) 2014 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 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h>
19 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-wobble-effect-impl.h>
20 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h>
21
22 using namespace Dali;
23
24 namespace // unnamed namespace
25 {
26
27 const float WOBBLEEFFECT_FRICTION_COEFFICIENT = 0.8f;     ///< (deacc) - velocity multiplier per unit time (80%)
28 const float WOBBLEEFFECT_IMPULSE_DISTANCE_FACTOR = 0.1f;  ///< (acc) - move by 10% of distance-delta per unit time
29 const float WOBBLEEFFECT_TIME_FACTOR = 30.0f;             ///< (T) - 30 times faster (unit time = 1/30th sec)
30 const float WOBBLEEFFECT_ANIMATION_MAX_TIME = 60.0f;      ///< Animation time (every time finishes, checks if it needs to go again)
31 const float WOBBLEEFFECT_STABLE_TIME_THRESHOLD = 0.5f;    ///< Must be stable for more than half a second to stop animating.
32 const float STABILITY_DELTA_THRESHOLD = Math::MACHINE_EPSILON_10000;  ///< When velocity delta is greater than this threshold it is considered in motion.
33
34 /**
35  * Gets a property index. If the property doesn't already exist, then
36  * it will create the property.
37  * @param[in] handle The handle that owns or will own the property
38  * @param[in] name The name for this property
39  * @param[in] propertyValue The initial value for this property
40  * @return The property index for this property is returned.
41  */
42 Property::Index SafeRegisterProperty( Handle& handle, const std::string& name, Property::Value propertyValue )
43 {
44   Property::Index index = handle.GetPropertyIndex( name );
45
46   if(index == Property::INVALID_INDEX)
47   {
48     index = handle.RegisterProperty( name, propertyValue );
49   }
50
51   return index;
52 }
53
54 } // unnamed namespace
55
56 namespace Dali
57 {
58
59 namespace Toolkit
60 {
61
62 namespace Internal
63 {
64
65 /**
66  * ScrollView WobbleEffect constraint
67  * This constraint has a chase position and velocity, that chases
68  * a target position (scroll-position + scroll-offset). As it has a
69  * velocity. It will eventually pass it's target position, and chase back
70  * in the opposite direction. As it has a friction coefficient, it will
71  * gradually slow, and reach it's target position (stabilized).
72  */
73 struct ScrollViewWobbleEffectConstraint
74 {
75   /**
76    * @param[in,out] wobbleEffect Reference to wobbleEffect instance
77    */
78   ScrollViewWobbleEffectConstraint(ScrollViewWobbleEffect& wobbleEffect)
79   : mChase(Vector3::ZERO),
80     mVelocity(Vector3::ZERO),
81     mTime(0.0f),
82     mStabilityTimeCounter(0),
83     mStabilized(true),
84     mWobbleEffect(wobbleEffect),
85     mAnimationCycleId(0)
86   {
87
88   }
89
90   /**
91    * @param[out] current The new wobble value
92    * @param[in] propertyTime The current time since the wobble effect started
93    * @param[in] propertyPosition The scroll-position
94    * @param[in] propertyOffset The scroll-overshoot
95    */
96   Vector3 operator()(const Vector3& current,
97                     const PropertyInput& propertyTime,
98                     const PropertyInput& propertyPosition,
99                     const PropertyInput& propertyOffsetX,
100                     const PropertyInput& propertyOffsetY)
101   {
102     Vector3 dir;
103
104     if(mStabilized)
105     {
106       // check if animation cycle id has changed (if so then this spells
107       // the start of a new animation)
108       if(mAnimationCycleId != mWobbleEffect.GetAnimationCycleId())
109       {
110         mStabilized = false;
111       }
112     }
113     else
114     {
115       // not stable (i.e. wobbling)
116       Vector3 offset(propertyOffsetX.GetFloat(), propertyOffsetY.GetFloat(), 0.0f);
117       const Vector3& position = propertyPosition.GetVector3() - offset;
118       const float time = propertyTime.GetFloat();
119       const float timePassed = time - mTime;
120
121       mTime = time;
122
123       if(timePassed>0)
124       {
125         const Vector3 delta = position - mChase;
126
127         // Check to see if wobble has stabilized.
128         if( (fabsf(delta.x) < STABILITY_DELTA_THRESHOLD) &&
129             (fabsf(mVelocity.x) < STABILITY_DELTA_THRESHOLD) )
130         {
131           mStabilityTimeCounter+=timePassed;
132
133           if(mStabilityTimeCounter > WOBBLEEFFECT_STABLE_TIME_THRESHOLD)
134           {
135             mStabilityTimeCounter = 0.0f;
136             mStabilized = true;
137             mWobbleEffect.IncrementStableCount();
138             mAnimationCycleId = mWobbleEffect.GetAnimationCycleId();
139           }
140         }
141         else
142         {
143           mStabilityTimeCounter = 0.0f;
144         }
145
146         if(!mStabilized)
147         {
148           float t = timePassed * WOBBLEEFFECT_TIME_FACTOR;
149
150           mVelocity *= pow( WOBBLEEFFECT_FRICTION_COEFFICIENT, t );
151           mVelocity += delta * 0.1f * t;
152           mChase += mVelocity;
153         }
154         else
155         {
156           // stabilized, so chase should be exactly on position.
157           mChase = position;
158         }
159       }
160
161       dir.x = propertyPosition.GetVector3().x - mChase.x;
162       dir.y = propertyPosition.GetVector3().y - mChase.y;
163     } // end else
164
165     return dir;
166   }
167
168   Vector3 mChase;                                 ///< Chaser position
169   Vector3 mVelocity;                              ///< Velocity of Chaser
170   float mTime;                                    ///< Current time.
171   float mStabilityTimeCounter;                    ///< Time in seconds that stable for.
172   bool mStabilized;                               ///< Stabilized flag.
173   ScrollViewWobbleEffect& mWobbleEffect;          ///< Reference to Wobble Effect
174   unsigned int mAnimationCycleId;                 ///< Animation Cycle Id
175 };
176
177 ScrollViewWobbleEffect::ScrollViewWobbleEffect()
178 : mPropertyTime(Property::INVALID_INDEX)
179 {
180
181 }
182
183 ScrollViewWobbleEffect::~ScrollViewWobbleEffect()
184 {
185 }
186
187 void ScrollViewWobbleEffect::IncrementStableCount()
188 {
189   mStableCurrent++;
190 }
191
192 unsigned int ScrollViewWobbleEffect::GetAnimationCycleId() const
193 {
194   return mAnimationCycleId;
195 }
196
197 void ScrollViewWobbleEffect::OnAttach(Toolkit::ScrollView& scrollView)
198 {
199   mStableCurrent = 0;
200   mAnimationCycleId = 0;
201
202   // Create effect-time property if not already created.
203   if(mPropertyTime == Property::INVALID_INDEX)
204   {
205     mPropertyTime = SafeRegisterProperty( scrollView, Toolkit::ScrollViewWobbleEffect::EFFECT_TIME, 0.0f );
206   }
207
208   // Connect to the scroll view signals
209   scrollView.ScrollStartedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollStart);
210   scrollView.ScrollUpdatedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollUpdate);
211   scrollView.ScrollCompletedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollComplete);
212
213   AttachActor(scrollView);
214 }
215
216 void ScrollViewWobbleEffect::OnDetach(Toolkit::ScrollView& scrollView)
217 {
218   scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollStart);
219   scrollView.ScrollUpdatedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollUpdate);
220   scrollView.ScrollCompletedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollComplete);
221
222   if(mAnimation)
223   {
224     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnAnimationFinished);
225     mAnimation.Clear();
226     mAnimation.Reset();
227   }
228 }
229
230 void ScrollViewWobbleEffect::AttachActor(Actor actor)
231 {
232   Property::Index propertyPosition = actor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME);
233   Property::Index propertyOvershootX = actor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
234   Property::Index propertyOvershootY = actor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME);
235
236   // Create effect-overshoot property if not already created.
237   Property::Index propertyEffectOvershoot = actor.GetPropertyIndex(Toolkit::ScrollViewWobbleEffect::EFFECT_OVERSHOOT);
238   if(propertyEffectOvershoot == Property::INVALID_INDEX)
239   {
240     propertyEffectOvershoot = actor.RegisterProperty(Toolkit::ScrollViewWobbleEffect::EFFECT_OVERSHOOT, Vector3::ZERO);
241   }
242
243   Actor scrollView = GetScrollView();
244
245   Constraint constraint = Constraint::New<Vector3>( propertyEffectOvershoot,
246                                                     Source(scrollView, mPropertyTime),
247                                                     Source(actor, propertyPosition),
248                                                     Source(actor, propertyOvershootX),
249                                                     Source(actor, propertyOvershootY),
250                                                     ScrollViewWobbleEffectConstraint(*this) );
251   actor.ApplyConstraint(constraint);
252 }
253
254 void ScrollViewWobbleEffect::DetachActor(Actor actor)
255 {
256   // TODO: remove the specific constraint defined in AttachActor (and possibly
257   // unregister property) - neither functionality exists in Dali.
258 }
259
260 void ScrollViewWobbleEffect::ContinueAnimation(float endTime)
261 {
262
263   // continue animating
264   if(mAnimation)
265   {
266     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnAnimationFinished);
267     mAnimation.Clear();
268   }
269
270   Actor scrollView = GetScrollView();
271
272   mAnimation = Animation::New(WOBBLEEFFECT_ANIMATION_MAX_TIME);
273   mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunctions::Linear );
274   mAnimation.FinishedSignal().Connect(this, &ScrollViewWobbleEffect::OnAnimationFinished);
275   mAnimation.Play();
276
277 }
278
279 void ScrollViewWobbleEffect::OnScrollStart( const Vector3& position )
280 {
281   // When animation starts, all constraints all unstable,
282   // and we change the animation cycle id.
283   mStableCurrent = 0;
284   mAnimationCycleId++;
285
286   GetScrollView().SetProperty(mPropertyTime, 0.0f);
287
288   ContinueAnimation(WOBBLEEFFECT_ANIMATION_MAX_TIME);
289 }
290
291 void ScrollViewWobbleEffect::OnScrollUpdate( const Vector3& position )
292 {
293   // nothing to do
294 }
295
296 void ScrollViewWobbleEffect::OnScrollComplete( const Vector3& position )
297 {
298   // nothing to do
299 }
300
301 void ScrollViewWobbleEffect::OnAnimationFinished( Animation& animation )
302 {
303   if(mStableCurrent!=1)
304   {
305     // still unstable, so continue animating.
306     float endTime = GetScrollView().GetProperty<float>(mPropertyTime) + WOBBLEEFFECT_ANIMATION_MAX_TIME;
307     ContinueAnimation(endTime);
308   }
309 }
310
311 } // namespace Internal
312
313 } // namespace Toolkit
314
315 } // namespace Dali