Merge branch 'develop'

This commit is contained in:
Jürg Lehni 2017-04-23 17:15:08 +02:00
commit 48c8eacf0b
27 changed files with 1670 additions and 1429 deletions

View file

@ -41,4 +41,4 @@ script:
- gulp minify - gulp minify
- gulp test - gulp test
- gulp zip - gulp zip
- '[ "${TRAVIS_BRANCH}" = "develop" ] && [ "${TRAVIS_NODE_VERSION}" = "stable" ] && travis/deploy-prebuilt.sh' - '[ "${TRAVIS_BRANCH}" = "develop" ] && [ "${TRAVIS_NODE_VERSION}" = "stable" ] && travis/deploy-prebuilt.sh || true'

View file

@ -51,17 +51,17 @@ generally not recommended to install Node.js through OS-supplied package
managers, as the its development cycles move fast and these versions are often managers, as the its development cycles move fast and these versions are often
out-of-date. out-of-date.
On macOS, [Homebrew](http://brew.sh/) is a good option if one version of
Node.js that is kept up to date with `brew upgrade` is enough:
<http://treehouse.github.io/installation-guides/mac/node-mac.html>
[NVM](https://github.com/creationix/nvm) can be used instead to install and [NVM](https://github.com/creationix/nvm) can be used instead to install and
maintain multiple versions of Node.js on the same platform, as often required by maintain multiple versions of Node.js on the same platform, as often required by
different projects: different projects:
<https://nodesource.com/blog/installing-node-js-tutorial-using-nvm-on-mac-os-x-and-ubuntu/> <https://nodesource.com/blog/installing-node-js-tutorial-using-nvm-on-mac-os-x-and-ubuntu/>
on OSX, [Homebrew](http://brew.sh/) is also a good option if one version of Homebrew is recommended on macOS also if you intend to install Paper.js with
Node.js that is kept up to date with `brew update` is enough: rendering to the Canvas on Node.js, as described in the next paragraph.
<http://treehouse.github.io/installation-guides/mac/node-mac.html>
Homebrew is recommended on OSX also if you intend to install Paper.js for
Node.js, as described in the next paragraph.
For Linux, see <http://nodejs.org/download/> to locate 32-bit and 64-bit Node.js For Linux, see <http://nodejs.org/download/> to locate 32-bit and 64-bit Node.js
binaries as well as sources, or use NVM, as described in the paragraph above. binaries as well as sources, or use NVM, as described in the paragraph above.
@ -83,14 +83,14 @@ different one:
In order to install `paper-jsdom-canvas`, you need the [Cairo Graphics In order to install `paper-jsdom-canvas`, you need the [Cairo Graphics
library](http://cairographics.org/) installed in your system: library](http://cairographics.org/) installed in your system:
##### Installing Cairo and Pango on OSX: ##### Installing Cairo and Pango on macOS:
The easiest way to install Cairo is through [Homebrew](http://brew.sh/), by The easiest way to install Cairo is through [Homebrew](http://brew.sh/), by
issuing the command: issuing the command:
brew install cairo pango brew install cairo pango
Note that currently there is an issue on OSX with Cairo. If the above causes Note that currently there is an issue on macOS with Cairo. If the above causes
errors, the following will most likely fix it: errors, the following will most likely fix it:
PKG_CONFIG_PATH=/opt/X11/lib/pkgconfig/ npm install paper PKG_CONFIG_PATH=/opt/X11/lib/pkgconfig/ npm install paper

223
dist/paper-core.js vendored
View file

@ -1,5 +1,5 @@
/*! /*!
* Paper.js v0.11.2 - The Swiss Army Knife of Vector Graphics Scripting. * Paper.js v0.11.3 - The Swiss Army Knife of Vector Graphics Scripting.
* http://paperjs.org/ * http://paperjs.org/
* *
* Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey * Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey
@ -9,7 +9,7 @@
* *
* All rights reserved. * All rights reserved.
* *
* Date: Thu Apr 20 19:14:30 2017 +0200 * Date: Sat Apr 22 20:01:34 2017 +0200
* *
*** ***
* *
@ -98,12 +98,13 @@ var Base = new function() {
&& (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/))) && (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/)))
beansNames[bean[3].toLowerCase() + bean[4]] = bean[2]; beansNames[bean[3].toLowerCase() + bean[4]] = bean[2];
if (!res || isFunc || !res.get || typeof res.get !== 'function' if (!res || isFunc || !res.get || typeof res.get !== 'function'
|| !Base.isPlainObject(res)) || !Base.isPlainObject(res)) {
res = { value: res, writable: true }; res = { value: res, writable: true };
}
if ((describe(dest, name) if ((describe(dest, name)
|| { configurable: true }).configurable) { || { configurable: true }).configurable) {
res.configurable = true; res.configurable = true;
res.enumerable = enumerable; res.enumerable = enumerable != null ? enumerable : !bean;
} }
define(dest, name, res); define(dest, name, res);
} }
@ -141,7 +142,7 @@ var Base = new function() {
preserve = src.preserve; preserve = src.preserve;
if (statics !== src) if (statics !== src)
inject(this.prototype, src, src.enumerable, beans, preserve); inject(this.prototype, src, src.enumerable, beans, preserve);
inject(this, statics, true, beans, preserve); inject(this, statics, null, beans, preserve);
} }
for (var i = 1, l = arguments.length; i < l; i++) for (var i = 1, l = arguments.length; i < l; i++)
this.inject(arguments[i]); this.inject(arguments[i]);
@ -164,13 +165,15 @@ var Base = new function() {
proto = ctor.prototype = proto || create(this.prototype); proto = ctor.prototype = proto || create(this.prototype);
define(proto, 'constructor', define(proto, 'constructor',
{ value: ctor, writable: true, configurable: true }); { value: ctor, writable: true, configurable: true });
inject(ctor, this, true); inject(ctor, this);
if (arguments.length) if (arguments.length)
this.inject.apply(ctor, arguments); this.inject.apply(ctor, arguments);
ctor.base = base; ctor.base = base;
return ctor; return ctor;
} }
}, true).inject({ }).inject({
enumerable: false,
initialize: Base, initialize: Base,
set: Base, set: Base,
@ -230,6 +233,8 @@ if (typeof module !== 'undefined')
module.exports = Base; module.exports = Base;
Base.inject({ Base.inject({
enumerable: false,
toString: function() { toString: function() {
return this._id != null return this._id != null
? (this._class || 'Object') + (this._name ? (this._class || 'Object') + (this._name
@ -265,13 +270,12 @@ Base.inject({
if (props) if (props)
Base.filter(this, props, exclude, this._prioritize); Base.filter(this, props, exclude, this._prioritize);
return this; return this;
}, }
}, {
statics: { beans: false,
statics: {
exports: { exports: {},
enumerable: true
},
extend: function extend() { extend: function extend() {
var res = extend.base.apply(this, arguments), var res = extend.base.apply(this, arguments),
@ -341,6 +345,11 @@ Base.inject({
: list) || obj; : list) || obj;
if (readIndex) { if (readIndex) {
list.__index = begin + obj.__read; list.__index = begin + obj.__read;
var filtered = obj.__filtered;
if (filtered) {
list.__filtered = filtered;
obj.__filtered = undefined;
}
obj.__read = undefined; obj.__read = undefined;
} }
return obj; return obj;
@ -371,14 +380,16 @@ Base.inject({
var value = this.getNamed(list, name), var value = this.getNamed(list, name),
hasObject = value !== undefined; hasObject = value !== undefined;
if (hasObject) { if (hasObject) {
var filtered = list._filtered; var filtered = list.__filtered;
if (!filtered) { if (!filtered) {
filtered = list._filtered = Base.create(list[0]); filtered = list.__filtered = Base.create(list[0]);
filtered._unfiltered = list[0]; filtered.__unfiltered = list[0];
} }
filtered[name] = undefined; filtered[name] = undefined;
} }
return this.read(hasObject ? [value] : list, start, options, amount); var l = hasObject ? [value] : list,
res = this.read(l, start, options, amount);
return res;
}, },
getNamed: function(list, name) { getNamed: function(list, name) {
@ -386,7 +397,7 @@ Base.inject({
if (list._hasObject === undefined) if (list._hasObject === undefined)
list._hasObject = list.length === 1 && Base.isPlainObject(arg); list._hasObject = list.length === 1 && Base.isPlainObject(arg);
if (list._hasObject) if (list._hasObject)
return name ? arg[name] : list._filtered || arg; return name ? arg[name] : list.__filtered || arg;
}, },
hasNamed: function(list, name) { hasNamed: function(list, name) {
@ -416,7 +427,7 @@ Base.inject({
processed = keys; processed = keys;
} }
Object.keys(source._unfiltered || source).forEach(handleKey); Object.keys(source.__unfiltered || source).forEach(handleKey);
return dest; return dest;
}, },
@ -462,8 +473,7 @@ Base.inject({
} else if (Array.isArray(obj)) { } else if (Array.isArray(obj)) {
res = []; res = [];
for (var i = 0, l = obj.length; i < l; i++) for (var i = 0, l = obj.length; i < l; i++)
res[i] = Base.serialize(obj[i], options, compact, res[i] = Base.serialize(obj[i], options, compact, dictionary);
dictionary);
} else if (Base.isPlainObject(obj)) { } else if (Base.isPlainObject(obj)) {
res = {}; res = {};
var keys = Object.keys(obj); var keys = Object.keys(obj);
@ -586,8 +596,7 @@ Base.inject({
hyphenate: function(str) { hyphenate: function(str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
} }
} }});
});
var Emitter = { var Emitter = {
on: function(type, func) { on: function(type, func) {
@ -769,7 +778,7 @@ var PaperScope = Base.extend({
} }
}, },
version: "0.11.2", version: "0.11.3",
getView: function() { getView: function() {
var project = this.project; var project = this.project;
@ -1739,6 +1748,9 @@ var Rectangle = Base.extend({
} }
this._set(x, y, width, height); this._set(x, y, width, height);
read = arguments.__index; read = arguments.__index;
var filtered = arguments.__filtered;
if (filtered)
this.__filtered = filtered;
} }
if (this.__read) if (this.__read)
this.__read = read; this.__read = read;
@ -2262,7 +2274,7 @@ var Matrix = Base.extend({
return this.shear(shear, center); return this.shear(shear, center);
}, },
append: function(mx) { append: function(mx, _dontNotify) {
if (mx) { if (mx) {
var a1 = this._a, var a1 = this._a,
b1 = this._b, b1 = this._b,
@ -2280,12 +2292,13 @@ var Matrix = Base.extend({
this._d = b2 * b1 + d2 * d1; this._d = b2 * b1 + d2 * d1;
this._tx += tx2 * a1 + ty2 * c1; this._tx += tx2 * a1 + ty2 * c1;
this._ty += tx2 * b1 + ty2 * d1; this._ty += tx2 * b1 + ty2 * d1;
if (!_dontNotify)
this._changed(); this._changed();
} }
return this; return this;
}, },
prepend: function(mx) { prepend: function(mx, _dontNotify) {
if (mx) { if (mx) {
var a1 = this._a, var a1 = this._a,
b1 = this._b, b1 = this._b,
@ -2305,6 +2318,7 @@ var Matrix = Base.extend({
this._d = c2 * c1 + d2 * d1; this._d = c2 * c1 + d2 * d1;
this._tx = a2 * tx1 + b2 * ty1 + tx2; this._tx = a2 * tx1 + b2 * ty1 + tx2;
this._ty = c2 * tx1 + d2 * ty1 + ty2; this._ty = c2 * tx1 + d2 * ty1 + ty2;
if (!_dontNotify)
this._changed(); this._changed();
} }
return this; return this;
@ -3236,11 +3250,11 @@ new function() {
this._boundsOptions); this._boundsOptions);
if (!opts.stroke || this.getStrokeScaling()) if (!opts.stroke || this.getStrokeScaling())
opts.cacheItem = this; opts.cacheItem = this;
var bounds = this._getCachedBounds(hasMatrix && matrix, opts); var rect = this._getCachedBounds(hasMatrix && matrix, opts).rect;
return !arguments.length return !arguments.length
? new LinkedRectangle(bounds.x, bounds.y, bounds.width, ? new LinkedRectangle(rect.x, rect.y, rect.width, rect.height,
bounds.height, this, 'setBounds') this, 'setBounds')
: bounds; : rect;
}, },
setBounds: function() { setBounds: function() {
@ -3273,29 +3287,49 @@ new function() {
return Item._getBounds(children, matrix, options); return Item._getBounds(children, matrix, options);
}, },
_getBoundsCacheKey: function(options, internal) {
return [
options.stroke ? 1 : 0,
options.handle ? 1 : 0,
internal ? 1 : 0
].join('');
},
_getCachedBounds: function(matrix, options, noInternal) { _getCachedBounds: function(matrix, options, noInternal) {
matrix = matrix && matrix._orNullIfIdentity(); matrix = matrix && matrix._orNullIfIdentity();
var internal = options.internal && !noInternal, var internal = options.internal && !noInternal,
cacheItem = options.cacheItem, cacheItem = options.cacheItem,
_matrix = internal ? null : this._matrix._orNullIfIdentity(), _matrix = internal ? null : this._matrix._orNullIfIdentity(),
cacheKey = cacheItem && (!matrix || matrix.equals(_matrix)) && [ cacheKey = cacheItem && (!matrix || matrix.equals(_matrix))
options.stroke ? 1 : 0, && this._getBoundsCacheKey(options, internal),
options.handle ? 1 : 0, bounds = this._bounds;
internal ? 1 : 0
].join('');
Item._updateBoundsCache(this._parent || this._symbol, cacheItem); Item._updateBoundsCache(this._parent || this._symbol, cacheItem);
if (cacheKey && this._bounds && cacheKey in this._bounds) if (cacheKey && bounds && cacheKey in bounds) {
return this._bounds[cacheKey].rect.clone(); var cached = bounds[cacheKey];
var bounds = this._getBounds(matrix || _matrix, options); return {
rect: cached.rect.clone(),
nonscaling: cached.nonscaling
};
}
var res = this._getBounds(matrix || _matrix, options),
rect = res.rect || res,
style = this._style,
nonscaling = res.nonscaling || style.hasStroke()
&& !style.getStrokeScaling();
if (cacheKey) { if (cacheKey) {
if (!this._bounds) if (!bounds) {
this._bounds = {}; this._bounds = bounds = {};
var cached = this._bounds[cacheKey] = { }
rect: bounds.clone(), var cached = bounds[cacheKey] = {
rect: rect.clone(),
nonscaling: nonscaling,
internal: internal internal: internal
}; };
} }
return bounds; return {
rect: rect,
nonscaling: nonscaling
};
}, },
_getStrokeMatrix: function(matrix, options) { _getStrokeMatrix: function(matrix, options) {
@ -3340,22 +3374,29 @@ new function() {
var x1 = Infinity, var x1 = Infinity,
x2 = -x1, x2 = -x1,
y1 = x1, y1 = x1,
y2 = x2; y2 = x2,
nonscaling = false;
options = options || {}; options = options || {};
for (var i = 0, l = items.length; i < l; i++) { for (var i = 0, l = items.length; i < l; i++) {
var item = items[i]; var item = items[i];
if (item._visible && !item.isEmpty()) { if (item._visible && !item.isEmpty()) {
var rect = item._getCachedBounds( var bounds = item._getCachedBounds(
matrix && matrix.appended(item._matrix), options, true); matrix && matrix.appended(item._matrix), options, true),
rect = bounds.rect;
x1 = Math.min(rect.x, x1); x1 = Math.min(rect.x, x1);
y1 = Math.min(rect.y, y1); y1 = Math.min(rect.y, y1);
x2 = Math.max(rect.x + rect.width, x2); x2 = Math.max(rect.x + rect.width, x2);
y2 = Math.max(rect.y + rect.height, y2); y2 = Math.max(rect.y + rect.height, y2);
if (bounds.nonscaling)
nonscaling = true;
} }
} }
return isFinite(x1) return {
rect: isFinite(x1)
? new Rectangle(x1, y1, x2 - x1, y2 - y1) ? new Rectangle(x1, y1, x2 - x1, y2 - y1)
: new Rectangle(); : new Rectangle(),
nonscaling: nonscaling
};
} }
} }
@ -3395,8 +3436,18 @@ new function() {
var current = this.getScaling(), var current = this.getScaling(),
scaling = Point.read(arguments, 0, { clone: true, readNull: true }); scaling = Point.read(arguments, 0, { clone: true, readNull: true });
if (current && scaling && !current.equals(scaling)) { if (current && scaling && !current.equals(scaling)) {
var decomposed = this._decomposed; var rotation = this.getRotation(),
this.scale(scaling.x / current.x, scaling.y / current.y); decomposed = this._decomposed,
matrix = new Matrix(),
center = this.getPosition(true);
matrix.translate(center);
if (rotation)
matrix.rotate(rotation);
matrix.scale(scaling.x / current.x, scaling.y / current.y);
if (rotation)
matrix.rotate(-rotation);
matrix.translate(center.negate());
this.transform(matrix);
if (decomposed) { if (decomposed) {
decomposed.scaling = scaling; decomposed.scaling = scaling;
this._decomposed = decomposed; this._decomposed = decomposed;
@ -4218,8 +4269,6 @@ new function() {
transform: function(matrix, _applyMatrix, _applyRecursively, transform: function(matrix, _applyMatrix, _applyRecursively,
_setApplyMatrix) { _setApplyMatrix) {
if (matrix && matrix.isIdentity())
matrix = null;
var _matrix = this._matrix, var _matrix = this._matrix,
transform = matrix && !matrix.isIdentity(), transform = matrix && !matrix.isIdentity(),
applyMatrix = (_applyMatrix || this._applyMatrix) applyMatrix = (_applyMatrix || this._applyMatrix)
@ -4230,22 +4279,7 @@ new function() {
if (transform) { if (transform) {
if (!matrix.isInvertible() && _matrix.isInvertible()) if (!matrix.isInvertible() && _matrix.isInvertible())
_matrix._backup = _matrix.getValues(); _matrix._backup = _matrix.getValues();
_matrix.prepend(matrix); _matrix.prepend(matrix, true);
}
if (applyMatrix) {
if (this._transformContent(_matrix, _applyRecursively,
_setApplyMatrix)) {
var pivot = this._pivot;
if (pivot)
_matrix._transformPoint(pivot, pivot, true);
_matrix.reset(true);
if (_setApplyMatrix && this._canApplyMatrix)
this._applyMatrix = true;
} else {
applyMatrix = transform = false;
}
}
if (transform) {
var style = this._style, var style = this._style,
fillColor = style.getFillColor(true), fillColor = style.getFillColor(true),
strokeColor = style.getStrokeColor(true); strokeColor = style.getStrokeColor(true);
@ -4254,24 +4288,38 @@ new function() {
if (strokeColor) if (strokeColor)
strokeColor.transform(matrix); strokeColor.transform(matrix);
} }
if (applyMatrix && (applyMatrix = this._transformContent(_matrix,
_applyRecursively, _setApplyMatrix))) {
var pivot = this._pivot;
if (pivot)
_matrix._transformPoint(pivot, pivot, true);
_matrix.reset(true);
if (_setApplyMatrix && this._canApplyMatrix)
this._applyMatrix = true;
}
var bounds = this._bounds, var bounds = this._bounds,
position = this._position; position = this._position;
if (transform || applyMatrix) {
this._changed(9); this._changed(9);
var decomp = bounds && matrix && matrix.decompose(); }
if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) { var decomp = transform && bounds && matrix.decompose();
if (decomp && decomp.skewing.isZero() && decomp.rotation % 90 === 0) {
for (var key in bounds) { for (var key in bounds) {
var cache = bounds[key]; var cache = bounds[key];
if (applyMatrix || !cache.internal) { if (cache.nonscaling) {
delete bounds[key];
} else if (applyMatrix || !cache.internal) {
var rect = cache.rect; var rect = cache.rect;
matrix._transformBounds(rect, rect); matrix._transformBounds(rect, rect);
} }
} }
var getter = this._boundsGetter,
rect = bounds[getter && getter.getBounds || getter || 'getBounds'];
if (rect)
this._position = rect.getCenter(true);
this._bounds = bounds; this._bounds = bounds;
} else if (matrix && position) { var cached = bounds[this._getBoundsCacheKey(
this._boundsOptions || {})];
if (cached) {
this._position = cached.rect.getCenter(true);
}
} else if (transform && position && this._pivot) {
this._position = matrix._transformPoint(position, position); this._position = matrix._transformPoint(position, position);
} }
return this; return this;
@ -5483,10 +5531,8 @@ var HitResult = Base.extend({
initialize: function HitResult(type, item, values) { initialize: function HitResult(type, item, values) {
this.type = type; this.type = type;
this.item = item; this.item = item;
if (values) { if (values)
values.enumerable = true;
this.inject(values); this.inject(values);
}
}, },
statics: { statics: {
@ -7006,7 +7052,7 @@ new function() {
cos = Math.cos(angle), cos = Math.cos(angle),
rv = [], rv = [],
roots = []; roots = [];
for(var i = 0; i < 8; i += 2) { for (var i = 0; i < 8; i += 2) {
var x = v[i] - px, var x = v[i] - px,
y = v[i + 1] - py; y = v[i + 1] - py;
rv.push( rv.push(
@ -11016,10 +11062,10 @@ var PointText = TextItem.extend({
x = 0; x = 0;
if (justification !== 'left') if (justification !== 'left')
x -= width / (justification === 'center' ? 2: 1); x -= width / (justification === 'center' ? 2: 1);
var bounds = new Rectangle(x, var rect = new Rectangle(x,
numLines ? - 0.75 * leading : 0, numLines ? - 0.75 * leading : 0,
width, numLines * leading); width, numLines * leading);
return matrix ? matrix._transformBounds(bounds, bounds) : bounds; return matrix ? matrix._transformBounds(rect, rect) : rect;
} }
}); });
@ -12694,8 +12740,8 @@ new function() {
point, prevPoint) point, prevPoint)
|| hitItem && hitItem !== dragItem || hitItem && hitItem !== dragItem
&& !hitItem.isDescendant(dragItem) && !hitItem.isDescendant(dragItem)
&& emitMouseEvent(hitItem, null, fallbacks[type] || type, event, && emitMouseEvent(hitItem, null, type, event, point, prevPoint,
point, prevPoint, dragItem) dragItem)
|| emitMouseEvent(view, dragItem || hitItem || view, type, event, || emitMouseEvent(view, dragItem || hitItem || view, type, event,
point, prevPoint)); point, prevPoint));
} }
@ -13807,9 +13853,9 @@ new function() {
if (!Numerical.isZero(scale.x - 1) if (!Numerical.isZero(scale.x - 1)
|| !Numerical.isZero(scale.y - 1)) || !Numerical.isZero(scale.y - 1))
parts.push('scale(' + formatter.point(scale) +')'); parts.push('scale(' + formatter.point(scale) +')');
if (skew && skew.x) if (skew.x)
parts.push('skewX(' + formatter.number(skew.x) + ')'); parts.push('skewX(' + formatter.number(skew.x) + ')');
if (skew && skew.y) if (skew.y)
parts.push('skewY(' + formatter.number(skew.y) + ')'); parts.push('skewY(' + formatter.number(skew.y) + ')');
attrs.transform = parts.join(' '); attrs.transform = parts.join(' ');
} else { } else {
@ -13870,8 +13916,9 @@ new function() {
if (length > 2) { if (length > 2) {
type = item._closed ? 'polygon' : 'polyline'; type = item._closed ? 'polygon' : 'polyline';
var parts = []; var parts = [];
for(var i = 0; i < length; i++) for (var i = 0; i < length; i++) {
parts.push(formatter.point(segments[i]._point)); parts.push(formatter.point(segments[i]._point));
}
attrs.points = parts.join(' '); attrs.points = parts.join(' ');
} else { } else {
type = 'line'; type = 'line';
@ -14142,6 +14189,7 @@ new function() {
? new Rectangle([0, 0], view.getViewSize()) ? new Rectangle([0, 0], view.getViewSize())
: bounds === 'content' : bounds === 'content'
? Item._getBounds(children, matrix, { stroke: true }) ? Item._getBounds(children, matrix, { stroke: true })
.rect
: Rectangle.read([bounds], 0, { readNull: true }), : Rectangle.read([bounds], 0, { readNull: true }),
attrs = { attrs = {
version: '1.1', version: '1.1',
@ -14715,7 +14763,6 @@ new function() {
}; };
paper = new (PaperScope.inject(Base.exports, { paper = new (PaperScope.inject(Base.exports, {
enumerable: true,
Base: Base, Base: Base,
Numerical: Numerical, Numerical: Numerical,
Key: Key, Key: Key,

223
dist/paper-full.js vendored
View file

@ -1,5 +1,5 @@
/*! /*!
* Paper.js v0.11.2 - The Swiss Army Knife of Vector Graphics Scripting. * Paper.js v0.11.3 - The Swiss Army Knife of Vector Graphics Scripting.
* http://paperjs.org/ * http://paperjs.org/
* *
* Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey * Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey
@ -9,7 +9,7 @@
* *
* All rights reserved. * All rights reserved.
* *
* Date: Thu Apr 20 19:14:30 2017 +0200 * Date: Sat Apr 22 20:01:34 2017 +0200
* *
*** ***
* *
@ -98,12 +98,13 @@ var Base = new function() {
&& (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/))) && (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/)))
beansNames[bean[3].toLowerCase() + bean[4]] = bean[2]; beansNames[bean[3].toLowerCase() + bean[4]] = bean[2];
if (!res || isFunc || !res.get || typeof res.get !== 'function' if (!res || isFunc || !res.get || typeof res.get !== 'function'
|| !Base.isPlainObject(res)) || !Base.isPlainObject(res)) {
res = { value: res, writable: true }; res = { value: res, writable: true };
}
if ((describe(dest, name) if ((describe(dest, name)
|| { configurable: true }).configurable) { || { configurable: true }).configurable) {
res.configurable = true; res.configurable = true;
res.enumerable = enumerable; res.enumerable = enumerable != null ? enumerable : !bean;
} }
define(dest, name, res); define(dest, name, res);
} }
@ -141,7 +142,7 @@ var Base = new function() {
preserve = src.preserve; preserve = src.preserve;
if (statics !== src) if (statics !== src)
inject(this.prototype, src, src.enumerable, beans, preserve); inject(this.prototype, src, src.enumerable, beans, preserve);
inject(this, statics, true, beans, preserve); inject(this, statics, null, beans, preserve);
} }
for (var i = 1, l = arguments.length; i < l; i++) for (var i = 1, l = arguments.length; i < l; i++)
this.inject(arguments[i]); this.inject(arguments[i]);
@ -164,13 +165,15 @@ var Base = new function() {
proto = ctor.prototype = proto || create(this.prototype); proto = ctor.prototype = proto || create(this.prototype);
define(proto, 'constructor', define(proto, 'constructor',
{ value: ctor, writable: true, configurable: true }); { value: ctor, writable: true, configurable: true });
inject(ctor, this, true); inject(ctor, this);
if (arguments.length) if (arguments.length)
this.inject.apply(ctor, arguments); this.inject.apply(ctor, arguments);
ctor.base = base; ctor.base = base;
return ctor; return ctor;
} }
}, true).inject({ }).inject({
enumerable: false,
initialize: Base, initialize: Base,
set: Base, set: Base,
@ -230,6 +233,8 @@ if (typeof module !== 'undefined')
module.exports = Base; module.exports = Base;
Base.inject({ Base.inject({
enumerable: false,
toString: function() { toString: function() {
return this._id != null return this._id != null
? (this._class || 'Object') + (this._name ? (this._class || 'Object') + (this._name
@ -265,13 +270,12 @@ Base.inject({
if (props) if (props)
Base.filter(this, props, exclude, this._prioritize); Base.filter(this, props, exclude, this._prioritize);
return this; return this;
}, }
}, {
statics: { beans: false,
statics: {
exports: { exports: {},
enumerable: true
},
extend: function extend() { extend: function extend() {
var res = extend.base.apply(this, arguments), var res = extend.base.apply(this, arguments),
@ -341,6 +345,11 @@ Base.inject({
: list) || obj; : list) || obj;
if (readIndex) { if (readIndex) {
list.__index = begin + obj.__read; list.__index = begin + obj.__read;
var filtered = obj.__filtered;
if (filtered) {
list.__filtered = filtered;
obj.__filtered = undefined;
}
obj.__read = undefined; obj.__read = undefined;
} }
return obj; return obj;
@ -371,14 +380,16 @@ Base.inject({
var value = this.getNamed(list, name), var value = this.getNamed(list, name),
hasObject = value !== undefined; hasObject = value !== undefined;
if (hasObject) { if (hasObject) {
var filtered = list._filtered; var filtered = list.__filtered;
if (!filtered) { if (!filtered) {
filtered = list._filtered = Base.create(list[0]); filtered = list.__filtered = Base.create(list[0]);
filtered._unfiltered = list[0]; filtered.__unfiltered = list[0];
} }
filtered[name] = undefined; filtered[name] = undefined;
} }
return this.read(hasObject ? [value] : list, start, options, amount); var l = hasObject ? [value] : list,
res = this.read(l, start, options, amount);
return res;
}, },
getNamed: function(list, name) { getNamed: function(list, name) {
@ -386,7 +397,7 @@ Base.inject({
if (list._hasObject === undefined) if (list._hasObject === undefined)
list._hasObject = list.length === 1 && Base.isPlainObject(arg); list._hasObject = list.length === 1 && Base.isPlainObject(arg);
if (list._hasObject) if (list._hasObject)
return name ? arg[name] : list._filtered || arg; return name ? arg[name] : list.__filtered || arg;
}, },
hasNamed: function(list, name) { hasNamed: function(list, name) {
@ -416,7 +427,7 @@ Base.inject({
processed = keys; processed = keys;
} }
Object.keys(source._unfiltered || source).forEach(handleKey); Object.keys(source.__unfiltered || source).forEach(handleKey);
return dest; return dest;
}, },
@ -462,8 +473,7 @@ Base.inject({
} else if (Array.isArray(obj)) { } else if (Array.isArray(obj)) {
res = []; res = [];
for (var i = 0, l = obj.length; i < l; i++) for (var i = 0, l = obj.length; i < l; i++)
res[i] = Base.serialize(obj[i], options, compact, res[i] = Base.serialize(obj[i], options, compact, dictionary);
dictionary);
} else if (Base.isPlainObject(obj)) { } else if (Base.isPlainObject(obj)) {
res = {}; res = {};
var keys = Object.keys(obj); var keys = Object.keys(obj);
@ -586,8 +596,7 @@ Base.inject({
hyphenate: function(str) { hyphenate: function(str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
} }
} }});
});
var Emitter = { var Emitter = {
on: function(type, func) { on: function(type, func) {
@ -769,7 +778,7 @@ var PaperScope = Base.extend({
} }
}, },
version: "0.11.2", version: "0.11.3",
getView: function() { getView: function() {
var project = this.project; var project = this.project;
@ -1739,6 +1748,9 @@ var Rectangle = Base.extend({
} }
this._set(x, y, width, height); this._set(x, y, width, height);
read = arguments.__index; read = arguments.__index;
var filtered = arguments.__filtered;
if (filtered)
this.__filtered = filtered;
} }
if (this.__read) if (this.__read)
this.__read = read; this.__read = read;
@ -2262,7 +2274,7 @@ var Matrix = Base.extend({
return this.shear(shear, center); return this.shear(shear, center);
}, },
append: function(mx) { append: function(mx, _dontNotify) {
if (mx) { if (mx) {
var a1 = this._a, var a1 = this._a,
b1 = this._b, b1 = this._b,
@ -2280,12 +2292,13 @@ var Matrix = Base.extend({
this._d = b2 * b1 + d2 * d1; this._d = b2 * b1 + d2 * d1;
this._tx += tx2 * a1 + ty2 * c1; this._tx += tx2 * a1 + ty2 * c1;
this._ty += tx2 * b1 + ty2 * d1; this._ty += tx2 * b1 + ty2 * d1;
if (!_dontNotify)
this._changed(); this._changed();
} }
return this; return this;
}, },
prepend: function(mx) { prepend: function(mx, _dontNotify) {
if (mx) { if (mx) {
var a1 = this._a, var a1 = this._a,
b1 = this._b, b1 = this._b,
@ -2305,6 +2318,7 @@ var Matrix = Base.extend({
this._d = c2 * c1 + d2 * d1; this._d = c2 * c1 + d2 * d1;
this._tx = a2 * tx1 + b2 * ty1 + tx2; this._tx = a2 * tx1 + b2 * ty1 + tx2;
this._ty = c2 * tx1 + d2 * ty1 + ty2; this._ty = c2 * tx1 + d2 * ty1 + ty2;
if (!_dontNotify)
this._changed(); this._changed();
} }
return this; return this;
@ -3236,11 +3250,11 @@ new function() {
this._boundsOptions); this._boundsOptions);
if (!opts.stroke || this.getStrokeScaling()) if (!opts.stroke || this.getStrokeScaling())
opts.cacheItem = this; opts.cacheItem = this;
var bounds = this._getCachedBounds(hasMatrix && matrix, opts); var rect = this._getCachedBounds(hasMatrix && matrix, opts).rect;
return !arguments.length return !arguments.length
? new LinkedRectangle(bounds.x, bounds.y, bounds.width, ? new LinkedRectangle(rect.x, rect.y, rect.width, rect.height,
bounds.height, this, 'setBounds') this, 'setBounds')
: bounds; : rect;
}, },
setBounds: function() { setBounds: function() {
@ -3273,29 +3287,49 @@ new function() {
return Item._getBounds(children, matrix, options); return Item._getBounds(children, matrix, options);
}, },
_getBoundsCacheKey: function(options, internal) {
return [
options.stroke ? 1 : 0,
options.handle ? 1 : 0,
internal ? 1 : 0
].join('');
},
_getCachedBounds: function(matrix, options, noInternal) { _getCachedBounds: function(matrix, options, noInternal) {
matrix = matrix && matrix._orNullIfIdentity(); matrix = matrix && matrix._orNullIfIdentity();
var internal = options.internal && !noInternal, var internal = options.internal && !noInternal,
cacheItem = options.cacheItem, cacheItem = options.cacheItem,
_matrix = internal ? null : this._matrix._orNullIfIdentity(), _matrix = internal ? null : this._matrix._orNullIfIdentity(),
cacheKey = cacheItem && (!matrix || matrix.equals(_matrix)) && [ cacheKey = cacheItem && (!matrix || matrix.equals(_matrix))
options.stroke ? 1 : 0, && this._getBoundsCacheKey(options, internal),
options.handle ? 1 : 0, bounds = this._bounds;
internal ? 1 : 0
].join('');
Item._updateBoundsCache(this._parent || this._symbol, cacheItem); Item._updateBoundsCache(this._parent || this._symbol, cacheItem);
if (cacheKey && this._bounds && cacheKey in this._bounds) if (cacheKey && bounds && cacheKey in bounds) {
return this._bounds[cacheKey].rect.clone(); var cached = bounds[cacheKey];
var bounds = this._getBounds(matrix || _matrix, options); return {
rect: cached.rect.clone(),
nonscaling: cached.nonscaling
};
}
var res = this._getBounds(matrix || _matrix, options),
rect = res.rect || res,
style = this._style,
nonscaling = res.nonscaling || style.hasStroke()
&& !style.getStrokeScaling();
if (cacheKey) { if (cacheKey) {
if (!this._bounds) if (!bounds) {
this._bounds = {}; this._bounds = bounds = {};
var cached = this._bounds[cacheKey] = { }
rect: bounds.clone(), var cached = bounds[cacheKey] = {
rect: rect.clone(),
nonscaling: nonscaling,
internal: internal internal: internal
}; };
} }
return bounds; return {
rect: rect,
nonscaling: nonscaling
};
}, },
_getStrokeMatrix: function(matrix, options) { _getStrokeMatrix: function(matrix, options) {
@ -3340,22 +3374,29 @@ new function() {
var x1 = Infinity, var x1 = Infinity,
x2 = -x1, x2 = -x1,
y1 = x1, y1 = x1,
y2 = x2; y2 = x2,
nonscaling = false;
options = options || {}; options = options || {};
for (var i = 0, l = items.length; i < l; i++) { for (var i = 0, l = items.length; i < l; i++) {
var item = items[i]; var item = items[i];
if (item._visible && !item.isEmpty()) { if (item._visible && !item.isEmpty()) {
var rect = item._getCachedBounds( var bounds = item._getCachedBounds(
matrix && matrix.appended(item._matrix), options, true); matrix && matrix.appended(item._matrix), options, true),
rect = bounds.rect;
x1 = Math.min(rect.x, x1); x1 = Math.min(rect.x, x1);
y1 = Math.min(rect.y, y1); y1 = Math.min(rect.y, y1);
x2 = Math.max(rect.x + rect.width, x2); x2 = Math.max(rect.x + rect.width, x2);
y2 = Math.max(rect.y + rect.height, y2); y2 = Math.max(rect.y + rect.height, y2);
if (bounds.nonscaling)
nonscaling = true;
} }
} }
return isFinite(x1) return {
rect: isFinite(x1)
? new Rectangle(x1, y1, x2 - x1, y2 - y1) ? new Rectangle(x1, y1, x2 - x1, y2 - y1)
: new Rectangle(); : new Rectangle(),
nonscaling: nonscaling
};
} }
} }
@ -3395,8 +3436,18 @@ new function() {
var current = this.getScaling(), var current = this.getScaling(),
scaling = Point.read(arguments, 0, { clone: true, readNull: true }); scaling = Point.read(arguments, 0, { clone: true, readNull: true });
if (current && scaling && !current.equals(scaling)) { if (current && scaling && !current.equals(scaling)) {
var decomposed = this._decomposed; var rotation = this.getRotation(),
this.scale(scaling.x / current.x, scaling.y / current.y); decomposed = this._decomposed,
matrix = new Matrix(),
center = this.getPosition(true);
matrix.translate(center);
if (rotation)
matrix.rotate(rotation);
matrix.scale(scaling.x / current.x, scaling.y / current.y);
if (rotation)
matrix.rotate(-rotation);
matrix.translate(center.negate());
this.transform(matrix);
if (decomposed) { if (decomposed) {
decomposed.scaling = scaling; decomposed.scaling = scaling;
this._decomposed = decomposed; this._decomposed = decomposed;
@ -4218,8 +4269,6 @@ new function() {
transform: function(matrix, _applyMatrix, _applyRecursively, transform: function(matrix, _applyMatrix, _applyRecursively,
_setApplyMatrix) { _setApplyMatrix) {
if (matrix && matrix.isIdentity())
matrix = null;
var _matrix = this._matrix, var _matrix = this._matrix,
transform = matrix && !matrix.isIdentity(), transform = matrix && !matrix.isIdentity(),
applyMatrix = (_applyMatrix || this._applyMatrix) applyMatrix = (_applyMatrix || this._applyMatrix)
@ -4230,22 +4279,7 @@ new function() {
if (transform) { if (transform) {
if (!matrix.isInvertible() && _matrix.isInvertible()) if (!matrix.isInvertible() && _matrix.isInvertible())
_matrix._backup = _matrix.getValues(); _matrix._backup = _matrix.getValues();
_matrix.prepend(matrix); _matrix.prepend(matrix, true);
}
if (applyMatrix) {
if (this._transformContent(_matrix, _applyRecursively,
_setApplyMatrix)) {
var pivot = this._pivot;
if (pivot)
_matrix._transformPoint(pivot, pivot, true);
_matrix.reset(true);
if (_setApplyMatrix && this._canApplyMatrix)
this._applyMatrix = true;
} else {
applyMatrix = transform = false;
}
}
if (transform) {
var style = this._style, var style = this._style,
fillColor = style.getFillColor(true), fillColor = style.getFillColor(true),
strokeColor = style.getStrokeColor(true); strokeColor = style.getStrokeColor(true);
@ -4254,24 +4288,38 @@ new function() {
if (strokeColor) if (strokeColor)
strokeColor.transform(matrix); strokeColor.transform(matrix);
} }
if (applyMatrix && (applyMatrix = this._transformContent(_matrix,
_applyRecursively, _setApplyMatrix))) {
var pivot = this._pivot;
if (pivot)
_matrix._transformPoint(pivot, pivot, true);
_matrix.reset(true);
if (_setApplyMatrix && this._canApplyMatrix)
this._applyMatrix = true;
}
var bounds = this._bounds, var bounds = this._bounds,
position = this._position; position = this._position;
if (transform || applyMatrix) {
this._changed(9); this._changed(9);
var decomp = bounds && matrix && matrix.decompose(); }
if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) { var decomp = transform && bounds && matrix.decompose();
if (decomp && decomp.skewing.isZero() && decomp.rotation % 90 === 0) {
for (var key in bounds) { for (var key in bounds) {
var cache = bounds[key]; var cache = bounds[key];
if (applyMatrix || !cache.internal) { if (cache.nonscaling) {
delete bounds[key];
} else if (applyMatrix || !cache.internal) {
var rect = cache.rect; var rect = cache.rect;
matrix._transformBounds(rect, rect); matrix._transformBounds(rect, rect);
} }
} }
var getter = this._boundsGetter,
rect = bounds[getter && getter.getBounds || getter || 'getBounds'];
if (rect)
this._position = rect.getCenter(true);
this._bounds = bounds; this._bounds = bounds;
} else if (matrix && position) { var cached = bounds[this._getBoundsCacheKey(
this._boundsOptions || {})];
if (cached) {
this._position = cached.rect.getCenter(true);
}
} else if (transform && position && this._pivot) {
this._position = matrix._transformPoint(position, position); this._position = matrix._transformPoint(position, position);
} }
return this; return this;
@ -5483,10 +5531,8 @@ var HitResult = Base.extend({
initialize: function HitResult(type, item, values) { initialize: function HitResult(type, item, values) {
this.type = type; this.type = type;
this.item = item; this.item = item;
if (values) { if (values)
values.enumerable = true;
this.inject(values); this.inject(values);
}
}, },
statics: { statics: {
@ -7006,7 +7052,7 @@ new function() {
cos = Math.cos(angle), cos = Math.cos(angle),
rv = [], rv = [],
roots = []; roots = [];
for(var i = 0; i < 8; i += 2) { for (var i = 0; i < 8; i += 2) {
var x = v[i] - px, var x = v[i] - px,
y = v[i + 1] - py; y = v[i + 1] - py;
rv.push( rv.push(
@ -11016,10 +11062,10 @@ var PointText = TextItem.extend({
x = 0; x = 0;
if (justification !== 'left') if (justification !== 'left')
x -= width / (justification === 'center' ? 2: 1); x -= width / (justification === 'center' ? 2: 1);
var bounds = new Rectangle(x, var rect = new Rectangle(x,
numLines ? - 0.75 * leading : 0, numLines ? - 0.75 * leading : 0,
width, numLines * leading); width, numLines * leading);
return matrix ? matrix._transformBounds(bounds, bounds) : bounds; return matrix ? matrix._transformBounds(rect, rect) : rect;
} }
}); });
@ -12694,8 +12740,8 @@ new function() {
point, prevPoint) point, prevPoint)
|| hitItem && hitItem !== dragItem || hitItem && hitItem !== dragItem
&& !hitItem.isDescendant(dragItem) && !hitItem.isDescendant(dragItem)
&& emitMouseEvent(hitItem, null, fallbacks[type] || type, event, && emitMouseEvent(hitItem, null, type, event, point, prevPoint,
point, prevPoint, dragItem) dragItem)
|| emitMouseEvent(view, dragItem || hitItem || view, type, event, || emitMouseEvent(view, dragItem || hitItem || view, type, event,
point, prevPoint)); point, prevPoint));
} }
@ -13807,9 +13853,9 @@ new function() {
if (!Numerical.isZero(scale.x - 1) if (!Numerical.isZero(scale.x - 1)
|| !Numerical.isZero(scale.y - 1)) || !Numerical.isZero(scale.y - 1))
parts.push('scale(' + formatter.point(scale) +')'); parts.push('scale(' + formatter.point(scale) +')');
if (skew && skew.x) if (skew.x)
parts.push('skewX(' + formatter.number(skew.x) + ')'); parts.push('skewX(' + formatter.number(skew.x) + ')');
if (skew && skew.y) if (skew.y)
parts.push('skewY(' + formatter.number(skew.y) + ')'); parts.push('skewY(' + formatter.number(skew.y) + ')');
attrs.transform = parts.join(' '); attrs.transform = parts.join(' ');
} else { } else {
@ -13870,8 +13916,9 @@ new function() {
if (length > 2) { if (length > 2) {
type = item._closed ? 'polygon' : 'polyline'; type = item._closed ? 'polygon' : 'polyline';
var parts = []; var parts = [];
for(var i = 0; i < length; i++) for (var i = 0; i < length; i++) {
parts.push(formatter.point(segments[i]._point)); parts.push(formatter.point(segments[i]._point));
}
attrs.points = parts.join(' '); attrs.points = parts.join(' ');
} else { } else {
type = 'line'; type = 'line';
@ -14142,6 +14189,7 @@ new function() {
? new Rectangle([0, 0], view.getViewSize()) ? new Rectangle([0, 0], view.getViewSize())
: bounds === 'content' : bounds === 'content'
? Item._getBounds(children, matrix, { stroke: true }) ? Item._getBounds(children, matrix, { stroke: true })
.rect
: Rectangle.read([bounds], 0, { readNull: true }), : Rectangle.read([bounds], 0, { readNull: true }),
attrs = { attrs = {
version: '1.1', version: '1.1',
@ -16413,7 +16461,6 @@ Base.exports.PaperScript = function() {
}.call(this); }.call(this);
paper = new (PaperScope.inject(Base.exports, { paper = new (PaperScope.inject(Base.exports, {
enumerable: true,
Base: Base, Base: Base,
Numerical: Numerical, Numerical: Numerical,
Key: Key, Key: Key,

View file

@ -13,7 +13,7 @@
var clones = 30; var clones = 30;
var angle = 360 / clones; var angle = 360 / clones;
for(var i = 0; i < clones; i++) { for (var i = 0; i < clones; i++) {
var clonedPath = circlePath.clone(); var clonedPath = circlePath.clone();
clonedPath.rotate(angle * i, circlePath.bounds.topLeft); clonedPath.rotate(angle * i, circlePath.bounds.topLeft);
}; };

View file

@ -120,7 +120,7 @@
function getEqualizerBands(data) { function getEqualizerBands(data) {
var bands = []; var bands = [];
var amount = Math.sqrt(data.length) / 2; var amount = Math.sqrt(data.length) / 2;
for(var i = 0; i < amount; i++) { for (var i = 0; i < amount; i++) {
var start = Math.pow(2, i) - 1; var start = Math.pow(2, i) - 1;
var end = start * 2 + 1; var end = start * 2 + 1;
var sum = 0; var sum = 0;

View file

@ -55,7 +55,7 @@
function removeSmallBits(path) { function removeSmallBits(path) {
var averageLength = path.length / path.segments.length; var averageLength = path.length / path.segments.length;
var min = path.length / 50; var min = path.length / 50;
for(var i = path.segments.length - 1; i >= 0; i--) { for (var i = path.segments.length - 1; i >= 0; i--) {
var segment = path.segments[i]; var segment = path.segments[i];
var cur = segment.point; var cur = segment.point;
var nextSegment = segment.next; var nextSegment = segment.next;
@ -69,8 +69,8 @@
function generateBeeHivePoints(size, loose) { function generateBeeHivePoints(size, loose) {
var points = []; var points = [];
var col = view.size / size; var col = view.size / size;
for(var i = -1; i < size.width + 1; i++) { for (var i = -1; i < size.width + 1; i++) {
for(var j = -1; j < size.height + 1; j++) { for (var j = -1; j < size.height + 1; j++) {
var point = new Point(i, j) / new Point(size) * view.size + col / 2; var point = new Point(i, j) / new Point(size) * view.size + col / 2;
if (j % 2) if (j % 2)
point += new Point(col.width / 2, 0); point += new Point(col.width / 2, 0);

View file

@ -15,7 +15,7 @@
var clones = 30; var clones = 30;
var angle = 360 / clones; var angle = 360 / clones;
for(var i = 0; i < clones; i++) { for (var i = 0; i < clones; i++) {
var clonedPath = circlePath.clone(); var clonedPath = circlePath.clone();
clonedPath.rotate(angle * i, circlePath.bounds.topLeft); clonedPath.rotate(angle * i, circlePath.bounds.topLeft);
}; };

View file

@ -36,13 +36,15 @@ gulp.task('publish', function() {
if (options.branch !== 'develop') { if (options.branch !== 'develop') {
throw new Error('Publishing is only allowed on the develop branch.'); throw new Error('Publishing is only allowed on the develop branch.');
} }
// publish:website comes before publish:release, so paperjs.zip file is gone
// before npm publish:
return run( return run(
'publish:json', 'publish:json',
'publish:dist', 'publish:dist',
'publish:packages', 'publish:packages',
'publish:commit', 'publish:commit',
'publish:release',
'publish:website', 'publish:website',
'publish:release',
'publish:load' 'publish:load'
); );
}); });
@ -115,8 +117,18 @@ gulp.task('publish:website', function() {
} }
}); });
gulp.task('publish:website:build', gulp.task('publish:website:build', [
['publish:website:docs', 'publish:website:zip', 'publish:website:lib']); 'publish:website:json', 'publish:website:docs',
'publish:website:zip', 'publish:website:assets'
]);
gulp.task('publish:website:json', ['publish:version'], function() {
return gulp.src([sitePath + '/package.json'])
.pipe(jsonEditor({
version: options.version
}, jsonOptions))
.pipe(gulp.dest(sitePath));
});
gulp.task('publish:website:docs:clean', function() { gulp.task('publish:website:docs:clean', function() {
return del([ referencePath + '/*' ], { force: true }); return del([ referencePath + '/*' ], { force: true });
@ -135,7 +147,10 @@ gulp.task('publish:website:zip', ['publish:version'], function() {
.pipe(gulp.dest(downloadPath)); .pipe(gulp.dest(downloadPath));
}); });
gulp.task('publish:website:lib', ['publish:version'], function() { gulp.task('publish:website:assets', function() {
// Always delete the old asset first, in case it's a symlink which Gulp
// doesn't handle well.
fs.unlinkSync(assetPath + '/paper.js');
return gulp.src('dist/paper-full.js') return gulp.src('dist/paper-full.js')
.pipe(rename({ basename: 'paper' })) .pipe(rename({ basename: 'paper' }))
.pipe(gulp.dest(assetPath)); .pipe(gulp.dest(assetPath));

View file

@ -1,12 +1,12 @@
{ {
"name": "paper", "name": "paper",
"version": "0.11.2", "version": "0.11.3",
"description": "The Swiss Army Knife of Vector Graphics Scripting", "description": "The Swiss Army Knife of Vector Graphics Scripting",
"license": "MIT", "license": "MIT",
"homepage": "http://paperjs.org", "homepage": "http://paperjs.org",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/paperjs/paper.js" "url": "https://github.com/paperjs/paper.js"
}, },
"bugs": "https://github.com/paperjs/paper.js/issues", "bugs": "https://github.com/paperjs/paper.js/issues",
"contributors": [ "contributors": [
@ -69,7 +69,7 @@
"run-sequence": "^1.2.2", "run-sequence": "^1.2.2",
"source-map-support": "^0.4.0", "source-map-support": "^0.4.0",
"stats.js": "0.16.0", "stats.js": "0.16.0",
"straps": "^2.1.0" "straps": "^3.0.1"
}, },
"browser": { "browser": {
"canvas": false, "canvas": false,

@ -1 +1 @@
Subproject commit bab27f25fed8d78f072d8f9a9f68da61e7d1e975 Subproject commit fc7ac57828aefadff29f7559a5e39f88d35b0c66

@ -1 +1 @@
Subproject commit 2e257a436e1cfec74ca6ffe4828a761ec058b42f Subproject commit 18feab4d8968339c60d8610584ab3574c49d7a91

View file

@ -377,7 +377,7 @@ var Matrix = Base.extend(/** @lends Matrix# */{
* @param {Matrix} matrix the matrix to append * @param {Matrix} matrix the matrix to append
* @return {Matrix} this matrix, modified * @return {Matrix} this matrix, modified
*/ */
append: function(mx) { append: function(mx, _dontNotify) {
if (mx) { if (mx) {
var a1 = this._a, var a1 = this._a,
b1 = this._b, b1 = this._b,
@ -395,6 +395,7 @@ var Matrix = Base.extend(/** @lends Matrix# */{
this._d = b2 * b1 + d2 * d1; this._d = b2 * b1 + d2 * d1;
this._tx += tx2 * a1 + ty2 * c1; this._tx += tx2 * a1 + ty2 * c1;
this._ty += tx2 * b1 + ty2 * d1; this._ty += tx2 * b1 + ty2 * d1;
if (!_dontNotify)
this._changed(); this._changed();
} }
return this; return this;
@ -407,7 +408,7 @@ var Matrix = Base.extend(/** @lends Matrix# */{
* @param {Matrix} matrix the matrix to prepend * @param {Matrix} matrix the matrix to prepend
* @return {Matrix} this matrix, modified * @return {Matrix} this matrix, modified
*/ */
prepend: function(mx) { prepend: function(mx, _dontNotify) {
if (mx) { if (mx) {
var a1 = this._a, var a1 = this._a,
b1 = this._b, b1 = this._b,
@ -427,6 +428,7 @@ var Matrix = Base.extend(/** @lends Matrix# */{
this._d = c2 * c1 + d2 * d1; this._d = c2 * c1 + d2 * d1;
this._tx = a2 * tx1 + b2 * ty1 + tx2; this._tx = a2 * tx1 + b2 * ty1 + tx2;
this._ty = c2 * tx1 + d2 * ty1 + ty2; this._ty = c2 * tx1 + d2 * ty1 + ty2;
if (!_dontNotify)
this._changed(); this._changed();
} }
return this; return this;
@ -671,7 +673,7 @@ var Matrix = Base.extend(/** @lends Matrix# */{
/** /**
* Attempts to decompose the affine transformation described by this matrix * Attempts to decompose the affine transformation described by this matrix
* into `scaling`, `rotation` and `shearing`, and returns an object with * into `scaling`, `rotation` and `skewing`, and returns an object with
* these properties if it succeeded, `null` otherwise. * these properties if it succeeded, `null` otherwise.
* *
* @return {Object} the decomposed matrix, or `null` if decomposition is not * @return {Object} the decomposed matrix, or `null` if decomposition is not

View file

@ -141,6 +141,12 @@ var Rectangle = Base.extend(/** @lends Rectangle# */{
} }
this._set(x, y, width, height); this._set(x, y, width, height);
read = arguments.__index; read = arguments.__index;
// arguments.__filtered wouldn't survive the function call even if a
// previous arguments list was passed through Function#apply().
// Return it on the object instead, see Base.read()
var filtered = arguments.__filtered;
if (filtered)
this.__filtered = filtered;
} }
if (this.__read) if (this.__read)
this.__read = read; this.__read = read;

View file

@ -17,6 +17,8 @@
*/ */
// Extend Base with utility functions used across the library. // Extend Base with utility functions used across the library.
Base.inject(/** @lends Base# */{ Base.inject(/** @lends Base# */{
enumerable: false,
/** /**
* Renders base objects to strings in object literal notation. * Renders base objects to strings in object literal notation.
*/ */
@ -92,14 +94,15 @@ Base.inject(/** @lends Base# */{
if (props) if (props)
Base.filter(this, props, exclude, this._prioritize); Base.filter(this, props, exclude, this._prioritize);
return this; return this;
}, }
}, /** @lends Base# */{
statics: /** @lends Base */{ // Mess with indentation in order to get more line-space for the statics below.
// Explicitly deactivate the creation of beans, as we have functions here
// that look like bean getters but actually read arguments, see getNamed().
beans: false,
statics: /** @lends Base */{
// Keep track of all named classes for serialization and exporting. // Keep track of all named classes for serialization and exporting.
exports: { exports: {},
enumerable: true // For PaperScope.inject() in export.js
},
extend: function extend() { extend: function extend() {
// Override Base.extend() to register named classes in Base.exports, // Override Base.extend() to register named classes in Base.exports,
@ -112,9 +115,9 @@ Base.inject(/** @lends Base# */{
}, },
/** /**
* Checks if two values or objects are equals to each other, by using * Checks if two values or objects are equals to each other, by using their
* their equals() methods if available, and also comparing elements of * equals() methods if available, and also comparing elements of arrays and
* arrays and properties of objects. * properties of objects.
*/ */
equals: function(obj1, obj2) { equals: function(obj1, obj2) {
if (obj1 === obj2) if (obj1 === obj2)
@ -158,26 +161,25 @@ Base.inject(/** @lends Base# */{
}, },
/** /**
* When called on a subclass of Base, it reads arguments of the type of * When called on a subclass of Base, it reads arguments of the type of the
* the subclass from the passed arguments list or array, at the given * subclass from the passed arguments list or array, at the given index, up
* index, up to the specified length. * to the specified length. When called directly on Base, it reads any value
* When called directly on Base, it reads any value without conversion * without conversion from the passed arguments list or array. This is used
* from the passed arguments list or array. * in argument conversion, e.g. by all basic types (Point, Size, Rectangle)
* This is used in argument conversion, e.g. by all basic types (Point, * and also higher classes such as Color and Segment.
* Size, Rectangle) and also higher classes such as Color and Segment.
* *
* @param {Array} list the list to read from, either an arguments object * @param {Array} list the list to read from, either an arguments object or
* or a normal array * a normal array
* @param {Number} start the index at which to start reading in the list * @param {Number} start the index at which to start reading in the list
* @param {Object} options `options.readNull` controls whether null is * @param {Object} options `options.readNull` controls whether null is
* returned or converted. `options.clone` controls whether passed * returned or converted. `options.clone` controls whether passed
* objects should be cloned if they are already provided in the * objects should be cloned if they are already provided in the required
* required type * type
* @param {Number} length the amount of elements that can be read * @param {Number} length the amount of elements that can be read
*/ */
read: function(list, start, options, amount) { read: function(list, start, options, amount) {
// See if it's called directly on Base, and if so, read value and // See if it's called directly on Base, and if so, read value and return
// return without object conversion. // without object conversion.
if (this === Base) { if (this === Base) {
var value = this.peek(list, start); var value = this.peek(list, start);
list.__index++; list.__index++;
@ -189,9 +191,9 @@ Base.inject(/** @lends Base# */{
length = list.length, length = list.length,
obj = list[begin]; obj = list[begin];
amount = amount || length - begin; amount = amount || length - begin;
// When read() is called on a sub-class of which the object is // When read() is called on a sub-class of which the object is already
// already an instance, or when there is only one value in the list // an instance, or when there is only one value in the list and it's
// and it's null or undefined, return the obj. // null or undefined, return the obj.
if (obj instanceof this if (obj instanceof this
|| options && options.readNull && obj == null && amount <= 1) { || options && options.readNull && obj == null && amount <= 1) {
if (readIndex) if (readIndex)
@ -208,6 +210,14 @@ Base.inject(/** @lends Base# */{
: list) || obj; : list) || obj;
if (readIndex) { if (readIndex) {
list.__index = begin + obj.__read; list.__index = begin + obj.__read;
// This is only in use in Rectangle so far: Nested calls to
// `Base.readNamed()` would loose __filtered if it wasn't returned
// on the object.
var filtered = obj.__filtered;
if (filtered) {
list.__filtered = filtered;
obj.__filtered = undefined;
}
obj.__read = undefined; obj.__read = undefined;
} }
return obj; return obj;
@ -259,20 +269,20 @@ Base.inject(/** @lends Base# */{
}, },
/** /**
* Allows using of Base.read() mechanism in combination with reading * Allows using of Base.read() mechanism in combination with reading named
* named arguments form a passed property object literal. Calling * arguments form a passed property object literal. Calling Base.readNamed()
* Base.readNamed() can read both from such named properties and normal * can read both from such named properties and normal unnamed arguments
* unnamed arguments through Base.read(). In use for example for the * through Base.read(). In use for example for the various
* various Path.Constructors. * Path.Constructors.
* *
* @param {Array} list the list to read from, either an arguments object * @param {Array} list the list to read from, either an arguments object or
* or a normal array * a normal array
* @param {String} name the property name to read from * @param {String} name the property name to read from
* @param {Number} start the index at which to start reading in the list * @param {Number} start the index at which to start reading in the list
* @param {Object} options `options.readNull` controls whether null is * @param {Object} options `options.readNull` controls whether null is
* returned or converted. `options.clone` controls whether passed * returned or converted. `options.clone` controls whether passed
* objects should be cloned if they are already provided in the * objects should be cloned if they are already provided in the required
* required type * type
* @param {Number} amount the amount of elements that can be read * @param {Number} amount the amount of elements that can be read
*/ */
readNamed: function(list, name, start, options, amount) { readNamed: function(list, name, start, options, amount) {
@ -281,18 +291,20 @@ Base.inject(/** @lends Base# */{
if (hasObject) { if (hasObject) {
// Create a _filtered object that inherits from list[0], and // Create a _filtered object that inherits from list[0], and
// override all fields that were already read with undefined. // override all fields that were already read with undefined.
var filtered = list._filtered; var filtered = list.__filtered;
if (!filtered) { if (!filtered) {
filtered = list._filtered = Base.create(list[0]); filtered = list.__filtered = Base.create(list[0]);
// Point _unfiltered to the original so Base#_set() can // Point _unfiltered to the original so Base#_set() can
// execute hasOwnProperty on it. // execute hasOwnProperty on it.
filtered._unfiltered = list[0]; filtered.__unfiltered = list[0];
} }
// delete wouldn't work since the masked parent's value would // delete wouldn't work since the masked parent's value would
// shine through. // shine through.
filtered[name] = undefined; filtered[name] = undefined;
} }
return this.read(hasObject ? [value] : list, start, options, amount); var l = hasObject ? [value] : list,
res = this.read(l, start, options, amount);
return res;
}, },
/** /**
@ -307,13 +319,12 @@ Base.inject(/** @lends Base# */{
list._hasObject = list.length === 1 && Base.isPlainObject(arg); list._hasObject = list.length === 1 && Base.isPlainObject(arg);
if (list._hasObject) if (list._hasObject)
// Return the whole arguments object if no name is provided. // Return the whole arguments object if no name is provided.
return name ? arg[name] : list._filtered || arg; return name ? arg[name] : list.__filtered || arg;
}, },
/** /**
* Checks if the argument list has a named argument with the given name. * Checks if the argument list has a named argument with the given name. If
* If name is `null`, it returns `true` if there are any named * name is `null`, it returns `true` if there are any named arguments.
* arguments.
*/ */
hasNamed: function(list, name) { hasNamed: function(list, name) {
return !!this.getNamed(list, name); return !!this.getNamed(list, name);
@ -321,18 +332,17 @@ Base.inject(/** @lends Base# */{
/** /**
* Copies all properties from `source` over to `dest`, supporting * Copies all properties from `source` over to `dest`, supporting
* `_filtered` handling as required by {@link Base.readNamed()} * `_filtered` handling as required by {@link Base.readNamed()} mechanism,
* mechanism, as well as a way to exclude and prioritize properties. * as well as a way to exclude and prioritize properties.
* *
* @param {Object} dest the destination that is to receive the * @param {Object} dest the destination that is to receive the properties
* properties * @param {Object} source the source from where to retrieve the properties
* @param {Object} source the source from where to retrieve the * to be copied
* properties to be copied * @param {Object} [exclude] an object that can define any properties as
* @param {Object} [exclude] an object that can define any properties * `true` that should be excluded when copying
* as `true` that should be excluded when copying * @param {String[]} [prioritize] a list of keys that should be prioritized
* @param {String[]} [prioritize] a list of keys that should be * when copying, if they are defined in `source`, processed in the order
* prioritized when copying, if they are defined in `source`, * of appearance
* processed in the order of appearance
*/ */
filter: function(dest, source, exclude, prioritize) { filter: function(dest, source, exclude, prioritize) {
var processed; var processed;
@ -362,16 +372,16 @@ Base.inject(/** @lends Base# */{
processed = keys; processed = keys;
} }
// If source is a filtered object, we get the keys from the // If source is a filtered object, we get the keys from the the original
// the original object (it's parent / prototype). See _filtered // object (it's parent / prototype). See _filtered inheritance trick in
// inheritance trick in the argument reading code. // the argument reading code.
Object.keys(source._unfiltered || source).forEach(handleKey); Object.keys(source.__unfiltered || source).forEach(handleKey);
return dest; return dest;
}, },
/** /**
* Returns true if obj is either a plain object or an array, as used by * Returns true if obj is either a plain object or an array, as used by many
* many argument reading methods. * argument reading methods.
*/ */
isPlainValue: function(obj, asString) { isPlainValue: function(obj, asString) {
return Base.isPlainObject(obj) || Array.isArray(obj) return Base.isPlainObject(obj) || Array.isArray(obj)
@ -380,7 +390,7 @@ Base.inject(/** @lends Base# */{
/** /**
* Serializes the passed object into a format that can be passed to * Serializes the passed object into a format that can be passed to
* JSON.stringify() for JSON serialization. * `JSON.stringify()` for JSON serialization.
*/ */
serialize: function(obj, options, compact, dictionary) { serialize: function(obj, options, compact, dictionary) {
options = options || {}; options = options || {};
@ -389,10 +399,10 @@ Base.inject(/** @lends Base# */{
res; res;
if (isRoot) { if (isRoot) {
options.formatter = new Formatter(options.precision); options.formatter = new Formatter(options.precision);
// Create a simple dictionary object that handles all the // Create a simple dictionary object that handles all the storing
// storing and retrieving of dictionary definitions and // and retrieving of dictionary definitions and references, e.g. for
// references, e.g. for symbols and gradients. Items that want // symbols and gradients. Items that want to support this need to
// to support this need to define globally unique _id attribute. // define globally unique _id attribute.
/** /**
* @namespace * @namespace
* @private * @private
@ -402,10 +412,9 @@ Base.inject(/** @lends Base# */{
definitions: {}, definitions: {},
references: {}, references: {},
add: function(item, create) { add: function(item, create) {
// See if we have reference entry with the given id // See if we have reference entry with the given id already.
// already. If not, call create on the item to allow it // If not, call create on the item to allow it to create the
// to create the definition, then store the reference // definition, then store the reference to it and return it.
// to it and return it.
var id = '#' + item._id, var id = '#' + item._id,
ref = this.references[id]; ref = this.references[id];
if (!ref) { if (!ref) {
@ -426,11 +435,11 @@ Base.inject(/** @lends Base# */{
if (obj && obj._serialize) { if (obj && obj._serialize) {
res = obj._serialize(options, dictionary); res = obj._serialize(options, dictionary);
// If we don't serialize to compact form (meaning no type // If we don't serialize to compact form (meaning no type
// identifier), see if _serialize didn't already add the class, // identifier), see if _serialize didn't already add the class, e.g.
// e.g. for classes that do not support compact form. // for classes that do not support compact form.
var name = obj._class; var name = obj._class;
// Enforce class names on root level, except if the class // Enforce class names on root level, except if the class explicitly
// explicitly asks to be serialized in compact form (Project). // asks to be serialized in compact form (Project).
if (name && !obj._compactSerialize && (isRoot || !compact) if (name && !obj._compactSerialize && (isRoot || !compact)
&& res[0] !== name) { && res[0] !== name) {
res.unshift(name); res.unshift(name);
@ -438,8 +447,7 @@ Base.inject(/** @lends Base# */{
} else if (Array.isArray(obj)) { } else if (Array.isArray(obj)) {
res = []; res = [];
for (var i = 0, l = obj.length; i < l; i++) for (var i = 0, l = obj.length; i < l; i++)
res[i] = Base.serialize(obj[i], options, compact, res[i] = Base.serialize(obj[i], options, compact, dictionary);
dictionary);
} else if (Base.isPlainObject(obj)) { } else if (Base.isPlainObject(obj)) {
res = {}; res = {};
var keys = Object.keys(obj); var keys = Object.keys(obj);
@ -461,37 +469,36 @@ Base.inject(/** @lends Base# */{
/** /**
* Deserializes from parsed JSON data. A simple convention is followed: * Deserializes from parsed JSON data. A simple convention is followed:
* Array values with a string at the first position are links to * Array values with a string at the first position are links to
* deserializable types through Base.exports, and the values following * deserializable types through Base.exports, and the values following in
* in the array are the arguments to their initialize function. * the array are the arguments to their initialize function. Any other value
* Any other value is passed on unmodified. * is passed on unmodified. The passed json data is recoursively traversed
* The passed json data is recoursively traversed and converted, leaves * and converted, leaves first.
* first
*/ */
deserialize: function(json, create, _data, _setDictionary, _isRoot) { deserialize: function(json, create, _data, _setDictionary, _isRoot) {
var res = json, var res = json,
isFirst = !_data, isFirst = !_data,
hasDictionary = isFirst && json && json.length hasDictionary = isFirst && json && json.length
&& json[0][0] === 'dictionary'; && json[0][0] === 'dictionary';
// A _data side-car to deserialize that can hold any kind of // A _data side-car to deserialize that can hold any kind of 'global'
// 'global' data across a deserialization. It's currently only used // data across a deserialization. It's currently only used to hold
// to hold dictionary definitions. // dictionary definitions.
_data = _data || {}; _data = _data || {};
if (Array.isArray(json)) { if (Array.isArray(json)) {
// See if it's a serialized type. If so, the rest of the array // See if it's a serialized type. If so, the rest of the array are
// are the arguments to #initialize(). Either way, we simply // the arguments to #initialize(). Either way, we simply deserialize
// deserialize all elements of the array. // all elements of the array.
var type = json[0], var type = json[0],
// Handle stored dictionary specially, since we need to // Handle stored dictionary specially, since we need to keep a
// keep a lookup table to retrieve referenced items from. // lookup table to retrieve referenced items from.
isDictionary = type === 'dictionary'; isDictionary = type === 'dictionary';
// First see if this is perhaps a dictionary reference, and // First see if this is perhaps a dictionary reference, and if so
// if so return its definition instead. // return its definition instead.
if (json.length == 1 && /^#/.test(type)) { if (json.length == 1 && /^#/.test(type)) {
return _data.dictionary[type]; return _data.dictionary[type];
} }
type = Base.exports[type]; type = Base.exports[type];
res = []; res = [];
// Skip first type entry for arguments // Skip first type entry for arguments.
// Pass true for _isRoot in children if we have a dictionary, // Pass true for _isRoot in children if we have a dictionary,
// in which case we need to shift the root level one down. // in which case we need to shift the root level one down.
for (var i = type ? 1 : 0, l = json.length; i < l; i++) { for (var i = type ? 1 : 0, l = json.length; i < l; i++) {
@ -502,9 +509,9 @@ Base.inject(/** @lends Base# */{
// Create serialized type and pass collected arguments to // Create serialized type and pass collected arguments to
// constructor(). // constructor().
var args = res; var args = res;
// If a create method is provided, handle our own // If a create method is provided, handle our own creation. This
// creation. This is used in #importJSON() to pass // is used in #importJSON() to pass on insert = false to all
// on insert = false to all items except layers. // items except layers.
if (create) { if (create) {
res = create(type, args, isFirst || _isRoot); res = create(type, args, isFirst || _isRoot);
} else { } else {
@ -545,10 +552,10 @@ Base.inject(/** @lends Base# */{
&& target.constructor === ctor, && target.constructor === ctor,
obj = useTarget ? target obj = useTarget ? target
: Base.create(ctor.prototype); : Base.create(ctor.prototype);
// NOTE: We don't set insert false for layers since we // NOTE: We don't set insert false for layers since we want
// want these to be created on the fly in the active // these to be created on the fly in the active project into
// project into which we're importing (except for if // which we're importing (except for if it's a preexisting
// it's a preexisting target layer). // target layer).
if (args.length === 1 && obj instanceof Item if (args.length === 1 && obj instanceof Item
&& (useTarget || !(obj instanceof Layer))) { && (useTarget || !(obj instanceof Layer))) {
var arg = args[0]; var arg = args[0];
@ -566,9 +573,9 @@ Base.inject(/** @lends Base# */{
}, },
/** /**
* Utility function for adding and removing items from a list of which * Utility function for adding and removing items from a list of which each
* each entry keeps a reference to its index in the list in the private * entry keeps a reference to its index in the list in the private _index
* _index property. Used for PaperScope#projects and Item#children. * property. Used for PaperScope#projects and Item#children.
*/ */
splice: function(list, items, index, remove) { splice: function(list, items, index, remove) {
var amount = items && items.length, var amount = items && items.length,
@ -624,5 +631,4 @@ Base.inject(/** @lends Base# */{
hyphenate: function(str) { hyphenate: function(str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
} }
} }});
});

View file

@ -17,8 +17,6 @@
// global one in the whole scope. // global one in the whole scope.
paper = new (PaperScope.inject(Base.exports, { paper = new (PaperScope.inject(Base.exports, {
// Mark fields as enumerable so PaperScope.inject can pick them up
enumerable: true,
Base: Base, Base: Base,
Numerical: Numerical, Numerical: Numerical,
Key: Key, Key: Key,

View file

@ -26,11 +26,8 @@ var HitResult = Base.extend(/** @lends HitResult# */{
// Inject passed values, so we can be flexible about the HitResult // Inject passed values, so we can be flexible about the HitResult
// properties. // properties.
// This allows the definition of getters too, e.g. for 'pixel'. // This allows the definition of getters too, e.g. for 'pixel'.
if (values) { if (values)
// Make enumerable so toString() works.
values.enumerable = true;
this.inject(values); this.inject(values);
}
}, },
/** /**

View file

@ -827,14 +827,14 @@ new function() { // Injection scope for various item event handlers
opts.cacheItem = this; opts.cacheItem = this;
// If we're caching bounds, pass on this item as cacheItem, so // If we're caching bounds, pass on this item as cacheItem, so
// the children can setup _boundsCache structures for it. // the children can setup _boundsCache structures for it.
var bounds = this._getCachedBounds(hasMatrix && matrix, opts); var rect = this._getCachedBounds(hasMatrix && matrix, opts).rect;
// If we're returning '#bounds', create a LinkedRectangle that uses // If we're returning '#bounds', create a LinkedRectangle that uses
// the setBounds() setter to update the Item whenever the bounds are // the setBounds() setter to update the Item whenever the bounds are
// changed: // changed:
return !arguments.length return !arguments.length
? new LinkedRectangle(bounds.x, bounds.y, bounds.width, ? new LinkedRectangle(rect.x, rect.y, rect.width, rect.height,
bounds.height, this, 'setBounds') this, 'setBounds')
: bounds; : rect;
}, },
setBounds: function(/* rect */) { setBounds: function(/* rect */) {
@ -889,6 +889,14 @@ new function() { // Injection scope for various item event handlers
return Item._getBounds(children, matrix, options); return Item._getBounds(children, matrix, options);
}, },
_getBoundsCacheKey: function(options, internal) {
return [
options.stroke ? 1 : 0,
options.handle ? 1 : 0,
internal ? 1 : 0
].join('');
},
/** /**
* Private method that deals with the calling of _getBounds, recursive * Private method that deals with the calling of _getBounds, recursive
* matrix concatenation and handles all the complicated caching mechanisms. * matrix concatenation and handles all the complicated caching mechanisms.
@ -904,29 +912,43 @@ new function() { // Injection scope for various item event handlers
cacheItem = options.cacheItem, cacheItem = options.cacheItem,
_matrix = internal ? null : this._matrix._orNullIfIdentity(), _matrix = internal ? null : this._matrix._orNullIfIdentity(),
// Create a key for caching, reflecting all bounds options. // Create a key for caching, reflecting all bounds options.
cacheKey = cacheItem && (!matrix || matrix.equals(_matrix)) && [ cacheKey = cacheItem && (!matrix || matrix.equals(_matrix))
options.stroke ? 1 : 0, && this._getBoundsCacheKey(options, internal),
options.handle ? 1 : 0, bounds = this._bounds;
internal ? 1 : 0
].join('');
// NOTE: This needs to happen before returning cached values, since even // NOTE: This needs to happen before returning cached values, since even
// then, _boundsCache needs to be kept up-to-date. // then, _boundsCache needs to be kept up-to-date.
Item._updateBoundsCache(this._parent || this._symbol, cacheItem); Item._updateBoundsCache(this._parent || this._symbol, cacheItem);
if (cacheKey && this._bounds && cacheKey in this._bounds) if (cacheKey && bounds && cacheKey in bounds) {
return this._bounds[cacheKey].rect.clone(); var cached = bounds[cacheKey];
var bounds = this._getBounds(matrix || _matrix, options); return {
rect: cached.rect.clone(),
nonscaling: cached.nonscaling
};
}
var res = this._getBounds(matrix || _matrix, options),
// Support two versions of _getBounds(): One that directly returns a
// Rectangle, and one that returns a bounds object with nonscaling.
rect = res.rect || res,
style = this._style,
nonscaling = res.nonscaling || style.hasStroke()
&& !style.getStrokeScaling();
// If we can cache the result, update the _bounds cache structure // If we can cache the result, update the _bounds cache structure
// before returning // before returning
if (cacheKey) { if (cacheKey) {
if (!this._bounds) if (!bounds) {
this._bounds = {}; this._bounds = bounds = {};
var cached = this._bounds[cacheKey] = { }
rect: bounds.clone(), var cached = bounds[cacheKey] = {
rect: rect.clone(),
nonscaling: nonscaling,
// Mark as internal, so Item#transform() won't transform it // Mark as internal, so Item#transform() won't transform it
internal: internal internal: internal
}; };
} }
return bounds; return {
rect: rect,
nonscaling: nonscaling
};
}, },
/** /**
@ -1005,7 +1027,10 @@ new function() { // Injection scope for various item event handlers
var x1 = Infinity, var x1 = Infinity,
x2 = -x1, x2 = -x1,
y1 = x1, y1 = x1,
y2 = x2; y2 = x2,
nonscaling = false;
// NOTE: As soon as one child-item has non-scaling strokes, the full
// bounds need to be considered non-scaling for caching purposes.
options = options || {}; options = options || {};
for (var i = 0, l = items.length; i < l; i++) { for (var i = 0, l = items.length; i < l; i++) {
var item = items[i]; var item = items[i];
@ -1013,17 +1038,23 @@ new function() { // Injection scope for various item event handlers
// Pass true for noInternal, since even when getting // Pass true for noInternal, since even when getting
// internal bounds for this item, we need to apply the // internal bounds for this item, we need to apply the
// matrices to its children. // matrices to its children.
var rect = item._getCachedBounds( var bounds = item._getCachedBounds(
matrix && matrix.appended(item._matrix), options, true); matrix && matrix.appended(item._matrix), options, true),
rect = bounds.rect;
x1 = Math.min(rect.x, x1); x1 = Math.min(rect.x, x1);
y1 = Math.min(rect.y, y1); y1 = Math.min(rect.y, y1);
x2 = Math.max(rect.x + rect.width, x2); x2 = Math.max(rect.x + rect.width, x2);
y2 = Math.max(rect.y + rect.height, y2); y2 = Math.max(rect.y + rect.height, y2);
if (bounds.nonscaling)
nonscaling = true;
} }
} }
return isFinite(x1) return {
rect: isFinite(x1)
? new Rectangle(x1, y1, x2 - x1, y2 - y1) ? new Rectangle(x1, y1, x2 - x1, y2 - y1)
: new Rectangle(); : new Rectangle(),
nonscaling: nonscaling
};
} }
} }
@ -1122,8 +1153,22 @@ new function() { // Injection scope for various item event handlers
scaling = Point.read(arguments, 0, { clone: true, readNull: true }); scaling = Point.read(arguments, 0, { clone: true, readNull: true });
if (current && scaling && !current.equals(scaling)) { if (current && scaling && !current.equals(scaling)) {
// See #setRotation() for preservation of _decomposed. // See #setRotation() for preservation of _decomposed.
var decomposed = this._decomposed; var rotation = this.getRotation(),
this.scale(scaling.x / current.x, scaling.y / current.y); decomposed = this._decomposed,
matrix = new Matrix(),
center = this.getPosition(true);
// Create a matrix in which the scaling is applied in the non-
// rotated state, so it is always applied before the rotation.
// TODO: What about skewing? Do we need separately stored values for
// these properties, and apply them separately from the matrix?
matrix.translate(center);
if (rotation)
matrix.rotate(rotation);
matrix.scale(scaling.x / current.x, scaling.y / current.y);
if (rotation)
matrix.rotate(-rotation);
matrix.translate(center.negate());
this.transform(matrix);
if (decomposed) { if (decomposed) {
decomposed.scaling = scaling; decomposed.scaling = scaling;
this._decomposed = decomposed; this._decomposed = decomposed;
@ -3376,11 +3421,9 @@ new function() { // Injection scope for hit-test functions shared with project
// 'lines'. Default: ['objects', 'children'] // 'lines'. Default: ['objects', 'children']
transform: function(matrix, _applyMatrix, _applyRecursively, transform: function(matrix, _applyMatrix, _applyRecursively,
_setApplyMatrix) { _setApplyMatrix) {
var _matrix = this._matrix,
// If no matrix is provided, or the matrix is the identity, we might // If no matrix is provided, or the matrix is the identity, we might
// still have some work to do in case _applyMatrix is true // still have some work to do in case _applyMatrix is true
if (matrix && matrix.isIdentity())
matrix = null;
var _matrix = this._matrix,
transform = matrix && !matrix.isIdentity(), transform = matrix && !matrix.isIdentity(),
applyMatrix = (_applyMatrix || this._applyMatrix) applyMatrix = (_applyMatrix || this._applyMatrix)
// Don't apply _matrix if the result of concatenating with // Don't apply _matrix if the result of concatenating with
@ -3398,30 +3441,8 @@ new function() { // Injection scope for hit-test functions shared with project
// non-invertible. This is then used again in setBounds to restore. // non-invertible. This is then used again in setBounds to restore.
if (!matrix.isInvertible() && _matrix.isInvertible()) if (!matrix.isInvertible() && _matrix.isInvertible())
_matrix._backup = _matrix.getValues(); _matrix._backup = _matrix.getValues();
_matrix.prepend(matrix); // Pass `true` for _dontNotify, as we're handling this after.
} _matrix.prepend(matrix, true);
// Call #_transformContent() now, if we need to directly apply the
// internal _matrix transformations to the item's content.
// Application is not possible on Raster, PointText, SymbolItem, since
// the matrix is where the actual transformation state is stored.
if (applyMatrix) {
if (this._transformContent(_matrix, _applyRecursively,
_setApplyMatrix)) {
var pivot = this._pivot;
if (pivot)
_matrix._transformPoint(pivot, pivot, true);
// Reset the internal matrix to the identity transformation if
// it was possible to apply it.
_matrix.reset(true);
// Set the internal _applyMatrix flag to true if we're told to
// do so
if (_setApplyMatrix && this._canApplyMatrix)
this._applyMatrix = true;
} else {
applyMatrix = transform = false;
}
}
if (transform) {
// When a new matrix was applied, we also need to transform gradient // When a new matrix was applied, we also need to transform gradient
// color points. These always need transforming, regardless of // color points. These always need transforming, regardless of
// #applyMatrix, as they are defined in the parent's coordinate // #applyMatrix, as they are defined in the parent's coordinate
@ -3438,39 +3459,65 @@ new function() { // Injection scope for hit-test functions shared with project
if (strokeColor) if (strokeColor)
strokeColor.transform(matrix); strokeColor.transform(matrix);
} }
// Call #_transformContent() now, if we need to directly apply the
// internal _matrix transformations to the item's content.
// Application is not possible on Raster, PointText, SymbolItem, since
// the matrix is where the actual transformation state is stored.
if (applyMatrix && (applyMatrix = this._transformContent(_matrix,
_applyRecursively, _setApplyMatrix))) {
// Pivot is provided in the parent's coordinate system, so transform
// it along too.
var pivot = this._pivot;
if (pivot)
_matrix._transformPoint(pivot, pivot, true);
// Reset the internal matrix to the identity transformation if
// it was possible to apply it, but do not notify owner of change.
_matrix.reset(true);
// Set the internal _applyMatrix flag to true if we're told to
// do so
if (_setApplyMatrix && this._canApplyMatrix)
this._applyMatrix = true;
}
// Calling _changed will clear _bounds and _position, but depending // Calling _changed will clear _bounds and _position, but depending
// on matrix we can calculate and set them again, so preserve them. // on matrix we can calculate and set them again, so preserve them.
var bounds = this._bounds, var bounds = this._bounds,
position = this._position; position = this._position;
// We always need to call _changed since we're caching bounds on all if (transform || applyMatrix) {
// items, including Group.
this._changed(/*#=*/Change.GEOMETRY); this._changed(/*#=*/Change.GEOMETRY);
}
// Detect matrices that contain only translations and scaling // Detect matrices that contain only translations and scaling
// and transform the cached _bounds and _position without having to // and transform the cached _bounds and _position without having to
// fully recalculate each time. // fully recalculate each time.
var decomp = bounds && matrix && matrix.decompose(); var decomp = transform && bounds && matrix.decompose();
if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) { if (decomp && decomp.skewing.isZero() && decomp.rotation % 90 === 0) {
// Transform the old bound by looping through all the cached bounds // Transform the old bound by looping through all the cached
// in _bounds and transform each. // bounds in _bounds and transform each.
for (var key in bounds) { for (var key in bounds) {
var cache = bounds[key]; var cache = bounds[key];
// If any item involved in the determination of these bounds has
// non-scaling strokes, delete the cache now as it can't be
// preserved through the transformation.
if (cache.nonscaling) {
delete bounds[key];
} else if (applyMatrix || !cache.internal) {
// If these are internal bounds, only transform them if this // If these are internal bounds, only transform them if this
// item applied its matrix. // item applied its matrix.
if (applyMatrix || !cache.internal) {
var rect = cache.rect; var rect = cache.rect;
matrix._transformBounds(rect, rect); matrix._transformBounds(rect, rect);
} }
} }
// If we have cached bounds, update _position again as its
// center. We need to take into account _boundsGetter here too, in
// case another getter is assigned to it, e.g. 'getStrokeBounds'.
var getter = this._boundsGetter,
rect = bounds[getter && getter.getBounds || getter || 'getBounds'];
if (rect)
this._position = rect.getCenter(true);
this._bounds = bounds; this._bounds = bounds;
} else if (matrix && position) { // If we have cached bounds, try to determine _position as its
// Transform position as well. // center. Use _boundsOptions do get the cached default bounds.
var cached = bounds[this._getBoundsCacheKey(
this._boundsOptions || {})];
if (cached) {
this._position = cached.rect.getCenter(true);
}
} else if (transform && position && this._pivot) {
// If the item has a pivot defined, it means that the default
// position defined as the center of the bounds won't shift with
// arbitrary transformations and we can therefore update _position:
this._position = matrix._transformPoint(position, position); this._position = matrix._transformPoint(position, position);
} }
// Allow chaining here, since transform() is related to Matrix functions // Allow chaining here, since transform() is related to Matrix functions

View file

@ -17,7 +17,7 @@
// The paper.js version. // The paper.js version.
// NOTE: Adjust value here before calling `gulp publish`, which then updates and // NOTE: Adjust value here before calling `gulp publish`, which then updates and
// publishes the various JSON package files automatically. // publishes the various JSON package files automatically.
var version = '0.11.2'; var version = '0.11.3';
// If this file is loaded in the browser, we're in load.js mode. // If this file is loaded in the browser, we're in load.js mode.
var load = typeof window === 'object'; var load = typeof window === 'object';

View file

@ -1955,7 +1955,7 @@ new function() { // Scope for bezier intersection using fat-line clipping
// Calculate the curve values of the rotated curve. // Calculate the curve values of the rotated curve.
rv = [], rv = [],
roots = []; roots = [];
for(var i = 0; i < 8; i += 2) { for (var i = 0; i < 8; i += 2) {
var x = v[i] - px, var x = v[i] - px,
y = v[i + 1] - py; y = v[i + 1] - py;
rv.push( rv.push(

View file

@ -51,9 +51,9 @@ new function() {
if (!Numerical.isZero(scale.x - 1) if (!Numerical.isZero(scale.x - 1)
|| !Numerical.isZero(scale.y - 1)) || !Numerical.isZero(scale.y - 1))
parts.push('scale(' + formatter.point(scale) +')'); parts.push('scale(' + formatter.point(scale) +')');
if (skew && skew.x) if (skew.x)
parts.push('skewX(' + formatter.number(skew.x) + ')'); parts.push('skewX(' + formatter.number(skew.x) + ')');
if (skew && skew.y) if (skew.y)
parts.push('skewY(' + formatter.number(skew.y) + ')'); parts.push('skewY(' + formatter.number(skew.y) + ')');
attrs.transform = parts.join(' '); attrs.transform = parts.join(' ');
} else { } else {
@ -115,8 +115,9 @@ new function() {
if (length > 2) { if (length > 2) {
type = item._closed ? 'polygon' : 'polyline'; type = item._closed ? 'polygon' : 'polyline';
var parts = []; var parts = [];
for(var i = 0; i < length; i++) for (var i = 0; i < length; i++) {
parts.push(formatter.point(segments[i]._point)); parts.push(formatter.point(segments[i]._point));
}
attrs.points = parts.join(' '); attrs.points = parts.join(' ');
} else { } else {
type = 'line'; type = 'line';
@ -416,6 +417,7 @@ new function() {
? new Rectangle([0, 0], view.getViewSize()) ? new Rectangle([0, 0], view.getViewSize())
: bounds === 'content' : bounds === 'content'
? Item._getBounds(children, matrix, { stroke: true }) ? Item._getBounds(children, matrix, { stroke: true })
.rect
: Rectangle.read([bounds], 0, { readNull: true }), : Rectangle.read([bounds], 0, { readNull: true }),
attrs = { attrs = {
version: '1.1', version: '1.1',

View file

@ -115,9 +115,9 @@ var PointText = TextItem.extend(/** @lends PointText# */{
x -= width / (justification === 'center' ? 2: 1); x -= width / (justification === 'center' ? 2: 1);
// Until we don't have baseline measuring, assume 1 / 4 leading as a // Until we don't have baseline measuring, assume 1 / 4 leading as a
// rough guess: // rough guess:
var bounds = new Rectangle(x, var rect = new Rectangle(x,
numLines ? - 0.75 * leading : 0, numLines ? - 0.75 * leading : 0,
width, numLines * leading); width, numLines * leading);
return matrix ? matrix._transformBounds(bounds, bounds) : bounds; return matrix ? matrix._transformBounds(rect, rect) : rect;
} }
}); });

View file

@ -125,7 +125,7 @@ var Numerical = new function() {
/** /**
* The machine epsilon for a double precision (Javascript Number) is * The machine epsilon for a double precision (Javascript Number) is
* 2.220446049250313e-16. (try this in the js console: * 2.220446049250313e-16. (try this in the js console:
* (function(){for(var e=1;1<1+e/2;)e/=2;return e}()) * (function(){ for (var e = 1; 1 < 1+e/2;) e/=2; return e }())
* *
* The constant MACHINE_EPSILON here refers to the constants δ and ε * The constant MACHINE_EPSILON here refers to the constants δ and ε
* such that, the error introduced by addition, multiplication on a * such that, the error introduced by addition, multiplication on a

View file

@ -1271,12 +1271,11 @@ new function() { // Injection scope for event handling on the browser
point, prevPoint) point, prevPoint)
// Next handle the hit-item, if it's different from the drag-item // Next handle the hit-item, if it's different from the drag-item
// and not a descendant of it (in which case it would already have // and not a descendant of it (in which case it would already have
// received an event in the call above). Use fallbacks to translate // received an event in the call above).
// mousedrag to mousemove, since drag is handled above.
|| hitItem && hitItem !== dragItem || hitItem && hitItem !== dragItem
&& !hitItem.isDescendant(dragItem) && !hitItem.isDescendant(dragItem)
&& emitMouseEvent(hitItem, null, fallbacks[type] || type, event, && emitMouseEvent(hitItem, null, type, event, point, prevPoint,
point, prevPoint, dragItem) dragItem)
// Lastly handle the mouse events on the view, if we're still here. // Lastly handle the mouse events on the view, if we're still here.
|| emitMouseEvent(view, dragItem || hitItem || view, type, event, || emitMouseEvent(view, dragItem || hitItem || view, type, event,
point, prevPoint)); point, prevPoint));

View file

@ -876,3 +876,56 @@ test('Item#pivot', function() {
equals(path2.pivot, pivot.add(difference), equals(path2.pivot, pivot.add(difference),
'Changing position of an item with applyMatrix = true should change pivot'); 'Changing position of an item with applyMatrix = true should change pivot');
}); });
test('Item#position with irregular shape, #pivot and rotation', function() {
var path1 = new Path([ [0, 0], [200, 100], [0, 100] ]);
var path2 = path1.clone();
path2.pivot = path2.position;
equals(path1.position, new Point(100, 50),
'path1.position, before rotation');
path1.rotate(45);
equals(path1.position, new Point(64.64466, 50),
'path1.position, after rotation');
equals(path2.position, new Point(100, 50),
'path2.position with pivot, before rotation');
path2.rotate(45);
equals(path2.position, new Point(100, 50),
'path2.position with pivot, after rotation');
});
test('Item#scaling, #rotation', function() {
var expected = new Rectangle(100, 50, 100, 200);
var rect1 = new Path.Rectangle({
from: [100, 100],
to: [200, 200],
applyMatrix: false
});
var rect2 = rect1.clone();
rect1.scaling = [2, 1];
rect1.rotation = 90;
equals(rect1.bounds, expected,
'rect1.bounds, setting rect1.scaling before rect1.rotation');
rect2.rotation = 90;
rect2.scaling = [2, 1];
equals(rect2.bounds, expected,
'rect2.bounds, setting rect2.scaling before rect2.rotation');
var shape1 = new Shape.Rectangle({
from: [100, 100],
to: [200, 200]
});
var shape2 = shape1.clone();
shape1.scaling = [2, 1];
shape1.rotation = 90;
equals(shape1.bounds, expected,
'shape1.bounds, setting shape1.scaling before shape1.rotation');
shape2.rotation = 90;
shape2.scaling = [2, 1];
equals(shape2.bounds, expected,
'shape2.bounds, setting shape2.scaling before shape2.rotation');
});

View file

@ -694,12 +694,15 @@ test('path.strokeBounds with applyMatrix disabled', function() {
strokeColor: 'red', strokeColor: 'red',
strokeWidth: 10 strokeWidth: 10
}); });
equals(path.strokeBounds, new Rectangle(5, 5, 30, 30), 'path.strokeBounds, applyMatrix enabled'); equals(path.strokeBounds, new Rectangle(5, 5, 30, 30),
'path.strokeBounds, applyMatrix enabled');
path.applyMatrix = false; path.applyMatrix = false;
equals(path.strokeBounds, new Rectangle(5, 5, 30, 30), 'path.strokeBounds, applyMatrix disabled'); equals(path.strokeBounds, new Rectangle(5, 5, 30, 30),
'path.strokeBounds, applyMatrix disabled');
path.scale([4, 2], [0, 0]); path.scale([4, 2], [0, 0]);
var expected = new Rectangle(20, 10, 120, 60); var expected = new Rectangle(20, 10, 120, 60);
equals(path.strokeBounds, expected, 'path.strokeBounds after scaling, applyMatrix disabled'); equals(path.strokeBounds, expected,
'path.strokeBounds after scaling, applyMatrix disabled');
function testHitResult() { function testHitResult() {
// Hit-testing needs to handle applyMatrix disabled with stroke scaling, // Hit-testing needs to handle applyMatrix disabled with stroke scaling,
// even when hit-testing on "distorted" stroke joins: // even when hit-testing on "distorted" stroke joins:
@ -714,10 +717,29 @@ test('path.strokeBounds with applyMatrix disabled', function() {
testHitResult(); testHitResult();
path.applyMatrix = true; path.applyMatrix = true;
expected = new Rectangle(35, 15, 90, 50); expected = new Rectangle(35, 15, 90, 50);
equals(path.strokeBounds, expected, 'path.strokeBounds after scaling, applyMatrix enabled'); equals(path.strokeBounds, expected,
'path.strokeBounds after scaling, applyMatrix enabled');
testHitResult(); testHitResult();
}); });
test('TEST', function() {
var path = new Path.Rectangle({
applyMatrix: false,
point: [10, 10],
size: [20, 20],
strokeScaling: true,
strokeColor: 'red',
strokeWidth: 10
});
path.scale([4, 2], [0, 0]);
equals(path.strokeBounds, new Rectangle(20, 10, 120, 60),
'path.strokeBounds after scaling, applyMatrix disabled');
path.applyMatrix = true;
equals(path.strokeBounds, new Rectangle(35, 15, 90, 50),
'path.strokeBounds after scaling, applyMatrix enabled');
});
test('symbolItem.bounds with strokeScaling disabled', function() { test('symbolItem.bounds with strokeScaling disabled', function() {
var path = new Path.Rectangle({ var path = new Path.Rectangle({
size: [20, 20], size: [20, 20],

View file

@ -154,7 +154,7 @@ test('transform test 1', function() {
var clones = 30; var clones = 30;
var angle = 360 / clones; var angle = 360 / clones;
for(var i = 0; i < clones; i++) { for (var i = 0; i < clones; i++) {
var clonedPath = circlePath.clone(); var clonedPath = circlePath.clone();
clonedPath.rotate(angle * i, circlePath.bounds.topLeft); clonedPath.rotate(angle * i, circlePath.bounds.topLeft);
} }