From 70745df23a46636028281bb0f1eaafadbf856e1f Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Wed, 11 Nov 2009 07:42:23 +0000 Subject: [PATCH] Fix matrixMultiply() to properly seek rotate/scales git-svn-id: http://svg-edit.googlecode.com/svn/branches/transformlist@929 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 145 ++++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 72 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 5d50a63a..2bc72a9c 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1388,28 +1388,52 @@ function BatchCommand(text) { // if it's a group, we have special reduction loops to flatten transforms if (selected.tagName == "g") { - // always remove translates by transferring them down to the children - // otherwise just reduce adjacent scales - - // The first pass is to collapse all adjacent translates and push - // them down to the children - var n = tlist.numberOfItems; + // loop through transforms and accumulate translation and scaling + var mat = svgroot.createSVGMatrix(); + var bFoundRotate = false; + n = tlist.numberOfItems; while (n--) { var xform = tlist.getItem(n); - // only flatten the translate if it is not before or after a scale - if (xform.type != 2 || (n < (tlist.numberOfItems-1) && tlist.getItem(n+1).type == 3) || - (n > 0 && tlist.getItem(n-1).type == 3)) - { - continue; + + if (xform.type == 4) { + // update the frame of reference + + mat = matrixMultiply(xform.matrix, mat); + bFoundRotate = true; +// break; } + // accumulate the transformed translation + else if (xform.type == 2) { + // get the centroid of scaling and determine the net translation for this scale + var tobj = transformToObj(xform); + + if (bFoundRotate && false) { + tx += tobj.tx; + ty += tobj.ty; + } + else { + var t = transformPoint(tobj.tx, tobj.ty, mat.inverse()); + // accumulate translation + tx += t.x; + ty += t.y; + } + + tlist.removeItem(n); + } + // if it's a scale, we accumulate it + else if (xform.type == 3) { + // update the frame of reference + mat = matrixMultiply(mat, xform.matrix); + // accumulate the scale values + var sobj = transformToObj(xform); + sx *= sobj.sx; + sy *= sobj.sy; - var tobj = transformToObj(xform); - tx += tobj.tx; - ty += tobj.ty; - // remove the translate from the group - tlist.removeItem(n); + tlist.removeItem(n); + } } - // now restore just the one translate + + // force the accumulated translation down to the children if (tx != 0 || ty != 0) { // we pass the translates down to the individual children var children = selected.childNodes; @@ -1426,56 +1450,7 @@ function BatchCommand(text) { } } - - // The second pass is to find all adjacent transform sets of the form: - // translate(tx,ty) scale(sx,sy) translate(-tx,-ty) and collapse them by - // applying the translates to the children and accumulating the scale factors - var mat = svgroot.createSVGMatrix(); - n = tlist.numberOfItems; - while (n--) { - var xform = tlist.getItem(n); - // we stop as soon as we encounter a rotate - if (xform.type == 4) { - // update the frame of reference - mat = matrixMultiply(mat, xform.matrix); - break; - } - // if it's a translate we push it to the children - else if (xform.type == 2) { - // get the centroid of scaling and determine the net translation for this scale - var tobj = transformToObj(xform); - - var t = transformPoint(tobj.tx, tobj.ty, mat.inverse()); - - // we pass the translates down to the individual children - var children = selected.childNodes; - var c = children.length; - while (c--) { - var child = children.item(c); - if (child.nodeType == 1) { - var childTlist = canvas.getTransformList(child); - var newxlate = svgroot.createSVGTransform(); - newxlate.setTranslate(t.x, t.y); - childTlist.insertItemBefore(newxlate, 0); - batchCmd.addSubCommand( recalculateDimensions(child) ); - } - } - - tlist.removeItem(n); - } - // if it's a scale, we accumulate it - else if (xform.type == 3) { - // update the frame of reference - mat = matrixMultiply(mat, xform.matrix); - // accumulate the scale values - var sobj = transformToObj(xform); - sx *= sobj.sx; - sy *= sobj.sy; - - tlist.removeItem(n); - } - } - // now restore just the one scale + // now apply only one scale if (sx != 1 || sy != 1) { var newscale = svgroot.createSVGTransform(); newscale.setScale(sx,sy); @@ -2034,9 +2009,13 @@ function BatchCommand(text) { return { x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f}; }; - // This is provided because WebKit doesn't implement multiply() correctly + // matrixMultiply() is provided because WebKit didn't implement multiply() correctly // on the SVGMatrix interface. See https://bugs.webkit.org/show_bug.cgi?id=16062 - // It returns a SVGMatrix that is the multiplication m1*m2. + // This function tries to return a SVGMatrix that is the multiplication m1*m2. + // As far as I can tell, there is no way for us to handle matrix multiplication + // of arbitrary matrices because we cannot directly set the a,b,c,d,e,f values + // of the resulting matrix, we have to do it with translate/rotate/scale + // TODO: investigate if webkit allows direct setting of matrix.a, etc var matrixMultiply = function(m1, m2) { var a = m1.a*m2.a + m1.c*m2.b, b = m1.b*m2.a + m1.d*m2.b, @@ -2044,11 +2023,33 @@ function BatchCommand(text) { d = m1.b*m2.c + m1.d*m2.d, e = m1.a*m2.e + m1.c*m2.f + m1.e, f = m1.b*m2.e + m1.d*m2.f + m1.f; + + // now construct a matrix by analyzing a,b,c,d,e,f and trying to + // translate, rotate, and scale the thing into place var m = svgroot.createSVGMatrix(); + var sx = 1, sy = 1, angle = 0; + + // translate m = m.translate(e,f); - m = m.scaleNonUniform(a,d); - m = m.skewX( Math.atan(c) ); - m = m.skewY( Math.atan(b) ); + + // see if there was a rotation + var rad = Math.atan2(b,a); + if (rad != 0 && rad != Math.PI && rad != -Math.PI) { + m = m.rotate(180.0 * rad / Math.PI); + sx = b / Math.sin(rad); + sy = -c / Math.sin(rad); + } + else { // in the case of no rotation, scales are simply a,d + sx = a; + sy = d; + } + + // scale + if (sx != 1 || sy != 1) { + m = m.scaleNonUniform(sx,sy); + } + + // TODO: handle skews? return m; }