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::addLine(const VPointF &p)
111 if (mIsCurrentOperationGap) return;
114 mDashedPath.moveTo(mCurPt);
117 mDashedPath.lineTo(p);
120 void VDasher::updateActiveSegment()
122 if (!mIsCurrentOperationGap) {
123 mIsCurrentOperationGap = true;
125 mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
127 mIsCurrentOperationGap = false;
128 mCurrentDashIndex = (mCurrentDashIndex + 1) % mArraySize;
129 mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
133 void VDasher::lineTo(const VPointF &p)
136 VLine line(mCurPt, p);
137 float length = line.length();
138 if (length < mCurrentDashLength) {
139 mCurrentDashLength -= length;
142 while (length > mCurrentDashLength) {
143 length -= mCurrentDashLength;
144 line.splitAtLength(mCurrentDashLength, left, right);
147 updateActiveSegment();
154 mCurrentDashLength -= length;
157 if (mCurrentDashLength < 1.0) {
159 updateActiveSegment();
165 void VDasher::addCubic(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
167 if (mIsCurrentOperationGap) return;
170 mDashedPath.moveTo(mCurPt);
173 mDashedPath.cubicTo(cp1, cp2, e);
176 void VDasher::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
180 VBezier b = VBezier::fromPoints(mCurPt, cp1, cp2, e);
182 if (bezLen < mCurrentDashLength) {
183 mCurrentDashLength -= bezLen;
184 addCubic(cp1, cp2, e);
186 while (bezLen > mCurrentDashLength) {
187 bezLen -= mCurrentDashLength;
188 b.splitAtLength(mCurrentDashLength, &left, &right);
190 addCubic(left.pt2(), left.pt3(), left.pt4());
191 updateActiveSegment();
198 mCurrentDashLength -= bezLen;
199 addCubic(b.pt2(), b.pt3(), b.pt4());
201 if (mCurrentDashLength < 1.0) {
203 updateActiveSegment();
209 VPath VDasher::dashed(const VPath &path)
211 if (path.isEmpty()) return VPath();
213 mDashedPath = VPath();
214 mCurrentDashIndex = 0;
215 const std::vector<VPath::Element> &elms = path.elements();
216 const std::vector<VPointF> & pts = path.points();
217 const VPointF * ptPtr = pts.data();
219 for (auto i : elms) {
221 case VPath::Element::MoveTo: {
225 case VPath::Element::LineTo: {
229 case VPath::Element::CubicTo: {
230 cubicTo(*ptPtr, *(ptPtr + 1), *(ptPtr + 2));
234 case VPath::Element::Close: {
235 // The point is already joined to start point in VPath
236 // no need to do anything here.