diff --git a/editor/images/svg_edit_icons.svg b/editor/images/svg_edit_icons.svg index f49e173b..59f0e09d 100644 --- a/editor/images/svg_edit_icons.svg +++ b/editor/images/svg_edit_icons.svg @@ -533,6 +533,16 @@ + + + + + + + + + + diff --git a/editor/svg-editor.html b/editor/svg-editor.html index cdb2c87f..ff533c4f 100644 --- a/editor/svg-editor.html +++ b/editor/svg-editor.html @@ -232,6 +232,9 @@ script type="text/javascript" src="locale/locale.min.js">
+
+ +
diff --git a/editor/svg-editor.js b/editor/svg-editor.js index 551031ff..8203d2d0 100644 --- a/editor/svg-editor.js +++ b/editor/svg-editor.js @@ -364,8 +364,10 @@ function svg_edit_setup() { // Get BBox vals for g, polyline and path if($.inArray(elname, ['g', 'polyline', 'path']) != -1) { var bb = svgCanvas.getStrokedBBox([elem]); - x = bb.x; - y = bb.y; + if(bb) { + x = bb.x; + y = bb.y; + } } else { x = elem.getAttribute('x'); y = elem.getAttribute('y'); @@ -900,6 +902,12 @@ function svg_edit_setup() { } }; + var linkControlPoints = function() { + $('#tool_node_link').toggleClass('push_button_pressed'); + var linked = $('#tool_node_link').hasClass('push_button_pressed'); + svgCanvas.linkControlPoints(linked); + } + var clonePathNode = function() { if (svgCanvas.getNodePoint()) { svgCanvas.clonePathNode(); @@ -1012,7 +1020,7 @@ function svg_edit_setup() { } var clickWireframe = function() { - $('#tool_wireframe').toggleClass('push_button_pressed'); + $('#tool_wireframe').toggleClass('push_button_pressed'); $('#workarea').toggleClass('wireframe'); if(supportsNonSS) return; @@ -1380,6 +1388,7 @@ function svg_edit_setup() { $('#tool_delete').click(deleteSelected); $('#tool_delete_multi').click(deleteSelected); $('#tool_reorient').click(reorientPath); + $('#tool_node_link').click(linkControlPoints); $('#tool_node_clone').click(clonePathNode); $('#tool_node_delete').click(deletePathNode); $('#tool_move_top').click(moveToTopSelected); @@ -2208,6 +2217,7 @@ function setSVGIcons() { '#tool_move_top':'move_top', '#tool_move_bottom':'move_bottom', '#tool_topath':'to_path', + '#tool_node_link':'link_controls', '#tool_reorient':'reorient', '#tool_group':'group', '#tool_ungroup':'ungroup', diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 9fa6ba86..aaa9a769 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -861,6 +861,7 @@ function BatchCommand(text) { var current_path_pt_drag = -1; var current_path_oldd = null; var current_ctrl_pt_drag = -1; + var link_control_pts = false; var current_zoom = 1; // this will hold all the currently selected elements // default size of 1 until it needs to grow bigger @@ -1928,7 +1929,7 @@ function BatchCommand(text) { // Currently only one node can be selected at a time, should allow more later // Should point be the index or the grip element? - var is_closed = current_path.getAttribute('d').toLowerCase().indexOf('z') != -1; + var is_closed = pathIsClosed(); if(is_closed && point == current_path_pts.length/2 - 1) { current_path_pt = 0; @@ -2647,40 +2648,39 @@ function BatchCommand(text) { } else if (current_ctrl_pt_drag != -1 && current_path) { // Moving the control point. Since only one segment is altered, // we only need to do a pathSegList replace. + var data = current_ctrl_pt_drag.split('c'); var index = data[0]-0; var ctrl_num = data[1]-0; - var c_item = current_path.pathSegList.getItem(index+1); - - var angle = canvas.getRotationAngle(current_path) * Math.PI / 180.0; - if (angle) { - // calculate the shape's old center that was used for rotation - var box = selectedBBoxes[0]; - var cx = round(box.x + box.width/2) * current_zoom, - cy = round(box.y + box.height/2) * current_zoom; - var dx = mouse_x - cx, dy = mouse_y - cy; - var r = Math.sqrt( dx*dx + dy*dy ); - var theta = Math.atan2(dy,dx) - angle; - current_path_pts[i] = mouse_x = cx + r * Math.cos(theta); - current_path_pts[i+1] = mouse_y = cy + r * Math.sin(theta); - x = mouse_x / current_zoom; - y = mouse_y / current_zoom; - } - - c_item['x' + ctrl_num] = x; - c_item['y' + ctrl_num] = y; - replacePathSeg(6, index+1, [c_item.x,c_item.y, c_item.x1,c_item.y1, c_item.x2,c_item.y2]); - - updateSegLine(true); - - var grip = document.getElementById("ctrlpointgrip_" + current_ctrl_pt_drag); - if(grip) { - grip.setAttribute("cx", mouse_x); - grip.setAttribute("cy", mouse_y); + var pt_index; + var pt_count = current_path_pts.length/2; + updateCurvedSegment(mouse_x, mouse_y, index, ctrl_num); + if(link_control_pts) { + var is_closed = pathIsClosed(); + if(ctrl_num == 1) { + ctrl_num = 2; + index--; + pt_index = index+1; + + if(index < 0) { + index = pt_count - 2; + if(!is_closed) break; + } + } else { + ctrl_num = 1; + index++; + pt_index = index; + + if(index >= pt_count - 1) { + index = 0; + if(!is_closed) break; + } + } - var line = document.getElementById("ctrlLine_"+current_ctrl_pt_drag); - line.setAttribute("x2", mouse_x); - line.setAttribute("y2", mouse_y); + var pt = getPathPoint(pt_index, true); + var new_x = pt[0] - (mouse_x - pt[0]); + var new_y = pt[1] - (mouse_y - pt[1]); + updateCurvedSegment(new_x, new_y, index, ctrl_num, true); } } break; @@ -3203,6 +3203,11 @@ function BatchCommand(text) { }); }; + var pathIsClosed = function() { + if(!current_path) return; + return current_path.getAttribute('d').substr(-1,1).toLowerCase() == 'z'; + } + var updateSegLine = function(next_node) { // create segment line var segLine = document.getElementById("segline"); @@ -3247,7 +3252,7 @@ function BatchCommand(text) { var x = mouse_x / current_zoom; var y = mouse_y / current_zoom; - var is_closed = current_path.getAttribute('d').toLowerCase().indexOf('z') != -1; + var is_closed = pathIsClosed(); var i = current_path_pt_drag * 2; var last_index = current_path_pts.length/2 - 1; @@ -3257,7 +3262,7 @@ function BatchCommand(text) { // if the image is rotated, then we must modify the x,y mouse coordinates // and rotate them into the shape's rotated coordinate system // we also re-map mouse_x/y and x/y into the rotated coordinate system - var angle = canvas.getRotationAngle(current_path) * Math.PI / 180.0; + var angle = canvas.getRotationAngle(current_path, true); if (angle) { // calculate the shape's old center that was used for rotation var box = selectedBBoxes[0]; @@ -3392,6 +3397,55 @@ function BatchCommand(text) { } } + var updateCurvedSegment = function(mouse_x, mouse_y, index, ctrl_num) { + var list = current_path.pathSegList; + if(index+1 >= list.numberOfItems) { + index = -1; + } + var c_item = list.getItem(index+1); + + // Only do curves + if(c_item.pathSegType != 6) return; + + ctrl_pt_drag = index + 'c' + ctrl_num; + + var x = mouse_x / current_zoom; + var y = mouse_y / current_zoom; + + var angle = canvas.getRotationAngle(current_path, true); + + // TODO: Make sure this works for linked control points + if (angle) { + // calculate the shape's old center that was used for rotation + var box = selectedBBoxes[0]; + var cx = round(box.x + box.width/2) * current_zoom, + cy = round(box.y + box.height/2) * current_zoom; + var dx = mouse_x - cx, dy = mouse_y - cy; + var r = Math.sqrt( dx*dx + dy*dy ); + var theta = Math.atan2(dy,dx) - angle; + mouse_x = cx + r * Math.cos(theta); + mouse_y = cy + r * Math.sin(theta); + x = mouse_x / current_zoom; + y = mouse_y / current_zoom; + } + + c_item['x' + ctrl_num] = x; + c_item['y' + ctrl_num] = y; + replacePathSeg(6, index+1, [c_item.x,c_item.y, c_item.x1,c_item.y1, c_item.x2,c_item.y2]); + + updateSegLine(true); + + var grip = document.getElementById("ctrlpointgrip_" + ctrl_pt_drag); + if(grip) { + grip.setAttribute("cx", mouse_x); + grip.setAttribute("cy", mouse_y); + + var line = document.getElementById("ctrlLine_"+ctrl_pt_drag); + line.setAttribute("x2", mouse_x); + line.setAttribute("y2", mouse_y); + } + } + var getPathPoint = function(index, raw_val) { var len = current_path_pts.length; var pt_num = len/2; @@ -3791,7 +3845,7 @@ function BatchCommand(text) { // Every path point must be rotated into the rotated coordinate system of // its old center, then determine the new center, then rotate it back // This is because we want the path to remember its rotation - var angle = canvas.getRotationAngle(current_path) * Math.PI / 180.0; + var angle = canvas.getRotationAngle(current_path, true); if (angle) { var box = canvas.getBBox(current_path); @@ -3875,7 +3929,7 @@ function BatchCommand(text) { call("changed", [current_path]); // If connected, last point should equal first - if(current_path.getAttribute('d').toLowerCase().indexOf('z') != -1) { + if(pathIsClosed()) { current_path_pts[current_path_pts.length-2] = getPathPoint(0,true)[0]; current_path_pts[current_path_pts.length-1] = getPathPoint(0,true)[1]; } @@ -4556,6 +4610,10 @@ function BatchCommand(text) { } } + this.linkControlPoints = function(linkPoints) { + link_control_pts = linkPoints; + } + this.clonePathNode = function() { var pt = current_path_pt, list = current_path.pathSegList; @@ -5045,7 +5103,7 @@ function BatchCommand(text) { }; // TODO: do we need to sum up all rotation angles? - this.getRotationAngle = function(elem) { + this.getRotationAngle = function(elem, to_rad) { var selected = elem || selectedElements[0]; // find the rotation transform (if any) and set it var tlist = canvas.getTransformList(selected); @@ -5053,7 +5111,7 @@ function BatchCommand(text) { while (t--) { var xform = tlist.getItem(t); if (xform.type == 4) { - return xform.angle; + return to_rad ? xform.angle * Math.PI / 180.0 : xform.angle; } } return 0; @@ -5206,7 +5264,7 @@ function BatchCommand(text) { var index = grip[0].id.split('_')[1] - 0; var last_index = current_path_pts.length/2 - 1; - var is_closed = current_path.getAttribute('d').toLowerCase().indexOf('z') != -1; + var is_closed = pathIsClosed(); if(!is_closed && index == last_index) { return; // Last point of unclosed path should do nothing @@ -5346,10 +5404,10 @@ function BatchCommand(text) { // var box=canvas.getBBox(elem), left=box.x, top=box.y, width=box.width, // height=box.height, dx = width - old_w, dy=0; -// var angle = canvas.getRotationAngle(elem); +// var angle = canvas.getRotationAngle(elem, true); // if (angle) { // var r = Math.sqrt( dx*dx + dy*dy ); -// var theta = Math.atan2(dy,dx) - angle * Math.PI / 180.0; +// var theta = Math.atan2(dy,dx) - angle; // dx = r * Math.cos(theta); // dy = r * Math.sin(theta); // @@ -5497,7 +5555,7 @@ function BatchCommand(text) { var gbox = g.getBBox(), gx = gbox.x + gbox.width/2, gy = gbox.y + gbox.height/2; - var gangle = canvas.getRotationAngle(g) * Math.PI / 180.0; + var gangle = canvas.getRotationAngle(g, true); while (g.firstChild) { var elem = g.firstChild; var oldNextSibling = elem.nextSibling; @@ -5519,7 +5577,7 @@ function BatchCommand(text) { // now we add the angle that the element was rotated by // if it's non-zero, we need to set the new transform // otherwise, we clear it - var angle = gangle + canvas.getRotationAngle(elem) * Math.PI / 180.0; + var angle = gangle + canvas.getRotationAngle(elem, true); var changes = {}; changes["transform"] = elem.getAttribute("transform"); if (angle != 0) {