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 test
- 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
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
maintain multiple versions of Node.js on the same platform, as often required by
different projects:
<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
Node.js that is kept up to date with `brew update` is enough:
<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.
Homebrew is recommended on macOS also if you intend to install Paper.js with
rendering to the Canvas on Node.js, as described in the next paragraph.
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.
@ -83,14 +83,14 @@ different one:
In order to install `paper-jsdom-canvas`, you need the [Cairo Graphics
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
issuing the command:
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:
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/
*
* Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey
@ -9,7 +9,7 @@
*
* 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])(.*))$/)))
beansNames[bean[3].toLowerCase() + bean[4]] = bean[2];
if (!res || isFunc || !res.get || typeof res.get !== 'function'
|| !Base.isPlainObject(res))
|| !Base.isPlainObject(res)) {
res = { value: res, writable: true };
}
if ((describe(dest, name)
|| { configurable: true }).configurable) {
res.configurable = true;
res.enumerable = enumerable;
res.enumerable = enumerable != null ? enumerable : !bean;
}
define(dest, name, res);
}
@ -141,7 +142,7 @@ var Base = new function() {
preserve = src.preserve;
if (statics !== src)
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++)
this.inject(arguments[i]);
@ -164,13 +165,15 @@ var Base = new function() {
proto = ctor.prototype = proto || create(this.prototype);
define(proto, 'constructor',
{ value: ctor, writable: true, configurable: true });
inject(ctor, this, true);
inject(ctor, this);
if (arguments.length)
this.inject.apply(ctor, arguments);
ctor.base = base;
return ctor;
}
}, true).inject({
}).inject({
enumerable: false,
initialize: Base,
set: Base,
@ -230,6 +233,8 @@ if (typeof module !== 'undefined')
module.exports = Base;
Base.inject({
enumerable: false,
toString: function() {
return this._id != null
? (this._class || 'Object') + (this._name
@ -265,13 +270,12 @@ Base.inject({
if (props)
Base.filter(this, props, exclude, this._prioritize);
return this;
},
}
}, {
statics: {
exports: {
enumerable: true
},
beans: false,
statics: {
exports: {},
extend: function extend() {
var res = extend.base.apply(this, arguments),
@ -341,6 +345,11 @@ Base.inject({
: list) || obj;
if (readIndex) {
list.__index = begin + obj.__read;
var filtered = obj.__filtered;
if (filtered) {
list.__filtered = filtered;
obj.__filtered = undefined;
}
obj.__read = undefined;
}
return obj;
@ -371,14 +380,16 @@ Base.inject({
var value = this.getNamed(list, name),
hasObject = value !== undefined;
if (hasObject) {
var filtered = list._filtered;
var filtered = list.__filtered;
if (!filtered) {
filtered = list._filtered = Base.create(list[0]);
filtered._unfiltered = list[0];
filtered = list.__filtered = Base.create(list[0]);
filtered.__unfiltered = list[0];
}
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) {
@ -386,7 +397,7 @@ Base.inject({
if (list._hasObject === undefined)
list._hasObject = list.length === 1 && Base.isPlainObject(arg);
if (list._hasObject)
return name ? arg[name] : list._filtered || arg;
return name ? arg[name] : list.__filtered || arg;
},
hasNamed: function(list, name) {
@ -416,7 +427,7 @@ Base.inject({
processed = keys;
}
Object.keys(source._unfiltered || source).forEach(handleKey);
Object.keys(source.__unfiltered || source).forEach(handleKey);
return dest;
},
@ -462,8 +473,7 @@ Base.inject({
} else if (Array.isArray(obj)) {
res = [];
for (var i = 0, l = obj.length; i < l; i++)
res[i] = Base.serialize(obj[i], options, compact,
dictionary);
res[i] = Base.serialize(obj[i], options, compact, dictionary);
} else if (Base.isPlainObject(obj)) {
res = {};
var keys = Object.keys(obj);
@ -586,8 +596,7 @@ Base.inject({
hyphenate: function(str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
}
});
}});
var Emitter = {
on: function(type, func) {
@ -769,7 +778,7 @@ var PaperScope = Base.extend({
}
},
version: "0.11.2",
version: "0.11.3",
getView: function() {
var project = this.project;
@ -1739,6 +1748,9 @@ var Rectangle = Base.extend({
}
this._set(x, y, width, height);
read = arguments.__index;
var filtered = arguments.__filtered;
if (filtered)
this.__filtered = filtered;
}
if (this.__read)
this.__read = read;
@ -2262,7 +2274,7 @@ var Matrix = Base.extend({
return this.shear(shear, center);
},
append: function(mx) {
append: function(mx, _dontNotify) {
if (mx) {
var a1 = this._a,
b1 = this._b,
@ -2280,12 +2292,13 @@ var Matrix = Base.extend({
this._d = b2 * b1 + d2 * d1;
this._tx += tx2 * a1 + ty2 * c1;
this._ty += tx2 * b1 + ty2 * d1;
if (!_dontNotify)
this._changed();
}
return this;
},
prepend: function(mx) {
prepend: function(mx, _dontNotify) {
if (mx) {
var a1 = this._a,
b1 = this._b,
@ -2305,6 +2318,7 @@ var Matrix = Base.extend({
this._d = c2 * c1 + d2 * d1;
this._tx = a2 * tx1 + b2 * ty1 + tx2;
this._ty = c2 * tx1 + d2 * ty1 + ty2;
if (!_dontNotify)
this._changed();
}
return this;
@ -3236,11 +3250,11 @@ new function() {
this._boundsOptions);
if (!opts.stroke || this.getStrokeScaling())
opts.cacheItem = this;
var bounds = this._getCachedBounds(hasMatrix && matrix, opts);
var rect = this._getCachedBounds(hasMatrix && matrix, opts).rect;
return !arguments.length
? new LinkedRectangle(bounds.x, bounds.y, bounds.width,
bounds.height, this, 'setBounds')
: bounds;
? new LinkedRectangle(rect.x, rect.y, rect.width, rect.height,
this, 'setBounds')
: rect;
},
setBounds: function() {
@ -3273,29 +3287,49 @@ new function() {
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) {
matrix = matrix && matrix._orNullIfIdentity();
var internal = options.internal && !noInternal,
cacheItem = options.cacheItem,
_matrix = internal ? null : this._matrix._orNullIfIdentity(),
cacheKey = cacheItem && (!matrix || matrix.equals(_matrix)) && [
options.stroke ? 1 : 0,
options.handle ? 1 : 0,
internal ? 1 : 0
].join('');
cacheKey = cacheItem && (!matrix || matrix.equals(_matrix))
&& this._getBoundsCacheKey(options, internal),
bounds = this._bounds;
Item._updateBoundsCache(this._parent || this._symbol, cacheItem);
if (cacheKey && this._bounds && cacheKey in this._bounds)
return this._bounds[cacheKey].rect.clone();
var bounds = this._getBounds(matrix || _matrix, options);
if (cacheKey && bounds && cacheKey in bounds) {
var cached = bounds[cacheKey];
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 (!this._bounds)
this._bounds = {};
var cached = this._bounds[cacheKey] = {
rect: bounds.clone(),
if (!bounds) {
this._bounds = bounds = {};
}
var cached = bounds[cacheKey] = {
rect: rect.clone(),
nonscaling: nonscaling,
internal: internal
};
}
return bounds;
return {
rect: rect,
nonscaling: nonscaling
};
},
_getStrokeMatrix: function(matrix, options) {
@ -3340,22 +3374,29 @@ new function() {
var x1 = Infinity,
x2 = -x1,
y1 = x1,
y2 = x2;
y2 = x2,
nonscaling = false;
options = options || {};
for (var i = 0, l = items.length; i < l; i++) {
var item = items[i];
if (item._visible && !item.isEmpty()) {
var rect = item._getCachedBounds(
matrix && matrix.appended(item._matrix), options, true);
var bounds = item._getCachedBounds(
matrix && matrix.appended(item._matrix), options, true),
rect = bounds.rect;
x1 = Math.min(rect.x, x1);
y1 = Math.min(rect.y, y1);
x2 = Math.max(rect.x + rect.width, x2);
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();
: new Rectangle(),
nonscaling: nonscaling
};
}
}
@ -3395,8 +3436,18 @@ new function() {
var current = this.getScaling(),
scaling = Point.read(arguments, 0, { clone: true, readNull: true });
if (current && scaling && !current.equals(scaling)) {
var decomposed = this._decomposed;
this.scale(scaling.x / current.x, scaling.y / current.y);
var rotation = this.getRotation(),
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) {
decomposed.scaling = scaling;
this._decomposed = decomposed;
@ -4218,8 +4269,6 @@ new function() {
transform: function(matrix, _applyMatrix, _applyRecursively,
_setApplyMatrix) {
if (matrix && matrix.isIdentity())
matrix = null;
var _matrix = this._matrix,
transform = matrix && !matrix.isIdentity(),
applyMatrix = (_applyMatrix || this._applyMatrix)
@ -4230,22 +4279,7 @@ new function() {
if (transform) {
if (!matrix.isInvertible() && _matrix.isInvertible())
_matrix._backup = _matrix.getValues();
_matrix.prepend(matrix);
}
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) {
_matrix.prepend(matrix, true);
var style = this._style,
fillColor = style.getFillColor(true),
strokeColor = style.getStrokeColor(true);
@ -4254,24 +4288,38 @@ new function() {
if (strokeColor)
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,
position = this._position;
if (transform || applyMatrix) {
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) {
var cache = bounds[key];
if (applyMatrix || !cache.internal) {
if (cache.nonscaling) {
delete bounds[key];
} else if (applyMatrix || !cache.internal) {
var rect = cache.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;
} 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);
}
return this;
@ -5483,10 +5531,8 @@ var HitResult = Base.extend({
initialize: function HitResult(type, item, values) {
this.type = type;
this.item = item;
if (values) {
values.enumerable = true;
if (values)
this.inject(values);
}
},
statics: {
@ -7006,7 +7052,7 @@ new function() {
cos = Math.cos(angle),
rv = [],
roots = [];
for(var i = 0; i < 8; i += 2) {
for (var i = 0; i < 8; i += 2) {
var x = v[i] - px,
y = v[i + 1] - py;
rv.push(
@ -11016,10 +11062,10 @@ var PointText = TextItem.extend({
x = 0;
if (justification !== 'left')
x -= width / (justification === 'center' ? 2: 1);
var bounds = new Rectangle(x,
var rect = new Rectangle(x,
numLines ? - 0.75 * leading : 0,
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)
|| hitItem && hitItem !== dragItem
&& !hitItem.isDescendant(dragItem)
&& emitMouseEvent(hitItem, null, fallbacks[type] || type, event,
point, prevPoint, dragItem)
&& emitMouseEvent(hitItem, null, type, event, point, prevPoint,
dragItem)
|| emitMouseEvent(view, dragItem || hitItem || view, type, event,
point, prevPoint));
}
@ -13807,9 +13853,9 @@ new function() {
if (!Numerical.isZero(scale.x - 1)
|| !Numerical.isZero(scale.y - 1))
parts.push('scale(' + formatter.point(scale) +')');
if (skew && skew.x)
if (skew.x)
parts.push('skewX(' + formatter.number(skew.x) + ')');
if (skew && skew.y)
if (skew.y)
parts.push('skewY(' + formatter.number(skew.y) + ')');
attrs.transform = parts.join(' ');
} else {
@ -13870,8 +13916,9 @@ new function() {
if (length > 2) {
type = item._closed ? 'polygon' : 'polyline';
var parts = [];
for(var i = 0; i < length; i++)
for (var i = 0; i < length; i++) {
parts.push(formatter.point(segments[i]._point));
}
attrs.points = parts.join(' ');
} else {
type = 'line';
@ -14142,6 +14189,7 @@ new function() {
? new Rectangle([0, 0], view.getViewSize())
: bounds === 'content'
? Item._getBounds(children, matrix, { stroke: true })
.rect
: Rectangle.read([bounds], 0, { readNull: true }),
attrs = {
version: '1.1',
@ -14715,7 +14763,6 @@ new function() {
};
paper = new (PaperScope.inject(Base.exports, {
enumerable: true,
Base: Base,
Numerical: Numerical,
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/
*
* Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey
@ -9,7 +9,7 @@
*
* 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])(.*))$/)))
beansNames[bean[3].toLowerCase() + bean[4]] = bean[2];
if (!res || isFunc || !res.get || typeof res.get !== 'function'
|| !Base.isPlainObject(res))
|| !Base.isPlainObject(res)) {
res = { value: res, writable: true };
}
if ((describe(dest, name)
|| { configurable: true }).configurable) {
res.configurable = true;
res.enumerable = enumerable;
res.enumerable = enumerable != null ? enumerable : !bean;
}
define(dest, name, res);
}
@ -141,7 +142,7 @@ var Base = new function() {
preserve = src.preserve;
if (statics !== src)
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++)
this.inject(arguments[i]);
@ -164,13 +165,15 @@ var Base = new function() {
proto = ctor.prototype = proto || create(this.prototype);
define(proto, 'constructor',
{ value: ctor, writable: true, configurable: true });
inject(ctor, this, true);
inject(ctor, this);
if (arguments.length)
this.inject.apply(ctor, arguments);
ctor.base = base;
return ctor;
}
}, true).inject({
}).inject({
enumerable: false,
initialize: Base,
set: Base,
@ -230,6 +233,8 @@ if (typeof module !== 'undefined')
module.exports = Base;
Base.inject({
enumerable: false,
toString: function() {
return this._id != null
? (this._class || 'Object') + (this._name
@ -265,13 +270,12 @@ Base.inject({
if (props)
Base.filter(this, props, exclude, this._prioritize);
return this;
},
}
}, {
statics: {
exports: {
enumerable: true
},
beans: false,
statics: {
exports: {},
extend: function extend() {
var res = extend.base.apply(this, arguments),
@ -341,6 +345,11 @@ Base.inject({
: list) || obj;
if (readIndex) {
list.__index = begin + obj.__read;
var filtered = obj.__filtered;
if (filtered) {
list.__filtered = filtered;
obj.__filtered = undefined;
}
obj.__read = undefined;
}
return obj;
@ -371,14 +380,16 @@ Base.inject({
var value = this.getNamed(list, name),
hasObject = value !== undefined;
if (hasObject) {
var filtered = list._filtered;
var filtered = list.__filtered;
if (!filtered) {
filtered = list._filtered = Base.create(list[0]);
filtered._unfiltered = list[0];
filtered = list.__filtered = Base.create(list[0]);
filtered.__unfiltered = list[0];
}
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) {
@ -386,7 +397,7 @@ Base.inject({
if (list._hasObject === undefined)
list._hasObject = list.length === 1 && Base.isPlainObject(arg);
if (list._hasObject)
return name ? arg[name] : list._filtered || arg;
return name ? arg[name] : list.__filtered || arg;
},
hasNamed: function(list, name) {
@ -416,7 +427,7 @@ Base.inject({
processed = keys;
}
Object.keys(source._unfiltered || source).forEach(handleKey);
Object.keys(source.__unfiltered || source).forEach(handleKey);
return dest;
},
@ -462,8 +473,7 @@ Base.inject({
} else if (Array.isArray(obj)) {
res = [];
for (var i = 0, l = obj.length; i < l; i++)
res[i] = Base.serialize(obj[i], options, compact,
dictionary);
res[i] = Base.serialize(obj[i], options, compact, dictionary);
} else if (Base.isPlainObject(obj)) {
res = {};
var keys = Object.keys(obj);
@ -586,8 +596,7 @@ Base.inject({
hyphenate: function(str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
}
});
}});
var Emitter = {
on: function(type, func) {
@ -769,7 +778,7 @@ var PaperScope = Base.extend({
}
},
version: "0.11.2",
version: "0.11.3",
getView: function() {
var project = this.project;
@ -1739,6 +1748,9 @@ var Rectangle = Base.extend({
}
this._set(x, y, width, height);
read = arguments.__index;
var filtered = arguments.__filtered;
if (filtered)
this.__filtered = filtered;
}
if (this.__read)
this.__read = read;
@ -2262,7 +2274,7 @@ var Matrix = Base.extend({
return this.shear(shear, center);
},
append: function(mx) {
append: function(mx, _dontNotify) {
if (mx) {
var a1 = this._a,
b1 = this._b,
@ -2280,12 +2292,13 @@ var Matrix = Base.extend({
this._d = b2 * b1 + d2 * d1;
this._tx += tx2 * a1 + ty2 * c1;
this._ty += tx2 * b1 + ty2 * d1;
if (!_dontNotify)
this._changed();
}
return this;
},
prepend: function(mx) {
prepend: function(mx, _dontNotify) {
if (mx) {
var a1 = this._a,
b1 = this._b,
@ -2305,6 +2318,7 @@ var Matrix = Base.extend({
this._d = c2 * c1 + d2 * d1;
this._tx = a2 * tx1 + b2 * ty1 + tx2;
this._ty = c2 * tx1 + d2 * ty1 + ty2;
if (!_dontNotify)
this._changed();
}
return this;
@ -3236,11 +3250,11 @@ new function() {
this._boundsOptions);
if (!opts.stroke || this.getStrokeScaling())
opts.cacheItem = this;
var bounds = this._getCachedBounds(hasMatrix && matrix, opts);
var rect = this._getCachedBounds(hasMatrix && matrix, opts).rect;
return !arguments.length
? new LinkedRectangle(bounds.x, bounds.y, bounds.width,
bounds.height, this, 'setBounds')
: bounds;
? new LinkedRectangle(rect.x, rect.y, rect.width, rect.height,
this, 'setBounds')
: rect;
},
setBounds: function() {
@ -3273,29 +3287,49 @@ new function() {
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) {
matrix = matrix && matrix._orNullIfIdentity();
var internal = options.internal && !noInternal,
cacheItem = options.cacheItem,
_matrix = internal ? null : this._matrix._orNullIfIdentity(),
cacheKey = cacheItem && (!matrix || matrix.equals(_matrix)) && [
options.stroke ? 1 : 0,
options.handle ? 1 : 0,
internal ? 1 : 0
].join('');
cacheKey = cacheItem && (!matrix || matrix.equals(_matrix))
&& this._getBoundsCacheKey(options, internal),
bounds = this._bounds;
Item._updateBoundsCache(this._parent || this._symbol, cacheItem);
if (cacheKey && this._bounds && cacheKey in this._bounds)
return this._bounds[cacheKey].rect.clone();
var bounds = this._getBounds(matrix || _matrix, options);
if (cacheKey && bounds && cacheKey in bounds) {
var cached = bounds[cacheKey];
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 (!this._bounds)
this._bounds = {};
var cached = this._bounds[cacheKey] = {
rect: bounds.clone(),
if (!bounds) {
this._bounds = bounds = {};
}
var cached = bounds[cacheKey] = {
rect: rect.clone(),
nonscaling: nonscaling,
internal: internal
};
}
return bounds;
return {
rect: rect,
nonscaling: nonscaling
};
},
_getStrokeMatrix: function(matrix, options) {
@ -3340,22 +3374,29 @@ new function() {
var x1 = Infinity,
x2 = -x1,
y1 = x1,
y2 = x2;
y2 = x2,
nonscaling = false;
options = options || {};
for (var i = 0, l = items.length; i < l; i++) {
var item = items[i];
if (item._visible && !item.isEmpty()) {
var rect = item._getCachedBounds(
matrix && matrix.appended(item._matrix), options, true);
var bounds = item._getCachedBounds(
matrix && matrix.appended(item._matrix), options, true),
rect = bounds.rect;
x1 = Math.min(rect.x, x1);
y1 = Math.min(rect.y, y1);
x2 = Math.max(rect.x + rect.width, x2);
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();
: new Rectangle(),
nonscaling: nonscaling
};
}
}
@ -3395,8 +3436,18 @@ new function() {
var current = this.getScaling(),
scaling = Point.read(arguments, 0, { clone: true, readNull: true });
if (current && scaling && !current.equals(scaling)) {
var decomposed = this._decomposed;
this.scale(scaling.x / current.x, scaling.y / current.y);
var rotation = this.getRotation(),
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) {
decomposed.scaling = scaling;
this._decomposed = decomposed;
@ -4218,8 +4269,6 @@ new function() {
transform: function(matrix, _applyMatrix, _applyRecursively,
_setApplyMatrix) {
if (matrix && matrix.isIdentity())
matrix = null;
var _matrix = this._matrix,
transform = matrix && !matrix.isIdentity(),
applyMatrix = (_applyMatrix || this._applyMatrix)
@ -4230,22 +4279,7 @@ new function() {
if (transform) {
if (!matrix.isInvertible() && _matrix.isInvertible())
_matrix._backup = _matrix.getValues();
_matrix.prepend(matrix);
}
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) {
_matrix.prepend(matrix, true);
var style = this._style,
fillColor = style.getFillColor(true),
strokeColor = style.getStrokeColor(true);
@ -4254,24 +4288,38 @@ new function() {
if (strokeColor)
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,
position = this._position;
if (transform || applyMatrix) {
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) {
var cache = bounds[key];
if (applyMatrix || !cache.internal) {
if (cache.nonscaling) {
delete bounds[key];
} else if (applyMatrix || !cache.internal) {
var rect = cache.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;
} 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);
}
return this;
@ -5483,10 +5531,8 @@ var HitResult = Base.extend({
initialize: function HitResult(type, item, values) {
this.type = type;
this.item = item;
if (values) {
values.enumerable = true;
if (values)
this.inject(values);
}
},
statics: {
@ -7006,7 +7052,7 @@ new function() {
cos = Math.cos(angle),
rv = [],
roots = [];
for(var i = 0; i < 8; i += 2) {
for (var i = 0; i < 8; i += 2) {
var x = v[i] - px,
y = v[i + 1] - py;
rv.push(
@ -11016,10 +11062,10 @@ var PointText = TextItem.extend({
x = 0;
if (justification !== 'left')
x -= width / (justification === 'center' ? 2: 1);
var bounds = new Rectangle(x,
var rect = new Rectangle(x,
numLines ? - 0.75 * leading : 0,
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)
|| hitItem && hitItem !== dragItem
&& !hitItem.isDescendant(dragItem)
&& emitMouseEvent(hitItem, null, fallbacks[type] || type, event,
point, prevPoint, dragItem)
&& emitMouseEvent(hitItem, null, type, event, point, prevPoint,
dragItem)
|| emitMouseEvent(view, dragItem || hitItem || view, type, event,
point, prevPoint));
}
@ -13807,9 +13853,9 @@ new function() {
if (!Numerical.isZero(scale.x - 1)
|| !Numerical.isZero(scale.y - 1))
parts.push('scale(' + formatter.point(scale) +')');
if (skew && skew.x)
if (skew.x)
parts.push('skewX(' + formatter.number(skew.x) + ')');
if (skew && skew.y)
if (skew.y)
parts.push('skewY(' + formatter.number(skew.y) + ')');
attrs.transform = parts.join(' ');
} else {
@ -13870,8 +13916,9 @@ new function() {
if (length > 2) {
type = item._closed ? 'polygon' : 'polyline';
var parts = [];
for(var i = 0; i < length; i++)
for (var i = 0; i < length; i++) {
parts.push(formatter.point(segments[i]._point));
}
attrs.points = parts.join(' ');
} else {
type = 'line';
@ -14142,6 +14189,7 @@ new function() {
? new Rectangle([0, 0], view.getViewSize())
: bounds === 'content'
? Item._getBounds(children, matrix, { stroke: true })
.rect
: Rectangle.read([bounds], 0, { readNull: true }),
attrs = {
version: '1.1',
@ -16413,7 +16461,6 @@ Base.exports.PaperScript = function() {
}.call(this);
paper = new (PaperScope.inject(Base.exports, {
enumerable: true,
Base: Base,
Numerical: Numerical,
Key: Key,

View file

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

View file

@ -120,7 +120,7 @@
function getEqualizerBands(data) {
var bands = [];
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 end = start * 2 + 1;
var sum = 0;

View file

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

View file

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

View file

@ -36,13 +36,15 @@ gulp.task('publish', function() {
if (options.branch !== 'develop') {
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(
'publish:json',
'publish:dist',
'publish:packages',
'publish:commit',
'publish:release',
'publish:website',
'publish:release',
'publish:load'
);
});
@ -115,8 +117,18 @@ gulp.task('publish:website', function() {
}
});
gulp.task('publish:website:build',
['publish:website:docs', 'publish:website:zip', 'publish:website:lib']);
gulp.task('publish:website:build', [
'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() {
return del([ referencePath + '/*' ], { force: true });
@ -135,7 +147,10 @@ gulp.task('publish:website:zip', ['publish:version'], function() {
.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')
.pipe(rename({ basename: 'paper' }))
.pipe(gulp.dest(assetPath));

View file

@ -1,12 +1,12 @@
{
"name": "paper",
"version": "0.11.2",
"version": "0.11.3",
"description": "The Swiss Army Knife of Vector Graphics Scripting",
"license": "MIT",
"homepage": "http://paperjs.org",
"repository": {
"type": "git",
"url": "git://github.com/paperjs/paper.js"
"url": "https://github.com/paperjs/paper.js"
},
"bugs": "https://github.com/paperjs/paper.js/issues",
"contributors": [
@ -69,7 +69,7 @@
"run-sequence": "^1.2.2",
"source-map-support": "^0.4.0",
"stats.js": "0.16.0",
"straps": "^2.1.0"
"straps": "^3.0.1"
},
"browser": {
"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
* @return {Matrix} this matrix, modified
*/
append: function(mx) {
append: function(mx, _dontNotify) {
if (mx) {
var a1 = this._a,
b1 = this._b,
@ -395,6 +395,7 @@ var Matrix = Base.extend(/** @lends Matrix# */{
this._d = b2 * b1 + d2 * d1;
this._tx += tx2 * a1 + ty2 * c1;
this._ty += tx2 * b1 + ty2 * d1;
if (!_dontNotify)
this._changed();
}
return this;
@ -407,7 +408,7 @@ var Matrix = Base.extend(/** @lends Matrix# */{
* @param {Matrix} matrix the matrix to prepend
* @return {Matrix} this matrix, modified
*/
prepend: function(mx) {
prepend: function(mx, _dontNotify) {
if (mx) {
var a1 = this._a,
b1 = this._b,
@ -427,6 +428,7 @@ var Matrix = Base.extend(/** @lends Matrix# */{
this._d = c2 * c1 + d2 * d1;
this._tx = a2 * tx1 + b2 * ty1 + tx2;
this._ty = c2 * tx1 + d2 * ty1 + ty2;
if (!_dontNotify)
this._changed();
}
return this;
@ -671,7 +673,7 @@ var Matrix = Base.extend(/** @lends 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.
*
* @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);
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)
this.__read = read;

View file

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

View file

@ -17,8 +17,6 @@
// global one in the whole scope.
paper = new (PaperScope.inject(Base.exports, {
// Mark fields as enumerable so PaperScope.inject can pick them up
enumerable: true,
Base: Base,
Numerical: Numerical,
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
// properties.
// This allows the definition of getters too, e.g. for 'pixel'.
if (values) {
// Make enumerable so toString() works.
values.enumerable = true;
if (values)
this.inject(values);
}
},
/**

View file

@ -827,14 +827,14 @@ new function() { // Injection scope for various item event handlers
opts.cacheItem = this;
// If we're caching bounds, pass on this item as cacheItem, so
// 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
// the setBounds() setter to update the Item whenever the bounds are
// changed:
return !arguments.length
? new LinkedRectangle(bounds.x, bounds.y, bounds.width,
bounds.height, this, 'setBounds')
: bounds;
? new LinkedRectangle(rect.x, rect.y, rect.width, rect.height,
this, 'setBounds')
: rect;
},
setBounds: function(/* rect */) {
@ -889,6 +889,14 @@ new function() { // Injection scope for various item event handlers
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
* 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,
_matrix = internal ? null : this._matrix._orNullIfIdentity(),
// Create a key for caching, reflecting all bounds options.
cacheKey = cacheItem && (!matrix || matrix.equals(_matrix)) && [
options.stroke ? 1 : 0,
options.handle ? 1 : 0,
internal ? 1 : 0
].join('');
cacheKey = cacheItem && (!matrix || matrix.equals(_matrix))
&& this._getBoundsCacheKey(options, internal),
bounds = this._bounds;
// NOTE: This needs to happen before returning cached values, since even
// then, _boundsCache needs to be kept up-to-date.
Item._updateBoundsCache(this._parent || this._symbol, cacheItem);
if (cacheKey && this._bounds && cacheKey in this._bounds)
return this._bounds[cacheKey].rect.clone();
var bounds = this._getBounds(matrix || _matrix, options);
if (cacheKey && bounds && cacheKey in bounds) {
var cached = bounds[cacheKey];
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
// before returning
if (cacheKey) {
if (!this._bounds)
this._bounds = {};
var cached = this._bounds[cacheKey] = {
rect: bounds.clone(),
if (!bounds) {
this._bounds = bounds = {};
}
var cached = bounds[cacheKey] = {
rect: rect.clone(),
nonscaling: nonscaling,
// Mark as internal, so Item#transform() won't transform it
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,
x2 = -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 || {};
for (var i = 0, l = items.length; i < l; 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
// internal bounds for this item, we need to apply the
// matrices to its children.
var rect = item._getCachedBounds(
matrix && matrix.appended(item._matrix), options, true);
var bounds = item._getCachedBounds(
matrix && matrix.appended(item._matrix), options, true),
rect = bounds.rect;
x1 = Math.min(rect.x, x1);
y1 = Math.min(rect.y, y1);
x2 = Math.max(rect.x + rect.width, x2);
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();
: 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 });
if (current && scaling && !current.equals(scaling)) {
// See #setRotation() for preservation of _decomposed.
var decomposed = this._decomposed;
this.scale(scaling.x / current.x, scaling.y / current.y);
var rotation = this.getRotation(),
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) {
decomposed.scaling = scaling;
this._decomposed = decomposed;
@ -3376,11 +3421,9 @@ new function() { // Injection scope for hit-test functions shared with project
// 'lines'. Default: ['objects', 'children']
transform: function(matrix, _applyMatrix, _applyRecursively,
_setApplyMatrix) {
var _matrix = this._matrix,
// If no matrix is provided, or the matrix is the identity, we might
// 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(),
applyMatrix = (_applyMatrix || this._applyMatrix)
// 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.
if (!matrix.isInvertible() && _matrix.isInvertible())
_matrix._backup = _matrix.getValues();
_matrix.prepend(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) {
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) {
// Pass `true` for _dontNotify, as we're handling this after.
_matrix.prepend(matrix, true);
// When a new matrix was applied, we also need to transform gradient
// color points. These always need transforming, regardless of
// #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)
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
// on matrix we can calculate and set them again, so preserve them.
var bounds = this._bounds,
position = this._position;
// We always need to call _changed since we're caching bounds on all
// items, including Group.
if (transform || applyMatrix) {
this._changed(/*#=*/Change.GEOMETRY);
}
// Detect matrices that contain only translations and scaling
// and transform the cached _bounds and _position without having to
// fully recalculate each time.
var decomp = bounds && matrix && matrix.decompose();
if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) {
// Transform the old bound by looping through all the cached bounds
// in _bounds and transform each.
var decomp = transform && bounds && matrix.decompose();
if (decomp && decomp.skewing.isZero() && decomp.rotation % 90 === 0) {
// Transform the old bound by looping through all the cached
// bounds in _bounds and transform each.
for (var key in bounds) {
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
// item applied its matrix.
if (applyMatrix || !cache.internal) {
var rect = cache.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;
} else if (matrix && position) {
// Transform position as well.
// If we have cached bounds, try to determine _position as its
// 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);
}
// Allow chaining here, since transform() is related to Matrix functions

View file

@ -17,7 +17,7 @@
// The paper.js version.
// NOTE: Adjust value here before calling `gulp publish`, which then updates and
// 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.
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.
rv = [],
roots = [];
for(var i = 0; i < 8; i += 2) {
for (var i = 0; i < 8; i += 2) {
var x = v[i] - px,
y = v[i + 1] - py;
rv.push(

View file

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

View file

@ -115,9 +115,9 @@ var PointText = TextItem.extend(/** @lends PointText# */{
x -= width / (justification === 'center' ? 2: 1);
// Until we don't have baseline measuring, assume 1 / 4 leading as a
// rough guess:
var bounds = new Rectangle(x,
var rect = new Rectangle(x,
numLines ? - 0.75 * leading : 0,
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
* 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 ε
* 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)
// 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
// received an event in the call above). Use fallbacks to translate
// mousedrag to mousemove, since drag is handled above.
// received an event in the call above).
|| hitItem && hitItem !== dragItem
&& !hitItem.isDescendant(dragItem)
&& emitMouseEvent(hitItem, null, fallbacks[type] || type, event,
point, prevPoint, dragItem)
&& emitMouseEvent(hitItem, null, type, event, point, prevPoint,
dragItem)
// Lastly handle the mouse events on the view, if we're still here.
|| emitMouseEvent(view, dragItem || hitItem || view, type, event,
point, prevPoint));

View file

@ -876,3 +876,56 @@ test('Item#pivot', function() {
equals(path2.pivot, pivot.add(difference),
'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',
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;
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]);
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() {
// Hit-testing needs to handle applyMatrix disabled with stroke scaling,
// even when hit-testing on "distorted" stroke joins:
@ -714,10 +717,29 @@ test('path.strokeBounds with applyMatrix disabled', function() {
testHitResult();
path.applyMatrix = true;
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();
});
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() {
var path = new Path.Rectangle({
size: [20, 20],

View file

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