ab7e1a453191c82ef105238984e810c1541e9791
[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 : LayoutManager
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         public override void Layout(float scrollPosition)
70         {
71             RecycleItem centerItem = Container.Children[FocusedIndex] as RecycleItem;
72             float centerItemPosition = LayoutOrientation == Orientation.Horizontal ? centerItem.Position.X : centerItem.Position.Y;
73
74             Vector2 stepRange = new Vector2(-scrollPosition - mStepSize + 1.0f, -scrollPosition + mStepSize - 1.0f);
75             Vector2 visibleRange = new Vector2(Math.Abs(scrollPosition) - 180, Math.Abs(scrollPosition) + 180);
76
77             if (mStepSize != 0 && centerItemPosition <= stepRange.X)
78             {
79                 FocusedIndex = Math.Min(Container.Children.Count - 1, FocusedIndex + 1);
80                 centerItem = Container.Children[FocusedIndex] as RecycleItem;
81                 centerItem.Position = new Position(0.0f, Math.Abs(mStepSize * (centerItem.DataIndex)));
82                 centerItem.Scale = new Vector3(1.0f, 1.0f, 1.0f);
83             }
84             else if (mStepSize != 0 && centerItemPosition >= stepRange.Y)
85             {
86                 FocusedIndex = Math.Max(0, FocusedIndex - 1);
87                 centerItem = Container.Children[FocusedIndex] as RecycleItem;
88                 centerItem.Position = new Position(0.0f, Math.Abs(mStepSize * (centerItem.DataIndex)));
89                 centerItem.Scale = new Vector3(1.0f, 1.0f, 1.0f);
90             }
91             else
92             {
93                 float centerItemScale = CalculateScaleFactor(centerItemPosition, scrollPosition);
94                 centerItem.Scale = new Vector3(centerItemScale, centerItemScale, 1.0f);
95             }
96
97             RecycleItem prevItem = centerItem;
98
99             // Front of center item
100             for (int i = FocusedIndex - 1; i > -1; i--)
101             {
102                 RecycleItem target = Container.Children[i] as RecycleItem;
103
104                 float prevItemPosition = LayoutOrientation == Orientation.Horizontal ? prevItem.Position.X : prevItem.Position.Y;
105                 float prevItemSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.Size.Width : prevItem.Size.Height);
106                 float prevItemCurrentSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.GetCurrentSizeFloat().Width : prevItem.GetCurrentSizeFloat().Height);
107                 prevItemSize = prevItemCurrentSize == 0 ? prevItemSize : prevItemCurrentSize;
108                 prevItemSize = prevItemSize * prevItem.Scale.X;
109
110                 float startPosition = prevItemPosition - prevItemSize / 2.0f;
111
112                 if (startPosition > visibleRange.X)
113                 {
114                     float candidatePosition = visibleRange.X;
115                     candidatePosition = FindCandidatePosition(startPosition, scrollPosition, false);
116                     target.Position = LayoutOrientation == Orientation.Horizontal ?
117                                 new Position(candidatePosition, target.Position.Y) :
118                                 new Position(target.Position.X, candidatePosition);
119
120                     float scaleFactor = CalculateScaleFactor(candidatePosition, scrollPosition);
121                     target.Scale = new Vector3(scaleFactor, scaleFactor, 1.0f);
122
123                     prevItem = target;
124                 }
125                 else
126                 {
127                     target.Scale = new Vector3(0.0f, 0.0f, 1.0f);
128                 }
129             }
130
131             prevItem = centerItem;
132
133             // Back of center item
134             for (int i = FocusedIndex + 1; i < Container.Children.Count; i++)
135             {
136                 RecycleItem target = Container.Children[i] as RecycleItem;
137
138                 float prevItemPosition = LayoutOrientation == Orientation.Horizontal ? prevItem.Position.X : prevItem.Position.Y;
139                 float prevItemSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.Size.Width : prevItem.Size.Height);
140                 float prevItemCurrentSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.GetCurrentSizeFloat().Width : prevItem.GetCurrentSizeFloat().Height);
141                 prevItemSize = prevItemCurrentSize == 0 ? prevItemSize : prevItemCurrentSize;
142                 prevItemSize = prevItemSize * prevItem.Scale.X;
143
144                 float startPosition = prevItemPosition + prevItemSize / 2.0f;
145
146                 if (startPosition < visibleRange.Y)
147                 {
148                     float candidatePosition = visibleRange.Y;
149                     candidatePosition = FindCandidatePosition(startPosition, scrollPosition, true);
150                     target.Position = LayoutOrientation == Orientation.Horizontal ?
151                                 new Position(candidatePosition, target.Position.Y) :
152                                 new Position(target.Position.X, candidatePosition);
153
154                     float scaleFactor = CalculateScaleFactor(candidatePosition, scrollPosition);
155                     target.Scale = new Vector3(scaleFactor, scaleFactor, 1.0f);
156
157                     prevItem = target;
158                 }
159                 else
160                 {
161                     target.Scale = new Vector3(0.0f, 0.0f, 1.0f);
162                 }
163             }
164
165             if (mStepSize == 0)
166             {
167                 if (LayoutOrientation == Orientation.Horizontal)
168                 {
169                     mStepSize = Container.Children[0].Size.Width / 2.0f + Container.Children[1].Size.Width * Container.Children[1].Scale.X / 2.0f;
170                 }
171                 else
172                 {
173                     mStepSize = Container.Children[0].Size.Height / 2.0f + Container.Children[1].Size.Height * Container.Children[1].Scale.X / 2.0f;
174                 }
175
176             }
177         }
178
179         public override List<RecycleItem> Recycle(float scrollPosition)
180         {
181             List<RecycleItem> result = new List<RecycleItem>();
182
183             bool isBack = scrollPosition - mPrevScrollPosition < 0;
184
185             int previousFocusIndex = FocusedIndex;
186
187             if (!isBack && FocusedIndex < 6)
188             {
189                 RecycleItem target = Container.Children[Container.Children.Count - 1] as RecycleItem;
190
191                 int previousSiblingOrder = target.SiblingOrder;
192                 target.SiblingOrder = 0;
193                 target.DataIndex = target.DataIndex - Container.Children.Count;
194                 target.Position = new Position(0, Math.Abs(scrollPosition) - 360);
195                 target.Scale = new Vector3(0, 0, 0);
196
197                 result.Add(target);
198
199                 FocusedIndex++;
200             }
201             else if (isBack && FocusedIndex > 8)
202             {
203                 RecycleItem target = Container.Children[0] as RecycleItem;
204
205                 int previousSiblingOrder = target.SiblingOrder;
206                 target.SiblingOrder = Container.Children.Count - 1;
207                 target.DataIndex = target.DataIndex + Container.Children.Count;
208                 target.Position = new Position(0, Math.Abs(scrollPosition) + 360);
209                 target.Scale = new Vector3(0, 0, 0);
210
211                 result.Add(target);
212
213                 FocusedIndex--;
214             }
215
216             mPrevScrollPosition = scrollPosition;
217
218             return result;
219         }
220
221         private double CalculateXFactor(double y)
222         {
223             double factor1 = Math.Pow(180, 2);
224             double factor2 = Math.Pow(333, 2);
225             double factor3 = Math.Pow((y + 153), 2);
226
227             return Math.Sqrt(factor1 - (factor1 / factor2 * factor3));
228         }
229
230         private float CalculateScaleFactor(float position, float scrollPosition)
231         {
232             float origin = Math.Abs(scrollPosition);
233             float diff = position - origin;
234
235             diff = Math.Max(diff, -180);
236             diff = Math.Min(diff, 180);
237             diff = Math.Abs(diff);
238
239             float result = (float)(CalculateXFactor(diff) / CalculateXFactor(0));
240             return result;
241         }
242
243         public override float CalculateCandidateScrollPosition(float scrollPosition)
244         {
245             int value = (int)(Math.Abs(scrollPosition) / mStepSize);
246             float remain = Math.Abs(scrollPosition) % mStepSize;
247
248             int newValue = remain > mStepSize / 2.0f ? value + 1 : value;
249
250             CurrentFocusedIndex = newValue;
251             return -newValue * mStepSize;
252         }
253     }
254 }