2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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>
22 using namespace Dali::Toolkit;
25 namespace // unnamed namespace
28 const float DEFAULT_ITEMS_PER_SPIRAL_TURN = 9.5f;
29 const float DEFAULT_ITEM_SPACING_RADIANS = Math::PI*2.0f/DEFAULT_ITEMS_PER_SPIRAL_TURN;
31 const float DEFAULT_REVOLUTION_DISTANCE = 190.0f;
32 const float DEFAULT_ITEM_DESCENT = DEFAULT_REVOLUTION_DISTANCE / DEFAULT_ITEMS_PER_SPIRAL_TURN;
34 const float DEFAULT_TOP_ITEM_ALIGNMENT = -0.125f;
36 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.01f;
37 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 30.0f;
38 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.1f;
40 struct DefaultItemSizeFunction
42 Vector3 operator()(const Vector3& layoutSize)
44 float width = layoutSize.width * 0.25f;
47 return Vector3(width, (width/4)*3, (width/4)*3);
51 struct DefaultSpiralRadiusFunction
53 float operator()(const Vector3& layoutSize)
55 return layoutSize.width*0.4f;
59 struct SpiralPositionConstraintUp
61 SpiralPositionConstraintUp(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
62 : mSpiralRadius(spiralRadius),
63 mItemSpacingRadians(itemSpacingRadians),
64 mItemDescent(itemDescent),
65 mTopItemAlignment(topItemAlignment)
69 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
71 float spiralRadius = mSpiralRadius(layoutSize);
73 float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
75 return Vector3( -spiralRadius * cosf(angle),
76 (mItemDescent * layoutPosition) + layoutSize.height*mTopItemAlignment,
77 -spiralRadius * sinf(angle) );
80 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
81 float mItemSpacingRadians;
83 float mTopItemAlignment;
86 struct SpiralPositionConstraintLeft
88 SpiralPositionConstraintLeft(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
89 : mSpiralRadius(spiralRadius),
90 mItemSpacingRadians(itemSpacingRadians),
91 mItemDescent(itemDescent),
92 mTopItemAlignment(topItemAlignment)
96 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
98 float spiralRadius = mSpiralRadius(layoutSize);
100 float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
102 return Vector3( (mItemDescent * layoutPosition) + layoutSize.width*mTopItemAlignment,
103 -spiralRadius * cosf(angle),
104 spiralRadius * sinf(angle) );
107 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
108 float mItemSpacingRadians;
110 float mTopItemAlignment;
113 struct SpiralPositionConstraintDown
115 SpiralPositionConstraintDown(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
116 : mSpiralRadius(spiralRadius),
117 mItemSpacingRadians(itemSpacingRadians),
118 mItemDescent(itemDescent),
119 mTopItemAlignment(topItemAlignment)
123 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
125 float spiralRadius = mSpiralRadius(layoutSize);
127 float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
129 return Vector3( -spiralRadius * cosf(angle),
130 (-mItemDescent * layoutPosition) - layoutSize.height*mTopItemAlignment,
131 spiralRadius * sinf(angle) );
134 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
135 float mItemSpacingRadians;
137 float mTopItemAlignment;
140 struct SpiralPositionConstraintRight
142 SpiralPositionConstraintRight(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
143 : mSpiralRadius(spiralRadius),
144 mItemSpacingRadians(itemSpacingRadians),
145 mItemDescent(itemDescent),
146 mTopItemAlignment(topItemAlignment)
150 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
152 float spiralRadius = mSpiralRadius(layoutSize);
154 float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
156 return Vector3( (-mItemDescent * layoutPosition) - layoutSize.width*mTopItemAlignment,
157 -spiralRadius * cosf(angle),
158 -spiralRadius * sinf(angle) );
161 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
162 float mItemSpacingRadians;
164 float mTopItemAlignment;
167 struct SpiralRotationConstraintUp
169 SpiralRotationConstraintUp(float itemSpacingRadians)
170 : mItemSpacingRadians(itemSpacingRadians)
174 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
176 float angle = -mItemSpacingRadians * layoutPosition;
178 return Quaternion(angle, Vector3::YAXIS);
181 float mItemSpacingRadians;
184 struct SpiralRotationConstraintLeft
186 SpiralRotationConstraintLeft(float itemSpacingRadians)
187 : mItemSpacingRadians(itemSpacingRadians)
191 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
193 float angle = -mItemSpacingRadians * layoutPosition;
195 return Quaternion(-Math::PI*0.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
198 float mItemSpacingRadians;
201 struct SpiralRotationConstraintDown
203 SpiralRotationConstraintDown(float itemSpacingRadians)
204 : mItemSpacingRadians(itemSpacingRadians)
208 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
210 float angle = -mItemSpacingRadians * layoutPosition;
212 return Quaternion(-Math::PI, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
215 float mItemSpacingRadians;
218 struct SpiralRotationConstraintRight
220 SpiralRotationConstraintRight(float itemSpacingRadians)
221 : mItemSpacingRadians(itemSpacingRadians)
225 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
227 float angle = -mItemSpacingRadians * layoutPosition;
229 return Quaternion(-Math::PI*1.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
232 float mItemSpacingRadians;
235 struct SpiralColorConstraint
237 SpiralColorConstraint(float itemSpacingRadians)
238 : mItemSpacingRadians(itemSpacingRadians)
242 Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
244 Degree angle = Radian(mItemSpacingRadians * fabsf(layoutPosition));
245 angle = (float)((int)angle % 360);
247 float progress = angle / 360.0f;
248 progress = (progress > 0.5f) ? 2.0f*(1.0f - progress) : progress*2.0f;
250 float darkness(1.0f);
252 const float startMarker = 0.10f; // The progress at which darkening starts
253 const float endMarker = 0.35f; // The progress at which darkening ends
254 const float minDarkness = 0.15f; // The darkness at end marker
256 if (progress > endMarker)
258 darkness = minDarkness;
260 else if (progress > startMarker)
262 darkness = 1.0f - ( (1.0f - minDarkness) * ((progress-startMarker) / (endMarker-startMarker)) );
266 return Vector4( darkness, darkness, darkness, current.a );
269 float mItemSpacingRadians;
272 struct SpiralVisibilityConstraintPortrait
274 SpiralVisibilityConstraintPortrait(float itemSpacingRadians, float itemDescent, float topItemAlignment)
275 : mItemSpacingRadians(itemSpacingRadians),
276 mItemDescent(itemDescent),
277 mTopItemAlignment(topItemAlignment)
281 bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
283 float itemsCachedBeforeTopItem = layoutSize.height*(mTopItemAlignment+0.5f) / mItemDescent;
284 return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.height / mItemDescent) + 1.0f);
287 float mItemSpacingRadians;
289 float mTopItemAlignment;
292 struct SpiralVisibilityConstraintLandscape
294 SpiralVisibilityConstraintLandscape(float itemSpacingRadians, float itemDescent, float topItemAlignment)
295 : mItemSpacingRadians(itemSpacingRadians),
296 mItemDescent(itemDescent),
297 mTopItemAlignment(topItemAlignment)
301 bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
303 float itemsCachedBeforeTopItem = layoutSize.width*(mTopItemAlignment+0.5f) / mItemDescent;
304 return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.width / mItemDescent) + 1.0f);
307 float mItemSpacingRadians;
309 float mTopItemAlignment;
312 } // unnamed namespace
320 struct SpiralLayout::Impl
323 : mItemSizeFunction(DefaultItemSizeFunction()),
324 mSpiralRadiusFunction(DefaultSpiralRadiusFunction()),
325 mItemSpacingRadians(DEFAULT_ITEM_SPACING_RADIANS),
326 mRevolutionDistance(DEFAULT_REVOLUTION_DISTANCE),
327 mItemDescent(DEFAULT_ITEM_DESCENT),
328 mTopItemAlignment(DEFAULT_TOP_ITEM_ALIGNMENT),
329 mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
330 mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
331 mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION)
335 ItemSizeFunction mItemSizeFunction;
336 SpiralRadiusFunction mSpiralRadiusFunction;
338 float mItemSpacingRadians;
339 float mRevolutionDistance;
341 float mTopItemAlignment;
342 float mScrollSpeedFactor;
343 float mMaximumSwipeSpeed;
344 float mItemFlickAnimationDuration;
347 SpiralLayoutPtr SpiralLayout::New()
349 return SpiralLayoutPtr(new SpiralLayout());
352 SpiralLayout::~SpiralLayout()
357 void SpiralLayout::SetItemSizeFunction(ItemSizeFunction function)
359 mImpl->mItemSizeFunction = function;
362 SpiralLayout::ItemSizeFunction SpiralLayout::GetItemSizeFunction() const
364 return mImpl->mItemSizeFunction;
367 void SpiralLayout::SetItemSpacing(Radian itemSpacing)
369 mImpl->mItemSpacingRadians = itemSpacing;
371 float itemsPerSpiral = max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
372 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
375 Radian SpiralLayout::GetItemSpacing() const
377 return Radian( mImpl->mItemSpacingRadians );
380 void SpiralLayout::SetRevolutionDistance(float distance)
382 mImpl->mRevolutionDistance = distance;
384 float itemsPerSpiral = max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
385 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
388 float SpiralLayout::GetRevolutionDistance() const
390 return mImpl->mRevolutionDistance;
393 void SpiralLayout::SetSpiralRadiusFunction(SpiralRadiusFunction function)
395 mImpl->mSpiralRadiusFunction = function;
398 SpiralLayout::SpiralRadiusFunction SpiralLayout::GetSpiralRadiusFunction() const
400 return mImpl->mSpiralRadiusFunction;
403 void SpiralLayout::SetTopItemAlignment(float alignment)
405 mImpl->mTopItemAlignment = alignment;
408 float SpiralLayout::GetTopItemAlignment() const
410 return mImpl->mTopItemAlignment;
413 void SpiralLayout::SetScrollSpeedFactor(float scrollSpeed)
415 mImpl->mScrollSpeedFactor = scrollSpeed;
418 void SpiralLayout::SetMaximumSwipeSpeed(float speed)
420 mImpl->mMaximumSwipeSpeed = speed;
423 void SpiralLayout::SetItemFlickAnimationDuration(float durationSeconds)
425 mImpl->mItemFlickAnimationDuration = durationSeconds;
428 float SpiralLayout::GetScrollSpeedFactor() const
430 return mImpl->mScrollSpeedFactor;
433 float SpiralLayout::GetMaximumSwipeSpeed() const
435 return mImpl->mMaximumSwipeSpeed;
438 float SpiralLayout::GetItemFlickAnimationDuration() const
440 return mImpl->mItemFlickAnimationDuration;
443 float SpiralLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
445 return 1.0f - static_cast<float>(numberOfItems);
448 float SpiralLayout::GetClosestAnchorPosition(float layoutPosition) const
450 return round(layoutPosition);
453 float SpiralLayout::GetItemScrollToPosition(unsigned int itemId) const
455 return -(static_cast<float>(itemId));
458 ItemRange SpiralLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
460 float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
461 float itemsPerSpiral = layoutHeight / mImpl->mItemDescent;
462 float itemsCachedBeforeTopItem = layoutHeight * (mImpl->mTopItemAlignment + 0.5f) / mImpl->mItemDescent;
463 float itemsViewable = min(itemsPerSpiral, itemsPerSpiral - itemsCachedBeforeTopItem - firstItemPosition + 1.0f);
465 unsigned int firstItem = static_cast<unsigned int>(max(0.0f, -firstItemPosition - itemsCachedBeforeTopItem - 1.0f));
466 unsigned int lastItem = static_cast<unsigned int>(max(0.0f, firstItem + itemsViewable));
468 return ItemRange(firstItem, lastItem+1);
471 unsigned int SpiralLayout::GetReserveItemCount(Vector3 layoutSize) const
473 float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
474 return static_cast<unsigned int>(layoutHeight / mImpl->mItemDescent);
477 bool SpiralLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const
479 // Note: itemId is not checked, since every item has the same size
481 itemSize = mImpl->mItemSizeFunction(layoutSize);
485 void SpiralLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const
489 animation.Resize(actor, size);
493 bool SpiralLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
495 if (mOrientation == ControlOrientation::Up)
497 constraint = SpiralPositionConstraintUp(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
499 else if (mOrientation == ControlOrientation::Left)
501 constraint = SpiralPositionConstraintLeft(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
503 else if (mOrientation == ControlOrientation::Down)
505 constraint = SpiralPositionConstraintDown(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
507 else // mOrientation == ControlOrientation::Right
509 constraint = SpiralPositionConstraintRight(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
515 bool SpiralLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const
517 if (mOrientation == ControlOrientation::Up)
519 constraint = SpiralRotationConstraintUp(mImpl->mItemSpacingRadians);
521 else if (mOrientation == ControlOrientation::Left)
523 constraint = SpiralRotationConstraintLeft(mImpl->mItemSpacingRadians);
525 else if (mOrientation == ControlOrientation::Down)
527 constraint = SpiralRotationConstraintDown(mImpl->mItemSpacingRadians);
529 else // mOrientation == ControlOrientation::Right
531 constraint = SpiralRotationConstraintRight(mImpl->mItemSpacingRadians);
537 bool SpiralLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
539 return false; // No scaling
542 bool SpiralLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const
544 constraint = SpiralColorConstraint(mImpl->mItemSpacingRadians);
548 bool SpiralLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const
550 if (IsVertical(mOrientation))
552 constraint = SpiralVisibilityConstraintPortrait(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
556 constraint = SpiralVisibilityConstraintLandscape(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
562 Degree SpiralLayout::GetScrollDirection() const
564 Degree scrollDirection(0);
566 if (mOrientation == ControlOrientation::Up)
568 scrollDirection = 0.0f - 45.0f; // Allow swiping horizontally & vertically
570 else if (mOrientation == ControlOrientation::Left)
572 scrollDirection = 90.0f - 45.0f;
574 else if (mOrientation == ControlOrientation::Down)
576 scrollDirection = 180.0f - 45.0f;
578 else // mOrientation == ControlOrientation::Right
580 scrollDirection = 270.0f - 45.0f;
583 return scrollDirection;
586 SpiralLayout::SpiralLayout()
592 float SpiralLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
594 return GetItemScrollToPosition(itemID);
597 } // namespace Toolkit