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;
29 namespace // unnamed namespace
32 const float DEFAULT_ITEMS_PER_SPIRAL_TURN = 9.5f;
33 const float DEFAULT_ITEM_SPACING_RADIANS = Math::PI*2.0f/DEFAULT_ITEMS_PER_SPIRAL_TURN;
35 const float DEFAULT_REVOLUTION_DISTANCE = 190.0f;
36 const float DEFAULT_ITEM_DESCENT = DEFAULT_REVOLUTION_DISTANCE / DEFAULT_ITEMS_PER_SPIRAL_TURN;
38 const float DEFAULT_TOP_ITEM_ALIGNMENT = -0.125f;
40 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.01f;
41 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 30.0f;
42 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.1f;
44 struct DefaultItemSizeFunction
46 Vector3 operator()(const Vector3& layoutSize)
48 float width = layoutSize.width * 0.25f;
51 return Vector3(width, (width/4)*3, (width/4)*3);
55 struct DefaultSpiralRadiusFunction
57 float operator()(const Vector3& layoutSize)
59 return layoutSize.width*0.4f;
63 struct SpiralPositionConstraintUp
65 SpiralPositionConstraintUp(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
66 : mSpiralRadius(spiralRadius),
67 mItemSpacingRadians(itemSpacingRadians),
68 mItemDescent(itemDescent),
69 mTopItemAlignment(topItemAlignment)
73 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
75 float spiralRadius = mSpiralRadius(layoutSize);
77 float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
79 return Vector3( -spiralRadius * cosf(angle),
80 (mItemDescent * layoutPosition) + layoutSize.height*mTopItemAlignment,
81 -spiralRadius * sinf(angle) );
84 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
85 float mItemSpacingRadians;
87 float mTopItemAlignment;
90 struct SpiralPositionConstraintLeft
92 SpiralPositionConstraintLeft(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
93 : mSpiralRadius(spiralRadius),
94 mItemSpacingRadians(itemSpacingRadians),
95 mItemDescent(itemDescent),
96 mTopItemAlignment(topItemAlignment)
100 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
102 float spiralRadius = mSpiralRadius(layoutSize);
104 float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
106 return Vector3( (mItemDescent * layoutPosition) + layoutSize.width*mTopItemAlignment,
107 -spiralRadius * cosf(angle),
108 spiralRadius * sinf(angle) );
111 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
112 float mItemSpacingRadians;
114 float mTopItemAlignment;
117 struct SpiralPositionConstraintDown
119 SpiralPositionConstraintDown(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
120 : mSpiralRadius(spiralRadius),
121 mItemSpacingRadians(itemSpacingRadians),
122 mItemDescent(itemDescent),
123 mTopItemAlignment(topItemAlignment)
127 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
129 float spiralRadius = mSpiralRadius(layoutSize);
131 float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
133 return Vector3( -spiralRadius * cosf(angle),
134 (-mItemDescent * layoutPosition) - layoutSize.height*mTopItemAlignment,
135 spiralRadius * sinf(angle) );
138 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
139 float mItemSpacingRadians;
141 float mTopItemAlignment;
144 struct SpiralPositionConstraintRight
146 SpiralPositionConstraintRight(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
147 : mSpiralRadius(spiralRadius),
148 mItemSpacingRadians(itemSpacingRadians),
149 mItemDescent(itemDescent),
150 mTopItemAlignment(topItemAlignment)
154 Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
156 float spiralRadius = mSpiralRadius(layoutSize);
158 float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
160 return Vector3( (-mItemDescent * layoutPosition) - layoutSize.width*mTopItemAlignment,
161 -spiralRadius * cosf(angle),
162 -spiralRadius * sinf(angle) );
165 SpiralLayout::SpiralRadiusFunction mSpiralRadius;
166 float mItemSpacingRadians;
168 float mTopItemAlignment;
171 struct SpiralRotationConstraintUp
173 SpiralRotationConstraintUp(float itemSpacingRadians)
174 : mItemSpacingRadians(itemSpacingRadians)
178 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
180 float angle = -mItemSpacingRadians * layoutPosition;
182 return Quaternion(angle, Vector3::YAXIS);
185 float mItemSpacingRadians;
188 struct SpiralRotationConstraintLeft
190 SpiralRotationConstraintLeft(float itemSpacingRadians)
191 : mItemSpacingRadians(itemSpacingRadians)
195 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
197 float angle = -mItemSpacingRadians * layoutPosition;
199 return Quaternion(-Math::PI*0.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
202 float mItemSpacingRadians;
205 struct SpiralRotationConstraintDown
207 SpiralRotationConstraintDown(float itemSpacingRadians)
208 : mItemSpacingRadians(itemSpacingRadians)
212 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
214 float angle = -mItemSpacingRadians * layoutPosition;
216 return Quaternion(-Math::PI, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
219 float mItemSpacingRadians;
222 struct SpiralRotationConstraintRight
224 SpiralRotationConstraintRight(float itemSpacingRadians)
225 : mItemSpacingRadians(itemSpacingRadians)
229 Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
231 float angle = -mItemSpacingRadians * layoutPosition;
233 return Quaternion(-Math::PI*1.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
236 float mItemSpacingRadians;
239 struct SpiralColorConstraint
241 SpiralColorConstraint(float itemSpacingRadians)
242 : mItemSpacingRadians(itemSpacingRadians)
246 Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
248 Degree angle = Radian(mItemSpacingRadians * fabsf(layoutPosition));
249 angle = (float)((int)angle % 360);
251 float progress = angle / 360.0f;
252 progress = (progress > 0.5f) ? 2.0f*(1.0f - progress) : progress*2.0f;
254 float darkness(1.0f);
256 const float startMarker = 0.10f; // The progress at which darkening starts
257 const float endMarker = 0.35f; // The progress at which darkening ends
258 const float minDarkness = 0.15f; // The darkness at end marker
260 if (progress > endMarker)
262 darkness = minDarkness;
264 else if (progress > startMarker)
266 darkness = 1.0f - ( (1.0f - minDarkness) * ((progress-startMarker) / (endMarker-startMarker)) );
270 return Vector4( darkness, darkness, darkness, current.a );
273 float mItemSpacingRadians;
276 struct SpiralVisibilityConstraintPortrait
278 SpiralVisibilityConstraintPortrait(float itemSpacingRadians, float itemDescent, float topItemAlignment)
279 : mItemSpacingRadians(itemSpacingRadians),
280 mItemDescent(itemDescent),
281 mTopItemAlignment(topItemAlignment)
285 bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
287 float itemsCachedBeforeTopItem = layoutSize.height*(mTopItemAlignment+0.5f) / mItemDescent;
288 return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.height / mItemDescent) + 1.0f);
291 float mItemSpacingRadians;
293 float mTopItemAlignment;
296 struct SpiralVisibilityConstraintLandscape
298 SpiralVisibilityConstraintLandscape(float itemSpacingRadians, float itemDescent, float topItemAlignment)
299 : mItemSpacingRadians(itemSpacingRadians),
300 mItemDescent(itemDescent),
301 mTopItemAlignment(topItemAlignment)
305 bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
307 float itemsCachedBeforeTopItem = layoutSize.width*(mTopItemAlignment+0.5f) / mItemDescent;
308 return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.width / mItemDescent) + 1.0f);
311 float mItemSpacingRadians;
313 float mTopItemAlignment;
316 } // unnamed namespace
324 struct SpiralLayout::Impl
327 : mItemSizeFunction(DefaultItemSizeFunction()),
328 mSpiralRadiusFunction(DefaultSpiralRadiusFunction()),
329 mItemSpacingRadians(DEFAULT_ITEM_SPACING_RADIANS),
330 mRevolutionDistance(DEFAULT_REVOLUTION_DISTANCE),
331 mItemDescent(DEFAULT_ITEM_DESCENT),
332 mTopItemAlignment(DEFAULT_TOP_ITEM_ALIGNMENT),
333 mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
334 mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
335 mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION)
339 ItemSizeFunction mItemSizeFunction;
340 SpiralRadiusFunction mSpiralRadiusFunction;
342 float mItemSpacingRadians;
343 float mRevolutionDistance;
345 float mTopItemAlignment;
346 float mScrollSpeedFactor;
347 float mMaximumSwipeSpeed;
348 float mItemFlickAnimationDuration;
351 SpiralLayoutPtr SpiralLayout::New()
353 return SpiralLayoutPtr(new SpiralLayout());
356 SpiralLayout::~SpiralLayout()
361 void SpiralLayout::SetItemSizeFunction(ItemSizeFunction function)
363 mImpl->mItemSizeFunction = function;
366 SpiralLayout::ItemSizeFunction SpiralLayout::GetItemSizeFunction() const
368 return mImpl->mItemSizeFunction;
371 void SpiralLayout::SetItemSpacing(Radian itemSpacing)
373 mImpl->mItemSpacingRadians = itemSpacing;
375 float itemsPerSpiral = max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
376 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
379 Radian SpiralLayout::GetItemSpacing() const
381 return Radian( mImpl->mItemSpacingRadians );
384 void SpiralLayout::SetRevolutionDistance(float distance)
386 mImpl->mRevolutionDistance = distance;
388 float itemsPerSpiral = max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
389 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
392 float SpiralLayout::GetRevolutionDistance() const
394 return mImpl->mRevolutionDistance;
397 void SpiralLayout::SetSpiralRadiusFunction(SpiralRadiusFunction function)
399 mImpl->mSpiralRadiusFunction = function;
402 SpiralLayout::SpiralRadiusFunction SpiralLayout::GetSpiralRadiusFunction() const
404 return mImpl->mSpiralRadiusFunction;
407 void SpiralLayout::SetTopItemAlignment(float alignment)
409 mImpl->mTopItemAlignment = alignment;
412 float SpiralLayout::GetTopItemAlignment() const
414 return mImpl->mTopItemAlignment;
417 void SpiralLayout::SetScrollSpeedFactor(float scrollSpeed)
419 mImpl->mScrollSpeedFactor = scrollSpeed;
422 void SpiralLayout::SetMaximumSwipeSpeed(float speed)
424 mImpl->mMaximumSwipeSpeed = speed;
427 void SpiralLayout::SetItemFlickAnimationDuration(float durationSeconds)
429 mImpl->mItemFlickAnimationDuration = durationSeconds;
432 float SpiralLayout::GetScrollSpeedFactor() const
434 return mImpl->mScrollSpeedFactor;
437 float SpiralLayout::GetMaximumSwipeSpeed() const
439 return mImpl->mMaximumSwipeSpeed;
442 float SpiralLayout::GetItemFlickAnimationDuration() const
444 return mImpl->mItemFlickAnimationDuration;
447 float SpiralLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
449 return 1.0f - static_cast<float>(numberOfItems);
452 float SpiralLayout::GetClosestAnchorPosition(float layoutPosition) const
454 return round(layoutPosition);
457 float SpiralLayout::GetItemScrollToPosition(unsigned int itemId) const
459 return -(static_cast<float>(itemId));
462 ItemRange SpiralLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
464 float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
465 float itemsPerSpiral = layoutHeight / mImpl->mItemDescent;
466 float itemsCachedBeforeTopItem = layoutHeight * (mImpl->mTopItemAlignment + 0.5f) / mImpl->mItemDescent;
467 float itemsViewable = min(itemsPerSpiral, itemsPerSpiral - itemsCachedBeforeTopItem - firstItemPosition + 1.0f);
469 unsigned int firstItem = static_cast<unsigned int>(max(0.0f, -firstItemPosition - itemsCachedBeforeTopItem - 1.0f));
470 unsigned int lastItem = static_cast<unsigned int>(max(0.0f, firstItem + itemsViewable));
472 return ItemRange(firstItem, lastItem+1);
475 unsigned int SpiralLayout::GetReserveItemCount(Vector3 layoutSize) const
477 float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
478 return static_cast<unsigned int>(layoutHeight / mImpl->mItemDescent);
481 bool SpiralLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const
483 // Note: itemId is not checked, since every item has the same size
485 itemSize = mImpl->mItemSizeFunction(layoutSize);
489 void SpiralLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const
493 animation.Resize(actor, size);
497 bool SpiralLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
499 if (mOrientation == ControlOrientation::Up)
501 constraint = SpiralPositionConstraintUp(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
503 else if (mOrientation == ControlOrientation::Left)
505 constraint = SpiralPositionConstraintLeft(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
507 else if (mOrientation == ControlOrientation::Down)
509 constraint = SpiralPositionConstraintDown(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
511 else // mOrientation == ControlOrientation::Right
513 constraint = SpiralPositionConstraintRight(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
519 bool SpiralLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const
521 if (mOrientation == ControlOrientation::Up)
523 constraint = SpiralRotationConstraintUp(mImpl->mItemSpacingRadians);
525 else if (mOrientation == ControlOrientation::Left)
527 constraint = SpiralRotationConstraintLeft(mImpl->mItemSpacingRadians);
529 else if (mOrientation == ControlOrientation::Down)
531 constraint = SpiralRotationConstraintDown(mImpl->mItemSpacingRadians);
533 else // mOrientation == ControlOrientation::Right
535 constraint = SpiralRotationConstraintRight(mImpl->mItemSpacingRadians);
541 bool SpiralLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
543 return false; // No scaling
546 bool SpiralLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const
548 constraint = SpiralColorConstraint(mImpl->mItemSpacingRadians);
552 bool SpiralLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const
554 if (IsVertical(mOrientation))
556 constraint = SpiralVisibilityConstraintPortrait(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
560 constraint = SpiralVisibilityConstraintLandscape(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
566 Degree SpiralLayout::GetScrollDirection() const
568 Degree scrollDirection(0);
570 if (mOrientation == ControlOrientation::Up)
572 scrollDirection = 0.0f - 45.0f; // Allow swiping horizontally & vertically
574 else if (mOrientation == ControlOrientation::Left)
576 scrollDirection = 90.0f - 45.0f;
578 else if (mOrientation == ControlOrientation::Down)
580 scrollDirection = 180.0f - 45.0f;
582 else // mOrientation == ControlOrientation::Right
584 scrollDirection = 270.0f - 45.0f;
587 return scrollDirection;
590 SpiralLayout::SpiralLayout()
596 float SpiralLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
598 return GetItemScrollToPosition(itemID);
601 } // namespace Toolkit