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