fabric.js/src/shapes/path.class.js

968 lines
27 KiB
JavaScript
Raw Normal View History

(function(global) {
'use strict';
var fabric = global.fabric || (global.fabric = { }),
min = fabric.util.array.min,
max = fabric.util.array.max,
extend = fabric.util.object.extend,
_toString = Object.prototype.toString,
drawArc = fabric.util.drawArc,
commandLengths = {
m: 2,
l: 2,
h: 1,
v: 1,
c: 6,
s: 4,
q: 4,
t: 2,
a: 7
},
repeatedCommands = {
m: 'l',
M: 'L'
};
if (fabric.Path) {
fabric.warn('fabric.Path is already defined');
2010-06-09 22:34:55 +00:00
return;
}
/**
* Path class
2013-04-25 18:21:32 +00:00
* @class fabric.Path
* @extends fabric.Object
* @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#path_and_pathgroup}
2013-10-05 18:21:28 +00:00
* @see {@link fabric.Path#initialize} for constructor definition
*/
fabric.Path = fabric.util.createClass(fabric.Object, /** @lends fabric.Path.prototype */ {
/**
2012-12-02 10:53:38 +00:00
* Type of an object
* @type String
* @default
*/
2010-06-09 22:34:55 +00:00
type: 'path',
/**
* Array of path points
* @type Array
* @default
*/
path: null,
2014-09-18 12:51:46 +00:00
/**
* Minimum X from points values, necessary to offset points
* @type Number
* @default
*/
minX: 0,
/**
* Minimum Y from points values, necessary to offset points
* @type Number
* @default
*/
minY: 0,
2010-06-09 22:34:55 +00:00
/**
* Constructor
* @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
* @param {Object} [options] Options object
* @return {fabric.Path} thisArg
2010-06-09 22:34:55 +00:00
*/
initialize: function(path, options) {
options = options || { };
2010-06-09 22:34:55 +00:00
this.setOptions(options);
2010-06-09 22:34:55 +00:00
if (!path) {
throw new Error('`path` argument is required');
2010-06-09 22:34:55 +00:00
}
var fromArray = _toString.call(path) === '[object Array]';
2010-06-09 22:34:55 +00:00
this.path = fromArray
? path
// one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values)
: path.match && path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);
if (!this.path) {
return;
}
2010-06-09 22:34:55 +00:00
if (!fromArray) {
2012-11-29 00:46:16 +00:00
this.path = this._parsePath();
}
2014-09-18 12:51:46 +00:00
2015-04-16 09:31:52 +00:00
this._setPositionDimensions(options);
if (options.sourcePath) {
this.setSourcePath(options.sourcePath);
}
},
/**
* @private
2015-04-16 09:47:04 +00:00
* @param {Object} options Options object
*/
2015-04-16 09:31:52 +00:00
_setPositionDimensions: function(options) {
2014-09-18 12:51:46 +00:00
var calcDim = this._parseDimensions();
2014-09-18 12:51:46 +00:00
this.minX = calcDim.left;
this.minY = calcDim.top;
this.width = calcDim.width;
this.height = calcDim.height;
2015-04-16 09:31:52 +00:00
if (typeof options.left === 'undefined') {
this.left = calcDim.left + (this.originX === 'center'
? this.width / 2
: this.originX === 'right'
? this.width
: 0);
}
2015-04-16 09:31:52 +00:00
if (typeof options.top === 'undefined') {
this.top = calcDim.top + (this.originY === 'center'
? this.height / 2
: this.originY === 'bottom'
? this.height
: 0);
}
2014-09-18 12:51:46 +00:00
this.pathOffset = this.pathOffset || {
x: this.minX + this.width / 2,
y: this.minY + this.height / 2
};
2010-06-09 22:34:55 +00:00
},
2010-10-19 20:27:24 +00:00
/**
* @private
* @param {CanvasRenderingContext2D} ctx context to render path on
2010-10-19 20:27:24 +00:00
*/
2014-09-18 12:51:46 +00:00
_render: function(ctx) {
var current, // current instruction
previous = null,
subpathStartX = 0,
subpathStartY = 0,
x = 0, // current x
2010-06-09 22:34:55 +00:00
y = 0, // current y
controlX = 0, // current control point x
controlY = 0, // current control point y
tempX,
2010-06-09 22:34:55 +00:00
tempY,
2014-09-18 12:51:46 +00:00
l = -this.pathOffset.x,
t = -this.pathOffset.y;
2014-09-18 12:51:46 +00:00
if (this.group && this.group.type === 'path-group') {
l = 0;
t = 0;
2014-07-18 14:27:29 +00:00
}
2014-09-18 12:51:46 +00:00
ctx.beginPath();
for (var i = 0, len = this.path.length; i < len; ++i) {
2010-06-09 22:34:55 +00:00
current = this.path[i];
2010-06-09 22:34:55 +00:00
switch (current[0]) { // first letter
2010-06-09 22:34:55 +00:00
case 'l': // lineto, relative
x += current[1];
y += current[2];
ctx.lineTo(x + l, y + t);
break;
2010-06-09 22:34:55 +00:00
case 'L': // lineto, absolute
x = current[1];
y = current[2];
ctx.lineTo(x + l, y + t);
break;
2010-06-09 22:34:55 +00:00
case 'h': // horizontal lineto, relative
x += current[1];
ctx.lineTo(x + l, y + t);
break;
2010-06-09 22:34:55 +00:00
case 'H': // horizontal lineto, absolute
x = current[1];
ctx.lineTo(x + l, y + t);
break;
2010-06-09 22:34:55 +00:00
case 'v': // vertical lineto, relative
y += current[1];
ctx.lineTo(x + l, y + t);
break;
2010-06-09 22:34:55 +00:00
case 'V': // verical lineto, absolute
y = current[1];
ctx.lineTo(x + l, y + t);
break;
2010-06-09 22:34:55 +00:00
case 'm': // moveTo, relative
x += current[1];
y += current[2];
subpathStartX = x;
subpathStartY = y;
ctx.moveTo(x + l, y + t);
2010-06-09 22:34:55 +00:00
break;
2010-06-09 22:34:55 +00:00
case 'M': // moveTo, absolute
x = current[1];
y = current[2];
subpathStartX = x;
subpathStartY = y;
ctx.moveTo(x + l, y + t);
2010-06-09 22:34:55 +00:00
break;
2010-06-09 22:34:55 +00:00
case 'c': // bezierCurveTo, relative
tempX = x + current[5];
tempY = y + current[6];
controlX = x + current[3];
controlY = y + current[4];
ctx.bezierCurveTo(
x + current[1] + l, // x1
y + current[2] + t, // y1
controlX + l, // x2
controlY + t, // y2
tempX + l,
tempY + t
);
x = tempX;
y = tempY;
break;
2010-06-09 22:34:55 +00:00
case 'C': // bezierCurveTo, absolute
x = current[5];
y = current[6];
controlX = current[3];
controlY = current[4];
ctx.bezierCurveTo(
current[1] + l,
current[2] + t,
controlX + l,
controlY + t,
x + l,
2010-06-09 22:34:55 +00:00
y + t
);
break;
2010-06-09 22:34:55 +00:00
case 's': // shorthand cubic bezierCurveTo, relative
2010-06-09 22:34:55 +00:00
// transform to absolute x,y
tempX = x + current[3];
tempY = y + current[4];
if (previous[0].match(/[CcSs]/) === null) {
// If there is no previous command or if the previous command was not a C, c, S, or s,
// the control point is coincident with the current point
controlX = x;
controlY = y;
2015-01-08 00:34:56 +00:00
}
else {
// calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
2010-06-09 22:34:55 +00:00
ctx.bezierCurveTo(
controlX + l,
controlY + t,
x + current[1] + l,
y + current[2] + t,
tempX + l,
tempY + t
);
// set control point to 2nd one of this command
2013-11-20 11:47:03 +00:00
// "... the first control point is assumed to be
// the reflection of the second control point on
// the previous command relative to the current point."
controlX = x + current[1];
controlY = y + current[2];
2010-06-09 22:34:55 +00:00
x = tempX;
y = tempY;
break;
2010-06-09 22:34:55 +00:00
case 'S': // shorthand cubic bezierCurveTo, absolute
tempX = current[3];
tempY = current[4];
if (previous[0].match(/[CcSs]/) === null) {
// If there is no previous command or if the previous command was not a C, c, S, or s,
// the control point is coincident with the current point
controlX = x;
controlY = y;
2015-01-08 00:34:56 +00:00
}
else {
// calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
2010-06-09 22:34:55 +00:00
ctx.bezierCurveTo(
controlX + l,
controlY + t,
current[1] + l,
current[2] + t,
tempX + l,
tempY + t
);
x = tempX;
y = tempY;
// set control point to 2nd one of this command
2013-11-20 11:47:03 +00:00
// "... the first control point is assumed to be
// the reflection of the second control point on
// the previous command relative to the current point."
controlX = current[1];
controlY = current[2];
2010-06-09 22:34:55 +00:00
break;
2010-06-09 22:34:55 +00:00
case 'q': // quadraticCurveTo, relative
// transform to absolute x,y
tempX = x + current[3];
tempY = y + current[4];
controlX = x + current[1];
controlY = y + current[2];
2010-06-09 22:34:55 +00:00
ctx.quadraticCurveTo(
controlX + l,
controlY + t,
tempX + l,
tempY + t
2010-06-09 22:34:55 +00:00
);
x = tempX;
y = tempY;
2010-06-09 22:34:55 +00:00
break;
2010-06-09 22:34:55 +00:00
case 'Q': // quadraticCurveTo, absolute
tempX = current[3];
tempY = current[4];
ctx.quadraticCurveTo(
current[1] + l,
current[2] + t,
tempX + l,
tempY + t
);
x = tempX;
y = tempY;
2010-06-09 22:34:55 +00:00
controlX = current[1];
controlY = current[2];
break;
case 't': // shorthand quadraticCurveTo, relative
// transform to absolute x,y
tempX = x + current[1];
tempY = y + current[2];
if (previous[0].match(/[QqTt]/) === null) {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
controlX = x;
controlY = y;
}
else {
// calculate reflection of previous control point
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
2010-06-09 22:34:55 +00:00
ctx.quadraticCurveTo(
controlX + l,
controlY + t,
tempX + l,
tempY + t
2010-06-09 22:34:55 +00:00
);
x = tempX;
y = tempY;
2010-06-09 22:34:55 +00:00
break;
2010-06-09 22:34:55 +00:00
case 'T':
tempX = current[1];
tempY = current[2];
if (previous[0].match(/[QqTt]/) === null) {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
controlX = x;
controlY = y;
}
else {
// calculate reflection of previous control point
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
2010-06-09 22:34:55 +00:00
ctx.quadraticCurveTo(
controlX + l,
controlY + t,
tempX + l,
tempY + t
2010-06-09 22:34:55 +00:00
);
x = tempX;
y = tempY;
2010-06-09 22:34:55 +00:00
break;
2010-06-09 22:34:55 +00:00
case 'a':
// TODO: optimize this
drawArc(ctx, x + l, y + t, [
current[1],
current[2],
current[3],
current[4],
current[5],
current[6] + x + l,
current[7] + y + t
]);
x += current[6];
y += current[7];
2010-06-09 22:34:55 +00:00
break;
2010-06-09 22:34:55 +00:00
case 'A':
// TODO: optimize this
drawArc(ctx, x + l, y + t, [
current[1],
current[2],
current[3],
current[4],
current[5],
current[6] + l,
current[7] + t
]);
x = current[6];
y = current[7];
2010-06-09 22:34:55 +00:00
break;
2010-06-09 22:34:55 +00:00
case 'z':
case 'Z':
x = subpathStartX;
y = subpathStartY;
2010-06-09 22:34:55 +00:00
ctx.closePath();
break;
}
previous = current;
2010-06-09 22:34:55 +00:00
}
2014-09-18 12:51:46 +00:00
this._renderFill(ctx);
this._renderStroke(ctx);
2010-06-09 22:34:55 +00:00
},
2010-06-09 22:34:55 +00:00
/**
* Returns string representation of an instance
* @return {String} string representation of an instance
*/
toString: function() {
return '#<fabric.Path (' + this.complexity() +
'): { "top": ' + this.top + ', "left": ' + this.left + ' }>';
2010-06-09 22:34:55 +00:00
},
2010-06-09 22:34:55 +00:00
/**
2010-10-19 20:27:24 +00:00
* Returns object representation of an instance
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {Object} object representation of an instance
2010-06-09 22:34:55 +00:00
*/
toObject: function(propertiesToInclude) {
var o = extend(this.callSuper('toObject', propertiesToInclude), {
path: this.path.map(function(item) { return item.slice() }),
pathOffset: this.pathOffset
2010-06-09 22:34:55 +00:00
});
if (this.sourcePath) {
o.sourcePath = this.sourcePath;
}
2010-06-09 22:34:55 +00:00
if (this.transformMatrix) {
o.transformMatrix = this.transformMatrix;
}
return o;
},
2010-06-09 22:34:55 +00:00
/**
2010-10-19 20:27:24 +00:00
* Returns dataless object representation of an instance
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {Object} object representation of an instance
2010-06-09 22:34:55 +00:00
*/
toDatalessObject: function(propertiesToInclude) {
var o = this.toObject(propertiesToInclude);
2010-06-09 22:34:55 +00:00
if (this.sourcePath) {
o.path = this.sourcePath;
}
delete o.sourcePath;
return o;
},
/* _TO_SVG_START_ */
/**
* Returns svg representation of an instance
* @param {Function} [reviver] Method for further parsing of svg representation.
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var chunks = [],
markup = this._createBaseSVGMarkup(), addTransform = '';
for (var i = 0, len = this.path.length; i < len; i++) {
chunks.push(this.path[i].join(' '));
}
var path = chunks.join(' ');
2014-09-13 12:05:12 +00:00
if (!(this.group && this.group.type === 'path-group')) {
2015-01-19 22:07:09 +00:00
addTransform = ' translate(' + (-this.pathOffset.x) + ', ' + (-this.pathOffset.y) + ') ';
}
markup.push(
//jscs:disable validateIndentation
2014-08-05 10:51:20 +00:00
'<path ',
'd="', path,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(), addTransform,
2014-08-05 10:51:20 +00:00
this.getSvgTransformMatrix(), '" stroke-linecap="round" ',
'/>\n'
//jscs:enable validateIndentation
);
return reviver ? reviver(markup.join('')) : markup.join('');
},
/* _TO_SVG_END_ */
/**
* Returns number representation of an instance complexity
* @return {Number} complexity of this instance
*/
2010-06-09 22:34:55 +00:00
complexity: function() {
return this.path.length;
},
2010-10-19 20:27:24 +00:00
/**
* @private
*/
2010-06-09 22:34:55 +00:00
_parsePath: function() {
var result = [ ],
coords = [ ],
currentPath,
parsed,
re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig,
match,
coordsStr;
for (var i = 0, coordsParsed, len = this.path.length; i < len; i++) {
2010-06-09 22:34:55 +00:00
currentPath = this.path[i];
coordsStr = currentPath.slice(1).trim();
coords.length = 0;
while ((match = re.exec(coordsStr))) {
coords.push(match[0]);
}
coordsParsed = [ currentPath.charAt(0) ];
for (var j = 0, jlen = coords.length; j < jlen; j++) {
parsed = parseFloat(coords[j]);
if (!isNaN(parsed)) {
coordsParsed.push(parsed);
}
}
var command = coordsParsed[0],
commandLength = commandLengths[command.toLowerCase()],
repeatedCommand = repeatedCommands[command] || command;
if (coordsParsed.length - 1 > commandLength) {
for (var k = 1, klen = coordsParsed.length; k < klen; k += commandLength) {
result.push([ command ].concat(coordsParsed.slice(k, k + commandLength)));
command = repeatedCommand;
}
}
else {
result.push(coordsParsed);
}
2010-06-09 22:34:55 +00:00
}
2010-06-09 22:34:55 +00:00
return result;
},
/**
* @private
*/
2010-06-09 22:34:55 +00:00
_parseDimensions: function() {
2014-09-18 12:51:46 +00:00
var aX = [],
aY = [],
2014-09-18 12:51:46 +00:00
current, // current instruction
previous = null,
subpathStartX = 0,
subpathStartY = 0,
x = 0, // current x
y = 0, // current y
controlX = 0, // current control point x
controlY = 0, // current control point y
tempX,
tempY,
bounds;
2014-09-18 12:51:46 +00:00
for (var i = 0, len = this.path.length; i < len; ++i) {
2014-09-18 12:51:46 +00:00
current = this.path[i];
2014-09-18 12:51:46 +00:00
switch (current[0]) { // first letter
2014-09-18 12:51:46 +00:00
case 'l': // lineto, relative
x += current[1];
y += current[2];
bounds = [ ];
break;
2013-10-25 09:18:44 +00:00
2014-09-18 12:51:46 +00:00
case 'L': // lineto, absolute
x = current[1];
y = current[2];
bounds = [ ];
break;
2013-10-25 09:18:44 +00:00
2014-09-18 12:51:46 +00:00
case 'h': // horizontal lineto, relative
x += current[1];
bounds = [ ];
break;
2013-10-25 09:18:44 +00:00
2014-09-18 12:51:46 +00:00
case 'H': // horizontal lineto, absolute
x = current[1];
bounds = [ ];
break;
2013-10-25 09:18:44 +00:00
2014-09-18 12:51:46 +00:00
case 'v': // vertical lineto, relative
y += current[1];
bounds = [ ];
break;
2013-10-25 09:18:44 +00:00
2014-09-18 12:51:46 +00:00
case 'V': // verical lineto, absolute
y = current[1];
bounds = [ ];
break;
2013-10-25 09:23:24 +00:00
2014-09-18 12:51:46 +00:00
case 'm': // moveTo, relative
x += current[1];
y += current[2];
subpathStartX = x;
subpathStartY = y;
bounds = [ ];
break;
case 'M': // moveTo, absolute
x = current[1];
y = current[2];
subpathStartX = x;
subpathStartY = y;
bounds = [ ];
break;
case 'c': // bezierCurveTo, relative
tempX = x + current[5];
tempY = y + current[6];
controlX = x + current[3];
controlY = y + current[4];
bounds = fabric.util.getBoundsOfCurve(x, y,
x + current[1], // x1
y + current[2], // y1
controlX, // x2
controlY, // y2
tempX,
tempY
);
x = tempX;
y = tempY;
break;
case 'C': // bezierCurveTo, absolute
x = current[5];
y = current[6];
controlX = current[3];
controlY = current[4];
bounds = fabric.util.getBoundsOfCurve(x, y,
current[1],
current[2],
controlX,
controlY,
x,
y
);
break;
case 's': // shorthand cubic bezierCurveTo, relative
// transform to absolute x,y
tempX = x + current[3];
tempY = y + current[4];
2013-10-25 09:23:24 +00:00
if (previous[0].match(/[CcSs]/) === null) {
// If there is no previous command or if the previous command was not a C, c, S, or s,
// the control point is coincident with the current point
controlX = x;
controlY = y;
}
else {
// calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
2014-09-18 12:51:46 +00:00
bounds = fabric.util.getBoundsOfCurve(x, y,
controlX,
controlY,
x + current[1],
y + current[2],
tempX,
tempY
);
// set control point to 2nd one of this command
// "... the first control point is assumed to be
// the reflection of the second control point on
// the previous command relative to the current point."
controlX = x + current[1];
controlY = y + current[2];
x = tempX;
y = tempY;
break;
2013-10-25 09:23:24 +00:00
2014-09-18 12:51:46 +00:00
case 'S': // shorthand cubic bezierCurveTo, absolute
tempX = current[3];
tempY = current[4];
if (previous[0].match(/[CcSs]/) === null) {
// If there is no previous command or if the previous command was not a C, c, S, or s,
// the control point is coincident with the current point
controlX = x;
controlY = y;
}
else {
// calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
2014-09-18 12:51:46 +00:00
bounds = fabric.util.getBoundsOfCurve(x, y,
controlX,
controlY,
current[1],
current[2],
tempX,
tempY
);
x = tempX;
y = tempY;
// set control point to 2nd one of this command
// "... the first control point is assumed to be
// the reflection of the second control point on
// the previous command relative to the current point."
controlX = current[1];
controlY = current[2];
break;
2013-10-25 09:18:44 +00:00
2014-09-18 12:51:46 +00:00
case 'q': // quadraticCurveTo, relative
// transform to absolute x,y
tempX = x + current[3];
tempY = y + current[4];
controlX = x + current[1];
controlY = y + current[2];
bounds = fabric.util.getBoundsOfCurve(x, y,
controlX,
controlY,
controlX,
controlY,
tempX,
tempY
);
x = tempX;
y = tempY;
break;
2013-10-25 09:18:44 +00:00
2014-09-18 12:51:46 +00:00
case 'Q': // quadraticCurveTo, absolute
controlX = current[1];
controlY = current[2];
bounds = fabric.util.getBoundsOfCurve(x, y,
controlX,
controlY,
controlX,
controlY,
current[3],
current[4]
);
x = current[3];
y = current[4];
break;
2013-10-25 09:18:44 +00:00
2014-09-18 12:51:46 +00:00
case 't': // shorthand quadraticCurveTo, relative
// transform to absolute x,y
tempX = x + current[1];
tempY = y + current[2];
if (previous[0].match(/[QqTt]/) === null) {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
controlX = x;
controlY = y;
}
else {
// calculate reflection of previous control point
2014-09-18 12:51:46 +00:00
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
bounds = fabric.util.getBoundsOfCurve(x, y,
controlX,
controlY,
controlX,
controlY,
tempX,
tempY
);
x = tempX;
y = tempY;
2014-09-18 12:51:46 +00:00
break;
case 'T':
tempX = current[1];
tempY = current[2];
if (previous[0].match(/[QqTt]/) === null) {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
controlX = x;
controlY = y;
}
else {
// calculate reflection of previous control point
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
2014-09-18 12:51:46 +00:00
bounds = fabric.util.getBoundsOfCurve(x, y,
controlX,
controlY,
controlX,
controlY,
tempX,
tempY
);
x = tempX;
y = tempY;
break;
case 'a':
// TODO: optimize this
bounds = fabric.util.getBoundsOfArc(x, y,
current[1],
current[2],
current[3],
current[4],
current[5],
current[6] + x,
current[7] + y
);
x += current[6];
y += current[7];
break;
case 'A':
// TODO: optimize this
bounds = fabric.util.getBoundsOfArc(x, y,
current[1],
current[2],
current[3],
current[4],
current[5],
current[6],
current[7]
);
x = current[6];
y = current[7];
break;
case 'z':
case 'Z':
x = subpathStartX;
y = subpathStartY;
break;
}
previous = current;
bounds.forEach(function (point) {
aX.push(point.x);
aY.push(point.y);
});
aX.push(x);
aY.push(y);
}
var minX = min(aX),
minY = min(aY),
maxX = max(aX),
maxY = max(aY),
deltaX = maxX - minX,
deltaY = maxY - minY,
o = {
left: minX,
top: minY,
width: deltaX,
height: deltaY
};
return o;
2010-06-09 22:34:55 +00:00
}
});
2010-06-09 22:34:55 +00:00
/**
* Creates an instance of fabric.Path from an object
2010-06-09 22:34:55 +00:00
* @static
* @memberOf fabric.Path
* @param {Object} object
* @param {Function} callback Callback to invoke when an fabric.Path instance is created
2010-06-09 22:34:55 +00:00
*/
fabric.Path.fromObject = function(object, callback) {
if (typeof object.path === 'string') {
fabric.loadSVGFromURL(object.path, function (elements) {
var path = elements[0],
pathUrl = object.path;
delete object.path;
fabric.util.object.extend(path, object);
path.setSourcePath(pathUrl);
callback(path);
});
}
else {
callback(new fabric.Path(object.path, object));
}
2010-06-09 22:34:55 +00:00
};
/* _FROM_SVG_START_ */
2010-10-19 20:27:24 +00:00
/**
* List of attribute names to account for when parsing SVG element (used by `fabric.Path.fromElement`)
* @static
* @memberOf fabric.Path
2010-10-19 20:27:24 +00:00
* @see http://www.w3.org/TR/SVG/paths.html#PathElement
*/
fabric.Path.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(['d']);
2010-06-09 22:34:55 +00:00
/**
2010-10-19 20:27:24 +00:00
* Creates an instance of fabric.Path from an SVG <path> element
2010-06-09 22:34:55 +00:00
* @static
* @memberOf fabric.Path
2010-06-09 22:34:55 +00:00
* @param {SVGElement} element to parse
* @param {Function} callback Callback to invoke when an fabric.Path instance is created
2012-12-13 14:36:43 +00:00
* @param {Object} [options] Options object
2010-06-09 22:34:55 +00:00
*/
fabric.Path.fromElement = function(element, callback, options) {
var parsedAttributes = fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES);
callback && callback(new fabric.Path(parsedAttributes.d, extend(parsedAttributes, options)));
};
/* _FROM_SVG_END_ */
2011-08-05 23:00:26 +00:00
/**
* Indicates that instances of this type are async
* @static
* @memberOf fabric.Path
* @type Boolean
* @default
*/
fabric.Path.async = true;
})(typeof exports !== 'undefined' ? exports : this);