lottie: Follow Tizen coding guideline.
[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 public:
8     VLine() : mX1(0), mY1(0), mX2(0), mY2(0) {}
9     VLine(float x1, float y1, float x2, float y2)
10         : mX1(x1), mY1(y1), mX2(x2), mY2(y2)
11     {
12     }
13     VLine(const VPointF &p1, const VPointF &p2)
14         : mX1(p1.x()), mY1(p1.y()), mX2(p2.x()), mY2(p2.y())
15     {
16     }
17     float   length() const;
18     void    splitAtLength(float length, VLine &left, VLine &right) const;
19     VPointF p1() const { return VPointF(mX1, mY1); }
20     VPointF p2() const { return VPointF(mX2, mY2); }
21
22 private:
23     float mX1;
24     float mY1;
25     float mX2;
26     float mY2;
27 };
28
29 // approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
30 // With alpha = 1, beta = 3/8, giving results with the largest error less
31 // than 7% compared to the exact value.
32 float VLine::length() const
33 {
34     float x = mX2 - mX1;
35     float y = mY2 - mY1;
36     x = x < 0 ? -x : x;
37     y = y < 0 ? -y : y;
38     return (x > y ? x + 0.375 * y : y + 0.375 * x);
39 }
40
41 void VLine::splitAtLength(float lengthAt, VLine &left, VLine &right) const
42 {
43     float  len = length();
44     double dx = ((mX2 - mX1) / len) * lengthAt;
45     double dy = ((mY2 - mY1) / len) * lengthAt;
46
47     left.mX1 = mX1;
48     left.mY1 = mY1;
49     left.mX2 = left.mX1 + dx;
50     left.mY2 = left.mY1 + dy;
51
52     right.mX1 = left.mX2;
53     right.mY1 = left.mY2;
54     right.mX2 = mX2;
55     right.mY2 = mY2;
56 }
57
58 VDasher::VDasher(const float *dashArray, int size)
59 {
60     if (!(size % 2)) vCritical << "invalid dashArray format";
61
62     mDashArray = reinterpret_cast<const VDasher::Dash *>(dashArray);
63     mArraySize = size / 2;
64     mDashOffset = dashArray[size - 1];
65     mCurrentDashIndex = 0;
66     mCurrentDashLength = 0;
67     mIsCurrentOperationGap = false;
68 }
69
70 void VDasher::moveTo(const VPointF &p)
71 {
72     mIsCurrentOperationGap = false;
73     mStartPt = p;
74     mCurPt = p;
75
76     if (!vCompare(mDashOffset, 0.0f)) {
77         float totalLength = 0.0;
78         for (int i = 0; i < mArraySize; i++) {
79             totalLength = mDashArray[i].length + mDashArray[i].gap;
80         }
81         float normalizeLen = fmod(mDashOffset, totalLength);
82         if (normalizeLen < 0.0) {
83             normalizeLen = totalLength + normalizeLen;
84         }
85         // now the length is less than total length and +ve
86         // findout the current dash index , dashlength and gap.
87         for (int i = 0; i < mArraySize; i++) {
88             if (normalizeLen < mDashArray[i].length) {
89                 mCurrentDashIndex = i;
90                 mCurrentDashLength = mDashArray[i].length - normalizeLen;
91                 mIsCurrentOperationGap = false;
92                 break;
93             }
94             normalizeLen -= mDashArray[i].length;
95             if (normalizeLen < mDashArray[i].gap) {
96                 mCurrentDashIndex = i;
97                 mCurrentDashLength = mDashArray[i].gap - normalizeLen;
98                 mIsCurrentOperationGap = true;
99                 break;
100             }
101             normalizeLen -= mDashArray[i].gap;
102         }
103     } else {
104         mCurrentDashIndex = 0;
105         mCurrentDashLength = mDashArray[0].length;
106     }
107 }
108
109 void VDasher::lineTo(const VPointF &p)
110 {
111     VLine left, right;
112     VLine line(mCurPt, p);
113     float length = line.length();
114     if (length < mCurrentDashLength) {
115         mCurrentDashLength -= length;
116         if (!mIsCurrentOperationGap) {
117             mDashedPath.moveTo(mCurPt);
118             mDashedPath.lineTo(p);
119         }
120     } else {
121         while (length > mCurrentDashLength) {
122             length -= mCurrentDashLength;
123             line.splitAtLength(mCurrentDashLength, left, right);
124             if (!mIsCurrentOperationGap) {
125                 mDashedPath.moveTo(left.p1());
126                 mDashedPath.lineTo(left.p2());
127                 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
128             } else {
129                 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
130                 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
131             }
132             mIsCurrentOperationGap = !mIsCurrentOperationGap;
133             line = right;
134             mCurPt = line.p1();
135         }
136         // remainder
137         mCurrentDashLength -= length;
138         if (!mIsCurrentOperationGap) {
139             mDashedPath.moveTo(line.p1());
140             mDashedPath.lineTo(line.p2());
141         }
142         if (mCurrentDashLength < 1.0) {
143             // move to next dash
144             if (!mIsCurrentOperationGap) {
145                 mIsCurrentOperationGap = true;
146                 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
147             } else {
148                 mIsCurrentOperationGap = false;
149                 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
150                 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
151             }
152         }
153     }
154     mCurPt = p;
155 }
156
157 void VDasher::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
158 {
159     VBezier left, right;
160     float   bezLen = 0.0;
161     VBezier b = VBezier::fromPoints(mCurPt, cp1, cp2, e);
162     bezLen = b.length();
163     if (bezLen < mCurrentDashLength) {
164         mCurrentDashLength -= bezLen;
165         if (!mIsCurrentOperationGap) {
166             mDashedPath.moveTo(mCurPt);
167             mDashedPath.cubicTo(cp1, cp2, e);
168         }
169     } else {
170         while (bezLen > mCurrentDashLength) {
171             bezLen -= mCurrentDashLength;
172             b.splitAtLength(mCurrentDashLength, &left, &right);
173             if (!mIsCurrentOperationGap) {
174                 mDashedPath.moveTo(left.pt1());
175                 mDashedPath.cubicTo(left.pt2(), left.pt3(), left.pt4());
176                 ;
177                 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
178             } else {
179                 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
180                 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
181             }
182             mIsCurrentOperationGap = !mIsCurrentOperationGap;
183             b = right;
184             mCurPt = b.pt1();
185         }
186         // remainder
187         mCurrentDashLength -= bezLen;
188         if (!mIsCurrentOperationGap) {
189             mDashedPath.moveTo(b.pt1());
190             mDashedPath.cubicTo(b.pt2(), b.pt3(), b.pt4());
191         }
192         if (mCurrentDashLength < 1.0) {
193             // move to next dash
194             if (!mIsCurrentOperationGap) {
195                 mIsCurrentOperationGap = true;
196                 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
197             } else {
198                 mIsCurrentOperationGap = false;
199                 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
200                 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
201             }
202         }
203     }
204     mCurPt = e;
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