Merge remote-tracking branch 'origin/tizen' into devel/new_mesh
[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[in,out] direction The new wobble value
93    * @param[in] inputs Contains:
94    *                    The current time since the wobble effect started
95    *                    The scroll-position
96    *                    The scroll-overshoot x & y
97    */
98   void operator()( Vector3& direction, const PropertyInputContainer& inputs )
99   {
100     if(mStabilized)
101     {
102       // check if animation cycle id has changed (if so then this spells
103       // the start of a new animation)
104       if(mAnimationCycleId != mWobbleEffect.GetAnimationCycleId())
105       {
106         mStabilized = false;
107       }
108     }
109     else
110     {
111       // not stable (i.e. wobbling)
112       Vector3 offset(inputs[2]->GetFloat(), inputs[3]->GetFloat(), 0.0f);
113       const Vector3& position = inputs[1]->GetVector3() - offset;
114       const float time = inputs[0]->GetFloat();
115       const float timePassed = time - mTime;
116
117       mTime = time;
118
119       if(timePassed>0)
120       {
121         const Vector3 delta = position - mChase;
122
123         // Check to see if wobble has stabilized.
124         if( (fabsf(delta.x) < STABILITY_DELTA_THRESHOLD) &&
125             (fabsf(mVelocity.x) < STABILITY_DELTA_THRESHOLD) )
126         {
127           mStabilityTimeCounter+=timePassed;
128
129           if(mStabilityTimeCounter > WOBBLEEFFECT_STABLE_TIME_THRESHOLD)
130           {
131             mStabilityTimeCounter = 0.0f;
132             mStabilized = true;
133             mWobbleEffect.IncrementStableCount();
134             mAnimationCycleId = mWobbleEffect.GetAnimationCycleId();
135           }
136         }
137         else
138         {
139           mStabilityTimeCounter = 0.0f;
140         }
141
142         if(!mStabilized)
143         {
144           float t = timePassed * WOBBLEEFFECT_TIME_FACTOR;
145
146           mVelocity *= pow( WOBBLEEFFECT_FRICTION_COEFFICIENT, t );
147           mVelocity += delta * 0.1f * t;
148           mChase += mVelocity;
149         }
150         else
151         {
152           // stabilized, so chase should be exactly on position.
153           mChase = position;
154         }
155       }
156
157       direction.x = position.x - mChase.x;
158       direction.y = position.y - mChase.y;
159     } // end else
160   }
161
162   Vector3 mChase;                                 ///< Chaser position
163   Vector3 mVelocity;                              ///< Velocity of Chaser
164   float mTime;                                    ///< Current time.
165   float mStabilityTimeCounter;                    ///< Time in seconds that stable for.
166   bool mStabilized;                               ///< Stabilized flag.
167   ScrollViewWobbleEffect& mWobbleEffect;          ///< Reference to Wobble Effect
168   unsigned int mAnimationCycleId;                 ///< Animation Cycle Id
169 };
170
171 ScrollViewWobbleEffect::ScrollViewWobbleEffect()
172 : mPropertyTime(Property::INVALID_INDEX),
173   mStableCurrent(0),
174   mAnimationCycleId(0)
175 {
176 }
177
178 ScrollViewWobbleEffect::~ScrollViewWobbleEffect()
179 {
180 }
181
182 void ScrollViewWobbleEffect::IncrementStableCount()
183 {
184   mStableCurrent++;
185 }
186
187 unsigned int ScrollViewWobbleEffect::GetAnimationCycleId() const
188 {
189   return mAnimationCycleId;
190 }
191
192 void ScrollViewWobbleEffect::OnAttach(Toolkit::ScrollView& scrollView)
193 {
194   mStableCurrent = 0;
195   mAnimationCycleId = 0;
196
197   // Create effect-time property if not already created.
198   if(mPropertyTime == Property::INVALID_INDEX)
199   {
200     mPropertyTime = SafeRegisterProperty( scrollView, Toolkit::ScrollViewWobbleEffect::EFFECT_TIME, 0.0f );
201   }
202
203   // Connect to the scroll view signals
204   scrollView.ScrollStartedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollStart);
205   scrollView.ScrollUpdatedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollUpdate);
206   scrollView.ScrollCompletedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollComplete);
207
208   AttachActor(scrollView);
209 }
210
211 void ScrollViewWobbleEffect::OnDetach(Toolkit::ScrollView& scrollView)
212 {
213   scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollStart);
214   scrollView.ScrollUpdatedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollUpdate);
215   scrollView.ScrollCompletedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollComplete);
216
217   if(mAnimation)
218   {
219     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnAnimationFinished);
220     mAnimation.Clear();
221     mAnimation.Reset();
222   }
223 }
224
225 void ScrollViewWobbleEffect::AttachActor(Actor actor)
226 {
227   // Create effect-overshoot property if not already created.
228   Property::Index propertyEffectOvershoot = actor.GetPropertyIndex(Toolkit::ScrollViewWobbleEffect::EFFECT_OVERSHOOT);
229   if(propertyEffectOvershoot == Property::INVALID_INDEX)
230   {
231     propertyEffectOvershoot = actor.RegisterProperty(Toolkit::ScrollViewWobbleEffect::EFFECT_OVERSHOOT, Vector3::ZERO);
232   }
233
234   Actor scrollView = GetScrollView();
235
236   Constraint constraint = Constraint::New<Vector3>( actor, propertyEffectOvershoot, ScrollViewWobbleEffectConstraint(*this) );
237   constraint.AddSource( Source( scrollView, mPropertyTime ) );
238   constraint.AddSource( Source( actor, Toolkit::ScrollView::Property::SCROLL_POSITION ) );
239   constraint.AddSource( Source( actor, Toolkit::ScrollView::Property::OVERSHOOT_X ) );
240   constraint.AddSource( Source( actor, Toolkit::ScrollView::Property::OVERSHOOT_Y ) );
241   constraint.Apply();
242 }
243
244 void ScrollViewWobbleEffect::DetachActor(Actor actor)
245 {
246   // TODO: remove the specific constraint defined in AttachActor (and possibly
247   // unregister property) - neither functionality exists in Dali.
248 }
249
250 void ScrollViewWobbleEffect::ContinueAnimation(float endTime)
251 {
252
253   // continue animating
254   if(mAnimation)
255   {
256     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnAnimationFinished);
257     mAnimation.Clear();
258   }
259
260   Actor scrollView = GetScrollView();
261
262   mAnimation = Animation::New(WOBBLEEFFECT_ANIMATION_MAX_TIME);
263   mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunction::LINEAR );
264   mAnimation.FinishedSignal().Connect(this, &ScrollViewWobbleEffect::OnAnimationFinished);
265   mAnimation.Play();
266
267 }
268
269 void ScrollViewWobbleEffect::OnScrollStart( const Vector3& position )
270 {
271   // When animation starts, all constraints all unstable,
272   // and we change the animation cycle id.
273   mStableCurrent = 0;
274   mAnimationCycleId++;
275
276   GetScrollView().SetProperty(mPropertyTime, 0.0f);
277
278   ContinueAnimation(WOBBLEEFFECT_ANIMATION_MAX_TIME);
279 }
280
281 void ScrollViewWobbleEffect::OnScrollUpdate( const Vector3& position )
282 {
283   // nothing to do
284 }
285
286 void ScrollViewWobbleEffect::OnScrollComplete( const Vector3& position )
287 {
288   // nothing to do
289 }
290
291 void ScrollViewWobbleEffect::OnAnimationFinished( Animation& animation )
292 {
293   if(mStableCurrent!=1)
294   {
295     // still unstable, so continue animating.
296     float endTime = GetScrollView().GetProperty<float>(mPropertyTime) + WOBBLEEFFECT_ANIMATION_MAX_TIME;
297     ContinueAnimation(endTime);
298   }
299 }
300
301 } // namespace Internal
302
303 } // namespace Toolkit
304
305 } // namespace Dali