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(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(-Math::PI*0.5f, Vector3::ZAXIS) * Quaternion(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(-Math::PI, Vector3::ZAXIS) * Quaternion(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(-Math::PI*1.5f, Vector3::ZAXIS) * Quaternion(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 Degree angle = Radian(mItemSpacingRadians * fabsf(layoutPosition));
248 angle = (float)((int)angle % 360);
250 float progress = angle / 360.0f;
251 progress = (progress > 0.5f) ? 2.0f*(1.0f - progress) : progress*2.0f;
253 float darkness(1.0f);
255 const float startMarker = 0.10f; // The progress at which darkening starts
256 const float endMarker = 0.35f; // The progress at which darkening ends
257 const float minDarkness = 0.15f; // The darkness at end marker
259 if (progress > endMarker)
261 darkness = minDarkness;
263 else if (progress > startMarker)
265 darkness = 1.0f - ( (1.0f - minDarkness) * ((progress-startMarker) / (endMarker-startMarker)) );
269 return Vector4( darkness, darkness, darkness, current.a );
272 float mItemSpacingRadians;
275 struct SpiralVisibilityConstraintPortrait
277 SpiralVisibilityConstraintPortrait(float itemSpacingRadians, float itemDescent, float topItemAlignment)
278 : mItemSpacingRadians(itemSpacingRadians),
279 mItemDescent(itemDescent),
280 mTopItemAlignment(topItemAlignment)
284 bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
286 float itemsCachedBeforeTopItem = layoutSize.height*(mTopItemAlignment+0.5f) / mItemDescent;
287 return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.height / mItemDescent) + 1.0f);
290 float mItemSpacingRadians;
292 float mTopItemAlignment;
295 struct SpiralVisibilityConstraintLandscape
297 SpiralVisibilityConstraintLandscape(float itemSpacingRadians, float itemDescent, float topItemAlignment)
298 : mItemSpacingRadians(itemSpacingRadians),
299 mItemDescent(itemDescent),
300 mTopItemAlignment(topItemAlignment)
304 bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
306 float itemsCachedBeforeTopItem = layoutSize.width*(mTopItemAlignment+0.5f) / mItemDescent;
307 return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.width / mItemDescent) + 1.0f);
310 float mItemSpacingRadians;
312 float mTopItemAlignment;
315 } // unnamed namespace
323 struct SpiralLayout::Impl
326 : mItemSizeFunction(DefaultItemSizeFunction()),
327 mSpiralRadiusFunction(DefaultSpiralRadiusFunction()),
328 mItemSpacingRadians(DEFAULT_ITEM_SPACING_RADIANS),
329 mRevolutionDistance(DEFAULT_REVOLUTION_DISTANCE),
330 mItemDescent(DEFAULT_ITEM_DESCENT),
331 mTopItemAlignment(DEFAULT_TOP_ITEM_ALIGNMENT),
332 mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
333 mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
334 mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION)
338 ItemSizeFunction mItemSizeFunction;
339 SpiralRadiusFunction mSpiralRadiusFunction;
341 float mItemSpacingRadians;
342 float mRevolutionDistance;
344 float mTopItemAlignment;
345 float mScrollSpeedFactor;
346 float mMaximumSwipeSpeed;
347 float mItemFlickAnimationDuration;
350 SpiralLayoutPtr SpiralLayout::New()
352 return SpiralLayoutPtr(new SpiralLayout());
355 SpiralLayout::~SpiralLayout()
360 void SpiralLayout::SetItemSizeFunction(ItemSizeFunction function)
362 mImpl->mItemSizeFunction = function;
365 SpiralLayout::ItemSizeFunction SpiralLayout::GetItemSizeFunction() const
367 return mImpl->mItemSizeFunction;
370 void SpiralLayout::SetItemSpacing(Radian itemSpacing)
372 mImpl->mItemSpacingRadians = itemSpacing;
374 float itemsPerSpiral = std::max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
375 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
378 Radian SpiralLayout::GetItemSpacing() const
380 return Radian( mImpl->mItemSpacingRadians );
383 void SpiralLayout::SetRevolutionDistance(float distance)
385 mImpl->mRevolutionDistance = distance;
387 float itemsPerSpiral = std::max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
388 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
391 float SpiralLayout::GetRevolutionDistance() const
393 return mImpl->mRevolutionDistance;
396 void SpiralLayout::SetSpiralRadiusFunction(SpiralRadiusFunction function)
398 mImpl->mSpiralRadiusFunction = function;
401 SpiralLayout::SpiralRadiusFunction SpiralLayout::GetSpiralRadiusFunction() const
403 return mImpl->mSpiralRadiusFunction;
406 void SpiralLayout::SetTopItemAlignment(float alignment)
408 mImpl->mTopItemAlignment = alignment;
411 float SpiralLayout::GetTopItemAlignment() const
413 return mImpl->mTopItemAlignment;
416 void SpiralLayout::SetScrollSpeedFactor(float scrollSpeed)
418 mImpl->mScrollSpeedFactor = scrollSpeed;
421 void SpiralLayout::SetMaximumSwipeSpeed(float speed)
423 mImpl->mMaximumSwipeSpeed = speed;
426 void SpiralLayout::SetItemFlickAnimationDuration(float durationSeconds)
428 mImpl->mItemFlickAnimationDuration = durationSeconds;
431 float SpiralLayout::GetScrollSpeedFactor() const
433 return mImpl->mScrollSpeedFactor;
436 float SpiralLayout::GetMaximumSwipeSpeed() const
438 return mImpl->mMaximumSwipeSpeed;
441 float SpiralLayout::GetItemFlickAnimationDuration() const
443 return mImpl->mItemFlickAnimationDuration;
446 float SpiralLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
448 return 1.0f - static_cast<float>(numberOfItems);
451 float SpiralLayout::GetClosestAnchorPosition(float layoutPosition) const
453 return round(layoutPosition);
456 float SpiralLayout::GetItemScrollToPosition(unsigned int itemId) const
458 return -(static_cast<float>(itemId));
461 ItemRange SpiralLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
463 float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
464 float itemsPerSpiral = layoutHeight / mImpl->mItemDescent;
465 float itemsCachedBeforeTopItem = layoutHeight * (mImpl->mTopItemAlignment + 0.5f) / mImpl->mItemDescent;
466 float itemsViewable = std::min(itemsPerSpiral, itemsPerSpiral - itemsCachedBeforeTopItem - firstItemPosition + 1.0f);
468 unsigned int firstItem = static_cast<unsigned int>(std::max(0.0f, -firstItemPosition - itemsCachedBeforeTopItem - 1.0f));
469 unsigned int lastItem = static_cast<unsigned int>(std::max(0.0f, firstItem + itemsViewable));
471 return ItemRange(firstItem, lastItem+1);
474 unsigned int SpiralLayout::GetReserveItemCount(Vector3 layoutSize) const
476 float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
477 return static_cast<unsigned int>(layoutHeight / mImpl->mItemDescent);
480 bool SpiralLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const
482 // Note: itemId is not checked, since every item has the same size
484 itemSize = mImpl->mItemSizeFunction(layoutSize);
488 void SpiralLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const
492 animation.Resize(actor, size);
496 bool SpiralLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
498 if (mOrientation == ControlOrientation::Up)
500 constraint = SpiralPositionConstraintUp(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
502 else if (mOrientation == ControlOrientation::Left)
504 constraint = SpiralPositionConstraintLeft(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
506 else if (mOrientation == ControlOrientation::Down)
508 constraint = SpiralPositionConstraintDown(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
510 else // mOrientation == ControlOrientation::Right
512 constraint = SpiralPositionConstraintRight(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
518 bool SpiralLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const
520 if (mOrientation == ControlOrientation::Up)
522 constraint = SpiralRotationConstraintUp(mImpl->mItemSpacingRadians);
524 else if (mOrientation == ControlOrientation::Left)
526 constraint = SpiralRotationConstraintLeft(mImpl->mItemSpacingRadians);
528 else if (mOrientation == ControlOrientation::Down)
530 constraint = SpiralRotationConstraintDown(mImpl->mItemSpacingRadians);
532 else // mOrientation == ControlOrientation::Right
534 constraint = SpiralRotationConstraintRight(mImpl->mItemSpacingRadians);
540 bool SpiralLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
542 return false; // No scaling
545 bool SpiralLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const
547 constraint = SpiralColorConstraint(mImpl->mItemSpacingRadians);
551 bool SpiralLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const
553 if (IsVertical(mOrientation))
555 constraint = SpiralVisibilityConstraintPortrait(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
559 constraint = SpiralVisibilityConstraintLandscape(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
565 Degree SpiralLayout::GetScrollDirection() const
567 Degree scrollDirection(0);
569 if (mOrientation == ControlOrientation::Up)
571 scrollDirection = 0.0f - 45.0f; // Allow swiping horizontally & vertically
573 else if (mOrientation == ControlOrientation::Left)
575 scrollDirection = 90.0f - 45.0f;
577 else if (mOrientation == ControlOrientation::Down)
579 scrollDirection = 180.0f - 45.0f;
581 else // mOrientation == ControlOrientation::Right
583 scrollDirection = 270.0f - 45.0f;
586 return scrollDirection;
589 SpiralLayout::SpiralLayout()
595 float SpiralLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
597 return GetItemScrollToPosition(itemID);
600 } // namespace Toolkit