changes
parent
5be8246a20
commit
65ad8aa92e
|
@ -383,10 +383,12 @@
|
|||
<input id="textPath_offset" class="attr_changer" data-title="Change offset" data-attr="offset" pattern="[0-9]*" />
|
||||
<span>Offset</span>
|
||||
</label>
|
||||
<div class="draginput draginput-button draginput-button-delete" id="tool_release_text_on_path" data-title="Remove path">Remove path</div>
|
||||
<div class="draginput draginput-button" id="tool_change_text_on_path" data-title="Remove path">Change side</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="button full" id="tool_release_text_on_path" data-title="Remove Path">Remove path</div>
|
||||
|
||||
</div>
|
||||
|
||||
<h4>Text</h4>
|
||||
|
@ -477,7 +479,7 @@
|
|||
<div class="button-container clearfix">
|
||||
<div class="button full" id="tool_node_clone" title="Adds a node">Add Node</div>
|
||||
<div class="button full" id="tool_node_delete" title="Delete Node">Delete Node</div>
|
||||
<div class="button full" id="tool_openclose_path" title="Open/close sub-path">Open Path</div>
|
||||
<div class="button full" id="tool_openclose_path" title="Open/close sub-path">Open/close Path</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -22,6 +22,19 @@ MD.Text = function(){
|
|||
editor.selectedChanged(window, [text]);
|
||||
}
|
||||
|
||||
function changeTextOnPath(){
|
||||
const elem = svgCanvas.getSelectedElems()[0];
|
||||
const textPath = elem.querySelector("textPath");
|
||||
if (!textPath) return;
|
||||
const path = svgCanvas.getTextPath(elem);
|
||||
const d = path.getAttribute("d");
|
||||
const reversed = utils.SVGPathEditor.reverse(d);
|
||||
path.setAttribute("d", reversed);
|
||||
const selector = svgCanvas.selectorManager.requestSelector(elem);
|
||||
selector.resize();
|
||||
editor.selectedChanged(window, [elem]);
|
||||
}
|
||||
|
||||
function setBold(){
|
||||
if ($(this).hasClass("disabled")) return;
|
||||
svgCanvas.setBold( !svgCanvas.getBold() );
|
||||
|
@ -40,6 +53,7 @@ MD.Text = function(){
|
|||
|
||||
$('#tool_text_on_path').click(placeTextOnPath);
|
||||
$('#tool_release_text_on_path').click(releaseTextOnPath);
|
||||
$('#tool_change_text_on_path').click(changeTextOnPath);
|
||||
|
||||
$("#tool_bold").on("click", setBold);
|
||||
$("#tool_italic").on("click", setItalic);
|
||||
|
|
|
@ -104,7 +104,7 @@ MD.Editor = function(){
|
|||
svgCanvas.convertToPath();
|
||||
var elems = svgCanvas.getSelectedElems()
|
||||
svgCanvas.selectorManager.requestSelector(elems[0]).reset(elems[0])
|
||||
svgCanvas.selectorManager.requestSelector(elems[0]).selectorRect.setAttribute("display", "none");
|
||||
//svgCanvas.selectorManager.requestSelector(elems[0]).selectorRect.setAttribute("display", "none");
|
||||
svgCanvas.setMode("pathedit");
|
||||
svgCanvas.pathActions.toEditMode(elems[0]);
|
||||
svgCanvas.clearSelection();
|
||||
|
|
|
@ -3360,6 +3360,11 @@ var getMouseTarget = this.getMouseTarget = function(evt) {
|
|||
canvas.pathActions.toEditMode(evt_target);
|
||||
}
|
||||
|
||||
// Reset context
|
||||
if(tagName === "ellipse" || tagName === "circle" || tagName === "line" || tagName === "rect") {
|
||||
editor.convertToPath();
|
||||
}
|
||||
|
||||
if((parent.tagName !== 'g' && parent.tagName !== 'a') ||
|
||||
parent === getCurrentDrawing().getCurrentLayer() ||
|
||||
mouse_target === selectorManager.selectorParentGroup)
|
||||
|
@ -4038,13 +4043,15 @@ var pathActions = canvas.pathActions = function() {
|
|||
var endseg = drawn_path.createSVGPathSegClosePath();
|
||||
seglist.appendItem(newseg);
|
||||
seglist.appendItem(endseg);
|
||||
selectorManager.requestSelector(newpath).showGrips(true)
|
||||
selectorManager.requestSelector(newpath).showGrips(true);
|
||||
} else if(len < 3) {
|
||||
keep = false;
|
||||
|
||||
return keep;
|
||||
}
|
||||
|
||||
stretchy.parentNode.removeChild(stretchy);
|
||||
state.set("canvasMode", "select");
|
||||
|
||||
// this will signal to commit the path
|
||||
element = newpath;
|
||||
|
@ -4527,6 +4534,15 @@ var pathActions = canvas.pathActions = function() {
|
|||
call("changed", selectedElements);
|
||||
},
|
||||
|
||||
reverse: function() {
|
||||
|
||||
var elem = selectedElements[0];
|
||||
if(!elem || !elem.getAttribute("d")) return;
|
||||
|
||||
const d = elem.getAttribute("d");
|
||||
var reversed = reverse(path);
|
||||
},
|
||||
|
||||
clear: function(remove) {
|
||||
current_path = null;
|
||||
if (drawn_path) {
|
||||
|
@ -4623,113 +4639,114 @@ var pathActions = canvas.pathActions = function() {
|
|||
return svgedit.path.path;
|
||||
},
|
||||
opencloseSubPath: function() {
|
||||
var sel_pts = svgedit.path.path.selected_pts;
|
||||
const path = svgedit.path.path;
|
||||
const selPts = path.selected_pts;
|
||||
// Only allow one selected node for now
|
||||
if(sel_pts.length !== 1) return;
|
||||
if (selPts.length !== 1) { return; }
|
||||
|
||||
var elem = svgedit.path.path.elem;
|
||||
var list = elem.pathSegList;
|
||||
const { elem } = path;
|
||||
const list = elem.pathSegList;
|
||||
|
||||
var len = list.numberOfItems;
|
||||
// const len = list.numberOfItems;
|
||||
|
||||
var index = sel_pts[0];
|
||||
const index = selPts[0];
|
||||
|
||||
var open_pt = null;
|
||||
var start_item = null;
|
||||
let openPt = null;
|
||||
let startItem = null;
|
||||
|
||||
// Check if subpath is already open
|
||||
svgedit.path.path.eachSeg(function(i) {
|
||||
if(this.type === 2 && i <= index) {
|
||||
start_item = this.item;
|
||||
path.eachSeg(function (i) {
|
||||
if (this.type === 2 && i <= index) {
|
||||
startItem = this.item;
|
||||
}
|
||||
if(i <= index) return true;
|
||||
if(this.type === 2) {
|
||||
if (i <= index) { return true; }
|
||||
if (this.type === 2) {
|
||||
// Found M first, so open
|
||||
open_pt = i;
|
||||
return false;
|
||||
} else if(this.type === 1) {
|
||||
// Found Z first, so closed
|
||||
open_pt = false;
|
||||
openPt = i;
|
||||
return false;
|
||||
}
|
||||
if (this.type === 1) {
|
||||
// Found Z first, so closed
|
||||
openPt = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if(open_pt == null) {
|
||||
if (openPt == null) {
|
||||
// Single path, so close last seg
|
||||
open_pt = svgedit.path.path.segs.length - 1;
|
||||
openPt = path.segs.length - 1;
|
||||
}
|
||||
|
||||
if(open_pt !== false) {
|
||||
if (openPt !== false) {
|
||||
// Close this path
|
||||
|
||||
// Create a line going to the previous "M"
|
||||
var newseg = elem.createSVGPathSegLinetoAbs(start_item.x, start_item.y);
|
||||
const newseg = elem.createSVGPathSegLinetoAbs(startItem.x, startItem.y);
|
||||
|
||||
var closer = elem.createSVGPathSegClosePath();
|
||||
if(open_pt == svgedit.path.path.segs.length) {
|
||||
const closer = elem.createSVGPathSegClosePath();
|
||||
if (openPt === path.segs.length - 1) {
|
||||
list.appendItem(newseg);
|
||||
list.appendItem(closer);
|
||||
} else {
|
||||
svgedit.path.insertItemBefore(elem, closer, open_pt);
|
||||
svgedit.path.insertItemBefore(elem, newseg, open_pt);
|
||||
list.insertItemBefore(closer, openPt);
|
||||
list.insertItemBefore(newseg, openPt);
|
||||
}
|
||||
|
||||
svgedit.path.path.init().selectPt(open_pt+1);
|
||||
path.init().selectPt(openPt + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// M 1,1 L 2,2 L 3,3 L 1,1 z // open at 2,2
|
||||
// M 2,2 L 3,3 L 1,1
|
||||
|
||||
// M 1,1 L 2,2 L 1,1 z M 4,4 L 5,5 L6,6 L 5,5 z
|
||||
// M 1,1 L 2,2 L 1,1 z [M 4,4] L 5,5 L(M)6,6 L 5,5 z
|
||||
|
||||
var seg = svgedit.path.path.segs[index];
|
||||
const seg = path.segs[index];
|
||||
|
||||
if(seg.mate) {
|
||||
if (seg.mate) {
|
||||
list.removeItem(index); // Removes last "L"
|
||||
list.removeItem(index); // Removes the "Z"
|
||||
svgedit.path.path.init().selectPt(index - 1);
|
||||
path.init().selectPt(index - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
var last_m, z_seg;
|
||||
let lastM; let zSeg;
|
||||
|
||||
// Find this sub-path's closing point and remove
|
||||
for(var i=0; i<list.numberOfItems; i++) {
|
||||
var item = list.getItem(i);
|
||||
for (let i = 0; i < list.numberOfItems; i++) {
|
||||
const item = list.getItem(i);
|
||||
|
||||
if(item.pathSegType === 2) {
|
||||
if (item.pathSegType === 2) {
|
||||
// Find the preceding M
|
||||
last_m = i;
|
||||
} else if(i === index) {
|
||||
lastM = i;
|
||||
} else if (i === index) {
|
||||
// Remove it
|
||||
list.removeItem(last_m);
|
||||
// index--;
|
||||
} else if(item.pathSegType === 1 && index < i) {
|
||||
list.removeItem(lastM);
|
||||
// index--;
|
||||
} else if (item.pathSegType === 1 && index < i) {
|
||||
// Remove the closing seg of this subpath
|
||||
z_seg = i-1;
|
||||
zSeg = i - 1;
|
||||
list.removeItem(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var num = (index - last_m) - 1;
|
||||
let num = (index - lastM) - 1;
|
||||
|
||||
while(num--) {
|
||||
svgedit.path.insertItemBefore(elem, list.getItem(last_m), z_seg);
|
||||
while (num--) {
|
||||
list.insertItemBefore(list.getItem(lastM), zSeg);
|
||||
}
|
||||
|
||||
var pt = list.getItem(last_m);
|
||||
const pt = list.getItem(lastM);
|
||||
|
||||
// Make this point the new "M"
|
||||
svgedit.path.replacePathSeg(2, last_m, [pt.x, pt.y]);
|
||||
svgedit.path.replacePathSeg(2, lastM, [ pt.x, pt.y ]);
|
||||
|
||||
var i = index
|
||||
// i = index; // i is local here, so has no effect; what was the intent for this?
|
||||
|
||||
svgedit.path.path.init().selectPt(0);
|
||||
path.init().selectPt(0);
|
||||
},
|
||||
deletePathNode: function() {
|
||||
if(!pathActions.canDeleteNodes) return;
|
||||
|
@ -5754,7 +5771,6 @@ this.styleToAttr = function(doc) {
|
|||
// Returns:
|
||||
// This function returns false if the set was unsuccessful, true otherwise.
|
||||
this.setSvgString = function(xmlString) {
|
||||
console.log("opened")
|
||||
try {
|
||||
// convert string into XML document
|
||||
var newDoc = svgedit.utilities.text2xml(xmlString);
|
||||
|
|
413
src/js/utils.js
413
src/js/utils.js
|
@ -12,8 +12,8 @@ utils.findGetParameter = function(parameterName) {
|
|||
}
|
||||
|
||||
utils.throttle = function (callback, limit) {
|
||||
var waiting = false;
|
||||
return function () {
|
||||
var waiting = false;
|
||||
return function () {
|
||||
if (!waiting) {
|
||||
callback.apply(this, arguments);
|
||||
waiting = true;
|
||||
|
@ -21,5 +21,414 @@ utils.throttle = function (callback, limit) {
|
|||
waiting = false;
|
||||
}, limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* https://github.com/Pomax/svg-path-reverse
|
||||
*
|
||||
* This code is in the public domain, except in jurisdictions that do
|
||||
* not recognise the public domain, where this code is MIT licensed.
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define(factory);
|
||||
} else if (typeof exports === "object") {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
utils.SVGPathEditor = factory();
|
||||
}
|
||||
}(this, function () {
|
||||
|
||||
/**
|
||||
* Normalise an SVG path to absolute coordinates
|
||||
* and full commands, rather than relative coordinates
|
||||
* and/or shortcut commands.
|
||||
*/
|
||||
function normalizePath(d) {
|
||||
// preprocess "d" so that we have spaces between values
|
||||
d = d.replace(/,/g,' ')
|
||||
.replace(/([^eE])-/g,'$1 -')
|
||||
.replace(/\s*([achlmqstvzACHLMQSTVZ])\s*/g," $1 ")
|
||||
.replace(/\s+/g, ' ');
|
||||
|
||||
// set up the variables used in this function
|
||||
var instructions = d.replace(/([achlmqstvzACHLMQSTVZ])\s?/g,"|$1").split("|"),
|
||||
instructionLength = instructions.length,
|
||||
i,
|
||||
instruction,
|
||||
op,
|
||||
lop,
|
||||
args = [],
|
||||
oargs = [],
|
||||
alen,
|
||||
a,
|
||||
sx = 0,
|
||||
sy = 0,
|
||||
x = 0,
|
||||
y = 0,
|
||||
cx = 0,
|
||||
cy = 0,
|
||||
cx2 = 0,
|
||||
cy2 = 0,
|
||||
rx = 0,
|
||||
ry = 0,
|
||||
xrot = 0,
|
||||
lflag = 0,
|
||||
sweep = 0,
|
||||
normalized = "";
|
||||
|
||||
// we run through the instruction list starting at 1, not 0,
|
||||
// because we split up "|M x y ...." so the first element will
|
||||
// always be an empty string. By design.
|
||||
for (i = 1; i < instructionLength; i++) {
|
||||
|
||||
// which instruction is this?
|
||||
instruction = instructions[i];
|
||||
op = instruction.substring(0,1);
|
||||
lop = op.toLowerCase();
|
||||
|
||||
// what are the arguments? note that we need to convert
|
||||
// all strings into numbers, or + will do silly things.
|
||||
args = instruction.replace(op,'').trim().split(' ').filter(function(v) { return v !== ''; });
|
||||
oargs = args;
|
||||
args = args.map(parseFloat);
|
||||
alen = args.length;
|
||||
|
||||
// we could use a switch, but elaborate code in a "case" with
|
||||
// fallthrough is just horrid to read. So let's use ifthen
|
||||
// statements instead.
|
||||
|
||||
// moveto command (plus possible lineto)
|
||||
if(lop === "m") {
|
||||
normalized += "M ";
|
||||
if (op === "m") {
|
||||
x += args[0];
|
||||
y += args[1];
|
||||
} else {
|
||||
x = args[0];
|
||||
y = args[1];
|
||||
}
|
||||
// records start position, for dealing
|
||||
// with the shape close operator ('Z')
|
||||
sx = x;
|
||||
sy = y;
|
||||
normalized += x + " " + y + " ";
|
||||
if (alen > 2) {
|
||||
for (a = 0; a < alen; a += 2) {
|
||||
if (op === "m") {
|
||||
x += args[a];
|
||||
y += args[a+1];
|
||||
} else {
|
||||
x = args[a];
|
||||
y = args[a+1];
|
||||
}
|
||||
normalized += "L " + x + " " + y + " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lineto commands
|
||||
else if(lop === "l") {
|
||||
for (a = 0; a < alen; a += 2) {
|
||||
if (op === "l") {
|
||||
x += args[a];
|
||||
y += args[a+1];
|
||||
} else {
|
||||
x = args[a];
|
||||
y = args[a+1];
|
||||
}
|
||||
normalized += "L " + x + " " + y + " ";
|
||||
}
|
||||
}
|
||||
else if(lop === "h") {
|
||||
for (a = 0; a < alen; a++) {
|
||||
if (op === "h") {
|
||||
x += args[a];
|
||||
} else {
|
||||
x = args[a];
|
||||
}
|
||||
normalized += "L " + x + " " + y + " ";
|
||||
}
|
||||
}
|
||||
else if(lop === "v") {
|
||||
for (a = 0; a < alen; a++) {
|
||||
if (op === "v") {
|
||||
y += args[a];
|
||||
} else {
|
||||
y = args[a];
|
||||
}
|
||||
normalized += "L " + x + " " + y + " ";
|
||||
}
|
||||
}
|
||||
|
||||
// quadratic curveto commands
|
||||
else if(lop === "q") {
|
||||
for (a = 0; a < alen; a += 4) {
|
||||
if (op === "q") {
|
||||
cx = x + args[a];
|
||||
cy = y + args[a+1];
|
||||
x += args[a+2];
|
||||
y += args[a+3];
|
||||
} else {
|
||||
cx = args[a];
|
||||
cy = args[a+1];
|
||||
x = args[a+2];
|
||||
y = args[a+3];
|
||||
}
|
||||
normalized += "Q " + cx + " " + cy + " " + x + " " + y + " ";
|
||||
}
|
||||
}
|
||||
else if(lop === "t") {
|
||||
for (a = 0; a < alen; a += 2) {
|
||||
// reflect previous cx/cy over x/y
|
||||
cx = x + (x-cx);
|
||||
cy = y + (y-cy);
|
||||
// then get real end point
|
||||
if (op === "t") {
|
||||
x += args[a];
|
||||
y += args[a+1];
|
||||
} else {
|
||||
x = args[a];
|
||||
y = args[a+1];
|
||||
}
|
||||
normalized += "Q " + cx + " " + cy + " " + x + " " + y + " ";
|
||||
}
|
||||
}
|
||||
|
||||
// cubic curveto commands
|
||||
else if(lop === "c") {
|
||||
for (a = 0; a < alen; a += 6) {
|
||||
if (op === "c") {
|
||||
cx = x + args[a];
|
||||
cy = y + args[a+1];
|
||||
cx2 = x + args[a+2];
|
||||
cy2 = y + args[a+3];
|
||||
x += args[a+4];
|
||||
y += args[a+5];
|
||||
} else {
|
||||
cx = args[a];
|
||||
cy = args[a+1];
|
||||
cx2 = args[a+2];
|
||||
cy2 = args[a+3];
|
||||
x = args[a+4]
|
||||
y = args[a+5];
|
||||
}
|
||||
normalized += "C " + cx + " " + cy + " " + cx2 + " " + cy2 + " " + x + " " + y + " ";
|
||||
}
|
||||
}
|
||||
else if(lop === "s") {
|
||||
for (a = 0; a < alen; a += 4) {
|
||||
// reflect previous cx2/cy2 over x/y
|
||||
cx = x + (x-cx2);
|
||||
cy = y + (y-cy2);
|
||||
// then get real control and end point
|
||||
if (op === "s") {
|
||||
cx2 = x + args[a];
|
||||
cy2 = y + args[a+1];
|
||||
x += args[a+2];
|
||||
y += args[a+3];
|
||||
} else {
|
||||
cx2 = args[a];
|
||||
cy2 = args[a+1];
|
||||
x = args[a+2]
|
||||
y = args[a+3];
|
||||
}
|
||||
normalized += "C " + cx + " " + cy + " " + cx2 + " " + cy2 + " " + x + " " + y + " ";
|
||||
}
|
||||
}
|
||||
|
||||
// rx ry x-axis-rotation large-arc-flag sweep-flag x y
|
||||
// a 25,25 -30 0, 1 50,-25
|
||||
|
||||
// arc command
|
||||
else if(lop === "a") {
|
||||
for (a = 0; a < alen; a += 7) {
|
||||
rx = args[a];
|
||||
ry = args[a+1];
|
||||
xrot = args[a+2];
|
||||
|
||||
lflag = oargs[a+3]; // we need the original string to deal with leading zeroes
|
||||
let fixed = false;
|
||||
|
||||
if(lflag.length > 1) {
|
||||
let b1 = parseInt(lflag[0]),
|
||||
b2 = parseInt(lflag[1]),
|
||||
rest = undefined;
|
||||
if(lflag.length > 2) rest = parseFloat(lflag.substring(2));
|
||||
args[a+3] = b1;
|
||||
args.splice(a+4, 0, b2);
|
||||
if (rest!==undefined) args.splice(a+5, 0, rest);
|
||||
fixed = true;
|
||||
}
|
||||
lflag = args[a+3];
|
||||
|
||||
sweep = fixed ? args[a+4] : oargs[a+4]; // we need the original string to deal with leading zeroes
|
||||
if(!fixed && sweep.length > 1) {
|
||||
args[a+4] = parseInt(sweep[0]);
|
||||
args.splice(a+5, 0, parseFloat(sweep.substring(1)));
|
||||
}
|
||||
sweep = args[a+4];
|
||||
|
||||
if (op === "a") {
|
||||
x += args[a+5];
|
||||
y += args[a+6];
|
||||
} else {
|
||||
x = args[a+5];
|
||||
y = args[a+6];
|
||||
}
|
||||
|
||||
normalized += "A " + rx + " " + ry + " " + xrot + " " + lflag + " " + sweep + " " + x + " " + y + " ";
|
||||
}
|
||||
}
|
||||
|
||||
else if(lop === "z") {
|
||||
normalized += "Z ";
|
||||
// not unimportant: path closing changes the current x/y coordinate
|
||||
x = sx;
|
||||
y = sy;
|
||||
}
|
||||
}
|
||||
return normalized.trim();
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverse an SVG path.
|
||||
* As long as the input path is normalised, this is actually really
|
||||
* simple to do. As all pathing commands are symmetrical, meaning
|
||||
* that they render the same when you reverse the coordinate order,
|
||||
* the grand trick here is to reverse the path (making sure to keep
|
||||
* coordinates ordered pairwise) and shift the operators left by
|
||||
* one or two coordinate pairs depending on the operator:
|
||||
*
|
||||
* - Z is removed (after noting it existed),
|
||||
* - L moves to 2 spots earlier (skipping one coordinate),
|
||||
* - Q moves to 2 spots earlier (skipping one coordinate),
|
||||
* - C moves to 4 spots earlier (skipping two coordinates)
|
||||
* and its arguments get reversed,
|
||||
* - the path start becomes M.
|
||||
* - the path end becomes Z iff it was there to begin with.
|
||||
*/
|
||||
function reverseNormalizedPath(normalized) {
|
||||
var terms = normalized.trim().split(' '),
|
||||
term,
|
||||
tlen = terms.length,
|
||||
tlen1 = tlen-1,
|
||||
t,
|
||||
reversed = [],
|
||||
x, y,
|
||||
pair, pairs,
|
||||
shift,
|
||||
matcher = new RegExp('[QAZLCM]',''),
|
||||
closed = terms.slice(-1)[0].toUpperCase() === 'Z';
|
||||
|
||||
for (t = 0; t < tlen; t++) {
|
||||
term = terms[t];
|
||||
|
||||
// Is this an operator? If it is, run through its
|
||||
// argument list, which we know is fixed length.
|
||||
if (matcher.test(term)) {
|
||||
|
||||
// Arc processing relies on not-just-coordinates
|
||||
if (term === "A") {
|
||||
reversed.push(terms[t+5] === '0' ? '1' : '0');
|
||||
reversed.push(terms[t+4]);
|
||||
reversed.push(terms[t+3]);
|
||||
reversed.push(terms[t+2]);
|
||||
reversed.push(terms[t+1]);
|
||||
reversed.push(term);
|
||||
reversed.push(terms[t+7]);
|
||||
reversed.push(terms[t+6]);
|
||||
t += 7;
|
||||
continue;
|
||||
}
|
||||
|
||||
// how many coordinate pairs do we need to read,
|
||||
// and by how many pairs should this operator be
|
||||
// shifted left?
|
||||
else if (term === "C") { pairs = 3; shift = 2; }
|
||||
else if (term === "Q") { pairs = 2; shift = 1; }
|
||||
else if (term === "L") { pairs = 1; shift = 1; }
|
||||
else if (term === "M") { pairs = 1; shift = 0; }
|
||||
else { continue; }
|
||||
|
||||
// do the argument reading and operator shifting
|
||||
if (pairs === shift) {
|
||||
reversed.push(term);
|
||||
}
|
||||
for (pair = 0; pair < pairs; pair++) {
|
||||
if (pair === shift) {
|
||||
reversed.push(term);
|
||||
}
|
||||
x = terms[++t];
|
||||
y = terms[++t];
|
||||
reversed.push(y);
|
||||
reversed.push(x);
|
||||
}
|
||||
}
|
||||
// the code has been set up so that every time we
|
||||
// iterate because of the for() operation, the term
|
||||
// we see is a pathing operator, not a number. As
|
||||
// such, if we get to this "else" the path is malformed.
|
||||
else {
|
||||
var pre = terms.slice(Math.max(t-3,0),3).join(" ");
|
||||
post = terms.slice(t+1,Math.min(t+4,tlen1)).join(" ");
|
||||
range = pre + " [" + term + "] " + post;
|
||||
throw("Error while trying to reverse normalized SVG path, at position "+t+" ("+range+").\n" +
|
||||
"Either the path is not normalised, or it's malformed."); }
|
||||
}
|
||||
|
||||
reversed.push('M');
|
||||
|
||||
// generating the reversed path string involves
|
||||
// running through our transformed terms in reverse.
|
||||
var revstring = "", rlen1 = reversed.length-1, r;
|
||||
for (r = rlen1; r > 0; r--) {
|
||||
revstring += reversed[r] + " ";
|
||||
}
|
||||
if (closed) revstring += "Z";
|
||||
revstring = revstring.replace(/M M/g,'Z M');
|
||||
|
||||
return revstring;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the function that you'll actually want to
|
||||
* make use of, because it lets you reverse individual
|
||||
* subpaths in some <path> "d" attribute.
|
||||
*/
|
||||
function reverseSubPath(path, subpath) {
|
||||
subpath = parseInt(subpath)==subpath ? subpath : false;
|
||||
var path = normalizePath(path),
|
||||
paths = path.replace(/M/g,'|M').split("|"),
|
||||
revpath;
|
||||
paths.splice(0,1);
|
||||
if (subpath !== false && subpath >= paths.length) {
|
||||
return path;
|
||||
}
|
||||
|
||||
if (subpath === false) {
|
||||
paths = paths.map(function(spath) {
|
||||
return reverseNormalizedPath(spath.trim());
|
||||
});
|
||||
} else {
|
||||
var spath = paths[subpath];
|
||||
if (spath) {
|
||||
revpath = reverseNormalizedPath(spath.trim());
|
||||
paths[subpath] = revpath;
|
||||
}
|
||||
}
|
||||
|
||||
return paths.join(" ").replace(/ +/g,' ').trim();
|
||||
};
|
||||
|
||||
/**
|
||||
* Our return object
|
||||
*/
|
||||
var SVGPathEditor = {
|
||||
normalize: normalizePath,
|
||||
reverseNormalized: reverseNormalizedPath,
|
||||
reverse: reverseSubPath
|
||||
};
|
||||
|
||||
return SVGPathEditor;
|
||||
}));
|
||||
|
|
Loading…
Reference in New Issue