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)
13 VLine(const VPointF &p1, const VPointF &p2)
14 : mX1(p1.x()), mY1(p1.y()), mX2(p2.x()), mY2(p2.y())
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); }
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
38 return (x > y ? x + 0.375 * y : y + 0.375 * x);
41 void VLine::splitAtLength(float lengthAt, VLine &left, VLine &right) const
44 double dx = ((mX2 - mX1) / len) * lengthAt;
45 double dy = ((mY2 - mY1) / len) * lengthAt;
49 left.mX2 = left.mX1 + dx;
50 left.mY2 = left.mY1 + dy;
58 VDasher::VDasher(const float *dashArray, int size)
60 if (!(size % 2)) vCritical << "invalid dashArray format";
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;
70 void VDasher::moveTo(const VPointF &p)
72 mIsCurrentOperationGap = false;
77 if (!vCompare(mDashOffset, 0.0f)) {
78 float totalLength = 0.0;
79 for (int i = 0; i < mArraySize; i++) {
80 totalLength = mDashArray[i].length + mDashArray[i].gap;
82 float normalizeLen = fmod(mDashOffset, totalLength);
83 if (normalizeLen < 0.0) {
84 normalizeLen = totalLength + normalizeLen;
86 // now the length is less than total length and +ve
87 // findout the current dash index , dashlength and gap.
88 for (int i = 0; i < mArraySize; i++) {
89 if (normalizeLen < mDashArray[i].length) {
90 mCurrentDashIndex = i;
91 mCurrentDashLength = mDashArray[i].length - normalizeLen;
92 mIsCurrentOperationGap = false;
95 normalizeLen -= mDashArray[i].length;
96 if (normalizeLen < mDashArray[i].gap) {
97 mCurrentDashIndex = i;
98 mCurrentDashLength = mDashArray[i].gap - normalizeLen;
99 mIsCurrentOperationGap = true;
102 normalizeLen -= mDashArray[i].gap;
105 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
109 void VDasher::lineTo(const VPointF &p)
112 VLine line(mCurPt, p);
113 float length = line.length();
114 if (length < mCurrentDashLength) {
115 mCurrentDashLength -= length;
116 if (!mIsCurrentOperationGap) {
118 mDashedPath.moveTo(mCurPt);
121 mDashedPath.lineTo(p);
124 while (length > mCurrentDashLength) {
125 length -= mCurrentDashLength;
126 line.splitAtLength(mCurrentDashLength, left, right);
127 if (!mIsCurrentOperationGap) {
129 mDashedPath.moveTo(left.p1());
132 mDashedPath.lineTo(left.p2());
133 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
135 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
136 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
138 mIsCurrentOperationGap = !mIsCurrentOperationGap;
139 if (mIsCurrentOperationGap)
145 mCurrentDashLength -= length;
146 if (!mIsCurrentOperationGap) {
147 mDashedPath.moveTo(line.p1());
148 mDashedPath.lineTo(line.p2());
150 if (mCurrentDashLength < 1.0) {
152 if (!mIsCurrentOperationGap) {
153 mIsCurrentOperationGap = true;
154 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
157 mIsCurrentOperationGap = false;
158 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
159 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
166 void VDasher::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
170 VBezier b = VBezier::fromPoints(mCurPt, cp1, cp2, e);
172 if (bezLen < mCurrentDashLength) {
173 mCurrentDashLength -= bezLen;
174 if (!mIsCurrentOperationGap) {
176 mDashedPath.moveTo(mCurPt);
179 mDashedPath.cubicTo(cp1, cp2, e);
182 while (bezLen > mCurrentDashLength) {
183 bezLen -= mCurrentDashLength;
184 b.splitAtLength(mCurrentDashLength, &left, &right);
185 if (!mIsCurrentOperationGap) {
187 mDashedPath.moveTo(left.pt1());
190 mDashedPath.cubicTo(left.pt2(), left.pt3(), left.pt4());
191 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
193 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
194 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
196 mIsCurrentOperationGap = !mIsCurrentOperationGap;
197 if (mIsCurrentOperationGap)
203 mCurrentDashLength -= bezLen;
204 if (!mIsCurrentOperationGap) {
206 mDashedPath.moveTo(b.pt1());
209 mDashedPath.cubicTo(b.pt2(), b.pt3(), b.pt4());
211 if (mCurrentDashLength < 1.0) {
213 if (!mIsCurrentOperationGap) {
214 mIsCurrentOperationGap = true;
215 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
218 mIsCurrentOperationGap = false;
219 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
220 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
227 VPath VDasher::dashed(const VPath &path)
229 if (path.isEmpty()) return VPath();
231 mDashedPath = VPath();
232 mCurrentDashIndex = 0;
233 const std::vector<VPath::Element> &elms = path.elements();
234 const std::vector<VPointF> & pts = path.points();
235 const VPointF * ptPtr = pts.data();
237 for (auto i : elms) {
239 case VPath::Element::MoveTo: {
243 case VPath::Element::LineTo: {
247 case VPath::Element::CubicTo: {
248 cubicTo(*ptPtr, *(ptPtr + 1), *(ptPtr + 2));
252 case VPath::Element::Close: {
253 // The point is already joined to start point in VPath
254 // no need to do anything here.