From dba61fb80391db9fb68790fe01af9359a0b21160 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Thu, 3 Dec 2009 16:31:00 +0000 Subject: [PATCH 01/38] Making a branch to fix transform problems in groups and ungrouped elements git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@992 eee81c28-f429-11dd-99c0-75d572ba1ddd From 464b1580bfd4904128b11c5786360bcf4a359568 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Thu, 3 Dec 2009 16:34:44 +0000 Subject: [PATCH 02/38] fixtransforms branch: Another attempt to fix transforms on groups/ungrouped elements - mostly broken, still a work-in-progress git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@993 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 275 +++++++++++++++----------------------------- 1 file changed, 95 insertions(+), 180 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index ca94887c..43ab595b 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -11,6 +11,12 @@ /* TODOs for TransformList: + * When ungrouping, always end up with a single [M] + * When rotating, always end up with [Rc][M] + * When scaling a group, [Rc][M] + * When scaling a shape, [Rc] + * When moving, always end up with a single [M] + * Fix Opera's centering of rotated, resized groups * Fix resizing of rotated already-resized groups (scales incorrect with mouse) * Ensure ungrouping works (Issue 204) @@ -1417,118 +1423,38 @@ function BatchCommand(text) { // save the start transform value too initial["transform"] = start_transform ? start_transform : ""; - // reduce the transform list here... - // if it's a group, we have special processing to flatten transforms if (selected.tagName == "g") { - - var angle = 0; - var sx = 1, sy = 1; var tx = 0, ty = 0; - var oldcx = 0, oldcy = 0, newcx = 0, newcy = 0; - var opType = 0; - - /* - The current concatenated matrix will be of the form: - - [ T ] [ R,oldc ] [ S ] [ T,s ] [ S,new ] [ - T,s ] - - which can be simply represented as: - - | x' | | a c e | | x | - | y' | = | b d f | * | y | - | 1 | | 0 0 1 | | 1 | - - where: a = A*cos(r), c = -C*sin(r) - b = A*sin(r), d = C*cos(r) - e,f are the translation required to recenter and properly scale it. - A is the total x scale factor - C is the total y scale factor - - We always want to reduce the new transformation matrix to: - - [ R,newc ] [ S ] - - which will be of the form: - - | x' | | a c g | | x + tx | - | y' | = | b d h | * | y + ty | - | 1 | | 0 0 1 | | 1 | - - where: tx,ty are appropriate translations on the children so that the effect - is identical to the original concatenated matrix above - g,h are translations to recenter the rotation. - - We can get a, b, c, d, e, f from the actual tlist matrix. - We can calculate g,h from the new bounding box. - - Thus, we solve for tx,ty and we get: - - tx = ( (e-g)*cos(r) + (f-h)*sin(r) ) / A - ty = ( -(e-g)*sin(r) + (e-g)*cos(r) ) / C - */ - - // First, we quickly extract the factors: - var N = tlist.numberOfItems; - var i = N; - while (i--) { - var xform = tlist.getItem(i); - if (xform.type == 3) { - // extract scale factors - var sobj = transformToObj(xform); - sx *= sobj.sx; - sy *= sobj.sy; - } - else if (xform.type == 4) { - var robj = transformToObj(xform); - // extract angle and old center - angle = robj.angle; - oldcx = robj.cx; - oldcy = robj.cy; - newcx = oldcx; - newcy = oldcy; - } - } - - // now find the new transformed bbox so we can determine what the - // new center of rotation should be - var origm = transformListToTransform(tlist).matrix; - if (angle != 0) { - var box = canvas.getBBox(selected); - var topleft = transformPoint(box.x,box.y,origm); - var botright = transformPoint(box.x+box.width,box.y+box.height,origm); - newcx = topleft.x + (botright.x-topleft.x)/2; - newcy = topleft.y + (botright.y-topleft.y)/2; - } - - // now get e,f and calculate g,h - var e = origm.e, - f = origm.f; - var rad = angle * Math.PI / 180; - var g = newcx * (1 - Math.cos(rad)) + newcy * Math.sin(rad); - h = newcy * (1 - Math.cos(rad)) - newcx * Math.sin(rad); - - // now actually calculate the new scale factors - var tx = ( (e-g)*Math.cos(rad) + (f-h)*Math.sin(rad) ) / sx, - ty = ( -(e-g)*Math.sin(rad) + (f-h)*Math.cos(rad) ) / sy; - - // now we can remove all transforms from the list and create our new transforms - tlist.clear(); - if (angle) { - var rot = svgroot.createSVGTransform(); - rot.setRotate(angle,newcx,newcy); - tlist.appendItem(rot); + // if we have a translate as the first transform, let's push it down to the children + var xform = tlist.getItem(0); + if (xform.type == 2 && (tlist.numberOfItems < 3 || tlist.getItem(1).type != 3)) { + var m = xform.matrix; + tx = m.e; + ty = m.f; + tlist.removeItem(0); } - if (sx != 1 || sy != 1) { - var scale = svgroot.createSVGTransform(); - scale.setScale(sx,sy); - tlist.appendItem(scale); + // if we have any other transforms, collapse them all down to a matrix + var m = transformListToTransform(tlist).matrix; + if (tlist.numberOfItems > 0) { + var newxform = svgroot.createSVGTransform(); + newxform.setMatrix(m); + tlist.clear(); + tlist.appendItem(newxform); } - - // force the accumulated translation down to the children + if (tx != 0 || ty != 0) { + var a = m.a, b = m.b, c = m.c, d = m.d; + // is it possible for (bc - ad) to equal zero? + var denom = b*c - a*d; + var ntx = (c*ty - d*tx) / denom, + nty = (b*tx - a*ty) / denom; + tx = ntx; + ty = nty; + + // now push this transform down to the children // FIXME: unfortunately recalculateDimensions depends on this global variable var old_start_transform = start_transform; start_transform = null; @@ -1556,28 +1482,32 @@ function BatchCommand(text) { // This pass loop in reverse order and removes any translates or scales. // Once we hit our first rotate(), we will only remove translates. - var bRemoveTransform = true; n = tlist.numberOfItems; + var m = svgroot.createSVGMatrix(); // identity while (n--) { - // once we reach an unmoveable transform, we can stop var xform = tlist.getItem(n); - var m = xform.matrix; - // if translate... + // get the matrix of all transformations to the right of this transform + // and its inverse (B and B_inv) + var tail = transformListToTransform(tlist, n+1, tlist.numberOfItems-1).matrix, + tail_inv = tail.inverse(); + // multiply (B_inv * A * B) + m = matrixMultiply(tail_inv, matrixMultiply(xform.matrix,tail)); + var remap = null, scalew = null, scaleh = null; switch (xform.type) { + case 1: // MATRIX - continue + continue; case 2: // TRANSLATE - always remove remap = function(x,y) { return transformPoint(x,y,m); }; scalew = function(w) { return w; } scaleh = function(h) { return h; } break; - case 3: // SCALE - only remove if we haven't hit a rotate - if (!bRemoveTransform) continue; + case 3: // SCALE - always remove remap = function(x,y) { return transformPoint(x,y,m); }; scalew = function(w) { return m.a * w; } scaleh = function(h) { return m.d * h; } break; - case 4: // ROTATE - only re-center if we haven't previously hit a rotate - if (!bRemoveTransform) continue; + case 4: // ROTATE // if the new center of the shape has moved, then // re-center the rotation, and determine the movement // offset required to keep the shape in the same place @@ -1600,8 +1530,6 @@ function BatchCommand(text) { }; scalew = function(w) { return w; } scaleh = function(h) { return h; } - // this latches to false once we hit our first rotate transform - bRemoveTransform = false; var newrot = svgroot.createSVGTransform(); newrot.setRotate(xform.angle, cx, cy); tlist.replaceItem(newrot, n); @@ -1610,8 +1538,9 @@ function BatchCommand(text) { default: continue; } + if (!remap) continue; - + newcenter = remap(box.x+box.width/2, box.y+box.height/2); var bpt = remap(box.x,box.y); box.x = bpt.x; @@ -1706,23 +1635,10 @@ function BatchCommand(text) { break; } // switch on element type to get initial values - // we have eliminated the transform, so remove it from the list - if (bRemoveTransform) { + // if it wasn't a rotate, we have eliminated the transform, so remove it + if (xform.type != 4) { tlist.removeItem(n); } - - // now loop through the other transforms and adjust accordingly - for ( var j = n; j < tlist.numberOfItems; ++j) { - var changed_xform = tlist.getItem(j); - switch (changed_xform.type) { - // TODO: TRANSLATE, SCALE? - case 4: // rotate - var newrot = svgroot.createSVGTransform(); - newrot.setRotate(changed_xform.angle, newcenter.x, newcenter.y); - tlist.replaceItem(newrot, j); - break; - } - } } // looping for each transform } // a non-group @@ -2010,13 +1926,21 @@ function BatchCommand(text) { // This returns a single matrix Transform for a given Transform List // (this is the equivalent of SVGTransformList.consolidate() but unlike // that method, this one does not modify the actual SVGTransformList) + // This function is very liberal with its min,max arguments var transformListToTransform = function(tlist, min, max) { - if (min > max) { throw "min>max"; } var min = min || 0; - var max = max || tlist.numberOfItems; + var max = max || (tlist.numberOfItems-1); + min = parseInt(min); + max = parseInt(max); + if (min > max) { var temp = max; max = min; min = temp; } + var m = svgroot.createSVGMatrix(); - for (var i = min; i < max; ++i) { - m = matrixMultiply(m, tlist.getItem(i).matrix); + for (var i = min; i <= max; ++i) { + // if our indices are out of range, just use a harmless identity matrix + var mtom = (i >= 0 && i < tlist.numberOfItems ? + tlist.getItem(i).matrix : + svgroot.createSVGMatrix()); + m = matrixMultiply(m, mtom); } return svgroot.createSVGTransformFromMatrix(m); }; @@ -2395,6 +2319,8 @@ function BatchCommand(text) { break; case "rotate": started = true; + // append a dummy transform that will be used as the rotate + tlist.appendItem(svgroot.createSVGTransform()); // we are starting an undoable change (a drag-rotation) canvas.beginUndoableChange("transform", selectedElements); break; @@ -5125,21 +5051,31 @@ function BatchCommand(text) { return ret; }; - // TODO: do we need to sum up all rotation angles? + // we get the rotation angle in the tlist this.getRotationAngle = function(elem, to_rad) { var selected = elem || selectedElements[0]; // find the rotation transform (if any) and set it var tlist = canvas.getTransformList(selected); var t = tlist.numberOfItems; + var sangle = 0; while (t--) { var xform = tlist.getItem(t); + // rotation transform if (xform.type == 4) { - return to_rad ? xform.angle * Math.PI / 180.0 : xform.angle; + sangle += tlist.getItem(t).angle; } + // matrix transform +// else if (xform.type == 1) { +// var m = xform.matrix; +// sangle += Math.atan2(m.b,m.a) * 180.0 / Math.PI; +// } } - return 0; + return to_rad ? sangle * Math.PI / 180.0 : sangle; }; + // this should: + // - remove any old rotations if present + // - prepend a new rotation at the transformed center this.setRotationAngle = function(val,preventUndo) { // ensure val is the proper type val = parseFloat(val); @@ -5148,24 +5084,22 @@ function BatchCommand(text) { var bbox = elem.getBBox(); var cx = round(bbox.x+bbox.width/2), cy = round(bbox.y+bbox.height/2); var tlist = canvas.getTransformList(elem); - var rotIndex = 0; - // find the index of the rotation transform + + // remove the rotation transform var n = tlist.numberOfItems; while (n--) { - var xform = tlist.getItem(n); - if (xform.type == 4) { - rotIndex = n; - // TODO: get the rotational center here? + if (tlist.getItem(n).type == 4) { tlist.removeItem(n); - break; } } - // if we are not rotated yet, insert a dummy xform - var m = transformListToTransform(tlist).matrix; - var center = transformPoint(cx,cy,m); - var newrot = svgroot.createSVGTransform(); - newrot.setRotate(val, center.x, center.y); - tlist.insertItemBefore(newrot, rotIndex); + + // find R_nc and insert it + var center = transformPoint(cx,cy,transformListToTransform(tlist).matrix); + var R_nc = svgroot.createSVGTransform(); + R_nc.setRotate(val, center.x, center.y); + + tlist.insertItemBefore(R_nc,0); + if (!preventUndo) { // we need to undo it, then redo it so it can be undo-able! :) // TODO: figure out how to make changes to transform list undo-able cross-browser? @@ -5564,8 +5498,6 @@ function BatchCommand(text) { canvas.addToSelection([g], true); }; - // TODO: when transferring group's rotational transform to the children, must deal - // with children who are already rotated within the group (Issue 204) this.ungroupSelectedElement = function() { var g = selectedElements[0]; if (g.tagName == "g") { @@ -5574,6 +5506,7 @@ function BatchCommand(text) { var anchor = g.previousSibling; var children = new Array(g.childNodes.length); var xform = g.getAttribute("transform"); + // get consolidated matrix var m = transformListToTransform(canvas.getTransformList(g)).matrix; // TODO: get all fill/stroke properties from the group that we are about to destroy @@ -5585,43 +5518,25 @@ function BatchCommand(text) { // TODO: get the group's opacity and propagate it down to the children (multiply it // by the child's opacity (or 1.0) - var i = 0; - var gbox = g.getBBox(), - gx = gbox.x + gbox.width/2, - gy = gbox.y + gbox.height/2; - var gangle = canvas.getRotationAngle(g, true); while (g.firstChild) { var elem = g.firstChild; var oldNextSibling = elem.nextSibling; var oldParent = elem.parentNode; children[i++] = elem = parent.insertBefore(elem, anchor); batchCmd.addSubCommand(new MoveElementCommand(elem, oldNextSibling, oldParent)); + + // transfer the group's transform down to each child and then + // call recalculateDimensions() if (xform) { - var childBox = elem.getBBox(); - var cx = childBox.x + childBox.width/2, - cy = childBox.y + childBox.height/2, - dx = cx - gx, - dy = cy - gy, - r = Math.sqrt(dx*dx + dy*dy); - var tangle = gangle + Math.atan2(dy,dx); - var newcx = r * Math.cos(tangle) + gx, - newcy = r * Math.sin(tangle) + gy; - childBox.x += (newcx - cx); - childBox.y += (newcy - cy); - // now we add the angle that the element was rotated by - // if it's non-zero, we need to set the new transform - // otherwise, we clear it - var angle = gangle + canvas.getRotationAngle(elem, true); + var oldxform = elem.getAttribute("transform"); var changes = {}; - changes["transform"] = elem.getAttribute("transform"); - if (angle != 0) { - elem.setAttribute("transform", "rotate(" + (angle*180.0)/Math.PI + " " + cx + "," + cy + ")"); - } - else { - elem.setAttribute("transform", ""); - } - batchCmd.addSubCommand(new ChangeElementCommand(elem, changes)); + changes["transform"] = oldxform ? oldxform : ""; + + var newxform = svgroot.createSVGTransform(); + var chtlist = canvas.getTransformList(elem); + newxform.setMatrix(matrixMultiply(m,transformListToTransform(chtlist).matrix.inverse())); + chtlist.insertItemBefore(newxform,0); batchCmd.addSubCommand(recalculateDimensions(elem)); } } From 3cebabc86a9023d7aae44a50c5153c8759c5a5ac Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Thu, 3 Dec 2009 21:41:13 +0000 Subject: [PATCH 03/38] fixtransforms branch: Ensure rotations are re-centered git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@994 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 43ab595b..5a5189b8 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1479,6 +1479,7 @@ function BatchCommand(text) { var box = canvas.getBBox(selected); var origcenter = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; var newcenter = {x: origcenter.x, y: origcenter.y}; + var rotAngle = 0; // This pass loop in reverse order and removes any translates or scales. // Once we hit our first rotate(), we will only remove translates. @@ -1512,6 +1513,7 @@ function BatchCommand(text) { // re-center the rotation, and determine the movement // offset required to keep the shape in the same place if (origcenter.x != newcenter.x || origcenter.y != newcenter.y) { + rotAngle = xform.angle; var alpha = xform.angle * Math.PI / 180.0; // determine where the new rotated center should be @@ -1530,9 +1532,6 @@ function BatchCommand(text) { }; scalew = function(w) { return w; } scaleh = function(h) { return h; } - var newrot = svgroot.createSVGTransform(); - newrot.setRotate(xform.angle, cx, cy); - tlist.replaceItem(newrot, n); } break; default: @@ -1635,11 +1634,16 @@ function BatchCommand(text) { break; } // switch on element type to get initial values - // if it wasn't a rotate, we have eliminated the transform, so remove it - if (xform.type != 4) { - tlist.removeItem(n); - } + // we have eliminated the transform, so remove it + tlist.removeItem(n); } // looping for each transform + + // we may need to insert a rotation back now + if (rotAngle != 0) { + var newrot = svgroot.createSVGTransform(); + newrot.setRotate(xform.angle, newcenter.x, newcenter.y); + tlist.insertItemBefore(newrot, 0); + } } // a non-group // now we have a set of changes and an applied reduced transform list From 61d03c8cbfbc312d8d7a6aab306fb7b5cc5ec839 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Thu, 3 Dec 2009 21:50:46 +0000 Subject: [PATCH 04/38] fixtransforms branch: really fix rotations this time (I am not just trying to rack up revision numbers) git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@995 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 5a5189b8..dd291c40 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1512,6 +1512,7 @@ function BatchCommand(text) { // if the new center of the shape has moved, then // re-center the rotation, and determine the movement // offset required to keep the shape in the same place + rotAngle = xform.angle; if (origcenter.x != newcenter.x || origcenter.y != newcenter.y) { rotAngle = xform.angle; var alpha = xform.angle * Math.PI / 180.0; @@ -1533,6 +1534,9 @@ function BatchCommand(text) { scalew = function(w) { return w; } scaleh = function(h) { return h; } } + else { + tlist.removeItem(n); + } break; default: continue; @@ -1641,7 +1645,7 @@ function BatchCommand(text) { // we may need to insert a rotation back now if (rotAngle != 0) { var newrot = svgroot.createSVGTransform(); - newrot.setRotate(xform.angle, newcenter.x, newcenter.y); + newrot.setRotate(rotAngle, newcenter.x, newcenter.y); tlist.insertItemBefore(newrot, 0); } } // a non-group From 4b0d3b23d3312492df152aee4b2ba3be9d96b699 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Fri, 4 Dec 2009 05:18:20 +0000 Subject: [PATCH 05/38] fixtransforms branch: Properly update rotation center git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@996 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index dd291c40..66fb76e9 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -10,6 +10,9 @@ */ /* TODOs for TransformList: + + * Fix problem when moving elements that have [R][M] + * Fix problem when ungrouping rotated elements that were scaled in a group * When ungrouping, always end up with a single [M] * When rotating, always end up with [Rc][M] @@ -1514,7 +1517,6 @@ function BatchCommand(text) { // offset required to keep the shape in the same place rotAngle = xform.angle; if (origcenter.x != newcenter.x || origcenter.y != newcenter.y) { - rotAngle = xform.angle; var alpha = xform.angle * Math.PI / 180.0; // determine where the new rotated center should be @@ -5399,12 +5401,19 @@ function BatchCommand(text) { // if this element was rotated, and we changed the position of this element // we need to update the rotational transform attribute var angle = canvas.getRotationAngle(elem); - if (angle && attr != "transform") { - var cx = round(selectedBBoxes[i].x + selectedBBoxes[i].width/2), - cy = round(selectedBBoxes[i].y + selectedBBoxes[i].height/2); - var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join(''); - if (rotate != elem.getAttribute("transform")) { - elem.setAttribute("transform", rotate); + if (angle != 0 && attr != "transform") { + var tlist = canvas.getTransformList(elem); + var n = tlist.numberOfItems; + while (n--) { + var xform = tlist.getItem(n); + if (xform.type == 4) { + var cx = round(selectedBBoxes[i].x + selectedBBoxes[i].width/2), + cy = round(selectedBBoxes[i].y + selectedBBoxes[i].height/2); + var newrot = svgroot.createSVGTransform(); + newrot.setRotate(angle, cx, cy); + tlist.replaceItem(newrot, n); + break; + } } } } // if oldValue != newValue @@ -5543,8 +5552,15 @@ function BatchCommand(text) { var newxform = svgroot.createSVGTransform(); var chtlist = canvas.getTransformList(elem); - newxform.setMatrix(matrixMultiply(m,transformListToTransform(chtlist).matrix.inverse())); - chtlist.insertItemBefore(newxform,0); + + // [ gm ] [ chm ] = [ chm ] [ gm' ] + // [ gm' ] = [ chm_inv ] [ gm ] [ chm ] + var chm = transformListToTransform(chtlist).matrix, + chm_inv = chm.inverse(); + var gm = matrixMultiply( chm_inv, matrixMultiply( m, chm ) ); + newxform.setMatrix(gm); + chtlist.appendItem(newxform); + batchCmd.addSubCommand(recalculateDimensions(elem)); } } From 4c66ccbabe3bc6a79a4fcb13bc503ef77740d9b2 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Fri, 4 Dec 2009 17:53:05 +0000 Subject: [PATCH 06/38] Made un-rotated child elements properly resize when resizing non-rotated groups git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@997 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 62 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 66fb76e9..a660eba3 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1437,15 +1437,50 @@ function BatchCommand(text) { tx = m.e; ty = m.f; tlist.removeItem(0); - } + } // if we have a translate/scale/translate transform, push down to children + else if(tlist.numberOfItems >= 3 && tlist.getItem(1).type == 3) { + var trans_m = tlist.getItem(0).matrix; + var scale_m = tlist.getItem(1).matrix; - // if we have any other transforms, collapse them all down to a matrix - var m = transformListToTransform(tlist).matrix; - if (tlist.numberOfItems > 0) { - var newxform = svgroot.createSVGTransform(); - newxform.setMatrix(m); - tlist.clear(); - tlist.appendItem(newxform); + var children = selected.childNodes; + var c = children.length; + while (c--) { + var child = children.item(c); + if (child.nodeType == 1) { + + var angle = canvas.getRotationAngle(child); + if(angle) { + // TODO: Deal with rotated childen... + } + + // update the transform list with translate,scale,translate + var childTlist = canvas.getTransformList(child); + var translateOrigin = svgroot.createSVGTransform(), + scale = svgroot.createSVGTransform(), + translateBack = svgroot.createSVGTransform(); + translateOrigin.setTranslate(trans_m.e*-1, trans_m.f*-1); + scale.setScale(scale_m.a, scale_m.d); + translateBack.setTranslate(trans_m.e, trans_m.f); + childTlist.appendItem(translateBack); + childTlist.appendItem(scale); + childTlist.appendItem(translateOrigin); + batchCmd.addSubCommand( recalculateDimensions(child) ); + } + } + // Remove these transforms from group + var N = tlist.numberOfItems; + tlist.removeItem(N-1); + tlist.removeItem(N-2); + tlist.removeItem(N-3); + } // if we have any other transforms, collapse them all down to a matrix + else if(tlist.numberOfItems > 0) { + var m = transformListToTransform(tlist).matrix; + if (tlist.numberOfItems > 0) { + var newxform = svgroot.createSVGTransform(); + newxform.setMatrix(m); + tlist.clear(); + tlist.appendItem(newxform); + } } if (tx != 0 || ty != 0) { @@ -1954,6 +1989,17 @@ function BatchCommand(text) { } return svgroot.createSVGTransformFromMatrix(m); }; + +// // Easy way to loop through transform list, but may not be worthwhile +// var eachXform = function(elem, callback) { +// var tlist = canvas.getTransformList(elem); +// var num = tlist.numberOfItems; +// if(num == 0) return; +// while(num--) { +// var xform = tlist.getItem(num); +// callback(xform, tlist); +// } +// } // FIXME: this should not have anything to do with zoom here - update the one place it is used this way // converts a tiny object equivalent of a SVGTransform From 789c97759435bdc3c31fda363345cb8a5deb669a Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Sat, 5 Dec 2009 16:21:18 +0000 Subject: [PATCH 07/38] fixtransforms branch: switch order of translate/scale conversion for groups. Also fixed bug where dragging multiple elements would strip off their rotation git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@998 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 139 +++++++++++++++++++++++++++++++++----------- 1 file changed, 104 insertions(+), 35 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index a660eba3..f85ae874 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -11,6 +11,7 @@ /* TODOs for TransformList: + * Fix rotating groups turning into matrix() * Fix problem when moving elements that have [R][M] * Fix problem when ungrouping rotated elements that were scaled in a group @@ -1429,49 +1430,121 @@ function BatchCommand(text) { // if it's a group, we have special processing to flatten transforms if (selected.tagName == "g") { var tx = 0, ty = 0; + var N = tlist.numberOfItems; - // if we have a translate as the first transform, let's push it down to the children - var xform = tlist.getItem(0); - if (xform.type == 2 && (tlist.numberOfItems < 3 || tlist.getItem(1).type != 3)) { - var m = xform.matrix; - tx = m.e; - ty = m.f; - tlist.removeItem(0); - } // if we have a translate/scale/translate transform, push down to children - else if(tlist.numberOfItems >= 3 && tlist.getItem(1).type == 3) { - var trans_m = tlist.getItem(0).matrix; - var scale_m = tlist.getItem(1).matrix; + // first, if it was a scale then the second-last transform will be it + if (N >= 3 && tlist.getItem(N-2).type == 3 && + tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) + { + // if the children are unrotated, pass the scale down directly + // otherwise pass the equivalent matrix() down directly + var tm = tlist.getItem(N-3).matrix; + var sm = tlist.getItem(N-2).matrix; + var tmn = tlist.getItem(N-1).matrix; + var em = matrixMultiply(tm, matrixMultiply(sm, tmn)); 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 m = transformListToTransform(childTlist).matrix; var angle = canvas.getRotationAngle(child); if(angle) { - // TODO: Deal with rotated childen... + // TODO: this does not work yet + /* + // if the child is rotated, we get: + // [E][M] + // where [M] = [R][M_remainder] + + // [E][M] = [M][E2] + // [E2] = [M_inv][E][M] + var e2 = matrixMultiply(m.inverse(), matrixMultiply(em, m)); + + // this does not appear to work, something wrong with my logic here + var e2t = svgroot.createSVGTransform(); + e2t.setMatrix(e2); + childTlist.appendItem(e2t); + + // the rotation is no longer centered + // so we need to re-center it + + // TODO: find the old center + // TODO: find the new center + // TODO: find the transformed translation + // Is this taken care of by recalculateDimensions? + */ + } + else { + // update the transform list with translate,scale,translate + + // slide the [T][S][-T] from the front to the back + // [T][S][-T][M] = [M][T2][S2][-T2] + + // [T][S][-T][M] = [T][S][M][-T2] + // [-T2] = [M_inv][-T][M] + var t2n = matrixMultiply(m.inverse(), matrixMultiply(tmn,m)); + // [T2] is always negative translation of [-T2] + var t2 = svgroot.createSVGMatrix(); + t2.e = -t2n.e; + t2.f = -t2n.f; + + // [T][S][-T][M] = [M][T2][S2][-T2] + // [S2] = [T2_inv][M_inv][T][S][-T][M][-T2_inv] + var s2 = matrixMultiply( + t2.inverse(), matrixMultiply( + m.inverse(), matrixMultiply( + tm, matrixMultiply( + sm, matrixMultiply( + tmn, matrixMultiply( + m, t2n.inverse()) ) ) ) ) ); + + var translateOrigin = svgroot.createSVGTransform(), + scale = svgroot.createSVGTransform(), + translateBack = svgroot.createSVGTransform(); + translateOrigin.setTranslate(t2n.e, t2n.f); + scale.setScale(s2.a, s2.d); + translateBack.setTranslate(t2.e, t2.f); + childTlist.appendItem(translateBack); + childTlist.appendItem(scale); + childTlist.appendItem(translateOrigin); } - - // update the transform list with translate,scale,translate - var childTlist = canvas.getTransformList(child); - var translateOrigin = svgroot.createSVGTransform(), - scale = svgroot.createSVGTransform(), - translateBack = svgroot.createSVGTransform(); - translateOrigin.setTranslate(trans_m.e*-1, trans_m.f*-1); - scale.setScale(scale_m.a, scale_m.d); - translateBack.setTranslate(trans_m.e, trans_m.f); - childTlist.appendItem(translateBack); - childTlist.appendItem(scale); - childTlist.appendItem(translateOrigin); batchCmd.addSubCommand( recalculateDimensions(child) ); } } // Remove these transforms from group - var N = tlist.numberOfItems; tlist.removeItem(N-1); tlist.removeItem(N-2); tlist.removeItem(N-3); + } + + N = tlist.numberOfItems; + + // next, check if the first transform was a translate + // if we had [ T1 ] [ M ] we want to transform this into [ M ] [ T2 ] + // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] + if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && + tlist.getItem(0).type == 2) + { + var T_M = transformListToTransform(tlist).matrix; + logMatrix(T_M); + tlist.removeItem(0); + var M_inv = transformListToTransform(tlist).matrix.inverse(); + logMatrix(M_inv); + var M2 = matrixMultiply( M_inv, T_M ); + logMatrix(M2); + + tx = M2.e; + ty = M2.f; + } + // rotate? + else { + console.log('rotate?'); + } + + /* } // if we have any other transforms, collapse them all down to a matrix else if(tlist.numberOfItems > 0) { var m = transformListToTransform(tlist).matrix; @@ -1482,16 +1555,9 @@ function BatchCommand(text) { tlist.appendItem(newxform); } } + */ if (tx != 0 || ty != 0) { - var a = m.a, b = m.b, c = m.c, d = m.d; - // is it possible for (bc - ad) to equal zero? - var denom = b*c - a*d; - var ntx = (c*ty - d*tx) / denom, - nty = (b*tx - a*ty) / denom; - tx = ntx; - ty = nty; - // now push this transform down to the children // FIXME: unfortunately recalculateDimensions depends on this global variable var old_start_transform = start_transform; @@ -2119,9 +2185,12 @@ function BatchCommand(text) { } // else if it's a path, go into pathedit mode in mouseup - // insert a dummy transform so if the element is moved it will have + // insert a dummy transform so if the element(s) are moved it will have // a transform to use for its translate - tlist.insertItemBefore(svgroot.createSVGTransform(), 0); + for (var i = 0; i < selectedElements.length; ++i) { + var slist = canvas.getTransformList(selectedElements[i]); + slist.insertItemBefore(svgroot.createSVGTransform(), 0); + } } else { canvas.clearSelection(); From fecc1da764630996cfff335825753601dbee88e7 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Mon, 7 Dec 2009 14:51:32 +0000 Subject: [PATCH 08/38] Fixtransforms branch: Fixed issues 346 and 347, changed resetPathOrientation() to use transformPoint() git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@999 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svg-editor.js | 14 ++++++++++---- editor/svgcanvas.js | 25 ++++++++++--------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/editor/svg-editor.js b/editor/svg-editor.js index c86d3391..529ab264 100644 --- a/editor/svg-editor.js +++ b/editor/svg-editor.js @@ -197,15 +197,20 @@ function svg_edit_setup() { // called when any element has changed var elementChanged = function(window,elems) { - // selectedElement must be updated here in case it was changed - selectedElement = (elems.length == 1 || elems[1] == null ? elems[0] : null); + for (var i = 0; i < elems.length; ++i) { var elem = elems[i]; + // if the element changed was the svg, then it could be a resolution change if (elem && elem.tagName == "svg" && elem.getAttribute("viewBox")) { var vb = elem.getAttribute("viewBox").split(' '); changeResolution(parseInt(vb[2]), parseInt(vb[3])); + } + // Update selectedElement if element is no longer part of the image. + // This occurs for the text elements in Firefox + else if(elem && selectedElement && selectedElement.parentNode == null) { + selectedElement = elem; } } @@ -340,8 +345,9 @@ function svg_edit_setup() { var updateContextPanel = function() { var elem = selectedElement; var currentLayer = svgCanvas.getCurrentLayer(); + var currentMode = svgCanvas.getMode(); // No need to update anything else in rotate mode - if (svgCanvas.getMode() == 'rotate' && elem != null) { + if (currentMode == 'rotate' && elem != null) { $('#angle').val(svgCanvas.getRotationAngle(elem)); return; } else if(svgCanvas.addedNew && elem != null && elname == 'image') { @@ -356,7 +362,7 @@ function svg_edit_setup() { var angle = svgCanvas.getRotationAngle(elem); $('#angle').val(angle); - if(!is_node) { + if(!is_node && currentMode != 'pathedit') { $('#selected_panel').show(); // Elements in this array already have coord fields if($.inArray(elname, ['line', 'circle', 'ellipse']) != -1) { diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index f85ae874..2cf19f6e 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1476,6 +1476,7 @@ function BatchCommand(text) { // TODO: find the transformed translation // Is this taken care of by recalculateDimensions? */ + } else { // update the transform list with translate,scale,translate @@ -2188,6 +2189,7 @@ function BatchCommand(text) { // insert a dummy transform so if the element(s) are moved it will have // a transform to use for its translate for (var i = 0; i < selectedElements.length; ++i) { + if(selectedElements[i] == null) continue; var slist = canvas.getTransformList(selectedElements[i]); slist.insertItemBefore(svgroot.createSVGTransform(), 0); } @@ -2796,7 +2798,7 @@ function BatchCommand(text) { }; batchCmd.addSubCommand(new ChangeElementCommand(elem, changes)); canvas.clearSelection(); - resetPathOrientation(elem, angle); + resetPathOrientation(elem); addCommandToHistory(batchCmd); current_path = elem; setPointContainerTransform(""); // Maybe this should be in resetPointGrips? @@ -2808,20 +2810,12 @@ function BatchCommand(text) { } // Rotate all points of a path and remove its transform value - var resetPathOrientation = function(path, angle) { + var resetPathOrientation = function(path) { if(path == null || path.nodeName != 'path') return false; - var angle = angle * Math.PI / 180.0; - if(!angle) return false; + + var tlist = canvas.getTransformList(path); + var m = transformListToTransform(tlist).matrix; path.removeAttribute("transform"); - var bb = path.getBBox(); - var cx = bb.x + bb.width/2, - cy = bb.y + bb.height/2; - var rotate = function(x,y) { - x -= cx; y -= cy; - var r = Math.sqrt(x*x + y*y); - var theta = Math.atan2(y,x) + angle; - return [r * Math.cos(theta) + cx, r * Math.sin(theta) + cy]; - } var segList = path.pathSegList; var len = segList.numberOfItems; @@ -2834,7 +2828,8 @@ function BatchCommand(text) { $.each(['',1,2], function(j, n) { var x = seg['x'+n], y = seg['y'+n]; if(x && y) { - $.merge(pts, rotate(x,y)); + var pt = transformPoint(x, y, m); + pts.splice(pts.length, 0, pt.x, pt.y); } }); replacePathSeg(type, i, pts, path); @@ -2990,7 +2985,7 @@ function BatchCommand(text) { } else { // Get the correct BBox of the new path, then discard it - resetPathOrientation(path, angle); + resetPathOrientation(path); var bb = false; try { bb = path.getBBox(); From ed45a7b2fa7d4747652c7c1931fcc7df9f735ec0 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Tue, 8 Dec 2009 15:01:47 +0000 Subject: [PATCH 09/38] fixtransforms branch: Fixed issues 348, 349 and 350. Made multiplyMatrix() accept any amount of matrices as parameters. Improved BBox calculation on rotated groups git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1002 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 182 +++++++++++++++++++++++--------------------- 1 file changed, 95 insertions(+), 87 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 2cf19f6e..e0b29d39 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -88,10 +88,10 @@ function SvgCanvas(c) { var toXml = function(str) { - return str.replace("&", "&").replace("<", "<").replace(">",">"); + return $('

').text(str).html(); }; var fromXml = function(str) { - return str.replace(">", ">").replace("<", "<").replace("&", "&"); + return $('

').html(str).text(); }; var pathFuncsStrs = ['Moveto','Lineto','CurvetoCubic','CurvetoQuadratic','Arc','LinetoHorizontal','LinetoVertical','CurvetoCubicSmooth','CurvetoQuadraticSmooth'] @@ -1441,7 +1441,7 @@ function BatchCommand(text) { var tm = tlist.getItem(N-3).matrix; var sm = tlist.getItem(N-2).matrix; var tmn = tlist.getItem(N-1).matrix; - var em = matrixMultiply(tm, matrixMultiply(sm, tmn)); + var em = matrixMultiply(tm, sm, tmn); var children = selected.childNodes; var c = children.length; @@ -1461,7 +1461,7 @@ function BatchCommand(text) { // [E][M] = [M][E2] // [E2] = [M_inv][E][M] - var e2 = matrixMultiply(m.inverse(), matrixMultiply(em, m)); + var e2 = matrixMultiply(m.inverse(), em, m); // this does not appear to work, something wrong with my logic here var e2t = svgroot.createSVGTransform(); @@ -1486,7 +1486,7 @@ function BatchCommand(text) { // [T][S][-T][M] = [T][S][M][-T2] // [-T2] = [M_inv][-T][M] - var t2n = matrixMultiply(m.inverse(), matrixMultiply(tmn,m)); + var t2n = matrixMultiply(m.inverse(), tmn, m); // [T2] is always negative translation of [-T2] var t2 = svgroot.createSVGMatrix(); t2.e = -t2n.e; @@ -1494,13 +1494,7 @@ function BatchCommand(text) { // [T][S][-T][M] = [M][T2][S2][-T2] // [S2] = [T2_inv][M_inv][T][S][-T][M][-T2_inv] - var s2 = matrixMultiply( - t2.inverse(), matrixMultiply( - m.inverse(), matrixMultiply( - tm, matrixMultiply( - sm, matrixMultiply( - tmn, matrixMultiply( - m, t2n.inverse()) ) ) ) ) ); + var s2 = matrixMultiply(t2.inverse(), m.inverse(), tm, sm, tmn, m, t2n.inverse()); var translateOrigin = svgroot.createSVGTransform(), scale = svgroot.createSVGTransform(), @@ -1597,7 +1591,7 @@ function BatchCommand(text) { var tail = transformListToTransform(tlist, n+1, tlist.numberOfItems-1).matrix, tail_inv = tail.inverse(); // multiply (B_inv * A * B) - m = matrixMultiply(tail_inv, matrixMultiply(xform.matrix,tail)); + m = matrixMultiply(tail_inv, xform.matrix, tail); var remap = null, scalew = null, scaleh = null; switch (xform.type) { @@ -1913,6 +1907,8 @@ function BatchCommand(text) { selectedElements.sort(function(a,b) { if(a && b && a.compareDocumentPosition) { return 3 - (b.compareDocumentPosition(a) & 6); + } else if(a == null) { + return 1; } }); @@ -1998,40 +1994,51 @@ function BatchCommand(text) { // of the resulting matrix, we have to do it with translate/rotate/scale // TODO: Actually all browsers seem to allow setting of a-f in a SVGMatrix without // throwing an exception - perhaps an update was issued in SVG 1.1 2e? - var matrixMultiply = function(m1, m2) { - var a = m1.a*m2.a + m1.c*m2.b, - b = m1.b*m2.a + m1.d*m2.b, - c = m1.a*m2.c + m1.c*m2.d, - 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); - - // 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 { - sx = a / Math.cos(rad); - sy = d / Math.cos(rad); + var matrixMultiply = function() { + var multi2 = function(m1, m2) { + var a = m1.a*m2.a + m1.c*m2.b, + b = m1.b*m2.a + m1.d*m2.b, + c = m1.a*m2.c + m1.c*m2.d, + 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); + + // 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 { + sx = a / Math.cos(rad); + sy = d / Math.cos(rad); + } + + // scale + if (sx != 1 || sy != 1) { + m = m.scaleNonUniform(sx,sy); + } + + // TODO: handle skews? + + return m; } - // scale - if (sx != 1 || sy != 1) { - m = m.scaleNonUniform(sx,sy); + var args = arguments, i = args.length, m = args[i-1]; + + while(i-- > 1) { + var m1 = args[i-1]; + m = multi2(m1, m); } - - // TODO: handle skews? return m; } @@ -5472,7 +5479,7 @@ function BatchCommand(text) { if (oldval == null) oldval = ""; if (oldval !== newValue) { if (attr == "#text") { - var old_w = elem.getBBox().width; + var old_w = canvas.getBBox(elem).width; elem.textContent = newValue; elem = canvas.quickClone(elem); @@ -5667,7 +5674,7 @@ function BatchCommand(text) { // [ gm' ] = [ chm_inv ] [ gm ] [ chm ] var chm = transformListToTransform(chtlist).matrix, chm_inv = chm.inverse(); - var gm = matrixMultiply( chm_inv, matrixMultiply( m, chm ) ); + var gm = matrixMultiply( chm_inv, m, chm ); newxform.setMatrix(gm); chtlist.appendItem(newxform); @@ -5779,45 +5786,46 @@ function BatchCommand(text) { // Make sure the expected BBox is returned if the element is a group var getCheckedBBox = function(elem) { - if(elem.tagName == 'g') { - return canvas.getStrokedBBox($(elem).children()); - } else { - try { - var bb = elem.getBBox(); - var angle = canvas.getRotationAngle(elem); - if (angle && angle % 90) { - // Accurate way to get BBox of rotated element in Firefox: - // Put element in group and get its BBox - - var good_bb = false; - - // Get the BBox from the raw path for these elements - var elemNames = ['ellipse','path','line','polyline','polygon']; - if($.inArray(elem.tagName, elemNames) != -1) { + try { + // TODO: Fix issue with rotated groups. Currently they work + // fine in FF, but not in other browsers (same problem mentioned + // in Issue 339 comment #2). + + var bb = elem.getBBox(); + var angle = canvas.getRotationAngle(elem); + if (angle && angle % 90) { + // Accurate way to get BBox of rotated element in Firefox: + // Put element in group and get its BBox + + var good_bb = false; + + // Get the BBox from the raw path for these elements + var elemNames = ['ellipse','path','line','polyline','polygon']; + if($.inArray(elem.tagName, elemNames) != -1) { + bb = good_bb = canvas.convertToPath(elem, true, angle); + } else if(elem.tagName == 'rect') { + // Look for radius + var rx = elem.getAttribute('rx'); + var ry = elem.getAttribute('ry'); + if(rx || ry) { bb = good_bb = canvas.convertToPath(elem, true, angle); - } else if(elem.tagName == 'rect') { - // Look for radius - var rx = elem.getAttribute('rx'); - var ry = elem.getAttribute('ry'); - if(rx || ry) { - bb = good_bb = canvas.convertToPath(elem, true, angle); - } } - - if(!good_bb) { - var g = document.createElementNS(svgns, "g"); - var parent = elem.parentNode; - parent.replaceChild(g, elem); - g.appendChild(elem); - bb = g.getBBox(); - parent.insertBefore(elem,g); - parent.removeChild(g); - } - + } + + if(!good_bb) { + var g = document.createElementNS(svgns, "g"); + var parent = elem.parentNode; + parent.replaceChild(g, elem); + g.appendChild(elem); + bb = g.getBBox(); + parent.insertBefore(elem,g); + parent.removeChild(g); + } + - // Old method: Works by giving the rotated BBox, - // this is (unfortunately) what Opera and Safari do - // natively when getting the BBox of the parent group + // Old method: Works by giving the rotated BBox, + // this is (unfortunately) what Opera and Safari do + // natively when getting the BBox of the parent group // var angle = angle * Math.PI / 180.0; // var rminx = Number.MAX_VALUE, rminy = Number.MAX_VALUE, // rmaxx = Number.MIN_VALUE, rmaxy = Number.MIN_VALUE; @@ -5847,11 +5855,11 @@ function BatchCommand(text) { // bb.y = rminy; // bb.width = rmaxx - rminx; // bb.height = rmaxy - rminy; - } - - return bb; - } catch(e) { return null; } - } + } + + return bb; + } catch(e) { return null; } + } var full_bb; $.each(elems, function() { From 32f23b69af5ca7870f824414a4c383057b0feebc Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Thu, 10 Dec 2009 05:11:09 +0000 Subject: [PATCH 10/38] fixtransforms branch: Fix long-standing bug in matrixMultiply() that caused problems when transforms became complicated, implement some of resolving/flattening transforms on skewed elements git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1008 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 175 +++++++++++++++++--------------------------- 1 file changed, 67 insertions(+), 108 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index e0b29d39..5f7018f5 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -11,8 +11,14 @@ /* TODOs for TransformList: - * Fix rotating groups turning into matrix() - * Fix problem when moving elements that have [R][M] + * Maybe: + * we need do nothing about a rotate() - i.e. just look for [T] at beginning or [T][S][-T] + at the end and update + * if no matrix() exists, we can freely recalculateDimensions as we used to do + * if a matrix() exists, we should no longer update the shape's dimensions - instead + we should just update the matrix() transform to reflect any moving, stretch + * Fix problem when moving elements that are rotated in a group (all rotates must have their + center updated?) and what about matrix transforms - do they need update as well? * Fix problem when ungrouping rotated elements that were scaled in a group * When ungrouping, always end up with a single [M] @@ -1322,7 +1328,7 @@ function BatchCommand(text) { // this function returns the command which resulted from the selected change // TODO: use suspendRedraw() and unsuspendRedraw() around this function var recalculateDimensions = function(selected) { - if (selected == null) return null; + if (true || selected == null) return null; var tlist = canvas.getTransformList(selected); @@ -1429,6 +1435,7 @@ function BatchCommand(text) { // if it's a group, we have special processing to flatten transforms if (selected.tagName == "g") { + console.log("recalculateDimensions(" + selected.tagName + ")"); var tx = 0, ty = 0; var N = tlist.numberOfItems; @@ -1441,42 +1448,27 @@ function BatchCommand(text) { var tm = tlist.getItem(N-3).matrix; var sm = tlist.getItem(N-2).matrix; var tmn = tlist.getItem(N-1).matrix; - var em = matrixMultiply(tm, sm, tmn); var children = selected.childNodes; var c = children.length; while (c--) { + console.log("child #" + c); var child = children.item(c); + tx = 0; + ty = 0; if (child.nodeType == 1) { var childTlist = canvas.getTransformList(child); var m = transformListToTransform(childTlist).matrix; var angle = canvas.getRotationAngle(child); if(angle) { - // TODO: this does not work yet - /* - // if the child is rotated, we get: - // [E][M] - // where [M] = [R][M_remainder] - - // [E][M] = [M][E2] - // [E2] = [M_inv][E][M] - var e2 = matrixMultiply(m.inverse(), em, m); - + var em = matrixMultiply(tm, sm, tmn); + // this does not appear to work, something wrong with my logic here var e2t = svgroot.createSVGTransform(); - e2t.setMatrix(e2); - childTlist.appendItem(e2t); - - // the rotation is no longer centered - // so we need to re-center it - - // TODO: find the old center - // TODO: find the new center - // TODO: find the transformed translation - // Is this taken care of by recalculateDimensions? - */ - + e2t.setMatrix(em); + childTlist.insertItemBefore(e2t,0); +// alert(child.getAttribute("transform")); } else { // update the transform list with translate,scale,translate @@ -1505,10 +1497,10 @@ function BatchCommand(text) { childTlist.appendItem(translateBack); childTlist.appendItem(scale); childTlist.appendItem(translateOrigin); - } - batchCmd.addSubCommand( recalculateDimensions(child) ); - } - } + batchCmd.addSubCommand( recalculateDimensions(child) ); + } // not rotated + } // element + } // for each child // Remove these transforms from group tlist.removeItem(N-1); tlist.removeItem(N-2); @@ -1524,7 +1516,6 @@ function BatchCommand(text) { tlist.getItem(0).type == 2) { var T_M = transformListToTransform(tlist).matrix; - logMatrix(T_M); tlist.removeItem(0); var M_inv = transformListToTransform(tlist).matrix.inverse(); logMatrix(M_inv); @@ -1533,10 +1524,27 @@ function BatchCommand(text) { tx = M2.e; ty = M2.f; - } - // rotate? - else { - console.log('rotate?'); + + if (tx != 0 || ty != 0) { + // now push this transform down to the children + // FIXME: unfortunately recalculateDimensions depends on this global variable + var old_start_transform = start_transform; + start_transform = null; + // 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(tx,ty); + childTlist.insertItemBefore(newxlate, 0); + batchCmd.addSubCommand( recalculateDimensions(child) ); + } + } + start_transform = old_start_transform; + } } /* @@ -1551,35 +1559,19 @@ function BatchCommand(text) { } } */ - - if (tx != 0 || ty != 0) { - // now push this transform down to the children - // FIXME: unfortunately recalculateDimensions depends on this global variable - var old_start_transform = start_transform; - start_transform = null; - // 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(tx,ty); - childTlist.insertItemBefore(newxlate, 0); - batchCmd.addSubCommand( recalculateDimensions(child) ); - } - } - start_transform = old_start_transform; - } } // else, it's a non-group else { + console.log("recalculateDimensions(" + selected.tagName + ")"); var box = canvas.getBBox(selected); var origcenter = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; var newcenter = {x: origcenter.x, y: origcenter.y}; var rotAngle = 0; + // TODO: re-do this: instead of looping through transforms just check + // like we do above for: scales, translates, rotations (in that order) + // and ignore all other transforms in the tlist. + // This pass loop in reverse order and removes any translates or scales. // Once we hit our first rotate(), we will only remove translates. n = tlist.numberOfItems; @@ -1594,9 +1586,11 @@ function BatchCommand(text) { m = matrixMultiply(tail_inv, xform.matrix, tail); var remap = null, scalew = null, scaleh = null; + console.log("xform.type=" + xform.type); switch (xform.type) { case 1: // MATRIX - continue - continue; +// newcenter = transformPoint(newcenter.x,newcenter.y,xform.matrix); + break; case 2: // TRANSLATE - always remove remap = function(x,y) { return transformPoint(x,y,m); }; scalew = function(w) { return w; } @@ -1611,6 +1605,7 @@ function BatchCommand(text) { // if the new center of the shape has moved, then // re-center the rotation, and determine the movement // offset required to keep the shape in the same place +// if (n != 0) continue; rotAngle = xform.angle; if (origcenter.x != newcenter.x || origcenter.y != newcenter.y) { var alpha = xform.angle * Math.PI / 180.0; @@ -1996,40 +1991,13 @@ function BatchCommand(text) { // throwing an exception - perhaps an update was issued in SVG 1.1 2e? var matrixMultiply = function() { var multi2 = function(m1, m2) { - var a = m1.a*m2.a + m1.c*m2.b, - b = m1.b*m2.a + m1.d*m2.b, - c = m1.a*m2.c + m1.c*m2.d, - 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); - - // 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 { - sx = a / Math.cos(rad); - sy = d / Math.cos(rad); - } - - // scale - if (sx != 1 || sy != 1) { - m = m.scaleNonUniform(sx,sy); - } - - // TODO: handle skews? - + m.a = m1.a*m2.a + m1.c*m2.b; + m.b = m1.b*m2.a + m1.d*m2.b, + m.c = m1.a*m2.c + m1.c*m2.d, + m.d = m1.b*m2.c + m1.d*m2.d, + m.e = m1.a*m2.e + m1.c*m2.f + m1.e, + m.f = m1.b*m2.e + m1.d*m2.f + m1.f; return m; } @@ -5183,21 +5151,13 @@ function BatchCommand(text) { var selected = elem || selectedElements[0]; // find the rotation transform (if any) and set it var tlist = canvas.getTransformList(selected); - var t = tlist.numberOfItems; - var sangle = 0; - while (t--) { - var xform = tlist.getItem(t); - // rotation transform + if (tlist.numberOfItems > 0) { + var xform = tlist.getItem(0); if (xform.type == 4) { - sangle += tlist.getItem(t).angle; + return to_rad ? xform.angle * Math.PI / 180.0 : xform.angle; } - // matrix transform -// else if (xform.type == 1) { -// var m = xform.matrix; -// sangle += Math.atan2(m.b,m.a) * 180.0 / Math.PI; -// } } - return to_rad ? sangle * Math.PI / 180.0 : sangle; + return 0.0; }; // this should: @@ -5212,11 +5172,11 @@ function BatchCommand(text) { var cx = round(bbox.x+bbox.width/2), cy = round(bbox.y+bbox.height/2); var tlist = canvas.getTransformList(elem); - // remove the rotation transform - var n = tlist.numberOfItems; - while (n--) { - if (tlist.getItem(n).type == 4) { - tlist.removeItem(n); + // only remove the real rotational transform if present (i.e. at index=0) + if (tlist.numberOfItems > 0) { + var xform = tlist.getItem(0); + if (xform.type == 4) { + tlist.removeItem(0); } } @@ -5224,7 +5184,6 @@ function BatchCommand(text) { var center = transformPoint(cx,cy,transformListToTransform(tlist).matrix); var R_nc = svgroot.createSVGTransform(); R_nc.setRotate(val, center.x, center.y); - tlist.insertItemBefore(R_nc,0); if (!preventUndo) { From 5e2883cfd52198389f3f09d8d5e4d998dc68e93b Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Thu, 10 Dec 2009 06:23:32 +0000 Subject: [PATCH 11/38] fixtransforms branch: forgot to enable recalculateDimensions() git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1009 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 5f7018f5..3ac20f46 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1328,7 +1328,7 @@ function BatchCommand(text) { // this function returns the command which resulted from the selected change // TODO: use suspendRedraw() and unsuspendRedraw() around this function var recalculateDimensions = function(selected) { - if (true || selected == null) return null; + if (selected == null) return null; var tlist = canvas.getTransformList(selected); @@ -1435,7 +1435,6 @@ function BatchCommand(text) { // if it's a group, we have special processing to flatten transforms if (selected.tagName == "g") { - console.log("recalculateDimensions(" + selected.tagName + ")"); var tx = 0, ty = 0; var N = tlist.numberOfItems; @@ -1452,7 +1451,6 @@ function BatchCommand(text) { var children = selected.childNodes; var c = children.length; while (c--) { - console.log("child #" + c); var child = children.item(c); tx = 0; ty = 0; @@ -1468,7 +1466,6 @@ function BatchCommand(text) { var e2t = svgroot.createSVGTransform(); e2t.setMatrix(em); childTlist.insertItemBefore(e2t,0); -// alert(child.getAttribute("transform")); } else { // update the transform list with translate,scale,translate @@ -1506,21 +1503,16 @@ function BatchCommand(text) { tlist.removeItem(N-2); tlist.removeItem(N-3); } - - N = tlist.numberOfItems; - // next, check if the first transform was a translate // if we had [ T1 ] [ M ] we want to transform this into [ M ] [ T2 ] // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] - if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && + else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && tlist.getItem(0).type == 2) { var T_M = transformListToTransform(tlist).matrix; tlist.removeItem(0); var M_inv = transformListToTransform(tlist).matrix.inverse(); - logMatrix(M_inv); var M2 = matrixMultiply( M_inv, T_M ); - logMatrix(M2); tx = M2.e; ty = M2.f; @@ -1546,23 +1538,9 @@ function BatchCommand(text) { start_transform = old_start_transform; } } - - /* - } // if we have any other transforms, collapse them all down to a matrix - else if(tlist.numberOfItems > 0) { - var m = transformListToTransform(tlist).matrix; - if (tlist.numberOfItems > 0) { - var newxform = svgroot.createSVGTransform(); - newxform.setMatrix(m); - tlist.clear(); - tlist.appendItem(newxform); - } - } - */ } // else, it's a non-group else { - console.log("recalculateDimensions(" + selected.tagName + ")"); var box = canvas.getBBox(selected); var origcenter = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; var newcenter = {x: origcenter.x, y: origcenter.y}; @@ -1586,7 +1564,6 @@ function BatchCommand(text) { m = matrixMultiply(tail_inv, xform.matrix, tail); var remap = null, scalew = null, scaleh = null; - console.log("xform.type=" + xform.type); switch (xform.type) { case 1: // MATRIX - continue // newcenter = transformPoint(newcenter.x,newcenter.y,xform.matrix); @@ -1605,7 +1582,7 @@ function BatchCommand(text) { // if the new center of the shape has moved, then // re-center the rotation, and determine the movement // offset required to keep the shape in the same place -// if (n != 0) continue; + if (n != 0) continue; rotAngle = xform.angle; if (origcenter.x != newcenter.x || origcenter.y != newcenter.y) { var alpha = xform.angle * Math.PI / 180.0; From 6236b5cda1e94add3cb839d8f059ed1b41f153e0 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Thu, 10 Dec 2009 15:33:38 +0000 Subject: [PATCH 12/38] fixtransforms branch: More updates for fixtransforms. Resizing rotated elements now broken git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1010 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 186 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 152 insertions(+), 34 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 3ac20f46..3af93752 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -10,26 +10,11 @@ */ /* TODOs for TransformList: - - * Maybe: - * we need do nothing about a rotate() - i.e. just look for [T] at beginning or [T][S][-T] - at the end and update - * if no matrix() exists, we can freely recalculateDimensions as we used to do - * if a matrix() exists, we should no longer update the shape's dimensions - instead - we should just update the matrix() transform to reflect any moving, stretch - * Fix problem when moving elements that are rotated in a group (all rotates must have their - center updated?) and what about matrix transforms - do they need update as well? - * Fix problem when ungrouping rotated elements that were scaled in a group - * When ungrouping, always end up with a single [M] - * When rotating, always end up with [Rc][M] - * When scaling a group, [Rc][M] - * When scaling a shape, [Rc] - * When moving, always end up with a single [M] - - * Fix Opera's centering of rotated, resized groups - * Fix resizing of rotated already-resized groups (scales incorrect with mouse) - * Ensure ungrouping works (Issue 204) + * fix resizing of rotated elements + * when resizing a group and elements are rotated within, collapse matrices now that it works + * go through ungrouping again + * go through Selector.resize() and rework so that they are always rectangular */ /* TODOs for Localizing: @@ -1542,18 +1527,151 @@ function BatchCommand(text) { // else, it's a non-group else { var box = canvas.getBBox(selected); - var origcenter = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; - var newcenter = {x: origcenter.x, y: origcenter.y}; - var rotAngle = 0; - - // TODO: re-do this: instead of looping through transforms just check - // like we do above for: scales, translates, rotations (in that order) - // and ignore all other transforms in the tlist. + var center = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; + + // temporarily strip off the rotate + var angle = canvas.getRotationAngle(selected); + if (angle) { + for (var i = 0; i < tlist.numberOfItems; ++i) { + var xform = tlist.getItem(i); + if (xform.type == 4) { + tlist.removeItem(i); + break; + } + } + } + + var N = tlist.numberOfItems; - // This pass loop in reverse order and removes any translates or scales. - // Once we hit our first rotate(), we will only remove translates. - n = tlist.numberOfItems; - var m = svgroot.createSVGMatrix(); // identity + // first, if it was a scale then the second-last transform will be it + var remap = null, scalew = null, scaleh = null; + // if we had [M][T][S][T] we want to extract the matrix equivalent of + // [T][S][T] and push it down + if (N >= 3 && tlist.getItem(N-2).type == 3 && + tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) + { + console.log("scale"); + var m = transformListToTransform(tlist,N-3,N-1).matrix; + remap = function(x,y) { return transformPoint(x,y,m); }; + scalew = function(w) { return m.a * w; } + scaleh = function(h) { return m.d * h; } + tlist.removeItem(N-1); + tlist.removeItem(N-2); + tlist.removeItem(N-3); + } + // if we had [T1][M] we want to transform this into [M][T2] + // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] + else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && + tlist.getItem(0).type == 2) + { + console.log("translate"); + var oldxlate = tlist.removeItem(0).matrix, + m = transformListToTransform(tlist).matrix, + m_inv = m.inverse(); + var newxlate = matrixMultiply( m_inv, oldxlate, m ); + remap = function(x,y) { return transformPoint(x,y,newxlate); }; + scalew = function(w) { return w; } + scaleh = function(h) { return h; } + } + + if (remap) { + switch (selected.tagName) + { + case "line": + var pt1 = remap(changes["x1"],changes["y1"]), + pt2 = remap(changes["x2"],changes["y2"]); + changes["x1"] = pt1.x; + changes["y1"] = pt1.y; + changes["x2"] = pt2.x; + changes["y2"] = pt2.y; + break; + case "circle": + var c = remap(changes["cx"],changes["cy"]); + changes["cx"] = c.x; + changes["cy"] = c.y; + // take the minimum of the new selected box's dimensions for the new circle radius + changes["r"] = Math.min(box.width/2,box.height/2); + break; + case "ellipse": + var c = remap(changes["cx"],changes["cy"]); + changes["cx"] = c.x; + changes["cy"] = c.y; + changes["rx"] = scalew(changes["rx"]); + changes["ry"] = scaleh(changes["ry"]); + break; + case "rect": + case "image": + var pt1 = remap(changes["x"],changes["y"]); + changes["x"] = pt1.x; + changes["y"] = pt1.y; + changes["width"] = scalew(changes["width"]); + changes["height"] = scaleh(changes["height"]); + break; + case "text": + var pt1 = remap(changes["x"],changes["y"]); + changes["x"] = pt1.x; + changes["y"] = pt1.y; + break; + case "polygon": + case "polyline": + var len = changes["points"].length; + for (var i = 0; i < len; ++i) { + var pt = changes["points"][i]; + pt = remap(pt.x,pt.y); + changes["points"][i].x = pt.x; + changes["points"][i].y = pt.y; + } + break; + case "path": + var len = changes["d"].length; + var firstseg = changes["d"][0]; + var firstpt = remap(firstseg.x,firstseg.y); + changes["d"][0].x = firstpt.x; + changes["d"][0].y = firstpt.y; + for (var i = 1; i < len; ++i) { + var seg = changes["d"][i]; + var type = seg.type; + // if absolute or first segment, we want to remap x, y, x1, y1, x2, y2 + // if relative, we want to scalew, scaleh + if (type % 2 == 0) { // absolute + var pt = remap(seg.x,seg.y), + pt1 = remap(seg.x1,seg.y1), + pt2 = remap(seg.x2,seg.y2); + seg.x = pt.x; + seg.y = pt.y; + seg.x1 = pt1.x; + seg.y1 = pt1.y; + seg.x2 = pt2.x; + seg.y2 = pt2.y; + seg.r1 = scalew(seg.r1), + seg.r2 = scaleh(seg.r2); + } + else { // relative + seg.x = scalew(seg.x); + seg.y = scaleh(seg.y); + seg.x1 = scalew(seg.x1); + seg.y1 = scaleh(seg.y1); + seg.x2 = scalew(seg.x2); + seg.y2 = scaleh(seg.y2); + seg.r1 = scalew(seg.r1), + seg.r2 = scaleh(seg.r2); + } + } // for each segment + break; + } // switch on element type to get initial values + + var newcenter = remap(center.x,center.y); + center.x = newcenter.x; + center.y = newcenter.y; + } // if we are remapping + + // if we had a rotation, remap its center + if (angle) { + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(angle,center.x,center.y); + tlist.insertItemBefore(newRot,0); + } + /* while (n--) { var xform = tlist.getItem(n); // get the matrix of all transformations to the right of this transform @@ -1623,8 +1741,6 @@ function BatchCommand(text) { switch (selected.tagName) { - case "g": - break; case "line": var pt1 = remap(changes["x1"],changes["y1"]), pt2 = remap(changes["x2"],changes["y2"]); @@ -1718,6 +1834,7 @@ function BatchCommand(text) { newrot.setRotate(rotAngle, newcenter.x, newcenter.y); tlist.insertItemBefore(newrot, 0); } + */ } // a non-group // now we have a set of changes and an applied reduced transform list @@ -5128,8 +5245,9 @@ function BatchCommand(text) { var selected = elem || selectedElements[0]; // find the rotation transform (if any) and set it var tlist = canvas.getTransformList(selected); - if (tlist.numberOfItems > 0) { - var xform = tlist.getItem(0); + var N = tlist.numberOfItems; + for (var i = 0; i < N; ++i) { + var xform = tlist.getItem(i); if (xform.type == 4) { return to_rad ? xform.angle * Math.PI / 180.0 : xform.angle; } From 0182a59d06477929563cc69578787f3cd3c0401d Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Fri, 11 Dec 2009 17:44:56 +0000 Subject: [PATCH 13/38] Move out remapping of element to a separate function git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1023 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 244 +++++++++++++++++++++++++------------------- 1 file changed, 140 insertions(+), 104 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 3af93752..ad817eb6 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1310,6 +1310,99 @@ function BatchCommand(text) { console.log([m.a,m.b,m.c,m.d,m.e,m.f]); }; + var remapElement = function(selected,changes,m) { + var remap = function(x,y) { return transformPoint(x,y,m); }; + var scalew = function(w) { return m.a*w; } + var scaleh = function(h) { return m.d*h; } + var box = canvas.getBBox(selected); + + switch (selected.tagName) + { + case "line": + var pt1 = remap(changes["x1"],changes["y1"]), + pt2 = remap(changes["x2"],changes["y2"]); + changes["x1"] = pt1.x; + changes["y1"] = pt1.y; + changes["x2"] = pt2.x; + changes["y2"] = pt2.y; + break; + case "circle": + var c = remap(changes["cx"],changes["cy"]); + changes["cx"] = c.x; + changes["cy"] = c.y; + // take the minimum of the new selected box's dimensions for the new circle radius + changes["r"] = Math.min(box.width/2,box.height/2); + break; + case "ellipse": + var c = remap(changes["cx"],changes["cy"]); + changes["cx"] = c.x; + changes["cy"] = c.y; + changes["rx"] = scalew(changes["rx"]); + changes["ry"] = scaleh(changes["ry"]); + break; + case "rect": + case "image": + var pt1 = remap(changes["x"],changes["y"]); + changes["x"] = pt1.x; + changes["y"] = pt1.y; + changes["width"] = scalew(changes["width"]); + changes["height"] = scaleh(changes["height"]); + break; + case "text": + var pt1 = remap(changes["x"],changes["y"]); + changes["x"] = pt1.x; + changes["y"] = pt1.y; + break; + case "polygon": + case "polyline": + var len = changes["points"].length; + for (var i = 0; i < len; ++i) { + var pt = changes["points"][i]; + pt = remap(pt.x,pt.y); + changes["points"][i].x = pt.x; + changes["points"][i].y = pt.y; + } + break; + case "path": + var len = changes["d"].length; + var firstseg = changes["d"][0]; + var firstpt = remap(firstseg.x,firstseg.y); + changes["d"][0].x = firstpt.x; + changes["d"][0].y = firstpt.y; + for (var i = 1; i < len; ++i) { + var seg = changes["d"][i]; + var type = seg.type; + // if absolute or first segment, we want to remap x, y, x1, y1, x2, y2 + // if relative, we want to scalew, scaleh + if (type % 2 == 0) { // absolute + var pt = remap(seg.x,seg.y), + pt1 = remap(seg.x1,seg.y1), + pt2 = remap(seg.x2,seg.y2); + seg.x = pt.x; + seg.y = pt.y; + seg.x1 = pt1.x; + seg.y1 = pt1.y; + seg.x2 = pt2.x; + seg.y2 = pt2.y; + seg.r1 = scalew(seg.r1), + seg.r2 = scaleh(seg.r2); + } + else { // relative + seg.x = scalew(seg.x); + seg.y = scaleh(seg.y); + seg.x1 = scalew(seg.x1); + seg.y1 = scaleh(seg.y1); + seg.x2 = scalew(seg.x2); + seg.y2 = scaleh(seg.y2); + seg.r1 = scalew(seg.r1), + seg.r2 = scaleh(seg.r2); + } + } // for each segment + break; + } // switch on element type to get initial values + return changes; + }; + // this function returns the command which resulted from the selected change // TODO: use suspendRedraw() and unsuspendRedraw() around this function var recalculateDimensions = function(selected) { @@ -1528,6 +1621,8 @@ function BatchCommand(text) { else { var box = canvas.getBBox(selected); var center = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; + var newcenter = {x: center.x, y: center.y }; + var m = svgroot.createSVGMatrix(); // temporarily strip off the rotate var angle = canvas.getRotationAngle(selected); @@ -1542,133 +1637,74 @@ function BatchCommand(text) { } var N = tlist.numberOfItems; + var bNeedRemap = false; // first, if it was a scale then the second-last transform will be it - var remap = null, scalew = null, scaleh = null; // if we had [M][T][S][T] we want to extract the matrix equivalent of // [T][S][T] and push it down if (N >= 3 && tlist.getItem(N-2).type == 3 && tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) { - console.log("scale"); - var m = transformListToTransform(tlist,N-3,N-1).matrix; - remap = function(x,y) { return transformPoint(x,y,m); }; - scalew = function(w) { return m.a * w; } - scaleh = function(h) { return m.d * h; } + m = transformListToTransform(tlist,N-3,N-1).matrix; tlist.removeItem(N-1); tlist.removeItem(N-2); tlist.removeItem(N-3); + + bNeedRemap = true; } // if we had [T1][M] we want to transform this into [M][T2] // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && tlist.getItem(0).type == 2) { - console.log("translate"); - var oldxlate = tlist.removeItem(0).matrix, - m = transformListToTransform(tlist).matrix, - m_inv = m.inverse(); - var newxlate = matrixMultiply( m_inv, oldxlate, m ); - remap = function(x,y) { return transformPoint(x,y,newxlate); }; - scalew = function(w) { return w; } - scaleh = function(h) { return h; } + var oldxlate = tlist.getItem(0).matrix, + meq = transformListToTransform(tlist).matrix, + meq_inv = meq.inverse(); + m = matrixMultiply( meq_inv, oldxlate, meq ); + tlist.removeItem(0); + + bNeedRemap = true; } - if (remap) { - switch (selected.tagName) - { - case "line": - var pt1 = remap(changes["x1"],changes["y1"]), - pt2 = remap(changes["x2"],changes["y2"]); - changes["x1"] = pt1.x; - changes["y1"] = pt1.y; - changes["x2"] = pt2.x; - changes["y2"] = pt2.y; - break; - case "circle": - var c = remap(changes["cx"],changes["cy"]); - changes["cx"] = c.x; - changes["cy"] = c.y; - // take the minimum of the new selected box's dimensions for the new circle radius - changes["r"] = Math.min(box.width/2,box.height/2); - break; - case "ellipse": - var c = remap(changes["cx"],changes["cy"]); - changes["cx"] = c.x; - changes["cy"] = c.y; - changes["rx"] = scalew(changes["rx"]); - changes["ry"] = scaleh(changes["ry"]); - break; - case "rect": - case "image": - var pt1 = remap(changes["x"],changes["y"]); - changes["x"] = pt1.x; - changes["y"] = pt1.y; - changes["width"] = scalew(changes["width"]); - changes["height"] = scaleh(changes["height"]); - break; - case "text": - var pt1 = remap(changes["x"],changes["y"]); - changes["x"] = pt1.x; - changes["y"] = pt1.y; - break; - case "polygon": - case "polyline": - var len = changes["points"].length; - for (var i = 0; i < len; ++i) { - var pt = changes["points"][i]; - pt = remap(pt.x,pt.y); - changes["points"][i].x = pt.x; - changes["points"][i].y = pt.y; - } - break; - case "path": - var len = changes["d"].length; - var firstseg = changes["d"][0]; - var firstpt = remap(firstseg.x,firstseg.y); - changes["d"][0].x = firstpt.x; - changes["d"][0].y = firstpt.y; - for (var i = 1; i < len; ++i) { - var seg = changes["d"][i]; - var type = seg.type; - // if absolute or first segment, we want to remap x, y, x1, y1, x2, y2 - // if relative, we want to scalew, scaleh - if (type % 2 == 0) { // absolute - var pt = remap(seg.x,seg.y), - pt1 = remap(seg.x1,seg.y1), - pt2 = remap(seg.x2,seg.y2); - seg.x = pt.x; - seg.y = pt.y; - seg.x1 = pt1.x; - seg.y1 = pt1.y; - seg.x2 = pt2.x; - seg.y2 = pt2.y; - seg.r1 = scalew(seg.r1), - seg.r2 = scaleh(seg.r2); - } - else { // relative - seg.x = scalew(seg.x); - seg.y = scaleh(seg.y); - seg.x1 = scalew(seg.x1); - seg.y1 = scaleh(seg.y1); - seg.x2 = scalew(seg.x2); - seg.y2 = scaleh(seg.y2); - seg.r1 = scalew(seg.r1), - seg.r2 = scaleh(seg.r2); - } - } // for each segment - break; - } // switch on element type to get initial values + // if the shape was rotated, calculate the new center and account for + // the resulting translation in the matrix + if (angle) { + // calculate the new center + newcenter = transformPoint(center.x,center.y,m); - var newcenter = remap(center.x,center.y); - center.x = newcenter.x; - center.y = newcenter.y; + // if the centers are different, then determine if the resultant xlate + var alpha = angle * Math.PI / 180.0; + var dx = newcenter.x - center.x, + dy = newcenter.y - center.y; + if (dx != 0 || dy != 0) { + var r = Math.sqrt(dx*dx + dy*dy), + theta = Math.atan2(dy,dx) + alpha; + var cx = r * Math.cos(theta) + center.x, + cy = r * Math.sin(theta) + center.y; + + dx = cx - newcenter.x; + dy = cy - newcenter.y; + console.log([center.x,center.y]); + console.log([newcenter.x,newcenter.y]); + console.log([cx,cy]); + console.log([dx,dy]); + + // post- or pre- multiply? + var delta = svgroot.createSVGMatrix(); + delta.translate(dx,dy); + m = matrixMultiply(delta,m); + } + } + + if (bNeedRemap) { + remapElement(selected,changes,m); } // if we are remapping - // if we had a rotation, remap its center + // if we had a rotation, re-add the rotation with the newcenter + // (this will change if we had a move or a resize) if (angle) { var newRot = svgroot.createSVGTransform(); - newRot.setRotate(angle,center.x,center.y); + newRot.setRotate(angle,newcenter.x,newcenter.y); tlist.insertItemBefore(newRot,0); } /* From 4ca4260fc9e0a17d4666100aa542a324ded0d248 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Fri, 11 Dec 2009 22:42:26 +0000 Subject: [PATCH 14/38] fixtransforms branch: Fix resizing of rotated elements git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1027 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 390 +++++++++++++------------------------------- 1 file changed, 115 insertions(+), 275 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index ad817eb6..c0a2229e 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1400,7 +1400,87 @@ function BatchCommand(text) { } // for each segment break; } // switch on element type to get initial values - return changes; + + // now we have a set of changes and an applied reduced transform list + // we apply the changes directly to the DOM + // TODO: merge this switch with the above one and optimize + switch (selected.tagName) + { + case "rect": + case "image": + changes.x = changes.x-0 + Math.min(0,changes.width); + changes.y = changes.y-0 + Math.min(0,changes.height); + changes.width = Math.abs(changes.width); + changes.height = Math.abs(changes.height); + assignAttributes(selected, changes, 1000); + break; + case "ellipse": + changes.rx = Math.abs(changes.rx); + changes.ry = Math.abs(changes.ry); + case "circle": + if(changes.r) changes.r = Math.abs(changes.r); + case "line": + case "text": + assignAttributes(selected, changes, 1000); + break; + case "polyline": + case "polygon": + var len = changes["points"].length; + var pstr = ""; + for (var i = 0; i < len; ++i) { + var pt = changes["points"][i]; + pstr += pt.x + "," + pt.y + " "; + } + selected.setAttribute("points", pstr); + break; + case "path": + var dstr = ""; + var len = changes["d"].length; + for (var i = 0; i < len; ++i) { + var seg = changes["d"][i]; + var type = seg.type; + dstr += truePathMap[type]; + switch(type) { + case 13: // relative horizontal line (h) + case 12: // absolute horizontal line (H) + dstr += seg.x + " "; + break; + case 15: // relative vertical line (v) + case 14: // absolute vertical line (V) + dstr += seg.y + " "; + break; + case 3: // relative move (m) + case 5: // relative line (l) + case 19: // relative smooth quad (t) + case 2: // absolute move (M) + case 4: // absolute line (L) + case 18: // absolute smooth quad (T) + dstr += seg.x + "," + seg.y + " "; + break; + case 7: // relative cubic (c) + case 6: // absolute cubic (C) + dstr += seg.x1 + "," + seg.y1 + " " + seg.x2 + "," + seg.y2 + " " + + seg.x + "," + seg.y + " "; + break; + case 9: // relative quad (q) + case 8: // absolute quad (Q) + dstr += seg.x + "," + seg.y + " " + seg.x1 + "," + seg.y1 + " "; + break; + case 11: // relative elliptical arc (a) + case 10: // absolute elliptical arc (A) + dstr += seg.r1 + "," + seg.r2 + " " + seg.angle + " " + seg.largeArcFlag + + " " + seg.sweepFlag + " " + seg.x + "," + seg.y + " "; + break; + case 17: // relative smooth cubic (s) + case 16: // absolute smooth cubic (S) + dstr += seg.x + "," + seg.y + " " + seg.x2 + "," + seg.y2 + " "; + break; + } + } + selected.setAttribute("d", dstr); + break; + } + }; // this function returns the command which resulted from the selected change @@ -1637,7 +1717,7 @@ function BatchCommand(text) { } var N = tlist.numberOfItems; - var bNeedRemap = false; + var operation = 0; // first, if it was a scale then the second-last transform will be it // if we had [M][T][S][T] we want to extract the matrix equivalent of @@ -1645,312 +1725,72 @@ function BatchCommand(text) { if (N >= 3 && tlist.getItem(N-2).type == 3 && tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) { + operation = 3; // scale m = transformListToTransform(tlist,N-3,N-1).matrix; tlist.removeItem(N-1); tlist.removeItem(N-2); tlist.removeItem(N-3); - - bNeedRemap = true; } // if we had [T1][M] we want to transform this into [M][T2] // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && tlist.getItem(0).type == 2) { + operation = 2; // translate var oldxlate = tlist.getItem(0).matrix, meq = transformListToTransform(tlist).matrix, meq_inv = meq.inverse(); m = matrixMultiply( meq_inv, oldxlate, meq ); tlist.removeItem(0); - - bNeedRemap = true; + } + else { + operation = 4; // rotate } - // if the shape was rotated, calculate the new center and account for - // the resulting translation in the matrix + // if the shape was rotated, calculate what the center will be + var xcenter = null; if (angle) { - // calculate the new center - newcenter = transformPoint(center.x,center.y,m); - - // if the centers are different, then determine if the resultant xlate - var alpha = angle * Math.PI / 180.0; - var dx = newcenter.x - center.x, - dy = newcenter.y - center.y; - if (dx != 0 || dy != 0) { - var r = Math.sqrt(dx*dx + dy*dy), - theta = Math.atan2(dy,dx) + alpha; - var cx = r * Math.cos(theta) + center.x, - cy = r * Math.sin(theta) + center.y; - - dx = cx - newcenter.x; - dy = cy - newcenter.y; - console.log([center.x,center.y]); - console.log([newcenter.x,newcenter.y]); - console.log([cx,cy]); - console.log([dx,dy]); - - // post- or pre- multiply? - var delta = svgroot.createSVGMatrix(); - delta.translate(dx,dy); - m = matrixMultiply(delta,m); + // calculate the new center from the translate + if (operation == 2) { + newcenter = transformPoint(center.x,center.y,m); + } + else if (operation == 3) { + xcenter = transformPoint(center.x,center.y,m); } } - if (bNeedRemap) { + if (operation == 2 || operation == 3) { remapElement(selected,changes,m); } // if we are remapping - // if we had a rotation, re-add the rotation with the newcenter - // (this will change if we had a move or a resize) + // if we had a rotation, re-add the rotation back with its original center if (angle) { var newRot = svgroot.createSVGTransform(); newRot.setRotate(angle,newcenter.x,newcenter.y); tlist.insertItemBefore(newRot,0); } - /* - while (n--) { - var xform = tlist.getItem(n); - // get the matrix of all transformations to the right of this transform - // and its inverse (B and B_inv) - var tail = transformListToTransform(tlist, n+1, tlist.numberOfItems-1).matrix, - tail_inv = tail.inverse(); - // multiply (B_inv * A * B) - m = matrixMultiply(tail_inv, xform.matrix, tail); + + // at this point, the element looks exactly how we want it to look but its + // rotational center may be off in the case of a resize - we need to fix that + if (angle && operation == 3) { + box = canvas.getBBox(selected); + m = transformListToTransform(tlist).matrix; + + // get its actual center + newcenter = transformPoint(box.x+box.width/2, box.y+box.height/2, m); + + // determine the resultant delta translate to re-center + var dx = newcenter.x - xcenter.x, + dy = newcenter.y - xcenter.y; - var remap = null, scalew = null, scaleh = null; - switch (xform.type) { - case 1: // MATRIX - continue -// newcenter = transformPoint(newcenter.x,newcenter.y,xform.matrix); - break; - case 2: // TRANSLATE - always remove - remap = function(x,y) { return transformPoint(x,y,m); }; - scalew = function(w) { return w; } - scaleh = function(h) { return h; } - break; - case 3: // SCALE - always remove - remap = function(x,y) { return transformPoint(x,y,m); }; - scalew = function(w) { return m.a * w; } - scaleh = function(h) { return m.d * h; } - break; - case 4: // ROTATE - // if the new center of the shape has moved, then - // re-center the rotation, and determine the movement - // offset required to keep the shape in the same place - if (n != 0) continue; - rotAngle = xform.angle; - if (origcenter.x != newcenter.x || origcenter.y != newcenter.y) { - var alpha = xform.angle * Math.PI / 180.0; - - // determine where the new rotated center should be - var dx = newcenter.x - origcenter.x, - dy = newcenter.y - origcenter.y, - r = Math.sqrt(dx*dx + dy*dy), - theta = Math.atan2(dy,dx) + alpha; - var cx = r * Math.cos(theta) + origcenter.x, - cy = r * Math.sin(theta) + origcenter.y; + var delta = svgroot.createSVGMatrix().translate(dx,dy); + remapElement(selected,changes,delta); - dx = cx - newcenter.x; - dy = cy - newcenter.y; - - remap = function(x,y) { - return { x: x + dx, y: y + dy }; - }; - scalew = function(w) { return w; } - scaleh = function(h) { return h; } - } - else { - tlist.removeItem(n); - } - break; - default: - continue; - } - - if (!remap) continue; - - newcenter = remap(box.x+box.width/2, box.y+box.height/2); - var bpt = remap(box.x,box.y); - box.x = bpt.x; - box.y = bpt.y; - box.width = scalew(box.width); - box.height = scaleh(box.height); - - switch (selected.tagName) - { - case "line": - var pt1 = remap(changes["x1"],changes["y1"]), - pt2 = remap(changes["x2"],changes["y2"]); - changes["x1"] = pt1.x; - changes["y1"] = pt1.y; - changes["x2"] = pt2.x; - changes["y2"] = pt2.y; - break; - case "circle": - var c = remap(changes["cx"],changes["cy"]); - changes["cx"] = c.x; - changes["cy"] = c.y; - // take the minimum of the new selected box's dimensions for the new circle radius - changes["r"] = Math.min(box.width/2,box.height/2); - break; - case "ellipse": - var c = remap(changes["cx"],changes["cy"]); - changes["cx"] = c.x; - changes["cy"] = c.y; - changes["rx"] = scalew(changes["rx"]); - changes["ry"] = scaleh(changes["ry"]); - break; - case "rect": - case "image": - var pt1 = remap(changes["x"],changes["y"]); - changes["x"] = pt1.x; - changes["y"] = pt1.y; - changes["width"] = scalew(changes["width"]); - changes["height"] = scaleh(changes["height"]); - break; - case "text": - var pt1 = remap(changes["x"],changes["y"]); - changes["x"] = pt1.x; - changes["y"] = pt1.y; - break; - case "polygon": - case "polyline": - var len = changes["points"].length; - for (var i = 0; i < len; ++i) { - var pt = changes["points"][i]; - pt = remap(pt.x,pt.y); - changes["points"][i].x = pt.x; - changes["points"][i].y = pt.y; - } - break; - case "path": - var len = changes["d"].length; - var firstseg = changes["d"][0]; - var firstpt = remap(firstseg.x,firstseg.y); - changes["d"][0].x = firstpt.x; - changes["d"][0].y = firstpt.y; - for (var i = 1; i < len; ++i) { - var seg = changes["d"][i]; - var type = seg.type; - // if absolute or first segment, we want to remap x, y, x1, y1, x2, y2 - // if relative, we want to scalew, scaleh - if (type % 2 == 0) { // absolute - var pt = remap(seg.x,seg.y), - pt1 = remap(seg.x1,seg.y1), - pt2 = remap(seg.x2,seg.y2); - seg.x = pt.x; - seg.y = pt.y; - seg.x1 = pt1.x; - seg.y1 = pt1.y; - seg.x2 = pt2.x; - seg.y2 = pt2.y; - seg.r1 = scalew(seg.r1), - seg.r2 = scaleh(seg.r2); - } - else { // relative - seg.x = scalew(seg.x); - seg.y = scaleh(seg.y); - seg.x1 = scalew(seg.x1); - seg.y1 = scaleh(seg.y1); - seg.x2 = scalew(seg.x2); - seg.y2 = scaleh(seg.y2); - seg.r1 = scalew(seg.r1), - seg.r2 = scaleh(seg.r2); - } - } // for each segment - break; - } // switch on element type to get initial values - - // we have eliminated the transform, so remove it - tlist.removeItem(n); - } // looping for each transform - - // we may need to insert a rotation back now - if (rotAngle != 0) { - var newrot = svgroot.createSVGTransform(); - newrot.setRotate(rotAngle, newcenter.x, newcenter.y); - tlist.insertItemBefore(newrot, 0); + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(angle,newcenter.x,newcenter.y); + tlist.replaceItem(newRot,0); } - */ } // a non-group - - // now we have a set of changes and an applied reduced transform list - // we apply the changes directly to the DOM - switch (selected.tagName) - { - case "rect": - case "image": - changes.x = changes.x-0 + Math.min(0,changes.width); - changes.y = changes.y-0 + Math.min(0,changes.height); - changes.width = Math.abs(changes.width); - changes.height = Math.abs(changes.height); - assignAttributes(selected, changes, 1000); - break; - case "ellipse": - changes.rx = Math.abs(changes.rx); - changes.ry = Math.abs(changes.ry); - case "circle": - if(changes.r) changes.r = Math.abs(changes.r); - case "line": - case "text": - assignAttributes(selected, changes, 1000); - break; - case "polyline": - case "polygon": - var len = changes["points"].length; - var pstr = ""; - for (var i = 0; i < len; ++i) { - var pt = changes["points"][i]; - pstr += pt.x + "," + pt.y + " "; - } - selected.setAttribute("points", pstr); - break; - case "path": - var dstr = ""; - var len = changes["d"].length; - for (var i = 0; i < len; ++i) { - var seg = changes["d"][i]; - var type = seg.type; - dstr += truePathMap[type]; - switch(type) { - case 13: // relative horizontal line (h) - case 12: // absolute horizontal line (H) - dstr += seg.x + " "; - break; - case 15: // relative vertical line (v) - case 14: // absolute vertical line (V) - dstr += seg.y + " "; - break; - case 3: // relative move (m) - case 5: // relative line (l) - case 19: // relative smooth quad (t) - case 2: // absolute move (M) - case 4: // absolute line (L) - case 18: // absolute smooth quad (T) - dstr += seg.x + "," + seg.y + " "; - break; - case 7: // relative cubic (c) - case 6: // absolute cubic (C) - dstr += seg.x1 + "," + seg.y1 + " " + seg.x2 + "," + seg.y2 + " " + - seg.x + "," + seg.y + " "; - break; - case 9: // relative quad (q) - case 8: // absolute quad (Q) - dstr += seg.x + "," + seg.y + " " + seg.x1 + "," + seg.y1 + " "; - break; - case 11: // relative elliptical arc (a) - case 10: // absolute elliptical arc (A) - dstr += seg.r1 + "," + seg.r2 + " " + seg.angle + " " + seg.largeArcFlag + - " " + seg.sweepFlag + " " + seg.x + "," + seg.y + " "; - break; - case 17: // relative smooth cubic (s) - case 16: // absolute smooth cubic (S) - dstr += seg.x + "," + seg.y + " " + seg.x2 + "," + seg.y2 + " "; - break; - } - } - selected.setAttribute("d", dstr); - break; - } // if the transform list has been emptied, remove it if (tlist.numberOfItems == 0) { From f2c98ab46ed99781cecb8396150c055d0ce5e4cd Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Sun, 13 Dec 2009 03:31:30 +0000 Subject: [PATCH 15/38] fixtransforms branch: groups with skewed elements now resize/move properly git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1034 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index c0a2229e..aaac6973 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -11,10 +11,9 @@ /* TODOs for TransformList: - * fix resizing of rotated elements - * when resizing a group and elements are rotated within, collapse matrices now that it works - * go through ungrouping again + * go through Selector.resize() and make sure groups account for rotated/skewed children * go through Selector.resize() and rework so that they are always rectangular + * go through ungrouping again */ /* TODOs for Localizing: @@ -1617,14 +1616,16 @@ function BatchCommand(text) { var m = transformListToTransform(childTlist).matrix; var angle = canvas.getRotationAngle(child); - if(angle) { - var em = matrixMultiply(tm, sm, tmn); + if(angle || hasMatrixTransform(childTlist)) { + var em = matrixMultiply(tm, sm, tmn, m); // this does not appear to work, something wrong with my logic here var e2t = svgroot.createSVGTransform(); e2t.setMatrix(em); - childTlist.insertItemBefore(e2t,0); + childTlist.clear(); + childTlist.appendItem(e2t,0); } + // if not rotated or skewed, push the [T][S][-T] down to the child else { // update the transform list with translate,scale,translate @@ -2001,6 +2002,14 @@ function BatchCommand(text) { } return svgroot.createSVGTransformFromMatrix(m); }; + + var hasMatrixTransform = function(tlist) { + var num = tlist.numberOfItems; + while (num--) { + if (tlist.getItem(num).type == 1) return true; + } + return false; + } // // Easy way to loop through transform list, but may not be worthwhile // var eachXform = function(elem, callback) { From 56884531a5c34a944f511b97c15629d59e001081 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Sun, 13 Dec 2009 04:14:41 +0000 Subject: [PATCH 16/38] fixtransforms branch: Fix webkit by handling matrix() transforms in SVGEditTransformList shim git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1035 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index aaac6973..8bcb352f 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -2031,6 +2031,9 @@ function BatchCommand(text) { var tobj = {tx:0,ty:0,sx:1,sy:1,angle:0,cx:0,cy:0,text:""}; var z = mZoom?current_zoom:1; switch(xform.type) { + case 1: // MATRIX + tobj.text = "matrix(" + [m.a,m.b,m.c,m.d,m.e,m.f].join(",") + ")"; + break; case 2: // TRANSLATE tobj.tx = m.e; tobj.ty = m.f; From 9c249d4ee169201dd838dc92ae78447705062dbe Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Sun, 13 Dec 2009 06:42:59 +0000 Subject: [PATCH 17/38] fixtransforms branch: groups with skewed children now have correct selector boxes git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1036 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 8bcb352f..a703aa54 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -11,9 +11,10 @@ /* TODOs for TransformList: - * go through Selector.resize() and make sure groups account for rotated/skewed children * go through Selector.resize() and rework so that they are always rectangular * go through ungrouping again + * ensure zooming works properly + * ensure undo/redo works perfectly */ /* TODOs for Localizing: @@ -5727,6 +5728,9 @@ function BatchCommand(text) { if(!elems.length) return false; // Make sure the expected BBox is returned if the element is a group + // FIXME: doesn't this mean that every time we call getStrokedBBox() that we are + // re-creating the getCheckedBBox() function? shouldn't we make this a function + // at the 'canvas' level var getCheckedBBox = function(elem) { try { // TODO: Fix issue with rotated groups. Currently they work @@ -5735,7 +5739,7 @@ function BatchCommand(text) { var bb = elem.getBBox(); var angle = canvas.getRotationAngle(elem); - if (angle && angle % 90) { + if ((angle && angle % 90) || hasMatrixTransform(canvas.getTransformList(elem))) { // Accurate way to get BBox of rotated element in Firefox: // Put element in group and get its BBox @@ -5819,6 +5823,7 @@ function BatchCommand(text) { var min_x = full_bb.x; var min_y = full_bb.y; + // FIXME: same re-creation problem with this function as getCheckedBBox() above var getOffset = function(elem) { var sw = elem.getAttribute("stroke-width"); var offset = 0; From c513aac9d035cfd55d119ebfec713fc5d3ab921f Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Mon, 14 Dec 2009 16:26:44 +0000 Subject: [PATCH 18/38] Start on making selection boxes rectangular regardless of matrix-transformation git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1037 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 87 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index a703aa54..87b4d2d6 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -46,6 +46,11 @@ if( window.opera ) { window.console.log = function(str) {opera.postError(str);} } +var uiStrings = { + "pathNodeTooltip":"Drag node to move it. Double-click node to change segment type", + "pathCtrlPtTooltip":"Drag control point to adjust curve properties" +}; + // this defines which elements and attributes that we support // TODO: add elements to this // TODO: add to this @@ -434,7 +439,7 @@ function BatchCommand(text) { // loop and transform our bounding box until we reach our first rotation var tlist = canvas.getTransformList(this.selectedElement); - var m = transformListToTransform(tlist).matrix; + var m = transformListToTransform(tlist, null, null, true).matrix; // This should probably be handled somewhere else, but for now // it keeps the selection box correctly positioned when zoomed @@ -455,6 +460,14 @@ function BatchCommand(text) { + " " + nbox.bl.x + "," + nbox.bl.y + "z"; assignAttributes(selectedBox, {'d': dstr}); + var angle = canvas.getRotationAngle(selected); + var mid_x = (nbox.tr.x + nbox.tl.x)/2; + var mid_y = (nbox.bl.y + nbox.tl.y)/2; + + if(canvas.getRotationAngle(selected)) { + this.selectorGroup.setAttribute("transform","rotate(" + angle + "," + mid_x + "," + mid_y + ")"); + } + var gripCoords = { nw: [nbox.tl.x, nbox.tl.y], ne: [nbox.tr.x, nbox.tr.y], @@ -472,17 +485,24 @@ function BatchCommand(text) { }); // we want to go 20 pixels in the negative transformed y direction, ignoring scale - var dy = (nbox.tl.y - nbox.tr.y), - dx = (nbox.tr.x - nbox.tl.x), - theta = Math.atan2(dy,dx); - dy = 20 * Math.cos(theta); - dx = 20 * Math.sin(theta) + var rotatept = {x:(l+w/2)*current_zoom,y:t*current_zoom}; - rotatept = transformPoint( rotatept.x, rotatept.y, m); - assignAttributes(this.rotateGripConnector, { x1: nbox.tl.x + (nbox.tr.x-nbox.tl.x)/2, - y1: nbox.tl.y + (nbox.tr.y-nbox.tl.y)/2, - x2: rotatept.x-dx, y2: rotatept.y-dy }); - assignAttributes(this.rotateGrip, { cx: rotatept.x-dx, cy: rotatept.y-dy }); + assignAttributes(this.rotateGripConnector, { x1: mid_x, + y1: nbox.tl.y, + x2: mid_x, y2: nbox.tl.y - 20 }); + assignAttributes(this.rotateGrip, { cx: mid_x, cy: nbox.tl.y - 20 }); + +// var dy = (nbox.tl.y - nbox.tr.y), +// dx = (nbox.tr.x - nbox.tl.x), +// theta = Math.atan2(dy,dx); +// dy = 20 * Math.cos(theta); +// dx = 20 * Math.sin(theta) +// var rotatept = {x:(l+w/2)*current_zoom,y:t*current_zoom}; +// rotatept = transformPoint( rotatept.x, rotatept.y, m); +// assignAttributes(this.rotateGripConnector, { x1: nbox.tl.x + (nbox.tr.x-nbox.tl.x)/2, +// y1: nbox.tl.y + (nbox.tr.y-nbox.tl.y)/2, +// x2: rotatept.x-dx, y2: rotatept.y-dy }); +// assignAttributes(this.rotateGrip, { cx: rotatept.x-dx, cy: rotatept.y-dy }); svgroot.unsuspendRedraw(sr_handle); }; @@ -1986,7 +2006,7 @@ function BatchCommand(text) { // (this is the equivalent of SVGTransformList.consolidate() but unlike // that method, this one does not modify the actual SVGTransformList) // This function is very liberal with its min,max arguments - var transformListToTransform = function(tlist, min, max) { + var transformListToTransform = function(tlist, min, max, noRotate) { var min = min || 0; var max = max || (tlist.numberOfItems-1); min = parseInt(min); @@ -1995,6 +2015,8 @@ function BatchCommand(text) { var m = svgroot.createSVGMatrix(); for (var i = min; i <= max; ++i) { + if(noRotate && i >= 0 && i < tlist.numberOfItems && tlist.getItem(i).type == 4) continue; + // if our indices are out of range, just use a harmless identity matrix var mtom = (i >= 0 && i < tlist.numberOfItems ? tlist.getItem(i).matrix : @@ -2066,10 +2088,35 @@ function BatchCommand(text) { topright = {x:(l+w),y:t}, botright = {x:(l+w),y:(t+h)}, botleft = {x:l,y:(t+h)}; - topleft = transformPoint( topleft.x, topleft.y, m ); - topright = transformPoint( topright.x, topright.y, m ); - botleft = transformPoint( botleft.x, botleft.y, m); - botright = transformPoint( botright.x, botright.y, m ); + + var first = transformPoint( topleft.x, topleft.y, m ); + // Get all the new points of this box + var transPts = [ + first, + transformPoint( topright.x, topright.y, m ), + transformPoint( botleft.x, botleft.y, m), + transformPoint( botright.x, botright.y, m ) + ]; + + var min_x = first.x, min_y = first.y, max_x = first.x, max_y = first.y; + + // Make new box based on max/min, etc. + $.each(transPts, function(i, pt) { + min_x = Math.min(min_x, pt.x); + min_y = Math.min(min_y, pt.y); + max_x = Math.max(max_x, pt.x); + max_y = Math.max(max_y, pt.y); + }); + + topleft = {x: min_x, y: min_y}; + topright = {x: max_x, y: min_y}; + botleft = {x: min_x, y: max_y}; + botright = {x: max_x, y: max_y}; + +// topleft = transformPoint( topleft.x, topleft.y, m ); +// topright = transformPoint( topright.x, topright.y, m ); +// botleft = transformPoint( botleft.x, botleft.y, m); +// botright = transformPoint( botright.x, botright.y, m ); return {tl:topleft, tr:topright, bl:botleft, br:botright}; }; @@ -3212,7 +3259,7 @@ function BatchCommand(text) { 'stroke-width': 2, 'cursor': 'move', 'style': 'pointer-events:all', - 'xlink:title': 'Drag point to move it. Double-click point to change segment type.' + 'xlink:title': uiStrings.pathNodeTooltip }); pointGrip = pointGripContainer.appendChild(pointGrip); @@ -3541,7 +3588,7 @@ function BatchCommand(text) { 'stroke-width': 1, 'cursor': 'move', 'style': 'pointer-events:all', - 'xlink:title': 'Drag control point to adjust curve properties' + 'xlink:title': uiStrings.pathCtrlPtTooltip }); pointGrip = ctrlPointGripContainer.appendChild(pointGrip); } @@ -6092,6 +6139,10 @@ function BatchCommand(text) { return "svgcanvas.js ($Rev$)"; }; + this.setUiStrings = function(strs) { + $.extend(uiStrings, strs); + } + this.clear(); }; From cf46838cd1beda6639e78e48a2e464c8ea62cc58 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Mon, 14 Dec 2009 16:53:21 +0000 Subject: [PATCH 19/38] Fixed bug on selecting un-rotated element after having rotated previous one git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1038 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 87b4d2d6..4ae5b2ec 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -466,6 +466,8 @@ function BatchCommand(text) { if(canvas.getRotationAngle(selected)) { this.selectorGroup.setAttribute("transform","rotate(" + angle + "," + mid_x + "," + mid_y + ")"); + } else { + this.selectorGroup.removeAttribute("transform"); } var gripCoords = { From 806f851107c5f5bb22bba39176daab52976ad641 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Tue, 15 Dec 2009 12:37:32 +0000 Subject: [PATCH 20/38] fixtransforms branch: selector boxes are always rectangular and properly box skewed items git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1039 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 181 ++++++++++++++++++++++++-------------------- 1 file changed, 98 insertions(+), 83 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 4ae5b2ec..b793d7dd 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -11,7 +11,6 @@ /* TODOs for TransformList: - * go through Selector.resize() and rework so that they are always rectangular * go through ungrouping again * ensure zooming works properly * ensure undo/redo works perfectly @@ -427,7 +426,7 @@ function BatchCommand(text) { if (selected.tagName == "text") { offset += 2/canvas.getZoom(); } - var bbox = canvas.getBBox(this.selectedElement); + var bbox = canvas.getBBox(selected); if(selected.tagName == 'g') { // The bbox for a group does not include stroke vals, so we // get the bbox based on its children. @@ -438,8 +437,8 @@ function BatchCommand(text) { } // loop and transform our bounding box until we reach our first rotation - var tlist = canvas.getTransformList(this.selectedElement); - var m = transformListToTransform(tlist, null, null, true).matrix; + var tlist = canvas.getTransformList(selected); + var m = transformListToTransform(tlist).matrix; // This should probably be handled somewhere else, but for now // it keeps the selection box correctly positioned when zoomed @@ -448,37 +447,64 @@ function BatchCommand(text) { // apply the transforms var l=bbox.x-offset, t=bbox.y-offset, w=bbox.width+(offset<<1), h=bbox.height+(offset<<1); + var bbox = {x:l, y:t, width:w, height:h}; + + // we need to handle temporary transforms too + // if skewed, get its transformed box, then find its axis-aligned bbox + + //* var nbox = transformBox(l*current_zoom, t*current_zoom, w*current_zoom, h*current_zoom, m); + + // now if the shape is rotated, un-rotate it + var cx = nbox.aabox.x + nbox.aabox.width/2; //nbox.tl.x + (nbox.tr.x - nbox.tl.x)/2; + var cy = nbox.aabox.y + nbox.aabox.height/2; //nbox.tl.y + (nbox.bl.y - nbox.tl.y)/2; + var angle = canvas.getRotationAngle(selected); + if (angle) { + + var rot = svgroot.createSVGTransform(); + rot.setRotate(-angle,cx,cy); + var rotm = rot.matrix; + nbox.tl = transformPoint(nbox.tl.x,nbox.tl.y,rotm); + nbox.tr = transformPoint(nbox.tr.x,nbox.tr.y,rotm); + nbox.bl = transformPoint(nbox.bl.x,nbox.bl.y,rotm); + nbox.br = transformPoint(nbox.br.x,nbox.br.y,rotm); + + // calculate the axis-aligned bbox + var minx = nbox.tl.x, + miny = nbox.tl.y, + maxx = nbox.tl.x, + maxy = nbox.tl.y; + + minx = Math.min(minx, Math.min(nbox.tr.x, Math.min(nbox.bl.x, nbox.br.x) ) ); + miny = Math.min(miny, Math.min(nbox.tr.y, Math.min(nbox.bl.y, nbox.br.y) ) ); + maxx = Math.max(maxx, Math.max(nbox.tr.x, Math.max(nbox.bl.x, nbox.br.x) ) ); + maxy = Math.max(maxy, Math.max(nbox.tr.y, Math.max(nbox.bl.y, nbox.br.y) ) ); + + nbox.aabox.x = minx; + nbox.aabox.y = miny; + nbox.aabox.width = (maxx-minx); + nbox.aabox.height = (maxy-miny); + } // TODO: handle negative? var sr_handle = svgroot.suspendRedraw(100); - var dstr = "M" + nbox.tl.x + "," + nbox.tl.y - + " L" + nbox.tr.x + "," + nbox.tr.y - + " " + nbox.br.x + "," + nbox.br.y - + " " + nbox.bl.x + "," + nbox.bl.y + "z"; + var dstr = "M" + nbox.aabox.x + "," + nbox.aabox.y + + " L" + (nbox.aabox.x+nbox.aabox.width) + "," + nbox.aabox.y + + " " + (nbox.aabox.x+nbox.aabox.width) + "," + (nbox.aabox.y+nbox.aabox.height) + + " " + nbox.aabox.x + "," + (nbox.aabox.y+nbox.aabox.height) + "z"; assignAttributes(selectedBox, {'d': dstr}); - var angle = canvas.getRotationAngle(selected); - var mid_x = (nbox.tr.x + nbox.tl.x)/2; - var mid_y = (nbox.bl.y + nbox.tl.y)/2; - - if(canvas.getRotationAngle(selected)) { - this.selectorGroup.setAttribute("transform","rotate(" + angle + "," + mid_x + "," + mid_y + ")"); - } else { - this.selectorGroup.removeAttribute("transform"); - } - var gripCoords = { - nw: [nbox.tl.x, nbox.tl.y], - ne: [nbox.tr.x, nbox.tr.y], - sw: [nbox.bl.x, nbox.bl.y], - se: [nbox.br.x, nbox.br.y], - n: [nbox.tl.x + (nbox.tr.x-nbox.tl.x)/2, nbox.tl.y + (nbox.tr.y-nbox.tl.y)/2], - w: [nbox.tl.x + (nbox.bl.x-nbox.tl.x)/2, nbox.tl.y + (nbox.bl.y-nbox.tl.y)/2], - e: [nbox.tr.x + (nbox.br.x-nbox.tr.x)/2, nbox.tr.y + (nbox.br.y-nbox.tr.y)/2], - s: [nbox.bl.x + (nbox.br.x-nbox.bl.x)/2, nbox.bl.y + (nbox.br.y-nbox.bl.y)/2] + nw: [nbox.aabox.x, nbox.aabox.y], + ne: [nbox.aabox.x+nbox.aabox.width, nbox.aabox.y], + sw: [nbox.aabox.x, nbox.aabox.y+nbox.aabox.height], + se: [nbox.aabox.x+nbox.aabox.width, nbox.aabox.y+nbox.aabox.height], + n: [nbox.aabox.x + (nbox.aabox.width)/2, nbox.aabox.y], + w: [nbox.aabox.x, nbox.aabox.y + (nbox.aabox.height)/2], + e: [nbox.aabox.x + nbox.aabox.width, nbox.aabox.y + (nbox.aabox.height)/2], + s: [nbox.aabox.x + (nbox.aabox.width)/2, nbox.aabox.y + nbox.aabox.height], }; $.each(gripCoords, function(dir, coords) { assignAttributes(selectedGrips[dir], { @@ -486,25 +512,20 @@ function BatchCommand(text) { }); }); - // we want to go 20 pixels in the negative transformed y direction, ignoring scale - - var rotatept = {x:(l+w/2)*current_zoom,y:t*current_zoom}; - assignAttributes(this.rotateGripConnector, { x1: mid_x, - y1: nbox.tl.y, - x2: mid_x, y2: nbox.tl.y - 20 }); - assignAttributes(this.rotateGrip, { cx: mid_x, cy: nbox.tl.y - 20 }); + if (angle) { + this.selectorGroup.setAttribute("transform", "rotate(" + [angle,cx,cy].join(",") + ")"); + } + else { + this.selectorGroup.setAttribute("transform", ""); + } -// var dy = (nbox.tl.y - nbox.tr.y), -// dx = (nbox.tr.x - nbox.tl.x), -// theta = Math.atan2(dy,dx); -// dy = 20 * Math.cos(theta); -// dx = 20 * Math.sin(theta) -// var rotatept = {x:(l+w/2)*current_zoom,y:t*current_zoom}; -// rotatept = transformPoint( rotatept.x, rotatept.y, m); -// assignAttributes(this.rotateGripConnector, { x1: nbox.tl.x + (nbox.tr.x-nbox.tl.x)/2, -// y1: nbox.tl.y + (nbox.tr.y-nbox.tl.y)/2, -// x2: rotatept.x-dx, y2: rotatept.y-dy }); -// assignAttributes(this.rotateGrip, { cx: rotatept.x-dx, cy: rotatept.y-dy }); + // we want to go 20 pixels in the negative transformed y direction, ignoring scale + assignAttributes(this.rotateGripConnector, { x1: nbox.aabox.x + (nbox.aabox.width)/2, + y1: nbox.aabox.y, + x2: nbox.aabox.x + (nbox.aabox.width)/2, + y2: nbox.aabox.y- 20}); + assignAttributes(this.rotateGrip, { cx: nbox.aabox.x + (nbox.aabox.width)/2, + cy: nbox.aabox.y - 20 }); svgroot.unsuspendRedraw(sr_handle); }; @@ -1521,8 +1542,7 @@ function BatchCommand(text) { tlist.removeItem(k); } else if (xform.type == 1) { - var m = xform.matrix; - if (m.a == 1 && m.b == 0 && m.c == 0 && m.d == 1 && m.e == 0 && m.f == 0) { + if (isIdentity(xform.matrix)) { tlist.removeItem(k); } } @@ -1975,6 +1995,10 @@ function BatchCommand(text) { return { x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f}; }; + var isIdentity = function(m) { + return (m.a == 1 && m.b == 0 && m.c == 0 && m.d == 1 && m.e == 0 && m.f == 0); + } + // matrixMultiply() is provided because WebKit didn't implement multiply() correctly // on the SVGMatrix interface. See https://bugs.webkit.org/show_bug.cgi?id=16062 // This function tries to return a SVGMatrix that is the multiplication m1*m2. @@ -2008,17 +2032,14 @@ function BatchCommand(text) { // (this is the equivalent of SVGTransformList.consolidate() but unlike // that method, this one does not modify the actual SVGTransformList) // This function is very liberal with its min,max arguments - var transformListToTransform = function(tlist, min, max, noRotate) { - var min = min || 0; - var max = max || (tlist.numberOfItems-1); + var transformListToTransform = function(tlist, min, max) { + var min = min == undefined ? 0 : min; + var max = max == undefined ? (tlist.numberOfItems-1) : max; min = parseInt(min); max = parseInt(max); if (min > max) { var temp = max; max = min; min = temp; } - var m = svgroot.createSVGMatrix(); for (var i = min; i <= max; ++i) { - if(noRotate && i >= 0 && i < tlist.numberOfItems && tlist.getItem(i).type == 4) continue; - // if our indices are out of range, just use a harmless identity matrix var mtom = (i >= 0 && i < tlist.numberOfItems ? tlist.getItem(i).matrix : @@ -2031,7 +2052,8 @@ function BatchCommand(text) { var hasMatrixTransform = function(tlist) { var num = tlist.numberOfItems; while (num--) { - if (tlist.getItem(num).type == 1) return true; + var xform = tlist.getItem(num); + if (xform.type == 1 && !isIdentity(xform.matrix)) return true; } return false; } @@ -2090,36 +2112,29 @@ function BatchCommand(text) { topright = {x:(l+w),y:t}, botright = {x:(l+w),y:(t+h)}, botleft = {x:l,y:(t+h)}; + topleft = transformPoint( topleft.x, topleft.y, m ); + var minx = topleft.x, + maxx = topleft.x, + miny = topleft.y, + maxy = topleft.y; + topright = transformPoint( topright.x, topright.y, m ); + minx = Math.min(minx, topright.x); + maxx = Math.max(maxx, topright.x); + miny = Math.min(miny, topright.y); + maxy = Math.max(maxy, topright.y); + botleft = transformPoint( botleft.x, botleft.y, m); + minx = Math.min(minx, botleft.x); + maxx = Math.max(maxx, botleft.x); + miny = Math.min(miny, botleft.y); + maxy = Math.max(maxy, botleft.y); + botright = transformPoint( botright.x, botright.y, m ); + minx = Math.min(minx, botright.x); + maxx = Math.max(maxx, botright.x); + miny = Math.min(miny, botright.y); + maxy = Math.max(maxy, botright.y); - var first = transformPoint( topleft.x, topleft.y, m ); - // Get all the new points of this box - var transPts = [ - first, - transformPoint( topright.x, topright.y, m ), - transformPoint( botleft.x, botleft.y, m), - transformPoint( botright.x, botright.y, m ) - ]; - - var min_x = first.x, min_y = first.y, max_x = first.x, max_y = first.y; - - // Make new box based on max/min, etc. - $.each(transPts, function(i, pt) { - min_x = Math.min(min_x, pt.x); - min_y = Math.min(min_y, pt.y); - max_x = Math.max(max_x, pt.x); - max_y = Math.max(max_y, pt.y); - }); - - topleft = {x: min_x, y: min_y}; - topright = {x: max_x, y: min_y}; - botleft = {x: min_x, y: max_y}; - botright = {x: max_x, y: max_y}; - -// topleft = transformPoint( topleft.x, topleft.y, m ); -// topright = transformPoint( topright.x, topright.y, m ); -// botleft = transformPoint( botleft.x, botleft.y, m); -// botright = transformPoint( botright.x, botright.y, m ); - return {tl:topleft, tr:topright, bl:botleft, br:botright}; + return {tl:topleft, tr:topright, bl:botleft, br:botright, + aabox: {x:minx, y:miny, width:(maxx-minx), height:(maxy-miny)} }; }; // - when we are in a create mode, the element is added to the canvas @@ -2454,7 +2469,7 @@ function BatchCommand(text) { case "rotate": started = true; // append a dummy transform that will be used as the rotate - tlist.appendItem(svgroot.createSVGTransform()); +// tlist.appendItem(svgroot.createSVGTransform()); // we are starting an undoable change (a drag-rotation) canvas.beginUndoableChange("transform", selectedElements); break; From 2d1fb52e35af68e517f264268d5bf0a23d2132a3 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Tue, 15 Dec 2009 14:16:19 +0000 Subject: [PATCH 21/38] fixtransforms branch: Fixed ConvertToPath for elements with matrix git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1040 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index b793d7dd..14c43462 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -2994,6 +2994,15 @@ function BatchCommand(text) { if(!getBBox) { // Replace the current element with the converted one + + // Reorient if it has a matrix + if(eltrans) { + var tlist = canvas.getTransformList(path); + if(hasMatrixTransform(tlist)) { + resetPathOrientation(path); + } + } + batchCmd.addSubCommand(new RemoveElementCommand(elem, parent)); batchCmd.addSubCommand(new InsertElementCommand(path)); From 194e80ff77de6e58f33a7c50be1c5af9d736333b Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Tue, 15 Dec 2009 14:34:09 +0000 Subject: [PATCH 22/38] fixtransform branch: TODO for rotating skewed elements git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1042 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 14c43462..796e891c 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1744,6 +1744,8 @@ function BatchCommand(text) { // else, it's a non-group else { var box = canvas.getBBox(selected); + // TODO: fix this, it is not correct in the case of a skewed element + // - use transformBox? var center = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; var newcenter = {x: center.x, y: center.y }; var m = svgroot.createSVGMatrix(); From 8d339e26138efc3033d44c22c6a8fe9cdad3390a Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Wed, 16 Dec 2009 04:52:49 +0000 Subject: [PATCH 23/38] fixtransforms branch: Fixes to rotating skewed elements git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1047 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 796e891c..54527914 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1746,8 +1746,11 @@ function BatchCommand(text) { var box = canvas.getBBox(selected); // TODO: fix this, it is not correct in the case of a skewed element // - use transformBox? + // TODO: if a rotated, skewed element is moved, at first no problem + // however, if another element is selected, then back to the first element + // it will hop around... var center = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; - var newcenter = {x: center.x, y: center.y }; + var newcenter = transformPoint(center.x,center.y,transformListToTransform(tlist).matrix);//{x: center.x, y: center.y }; var m = svgroot.createSVGMatrix(); // temporarily strip off the rotate @@ -1784,7 +1787,7 @@ function BatchCommand(text) { { operation = 2; // translate var oldxlate = tlist.getItem(0).matrix, - meq = transformListToTransform(tlist).matrix, + meq = transformListToTransform(tlist,1).matrix, meq_inv = meq.inverse(); m = matrixMultiply( meq_inv, oldxlate, meq ); tlist.removeItem(0); @@ -1798,7 +1801,8 @@ function BatchCommand(text) { if (angle) { // calculate the new center from the translate if (operation == 2) { - newcenter = transformPoint(center.x,center.y,m); + // TODO: avoid this? +// newcenter = transformPoint(center.x,center.y,m); } else if (operation == 3) { xcenter = transformPoint(center.x,center.y,m); @@ -1818,6 +1822,7 @@ function BatchCommand(text) { // at this point, the element looks exactly how we want it to look but its // rotational center may be off in the case of a resize - we need to fix that + // TODO: if we get in here, I think there are problems if (angle && operation == 3) { box = canvas.getBBox(selected); m = transformListToTransform(tlist).matrix; @@ -5495,6 +5500,7 @@ function BatchCommand(text) { if (elem.tagName == "g" && (attr != "transform" && attr != "opacity")) continue; var oldval = attr == "#text" ? elem.textContent : elem.getAttribute(attr); if (oldval == null) oldval = ""; + // TODO: determine why r877 changed this to !== which will means this if will always run if (oldval !== newValue) { if (attr == "#text") { var old_w = canvas.getBBox(elem).width; @@ -5542,11 +5548,16 @@ function BatchCommand(text) { while (n--) { var xform = tlist.getItem(n); if (xform.type == 4) { - var cx = round(selectedBBoxes[i].x + selectedBBoxes[i].width/2), - cy = round(selectedBBoxes[i].y + selectedBBoxes[i].height/2); + // remove old rotate + tlist.removeItem(xform); + + var box = canvas.getBBox(elem); + var center = transformPoint(box.x+box.width/2, box.y+box.height/2, transformListToTransform(tlist).matrix); + var cx = center.x, + cy = center.y; var newrot = svgroot.createSVGTransform(); newrot.setRotate(angle, cx, cy); - tlist.replaceItem(newrot, n); + tlist.insertItemBefore(newrot, n); break; } } From 54e327b22b06f3a9c2f98463f645ba44fc6a500e Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Wed, 16 Dec 2009 07:11:33 +0000 Subject: [PATCH 24/38] fixtransforms branch: undo some damage I did to normal shapes being rotated/resized, but skewed+rotated shapes go back to jumping git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1048 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 54527914..6075efbf 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1744,13 +1744,8 @@ function BatchCommand(text) { // else, it's a non-group else { var box = canvas.getBBox(selected); - // TODO: fix this, it is not correct in the case of a skewed element - // - use transformBox? - // TODO: if a rotated, skewed element is moved, at first no problem - // however, if another element is selected, then back to the first element - // it will hop around... var center = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; - var newcenter = transformPoint(center.x,center.y,transformListToTransform(tlist).matrix);//{x: center.x, y: center.y }; + var newcenter = transformPoint(center.x,center.y,transformListToTransform(tlist).matrix); var m = svgroot.createSVGMatrix(); // temporarily strip off the rotate @@ -1797,14 +1792,9 @@ function BatchCommand(text) { } // if the shape was rotated, calculate what the center will be - var xcenter = null; + var xcenter = {x:0, y:0}; if (angle) { - // calculate the new center from the translate - if (operation == 2) { - // TODO: avoid this? -// newcenter = transformPoint(center.x,center.y,m); - } - else if (operation == 3) { + if (operation == 3) { xcenter = transformPoint(center.x,center.y,m); } } @@ -1822,24 +1812,14 @@ function BatchCommand(text) { // at this point, the element looks exactly how we want it to look but its // rotational center may be off in the case of a resize - we need to fix that - // TODO: if we get in here, I think there are problems - if (angle && operation == 3) { - box = canvas.getBBox(selected); - m = transformListToTransform(tlist).matrix; - - // get its actual center - newcenter = transformPoint(box.x+box.width/2, box.y+box.height/2, m); - + if (false && angle && operation == 3) { // determine the resultant delta translate to re-center var dx = newcenter.x - xcenter.x, dy = newcenter.y - xcenter.y; var delta = svgroot.createSVGMatrix().translate(dx,dy); + console.log([dx,dy]); remapElement(selected,changes,delta); - - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(angle,newcenter.x,newcenter.y); - tlist.replaceItem(newRot,0); } } // a non-group From c54d14f394989fd3266dc5c951bb63c90b4e8119 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Thu, 17 Dec 2009 07:41:33 +0000 Subject: [PATCH 25/38] fixtransform branch: rework recalculateDimensions() for non-groups to deal with rotations, scales, translations of skewed/non-skewed, rotated/unrotated elements git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1053 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 70 ++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 6075efbf..7005584e 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1743,29 +1743,36 @@ function BatchCommand(text) { } // else, it's a non-group else { + // TODO: it all seems to work now, but we need to make sure it's undo-able var box = canvas.getBBox(selected); - var center = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; - var newcenter = transformPoint(center.x,center.y,transformListToTransform(tlist).matrix); + var oldcenter = {x: box.x+box.width/2, y: box.y+box.height/2}; + var newcenter = transformPoint(box.x+box.width/2, box.y+box.height/2, + transformListToTransform(tlist).matrix); var m = svgroot.createSVGMatrix(); - - // temporarily strip off the rotate + + // temporarily strip off the rotate and save the old center var angle = canvas.getRotationAngle(selected); if (angle) { for (var i = 0; i < tlist.numberOfItems; ++i) { var xform = tlist.getItem(i); if (xform.type == 4) { + // extract old center through mystical arts + var rm = xform.matrix; + var a = angle * Math.PI / 180; + oldcenter.y = 0.5 * (Math.sin(a)*rm.e + (1-Math.cos(a))*rm.f) / (1 - Math.cos(a)); + oldcenter.x = ((1 - Math.cos(a)) * oldcenter.y - rm.f) / Math.sin(a); tlist.removeItem(i); break; } } } - - var N = tlist.numberOfItems; - var operation = 0; + var operation = 0; + var N = tlist.numberOfItems; // first, if it was a scale then the second-last transform will be it // if we had [M][T][S][T] we want to extract the matrix equivalent of // [T][S][T] and push it down + console.log("N=" + N); if (N >= 3 && tlist.getItem(N-2).type == 3 && tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) { @@ -1788,38 +1795,41 @@ function BatchCommand(text) { tlist.removeItem(0); } else { - operation = 4; // rotate + operation = 4; // rotation + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(angle,newcenter.x,newcenter.y); + tlist.insertItemBefore(newRot, 0); } - // if the shape was rotated, calculate what the center will be - var xcenter = {x:0, y:0}; - if (angle) { - if (operation == 3) { - xcenter = transformPoint(center.x,center.y,m); - } - } - if (operation == 2 || operation == 3) { remapElement(selected,changes,m); } // if we are remapping - // if we had a rotation, re-add the rotation back with its original center - if (angle) { - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(angle,newcenter.x,newcenter.y); - tlist.insertItemBefore(newRot,0); + if (operation == 2) { + if (angle) { + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(angle,newcenter.x,newcenter.y); + tlist.insertItemBefore(newRot, 0); + } } + // [Rold][M][T][S][-T] became [Rold][M] + // we want it to be [Rnew][M][Tr] where Tr is the + // translation required to re-center it + // Therefore, [Tr][ = [M_inv][Rnew_inv][Rold][M] + else if (operation == 3) { + var m = transformListToTransform(tlist).matrix; + var roldt = svgroot.createSVGTransform(); + roldt.setRotate(angle, oldcenter.x, oldcenter.y); + var rold = roldt.matrix; + var rnew = svgroot.createSVGTransform(); + rnew.setRotate(angle, newcenter.x, newcenter.y); + var rnew_inv = rnew.matrix.inverse(); + var m_inv = m.inverse(); + var extrat = matrixMultiply(m_inv, rnew_inv, rold, m); - // at this point, the element looks exactly how we want it to look but its - // rotational center may be off in the case of a resize - we need to fix that - if (false && angle && operation == 3) { - // determine the resultant delta translate to re-center - var dx = newcenter.x - xcenter.x, - dy = newcenter.y - xcenter.y; + remapElement(selected,changes,extrat); - var delta = svgroot.createSVGMatrix().translate(dx,dy); - console.log([dx,dy]); - remapElement(selected,changes,delta); + tlist.insertItemBefore(rnew,0); } } // a non-group From 48c4aa0e9752ebadcf4b344decd491154828e1e8 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Thu, 17 Dec 2009 15:02:14 +0000 Subject: [PATCH 26/38] fixtransforms branch: Fix undo of transforms git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1054 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 7005584e..e63896d2 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1743,7 +1743,6 @@ function BatchCommand(text) { } // else, it's a non-group else { - // TODO: it all seems to work now, but we need to make sure it's undo-able var box = canvas.getBBox(selected); var oldcenter = {x: box.x+box.width/2, y: box.y+box.height/2}; var newcenter = transformPoint(box.x+box.width/2, box.y+box.height/2, @@ -1769,10 +1768,9 @@ function BatchCommand(text) { var operation = 0; var N = tlist.numberOfItems; - // first, if it was a scale then the second-last transform will be it + // first, if it was a scale then the second-last transform will be the [S] // if we had [M][T][S][T] we want to extract the matrix equivalent of - // [T][S][T] and push it down - console.log("N=" + N); + // [T][S][T] and push it down to the element if (N >= 3 && tlist.getItem(N-2).type == 3 && tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) { @@ -1783,7 +1781,8 @@ function BatchCommand(text) { tlist.removeItem(N-3); } // if we had [T1][M] we want to transform this into [M][T2] - // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] + // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] and we can push [T2] + // down to the element else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && tlist.getItem(0).type == 2) { @@ -1794,17 +1793,22 @@ function BatchCommand(text) { m = matrixMultiply( meq_inv, oldxlate, meq ); tlist.removeItem(0); } + // if it was a rotation, put the rotate back and return without a command + // (this function has zero work to do for a rotate()) else { operation = 4; // rotation var newRot = svgroot.createSVGTransform(); newRot.setRotate(angle,newcenter.x,newcenter.y); - tlist.insertItemBefore(newRot, 0); + tlist.insertItemBefore(newRot, 0); + return null; } + // if it was a translate or resize, we need to remap the element and absorb the xform if (operation == 2 || operation == 3) { remapElement(selected,changes,m); } // if we are remapping + // if it was a translate, put back the rotate at the new center if (operation == 2) { if (angle) { var newRot = svgroot.createSVGTransform(); @@ -1815,7 +1819,7 @@ function BatchCommand(text) { // [Rold][M][T][S][-T] became [Rold][M] // we want it to be [Rnew][M][Tr] where Tr is the // translation required to re-center it - // Therefore, [Tr][ = [M_inv][Rnew_inv][Rold][M] + // Therefore, [Tr] = [M_inv][Rnew_inv][Rold][M] else if (operation == 3) { var m = transformListToTransform(tlist).matrix; var roldt = svgroot.createSVGTransform(); @@ -1837,7 +1841,6 @@ function BatchCommand(text) { if (tlist.numberOfItems == 0) { selected.removeAttribute("transform"); } - batchCmd.addSubCommand(new ChangeElementCommand(selected, initial)); return batchCmd; @@ -5490,8 +5493,7 @@ function BatchCommand(text) { if (elem.tagName == "g" && (attr != "transform" && attr != "opacity")) continue; var oldval = attr == "#text" ? elem.textContent : elem.getAttribute(attr); if (oldval == null) oldval = ""; - // TODO: determine why r877 changed this to !== which will means this if will always run - if (oldval !== newValue) { + if (oldval != String(newValue)) { if (attr == "#text") { var old_w = canvas.getBBox(elem).width; elem.textContent = newValue; From 729900d796631e5e6ea26ca4ff88d9826bf57578 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Thu, 17 Dec 2009 15:12:54 +0000 Subject: [PATCH 27/38] fixtransforms branch: Remove zero-degree rotations and empty transform strings git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1055 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index e63896d2..1c698995 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1550,7 +1550,10 @@ function BatchCommand(text) { } // if this element had no transforms, we are done - if (tlist.numberOfItems == 0) return null; + if (tlist.numberOfItems == 0) { + selected.removeAttribute("transform"); + return null; + } // we know we have some transforms, so set up return variable var batchCmd = new BatchCommand("Transform"); @@ -1797,9 +1800,14 @@ function BatchCommand(text) { // (this function has zero work to do for a rotate()) else { operation = 4; // rotation - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(angle,newcenter.x,newcenter.y); - tlist.insertItemBefore(newRot, 0); + if (angle) { + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(angle,newcenter.x,newcenter.y); + tlist.insertItemBefore(newRot, 0); + } + if (tlist.numberOfItems == 0) { + selected.removeAttribute("transform"); + } return null; } @@ -4109,7 +4117,7 @@ function BatchCommand(text) { addCommandToHistory(new InsertElementCommand(element)); call("changed",[element]); } - + start_transform = null; }; @@ -5238,10 +5246,15 @@ function BatchCommand(text) { } // find R_nc and insert it - var center = transformPoint(cx,cy,transformListToTransform(tlist).matrix); - var R_nc = svgroot.createSVGTransform(); - R_nc.setRotate(val, center.x, center.y); - tlist.insertItemBefore(R_nc,0); + if (val != 0) { + var center = transformPoint(cx,cy,transformListToTransform(tlist).matrix); + var R_nc = svgroot.createSVGTransform(); + R_nc.setRotate(val, center.x, center.y); + tlist.insertItemBefore(R_nc,0); + } + else if (tlist.numberOfItems == 0) { + elem.removeAttribute("transform"); + } if (!preventUndo) { // we need to undo it, then redo it so it can be undo-able! :) From 0baff00fd5d747cf67bb2b59e81840ae26921523 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Thu, 17 Dec 2009 18:16:00 +0000 Subject: [PATCH 28/38] fixtransforms branch: Made point-based elements remap instead of use matrix(), also added possible fix for r357 git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1057 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 46 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 1c698995..76217bdb 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -15,26 +15,7 @@ * ensure zooming works properly * ensure undo/redo works perfectly */ -/* - TODOs for Localizing: - - - rename tool_path to tool_fhpath in all localization files (already updated in UI and script) - - rename tool_poly to tool_path in all localization files (already updated in UI and script) - - rename poly_node_x to path_node_x globally - - rename poly_node_y to path_node_y globally - - rename svninfo_dim to svginfo_dim globally (typo) - - provide translations in all other non-EN lang.XX.js files for: - - path_node_x - - path_node_y - - seg_type - - straight_segments - - curve_segments - - tool_node_clone - - tool_node_delete - - selLayerLabel - - selLayerNames - - sidepanel_handle -*/ + var isWebkit = navigator.userAgent.indexOf("AppleWebKit") != -1; if(!window.console) { window.console = {}; @@ -1664,12 +1645,29 @@ function BatchCommand(text) { var angle = canvas.getRotationAngle(child); if(angle || hasMatrixTransform(childTlist)) { var em = matrixMultiply(tm, sm, tmn, m); - - // this does not appear to work, something wrong with my logic here var e2t = svgroot.createSVGTransform(); e2t.setMatrix(em); childTlist.clear(); childTlist.appendItem(e2t,0); + + // Remap all point-based elements + var ch = {}; + switch (child.tagName) { + case 'line': + ch.x1 = child.getAttribute("x1"); + ch.y1 = child.getAttribute("y1"); + ch.x2 = child.getAttribute("x2"); + ch.y2 = child.getAttribute("y2"); + case 'polyline': + case 'polygon': + ch.points = child.getAttribute("points"); + case 'path': + ch.d = child.getAttribute("d"); + remapElement(child, ch, em); + childTlist.clear(); + default: + break; + } } // if not rotated or skewed, push the [T][S][-T] down to the child else { @@ -3646,7 +3644,7 @@ function BatchCommand(text) { // identified, a ChangeElementCommand is created and stored on the stack for those attrs // this is done in when we recalculate the selected dimensions() var mouseUp = function(evt) - { + { var tempJustSelected = justSelected; justSelected = null; if (!started) return; @@ -5610,7 +5608,7 @@ function BatchCommand(text) { } }; - $(container).mouseup(mouseUp); + $(window).mouseup(mouseUp); $(container).mousedown(mouseDown); $(container).mousemove(mouseMove); From 9c12d0af58f4768fe0b6cc2f8b3bea328d4a61d6 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Thu, 17 Dec 2009 18:50:40 +0000 Subject: [PATCH 29/38] fixtransforms branch: Fixed last revision to make polylines and paths work git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1058 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 76217bdb..d3afd033 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1661,8 +1661,39 @@ function BatchCommand(text) { case 'polyline': case 'polygon': ch.points = child.getAttribute("points"); + if(ch.points) { + var list = child.points; + var len = list.numberOfItems; + ch.points = new Array(len); + for (var i = 0; i < len; ++i) { + var pt = list.getItem(i); + ch.points[i] = {x:pt.x,y:pt.y}; + } + } case 'path': ch.d = child.getAttribute("d"); + if(ch.d) { + var segList = child.pathSegList; + var len = segList.numberOfItems; + ch.d = new Array(len); + for (var i = 0; i < len; ++i) { + var seg = segList.getItem(i); + ch.d[i] = { + type: seg.pathSegType, + x: seg.x, + y: seg.y, + x1: seg.x1, + y1: seg.y1, + x2: seg.x2, + y2: seg.y2, + r1: seg.r1, + r2: seg.r2, + angle: seg.angle, + largeArcFlag: seg.largeArcFlag, + sweepFlag: seg.sweepFlag + }; + } + } remapElement(child, ch, em); childTlist.clear(); default: From 070a710250bc14ee07dd3dd8f52fdd2f80170530 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Thu, 17 Dec 2009 19:24:24 +0000 Subject: [PATCH 30/38] fixtransforms branch: Fixed bug that messed up aligning matrix/rotated elements (in moveSelectedElements) git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1059 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index d3afd033..c8684f5a 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -5827,7 +5827,7 @@ function BatchCommand(text) { xform.setTranslate(dx,dy); } - tlist.appendItem(xform); + tlist.insertItemBefore(xform, 0); var cmd = recalculateDimensions(selected); if (cmd) { From 4259f84e991a477f95c590ef1641573389d88ab3 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Fri, 18 Dec 2009 05:00:45 +0000 Subject: [PATCH 31/38] fixtransforms branch: Properly push down and collapse rotate() transforms when ungrouping. Fix a bug in addToSelection() git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1060 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 103 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 19 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index c8684f5a..5ef4bc97 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1527,6 +1527,11 @@ function BatchCommand(text) { tlist.removeItem(k); } } + else if (xform.type == 4) { + if (xform.angle == 0) { + tlist.removeItem(k); + } + } } } @@ -1921,7 +1926,7 @@ function BatchCommand(text) { } ++j; } - + // now add each element consecutively var i = elemsToAdd.length; while (i--) { @@ -1933,12 +1938,13 @@ function BatchCommand(text) { selectedElements[j] = elem; selectedBBoxes[j++] = this.getBBox(elem); var sel = selectorManager.requestSelector(elem); + if (selectedElements.length > 1) { sel.showGrips(false); } - call("selected", selectedElements); } } + call("selected", selectedElements); if(showGrips) { selectorManager.requestSelector(selectedElements[0]).showGrips(true); @@ -5704,7 +5710,8 @@ function BatchCommand(text) { var children = new Array(g.childNodes.length); var xform = g.getAttribute("transform"); // get consolidated matrix - var m = transformListToTransform(canvas.getTransformList(g)).matrix; + var glist = canvas.getTransformList(g); + var m = transformListToTransform(glist).matrix; // TODO: get all fill/stroke properties from the group that we are about to destroy // "fill", "fill-opacity", "fill-rule", "stroke", "stroke-dasharray", "stroke-dashoffset", @@ -5715,7 +5722,9 @@ function BatchCommand(text) { // TODO: get the group's opacity and propagate it down to the children (multiply it // by the child's opacity (or 1.0) + var i = 0; + var gangle = canvas.getRotationAngle(g); while (g.firstChild) { var elem = g.firstChild; var oldNextSibling = elem.nextSibling; @@ -5723,24 +5732,80 @@ function BatchCommand(text) { children[i++] = elem = parent.insertBefore(elem, anchor); batchCmd.addSubCommand(new MoveElementCommand(elem, oldNextSibling, oldParent)); - // transfer the group's transform down to each child and then - // call recalculateDimensions() - if (xform) { - var oldxform = elem.getAttribute("transform"); - var changes = {}; - changes["transform"] = oldxform ? oldxform : ""; + var chtlist = canvas.getTransformList(elem); + + if (glist.numberOfItems) { + // TODO: if the group's transform is just a rotate, we can always transfer the + // rotate() down to the children (collapsing consecutive rotates and factoring + // out any translates) + if (gangle && glist.numberOfItems == 1) { + // [Rg] [Rc] [Mc] + // we want [Tr] [Rc2] [Mc] where: + // - [Rc2] is at the child's current center but has the + // sum of the group and child's rotation angles + // - [Tr] is the equivalent translation that this child + // undergoes if the group wasn't there + + // [Tr] = [Rg] [Rc] [Mc] [Mc_inv] [Rc2_inv] + // [Tr] = [Rg] [Rc] [Rc2_inv] + + // get group's rotation matrix (Rg) + var rgm = glist.getItem(0).matrix; + + // get child's rotation matrix (Rc) + var rcm = svgroot.createSVGMatrix(); + var cangle = canvas.getRotationAngle(elem); + if (cangle) { + rcm = chtlist.getItem(0).matrix; + } + + // get child's old center of rotation + var cbox = canvas.getBBox(elem); + var ceqm = transformListToTransform(chtlist).matrix; + var coldc = transformPoint(cbox.x+cbox.width/2, cbox.y+cbox.height/2,ceqm); + + // sum group and child's angles + var sangle = gangle + cangle; + + // TODO: get child's rotation at the old center (Rc2_inv) + var r2 = svgroot.createSVGTransform(); + r2.setRotate(sangle, coldc.x, coldc.y); + + // calculate equivalent translate + var trm = matrixMultiply(rgm, rcm, r2.matrix.inverse()); + + // set up tlist + if (cangle) { + chtlist.removeItem(0); + } + + if (sangle) { + chtlist.insertItemBefore(r2, 0); + } - var newxform = svgroot.createSVGTransform(); - var chtlist = canvas.getTransformList(elem); + if (trm.e || trm.f) { + var tr = svgroot.createSVGTransform(); + tr.setTranslate(trm.e, trm.f); + chtlist.insertItemBefore(tr, 0); + } + } + else { // more complicated than just a rotate + // transfer the group's transform down to each child and then + // call recalculateDimensions() + var oldxform = elem.getAttribute("transform"); + var changes = {}; + changes["transform"] = oldxform ? oldxform : ""; - // [ gm ] [ chm ] = [ chm ] [ gm' ] - // [ gm' ] = [ chm_inv ] [ gm ] [ chm ] - var chm = transformListToTransform(chtlist).matrix, - chm_inv = chm.inverse(); - var gm = matrixMultiply( chm_inv, m, chm ); - newxform.setMatrix(gm); - chtlist.appendItem(newxform); - + var newxform = svgroot.createSVGTransform(); + + // [ gm ] [ chm ] = [ chm ] [ gm' ] + // [ gm' ] = [ chm_inv ] [ gm ] [ chm ] + var chm = transformListToTransform(chtlist).matrix, + chm_inv = chm.inverse(); + var gm = matrixMultiply( chm_inv, m, chm ); + newxform.setMatrix(gm); + chtlist.appendItem(newxform); + } batchCmd.addSubCommand(recalculateDimensions(elem)); } } From 0dc0ec0d41e58017967b5efadc22655dae764430 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Fri, 18 Dec 2009 06:13:42 +0000 Subject: [PATCH 32/38] fixtransforms branch: Text elements maintain stretches/translations in groups git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1061 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 5ef4bc97..31e9cab9 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1373,9 +1373,12 @@ function BatchCommand(text) { changes["height"] = scaleh(changes["height"]); break; case "text": - var pt1 = remap(changes["x"],changes["y"]); - changes["x"] = pt1.x; - changes["y"] = pt1.y; + // we just absorb all matrices into the element and don't do any remapping + var chlist = canvas.getTransformList(selected); + var mt = svgroot.createSVGTransform(); + mt.setMatrix(matrixMultiply(transformListToTransform(chlist).matrix,m)); + chlist.clear(); + chlist.appendItem(mt); break; case "polygon": case "polyline": @@ -1514,7 +1517,7 @@ function BatchCommand(text) { var tlist = canvas.getTransformList(selected); - // remove any stray identity transforms + // remove any unnecessary transforms if (tlist && tlist.numberOfItems > 0) { var k = tlist.numberOfItems; while (k--) { @@ -1522,11 +1525,13 @@ function BatchCommand(text) { if (xform.type == 0) { tlist.removeItem(k); } + // remove identity matrices else if (xform.type == 1) { if (isIdentity(xform.matrix)) { tlist.removeItem(k); } } + // remove zero-degree rotations else if (xform.type == 4) { if (xform.angle == 0) { tlist.removeItem(k); @@ -1874,8 +1879,9 @@ function BatchCommand(text) { var extrat = matrixMultiply(m_inv, rnew_inv, rold, m); remapElement(selected,changes,extrat); - - tlist.insertItemBefore(rnew,0); + if (angle) { + tlist.insertItemBefore(rnew,0); + } } } // a non-group From 3d3711e499734cbcd9ceaa3939307c5c96c2408f Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Fri, 18 Dec 2009 06:20:37 +0000 Subject: [PATCH 33/38] fixtransforms branch: Fix bug in changeSelectedAttributeNoUndo() that made Opera and WebKit hiccup git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1062 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 31e9cab9..99ae8698 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -5595,7 +5595,7 @@ function BatchCommand(text) { var xform = tlist.getItem(n); if (xform.type == 4) { // remove old rotate - tlist.removeItem(xform); + tlist.removeItem(n); var box = canvas.getBBox(elem); var center = transformPoint(box.x+box.width/2, box.y+box.height/2, transformListToTransform(tlist).matrix); From 30ac733e769bdd0e4dd34773330396dfca8c38bb Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Fri, 18 Dec 2009 06:50:55 +0000 Subject: [PATCH 34/38] fixtransforms branch: Remove unnecessary rounding in center of rotations (can cause weird problems) git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1064 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 99ae8698..e99da733 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -107,8 +107,8 @@ function ChangeElementCommand(elem, attrs, text) { var angle = canvas.getRotationAngle(elem); if (angle) { var bbox = elem.getBBox(); - var cx = round(bbox.x + bbox.width/2), - cy = round(bbox.y + bbox.height/2); + var cx = bbox.x + bbox.width/2, + cy = bbox.y + bbox.height/2; var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join(''); if (rotate != elem.getAttribute("transform")) { elem.setAttribute("transform", rotate); @@ -140,8 +140,8 @@ function ChangeElementCommand(elem, attrs, text) { var angle = canvas.getRotationAngle(elem); if (angle) { var bbox = elem.getBBox(); - var cx = round(bbox.x + bbox.width/2), - cy = round(bbox.y + bbox.height/2); + var cx = bbox.x + bbox.width/2, + cy = bbox.y + bbox.height/2; var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join(''); if (rotate != elem.getAttribute("transform")) { elem.setAttribute("transform", rotate); @@ -2833,13 +2833,13 @@ function BatchCommand(text) { break; case "rotate": var box = canvas.getBBox(selected), - cx = round(box.x + box.width/2), - cy = round(box.y + box.height/2); + cx = box.x + box.width/2, + cy = box.y + box.height/2; var m = transformListToTransform(canvas.getTransformList(selected)).matrix; var center = transformPoint(cx,cy,m); cx = center.x; cy = center.y; - var angle = round(((Math.atan2(cy-y,cx-x) * (180/Math.PI))-90) % 360); + var angle = ((Math.atan2(cy-y,cx-x) * (180/Math.PI))-90) % 360; canvas.setRotationAngle(angle<-180?(360+angle):angle, true); call("changed", selectedElements); break; @@ -3415,8 +3415,8 @@ function BatchCommand(text) { if (angle) { // calculate the shape's old center that was used for rotation var box = selectedBBoxes[0]; - var cx = round(box.x + box.width/2) * current_zoom, - cy = round(box.y + box.height/2) * current_zoom; + var cx = (box.x + box.width/2) * current_zoom, + cy = (box.y + box.height/2) * current_zoom; var dx = mouse_x - cx, dy = mouse_y - cy; var r = Math.sqrt( dx*dx + dy*dy ); var theta = Math.atan2(dy,dx) - angle; @@ -3567,8 +3567,8 @@ function BatchCommand(text) { if (angle) { // calculate the shape's old center that was used for rotation var box = selectedBBoxes[0]; - var cx = round(box.x + box.width/2) * current_zoom, - cy = round(box.y + box.height/2) * current_zoom; + var cx = (box.x + box.width/2) * current_zoom, + cy = (box.y + box.height/2) * current_zoom; var dx = mouse_x - cx, dy = mouse_y - cy; var r = Math.sqrt( dx*dx + dy*dy ); var theta = Math.atan2(dy,dx) - angle; @@ -5275,7 +5275,7 @@ function BatchCommand(text) { var elem = selectedElements[0]; var oldTransform = elem.getAttribute("transform"); var bbox = elem.getBBox(); - var cx = round(bbox.x+bbox.width/2), cy = round(bbox.y+bbox.height/2); + var cx = bbox.x+bbox.width/2, cy = bbox.y+bbox.height/2; var tlist = canvas.getTransformList(elem); // only remove the real rotational transform if present (i.e. at index=0) @@ -5752,7 +5752,6 @@ function BatchCommand(text) { // - [Tr] is the equivalent translation that this child // undergoes if the group wasn't there - // [Tr] = [Rg] [Rc] [Mc] [Mc_inv] [Rc2_inv] // [Tr] = [Rg] [Rc] [Rc2_inv] // get group's rotation matrix (Rg) @@ -5773,7 +5772,7 @@ function BatchCommand(text) { // sum group and child's angles var sangle = gangle + cangle; - // TODO: get child's rotation at the old center (Rc2_inv) + // get child's rotation at the old center (Rc2_inv) var r2 = svgroot.createSVGTransform(); r2.setRotate(sangle, coldc.x, coldc.y); From fc6014768e64cd1a5455c6a20bbfaac5d4e1c003 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Fri, 18 Dec 2009 17:12:48 +0000 Subject: [PATCH 35/38] fixtransforms branch: Mostly fixed resizing matrix-elements (still bug on rotated matrix-elements) git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1067 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 73 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 6106748a..cdaab618 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -850,6 +850,7 @@ function BatchCommand(text) { var start_x = null; var start_y = null; var start_transform = null; + var init_bbox = {}; var current_mode = "select"; var current_resize_mode = "none"; @@ -1815,6 +1816,7 @@ function BatchCommand(text) { var operation = 0; var N = tlist.numberOfItems; + // first, if it was a scale then the second-last transform will be the [S] // if we had [M][T][S][T] we want to extract the matrix equivalent of // [T][S][T] and push it down to the element @@ -1826,7 +1828,31 @@ function BatchCommand(text) { tlist.removeItem(N-1); tlist.removeItem(N-2); tlist.removeItem(N-3); - } + } // if we had [T][S][-T][M], then this was a matrix-element being + // resized. Thus, we simply combine it all into one matrix + else if(N == 4 && tlist.getItem(N-1).type == 1 && !angle) { + m = transformListToTransform(tlist).matrix; + var e2t = svgroot.createSVGTransform(); + e2t.setMatrix(m); + tlist.clear(); + tlist.appendItem(e2t); + return null; + } // if we had [R][T][S][-T][M], then this was a rotated matrix-element + // being resized. Thus, we simply combine the matrix and keep the rotate + else if(N == 4 && tlist.getItem(N-1).type == 1 && angle) { + m = transformListToTransform(tlist).matrix; + var e2t = svgroot.createSVGTransform(); + e2t.setMatrix(m); + tlist.clear(); + tlist.appendItem(e2t); + + // Not positioned correctly yet. + // FIXME codedread! You're my only hope... + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(angle,newcenter.x,newcenter.y); + tlist.insertItemBefore(newRot, 0); + return null; + } // if we had [T1][M] we want to transform this into [M][T2] // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] and we can push [T2] // down to the element @@ -1855,6 +1881,7 @@ function BatchCommand(text) { return null; } + // if it was a translate or resize, we need to remap the element and absorb the xform if (operation == 2 || operation == 3) { remapElement(selected,changes,m); @@ -2306,11 +2333,27 @@ function BatchCommand(text) { started = true; start_x = x; start_y = y; + + // Getting the BBox from the selection box, since we know we + // want to orient around it + init_bbox = canvas.getBBox($('#selectedBox0')[0]); + $.each(init_bbox, function(key, val) { + init_bbox[key] = val/current_zoom; + }); + // append three dummy transforms to the tlist so that // we can translate,scale,translate in mousemove - tlist.appendItem(svgroot.createSVGTransform()); - tlist.appendItem(svgroot.createSVGTransform()); - tlist.appendItem(svgroot.createSVGTransform()); + var pos = canvas.getRotationAngle(mouse_target)?1:0; + + if(hasMatrixTransform(tlist)) { + tlist.insertItemBefore(svgroot.createSVGTransform(), pos); + tlist.insertItemBefore(svgroot.createSVGTransform(), pos); + tlist.insertItemBefore(svgroot.createSVGTransform(), pos); + } else { + tlist.appendItem(svgroot.createSVGTransform()); + tlist.appendItem(svgroot.createSVGTransform()); + tlist.appendItem(svgroot.createSVGTransform()); + } break; case "fhellipse": case "fhrect": @@ -2652,7 +2695,9 @@ function BatchCommand(text) { // we track the resize bounding box and translate/scale the selected element // while the mouse is down, when mouse goes up, we use this to recalculate // the shape's coordinates - var box=canvas.getBBox(selected), left=box.x, top=box.y, width=box.width, + var tlist = canvas.getTransformList(selected); + var hasMatrix = hasMatrixTransform(tlist); + var box=hasMatrix?init_bbox:canvas.getBBox(selected), left=box.x, top=box.y, width=box.width, height=box.height, dx=(x-start_x), dy=(y-start_y); // if rotated, adjust the dx,dy values @@ -2690,7 +2735,6 @@ function BatchCommand(text) { } // update the transform list with translate,scale,translate - var tlist = canvas.getTransformList(selected); var translateOrigin = svgroot.createSVGTransform(), scale = svgroot.createSVGTransform(), translateBack = svgroot.createSVGTransform(); @@ -2700,14 +2744,19 @@ function BatchCommand(text) { else sy = sx; } scale.setScale(sx,sy); - translateBack.setTranslate(left+tx,top+ty); - var N = tlist.numberOfItems; - tlist.replaceItem(translateBack, N-3); - tlist.replaceItem(scale, N-2); - tlist.replaceItem(translateOrigin, N-1); - + if(hasMatrix) { + var diff = angle?1:0; + tlist.replaceItem(translateOrigin, 2+diff); + tlist.replaceItem(scale, 1+diff); + tlist.replaceItem(translateBack, 0+diff); + } else { + var N = tlist.numberOfItems; + tlist.replaceItem(translateBack, N-3); + tlist.replaceItem(scale, N-2); + tlist.replaceItem(translateOrigin, N-1); + } var selectedBBox = selectedBBoxes[0]; // reset selected bbox top-left position From ccade30124119b281ab738447f69da0bb5d74581 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Fri, 18 Dec 2009 17:39:39 +0000 Subject: [PATCH 36/38] fixtransforms branch: Fix Issue 363, moved/resized groups get their rotational center updated git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1068 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 91 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index cdaab618..29b35782 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1635,13 +1635,40 @@ function BatchCommand(text) { // if it's a group, we have special processing to flatten transforms if (selected.tagName == "g") { + var box = canvas.getBBox(selected); + var oldcenter = {x: box.x+box.width/2, y: box.y+box.height/2}; + var newcenter = transformPoint(box.x+box.width/2, box.y+box.height/2, + transformListToTransform(tlist).matrix); + var m = svgroot.createSVGMatrix(); + + // temporarily strip off the rotate and save the old center + var gangle = canvas.getRotationAngle(selected); + if (gangle) { + for (var i = 0; i < tlist.numberOfItems; ++i) { + var xform = tlist.getItem(i); + if (xform.type == 4) { + // extract old center through mystical arts + var rm = xform.matrix; + var a = gangle * Math.PI / 180; + // FIXME: This blows up if the angle is exactly 0 or 180 degrees! + oldcenter.y = 0.5 * (Math.sin(a)*rm.e + (1-Math.cos(a))*rm.f) / (1 - Math.cos(a)); + oldcenter.x = ((1 - Math.cos(a)) * oldcenter.y - rm.f) / Math.sin(a); + tlist.removeItem(i); + break; + } + } + } + var tx = 0, ty = 0; + var operation = 0; var N = tlist.numberOfItems; // first, if it was a scale then the second-last transform will be it if (N >= 3 && tlist.getItem(N-2).type == 3 && tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2) { + operation = 3; // scale + // if the children are unrotated, pass the scale down directly // otherwise pass the equivalent matrix() down directly var tm = tlist.getItem(N-3).matrix; @@ -1715,6 +1742,7 @@ function BatchCommand(text) { default: break; } + // FIXME: we're not saving a subcommand to the batchCmd for this child } // if not rotated or skewed, push the [T][S][-T] down to the child else { @@ -1759,6 +1787,7 @@ function BatchCommand(text) { else if ( (N == 1 || (N > 1 && tlist.getItem(1).type != 3)) && tlist.getItem(0).type == 2) { + operation = 2; // translate var T_M = transformListToTransform(tlist).matrix; tlist.removeItem(0); var M_inv = transformListToTransform(tlist).matrix.inverse(); @@ -1788,6 +1817,67 @@ function BatchCommand(text) { start_transform = old_start_transform; } } + // else it was just a rotate + else { + if (gangle) { + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(gangle,newcenter.x,newcenter.y); + tlist.insertItemBefore(newRot, 0); + } + if (tlist.numberOfItems == 0) { + selected.removeAttribute("transform"); + } + return null; + } + + // if it was a translate, put back the rotate at the new center + if (operation == 2) { + if (gangle) { + var newRot = svgroot.createSVGTransform(); + newRot.setRotate(gangle,newcenter.x,newcenter.y); + tlist.insertItemBefore(newRot, 0); + } + } + // if it was a resize + else if (operation == 3) { + var m = transformListToTransform(tlist).matrix; + var roldt = svgroot.createSVGTransform(); + roldt.setRotate(gangle, oldcenter.x, oldcenter.y); + var rold = roldt.matrix; + var rnew = svgroot.createSVGTransform(); + rnew.setRotate(gangle, newcenter.x, newcenter.y); + var rnew_inv = rnew.matrix.inverse(); + var m_inv = m.inverse(); + var extrat = matrixMultiply(m_inv, rnew_inv, rold, m); + + tx = extrat.e; + ty = extrat.f; + + if (tx != 0 || ty != 0) { + // now push this transform down to the children + // FIXME: unfortunately recalculateDimensions depends on this global variable + var old_start_transform = start_transform; + start_transform = null; + // 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(tx,ty); + childTlist.insertItemBefore(newxlate, 0); + batchCmd.addSubCommand( recalculateDimensions(child) ); + } + } + start_transform = old_start_transform; + } + + if (gangle) { + tlist.insertItemBefore(rnew, 0); + } + } } // else, it's a non-group else { @@ -1806,6 +1896,7 @@ function BatchCommand(text) { // extract old center through mystical arts var rm = xform.matrix; var a = angle * Math.PI / 180; + // FIXME: This blows up if the angle is exactly 0 or 180 degrees! oldcenter.y = 0.5 * (Math.sin(a)*rm.e + (1-Math.cos(a))*rm.f) / (1 - Math.cos(a)); oldcenter.x = ((1 - Math.cos(a)) * oldcenter.y - rm.f) / Math.sin(a); tlist.removeItem(i); From 2736be370bdb4b7dc528126715b862ffe4be3c38 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Fri, 18 Dec 2009 18:22:06 +0000 Subject: [PATCH 37/38] fixtransforms branch: resizing a skewed element now works properly git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1069 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 29b35782..32a27191 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1908,7 +1908,8 @@ function BatchCommand(text) { var operation = 0; var N = tlist.numberOfItems; - // first, if it was a scale then the second-last transform will be the [S] + // first, if it was a scale of a non-skewed element, then the second-last + // transform will be the [S] // if we had [M][T][S][T] we want to extract the matrix equivalent of // [T][S][T] and push it down to the element if (N >= 3 && tlist.getItem(N-2).type == 3 && @@ -1919,31 +1920,18 @@ function BatchCommand(text) { tlist.removeItem(N-1); tlist.removeItem(N-2); tlist.removeItem(N-3); - } // if we had [T][S][-T][M], then this was a matrix-element being - // resized. Thus, we simply combine it all into one matrix - else if(N == 4 && tlist.getItem(N-1).type == 1 && !angle) { + } // if we had [T][S][-T][M], then this was a skewed element being resized + // Thus, we simply combine it all into one matrix + else if(N == 4 && tlist.getItem(N-1).type == 1) { + operation = 3; // scale m = transformListToTransform(tlist).matrix; var e2t = svgroot.createSVGTransform(); e2t.setMatrix(m); tlist.clear(); tlist.appendItem(e2t); - return null; + // reset the matrix so that the element is not re-mapped + m = svgroot.createSVGMatrix(); } // if we had [R][T][S][-T][M], then this was a rotated matrix-element - // being resized. Thus, we simply combine the matrix and keep the rotate - else if(N == 4 && tlist.getItem(N-1).type == 1 && angle) { - m = transformListToTransform(tlist).matrix; - var e2t = svgroot.createSVGTransform(); - e2t.setMatrix(m); - tlist.clear(); - tlist.appendItem(e2t); - - // Not positioned correctly yet. - // FIXME codedread! You're my only hope... - var newRot = svgroot.createSVGTransform(); - newRot.setRotate(angle,newcenter.x,newcenter.y); - tlist.insertItemBefore(newRot, 0); - return null; - } // if we had [T1][M] we want to transform this into [M][T2] // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] and we can push [T2] // down to the element From 7bfef032aada6d5967c66bd47790f5f2e5597dd3 Mon Sep 17 00:00:00 2001 From: Jeff Schiller Date: Fri, 18 Dec 2009 19:07:01 +0000 Subject: [PATCH 38/38] fixtransforms branch: skewed and rotated groups can now be resized properly git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1070 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/svgcanvas.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 32a27191..8890a61b 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -1781,6 +1781,15 @@ function BatchCommand(text) { tlist.removeItem(N-2); tlist.removeItem(N-3); } + else if (N >= 3 && tlist.getItem(N-1).type == 1) + { + operation = 3; // scale + m = transformListToTransform(tlist).matrix; + var e2t = svgroot.createSVGTransform(); + e2t.setMatrix(m); + tlist.clear(); + tlist.appendItem(e2t); + } // next, check if the first transform was a translate // if we had [ T1 ] [ M ] we want to transform this into [ M ] [ T2 ] // therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ]