lottie/vector: move line related api to its own class .
[platform/core/uifw/lottie-player.git] / src / vector / vdasher.cpp
1 #include "vdasher.h"
2 #include "vbezier.h"
3 #include "vline.h"
4
5 V_BEGIN_NAMESPACE
6
7 VDasher::VDasher(const float *dashArray, int size)
8 {
9     if (!(size % 2)) vCritical << "invalid dashArray format";
10
11     mDashArray = reinterpret_cast<const VDasher::Dash *>(dashArray);
12     mArraySize = size / 2;
13     mDashOffset = dashArray[size - 1];
14     mIndex = 0;
15     mCurrentLength = 0;
16     mDiscard = false;
17 }
18
19 void VDasher::moveTo(const VPointF &p)
20 {
21     mDiscard = false;
22     mStartNewSegment = true;
23     mCurPt = p;
24     mIndex = 0;
25
26     if (!vCompare(mDashOffset, 0.0f)) {
27         float totalLength = 0.0;
28         for (int i = 0; i < mArraySize; i++) {
29             totalLength = mDashArray[i].length + mDashArray[i].gap;
30         }
31         float normalizeLen = fmod(mDashOffset, totalLength);
32         if (normalizeLen < 0.0) {
33             normalizeLen = totalLength + normalizeLen;
34         }
35         // now the length is less than total length and +ve
36         // findout the current dash index , dashlength and gap.
37         for (int i = 0; i < mArraySize; i++) {
38             if (normalizeLen < mDashArray[i].length) {
39                 mIndex = i;
40                 mCurrentLength = mDashArray[i].length - normalizeLen;
41                 mDiscard = false;
42                 break;
43             }
44             normalizeLen -= mDashArray[i].length;
45             if (normalizeLen < mDashArray[i].gap) {
46                 mIndex = i;
47                 mCurrentLength = mDashArray[i].gap - normalizeLen;
48                 mDiscard = true;
49                 break;
50             }
51             normalizeLen -= mDashArray[i].gap;
52         }
53     } else {
54         mCurrentLength = mDashArray[mIndex].length;
55     }
56     if (vIsZero(mCurrentLength)) updateActiveSegment();
57 }
58
59 void VDasher::addLine(const VPointF &p)
60 {
61    if (mDiscard) return;
62
63    if (mStartNewSegment) {
64         mResult.moveTo(mCurPt);
65         mStartNewSegment = false;
66    }
67    mResult.lineTo(p);
68 }
69
70 void VDasher::updateActiveSegment()
71 {
72     mStartNewSegment = true;
73
74     if (mDiscard) {
75         mDiscard = false;
76         mIndex = (mIndex + 1) % mArraySize;
77         mCurrentLength = mDashArray[mIndex].length;
78     } else {
79         mDiscard = true;
80         mCurrentLength = mDashArray[mIndex].gap;
81     }
82     if (vIsZero(mCurrentLength)) updateActiveSegment();
83 }
84
85 void VDasher::lineTo(const VPointF &p)
86 {
87     VLine left, right;
88     VLine line(mCurPt, p);
89     float length = line.length();
90
91     if (length <= mCurrentLength) {
92         mCurrentLength -= length;
93         addLine(p);
94     } else {
95         while (length > mCurrentLength) {
96             length -= mCurrentLength;
97             line.splitAtLength(mCurrentLength, left, right);
98
99             addLine(left.p2());
100             updateActiveSegment();
101
102             line = right;
103             mCurPt = line.p1();
104         }
105         // handle remainder
106         if (length > 1.0) {
107             mCurrentLength -= length;
108             addLine(line.p2());
109         }
110     }
111
112     if (mCurrentLength < 1.0) updateActiveSegment();
113
114     mCurPt = p;
115 }
116
117 void VDasher::addCubic(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
118 {
119     if (mDiscard) return;
120
121     if (mStartNewSegment) {
122         mResult.moveTo(mCurPt);
123         mStartNewSegment = false;
124     }
125     mResult.cubicTo(cp1, cp2, e);
126 }
127
128 void VDasher::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
129 {
130     VBezier left, right;
131     float   bezLen = 0.0;
132     VBezier b = VBezier::fromPoints(mCurPt, cp1, cp2, e);
133     bezLen = b.length();
134
135     if (bezLen <= mCurrentLength) {
136         mCurrentLength -= bezLen;
137         addCubic(cp1, cp2, e);
138     } else {
139         while (bezLen > mCurrentLength) {
140             bezLen -= mCurrentLength;
141             b.splitAtLength(mCurrentLength, &left, &right);
142
143             addCubic(left.pt2(), left.pt3(), left.pt4());
144             updateActiveSegment();
145
146             b = right;
147             mCurPt = b.pt1();
148         }
149         // handle remainder
150         if (bezLen > 1.0) {
151             mCurrentLength -= bezLen;
152             addCubic(b.pt2(), b.pt3(), b.pt4());
153         }
154     }
155
156     if (mCurrentLength < 1.0) updateActiveSegment();
157
158     mCurPt = e;
159 }
160
161 VPath VDasher::dashed(const VPath &path)
162 {
163     if (path.isEmpty()) return VPath();
164
165     mResult = VPath();
166     mIndex = 0;
167     const std::vector<VPath::Element> &elms = path.elements();
168     const std::vector<VPointF> &       pts = path.points();
169     const VPointF *                    ptPtr = pts.data();
170
171     for (auto &i : elms) {
172         switch (i) {
173         case VPath::Element::MoveTo: {
174             moveTo(*ptPtr++);
175             break;
176         }
177         case VPath::Element::LineTo: {
178             lineTo(*ptPtr++);
179             break;
180         }
181         case VPath::Element::CubicTo: {
182             cubicTo(*ptPtr, *(ptPtr + 1), *(ptPtr + 2));
183             ptPtr += 3;
184             break;
185         }
186         case VPath::Element::Close: {
187             // The point is already joined to start point in VPath
188             // no need to do anything here.
189             break;
190         }
191         default:
192             break;
193         }
194     }
195     return std::move(mResult);
196 }
197
198 V_END_NAMESPACE