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 : LayoutManager
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);
69 public override void Layout(float scrollPosition)
71 RecycleItem centerItem = Container.Children[FocusedIndex] as RecycleItem;
72 float centerItemPosition = LayoutOrientation == Orientation.Horizontal ? centerItem.Position.X : centerItem.Position.Y;
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);
77 if (mStepSize != 0 && centerItemPosition <= stepRange.X)
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);
84 else if (mStepSize != 0 && centerItemPosition >= stepRange.Y)
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);
93 float centerItemScale = CalculateScaleFactor(centerItemPosition, scrollPosition);
94 centerItem.Scale = new Vector3(centerItemScale, centerItemScale, 1.0f);
97 RecycleItem prevItem = centerItem;
99 // Front of center item
100 for (int i = FocusedIndex - 1; i > -1; i--)
102 RecycleItem target = Container.Children[i] as RecycleItem;
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;
110 float startPosition = prevItemPosition - prevItemSize / 2.0f;
112 if (startPosition > visibleRange.X)
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);
120 float scaleFactor = CalculateScaleFactor(candidatePosition, scrollPosition);
121 target.Scale = new Vector3(scaleFactor, scaleFactor, 1.0f);
127 target.Scale = new Vector3(0.0f, 0.0f, 1.0f);
131 prevItem = centerItem;
133 // Back of center item
134 for (int i = FocusedIndex + 1; i < Container.Children.Count; i++)
136 RecycleItem target = Container.Children[i] as RecycleItem;
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;
144 float startPosition = prevItemPosition + prevItemSize / 2.0f;
146 if (startPosition < visibleRange.Y)
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);
154 float scaleFactor = CalculateScaleFactor(candidatePosition, scrollPosition);
155 target.Scale = new Vector3(scaleFactor, scaleFactor, 1.0f);
161 target.Scale = new Vector3(0.0f, 0.0f, 1.0f);
167 if (LayoutOrientation == Orientation.Horizontal)
169 mStepSize = Container.Children[0].Size.Width / 2.0f + Container.Children[1].Size.Width * Container.Children[1].Scale.X / 2.0f;
173 mStepSize = Container.Children[0].Size.Height / 2.0f + Container.Children[1].Size.Height * Container.Children[1].Scale.X / 2.0f;
179 public override List<RecycleItem> Recycle(float scrollPosition)
181 List<RecycleItem> result = new List<RecycleItem>();
183 bool isBack = scrollPosition - mPrevScrollPosition < 0;
185 int previousFocusIndex = FocusedIndex;
187 if (!isBack && FocusedIndex < 6)
189 RecycleItem target = Container.Children[Container.Children.Count - 1] as RecycleItem;
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);
201 else if (isBack && FocusedIndex > 8)
203 RecycleItem target = Container.Children[0] as RecycleItem;
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);
216 mPrevScrollPosition = scrollPosition;
221 private double CalculateXFactor(double y)
223 double factor1 = Math.Pow(180, 2);
224 double factor2 = Math.Pow(333, 2);
225 double factor3 = Math.Pow((y + 153), 2);
227 return Math.Sqrt(factor1 - (factor1 / factor2 * factor3));
230 private float CalculateScaleFactor(float position, float scrollPosition)
232 float origin = Math.Abs(scrollPosition);
233 float diff = position - origin;
235 diff = Math.Max(diff, -180);
236 diff = Math.Min(diff, 180);
237 diff = Math.Abs(diff);
239 float result = (float)(CalculateXFactor(diff) / CalculateXFactor(0));
243 public override float CalculateCandidateScrollPosition(float scrollPosition)
245 int value = (int)(Math.Abs(scrollPosition) / mStepSize);
246 float remain = Math.Abs(scrollPosition) % mStepSize;
248 int newValue = remain > mStepSize / 2.0f ? value + 1 : value;
250 CurrentFocusedIndex = newValue;
251 return -newValue * mStepSize;