mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-20 22:39:50 -05:00
Improve performance of Path constructors and handling of { insert: false } Item creation.
This commit is contained in:
parent
737466d15c
commit
ccfd51a65a
9 changed files with 107 additions and 85 deletions
|
@ -39,7 +39,15 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
if (name)
|
||||
proto._type = Base.hyphenate(name);
|
||||
return res;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* An object constant that can be passed to Item#initialize() to avoid
|
||||
* insertion into the DOM.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NO_INSERT: { insert: false }
|
||||
},
|
||||
|
||||
_class: 'Item',
|
||||
|
@ -68,6 +76,16 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
// Do nothing, but declare it for named constructors.
|
||||
},
|
||||
|
||||
/**
|
||||
* Private helper for #initialize() that tries setting properties from the
|
||||
* passed props object, and apply the point translation to the internal
|
||||
* matrix.
|
||||
*
|
||||
* @param {Object} props the properties to be applied to the item
|
||||
* @param {Point} point the point by which to transform the internal matrix
|
||||
* @returns {Boolean} {@true if the properties were successfully be applied,
|
||||
* or if none were provided}
|
||||
*/
|
||||
_initialize: function(props, point) {
|
||||
// Define this Item's unique id. But allow the creation of internally
|
||||
// used paths with no ids.
|
||||
|
@ -95,7 +113,10 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
}
|
||||
}
|
||||
this._style = new Style(this._project._currentStyle, this);
|
||||
return props ? this._set(props, { insert: true }) : true;
|
||||
// Filter out Item.NO_INSERT before _set(), for performance reasons
|
||||
return props && props !== Item.NO_INSERT
|
||||
? this._set(props, { insert: true }) // Filter out insert prop.
|
||||
: true;
|
||||
},
|
||||
|
||||
_events: new function() {
|
||||
|
@ -1443,7 +1464,7 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
* }
|
||||
*/
|
||||
clone: function(insert) {
|
||||
return this._clone(new this.constructor({ insert: false }), insert);
|
||||
return this._clone(new this.constructor(Item.NO_INSERT), insert);
|
||||
},
|
||||
|
||||
_clone: function(copy, insert) {
|
||||
|
@ -1538,12 +1559,10 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
|||
// See Project#draw() for an explanation of new Base()
|
||||
this.draw(ctx, new Base({ transforms: [matrix] }));
|
||||
ctx.restore();
|
||||
var raster = new Raster({
|
||||
canvas: canvas,
|
||||
insert: false
|
||||
});
|
||||
var raster = new Raster(Item.NO_INSERT);
|
||||
raster.setCanvas(canvas);
|
||||
raster.transform(new Matrix().translate(topLeft.add(size.divide(2)))
|
||||
// Take resolution into acocunt and scale back to original size.
|
||||
// Take resolution into account and scale back to original size.
|
||||
.scale(1 / scale));
|
||||
raster.insertAbove(this);
|
||||
// NOTE: We don't need to release the canvas since it now belongs to the
|
||||
|
|
|
@ -99,10 +99,9 @@ var PlacedSymbol = Item.extend(/** @lends PlacedSymbol# */{
|
|||
},
|
||||
|
||||
clone: function(insert) {
|
||||
return this._clone(new PlacedSymbol({
|
||||
symbol: this.symbol,
|
||||
insert: false
|
||||
}), insert);
|
||||
var copy = new PlacedSymbol(Item.NO_INSERT);
|
||||
copy.setSymbol(this._symbol);
|
||||
return this._clone(copy, insert);
|
||||
},
|
||||
|
||||
isEmpty: function() {
|
||||
|
|
|
@ -97,17 +97,19 @@ var Raster = Item.extend(/** @lends Raster# */{
|
|||
},
|
||||
|
||||
clone: function(insert) {
|
||||
var param = { insert: false },
|
||||
image = this._image;
|
||||
var copy = new Raster(Item.NO_INSERT),
|
||||
image = this._image,
|
||||
canvas = this._canvas;
|
||||
if (image) {
|
||||
param.image = image;
|
||||
} else if (this._canvas) {
|
||||
// If the Raster contains a Canvas object, we need to create
|
||||
// a new one and draw this raster's canvas on it.
|
||||
var canvas = param.canvas = CanvasProvider.getCanvas(this._size);
|
||||
canvas.getContext('2d').drawImage(this._canvas, 0, 0);
|
||||
copy.setImage(image);
|
||||
} else if (canvas) {
|
||||
// If the Raster contains a Canvas object, we need to create a new
|
||||
// one and draw this raster's canvas on it.
|
||||
var copyCanvas = CanvasProvider.getCanvas(this._size);
|
||||
copyCanvas.getContext('2d').drawImage(canvas, 0, 0);
|
||||
copy.setCanvas(copyCanvas);
|
||||
}
|
||||
return this._clone(new Raster(param), insert);
|
||||
return this._clone(copy, insert);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -394,10 +396,8 @@ var Raster = Item.extend(/** @lends Raster# */{
|
|||
*/
|
||||
getSubRaster: function(rect) { // TODO: Fix argument assignment!
|
||||
var rect = Rectangle.read(arguments),
|
||||
raster = new Raster({
|
||||
canvas: this.getSubCanvas(rect),
|
||||
insert: false
|
||||
});
|
||||
raster = new Raster(Item.NO_INSERT);
|
||||
raster.setCanvas(this.getSubCanvas(rect));
|
||||
raster.translate(rect.getCenter().subtract(this.getSize().divide(2)));
|
||||
raster._matrix.preConcatenate(this._matrix);
|
||||
raster.insertAbove(this);
|
||||
|
|
|
@ -39,12 +39,11 @@ var Shape = Item.extend(/** @lends Shape# */{
|
|||
},
|
||||
|
||||
clone: function(insert) {
|
||||
return this._clone(new Shape({
|
||||
shape: this._shape,
|
||||
size: this._size,
|
||||
radius: this._radius,
|
||||
insert: false
|
||||
}), insert);
|
||||
var copy = new Shape(Item.NO_INSERT);
|
||||
copy.setShape(this._shape);
|
||||
copy.setSize(this._size);
|
||||
copy.setRadius(this._radius);
|
||||
return this._clone(copy, insert);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,9 +20,19 @@ Path.inject({ statics: new function() {
|
|||
new Segment([0, 1], [kappa, 0 ], [-kappa, 0])
|
||||
];
|
||||
|
||||
function createPath(segments, closed, args) {
|
||||
var props = Base.getNamed(args),
|
||||
path = new Path(props && props.insert === false && Item.NO_INSERT);
|
||||
path._add(segments);
|
||||
// No need to use setter for _closed since _add() called _changed().
|
||||
path._closed = true;
|
||||
// Set named arguments at the end, since some depend on geometry to be
|
||||
// defined (e.g. #clockwise)
|
||||
return path.set(props);
|
||||
}
|
||||
|
||||
function createEllipse(center, radius, args) {
|
||||
var path = new Path(),
|
||||
segments = new Array(4);
|
||||
var segments = new Array(4);
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var segment = ellipseSegments[i];
|
||||
segments[i] = new Segment(
|
||||
|
@ -31,11 +41,7 @@ Path.inject({ statics: new function() {
|
|||
segment._handleOut.multiply(radius)
|
||||
);
|
||||
}
|
||||
path._add(segments);
|
||||
path._closed = true;
|
||||
// Set named arguments at the end, since some depend on geometry to be
|
||||
// defined (e.g. #clockwise)
|
||||
return path.set(Base.getNamed(args));
|
||||
return createPath(segments, true, args);
|
||||
}
|
||||
|
||||
|
||||
|
@ -73,10 +79,10 @@ Path.inject({ statics: new function() {
|
|||
* });
|
||||
*/
|
||||
Line: function(/* from, to */) {
|
||||
return new Path([
|
||||
Point.readNamed(arguments, 'from'),
|
||||
Point.readNamed(arguments, 'to')
|
||||
]).set(Base.getNamed(arguments));
|
||||
return createPath([
|
||||
new Segment(Point.readNamed(arguments, 'from')),
|
||||
new Segment(Point.readNamed(arguments, 'to'))
|
||||
], false, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -210,22 +216,22 @@ Path.inject({ statics: new function() {
|
|||
bl = rect.getBottomLeft(true),
|
||||
tl = rect.getTopLeft(true),
|
||||
tr = rect.getTopRight(true),
|
||||
br = rect.getBottomRight(true);
|
||||
path = new Path();
|
||||
br = rect.getBottomRight(true),
|
||||
segments;
|
||||
if (!radius || radius.isZero()) {
|
||||
path._add([
|
||||
segments = [
|
||||
new Segment(bl),
|
||||
new Segment(tl),
|
||||
new Segment(tr),
|
||||
new Segment(br)
|
||||
]);
|
||||
];
|
||||
} else {
|
||||
radius = Size.min(radius, rect.getSize(true).divide(2));
|
||||
var rx = radius.width,
|
||||
ry = radius.height,
|
||||
hx = rx * kappa,
|
||||
hy = ry * kappa;
|
||||
path._add([
|
||||
segments = [
|
||||
new Segment(bl.add(rx, 0), null, [-hx, 0]),
|
||||
new Segment(bl.subtract(0, ry), [0, hy]),
|
||||
new Segment(tl.add(0, ry), null, [0, -hy]),
|
||||
|
@ -234,11 +240,9 @@ Path.inject({ statics: new function() {
|
|||
new Segment(tr.add(0, ry), [0, -hy], null),
|
||||
new Segment(br.subtract(0, ry), null, [0, hy]),
|
||||
new Segment(br.subtract(rx, 0), [hx, 0])
|
||||
]);
|
||||
];
|
||||
}
|
||||
// No need to use setter for _closed since _add() called _changed().
|
||||
path._closed = true;
|
||||
return path.set(Base.getNamed(arguments));
|
||||
return createPath(segments, true, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -329,10 +333,13 @@ Path.inject({ statics: new function() {
|
|||
var from = Point.readNamed(arguments, 'from'),
|
||||
through = Point.readNamed(arguments, 'through'),
|
||||
to = Point.readNamed(arguments, 'to'),
|
||||
path = new Path();
|
||||
props = Base.getNamed(arguments),
|
||||
// See createPath() for an explanation of the following sequence
|
||||
path = new Path(props && props.insert === false
|
||||
&& Item.NO_INSERT);
|
||||
path.moveTo(from);
|
||||
path.arcTo(through, to);
|
||||
return path.set(Base.getNamed(arguments));
|
||||
return path.set(props);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -372,19 +379,15 @@ Path.inject({ statics: new function() {
|
|||
var center = Point.readNamed(arguments, 'center'),
|
||||
sides = Base.readNamed(arguments, 'sides'),
|
||||
radius = Base.readNamed(arguments, 'radius'),
|
||||
path = new Path(),
|
||||
step = 360 / sides,
|
||||
three = !(sides % 3),
|
||||
vector = new Point(0, three ? -radius : radius),
|
||||
offset = three ? -1 : 0.5,
|
||||
segments = new Array(sides);
|
||||
for (var i = 0; i < sides; i++) {
|
||||
for (var i = 0; i < sides; i++)
|
||||
segments[i] = new Segment(center.add(
|
||||
vector.rotate((i + offset) * step)));
|
||||
}
|
||||
path._add(segments);
|
||||
path._closed = true;
|
||||
return path.set(Base.getNamed(arguments));
|
||||
return createPath(segments, true, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -432,17 +435,13 @@ Path.inject({ statics: new function() {
|
|||
points = Base.readNamed(arguments, 'points') * 2,
|
||||
radius1 = Base.readNamed(arguments, 'radius1'),
|
||||
radius2 = Base.readNamed(arguments, 'radius2'),
|
||||
path = new Path(),
|
||||
step = 360 / points,
|
||||
vector = new Point(0, -1),
|
||||
segments = new Array(points);
|
||||
for (var i = 0; i < points; i++) {
|
||||
segments[i] = new Segment(center.add(
|
||||
vector.rotate(step * i).multiply(i % 2 ? radius2 : radius1)));
|
||||
}
|
||||
path._add(segments);
|
||||
path._closed = true;
|
||||
return path.set(Base.getNamed(arguments));
|
||||
for (var i = 0; i < points; i++)
|
||||
segments[i] = new Segment(center.add(vector.rotate(step * i)
|
||||
.multiply(i % 2 ? radius2 : radius1)));
|
||||
return createPath(segments, true, arguments);
|
||||
}
|
||||
};
|
||||
}});
|
||||
|
|
|
@ -106,11 +106,17 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
? arguments
|
||||
: null;
|
||||
// Always call setSegments() to initialize a few related variables.
|
||||
this.setSegments(segments || []);
|
||||
if (!segments && typeof arg === 'string') {
|
||||
this.setPathData(arg);
|
||||
// Erase for _initialize() call below.
|
||||
arg = null;
|
||||
if (segments && segments.length > 0) {
|
||||
// This sets _curves and _selectedSegmentState too!
|
||||
this.setSegments(segments);
|
||||
} else {
|
||||
this._curves = undefined; // For hidden class optimization
|
||||
this._selectedSegmentState = 0;
|
||||
if (!segments && typeof arg === 'string') {
|
||||
this.setPathData(arg);
|
||||
// Erase for _initialize() call below.
|
||||
arg = null;
|
||||
}
|
||||
}
|
||||
// Only pass on arg as props if it wasn't consumed for segments already.
|
||||
this._initialize(!segments && arg);
|
||||
|
@ -121,15 +127,12 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
},
|
||||
|
||||
clone: function(insert) {
|
||||
var copy = this._clone(new Path({
|
||||
segments: this._segments,
|
||||
insert: false
|
||||
}), insert);
|
||||
// Speed up things a little by copy over values that don't need checking
|
||||
var copy = new Path(Item.NO_INSERT);
|
||||
copy.setSegments(this._segments);
|
||||
copy._closed = this._closed;
|
||||
if (this._clockwise !== undefined)
|
||||
copy._clockwise = this._clockwise;
|
||||
return copy;
|
||||
return this._clone(copy, insert);
|
||||
},
|
||||
|
||||
_changed: function _changed(flags) {
|
||||
|
@ -177,7 +180,10 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
this._selectedSegmentState = 0;
|
||||
// Calculate new curves next time we call getCurves()
|
||||
this._curves = undefined;
|
||||
this._add(Segment.readAll(segments));
|
||||
if (segments && segments.length > 0)
|
||||
this._add(Segment.readAll(segments));
|
||||
// Preserve fullySelected state.
|
||||
// TODO: Do we still need this?
|
||||
if (fullySelected)
|
||||
this.setFullySelected(true);
|
||||
},
|
||||
|
@ -1785,7 +1791,7 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
|
||||
// Code to check stroke join / cap areas
|
||||
|
||||
function addAreaPoint(point) {
|
||||
function addToArea(point) {
|
||||
area.add(point);
|
||||
}
|
||||
|
||||
|
@ -1803,10 +1809,10 @@ var Path = PathItem.extend(/** @lends Path# */{
|
|||
if (join !== 'round' && (segment._handleIn.isZero()
|
||||
|| segment._handleOut.isZero()))
|
||||
Path._addSquareJoin(segment, join, radius, miterLimit,
|
||||
addAreaPoint, true);
|
||||
addToArea, true);
|
||||
} else if (cap !== 'round') {
|
||||
// It's a cap
|
||||
Path._addSquareCap(segment, cap, radius, addAreaPoint, true);
|
||||
Path._addSquareCap(segment, cap, radius, addToArea, true);
|
||||
}
|
||||
// See if the above produced an area to check for
|
||||
if (!area.isEmpty()) {
|
||||
|
|
|
@ -364,7 +364,7 @@ PathItem.inject(new function() {
|
|||
seg = startSeg = segments[i];
|
||||
if (seg._visited || !operator(seg._winding))
|
||||
continue;
|
||||
var path = new Path({ insert: false }),
|
||||
var path = new Path(Item.NO_INSERT),
|
||||
inter = seg._intersection,
|
||||
startInterSeg = inter && inter._segment,
|
||||
added = false, // Wether a first segment as added already
|
||||
|
|
|
@ -183,7 +183,7 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
|
|||
// NOTE: If there is no layer and this project is not the active
|
||||
// one, passing insert: false and calling addChild on the
|
||||
// project will handle it correctly.
|
||||
|| this.addChild(new Layer({ insert: false }))).addChild(child);
|
||||
|| this.addChild(new Layer(Item.NO_INSERT))).addChild(child);
|
||||
} else {
|
||||
child = null;
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ var PointText = TextItem.extend(/** @lends PointText# */{
|
|||
},
|
||||
|
||||
clone: function(insert) {
|
||||
return this._clone(new PointText({ insert: false }), insert);
|
||||
return this._clone(new PointText(Item.NO_INSERT), insert);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue