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
master
Jeff Schiller 2009-12-10 05:11:09 +00:00
parent ed45a7b2fa
commit 32f23b69af
1 changed files with 67 additions and 108 deletions

View File

@ -11,8 +11,14 @@
/* /*
TODOs for TransformList: TODOs for TransformList:
* Fix rotating groups turning into matrix() * Maybe:
* Fix problem when moving elements that have [R][M] * 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 * Fix problem when ungrouping rotated elements that were scaled in a group
* When ungrouping, always end up with a single [M] * 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 // this function returns the command which resulted from the selected change
// TODO: use suspendRedraw() and unsuspendRedraw() around this function // TODO: use suspendRedraw() and unsuspendRedraw() around this function
var recalculateDimensions = function(selected) { var recalculateDimensions = function(selected) {
if (selected == null) return null; if (true || selected == null) return null;
var tlist = canvas.getTransformList(selected); 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 it's a group, we have special processing to flatten transforms
if (selected.tagName == "g") { if (selected.tagName == "g") {
console.log("recalculateDimensions(" + selected.tagName + ")");
var tx = 0, ty = 0; var tx = 0, ty = 0;
var N = tlist.numberOfItems; var N = tlist.numberOfItems;
@ -1441,42 +1448,27 @@ function BatchCommand(text) {
var tm = tlist.getItem(N-3).matrix; var tm = tlist.getItem(N-3).matrix;
var sm = tlist.getItem(N-2).matrix; var sm = tlist.getItem(N-2).matrix;
var tmn = tlist.getItem(N-1).matrix; var tmn = tlist.getItem(N-1).matrix;
var em = matrixMultiply(tm, sm, tmn);
var children = selected.childNodes; var children = selected.childNodes;
var c = children.length; var c = children.length;
while (c--) { while (c--) {
console.log("child #" + c);
var child = children.item(c); var child = children.item(c);
tx = 0;
ty = 0;
if (child.nodeType == 1) { if (child.nodeType == 1) {
var childTlist = canvas.getTransformList(child); var childTlist = canvas.getTransformList(child);
var m = transformListToTransform(childTlist).matrix; var m = transformListToTransform(childTlist).matrix;
var angle = canvas.getRotationAngle(child); var angle = canvas.getRotationAngle(child);
if(angle) { if(angle) {
// TODO: this does not work yet var em = matrixMultiply(tm, sm, tmn);
/*
// 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);
// this does not appear to work, something wrong with my logic here // this does not appear to work, something wrong with my logic here
var e2t = svgroot.createSVGTransform(); var e2t = svgroot.createSVGTransform();
e2t.setMatrix(e2); e2t.setMatrix(em);
childTlist.appendItem(e2t); childTlist.insertItemBefore(e2t,0);
// alert(child.getAttribute("transform"));
// 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 { else {
// update the transform list with translate,scale,translate // update the transform list with translate,scale,translate
@ -1505,10 +1497,10 @@ function BatchCommand(text) {
childTlist.appendItem(translateBack); childTlist.appendItem(translateBack);
childTlist.appendItem(scale); childTlist.appendItem(scale);
childTlist.appendItem(translateOrigin); childTlist.appendItem(translateOrigin);
} batchCmd.addSubCommand( recalculateDimensions(child) );
batchCmd.addSubCommand( recalculateDimensions(child) ); } // not rotated
} } // element
} } // for each child
// Remove these transforms from group // Remove these transforms from group
tlist.removeItem(N-1); tlist.removeItem(N-1);
tlist.removeItem(N-2); tlist.removeItem(N-2);
@ -1524,7 +1516,6 @@ function BatchCommand(text) {
tlist.getItem(0).type == 2) tlist.getItem(0).type == 2)
{ {
var T_M = transformListToTransform(tlist).matrix; var T_M = transformListToTransform(tlist).matrix;
logMatrix(T_M);
tlist.removeItem(0); tlist.removeItem(0);
var M_inv = transformListToTransform(tlist).matrix.inverse(); var M_inv = transformListToTransform(tlist).matrix.inverse();
logMatrix(M_inv); logMatrix(M_inv);
@ -1533,10 +1524,27 @@ function BatchCommand(text) {
tx = M2.e; tx = M2.e;
ty = M2.f; ty = M2.f;
}
// rotate? if (tx != 0 || ty != 0) {
else { // now push this transform down to the children
console.log('rotate?'); // 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, it's a non-group
else { else {
console.log("recalculateDimensions(" + selected.tagName + ")");
var box = canvas.getBBox(selected); var box = canvas.getBBox(selected);
var origcenter = {x: (box.x+box.width/2), y: (box.y+box.height/2)}; var origcenter = {x: (box.x+box.width/2), y: (box.y+box.height/2)};
var newcenter = {x: origcenter.x, y: origcenter.y}; var newcenter = {x: origcenter.x, y: origcenter.y};
var rotAngle = 0; 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. // This pass loop in reverse order and removes any translates or scales.
// Once we hit our first rotate(), we will only remove translates. // Once we hit our first rotate(), we will only remove translates.
n = tlist.numberOfItems; n = tlist.numberOfItems;
@ -1594,9 +1586,11 @@ function BatchCommand(text) {
m = matrixMultiply(tail_inv, xform.matrix, tail); m = matrixMultiply(tail_inv, xform.matrix, tail);
var remap = null, scalew = null, scaleh = null; var remap = null, scalew = null, scaleh = null;
console.log("xform.type=" + xform.type);
switch (xform.type) { switch (xform.type) {
case 1: // MATRIX - continue case 1: // MATRIX - continue
continue; // newcenter = transformPoint(newcenter.x,newcenter.y,xform.matrix);
break;
case 2: // TRANSLATE - always remove case 2: // TRANSLATE - always remove
remap = function(x,y) { return transformPoint(x,y,m); }; remap = function(x,y) { return transformPoint(x,y,m); };
scalew = function(w) { return w; } scalew = function(w) { return w; }
@ -1611,6 +1605,7 @@ function BatchCommand(text) {
// if the new center of the shape has moved, then // if the new center of the shape has moved, then
// re-center the rotation, and determine the movement // re-center the rotation, and determine the movement
// offset required to keep the shape in the same place // offset required to keep the shape in the same place
// if (n != 0) continue;
rotAngle = xform.angle; rotAngle = xform.angle;
if (origcenter.x != newcenter.x || origcenter.y != newcenter.y) { if (origcenter.x != newcenter.x || origcenter.y != newcenter.y) {
var alpha = xform.angle * Math.PI / 180.0; 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? // throwing an exception - perhaps an update was issued in SVG 1.1 2e?
var matrixMultiply = function() { var matrixMultiply = function() {
var multi2 = function(m1, m2) { 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 m = svgroot.createSVGMatrix();
var sx = 1, sy = 1, angle = 0; m.a = m1.a*m2.a + m1.c*m2.b;
m.b = m1.b*m2.a + m1.d*m2.b,
// translate m.c = m1.a*m2.c + m1.c*m2.d,
m = m.translate(e,f); m.d = m1.b*m2.c + m1.d*m2.d,
m.e = m1.a*m2.e + m1.c*m2.f + m1.e,
// see if there was a rotation m.f = m1.b*m2.e + m1.d*m2.f + m1.f;
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; return m;
} }
@ -5183,21 +5151,13 @@ function BatchCommand(text) {
var selected = elem || selectedElements[0]; var selected = elem || selectedElements[0];
// find the rotation transform (if any) and set it // find the rotation transform (if any) and set it
var tlist = canvas.getTransformList(selected); var tlist = canvas.getTransformList(selected);
var t = tlist.numberOfItems; if (tlist.numberOfItems > 0) {
var sangle = 0; var xform = tlist.getItem(0);
while (t--) {
var xform = tlist.getItem(t);
// rotation transform
if (xform.type == 4) { 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: // 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 cx = round(bbox.x+bbox.width/2), cy = round(bbox.y+bbox.height/2);
var tlist = canvas.getTransformList(elem); var tlist = canvas.getTransformList(elem);
// remove the rotation transform // only remove the real rotational transform if present (i.e. at index=0)
var n = tlist.numberOfItems; if (tlist.numberOfItems > 0) {
while (n--) { var xform = tlist.getItem(0);
if (tlist.getItem(n).type == 4) { if (xform.type == 4) {
tlist.removeItem(n); tlist.removeItem(0);
} }
} }
@ -5224,7 +5184,6 @@ function BatchCommand(text) {
var center = transformPoint(cx,cy,transformListToTransform(tlist).matrix); var center = transformPoint(cx,cy,transformListToTransform(tlist).matrix);
var R_nc = svgroot.createSVGTransform(); var R_nc = svgroot.createSVGTransform();
R_nc.setRotate(val, center.x, center.y); R_nc.setRotate(val, center.x, center.y);
tlist.insertItemBefore(R_nc,0); tlist.insertItemBefore(R_nc,0);
if (!preventUndo) { if (!preventUndo) {