Big refactoring of the way bounds are handled and cached, properly supporting Groups inside Symbols, and also adding #roughBounds, to be used by upcoming hit testing code.

This commit is contained in:
Jürg Lehni 2011-07-04 19:45:53 +02:00
parent 10b0062ce2
commit 45a3e7f873
5 changed files with 94 additions and 35 deletions

View file

@ -1042,8 +1042,8 @@ var Item = this.Item = Base.extend(/** @lends Item# */{
* @type Rectangle
* @bean
*/
getBounds: function() {
return this._getBounds('getBounds');
getBounds: function(/* matrix */) {
return this._getBounds('getBounds', '_bounds', arguments);
},
setBounds: function(rect) {
@ -1073,8 +1073,8 @@ var Item = this.Item = Base.extend(/** @lends Item# */{
* @type Rectangle
* @bean
*/
getStrokeBounds: function() {
return this._getBounds('getStrokeBounds');
getStrokeBounds: function(/* matrix */) {
return this._getBounds('getStrokeBounds', '_strokeBounds', arguments);
},
/**
@ -1083,15 +1083,30 @@ var Item = this.Item = Base.extend(/** @lends Item# */{
* @type Rectangle
* @bean
*/
getHandleBounds: function() {
return this._getBounds('getHandleBounds');
getHandleBounds: function(/* matrix */) {
return this._getBounds('getHandleBounds', '_handleBounds', arguments);
},
/**
* The rough bounding rectangle of the item that is shure to include all of
* the drawing, including stroke width.
*
* @type Rectangle
* @bean
* @ignore
*/
getRoughBounds: function(/* matrix */) {
return this._getBounds('getRoughBounds', '_roughBounds', arguments);
},
/**
* Loops through all children, gets their bounds and finds the bounds around
* all of them.
*/
_getBounds: function(getter) {
_getBounds: function(getter, cacheName, args) {
// Note: We cannot cache these results here, since we do not get
// _changed() notifications here for changing geometry in children.
// But cacheName is used in sub-classes such as PlacedItem.
var children = this._children;
// TODO: What to return if nothing is defined, e.g. empty Groups?
// Scriptographer behaves weirdly then too.
@ -1104,7 +1119,7 @@ var Item = this.Item = Base.extend(/** @lends Item# */{
for (var i = 0, l = children.length; i < l; i++) {
var child = children[i];
if (child._visible) {
var rect = child[getter]();
var rect = child[getter](args[0]);
x1 = Math.min(rect.x, x1);
y1 = Math.min(rect.y, y1);
x2 = Math.max(rect.x + rect.width, x2);
@ -1535,6 +1550,8 @@ var Item = this.Item = Base.extend(/** @lends Item# */{
* path.fitBounds(view.bounds);
*/
fitBounds: function(rectangle, fill) {
// TODO: Think about passing options with various ways of defining
// fitting.
rectangle = Rectangle.read(arguments);
var bounds = this.getBounds(),
itemRatio = bounds.height / bounds.width,

View file

@ -32,6 +32,17 @@ var PlacedItem = this.PlacedItem = Item.extend(/** @lends PlacedItem# */{
this._matrix.preConcatenate(matrix);
},
_changed: function(flags) {
// Don't use base() for reasons of performance.
Item.prototype._changed.call(this, flags);
if (flags & ChangeFlag.GEOMETRY) {
delete this._strokeBounds;
// TODO: These are not used in Raster. Do we mind?
delete this._handleBounds;
delete this._roughBounds;
}
},
/**
* The item's transformation matrix, defining position and dimensions in the
* document.
@ -48,7 +59,30 @@ var PlacedItem = this.PlacedItem = Item.extend(/** @lends PlacedItem# */{
this._changed(Change.GEOMETRY);
},
getStrokeBounds: function() {
return this.getBounds();
getBounds: function(/* matrix */) {
// The bounds of PlacedItems are the same as the strokeBounds, but are
// wrapped in a LinkedRectangle that catch changes for us.
var useCache = arguments[0] === undefined;
if (useCache && this._bounds)
return this._bounds;
var bounds = this.getStrokeBounds(arguments[0]);
if (useCache)
bounds = this._bounds = this._createBounds(bounds);
return bounds;
},
_getBounds: function(getter, cacheName, args) {
var matrix = args[0],
useCache = matrix === undefined;
if (useCache && this[cacheName])
return this[cacheName];
matrix = matrix ? matrix.clone().concatenate(this._matrix)
: this._matrix;
// Call _calculateBounds, which needs to be defined in the subclasses:
var bounds = this._calculateBounds(getter, matrix);
// TODO: Clear cache
if (useCache)
this[cacheName] = bounds;
return bounds;
}
});

View file

@ -71,6 +71,9 @@ var PlacedSymbol = this.PlacedSymbol = PlacedItem.extend(/** @lends PlacedSymbol
: new Matrix();
},
// TODO: Symbols need to register their placed instances, so whenever a
// symbol definition changes, all instances are notified through _changed()
/**
* The symbol that the placed symbol refers to:
*
@ -82,12 +85,9 @@ var PlacedSymbol = this.PlacedSymbol = PlacedItem.extend(/** @lends PlacedSymbol
return this._clone(new PlacedSymbol(this.symbol, this._matrix.clone()));
},
getBounds: function() {
if (!this._bounds) {
this._bounds = this._createBounds(
this.symbol._definition.getStrokeBounds(this._matrix));
}
return this._bounds;
_calculateBounds: function(getter, matrix) {
// Ask the symbol definition to calculate the bounds for us
return this.symbol._definition[getter](matrix);
},
draw: function(ctx, param) {

View file

@ -368,11 +368,21 @@ var Raster = this.Raster = PlacedItem.extend(/** @lends Raster# */{
this.getContext(true).putImageData(data, point.x, point.y);
},
getBounds: function() {
if (!this._bounds)
this._bounds = this._createBounds(this._matrix._transformBounds(
new Rectangle(this._size).setCenter(0, 0)));
return this._bounds;
_calculateBounds: function(getter, matrix) {
// The getter is only used for PlacedSymbol, not Raster
return matrix._transformBounds(
new Rectangle(this._size).setCenter(0, 0));
},
// Since Raster doesn't make the distinction between the different bounds,
// simply redirect to strokeBounds so the cached values can be reused.
getHandleBounds: function(/* matrix */) {
return this.getStrokeBounds(arguments[0]);
},
getRoughBounds: function(/* matrix */) {
return this.getStrokeBounds(arguments[0]);
},
draw: function(ctx, param) {

View file

@ -1787,16 +1787,15 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
* @ignore
*/
getBounds: function(/* matrix */) {
var useCache = arguments.length == 0;
var useCache = arguments[0] === undefined;
// Pass the matrix hidden from Bootstrap, so it still inject
// getBounds as bean too.
if (!useCache || !this._bounds) {
var bounds = this._createBounds(getBounds(this, arguments[0]));
if (useCache)
this._bounds = bounds;
return bounds;
}
return this._bounds;
if (useCache && this._bounds)
return this._bounds;
var bounds = this._createBounds(getBounds(this, arguments[0]));
if (useCache)
this._bounds = bounds;
return bounds;
},
/**
@ -1807,8 +1806,8 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
getStrokeBounds: function(/* matrix */) {
if (!this._style._strokeColor || !this._style._strokeWidth)
return this.getBounds.apply(this, arguments);
var useCache = arguments.length == 0;
if (this._strokeBounds && useCache)
var useCache = arguments[0] === undefined;
if (useCache && this._strokeBounds)
return this._strokeBounds;
var matrix = arguments[0], // set #getBounds()
width = this.getStrokeWidth(),
@ -1915,7 +1914,7 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
getHandleBounds: function(/* matrix, stroke, join */) {
var matrix = arguments[0],
useCache = matrix === undefined;
if (this._handleBounds && useCache)
if (useCache && this._handleBounds)
return this._handleBounds;
var coords = new Array(6),
stroke = arguments[1] / 2 || 0, // Stroke padding
@ -1963,9 +1962,8 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
* @ignore
*/
getRoughBounds: function(/* matrix */) {
var matrix = arguments[0],
useCache = matrix === undefined;
if (this._roughBounds && useCache)
var useCache = arguments[0] === undefined;
if (useCache && this._roughBounds)
return this._roughBounds;
// Delegate to #getHandleBounds(), but pass on radius values for
// stroke and joins. Hanlde miter joins specially, by passing the