Merge base & optional
[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 #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   mStableCurrent(0),
180   mAnimationCycleId(0)
181 {
182 }
183
184 ScrollViewWobbleEffect::~ScrollViewWobbleEffect()
185 {
186 }
187
188 void ScrollViewWobbleEffect::IncrementStableCount()
189 {
190   mStableCurrent++;
191 }
192
193 unsigned int ScrollViewWobbleEffect::GetAnimationCycleId() const
194 {
195   return mAnimationCycleId;
196 }
197
198 void ScrollViewWobbleEffect::OnAttach(Toolkit::ScrollView& scrollView)
199 {
200   mStableCurrent = 0;
201   mAnimationCycleId = 0;
202
203   // Create effect-time property if not already created.
204   if(mPropertyTime == Property::INVALID_INDEX)
205   {
206     mPropertyTime = SafeRegisterProperty( scrollView, Toolkit::ScrollViewWobbleEffect::EFFECT_TIME, 0.0f );
207   }
208
209   // Connect to the scroll view signals
210   scrollView.ScrollStartedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollStart);
211   scrollView.ScrollUpdatedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollUpdate);
212   scrollView.ScrollCompletedSignal().Connect(this, &ScrollViewWobbleEffect::OnScrollComplete);
213
214   AttachActor(scrollView);
215 }
216
217 void ScrollViewWobbleEffect::OnDetach(Toolkit::ScrollView& scrollView)
218 {
219   scrollView.ScrollStartedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollStart);
220   scrollView.ScrollUpdatedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollUpdate);
221   scrollView.ScrollCompletedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnScrollComplete);
222
223   if(mAnimation)
224   {
225     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnAnimationFinished);
226     mAnimation.Clear();
227     mAnimation.Reset();
228   }
229 }
230
231 void ScrollViewWobbleEffect::AttachActor(Actor actor)
232 {
233   Property::Index propertyPosition = actor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_POSITION_PROPERTY_NAME);
234   Property::Index propertyOvershootX = actor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_X_PROPERTY_NAME);
235   Property::Index propertyOvershootY = actor.GetPropertyIndex(Toolkit::ScrollView::SCROLL_OVERSHOOT_Y_PROPERTY_NAME);
236
237   // Create effect-overshoot property if not already created.
238   Property::Index propertyEffectOvershoot = actor.GetPropertyIndex(Toolkit::ScrollViewWobbleEffect::EFFECT_OVERSHOOT);
239   if(propertyEffectOvershoot == Property::INVALID_INDEX)
240   {
241     propertyEffectOvershoot = actor.RegisterProperty(Toolkit::ScrollViewWobbleEffect::EFFECT_OVERSHOOT, Vector3::ZERO);
242   }
243
244   Actor scrollView = GetScrollView();
245
246   Constraint constraint = Constraint::New<Vector3>( propertyEffectOvershoot,
247                                                     Source(scrollView, mPropertyTime),
248                                                     Source(actor, propertyPosition),
249                                                     Source(actor, propertyOvershootX),
250                                                     Source(actor, propertyOvershootY),
251                                                     ScrollViewWobbleEffectConstraint(*this) );
252   actor.ApplyConstraint(constraint);
253 }
254
255 void ScrollViewWobbleEffect::DetachActor(Actor actor)
256 {
257   // TODO: remove the specific constraint defined in AttachActor (and possibly
258   // unregister property) - neither functionality exists in Dali.
259 }
260
261 void ScrollViewWobbleEffect::ContinueAnimation(float endTime)
262 {
263
264   // continue animating
265   if(mAnimation)
266   {
267     mAnimation.FinishedSignal().Disconnect(this, &ScrollViewWobbleEffect::OnAnimationFinished);
268     mAnimation.Clear();
269   }
270
271   Actor scrollView = GetScrollView();
272
273   mAnimation = Animation::New(WOBBLEEFFECT_ANIMATION_MAX_TIME);
274   mAnimation.AnimateTo( Property(scrollView, mPropertyTime), endTime, AlphaFunctions::Linear );
275   mAnimation.FinishedSignal().Connect(this, &ScrollViewWobbleEffect::OnAnimationFinished);
276   mAnimation.Play();
277
278 }
279
280 void ScrollViewWobbleEffect::OnScrollStart( const Vector3& position )
281 {
282   // When animation starts, all constraints all unstable,
283   // and we change the animation cycle id.
284   mStableCurrent = 0;
285   mAnimationCycleId++;
286
287   GetScrollView().SetProperty(mPropertyTime, 0.0f);
288
289   ContinueAnimation(WOBBLEEFFECT_ANIMATION_MAX_TIME);
290 }
291
292 void ScrollViewWobbleEffect::OnScrollUpdate( const Vector3& position )
293 {
294   // nothing to do
295 }
296
297 void ScrollViewWobbleEffect::OnScrollComplete( const Vector3& position )
298 {
299   // nothing to do
300 }
301
302 void ScrollViewWobbleEffect::OnAnimationFinished( Animation& animation )
303 {
304   if(mStableCurrent!=1)
305   {
306     // still unstable, so continue animating.
307     float endTime = GetScrollView().GetProperty<float>(mPropertyTime) + WOBBLEEFFECT_ANIMATION_MAX_TIME;
308     ContinueAnimation(endTime);
309   }
310 }
311
312 } // namespace Internal
313
314 } // namespace Toolkit
315
316 } // namespace Dali