Merge branch 'master' of github.com:airbnb/lottie-web
diff --git a/player/js/utils/shapes/ZigZagModifier.js b/player/js/utils/shapes/ZigZagModifier.js
index 6afbb93..f84538b 100644
--- a/player/js/utils/shapes/ZigZagModifier.js
+++ b/player/js/utils/shapes/ZigZagModifier.js
@@ -14,44 +14,93 @@
   this.getValue = this.processKeys;
   this.amplitude = PropertyFactory.getProp(elem, data.s, 0, null, this);
   this.frequency = PropertyFactory.getProp(elem, data.r, 0, null, this);
-  this._isAnimated = this.amplitude.effectsSequence.length !== 0 && this.frequency.effectsSequence.length !== 0;
+  this.pointsType = PropertyFactory.getProp(elem, data.pt, 0, null, this);
+  this._isAnimated = this.amplitude.effectsSequence.length !== 0 || this.frequency.effectsSequence.length !== 0 || this.pointsType.effectsSequence.length !== 0;
 };
 
-function angleMean(a, b) {
-  if (Math.abs(a - b) > Math.PI) return (a + b) / 2 + Math.PI;
-
-  return (a + b) / 2;
-}
-
-function zigZagCorner(outputBezier, segmentBefore, segmentAfter, amplitude, direction) {
-  var point;
-  var angle;
-
-  if (!segmentBefore) {
-    point = segmentAfter.points[0];
-    angle = segmentAfter.normalAngle(0);
-  } else if (!segmentAfter) {
-    point = segmentBefore.points[3];
-    angle = segmentBefore.normalAngle(1);
-  } else {
-    point = segmentAfter.points[0];
-    angle = angleMean(segmentAfter.normalAngle(0), segmentBefore.normalAngle(1));
-  }
-
+function setPoint(outputBezier, point, angle, direction, amplitude, outAmplitude, inAmplitude) {
+  var angO = angle - Math.PI / 2;
+  var angI = angle + Math.PI / 2;
   var px = point[0] + Math.cos(angle) * direction * amplitude;
   var py = point[1] - Math.sin(angle) * direction * amplitude;
-  outputBezier.setTripleAt(px, py, px, py, px, py, outputBezier.length());
+
+  outputBezier.setTripleAt(
+    px,
+    py,
+    px + Math.cos(angO) * outAmplitude,
+    py - Math.sin(angO) * outAmplitude,
+    px + Math.cos(angI) * inAmplitude,
+    py - Math.sin(angI) * inAmplitude,
+    outputBezier.length()
+  );
 }
 
-function zigZagSegment(outputBezier, segment, amplitude, frequency, direction) {
+function getPerpendicularVector(pt1, pt2) {
+  var vector = [
+    pt2[0] - pt1[0],
+    pt2[1] - pt1[1],
+  ];
+  var rot = -Math.PI * 0.5;
+  var rotatedVector = [
+    Math.cos(rot) * vector[0] - Math.sin(rot) * vector[1],
+    Math.sin(rot) * vector[0] + Math.cos(rot) * vector[1],
+  ];
+  return rotatedVector;
+}
+
+function getProjectingAngle(path, cur) {
+  var prevIndex = cur === 0 ? path.length() - 1 : cur - 1;
+  var nextIndex = (cur + 1) % path.length();
+  var prevPoint = path.v[prevIndex];
+  var nextPoint = path.v[nextIndex];
+  var pVector = getPerpendicularVector(prevPoint, nextPoint);
+  return Math.atan2(0, 1) - Math.atan2(pVector[1], pVector[0]);
+}
+
+function zigZagCorner(outputBezier, path, cur, amplitude, frequency, pointType, direction) {
+  var angle = getProjectingAngle(path, cur);
+  var point = path.v[cur % path._length];
+  var prevPoint = path.v[cur === 0 ? path._length - 1 : cur - 1];
+  var nextPoint = path.v[(cur + 1) % path._length];
+  var prevDist = pointType === 2
+    ? Math.sqrt(Math.pow(point[0] - prevPoint[0], 2) + Math.pow(point[1] - prevPoint[1], 2))
+    : 0;
+  var nextDist = pointType === 2
+    ? Math.sqrt(Math.pow(point[0] - nextPoint[0], 2) + Math.pow(point[1] - nextPoint[1], 2))
+    : 0;
+
+  setPoint(
+    outputBezier,
+    path.v[cur % path._length],
+    angle,
+    direction,
+    amplitude,
+    nextDist / ((frequency + 1) * 2),
+    prevDist / ((frequency + 1) * 2),
+    pointType
+  );
+}
+
+function zigZagSegment(outputBezier, segment, amplitude, frequency, pointType, direction) {
   for (var i = 0; i < frequency; i += 1) {
     var t = (i + 1) / (frequency + 1);
+
+    var dist = pointType === 2
+      ? Math.sqrt(Math.pow(segment.points[3][0] - segment.points[0][0], 2) + Math.pow(segment.points[3][1] - segment.points[0][1], 2))
+      : 0;
+
     var angle = segment.normalAngle(t);
     var point = segment.point(t);
-    var px = point[0] + Math.cos(angle) * direction * amplitude;
-    var py = point[1] - Math.sin(angle) * direction * amplitude;
-
-    outputBezier.setTripleAt(px, py, px, py, px, py, outputBezier.length());
+    setPoint(
+      outputBezier,
+      point,
+      angle,
+      direction,
+      amplitude,
+      dist / ((frequency + 1) * 2),
+      dist / ((frequency + 1) * 2),
+      pointType
+    );
 
     direction = -direction;
   }
@@ -59,7 +108,7 @@
   return direction;
 }
 
-ZigZagModifier.prototype.processPath = function (path, amplitude, frequency) {
+ZigZagModifier.prototype.processPath = function (path, amplitude, frequency, pointType) {
   var count = path._length;
   var clonedPath = shapePool.newElement();
   clonedPath.c = path.c;
@@ -71,23 +120,19 @@
   if (count === 0) return clonedPath;
 
   var direction = -1;
-  var segment = path.c ? PolynomialBezier.shapeSegment(path, count - 1) : null;
-  var nextSegment = PolynomialBezier.shapeSegment(path, 0);
-
-  zigZagCorner(clonedPath, segment, nextSegment, amplitude, -1);
+  var segment = PolynomialBezier.shapeSegment(path, 0);
+  zigZagCorner(clonedPath, path, 0, amplitude, frequency, pointType, direction);
 
   for (var i = 0; i < count; i += 1) {
-    segment = nextSegment;
-
-    direction = zigZagSegment(clonedPath, segment, amplitude, frequency, -direction);
+    direction = zigZagSegment(clonedPath, segment, amplitude, frequency, pointType, -direction);
 
     if (i === count - 1 && !path.c) {
-      nextSegment = null;
+      segment = null;
     } else {
-      nextSegment = PolynomialBezier.shapeSegment(path, (i + 1) % count);
+      segment = PolynomialBezier.shapeSegment(path, (i + 1) % count);
     }
 
-    zigZagCorner(clonedPath, segment, nextSegment, amplitude, direction);
+    zigZagCorner(clonedPath, path, i + 1, amplitude, frequency, pointType, direction);
   }
 
   return clonedPath;
@@ -101,6 +146,7 @@
   var jLen;
   var amplitude = this.amplitude.v;
   var frequency = Math.max(0, Math.round(this.frequency.v));
+  var pointType = this.pointsType.v;
 
   if (amplitude !== 0) {
     var shapeData;
@@ -114,7 +160,7 @@
         shapePaths = shapeData.shape.paths.shapes;
         jLen = shapeData.shape.paths._length;
         for (j = 0; j < jLen; j += 1) {
-          localShapeCollection.addShape(this.processPath(shapePaths[j], amplitude, frequency));
+          localShapeCollection.addShape(this.processPath(shapePaths[j], amplitude, frequency, pointType));
         }
       }
       shapeData.shape.paths = shapeData.localShapeCollection;