]> source.dussan.org Git - svg.js.git/commitdiff
Merged the 3.0.0 branch to add the linter
authorSaivan <savian@me.com>
Tue, 27 Feb 2018 00:41:21 +0000 (11:41 +1100)
committerSaivan <savian@me.com>
Tue, 27 Feb 2018 00:41:21 +0000 (11:41 +1100)
I added the linter here to make programming and running the tests
easier throughout the programming process.

1  2 
src/fx.js
src/helpers.js
src/matrix.js
src/sugar.js
src/transform.js

diff --cc src/fx.js
index ed81cc74a28e5eb19ac445823efc724653a3d5c6,242c9978ddf075d78e7cd350b641a0950d560283..9fcdd4c46c1908f945c9dbea9ad22f7f8941c197
+++ b/src/fx.js
@@@ -597,17 -592,18 +592,18 @@@ SVG.FX = SVG.invent(
        // save last eased position for once callback triggering
        this.lastPos = eased
        return this
-     }
+     },
  
      // calculates the step for every property and calls block with it
-   , eachAt: function(){
-       var i, len, at, self = this, target = this.target(), s = this.situation
+     eachAt: function () {
 -      var i, len, at
++      var i, at
+       var self = this
+       var target = this.target()
+       var s = this.situation
  
        // apply animations which can be called trough a method
-       for(i in s.animations){
-         at = [].concat(s.animations[i]).map(function(el){
+       for (i in s.animations) {
+         at = [].concat(s.animations[i]).map(function (el) {
            return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el
          })
  
        }
  
        // animate initialTransformation which has to be chained
-       if(s.transforms.length){
+       if (s.transforms.length) {
 -        // get initial initialTransformation
 -        at = s.initialTransformation
 -        for (i = 0, len = s.transforms.length; i < len; i++) {
 -          // get next transformation in chain
 -          var a = s.transforms[i]
 -
 -          // multiply matrix directly
 -          if (a instanceof SVG.Matrix) {
 -            if (a.relative) {
 -              at = at.multiply(new SVG.Matrix().morph(a).at(s.ease(this.pos)))
 -            } else {
 -              at = at.morph(a).at(s.ease(this.pos))
 -            }
 -            continue
 -          }
 -
 -          // when transformation is absolute we have to reset the needed transformation first
 -          if (!a.relative) {
 -            a.undo(at.extract())
 -          }
  
 -          // and reapply it after
 -          at = at.multiply(a.at(s.ease(this.pos)))
 -        }
 -
 -        // set new matrix on element
 -        target.matrix(at)
 +        // TODO: ANIMATE THE TRANSFORMS
 +
 +        // // get initial initialTransformation
 +        // at = s.initialTransformation
 +        // for(i = 0, len = s.transforms.length; i < len; i++){
 +        //
 +        //   // get next transformation in chain
 +        //   var a = s.transforms[i]
 +        //
 +        //   // multiply matrix directly
 +        //   if(a instanceof SVG.Matrix){
 +        //
 +        //     if(a.relative){
 +        //       at = at.multiply(new SVG.Matrix().morph(a).at(s.ease(this.pos)))
 +        //     }else{
 +        //       at = at.morph(a).at(s.ease(this.pos))
 +        //     }
 +        //     continue
 +        //   }
 +        //
 +        //   // when transformation is absolute we have to reset the needed transformation first
 +        //   if(!a.relative)
 +        //     a.undo(at.decompose())
 +        //
 +        //   // and reapply it after
 +        //   at = at.multiply(a.at(s.ease(this.pos)))
 +        //
 +        // }
 +        //
 +        // // set new matrix on element
 +        // target.matrix(at)
        }
  
        return this
diff --cc src/helpers.js
index b6c2fe6b6cb2c6b5ba2583676c46108deec9da2e,9a7f47ec3997d91fbdd24663278f84e16818b25c..56565a04d84632496a2a70643f11ddadbfc66fb0
@@@ -105,8 -109,16 +109,8 @@@ function proportionalSize (element, wid
    }
  }
  
 -// Delta transform point
 -function deltaTransformPoint (matrix, x, y) {
 -  return {
 -    x: x * matrix.a + y * matrix.c + 0,
 -    y: x * matrix.b + y * matrix.d + 0
 -  }
 -}
 -
  // Map matrix array to object
- function arrayToMatrix(a) {
+ function arrayToMatrix (a) {
    return { a: a[0], b: a[1], c: a[2], d: a[3], e: a[4], f: a[5] }
  }
  
@@@ -201,18 -216,3 +208,18 @@@ function idFromReference (url) 
  
  // Create matrix array for looping
  var abcdef = 'abcdef'.split('')
-     , cos = a / len
-     , sin = b / len
-     , theta = Math.atan2(b, a) * 180 / Math.PI
 +
 +// Gets the distance of a point (a, b) from the origin
 +function mag (a, b) {
 +  return Math.sqrt(a * a + b * b)
 +}
 +
 +// Given a coordinate (a, b), this will calculate the sin, cosine and angle
 +// of this point projected onto the unit circle directly
 +function unitCircle (a, b) {
 +  var len = Math.sqrt(a * a + b * b)
++  var cos = a / len
++  var sin = b / len
++  var theta = Math.atan2(b, a) * 180 / Math.PI
 +  return {theta: theta, cos: cos, sin: sin}
 +}
diff --cc src/matrix.js
index cde44ca63b99a46b2bbfb6743f6458991111eabf,6e918d8207e26a1277be6a406b697f53049c9877..48ed7a1f72d22afe8fbc4bb9bfed88b0dcfe8dce
+ /* global abcdef, arrayToMatrix, deltaTransformPoint, parseMatrix */
  SVG.Matrix = SVG.invent({
    // Initialize
-   create: function(source) {
-     var i, base = arrayToMatrix([1, 0, 0, 1, 0, 0])
+   create: function (source) {
+     var base = arrayToMatrix([1, 0, 0, 1, 0, 0])
+     var i
  
      // ensure source as object
-     source = source instanceof SVG.Element ?
-       source.matrixify() :
-     typeof source === 'string' ?
-       arrayToMatrix(source.split(SVG.regex.delimiter).map(parseFloat)) :
-     arguments.length == 6 ?
-       arrayToMatrix([].slice.call(arguments)) :
-     Array.isArray(source) ?
-       arrayToMatrix(source) :
-     typeof source === 'object' ?
-       source : base
+     source = source instanceof SVG.Element ? source.matrixify()
+       : typeof source === 'string' ? arrayToMatrix(source.split(SVG.regex.delimiter).map(parseFloat))
+       : arguments.length === 6 ? arrayToMatrix([].slice.call(arguments))
+       : Array.isArray(source) ? arrayToMatrix(source)
+       : typeof source === 'object' ? source
+       : base
  
      // merge source
-     for (i = abcdef.length - 1; i >= 0; --i)
-       this[abcdef[i]] = source[abcdef[i]] != null ?
-         source[abcdef[i]] : base[abcdef[i]]
-   }
+     for (i = abcdef.length - 1; i >= 0; --i) {
+       this[abcdef[i]] = source[abcdef[i]] != null
+         ? source[abcdef[i]]
+         : base[abcdef[i]]
+     }
+   },
  
    // Add methods
 -  extend: {
 -    // Extract individual transformations
 -    extract: function () {
 -      // find delta transform points
 -      var px = deltaTransformPoint(this, 0, 1)
 -      var py = deltaTransformPoint(this, 1, 0)
 -      var skewX = 180 / Math.PI * Math.atan2(px.y, px.x) - 90
 +, extend: {
  
 +    // Convert an object of affine parameters into a matrix
 +    compose: function (o, cx, cy) {
 +
 +      // Set the defaults
 +      var tx = o.translateX || 0
 +        , ty = o.translateY || 0
 +        , theta = o.theta || 0
 +        , sx = o.scaleX || 1
 +        , sy = o.scaleY || 1
 +        , lam = o.shear || 0
 +        , cx = cx || 0
 +        , cy = cy || 0
 +
 +      // Calculate the trigonometric values
 +      var ct = Math.cos(theta * Math.PI / 180)
 +        , st Math.sin(theta * Math.PI / 180)
 +
 +      // Calculate the matrix components directly
 +      var a = sx * ct
 +        , b = sx * st
 +        , c = lam * sx * ct - sy * st
 +        , d = lam * sx * st + sy * ct
 +        , e = - sx * ct * (cx + cy * lam) + sy * st * cy + tx + cx
 +        , f = - sx * st * (cx + cy * lam) - sy * ct * cy + ty + cy
 +
 +      // Construct a new matrix and return it
 +      var matrix = new SVG.Matrix([a, b, c, d, e, f])
 +      return matrix
 +    }
 +    // Decompose a matrix into the affine parameters needed to form it
 +  , decompose: function (matrix, cx, cy) {
 +
 +      // Get the paramaters of the current matrix
 +      var a = matrix.a
 +        , b = matrix.b
 +        , c = matrix.c
 +        , d = matrix.d
 +        , e = matrix.e
 +        , f = matrix.f
 +
 +      // Project the first basis vector onto the unit circle
 +      var circle = unitCircle (a, b)
 +        , theta = circle.theta
 +        , ct = circle.cos
 +        , st = circle.sin
 +
 +      // Work out the transformation parameters
 +      var signX = Math.sign(a * ct + b * st)
 +        , sx = signX * mag (a, b)
 +        , lam = (st * d + ct * c) / (ct * a + st * b)
 +        , signY = Math.sign(- c * st + d * ct)
 +        , sy = mag (lam * a - c, d - lam * b)
 +        , tx = e - cx + cx * ct * sx + cy * (lam * ct * sx - st * sy)
 +        , ty = f - cy + cx * st * sx + cy * (lam * st * sx + ct * sy)
 +
 +      // Package and return the parameters
        return {
 +
 +        // Bundle the affine parameters
 +        translateX: tx
 +      , translateY: ty
 +      , theta: theta
 +      , scaleX: sx
 +      , scaleY: sy
 +      , shear: lam
 +
 +      // Bundle the matrix parameters
 +      , a: this.a
 +      , b: this.b
 +      , c: this.c
 +      , d: this.d
 +      , e: this.e
 +      , f: this.f
 +
 +      // Return the new origin point
 +      , x: this.e
 +      , y: this.f
 +
 +      // Store the matrix
 +      , matrix: new SVG.Matrix(this)
+         // translation
+         x: this.e,
+         y: this.f,
+         transformedX: (this.e * Math.cos(skewX * Math.PI / 180) + this.f * Math.sin(skewX * Math.PI / 180)) / Math.sqrt(this.a * this.a + this.b * this.b),
+         transformedY: (this.f * Math.cos(skewX * Math.PI / 180) + this.e * Math.sin(-skewX * Math.PI / 180)) / Math.sqrt(this.c * this.c + this.d * this.d),
+         // skew
+         skewX: -skewX,
+         skewY: 180 / Math.PI * Math.atan2(py.y, py.x),
+         // scale
+         scaleX: Math.sqrt(this.a * this.a + this.b * this.b),
+         scaleY: Math.sqrt(this.c * this.c + this.d * this.d),
+         // rotation
+         rotation: skewX,
+         a: this.a,
+         b: this.b,
+         c: this.c,
+         d: this.d,
+         e: this.e,
+         f: this.f,
+         matrix: new SVG.Matrix(this)
++>>>>>>> 3.0.0
        }
-     }
+     },
      // Clone matrix
 +  , form: function (o) {
 +
 +    // Get all of the parameters required to form the matrix
 +    var flipX = o.flip && (o.flip == "x" || o.flip == "both") ? -1 : 1
 +      , flipY = o.flip && (o.flip == "y" || o.flip == "both") ? -1 : 1
 +      , kX = o.skew.length ? o.skew[0]
 +        : isFinite(o.skew) ? o.skew
 +        : isFinite(o.skewX) ? o.skewX
 +        : 0
 +      , kY = o.skew.length ? o.skew[1]
 +        : isFinite(o.skew) ? o.skew
 +        : isFinite(o.skewY) ? o.skewY
 +        : 0
 +      , skewX = o.scale.length ? o.scale[0] * flipX
 +        : isFinite(o.scale) ? o.scale * flipX
 +        : isFinite(o.scaleX) ? o.scaleX * flipX
 +        : flipX
 +      , skewY = o.scale.length ? o.scale[1] * flipY
 +        : isFinite(o.scale) ? o.scale * flipY
 +        : isFinite(o.scaleY) ? o.scaleY * flipY
 +        : flipY
 +      , kx = Math.tan(SVG.utils.radians(skewX))
 +      , ky = Math.tan(SVG.utils.radians(skewY))
 +      , lam = o.shear || 0
 +      , theta = SVG.utils.radians(o.rotate || 0)
 +      , st = Math.sin(theta)
 +      , ct = Math.cos(theta)
 +      , ox = o.origin.length ? o.origin[0] : o.ox || 0
 +      , oy = o.origin.length ? o.origin[1] : o.oy || 0
 +      , px = o.position.length ? o.position[0] : o.px || ox
 +      , py = o.position.length ? o.position[1] : o.py || oy
 +      , tx = o.translate.length ? o.translate[0] : o.tx || 0
 +      , ty = o.translate.length ? o.translate[1] : o.ty || 0
 +
 +    // Form the matrix parameters... aka. welcome to wonderland! (used wolfram)
 +    var a = ct*sx + ky*st*sy
 +      , b = ct*ky*sy - st*sx
 +      , c = ct*kx*sx + st*sy + lam*(ct*sx+ky*st*sy)
 +      , d = -kx*st*sx + ct*sy + lam*(-st*sx + ct*ky*sy)
 +      , e = px + tx + cx*(ct*sx+ky*st*sy) + cy*(ct*kx*sx+st*sy+lam*(ct*sx+ky*st*sy))
 +      , f = py + ty + cx*(-st*sx + ct*ky*sy) + cy*(-kx*st*sx + ct*sy + lam*(-st*sx + ct*ky*sy))
 +      , result = new Matrix(a, b, c, d, e, f)
 +    return result
 +  }
 +  , clone: function() {
+     clone: function () {
++>>>>>>> 3.0.0
        return new SVG.Matrix(this)
-     }
+     },
      // Morph one matrix into another
-   , morph: function(matrix) {
+     morph: function (matrix) {
        // store new destination
        this.destination = new SVG.Matrix(matrix)
  
        })
  
        return matrix
-     }
+     },
      // Multiplies by given matrix
-   , multiply: function(matrix) {
+     multiply: function (matrix) {
        return new SVG.Matrix(this.native().multiply(parseMatrix(matrix).native()))
-     }
+     },
      // Inverses matrix
-   , inverse: function() {
+     inverse: function () {
        return new SVG.Matrix(this.native().inverse())
-     }
+     },
      // Translate matrix
 -    translate: function (x, y) {
 -      return new SVG.Matrix(this.native().translate(x || 0, y || 0))
 -    },
 +  , translate: function(x, y) {
 +    var translation = new SVG.Matrix(this.native().translate(x || 0, y || 0))
 +      , matrix = this.multiply(translation)
 +      return matrix
 +    }
      // Scale matrix
 -    scale: function (x, y, cx, cy) {
 -      // support uniformal scale
 -      if (arguments.length === 1) {
 +  , scale: function(x, y, cx, cy) {
 +      // Support uniform scaling
 +      if (arguments.length == 1) {
          y = x
-       } else if (arguments.length == 3) {
+       } else if (arguments.length === 3) {
          cy = cx
          cx = y
          y = x
        }
  
 -      return this.around(cx, cy, new SVG.Matrix(x, 0, 0, y, 0, 0))
 -    },
 +      // Rotate the current matrix
 +      var scale = new SVG.Matrix(x, 0, 0, y, 0, 0)
 +        , centered = this.around(cx, cy, rotation)
 +        , matrix = this.multiply(centered)
 +      return scale
 +    }
      // Rotate matrix
 -    rotate: function (r, cx, cy) {
 -      // convert degrees to radians
 +  , rotate: function(r, cx, cy) {
 +
 +      // Convert degrees to radians
        r = SVG.utils.radians(r)
  
 -      return this.around(cx, cy, new SVG.Matrix(Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0))
 -    },
 +      // Construct the rotation matrix
 +      var rotation = new SVG.Matrix(Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0)
 +        , centered = this.around(cx, cy, rotation)
 +        , matrix = this.multiply(centered)
 +      return matrix
 +    }
      // Flip matrix on x or y, at a given offset
-   , flip: function(a, o) {
-       return a == 'x' ?
-           this.scale(-1, 1, o, 0) :
-         a == 'y' ?
-           this.scale(1, -1, 0, o) :
-           this.scale(-1, -1, a, o != null ? o : a)
-     }
+     flip: function (a, o) {
+       return a === 'x' ? this.scale(-1, 1, o, 0)
+         : a === 'y' ? this.scale(1, -1, 0, o)
+         : this.scale(-1, -1, a, o != null ? o : a)
+     },
      // Skew
 -    skew: function (x, y, cx, cy) {
 +  , shear: function(a, cx, cy) {
 +    var shear = new SVG.Matrix(1, a, 0, 1, 0, 0)
 +      , centered = this.around(cx, cy, shear)
 +      , matrix = this.multiply(centered)
 +    return matrix
 +  }
 +  , skew: function(x, y, cx, cy) {
        // support uniformal skew
-       if (arguments.length == 1) {
+       if (arguments.length === 1) {
          y = x
-       } else if (arguments.length == 3) {
+       } else if (arguments.length === 3) {
          cy = cx
          cx = y
          y = x
        x = SVG.utils.radians(x)
        y = SVG.utils.radians(y)
  
 -      return this.around(cx, cy, new SVG.Matrix(1, Math.tan(y), Math.tan(x), 1, 0, 0))
 -    },
 +      // Construct the matrix
 +      var skew = new SVG.Matrix(1, Math.tan(y), Math.tan(x), 1, 0, 0)
 +        , centered = this.around(cx, cy, skew)
 +        , matrix = this.multiply(centered)
 +      return matrix
 +    }
      // SkewX
-   , skewX: function(x, cx, cy) {
+     skewX: function (x, cx, cy) {
        return this.skew(x, 0, cx, cy)
-     }
+     },
      // SkewY
-   , skewY: function(y, cx, cy) {
+     skewY: function (y, cx, cy) {
        return this.skew(0, y, cx, cy)
-     }
+     },
      // Transform around a center point
-   , around: function(cx, cy, matrix) {
+     around: function (cx, cy, matrix) {
        return this
          .multiply(new SVG.Matrix(1, 0, 0, 1, cx || 0, cy || 0))
          .multiply(matrix)
diff --cc src/sugar.js
index 838592ce49becf4a419eef84605d9274c4bce69e,c3a3332f996dc8e6f58f7298771bc60c1ea5c962..7c555dad2be2a6b252c4844acfc18b3dab1c2137
@@@ -32,46 -35,48 +35,46 @@@ var sugar = 
  
  SVG.extend([SVG.Element, SVG.FX], {
    // Map rotation to transform
 -  rotate: function (d, cx, cy) {
 +, rotate: function(angle, cx, cy) {
 +    var matrix = new SVG.Matrix().rotate(angle, cx, cy)
-     
++
      return this.transform({ rotation: d, cx: cx, cy: cy })
-   }
+   },
    // Map skew to transform
 -  skew: function (x, y, cx, cy) {
 -    return arguments.length === 1 || arguments.length === 3
 -      ? this.transform({ skew: x, cx: y, cy: cx })
 -      this.transform({ skewX: x, skewY: y, cx: cx, cy: cy })
 -  },
 +, skew: function(x, y, cx, cy) {
 +    return arguments.length == 1  || arguments.length == 3 ?
 +      this.transform({ skew: x, cx: y, cy: cx }) :
 +      this.transform({ skewX: x, skewY: y, cx: cx, cy: cy })
 +  }
    // Map scale to transform
, scale: function(x, y, cx, cy) {
-     return arguments.length == 1  || arguments.length == 3 ?
-       this.transform({ scale: x, cx: y, cy: cx }) :
-       this.transform({ scaleX: x, scaleY: y, cx: cx, cy: cy })
-   }
  scale: function (x, y, cx, cy) {
+     return arguments.length === 1 || arguments.length === 3
+       ? this.transform({ scale: x, cx: y, cy: cx })
+       this.transform({ scaleX: x, scaleY: y, cx: cx, cy: cy })
+   },
    // Map translate to transform
, translate: function(x, y) {
  translate: function (x, y) {
      return this.transform({ x: x, y: y })
-   }
+   },
    // Map flip to transform
, flip: function(a, o) {
-     o = typeof a == 'number' ? a : o
  flip: function (a, o) {
+     o = typeof a === 'number' ? a : o
      return this.transform({ flip: a || 'both', offset: o })
 -  },
 -  // Map matrix to transform
 -  matrix: function (m) {
 -    return this.attr('transform', new SVG.Matrix(arguments.length === 6 ? [].slice.call(arguments) : m))
 -  },
 +  }
    // Opacity
, opacity: function(value) {
  opacity: function (value) {
      return this.attr('opacity', value)
-   }
+   },
    // Relative move over x axis
, dx: function(x) {
  dx: function (x) {
      return this.x(new SVG.Number(x).plus(this instanceof SVG.FX ? 0 : this.x()), true)
-   }
+   },
    // Relative move over y axis
, dy: function(y) {
  dy: function (y) {
      return this.y(new SVG.Number(y).plus(this instanceof SVG.FX ? 0 : this.y()), true)
-   }
+   },
    // Relative move over x and y axes
, dmove: function(x, y) {
  dmove: function (x, y) {
      return this.dx(x).dy(y)
    }
  })
index 1175b13781c91a6c06c9edb2f012c4143d84e42e,54e9900942fac0a48758274e3292d744d3b06fb5..407630163f818e2660b9a87b0909d847f1a00113
+ /* global ensureCentre, capitalize, arrayToMatrix */
  
  SVG.extend(SVG.Element, {
 +
    // Add transformations
 -  transform: function (o, relative) {
 -    // get target in case of the fx module, otherwise reference this
 -    var target = this
 -    var matrix, bbox
 +  transform: function (o) {
  
 -    // act as a getter
 -    if (typeof o !== 'object') {
 -      // get current matrix
 -      matrix = new SVG.Matrix(target).extract()
 +    /**
 +     * EXTRACTING PARAMETERS
 +     */
  
 +    // Act as a getter if no object was passed
 +    if (typeof o !== 'object') {
 +      matrix = new SVG.Matrix(this).extract()
        return typeof o === 'string' ? matrix[o] : matrix
      }
  
 +    // Allow the user to define the origin with a string
 +    if (typeof o.origin === "string") {
 +
 +      // Get the bounding box and string to use in our calculations
 +      var string = o.origin.toLowerCase().trim()
 +        , bbox = this.bbox()
 +        , x = bbox.x
 +        , y = bbox.y
 +        , width = bbox.width
 +        , height = bbox.height
 +
 +      // Set the bounds eg : "bottom-left", "Top right", "middle" etc...
 +      o.ox = string.includes("left") ? x
 +        : string.includes("right") ? x + width
 +        : x + width / 2
 +      o.oy = string.includes("top") ? y
 +        : string.includes("bottom") ? y + height
 +        : y + height / 2
 +
 +      // Make sure we only pass ox and oy
 +      o.origin = null
 +    }
 +
 +    // Get the resulting matrix and apply it to the element
 +    var result = new SVG.Matrix().form(o)
 +      , matrixString = result.toString()
+     // get current matrix
+     matrix = new SVG.Matrix(target)
+     // ensure relative flag
+     relative = !!relative || !!o.relative
+     // act on matrix
+     if (o.a != null) {
+       matrix = relative
+         ? matrix.multiply(new SVG.Matrix(o))
+         : new SVG.Matrix(o)
+     // act on rotation
+     } else if (o.rotation != null) {
+       // ensure centre point
+       ensureCentre(o, target)
+       // apply transformation
+       matrix = relative
+         ? matrix.rotate(o.rotation, o.cx, o.cy)
+         : matrix.rotate(o.rotation - matrix.extract().rotation, o.cx, o.cy)
+     // act on scale
+     } else if (o.scale != null || o.scaleX != null || o.scaleY != null) {
+       // ensure centre point
+       ensureCentre(o, target)
+       // ensure scale values on both axes
+       o.scaleX = o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1
+       o.scaleY = o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1
+       if (!relative) {
+         // absolute; multiply inversed values
+         var e = matrix.extract()
+         o.scaleX = o.scaleX * 1 / e.scaleX
+         o.scaleY = o.scaleY * 1 / e.scaleY
+       }
+       matrix = matrix.scale(o.scaleX, o.scaleY, o.cx, o.cy)
+     // act on skew
+     } else if (o.skew != null || o.skewX != null || o.skewY != null) {
+       // ensure centre point
+       ensureCentre(o, target)
+       // ensure skew values on both axes
+       o.skewX = o.skew != null ? o.skew : o.skewX != null ? o.skewX : 0
+       o.skewY = o.skew != null ? o.skew : o.skewY != null ? o.skewY : 0
+       if (!relative) {
+         // absolute; reset skew values
+         var el = matrix.extract()
+         matrix = matrix.multiply(new SVG.Matrix().skew(el.skewX, el.skewY, el.cx, el.cy).inverse())
+       }
++>>>>>>> 3.0.0
  
 -      matrix = matrix.skew(o.skewX, o.skewY, o.cx, o.cy)
 +    // Apply the result
 +    return this.attr('transform', matrix)
 +    }
 +  }
  
 -    // act on flip
 -    } else if (o.flip) {
 -      if (o.flip === 'x' || o.flip === 'y') {
 -        o.offset = o.offset == null ? target.bbox()['c' + o.flip] : o.offset
 -      } else {
 -        if (o.offset == null) {
 -          bbox = target.bbox()
 -          o.flip = bbox.cx
 -          o.offset = bbox.cy
 -        } else {
 -          o.flip = o.offset
 -        }
 -      }
 +  // Map matrix to transform
 +, matrix: function(m, relative) {
  
 -      matrix = new SVG.Matrix().flip(o.flip, o.offset)
 +    // Construct a matrix from the first parameter
 +    var matrix = new SVG.Matrix(m)
  
 -    // act on translate
 -    } else if (o.x != null || o.y != null) {
 -      if (relative) {
 -        // relative
 -        matrix = matrix.translate(o.x, o.y)
 -      } else {
 -        // absolute
 -        if (o.x != null) matrix.e = o.x
 -        if (o.y != null) matrix.f = o.y
 -      }
 +    // If we have a relative matrix, we just apply the old matrix
 +    if (relative) {
 +      var oldMatrix = new SVG.Matrix(this)
 +      matrix = oldMatrix.multiply(matrix)
      }
  
 +    // Apply the matrix directly
      return this.attr('transform', matrix)
    }
  })
  
  SVG.extend(SVG.FX, {
 -  transform: function (o, relative) {
 -    // get target in case of the fx module, otherwise reference this
 -    var target = this.target()
 -    var matrix, bbox
 -
 -    // act as a getter
 -    if (typeof o !== 'object') {
 -      // get current matrix
 -      matrix = new SVG.Matrix(target).extract()
 -      return typeof o === 'string' ? matrix[o] : matrix
 -    }
 -
 -    // ensure relative flag
 -    relative = !!relative || !!o.relative
 -
 -    // act on matrix
 -    if (o.a != null) {
 -      matrix = new SVG.Matrix(o)
 -
 -    // act on rotation
 -    } else if (o.rotation != null) {
 -      // ensure centre point
 -      ensureCentre(o, target)
 -
 -      // apply transformation
 -      matrix = new SVG.Rotate(o.rotation, o.cx, o.cy)
 -
 -    // act on scale
 -    } else if (o.scale != null || o.scaleX != null || o.scaleY != null) {
 -      // ensure centre point
 -      ensureCentre(o, target)
 -
 +  transform: function(o, relative) {
 +
 +
 +
 +
 +  //   // get target in case of the fx module, otherwise reference this
 +  //   var target = this.target()
 +  //     , matrix, bbox
 +  //
 +  //   // act as a getter
 +  //   if (typeof o !== 'object') {
 +  //     // get current matrix
 +  //     matrix = new SVG.Matrix(target).extract()
 +  //
 +  //     return typeof o === 'string' ? matrix[o] : matrix
 +  //   }
 +  //
 +  //   // ensure relative flag
 +  //   relative = !!relative || !!o.relative
 +  //
 +  //   // act on matrix
 +  //   if (o.a != null) {
 +  //     matrix = new SVG.Matrix(o)
 +  //
 +  //   // act on rotation
 +  //   } else if (o.rotation != null) {
 +  //     // ensure centre point
 +  //     ensureCentre(o, target)
 +  //
 +  //     // apply transformation
 +  //     matrix = new SVG.Rotate(o.rotation, o.cx, o.cy)
 +  //
 +  //   // act on scale
 +  //   } else if (o.scale != null || o.scaleX != null || o.scaleY != null) {
 +  //     // ensure centre point
 +  //     ensureCentre(o, target)
 +  //
 +  //     // ensure scale values on both axes
 +  //     o.scaleX = o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1
 +  //     o.scaleY = o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1
 +  //
 +  //     matrix = new SVG.Scale(o.scaleX, o.scaleY, o.cx, o.cy)
 +  //
 +  //   // act on skew
 +  //   } else if (o.skewX != null || o.skewY != null) {
 +  //     // ensure centre point
 +  //     ensureCentre(o, target)
 +  //
 +  //     // ensure skew values on both axes
 +  //     o.skewX = o.skewX != null ? o.skewX : 0
 +  //     o.skewY = o.skewY != null ? o.skewY : 0
 +  //
 +  //     matrix = new SVG.Skew(o.skewX, o.skewY, o.cx, o.cy)
 +  //
 +  //   // act on flip
 +  //   } else if (o.flip) {
 +  //     if(o.flip == 'x' || o.flip == 'y') {
 +  //       o.offset = o.offset == null ? target.bbox()['c' + o.flip] : o.offset
 +  //     } else {
 +  //       if(o.offset == null) {
 +  //         bbox = target.bbox()
 +  //         o.flip = bbox.cx
 +  //         o.offset = bbox.cy
 +  //       } else {
 +  //         o.flip = o.offset
 +  //       }
 +  //     }
 +  //
 +  //     matrix = new SVG.Matrix().flip(o.flip, o.offset)
 +  //
 +  //   // act on translate
 +  //   } else if (o.x != null || o.y != null) {
 +  //     matrix = new SVG.Translate(o.x, o.y)
 +  //   }
 +  //
 +  //   if(!matrix) return this
 +  //
 +  //   matrix.relative = relative
 +  //
 +  //   this.last().transforms.push(matrix)
 +  //
 +  //   return this._callStart()
 +  // }
+       // ensure scale values on both axes
+       o.scaleX = o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1
+       o.scaleY = o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1
+       matrix = new SVG.Scale(o.scaleX, o.scaleY, o.cx, o.cy)
+     // act on skew
+     } else if (o.skewX != null || o.skewY != null) {
+       // ensure centre point
+       ensureCentre(o, target)
+       // ensure skew values on both axes
+       o.skewX = o.skewX != null ? o.skewX : 0
+       o.skewY = o.skewY != null ? o.skewY : 0
+       matrix = new SVG.Skew(o.skewX, o.skewY, o.cx, o.cy)
+     // act on flip
+     } else if (o.flip) {
+       if (o.flip === 'x' || o.flip === 'y') {
+         o.offset = o.offset == null ? target.bbox()['c' + o.flip] : o.offset
+       } else {
+         if (o.offset == null) {
+           bbox = target.bbox()
+           o.flip = bbox.cx
+           o.offset = bbox.cy
+         } else {
+           o.flip = o.offset
+         }
+       }
+       matrix = new SVG.Matrix().flip(o.flip, o.offset)
+     // act on translate
+     } else if (o.x != null || o.y != null) {
+       matrix = new SVG.Translate(o.x, o.y)
+     }
+     if (!matrix) return this
+     matrix.relative = relative
+     this.last().transforms.push(matrix)
+     return this._callStart()
+   }
++>>>>>>> 3.0.0
  })
  
  SVG.extend(SVG.Element, {
      return this
    },
    // same as above with parent equals root-svg
-   toDoc: function() {
+   toDoc: function () {
      return this.toParent(this.doc())
    }
 -
  })
  
 +// TODO: DESTROY
 +//       =======
 +//
 +//
 +// SVG.Transformation = SVG.invent({
 +//
 +//   create: function(source, inversed){
 +//
 +//     if(arguments.length > 1 && typeof inversed != 'boolean'){
 +//       return this.constructor.call(this, [].slice.call(arguments))
 +//     }
 +//
 +//     if(Array.isArray(source)){
 +//       for(var i = 0, len = this.arguments.length; i < len; ++i){
 +//         this[this.arguments[i]] = source[i]
 +//       }
 +//     } else if(typeof source == 'object'){
 +//       for(var i = 0, len = this.arguments.length; i < len; ++i){
 +//         this[this.arguments[i]] = source[this.arguments[i]]
 +//       }
 +//     }
 +//
 +//     this.inversed = false
 +//
 +//     if(inversed === true){
 +//       this.inversed = true
 +//     }
 +//
 +//   }
 +//
 +// , extend: {
 +//
 +//     arguments: []
 +//   , method: ''
 +//
 +//   , at: function(pos){
 +//
 +//       var params = []
 +//
 +//       for(var i = 0, len = this.arguments.length; i < len; ++i){
 +//         params.push(this[this.arguments[i]])
 +//       }
 +//
 +//       var m = this._undo || new SVG.Matrix()
 +//
 +//       m = new SVG.Matrix().morph(SVG.Matrix.prototype[this.method].apply(m, params)).at(pos)
 +//
 +//       return this.inversed ? m.inverse() : m
 +//
 +//     }
 +//
 +//   , undo: function(o){
 +//       for(var i = 0, len = this.arguments.length; i < len; ++i){
 +//         o[this.arguments[i]] = typeof this[this.arguments[i]] == 'undefined' ? 0 : o[this.arguments[i]]
 +//       }
 +//
 +//       // The method SVG.Matrix.extract which was used before calling this
 +//       // method to obtain a value for the parameter o doesn't return a cx and
 +//       // a cy so we use the ones that were provided to this object at its creation
 +//       o.cx = this.cx
 +//       o.cy = this.cy
 +//
 +//       this._undo = new SVG[capitalize(this.method)](o, true).at(1)
 +//
 +//       return this
 +//     }
 +//
 +//   }
 +//
 +// })
 +//
 +// SVG.Translate = SVG.invent({
 +//
 +//   parent: SVG.Matrix
 +// , inherit: SVG.Transformation
 +//
 +// , create: function(source, inversed){
 +//     this.constructor.apply(this, [].slice.call(arguments))
 +//   }
 +//
 +// , extend: {
 +//     arguments: ['transformedX', 'transformedY']
 +//   , method: 'translate'
 +//   }
 +//
 +// })
 +//
 +// SVG.Rotate = SVG.invent({
 +//
 +//   parent: SVG.Matrix
 +// , inherit: SVG.Transformation
 +//
 +// , create: function(source, inversed){
 +//     this.constructor.apply(this, [].slice.call(arguments))
 +//   }
 +//
 +// , extend: {
 +//     arguments: ['rotation', 'cx', 'cy']
 +//   , method: 'rotate'
 +//   , at: function(pos){
 +//       var m = new SVG.Matrix().rotate(new SVG.Number().morph(this.rotation - (this._undo ? this._undo.rotation : 0)).at(pos), this.cx, this.cy)
 +//       return this.inversed ? m.inverse() : m
 +//     }
 +//   , undo: function(o){
 +//       this._undo = o
 +//       return this
 +//     }
 +//   }
 +//
 +// })
 +//
 +// SVG.Scale = SVG.invent({
 +//
 +//   parent: SVG.Matrix
 +// , inherit: SVG.Transformation
 +//
 +// , create: function(source, inversed){
 +//     this.constructor.apply(this, [].slice.call(arguments))
 +//   }
 +//
 +// , extend: {
 +//     arguments: ['scaleX', 'scaleY', 'cx', 'cy']
 +//   , method: 'scale'
 +//   }
 +//
 +// })
 +//
 +// SVG.Skew = SVG.invent({
 +//
 +//   parent: SVG.Matrix
 +// , inherit: SVG.Transformation
 +//
 +// , create: function(source, inversed){
 +//     this.constructor.apply(this, [].slice.call(arguments))
 +//   }
 +//
 +// , extend: {
 +//     arguments: ['skewX', 'skewY', 'cx', 'cy']
 +//   , method: 'skew'
 +//   }
 +//
 +// })
+ SVG.Transformation = SVG.invent({
+   create: function (source, inversed) {
+     if (arguments.length > 1 && typeof inversed !== 'boolean') {
+       return this.constructor.bind(this)([].slice.call(arguments))
+     }
+     var i, len
+     if (Array.isArray(source)) {
+       for (i = 0, len = this.arguments.length; i < len; ++i) {
+         this[this.arguments[i]] = source[i]
+       }
+     } else if (typeof source === 'object') {
+       for (i = 0, len = this.arguments.length; i < len; ++i) {
+         this[this.arguments[i]] = source[this.arguments[i]]
+       }
+     }
+     this.inversed = false
+     if (inversed === true) {
+       this.inversed = true
+     }
+   },
+   extend: {
+     arguments: [],
+     method: '',
+     at: function (pos) {
+       var params = []
+       for (var i = 0, len = this.arguments.length; i < len; ++i) {
+         params.push(this[this.arguments[i]])
+       }
+       var m = this._undo || new SVG.Matrix()
+       m = new SVG.Matrix().morph(SVG.Matrix.prototype[this.method].apply(m, params)).at(pos)
+       return this.inversed ? m.inverse() : m
+     },
+     undo: function (o) {
+       for (var i = 0, len = this.arguments.length; i < len; ++i) {
+         o[this.arguments[i]] = typeof this[this.arguments[i]] === 'undefined' ? 0 : o[this.arguments[i]]
+       }
+       // The method SVG.Matrix.extract which was used before calling this
+       // method to obtain a value for the parameter o doesn't return a cx and
+       // a cy so we use the ones that were provided to this object at its creation
+       o.cx = this.cx
+       o.cy = this.cy
+       this._undo = new SVG[capitalize(this.method)](o, true).at(1)
+       return this
+     }
+   }
+ })
+ SVG.Translate = SVG.invent({
+   parent: SVG.Matrix,
+   inherit: SVG.Transformation,
+   create: function (source, inversed) {
+     this.constructor.apply(this, [].slice.call(arguments))
+   },
+   extend: {
+     arguments: ['transformedX', 'transformedY'],
+     method: 'translate'
+   }
+ })
+ SVG.Rotate = SVG.invent({
+   parent: SVG.Matrix,
+   inherit: SVG.Transformation,
+   create: function (source, inversed) {
+     this.constructor.apply(this, [].slice.call(arguments))
+   },
+   extend: {
+     arguments: ['rotation', 'cx', 'cy'],
+     method: 'rotate',
+     at: function (pos) {
+       var m = new SVG.Matrix().rotate(new SVG.Number().morph(this.rotation - (this._undo ? this._undo.rotation : 0)).at(pos), this.cx, this.cy)
+       return this.inversed ? m.inverse() : m
+     },
+     undo: function (o) {
+       this._undo = o
+       return this
+     }
+   }
+ })
+ SVG.Scale = SVG.invent({
+   parent: SVG.Matrix,
+   inherit: SVG.Transformation,
+   create: function (source, inversed) {
+     this.constructor.apply(this, [].slice.call(arguments))
+   },
+   extend: {
+     arguments: ['scaleX', 'scaleY', 'cx', 'cy'],
+     method: 'scale'
+   }
+ })
+ SVG.Skew = SVG.invent({
+   parent: SVG.Matrix,
+   inherit: SVG.Transformation,
+   create: function (source, inversed) {
+     this.constructor.apply(this, [].slice.call(arguments))
+   },
+   extend: {
+     arguments: ['skewX', 'skewY', 'cx', 'cy'],
+     method: 'skew'
+   }
+ })
++>>>>>>> 3.0.0