Using New Constraints
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / scrollable / scroll-view / scroll-view-depth-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 // EXTERNAL INCLUDES
19 #include <dali/public-api/animation/constraint.h>
20 #include <dali/public-api/object/property-input.h>
21
22 // INTERNAL INCLUDES
23 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-effect-impl.h>
24 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-depth-effect-impl.h>
25
26 using namespace Dali;
27
28 namespace // unnamed namespace
29 {
30
31 // constraints ////////////////////////////////////////////////////////////////
32
33 /**
34  * Ramp equation is a variable easing equation
35  * of the form f(x) = |x|^y * x / |x|
36  *
37  * An exponent (y) of 1 will result in a fast (linear graph)
38  * Increasing the exponent will increase the Ease In
39  *
40  * @param[in] x mantissa (value from -1.0f to 1.0f)
41  * @param[in] y exponent (+ve value)
42  * @return The signed progress value from -1.0f to 1.0f
43  */
44 inline float RampFunction(float x, float y)
45 {
46   if(x < 0.0f)
47   {
48     return -powf(fabsf(x), y);
49   }
50
51   return powf(fabsf(x), y);
52 }
53
54 /**
55  * ScrollDepthScaleConstraint
56  *
57  * Scale constraint adjusts the scale of the Actors
58  * based on their parent page's position relative to the middle of the screen.
59  * When at middle of the screen the scale is not altered.
60  * As the page is moved away from the middle, Actors shrink in scale but at
61  * different rates defined by the RampFunction(x, f).
62  * All Actors eventually shrink to the same amount once at their destination.
63  */
64 struct ScrollDepthScaleConstraint
65 {
66   /**
67    * The scaling constraint uses the amount the actors
68    * have moved in position to determine scaling extent.
69    * So essentially very similar calculations are used here.
70    *
71    * @param[in] positionExtent Controls how much Actor's X and Y
72    * position affects their alpha function's exponent value
73    * @param[in] offsetExtent exponent offset for X and Y scrolling
74    * axes.
75    * @param[in] positionScale Changes the amount the page as a whole
76    * moves by.
77    * @param[in] scaleExtent Scale factor to reach when page is one whole
78    * page away from screen.
79    */
80   ScrollDepthScaleConstraint( Vector2 positionExtent = Vector2(0.5f, 1.0f),
81                               Vector2 offsetExtent = Vector2(1.0f, 1.0f),
82                               float positionScale = 1.5f,
83                               float scaleExtent = 0.5f)
84   : mPositionExtent(positionExtent),
85     mOffsetExtent(offsetExtent),
86     mMaxExtent(positionExtent.x + positionExtent.y),
87     mPositionScale(positionScale),
88     mScaleExtent(scaleExtent)
89   {
90   }
91
92   /**
93    * @param[in,out] current The current scale
94    * @param[in] inputs Contains:
95    *                    The page's position.
96    *                    The scroll-view's position property (SCROLL_POSITION)
97    *                    The minimum extent of this scroll domain. (SCROLL_POSITION_MIN)
98    *                    The maximum extent of this scroll domain. (SCROLL_POSITION_MIN)
99    *                    The size of the page. (scrollView SIZE)
100    *                    Whether scroll wrap has been enabled or not (SCROLL_WRAP)
101    * @return The new scale of this Actor.
102    */
103   void operator()( Vector3& currentScale, const PropertyInputContainer& inputs )
104   {
105     const Vector3& currentPosition = inputs[0]->GetVector3();
106     const Vector3& pagePosition = inputs[1]->GetVector3();
107     const Vector3& scrollPosition = inputs[2]->GetVector3();
108
109     // Get position of page.
110     Vector3 position = pagePosition + scrollPosition;
111
112     // short circuit: for orthognal view.
113     if( (fabsf(position.x) < Math::MACHINE_EPSILON_1) && (fabsf(position.y) < Math::MACHINE_EPSILON_1) )
114     {
115       return;
116     }
117
118     const Vector3& pageSize = inputs[5]->GetVector3();
119
120     // Don't have enough parameters, to provide Wrap mode (need a way of having 'uniforms' instead of scrollWrap.GetBoolean())
121
122     const Vector3& min = inputs[3]->GetVector3();
123     const Vector3& max = inputs[4]->GetVector3();
124
125     if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1)
126     {
127       // WRAP X (based on the position of the right side)
128       position.x = WrapInDomain(position.x + pageSize.x, min.x, max.x) - pageSize.x;
129     }
130
131     if(fabsf(min.y - max.y) > Math::MACHINE_EPSILON_1)
132     {
133       // WRAP Y (based on the position of the bottom side)
134       position.y = WrapInDomain(position.y + pageSize.y, min.y, max.y) - pageSize.y;
135     }
136
137     // short circuit: for pages outside of view.
138     if( (fabsf(position.x) >= pageSize.x) || (fabsf(position.y) >= pageSize.y) )
139     {
140       return;
141     }
142
143     // Calculate scale ////////////////////////////////////////////////////////
144
145     position.x /= pageSize.x;
146     position.y /= pageSize.y;
147
148     position *= mPositionScale;
149
150     Vector3 relCurrentPosition = currentPosition;
151     relCurrentPosition.x = relCurrentPosition.x / pageSize.x + 0.5f;
152     relCurrentPosition.y = relCurrentPosition.y / pageSize.y + 0.5f;
153
154     Vector3 extent( (relCurrentPosition.x * mPositionExtent.x + relCurrentPosition.y * mPositionExtent.y) * 1.0f,
155                     (relCurrentPosition.x * mPositionExtent.y + relCurrentPosition.y * mPositionExtent.x) * 1.0f,
156                     0.0f);
157
158     if(position.x>0.0f)
159     {
160       extent.x = mMaxExtent - extent.x; // Flip for right.
161     }
162     if(position.y>0.0f)
163     {
164       extent.y = mMaxExtent - extent.y; // Flip for bottom.
165     }
166
167     position.x = RampFunction(position.x, mOffsetExtent.x + extent.x);
168     position.y = RampFunction(position.y, mOffsetExtent.y + extent.y);
169
170     float f = mScaleExtent + cos(position.x * Math::PI_2) * cos(position.y * Math::PI_2) * (1.0f - mScaleExtent);
171
172     currentScale *= f;
173   }
174
175   const Vector2 mPositionExtent;                                ///< Determines how much of the Actor's X and Y position affects exponent value.
176   const Vector2 mOffsetExtent;                                  ///< Offset for exponent value.
177   const float mMaxExtent;                                       ///< Maximum possible extent (mOffsetExtent.x + mOffsetExtent.y)
178   const float mPositionScale;                                   ///< Position scaling factor (spreads out pages, to avoid overlap)
179   const float mScaleExtent;                                     ///< Scale factor when page is at furthest from
180 };
181
182 /**
183  * ScrollDepthPositionConstraint
184  *
185  * Position constraint adjusts the position of the Actors
186  * based on their parent page's position relative to the middle of the screen.
187  * When at middle of the screen the position is not altered.
188  * As the page is moved away from the middle, Actors move away but at
189  * different rates defined by the RampFunction(x, f).
190  * All Actors eventually arrive at their destination at the same time.
191  */
192 struct ScrollDepthPositionConstraint
193 {
194   /**
195    * @param [in] positionExtent Controls how much Actor's X and Y
196    * position affects their alpha function's exponent value
197    * @param [in] offsetExtent exponent offset for X and Y scrolling
198    * axes.
199    * @param [in] positionScale Changes the amount the page as a whole
200    * moves by.
201    */
202   ScrollDepthPositionConstraint( Vector2 positionExtent = Vector2(0.5f, 1.0f),
203                                  Vector2 offsetExtent = Vector2(1.0f, 1.0f),
204                                  float positionScale = 1.5f )
205   : mPositionExtent(positionExtent),
206     mOffsetExtent(offsetExtent),
207     mMaxExtent(positionExtent.x + positionExtent.y),
208     mPositionScale(positionScale)
209   {
210   }
211
212   /**
213    * @param[in,out] current The current position
214    * @param[in] inputs Contains:
215    *                    The page's position.
216    *                    The scroll-view's position property (SCROLL_POSITION)
217    *                    The minimum extent of this scroll domain. (SCROLL_POSITION_MIN)
218    *                    The maximum extent of this scroll domain. (SCROLL_POSITION_MIN)
219    *                    The size of the page. (scrollView SIZE)
220    *                    Whether scroll wrap has been enabled or not (SCROLL_WRAP)
221    * @return The new position of this Actor.
222    */
223   void operator()( Vector3& currentPosition, const PropertyInputContainer& inputs )
224   {
225     const Vector3& pagePosition = inputs[0]->GetVector3();
226     const Vector3& scrollPosition = inputs[1]->GetVector3();
227
228     // Get position of page.
229     Vector3 position = pagePosition + scrollPosition;
230
231     // short circuit: for orthognal view.
232     if( (fabsf(position.x) < Math::MACHINE_EPSILON_1) && (fabsf(position.y) < Math::MACHINE_EPSILON_1) )
233     {
234       currentPosition += scrollPosition;
235       return;
236     }
237
238     const Vector3& pageSize = inputs[4]->GetVector3();
239     bool wrap = inputs[5]->GetBoolean();
240
241     if(wrap)
242     {
243       const Vector3& min = inputs[2]->GetVector3();
244       const Vector3& max = inputs[3]->GetVector3();
245
246       if(fabsf(min.x - max.x) > Math::MACHINE_EPSILON_1)
247       {
248         // WRAP X (based on the position of the right side)
249         position.x = WrapInDomain(position.x + pageSize.x, min.x, max.x) - pageSize.x;
250       }
251
252       if(fabsf(min.y - max.y) > Math::MACHINE_EPSILON_1)
253       {
254         // WRAP Y (based on the position of the bottom side)
255         position.y = WrapInDomain(position.y + pageSize.y, min.y, max.y) - pageSize.y;
256       }
257     }
258
259     // short circuit: for pages outside of view.
260     if( (fabsf(position.x) >= pageSize.x) || (fabsf(position.y) >= pageSize.y) )
261     {
262       // position actors at: scrollposition (Property) + pagePosition (Parent) + current (this)
263       // they will be invisible so doesn't have to be precise, just away from stage.
264       currentPosition += scrollPosition;
265       return;
266     }
267
268     // Calculate position /////////////////////////////////////////////////////
269
270     position.x /= pageSize.x;
271     position.y /= pageSize.y;
272
273     position *= mPositionScale;
274
275     Vector3 relCurrentPosition = currentPosition;
276     relCurrentPosition.x = relCurrentPosition.x / pageSize.x + 0.5f;
277     relCurrentPosition.y = relCurrentPosition.y / pageSize.y + 0.5f;
278
279     Vector3 extent( (relCurrentPosition.x * mPositionExtent.x + relCurrentPosition.y * mPositionExtent.y) * 1.0f,
280                    (relCurrentPosition.x * mPositionExtent.y + relCurrentPosition.y * mPositionExtent.x) * 1.0f,
281                    0.0f);
282
283     if(position.x>0.0f)
284     {
285       extent.x = mMaxExtent - extent.x; // Flip for right.
286     }
287     if(position.y>0.0f)
288     {
289       extent.y = mMaxExtent - extent.y; // Flip for bottom.
290     }
291
292     position.x = RampFunction(position.x, mOffsetExtent.x + extent.x);
293     position.y = RampFunction(position.y, mOffsetExtent.y + extent.y);
294
295     currentPosition -= pagePosition;
296     currentPosition += pageSize * position;
297   }
298
299   const Vector2 mPositionExtent;                                ///< Determines how much of the Actor's X and Y position affects exponent value.
300   const Vector2 mOffsetExtent;                                  ///< Offset for exponent value.
301   const float mMaxExtent;                                       ///< Maximum possible extent (mOffsetExtent.x + mOffsetExtent.y)
302   const float mPositionScale;                                   ///< Position scaling factor (spreads out pages, to avoid overlap)
303 };
304
305 /**
306  * Applies the scroll depth constraints to the child actor
307  *
308  * @param[in] scrollView The ScrollView containing the pages.
309  * @param[in] child The child to be affected with the 3D Effect.
310  * @param[in] positionExtent Controls how much Actor's X and Y
311  * position affects their alpha function's exponent value
312  * @param[in] offsetExtent exponent offset for X and Y scrolling
313  * axes.
314  * @param[in] positionScale Changes the amount the page as a whole
315  * moves by.
316  * @param[in] scaleExtent Scale factor to reach when page is one whole
317  * page away from screen.
318  */
319 void ApplyScrollDepthConstraints(Toolkit::ScrollView scrollView,
320                                  Actor child,
321                                  const Vector2& positionExtent,
322                                  const Vector2& offsetExtent,
323                                  float positionScale,
324                                  float scaleExtent)
325 {
326   // Scale Constraint
327   Constraint constraint = Constraint::New<Vector3>( child, Actor::Property::SCALE, ScrollDepthScaleConstraint( positionExtent, offsetExtent, positionScale, scaleExtent ) );
328   constraint.AddSource( LocalSource( Actor::Property::POSITION ) );
329   constraint.AddSource( ParentSource( Actor::Property::POSITION ) );
330   constraint.AddSource( Source( scrollView, Toolkit::ScrollView::Property::SCROLL_POSITION ) );
331   constraint.AddSource( Source( scrollView, Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
332   constraint.AddSource( Source( scrollView, Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
333   constraint.AddSource( Source( scrollView, Actor::Property::SIZE ) );
334   constraint.SetRemoveAction( Constraint::Discard );
335   constraint.Apply();
336
337   // Position Constraint (apply last as other constraints use Actor::POSITION as a function input)
338   constraint = Constraint::New<Vector3>( child, Actor::Property::POSITION, ScrollDepthPositionConstraint( positionExtent, offsetExtent, positionScale ) );
339   constraint.AddSource( ParentSource(Actor::Property::POSITION) );
340   constraint.AddSource( Source(scrollView, Toolkit::ScrollView::Property::SCROLL_POSITION ) );
341   constraint.AddSource( Source(scrollView, Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
342   constraint.AddSource( Source(scrollView, Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
343   constraint.AddSource( Source(scrollView, Actor::Property::SIZE ) );
344   constraint.AddSource( Source(scrollView, Toolkit::ScrollView::Property::WRAP ) );
345   constraint.SetRemoveAction( Constraint::Discard );
346   constraint.Apply();
347 }
348
349 } // unnamed namespace
350
351 namespace Dali
352 {
353
354 namespace Toolkit
355 {
356
357 namespace Internal
358 {
359
360 ScrollViewDepthEffect::ScrollViewDepthEffect()
361 {
362
363 }
364
365 ScrollViewDepthEffect::~ScrollViewDepthEffect()
366 {
367 }
368
369 void ScrollViewDepthEffect::ApplyToActor(Actor child,
370                                          const Vector2& positionExtent,
371                                          const Vector2& offsetExtent,
372                                          float positionScale,
373                                          float scaleExtent)
374 {
375   ApplyScrollDepthConstraints( GetScrollView(), child, positionExtent, offsetExtent, positionScale, scaleExtent );
376 }
377
378 void ScrollViewDepthEffect::OnAttach(Toolkit::ScrollView& scrollView)
379 {
380 }
381
382 void ScrollViewDepthEffect::OnDetach(Toolkit::ScrollView& scrollView)
383 {
384 }
385
386 } // namespace Internal
387
388 } // namespace Toolkit
389
390 } // namespace Dali