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