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 <boost/bind.hpp>
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>
37 namespace // unnamed namespace
40 const float PAGE_EPSILON_FACTOR( 0.25f );
42 const float PAGE_SIZE_RELATIVE_ANGLE_FACTOR( 0.75f );
43 const float NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR( 1.5f );
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 );
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 );
54 using namespace ScrollViewHelperFunctions;
57 * ScrollPageSpiralEffectInfo
59 * Rotate constraint: adjusts the angle of the page based on its position relative to the middle of
61 * When at middle of screen Angles on X and Y Axes is 0.
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%.
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.
72 class ScrollPageSpiralEffectInfo : public Dali::RefObject
76 ScrollPageSpiralEffectInfo( const Vector2& spiralAngle, bool scrollWrap )
77 : mSpiralAngle( spiralAngle ),
78 mScrollWrap( scrollWrap )
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.
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)
100 const Vector3& pagePosition = pagePositionProperty.GetVector3();
101 const Vector3& scrollPosition = scrollPositionProperty.GetVector3();
102 const Vector3& scrollStartPagePosition = scrollStartPagePositionProperty.GetVector3();
104 // Get position of page.
105 Vector3 position = pagePosition + scrollPosition;
107 // short circuit: if we're looking straight on at the page.
108 if( IsStraightOnView( position ) )
113 const Vector3& pageSize = pageSizeProperty.GetVector3();
114 const Vector3& minScrollPosition( scrollPositionMin.GetVector3() );
115 const Vector3& maxScrollPosition( scrollPositionMax.GetVector3() );
119 WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition );
122 // short circuit: for pages outside of view.
123 if( IsOutsideView( position, pageSize ) )
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 );
134 Vector2 angleMaxMin( mSpiralAngle );
137 if ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) // Did scroll start on this page?
139 angle.x = -angle.x * 0.9f;
143 // If not then multiply by angle factor.
144 angleMaxMin.x *= NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR;
146 ClampInPlace( angle.x, -angleMaxMin.x, angleMaxMin.x );
149 if ( fabsf( distanceFromScrollPage.y ) > epsilon.y ) // If not on the scroll page then multiply by angle factor.
151 angleMaxMin.y *= NON_SCROLL_PAGE_SPIRAL_ANGLE_FACTOR;
154 ClampInPlace( angle.y, -angleMaxMin.y, angleMaxMin.y );
156 Quaternion rotation = Quaternion( angle.x, Vector3::YAXIS ) *
157 Quaternion( angle.y, Vector3::XAXIS ) *
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.
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)
181 const Vector3& pagePosition = pagePositionProperty.GetVector3();
182 const Vector3& scrollPosition = scrollPositionProperty.GetVector3();
183 const Vector3& scrollStartPagePosition = scrollStartPagePositionProperty.GetVector3();
185 // Get position of page.
186 Vector3 position = pagePosition + scrollPosition;
188 // short circuit: if we're looking straight on at the page.
189 if( IsStraightOnView( position ) )
194 const Vector3& pageSize = pageSizeProperty.GetVector3();
195 const Vector3& minScrollPosition( scrollPositionMin.GetVector3() );
196 const Vector3& maxScrollPosition( scrollPositionMax.GetVector3() );
200 WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition );
203 // short circuit: for pages outside of view.
204 if( IsOutsideView( position, pageSize ) )
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);
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 );
219 float fullyOpaqueBefore( 0.0f );
220 float fullyTransparentAfter( 1.0f );
222 if ( ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) && ( fabsf( distanceFromScrollPage.y ) <= epsilon.y )) // Did scroll start on this page?
224 fullyOpaqueBefore = SCROLL_PAGE_OPAQUE_BEFORE;
225 fullyTransparentAfter = SCROLL_PAGE_FULLY_TRANSPARENT_AFTER;
229 fullyOpaqueBefore = NON_SCROLL_PAGE_OPAQUE_BEFORE;
230 fullyTransparentAfter = NON_SCROLL_PAGE_FULLY_TRANSPARENT_AFTER;
233 if ( distanceLength <= fullyOpaqueBefore )
237 else if ( distanceLength <= fullyTransparentAfter )
239 float opacity( distanceLength - fullyOpaqueBefore );
240 opacity /= fullyTransparentAfter - fullyOpaqueBefore;
241 color.a = Clamp( 1.0f - opacity, 0.0f, 1.0f );
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.
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)
269 const Vector3& pagePosition = pagePositionProperty.GetVector3();
270 const Vector3& scrollPosition = scrollPositionProperty.GetVector3();
271 const Vector3& scrollStartPagePosition = scrollStartPagePositionProperty.GetVector3();
273 // Get position of page.
274 Vector3 position = pagePosition + scrollPosition;
276 // short circuit: if we're looking straight on at the page.
277 if( IsStraightOnView( position ) )
279 return current + scrollPosition;
282 const Vector3& pageSize = pageSizeProperty.GetVector3();
283 const Vector3& minScrollPosition( scrollPositionMin.GetVector3() );
284 const Vector3& maxScrollPosition( scrollPositionMax.GetVector3() );
288 WrapPositionWithinDomain( position, pageSize, minScrollPosition, maxScrollPosition );
291 // short circuit: for pages outside of view.
292 if( IsOutsideView( position, pageSize ) )
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;
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 );
306 // X position and relative Z position
307 if ( fabsf( distanceFromScrollPage.x ) <= epsilon.x ) // Did scroll start on this page?
309 position.x = radius.x * sin( angle.x ) * 0.77f;
310 position.z = fabsf( position.x ) * SCROLL_PAGE_Z_POSITION_FACTOR;
314 position.x = radius.x * ( sinf( angle.x * Math::PI * 0.4f ) );
316 position.z = fabsf( position.x ) * NON_SCROLL_PAGE_Z_POSITION_FACTOR;
319 // Y position and relative Z position
320 if ( fabsf( distanceFromScrollPage.y ) <= epsilon.y ) // Did scroll start on this page?
322 position.y = radius.y * sin( angle.y ) * 0.77f;
323 position.z += fabsf( position.y ) * SCROLL_PAGE_Z_POSITION_FACTOR;
327 position.y = radius.y * ( sinf( angle.y * Math::PI * 0.4f ) );
329 position.z += fabsf( position.y ) * NON_SCROLL_PAGE_Z_POSITION_FACTOR;
335 Vector2 mSpiralAngle; ///< The angle of the spirald page
336 bool mScrollWrap; ///< Whether the scroll view wraps or not.
339 typedef IntrusivePtr<ScrollPageSpiralEffectInfo> ScrollPageSpiralEffectInfoPtr;
342 * Helper: Applies the 3D scroll cube constraints to the child actor
344 * @param[in] scrollView The ScrollView containing the pages.
345 * @param[in] child The child to be affected with the 3D Effect.
346 * @param[in] info The effect info for the constraints
348 void ApplyScrollCubeConstraints(Toolkit::ScrollView scrollView,
350 ScrollPageSpiralEffectInfoPtr info)
352 // Apply constraints to this actor //
353 Constraint constraint;
354 constraint = Constraint::New<Quaternion>( Actor::ROTATION,
355 LocalSource(Actor::POSITION),
356 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ),
357 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
358 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
359 Source(scrollView, Actor::SIZE ),
360 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME ) ),
361 boost::bind( &ScrollPageSpiralEffectInfo::RotationConstraint, info, _1, _2, _3, _4, _5, _6, _7) );
363 constraint.SetRemoveAction( Constraint::Discard );
364 child.ApplyConstraint( constraint );
366 constraint = Constraint::New<Vector4>( Actor::COLOR,
367 LocalSource(Actor::POSITION),
368 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ),
369 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
370 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
371 Source(scrollView, Actor::SIZE ),
372 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME ) ),
373 boost::bind( &ScrollPageSpiralEffectInfo::ColorConstraint, info, _1, _2, _3, _4, _5, _6, _7) );
375 constraint.SetRemoveAction( Constraint::Discard );
376 child.ApplyConstraint( constraint );
378 constraint = Constraint::New<Vector3>( Actor::POSITION,
379 LocalSource(Actor::POSITION),
380 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_FINAL_PROPERTY_NAME ) ),
381 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
382 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
383 Source(scrollView, Actor::SIZE ),
384 Source(scrollView, scrollView.GetPropertyIndex( Toolkit::ScrollView::SCROLL_START_PAGE_POSITION_PROPERTY_NAME ) ),
385 boost::bind( &ScrollPageSpiralEffectInfo::PositionConstraint, info, _1, _2, _3, _4, _5, _6, _7) );
387 constraint.SetRemoveAction( Constraint::Discard );
388 child.ApplyConstraint( constraint );
391 } // unnamed namespace
393 ScrollViewPageSpiralEffect::ScrollViewPageSpiralEffect()
398 ScrollViewPageSpiralEffect::~ScrollViewPageSpiralEffect()
402 void ScrollViewPageSpiralEffect::ApplyToPage( Actor page, const Vector2& spiralAngle )
404 Toolkit::ScrollView scrollView( GetScrollView() );
408 ScrollPageSpiralEffectInfoPtr info(new ScrollPageSpiralEffectInfo( spiralAngle, GetImpl( scrollView ).GetWrapMode() ));
409 ApplyScrollCubeConstraints( scrollView, page, info );
413 void ScrollViewPageSpiralEffect::OnAttach(Toolkit::ScrollView& scrollView)
417 void ScrollViewPageSpiralEffect::OnDetach(Toolkit::ScrollView& scrollView)
421 } // namespace Internal
423 } // namespace Toolkit