diff --git a/editor/svg-editor.js b/editor/svg-editor.js index 4518ff4e..e871a587 100644 --- a/editor/svg-editor.js +++ b/editor/svg-editor.js @@ -246,7 +246,8 @@ function svg_edit_setup() { } // Update selectedElement if element is no longer part of the image. // This occurs for the text elements in Firefox - else if(elem && selectedElement && selectedElement.parentNode == null) { + else if(elem && selectedElement && selectedElement.parentNode == null + || elem && elem.tagName == "path") { selectedElement = elem; } } @@ -738,7 +739,6 @@ function svg_edit_setup() { if(elem != null && !elem.parentNode) elem = null; var currentLayer = svgCanvas.getCurrentLayer(); var currentMode = svgCanvas.getMode(); - // No need to update anything else in rotate mode if (currentMode == 'rotate' && elem != null) { var ang = svgCanvas.getRotationAngle(elem); @@ -747,7 +747,6 @@ function svg_edit_setup() { return; } var is_node = currentMode == 'pathedit'; //elem ? (elem.id && elem.id.indexOf('pathpointgrip') == 0) : false; - $('#selected_panel, #multiselected_panel, #g_panel, #rect_panel, #circle_panel,\ #ellipse_panel, #line_panel, #text_panel, #image_panel').hide(); if (elem != null) { diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index 75f967e0..ff89c898 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -3716,94 +3716,714 @@ function BatchCommand(text) { var pathActions = function() { + var pathData = {}; + var current_path; + var path; + var segData = { + 2: ['x','y'], + 4: ['x','y'], + 6: ['x','y','x1','y1','x2','y2'], + 8: ['x','y','x1','y1'], + 10: ['x','y','r1','r2','angle','largeArcFlag','sweepFlag'], + 12: ['x'], + 14: ['y'] + }; + + function retPath() { + return path; + } + + // TODO: See if this should just live in replacePathSeg + function ptObjToArr(type, seg_item) { + var arr = segData[type], len = arr.length; + var out = Array(len); + for(var i=0; i= len ? null : segs[i+1]; + var prev_seg = (i-1) < 0 ? null : segs[i-1]; + + if(seg.type === 2) { + if(prev_seg && prev_seg.type !== 1) { + // New sub-path, last one is open, + // so add a grip to last sub-path's first point + var start_seg = segs[start_i]; + start_seg.next = segs[start_i+1]; + start_seg.next.prev = start_seg; + start_seg.addGrip(); + } + // Remember that this is a starter seg + start_i = i; + } else if(next_seg && next_seg.type === 1) { + // This is the last real segment of a closed sub-path + // Next is first seg after "M" + seg.next = segs[start_i+1]; + + // First seg after "M"'s prev is this + seg.next.prev = seg; + seg.mate = segs[start_i]; + seg.addGrip(); + if(!p.first_seg) { + p.first_seg = seg; + } + } else if(!next_seg) { + if(seg.type !== 1) { + // Last seg, doesn't close so add a grip + // to last sub-path's first point + var start_seg = segs[start_i]; + start_seg.next = segs[start_i+1]; + start_seg.next.prev = start_seg; + start_seg.addGrip(); + seg.addGrip(); + + if(!p.first_seg) { + // Open path, so set first as real first and add grip + p.first_seg = segs[start_i]; + } + } + } else if(seg.type !== 1){ + // Regular segment, so add grip and its "next" + seg.addGrip(); + + // Don't set its "next" if it's an "M" + if(next_seg && next_seg.type !== 2) { + seg.next = next_seg; + seg.next.prev = seg; + } + } + } + return p; + } + + this.init(); + + // Update position of all points + this.update = function() { + if(canvas.getRotationAngle(p.elem)) { + var tlist = canvas.getTransformList(path.elem); + p.matrix = transformListToTransform(tlist).matrix; + p.imatrix = p.matrix.inverse(); + } + + p.eachSeg(function(i) { + this.item = elem.pathSegList.getItem(i); + this.update(); + }); + + return p; + } + + this.eachSeg = function(fn) { + var len = p.segs.length + for(var i=0; i < len; i++) { + fn.call(p.segs[i], i); + } + } + + this.addSeg = function(index) { + // Adds a new segment + var seg = p.segs[index]; + var prev = seg.prev; + + var new_x = (seg.item.x + prev.item.x) / 2; + var new_y = (seg.item.y + prev.item.y) / 2; + + var list = elem.pathSegList; + var newseg = elem.createSVGPathSegLinetoAbs(new_x, new_y); + if(support.pathInsertItemBefore) { + list.insertItemBefore(newseg, index); + } else { + //TODO: Fix this + var len = list.numberOfItems; + var arr = []; + for(var i=0; i= 0) { + p.selected_pts.push(index); + } + } + }; + p.selected_pts.sort(); + var i = p.selected_pts.length, + grips = new Array(i); + // Loop through points to be selected and highlight each + while(i--) { + var pt = p.selected_pts[i]; + var seg = p.segs[pt]; + seg.select(true); + grips[i] = seg.ptgrip; + } + pathActions.canDeleteNodes = true; + call("selected", grips); + } + + this.removePtFromSelection = function(index) { + var pos = $.inArray(index, p.selected_pts); + if(pos == -1) { + return; + } + p.segs[index].select(false); + p.selected_pts.splice(pos, 1); + } + + + this.clearSelection = function() { + p.eachSeg(function(i) { + this.select(false); + }); + p.selected_pts = []; + } + + this.selectPt = function(pt, ctrl_num) { + p.clearSelection(); + p.addPtsToSelection(pt); + if(ctrl_num) { + p.dragctrl = ctrl_num; + + if(link_control_pts) { + p.segs[pt].setLinked(ctrl_num); + } + } + } + + this.storeD = function() { + this.last_d = elem.getAttribute('d'); + } + + this.resetD = function() { + p.elem.setAttribute("d", convertToD(elem.pathSegList)); + } + + this.show = function(y) { + // Shows this path's segment grips + p.eachSeg(function() { + this.show(y); + }); + if(y) { + p.selectPt(p.first_seg.index); + } + return p; + } + + // Move selected points + this.movePts = function(d_x, d_y) { + var i = p.selected_pts.length; + while(i--) { + var seg = p.segs[p.selected_pts[i]]; + seg.move(d_x, d_y); + } + } + + this.moveCtrl = function(d_x, d_y) { + var seg = p.segs[p.selected_pts[0]]; + seg.moveCtrl(p.dragctrl, d_x, d_y); + if(link_control_pts) { + seg.setLinked(p.dragctrl); + } + } + + this.setSegType = function(new_type) { + p.storeD(); + var i = p.selected_pts.length; + var text; + while(i--) { + var sel_pt = p.selected_pts[i]; + + // Selected seg + var cur = p.segs[sel_pt]; + var prev = cur.prev; + if(!prev) continue; + + if(!new_type) { // double-click, so just toggle + text = "Toggle Path Segment Type"; + + // Toggle segment to curve/straight line + var old_type = cur.type; + + new_type = (old_type == 6) ? 4 : 6; + } + + new_type = new_type-0; + + var cur_x = cur.item.x; + var cur_y = cur.item.y; + var prev_x = prev.item.x; + var prev_y = prev.item.y; + var points; + switch ( new_type ) { + case 6: + if(cur.olditem) { + var old = cur.olditem; + points = [cur_x,cur_y, old.x1,old.y1, old.x2,old.y2]; + } else { + var diff_x = cur_x - prev_x; + var diff_y = cur_y - prev_y; + // get control points from straight line segment + var ct1_x = (prev_x + (diff_y/2)); + var ct1_y = (prev_y - (diff_x/2)); + var ct2_x = (cur_x + (diff_y/2)); + var ct2_y = (cur_y - (diff_x/2)); + points = [cur_x,cur_y, ct1_x,ct1_y, ct2_x,ct2_y]; + } + break; + case 4: + points = [cur_x,cur_y]; + + // Store original prevve segment nums + cur.olditem = cur.item; + break; + } + + cur.setType(new_type, points); + } + path.endChanges(text); + return; + } + + } + + function getPath(elem) { + var p = pathData[elem.id]; + if(!p) p = pathData[elem.id] = new Path(elem); + return p; + } + + var pathFuncs = [], current_path = null, current_path_pts = [], - current_path_pt = -1, - current_path_pt_drag = -1, - current_path_oldd = null, - current_ctrl_pt_drag = -1, link_control_pts = false, - selected_pts = [], - is_closed = false, hasMoved = false; - - var getD = function() { - return current_path.getAttribute('d'); - }; - - var endChanges = function(d, text) { - var cmd = new ChangeElementCommand(current_path, {d:d}, text); - addCommandToHistory(cmd); - call("changed", [current_path]); - } - - var resetPointGrips = function() { - if(!current_path) return; - var sr = svgroot.suspendRedraw(100); - removeAllPointGripsFromPath(); - recalcPathPoints(); - addAllPointGripsToPath(); - svgroot.unsuspendRedraw(sr); - }; - - var setPointContainerTransform = function(value) { - var conts = $('#pathpointgrip_container,#ctrlpointgrip_container'); - $.each(conts,function() { - if(!value) { - this.removeAttribute("transform"); - } else { - this.setAttribute("transform", value); - } - }); - } - - var recalcPathPoints = function() { - current_path_pts = []; - var segList = current_path.pathSegList; - var curx = segList.getItem(0).x, cury = segList.getItem(0).y; - current_path_pts.push(curx * current_zoom); - current_path_pts.push(cury * current_zoom); - var len = segList.numberOfItems; - for (var i = 1; i < len; ++i) { - var l = segList.getItem(i); - var x = l.x, y = l.y; - // paths can now be closed, skip Z segments - if (l.pathSegType == 1) { - break; - } - var type = l.pathSegType; - // current_path_pts just holds the absolute coords - if (type == 4) { - curx = x; - cury = y; - } // type 4 (abs line) - else if (type == 5) { - curx += x; - cury += y; - } // type 5 (rel line) - else if (type == 6) { - curx = x; - cury = y; - } // type 6 (abs curve) - else if (type == 7) { - curx += x; - cury += y; - } // type 7 (rel curve) - current_path_pts.push(curx * current_zoom); - current_path_pts.push(cury * current_zoom); - } // for each segment - } - - var removeAllPointGripsFromPath = function() { - // loop through and hide all pointgrips - $('#pathpointgrip_container > *').attr("display", "none"); - - var line = getElem("path_stretch_line"); - if (line) line.setAttribute("display", "none"); - - $('#ctrlpointgrip_container *').attr('display','none'); - }; // This function converts a polyline (created by the fh_path tool) into // a path element and coverts every three line segments into a single bezier @@ -3880,424 +4500,9 @@ function BatchCommand(text) { return element; }; - // - var addNodeToSelection = function(points) { -// var point = points; - - - if(!$.isArray(points)) points = [points]; - - for(var i=0; i< points.length; i++) { - var pt = points[i]; - if($.inArray(pt, selected_pts) == -1 && pt >= 0) { - selected_pts.push(pt); - } - } - selected_pts.sort(); - - var i = selected_pts.length, - last_pt = current_path_pts.length/2 - 1, - grips = new Array(i); - - // Check if amount of selected nodes are allowed to be deleted - var tot_pts = current_path_pts.length/2 - (is_closed?1:0); - pathActions.canDeleteNodes = (tot_pts - i >= 2); - - $('#pathpointgrip_container circle').attr('stroke','#00F'); - - // Loop through points to be selected and highlight each - while(i--) { - var point = selected_pts[i]; - - $('#pathpointgrip_' + point).attr('stroke','#0FF'); - grips[i] = $('#pathpointgrip_' + point)[0]; - $('#ctrlpointgrip_container circle').attr('fill', '#EEE'); - $('#ctrlpointgrip_' + point + 'c1, #ctrlpointgrip_' + point + 'c2').attr('fill','#0FF'); - $('#segline_' + point).attr('display','inline'); - } - - if(selected_pts.length <= 1) {//if(current_path_pt == -1) { - current_path_pt = selected_pts[0]; - } - - updateSegLines(); - - call("selected", grips); - }; - - var removeNodeFromSelection = function(point) { - - var pos = $.inArray(point, selected_pts); - if(pos == -1) { - return; - } else { - selected_pts.splice(pos, 1); - } - - $('#pathpointgrip_' + point).attr('stroke','#00F'); - // grips[i] = $('#pathpointgrip_' + point)[0]; - $('#ctrlpointgrip_container circle').attr('fill', '#EEE'); - $('#ctrlpointgrip_' + point + 'c1, #ctrlpointgrip_' + point + 'c2').attr('fill','#0FF'); - $('#segline_' + point).attr('display','none'); - - current_path_pt = selected_pts[0]; - - // TODO: Set grips -// call("selected", grips); - }; - - var removeAllNodesFromSelection = function() { - var i = selected_pts.length; - var last_pt = current_path_pts.length/2 - 1; - - $('#pathpointgrip_container circle').attr('stroke','#EEE'); - $('#ctrlpointgrip_container circle').attr('fill', '#EEE'); - selected_pts = []; -// current_path_pt = -1; -// call("selected", []); - }; - - var selectNode = function(point) { - removeAllNodesFromSelection(); - addNodeToSelection(point); - }; - - var addAllPointGripsToPath = function(pointToSelect) { - // loop through and show all pointgrips - var len = current_path_pts.length; - for (var i = 0; i < len; i += 2) { - var grip = getElem("pathpointgrip_"+i/2); - - // Skip last point if closed - if(!is_closed || i+2 < len) { - if (grip) { - assignAttributes(grip, { - 'cx': current_path_pts[i], - 'cy': current_path_pts[i+1], - 'display': 'inline' - }); - } - else { - addPointGripToPath(current_path_pts[i], current_path_pts[i+1],i/2); - } - } - - var index = i/2; - var item = current_path.pathSegList.getItem(index); - if(item.pathSegType == 6) { - index -= 1; - // Same code as when making a curve, needs to be in own function - var cur_x = getPathPoint(index)[0]; - var cur_y = getPathPoint(index)[1]; - var next_x = getPathPoint(index+1)[0]; - var next_y = getPathPoint(index+1)[1]; - addControlPointGrip(item.x1,item.y1, cur_x,cur_y, index+'c1'); - addControlPointGrip(item.x2,item.y2, next_x,next_y, index+'c2'); - } - } - // FIXME: we cannot just use the same transform as the path because we might be - // at a different zoom level - var angle = canvas.getRotationAngle(current_path); - if (angle) { - var bbox = canvas.getBBox(current_path); - var cx = (bbox.x + bbox.width/2) * current_zoom, - cy = (bbox.y + bbox.height/2) * current_zoom; - var xform = ["rotate(", angle, " ", cx, ",", cy, ")"].join(""); - setPointContainerTransform(xform); - } - if(pointToSelect != null) { - selectNode(pointToSelect); - } - }; - - var addPointGripToPath = function(x,y,index) { - // create the container of all the point grips - var pointGripContainer = getElem("pathpointgrip_container"); - if (!pointGripContainer) { - var parent = getElem("selectorParentGroup"); - pointGripContainer = parent.appendChild(document.createElementNS(svgns, "g")); - pointGripContainer.id = "pathpointgrip_container"; - } - - var pointGrip = getElem("pathpointgrip_"+index); - // create it - if (!pointGrip) { - pointGrip = document.createElementNS(svgns, "circle"); - assignAttributes(pointGrip, { - 'id': "pathpointgrip_" + index, - 'display': "none", - 'r': 4, - 'fill': "#0FF", - 'stroke': "#00F", - 'stroke-width': 2, - 'cursor': 'move', - 'style': 'pointer-events:all', - 'xlink:title': uiStrings.pathNodeTooltip - }); - pointGrip = pointGripContainer.appendChild(pointGrip); - - var grip = $('#pathpointgrip_'+index); - grip.dblclick(function() { - canvas.setSegType(); - }); - - // create segline - segLine = document.createElementNS(svgns, "path"); - assignAttributes(segLine, { - 'id': "segline_" + index, - 'display': 'none', - 'fill': "none", - 'stroke': "#0FF", - 'stroke-width': 2, - 'style':'pointer-events:none', - 'd': 'M0,0 0,0' - }); - segLine = pointGripContainer.appendChild(segLine); - } - - // set up the point grip element and display it - assignAttributes(pointGrip, { - 'cx': x, - 'cy': y, - 'display': "inline" - }); - }; - - var updateSegLines = function() { - // create segment lines - var len = current_path_pts.length/2 - (is_closed?1:0); - - for(var i=0; i= list.numberOfItems) { - segLine.setAttribute('display','none'); - continue; - } - - // Point is selected, so calculate and display - replacePathSeg(2, 0, getPathPoint(i, true), segLine); - - var seg = list.getItem(i+1); - var points = [seg.x, seg.y]; - if(seg.x1 != null && seg.x2 != null) { - points.splice(2, 0, seg.x1, seg.y1, seg.x2, seg.y2); - } - points = $.map(points, function(n){return n*current_zoom;}); - replacePathSeg(seg.pathSegType, 1, points, segLine); - segLine.setAttribute('display','inline'); - } else { - // Point is not selected, so just hide - segLine.setAttribute('display','none'); - } - } - } - - var updatePath = function(mouse_x, mouse_y, old_path_pts) { - var x = mouse_x / current_zoom, - y = mouse_y / current_zoom, - - i = current_path_pt_drag * 2, - last_index = current_path_pts.length/2 - 1, - is_first = current_path_pt_drag == 0, // || (is_closed && current_path_pt_drag == last_index); - is_last = !is_closed && current_path_pt_drag == last_index; - - // 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, true); - if (angle) { - // calculate the shape's old center that was used for rotation - var box = selectedBBoxes[0], - cx = (box.x + box.width/2) * current_zoom, - cy = (box.y + box.height/2) * current_zoom, - dx = mouse_x - cx, dy = mouse_y - cy, - r = Math.sqrt( dx*dx + dy*dy ), - 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; - } - else { - current_path_pts[i] = x * current_zoom; - current_path_pts[i+1] = y * current_zoom; - } - - if(is_first && is_closed) { - // Update the first point - current_path_pts[0] = current_path_pts[i]; - current_path_pts[1] = current_path_pts[i+1]; - current_path_pt_drag = 0; - } - - var index = current_path_pt_drag, - abs_x = getPathPoint(index)[0], - abs_y = getPathPoint(index)[1], - - item = current_path.pathSegList.getItem(index), - x_diff = x - old_path_pts[index*2], - y_diff = y - old_path_pts[index*2 + 1]; - - var cur_type = item.pathSegType; - var points = []; - - if(cur_type == 6) { - points = [abs_x,abs_y, item.x1,item.y1, item.x2 + x_diff,item.y2 + y_diff]; - } else { - if(is_first) { - // Need absolute position for first point - points = getPathPoint(0); - } else { - points = [abs_x, abs_y]; - } - } - replacePathSeg(cur_type, index, points); - - var setSeg = function(index,first) { - var points, item = current_path.pathSegList.getItem(index); - var type = item.pathSegType; - if(first) { - item.x += x_diff; - item.y += y_diff; - } - - switch (type) { - case 1: - points = []; - break; - case 4: - points = [item.x, item.y]; - break; - case 6: - if(first) { - item.x1 -= x_diff; - item.y1 -= y_diff; - item.x2 += x_diff; - item.y2 += y_diff; - } - - points = [item.x, item.y, item.x1 + x_diff,item.y1 + y_diff, item.x2,item.y2]; - break; - default: - break; - } - replacePathSeg(type, index, points); - return type; - } - - if(is_closed || !is_last) { - var next_type = setSeg(index+1); - } else { - var next_type = 0; - } - - if(is_first && is_closed) { - var last_type = setSeg(last_index,1); - } - - // move the point grip - var grip = getElem("pathpointgrip_" + current_path_pt_drag); - if (grip) { - grip.setAttribute("cx", mouse_x); - grip.setAttribute("cy", mouse_y); - call("changed", [grip]); - } - - if(is_first) cur_type = last_type; - - if(cur_type != 4) { - var num = is_first?last_index:index, - id2 = (num-1)+'c2', - line = getElem("ctrlLine_"+id2); - if(line) { - // Don't do if first point on open path - if(!(!is_closed && current_path_pt_drag == 0)) { - var x2 = line.getAttribute('x2') - 0 + x_diff*current_zoom, - y2 = line.getAttribute('y2') - 0 + y_diff*current_zoom; - addControlPointGrip(x2,y2, mouse_x,mouse_y, id2, true); - } - } - } - - if(next_type != 4) { - var id1 = (current_path_pt_drag)+'c1'; - var line = getElem("ctrlLine_"+id1); - if(line) { - var x2 = line.getAttribute('x2') - 0 + x_diff*current_zoom, - y2 = line.getAttribute('y2') - 0 + y_diff*current_zoom; - addControlPointGrip(x2,y2, mouse_x,mouse_y, id1, true); - } - } - } - - 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, - 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], - cx = (box.x + box.width/2) * current_zoom, - cy = (box.y + box.height/2) * current_zoom, - dx = mouse_x - cx, dy = mouse_y - cy, - r = Math.sqrt( dx*dx + dy*dy ), - 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]); - - updateSegLines(); - - var grip = getElem("ctrlpointgrip_" + ctrl_pt_drag); - if(grip) { - grip.setAttribute("cx", mouse_x); - grip.setAttribute("cy", mouse_y); - - var line = getElem("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; - if(index < 0) { - index += pt_num; - } else if(index >= pt_num) { - index -= pt_num; - } - var z = raw_val?1:current_zoom; - return [current_path_pts[index*2] / z, current_path_pts[index*2 + 1] / z]; - } - // This replaces the segment at the given index. Type is given as number. - var replacePathSeg = function(type, index, pts, path) { - if(!path) path = current_path; + var replacePathSeg = function(type, index, pts, elem) { + var path = elem || retPath().elem; var func = 'createSVGPathSeg' + pathFuncs[type]; var seg = path[func].apply(path, pts); @@ -4321,74 +4526,8 @@ function BatchCommand(text) { } } } - - var addControlPointGrip = function(x, y, source_x, source_y, id, raw_val) { - if(!raw_val) { - x *= current_zoom; y *= current_zoom; - source_x *= current_zoom; source_y *= current_zoom; - } - - // create the container of all the control point grips - var ctrlPointGripContainer = getElem("ctrlpointgrip_container"); - if (!ctrlPointGripContainer) { - var parent = getElem("selectorParentGroup"); - ctrlPointGripContainer = parent.appendChild(document.createElementNS(svgns, "g")); - ctrlPointGripContainer.id = "ctrlpointgrip_container"; - } - ctrlPointGripContainer.setAttribute("display", "inline"); - - var ctrlLine = getElem("ctrlLine_"+id); - if (!ctrlLine) { - ctrlLine = document.createElementNS(svgns, "line"); - assignAttributes(ctrlLine, { - 'id': "ctrlLine_"+id, - 'stroke': "#555", - 'stroke-width': 1, - "style": "pointer-events:none" - }); - ctrlLine = ctrlPointGripContainer.appendChild(ctrlLine); - } - - assignAttributes(ctrlLine, { - 'x1': source_x, - 'y1': source_y, - 'x2': x, - 'y2': y, - 'display': "inline" - }); - - var pointGrip = getElem("ctrlpointgrip_"+id); - // create it - if (!pointGrip) { - pointGrip = document.createElementNS(svgns, "circle"); - assignAttributes(pointGrip, { - 'id': "ctrlpointgrip_" + id, - 'display': "none", - 'r': 4, - 'fill': "#0FF", - 'stroke': "#55F", - 'stroke-width': 1, - 'cursor': 'move', - 'style': 'pointer-events:all', - 'xlink:title': uiStrings.pathCtrlPtTooltip - }); - pointGrip = ctrlPointGripContainer.appendChild(pointGrip); - } - - assignAttributes(pointGrip, { - 'cx': x, - 'cy': y, - 'display': "inline" - }); - } - - var removeControlPointGrips = function(index) { - for(var i=1; i <= 2; i++) { - $("#ctrlpointgrip_" + index + "c" + i + ",#ctrlLine_" + index + "c" + i).attr("display", "none"); - } - } - - // If the path was rotated, we must now pay the piper: + + // If the path was rotated, we must now pay the piper: // 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 @@ -4396,8 +4535,10 @@ function BatchCommand(text) { // TODO: This is still using ye olde transform methods, can probably // be optimized or even taken care of by recalculateDimensions var recalcRotatedPath = function() { + var current_path = path.elem; var angle = canvas.getRotationAngle(current_path, true); if(!angle) return; + selectedBBoxes[0] = path.oldbbox; var box = canvas.getBBox(current_path), oldbox = selectedBBoxes[0], oldcx = oldbox.x + oldbox.width/2, @@ -4465,15 +4606,6 @@ function BatchCommand(text) { tlist = canvas.getTransformList(current_path); R_nc.setRotate((angle * 180.0 / Math.PI), newcx, newcy); tlist.replaceItem(R_nc,0); - - if(getElem("pathpointgrip_container")) { - var pcx = newcx * current_zoom, - pcy = newcy * current_zoom; - var xform = ["rotate(", (angle*180.0/Math.PI), " ", pcx, ",", pcy, ")"].join(""); - setPointContainerTransform(xform); - } - resetPointGrips(); - updateSegLines(); } return { @@ -4482,43 +4614,47 @@ function BatchCommand(text) { var pathFuncsStrs = ['Moveto','Lineto','CurvetoCubic','CurvetoQuadratic','Arc','LinetoHorizontal','LinetoVertical','CurvetoCubicSmooth','CurvetoQuadraticSmooth']; $.each(pathFuncsStrs,function(i,s){pathFuncs.push(s+'Abs');pathFuncs.push(s+'Rel');}); }, + getPath: function() { + return path; + }, mouseDown: function(evt, mouse_target, start_x, start_y) { - if(current_mode == "path") { - setPointContainerTransform(""); - return; - } + if(current_mode == "path") return; - // Make sure current_path isn't null at this point - if(!current_path) return; + // TODO: Make sure current_path isn't null at this point + if(!path) return; + + path.storeD(); - current_path_oldd = getD(); var id = evt.target.id; if (id.substr(0,14) == "pathpointgrip_") { // Select this point - current_path_pt = current_path_pt_drag = parseInt(id.substr(14)); + var cur_pt = path.cur_pt = parseInt(id.substr(14)); + path.dragging = [start_x, start_y]; + var seg = path.segs[cur_pt]; // only clear selection if shift is not pressed (otherwise, add // node to selection) if (!evt.shiftKey) { - if(selected_pts.length <= 1 || $.inArray(current_path_pt, selected_pts) == -1) { - removeAllNodesFromSelection(); + if(path.selected_pts.length <= 1 || !seg.selected) { + path.clearSelection(); } - addNodeToSelection(current_path_pt); - } else if($.inArray(current_path_pt, selected_pts) != -1) { - removeNodeFromSelection(current_path_pt); + path.addPtsToSelection(cur_pt); + } else if(seg.selected) { + path.removePtFromSelection(cur_pt); } else { - addNodeToSelection(current_path_pt); + path.addPtsToSelection(cur_pt); } } else if(id.indexOf("ctrlpointgrip_") == 0) { - current_ctrl_pt_drag = id.split('_')[1]; - var node_num = current_ctrl_pt_drag.split('c')[0]-0; - selectNode(node_num); + path.dragging = [start_x, start_y]; + + var parts = id.split('_')[1].split('c'); + var cur_pt = parts[0]-0; + var ctrl_num = parts[1]-0; + path.selectPt(cur_pt, ctrl_num); } - if(current_path_pt_drag == -1 && current_ctrl_pt_drag == -1) { - -// start_x = x; -// start_y = y; + // Start selection box + if(!path.dragging) { if (rubberBox == null) { rubberBox = selectorManager.getRubberBandBox(); } @@ -4541,112 +4677,53 @@ function BatchCommand(text) { } return; } - // if we are dragging a point, let's move it - if (current_path_pt_drag != -1 && current_path) { - var old_path_pts = $.map(current_path_pts, function(n){return n/current_zoom;}); - var diff_x = mouse_x - current_path_pts[current_path_pt*2]; - var diff_y = mouse_y - current_path_pts[current_path_pt*2+1]; - - var rot = !!canvas.getRotationAngle(current_path); - if(rot) { - var m = canvas.getTransformList(current_path).getItem(0).matrix; - } - - for(var i=0; i= pt_count - 1) { - index = 0; - if(!is_closed) return; - } - } - - var pt = getPathPoint(pt_index, true), - new_x = pt[0] - (mouse_x - pt[0]), - new_y = pt[1] - (mouse_y - pt[1]); - updateCurvedSegment(new_x, new_y, index, ctrl_num, true); + if(path.dragctrl) { + path.moveCtrl(diff_x, diff_y); + } else { + path.movePts(diff_x, diff_y); } } else { - var len = current_path_pts.length, - sel_pts = []; - selected_pts = []; - var rot = !!canvas.getRotationAngle(current_path); - if(rot) { - var tlist = canvas.getTransformList(current_path), - m = transformListToTransform(tlist).matrix; - } - - if(is_closed) len -= 2; - - for(var i=0; i sel_pt+1) { - segtype = list.getItem(sel_pt+1).pathSegType; - } else { - segtype = false; - } + var seg = path.segs[sel_pt]; return { - x: pt[0], - y: pt[1], - type: segtype - } + x: seg.item.x, + y: seg.item.y, + type: seg.type + }; }, linkControlPoints: function(linkPoints) { link_control_pts = linkPoints; }, clonePathNode: function() { - var d = getD(); + path.storeD(); - var i = selected_pts.length, - nums = []; + var sel_pts = path.selected_pts; + var segs = path.segs; + + var i = sel_pts.length; + var nums = []; + while(i--) { - var pt = selected_pts[i], list = current_path.pathSegList; - if(pt+1 >= list.numberOfItems) { - pt--; - } + var pt = sel_pts[i]; + path.addSeg(pt); - var next_item = list.getItem(pt+1); - - // Get point in between nodes - if(next_item.pathSegType % 2 == 0) { // even num, so abs - var cur_item = list.getItem(pt); - var new_x = (next_item.x + cur_item.x) / 2; - var new_y = (next_item.y + cur_item.y) / 2; - } else { - var new_x = next_item.x/2; - var new_y = next_item.y/2; - } - - var seg = current_path.createSVGPathSegLinetoAbs(new_x, new_y); - - if(support.pathInsertItemBefore) { - list.insertItemBefore(seg, pt+1); - } else { - var segList = current_path.pathSegList; - var len = segList.numberOfItems; - var arr = []; - for(var i=0; i= last_index && is_closed) { - index = 0; - } - - var next_index = index+1; - var cur_x = getPathPoint(index)[0]; - var cur_y = getPathPoint(index)[1]; - var next_x = getPathPoint(next_index)[0]; - var next_y = getPathPoint(next_index)[1]; - - if(!new_type) { // double-click, so just toggle - var text = "Toggle Path Segment Type"; - - // Toggle segment to curve/straight line - var old_type = current_path.pathSegList.getItem(index+1).pathSegType; - - new_type = (old_type == 6) ? 4 : 6; - - } else { - new_type -= 0; - var text = "Change Path Segment Type"; - } - - var points; - - var bb = current_path.getBBox(); - var oldseg = current_path.pathSegList.getItem(next_index); - switch ( new_type ) { - case 6: - var diff_x = next_x - cur_x; - var diff_y = next_y - cur_y; - // get control points from straight line segment - var ct1_x = oldseg.c1_x || (cur_x + (diff_y/2)); - var ct1_y = oldseg.c1_y || (cur_y - (diff_x/2)); - var ct2_x = oldseg.c2_x || (next_x + (diff_y/2)); - var ct2_y = oldseg.c2_y || (next_y - (diff_x/2)); - points = [next_x,next_y, ct1_x,ct1_y, ct2_x,ct2_y]; - break; - case 4: - points = [next_x,next_y]; - removeControlPointGrips(index); - break; - } - - replacePathSeg(new_type, next_index, points); - - // if we changed to a straight line, store the old control points - var newseg = current_path.pathSegList.getItem(next_index); - if (newseg.pathSegType == 4 && oldseg.pathSegType == 6) { - newseg.c1_x = oldseg.x1; - newseg.c1_y = oldseg.y1; - newseg.c2_x = oldseg.x2; - newseg.c2_y = oldseg.y2; - } + var pt = sel_pts[i]; + path.deleteSeg(pt); } - - addAllPointGripsToPath(); - // recalculateDimensions(current_path); - updateSegLines(); - recalcRotatedPath(); - endChanges(old_d, text); + path.init(); + var sel_pt = sel_pts[0]-1 > 0 ? sel_pts[0]-1 : 1; + + path.clearSelection(); + path.addPtsToSelection(sel_pt); + + if(window.opera) { // Opera repaints incorrectly + var cp = $(path.elem); cp.attr('d',cp.attr('d')); + } + path.endChanges("Delete path node(s)"); + }, + smoothPolylineIntoPath: smoothPolylineIntoPath, + setSegType: function(v) { + path.setSegType(v); }, moveNode: function(attr, newValue) { - newValue *= current_zoom; - var num = (attr == 'x')?0:1; - var old_path_pts = $.map(current_path_pts, function(n){return n/current_zoom;}); - var d = getD(); - - current_path_pts[current_path_pt*2 + num] = newValue-0; - current_path_pt_drag = current_path_pt; - updatePath(current_path_pts[current_path_pt*2], current_path_pts[current_path_pt*2 + 1], old_path_pts); - updateSegLines(); - endChanges(d, "Move path point"); + var sel_pts = path.selected_pts; + if(!sel_pts.length) return; + + path.storeD(); + + // Get first selected point + var seg = path.segs[sel_pts[0]]; + var diff = {x:0, y:0}; + diff[attr] = newValue - seg.item[attr]; + + seg.move(diff.x, diff.y); + path.endChanges("Move path point"); }, // Convert a path to one with only absolute or relative values convertPath: function(path, toRel) { @@ -5674,6 +5569,7 @@ function BatchCommand(text) { // reset transform lists svgTransformLists = {}; canvas.clearSelection(); + pathActions.clearData(); svgroot.appendChild(selectorManager.selectorParentGroup); addCommandToHistory(batchCmd); @@ -6887,9 +6783,9 @@ function BatchCommand(text) { this.changeSelectedAttribute("transform",newTransform,selectedElements); } var pointGripContainer = getElem("pathpointgrip_container"); - if(elem.nodeName == "path" && pointGripContainer) { - pathActions.setPointContainerTransform(elem.getAttribute("transform")); - } +// if(elem.nodeName == "path" && pointGripContainer) { +// pathActions.setPointContainerTransform(elem.getAttribute("transform")); +// } var selector = selectorManager.requestSelector(selectedElements[0]); selector.resize(); selector.updateGripCursors(val); @@ -7748,9 +7644,9 @@ function BatchCommand(text) { this.undo = function() { if (undoStackPointer > 0) { this.clearSelection(); - pathActions.clear(); var cmd = undoStack[--undoStackPointer]; cmd.unapply(); + pathActions.clear(); call("changed", cmd.elements()); } }; @@ -7759,6 +7655,7 @@ function BatchCommand(text) { this.clearSelection(); var cmd = undoStack[undoStackPointer++]; cmd.apply(); + pathActions.clear(); call("changed", cmd.elements()); } };