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;
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;
81 float normalizeLen = fmod(mDashOffset, totalLength);
82 if (normalizeLen < 0.0) {
83 normalizeLen = totalLength + normalizeLen;
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;
94 normalizeLen -= mDashArray[i].length;
95 if (normalizeLen < mDashArray[i].gap) {
96 mCurrentDashIndex = i;
97 mCurrentDashLength = mDashArray[i].gap - normalizeLen;
98 mIsCurrentOperationGap = true;
101 normalizeLen -= mDashArray[i].gap;
104 mCurrentDashIndex = 0;
105 mCurrentDashLength = mDashArray[0].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) {
117 mDashedPath.moveTo(mCurPt);
118 mDashedPath.lineTo(p);
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;
129 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
130 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
132 mIsCurrentOperationGap = !mIsCurrentOperationGap;
137 mCurrentDashLength -= length;
138 if (!mIsCurrentOperationGap) {
139 mDashedPath.moveTo(line.p1());
140 mDashedPath.lineTo(line.p2());
142 if (mCurrentDashLength < 1.0) {
144 if (!mIsCurrentOperationGap) {
145 mIsCurrentOperationGap = true;
146 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
148 mIsCurrentOperationGap = false;
149 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
150 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
157 void VDasher::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
161 VBezier b = VBezier::fromPoints(mCurPt, cp1, cp2, e);
163 if (bezLen < mCurrentDashLength) {
164 mCurrentDashLength -= bezLen;
165 if (!mIsCurrentOperationGap) {
166 mDashedPath.moveTo(mCurPt);
167 mDashedPath.cubicTo(cp1, cp2, e);
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());
177 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
179 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
180 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
182 mIsCurrentOperationGap = !mIsCurrentOperationGap;
187 mCurrentDashLength -= bezLen;
188 if (!mIsCurrentOperationGap) {
189 mDashedPath.moveTo(b.pt1());
190 mDashedPath.cubicTo(b.pt2(), b.pt3(), b.pt4());
192 if (mCurrentDashLength < 1.0) {
194 if (!mIsCurrentOperationGap) {
195 mIsCurrentOperationGap = true;
196 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
198 mIsCurrentOperationGap = false;
199 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
200 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.