mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-07 13:22:07 -05:00
Make cachedBounds mechanism work for Symbols.
This commit is contained in:
parent
f8f60ec603
commit
81fe98d1bb
3 changed files with 107 additions and 105 deletions
182
src/item/Item.js
182
src/item/Item.js
|
@ -204,9 +204,9 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
||||||
* @param {ChangeFlag} flags describes what exactly has changed.
|
* @param {ChangeFlag} flags describes what exactly has changed.
|
||||||
*/
|
*/
|
||||||
_changed: function(flags) {
|
_changed: function(flags) {
|
||||||
var parent = this._parent,
|
var symbol = this._parentSymbol,
|
||||||
project = this._project,
|
cacheParent = this._parent || symbol,
|
||||||
symbol = this._parentSymbol;
|
project = this._project;
|
||||||
if (flags & /*#=*/ ChangeFlag.GEOMETRY) {
|
if (flags & /*#=*/ ChangeFlag.GEOMETRY) {
|
||||||
// Clear cached bounds and position whenever geometry changes
|
// Clear cached bounds and position whenever geometry changes
|
||||||
delete this._bounds;
|
delete this._bounds;
|
||||||
|
@ -214,19 +214,19 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
||||||
delete this._decomposed;
|
delete this._decomposed;
|
||||||
delete this._globalMatrix;
|
delete this._globalMatrix;
|
||||||
}
|
}
|
||||||
if (parent && (flags
|
if (cacheParent && (flags
|
||||||
& (/*#=*/ ChangeFlag.GEOMETRY | /*#=*/ ChangeFlag.STROKE))) {
|
& (/*#=*/ ChangeFlag.GEOMETRY | /*#=*/ ChangeFlag.STROKE))) {
|
||||||
// Clear cached bounds of all items that this item contributes to.
|
// Clear cached bounds of all items that this item contributes to.
|
||||||
// We call this on the parent, since the information is cached on
|
// We call this on the parent, since the information is cached on
|
||||||
// the parent, see getBounds().
|
// the parent, see getBounds().
|
||||||
parent._clearBoundsCache();
|
Item._clearBoundsCache(cacheParent);
|
||||||
}
|
}
|
||||||
if (flags & /*#=*/ ChangeFlag.HIERARCHY) {
|
if (flags & /*#=*/ ChangeFlag.HIERARCHY) {
|
||||||
// Clear cached bounds of all items that this item contributes to.
|
// Clear cached bounds of all items that this item contributes to.
|
||||||
// We don't call this on the parent, since we're already the parent
|
// We don't call this on the parent, since we're already the parent
|
||||||
// of the child that modified the hierarchy (that's where these
|
// of the child that modified the hierarchy (that's where these
|
||||||
// HIERARCHY notifications go)
|
// HIERARCHY notifications go)
|
||||||
this._clearBoundsCache();
|
Item._clearBoundsCache(this);
|
||||||
}
|
}
|
||||||
if (project) {
|
if (project) {
|
||||||
if (flags & /*#=*/ ChangeFlag.APPEARANCE) {
|
if (flags & /*#=*/ ChangeFlag.APPEARANCE) {
|
||||||
|
@ -820,89 +820,6 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
/** @lends Item# */{
|
/** @lends Item# */{
|
||||||
/**
|
|
||||||
* Private method that deals with the calling of _getBounds, recursive
|
|
||||||
* matrix concatenation and handles all the complicated caching mechanisms.
|
|
||||||
*/
|
|
||||||
_getCachedBounds: function(getter, matrix, cacheItem) {
|
|
||||||
// See if we can cache these bounds. We only cache the bounds
|
|
||||||
// transformed with the internally stored _matrix, (the default if no
|
|
||||||
// matrix is passed).
|
|
||||||
matrix = matrix && matrix.orNullIfIdentity();
|
|
||||||
var _matrix = this._matrix.orNullIfIdentity(),
|
|
||||||
cache = (!matrix || matrix.equals(_matrix)) && getter;
|
|
||||||
// Set up a boundsCache structure that keeps track of items that keep
|
|
||||||
// cached bounds that depend on this item. We store this in our parent,
|
|
||||||
// for multiple reasons:
|
|
||||||
// The parent receives HIERARCHY change notifications for when its
|
|
||||||
// children are added or removed and can thus clear the cache, and we
|
|
||||||
// save a lot of memory, e.g. when grouping 100 items and asking the
|
|
||||||
// group for its bounds. If stored on the children, we would have 100
|
|
||||||
// times the same structure.
|
|
||||||
// Note: This needs to happen before returning cached values, since even
|
|
||||||
// then, _boundsCache needs to be kept up-to-date.
|
|
||||||
if (cacheItem && this._parent) {
|
|
||||||
// Set-up the parent's boundsCache structure if it does not
|
|
||||||
// exist yet and add the cacheItem to it.
|
|
||||||
var id = cacheItem._id,
|
|
||||||
ref = this._parent._boundsCache
|
|
||||||
= this._parent._boundsCache || {
|
|
||||||
// Use both a hashtable for ids and an array for the list,
|
|
||||||
// so we can keep track of items that were added already
|
|
||||||
ids: {},
|
|
||||||
list: []
|
|
||||||
};
|
|
||||||
if (!ref.ids[id]) {
|
|
||||||
ref.list.push(cacheItem);
|
|
||||||
ref.ids[id] = cacheItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cache && this._bounds && this._bounds[cache])
|
|
||||||
return this._bounds[cache].clone();
|
|
||||||
// If the result of concatinating the passed matrix with our internal
|
|
||||||
// one is an identity transformation, set it to null for faster
|
|
||||||
// processing
|
|
||||||
matrix = !matrix
|
|
||||||
? _matrix
|
|
||||||
: _matrix
|
|
||||||
? matrix.clone().concatenate(_matrix)
|
|
||||||
: matrix;
|
|
||||||
// If we're caching bounds on this item, pass it on as cacheItem, so the
|
|
||||||
// children can setup the _boundsCache structures for it.
|
|
||||||
var bounds = this._getBounds(getter, matrix, cache ? this : cacheItem);
|
|
||||||
// If we can cache the result, update the _bounds cache structure
|
|
||||||
// before returning
|
|
||||||
if (cache) {
|
|
||||||
if (!this._bounds)
|
|
||||||
this._bounds = {};
|
|
||||||
this._bounds[cache] = bounds.clone();
|
|
||||||
}
|
|
||||||
return bounds;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears cached bounds of all items that the children of this item are
|
|
||||||
* contributing to. See #_getCachedBounds() for an explanation why this
|
|
||||||
* information is stored on parents, not the children themselves.
|
|
||||||
*/
|
|
||||||
_clearBoundsCache: function() {
|
|
||||||
if (this._boundsCache) {
|
|
||||||
for (var i = 0, list = this._boundsCache.list, l = list.length;
|
|
||||||
i < l; i++) {
|
|
||||||
var item = list[i];
|
|
||||||
delete item._bounds;
|
|
||||||
// Delete position as well, since it's depending on bounds.
|
|
||||||
delete item._position;
|
|
||||||
// We need to recursively call _clearBoundsCache, because if the
|
|
||||||
// cache for this item's children is not valid anymore, that
|
|
||||||
// propagates up the DOM tree.
|
|
||||||
if (item !== this && item._boundsCache)
|
|
||||||
item._clearBoundsCache();
|
|
||||||
}
|
|
||||||
delete this._boundsCache;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Protected method used in all the bounds getters. It loops through all the
|
* Protected method used in all the bounds getters. It loops through all the
|
||||||
* children, gets their bounds and finds the bounds around all of them.
|
* children, gets their bounds and finds the bounds around all of them.
|
||||||
|
@ -956,6 +873,93 @@ var Item = Base.extend(Callback, /** @lends Item# */{
|
||||||
matrix.translate(-center.x, -center.y);
|
matrix.translate(-center.x, -center.y);
|
||||||
// Now execute the transformation
|
// Now execute the transformation
|
||||||
this.transform(matrix);
|
this.transform(matrix);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private method that deals with the calling of _getBounds, recursive
|
||||||
|
* matrix concatenation and handles all the complicated caching mechanisms.
|
||||||
|
*/
|
||||||
|
_getCachedBounds: function(getter, matrix, cacheItem) {
|
||||||
|
// See if we can cache these bounds. We only cache the bounds
|
||||||
|
// transformed with the internally stored _matrix, (the default if no
|
||||||
|
// matrix is passed).
|
||||||
|
matrix = matrix && matrix.orNullIfIdentity();
|
||||||
|
var _matrix = this._matrix.orNullIfIdentity(),
|
||||||
|
cache = (!matrix || matrix.equals(_matrix)) && getter;
|
||||||
|
// Set up a boundsCache structure that keeps track of items that keep
|
||||||
|
// cached bounds that depend on this item. We store this in our parent,
|
||||||
|
// for multiple reasons:
|
||||||
|
// The parent receives HIERARCHY change notifications for when its
|
||||||
|
// children are added or removed and can thus clear the cache, and we
|
||||||
|
// save a lot of memory, e.g. when grouping 100 items and asking the
|
||||||
|
// group for its bounds. If stored on the children, we would have 100
|
||||||
|
// times the same structure.
|
||||||
|
// Note: This needs to happen before returning cached values, since even
|
||||||
|
// then, _boundsCache needs to be kept up-to-date.
|
||||||
|
var cacheParent = this._parent || this._parentSymbol;
|
||||||
|
if (cacheItem && cacheParent) {
|
||||||
|
// Set-up the parent's boundsCache structure if it does not
|
||||||
|
// exist yet and add the cacheItem to it.
|
||||||
|
var id = cacheItem._id,
|
||||||
|
ref = cacheParent._boundsCache
|
||||||
|
= cacheParent._boundsCache || {
|
||||||
|
// Use both a hashtable for ids and an array for the list,
|
||||||
|
// so we can keep track of items that were added already
|
||||||
|
ids: {},
|
||||||
|
list: []
|
||||||
|
};
|
||||||
|
if (!ref.ids[id]) {
|
||||||
|
ref.list.push(cacheItem);
|
||||||
|
ref.ids[id] = cacheItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cache && this._bounds && this._bounds[cache])
|
||||||
|
return this._bounds[cache].clone();
|
||||||
|
// If the result of concatinating the passed matrix with our internal
|
||||||
|
// one is an identity transformation, set it to null for faster
|
||||||
|
// processing
|
||||||
|
matrix = !matrix
|
||||||
|
? _matrix
|
||||||
|
: _matrix
|
||||||
|
? matrix.clone().concatenate(_matrix)
|
||||||
|
: matrix;
|
||||||
|
// If we're caching bounds on this item, pass it on as cacheItem, so the
|
||||||
|
// children can setup the _boundsCache structures for it.
|
||||||
|
var bounds = this._getBounds(getter, matrix, cache ? this : cacheItem);
|
||||||
|
// If we can cache the result, update the _bounds cache structure
|
||||||
|
// before returning
|
||||||
|
if (cache) {
|
||||||
|
if (!this._bounds)
|
||||||
|
this._bounds = {};
|
||||||
|
this._bounds[cache] = bounds.clone();
|
||||||
|
}
|
||||||
|
return bounds;
|
||||||
|
},
|
||||||
|
|
||||||
|
statics: {
|
||||||
|
/**
|
||||||
|
* Clears cached bounds of all items that the children of this item are
|
||||||
|
* contributing to. See #_getCachedBounds() for an explanation why this
|
||||||
|
* information is stored on parents, not the children themselves.
|
||||||
|
*/
|
||||||
|
_clearBoundsCache: function(item) {
|
||||||
|
// This is defined as a static method so Symbol can used it too.
|
||||||
|
if (item._boundsCache) {
|
||||||
|
for (var i = 0, list = item._boundsCache.list, l = list.length;
|
||||||
|
i < l; i++) {
|
||||||
|
var child = list[i];
|
||||||
|
delete child._bounds;
|
||||||
|
// Delete position as well, since it's depending on bounds.
|
||||||
|
delete child._position;
|
||||||
|
// We need to recursively call _clearBoundsCache, because if
|
||||||
|
// the cache for this child's children is not valid anymore,
|
||||||
|
// that propagates up the DOM tree.
|
||||||
|
if (child !== item && child._boundsCache)
|
||||||
|
child._clearBoundsCache();
|
||||||
|
}
|
||||||
|
delete item._boundsCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -94,12 +94,8 @@ var PlacedSymbol = Item.extend(/** @lends PlacedSymbol# */{
|
||||||
},
|
},
|
||||||
|
|
||||||
setSymbol: function(symbol) {
|
setSymbol: function(symbol) {
|
||||||
// Remove from previous symbol's instances
|
|
||||||
if (this._symbol)
|
|
||||||
delete this._symbol._instances[this._id];
|
|
||||||
this._symbol = symbol;
|
this._symbol = symbol;
|
||||||
// Add to the new one's
|
this._changed(/*#=*/ Change.GEOMETRY);
|
||||||
symbol._instances[this._id] = this;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
clone: function(insert) {
|
clone: function(insert) {
|
||||||
|
@ -113,11 +109,10 @@ var PlacedSymbol = Item.extend(/** @lends PlacedSymbol# */{
|
||||||
return this._symbol._definition.isEmpty();
|
return this._symbol._definition.isEmpty();
|
||||||
},
|
},
|
||||||
|
|
||||||
_getBounds: function(getter, matrix) {
|
_getBounds: function(getter, matrix, cacheItem) {
|
||||||
// Redirect the call to the symbol definition to calculate the bounds
|
// Redirect the call to the symbol definition to calculate the bounds
|
||||||
// TODO: Implement bounds caching through passing on of cacheItem, so
|
return this.symbol._definition._getCachedBounds(getter, matrix,
|
||||||
// that Symbol#_changed() notification become unnecessary!
|
cacheItem);
|
||||||
return this.symbol._definition._getCachedBounds(getter, matrix);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_hitTest: function(point, options, matrix) {
|
_hitTest: function(point, options, matrix) {
|
||||||
|
|
|
@ -66,8 +66,6 @@ var Symbol = Base.extend(/** @lends Symbol# */{
|
||||||
this.project.symbols.push(this);
|
this.project.symbols.push(this);
|
||||||
if (item)
|
if (item)
|
||||||
this.setDefinition(item, dontCenter);
|
this.setDefinition(item, dontCenter);
|
||||||
// Hash to keep track of placed instances
|
|
||||||
this._instances = {};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_serialize: function(options, dictionary) {
|
_serialize: function(options, dictionary) {
|
||||||
|
@ -95,11 +93,16 @@ var Symbol = Base.extend(/** @lends Symbol# */{
|
||||||
* @param {ChangeFlag} flags describes what exactly has changed.
|
* @param {ChangeFlag} flags describes what exactly has changed.
|
||||||
*/
|
*/
|
||||||
_changed: function(flags) {
|
_changed: function(flags) {
|
||||||
// Notify all PlacedSymbols of the change in our definition, so they
|
if (flags & /*#=*/ ChangeFlag.GEOMETRY) {
|
||||||
// can clear cached bounds.
|
// Clear cached bounds of all items that this item contributes to.
|
||||||
Base.each(this._instances, function(item) {
|
// We don't call this on the parent, since we're already the parent
|
||||||
item._changed(flags);
|
// of the child that modified the hierarchy (that's where these
|
||||||
});
|
// HIERARCHY notifications go)
|
||||||
|
Item._clearBoundsCache(this);
|
||||||
|
}
|
||||||
|
if (flags & /*#=*/ ChangeFlag.APPEARANCE) {
|
||||||
|
this.project._needsUpdate = true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue