| | |
| | | |
| | | svgedit.path.init = function(editorContext) { |
| | | editorContext_ = editorContext; |
| | | |
| | | |
| | | pathFuncs = [0,'ClosePath']; |
| | | var pathFuncsStrs = ['Moveto', 'Lineto', 'CurvetoCubic', 'CurvetoQuadratic', 'Arc', |
| | | 'LinetoHorizontal', 'LinetoVertical','CurvetoCubicSmooth','CurvetoQuadraticSmooth']; |
| | |
| | | svgedit.path.addCtrlGrip = function(id) { |
| | | var pointGrip = svgedit.utilities.getElem("ctrlpointgrip_"+id); |
| | | if(pointGrip) return pointGrip; |
| | | |
| | | |
| | | pointGrip = document.createElementNS(svgns, "circle"); |
| | | svgedit.utilities.assignAttributes(pointGrip, { |
| | | 'id': "ctrlpointgrip_" + id, |
| | |
| | | var item = seg.item; |
| | | var index = seg.index; |
| | | if(!item || !("x1" in item) || !("x2" in item)) return null; |
| | | var cpt = {}; |
| | | var cpt = {}; |
| | | var pointGripContainer = svgedit.path.getGripContainer(); |
| | | |
| | | // Note that this is intentionally not seg.prev.item |
| | |
| | | 'd': 'M0,0 0,0' |
| | | }); |
| | | pointGripContainer.appendChild(segLine); |
| | | } |
| | | } |
| | | |
| | | if(update) { |
| | | var prev = seg.prev; |
| | |
| | | |
| | | // Function: smoothControlPoints |
| | | // Takes three points and creates a smoother line based on them |
| | | // |
| | | // Parameters: |
| | | // |
| | | // Parameters: |
| | | // ct1 - Object with x and y values (first control point) |
| | | // ct2 - Object with x and y values (second control point) |
| | | // pt - Object with x and y values (third point) |
| | | // |
| | | // Returns: |
| | | // Returns: |
| | | // Array of two "smoothed" point objects |
| | | svgedit.path.smoothControlPoints = this.smoothControlPoints = function(ct1, ct2, pt) { |
| | | // each point must not be the origin |
| | |
| | | y1 = ct1.y - pt.y, |
| | | x2 = ct2.x - pt.x, |
| | | y2 = ct2.y - pt.y; |
| | | |
| | | |
| | | if ( (x1 != 0 || y1 != 0) && (x2 != 0 || y2 != 0) ) { |
| | | var anglea = Math.atan2(y1,x1), |
| | | angleb = Math.atan2(y2,x2), |
| | | r1 = Math.sqrt(x1*x1+y1*y1), |
| | | r2 = Math.sqrt(x2*x2+y2*y2), |
| | | nct1 = editorContext_.getSVGRoot().createSVGPoint(), |
| | | nct2 = editorContext_.getSVGRoot().createSVGPoint(); |
| | | nct2 = editorContext_.getSVGRoot().createSVGPoint(); |
| | | if (anglea < 0) { anglea += 2*Math.PI; } |
| | | if (angleb < 0) { angleb += 2*Math.PI; } |
| | | |
| | | |
| | | var angleBetween = Math.abs(anglea - angleb), |
| | | angleDiff = Math.abs(Math.PI - angleBetween)/2; |
| | | |
| | | |
| | | var new_anglea, new_angleb; |
| | | if (anglea - angleb > 0) { |
| | | new_anglea = angleBetween < Math.PI ? (anglea + angleDiff) : (anglea - angleDiff); |
| | |
| | | new_anglea = angleBetween < Math.PI ? (anglea - angleDiff) : (anglea + angleDiff); |
| | | new_angleb = angleBetween < Math.PI ? (angleb + angleDiff) : (angleb - angleDiff); |
| | | } |
| | | |
| | | |
| | | // rotate the points |
| | | nct1.x = r1 * Math.cos(new_anglea) + pt.x; |
| | | nct1.y = r1 * Math.sin(new_anglea) + pt.y; |
| | | nct2.x = r2 * Math.cos(new_angleb) + pt.x; |
| | | nct2.y = r2 * Math.sin(new_angleb) + pt.y; |
| | | |
| | | |
| | | return [nct1, nct2]; |
| | | } |
| | | return undefined; |
| | |
| | | this.index = index; |
| | | this.item = item; |
| | | this.type = item.pathSegType; |
| | | |
| | | |
| | | this.ctrlpts = []; |
| | | this.ptgrip = null; |
| | | this.segsel = null; |
| | |
| | | this.type = this.item.pathSegType; |
| | | } |
| | | svgedit.path.getControlPoints(this); |
| | | } |
| | | } |
| | | // this.segsel.setAttribute("display", y?"inline":"none"); |
| | | } |
| | | }; |
| | |
| | | var item = this.item; |
| | | // fix for path tool dom breakage, amending item does bad things now, so we take a copy and use that. Can probably improve to just take a shallow copy of object |
| | | var cloneItem = $.extend({}, item); |
| | | var cur_pts = (this.ctrlpts) |
| | | ? [cloneItem.x += dx, cloneItem.y += dy, |
| | | cloneItem.x1, cloneItem.y1, |
| | | var cur_pts = (this.ctrlpts) |
| | | ? [cloneItem.x += dx, cloneItem.y += dy, |
| | | cloneItem.x1, cloneItem.y1, |
| | | cloneItem.x2 += dx, cloneItem.y2 += dy] |
| | | : [cloneItem.x += dx, cloneItem.y += dy]; |
| | | |
| | | |
| | | svgedit.path.replacePathSeg(this.type, this.index, cur_pts); |
| | | |
| | | if(this.next && this.next.ctrlpts) { |
| | | var next = this.next.item; |
| | | var next_pts = [next.x, next.y, |
| | | var next_pts = [next.x, next.y, |
| | | next.x1 += dx, next.y1 += dy, next.x2, next.y2]; |
| | | svgedit.path.replacePathSeg(this.next.type, this.next.index, next_pts); |
| | | } |
| | |
| | | cloneItem['x' + anum ] = pt.x + (pt.x - this.item['x' + num]); |
| | | cloneItem['y' + anum ] = pt.y + (pt.y - this.item['y' + num]); |
| | | |
| | | var pts = [ |
| | | var pts = [ |
| | | cloneItem.x, cloneItem.y, |
| | | cloneItem.x1, cloneItem.y1, |
| | | cloneItem.x2, cloneItem.y2 |
| | |
| | | |
| | | var pts = [item.x,item.y, |
| | | item.x1,item.y1, item.x2,item.y2]; |
| | | |
| | | |
| | | svgedit.path.replacePathSeg(this.type, this.index, pts); |
| | | this.update(true); |
| | | }; |
| | |
| | | this.segs = []; |
| | | this.selected_pts = []; |
| | | this.first_seg = null; |
| | | |
| | | |
| | | // Set up segs array |
| | | for(var i=0; i < len; i++) { |
| | | var item = segList.getItem(i); |
| | | var segment = new svgedit.path.Segment(i, item); |
| | | segment.path = this; |
| | | this.segs.push(segment); |
| | | } |
| | | |
| | | } |
| | | |
| | | var segs = this.segs; |
| | | var start_i = null; |
| | | |
| | | for(var i=0; i < len; i++) { |
| | | var seg = segs[i]; |
| | | var seg = segs[i]; |
| | | var next_seg = (i+1) >= 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, |
| | |
| | | // 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]; |
| | |
| | | } 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; |
| | |
| | | svgedit.path.Path.prototype.deleteSeg = function(index) { |
| | | var seg = this.segs[index]; |
| | | var list = this.elem.pathSegList; |
| | | |
| | | |
| | | seg.show(false); |
| | | var next = seg.next; |
| | | if(seg.mate) { |
| | | // Make the next point be the "M" point |
| | | var pt = [next.item.x, next.item.y]; |
| | | svgedit.path.replacePathSeg(2, next.index, pt); |
| | | |
| | | |
| | | // Reposition last node |
| | | svgedit.path.replacePathSeg(4, seg.index, pt); |
| | | |
| | | |
| | | list.removeItem(seg.mate.index); |
| | | } else if(!seg.prev) { |
| | | // First node of open path, make next point the M |
| | |
| | | var pt = [next.item.x, next.item.y]; |
| | | svgedit.path.replacePathSeg(2, seg.next.index, pt); |
| | | list.removeItem(index); |
| | | |
| | | |
| | | } else { |
| | | list.removeItem(index); |
| | | } |
| | |
| | | return false; |
| | | } |
| | | }); |
| | | |
| | | |
| | | return closed; |
| | | }; |
| | | |
| | |
| | | var pos = this.selected_pts.indexOf(index); |
| | | if(pos == -1) { |
| | | return; |
| | | } |
| | | } |
| | | this.segs[index].select(false); |
| | | this.selected_pts.splice(pos, 1); |
| | | }; |
| | |
| | | return this; |
| | | }; |
| | | |
| | | // Move selected points |
| | | // Move selected points |
| | | svgedit.path.Path.prototype.movePts = function(d_x, d_y) { |
| | | var i = this.selected_pts.length; |
| | | while(i--) { |
| | |
| | | var text; |
| | | while(i--) { |
| | | var sel_pt = this.selected_pts[i]; |
| | | |
| | | |
| | | // Selected seg |
| | | var cur = this.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; |
| | |
| | | break; |
| | | case 4: |
| | | points = [cur_x,cur_y]; |
| | | |
| | | |
| | | // Store original prevve segment nums |
| | | cur.olditem = cur.item; |
| | | break; |
| | | } |
| | | |
| | | |
| | | cur.setType(new_type, points); |
| | | } |
| | | svgedit.path.path.endChanges(text); |
| | |
| | | this.addPtsToSelection(pt); |
| | | if(ctrl_num) { |
| | | this.dragctrl = ctrl_num; |
| | | |
| | | |
| | | if(link_control_pts) { |
| | | this.segs[pt].setLinked(ctrl_num); |
| | | } |
| | |
| | | var getRotVals = function(x, y, oldcx, oldcy, newcx, newcy, angle) { |
| | | dx = x - oldcx; |
| | | dy = y - oldcy; |
| | | |
| | | |
| | | // rotate the point around the old center |
| | | r = Math.sqrt(dx*dx + dy*dy); |
| | | theta = Math.atan2(dy,dx) + angle; |
| | | dx = r * Math.cos(theta) + oldcx; |
| | | dy = r * Math.sin(theta) + oldcy; |
| | | |
| | | |
| | | // dx,dy should now hold the actual coordinates of each |
| | | // point after being rotated |
| | | |
| | | // now we want to rotate them around the new center in the reverse direction |
| | | dx -= newcx; |
| | | dy -= newcy; |
| | | |
| | | |
| | | r = Math.sqrt(dx*dx + dy*dy); |
| | | theta = Math.atan2(dy,dx) - angle; |
| | | return {'x':(r * Math.cos(theta) + newcx)/1, |
| | |
| | | }; |
| | | |
| | | // If the path was rotated, we must now pay the piper: |
| | | // Every path point must be rotated into the rotated coordinate system of |
| | | // 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 |
| | | |
| | |
| | | oldcy = oldbox.y + oldbox.height/2, |
| | | newcx = box.x + box.width/2, |
| | | newcy = box.y + box.height/2, |
| | | |
| | | |
| | | // un-rotate the new center to the proper position |
| | | dx = newcx - oldcx, |
| | | dy = newcy - oldcy, |
| | | r = Math.sqrt(dx*dx + dy*dy), |
| | | theta = Math.atan2(dy,dx) + angle; |
| | | |
| | | |
| | | newcx = r * Math.cos(theta) + oldcx; |
| | | newcy = r * Math.sin(theta) + oldcy; |
| | | |
| | | |
| | | var list = current_path.pathSegList, |
| | | i = list.numberOfItems; |
| | | while (i) { |
| | |
| | | var seg = list.getItem(i), |
| | | type = seg.pathSegType; |
| | | if(type == 1) continue; |
| | | |
| | | |
| | | var rvals = getRotVals(seg.x,seg.y, oldcx, oldcy, newcx, newcy, angle), |
| | | points = [rvals.x, rvals.y]; |
| | | if(seg.x1 != null && seg.x2 != null) { |
| | |
| | | svgedit.path.replacePathSeg(type, i, points); |
| | | } // loop for each point |
| | | |
| | | box = svgedit.utilities.getBBox(current_path); |
| | | box = svgedit.utilities.getBBox(current_path); |
| | | // selectedBBoxes[0].x = box.x; selectedBBoxes[0].y = box.y; |
| | | // selectedBBoxes[0].width = box.width; selectedBBoxes[0].height = box.height; |
| | | |
| | | |
| | | // now we must set the new transform to be rotated around the new center |
| | | var R_nc = svgroot.createSVGTransform(), |
| | | tlist = svgedit.transformlist.getTransformList(current_path); |