Merge pull request #2822 from mbasaglia/offset-path-open

Properly handle offset path for open shapes
diff --git a/player/js/utils/PolynomialBezier.js b/player/js/utils/PolynomialBezier.js
index 20bc5fe..07e9786 100644
--- a/player/js/utils/PolynomialBezier.js
+++ b/player/js/utils/PolynomialBezier.js
@@ -199,6 +199,11 @@
   return new PolynomialBezier(shapePath.v[index], shapePath.o[index], shapePath.i[nextIndex], shapePath.v[nextIndex], true);
 };
 
+PolynomialBezier.shapeSegmentInverted = function (shapePath, index) {
+  var nextIndex = (index + 1) % shapePath.length();
+  return new PolynomialBezier(shapePath.v[nextIndex], shapePath.i[nextIndex], shapePath.o[index], shapePath.v[index], true);
+};
+
 function crossProduct(a, b) {
   return [
     a[1] * b[2] - a[2] * b[1],
diff --git a/player/js/utils/shapes/OffsetPathModifier.js b/player/js/utils/shapes/OffsetPathModifier.js
index a8bfab0..ef08e05 100644
--- a/player/js/utils/shapes/OffsetPathModifier.js
+++ b/player/js/utils/shapes/OffsetPathModifier.js
@@ -145,6 +145,49 @@
   return segments;
 }
 
+function offsetSegmentSplit(segment, amount) {
+  /*
+    We split each bezier segment into smaller pieces based
+    on inflection points, this ensures the control point
+    polygon is convex.
+
+    (A cubic bezier can have none, one, or two inflection points)
+  */
+  var flex = segment.inflectionPoints();
+  var left;
+  var right;
+  var split;
+  var mid;
+
+  if (flex.length === 0) {
+    return [offsetSegment(segment, amount)];
+  }
+
+  if (flex.length === 1 || floatEqual(flex[1], 1)) {
+    split = segment.split(flex[0]);
+    left = split[0];
+    right = split[1];
+
+    return [
+      offsetSegment(left, amount),
+      offsetSegment(right, amount),
+    ];
+  }
+
+  split = segment.split(flex[0]);
+  left = split[0];
+  var t = (flex[1] - flex[0]) / (1 - flex[0]);
+  split = split[1].split(t);
+  mid = split[0];
+  right = split[1];
+
+  return [
+    offsetSegment(left, amount),
+    offsetSegment(mid, amount),
+    offsetSegment(right, amount),
+  ];
+}
+
 function OffsetPathModifier() {}
 
 extendPrototype([ShapeModifier], OffsetPathModifier);
@@ -163,45 +206,18 @@
   if (!inputBezier.c) {
     count -= 1;
   }
-  var left; var right; var mid; var split;
   var i; var j; var segment;
   var multiSegments = [];
 
   for (i = 0; i < count; i += 1) {
     segment = PolynomialBezier.shapeSegment(inputBezier, i);
-    /*
-      We split each bezier segment into smaller pieces based
-      on inflection points, this ensures the control point
-      polygon is convex.
+    multiSegments.push(offsetSegmentSplit(segment, amount));
+  }
 
-      (A cubic bezier can have none, one, or two inflection points)
-    */
-    var flex = segment.inflectionPoints();
-
-    if (flex.length === 0) {
-      multiSegments.push([offsetSegment(segment, amount)]);
-    } else if (flex.length === 1 || floatEqual(flex[1], 1)) {
-      split = segment.split(flex[0]);
-      left = split[0];
-      right = split[1];
-
-      multiSegments.push([
-        offsetSegment(left, amount),
-        offsetSegment(right, amount),
-      ]);
-    } else {
-      split = segment.split(flex[0]);
-      left = split[0];
-      var t = (flex[1] - flex[0]) / (1 - flex[0]);
-      split = split[1].split(t);
-      mid = split[0];
-      right = split[1];
-
-      multiSegments.push([
-        offsetSegment(left, amount),
-        offsetSegment(mid, amount),
-        offsetSegment(right, amount),
-      ]);
+  if (!inputBezier.c) {
+    for (i = count - 1; i >= 0; i -= 1) {
+      segment = PolynomialBezier.shapeSegmentInverted(inputBezier, i);
+      multiSegments.push(offsetSegmentSplit(segment, amount));
     }
   }
 
@@ -249,7 +265,7 @@
     }
   }
 
-  if (inputBezier.c && multiSegments.length) joinLines(outputBezier, lastSeg, multiSegments[0][0], lineJoin, miterLimit);
+  if (multiSegments.length) joinLines(outputBezier, lastSeg, multiSegments[0][0], lineJoin, miterLimit);
 
   return outputBezier;
 };