2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-page-spiral-effect-impl.h>
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>
34 namespace // unnamed namespace
37 const float PAGE_EPSILON_FACTOR( 0.25f );
39 const float PAGE_SIZE_RELATIVE_ANGLE_FACTOR( 0.75f );
40 const float NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR( 1.5f );
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 );
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 );
51 using namespace ScrollViewHelperFunctions;
54 * ScrollPageSpiralEffectInfo
56 * Rotate constraint: adjusts the angle of the page based on its position relative to the middle of
58 * When at middle of screen Angles on X and Y Axes is 0.
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%.
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.
69 class ScrollPageSpiralEffectInfo
73 ScrollPageSpiralEffectInfo( const Vector2& spiralAngle, bool scrollWrap )
74 : mSpiralAngle( spiralAngle ),
75 mScrollWrap( scrollWrap )
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.
90 void RotationConstraint( Quaternion& current, const PropertyInputContainer& inputs )
92 const Vector3& pagePosition = inputs[0]->GetVector3();
93 const Vector3& scrollPosition = inputs[1]->GetVector3();
94 const Vector3& scrollStartPagePosition = inputs[5]->GetVector3();
96 // Get position of page.
97 Vector3 position = pagePosition + scrollPosition;
99 // short circuit: if we're looking straight on at the page.
100 if( IsStraightOnView( position ) )
105 const Vector3& pageSize = inputs[4]->GetVector3();
106 const Vector3& minScrollPosition( inputs[2]->GetVector3() );
107 const Vector3& maxScrollPosition( inputs[3]->GetVector3() );
111 WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition );
114 // short circuit: for pages outside of view.
115 if( IsOutsideView( position, pageSize ) )
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 );
126 Vector2 angleMaxMin( mSpiralAngle );
129 if ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) // Did scroll start on this page?
131 angle.x = -angle.x * 0.9f;
135 // If not then multiply by angle factor.
136 angleMaxMin.x *= NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR;
138 ClampInPlace( angle.x, -angleMaxMin.x, angleMaxMin.x );
141 if ( fabsf( distanceFromScrollPage.y ) > epsilon.y ) // If not on the scroll page then multiply by angle factor.
143 angleMaxMin.y *= NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR;
146 ClampInPlace( angle.y, -angleMaxMin.y, angleMaxMin.y );
148 current = Quaternion( angle.x, Vector3::YAXIS ) *
149 Quaternion( angle.y, Vector3::XAXIS ) *
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.
164 void ColorConstraint( Vector4& color, const PropertyInputContainer& inputs )
166 const Vector3& pagePosition = inputs[0]->GetVector3();
167 const Vector3& scrollPosition = inputs[1]->GetVector3();
168 const Vector3& scrollStartPagePosition = inputs[5]->GetVector3();
170 // Get position of page.
171 Vector3 position = pagePosition + scrollPosition;
173 // short circuit: if we're looking straight on at the page.
174 if( IsStraightOnView( position ) )
179 const Vector3& pageSize = inputs[4]->GetVector3();
180 const Vector3& minScrollPosition( inputs[2]->GetVector3() );
181 const Vector3& maxScrollPosition( inputs[3]->GetVector3() );
185 WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition );
188 // short circuit: for pages outside of view.
189 if( IsOutsideView( position, pageSize ) )
191 // note preserve color channels incase there is a shader/further constraint
192 // that wishes to do something with that information.
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 );
204 float fullyOpaqueBefore( 0.0f );
205 float fullyTransparentAfter( 1.0f );
207 if ( ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) && ( fabsf( distanceFromScrollPage.y ) <= epsilon.y )) // Did scroll start on this page?
209 fullyOpaqueBefore = SCROLL_PAGE_OPAQUE_BEFORE;
210 fullyTransparentAfter = SCROLL_PAGE_FULLY_TRANSPARENT_AFTER;
214 fullyOpaqueBefore = NON_SCROLL_PAGE_OPAQUE_BEFORE;
215 fullyTransparentAfter = NON_SCROLL_PAGE_FULLY_TRANSPARENT_AFTER;
218 if ( distanceLength <= fullyOpaqueBefore )
222 else if ( distanceLength <= fullyTransparentAfter )
224 float opacity( distanceLength - fullyOpaqueBefore );
225 opacity /= fullyTransparentAfter - fullyOpaqueBefore;
226 color.a = Clamp( 1.0f - opacity, 0.0f, 1.0f );
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.
245 void PositionConstraint( Vector3& current, const PropertyInputContainer& inputs )
247 const Vector3& pagePosition = inputs[0]->GetVector3();
248 const Vector3& scrollPosition = inputs[1]->GetVector3();
249 const Vector3& scrollStartPagePosition = inputs[5]->GetVector3();
251 // Get position of page.
252 Vector3 position = pagePosition + scrollPosition;
254 // short circuit: if we're looking straight on at the page.
255 if( IsStraightOnView( position ) )
257 current += scrollPosition;
261 const Vector3& pageSize = inputs[4]->GetVector3();
262 const Vector3& minScrollPosition( inputs[2]->GetVector3() );
263 const Vector3& maxScrollPosition( inputs[3]->GetVector3() );
267 WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition );
270 // short circuit: for pages outside of view.
271 if( IsOutsideView( position, pageSize ) )
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;
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 );
286 // X position and relative Z position
287 if ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) // Did scroll start on this page?
289 position.x = radius.x * sin( angle.x ) * 0.77f;
290 position.z = fabsf( position.x ) * SCROLL_PAGE_Z_POSITION_FACTOR;
294 position.x = radius.x * ( sinf( angle.x * Math::PI * 0.4f ) );
296 position.z = fabsf( position.x ) * NON_SCROLL_PAGE_Z_POSITION_FACTOR;
299 // Y position and relative Z position
300 if ( fabsf( distanceFromScrollPage.y ) <= epsilon.y ) // Did scroll start on this page?
302 position.y = radius.y * sin( angle.y ) * 0.77f;
303 position.z += fabsf( position.y ) * SCROLL_PAGE_Z_POSITION_FACTOR;
307 position.y = radius.y * ( sinf( angle.y * Math::PI * 0.4f ) );
309 position.z += fabsf( position.y ) * NON_SCROLL_PAGE_Z_POSITION_FACTOR;
315 Vector2 mSpiralAngle; ///< The angle of the spirald page
316 bool mScrollWrap; ///< Whether the scroll view wraps or not.
320 * Helper: Applies the 3D scroll cube constraints to the child actor
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
326 void ApplyScrollCubeConstraints(Toolkit::ScrollView scrollView,
328 ScrollPageSpiralEffectInfo& info)
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 );
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 );
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 );
363 } // unnamed namespace
365 ScrollViewPageSpiralEffect::ScrollViewPageSpiralEffect()
370 ScrollViewPageSpiralEffect::~ScrollViewPageSpiralEffect()
374 void ScrollViewPageSpiralEffect::ApplyToPage( Actor page, const Vector2& spiralAngle )
376 Toolkit::ScrollView scrollView( GetScrollView() );
380 ScrollPageSpiralEffectInfo info( spiralAngle, GetImpl( scrollView ).GetWrapMode() );
381 ApplyScrollCubeConstraints( scrollView, page, info );
385 void ScrollViewPageSpiralEffect::OnAttach(Toolkit::ScrollView& scrollView)
389 void ScrollViewPageSpiralEffect::OnDetach(Toolkit::ScrollView& scrollView)
393 } // namespace Internal
395 } // namespace Toolkit