From dd4269d61b134fc80445011a8bc993d826ef6747 Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Sun, 4 Nov 2012 12:42:28 -0800 Subject: [PATCH] Add Sprite.getBounds. --- src/flambe/display/Sprite.hx | 74 ++++++++++++++++++++++++++++++++++ src/flambe/math/Rectangle.hx | 29 +++++++++++++ src/flambe/swf/BitmapSprite.hx | 12 ++---- src/flambe/swf/MovieSprite.hx | 12 +++--- 4 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 src/flambe/math/Rectangle.hx diff --git a/src/flambe/display/Sprite.hx b/src/flambe/display/Sprite.hx index a3fbb24e..097b105a 100644 --- a/src/flambe/display/Sprite.hx +++ b/src/flambe/display/Sprite.hx @@ -10,6 +10,7 @@ import flambe.input.PointerEvent; import flambe.math.FMath; import flambe.math.Matrix; import flambe.math.Point; +import flambe.math.Rectangle; import flambe.scene.Director; import flambe.util.Signal1; import flambe.util.Value; @@ -157,6 +158,26 @@ class Sprite extends Component return (sprite != null && sprite.containsLocal(x, y)) ? sprite : null; } + /** + * Calculate the bounding box of an entity hierarchy. Returns the smallest rectangle in local + * coordinates that fully encloses all child sprites. + */ + public static function getBounds (entity :Entity, ?result :Rectangle) :Rectangle + { + if (result == null) { + result = new Rectangle(); + } + + // The width and height of this rectangle are hijacked to store the bottom right corner + result.set(FMath.FLOAT_MAX, FMath.FLOAT_MAX, FMath.FLOAT_MIN, FMath.FLOAT_MIN); + getBoundsImpl(entity, null, result); + + // Convert back to a true width and height + result.width -= result.x; + result.height -= result.y; + return result; + } + /** * The "natural" width of this sprite, without any transformations being applied. Used for hit * testing. @@ -381,6 +402,59 @@ class Sprite extends Component return pointerEnabled; } + private static function getBoundsImpl (entity :Entity, matrix :Matrix, result :Rectangle) + { + var sprite = entity.get(Sprite); + if (sprite != null) { + matrix = (matrix != null) + ? Matrix.multiply(matrix, sprite.getLocalMatrix()) // Allocation! + : sprite.getLocalMatrix(); + + // Extend the rectangle out to fit this sprite + var width = sprite.getNaturalWidth(), height = sprite.getNaturalHeight(); + extendRect(matrix, 0, 0, result); + extendRect(matrix, width, 0, result); + extendRect(matrix, width, height, result); + extendRect(matrix, 0, height, result); + } + + // Recurse into all visible director scenes + var director = entity.get(Director); + if (director != null) { + var scenes = director.visibleScenes; + var ii = 0, ll = scenes.length; + while (ii < ll) { + getBoundsImpl(scenes[ii], matrix, result); + ++ii; + } + } + + // Recurse into all children + var children = entity._internal_children; + var ii = 0, ll = children.length; + while (ii < ll) { + var child = children[ii]; + if (child != null) { + getBoundsImpl(child, matrix, result); + } + ++ii; + } + } + + private static function extendRect (matrix :Matrix, x :Float, y :Float, rect :Rectangle) + { + var p = matrix.transform(x, y, _scratchPoint); + x = p.x; + y = p.y; + + // The width and height of the rectangle are treated like the bottom right point, rather + // than a true width and height offset + if (x < rect.x) rect.x = x; + if (y < rect.y) rect.y = y; + if (x > rect.width) rect.width = x; + if (y > rect.height) rect.height = y; + } + private static var _scratchPoint = new Point(); // Various flags used by Sprite and subclasses diff --git a/src/flambe/math/Rectangle.hx b/src/flambe/math/Rectangle.hx new file mode 100644 index 00000000..8fee8790 --- /dev/null +++ b/src/flambe/math/Rectangle.hx @@ -0,0 +1,29 @@ +// +// Flambe - Rapid game development +// https://github.com/aduros/flambe/blob/master/LICENSE.txt + +package flambe.math; + +/** + * A 2D rectangle. + */ +class Rectangle +{ + public var x :Float; + public var y :Float; + public var width :Float; + public var height :Float; + + public function new (x :Float = 0, y :Float = 0, width :Float = 0, height :Float = 0) + { + set(x, y, width, height); + } + + public function set (x :Float, y :Float, width :Float, height :Float) + { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } +} diff --git a/src/flambe/swf/BitmapSprite.hx b/src/flambe/swf/BitmapSprite.hx index 685e0bf9..b65b85fb 100644 --- a/src/flambe/swf/BitmapSprite.hx +++ b/src/flambe/swf/BitmapSprite.hx @@ -21,19 +21,13 @@ class BitmapSprite extends Sprite { super(); this.symbol = symbol; + anchorX._ = symbol.anchorX; + anchorY._ = symbol.anchorY; } override public function draw (ctx :DrawingContext) { - ctx.drawSubImage(symbol.atlas, -symbol.anchorX, -symbol.anchorY, - symbol.x, symbol.y, symbol.width, symbol.height); - } - - override public function containsLocal (localX :Float, localY :Float) - { - // We can't set the _anchorX/Y properties, since they're modified by LayerAnimator, so - // instead they have to be handled specially when drawing and hit testing - return super.containsLocal(localX+symbol.anchorX, localY+symbol.anchorY); + ctx.drawSubImage(symbol.atlas, 0, 0, symbol.x, symbol.y, symbol.width, symbol.height); } override public function getNaturalWidth () :Float diff --git a/src/flambe/swf/MovieSprite.hx b/src/flambe/swf/MovieSprite.hx index 128ce809..2bc8c418 100644 --- a/src/flambe/swf/MovieSprite.hx +++ b/src/flambe/swf/MovieSprite.hx @@ -260,19 +260,19 @@ private class LayerAnimator alpha += (nextKf.alpha-alpha) * interp; } - // From an identity matrix, append the skew + // From an identity matrix, append the translation, then skew var matrix = sprite.getLocalMatrix(); var sinX = Math.sin(skewX), cosX = Math.cos(skewX); var sinY = Math.sin(skewY), cosY = Math.cos(skewY); - matrix.set(cosY, sinY, -sinX, cosX, 0, 0); + matrix.set(cosY, sinY, -sinX, cosX, x, y); // Append the scale matrix.scale(scaleX, scaleY); - // Append the translation, and prepend the pivot - var pivotX = kf.pivotX, pivotY = kf.pivotY; - matrix.m02 = x - matrix.m00*pivotX - matrix.m01*pivotY; - matrix.m12 = y - matrix.m10*pivotY - matrix.m11*pivotY; + // Append the pivot + var pivotX = kf.pivotX + sprite.anchorX._; + var pivotY = kf.pivotY + sprite.anchorY._; + matrix.translate(-pivotX, -pivotY); sprite.alpha._ = alpha; }