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