Add 'ExclusiveArch: armv7l' limit build to arm architecture
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / public-api / controls / scrollable / item-view / spiral-layout.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
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
7 //
8 //     http://floralicense.org/license/
9 //
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.
15 //
16
17 #include <algorithm>
18
19 #include <dali-toolkit/public-api/controls/scrollable/item-view/spiral-layout.h>
20
21 using namespace Dali;
22 using namespace Dali::Toolkit;
23 using namespace std;
24
25 namespace // unnamed namespace
26 {
27
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;
30
31 const float DEFAULT_REVOLUTION_DISTANCE = 190.0f;
32 const float DEFAULT_ITEM_DESCENT = DEFAULT_REVOLUTION_DISTANCE / DEFAULT_ITEMS_PER_SPIRAL_TURN;
33
34 const float DEFAULT_TOP_ITEM_ALIGNMENT = -0.125f;
35
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;
39
40 struct DefaultItemSizeFunction
41 {
42   Vector3 operator()(const Vector3& layoutSize)
43   {
44     float width = layoutSize.width * 0.25f;
45
46     // 4x3 aspect ratio
47     return Vector3(width, (width/4)*3, (width/4)*3);
48   }
49 };
50
51 struct DefaultSpiralRadiusFunction
52 {
53   float operator()(const Vector3& layoutSize)
54   {
55     return layoutSize.width*0.4f;
56   }
57 };
58
59 struct SpiralPositionConstraintUp
60 {
61   SpiralPositionConstraintUp(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
62   : mSpiralRadius(spiralRadius),
63     mItemSpacingRadians(itemSpacingRadians),
64     mItemDescent(itemDescent),
65     mTopItemAlignment(topItemAlignment)
66   {
67   }
68
69   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
70   {
71     float spiralRadius = mSpiralRadius(layoutSize);
72
73     float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
74
75     return Vector3( -spiralRadius * cosf(angle),
76                     (mItemDescent * layoutPosition) + layoutSize.height*mTopItemAlignment,
77                     -spiralRadius * sinf(angle) );
78   }
79
80   SpiralLayout::SpiralRadiusFunction mSpiralRadius;
81   float mItemSpacingRadians;
82   float mItemDescent;
83   float mTopItemAlignment;
84 };
85
86 struct SpiralPositionConstraintLeft
87 {
88   SpiralPositionConstraintLeft(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
89   : mSpiralRadius(spiralRadius),
90     mItemSpacingRadians(itemSpacingRadians),
91     mItemDescent(itemDescent),
92     mTopItemAlignment(topItemAlignment)
93   {
94   }
95
96   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
97   {
98     float spiralRadius = mSpiralRadius(layoutSize);
99
100     float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
101
102     return Vector3(  (mItemDescent * layoutPosition) + layoutSize.width*mTopItemAlignment,
103                      -spiralRadius * cosf(angle),
104                       spiralRadius * sinf(angle) );
105   }
106
107   SpiralLayout::SpiralRadiusFunction mSpiralRadius;
108   float mItemSpacingRadians;
109   float mItemDescent;
110   float mTopItemAlignment;
111 };
112
113 struct SpiralPositionConstraintDown
114 {
115   SpiralPositionConstraintDown(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
116   : mSpiralRadius(spiralRadius),
117     mItemSpacingRadians(itemSpacingRadians),
118     mItemDescent(itemDescent),
119     mTopItemAlignment(topItemAlignment)
120   {
121   }
122
123   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
124   {
125     float spiralRadius = mSpiralRadius(layoutSize);
126
127     float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
128
129     return Vector3(  -spiralRadius * cosf(angle),
130                     (-mItemDescent * layoutPosition) - layoutSize.height*mTopItemAlignment,
131                       spiralRadius * sinf(angle) );
132   }
133
134   SpiralLayout::SpiralRadiusFunction mSpiralRadius;
135   float mItemSpacingRadians;
136   float mItemDescent;
137   float mTopItemAlignment;
138 };
139
140 struct SpiralPositionConstraintRight
141 {
142   SpiralPositionConstraintRight(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
143   : mSpiralRadius(spiralRadius),
144     mItemSpacingRadians(itemSpacingRadians),
145     mItemDescent(itemDescent),
146     mTopItemAlignment(topItemAlignment)
147   {
148   }
149
150   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
151   {
152     float spiralRadius = mSpiralRadius(layoutSize);
153
154     float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
155
156     return Vector3( (-mItemDescent * layoutPosition) - layoutSize.width*mTopItemAlignment,
157                      -spiralRadius * cosf(angle),
158                      -spiralRadius * sinf(angle) );
159   }
160
161   SpiralLayout::SpiralRadiusFunction mSpiralRadius;
162   float mItemSpacingRadians;
163   float mItemDescent;
164   float mTopItemAlignment;
165 };
166
167 struct SpiralRotationConstraintUp
168 {
169   SpiralRotationConstraintUp(float itemSpacingRadians)
170   : mItemSpacingRadians(itemSpacingRadians)
171   {
172   }
173
174   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
175   {
176     float angle = -mItemSpacingRadians * layoutPosition;
177
178     return Quaternion(angle, Vector3::YAXIS);
179   }
180
181   float mItemSpacingRadians;
182 };
183
184 struct SpiralRotationConstraintLeft
185 {
186   SpiralRotationConstraintLeft(float itemSpacingRadians)
187   : mItemSpacingRadians(itemSpacingRadians)
188   {
189   }
190
191   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
192   {
193     float angle = -mItemSpacingRadians * layoutPosition;
194
195     return Quaternion(-Math::PI*0.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
196   }
197
198   float mItemSpacingRadians;
199 };
200
201 struct SpiralRotationConstraintDown
202 {
203   SpiralRotationConstraintDown(float itemSpacingRadians)
204   : mItemSpacingRadians(itemSpacingRadians)
205   {
206   }
207
208   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
209   {
210     float angle = -mItemSpacingRadians * layoutPosition;
211
212     return Quaternion(-Math::PI, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
213   }
214
215   float mItemSpacingRadians;
216 };
217
218 struct SpiralRotationConstraintRight
219 {
220   SpiralRotationConstraintRight(float itemSpacingRadians)
221   : mItemSpacingRadians(itemSpacingRadians)
222   {
223   }
224
225   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
226   {
227     float angle = -mItemSpacingRadians * layoutPosition;
228
229     return Quaternion(-Math::PI*1.5f, Vector3::ZAXIS) * Quaternion(angle, Vector3::YAXIS);
230   }
231
232   float mItemSpacingRadians;
233 };
234
235 struct SpiralColorConstraint
236 {
237   SpiralColorConstraint(float itemSpacingRadians)
238   : mItemSpacingRadians(itemSpacingRadians)
239   {
240   }
241
242   Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
243   {
244     Degree angle = Radian(mItemSpacingRadians * fabsf(layoutPosition));
245     angle = (float)((int)angle % 360);
246
247     float progress = angle / 360.0f;
248     progress = (progress > 0.5f) ? 2.0f*(1.0f - progress) : progress*2.0f;
249
250     float darkness(1.0f);
251     {
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
255
256       if (progress > endMarker)
257       {
258         darkness = minDarkness;
259       }
260       else if (progress > startMarker)
261       {
262         darkness = 1.0f - ( (1.0f - minDarkness) * ((progress-startMarker) / (endMarker-startMarker)) );
263       }
264     }
265
266     return Vector4( darkness, darkness, darkness, current.a );
267   }
268
269   float mItemSpacingRadians;
270 };
271
272 struct SpiralVisibilityConstraintPortrait
273 {
274   SpiralVisibilityConstraintPortrait(float itemSpacingRadians, float itemDescent, float topItemAlignment)
275   : mItemSpacingRadians(itemSpacingRadians),
276     mItemDescent(itemDescent),
277     mTopItemAlignment(topItemAlignment)
278   {
279   }
280
281   bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
282   {
283     float itemsCachedBeforeTopItem = layoutSize.height*(mTopItemAlignment+0.5f) / mItemDescent;
284     return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.height / mItemDescent) + 1.0f);
285   }
286
287   float mItemSpacingRadians;
288   float mItemDescent;
289   float mTopItemAlignment;
290 };
291
292 struct SpiralVisibilityConstraintLandscape
293 {
294   SpiralVisibilityConstraintLandscape(float itemSpacingRadians, float itemDescent, float topItemAlignment)
295   : mItemSpacingRadians(itemSpacingRadians),
296     mItemDescent(itemDescent),
297     mTopItemAlignment(topItemAlignment)
298   {
299   }
300
301   bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
302   {
303     float itemsCachedBeforeTopItem = layoutSize.width*(mTopItemAlignment+0.5f) / mItemDescent;
304     return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.width / mItemDescent) + 1.0f);
305   }
306
307   float mItemSpacingRadians;
308   float mItemDescent;
309   float mTopItemAlignment;
310 };
311
312 } // unnamed namespace
313
314 namespace Dali
315 {
316
317 namespace Toolkit
318 {
319
320 struct SpiralLayout::Impl
321 {
322   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)
332   {
333   }
334
335   ItemSizeFunction     mItemSizeFunction;
336   SpiralRadiusFunction mSpiralRadiusFunction;
337
338   float mItemSpacingRadians;
339   float mRevolutionDistance;
340   float mItemDescent;
341   float mTopItemAlignment;
342   float mScrollSpeedFactor;
343   float mMaximumSwipeSpeed;
344   float mItemFlickAnimationDuration;
345 };
346
347 SpiralLayoutPtr SpiralLayout::New()
348 {
349   return SpiralLayoutPtr(new SpiralLayout());
350 }
351
352 SpiralLayout::~SpiralLayout()
353 {
354   delete mImpl;
355 }
356
357 void SpiralLayout::SetItemSizeFunction(ItemSizeFunction function)
358 {
359   mImpl->mItemSizeFunction = function;
360 }
361
362 SpiralLayout::ItemSizeFunction SpiralLayout::GetItemSizeFunction() const
363 {
364   return mImpl->mItemSizeFunction;
365 }
366
367 void SpiralLayout::SetItemSpacing(Radian itemSpacing)
368 {
369   mImpl->mItemSpacingRadians = itemSpacing;
370
371   float itemsPerSpiral = max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
372   mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
373 }
374
375 Radian SpiralLayout::GetItemSpacing() const
376 {
377   return Radian( mImpl->mItemSpacingRadians );
378 }
379
380 void SpiralLayout::SetRevolutionDistance(float distance)
381 {
382   mImpl->mRevolutionDistance = distance;
383
384   float itemsPerSpiral = max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
385   mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
386 }
387
388 float SpiralLayout::GetRevolutionDistance() const
389 {
390   return mImpl->mRevolutionDistance;
391 }
392
393 void SpiralLayout::SetSpiralRadiusFunction(SpiralRadiusFunction function)
394 {
395   mImpl->mSpiralRadiusFunction = function;
396 }
397
398 SpiralLayout::SpiralRadiusFunction SpiralLayout::GetSpiralRadiusFunction() const
399 {
400   return mImpl->mSpiralRadiusFunction;
401 }
402
403 void SpiralLayout::SetTopItemAlignment(float alignment)
404 {
405   mImpl->mTopItemAlignment = alignment;
406 }
407
408 float SpiralLayout::GetTopItemAlignment() const
409 {
410   return mImpl->mTopItemAlignment;
411 }
412
413 void SpiralLayout::SetScrollSpeedFactor(float scrollSpeed)
414 {
415   mImpl->mScrollSpeedFactor = scrollSpeed;
416 }
417
418 void SpiralLayout::SetMaximumSwipeSpeed(float speed)
419 {
420   mImpl->mMaximumSwipeSpeed = speed;
421 }
422
423 void SpiralLayout::SetItemFlickAnimationDuration(float durationSeconds)
424 {
425   mImpl->mItemFlickAnimationDuration = durationSeconds;
426 }
427
428 float SpiralLayout::GetScrollSpeedFactor() const
429 {
430   return mImpl->mScrollSpeedFactor;
431 }
432
433 float SpiralLayout::GetMaximumSwipeSpeed() const
434 {
435   return mImpl->mMaximumSwipeSpeed;
436 }
437
438 float SpiralLayout::GetItemFlickAnimationDuration() const
439 {
440   return mImpl->mItemFlickAnimationDuration;
441 }
442
443 float SpiralLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
444 {
445   return 1.0f - static_cast<float>(numberOfItems);
446 }
447
448 float SpiralLayout::GetClosestAnchorPosition(float layoutPosition) const
449 {
450   return round(layoutPosition);
451 }
452
453 float SpiralLayout::GetItemScrollToPosition(unsigned int itemId) const
454 {
455   return -(static_cast<float>(itemId));
456 }
457
458 ItemRange SpiralLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
459 {
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);
464
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));
467
468   return ItemRange(firstItem, lastItem+1);
469 }
470
471 unsigned int SpiralLayout::GetReserveItemCount(Vector3 layoutSize) const
472 {
473   float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
474   return static_cast<unsigned int>(layoutHeight / mImpl->mItemDescent);
475 }
476
477 bool SpiralLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const
478 {
479   // Note: itemId is not checked, since every item has the same size
480
481   itemSize = mImpl->mItemSizeFunction(layoutSize);
482   return true;
483 }
484
485 void SpiralLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const
486 {
487   if(animation)
488   {
489     animation.Resize(actor, size);
490   }
491 }
492
493 bool SpiralLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
494 {
495   if (mOrientation == ControlOrientation::Up)
496   {
497     constraint = SpiralPositionConstraintUp(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
498   }
499   else if (mOrientation == ControlOrientation::Left)
500   {
501     constraint = SpiralPositionConstraintLeft(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
502   }
503   else if (mOrientation == ControlOrientation::Down)
504   {
505     constraint = SpiralPositionConstraintDown(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
506   }
507   else // mOrientation == ControlOrientation::Right
508   {
509     constraint = SpiralPositionConstraintRight(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
510   }
511
512   return true;
513 }
514
515 bool SpiralLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const
516 {
517   if (mOrientation == ControlOrientation::Up)
518   {
519     constraint = SpiralRotationConstraintUp(mImpl->mItemSpacingRadians);
520   }
521   else if (mOrientation == ControlOrientation::Left)
522   {
523     constraint = SpiralRotationConstraintLeft(mImpl->mItemSpacingRadians);
524   }
525   else if (mOrientation == ControlOrientation::Down)
526   {
527     constraint = SpiralRotationConstraintDown(mImpl->mItemSpacingRadians);
528   }
529   else // mOrientation == ControlOrientation::Right
530   {
531     constraint = SpiralRotationConstraintRight(mImpl->mItemSpacingRadians);
532   }
533
534   return true;
535 }
536
537 bool SpiralLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
538 {
539   return false; // No scaling
540 }
541
542 bool SpiralLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const
543 {
544   constraint = SpiralColorConstraint(mImpl->mItemSpacingRadians);
545   return true;
546 }
547
548 bool SpiralLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const
549 {
550   if (IsVertical(mOrientation))
551   {
552     constraint = SpiralVisibilityConstraintPortrait(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
553   }
554   else // horizontal
555   {
556     constraint = SpiralVisibilityConstraintLandscape(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
557   }
558
559   return true;
560 }
561
562 Degree SpiralLayout::GetScrollDirection() const
563 {
564   Degree scrollDirection(0);
565
566   if (mOrientation == ControlOrientation::Up)
567   {
568     scrollDirection = 0.0f - 45.0f; // Allow swiping horizontally & vertically
569   }
570   else if (mOrientation == ControlOrientation::Left)
571   {
572     scrollDirection = 90.0f - 45.0f;
573   }
574   else if (mOrientation == ControlOrientation::Down)
575   {
576     scrollDirection = 180.0f - 45.0f;
577   }
578   else // mOrientation == ControlOrientation::Right
579   {
580     scrollDirection = 270.0f - 45.0f;
581   }
582
583   return scrollDirection;
584 }
585
586 SpiralLayout::SpiralLayout()
587 : mImpl(NULL)
588 {
589   mImpl = new Impl();
590 }
591
592 float SpiralLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
593 {
594   return GetItemScrollToPosition(itemID);
595 }
596
597 } // namespace Toolkit
598
599 } // namespace Dali