Path data processing improvements.
Improved path data saving. Collapse repeated now works at writing path data and doesn't affect pathJS to make further processing easier. Resolves #204.
290
plugins/_path.js
@ -1,8 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var regPathInstructions = /([MmLlHhVvCcSsQqTtAaZz])\s*/,
|
var regPathInstructions = /([MmLlHhVvCcSsQqTtAaZz])\s*/,
|
||||||
regPathData = /[\-+]?\d*\.?\d+([eE][\-+]?\d+)?/g,
|
regPathData = /[-+]?(?:\d*\.\d+|\d+\.?)([eE][-+]?\d+)?/g,
|
||||||
regNumericValues = /[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?/,
|
regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/,
|
||||||
transform2js = require('./_transforms').transform2js,
|
transform2js = require('./_transforms').transform2js,
|
||||||
transformsMultiply = require('./_transforms').transformsMultiply,
|
transformsMultiply = require('./_transforms').transformsMultiply,
|
||||||
collections = require('./_collections.js'),
|
collections = require('./_collections.js'),
|
||||||
@ -18,85 +18,69 @@ var regPathInstructions = /([MmLlHhVvCcSsQqTtAaZz])\s*/,
|
|||||||
* @param {Object} params plugin params
|
* @param {Object} params plugin params
|
||||||
* @return {Array} output array
|
* @return {Array} output array
|
||||||
*/
|
*/
|
||||||
exports.path2js = function(pathString) {
|
exports.path2js = function(path) {
|
||||||
|
if (path.pathJS) return path.pathJS;
|
||||||
|
|
||||||
// JS representation of the path data
|
var paramsLength = { // Number of parameters of every path command
|
||||||
var path = [],
|
H: 1, V: 1, M: 2, L: 2, T: 2, Q: 4, S: 4, C: 6, A: 7,
|
||||||
// current instruction context
|
h: 1, v: 1, m: 2, l: 2, t: 2, q: 4, s: 4, c: 6, a: 7
|
||||||
instruction;
|
},
|
||||||
|
pathData = [], // JS representation of the path data
|
||||||
|
instruction, // current instruction context
|
||||||
|
startMoveto = false;
|
||||||
|
|
||||||
// splitting path string into array like ['M', '10 50', 'L', '20 30']
|
// splitting path string into array like ['M', '10 50', 'L', '20 30']
|
||||||
pathString.split(regPathInstructions).forEach(function(data) {
|
path.attr('d').value.split(regPathInstructions).forEach(function(data) {
|
||||||
if (data) {
|
if (!data) return;
|
||||||
// instruction item
|
if (!startMoveto) {
|
||||||
if (regPathInstructions.test(data)) {
|
if (data == 'M' || data == 'm') {
|
||||||
instruction = data;
|
startMoveto = true;
|
||||||
|
} else return;
|
||||||
|
}
|
||||||
|
|
||||||
// z - instruction w/o data
|
// instruction item
|
||||||
if ('Zz'.indexOf(instruction) > -1) {
|
if (regPathInstructions.test(data)) {
|
||||||
path.push({
|
instruction = data;
|
||||||
instruction: 'z'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// data item
|
|
||||||
} else {
|
|
||||||
|
|
||||||
data = data.trim().match(regPathData);
|
// z - instruction w/o data
|
||||||
|
if (instruction == 'Z' || instruction == 'z') {
|
||||||
|
pathData.push({
|
||||||
|
instruction: 'z'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// data item
|
||||||
|
} else {
|
||||||
|
data = data.match(regPathData);
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
if (data) {
|
data = data.map(Number);
|
||||||
|
|
||||||
var index = 0,
|
// Subsequent moveto pairs of coordinates are threated as implicit lineto commands
|
||||||
pair = 2;
|
// http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
|
||||||
|
if (instruction == 'M' || instruction == 'm') {
|
||||||
data = data.map(function(str) {
|
pathData.push({
|
||||||
return +str;
|
instruction: pathData.length == 0 ? 'M' : instruction,
|
||||||
});
|
data: data.splice(0, 2)
|
||||||
|
});
|
||||||
// deal with very first 'Mm' and multiple points data
|
instruction = instruction == 'M' ? 'L' : 'l';
|
||||||
if ('Mm'.indexOf(instruction) > -1) {
|
}
|
||||||
|
|
||||||
path.push({
|
|
||||||
instruction: instruction,
|
|
||||||
data: data.slice(index, index + pair)
|
|
||||||
});
|
|
||||||
|
|
||||||
index += pair;
|
|
||||||
|
|
||||||
if (data.length) {
|
|
||||||
instruction = instruction === instruction.toLowerCase() ? 'l' : 'L';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('HhVv'.indexOf(instruction) > -1) {
|
|
||||||
pair = 1;
|
|
||||||
} else if ('LlTt'.indexOf(instruction) > -1) {
|
|
||||||
pair = 2;
|
|
||||||
} else if ('QqSs'.indexOf(instruction) > -1) {
|
|
||||||
pair = 4;
|
|
||||||
} else if ('Cc'.indexOf(instruction) > -1) {
|
|
||||||
pair = 6;
|
|
||||||
} else if ('Aa'.indexOf(instruction) > -1) {
|
|
||||||
pair = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(index < data.length) {
|
|
||||||
path.push({
|
|
||||||
instruction: instruction,
|
|
||||||
data: data.slice(index, index + pair)
|
|
||||||
});
|
|
||||||
|
|
||||||
index += pair;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (var pair = paramsLength[instruction]; data.length;) {
|
||||||
|
pathData.push({
|
||||||
|
instruction: instruction,
|
||||||
|
data: data.splice(0, pair)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return path;
|
// First moveto is actually absolute. Subsequent coordinates were separated above.
|
||||||
|
if (pathData.length && pathData[0].instruction == 'm') {
|
||||||
|
pathData[0].instruction = 'M';
|
||||||
|
}
|
||||||
|
path.pathJS = pathData;
|
||||||
|
|
||||||
|
return pathData;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,79 +89,76 @@ exports.path2js = function(pathString) {
|
|||||||
* @param {Array} data input data
|
* @param {Array} data input data
|
||||||
* @return {Array} output data
|
* @return {Array} output data
|
||||||
*/
|
*/
|
||||||
exports.relative2absolute = function(data) {
|
var relative2absolute = exports.relative2absolute = function(data) {
|
||||||
|
|
||||||
var currentPoint = [0, 0],
|
var currentPoint = [0, 0],
|
||||||
subpathPoint = [0, 0],
|
subpathPoint = [0, 0],
|
||||||
i;
|
i;
|
||||||
|
|
||||||
data.forEach(function(item) {
|
data = data.map(function(item) {
|
||||||
|
|
||||||
if (item.instruction === 'M') {
|
var instruction = item.instruction,
|
||||||
|
itemData = item.data && item.data.slice();
|
||||||
|
|
||||||
currentPoint = item.data.slice(-2);
|
if (instruction == 'M') {
|
||||||
subpathPoint = item.data.slice(-2);
|
|
||||||
|
|
||||||
} else if ('mlcsqta'.indexOf(item.instruction) > -1) {
|
set(currentPoint, itemData);
|
||||||
|
set(subpathPoint, itemData);
|
||||||
|
|
||||||
for (i = 0; i < item.data.length; i++) {
|
} else if ('mlcsqt'.indexOf(instruction) > -1) {
|
||||||
if (i % 2 === 0) {
|
|
||||||
item.data[i] += currentPoint[0];
|
|
||||||
} else {
|
|
||||||
item.data[i] += currentPoint[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i > 0) {
|
for (i = 0; i < itemData.length; i++) {
|
||||||
var index = i + 1;
|
itemData[i] += currentPoint[i % 2];
|
||||||
|
}
|
||||||
|
set(currentPoint, itemData);
|
||||||
|
|
||||||
if ('mlt'.indexOf(item.instruction) > -1 && index % 2 === 0) {
|
if (instruction == 'm') {
|
||||||
currentPoint[0] = item.data[i - 1];
|
set(subpathPoint, itemData);
|
||||||
currentPoint[1] = item.data[i];
|
|
||||||
} else if ('qs'.indexOf(item.instruction) > -1 && index % 4 === 0) {
|
|
||||||
currentPoint[0] = item.data[i - 1];
|
|
||||||
currentPoint[1] = item.data[i];
|
|
||||||
} else if (item.instruction === 'c' && index % 6 === 0) {
|
|
||||||
currentPoint[0] = item.data[i - 1];
|
|
||||||
currentPoint[1] = item.data[i];
|
|
||||||
} else if (item.instruction === 'a' && index % 7 === 0) {
|
|
||||||
currentPoint[0] = item.data[i - 1];
|
|
||||||
currentPoint[1] = item.data[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.instruction === 'm') {
|
} else if (instruction == 'a') {
|
||||||
subpathPoint = item.data.slice(-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (item.instruction === 'h') {
|
itemData[5] += currentPoint[0];
|
||||||
|
itemData[6] += currentPoint[1];
|
||||||
|
set(currentPoint, itemData);
|
||||||
|
|
||||||
for (i = 0; i < item.data.length; i++) {
|
} else if (instruction == 'h') {
|
||||||
item.data[i] += currentPoint[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPoint[0] = item.data[item.data.length - 1];
|
itemData[0] += currentPoint[0];
|
||||||
|
currentPoint[0] = itemData[0];
|
||||||
|
|
||||||
} else if (item.instruction === 'v') {
|
} else if (instruction == 'v') {
|
||||||
|
|
||||||
for (i = 0; i < item.data.length; i++) {
|
itemData[0] += currentPoint[1];
|
||||||
item.data[i] += currentPoint[1];
|
currentPoint[1] = itemData[0];
|
||||||
}
|
|
||||||
|
|
||||||
currentPoint[1] = item.data[item.data.length - 1];
|
} else if ('MZLCSQTA'.indexOf(instruction) > -1) {
|
||||||
|
|
||||||
} else {
|
set(currentPoint, itemData);
|
||||||
|
|
||||||
currentPoint = subpathPoint;
|
} else if (instruction == 'H') {
|
||||||
|
|
||||||
|
currentPoint[0] = itemData[0];
|
||||||
|
|
||||||
|
} else if (instruction == 'V') {
|
||||||
|
|
||||||
|
currentPoint[1] = itemData[0];
|
||||||
|
|
||||||
|
} else if (instruction == 'z') {
|
||||||
|
|
||||||
|
set(currentPoint, subpathPoint);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item.instruction = item.instruction.toUpperCase();
|
return instruction == 'z' ?
|
||||||
|
{ instruction: 'z' } :
|
||||||
|
{
|
||||||
|
instruction: instruction.toUpperCase(),
|
||||||
|
data: itemData
|
||||||
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,27 +222,26 @@ exports.applyTransforms = function(elem, path, applyTransformsStroked, floatPrec
|
|||||||
// If an 'a' command can't be transformed directly, convert path to curves.
|
// If an 'a' command can't be transformed directly, convert path to curves.
|
||||||
if (!splittedMatrix.isSimple && path.some(function(i) { return i.instruction == 'a' })) {
|
if (!splittedMatrix.isSimple && path.some(function(i) { return i.instruction == 'a' })) {
|
||||||
var prev;
|
var prev;
|
||||||
path = path.reduce(function(newPath, item){
|
path.forEach(function(item, index, path){
|
||||||
if (item.instruction == 'a') {
|
if (item.instruction == 'a') {
|
||||||
var curves = a2c.apply(0, [0, 0].concat(item.data)),
|
var curves = a2c.apply(0, [0, 0].concat(item.data)),
|
||||||
|
items = [],
|
||||||
curveData;
|
curveData;
|
||||||
while ((curveData = curves.splice(0,6)).length) {
|
while ((curveData = curves.splice(0,6)).length) {
|
||||||
item = {
|
var base = prev.coords;
|
||||||
|
items.push(prev = {
|
||||||
instruction: 'c',
|
instruction: 'c',
|
||||||
data: curveData,
|
data: curveData,
|
||||||
|
coords: [base[0] + item.data[4], base[1] + item.data[5]],
|
||||||
base: prev.coords
|
base: prev.coords
|
||||||
};
|
});
|
||||||
item.coords = [item.base[0] + item.data[4], item.base[1] + item.data[5]];
|
|
||||||
prev = item;
|
|
||||||
newPath.push(item);
|
|
||||||
}
|
}
|
||||||
|
path.splice.apply(path, [index, 1].concat(items));
|
||||||
} else {
|
} else {
|
||||||
newPath.push(item);
|
|
||||||
if (prev) item.base = prev.coords;
|
if (prev) item.base = prev.coords;
|
||||||
prev = item;
|
prev = item;
|
||||||
}
|
}
|
||||||
return newPath;
|
});
|
||||||
}, []);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
path.forEach(function(pathItem) {
|
path.forEach(function(pathItem) {
|
||||||
@ -291,8 +271,8 @@ exports.applyTransforms = function(elem, path, applyTransformsStroked, floatPrec
|
|||||||
|
|
||||||
// then apply it only to the first absoluted M
|
// then apply it only to the first absoluted M
|
||||||
newPoint = transformPoint(matrix.data, pathItem.data[0], pathItem.data[1]);
|
newPoint = transformPoint(matrix.data, pathItem.data[0], pathItem.data[1]);
|
||||||
pathItem.data[0] = pathItem.coords[0] = newPoint[0];
|
set(pathItem.data, newPoint);
|
||||||
pathItem.data[1] = pathItem.coords[1] = newPoint[1];
|
set(pathItem.coords, newPoint);
|
||||||
|
|
||||||
// clear translate() data from transform matrix
|
// clear translate() data from transform matrix
|
||||||
matrix.data[4] = 0;
|
matrix.data[4] = 0;
|
||||||
@ -553,21 +533,63 @@ function computeQuadraticFirstDerivativeRoot(a, b, c) {
|
|||||||
* @param {Object} params plugin params
|
* @param {Object} params plugin params
|
||||||
* @return {String} output path string
|
* @return {String} output path string
|
||||||
*/
|
*/
|
||||||
exports.js2path = function(path, params) {
|
exports.js2path = function(path, data, params) {
|
||||||
|
|
||||||
// output path data string
|
path.pathJS = data;
|
||||||
var pathString = '';
|
|
||||||
|
|
||||||
path.forEach(function(item) {
|
if (params.collapseRepeated) {
|
||||||
|
data = collapseRepeated(data);
|
||||||
|
}
|
||||||
|
|
||||||
pathString += item.instruction + (item.data ? cleanupOutData(item.data, params) : '');
|
path.attr('d').value = data.reduce(function(pathString, item) {
|
||||||
|
return pathString += item.instruction + (item.data ? cleanupOutData(item.data, params) : '');
|
||||||
});
|
}, '');
|
||||||
|
|
||||||
return pathString;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse repeated instructions data
|
||||||
|
*
|
||||||
|
* @param {Array} path input path data
|
||||||
|
* @return {Array} output path data
|
||||||
|
*/
|
||||||
|
function collapseRepeated(data) {
|
||||||
|
|
||||||
|
var prev,
|
||||||
|
prevIndex;
|
||||||
|
|
||||||
|
// copy an array and modifieds item to keep original data untouched
|
||||||
|
data = data.reduce(function(newPath, item) {
|
||||||
|
if (
|
||||||
|
prev && item.data &&
|
||||||
|
item.instruction == prev.instruction
|
||||||
|
) {
|
||||||
|
// concat previous data with current
|
||||||
|
prev = newPath[prevIndex] = {
|
||||||
|
instruction: prev.instruction,
|
||||||
|
data: prev.data.concat(item.data),
|
||||||
|
coords: item.coords,
|
||||||
|
base: prev.base
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newPath.push(item);
|
||||||
|
prev = item;
|
||||||
|
prevIndex = newPath.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPath;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function set(dest, source) {
|
||||||
|
dest[0] = source[source.length - 2];
|
||||||
|
dest[1] = source[source.length - 1];
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
/* Based on code from Snap.svg (Apache 2 license). http://snapsvg.io/
|
/* Based on code from Snap.svg (Apache 2 license). http://snapsvg.io/
|
||||||
* Thanks to Dmitry Baranovskiy for his great work!
|
* Thanks to Dmitry Baranovskiy for his great work!
|
||||||
*/
|
*/
|
||||||
|
@ -51,7 +51,7 @@ exports.fn = function(item, params) {
|
|||||||
error = precision !== false ? +Math.pow(.1, precision).toFixed(precision) : 1e-2;
|
error = precision !== false ? +Math.pow(.1, precision).toFixed(precision) : 1e-2;
|
||||||
hasMarkerMid = item.hasAttr('marker-mid');
|
hasMarkerMid = item.hasAttr('marker-mid');
|
||||||
|
|
||||||
var data = path2js(item.attr('d').value);
|
var data = path2js(item);
|
||||||
|
|
||||||
// TODO: get rid of functions returns
|
// TODO: get rid of functions returns
|
||||||
if (data.length) {
|
if (data.length) {
|
||||||
@ -63,17 +63,11 @@ exports.fn = function(item, params) {
|
|||||||
|
|
||||||
data = filters(data, params);
|
data = filters(data, params);
|
||||||
|
|
||||||
if (params.collapseRepeated) {
|
|
||||||
data = collapseRepeated(data, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.utilizeAbsolute) {
|
if (params.utilizeAbsolute) {
|
||||||
data = convertToMixed(data, params);
|
data = convertToMixed(data, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
item.pathJS = data;
|
js2path(item, data, params);
|
||||||
|
|
||||||
item.attr('d').value = js2path(data, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -91,7 +85,6 @@ function convertToRelative(path) {
|
|||||||
|
|
||||||
var point = [0, 0],
|
var point = [0, 0],
|
||||||
subpathPoint = [0, 0],
|
subpathPoint = [0, 0],
|
||||||
mM = false,
|
|
||||||
baseItem;
|
baseItem;
|
||||||
|
|
||||||
path.forEach(function(item, index) {
|
path.forEach(function(item, index) {
|
||||||
@ -106,18 +99,12 @@ function convertToRelative(path) {
|
|||||||
// recalculate current point
|
// recalculate current point
|
||||||
if ('mcslqta'.indexOf(instruction) > -1) {
|
if ('mcslqta'.indexOf(instruction) > -1) {
|
||||||
|
|
||||||
var newPoint = data.slice(-2);
|
point[0] += data[data.length - 2];
|
||||||
|
point[1] += data[data.length - 1];
|
||||||
point[0] += newPoint[0];
|
|
||||||
point[1] += newPoint[1];
|
|
||||||
|
|
||||||
if (instruction === 'm') {
|
if (instruction === 'm') {
|
||||||
if (index === 0) {
|
subpathPoint[0] = point[0];
|
||||||
instruction = 'M';
|
subpathPoint[1] = point[1];
|
||||||
mM = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
subpathPoint = point.slice(-2);
|
|
||||||
baseItem = item;
|
baseItem = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,20 +121,16 @@ function convertToRelative(path) {
|
|||||||
// convert absolute path data coordinates to relative
|
// convert absolute path data coordinates to relative
|
||||||
// if "M" was not transformed from "m"
|
// if "M" was not transformed from "m"
|
||||||
// M → m
|
// M → m
|
||||||
if (
|
if (instruction === 'M') {
|
||||||
instruction === 'M' &&
|
|
||||||
(!mM || index > 0)
|
|
||||||
) {
|
|
||||||
|
|
||||||
if (index > 0) instruction = 'm';
|
if (index > 0) instruction = 'm';
|
||||||
|
|
||||||
data[0] -= point[0];
|
data[0] -= point[0];
|
||||||
data[1] -= point[1];
|
data[1] -= point[1];
|
||||||
|
|
||||||
point[0] += data[0];
|
subpathPoint[0] = point[0] += data[0];
|
||||||
point[1] += data[1];
|
subpathPoint[1] = point[1] += data[1];
|
||||||
|
|
||||||
subpathPoint = point.slice(-2);
|
|
||||||
baseItem = item;
|
baseItem = item;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -241,11 +224,12 @@ function convertToRelative(path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// !data === z, reset current point
|
// !data === z, reset current point
|
||||||
else {
|
else if (instruction == 'z') {
|
||||||
if(baseItem)
|
if (baseItem) {
|
||||||
item.coords = baseItem.coords;
|
item.coords = baseItem.coords;
|
||||||
point = subpathPoint;
|
}
|
||||||
mM = false;
|
point[0] = subpathPoint[0];
|
||||||
|
point[1] = subpathPoint[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
item.base = index > 0 ? path[index - 1].coords : [0, 0];
|
item.base = index > 0 ? path[index - 1].coords : [0, 0];
|
||||||
@ -267,7 +251,7 @@ function filters(path, params) {
|
|||||||
|
|
||||||
var relSubpoint = [0, 0],
|
var relSubpoint = [0, 0],
|
||||||
pathBase = [0, 0],
|
pathBase = [0, 0],
|
||||||
prev;
|
prev = {};
|
||||||
|
|
||||||
path = path.filter(function(item, index) {
|
path = path.filter(function(item, index) {
|
||||||
|
|
||||||
@ -279,7 +263,7 @@ function filters(path, params) {
|
|||||||
if (instruction === 's') {
|
if (instruction === 's') {
|
||||||
var sdata = [0, 0].concat(data);
|
var sdata = [0, 0].concat(data);
|
||||||
|
|
||||||
if (prev && 'cs'.indexOf(prev.instruction) > -1) {
|
if ('cs'.indexOf(prev.instruction) > -1) {
|
||||||
var pdata = prev.data,
|
var pdata = prev.data,
|
||||||
n = pdata.length;
|
n = pdata.length;
|
||||||
|
|
||||||
@ -351,7 +335,6 @@ function filters(path, params) {
|
|||||||
|
|
||||||
// q
|
// q
|
||||||
else if (
|
else if (
|
||||||
prev &&
|
|
||||||
instruction === 'q' &&
|
instruction === 'q' &&
|
||||||
isCurveStraightLine(
|
isCurveStraightLine(
|
||||||
[ 0, data[0], data[2] ],
|
[ 0, data[0], data[2] ],
|
||||||
@ -372,7 +355,6 @@ function filters(path, params) {
|
|||||||
|
|
||||||
// q (original) + t
|
// q (original) + t
|
||||||
if (
|
if (
|
||||||
prev &&
|
|
||||||
prev.original &&
|
prev.original &&
|
||||||
prev.original.instruction === 'q'
|
prev.original.instruction === 'q'
|
||||||
) {
|
) {
|
||||||
@ -389,7 +371,7 @@ function filters(path, params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// [^qt] + t
|
// [^qt] + t
|
||||||
else if (!prev || 'qt'.indexOf(prev.instruction) === -1) {
|
else if ('qt'.indexOf(prev.instruction) < 0) {
|
||||||
instruction = 'l';
|
instruction = 'l';
|
||||||
data = data.slice(-2);
|
data = data.slice(-2);
|
||||||
}
|
}
|
||||||
@ -422,8 +404,30 @@ function filters(path, params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// collapse repeated commands
|
||||||
|
// h 20 h 30 -> h 50
|
||||||
|
if (
|
||||||
|
params.collapseRepeated &&
|
||||||
|
!hasMarkerMid &&
|
||||||
|
('mhv'.indexOf(instruction) > -1) &&
|
||||||
|
prev.instruction &&
|
||||||
|
instruction == prev.instruction.toLowerCase() &&
|
||||||
|
(
|
||||||
|
(instruction != 'h' && instruction != 'v') ||
|
||||||
|
(prev.data[0] >= 0) == (item.data[0] >= 0)
|
||||||
|
)) {
|
||||||
|
prev.data[0] += data[0];
|
||||||
|
if (instruction != 'h' && instruction != 'v') {
|
||||||
|
prev.data[1] += data[1];
|
||||||
|
}
|
||||||
|
prev.coords = item.coords;
|
||||||
|
if (prev.original) prev.original = null;
|
||||||
|
path[index] = prev;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// convert curves into smooth shorthands
|
// convert curves into smooth shorthands
|
||||||
if (params.curveSmoothShorthands && prev) {
|
if (params.curveSmoothShorthands && prev.instruction) {
|
||||||
|
|
||||||
// curveto
|
// curveto
|
||||||
if (instruction === 'c') {
|
if (instruction === 'c') {
|
||||||
@ -523,6 +527,8 @@ function filters(path, params) {
|
|||||||
// z resets coordinates
|
// z resets coordinates
|
||||||
relSubpoint[0] = pathBase[0];
|
relSubpoint[0] = pathBase[0];
|
||||||
relSubpoint[1] = pathBase[1];
|
relSubpoint[1] = pathBase[1];
|
||||||
|
if (prev.instruction == 'z') return false;
|
||||||
|
prev = item;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,74 +587,15 @@ function convertToMixed(path, params) {
|
|||||||
if (
|
if (
|
||||||
absoluteDataStr.length < relativeDataStr.length &&
|
absoluteDataStr.length < relativeDataStr.length &&
|
||||||
!(
|
!(
|
||||||
params.negativeExtraSpace && instruction == prev.instruction &&
|
params.negativeExtraSpace &&
|
||||||
|
instruction == prev.instruction &&
|
||||||
|
prev.instruction.charCodeAt(0) > 96 &&
|
||||||
absoluteDataStr.length == relativeDataStr.length - 1 &&
|
absoluteDataStr.length == relativeDataStr.length - 1 &&
|
||||||
(data[0] < 0 || 0 < data[0] && data[0] < 1 && prev.data[prev.data.length - 1] % 1)
|
(data[0] < 0 || 0 < data[0] && data[0] < 1 && prev.data[prev.data.length - 1] % 1)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (instruction.toUpperCase() != prev.instruction) {
|
item.instruction = instruction.toUpperCase();
|
||||||
item.instruction = instruction.toUpperCase();
|
item.data = adata;
|
||||||
item.data = adata;
|
|
||||||
} else {
|
|
||||||
prev.data = prev.data.concat(adata);
|
|
||||||
prev.coords = item.coords;
|
|
||||||
path[index] = prev;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (instruction == prev.instruction) {
|
|
||||||
prev.data = prev.data.concat(data);
|
|
||||||
prev.coords = item.coords;
|
|
||||||
path[index] = prev;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = item;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
return path;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collapse repeated instructions data
|
|
||||||
*
|
|
||||||
* @param {Array} path input path data
|
|
||||||
* @return {Array} output path data
|
|
||||||
*/
|
|
||||||
function collapseRepeated(path, params) {
|
|
||||||
|
|
||||||
var prev;
|
|
||||||
|
|
||||||
path = path.filter(function(item) {
|
|
||||||
|
|
||||||
if (
|
|
||||||
!hasMarkerMid &&
|
|
||||||
prev &&
|
|
||||||
item.instruction === prev.instruction &&
|
|
||||||
(
|
|
||||||
'Mmz'.indexOf(item.instruction) > -1 ||
|
|
||||||
'hv'.indexOf(item.instruction) > -1 && (prev.data[0] >= 0) == (item.data[0] >= 0) ||
|
|
||||||
!params.utilizeAbsolute
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// increase previous h or v data with current
|
|
||||||
if ('hv'.indexOf(item.instruction) > -1) {
|
|
||||||
prev.data[0] += item.data[0];
|
|
||||||
} else if (item.instruction.toLowerCase() === 'm') {
|
|
||||||
prev.data[0] += item.data[0];
|
|
||||||
prev.data[1] += item.data[1];
|
|
||||||
// concat previous data with current if it is not z
|
|
||||||
} else if (item.data) {
|
|
||||||
prev.data = prev.data.concat(item.data);
|
|
||||||
}
|
|
||||||
prev.coords = item.coords;
|
|
||||||
|
|
||||||
// filter out current item
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prev = item;
|
prev = item;
|
||||||
|
@ -4,8 +4,14 @@ exports.type = 'perItem';
|
|||||||
|
|
||||||
exports.active = true;
|
exports.active = true;
|
||||||
|
|
||||||
|
exports.params = {
|
||||||
|
collapseRepeated: true,
|
||||||
|
leadingZero: true,
|
||||||
|
negativeExtraSpace: true
|
||||||
|
};
|
||||||
|
|
||||||
var path2js = require('./_path.js').path2js,
|
var path2js = require('./_path.js').path2js,
|
||||||
relative2absolute = require('./_path.js').relative2absolute;
|
js2path = require('./_path.js').js2path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge multiple Paths into one.
|
* Merge multiple Paths into one.
|
||||||
@ -13,13 +19,13 @@ var path2js = require('./_path.js').path2js,
|
|||||||
* @param {Object} item current iteration item
|
* @param {Object} item current iteration item
|
||||||
* @return {Boolean} if false, item will be filtered out
|
* @return {Boolean} if false, item will be filtered out
|
||||||
*
|
*
|
||||||
* @author Kir Belevich
|
* @author Kir Belevich, Lev Solntsev
|
||||||
*/
|
*/
|
||||||
exports.fn = function(item) {
|
exports.fn = function(item, params) {
|
||||||
|
|
||||||
if (!item.isElem() || item.isEmpty()) return;
|
if (!item.isElem() || item.isEmpty()) return;
|
||||||
|
|
||||||
var prevContentItem,
|
var prevContentItem = null,
|
||||||
prevContentItemKeys = null;
|
prevContentItemKeys = null;
|
||||||
|
|
||||||
item.content = item.content.filter(function(contentItem) {
|
item.content = item.content.filter(function(contentItem) {
|
||||||
@ -31,7 +37,7 @@ exports.fn = function(item) {
|
|||||||
contentItem.hasAttr('d')
|
contentItem.hasAttr('d')
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if (prevContentItemKeys == null) {
|
if (!prevContentItemKeys) {
|
||||||
prevContentItemKeys = Object.keys(prevContentItem.attrs);
|
prevContentItemKeys = Object.keys(prevContentItem.attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,15 +47,12 @@ exports.fn = function(item) {
|
|||||||
return key == 'd' ||
|
return key == 'd' ||
|
||||||
prevContentItem.hasAttr(key) &&
|
prevContentItem.hasAttr(key) &&
|
||||||
prevContentItem.attr(key).value == contentItem.attr(key).value;
|
prevContentItem.attr(key).value == contentItem.attr(key).value;
|
||||||
});
|
}),
|
||||||
|
prevPathJS = path2js(prevContentItem),
|
||||||
|
curPathJS = path2js(contentItem);
|
||||||
|
|
||||||
if (equalData) {
|
if (equalData) {
|
||||||
var prevPathJS = prevContentItem.pathJS;
|
js2path(prevContentItem, prevPathJS.concat(curPathJS), params);
|
||||||
if (prevContentItem.pathJS) {
|
|
||||||
prevPathJS.push.apply(prevPathJS, contentItem.pathJS);
|
|
||||||
}
|
|
||||||
|
|
||||||
prevContentItem.attr('d').value += contentItem.attr('d').value.replace(/m/i, 'M');
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,13 @@ exports.params = {
|
|||||||
negativeExtraSpace: true
|
negativeExtraSpace: true
|
||||||
};
|
};
|
||||||
|
|
||||||
var relative2absolute = require('./_path.js').relative2absolute,
|
var _path = require('./_path.js'),
|
||||||
computeCubicBoundingBox = require('./_path.js').computeCubicBoundingBox,
|
relative2absolute = _path.relative2absolute,
|
||||||
computeQuadraticBoundingBox = require('./_path.js').computeQuadraticBoundingBox,
|
computeCubicBoundingBox = _path.computeCubicBoundingBox,
|
||||||
applyTransforms = require('./_path.js').applyTransforms,
|
computeQuadraticBoundingBox = _path.computeQuadraticBoundingBox,
|
||||||
js2path = require('./_path.js').js2path,
|
applyTransforms = _path.applyTransforms,
|
||||||
|
js2path = _path.js2path,
|
||||||
|
path2js = _path.path2js,
|
||||||
EXTEND = require('whet.extend');
|
EXTEND = require('whet.extend');
|
||||||
|
|
||||||
exports.fn = function(data, params) {
|
exports.fn = function(data, params) {
|
||||||
@ -52,7 +54,7 @@ exports.fn = function(data, params) {
|
|||||||
var svgElem = item,
|
var svgElem = item,
|
||||||
pathElem = svgElem.content[0],
|
pathElem = svgElem.content[0],
|
||||||
// get absoluted Path data
|
// get absoluted Path data
|
||||||
path = relative2absolute(EXTEND(true, [], pathElem.pathJS)),
|
path = relative2absolute(EXTEND(true, [], path2js(pathElem))),
|
||||||
xs = [],
|
xs = [],
|
||||||
ys = [],
|
ys = [],
|
||||||
cubicСontrolPoint = [0, 0],
|
cubicСontrolPoint = [0, 0],
|
||||||
@ -312,7 +314,7 @@ exports.fn = function(data, params) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// save new
|
// save new
|
||||||
pathElem.attr('d').value = js2path(path, params);
|
js2path(pathElem, path, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
@@@
|
@@@
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M10 50m10 10"/>
|
<path d="M20 60"/>
|
||||||
<path d="M10 50l10 10"/>
|
<path d="M10 50l10 10"/>
|
||||||
<path d="M10 50l10-20 20 30"/>
|
<path d="M10 50l10-20 20 30"/>
|
||||||
<path d="M10 50l10-20 20 30"/>
|
<path d="M10 50l10-20 20 30"/>
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@ -1,21 +1,21 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="m 0,0"/>
|
<path d="m 0,0"/>
|
||||||
<path d="l 0,0"/>
|
<path d="m 0,0l 0,0"/>
|
||||||
<path d="h 0"/>
|
<path d="m 0,0h 0"/>
|
||||||
<path d="v 0"/>
|
<path d="m 0,0v 0"/>
|
||||||
<path d="c 0,0 0,0 0,0 s 0,0 0,0"/>
|
<path d="m 0,0c 0,0 0,0 0,0 s 0,0 0,0"/>
|
||||||
<path d="q 0,0 0,0 t 0,0"/>
|
<path d="m 0,0q 0,0 0,0 t 0,0"/>
|
||||||
<path d="a 25,25 -30 0,1 0,0"/>
|
<path d="m 0,0a 25,25 -30 0,1 0,0"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
@@@
|
@@@
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M0 0"/>
|
<path d="M0 0"/>
|
||||||
<path d=""/>
|
<path d="M0 0"/>
|
||||||
<path d=""/>
|
<path d="M0 0"/>
|
||||||
<path d=""/>
|
<path d="M0 0"/>
|
||||||
<path d=""/>
|
<path d="M0 0"/>
|
||||||
<path d=""/>
|
<path d="M0 0"/>
|
||||||
<path d=""/>
|
<path d="M0 0"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 417 B After Width: | Height: | Size: 471 B |
@ -9,6 +9,7 @@
|
|||||||
<path d="M10 50v30v50"/>
|
<path d="M10 50v30v50"/>
|
||||||
<path d="M10 50L10 80L10 0"/>
|
<path d="M10 50L10 80L10 0"/>
|
||||||
<path d="M10 50L10 10L10 80"/>
|
<path d="M10 50L10 10L10 80"/>
|
||||||
|
<path d="M10 50l10 10l20 20l10 10"/>
|
||||||
<path d="M10 50L80 50L0 50"/>
|
<path d="M10 50L80 50L0 50"/>
|
||||||
<path d="M10 50L0 50L80 50"/>
|
<path d="M10 50L0 50L80 50"/>
|
||||||
<path d="M10 50L0 50M80 50M30 10L10 80"/>
|
<path d="M10 50L0 50M80 50M30 10L10 80"/>
|
||||||
@ -27,6 +28,7 @@
|
|||||||
<path d="M10 50v80"/>
|
<path d="M10 50v80"/>
|
||||||
<path d="M10 50v30V0"/>
|
<path d="M10 50v30V0"/>
|
||||||
<path d="M10 50V10v70"/>
|
<path d="M10 50V10v70"/>
|
||||||
|
<path d="M10 50l10 10 20 20 10 10"/>
|
||||||
<path d="M10 50h70H0"/>
|
<path d="M10 50h70H0"/>
|
||||||
<path d="M10 50H0h80"/>
|
<path d="M10 50H0h80"/>
|
||||||
<path d="M10 50H0m30-40L10 80"/>
|
<path d="M10 50H0m30-40L10 80"/>
|
||||||
|
Before Width: | Height: | Size: 898 B After Width: | Height: | Size: 980 B |
@ -1,7 +1,7 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M 0,0 z"/>
|
<path d="M 0,0 z"/>
|
||||||
<path d="M 10,10 z"/>
|
<path d="M 10,10 z"/>
|
||||||
<path d="M 20,20"/>
|
<path d="M 20,20 l 10,10 M 30,0 c 10,0 20,10 20,20"/>
|
||||||
<path d="M 30,30 z"/>
|
<path d="M 30,30 z"/>
|
||||||
<path d="M 30,30 z" fill="#f00"/>
|
<path d="M 30,30 z" fill="#f00"/>
|
||||||
<path d="M 40,40 z"/>
|
<path d="M 40,40 z"/>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
@@@
|
@@@
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M 0,0 zM 10,10 zM 20,20M 30,30 z"/>
|
<path d="M0 0zM10 10zM20 20l10 10M30 0c10 0 20 10 20 20M30 30z"/>
|
||||||
<path d="M 30,30 z" fill="#f00"/>
|
<path d="M 30,30 z" fill="#f00"/>
|
||||||
<path d="M 40,40 zM 50,50 z"/>
|
<path d="M40 40zM50 50z"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 465 B |
@ -18,8 +18,8 @@
|
|||||||
@@@
|
@@@
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M 0,0 zM 10,10 zM 20,20M 30,30 z" fill="#fff" stroke="#333"/>
|
<path d="M0 0zM10 10zM20 20 30 30z" fill="#fff" stroke="#333"/>
|
||||||
<path d="M 30,30 z" fill="#f00"/>
|
<path d="M 30,30 z" fill="#f00"/>
|
||||||
<path d="M 40,40 zM 50,50 zM 40,40M 50,50"/>
|
<path d="M40 40zM50 50zM40 40 50 50"/>
|
||||||
<path d="M 40,40 zM 50,50 zM 40,40M 50,50M 50,50 z" fill="#fff" stroke="#333"/>
|
<path d="M40 40zM50 50zM40 40 50 50 50 50z" fill="#fff" stroke="#333"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 946 B After Width: | Height: | Size: 925 B |