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()){}
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);}
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.
33 return (x > y ? x + 0.375 * y : y + 0.375 * x);
37 VLine::splitAtLength(float lengthAt, VLine &left, VLine &right) const
40 double dx = ((mX2 - mX1)/len) *lengthAt;
41 double dy = ((mY2 - mY1)/len) *lengthAt;
45 left.mX2 = left.mX1 + dx;
46 left.mY2 = left.mY1 + dy;
54 VDasher::VDasher(const float *dashArray, int size)
57 vCritical<<"invalid dashArray format";
59 mDashArray = reinterpret_cast<const VDasher::Dash *>(dashArray);
61 mDashOffset = dashArray[size-1];
62 mCurrentDashIndex = 0;
63 mCurrentDashLength = 0;
64 mIsCurrentOperationGap = false;
67 void VDasher::moveTo(const VPointF &p)
69 mIsCurrentOperationGap = false;
73 if (!floatCmp(mDashOffset, 0.0)) {
74 float totalLength = 0.0;
75 for (int i = 0; i < mArraySize ; i++) {
76 totalLength = mDashArray[i].length + mDashArray[i].gap;
78 float normalizeLen = fmod(mDashOffset, totalLength);
79 if (normalizeLen < 0.0 ) {
80 normalizeLen = totalLength + normalizeLen;
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;
91 normalizeLen -= mDashArray[i].length;
92 if (normalizeLen < mDashArray[i].gap) {
93 mCurrentDashIndex = i;
94 mCurrentDashLength = mDashArray[i].gap - normalizeLen;
95 mIsCurrentOperationGap = true;
98 normalizeLen -= mDashArray[i].gap;
101 mCurrentDashIndex = 0;
102 mCurrentDashLength = mDashArray[0].length;
106 void VDasher::lineTo(const VPointF &p)
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);
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;
126 mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize ;
127 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
129 mIsCurrentOperationGap = !mIsCurrentOperationGap;
134 mCurrentDashLength -= length;
135 if (!mIsCurrentOperationGap) {
136 mDashedPath.moveTo(line.p1());
137 mDashedPath.lineTo(line.p2());
139 if (mCurrentDashLength < 1.0) {
141 if (!mIsCurrentOperationGap) {
142 mIsCurrentOperationGap = true;
143 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
145 mIsCurrentOperationGap = false;
146 mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize;
147 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
154 void VDasher::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
158 VBezier b = VBezier::fromPoints(mCurPt, cp1, cp2, e);
160 if (bezLen < mCurrentDashLength) {
161 mCurrentDashLength -= bezLen;
162 if (!mIsCurrentOperationGap) {
163 mDashedPath.moveTo(mCurPt);
164 mDashedPath.cubicTo(cp1, cp2, e);
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;
175 mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize ;
176 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
178 mIsCurrentOperationGap = !mIsCurrentOperationGap;
183 mCurrentDashLength -= bezLen;
184 if (!mIsCurrentOperationGap) {
185 mDashedPath.moveTo(b.pt1());
186 mDashedPath.cubicTo(b.pt2(), b.pt3(), b.pt4());
188 if (mCurrentDashLength < 1.0) {
190 if (!mIsCurrentOperationGap)
192 mIsCurrentOperationGap = true;
193 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
197 mIsCurrentOperationGap = false;
198 mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize;
199 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
207 VPath VDasher::dashed(const VPath &path)
209 if (path.isEmpty()) return VPath();
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();
216 for (auto i : elms) {
218 case VPath::Element::MoveTo: {
222 case VPath::Element::LineTo: {
226 case VPath::Element::CubicTo: {
227 cubicTo(*ptPtr, *(ptPtr + 1), *(ptPtr + 2));
231 case VPath::Element::Close: {
232 // The point is already joined to start point in VPath
233 // no need to do anything here.