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