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:
* 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) {