updated licenses info
[platform/core/uifw/lottie-player.git] / src / vector / vdasher.cpp
index f819bf9..cc4a8b8 100644 (file)
-#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)
@@ -106,135 +102,112 @@ 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