<div id="draw"></div>
<svg id="native" width="100" height="100" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs"></svg>
<script src="../dist/svg.js"></script>
+ <script type="text/javascript" src="../src/helpers.js"></script>
+ <script type="text/javascript" src="../src/transform.js"></script>
+ <script type="text/javascript" src="../src/matrix.js"></script>
+ <script type="text/javascript" src="../src/morph.js"></script>
+ <script type="text/javascript" src="../src/runner.js"></script>
+ <script type="text/javascript" src="../src/timeline.js"></script>
+ <script type="text/javascript" src="../src/controller.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
<script src="svg.bench.js"></script>
<!--<script src="tests/10000-each.js"></script> -->
<script src="tests/10000-circles.js"></script>
<script src="tests/10000-paths.js"></script>
<script src="tests/10000-boxes.js"></script>
- <script src="tests/10000-pointArray-bbox.js"></script> -->
- <script src="tests/10000-accesses.js"></script>
+ <script src="tests/10000-pointArray-bbox.js"></script>
+ <script src="tests/10000-accesses.js"></script> -->
+ <script src="tests/10000-transform.js"></script>
<script>
SVG.bench.run()
</script>
--- /dev/null
+SVG.bench.describe('Transform 1000000 rects', function(bench) {
+ let parameters = {
+ translate: [20, 30],
+ origin: [100, 100],
+ rotate: 25,
+ skew: [10, 30],
+ scale: 0.5
+ }
+
+ let matrixLike = {a:2, b:3, c:1, d:2, e:49, f:100}
+ let matrix = new SVG.Matrix(matrixLike)
+
+ let worker = new SVG.Matrix()
+ bench.test('with parameters', function() {
+ for (var i = 0; i < 1000000; i++)
+ worker.transform(parameters)
+ })
+
+ worker = new SVG.Matrix()
+ bench.test('with matrix like', function() {
+ for (var i = 0; i < 1000000; i++) {
+ worker.transform(matrixLike)
+ }
+ })
+
+ worker = new SVG.Matrix()
+ bench.test('with SVG.Matrix', function() {
+ for (var i = 0; i < 1000000; i++)
+ worker.transform(matrix)
+ })
+})
+
/**
* FUZZYS EXAMPLE
*/
-// // // Make the pink rectangle
// let a = canvas.rect(200, 400).move(500, 400)
// .attr('opacity', 0.3)
// .addClass('pink')
// .transform({ tx: 300, ty: 500, origin: 'top-left' })
//
-//
-// var timer = 0
-// a.timeline().source(() => {
-// timer += 1
-// document.querySelector('#absolute span').textContent = timer
-// return timer
-// })
-//
// let obj = { rotate: 100, origin: 'top-left'}
// let obj2 = { rotate: 280, origin: 'center' }
// let obj3 = { rotate: 1000, origin: 'center', translate: [300, 200]}
//
//
-// // var c = a.clone()
-// // var d = a.clone()
-// //
-// //
-// // c.animate(1000)
-// // //.transform(obj)
-// // .transform(obj, true) // animation
-// //
-// // d.animate(3000)
-// // //.transform(obj)
-// // .transform(obj, true) // animation
-//
-// // a.clone().attr('fill', 'blue')
-// // //.transform(obj)
-// // .transform(obj2, true) // endPosition
+// var c = a.clone()
//
-// window.EL = canvas.ellipse(50, 50)
-//
-// let b = a.clone()
-// let bA = b.animate(new SVG.Spring(1000, 15))
-//
-// SVG.on(document, 'mousemove', (e) => {
-// let {x, y} = canvas.point(e.clientX, e.clientY)
-// bA.transform ({tx: x, ty: y, rotate: (x + y) / 3}, true)
-// })
+// c.animate(3000)
+// .transform(obj)
+// .transform(obj2, true) // animation
//
+// a.clone().attr('fill', 'blue')
+// .transform(obj)
+// .transform(obj2, true) // endPosition
+
+
+// SAIVANS EXAMPLE
+
canvas.ellipse(20, 20).center(100, 100)
let r = canvas.rect(200, 400).move(100, 100)
// canvas.circle(50).center(100, 0).attr('fill', 'gray')
//
// setTimeout(runTransformation({
-// rotate: 180,
+// rotate: 360,
// origin: [100, 0]
// }), 0.1 * wait )
//
//
// setTimeout(runTransformation({
-// rotate: 180,
+// rotate: 360,
// }), 0.4 * wait)
// const getConsole = (time) => {
<!-- include source files here... -->
<script src="../dist/svg.js" charset="utf-8"></script>
- <script src="../src/controller.js" charset="utf-8"></script>
- <script src="../src/morph.js" charset="utf-8"></script>
- <script src="../src/runner.js" charset="utf-8"></script>
- <script src="../src/timeline.js" charset="utf-8"></script>
+ <script type="text/javascript" src="../src/helpers.js"></script>
+ <script type="text/javascript" src="../src/transform.js"></script>
+ <script type="text/javascript" src="../src/matrix.js"></script>
+ <script type="text/javascript" src="../src/morph.js"></script>
+ <script type="text/javascript" src="../src/runner.js"></script>
+ <script type="text/javascript" src="../src/timeline.js"></script>
+ <script type="text/javascript" src="../src/controller.js"></script>
</head>
<script src="spec/ellipse.js"></script>
<script src="spec/event.js"></script>
<script src="spec/fx.js"></script>
- <script src="spec/gradient.js"></script>
+ <script src="spec/gradient.js"></script>-->
<script src="spec/helper.js"></script>
- <script src="spec/hyperlink.js"></script>
+ <!--<script src="spec/hyperlink.js"></script>
<script src="spec/image.js"></script>
<script src="spec/line.js"></script>
<script src="spec/marker.js"></script>
- <script src="spec/mask.js"></script>
+ <script src="spec/mask.js"></script>-->
<script src="spec/matrix.js"></script>
- <script src="spec/number.js"></script>
+ <!--<script src="spec/number.js"></script>
<script src="spec/path.js"></script>
<script src="spec/pattern.js"></script>
<script src="spec/point.js"></script>
<script src="spec/viewbox.js"></script> -->
<!-- <script src="spec/morphing.js"></script>
- <script src="spec/animator.js"></script> -->
- <script src="spec/runner.js"></script>
+ <script src="spec/animator.js"></script>
+ <script src="spec/runner.js"></script> -->
</body>
</html>
describe('Matrix', function() {
- var matrix
+ let comp = {a:2, b:0, c:0, d:2, e:100, f:50}
describe('initialization', function() {
- describe('without a source', function() {
-
- beforeEach(function() {
- matrix = new SVG.Matrix
- })
-
- it('creates a new matrix with default values', function() {
- expect(matrix.a).toBe(1)
- expect(matrix.b).toBe(0)
- expect(matrix.c).toBe(0)
- expect(matrix.d).toBe(1)
- expect(matrix.e).toBe(0)
- expect(matrix.f).toBe(0)
- })
-
- describe('toString()' , function() {
- it('exports correctly to a string', function() {
- expect(matrix.toString()).toBe('matrix(1,0,0,1,0,0)')
- })
- })
+ it('creates a new matrix with default values', function() {
+ let matrix = new SVG.Matrix()
+ expect(matrix).toEqual(jasmine.objectContaining(
+ {a:1, b:0, c:0, d:1, e:0, f:0}
+ ))
})
- describe('with an element given', function() {
- var rect
-
- beforeEach(function() {
- // Draw is defined in helpers
- rect = draw.rect(100, 100)
- .transform({
- rotate: -10,
- translate: [40, 50],
- scale: 2,
- })
- matrix = new SVG.Matrix(rect)
- })
-
- it('parses the current transform matrix from an element', function() {
-
- expect(matrix.a).toBeCloseTo(1.969615506024416)
- expect(matrix.b).toBeCloseTo(-0.34729635533386066)
- expect(matrix.c).toBeCloseTo(0.34729635533386066)
- expect(matrix.d).toBeCloseTo(1.969615506024416)
- expect(matrix.e).toBeCloseTo(-25.84559306791384)
- expect(matrix.f).toBeCloseTo(18.884042465472234)
- })
-
+ it('parses the current transform matrix from an element', function() {
+ let rect = draw.rect(100, 100).transform(comp)
+ let matrix = new SVG.Matrix(rect)
+ expect(matrix).toEqual(jasmine.objectContaining(comp))
})
- describe('with a string given', function() {
- it('parses the string value correctly', function() {
- var matrix = new SVG.Matrix('2, 0, 0, 2, 100, 50')
-
- expect(matrix.a).toBe(2)
- expect(matrix.b).toBe(0)
- expect(matrix.c).toBe(0)
- expect(matrix.d).toBe(2)
- expect(matrix.e).toBe(100)
- expect(matrix.f).toBe(50)
- })
+ it('parses a string value correctly', function() {
+ let matrix = new SVG.Matrix('2, 0, 0, 2, 100, 50')
+ expect(matrix).toEqual(jasmine.objectContaining(comp))
})
- describe('with an array given', function() {
- it('parses the array correctly', function() {
- var matrix = new SVG.Matrix([2, 0, 0, 2, 100, 50])
-
- expect(matrix.a).toBe(2)
- expect(matrix.b).toBe(0)
- expect(matrix.c).toBe(0)
- expect(matrix.d).toBe(2)
- expect(matrix.e).toBe(100)
- expect(matrix.f).toBe(50)
- })
+ it('parses an array correctly', function() {
+ let matrix = new SVG.Matrix([2, 0, 0, 2, 100, 50])
+ expect(matrix).toEqual(jasmine.objectContaining(comp))
})
- describe('with an object given', function() {
- it('parses the object correctly', function() {
- var matrix = new SVG.Matrix({a:2, b:0, c:0, d:2, e:100, f:50})
-
- expect(matrix.a).toBe(2)
- expect(matrix.b).toBe(0)
- expect(matrix.c).toBe(0)
- expect(matrix.d).toBe(2)
- expect(matrix.e).toBe(100)
- expect(matrix.f).toBe(50)
- })
+ it('parses an object correctly', function() {
+ let matrix = new SVG.Matrix(comp)
+ expect(matrix).toEqual(jasmine.objectContaining(comp))
})
- describe('with 6 arguments given', function() {
- it('parses the arguments correctly', function() {
- var matrix = new SVG.Matrix(2, 0, 0, 2, 100, 50)
+ it('parses a transform object correctly', function() {
+ let matrix = new SVG.Matrix({scale: 2, translate: [100, 50]})
+ expect(matrix).toEqual(jasmine.objectContaining(comp))
+ })
- expect(matrix.a).toBe(2)
- expect(matrix.b).toBe(0)
- expect(matrix.c).toBe(0)
- expect(matrix.d).toBe(2)
- expect(matrix.e).toBe(100)
- expect(matrix.f).toBe(50)
- })
+ it('parses 6 arguments correctly', function() {
+ let matrix = new SVG.Matrix(2, 0, 0, 2, 100, 50)
+ expect(matrix).toEqual(jasmine.objectContaining(comp))
})
+ })
+ describe('toString()' , function() {
+ it('exports correctly to a string', function() {
+ expect(new SVG.Matrix().toString()).toBe('matrix(1,0,0,1,0,0)')
+ })
})
describe('compose()', function() {
it('composes a matrix to form the correct result', function() {
- var composed = new SVG.Matrix().compose({
+ let composed = new SVG.Matrix().compose({
scaleX: 3, scaleY: 20, shear: 4, rotate: 50, translateX: 23, translateY: 52,
})
- var expected = new SVG.Matrix().scale(3, 20).shear(4).rotate(50).translate(23, 52)
+
+ let expected = new SVG.Matrix().scale(3, 20).shear(4).rotate(50).translate(23, 52)
expect(composed).toEqual(expected)
})
})
})
})
- describe('morph()', function() {
- it('stores a given matrix for morphing', function() {
- var matrix1 = new SVG.Matrix(2, 0, 0, 5, 0, 0)
- , matrix2 = new SVG.Matrix(1, 0, 0, 1, 4, 3)
-
- matrix1.morph(matrix2)
-
- expect(matrix1.destination).toEqual(matrix2)
- })
- it('stores a clone, not the given matrix itself', function() {
- var matrix1 = new SVG.Matrix(2, 0, 0, 5, 0, 0)
- , matrix2 = new SVG.Matrix(1, 0, 0, 1, 4, 3)
-
- matrix1.morph(matrix2)
-
- expect(matrix1.destination).not.toBe(matrix2)
- })
- })
-
- describe('at()', function() {
- it('returns a morphed array at a given position', function() {
- var matrix1 = new SVG.Matrix(2, 0, 0, 5, 0, 0)
- , matrix2 = new SVG.Matrix(1, 0, 0, 1, 4, 3)
- , matrix3 = matrix1.morph(matrix2).at(0.5)
-
- expect(matrix1.toString()).toBe('matrix(2,0,0,5,0,0)')
- expect(matrix2.toString()).toBe('matrix(1,0,0,1,4,3)')
- expect(matrix3.toString()).toBe('matrix(1.5,0,0,3,2,1.5)')
- })
- it('returns itself when no destination specified', function() {
- var matrix = new SVG.Matrix(2, 0, 0, 5, 0, 0)
- expect(matrix.at(0.5)).toBe(matrix)
- })
- })
-
describe('multiply()', function() {
- it('multiplies two matices', function() {
+ it('multiplies two matrices', function() {
var matrix1 = new SVG.Matrix(1, 4, 2, 5, 3, 6)
, matrix2 = new SVG.Matrix(7, 8, 8, 7, 9, 6)
, matrix3 = matrix1.multiply(matrix2)
var matrix1 = new SVG.Matrix(2, 0, 0, 5, 4, 3)
, matrix2 = matrix1.inverse()
- , abcdef = [0.5,0,0,0.2,-2,-0.6]
-
- expect(matrix1.toString()).toBe('matrix(2,0,0,5,4,3)')
+ , abcdef = [0.5, 0, 0, 0.2, -2, -0.6]
for(var i in 'abcdef') {
expect(matrix2['abcdef'[i]]).toBeCloseTo(abcdef[i])
}
+// left matrix, right matrix, target matrix which is overwritten
+function matrixMultiply (l, r, o) {
+ // Work out the product directly
+ var a = l.a * r.a + l.c * r.b
+ var b = l.b * r.a + l.d * r.b
+ var c = l.a * r.c + l.c * r.d
+ var d = l.b * r.c + l.d * r.d
+ var e = l.e + l.a * r.e + l.c * r.f
+ var f = l.f + l.b * r.e + l.d * r.f
+
+ // make sure to use local variables because l/r and o could be the same
+ o.a = a
+ o.b = b
+ o.c = c
+ o.d = d
+ o.e = e
+ o.f = f
+
+ return o
+}
+
+
function getOrigin (o, element, inSpace) {
// Allow origin or around as the names
let origin = o.origin // o.around == null ? o.origin : o.around
// Get the proposed transformations and the current transformations
var t = formatTransforms(o)
- var current = new SVG.Matrix(this)
+ var current = this//new SVG.Matrix(this) // FIXME: do we need a new matrix here?
let { x: ox, y: oy } = new SVG.Point(t.ox, t.oy).transform(current)
// Construct the resulting matrix
var transformer = new SVG.Matrix()
.translateO(t.rx, t.ry)
- .lmultiply(current)
+ .lmultiplyO(current)
.translateO(-ox, -oy)
.scaleO(t.scaleX, t.scaleY)
.skewO(t.skewX, t.skewY)
multiplyO: function (matrix) {
// Get the matrices
var l = this
- var r = new SVG.Matrix(matrix)
-
- // Work out the product directly
- var a = l.a * r.a + l.c * r.b
- var b = l.b * r.a + l.d * r.b
- var c = l.a * r.c + l.c * r.d
- var d = l.b * r.c + l.d * r.d
- var e = l.e + l.a * r.e + l.c * r.f
- var f = l.f + l.b * r.e + l.d * r.f
-
- this.a = a
- this.b = b
- this.c = c
- this.d = d
- this.e = e
- this.f = f
+ var r = matrix instanceof SVG.Matrix
+ ? matrix
+ : new SVG.Matrix(matrix)
- return this
+ return matrixMultiply(l, r, this)
},
lmultiply: function (matrix) {
- var result = new SVG.Matrix(matrix).multiplyO(this)
- return result
+ return this.clone().lmultiplyO(matrix)
+ },
+
+ lmultiplyO: function (matrix) {
+ var r = this
+ var l = matrix instanceof SVG.Matrix
+ ? matrix
+ : new SVG.Matrix(matrix)
+
+ return matrixMultiply(l, r, this)
},
// Inverses matrix
// Scale matrix
scale: function (x, y, cx, cy) {
- return this.clone().scaleO(x, y, cx, cy)
+ return this.scaleO.call(this.clone(), ...arguments)
+ //return this.clone().scaleO(x, y, cx, cy)
},
- scaleO: function (x, y, cx, cy) {
+ scaleO: function (x, y = x, cx = 0, cy = 0) {
// Support uniform scaling
- if (arguments.length === 1) {
- y = x
- } else if (arguments.length === 3) {
+ if (arguments.length == 3) {
cy = cx
cx = y
y = x
}
- // Scale the current matrix
- var scale = new SVG.Matrix(x, 0, 0, y, 0, 0)
- return this.aroundO(cx, cy, scale)
+ let {a, b, c, d, e, f} = this
+
+ this.a = a * x
+ this.b = b * y
+ this.c = c * x
+ this.d = d * y
+ this.e = e * x - cx * x + cx
+ this.f = f * y - cy * y + cy
+
+ return this
},
// Rotate matrix
return this.clone().rotateO(r, cx, cy)
},
- rotateO: function (r, cx, cy) {
+ rotateO: function (r, cx = 0, cy = 0) {
// Convert degrees to radians
r = SVG.utils.radians(r)
- // Construct the rotation matrix
- var rotation = new SVG.Matrix(Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0)
- return this.aroundO(cx, cy, rotation)
+ let cos = Math.cos(r)
+ let sin = Math.sin(r)
+
+ let {a, b, c, d, e, f} = this
+
+ this.a = a * cos - b * sin
+ this.b = b * cos + a * sin
+ this.c = c * cos - d * sin
+ this.d = d * cos + c * sin
+ this.e = e * cos - f * sin + cy * sin - cx * cos + cx
+ this.f = f * cos + e * sin - cx * sin - cy * cos + cy
+
+ return this
},
// Flip matrix on x or y, at a given offset
return this.clone().shearO(a, cx, cy)
},
- shearO: function (a, cx, cy) {
- var shear = new SVG.Matrix(1, 0, a, 1, 0, 0)
- return this.aroundO(cx, cy, shear)
+ shearO: function (lx, cx = 0, cy = 0) {
+ let {a, b, c, d, e, f} = this
+
+ this.a = a + b * lx
+ this.c = c + d * lx
+ this.e = e + f * lx - cy * lx
+
+ return this
},
// Skew Matrix
skew: function (x, y, cx, cy) {
- return this.clone().skewO(x, y, cx, cy)
+ return this.skewO.call(this.clone(), ...arguments)
+ //return this.clone().skew(x, y, cx, cy)
},
- skewO: function (x, y, cx, cy) {
+ skewO: function (x, y = x, cx = 0, cy = 0) {
// support uniformal skew
- if (arguments.length === 1) {
- y = x
- } else if (arguments.length === 3) {
+ if (arguments.length == 3) {
cy = cx
cx = y
y = x
x = SVG.utils.radians(x)
y = SVG.utils.radians(y)
- // Construct the matrix
- var skew = new SVG.Matrix(1, Math.tan(y), Math.tan(x), 1, 0, 0)
- return this.aroundO(cx, cy, skew)
+ let lx = Math.tan(x)
+ let ly = Math.tan(y)
+
+ let {a, b, c, d, e, f} = this
+
+ this.a = a + b * lx
+ this.b = b + a * ly
+ this.c = c + d * lx
+ this.d = d + c * ly
+ this.e = e + f * lx - cy * lx
+ this.f = f + e * ly - cx * ly
+
+ return this
},
// SkewX
aroundO: function (cx, cy, matrix) {
var dx = cx || 0
var dy = cy || 0
- return this.translateO(-dx, -dy).lmultiply(matrix).translateO(dx, dy)
+ return this.translateO(-dx, -dy).lmultiplyO(matrix).translateO(dx, dy)
},
around: function (cx, cy, matrix) {
// })
//
// SVG.extend(SVG.Matrix, extensions)
+
+
+
+
+// function matrixMultiplyParams (matrix, a, b, c, d, e, f) {
+// return matrixMultiply({a, b, c, d, e, f}, matrix, matrix)
+// }
},
addTransform: function (transform, index) {
- this.transforms = this.transforms.lmultiply(transform)
- // this._element.addToCurrentTransform(transform)
+ this.transforms.lmultiplyO(transform)
return this
},
SVG.FakeRunner = class {
constructor (transforms = new SVG.Matrix(), id = -1, done = true) {
- // Object.assign(this, {transforms, id, done})
-
this.transforms = transforms
this.id = id
this.done = done
})
-const lmultiply = (last, curr) => last.lmultiply(curr)
+const lmultiply = (last, curr) => last.lmultiplyO(curr)
const getRunnerTransform = (runner) => runner.transforms
function mergeTransforms () {
let runners = this._transformationRunners
let netTransform = runners
.map(getRunnerTransform)
- .reduce(lmultiply)
+ .reduce(lmultiply, new SVG.Matrix())
this.transform(netTransform)
// taken into account
.filter((runner) => runner.id <= current.id)
.map(getRunnerTransform)
- .reduce(lmultiply)
+ .reduce(lmultiply, new SVG.Matrix())
},
addRunner: function (runner) {
return this.plot([a, b, c, d])
}
+ // FIXME: this needs to be rewritten such that the element is only accesed
+ // in the init function
return this._queueObject('plot', new this._element.MorphArray(a))
/*var morpher = this._element.morphArray().to(a)