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.
20 #include <dali-toolkit/public-api/controls/scrollable/item-view/spiral-layout.h>
23 using namespace Dali::Toolkit;
26 namespace // unnamed namespace
29 const float DEFAULT_ITEMS_PER_SPIRAL_TURN = 9.5f;
30 const float DEFAULT_ITEM_SPACING_RADIANS = Math::PI*2.0f/DEFAULT_ITEMS_PER_SPIRAL_TURN;
32 const float DEFAULT_REVOLUTION_DISTANCE = 190.0f;
33 const float DEFAULT_ITEM_DESCENT = DEFAULT_REVOLUTION_DISTANCE / DEFAULT_ITEMS_PER_SPIRAL_TURN;
35 const float DEFAULT_TOP_ITEM_ALIGNMENT = -0.125f;
37 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.01f;
38 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 30.0f;
39 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.1f;
41 struct DefaultItemSizeFunction
43 Vector3 operator()(const Vector3& layoutSize)
45 float width = layoutSize.width * 0.25f;
48 return Vector3(width, (width/4)*3, (width/4)*3);
52 struct DefaultSpiralRadiusFunction
54 float operator()(const Vector3& layoutSize)
56 return layoutSize.width*0.4f;
60 struct SpiralPositionConstraintUp
62 SpiralPositionConstraintUp(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
63 : mSpiralRadius(spiralRadius),
64 mItemSpacingRadians(itemSpacingRadians),
65 mItemDescent(itemDescent),
66 mTopItemAlignment(topItemAlignment)
70 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
72 float spiralRadius = mSpiralRadius(layoutSize);
74 float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
76 return Vector3( -spiralRadius * cosf(angle),
77 (mItemDescent * layoutPosition) + layoutSize.height*mTopItemAlignment,
78 -spiralRadius * sinf(angle) );
81 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
82 float mItemSpacingRadians;
84 float mTopItemAlignment;
87 struct SpiralPositionConstraintLeft
89 SpiralPositionConstraintLeft(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
90 : mSpiralRadius(spiralRadius),
91 mItemSpacingRadians(itemSpacingRadians),
92 mItemDescent(itemDescent),
93 mTopItemAlignment(topItemAlignment)
97 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
99 float spiralRadius = mSpiralRadius(layoutSize);
101 float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
103 return Vector3( (mItemDescent * layoutPosition) + layoutSize.width*mTopItemAlignment,
104 -spiralRadius * cosf(angle),
105 spiralRadius * sinf(angle) );
108 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
109 float mItemSpacingRadians;
111 float mTopItemAlignment;
114 struct SpiralPositionConstraintDown
116 SpiralPositionConstraintDown(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
117 : mSpiralRadius(spiralRadius),
118 mItemSpacingRadians(itemSpacingRadians),
119 mItemDescent(itemDescent),
120 mTopItemAlignment(topItemAlignment)
124 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
126 float spiralRadius = mSpiralRadius(layoutSize);
128 float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
130 return Vector3( -spiralRadius * cosf(angle),
131 (-mItemDescent * layoutPosition) - layoutSize.height*mTopItemAlignment,
132 spiralRadius * sinf(angle) );
135 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
136 float mItemSpacingRadians;
138 float mTopItemAlignment;
141 struct SpiralPositionConstraintRight
143 SpiralPositionConstraintRight(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
144 : mSpiralRadius(spiralRadius),
145 mItemSpacingRadians(itemSpacingRadians),
146 mItemDescent(itemDescent),
147 mTopItemAlignment(topItemAlignment)
151 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
153 float spiralRadius = mSpiralRadius(layoutSize);
155 float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
157 return Vector3( (-mItemDescent * layoutPosition) - layoutSize.width*mTopItemAlignment,
158 -spiralRadius * cosf(angle),
159 -spiralRadius * sinf(angle) );
162 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
163 float mItemSpacingRadians;
165 float mTopItemAlignment;
168 struct SpiralRotationConstraintUp
170 SpiralRotationConstraintUp(float itemSpacingRadians)
171 : mItemSpacingRadians(itemSpacingRadians)
175 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
177 float angle = -mItemSpacingRadians * layoutPosition;
179 return Quaternion(angle, Vector3::YAXIS);
182 float mItemSpacingRadians;
185 struct SpiralRotationConstraintLeft
187 SpiralRotationConstraintLeft(float itemSpacingRadians)
188 : mItemSpacingRadians(itemSpacingRadians)
192 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
194 float angle = -mItemSpacingRadians * layoutPosition;
196 return Quaternion(-Math::PI*0.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
199 float mItemSpacingRadians;
202 struct SpiralRotationConstraintDown
204 SpiralRotationConstraintDown(float itemSpacingRadians)
205 : mItemSpacingRadians(itemSpacingRadians)
209 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
211 float angle = -mItemSpacingRadians * layoutPosition;
213 return Quaternion(-Math::PI, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
216 float mItemSpacingRadians;
219 struct SpiralRotationConstraintRight
221 SpiralRotationConstraintRight(float itemSpacingRadians)
222 : mItemSpacingRadians(itemSpacingRadians)
226 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
228 float angle = -mItemSpacingRadians * layoutPosition;
230 return Quaternion(-Math::PI*1.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
233 float mItemSpacingRadians;
236 struct SpiralColorConstraint
238 SpiralColorConstraint(float itemSpacingRadians)
239 : mItemSpacingRadians(itemSpacingRadians)
243 Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
245 Degree angle = Radian(mItemSpacingRadians * fabsf(layoutPosition));
246 angle = (float)((int)angle % 360);
248 float progress = angle / 360.0f;
249 progress = (progress > 0.5f) ? 2.0f*(1.0f - progress) : progress*2.0f;
251 float darkness(1.0f);
253 const float startMarker = 0.10f; // The progress at which darkening starts
254 const float endMarker = 0.35f; // The progress at which darkening ends
255 const float minDarkness = 0.15f; // The darkness at end marker
257 if (progress > endMarker)
259 darkness = minDarkness;
261 else if (progress > startMarker)
263 darkness = 1.0f - ( (1.0f - minDarkness) * ((progress-startMarker) / (endMarker-startMarker)) );
267 return Vector4( darkness, darkness, darkness, current.a );
270 float mItemSpacingRadians;
273 struct SpiralVisibilityConstraintPortrait
275 SpiralVisibilityConstraintPortrait(float itemSpacingRadians, float itemDescent, float topItemAlignment)
276 : mItemSpacingRadians(itemSpacingRadians),
277 mItemDescent(itemDescent),
278 mTopItemAlignment(topItemAlignment)
282 bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
284 float itemsCachedBeforeTopItem = layoutSize.height*(mTopItemAlignment+0.5f) / mItemDescent;
285 return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.height / mItemDescent) + 1.0f);
288 float mItemSpacingRadians;
290 float mTopItemAlignment;
293 struct SpiralVisibilityConstraintLandscape
295 SpiralVisibilityConstraintLandscape(float itemSpacingRadians, float itemDescent, float topItemAlignment)
296 : mItemSpacingRadians(itemSpacingRadians),
297 mItemDescent(itemDescent),
298 mTopItemAlignment(topItemAlignment)
302 bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
304 float itemsCachedBeforeTopItem = layoutSize.width*(mTopItemAlignment+0.5f) / mItemDescent;
305 return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.width / mItemDescent) + 1.0f);
308 float mItemSpacingRadians;
310 float mTopItemAlignment;
313 } // unnamed namespace
321 struct SpiralLayout::Impl
324 : mItemSizeFunction(DefaultItemSizeFunction()),
325 mSpiralRadiusFunction(DefaultSpiralRadiusFunction()),
326 mItemSpacingRadians(DEFAULT_ITEM_SPACING_RADIANS),
327 mRevolutionDistance(DEFAULT_REVOLUTION_DISTANCE),
328 mItemDescent(DEFAULT_ITEM_DESCENT),
329 mTopItemAlignment(DEFAULT_TOP_ITEM_ALIGNMENT),
330 mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
331 mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
332 mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION)
336 ItemSizeFunction mItemSizeFunction;
337 SpiralRadiusFunction mSpiralRadiusFunction;
339 float mItemSpacingRadians;
340 float mRevolutionDistance;
342 float mTopItemAlignment;
343 float mScrollSpeedFactor;
344 float mMaximumSwipeSpeed;
345 float mItemFlickAnimationDuration;
348 SpiralLayoutPtr SpiralLayout::New()
350 return SpiralLayoutPtr(new SpiralLayout());
353 SpiralLayout::~SpiralLayout()
358 void SpiralLayout::SetItemSizeFunction(ItemSizeFunction function)
360 mImpl->mItemSizeFunction = function;
363 SpiralLayout::ItemSizeFunction SpiralLayout::GetItemSizeFunction() const
365 return mImpl->mItemSizeFunction;
368 void SpiralLayout::SetItemSpacing(Radian itemSpacing)
370 mImpl->mItemSpacingRadians = itemSpacing;
372 float itemsPerSpiral = max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
373 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
376 Radian SpiralLayout::GetItemSpacing() const
378 return Radian( mImpl->mItemSpacingRadians );
381 void SpiralLayout::SetRevolutionDistance(float distance)
383 mImpl->mRevolutionDistance = distance;
385 float itemsPerSpiral = max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
386 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
389 float SpiralLayout::GetRevolutionDistance() const
391 return mImpl->mRevolutionDistance;
394 void SpiralLayout::SetSpiralRadiusFunction(SpiralRadiusFunction function)
396 mImpl->mSpiralRadiusFunction = function;
399 SpiralLayout::SpiralRadiusFunction SpiralLayout::GetSpiralRadiusFunction() const
401 return mImpl->mSpiralRadiusFunction;
404 void SpiralLayout::SetTopItemAlignment(float alignment)
406 mImpl->mTopItemAlignment = alignment;
409 float SpiralLayout::GetTopItemAlignment() const
411 return mImpl->mTopItemAlignment;
414 void SpiralLayout::SetScrollSpeedFactor(float scrollSpeed)
416 mImpl->mScrollSpeedFactor = scrollSpeed;
419 void SpiralLayout::SetMaximumSwipeSpeed(float speed)
421 mImpl->mMaximumSwipeSpeed = speed;
424 void SpiralLayout::SetItemFlickAnimationDuration(float durationSeconds)
426 mImpl->mItemFlickAnimationDuration = durationSeconds;
429 float SpiralLayout::GetScrollSpeedFactor() const
431 return mImpl->mScrollSpeedFactor;
434 float SpiralLayout::GetMaximumSwipeSpeed() const
436 return mImpl->mMaximumSwipeSpeed;
439 float SpiralLayout::GetItemFlickAnimationDuration() const
441 return mImpl->mItemFlickAnimationDuration;
444 float SpiralLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
446 return 1.0f - static_cast<float>(numberOfItems);
449 float SpiralLayout::GetClosestAnchorPosition(float layoutPosition) const
451 return round(layoutPosition);
454 float SpiralLayout::GetItemScrollToPosition(unsigned int itemId) const
456 return -(static_cast<float>(itemId));
459 ItemRange SpiralLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
461 float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
462 float itemsPerSpiral = layoutHeight / mImpl->mItemDescent;
463 float itemsCachedBeforeTopItem = layoutHeight * (mImpl->mTopItemAlignment + 0.5f) / mImpl->mItemDescent;
464 float itemsViewable = min(itemsPerSpiral, itemsPerSpiral - itemsCachedBeforeTopItem - firstItemPosition + 1.0f);
466 unsigned int firstItem = static_cast<unsigned int>(max(0.0f, -firstItemPosition - itemsCachedBeforeTopItem - 1.0f));
467 unsigned int lastItem = static_cast<unsigned int>(max(0.0f, firstItem + itemsViewable));
469 return ItemRange(firstItem, lastItem+1);
472 unsigned int SpiralLayout::GetReserveItemCount(Vector3 layoutSize) const
474 float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
475 return static_cast<unsigned int>(layoutHeight / mImpl->mItemDescent);
478 bool SpiralLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const
480 // Note: itemId is not checked, since every item has the same size
482 itemSize = mImpl->mItemSizeFunction(layoutSize);
486 void SpiralLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const
490 animation.Resize(actor, size);
494 bool SpiralLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
496 if (mOrientation == ControlOrientation::Up)
498 constraint = SpiralPositionConstraintUp(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
500 else if (mOrientation == ControlOrientation::Left)
502 constraint = SpiralPositionConstraintLeft(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
504 else if (mOrientation == ControlOrientation::Down)
506 constraint = SpiralPositionConstraintDown(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
508 else // mOrientation == ControlOrientation::Right
510 constraint = SpiralPositionConstraintRight(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
516 bool SpiralLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const
518 if (mOrientation == ControlOrientation::Up)
520 constraint = SpiralRotationConstraintUp(mImpl->mItemSpacingRadians);
522 else if (mOrientation == ControlOrientation::Left)
524 constraint = SpiralRotationConstraintLeft(mImpl->mItemSpacingRadians);
526 else if (mOrientation == ControlOrientation::Down)
528 constraint = SpiralRotationConstraintDown(mImpl->mItemSpacingRadians);
530 else // mOrientation == ControlOrientation::Right
532 constraint = SpiralRotationConstraintRight(mImpl->mItemSpacingRadians);
538 bool SpiralLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
540 return false; // No scaling
543 bool SpiralLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const
545 constraint = SpiralColorConstraint(mImpl->mItemSpacingRadians);
549 bool SpiralLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const
551 if (IsVertical(mOrientation))
553 constraint = SpiralVisibilityConstraintPortrait(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
557 constraint = SpiralVisibilityConstraintLandscape(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
563 Degree SpiralLayout::GetScrollDirection() const
565 Degree scrollDirection(0);
567 if (mOrientation == ControlOrientation::Up)
569 scrollDirection = 0.0f - 45.0f; // Allow swiping horizontally & vertically
571 else if (mOrientation == ControlOrientation::Left)
573 scrollDirection = 90.0f - 45.0f;
575 else if (mOrientation == ControlOrientation::Down)
577 scrollDirection = 180.0f - 45.0f;
579 else // mOrientation == ControlOrientation::Right
581 scrollDirection = 270.0f - 45.0f;
584 return scrollDirection;
587 SpiralLayout::SpiralLayout()
593 float SpiralLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
595 return GetItemScrollToPosition(itemID);
598 } // namespace Toolkit