2 * Copyright (c) 2015 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/item-view/spiral-layout.h>
23 #include <dali/public-api/animation/animation.h>
24 #include <dali/public-api/animation/constraint.h>
27 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-view.h>
30 using namespace Dali::Toolkit;
32 namespace // unnamed namespace
35 const float DEFAULT_ITEMS_PER_SPIRAL_TURN = 9.5f;
36 const float DEFAULT_ITEM_SPACING_RADIANS = Math::PI*2.0f/DEFAULT_ITEMS_PER_SPIRAL_TURN;
38 const float DEFAULT_REVOLUTION_DISTANCE = 190.0f;
39 const float DEFAULT_ITEM_DESCENT = DEFAULT_REVOLUTION_DISTANCE / DEFAULT_ITEMS_PER_SPIRAL_TURN;
41 const float DEFAULT_TOP_ITEM_ALIGNMENT = -0.125f;
43 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.01f;
44 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 30.0f;
45 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.1f;
47 float GetDefaultSpiralRadiusFunction(const Vector3& layoutSize)
49 return layoutSize.width*0.4f;
52 struct SpiralPositionConstraint
54 SpiralPositionConstraint( unsigned int itemId, float spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment )
56 mSpiralRadius( spiralRadius ),
57 mItemSpacingRadians( itemSpacingRadians ),
58 mItemDescent( itemDescent ),
59 mTopItemAlignment( topItemAlignment )
63 inline void OrientationUp( Vector3& current, float layoutPosition, const Vector3& layoutSize )
65 float angle = -Math::PI * 0.5f + mItemSpacingRadians * layoutPosition;
67 current.x = -mSpiralRadius * cosf( angle );
68 current.y = ( mItemDescent * layoutPosition ) + layoutSize.height * mTopItemAlignment;
69 current.z = -mSpiralRadius * sinf( angle );
72 inline void OrientationLeft( Vector3& current, float layoutPosition, const Vector3& layoutSize )
74 float angle = Math::PI * 0.5f + mItemSpacingRadians * layoutPosition;
76 current.x = ( mItemDescent * layoutPosition ) + layoutSize.width * mTopItemAlignment;
77 current.y = -mSpiralRadius * cosf( angle );
78 current.z = mSpiralRadius * sinf( angle );
81 inline void OrientationDown( Vector3& current, float layoutPosition, const Vector3& layoutSize )
83 float angle = Math::PI * 0.5f + mItemSpacingRadians * layoutPosition;
85 current.x = -mSpiralRadius * cosf( angle );
86 current.y = ( -mItemDescent * layoutPosition ) - layoutSize.height * mTopItemAlignment;
87 current.z = mSpiralRadius * sinf(angle);
90 inline void OrientationRight( Vector3& current, float layoutPosition, const Vector3& layoutSize )
92 float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
94 current.x = (-mItemDescent * layoutPosition) - layoutSize.width * mTopItemAlignment;
95 current.y = -mSpiralRadius * cosf( angle );
96 current.z = -mSpiralRadius * sinf( angle );
99 void OrientationUp( Vector3& current, const PropertyInputContainer& inputs )
101 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
102 const Vector3& layoutSize = inputs[1]->GetVector3();
103 OrientationUp( current, layoutPosition, layoutSize );
106 void OrientationLeft( Vector3& current, const PropertyInputContainer& inputs )
108 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
109 const Vector3& layoutSize = inputs[1]->GetVector3();
110 OrientationLeft( current, layoutPosition, layoutSize );
113 void OrientationDown( Vector3& current, const PropertyInputContainer& inputs )
115 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
116 const Vector3& layoutSize = inputs[1]->GetVector3();
117 OrientationDown( current, layoutPosition, layoutSize );
120 void OrientationRight( Vector3& current, const PropertyInputContainer& inputs )
122 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
123 const Vector3& layoutSize = inputs[1]->GetVector3();
124 OrientationRight( current, layoutPosition, layoutSize );
127 unsigned int mItemId;
129 float mItemSpacingRadians;
131 float mTopItemAlignment;
134 struct SpiralRotationConstraint
136 SpiralRotationConstraint( unsigned int itemId, float itemSpacingRadians )
138 mItemSpacingRadians( itemSpacingRadians )
142 void OrientationUp( Quaternion& current, const PropertyInputContainer& inputs )
144 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
145 float angle = -mItemSpacingRadians * layoutPosition;
147 current = Quaternion( Radian( angle ), Vector3::YAXIS);
150 void OrientationLeft( Quaternion& current, const PropertyInputContainer& inputs )
152 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
153 float angle = -mItemSpacingRadians * layoutPosition;
155 current = Quaternion( Radian( -Math::PI * 0.5f ), Vector3::ZAXIS ) * Quaternion( Radian( angle ), Vector3::YAXIS );
158 void OrientationDown( Quaternion& current, const PropertyInputContainer& inputs )
160 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
161 float angle = -mItemSpacingRadians * layoutPosition;
163 current = Quaternion( Radian( -Math::PI ), Vector3::ZAXIS) * Quaternion( Radian( angle ), Vector3::YAXIS );
166 void OrientationRight( Quaternion& current, const PropertyInputContainer& inputs )
168 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
169 float angle = -mItemSpacingRadians * layoutPosition;
171 current = Quaternion( Radian( -Math::PI * 1.5f ), Vector3::ZAXIS) * Quaternion( Radian( angle ), Vector3::YAXIS );
174 unsigned int mItemId;
175 float mItemSpacingRadians;
178 struct SpiralColorConstraint
180 SpiralColorConstraint( unsigned int itemId, float itemSpacingRadians )
182 mItemSpacingRadians( itemSpacingRadians )
186 void operator()( Vector4& current, const PropertyInputContainer& inputs )
188 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
189 Radian angle( mItemSpacingRadians * fabsf( layoutPosition ) / Dali::ANGLE_360 );
191 float progress = angle - floorf( angle ); // take fractional bit only to get between 0.0 - 1.0
192 progress = (progress > 0.5f) ? 2.0f*(1.0f - progress) : progress*2.0f;
194 float darkness(1.0f);
196 const float startMarker = 0.10f; // The progress at which darkening starts
197 const float endMarker = 0.35f; // The progress at which darkening ends
198 const float minDarkness = 0.15f; // The darkness at end marker
200 if (progress > endMarker)
202 darkness = minDarkness;
204 else if (progress > startMarker)
206 darkness = 1.0f - ( (1.0f - minDarkness) * ((progress-startMarker) / (endMarker-startMarker)) );
210 current.r = current.g = current.b = darkness;
213 unsigned int mItemId;
214 float mItemSpacingRadians;
217 struct SpiralVisibilityConstraint
219 SpiralVisibilityConstraint( unsigned int itemId, float itemSpacingRadians, float itemDescent, float topItemAlignment )
221 mItemSpacingRadians( itemSpacingRadians ),
222 mItemDescent( itemDescent ),
223 mTopItemAlignment( topItemAlignment )
227 void Portrait( bool& current, const PropertyInputContainer& inputs )
229 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
230 const Vector3& layoutSize = inputs[1]->GetVector3();
231 float itemsCachedBeforeTopItem = layoutSize.height*(mTopItemAlignment+0.5f) / mItemDescent;
232 current = ( layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= ( layoutSize.height / mItemDescent ) + 1.0f );
235 void Landscape( bool& current, const PropertyInputContainer& inputs )
237 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
238 const Vector3& layoutSize = inputs[1]->GetVector3();
239 float itemsCachedBeforeTopItem = layoutSize.width*(mTopItemAlignment+0.5f) / mItemDescent;
240 current = ( layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= ( layoutSize.width / mItemDescent ) + 1.0f );
243 unsigned int mItemId;
244 float mItemSpacingRadians;
246 float mTopItemAlignment;
249 } // unnamed namespace
260 struct SpiralLayout::Impl
263 : mItemSpacingRadians(DEFAULT_ITEM_SPACING_RADIANS),
264 mRevolutionDistance(DEFAULT_REVOLUTION_DISTANCE),
265 mItemDescent(DEFAULT_ITEM_DESCENT),
266 mTopItemAlignment(DEFAULT_TOP_ITEM_ALIGNMENT),
267 mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
268 mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
269 mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION)
273 float mItemSpacingRadians;
274 float mRevolutionDistance;
276 float mTopItemAlignment;
277 float mScrollSpeedFactor;
278 float mMaximumSwipeSpeed;
279 float mItemFlickAnimationDuration;
282 SpiralLayoutPtr SpiralLayout::New()
284 return SpiralLayoutPtr(new SpiralLayout());
287 SpiralLayout::~SpiralLayout()
292 void SpiralLayout::SetItemSpacing(Radian itemSpacing)
294 mImpl->mItemSpacingRadians = itemSpacing;
296 float itemsPerSpiral = std::max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
297 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
300 Radian SpiralLayout::GetItemSpacing() const
302 return Radian( mImpl->mItemSpacingRadians );
305 void SpiralLayout::SetRevolutionDistance(float distance)
307 mImpl->mRevolutionDistance = distance;
309 float itemsPerSpiral = std::max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
310 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
313 float SpiralLayout::GetRevolutionDistance() const
315 return mImpl->mRevolutionDistance;
318 void SpiralLayout::SetTopItemAlignment(float alignment)
320 mImpl->mTopItemAlignment = alignment;
323 float SpiralLayout::GetTopItemAlignment() const
325 return mImpl->mTopItemAlignment;
328 void SpiralLayout::SetScrollSpeedFactor(float scrollSpeed)
330 mImpl->mScrollSpeedFactor = scrollSpeed;
333 void SpiralLayout::SetMaximumSwipeSpeed(float speed)
335 mImpl->mMaximumSwipeSpeed = speed;
338 void SpiralLayout::SetItemFlickAnimationDuration(float durationSeconds)
340 mImpl->mItemFlickAnimationDuration = durationSeconds;
343 float SpiralLayout::GetScrollSpeedFactor() const
345 return mImpl->mScrollSpeedFactor;
348 float SpiralLayout::GetMaximumSwipeSpeed() const
350 return mImpl->mMaximumSwipeSpeed;
353 float SpiralLayout::GetItemFlickAnimationDuration() const
355 return mImpl->mItemFlickAnimationDuration;
358 float SpiralLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
360 return 1.0f - static_cast<float>(numberOfItems);
363 float SpiralLayout::GetClosestAnchorPosition(float layoutPosition) const
365 return round(layoutPosition);
368 float SpiralLayout::GetItemScrollToPosition(unsigned int itemId) const
370 return -(static_cast<float>(itemId));
373 ItemRange SpiralLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
375 float layoutHeight = IsHorizontal( GetOrientation() ) ? layoutSize.width : layoutSize.height;
376 float itemsPerSpiral = layoutHeight / mImpl->mItemDescent;
377 float itemsCachedBeforeTopItem = layoutHeight * (mImpl->mTopItemAlignment + 0.5f) / mImpl->mItemDescent;
378 float itemsViewable = std::min(itemsPerSpiral, itemsPerSpiral - itemsCachedBeforeTopItem - firstItemPosition + 1.0f);
380 unsigned int firstItem = static_cast<unsigned int>(std::max(0.0f, -firstItemPosition - itemsCachedBeforeTopItem - 1.0f));
381 unsigned int lastItem = static_cast<unsigned int>(std::max(0.0f, firstItem + itemsViewable));
383 return ItemRange(firstItem, lastItem+1);
386 unsigned int SpiralLayout::GetReserveItemCount(Vector3 layoutSize) const
388 float layoutHeight = IsHorizontal( GetOrientation() ) ? layoutSize.width : layoutSize.height;
389 return static_cast<unsigned int>(layoutHeight / mImpl->mItemDescent);
392 void SpiralLayout::GetDefaultItemSize( unsigned int itemId, const Vector3& layoutSize, Vector3& itemSize ) const
394 itemSize.width = layoutSize.width * 0.25f;
397 itemSize.height = itemSize.depth = ( itemSize.width / 4.0f ) * 3.0f;
400 Degree SpiralLayout::GetScrollDirection() const
402 Degree scrollDirection(0);
403 const ControlOrientation::Type orientation = GetOrientation();
405 if ( orientation == ControlOrientation::Up )
407 scrollDirection = Degree( -45.0f ); // Allow swiping horizontally & vertically
409 else if ( orientation == ControlOrientation::Left )
411 scrollDirection = Degree( 45.0f );
413 else if ( orientation == ControlOrientation::Down )
415 scrollDirection = Degree( 180.0f - 45.0f );
417 else // orientation == ControlOrientation::Right
419 scrollDirection = Degree( 270.0f - 45.0f );
422 return scrollDirection;
425 void SpiralLayout::ApplyConstraints( Actor& actor, const int itemId, const Vector3& layoutSize, const Actor& itemViewActor )
427 // This just implements the default behaviour of constraint application.
428 // Custom layouts can override this function to apply their custom constraints.
429 Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast( itemViewActor );
432 const ControlOrientation::Type orientation = GetOrientation();
434 // Position constraint
435 SpiralPositionConstraint positionConstraint( itemId, GetDefaultSpiralRadiusFunction( layoutSize ), mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment );
436 Constraint constraint;
437 if ( orientation == ControlOrientation::Up )
439 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &SpiralPositionConstraint::OrientationUp );
441 else if ( orientation == ControlOrientation::Left )
443 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &SpiralPositionConstraint::OrientationLeft );
445 else if ( orientation == ControlOrientation::Down )
447 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &SpiralPositionConstraint::OrientationDown );
449 else // orientation == ControlOrientation::Right
451 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &SpiralPositionConstraint::OrientationRight );
453 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
454 constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
457 // Rotation constraint
458 SpiralRotationConstraint rotationConstraint( itemId, mImpl->mItemSpacingRadians );
459 if ( orientation == ControlOrientation::Up )
461 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, rotationConstraint, &SpiralRotationConstraint::OrientationUp );
463 else if ( orientation == ControlOrientation::Left )
465 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, rotationConstraint, &SpiralRotationConstraint::OrientationLeft );
467 else if ( orientation == ControlOrientation::Down )
469 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, rotationConstraint, &SpiralRotationConstraint::OrientationDown );
471 else // orientation == ControlOrientation::Right
473 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, rotationConstraint, &SpiralRotationConstraint::OrientationRight );
475 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
479 constraint = Constraint::New< Vector4 >( actor, Actor::Property::COLOR, SpiralColorConstraint( itemId, mImpl->mItemSpacingRadians ) );
480 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
481 constraint.SetRemoveAction(Dali::Constraint::Discard);
484 // Visibility constraint
485 SpiralVisibilityConstraint visibilityConstraint( itemId, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment );
486 if (IsVertical( orientation ) )
488 constraint = Constraint::New< bool >( actor, Actor::Property::VISIBLE, visibilityConstraint, &SpiralVisibilityConstraint::Portrait );
492 constraint = Constraint::New< bool >( actor, Actor::Property::VISIBLE, visibilityConstraint, &SpiralVisibilityConstraint::Landscape );
494 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
495 constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
496 constraint.SetRemoveAction(Dali::Constraint::Discard);
501 Vector3 SpiralLayout::GetItemPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize) const
503 Vector3 itemPosition = Vector3::ZERO;
504 const ControlOrientation::Type orientation = GetOrientation();
506 SpiralPositionConstraint positionConstraint( itemID, GetDefaultSpiralRadiusFunction( layoutSize ), mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment );
508 if ( orientation == ControlOrientation::Up )
510 positionConstraint.OrientationUp( itemPosition, currentLayoutPosition + itemID, layoutSize );
512 else if ( orientation == ControlOrientation::Left )
514 positionConstraint.OrientationLeft( itemPosition, currentLayoutPosition + itemID, layoutSize );
516 else if ( orientation == ControlOrientation::Down )
518 positionConstraint.OrientationDown( itemPosition, currentLayoutPosition + itemID, layoutSize );
520 else // orientation == ControlOrientation::Right
522 positionConstraint.OrientationRight( itemPosition, currentLayoutPosition + itemID, layoutSize );
528 SpiralLayout::SpiralLayout()
534 float SpiralLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
536 return GetItemScrollToPosition(itemID);
539 } // namespace Internal
541 } // namespace Toolkit