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