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 float centerItemPosition = LayoutOrientation == Orientation.Horizontal ? centerItem.Position.X : centerItem.Position.Y;
80 Vector2 stepRange = new Vector2(-scrollPosition - StepSize + 1.0f, -scrollPosition + StepSize - 1.0f);
81 Vector2 visibleRange = new Vector2(Math.Abs(scrollPosition) - 180, Math.Abs(scrollPosition) + 180);
83 if (StepSize != 0 && centerItemPosition <= stepRange.X)
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(StepSize * (centerItem.DataIndex)));
88 centerItem.Scale = new Vector3(1.0f, 1.0f, 1.0f);
90 else if (StepSize != 0 && centerItemPosition >= stepRange.Y)
92 FocusedIndex = Math.Max(0, FocusedIndex - 1);
93 centerItem = Container.Children[FocusedIndex] as RecycleItem;
94 centerItem.Position = new Position(0.0f, Math.Abs(StepSize * (centerItem.DataIndex)));
95 centerItem.Scale = new Vector3(1.0f, 1.0f, 1.0f);
99 float centerItemScale = CalculateScaleFactor(centerItemPosition, scrollPosition);
100 centerItem.Scale = new Vector3(centerItemScale, centerItemScale, 1.0f);
103 RecycleItem prevItem = centerItem;
105 // Front of center item
106 for (int i = FocusedIndex - 1; i > -1; i--)
108 RecycleItem target = Container.Children[i] as RecycleItem;
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;
116 float startPosition = prevItemPosition - prevItemSize / 2.0f;
118 if (startPosition > visibleRange.X)
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);
126 float scaleFactor = CalculateScaleFactor(candidatePosition, scrollPosition);
127 target.Scale = new Vector3(scaleFactor, scaleFactor, 1.0f);
133 target.Scale = new Vector3(0.0f, 0.0f, 1.0f);
137 prevItem = centerItem;
139 // Back of center item
140 for (int i = FocusedIndex + 1; i < Container.Children.Count; i++)
142 RecycleItem target = Container.Children[i] as RecycleItem;
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;
150 float startPosition = prevItemPosition + prevItemSize / 2.0f;
152 if (startPosition < visibleRange.Y)
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);
160 float scaleFactor = CalculateScaleFactor(candidatePosition, scrollPosition);
161 target.Scale = new Vector3(scaleFactor, scaleFactor, 1.0f);
167 target.Scale = new Vector3(0.0f, 0.0f, 1.0f);
173 if (LayoutOrientation == Orientation.Horizontal)
175 StepSize = Container.Children[0].Size.Width / 2.0f + Container.Children[1].Size.Width * Container.Children[1].Scale.X / 2.0f;
179 StepSize = Container.Children[0].Size.Height / 2.0f + Container.Children[1].Size.Height * Container.Children[1].Scale.X / 2.0f;
182 StepSize = float.IsNaN(StepSize)?0:StepSize;
186 public override List<RecycleItem> Recycle(float scrollPosition)
188 List<RecycleItem> result = new List<RecycleItem>();
190 bool isBack = scrollPosition - PrevScrollPosition < 0;
192 int previousFocusIndex = FocusedIndex;
194 if (!isBack && FocusedIndex < 6)
196 RecycleItem target = Container.Children[Container.Children.Count - 1] as RecycleItem;
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);
208 else if (isBack && FocusedIndex > 8)
210 RecycleItem target = Container.Children[0] as RecycleItem;
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);
223 PrevScrollPosition = scrollPosition;
228 private double CalculateXFactor(double y)
230 double factor1 = Math.Pow(180, 2);
231 double factor2 = Math.Pow(333, 2);
232 double factor3 = Math.Pow((y + 153), 2);
234 return Math.Sqrt(factor1 - (factor1 / factor2 * factor3));
237 private float CalculateScaleFactor(float position, float scrollPosition)
239 float origin = Math.Abs(scrollPosition);
240 float diff = position - origin;
242 diff = Math.Max(diff, -180);
243 diff = Math.Min(diff, 180);
244 diff = Math.Abs(diff);
246 float result = (float)(CalculateXFactor(diff) / CalculateXFactor(0));
250 public override float CalculateCandidateScrollPosition(float scrollPosition)
252 int value = (int)(Math.Abs(scrollPosition) / StepSize);
253 float remain = Math.Abs(scrollPosition) % StepSize;
255 int newValue = remain > StepSize / 2.0f ? value + 1 : value;
257 CurrentFocusedIndex = newValue;
258 return -newValue * StepSize;