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-75d572ba1dddmaster
parent
ed45a7b2fa
commit
32f23b69af
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue