vector: clean up single, double-decision comparison
[platform/core/uifw/lottie-player.git] / src / vector / vdasher.cpp
1 #include"vdasher.h"
2 #include"vbezier.h"
3
4 V_BEGIN_NAMESPACE
5
6 class VLine
7 {
8 public:
9     VLine():mX1(0),mY1(0),mX2(0),mY2(0){}
10     VLine(float x1, float y1, float x2, float y2):mX1(x1),mY1(y1),mX2(x2),mY2(y2){}
11     VLine(const VPointF &p1, const VPointF &p2):mX1(p1.x()),mY1(p1.y()),mX2(p2.x()),mY2(p2.y()){}
12     float length() const;
13     void splitAtLength(float length, VLine &left, VLine &right) const;
14     VPointF p1() const {return VPointF(mX1, mY1);}
15     VPointF p2() const {return VPointF(mX2, mY2);}
16 private:
17     float mX1;
18     float mY1;
19     float mX2;
20     float mY2;
21 };
22
23 // approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
24 // With alpha = 1, beta = 3/8, giving results with the largest error less
25 // than 7% compared to the exact value.
26 float
27 VLine::length() const
28 {
29    float x = mX2 - mX1;
30    float y = mY2 - mY1;
31    x = x < 0 ? -x : x;
32    y = y < 0 ? -y : y;
33    return (x > y ? x + 0.375 * y : y + 0.375 * x);
34 }
35
36 void
37 VLine::splitAtLength(float lengthAt, VLine &left, VLine &right) const
38 {
39    float len = length();
40    double dx = ((mX2 - mX1)/len) *lengthAt;
41    double dy = ((mY2 - mY1)/len) *lengthAt;
42
43    left.mX1 = mX1;
44    left.mY1 = mY1;
45    left.mX2 = left.mX1 + dx;
46    left.mY2 = left.mY1 + dy;
47
48    right.mX1 = left.mX2;
49    right.mY1 = left.mY2;
50    right.mX2 = mX2;
51    right.mY2 = mY2;
52 }
53
54 VDasher::VDasher(const float *dashArray, int size)
55 {
56     if (!(size % 2))
57         vCritical<<"invalid dashArray format";
58
59     mDashArray = reinterpret_cast<const VDasher::Dash *>(dashArray);
60     mArraySize = size/2;
61     mDashOffset = dashArray[size-1];
62     mCurrentDashIndex = 0;
63     mCurrentDashLength = 0;
64     mIsCurrentOperationGap = false;
65 }
66
67 void VDasher::moveTo(const VPointF &p)
68 {
69     mIsCurrentOperationGap = false;
70     mStartPt = p;
71     mCurPt = p;
72
73     if (!vCompare(mDashOffset, 0.0f)) {
74         float totalLength = 0.0;
75         for (int i = 0; i < mArraySize ; i++) {
76             totalLength = mDashArray[i].length + mDashArray[i].gap;
77         }
78         float normalizeLen = fmod(mDashOffset, totalLength);
79         if (normalizeLen < 0.0 ) {
80             normalizeLen = totalLength + normalizeLen;
81         }
82         // now the length is less than total length and +ve
83         // findout the current dash index , dashlength and gap.
84         for (int i = 0; i < mArraySize; i++) {
85             if (normalizeLen < mDashArray[i].length) {
86                 mCurrentDashIndex = i;
87                 mCurrentDashLength = mDashArray[i].length - normalizeLen;
88                 mIsCurrentOperationGap = false;
89                 break;
90             }
91             normalizeLen -= mDashArray[i].length;
92             if (normalizeLen < mDashArray[i].gap) {
93                 mCurrentDashIndex = i;
94                 mCurrentDashLength = mDashArray[i].gap - normalizeLen;
95                 mIsCurrentOperationGap = true;
96                 break;
97             }
98             normalizeLen -= mDashArray[i].gap;
99         }
100     } else {
101         mCurrentDashIndex = 0;
102         mCurrentDashLength = mDashArray[0].length;
103     }
104 }
105
106 void VDasher::lineTo(const VPointF &p)
107 {
108     VLine left, right;
109     VLine line(mCurPt, p);
110     float length = line.length();
111     if (length < mCurrentDashLength) {
112          mCurrentDashLength -= length;
113          if (!mIsCurrentOperationGap) {
114              mDashedPath.moveTo(mCurPt);
115              mDashedPath.lineTo(p);
116          }
117     } else {
118          while (length > mCurrentDashLength) {
119               length -= mCurrentDashLength;
120               line.splitAtLength(mCurrentDashLength, left, right);
121               if (!mIsCurrentOperationGap) {
122                   mDashedPath.moveTo(left.p1());
123                   mDashedPath.lineTo(left.p2());
124                   mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
125               } else {
126                   mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize ;
127                   mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
128               }
129               mIsCurrentOperationGap = !mIsCurrentOperationGap;
130               line = right;
131               mCurPt = line.p1();
132          }
133          // remainder
134          mCurrentDashLength -= length;
135          if (!mIsCurrentOperationGap) {
136              mDashedPath.moveTo(line.p1());
137              mDashedPath.lineTo(line.p2());
138          }
139          if (mCurrentDashLength < 1.0) {
140               // move to next dash
141               if (!mIsCurrentOperationGap) {
142                    mIsCurrentOperationGap = true;
143                    mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
144               } else {
145                    mIsCurrentOperationGap = false;
146                    mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize;
147                    mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
148               }
149          }
150     }
151     mCurPt = p;
152 }
153
154 void VDasher::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
155 {
156     VBezier left, right;
157     float bezLen = 0.0;
158     VBezier b = VBezier::fromPoints(mCurPt, cp1, cp2, e);
159     bezLen = b.length();
160     if (bezLen < mCurrentDashLength) {
161          mCurrentDashLength -= bezLen;
162          if (!mIsCurrentOperationGap) {
163               mDashedPath.moveTo(mCurPt);
164               mDashedPath.cubicTo(cp1, cp2, e);
165          }
166     } else {
167          while (bezLen > mCurrentDashLength) {
168               bezLen -= mCurrentDashLength;
169               b.splitAtLength(mCurrentDashLength, &left, &right);
170               if (!mIsCurrentOperationGap) {
171                    mDashedPath.moveTo(left.pt1());
172                    mDashedPath.cubicTo(left.pt2(), left.pt3(), left.pt4());;
173                    mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
174               } else {
175                    mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize ;
176                    mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
177               }
178               mIsCurrentOperationGap = !mIsCurrentOperationGap;
179               b = right;
180               mCurPt = b.pt1();
181         }
182           // remainder
183          mCurrentDashLength -= bezLen;
184          if (!mIsCurrentOperationGap) {
185               mDashedPath.moveTo(b.pt1());
186               mDashedPath.cubicTo(b.pt2(), b.pt3(), b.pt4());
187          }
188          if (mCurrentDashLength < 1.0) {
189               // move to next dash
190               if (!mIsCurrentOperationGap)
191                 {
192                    mIsCurrentOperationGap = true;
193                    mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
194                 }
195               else
196                 {
197                    mIsCurrentOperationGap = false;
198                    mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize;
199                    mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
200                 }
201          }
202     }
203     mCurPt = e;
204 }
205
206
207 VPath VDasher::dashed(const VPath &path)
208 {
209     if (path.isEmpty()) return VPath();
210
211     mDashedPath = VPath();
212     const std::vector<VPath::Element> &elms = path.elements();
213     const std::vector<VPointF> &pts = path.points();
214     const VPointF *ptPtr = pts.data();
215
216     for (auto i : elms) {
217         switch (i) {
218             case VPath::Element::MoveTo: {
219                 moveTo(*ptPtr++);
220                 break;
221             }
222             case VPath::Element::LineTo: {
223                 lineTo(*ptPtr++);
224                 break;
225             }
226             case VPath::Element::CubicTo: {
227                 cubicTo(*ptPtr, *(ptPtr + 1), *(ptPtr + 2));
228                 ptPtr += 3;
229                 break;
230             }
231             case VPath::Element::Close: {
232                 // The point is already joined to start point in VPath
233                 // no need to do anything here.
234                 break;
235             }
236             default:
237                 break;
238         }
239     }
240     return mDashedPath;
241 }
242
243 V_END_NAMESPACE