fixtransforms branch: Fix resizing of rotated elements
git-svn-id: http://svg-edit.googlecode.com/svn/branches/fixtransforms@1027 eee81c28-f429-11dd-99c0-75d572ba1dddmaster
parent
0182a59d06
commit
4ca4260fc9
|
@ -1400,7 +1400,87 @@ function BatchCommand(text) {
|
||||||
} // for each segment
|
} // for each segment
|
||||||
break;
|
break;
|
||||||
} // switch on element type to get initial values
|
} // 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
|
// this function returns the command which resulted from the selected change
|
||||||
|
@ -1637,7 +1717,7 @@ function BatchCommand(text) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var N = tlist.numberOfItems;
|
var N = tlist.numberOfItems;
|
||||||
var bNeedRemap = false;
|
var operation = 0;
|
||||||
|
|
||||||
// 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 it
|
||||||
// if we had [M][T][S][T] we want to extract the matrix equivalent of
|
// if we had [M][T][S][T] we want to extract the matrix equivalent of
|
||||||
|
@ -1645,313 +1725,73 @@ function BatchCommand(text) {
|
||||||
if (N >= 3 && tlist.getItem(N-2).type == 3 &&
|
if (N >= 3 && tlist.getItem(N-2).type == 3 &&
|
||||||
tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2)
|
tlist.getItem(N-3).type == 2 && tlist.getItem(N-1).type == 2)
|
||||||
{
|
{
|
||||||
|
operation = 3; // scale
|
||||||
m = transformListToTransform(tlist,N-3,N-1).matrix;
|
m = transformListToTransform(tlist,N-3,N-1).matrix;
|
||||||
tlist.removeItem(N-1);
|
tlist.removeItem(N-1);
|
||||||
tlist.removeItem(N-2);
|
tlist.removeItem(N-2);
|
||||||
tlist.removeItem(N-3);
|
tlist.removeItem(N-3);
|
||||||
|
|
||||||
bNeedRemap = true;
|
|
||||||
}
|
}
|
||||||
// if we had [T1][M] we want to transform this into [M][T2]
|
// 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 ]
|
||||||
else 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)
|
tlist.getItem(0).type == 2)
|
||||||
{
|
{
|
||||||
|
operation = 2; // translate
|
||||||
var oldxlate = tlist.getItem(0).matrix,
|
var oldxlate = tlist.getItem(0).matrix,
|
||||||
meq = transformListToTransform(tlist).matrix,
|
meq = transformListToTransform(tlist).matrix,
|
||||||
meq_inv = meq.inverse();
|
meq_inv = meq.inverse();
|
||||||
m = matrixMultiply( meq_inv, oldxlate, meq );
|
m = matrixMultiply( meq_inv, oldxlate, meq );
|
||||||
tlist.removeItem(0);
|
tlist.removeItem(0);
|
||||||
|
}
|
||||||
bNeedRemap = true;
|
else {
|
||||||
|
operation = 4; // rotate
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the shape was rotated, calculate the new center and account for
|
// if the shape was rotated, calculate what the center will be
|
||||||
// the resulting translation in the matrix
|
var xcenter = null;
|
||||||
if (angle) {
|
if (angle) {
|
||||||
// calculate the new center
|
// calculate the new center from the translate
|
||||||
|
if (operation == 2) {
|
||||||
newcenter = transformPoint(center.x,center.y,m);
|
newcenter = transformPoint(center.x,center.y,m);
|
||||||
|
}
|
||||||
// if the centers are different, then determine if the resultant xlate
|
else if (operation == 3) {
|
||||||
var alpha = angle * Math.PI / 180.0;
|
xcenter = transformPoint(center.x,center.y,m);
|
||||||
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) {
|
if (operation == 2 || operation == 3) {
|
||||||
remapElement(selected,changes,m);
|
remapElement(selected,changes,m);
|
||||||
} // if we are remapping
|
} // if we are remapping
|
||||||
|
|
||||||
// if we had a rotation, re-add the rotation with the newcenter
|
// if we had a rotation, re-add the rotation back with its original center
|
||||||
// (this will change if we had a move or a resize)
|
|
||||||
if (angle) {
|
if (angle) {
|
||||||
var newRot = svgroot.createSVGTransform();
|
var newRot = svgroot.createSVGTransform();
|
||||||
newRot.setRotate(angle,newcenter.x,newcenter.y);
|
newRot.setRotate(angle,newcenter.x,newcenter.y);
|
||||||
tlist.insertItemBefore(newRot,0);
|
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);
|
|
||||||
|
|
||||||
var remap = null, scalew = null, scaleh = null;
|
// at this point, the element looks exactly how we want it to look but its
|
||||||
switch (xform.type) {
|
// rotational center may be off in the case of a resize - we need to fix that
|
||||||
case 1: // MATRIX - continue
|
if (angle && operation == 3) {
|
||||||
// newcenter = transformPoint(newcenter.x,newcenter.y,xform.matrix);
|
box = canvas.getBBox(selected);
|
||||||
break;
|
m = transformListToTransform(tlist).matrix;
|
||||||
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
|
// get its actual center
|
||||||
var dx = newcenter.x - origcenter.x,
|
newcenter = transformPoint(box.x+box.width/2, box.y+box.height/2, m);
|
||||||
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;
|
|
||||||
|
|
||||||
dx = cx - newcenter.x;
|
// determine the resultant delta translate to re-center
|
||||||
dy = cy - newcenter.y;
|
var dx = newcenter.x - xcenter.x,
|
||||||
|
dy = newcenter.y - xcenter.y;
|
||||||
|
|
||||||
remap = function(x,y) {
|
var delta = svgroot.createSVGMatrix().translate(dx,dy);
|
||||||
return { x: x + dx, y: y + dy };
|
remapElement(selected,changes,delta);
|
||||||
};
|
|
||||||
scalew = function(w) { return w; }
|
var newRot = svgroot.createSVGTransform();
|
||||||
scaleh = function(h) { return h; }
|
newRot.setRotate(angle,newcenter.x,newcenter.y);
|
||||||
|
tlist.replaceItem(newRot,0);
|
||||||
}
|
}
|
||||||
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);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
} // a non-group
|
} // 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 the transform list has been emptied, remove it
|
||||||
if (tlist.numberOfItems == 0) {
|
if (tlist.numberOfItems == 0) {
|
||||||
selected.removeAttribute("transform");
|
selected.removeAttribute("transform");
|
||||||
|
|
Loading…
Reference in New Issue