1 /* Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 using Tizen.NUI.Components;
18 using System.Collections.Generic;
20 namespace Tizen.NUI.Wearable
23 /// [Draft] This class implements a fish eye layout
25 internal class FishEyeLayoutManager : RecycleLayoutManager
27 public int CurrentFocusedIndex { get; set; } = 0;
28 public int FocusedIndex { get; set; } = 0;
29 public FishEyeLayoutManager()
33 private float FindCandidatePosition(float startPosition, float scrollPosition, bool isBack)
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.
40 double center = Math.Abs(scrollPosition);
41 double result = isBack ? center + 180.0f : center - 180.0f;
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)));
48 double semiMajorAxis = 333.0;
49 double centerOfEllipse = -153.0;
50 double centerOfEllipse2 = 153.0;
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);
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;
65 result = isBack ? result1 : result4;
66 return (float)(center + result);
70 public override float CalculateLayoutOrientationSize()
72 return StepSize * (DataCount-1);
75 public override void Layout(float scrollPosition)
77 RecycleItem centerItem = Container.Children[FocusedIndex] as RecycleItem;
78 if (centerItem == null)
83 float centerItemPosition = LayoutOrientation == Orientation.Horizontal ? centerItem.Position.X : centerItem.Position.Y;
85 Vector2 stepRange = new Vector2(-scrollPosition - StepSize + 1.0f, -scrollPosition + StepSize - 1.0f);
86 Vector2 visibleRange = new Vector2(Math.Abs(scrollPosition) - 180, Math.Abs(scrollPosition) + 180);
88 if (StepSize != 0 && centerItemPosition <= stepRange.X)
90 FocusedIndex = Math.Min(Container.Children.Count - 1, FocusedIndex + 1);
91 centerItem = Container.Children[FocusedIndex] as RecycleItem;
92 centerItem.Position = new Position(0.0f, Math.Abs(StepSize * (centerItem.DataIndex)));
93 centerItem.Scale = new Vector3(1.0f, 1.0f, 1.0f);
95 else if (StepSize != 0 && centerItemPosition >= stepRange.Y)
97 FocusedIndex = Math.Max(0, FocusedIndex - 1);
98 centerItem = Container.Children[FocusedIndex] as RecycleItem;
99 if (centerItem != null)
101 centerItem.Position = new Position(0.0f, Math.Abs(StepSize * (centerItem.DataIndex)));
102 centerItem.Scale = new Vector3(1.0f, 1.0f, 1.0f);
107 float centerItemScale = CalculateScaleFactor(centerItemPosition, scrollPosition);
108 centerItem.Scale = new Vector3(centerItemScale, centerItemScale, 1.0f);
111 RecycleItem prevItem = centerItem;
113 // Front of center item
114 for (int i = FocusedIndex - 1; i > -1; i--)
116 RecycleItem target = Container.Children[i] as RecycleItem;
122 float prevItemPosition = LayoutOrientation == Orientation.Horizontal ? prevItem.Position.X : prevItem.Position.Y;
123 float prevItemSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.Size.Width : prevItem.Size.Height);
124 float prevItemCurrentSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.GetCurrentSizeFloat().Width : prevItem.GetCurrentSizeFloat().Height);
125 prevItemSize = prevItemCurrentSize == 0 ? prevItemSize : prevItemCurrentSize;
126 prevItemSize = prevItemSize * prevItem.Scale.X;
128 float startPosition = prevItemPosition - prevItemSize / 2.0f;
130 if (startPosition > visibleRange.X)
132 float candidatePosition = visibleRange.X;
133 candidatePosition = FindCandidatePosition(startPosition, scrollPosition, false);
134 target.Position = LayoutOrientation == Orientation.Horizontal ?
135 new Position(candidatePosition, target.Position.Y) :
136 new Position(target.Position.X, candidatePosition);
138 float scaleFactor = CalculateScaleFactor(candidatePosition, scrollPosition);
139 target.Scale = new Vector3(scaleFactor, scaleFactor, 1.0f);
145 target.Scale = new Vector3(0.0f, 0.0f, 1.0f);
149 prevItem = centerItem;
151 // Back of center item
152 for (int i = FocusedIndex + 1; i < Container.Children.Count; i++)
154 RecycleItem target = Container.Children[i] as RecycleItem;
160 float prevItemPosition = LayoutOrientation == Orientation.Horizontal ? prevItem.Position.X : prevItem.Position.Y;
161 float prevItemSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.Size.Width : prevItem.Size.Height);
162 float prevItemCurrentSize = (LayoutOrientation == Orientation.Horizontal ? prevItem.GetCurrentSizeFloat().Width : prevItem.GetCurrentSizeFloat().Height);
163 prevItemSize = prevItemCurrentSize == 0 ? prevItemSize : prevItemCurrentSize;
164 prevItemSize = prevItemSize * prevItem.Scale.X;
166 float startPosition = prevItemPosition + prevItemSize / 2.0f;
168 if (startPosition < visibleRange.Y)
170 float candidatePosition = visibleRange.Y;
171 candidatePosition = FindCandidatePosition(startPosition, scrollPosition, true);
172 target.Position = LayoutOrientation == Orientation.Horizontal ?
173 new Position(candidatePosition, target.Position.Y) :
174 new Position(target.Position.X, candidatePosition);
176 float scaleFactor = CalculateScaleFactor(candidatePosition, scrollPosition);
177 target.Scale = new Vector3(scaleFactor, scaleFactor, 1.0f);
183 target.Scale = new Vector3(0.0f, 0.0f, 1.0f);
189 if (LayoutOrientation == Orientation.Horizontal)
191 StepSize = Container.Children[0].Size.Width / 2.0f + Container.Children[1].Size.Width * Container.Children[1].Scale.X / 2.0f;
195 StepSize = Container.Children[0].Size.Height / 2.0f + Container.Children[1].Size.Height * Container.Children[1].Scale.X / 2.0f;
198 StepSize = float.IsNaN(StepSize)?0:StepSize;
202 public override List<RecycleItem> Recycle(float scrollPosition)
204 List<RecycleItem> result = new List<RecycleItem>();
206 bool isBack = scrollPosition - PrevScrollPosition < 0;
208 int previousFocusIndex = FocusedIndex;
210 if (!isBack && FocusedIndex < 6)
212 RecycleItem target = Container.Children[Container.Children.Count - 1] as RecycleItem;
215 int previousSiblingOrder = target.SiblingOrder;
216 target.SiblingOrder = 0;
217 target.DataIndex = target.DataIndex - Container.Children.Count;
218 target.Position = new Position(0, Math.Abs(scrollPosition) - 360);
219 target.Scale = new Vector3(0, 0, 0);
226 else if (isBack && FocusedIndex > 8)
228 RecycleItem target = Container.Children[0] as RecycleItem;
231 int previousSiblingOrder = target.SiblingOrder;
232 target.SiblingOrder = Container.Children.Count - 1;
233 target.DataIndex = target.DataIndex + Container.Children.Count;
234 target.Position = new Position(0, Math.Abs(scrollPosition) + 360);
235 target.Scale = new Vector3(0, 0, 0);
243 PrevScrollPosition = scrollPosition;
248 private double CalculateXFactor(double y)
250 double factor1 = Math.Pow(180, 2);
251 double factor2 = Math.Pow(333, 2);
252 double factor3 = Math.Pow((y + 153), 2);
254 return Math.Sqrt(factor1 - (factor1 / factor2 * factor3));
257 private float CalculateScaleFactor(float position, float scrollPosition)
259 float origin = Math.Abs(scrollPosition);
260 float diff = position - origin;
262 diff = Math.Max(diff, -180);
263 diff = Math.Min(diff, 180);
264 diff = Math.Abs(diff);
266 float result = (float)(CalculateXFactor(diff) / CalculateXFactor(0));
270 public override float CalculateCandidateScrollPosition(float scrollPosition)
272 int value = (int)(Math.Abs(scrollPosition) / StepSize);
273 float remain = Math.Abs(scrollPosition) % StepSize;
275 int newValue = remain > StepSize / 2.0f ? value + 1 : value;
277 CurrentFocusedIndex = newValue;
278 return -newValue * StepSize;