Merge branch 'master' into shapes_transform_fix
diff --git a/player/index.html b/player/index.html
index 33f683a..c90b7fe 100644
--- a/player/index.html
+++ b/player/index.html
@@ -40,7 +40,7 @@
 <body style="background-color:#ccc; margin: 0px;height: 100%; font-family: sans-serif;font-size: 10px">
 
 <!-- <div style="width:1350px;height:650px;background-color:#faf5d3;display:inline-block;" class="bodymovin" data-bm-path="exports/render/" data-bm-type="svg" data-bm-loop="true" data-bm-prerender="false"></div> -->
-<div style="width:100%;height:100%;background-color:#333;display:inline-block;" id="bodymovin"></div>
+<div style="width:1500px;height:700px;background-color:#333;display:inline-block;" id="bodymovin"></div>
 
 <script>
 
@@ -55,7 +55,7 @@
     var animData = {
         wrapper: document.getElementById('bodymovin'),
         animType: 'svg',
-        loop: false,
+        loop: 1,
         prerender: false,
         autoplay: true,
         path: 'exports/render'
diff --git a/player/js/3rd_party/transformation-matrix.js b/player/js/3rd_party/transformation-matrix.js
index d1fc858..0d7388f 100644
--- a/player/js/3rd_party/transformation-matrix.js
+++ b/player/js/3rd_party/transformation-matrix.js
@@ -547,7 +547,7 @@
          };*/
     },
     applyToPointStringified: function(x, y) {
-        return (x * this.props[0] + y * this.props[2] + this.props[4])+','+(x * this.props[1] + y * this.props[3] + this.props[5]);
+        return (bm_rnd(x * this.props[0] + y * this.props[2] + this.props[4]))+','+(bm_rnd(x * this.props[1] + y * this.props[3] + this.props[5]));
     },
 
     /**
diff --git a/player/js/animation/AnimationItem.js b/player/js/animation/AnimationItem.js
index 97d4bb0..71c685d 100644
--- a/player/js/animation/AnimationItem.js
+++ b/player/js/animation/AnimationItem.js
@@ -203,8 +203,8 @@
     this.assets = this.animationData.assets;
     this.frameRate = this.animationData.fr;
     this.firstFrame = Math.round(this.animationData.ip*this.frameRate);
-    /*this.firstFrame = 0;
-    this.totalFrames = 1;*/
+    this.firstFrame = 39;
+    this.totalFrames = 2;
     this.frameMult = this.animationData.fr / 1000;
     this.trigger('bm:config_ready');
     this.loadSegments();
diff --git a/player/js/elements/ShapeItemElement.js b/player/js/elements/ShapeItemElement.js
index f626c99..fff273b 100644
--- a/player/js/elements/ShapeItemElement.js
+++ b/player/js/elements/ShapeItemElement.js
@@ -36,13 +36,17 @@
                     w: ''
                 }
             };
-            var pathElement = document.createElementNS(svgNS, "path");
+            var pathElement;
             if(arr[i].ty == 'st') {
+                pathElement = document.createElementNS(svgNS, "g");
                 pathElement.setAttribute('stroke-linecap', this.lcEnum[arr[i].lc] || 'round');
                 pathElement.setAttribute('stroke-linejoin',this.ljEnum[arr[i].lj] || 'round');
+                pathElement.setAttribute('fill-opacity','0');
                 if(arr[i].lj == 1) {
                     pathElement.setAttribute('stroke-miterlimit',arr[i].ml);
                 }
+            }else{
+                pathElement = document.createElementNS(svgNS, "path");
             }
             if(this.shape === this.parentContainer){
                 this.appendNodeToParent(pathElement);
@@ -67,7 +71,8 @@
                 transform : {
                     mat: new Matrix(),
                     opacity: 1
-                }
+                },
+                elements: []
             };
         }else if(arr[i].ty == 'sh' || arr[i].ty == 'rc' || arr[i].ty == 'el'){
             data[i] = {
@@ -81,14 +86,28 @@
                 }
             };
             jLen = this.stylesList.length;
+            var element, hasStrokes = false, hasFills = false;
             for(j=0;j<jLen;j+=1){
                 if(!this.stylesList[j].closed){
-                    data[i].styles.push(this.stylesList[j]);
-                    if(this.stylesList[j].type == 'st'){
-                        this.stylesList[j].pathElement.setAttribute('fill-opacity',0);
+                    if(this.stylesList[j].type === 'st'){
+                        hasStrokes = true;
+                        element = document.createElementNS(svgNS, "path");
+                        this.stylesList[j].pathElement.appendChild(element);
+                        data[i].elements.push({
+                            ty:this.stylesList[j].type,
+                            el:element
+                        });
+                    }else{
+                        hasFills = true;
+                        data[i].elements.push({
+                            ty:this.stylesList[j].type,
+                            st: this.stylesList[j]
+                        });
                     }
                 }
             }
+            data[i].st = hasStrokes;
+            data[i].fl = hasFills;
         }
     }
     len = ownArrays.length;
@@ -104,8 +123,13 @@
 ShapeItemElement.prototype.hideShape = function(){
     var i, len = this.stylesList.length;
     for(i=len-1;i>=0;i-=1){
-        this.stylesList[i].pathElement.setAttribute('d','M 0,0');
-        this.stylesList[i].ld = 'M 0,0';
+        if(this.stylesList[i].type === 'st'){
+            this.stylesList[i].pathElement.setAttribute('visibility','hidden');
+            this.stylesList[i].ld = 0;
+        }else{
+            this.stylesList[i].pathElement.setAttribute('d','M 0,0');
+            this.stylesList[i].ld = 'M 0,0';
+        }
     }
 };
 
@@ -156,7 +180,7 @@
         }else if(items[i].ty == 'fl'){
             this.renderFill(items[i],data[i],num,groupTransform);
         }else if(items[i].ty == 'st'){
-            this.renderStroke(items[i],data[i],num,groupTransform);
+            this.renderStroke(items[i],data[i],num);
         }else if(items[i].ty == 'gr'){
             this.renderShape(num,groupTransform,items[i].it,data[i].it);
         }else if(items[i].ty == 'tm'){
@@ -168,18 +192,23 @@
     }
     len = this.stylesList.length;
     for(i=0;i<len;i+=1){
-        if(this.stylesList[i].d == '' && this.stylesList[i].ld !== ''){
-            this.stylesList[i].pathElement.setAttribute('d','M 0,0');
-            this.stylesList[i].ld = this.stylesList[i].d;
-        }else if(this.stylesList[i].ld !== this.stylesList[i].d){
-            this.stylesList[i].pathElement.setAttribute('d',this.stylesList[i].d);
-            this.stylesList[i].ld = this.stylesList[i].d;
+        if(this.stylesList[i].type === 'fl'){
+            if(this.stylesList[i].d == '' && this.stylesList[i].ld !== ''){
+                this.stylesList[i].pathElement.setAttribute('d','M 0,0');
+                this.stylesList[i].ld = this.stylesList[i].d;
+            }else if(this.stylesList[i].ld !== this.stylesList[i].d){
+                this.stylesList[i].pathElement.setAttribute('d',this.stylesList[i].d);
+                this.stylesList[i].ld = this.stylesList[i].d;
+            }
+        }else if(this.stylesList[i].ld === 0){
+            this.stylesList[i].ld = 1;
+            this.stylesList[i].pathElement.setAttribute('visibility','visible');
         }
     }
 
 };
 
-ShapeItemElement.prototype.renderPath = function(pathData,viewData,num,transform){
+ShapeItemElement.prototype.renderPath = function(pathData,viewData,num,groupTransform){
     var len,i;
     if(!viewData.renderedFrames[this.globalData.frameNum]){
 
@@ -190,56 +219,108 @@
         len = pathNodes.v.length;
         var stops = pathNodes.s ? pathNodes.s : [];
         var pathStringTransformed = '';
+        var pathStringNonTransformed = '';
         for(i=1;i<len;i+=1){
             if(stops[i-1]){
-                pathStringTransformed += " M"+bm_rnd(stops[i-1][0])+','+bm_rnd(stops[i-1][1]);
-                //pathStringTransformed += " M"+stops[i-1][0]+','+stops[i-1][1];
+                if(viewData.st){
+                    pathStringNonTransformed += " M"+bm_rnd(stops[i-1][0])+','+bm_rnd(stops[i-1][1]);
+                }
+                if(viewData.fl) {
+                    pathStringTransformed += " M" + groupTransform.mat.applyToPointStringified(stops[i - 1][0], stops[i - 1][1]);
+                }
             }else if(i==1){
-                pathStringTransformed += " M"+bm_rnd(pathNodes.v[0][0])+','+bm_rnd(pathNodes.v[0][1]);
-                //pathStringTransformed += " M"+pathNodes.v[0][0]+','+pathNodes.v[0][1];
+                if(viewData.st) {
+                    pathStringNonTransformed += " M" + bm_rnd(pathNodes.v[0][0]) + ',' + bm_rnd(pathNodes.v[0][1]);
+                }
+
+                if(viewData.fl) {
+                    pathStringTransformed += " M" + groupTransform.mat.applyToPointStringified(pathNodes.v[0][0], pathNodes.v[0][1]);
+                }
             }
-            pathStringTransformed += " C"+bm_rnd(pathNodes.o[i-1][0])+','+bm_rnd(pathNodes.o[i-1][1]) + " "+bm_rnd(pathNodes.i[i][0])+','+bm_rnd(pathNodes.i[i][1]) + " "+bm_rnd(pathNodes.v[i][0])+','+bm_rnd(pathNodes.v[i][1]);
-            //pathStringTransformed += " C"+pathNodes.o[i-1][0]+','+pathNodes.o[i-1][1] + " "+pathNodes.i[i][0]+','+pathNodes.i[i][1] + " "+pathNodes.v[i][0]+','+pathNodes.v[i][1];
+            if(viewData.st) {
+                pathStringNonTransformed += " C" + bm_rnd(pathNodes.o[i - 1][0]) + ',' + bm_rnd(pathNodes.o[i - 1][1]) + " " + bm_rnd(pathNodes.i[i][0]) + ',' + bm_rnd(pathNodes.i[i][1]) + " " + bm_rnd(pathNodes.v[i][0]) + ',' + bm_rnd(pathNodes.v[i][1]);
+            }
+
+            if(viewData.fl) {
+                pathStringTransformed += " C" + groupTransform.mat.applyToPointStringified(pathNodes.o[i - 1][0], pathNodes.o[i - 1][1]) + " " + groupTransform.mat.applyToPointStringified(pathNodes.i[i][0], pathNodes.i[i][1]) + " " + groupTransform.mat.applyToPointStringified(pathNodes.v[i][0], pathNodes.v[i][1]);
+            }
         }
         if(len == 1){
             if(stops[0]){
-                pathStringTransformed += " M"+bm_rnd(stops[0][0])+','+bm_rnd(stops[0][1]);
-                //pathStringTransformed += " M"+stops[0][0]+','+stops[0][1];
+                if(viewData.st) {
+                    pathStringNonTransformed += " M" + bm_rnd(stops[0][0]) + ',' + bm_rnd(stops[0][1]);
+                }
+
+                if(viewData.fl) {
+                    pathStringTransformed += " M" + groupTransform.mat.applyToPointStringified(stops[0][0], stops[0][1]);
+                }
             }else{
-                pathStringTransformed += " M"+bm_rnd(pathNodes.v[0][0])+','+bm_rnd(pathNodes.v[0][1]);
-                //pathStringTransformed += " M"+pathNodes.v[0][0]+','+pathNodes.v[0][1];
+
+                if(viewData.st) {
+                    pathStringNonTransformed += " M" + bm_rnd(pathNodes.v[0][0]) + ',' + bm_rnd(pathNodes.v[0][1]);
+                }
+
+                if(viewData.fl) {
+                    pathStringTransformed += " M" + groupTransform.mat.applyToPointStringified(pathNodes.v[0][0], pathNodes.v[0][1]);
+                }
             }
         }
         if(pathData.closed && !(pathData.trimmed && !pathNodes.c)){
-            pathStringTransformed += " C"+bm_rnd(pathNodes.o[i-1][0])+','+bm_rnd(pathNodes.o[i-1][1]) + " "+bm_rnd(pathNodes.i[0][0])+','+bm_rnd(pathNodes.i[0][1]) + " "+bm_rnd(pathNodes.v[0][0])+','+bm_rnd(pathNodes.v[0][1]);
-            //pathStringTransformed += " C"+pathNodes.o[i-1][0]+','+pathNodes.o[i-1][1] + " "+pathNodes.i[0][0]+','+pathNodes.i[0][1] + " "+pathNodes.v[0][0]+','+pathNodes.v[0][1];
+            if(viewData.st) {
+                pathStringNonTransformed += " C" + bm_rnd(pathNodes.o[i - 1][0]) + ',' + bm_rnd(pathNodes.o[i - 1][1]) + " " + bm_rnd(pathNodes.i[0][0]) + ',' + bm_rnd(pathNodes.i[0][1]) + " " + bm_rnd(pathNodes.v[0][0]) + ',' + bm_rnd(pathNodes.v[0][1]);
+            }
+
+            if(viewData.fl) {
+                pathStringTransformed += " C" + groupTransform.mat.applyToPointStringified(pathNodes.o[i - 1][0], pathNodes.o[i - 1][1]) + " " + groupTransform.mat.applyToPointStringified(pathNodes.i[0][0], pathNodes.i[0][1]) + " " + groupTransform.mat.applyToPointStringified(pathNodes.v[0][0], pathNodes.v[0][1]);
+            }
+        }
+        var t;
+        if(viewData.st) {
+            t = 'matrix(' + groupTransform.mat.props.join(',') + ')';
         }
 
         viewData.renderedFrames[this.globalData.frameNum] = {
-            dTr: pathStringTransformed
+            dTr: pathStringTransformed,
+            dNTr: pathStringNonTransformed,
+            t: t,
+            o: groupTransform.opacity
         };
     }
     var renderedFrameData = viewData.renderedFrames[this.globalData.frameNum];
-
-    len = viewData.styles.length;
+    len = viewData.elements.length;
     for(i=0;i<len;i+=1){
-        viewData.styles[i].d += renderedFrameData.dTr;
+        if(viewData.elements[i].ty === 'st'){
+            if(viewData.ld != renderedFrameData.dNTr) {
+                viewData.elements[i].el.setAttribute('d', renderedFrameData.dNTr);
+                viewData.ld = renderedFrameData.dNTr;
+            }
+            if(viewData.lt != renderedFrameData.t) {
+                viewData.elements[i].el.setAttribute('transform',renderedFrameData.t);
+                viewData.lt = renderedFrameData.t;
+            }
+            if(viewData.lo != renderedFrameData.o) {
+                viewData.elements[i].el.setAttribute('opacity',renderedFrameData.o);
+                viewData.lo = renderedFrameData.o;
+            }
+        }else{
+            if(renderedFrameData.o !== 0){
+                viewData.elements[i].st.d += renderedFrameData.dTr;
+            }
+        }
     }
 };
 
-ShapeItemElement.prototype.renderFill = function(styleData,viewData,num,groupTransform){
+ShapeItemElement.prototype.renderFill = function(styleData,viewData,num, groupTransform){
     var fillData = styleData.renderedData[num];
     var styleElem = viewData.style;
     if(!viewData.renderedFrames[this.globalData.frameNum]){
-        var t = 'matrix('+groupTransform.mat.props.join(',')+')';
-        if(viewData._ld && viewData._ld.c === fillData.color && viewData._ld.o === fillData.opacity*groupTransform.opacity && viewData._ld.t === t){
+        if(viewData._ld && viewData._ld.c === fillData.color && viewData._ld.o === fillData.opacity*groupTransform.opacity){
             viewData.renderedFrames[this.globalData.frameNum] = viewData._ld;
             return;
         }else{
             viewData._ld = {
                 c: fillData.color,
-                o: fillData.opacity*groupTransform.opacity,
-                t: t
+                o: fillData.opacity*groupTransform.opacity
             };
             viewData.renderedFrames[this.globalData.frameNum] = viewData._ld;
         }
@@ -254,26 +335,20 @@
         styleElem.pathElement.setAttribute('fill-opacity',renderedFrameData.o);
         viewData.lastData.o = renderedFrameData.o;
     }
-    if(viewData.lastData.t != renderedFrameData.t){
-        styleElem.pathElement.setAttribute('transform',renderedFrameData.t);
-        viewData.lastData.t = renderedFrameData.t;
-    }
 };
 
-ShapeItemElement.prototype.renderStroke = function(styleData,viewData,num,groupTransform){
+ShapeItemElement.prototype.renderStroke = function(styleData,viewData,num){
     var fillData = styleData.renderedData[num];
     var styleElem = viewData.style;
     if(!viewData.renderedFrames[this.globalData.frameNum]){
-        var t = 'matrix('+groupTransform.mat.props.join(',')+')';
-        if(viewData._ld && viewData._ld.c === fillData.color && viewData._ld.o === fillData.opacity*groupTransform.opacity && viewData._ld.w === fillData.width && viewData._ld.t === t){
+        if(viewData._ld && viewData._ld.c === fillData.color && viewData._ld.o === fillData.opacity && viewData._ld.w === fillData.width){
             viewData.renderedFrames[this.globalData.frameNum] = viewData._ld;
             return;
         }else{
             viewData._ld = {
                 c: fillData.color,
-                o: fillData.opacity*groupTransform.opacity,
-                w: fillData.width,
-                t: t
+                o: fillData.opacity,
+                w: fillData.width
             };
             viewData.renderedFrames[this.globalData.frameNum] = viewData._ld;
         }
@@ -287,7 +362,6 @@
     var o = renderedFrameData.o;
     var w = renderedFrameData.w;
     var d = renderedFrameData.d;
-    var t = renderedFrameData.t;
     var dasharray,dashoffset;
     if(d){
         var j, jLen = d.length;
@@ -321,10 +395,6 @@
         styleElem.pathElement.setAttribute('stroke-width',w);
         viewData.lastData.w = w;
     }
-    if(viewData.lastData.t !== t){
-        styleElem.pathElement.setAttribute('transform',t);
-        viewData.lastData.t = t;
-    }
 };
 
 ShapeItemElement.prototype.destroy = function(items, data){