From cb6afda083ad01cd8a028931964561dba1701a8a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=BCrg=20Lehni?= <juerg@scratchdisk.com>
Date: Tue, 5 Jan 2016 14:51:55 +0100
Subject: [PATCH] Add sameDir parameter it #isCollinear() functions and use it
 in Path#reduce().

---
 src/basic/Line.js  |  5 +++--
 src/basic/Point.js | 13 ++++++++-----
 src/path/Curve.js  |  4 ++--
 src/path/Path.js   |  5 ++++-
 4 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/src/basic/Line.js b/src/basic/Line.js
index 13e45dd4..42ec32dd 100644
--- a/src/basic/Line.js
+++ b/src/basic/Line.js
@@ -113,8 +113,9 @@ var Line = Base.extend(/** @lends Line# */{
                 point.x, point.y, true));
     },
 
-    isCollinear: function(line) {
-        return Point.isCollinear(this._vx, this._vy, line._vx, line._vy);
+    isCollinear: function(line, sameDir) {
+        return Point.isCollinear(this._vx, this._vy, line._vx, line._vy,
+                sameDir);
     },
 
     isOrthogonal: function(line) {
diff --git a/src/basic/Point.js b/src/basic/Point.js
index 55f0265b..f8202465 100644
--- a/src/basic/Point.js
+++ b/src/basic/Point.js
@@ -704,8 +704,9 @@ var Point = Base.extend(/** @lends Point# */{
      * @return {Boolean} {@true it is collinear}
      */
     isCollinear: function(/* point */) {
-        var point = Point.read(arguments);
-        return Point.isCollinear(this.x, this.y, point.x, point.y);
+        var point = Point.read(arguments),
+            sameDir = Base.read(arguments);
+        return Point.isCollinear(this.x, this.y, point.x, point.y, sameDir);
     },
 
     // TODO: Remove version with typo after a while (deprecated June 2015)
@@ -917,14 +918,16 @@ var Point = Base.extend(/** @lends Point# */{
             return new Point(Math.random(), Math.random());
         },
 
-        isCollinear: function(x1, y1, x2, y2) {
+        isCollinear: function(x1, y1, x2, y2, sameDir) {
             // NOTE: We use normalized vectors so that the epsilon comparison is
             // reliable. We could instead scale the epsilon based on the vector
             // length. But instead of normalizing the vectors before calculating
             // the cross product, we can scale the epsilon accordingly.
+            var d2 = x2 * x2 + y2 * y2;
             return Math.abs(x1 * y2 - y1 * x2)
-                    <= Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2))
-                        * /*#=*/Numerical.TRIGONOMETRIC_EPSILON;
+                    <= Math.sqrt((x1 * x1 + y1 * y1) * d2)
+                        * /*#=*/Numerical.TRIGONOMETRIC_EPSILON
+                    && (!sameDir || (x1 * x2 + y1 * y2) / d2 >= 0);
         },
 
         isOrthogonal: function(x1, y1, x2, y2) {
diff --git a/src/path/Curve.js b/src/path/Curve.js
index 083eb3c0..694fb874 100644
--- a/src/path/Curve.js
+++ b/src/path/Curve.js
@@ -949,9 +949,9 @@ statics: {
      * @param {Curve} curve the other curve to check against
      * @return {Boolean} {@true if the two lines are collinear}
      */
-    isCollinear: function(curve) {
+    isCollinear: function(curve, sameDir) {
         return curve && this.isStraight() && curve.isStraight()
-                && this.getLine().isCollinear(curve.getLine());
+                && this.getLine().isCollinear(curve.getLine(), sameDir);
     },
 
     /**
diff --git a/src/path/Path.js b/src/path/Path.js
index 3384c8c0..46a79466 100644
--- a/src/path/Path.js
+++ b/src/path/Path.js
@@ -1032,7 +1032,10 @@ var Path = PathItem.extend(/** @lends Path# */{
             var curve = curves[i];
             if (!curve.hasHandles()
                 && (curve.getLength() < /*#=*/Numerical.GEOMETRIC_EPSILON
-                    || curve.isCollinear(curve.getNext())))
+                    // Pass true for sameDir, as we can only remove straight
+                    // curves if they point in the same direction as the next
+                    // curve, not 180° in the opposite direction.
+                    || curve.isCollinear(curve.getNext(), true)))
                 curve.remove();
         }
         return this;