[NUI] Add GridRecycleLayotManager (#1685)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Wearable / src / internal / FishEyeLayoutManager.cs
1 /* Copyright (c) 2020 Samsung Electronics Co., Ltd.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15  */
16 using System;
17 using Tizen.NUI.Components;
18 using System.Collections.Generic;
19
20 namespace Tizen.NUI.Wearable
21 {
22     /// <summary>
23     /// [Draft] This class implements a fish eye layout
24     /// </summary>
25     internal class FishEyeLayoutManager : RecycleLayoutManager
26     {
27         public int CurrentFocusedIndex { get; set; } = 0;
28         public int FocusedIndex { get; set; } = 0;
29         public FishEyeLayoutManager()
30         {
31         }
32
33         private float FindCandidatePosition(float startPosition, float scrollPosition, bool isBack)
34         {
35             // There are two ellipses which return how much scale needed.
36             // We can find candidate position by intersection of ellipse and line.
37             // Item size is always determined before calculation, so can get angle of it by height/width when orientation is vertical.
38             // Y intercept is on previous item.
39
40             double center = Math.Abs(scrollPosition);
41             double result = isBack ? center + 180.0f : center - 180.0f;
42
43             // double newFactor = Math.Pow(180,2) / CalculateXFactor(0);
44             double angle = ItemSize.Height / ItemSize.Width;
45             double intercept = (startPosition - center);
46             double constant = Math.Sqrt(Math.Pow((180 * 333), 2) / (Math.Pow(333, 2) - Math.Pow(153, 2)));
47
48             double semiMajorAxis = 333.0;
49             double centerOfEllipse = -153.0;
50             double centerOfEllipse2 = 153.0;
51
52             // Find intersection using qurdratic formula.
53             // We have two different equations because there are two different ellipse.
54             double a = Math.Pow(semiMajorAxis, 2) + Math.Pow(angle * constant, 2);
55             double b = -(intercept * Math.Pow(semiMajorAxis, 2) + centerOfEllipse * Math.Pow(angle * constant, 2));
56             double c = Math.Pow(intercept * semiMajorAxis, 2) + Math.Pow(angle * constant * centerOfEllipse, 2) - Math.Pow(angle * constant * semiMajorAxis, 2);
57             double b2 = -(intercept * Math.Pow(semiMajorAxis, 2) + centerOfEllipse2 * Math.Pow(angle * constant, 2));
58             double c2 = Math.Pow(intercept * semiMajorAxis, 2) + Math.Pow(angle * constant * centerOfEllipse2, 2) - Math.Pow(angle * constant * semiMajorAxis, 2);
59
60             double result1 = (-b + Math.Sqrt((Math.Pow(b, 2) - a * c))) / a;
61             double result2 = (-b - Math.Sqrt((Math.Pow(b, 2) - a * c))) / a;
62             double result3 = (-b2 + Math.Sqrt((Math.Pow(b2, 2) - a * c2))) / a;
63             double result4 = (-b2 - Math.Sqrt((Math.Pow(b2, 2) - a * c2))) / a;
64
65             result = isBack ? result1 : result4;
66             return (float)(center + result);
67         }
68
69
70         public override float CalculateLayoutOrientationSize()
71         {
72             return mStepSize * (DataCount-1);
73         }
74
75         public override void Layout(float scrollPosition)
76         {
77             RecycleItem centerItem = Container.Children[FocusedIndex] as RecycleItem;
78             float centerItemPosition = LayoutOrientation == Orientation.Horizontal ? centerItem.Position.X : centerItem.Position.Y;
79
80             Vector2 stepRange = new Vector2(-scrollPosition - mStepSize + 1.0f, -scrollPosition + mStepSize - 1.0f);
81             Vector2 visibleRange = new Vector2(Math.Abs(scrollPosition) - 180, Math.Abs(scrollPosition) + 180);
82
83             if (mStepSize != 0 && centerItemPosition <= stepRange.X)
84             {
85                 FocusedIndex = Math.Min(Container.Children.Count - 1, FocusedIndex + 1);
86                 centerItem = Container.Children[FocusedIndex] as RecycleItem;
87                 centerItem.Position = new Position(0.0f, Math.Abs(mStepSize * (centerItem.DataIndex)));
88                 centerItem.Scale = new Vector3(1.0f, 1.0f, 1.0f);
89             }
90             else if (mStepSize != 0 && centerItemPosition >= stepRange.Y)
91             {
92                 FocusedIndex = Math.Max(0, FocusedIndex - 1);
93                 centerItem = Container.Children[FocusedIndex] as RecycleItem;
94                 centerItem.Position = new Position(0.0f, Math.Abs(mStepSize * (centerItem.DataIndex)));
95                 centerItem.Scale = new Vector3(1.0f, 1.0f, 1.0f);
96             }
97             else
98             {
99                 float centerItemScale = CalculateScaleFactor(centerItemPosition, scrollPosition);
100                 centerItem.Scale = new Vector3(centerItemScale, centerItemScale, 1.0f);
101             }
102
103             RecycleItem prevItem = centerItem;
104
105             // Front of center item
106             for (int i = FocusedIndex - 1; i > -1; i--)
107             {
108                 RecycleItem target = Container.Children[i] as RecycleItem;
109
110                 float prevItemPosition = LayoutOrientation == Orientation.Horizontal ? prevItem.Position.X : prevItem.Position.Y;
111                 float prevItemSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.Size.Width : prevItem.Size.Height);
112                 float prevItemCurrentSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.GetCurrentSizeFloat().Width : prevItem.GetCurrentSizeFloat().Height);
113                 prevItemSize = prevItemCurrentSize == 0 ? prevItemSize : prevItemCurrentSize;
114                 prevItemSize = prevItemSize * prevItem.Scale.X;
115
116                 float startPosition = prevItemPosition - prevItemSize / 2.0f;
117
118                 if (startPosition > visibleRange.X)
119                 {
120                     float candidatePosition = visibleRange.X;
121                     candidatePosition = FindCandidatePosition(startPosition, scrollPosition, false);
122                     target.Position = LayoutOrientation == Orientation.Horizontal ?
123                                 new Position(candidatePosition, target.Position.Y) :
124                                 new Position(target.Position.X, candidatePosition);
125
126                     float scaleFactor = CalculateScaleFactor(candidatePosition, scrollPosition);
127                     target.Scale = new Vector3(scaleFactor, scaleFactor, 1.0f);
128
129                     prevItem = target;
130                 }
131                 else
132                 {
133                     target.Scale = new Vector3(0.0f, 0.0f, 1.0f);
134                 }
135             }
136
137             prevItem = centerItem;
138
139             // Back of center item
140             for (int i = FocusedIndex + 1; i < Container.Children.Count; i++)
141             {
142                 RecycleItem target = Container.Children[i] as RecycleItem;
143
144                 float prevItemPosition = LayoutOrientation == Orientation.Horizontal ? prevItem.Position.X : prevItem.Position.Y;
145                 float prevItemSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.Size.Width : prevItem.Size.Height);
146                 float prevItemCurrentSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.GetCurrentSizeFloat().Width : prevItem.GetCurrentSizeFloat().Height);
147                 prevItemSize = prevItemCurrentSize == 0 ? prevItemSize : prevItemCurrentSize;
148                 prevItemSize = prevItemSize * prevItem.Scale.X;
149
150                 float startPosition = prevItemPosition + prevItemSize / 2.0f;
151
152                 if (startPosition < visibleRange.Y)
153                 {
154                     float candidatePosition = visibleRange.Y;
155                     candidatePosition = FindCandidatePosition(startPosition, scrollPosition, true);
156                     target.Position = LayoutOrientation == Orientation.Horizontal ?
157                                 new Position(candidatePosition, target.Position.Y) :
158                                 new Position(target.Position.X, candidatePosition);
159
160                     float scaleFactor = CalculateScaleFactor(candidatePosition, scrollPosition);
161                     target.Scale = new Vector3(scaleFactor, scaleFactor, 1.0f);
162
163                     prevItem = target;
164                 }
165                 else
166                 {
167                     target.Scale = new Vector3(0.0f, 0.0f, 1.0f);
168                 }
169             }
170
171             if (mStepSize == 0)
172             {
173                 if (LayoutOrientation == Orientation.Horizontal)
174                 {
175                     mStepSize = Container.Children[0].Size.Width / 2.0f + Container.Children[1].Size.Width * Container.Children[1].Scale.X / 2.0f;
176                 }
177                 else
178                 {
179                     mStepSize = Container.Children[0].Size.Height / 2.0f + Container.Children[1].Size.Height * Container.Children[1].Scale.X / 2.0f;
180                 }
181
182                 mStepSize = float.IsNaN(mStepSize)?0:mStepSize;
183             }
184         }
185
186         public override List<RecycleItem> Recycle(float scrollPosition)
187         {
188             List<RecycleItem> result = new List<RecycleItem>();
189
190             bool isBack = scrollPosition - mPrevScrollPosition < 0;
191
192             int previousFocusIndex = FocusedIndex;
193
194             if (!isBack && FocusedIndex < 6)
195             {
196                 RecycleItem target = Container.Children[Container.Children.Count - 1] as RecycleItem;
197
198                 int previousSiblingOrder = target.SiblingOrder;
199                 target.SiblingOrder = 0;
200                 target.DataIndex = target.DataIndex - Container.Children.Count;
201                 target.Position = new Position(0, Math.Abs(scrollPosition) - 360);
202                 target.Scale = new Vector3(0, 0, 0);
203
204                 result.Add(target);
205
206                 FocusedIndex++;
207             }
208             else if (isBack && FocusedIndex > 8)
209             {
210                 RecycleItem target = Container.Children[0] as RecycleItem;
211
212                 int previousSiblingOrder = target.SiblingOrder;
213                 target.SiblingOrder = Container.Children.Count - 1;
214                 target.DataIndex = target.DataIndex + Container.Children.Count;
215                 target.Position = new Position(0, Math.Abs(scrollPosition) + 360);
216                 target.Scale = new Vector3(0, 0, 0);
217
218                 result.Add(target);
219
220                 FocusedIndex--;
221             }
222
223             mPrevScrollPosition = scrollPosition;
224
225             return result;
226         }
227
228         private double CalculateXFactor(double y)
229         {
230             double factor1 = Math.Pow(180, 2);
231             double factor2 = Math.Pow(333, 2);
232             double factor3 = Math.Pow((y + 153), 2);
233
234             return Math.Sqrt(factor1 - (factor1 / factor2 * factor3));
235         }
236
237         private float CalculateScaleFactor(float position, float scrollPosition)
238         {
239             float origin = Math.Abs(scrollPosition);
240             float diff = position - origin;
241
242             diff = Math.Max(diff, -180);
243             diff = Math.Min(diff, 180);
244             diff = Math.Abs(diff);
245
246             float result = (float)(CalculateXFactor(diff) / CalculateXFactor(0));
247             return result;
248         }
249
250         public override float CalculateCandidateScrollPosition(float scrollPosition)
251         {
252             int value = (int)(Math.Abs(scrollPosition) / mStepSize);
253             float remain = Math.Abs(scrollPosition) % mStepSize;
254
255             int newValue = remain > mStepSize / 2.0f ? value + 1 : value;
256
257             CurrentFocusedIndex = newValue;
258             return -newValue * mStepSize;
259         }
260     }
261 }