-#include"vdasher.h"
-#include"vbezier.h"
-
-class VLine
-{
-public:
- VLine():mX1(0),mY1(0),mX2(0),mY2(0){}
- VLine(float x1, float y1, float x2, float y2):mX1(x1),mY1(y1),mX2(x2),mY2(y2){}
- VLine(const VPointF &p1, const VPointF &p2):mX1(p1.x()),mY1(p1.y()),mX2(p2.x()),mY2(p2.y()){}
- float length() const;
- void splitAtLength(float length, VLine &left, VLine &right) const;
- VPointF p1() const {return VPointF(mX1, mY1);}
- VPointF p2() const {return VPointF(mX2, mY2);}
-private:
- float mX1;
- float mY1;
- float mX2;
- float mY2;
-};
-
-// approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
-// With alpha = 1, beta = 3/8, giving results with the largest error less
-// than 7% compared to the exact value.
-float
-VLine::length() const
-{
- float x = mX2 - mX1;
- float y = mY2 - mY1;
- x = x < 0 ? -x : x;
- y = y < 0 ? -y : y;
- return (x > y ? x + 0.375 * y : y + 0.375 * x);
-}
-
-void
-VLine::splitAtLength(float lengthAt, VLine &left, VLine &right) const
-{
- float len = length();
- double dx = ((mX2 - mX1)/len) *lengthAt;
- double dy = ((mY2 - mY1)/len) *lengthAt;
-
- left.mX1 = mX1;
- left.mY1 = mY1;
- left.mX2 = left.mX1 + dx;
- left.mY2 = left.mY1 + dy;
-
- right.mX1 = left.mX2;
- right.mY1 = left.mY2;
- right.mX2 = mX2;
- right.mY2 = mY2;
-}
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the LGPL License, Version 2.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.gnu.org/licenses/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vdasher.h"
+#include "vbezier.h"
+#include "vline.h"
+
+V_BEGIN_NAMESPACE
VDasher::VDasher(const float *dashArray, int size)
{
- if (!(size % 2))
- vCritical<<"invalid dashArray format";
-
mDashArray = reinterpret_cast<const VDasher::Dash *>(dashArray);
- mArraySize = size/2;
- mDashOffset = dashArray[size-1];
- mCurrentDashIndex = 0;
- mCurrentDashLength = 0;
- mIsCurrentOperationGap = false;
+ mArraySize = size / 2;
+ if (size % 2)
+ mDashOffset = dashArray[size - 1];
+ mIndex = 0;
+ mCurrentLength = 0;
+ mDiscard = false;
}
void VDasher::moveTo(const VPointF &p)
{
- mIsCurrentOperationGap = false;
- mStartPt = p;
+ mDiscard = false;
+ mStartNewSegment = true;
mCurPt = p;
+ mIndex = 0;
- if (!floatCmp(mDashOffset, 0.0)) {
+ if (!vCompare(mDashOffset, 0.0f)) {
float totalLength = 0.0;
- for (int i = 0; i < mArraySize ; i++) {
+ for (int i = 0; i < mArraySize; i++) {
totalLength = mDashArray[i].length + mDashArray[i].gap;
}
float normalizeLen = fmod(mDashOffset, totalLength);
- if (normalizeLen < 0.0 ) {
+ if (normalizeLen < 0.0) {
normalizeLen = totalLength + normalizeLen;
}
// now the length is less than total length and +ve
// findout the current dash index , dashlength and gap.
for (int i = 0; i < mArraySize; i++) {
if (normalizeLen < mDashArray[i].length) {
- mCurrentDashIndex = i;
- mCurrentDashLength = mDashArray[i].length - normalizeLen;
- mIsCurrentOperationGap = false;
+ mIndex = i;
+ mCurrentLength = mDashArray[i].length - normalizeLen;
+ mDiscard = false;
break;
}
normalizeLen -= mDashArray[i].length;
if (normalizeLen < mDashArray[i].gap) {
- mCurrentDashIndex = i;
- mCurrentDashLength = mDashArray[i].gap - normalizeLen;
- mIsCurrentOperationGap = true;
+ mIndex = i;
+ mCurrentLength = mDashArray[i].gap - normalizeLen;
+ mDiscard = true;
break;
}
normalizeLen -= mDashArray[i].gap;
}
} else {
- mCurrentDashIndex = 0;
- mCurrentDashLength = mDashArray[0].length;
+ mCurrentLength = mDashArray[mIndex].length;
+ }
+ if (vIsZero(mCurrentLength)) updateActiveSegment();
+}
+
+void VDasher::addLine(const VPointF &p)
+{
+ if (mDiscard) return;
+
+ if (mStartNewSegment) {
+ mResult.moveTo(mCurPt);
+ mStartNewSegment = false;
+ }
+ mResult.lineTo(p);
+}
+
+void VDasher::updateActiveSegment()
+{
+ mStartNewSegment = true;
+
+ if (mDiscard) {
+ mDiscard = false;
+ mIndex = (mIndex + 1) % mArraySize;
+ mCurrentLength = mDashArray[mIndex].length;
+ } else {
+ mDiscard = true;
+ mCurrentLength = mDashArray[mIndex].gap;
}
+ if (vIsZero(mCurrentLength)) updateActiveSegment();
}
void VDasher::lineTo(const VPointF &p)
VLine left, right;
VLine line(mCurPt, p);
float length = line.length();
- if (length < mCurrentDashLength) {
- mCurrentDashLength -= length;
- if (!mIsCurrentOperationGap) {
- mDashedPath.moveTo(mCurPt);
- mDashedPath.lineTo(p);
- }
+
+ if (length <= mCurrentLength) {
+ mCurrentLength -= length;
+ addLine(p);
} else {
- while (length > mCurrentDashLength) {
- length -= mCurrentDashLength;
- line.splitAtLength(mCurrentDashLength, left, right);
- if (!mIsCurrentOperationGap) {
- mDashedPath.moveTo(left.p1());
- mDashedPath.lineTo(left.p2());
- mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
- } else {
- mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize ;
- mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
- }
- mIsCurrentOperationGap = !mIsCurrentOperationGap;
- line = right;
- mCurPt = line.p1();
- }
- // remainder
- mCurrentDashLength -= length;
- if (!mIsCurrentOperationGap) {
- mDashedPath.moveTo(line.p1());
- mDashedPath.lineTo(line.p2());
- }
- if (mCurrentDashLength < 1.0) {
- // move to next dash
- if (!mIsCurrentOperationGap) {
- mIsCurrentOperationGap = true;
- mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
- } else {
- mIsCurrentOperationGap = false;
- mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize;
- mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
- }
- }
+ while (length > mCurrentLength) {
+ length -= mCurrentLength;
+ line.splitAtLength(mCurrentLength, left, right);
+
+ addLine(left.p2());
+ updateActiveSegment();
+
+ line = right;
+ mCurPt = line.p1();
+ }
+ // handle remainder
+ if (length > 1.0) {
+ mCurrentLength -= length;
+ addLine(line.p2());
+ }
}
+
+ if (mCurrentLength < 1.0) updateActiveSegment();
+
mCurPt = p;
}
+void VDasher::addCubic(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
+{
+ if (mDiscard) return;
+
+ if (mStartNewSegment) {
+ mResult.moveTo(mCurPt);
+ mStartNewSegment = false;
+ }
+ mResult.cubicTo(cp1, cp2, e);
+}
+
void VDasher::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
{
VBezier left, right;
- float bezLen = 0.0;
+ float bezLen = 0.0;
VBezier b = VBezier::fromPoints(mCurPt, cp1, cp2, e);
bezLen = b.length();
- if (bezLen < mCurrentDashLength) {
- mCurrentDashLength -= bezLen;
- if (!mIsCurrentOperationGap) {
- mDashedPath.moveTo(mCurPt);
- mDashedPath.cubicTo(cp1, cp2, e);
- }
+
+ if (bezLen <= mCurrentLength) {
+ mCurrentLength -= bezLen;
+ addCubic(cp1, cp2, e);
} else {
- while (bezLen > mCurrentDashLength) {
- bezLen -= mCurrentDashLength;
- b.splitAtLength(mCurrentDashLength, &left, &right);
- if (!mIsCurrentOperationGap) {
- mDashedPath.moveTo(left.pt1());
- mDashedPath.cubicTo(left.pt2(), left.pt3(), left.pt4());;
- mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
- } else {
- mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize ;
- mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
- }
- mIsCurrentOperationGap = !mIsCurrentOperationGap;
- b = right;
- mCurPt = b.pt1();
+ while (bezLen > mCurrentLength) {
+ bezLen -= mCurrentLength;
+ b.splitAtLength(mCurrentLength, &left, &right);
+
+ addCubic(left.pt2(), left.pt3(), left.pt4());
+ updateActiveSegment();
+
+ b = right;
+ mCurPt = b.pt1();
+ }
+ // handle remainder
+ if (bezLen > 1.0) {
+ mCurrentLength -= bezLen;
+ addCubic(b.pt2(), b.pt3(), b.pt4());
}
- // remainder
- mCurrentDashLength -= bezLen;
- if (!mIsCurrentOperationGap) {
- mDashedPath.moveTo(b.pt1());
- mDashedPath.cubicTo(b.pt2(), b.pt3(), b.pt4());
- }
- if (mCurrentDashLength < 1.0) {
- // move to next dash
- if (!mIsCurrentOperationGap)
- {
- mIsCurrentOperationGap = true;
- mCurrentDashLength = mDashArray[mCurrentDashIndex].gap;
- }
- else
- {
- mIsCurrentOperationGap = false;
- mCurrentDashIndex = (mCurrentDashIndex +1) % mArraySize;
- mCurrentDashLength = mDashArray[mCurrentDashIndex].length;
- }
- }
}
+
+ if (mCurrentLength < 1.0) updateActiveSegment();
+
mCurPt = e;
}
-
VPath VDasher::dashed(const VPath &path)
{
- if (path.isEmpty()) return VPath();
+ if (path.empty()) return VPath();
- mDashedPath = VPath();
+ mResult = VPath();
+ mIndex = 0;
const std::vector<VPath::Element> &elms = path.elements();
- const std::vector<VPointF> &pts = path.points();
- const VPointF *ptPtr = pts.data();
+ const std::vector<VPointF> & pts = path.points();
+ const VPointF * ptPtr = pts.data();
- for (auto i : elms) {
+ for (auto &i : elms) {
switch (i) {
- case VPath::Element::MoveTo: {
- moveTo(*ptPtr++);
- break;
- }
- case VPath::Element::LineTo: {
- lineTo(*ptPtr++);
- break;
- }
- case VPath::Element::CubicTo: {
- cubicTo(*ptPtr, *(ptPtr + 1), *(ptPtr + 2));
- ptPtr += 3;
- break;
- }
- case VPath::Element::Close: {
- // The point is already joined to start point in VPath
- // no need to do anything here.
- break;
- }
- default:
- break;
+ case VPath::Element::MoveTo: {
+ moveTo(*ptPtr++);
+ break;
+ }
+ case VPath::Element::LineTo: {
+ lineTo(*ptPtr++);
+ break;
+ }
+ case VPath::Element::CubicTo: {
+ cubicTo(*ptPtr, *(ptPtr + 1), *(ptPtr + 2));
+ ptPtr += 3;
+ break;
+ }
+ case VPath::Element::Close: {
+ // The point is already joined to start point in VPath
+ // no need to do anything here.
+ break;
+ }
+ default:
+ break;
}
}
- return mDashedPath;
+ return std::move(mResult);
}
+V_END_NAMESPACE