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/public-api/controls/scrollable/item-view/spiral-layout.h>
23 #include <dali/public-api/animation/animation.h>
26 using namespace Dali::Toolkit;
28 namespace // unnamed namespace
31 const float DEFAULT_ITEMS_PER_SPIRAL_TURN = 9.5f;
32 const float DEFAULT_ITEM_SPACING_RADIANS = Math::PI*2.0f/DEFAULT_ITEMS_PER_SPIRAL_TURN;
34 const float DEFAULT_REVOLUTION_DISTANCE = 190.0f;
35 const float DEFAULT_ITEM_DESCENT = DEFAULT_REVOLUTION_DISTANCE / DEFAULT_ITEMS_PER_SPIRAL_TURN;
37 const float DEFAULT_TOP_ITEM_ALIGNMENT = -0.125f;
39 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.01f;
40 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 30.0f;
41 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.1f;
43 struct DefaultItemSizeFunction
45 Vector3 operator()(const Vector3& layoutSize)
47 float width = layoutSize.width * 0.25f;
50 return Vector3(width, (width/4)*3, (width/4)*3);
54 struct DefaultSpiralRadiusFunction
56 float operator()(const Vector3& layoutSize)
58 return layoutSize.width*0.4f;
62 struct SpiralPositionConstraintUp
64 SpiralPositionConstraintUp(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
65 : mSpiralRadius(spiralRadius),
66 mItemSpacingRadians(itemSpacingRadians),
67 mItemDescent(itemDescent),
68 mTopItemAlignment(topItemAlignment)
72 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
74 float spiralRadius = mSpiralRadius(layoutSize);
76 float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
78 return Vector3( -spiralRadius * cosf(angle),
79 (mItemDescent * layoutPosition) + layoutSize.height*mTopItemAlignment,
80 -spiralRadius * sinf(angle) );
83 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
84 float mItemSpacingRadians;
86 float mTopItemAlignment;
89 struct SpiralPositionConstraintLeft
91 SpiralPositionConstraintLeft(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
92 : mSpiralRadius(spiralRadius),
93 mItemSpacingRadians(itemSpacingRadians),
94 mItemDescent(itemDescent),
95 mTopItemAlignment(topItemAlignment)
99 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
101 float spiralRadius = mSpiralRadius(layoutSize);
103 float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
105 return Vector3( (mItemDescent * layoutPosition) + layoutSize.width*mTopItemAlignment,
106 -spiralRadius * cosf(angle),
107 spiralRadius * sinf(angle) );
110 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
111 float mItemSpacingRadians;
113 float mTopItemAlignment;
116 struct SpiralPositionConstraintDown
118 SpiralPositionConstraintDown(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
119 : mSpiralRadius(spiralRadius),
120 mItemSpacingRadians(itemSpacingRadians),
121 mItemDescent(itemDescent),
122 mTopItemAlignment(topItemAlignment)
126 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
128 float spiralRadius = mSpiralRadius(layoutSize);
130 float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
132 return Vector3( -spiralRadius * cosf(angle),
133 (-mItemDescent * layoutPosition) - layoutSize.height*mTopItemAlignment,
134 spiralRadius * sinf(angle) );
137 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
138 float mItemSpacingRadians;
140 float mTopItemAlignment;
143 struct SpiralPositionConstraintRight
145 SpiralPositionConstraintRight(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
146 : mSpiralRadius(spiralRadius),
147 mItemSpacingRadians(itemSpacingRadians),
148 mItemDescent(itemDescent),
149 mTopItemAlignment(topItemAlignment)
153 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
155 float spiralRadius = mSpiralRadius(layoutSize);
157 float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
159 return Vector3( (-mItemDescent * layoutPosition) - layoutSize.width*mTopItemAlignment,
160 -spiralRadius * cosf(angle),
161 -spiralRadius * sinf(angle) );
164 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
165 float mItemSpacingRadians;
167 float mTopItemAlignment;
170 struct SpiralRotationConstraintUp
172 SpiralRotationConstraintUp(float itemSpacingRadians)
173 : mItemSpacingRadians(itemSpacingRadians)
177 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
179 float angle = -mItemSpacingRadians * layoutPosition;
181 return Quaternion( Radian( angle ), Vector3::YAXIS);
184 float mItemSpacingRadians;
187 struct SpiralRotationConstraintLeft
189 SpiralRotationConstraintLeft(float itemSpacingRadians)
190 : mItemSpacingRadians(itemSpacingRadians)
194 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
196 float angle = -mItemSpacingRadians * layoutPosition;
198 return Quaternion( Radian( -Math::PI * 0.5f ), Vector3::ZAXIS ) * Quaternion( Radian( angle ), Vector3::YAXIS );
201 float mItemSpacingRadians;
204 struct SpiralRotationConstraintDown
206 SpiralRotationConstraintDown(float itemSpacingRadians)
207 : mItemSpacingRadians(itemSpacingRadians)
211 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
213 float angle = -mItemSpacingRadians * layoutPosition;
215 return Quaternion( Radian( -Math::PI ), Vector3::ZAXIS) * Quaternion( Radian( angle ), Vector3::YAXIS );
218 float mItemSpacingRadians;
221 struct SpiralRotationConstraintRight
223 SpiralRotationConstraintRight(float itemSpacingRadians)
224 : mItemSpacingRadians(itemSpacingRadians)
228 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
230 float angle = -mItemSpacingRadians * layoutPosition;
232 return Quaternion( Radian( -Math::PI * 1.5f ), Vector3::ZAXIS) * Quaternion( Radian( angle ), Vector3::YAXIS );
235 float mItemSpacingRadians;
238 struct SpiralColorConstraint
240 SpiralColorConstraint(float itemSpacingRadians)
241 : mItemSpacingRadians(itemSpacingRadians)
245 Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
247 Radian angle( mItemSpacingRadians * fabsf(layoutPosition) / Dali::ANGLE_360 );
249 float progress = angle - floorf( angle ); // take fractional bit only to get between 0.0 - 1.0
250 progress = (progress > 0.5f) ? 2.0f*(1.0f - progress) : progress*2.0f;
252 float darkness(1.0f);
254 const float startMarker = 0.10f; // The progress at which darkening starts
255 const float endMarker = 0.35f; // The progress at which darkening ends
256 const float minDarkness = 0.15f; // The darkness at end marker
258 if (progress > endMarker)
260 darkness = minDarkness;
262 else if (progress > startMarker)
264 darkness = 1.0f - ( (1.0f - minDarkness) * ((progress-startMarker) / (endMarker-startMarker)) );
268 return Vector4( darkness, darkness, darkness, current.a );
271 float mItemSpacingRadians;
274 struct SpiralVisibilityConstraintPortrait
276 SpiralVisibilityConstraintPortrait(float itemSpacingRadians, float itemDescent, float topItemAlignment)
277 : mItemSpacingRadians(itemSpacingRadians),
278 mItemDescent(itemDescent),
279 mTopItemAlignment(topItemAlignment)
283 bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
285 float itemsCachedBeforeTopItem = layoutSize.height*(mTopItemAlignment+0.5f) / mItemDescent;
286 return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.height / mItemDescent) + 1.0f);
289 float mItemSpacingRadians;
291 float mTopItemAlignment;
294 struct SpiralVisibilityConstraintLandscape
296 SpiralVisibilityConstraintLandscape(float itemSpacingRadians, float itemDescent, float topItemAlignment)
297 : mItemSpacingRadians(itemSpacingRadians),
298 mItemDescent(itemDescent),
299 mTopItemAlignment(topItemAlignment)
303 bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
305 float itemsCachedBeforeTopItem = layoutSize.width*(mTopItemAlignment+0.5f) / mItemDescent;
306 return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.width / mItemDescent) + 1.0f);
309 float mItemSpacingRadians;
311 float mTopItemAlignment;
314 } // unnamed namespace
322 struct SpiralLayout::Impl
325 : mItemSizeFunction(DefaultItemSizeFunction()),
326 mSpiralRadiusFunction(DefaultSpiralRadiusFunction()),
327 mItemSpacingRadians(DEFAULT_ITEM_SPACING_RADIANS),
328 mRevolutionDistance(DEFAULT_REVOLUTION_DISTANCE),
329 mItemDescent(DEFAULT_ITEM_DESCENT),
330 mTopItemAlignment(DEFAULT_TOP_ITEM_ALIGNMENT),
331 mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
332 mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
333 mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION)
337 ItemSizeFunction mItemSizeFunction;
338 SpiralRadiusFunction mSpiralRadiusFunction;
340 float mItemSpacingRadians;
341 float mRevolutionDistance;
343 float mTopItemAlignment;
344 float mScrollSpeedFactor;
345 float mMaximumSwipeSpeed;
346 float mItemFlickAnimationDuration;
349 SpiralLayoutPtr SpiralLayout::New()
351 return SpiralLayoutPtr(new SpiralLayout());
354 SpiralLayout::~SpiralLayout()
359 void SpiralLayout::SetItemSizeFunction(ItemSizeFunction function)
361 mImpl->mItemSizeFunction = function;
364 SpiralLayout::ItemSizeFunction SpiralLayout::GetItemSizeFunction() const
366 return mImpl->mItemSizeFunction;
369 void SpiralLayout::SetItemSpacing(Radian itemSpacing)
371 mImpl->mItemSpacingRadians = itemSpacing;
373 float itemsPerSpiral = std::max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
374 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
377 Radian SpiralLayout::GetItemSpacing() const
379 return Radian( mImpl->mItemSpacingRadians );
382 void SpiralLayout::SetRevolutionDistance(float distance)
384 mImpl->mRevolutionDistance = distance;
386 float itemsPerSpiral = std::max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
387 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
390 float SpiralLayout::GetRevolutionDistance() const
392 return mImpl->mRevolutionDistance;
395 void SpiralLayout::SetSpiralRadiusFunction(SpiralRadiusFunction function)
397 mImpl->mSpiralRadiusFunction = function;
400 SpiralLayout::SpiralRadiusFunction SpiralLayout::GetSpiralRadiusFunction() const
402 return mImpl->mSpiralRadiusFunction;
405 void SpiralLayout::SetTopItemAlignment(float alignment)
407 mImpl->mTopItemAlignment = alignment;
410 float SpiralLayout::GetTopItemAlignment() const
412 return mImpl->mTopItemAlignment;
415 void SpiralLayout::SetScrollSpeedFactor(float scrollSpeed)
417 mImpl->mScrollSpeedFactor = scrollSpeed;
420 void SpiralLayout::SetMaximumSwipeSpeed(float speed)
422 mImpl->mMaximumSwipeSpeed = speed;
425 void SpiralLayout::SetItemFlickAnimationDuration(float durationSeconds)
427 mImpl->mItemFlickAnimationDuration = durationSeconds;
430 float SpiralLayout::GetScrollSpeedFactor() const
432 return mImpl->mScrollSpeedFactor;
435 float SpiralLayout::GetMaximumSwipeSpeed() const
437 return mImpl->mMaximumSwipeSpeed;
440 float SpiralLayout::GetItemFlickAnimationDuration() const
442 return mImpl->mItemFlickAnimationDuration;
445 float SpiralLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
447 return 1.0f - static_cast<float>(numberOfItems);
450 float SpiralLayout::GetClosestAnchorPosition(float layoutPosition) const
452 return round(layoutPosition);
455 float SpiralLayout::GetItemScrollToPosition(unsigned int itemId) const
457 return -(static_cast<float>(itemId));
460 ItemRange SpiralLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
462 float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
463 float itemsPerSpiral = layoutHeight / mImpl->mItemDescent;
464 float itemsCachedBeforeTopItem = layoutHeight * (mImpl->mTopItemAlignment + 0.5f) / mImpl->mItemDescent;
465 float itemsViewable = std::min(itemsPerSpiral, itemsPerSpiral - itemsCachedBeforeTopItem - firstItemPosition + 1.0f);
467 unsigned int firstItem = static_cast<unsigned int>(std::max(0.0f, -firstItemPosition - itemsCachedBeforeTopItem - 1.0f));
468 unsigned int lastItem = static_cast<unsigned int>(std::max(0.0f, firstItem + itemsViewable));
470 return ItemRange(firstItem, lastItem+1);
473 unsigned int SpiralLayout::GetReserveItemCount(Vector3 layoutSize) const
475 float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
476 return static_cast<unsigned int>(layoutHeight / mImpl->mItemDescent);
479 bool SpiralLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const
481 // Note: itemId is not checked, since every item has the same size
483 itemSize = mImpl->mItemSizeFunction(layoutSize);
487 void SpiralLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const
491 animation.AnimateTo( Property( actor, Actor::Property::SIZE ), size );
495 bool SpiralLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
497 if (mOrientation == ControlOrientation::Up)
499 constraint = SpiralPositionConstraintUp(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
501 else if (mOrientation == ControlOrientation::Left)
503 constraint = SpiralPositionConstraintLeft(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
505 else if (mOrientation == ControlOrientation::Down)
507 constraint = SpiralPositionConstraintDown(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
509 else // mOrientation == ControlOrientation::Right
511 constraint = SpiralPositionConstraintRight(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
517 bool SpiralLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const
519 if (mOrientation == ControlOrientation::Up)
521 constraint = SpiralRotationConstraintUp(mImpl->mItemSpacingRadians);
523 else if (mOrientation == ControlOrientation::Left)
525 constraint = SpiralRotationConstraintLeft(mImpl->mItemSpacingRadians);
527 else if (mOrientation == ControlOrientation::Down)
529 constraint = SpiralRotationConstraintDown(mImpl->mItemSpacingRadians);
531 else // mOrientation == ControlOrientation::Right
533 constraint = SpiralRotationConstraintRight(mImpl->mItemSpacingRadians);
539 bool SpiralLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
541 return false; // No scaling
544 bool SpiralLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const
546 constraint = SpiralColorConstraint(mImpl->mItemSpacingRadians);
550 bool SpiralLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const
552 if (IsVertical(mOrientation))
554 constraint = SpiralVisibilityConstraintPortrait(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
558 constraint = SpiralVisibilityConstraintLandscape(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
564 Degree SpiralLayout::GetScrollDirection() const
566 Degree scrollDirection(0);
568 if (mOrientation == ControlOrientation::Up)
570 scrollDirection = Degree( -45.0f ); // Allow swiping horizontally & vertically
572 else if (mOrientation == ControlOrientation::Left)
574 scrollDirection = Degree( 45.0f );
576 else if (mOrientation == ControlOrientation::Down)
578 scrollDirection = Degree( 180.0f - 45.0f );
580 else // mOrientation == ControlOrientation::Right
582 scrollDirection = Degree( 270.0f - 45.0f );
585 return scrollDirection;
588 SpiralLayout::SpiralLayout()
594 float SpiralLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
596 return GetItemScrollToPosition(itemID);
599 } // namespace Toolkit