Merge 422:442 from rotator branch into trunk

git-svn-id: http://svg-edit.googlecode.com/svn/trunk@443 eee81c28-f429-11dd-99c0-75d572ba1ddd
master
Jeff Schiller 2009-08-21 15:53:36 +00:00
parent 841d82e0a4
commit f0e1b6a57a
2 changed files with 52 additions and 176 deletions

View File

@ -877,7 +877,7 @@ function svg_edit_setup() {
$('#rect_rx').SpinButton({ min: 0, max: 1000, step: 1, callback: changeRectRadius }); $('#rect_rx').SpinButton({ min: 0, max: 1000, step: 1, callback: changeRectRadius });
$('#stroke_width').SpinButton({ min: 1, max: 99, step: 1, callback: changeStrokeWidth }); $('#stroke_width').SpinButton({ min: 1, max: 99, step: 1, callback: changeStrokeWidth });
$('#angle').SpinButton({ min: -359, max: 359, step: 5, callback: changeRotationAngle }); $('#angle').SpinButton({ min: -180, max: 180, step: 5, callback: changeRotationAngle });
// if Opera and in widget form, enable the Open button // if Opera and in widget form, enable the Open button
if (window.opera) { if (window.opera) {

View File

@ -1,3 +1,14 @@
/*
TODOs for Rotator:
- resize rotated elements must work properly with the selector (aligned to the axis of rotation)
- show the proper resize cursor based on the rotation
- add a rotator line/handle to the selector group
- respond to mouse down on the rotator handle to start 'rotate' mode
- respond to mouse move in rotate mode to change the rotation of the element
- upon mouse up in rotate mode go back to select mode
*/
if(!window.console) { if(!window.console) {
window.console = new function() { window.console = new function() {
this.log = function(str) {}; this.log = function(str) {};
@ -248,9 +259,7 @@ function SvgCanvas(c)
} }
}; };
// TODO: determine rotation angle this.resize = function(cur_bbox) {
this.resize = function(bbox) {
var selectedBox = this.selectorRect; var selectedBox = this.selectorRect;
var selectedGrips = this.selectorGrips; var selectedGrips = this.selectorGrips;
var selected = this.selectedElement; var selected = this.selectedElement;
@ -262,7 +271,7 @@ function SvgCanvas(c)
if (selected.tagName == "text") { if (selected.tagName == "text") {
offset += 2; offset += 2;
} }
var bbox = bbox || canvas.getBBox(this.selectedElement); var bbox = cur_bbox || canvas.getBBox(this.selectedElement);
var l=bbox.x-offset, t=bbox.y-offset, w=bbox.width+(offset<<1), h=bbox.height+(offset<<1); var l=bbox.x-offset, t=bbox.y-offset, w=bbox.width+(offset<<1), h=bbox.height+(offset<<1);
// TODO: use suspendRedraw() here // TODO: use suspendRedraw() here
selectedBox.setAttribute("x", l); selectedBox.setAttribute("x", l);
@ -285,6 +294,21 @@ function SvgCanvas(c)
selectedGrips.e.setAttribute("y", t+h/2-3); selectedGrips.e.setAttribute("y", t+h/2-3);
selectedGrips.s.setAttribute("x", l+w/2-3); selectedGrips.s.setAttribute("x", l+w/2-3);
selectedGrips.s.setAttribute("y", t+h-3); selectedGrips.s.setAttribute("y", t+h-3);
// empty out the transform attribute
this.selectorGroup.setAttribute("transform", "");
this.selectorGroup.removeAttribute("transform");
// align selector group with element coordinate axes
var transform = this.selectedElement.getAttribute("transform");
if (transform && transform != "") {
// this.selectorGroup.setAttribute("transform", transform);
var rotind = transform.indexOf("rotate(");
if (rotind != -1) {
var rotstr = transform.substr(rotind, transform.indexOf(')',rotind)+1);
this.selectorGroup.setAttribute("transform", rotstr);
}
}
}; };
// now initialize the selector // now initialize the selector
@ -1253,6 +1277,7 @@ function SvgCanvas(c)
if (selectedElements[0] != null) { if (selectedElements[0] != null) {
var dx = x - start_x; var dx = x - start_x;
var dy = y - start_y; var dy = y - start_y;
if (dx != 0 || dy != 0) { if (dx != 0 || dy != 0) {
var ts = ["translate(",dx,",",dy,")"].join(''); var ts = ["translate(",dx,",",dy,")"].join('');
var len = selectedElements.length; var len = selectedElements.length;
@ -1261,16 +1286,23 @@ function SvgCanvas(c)
if (selected == null) break; if (selected == null) break;
var box = canvas.getBBox(selected); var box = canvas.getBBox(selected);
selectedBBoxes[i].x = box.x + dx;
selectedBBoxes[i].y = box.y + dy;
var angle = canvas.getRotationAngle(selected); var angle = canvas.getRotationAngle(selected);
if (angle) { if (angle) {
var cx = box.x+box.width/2, var cx = box.x+box.width/2,
cy = box.y+box.height/2; cy = box.y+box.height/2;
ts += [" rotate(", angle, " ", cx, ",", cy, ")"].join(''); ts += [" rotate(", angle, " ", cx, ",", cy, ")"].join('');
var r = Math.sqrt( dx*dx + dy*dy );
var theta = Math.atan2(dy,dx) - angle * Math.PI / 180.0;
dx = r * Math.cos(theta);
dy = r * Math.sin(theta);
} }
selected.setAttribute("transform", ts); selected.setAttribute("transform", ts);
// update our internal bbox that we're tracking while dragging
box.x += dx; box.y += dy; box.x += dx; box.y += dy;
selectorManager.requestSelector(selected).resize(box); selectorManager.requestSelector(selected).resize(box);
selectedBBoxes[i] = box;
} }
} }
} }
@ -1315,13 +1347,22 @@ function SvgCanvas(c)
*/ */
break; break;
case "resize": case "resize":
// TODO: update this to handle rotated elements
// we track the resize bounding box and translate/scale the selected element // 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 // while the mouse is down, when mouse goes up, we use this to recalculate
// the shape's coordinates // the shape's coordinates
var box=canvas.getBBox(selected), left=box.x, top=box.y, width=box.width, var box=canvas.getBBox(selected), left=box.x, top=box.y, width=box.width,
height=box.height, dx=(x-start_x), dy=(y-start_y); height=box.height, dx=(x-start_x), dy=(y-start_y);
// if rotated, adjust the dx,dy values
console.log(box);
var angle = canvas.getRotationAngle(selected) * Math.PI / 180.0;
if (angle) {
var r = Math.sqrt( dx*dx + dy*dy );
var theta = Math.atan2(dy,dx) - angle;
dx = r * Math.cos(theta);
dy = r * Math.sin(theta);
}
var tx=0, ty=0, sx=1, sy=1; var tx=0, ty=0, sx=1, sy=1;
var ts = null; var ts = null;
if(current_resize_mode.indexOf("n") != -1) { if(current_resize_mode.indexOf("n") != -1) {
@ -2222,172 +2263,7 @@ function SvgCanvas(c)
this.getBBox = function(elem) { this.getBBox = function(elem) {
var selected = elem || selectedElements[0]; var selected = elem || selectedElements[0];
// get the bounding box from the DOM (which is in that element's coordinate system) // get the bounding box from the DOM (which is in that element's coordinate system)
var bbox = selected.getBBox(); return selected.getBBox();
// determine the bounding box if rotated
var angle = this.getRotationAngle(selected) * Math.PI / 180.0;
if (angle) {
switch(selected.tagName) {
// rotating a circle has no effect on its bounding box
case "circle":
break;
case "ellipse":
// For ellipse, I'm completely following the math from here:
// http://stackoverflow.com/questions/87734/how-do-you-calculate-the-axis-aligned-bounding-box-of-an-ellipse
// x(t) = h + a*cos(t)*cos(angle) - b*sin(t)*sin(angle)
// y(t) = k + b*sin(t)*cos(angle) + a*cos(t)*sin(angle)
var a = parseFloat(selected.getAttribute("rx")),
b = parseFloat(selected.getAttribute("ry")),
h = parseFloat(selected.getAttribute("cx")),
k = parseFloat(selected.getAttribute("cy"));
var tx = Math.atan( -b * Math.tan(angle)/a ),
ty = Math.atan( b * (1.0 / Math.tan(angle))/a );
var X = new Array(2), Y = new Array(2);
X[0] = h + a*Math.cos(tx)*Math.cos(angle) - b*Math.sin(tx)*Math.sin(angle);
Y[0] = k + b*Math.sin(ty)*Math.cos(angle) + a*Math.cos(ty)*Math.sin(angle);
// get other values
tx += Math.PI;
ty += Math.PI;
X[1] = h + a*Math.cos(tx)*Math.cos(angle) - b*Math.sin(tx)*Math.sin(angle);
Y[1] = k + b*Math.sin(ty)*Math.cos(angle) + a*Math.cos(ty)*Math.sin(angle);
if (X[1] < X[0]) { var temp = X[1]; X[1] = X[0]; X[0] = temp; }
if (Y[1] < Y[0]) { var temp = Y[1]; Y[1] = Y[0]; Y[0] = temp; }
bbox.x = parseInt(X[0]);
bbox.y = parseInt(Y[0]);
bbox.width = parseInt( X[1] - X[0] );
bbox.height = parseInt( Y[1] - Y[0] );
break;
case "polygon":
case "polyline":
var MINX = Number.MAX_VALUE, MINY = Number.MAX_VALUE,
MAXX = Number.MIN_VALUE, MAXY = Number.MIN_VALUE;
// calculate the cx,cy (pull from transform attr?)
var cx = bbox.x + bbox.width/2,
cy = bbox.y + bbox.height/2;
console.log('cx=' + cx + ', cy=' + cy);
var list = selected.points;
var i = list.numberOfItems;
while (i--) {
var pt = list.getItem(i);
var dx = pt.x - cx, dy = pt.y - cy;
var r = Math.sqrt( dx*dx + dy*dy );
var theta = angle + Math.atan2(dy,dx);
var x = r * Math.cos(theta), y = r * Math.sin(theta);
if (MINX > x) { MINX = x; }
if (MINY > y) { MINY = y; }
if (MAXX < x) { MAXX = x; }
if (MAXY < y) { MAXY = y; }
}
bbox.x = parseInt(cx + MINX);
bbox.y = parseInt(cy + MINY);
bbox.width = parseInt(MAXX-MINX);
bbox.height = parseInt(MAXY-MINY);
break;
case "path":
var MINX = Number.MAX_VALUE, MINY = Number.MAX_VALUE,
MAXX = Number.MIN_VALUE, MAXY = Number.MIN_VALUE;
// calculate the cx,cy (pull from transform attr?)
var cx = bbox.x + bbox.width/2,
cy = bbox.y + bbox.height/2;
var segList = selected.pathSegList;
var len = segList.numberOfItems;
var curx = 0, cury = 0;
for (var i = 0; i < len; ++i) {
var seg = segList.getItem(i);
// if these properties are not in the segment, set them to zero
var x = seg.x || 0,
y = seg.y || 0;
var type = seg.pathSegType;
switch (type) {
case 1: // z,Z closepath (Z/z)
x = curx;
y = cury;
break;
// turn this into a relative segment then fall through
case 2: // absolute move (M)
case 4: // absolute line (L)
case 6: // absolute cubic (C)
case 8: // absolute quad (Q)
case 10: // absolute elliptical arc (A)
case 12: // absolute horizontal line (H)
case 14: // absolute vertical line (V)
case 16: // absolute smooth cubic (S)
case 18: // absolute smooth quad (T)
curx = x;
cury = y;
break;
case 3: // relative move (m)
case 5: // relative line (l)
case 7: // relative cubic (c)
case 9: // relative quad (q)
case 13: // relative horizontal line (h)
case 15: // relative vertical line (v)
case 19: // relative smooth quad (t)
case 11: // relative elliptical arc (a)
case 17: // relative smooth cubic (s)
curx += x;
cury += y;
x = curx;
y = cury;
break;
}
var dx = x - cx, dy = y - cy;
var r = Math.sqrt( dx*dx + dy*dy );
var theta = angle + Math.atan2(dy,dx);
x = r * Math.cos(theta);
y = r * Math.sin(theta);
if (MINX > x) { MINX = x; }
if (MINY > y) { MINY = y; }
if (MAXX < x) { MAXX = x; }
if (MAXY < y) { MAXY = y; }
}
bbox.x = parseInt(cx + MINX);
bbox.y = parseInt(cy + MINY);
bbox.width = parseInt(MAXX-MINX);
bbox.height = parseInt(MAXY-MINY);
break;
// TODO: handle path here by iterating through all points
// transforming each point into the rotated coordinate system and tracking
// the MINX, MAXX, MINY, MAXY
// default case for rect, text, line is to just use the bbox from the DOM
default:
var w2 = bbox.width/2, h2 = bbox.height/2,
cx = bbox.x + w2, cy = bbox.y + h2;
var pts = [ [-w2,-h2], [w2,-h2], [w2,h2], [-w2,h2] ];
var r = Math.sqrt( w2*w2 + h2*h2 );
var i = 4;
var MINX = Number.MAX_VALUE, MINY = Number.MAX_VALUE,
MAXX = Number.MIN_VALUE, MAXY = Number.MIN_VALUE;
while (i--) {
var theta = angle + Math.atan2(pts[i][1],pts[i][0]);
var x = r * Math.cos(theta), y = r * Math.sin(theta);
if (MINX > x) { MINX = x; }
if (MINY > y) { MINY = y; }
if (MAXX < x) { MAXX = x; }
if (MAXY < y) { MAXY = y; }
}
bbox.x = parseInt(cx + MINX);
bbox.y = parseInt(cy + MINY);
bbox.width = parseInt(MAXX-MINX);
bbox.height = parseInt(MAXY-MINY);
break;
} // switch on element type
}
return bbox;
}; };
this.getRotationAngle = function(elem) { this.getRotationAngle = function(elem) {