2013-09-24 11:11:41 +00:00
|
|
|
(function(global) {
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2013-01-20 16:30:34 +00:00
|
|
|
var sqrt = Math.sqrt,
|
|
|
|
|
atan2 = Math.atan2;
|
|
|
|
|
|
2011-09-22 16:40:31 +00:00
|
|
|
/**
|
2013-04-24 16:58:04 +00:00
|
|
|
* @namespace fabric.util
|
2011-09-22 16:40:31 +00:00
|
|
|
*/
|
2011-08-14 21:35:36 +00:00
|
|
|
fabric.util = { };
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2010-06-09 22:34:55 +00:00
|
|
|
/**
|
2010-10-15 02:16:24 +00:00
|
|
|
* Removes value from an array.
|
2010-06-09 22:34:55 +00:00
|
|
|
* Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
|
2010-07-27 21:58:23 +00:00
|
|
|
* @static
|
2010-10-15 02:16:24 +00:00
|
|
|
* @memberOf fabric.util
|
2010-06-09 22:34:55 +00:00
|
|
|
* @param {Array} array
|
|
|
|
|
* @param {Any} value
|
|
|
|
|
* @return {Array} original array
|
|
|
|
|
*/
|
|
|
|
|
function removeFromArray(array, value) {
|
|
|
|
|
var idx = array.indexOf(value);
|
|
|
|
|
if (idx !== -1) {
|
|
|
|
|
array.splice(idx, 1);
|
|
|
|
|
}
|
|
|
|
|
return array;
|
2012-10-14 00:53:12 +00:00
|
|
|
}
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2010-06-09 22:34:55 +00:00
|
|
|
/**
|
2010-10-15 02:16:24 +00:00
|
|
|
* Returns random number between 2 specified ones.
|
2010-06-09 22:34:55 +00:00
|
|
|
* @static
|
2010-10-15 02:16:24 +00:00
|
|
|
* @memberOf fabric.util
|
2010-06-09 22:34:55 +00:00
|
|
|
* @param {Number} min lower limit
|
|
|
|
|
* @param {Number} max upper limit
|
|
|
|
|
* @return {Number} random value (between min and max)
|
|
|
|
|
*/
|
|
|
|
|
function getRandomInt(min, max) {
|
|
|
|
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
|
|
|
}
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2010-10-15 02:16:24 +00:00
|
|
|
var PiBy180 = Math.PI / 180;
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2010-06-09 22:34:55 +00:00
|
|
|
/**
|
2010-10-15 02:16:24 +00:00
|
|
|
* Transforms degrees to radians.
|
2010-07-27 21:58:23 +00:00
|
|
|
* @static
|
2010-10-15 02:16:24 +00:00
|
|
|
* @memberOf fabric.util
|
2010-06-09 22:34:55 +00:00
|
|
|
* @param {Number} degrees value in degrees
|
|
|
|
|
* @return {Number} value in radians
|
|
|
|
|
*/
|
|
|
|
|
function degreesToRadians(degrees) {
|
|
|
|
|
return degrees * PiBy180;
|
|
|
|
|
}
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2012-11-15 13:20:45 +00:00
|
|
|
/**
|
|
|
|
|
* Transforms radians to degrees.
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {Number} radians value in radians
|
|
|
|
|
* @return {Number} value in degrees
|
|
|
|
|
*/
|
|
|
|
|
function radiansToDegrees(radians) {
|
|
|
|
|
return radians / PiBy180;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-06 18:12:54 +00:00
|
|
|
/**
|
|
|
|
|
* Rotates `point` around `origin` with `radians`
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {fabric.Point} The point to rotate
|
|
|
|
|
* @param {fabric.Point} The origin of the rotation
|
|
|
|
|
* @param {Number} The radians of the angle for the rotation
|
|
|
|
|
* @return {fabric.Point} The new rotated point
|
|
|
|
|
*/
|
|
|
|
|
function rotatePoint(point, origin, radians) {
|
|
|
|
|
var sin = Math.sin(radians),
|
|
|
|
|
cos = Math.cos(radians);
|
|
|
|
|
|
|
|
|
|
point.subtractEquals(origin);
|
|
|
|
|
|
|
|
|
|
var rx = point.x * cos - point.y * sin;
|
|
|
|
|
var ry = point.x * sin + point.y * cos;
|
|
|
|
|
|
|
|
|
|
return new fabric.Point(rx, ry).addEquals(origin);
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-09 22:34:55 +00:00
|
|
|
/**
|
2010-10-15 02:16:24 +00:00
|
|
|
* A wrapper around Number#toFixed, which contrary to native method returns number, not string.
|
2010-07-27 21:58:23 +00:00
|
|
|
* @static
|
2010-10-15 02:16:24 +00:00
|
|
|
* @memberOf fabric.util
|
2010-06-09 22:34:55 +00:00
|
|
|
* @param {Number | String} number number to operate on
|
|
|
|
|
* @param {Number} fractionDigits number of fraction digits to "leave"
|
|
|
|
|
* @return {Number}
|
|
|
|
|
*/
|
|
|
|
|
function toFixed(number, fractionDigits) {
|
|
|
|
|
return parseFloat(Number(number).toFixed(fractionDigits));
|
|
|
|
|
}
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2010-07-27 21:58:23 +00:00
|
|
|
/**
|
2010-10-15 02:16:24 +00:00
|
|
|
* Function which always returns `false`.
|
2010-07-27 21:58:23 +00:00
|
|
|
* @static
|
2010-10-15 02:16:24 +00:00
|
|
|
* @memberOf fabric.util
|
2010-07-27 21:58:23 +00:00
|
|
|
* @return {Boolean}
|
|
|
|
|
*/
|
|
|
|
|
function falseFunction() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2013-05-31 18:52:04 +00:00
|
|
|
/**
|
2013-08-14 16:07:56 +00:00
|
|
|
* Returns klass "Class" object of given namespace
|
2013-05-31 18:52:04 +00:00
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {String} type Type of object (eg. 'circle')
|
2013-08-14 16:07:56 +00:00
|
|
|
* @param {String} namespace Namespace to get klass "Class" object from
|
2013-05-31 18:52:04 +00:00
|
|
|
* @return {Object} klass "Class"
|
|
|
|
|
*/
|
2013-08-14 16:07:56 +00:00
|
|
|
function getKlass(type, namespace) {
|
2013-09-24 11:11:41 +00:00
|
|
|
// capitalize first letter only
|
|
|
|
|
type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
|
|
|
|
|
return resolveNamespace(namespace)[type];
|
2013-08-14 16:07:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns object of given namespace
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {String} namespace Namespace string e.g. 'fabric.Image.filter' or 'fabric'
|
|
|
|
|
* @return {Object} Object for given namespace (default fabric)
|
|
|
|
|
*/
|
|
|
|
|
function resolveNamespace(namespace) {
|
2013-08-17 18:18:06 +00:00
|
|
|
if (!namespace) return fabric;
|
2013-08-14 16:07:56 +00:00
|
|
|
|
|
|
|
|
var parts = namespace.split('.'),
|
|
|
|
|
len = parts.length,
|
2013-09-24 11:11:41 +00:00
|
|
|
obj = global || fabric.window;
|
2013-08-14 16:07:56 +00:00
|
|
|
|
|
|
|
|
for (var i = 0; i < len; ++i) {
|
|
|
|
|
obj = obj[parts[i]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return obj;
|
2013-05-31 18:52:04 +00:00
|
|
|
}
|
|
|
|
|
|
2011-11-28 04:57:28 +00:00
|
|
|
/**
|
|
|
|
|
* Loads image element from given url and passes it to a callback
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {String} url URL representing an image
|
|
|
|
|
* @param {Function} callback Callback; invoked with loaded image
|
|
|
|
|
* @param {Any} context optional Context to invoke callback in
|
|
|
|
|
*/
|
|
|
|
|
function loadImage(url, callback, context) {
|
|
|
|
|
if (url) {
|
2013-04-29 19:16:51 +00:00
|
|
|
var img = fabric.util.createImage();
|
2011-11-28 04:57:28 +00:00
|
|
|
/** @ignore */
|
2012-06-15 15:35:05 +00:00
|
|
|
img.onload = function () {
|
2011-11-28 04:57:28 +00:00
|
|
|
callback && callback.call(context, img);
|
2013-10-27 12:12:48 +00:00
|
|
|
img = img.onload = img.onerror = null;
|
|
|
|
|
};
|
|
|
|
|
/** @ignore */
|
|
|
|
|
img.onerror = function() {
|
|
|
|
|
fabric.log('Error loading ' + img.src);
|
|
|
|
|
callback && callback(null, true);
|
|
|
|
|
img = img.onload = img.onerror = null;
|
2011-11-28 04:57:28 +00:00
|
|
|
};
|
|
|
|
|
img.src = url;
|
|
|
|
|
}
|
2012-01-22 04:07:16 +00:00
|
|
|
else {
|
|
|
|
|
callback && callback.call(context, url);
|
|
|
|
|
}
|
2011-11-28 04:57:28 +00:00
|
|
|
}
|
2012-06-23 17:28:53 +00:00
|
|
|
|
2012-12-18 10:46:51 +00:00
|
|
|
/**
|
|
|
|
|
* Creates corresponding fabric instances from their object representations
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {Array} objects Objects to enliven
|
|
|
|
|
* @param {Function} callback Callback to invoke when all objects are created
|
2013-09-12 18:10:23 +00:00
|
|
|
* @param {Function} [reviver] Method for further parsing of object elements, called after each fabric object created.
|
2012-12-18 10:46:51 +00:00
|
|
|
*/
|
2013-09-12 18:10:23 +00:00
|
|
|
function enlivenObjects(objects, callback, namespace, reviver) {
|
2012-06-23 17:28:53 +00:00
|
|
|
|
2012-09-02 14:39:07 +00:00
|
|
|
function onLoaded() {
|
|
|
|
|
if (++numLoadedObjects === numTotalObjects) {
|
|
|
|
|
if (callback) {
|
|
|
|
|
callback(enlivenedObjects);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-06-23 17:28:53 +00:00
|
|
|
|
2012-09-02 14:39:07 +00:00
|
|
|
var enlivenedObjects = [ ],
|
|
|
|
|
numLoadedObjects = 0,
|
|
|
|
|
numTotalObjects = objects.length;
|
2012-06-23 17:28:53 +00:00
|
|
|
|
2012-04-08 13:15:31 +00:00
|
|
|
objects.forEach(function (o, index) {
|
2013-09-24 13:58:52 +00:00
|
|
|
// if sparse array
|
|
|
|
|
if (!o || !o.type) {
|
2013-09-24 16:34:32 +00:00
|
|
|
onLoaded();
|
2012-04-08 13:15:31 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2013-08-14 16:07:56 +00:00
|
|
|
var klass = fabric.util.getKlass(o.type, namespace);
|
2012-04-08 13:15:31 +00:00
|
|
|
if (klass.async) {
|
2013-09-12 18:10:23 +00:00
|
|
|
klass.fromObject(o, function (obj, error) {
|
2013-01-04 17:15:25 +00:00
|
|
|
if (!error) {
|
2013-09-12 18:10:23 +00:00
|
|
|
enlivenedObjects[index] = obj;
|
|
|
|
|
reviver && reviver(o, enlivenedObjects[index]);
|
2013-01-04 17:15:25 +00:00
|
|
|
}
|
2012-09-02 14:39:07 +00:00
|
|
|
onLoaded();
|
2012-04-08 13:15:31 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
enlivenedObjects[index] = klass.fromObject(o);
|
2013-09-12 18:10:23 +00:00
|
|
|
reviver && reviver(o, enlivenedObjects[index]);
|
2012-09-02 14:39:07 +00:00
|
|
|
onLoaded();
|
2012-04-08 13:15:31 +00:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2011-07-06 18:38:56 +00:00
|
|
|
|
2012-10-14 00:53:12 +00:00
|
|
|
/**
|
|
|
|
|
* Groups SVG elements (usually those retrieved from SVG document)
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
2012-12-23 14:17:45 +00:00
|
|
|
* @param {Array} elements SVG elements to group
|
2012-12-13 14:36:43 +00:00
|
|
|
* @param {Object} [options] Options object
|
|
|
|
|
* @return {fabric.Object|fabric.PathGroup}
|
2012-10-14 00:53:12 +00:00
|
|
|
*/
|
2012-06-23 17:28:53 +00:00
|
|
|
function groupSVGElements(elements, options, path) {
|
2012-12-23 14:28:08 +00:00
|
|
|
var object;
|
|
|
|
|
|
2012-12-23 14:17:45 +00:00
|
|
|
if (elements.length > 1) {
|
2013-06-22 17:26:32 +00:00
|
|
|
object = new fabric.PathGroup(elements, options);
|
2012-12-23 14:17:45 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
object = elements[0];
|
|
|
|
|
}
|
2012-06-23 17:28:53 +00:00
|
|
|
|
|
|
|
|
if (typeof path !== 'undefined') {
|
|
|
|
|
object.setSourcePath(path);
|
|
|
|
|
}
|
|
|
|
|
return object;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-30 22:46:09 +00:00
|
|
|
/**
|
|
|
|
|
* Populates an object with properties of another object
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
2012-12-23 14:17:45 +00:00
|
|
|
* @param {Object} source Source object
|
|
|
|
|
* @param {Object} destination Destination object
|
|
|
|
|
* @return {Array} properties Propertie names to include
|
2012-11-30 22:46:09 +00:00
|
|
|
*/
|
|
|
|
|
function populateWithProperties(source, destination, properties) {
|
|
|
|
|
if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
|
|
|
|
|
for (var i = 0, len = properties.length; i < len; i++) {
|
2013-06-19 10:49:31 +00:00
|
|
|
if (properties[i] in source) {
|
|
|
|
|
destination[properties[i]] = source[properties[i]];
|
|
|
|
|
}
|
2012-11-30 22:46:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-20 16:30:34 +00:00
|
|
|
/**
|
|
|
|
|
* Draws a dashed line between two points
|
|
|
|
|
*
|
|
|
|
|
* This method is used to draw dashed line around selection area.
|
|
|
|
|
* See <a href="http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas">dotted stroke in canvas</a>
|
|
|
|
|
*
|
|
|
|
|
* @param ctx {Canvas} context
|
|
|
|
|
* @param x {Number} start x coordinate
|
|
|
|
|
* @param y {Number} start y coordinate
|
|
|
|
|
* @param x2 {Number} end x coordinate
|
|
|
|
|
* @param y2 {Number} end y coordinate
|
|
|
|
|
* @param da {Array} dash array pattern
|
|
|
|
|
*/
|
|
|
|
|
function drawDashedLine(ctx, x, y, x2, y2, da) {
|
|
|
|
|
var dx = x2 - x,
|
|
|
|
|
dy = y2 - y,
|
|
|
|
|
len = sqrt(dx*dx + dy*dy),
|
|
|
|
|
rot = atan2(dy, dx),
|
|
|
|
|
dc = da.length,
|
|
|
|
|
di = 0,
|
|
|
|
|
draw = true;
|
|
|
|
|
|
|
|
|
|
ctx.save();
|
|
|
|
|
ctx.translate(x, y);
|
|
|
|
|
ctx.moveTo(0, 0);
|
|
|
|
|
ctx.rotate(rot);
|
|
|
|
|
|
|
|
|
|
x = 0;
|
|
|
|
|
while (len > x) {
|
|
|
|
|
x += da[di++ % dc];
|
|
|
|
|
if (x > len) {
|
|
|
|
|
x = len;
|
|
|
|
|
}
|
|
|
|
|
ctx[draw ? 'lineTo' : 'moveTo'](x, 0);
|
|
|
|
|
draw = !draw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.restore();
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-19 12:34:50 +00:00
|
|
|
/**
|
|
|
|
|
* Creates canvas element and initializes it via excanvas if necessary
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {CanvasElement} [canvasEl] optional canvas element to initialize; when not given, element is created implicitly
|
|
|
|
|
* @return {CanvasElement} initialized canvas element
|
|
|
|
|
*/
|
|
|
|
|
function createCanvasElement(canvasEl) {
|
|
|
|
|
canvasEl || (canvasEl = fabric.document.createElement('canvas'));
|
2013-01-25 13:23:13 +00:00
|
|
|
if (!canvasEl.getContext && typeof G_vmlCanvasManager !== 'undefined') {
|
|
|
|
|
G_vmlCanvasManager.initElement(canvasEl);
|
|
|
|
|
}
|
|
|
|
|
return canvasEl;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-29 19:16:51 +00:00
|
|
|
/**
|
|
|
|
|
* Creates image element (works on client and node)
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
2013-08-14 16:07:56 +00:00
|
|
|
* @return {HTMLImageElement} HTML image element
|
2013-04-29 19:16:51 +00:00
|
|
|
*/
|
|
|
|
|
function createImage() {
|
|
|
|
|
return fabric.isLikelyNode
|
|
|
|
|
? new (require('canvas').Image)()
|
|
|
|
|
: fabric.document.createElement('img');
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-19 12:34:50 +00:00
|
|
|
/**
|
|
|
|
|
* Creates accessors (getXXX, setXXX) for a "class", based on "stateProperties" array
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {Object} klass "Class" to create accessors for
|
|
|
|
|
*/
|
2013-02-18 15:22:48 +00:00
|
|
|
function createAccessors(klass) {
|
|
|
|
|
var proto = klass.prototype;
|
|
|
|
|
|
|
|
|
|
for (var i = proto.stateProperties.length; i--; ) {
|
|
|
|
|
|
|
|
|
|
var propName = proto.stateProperties[i],
|
|
|
|
|
capitalizedPropName = propName.charAt(0).toUpperCase() + propName.slice(1),
|
|
|
|
|
setterName = 'set' + capitalizedPropName,
|
|
|
|
|
getterName = 'get' + capitalizedPropName;
|
|
|
|
|
|
|
|
|
|
// using `new Function` for better introspection
|
|
|
|
|
if (!proto[getterName]) {
|
|
|
|
|
proto[getterName] = (function(property) {
|
|
|
|
|
return new Function('return this.get("' + property + '")');
|
|
|
|
|
})(propName);
|
|
|
|
|
}
|
|
|
|
|
if (!proto[setterName]) {
|
|
|
|
|
proto[setterName] = (function(property) {
|
|
|
|
|
return new Function('value', 'return this.set("' + property + '", value)');
|
|
|
|
|
})(propName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-10 20:06:17 +00:00
|
|
|
/**
|
2013-04-24 16:58:04 +00:00
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {fabric.Object} receiver Object implementing `clipTo` method
|
|
|
|
|
* @param {CanvasRenderingContext2D} ctx Context to clip
|
2013-03-10 20:06:17 +00:00
|
|
|
*/
|
|
|
|
|
function clipContext(receiver, ctx) {
|
|
|
|
|
ctx.save();
|
|
|
|
|
ctx.beginPath();
|
|
|
|
|
receiver.clipTo(ctx);
|
|
|
|
|
ctx.clip();
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-16 06:26:22 +00:00
|
|
|
/**
|
|
|
|
|
* Multiply matrix A by matrix B to nest transformations
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {Array} matrixA First transformMatrix
|
|
|
|
|
* @param {Array} matrixB Second transformMatrix
|
|
|
|
|
* @return {Array} The product of the two transform matrices
|
|
|
|
|
*/
|
2013-04-17 17:54:11 +00:00
|
|
|
function multiplyTransformMatrices(matrixA, matrixB) {
|
2013-04-16 05:49:42 +00:00
|
|
|
// Matrix multiply matrixA * matrixB
|
|
|
|
|
var a = [
|
|
|
|
|
[matrixA[0], matrixA[2], matrixA[4]],
|
|
|
|
|
[matrixA[1], matrixA[3], matrixA[5]],
|
|
|
|
|
[0 , 0 , 1 ]
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
var b = [
|
|
|
|
|
[matrixB[0], matrixB[2], matrixB[4]],
|
|
|
|
|
[matrixB[1], matrixB[3], matrixB[5]],
|
|
|
|
|
[0 , 0 , 1 ]
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
var result = [];
|
|
|
|
|
for (var r=0; r<3; r++) {
|
|
|
|
|
result[r] = [];
|
|
|
|
|
for (var c=0; c<3; c++) {
|
|
|
|
|
var sum = 0;
|
|
|
|
|
for (var k=0; k<3; k++) {
|
|
|
|
|
sum += a[r][k]*b[k][c];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result[r][c] = sum;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
result[0][0],
|
|
|
|
|
result[1][0],
|
|
|
|
|
result[0][1],
|
|
|
|
|
result[1][1],
|
|
|
|
|
result[0][2],
|
|
|
|
|
result[1][2]
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-01 16:08:36 +00:00
|
|
|
function getFunctionBody(fn) {
|
2013-06-02 20:07:16 +00:00
|
|
|
return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
|
2013-06-01 16:08:36 +00:00
|
|
|
}
|
|
|
|
|
|
2013-06-06 00:49:42 +00:00
|
|
|
function drawArc(ctx, x, y, coords) {
|
|
|
|
|
var rx = coords[0];
|
|
|
|
|
var ry = coords[1];
|
|
|
|
|
var rot = coords[2];
|
|
|
|
|
var large = coords[3];
|
|
|
|
|
var sweep = coords[4];
|
|
|
|
|
var ex = coords[5];
|
|
|
|
|
var ey = coords[6];
|
|
|
|
|
var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
|
|
|
|
|
for (var i=0; i<segs.length; i++) {
|
|
|
|
|
var bez = segmentToBezier.apply(this, segs[i]);
|
|
|
|
|
ctx.bezierCurveTo.apply(ctx, bez);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var arcToSegmentsCache = { },
|
|
|
|
|
segmentToBezierCache = { },
|
|
|
|
|
_join = Array.prototype.join,
|
|
|
|
|
argsString;
|
|
|
|
|
|
|
|
|
|
// Generous contribution by Raph Levien, from libsvg-0.1.0.tar.gz
|
|
|
|
|
function arcToSegments(x, y, rx, ry, large, sweep, rotateX, ox, oy) {
|
|
|
|
|
argsString = _join.call(arguments);
|
|
|
|
|
if (arcToSegmentsCache[argsString]) {
|
|
|
|
|
return arcToSegmentsCache[argsString];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var th = rotateX * (Math.PI/180);
|
|
|
|
|
var sin_th = Math.sin(th);
|
|
|
|
|
var cos_th = Math.cos(th);
|
|
|
|
|
rx = Math.abs(rx);
|
|
|
|
|
ry = Math.abs(ry);
|
|
|
|
|
var px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
|
|
|
|
|
var py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
|
|
|
|
|
var pl = (px*px) / (rx*rx) + (py*py) / (ry*ry);
|
|
|
|
|
if (pl > 1) {
|
|
|
|
|
pl = Math.sqrt(pl);
|
|
|
|
|
rx *= pl;
|
|
|
|
|
ry *= pl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var a00 = cos_th / rx;
|
|
|
|
|
var a01 = sin_th / rx;
|
|
|
|
|
var a10 = (-sin_th) / ry;
|
|
|
|
|
var a11 = (cos_th) / ry;
|
|
|
|
|
var x0 = a00 * ox + a01 * oy;
|
|
|
|
|
var y0 = a10 * ox + a11 * oy;
|
|
|
|
|
var x1 = a00 * x + a01 * y;
|
|
|
|
|
var y1 = a10 * x + a11 * y;
|
|
|
|
|
|
|
|
|
|
var d = (x1-x0) * (x1-x0) + (y1-y0) * (y1-y0);
|
|
|
|
|
var sfactor_sq = 1 / d - 0.25;
|
|
|
|
|
if (sfactor_sq < 0) sfactor_sq = 0;
|
|
|
|
|
var sfactor = Math.sqrt(sfactor_sq);
|
|
|
|
|
if (sweep === large) sfactor = -sfactor;
|
|
|
|
|
var xc = 0.5 * (x0 + x1) - sfactor * (y1-y0);
|
|
|
|
|
var yc = 0.5 * (y0 + y1) + sfactor * (x1-x0);
|
|
|
|
|
|
|
|
|
|
var th0 = Math.atan2(y0-yc, x0-xc);
|
|
|
|
|
var th1 = Math.atan2(y1-yc, x1-xc);
|
|
|
|
|
|
|
|
|
|
var th_arc = th1-th0;
|
|
|
|
|
if (th_arc < 0 && sweep === 1){
|
|
|
|
|
th_arc += 2*Math.PI;
|
|
|
|
|
} else if (th_arc > 0 && sweep === 0) {
|
|
|
|
|
th_arc -= 2 * Math.PI;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001)));
|
|
|
|
|
var result = [];
|
|
|
|
|
for (var i=0; i<segments; i++) {
|
|
|
|
|
var th2 = th0 + i * th_arc / segments;
|
|
|
|
|
var th3 = th0 + (i+1) * th_arc / segments;
|
|
|
|
|
result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arcToSegmentsCache[argsString] = result;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function segmentToBezier(cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
|
|
|
|
|
argsString = _join.call(arguments);
|
|
|
|
|
if (segmentToBezierCache[argsString]) {
|
|
|
|
|
return segmentToBezierCache[argsString];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var a00 = cos_th * rx;
|
|
|
|
|
var a01 = -sin_th * ry;
|
|
|
|
|
var a10 = sin_th * rx;
|
|
|
|
|
var a11 = cos_th * ry;
|
|
|
|
|
|
|
|
|
|
var th_half = 0.5 * (th1 - th0);
|
|
|
|
|
var t = (8/3) * Math.sin(th_half * 0.5) * Math.sin(th_half * 0.5) / Math.sin(th_half);
|
|
|
|
|
var x1 = cx + Math.cos(th0) - t * Math.sin(th0);
|
|
|
|
|
var y1 = cy + Math.sin(th0) + t * Math.cos(th0);
|
|
|
|
|
var x3 = cx + Math.cos(th1);
|
|
|
|
|
var y3 = cy + Math.sin(th1);
|
|
|
|
|
var x2 = x3 + t * Math.sin(th1);
|
|
|
|
|
var y2 = y3 - t * Math.cos(th1);
|
|
|
|
|
|
|
|
|
|
segmentToBezierCache[argsString] = [
|
|
|
|
|
a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
|
|
|
|
|
a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
|
|
|
|
|
a00 * x3 + a01 * y3, a10 * x3 + a11 * y3
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return segmentToBezierCache[argsString];
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-26 18:20:48 +00:00
|
|
|
function normalizePoints(points, options) {
|
|
|
|
|
var minX = fabric.util.array.min(points, 'x'),
|
|
|
|
|
minY = fabric.util.array.min(points, 'y');
|
|
|
|
|
|
|
|
|
|
minX = minX < 0 ? minX : 0;
|
|
|
|
|
minY = minX < 0 ? minY : 0;
|
|
|
|
|
|
|
|
|
|
for (var i = 0, len = points.length; i < len; i++) {
|
|
|
|
|
// normalize coordinates, according to containing box (dimensions of which are passed via `options`)
|
|
|
|
|
points[i].x -= (options.width / 2 + minX) || 0;
|
|
|
|
|
points[i].y -= (options.height / 2 + minY) || 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-04 14:59:32 +00:00
|
|
|
fabric.util.removeFromArray = removeFromArray;
|
|
|
|
|
fabric.util.degreesToRadians = degreesToRadians;
|
2012-11-15 13:20:45 +00:00
|
|
|
fabric.util.radiansToDegrees = radiansToDegrees;
|
2012-12-06 18:12:54 +00:00
|
|
|
fabric.util.rotatePoint = rotatePoint;
|
2011-08-04 14:59:32 +00:00
|
|
|
fabric.util.toFixed = toFixed;
|
|
|
|
|
fabric.util.getRandomInt = getRandomInt;
|
|
|
|
|
fabric.util.falseFunction = falseFunction;
|
2013-05-31 18:52:04 +00:00
|
|
|
fabric.util.getKlass = getKlass;
|
2013-08-14 16:07:56 +00:00
|
|
|
fabric.util.resolveNamespace = resolveNamespace;
|
2011-11-28 04:57:28 +00:00
|
|
|
fabric.util.loadImage = loadImage;
|
2012-04-08 13:15:31 +00:00
|
|
|
fabric.util.enlivenObjects = enlivenObjects;
|
2012-06-23 17:28:53 +00:00
|
|
|
fabric.util.groupSVGElements = groupSVGElements;
|
2012-11-30 22:46:09 +00:00
|
|
|
fabric.util.populateWithProperties = populateWithProperties;
|
2013-01-20 16:30:34 +00:00
|
|
|
fabric.util.drawDashedLine = drawDashedLine;
|
2013-01-25 13:23:13 +00:00
|
|
|
fabric.util.createCanvasElement = createCanvasElement;
|
2013-04-29 19:16:51 +00:00
|
|
|
fabric.util.createImage = createImage;
|
2013-02-18 15:22:48 +00:00
|
|
|
fabric.util.createAccessors = createAccessors;
|
2013-03-10 20:06:17 +00:00
|
|
|
fabric.util.clipContext = clipContext;
|
2013-04-17 17:54:11 +00:00
|
|
|
fabric.util.multiplyTransformMatrices = multiplyTransformMatrices;
|
2013-06-01 16:08:36 +00:00
|
|
|
fabric.util.getFunctionBody = getFunctionBody;
|
2013-06-06 00:49:42 +00:00
|
|
|
fabric.util.drawArc = drawArc;
|
2013-10-26 18:20:48 +00:00
|
|
|
fabric.util.normalizePoints = normalizePoints;
|
2013-01-20 16:30:34 +00:00
|
|
|
|
2013-09-24 11:11:41 +00:00
|
|
|
})(typeof exports !== 'undefined' ? exports : this);
|