text: /**
   * æä¾›å˜æ¢æ‰©å±•
   * @module zrender/mixin/Transformable
   * @author pissang (https://www.github.com/pissang)
   */
  define(function (require) {
  
      'use strict';
  
      var matrix = require('../core/matrix.js');
      var vector = require('../core/vector.js');
      var mIdentity = matrix.identity;
  
      var EPSILON = 5e-5;
  
      function isNotAroundZero(val) {
          return val > EPSILON || val < -EPSILON;
      }
  
      /**
       * @alias module:zrender/mixin/Transformable
       * @constructor
       */
      var Transformable = function (opts) {
          opts = opts || {};
          // If there are no given position, rotation, scale
          if (!opts.position) {
              /**
               * å¹³ç§»
               * @type {Array.<number>}
               * @default [0, 0]
               */
              this.position = [0, 0];
          }
          if (opts.rotation == null) {
              /**
               * æ—‹è½¬
               * @type {Array.<number>}
               * @default 0
               */
              this.rotation = 0;
          }
          if (!opts.scale) {
              /**
               * ç¼©æ”¾
               * @type {Array.<number>}
               * @default [1, 1]
               */
              this.scale = [1, 1];
          }
          /**
           * æ—‹è½¬å’Œç¼©æ”¾çš„åŽŸç‚¹
           * @type {Array.<number>}
           * @default null
           */
          this.origin = this.origin || null;
      };
  
      var transformableProto = Transformable.prototype;
      transformableProto.transform = null;
  
      /**
       * åˆ¤æ–­æ˜¯å¦éœ€è¦æœ‰åæ ‡å˜æ¢
       * å¦‚æžœæœ‰åæ ‡å˜æ¢, åˆ™ä»Žposition, rotation, scaleä»¥åŠçˆ¶èŠ‚ç‚¹çš„transformè®¡ç®—å‡ºè‡ªèº«çš„transformçŸ©é˜µ
       */
      transformableProto.needLocalTransform = function () {
          return isNotAroundZero(this.rotation)
              || isNotAroundZero(this.position[0])
              || isNotAroundZero(this.position[1])
              || isNotAroundZero(this.scale[0] - 1)
              || isNotAroundZero(this.scale[1] - 1);
      };
  
      transformableProto.updateTransform = function () {
          var parent = this.parent;
          var parentHasTransform = parent && parent.transform;
          var needLocalTransform = this.needLocalTransform();
  
          var m = this.transform;
          if (!(needLocalTransform || parentHasTransform)) {
              m && mIdentity(m);
              return;
          }
  
          m = m || matrix.create();
  
          if (needLocalTransform) {
              this.getLocalTransform(m);
          }
          else {
              mIdentity(m);
          }
  
          // åº”ç”¨çˆ¶èŠ‚ç‚¹å˜æ¢
          if (parentHasTransform) {
              if (needLocalTransform) {
                  matrix.mul(m, parent.transform, m);
              }
              else {
                  matrix.copy(m, parent.transform);
              }
          }
          // ä¿å­˜è¿™ä¸ªå˜æ¢çŸ©é˜µ
          this.transform = m;
  
          this.invTransform = this.invTransform || matrix.create();
          matrix.invert(this.invTransform, m);
      };
  
      transformableProto.getLocalTransform = function (m) {
          m = m || [];
          mIdentity(m);
  
          var origin = this.origin;
  
          var scale = this.scale;
          var rotation = this.rotation;
          var position = this.position;
          if (origin) {
              // Translate to origin
              m[4] -= origin[0];
              m[5] -= origin[1];
          }
          matrix.scale(m, m, scale);
          if (rotation) {
              matrix.rotate(m, m, rotation);
          }
          if (origin) {
              // Translate back from origin
              m[4] += origin[0];
              m[5] += origin[1];
          }
  
          m[4] += position[0];
          m[5] += position[1];
  
          return m;
      };
      /**
       * å°†è‡ªå·±çš„transformåº”ç”¨åˆ°contextä¸Š
       * @param {Context2D} ctx
       */
      transformableProto.setTransform = function (ctx) {
          var m = this.transform;
          if (m) {
              ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
          }
      };
  
      var tmpTransform = [];
  
      /**
       * åˆ†è§£`transform`çŸ©é˜µåˆ°`position`, `rotation`, `scale`
       */
      transformableProto.decomposeTransform = function () {
          if (!this.transform) {
              return;
          }
          var parent = this.parent;
          var m = this.transform;
          if (parent && parent.transform) {
              // Get local transform and decompose them to position, scale, rotation
              matrix.mul(tmpTransform, parent.invTransform, m);
              m = tmpTransform;
          }
          var sx = m[0] * m[0] + m[1] * m[1];
          var sy = m[2] * m[2] + m[3] * m[3];
          var position = this.position;
          var scale = this.scale;
          if (isNotAroundZero(sx - 1)) {
              sx = Math.sqrt(sx);
          }
          if (isNotAroundZero(sy - 1)) {
              sy = Math.sqrt(sy);
          }
          if (m[0] < 0) {
              sx = -sx;
          }
          if (m[3] < 0) {
              sy = -sy;
          }
          position[0] = m[4];
          position[1] = m[5];
          scale[0] = sx;
          scale[1] = sy;
          this.rotation = Math.atan2(-m[1] / sy, m[0] / sx);
      };
  
      /**
       * Get global scale
       * @return {Array.<number>}
       */
      transformableProto.getGlobalScale = function () {
          var m = this.transform;
          if (!m) {
              return [1, 1];
          }
          var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
          var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
          if (m[0] < 0) {
              sx = -sx;
          }
          if (m[3] < 0) {
              sy = -sy;
          }
          return [sx, sy];
      };
      /**
       * å˜æ¢åæ ‡ä½ç½®åˆ° shape çš„å±€éƒ¨åæ ‡ç©ºé—´
       * @method
       * @param {number} x
       * @param {number} y
       * @return {Array.<number>}
       */
      transformableProto.transformCoordToLocal = function (x, y) {
          var v2 = [x, y];
          var invTransform = this.invTransform;
          if (invTransform) {
              vector.applyTransform(v2, v2, invTransform);
          }
          return v2;
      };
  
      /**
       * å˜æ¢å±€éƒ¨åæ ‡ä½ç½®åˆ°å…¨å±€åæ ‡ç©ºé—´
       * @method
       * @param {number} x
       * @param {number} y
       * @return {Array.<number>}
       */
      transformableProto.transformCoordToGlobal = function (x, y) {
          var v2 = [x, y];
          var transform = this.transform;
          if (transform) {
              vector.applyTransform(v2, v2, transform);
          }
          return v2;
      };
  
      return Transformable;
  });
  