123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992 |
- /*globals $, svgedit, svgroot*/
- /*jslint vars: true, eqeq: true, continue: true*/
- /**
- * Package: svgedit.path
- *
- * Licensed under the MIT License
- *
- * Copyright(c) 2011 Alexis Deveria
- * Copyright(c) 2011 Jeff Schiller
- */
- // Dependencies:
- // 1) jQuery
- // 2) browser.js
- // 3) math.js
- // 4) svgutils.js
- (function() {'use strict';
- if (!svgedit.path) {
- svgedit.path = {};
- }
- var NS = svgedit.NS;
- var uiStrings = {
- 'pathNodeTooltip': 'Drag node to move it. Double-click node to change segment type',
- 'pathCtrlPtTooltip': 'Drag control point to adjust curve properties'
- };
- 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'],
- 16: ['x', 'y', 'x2', 'y2'],
- 18: ['x', 'y']
- };
- var pathFuncs = [];
- var link_control_pts = true;
- // Stores references to paths via IDs.
- // TODO: Make this cross-document happy.
- var pathData = {};
- svgedit.path.setLinkControlPoints = function(lcp) {
- link_control_pts = lcp;
- };
- svgedit.path.path = null;
- var editorContext_ = null;
- svgedit.path.init = function(editorContext) {
- editorContext_ = editorContext;
- pathFuncs = [0, 'ClosePath'];
- var pathFuncsStrs = ['Moveto', 'Lineto', 'CurvetoCubic', 'CurvetoQuadratic', 'Arc',
- 'LinetoHorizontal', 'LinetoVertical', 'CurvetoCubicSmooth', 'CurvetoQuadraticSmooth'];
- $.each(pathFuncsStrs, function(i, s) {
- pathFuncs.push(s+'Abs');
- pathFuncs.push(s+'Rel');
- });
- };
- svgedit.path.insertItemBefore = function(elem, newseg, index) {
- // Support insertItemBefore on paths for FF2
- var list = elem.pathSegList;
- if (svgedit.browser.supportsPathInsertItemBefore()) {
- list.insertItemBefore(newseg, index);
- return;
- }
- var len = list.numberOfItems;
- var arr = [];
- var i;
- for (i=0; i < len; i++) {
- var cur_seg = list.getItem(i);
- arr.push(cur_seg);
- }
- list.clear();
- for (i=0; i < len; i++) {
- if (i == index) { //index+1
- list.appendItem(newseg);
- }
- list.appendItem(arr[i]);
- }
- };
- // TODO: See if this should just live in replacePathSeg
- svgedit.path.ptObjToArr = function(type, seg_item) {
- var arr = segData[type], len = arr.length;
- var i, out = [];
- for (i = 0; i < len; i++) {
- out[i] = seg_item[arr[i]];
- }
- return out;
- };
- svgedit.path.getGripPt = function(seg, alt_pt) {
- var out = {
- x: alt_pt? alt_pt.x : seg.item.x,
- y: alt_pt? alt_pt.y : seg.item.y
- }, path = seg.path;
- if (path.matrix) {
- var pt = svgedit.math.transformPoint(out.x, out.y, path.matrix);
- out = pt;
- }
- out.x *= editorContext_.getCurrentZoom();
- out.y *= editorContext_.getCurrentZoom();
- return out;
- };
- svgedit.path.getPointFromGrip = function(pt, path) {
- var out = {
- x: pt.x,
- y: pt.y
- };
- if (path.matrix) {
- pt = svgedit.math.transformPoint(out.x, out.y, path.imatrix);
- out.x = pt.x;
- out.y = pt.y;
- }
- out.x /= editorContext_.getCurrentZoom();
- out.y /= editorContext_.getCurrentZoom();
- return out;
- };
- svgedit.path.addPointGrip = function(index, x, y) {
- // create the container of all the point grips
- var pointGripContainer = svgedit.path.getGripContainer();
- var pointGrip = svgedit.utilities.getElem('pathpointgrip_'+index);
- // create it
- if (!pointGrip) {
- pointGrip = document.createElementNS(NS.SVG, 'circle');
- svgedit.utilities.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() {
- if (svgedit.path.path) {
- svgedit.path.path.setSegType();
- }
- });
- }
- if (x && y) {
- // set up the point grip element and display it
- svgedit.utilities.assignAttributes(pointGrip, {
- 'cx': x,
- 'cy': y,
- 'display': 'inline'
- });
- }
- return pointGrip;
- };
- svgedit.path.getGripContainer = function() {
- var c = svgedit.utilities.getElem('pathpointgrip_container');
- if (!c) {
- var parent = svgedit.utilities.getElem('selectorParentGroup');
- c = parent.appendChild(document.createElementNS(NS.SVG, 'g'));
- c.id = 'pathpointgrip_container';
- }
- return c;
- };
- svgedit.path.addCtrlGrip = function(id) {
- var pointGrip = svgedit.utilities.getElem('ctrlpointgrip_'+id);
- if (pointGrip) {return pointGrip;}
- pointGrip = document.createElementNS(NS.SVG, 'circle');
- svgedit.utilities.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
- });
- svgedit.path.getGripContainer().appendChild(pointGrip);
- return pointGrip;
- };
- svgedit.path.getCtrlLine = function(id) {
- var ctrlLine = svgedit.utilities.getElem('ctrlLine_'+id);
- if (ctrlLine) {return ctrlLine;}
- ctrlLine = document.createElementNS(NS.SVG, 'line');
- svgedit.utilities.assignAttributes(ctrlLine, {
- 'id': 'ctrlLine_'+id,
- 'stroke': '#555',
- 'stroke-width': 1,
- 'style': 'pointer-events:none'
- });
- svgedit.path.getGripContainer().appendChild(ctrlLine);
- return ctrlLine;
- };
- svgedit.path.getPointGrip = function(seg, update) {
- var index = seg.index;
- var pointGrip = svgedit.path.addPointGrip(index);
- if (update) {
- var pt = svgedit.path.getGripPt(seg);
- svgedit.utilities.assignAttributes(pointGrip, {
- 'cx': pt.x,
- 'cy': pt.y,
- 'display': 'inline'
- });
- }
- return pointGrip;
- };
- svgedit.path.getControlPoints = function(seg) {
- var item = seg.item;
- var index = seg.index;
- if (!('x1' in item) || !('x2' in item)) {return null;}
- var cpt = {};
- var pointGripContainer = svgedit.path.getGripContainer();
- // Note that this is intentionally not seg.prev.item
- var prev = svgedit.path.path.segs[index-1].item;
- var seg_items = [prev, item];
- var i;
- for (i = 1; i < 3; i++) {
- var id = index + 'c' + i;
- var ctrlLine = cpt['c' + i + '_line'] = svgedit.path.getCtrlLine(id);
- var pt = svgedit.path.getGripPt(seg, {x:item['x' + i], y:item['y' + i]});
- var gpt = svgedit.path.getGripPt(seg, {x:seg_items[i-1].x, y:seg_items[i-1].y});
- svgedit.utilities.assignAttributes(ctrlLine, {
- 'x1': pt.x,
- 'y1': pt.y,
- 'x2': gpt.x,
- 'y2': gpt.y,
- 'display': 'inline'
- });
- cpt['c' + i + '_line'] = ctrlLine;
- // create it
- var pointGrip = cpt['c' + i] = svgedit.path.addCtrlGrip(id);
- svgedit.utilities.assignAttributes(pointGrip, {
- 'cx': pt.x,
- 'cy': pt.y,
- 'display': 'inline'
- });
- cpt['c' + i] = pointGrip;
- }
- return cpt;
- };
- // This replaces the segment at the given index. Type is given as number.
- svgedit.path.replacePathSeg = function(type, index, pts, elem) {
- var path = elem || svgedit.path.path.elem;
- var func = 'createSVGPathSeg' + pathFuncs[type];
- var seg = path[func].apply(path, pts);
- if (svgedit.browser.supportsPathReplaceItem()) {
- path.pathSegList.replaceItem(seg, index);
- } else {
- var segList = path.pathSegList;
- var len = segList.numberOfItems;
- var arr = [];
- var i;
- for (i = 0; i < len; i++) {
- var cur_seg = segList.getItem(i);
- arr.push(cur_seg);
- }
- segList.clear();
- for (i = 0; i < len; i++) {
- if (i == index) {
- segList.appendItem(seg);
- } else {
- segList.appendItem(arr[i]);
- }
- }
- }
- };
- svgedit.path.getSegSelector = function(seg, update) {
- var index = seg.index;
- var segLine = svgedit.utilities.getElem('segline_' + index);
- if (!segLine) {
- var pointGripContainer = svgedit.path.getGripContainer();
- // create segline
- segLine = document.createElementNS(NS.SVG, 'path');
- svgedit.utilities.assignAttributes(segLine, {
- 'id': 'segline_' + index,
- 'display': 'none',
- 'fill': 'none',
- 'stroke': '#0FF',
- 'stroke-width': 2,
- 'style':'pointer-events:none',
- 'd': 'M0,0 0,0'
- });
- pointGripContainer.appendChild(segLine);
- }
- if (update) {
- var prev = seg.prev;
- if (!prev) {
- segLine.setAttribute('display', 'none');
- return segLine;
- }
- var pt = svgedit.path.getGripPt(prev);
- // Set start point
- svgedit.path.replacePathSeg(2, 0, [pt.x, pt.y], segLine);
- var pts = svgedit.path.ptObjToArr(seg.type, seg.item, true);
- var i;
- for (i = 0; i < pts.length; i += 2) {
- pt = svgedit.path.getGripPt(seg, {x:pts[i], y:pts[i+1]});
- pts[i] = pt.x;
- pts[i+1] = pt.y;
- }
- svgedit.path.replacePathSeg(seg.type, 1, pts, segLine);
- }
- return segLine;
- };
- // Function: smoothControlPoints
- // Takes three points and creates a smoother line based on them
- //
- // 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:
- // Array of two "smoothed" point objects
- svgedit.path.smoothControlPoints = function(ct1, ct2, pt) {
- // each point must not be the origin
- var x1 = ct1.x - pt.x,
- 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();
- 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_angleb = angleBetween < Math.PI ? (angleb - angleDiff) : (angleb + angleDiff);
- }
- else {
- 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;
- };
- svgedit.path.Segment = function(index, item) {
- this.selected = false;
- this.index = index;
- this.item = item;
- this.type = item.pathSegType;
- this.ctrlpts = [];
- this.ptgrip = null;
- this.segsel = null;
- };
- svgedit.path.Segment.prototype.showCtrlPts = function(y) {
- var i;
- for (i in this.ctrlpts) {
- if (this.ctrlpts.hasOwnProperty(i)) {
- this.ctrlpts[i].setAttribute('display', y ? 'inline' : 'none');
- }
- }
- };
- svgedit.path.Segment.prototype.selectCtrls = function(y) {
- $('#ctrlpointgrip_' + this.index + 'c1, #ctrlpointgrip_' + this.index + 'c2').
- attr('fill', y ? '#0FF' : '#EEE');
- };
- svgedit.path.Segment.prototype.show = function(y) {
- if (this.ptgrip) {
- this.ptgrip.setAttribute('display', y ? 'inline' : 'none');
- this.segsel.setAttribute('display', y ? 'inline' : 'none');
- // Show/hide all control points if available
- this.showCtrlPts(y);
- }
- };
- svgedit.path.Segment.prototype.select = function(y) {
- if (this.ptgrip) {
- this.ptgrip.setAttribute('stroke', y ? '#0FF' : '#00F');
- this.segsel.setAttribute('display', y ? 'inline' : 'none');
- if (this.ctrlpts) {
- this.selectCtrls(y);
- }
- this.selected = y;
- }
- };
- svgedit.path.Segment.prototype.addGrip = function() {
- this.ptgrip = svgedit.path.getPointGrip(this, true);
- this.ctrlpts = svgedit.path.getControlPoints(this, true);
- this.segsel = svgedit.path.getSegSelector(this, true);
- };
- svgedit.path.Segment.prototype.update = function(full) {
- if (this.ptgrip) {
- var pt = svgedit.path.getGripPt(this);
- svgedit.utilities.assignAttributes(this.ptgrip, {
- 'cx': pt.x,
- 'cy': pt.y
- });
- svgedit.path.getSegSelector(this, true);
- if (this.ctrlpts) {
- if (full) {
- this.item = svgedit.path.path.elem.pathSegList.getItem(this.index);
- this.type = this.item.pathSegType;
- }
- svgedit.path.getControlPoints(this);
- }
- // this.segsel.setAttribute('display', y?'inline':'none');
- }
- };
- svgedit.path.Segment.prototype.move = function(dx, dy) {
- var cur_pts, item = this.item;
- if (this.ctrlpts) {
- cur_pts = [item.x += dx, item.y += dy,
- item.x1, item.y1, item.x2 += dx, item.y2 += dy];
- } else {
- cur_pts = [item.x += dx, item.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,
- next.x1 += dx, next.y1 += dy, next.x2, next.y2];
- svgedit.path.replacePathSeg(this.next.type, this.next.index, next_pts);
- }
- if (this.mate) {
- // The last point of a closed subpath has a 'mate',
- // which is the 'M' segment of the subpath
- item = this.mate.item;
- var pts = [item.x += dx, item.y += dy];
- svgedit.path.replacePathSeg(this.mate.type, this.mate.index, pts);
- // Has no grip, so does not need 'updating'?
- }
- this.update(true);
- if (this.next) {this.next.update(true);}
- };
- svgedit.path.Segment.prototype.setLinked = function(num) {
- var seg, anum, pt;
- if (num == 2) {
- anum = 1;
- seg = this.next;
- if (!seg) {return;}
- pt = this.item;
- } else {
- anum = 2;
- seg = this.prev;
- if (!seg) {return;}
- pt = seg.item;
- }
- var item = seg.item;
- item['x' + anum] = pt.x + (pt.x - this.item['x' + num]);
- item['y' + anum] = pt.y + (pt.y - this.item['y' + num]);
- var pts = [item.x, item.y,
- item.x1, item.y1,
- item.x2, item.y2];
- svgedit.path.replacePathSeg(seg.type, seg.index, pts);
- seg.update(true);
- };
- svgedit.path.Segment.prototype.moveCtrl = function(num, dx, dy) {
- var item = this.item;
- item['x' + num] += dx;
- item['y' + num] += dy;
- 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);
- };
- svgedit.path.Segment.prototype.setType = function(new_type, pts) {
- svgedit.path.replacePathSeg(new_type, this.index, pts);
- this.type = new_type;
- this.item = svgedit.path.path.elem.pathSegList.getItem(this.index);
- this.showCtrlPts(new_type === 6);
- this.ctrlpts = svgedit.path.getControlPoints(this);
- this.update(true);
- };
- svgedit.path.Path = function(elem) {
- if (!elem || elem.tagName !== 'path') {
- throw 'svgedit.path.Path constructed without a <path> element';
- }
- this.elem = elem;
- this.segs = [];
- this.selected_pts = [];
- svgedit.path.path = this;
- this.init();
- };
- // Reset path data
- svgedit.path.Path.prototype.init = function() {
- // Hide all grips, etc
- $(svgedit.path.getGripContainer()).find('*').attr('display', 'none');
- var segList = this.elem.pathSegList;
- var len = segList.numberOfItems;
- this.segs = [];
- this.selected_pts = [];
- this.first_seg = null;
- // Set up segs array
- var i;
- for (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 (i = 0; i < len; 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];
- var start_seg;
- 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
- 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 (this.first_seg == null) {
- this.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
- start_seg = segs[start_i];
- start_seg.next = segs[start_i+1];
- start_seg.next.prev = start_seg;
- start_seg.addGrip();
- seg.addGrip();
- if (!this.first_seg) {
- // Open path, so set first as real first and add grip
- this.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 this;
- };
- svgedit.path.Path.prototype.eachSeg = function(fn) {
- var i;
- var len = this.segs.length;
- for (i = 0; i < len; i++) {
- var ret = fn.call(this.segs[i], i);
- if (ret === false) {break;}
- }
- };
- svgedit.path.Path.prototype.addSeg = function(index) {
- // Adds a new segment
- var seg = this.segs[index];
- if (!seg.prev) {return;}
- var prev = seg.prev;
- var newseg, new_x, new_y;
- switch(seg.item.pathSegType) {
- case 4:
- new_x = (seg.item.x + prev.item.x) / 2;
- new_y = (seg.item.y + prev.item.y) / 2;
- newseg = this.elem.createSVGPathSegLinetoAbs(new_x, new_y);
- break;
- case 6: //make it a curved segment to preserve the shape (WRS)
- // http://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm#Geometric_interpretation
- var p0_x = (prev.item.x + seg.item.x1)/2;
- var p1_x = (seg.item.x1 + seg.item.x2)/2;
- var p2_x = (seg.item.x2 + seg.item.x)/2;
- var p01_x = (p0_x + p1_x)/2;
- var p12_x = (p1_x + p2_x)/2;
- new_x = (p01_x + p12_x)/2;
- var p0_y = (prev.item.y + seg.item.y1)/2;
- var p1_y = (seg.item.y1 + seg.item.y2)/2;
- var p2_y = (seg.item.y2 + seg.item.y)/2;
- var p01_y = (p0_y + p1_y)/2;
- var p12_y = (p1_y + p2_y)/2;
- new_y = (p01_y + p12_y)/2;
- newseg = this.elem.createSVGPathSegCurvetoCubicAbs(new_x, new_y, p0_x, p0_y, p01_x, p01_y);
- var pts = [seg.item.x, seg.item.y, p12_x, p12_y, p2_x, p2_y];
- svgedit.path.replacePathSeg(seg.type, index, pts);
- break;
- }
- svgedit.path.insertItemBefore(this.elem, newseg, index);
- };
- svgedit.path.Path.prototype.deleteSeg = function(index) {
- var seg = this.segs[index];
- var list = this.elem.pathSegList;
- seg.show(false);
- var next = seg.next;
- var pt;
- if (seg.mate) {
- // Make the next point be the "M" point
- 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 item = seg.item;
- pt = [next.item.x, next.item.y];
- svgedit.path.replacePathSeg(2, seg.next.index, pt);
- list.removeItem(index);
- } else {
- list.removeItem(index);
- }
- };
- svgedit.path.Path.prototype.subpathIsClosed = function(index) {
- var closed = false;
- // Check if subpath is already open
- svgedit.path.path.eachSeg(function(i) {
- if (i <= index) {return true;}
- if (this.type === 2) {
- // Found M first, so open
- return false;
- }
- if (this.type === 1) {
- // Found Z first, so closed
- closed = true;
- return false;
- }
- });
- return closed;
- };
- svgedit.path.Path.prototype.removePtFromSelection = function(index) {
- var pos = this.selected_pts.indexOf(index);
- if (pos == -1) {
- return;
- }
- this.segs[index].select(false);
- this.selected_pts.splice(pos, 1);
- };
- svgedit.path.Path.prototype.clearSelection = function() {
- this.eachSeg(function() {
- // 'this' is the segment here
- this.select(false);
- });
- this.selected_pts = [];
- };
- svgedit.path.Path.prototype.storeD = function() {
- this.last_d = this.elem.getAttribute('d');
- };
- svgedit.path.Path.prototype.show = function(y) {
- // Shows this path's segment grips
- this.eachSeg(function() {
- // 'this' is the segment here
- this.show(y);
- });
- if (y) {
- this.selectPt(this.first_seg.index);
- }
- return this;
- };
- // Move selected points
- svgedit.path.Path.prototype.movePts = function(d_x, d_y) {
- var i = this.selected_pts.length;
- while(i--) {
- var seg = this.segs[this.selected_pts[i]];
- seg.move(d_x, d_y);
- }
- };
- svgedit.path.Path.prototype.moveCtrl = function(d_x, d_y) {
- var seg = this.segs[this.selected_pts[0]];
- seg.moveCtrl(this.dragctrl, d_x, d_y);
- if (link_control_pts) {
- seg.setLinked(this.dragctrl);
- }
- };
- svgedit.path.Path.prototype.setSegType = function(new_type) {
- this.storeD();
- var i = this.selected_pts.length;
- 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 = Number(new_type);
- 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));
- */
- //create control points on the line to preserve the shape (WRS)
- var ct1_x = (prev_x + (diff_x/3));
- var ct1_y = (prev_y + (diff_y/3));
- var ct2_x = (cur_x - (diff_x/3));
- var ct2_y = (cur_y - (diff_y/3));
- 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);
- }
- svgedit.path.path.endChanges(text);
- };
- svgedit.path.Path.prototype.selectPt = function(pt, ctrl_num) {
- this.clearSelection();
- if (pt == null) {
- this.eachSeg(function(i) {
- // 'this' is the segment here.
- if (this.prev) {
- pt = i;
- }
- });
- }
- this.addPtsToSelection(pt);
- if (ctrl_num) {
- this.dragctrl = ctrl_num;
- if (link_control_pts) {
- this.segs[pt].setLinked(ctrl_num);
- }
- }
- };
- // Update position of all points
- svgedit.path.Path.prototype.update = function() {
- var elem = this.elem;
- if (svgedit.utilities.getRotationAngle(elem)) {
- this.matrix = svgedit.math.getMatrix(elem);
- this.imatrix = this.matrix.inverse();
- } else {
- this.matrix = null;
- this.imatrix = null;
- }
- this.eachSeg(function(i) {
- this.item = elem.pathSegList.getItem(i);
- this.update();
- });
- return this;
- };
- svgedit.path.getPath_ = function(elem) {
- var p = pathData[elem.id];
- if (!p) {
- p = pathData[elem.id] = new svgedit.path.Path(elem);
- }
- return p;
- };
- svgedit.path.removePath_ = function(id) {
- if (id in pathData) {delete pathData[id];}
- };
- var newcx, newcy, oldcx, oldcy, angle;
- var getRotVals = function(x, y) {
- var dx = x - oldcx;
- var dy = y - oldcy;
- // rotate the point around the old center
- var r = Math.sqrt(dx*dx + dy*dy);
- var 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,
- 'y': r * Math.sin(theta) + newcy};
- };
- // 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
- // TODO: This is still using ye olde transform methods, can probably
- // be optimized or even taken care of by recalculateDimensions
- svgedit.path.recalcRotatedPath = function() {
- var current_path = svgedit.path.path.elem;
- angle = svgedit.utilities.getRotationAngle(current_path, true);
- if (!angle) {return;}
- // selectedBBoxes[0] = svgedit.path.path.oldbbox;
- var box = svgedit.utilities.getBBox(current_path),
- oldbox = svgedit.path.path.oldbbox; //selectedBBoxes[0],
- oldcx = oldbox.x + oldbox.width/2;
- 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
- var 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) {
- i -= 1;
- var seg = list.getItem(i),
- type = seg.pathSegType;
- if (type == 1) {continue;}
- var rvals = getRotVals(seg.x, seg.y),
- points = [rvals.x, rvals.y];
- if (seg.x1 != null && seg.x2 != null) {
- var c_vals1 = getRotVals(seg.x1, seg.y1);
- var c_vals2 = getRotVals(seg.x2, seg.y2);
- points.splice(points.length, 0, c_vals1.x , c_vals1.y, c_vals2.x, c_vals2.y);
- }
- svgedit.path.replacePathSeg(type, i, points);
- } // loop for each point
- 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);
- R_nc.setRotate((angle * 180.0 / Math.PI), newcx, newcy);
- tlist.replaceItem(R_nc,0);
- };
- // ====================================
- // Public API starts here
- svgedit.path.clearData = function() {
- pathData = {};
- };
- }());
|