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