Split dali-toolkit into Base & Optional
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / scrollable / scroll-view / scroll-view-page-spiral-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 // CLASS HEADER
18 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-spiral-effect-impl.h>
19
20 // EXTERNAL INCLUDES
21 #include <boost/bind.hpp>
22
23 // INTERNAL INCLUDES
24 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-helper-functions.h>
25 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.h>
26
27 namespace Dali
28 {
29
30 namespace Toolkit
31 {
32
33 namespace Internal
34 {
35
36 namespace // unnamed namespace
37 {
38
39 const float PAGE_EPSILON_FACTOR( 0.25f );
40
41 const float PAGE_SIZE_RELATIVE_ANGLE_FACTOR( 0.75f );
42 const float NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR( 1.5f );
43
44 const float SCROLL_PAGE_OPAQUE_BEFORE( 0.4f );
45 const float SCROLL_PAGE_FULLY_TRANSPARENT_AFTER( 0.9f );
46 const float NON_SCROLL_PAGE_OPAQUE_BEFORE( 0.8f );
47 const float NON_SCROLL_PAGE_FULLY_TRANSPARENT_AFTER( 1.0f );
48
49 const float RADIUS_FACTOR( 0.95f );
50 const float SCROLL_PAGE_Z_POSITION_FACTOR( -2.0f );
51 const float NON_SCROLL_PAGE_Z_POSITION_FACTOR( -0.75f );
52
53 using namespace ScrollViewHelperFunctions;
54
55 /**
56  * ScrollPageSpiralEffectInfo
57  *
58  * Rotate constraint: adjusts the angle of the page based on its position relative to the middle of
59  * the screen.
60  * When at middle of screen Angles on X and Y Axes is 0.
61  *
62  * Color constraint: adjusts the alpha of the page based on their parent page's position relative
63  * to the middle of the screen.
64  * When at middle of screen Alpha is 100% opacity.
65  * When outside the viewable area, the opacity is 0%.
66  *
67  * Position constraint: adjusts the position of the page based on their parent page's position
68  * relative to the middle of the screen.
69  * When at middle of the screen the position is not altered.
70  */
71 class ScrollPageSpiralEffectInfo : public Dali::RefObject
72 {
73 public:
74
75   ScrollPageSpiralEffectInfo( const Vector2& spiralAngle, bool scrollWrap )
76   : mSpiralAngle( spiralAngle ),
77     mScrollWrap( scrollWrap )
78   {
79   }
80
81   /**
82    * @param[in] current The current orientation of this Actor
83    * @param[in] pagePositionProperty The page's position.
84    * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
85    * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME)
86    * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME)
87    * @param[in] pageSizeProperty The size of the page. (scrollView SIZE)
88    * @param[in] scrollPageStartPositionProperty The position of the page where scrolling started. (SCROLL_START_PAGE_POSITION_PROPERTY_NAME)
89    * @return The new orientation of this Actor.
90    */
91   Quaternion RotationConstraint(const Quaternion& current,
92                                 const PropertyInput& pagePositionProperty,
93                                 const PropertyInput& scrollPositionProperty,
94                                 const PropertyInput& scrollPositionMin,
95                                 const PropertyInput& scrollPositionMax,
96                                 const PropertyInput& pageSizeProperty,
97                                 const PropertyInput& scrollStartPagePositionProperty)
98   {
99     const Vector3& pagePosition = pagePositionProperty.GetVector3();
100     const Vector3& scrollPosition = scrollPositionProperty.GetVector3();
101     const Vector3& scrollStartPagePosition = scrollStartPagePositionProperty.GetVector3();
102
103     // Get position of page.
104     Vector3 position = pagePosition + scrollPosition;
105
106     // short circuit: if we're looking straight on at the page.
107     if( IsStraightOnView( position ) )
108     {
109       return current;
110     }
111
112     const Vector3& pageSize = pageSizeProperty.GetVector3();
113     const Vector3& minScrollPosition( scrollPositionMin.GetVector3() );
114     const Vector3& maxScrollPosition( scrollPositionMax.GetVector3() );
115
116     if( mScrollWrap )
117     {
118       WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition );
119     }
120
121     // short circuit: for pages outside of view.
122     if( IsOutsideView( position, pageSize ) )
123     {
124       return current;
125     }
126
127     Vector2 angle( position / ( pageSize * PAGE_SIZE_RELATIVE_ANGLE_FACTOR ) * Vector3( mSpiralAngle ) );
128     const Vector2 epsilon( pageSize * PAGE_EPSILON_FACTOR );
129     Vector2 distanceFromScrollPage;
130     distanceFromScrollPage.x = ShortestDistanceInDomain( scrollStartPagePosition.x, pagePosition.x, minScrollPosition.x, maxScrollPosition.x );
131     distanceFromScrollPage.y = ShortestDistanceInDomain( scrollStartPagePosition.y, pagePosition.y, minScrollPosition.y, maxScrollPosition.y );
132
133     Vector2 angleMaxMin( mSpiralAngle );
134
135     // X rotation
136     if ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) // Did scroll start on this page?
137     {
138       angle.x = -angle.x * 0.9f;
139     }
140     else
141     {
142       // If not then multiply by angle factor.
143       angleMaxMin.x *= NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR;
144     }
145     ClampInPlace( angle.x, -angleMaxMin.x, angleMaxMin.x );
146
147     // Y rotation
148     if ( fabsf( distanceFromScrollPage.y ) > epsilon.y ) // If not on the scroll page then multiply by angle factor.
149     {
150       angleMaxMin.y *= NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR;
151       angle.y = -angle.y;
152     }
153     ClampInPlace( angle.y, -angleMaxMin.y, angleMaxMin.y );
154
155     Quaternion rotation = Quaternion( angle.x, Vector3::YAXIS ) *
156                           Quaternion( angle.y, Vector3::XAXIS ) *
157                           current;
158
159     return rotation;
160   }
161
162   /**
163    * @param[in] current The current color of this Actor
164    * @param[in] pagePositionProperty The page's position.
165    * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
166    * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME)
167    * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME)
168    * @param[in] pageSizeProperty The size of the page. (scrollView SIZE)
169    * @param[in] scrollPageStartPositionProperty The position of the page where scrolling started. (SCROLL_START_PAGE_POSITION_PROPERTY_NAME)
170    * @return The new color of this Actor.
171    */
172   Vector4 ColorConstraint(const Vector4& current,
173                           const PropertyInput& pagePositionProperty,
174                           const PropertyInput& scrollPositionProperty,
175                           const PropertyInput& scrollPositionMin,
176                           const PropertyInput& scrollPositionMax,
177                           const PropertyInput& pageSizeProperty,
178                           const PropertyInput& scrollStartPagePositionProperty)
179   {
180     const Vector3& pagePosition = pagePositionProperty.GetVector3();
181     const Vector3& scrollPosition = scrollPositionProperty.GetVector3();
182     const Vector3& scrollStartPagePosition = scrollStartPagePositionProperty.GetVector3();
183
184     // Get position of page.
185     Vector3 position = pagePosition + scrollPosition;
186
187     // short circuit: if we're looking straight on at the page.
188     if( IsStraightOnView( position ) )
189     {
190       return current;
191     }
192
193     const Vector3& pageSize = pageSizeProperty.GetVector3();
194     const Vector3& minScrollPosition( scrollPositionMin.GetVector3() );
195     const Vector3& maxScrollPosition( scrollPositionMax.GetVector3() );
196
197     if( mScrollWrap )
198     {
199       WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition );
200     }
201
202     // short circuit: for pages outside of view.
203     if( IsOutsideView( position, pageSize ) )
204     {
205       // note preserve color channels incase there is a shader/further constraint
206       // that wishes to do something with that information.
207       return Vector4(current.r, current.g, current.b, 0.0f);
208     }
209
210     Vector4 color( current );
211     Vector2 distance( position / pageSize );
212     float distanceLength( distance.Length() );
213     const Vector2 epsilon( pageSize * PAGE_EPSILON_FACTOR );
214     Vector2 distanceFromScrollPage;
215     distanceFromScrollPage.x = ShortestDistanceInDomain( scrollStartPagePosition.x, pagePosition.x, minScrollPosition.x, maxScrollPosition.x );
216     distanceFromScrollPage.y = ShortestDistanceInDomain( scrollStartPagePosition.y, pagePosition.y, minScrollPosition.y, maxScrollPosition.y );
217
218     float fullyOpaqueBefore( 0.0f );
219     float fullyTransparentAfter( 1.0f );
220
221     if ( ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) && ( fabsf( distanceFromScrollPage.y ) <= epsilon.y )) // Did scroll start on this page?
222     {
223       fullyOpaqueBefore = SCROLL_PAGE_OPAQUE_BEFORE;
224       fullyTransparentAfter = SCROLL_PAGE_FULLY_TRANSPARENT_AFTER;
225     }
226     else
227     {
228       fullyOpaqueBefore = NON_SCROLL_PAGE_OPAQUE_BEFORE;
229       fullyTransparentAfter = NON_SCROLL_PAGE_FULLY_TRANSPARENT_AFTER;
230     }
231
232     if ( distanceLength <= fullyOpaqueBefore )
233     {
234       color.a = 1.0f;
235     }
236     else if ( distanceLength <= fullyTransparentAfter )
237     {
238       float opacity( distanceLength - fullyOpaqueBefore );
239       opacity /= fullyTransparentAfter - fullyOpaqueBefore;
240       color.a = Clamp( 1.0f - opacity, 0.0f, 1.0f );
241     }
242     else
243     {
244       color.a = 0.0f;
245     }
246
247     return color;
248   }
249
250   /**
251    * @param[in] current The current position
252    * @param[in] pagePositionProperty The page's position.
253    * @param[in] scrollPositionProperty The scroll-view's position property (SCROLL_POSITION_PROPERTY_NAME)
254    * @param[in] scrollPositionMin The minimum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME)
255    * @param[in] scrollPositionMax The maximum extent of this scroll domain. (SCROLL_POSITION_MIN_PROPERTY_NAME)
256    * @param[in] pageSizeProperty The size of the page. (scrollView SIZE)
257    * @param[in] scrollPageStartPositionProperty The position of the page where scrolling started. (SCROLL_START_PAGE_POSITION_PROPERTY_NAME)
258    * @return The new position of this Actor.
259    */
260   Vector3 PositionConstraint(const Vector3& current,
261                              const PropertyInput& pagePositionProperty,
262                              const PropertyInput& scrollPositionProperty,
263                              const PropertyInput& scrollPositionMin,
264                              const PropertyInput& scrollPositionMax,
265                              const PropertyInput& pageSizeProperty,
266                              const PropertyInput& scrollStartPagePositionProperty)
267   {
268     const Vector3& pagePosition = pagePositionProperty.GetVector3();
269     const Vector3& scrollPosition = scrollPositionProperty.GetVector3();
270     const Vector3& scrollStartPagePosition = scrollStartPagePositionProperty.GetVector3();
271
272     // Get position of page.
273     Vector3 position = pagePosition + scrollPosition;
274
275     // short circuit: if we're looking straight on at the page.
276     if( IsStraightOnView( position ) )
277     {
278       return current + scrollPosition;
279     }
280
281     const Vector3& pageSize = pageSizeProperty.GetVector3();
282     const Vector3& minScrollPosition( scrollPositionMin.GetVector3() );
283     const Vector3& maxScrollPosition( scrollPositionMax.GetVector3() );
284
285     if( mScrollWrap )
286     {
287       WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition );
288     }
289
290     // short circuit: for pages outside of view.
291     if( IsOutsideView( position, pageSize ) )
292     {
293       // position actors at: scrollposition (Property) + pagePosition (Parent) + current (this)
294       // they will be invisible so doesn't have to be precise, just away from stage.
295       return current + scrollPosition;
296     }
297
298     const Vector2 angle( position / pageSize * ( Dali::Math::PI_4 ) );
299     const Vector2 radius( pageSize * RADIUS_FACTOR );
300     const Vector2 epsilon( pageSize * PAGE_EPSILON_FACTOR  );
301     Vector2 distanceFromScrollPage;
302     distanceFromScrollPage.x = ShortestDistanceInDomain( scrollStartPagePosition.x, pagePosition.x, minScrollPosition.x, maxScrollPosition.x );
303     distanceFromScrollPage.y = ShortestDistanceInDomain( scrollStartPagePosition.y, pagePosition.y, minScrollPosition.y, maxScrollPosition.y );
304
305     // X position and relative Z position
306     if ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) // Did scroll start on this page?
307     {
308       position.x = radius.x * sin( angle.x ) * 0.77f;
309       position.z = fabsf( position.x ) * SCROLL_PAGE_Z_POSITION_FACTOR;
310     }
311     else
312     {
313       position.x = radius.x * ( sinf( angle.x * Math::PI * 0.4f ) );
314
315       position.z = fabsf( position.x ) * NON_SCROLL_PAGE_Z_POSITION_FACTOR;
316     }
317
318     // Y position and relative Z position
319     if ( fabsf( distanceFromScrollPage.y ) <= epsilon.y ) // Did scroll start on this page?
320     {
321       position.y = radius.y * sin( angle.y ) * 0.77f;
322       position.z += fabsf( position.y ) * SCROLL_PAGE_Z_POSITION_FACTOR;
323     }
324     else
325     {
326       position.y = radius.y * ( sinf( angle.y * Math::PI * 0.4f ) );
327
328       position.z += fabsf( position.y ) * NON_SCROLL_PAGE_Z_POSITION_FACTOR;
329     }
330
331     return position;
332   }
333
334   Vector2 mSpiralAngle; ///< The angle of the spirald page
335   bool mScrollWrap;      ///< Whether the scroll view wraps or not.
336 };
337
338 typedef IntrusivePtr<ScrollPageSpiralEffectInfo> ScrollPageSpiralEffectInfoPtr;
339
340 /**
341  * Helper: Applies the 3D scroll cube constraints to the child actor
342  *
343  * @param[in] scrollView The ScrollView containing the pages.
344  * @param[in] child The child to be affected with the 3D Effect.
345  * @param[in] info The effect info for the constraints
346  */
347 void ApplyScrollCubeConstraints(Toolkit::ScrollView scrollView,
348                                 Actor child,
349                                 ScrollPageSpiralEffectInfoPtr info)
350 {
351   // Apply constraints to this actor //
352   Constraint constraint;
353   constraint = Constraint::New<Quaternion>( Actor::ROTATION,
354                                             LocalSource(Actor::POSITION),
355                                             Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ),
356                                             Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
357                                             Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
358                                             Source(scrollView, Actor::SIZE ),
359                                             Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME ) ),
360                                             boost::bind( &ScrollPageSpiralEffectInfo::RotationConstraint, info, _1, _2, _3, _4, _5, _6, _7) );
361
362   constraint.SetRemoveAction( Constraint::Discard );
363   child.ApplyConstraint( constraint );
364
365   constraint = Constraint::New<Vector4>( Actor::COLOR,
366                                          LocalSource(Actor::POSITION),
367                                          Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ),
368                                          Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
369                                          Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
370                                          Source(scrollView, Actor::SIZE ),
371                                          Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME ) ),
372                                          boost::bind( &ScrollPageSpiralEffectInfo::ColorConstraint, info, _1, _2, _3, _4, _5, _6, _7) );
373
374   constraint.SetRemoveAction( Constraint::Discard );
375   child.ApplyConstraint( constraint );
376
377   constraint = Constraint::New<Vector3>( Actor::POSITION,
378                                          LocalSource(Actor::POSITION),
379                                          Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ),
380                                          Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
381                                          Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
382                                          Source(scrollView, Actor::SIZE ),
383                                          Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME ) ),
384                                          boost::bind( &ScrollPageSpiralEffectInfo::PositionConstraint, info, _1, _2, _3, _4, _5, _6, _7) );
385
386   constraint.SetRemoveAction( Constraint::Discard );
387   child.ApplyConstraint( constraint );
388 }
389
390 } // unnamed namespace
391
392 ScrollViewPageSpiralEffect::ScrollViewPageSpiralEffect()
393 {
394
395 }
396
397 ScrollViewPageSpiralEffect::~ScrollViewPageSpiralEffect()
398 {
399 }
400
401 void ScrollViewPageSpiralEffect::ApplyToPage( Actor page, const Vector2& spiralAngle )
402 {
403   Toolkit::ScrollView scrollView( GetScrollView() );
404
405   if ( scrollView )
406   {
407     ScrollPageSpiralEffectInfoPtr info(new ScrollPageSpiralEffectInfo( spiralAngle, GetImpl( scrollView ).GetWrapMode() ));
408     ApplyScrollCubeConstraints( scrollView, page, info );
409   }
410 }
411
412 void ScrollViewPageSpiralEffect::OnAttach(Toolkit::ScrollView& scrollView)
413 {
414 }
415
416 void ScrollViewPageSpiralEffect::OnDetach(Toolkit::ScrollView& scrollView)
417 {
418 }
419
420 } // namespace Internal
421
422 } // namespace Toolkit
423
424 } // namespace Dali