From 8cec4eb354bb3718ea48c4a4dd2bd13ca870c0fa Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Tue, 24 Sep 2013 15:37:46 +0200 Subject: [PATCH] Simplified SkeletonBounds AABB computation. --- spine-csharp/src/SkeletonBounds.cs | 124 ++++-------------- .../spine/SkeletonBounds.java | 66 +++------- .../spine/SkeletonRendererDebug.java | 2 +- spine-xna/example/src/ExampleGame.cs | 2 +- spine-xna/src/SkeletonRenderer.cs | 2 +- 5 files changed, 50 insertions(+), 146 deletions(-) diff --git a/spine-csharp/src/SkeletonBounds.cs b/spine-csharp/src/SkeletonBounds.cs index 5b2d466ae..40bde6b95 100644 --- a/spine-csharp/src/SkeletonBounds.cs +++ b/spine-csharp/src/SkeletonBounds.cs @@ -36,78 +36,24 @@ using System.Collections.Generic; namespace Spine { public class SkeletonBounds { - private bool aabb; private List polygonPool = new List(); + private float minX, minY, maxX, maxY; public List BoundingBoxes { get; private set; } public List Polygons { get; private set; } - - private float minX; - public float MinX { - get { - if (!aabb) aabbCompute(); - return minX; - } - private set { - minX = value; - } - } - - private float maxX; - public float MaxX { - get { - if (!aabb) aabbCompute(); - return maxX; - } - private set { - maxX = value; - } - } - - private float minY; - public float MinY { - get { - if (!aabb) aabbCompute(); - return minY; - } - private set { - minY = value; - } - } - - private float maxY; - public float MaxY { - get { - if (!aabb) aabbCompute(); - return maxY; - } - private set { - maxY = value; - } - } - - public float Width { - get { - if (!aabb) aabbCompute(); - return maxX - minX; - } - } - - public float Height { - get { - if (!aabb) aabbCompute(); - return maxY - minY; - } - } + public float MinX { get { return minX; } set { minX = value; } } + public float MinY { get { return minY; } set { minY = value; } } + public float MaxX { get { return maxX; } set { maxX = value; } } + public float MaxY { get { return maxY; } set { maxY = value; } } + public float Width { get { return maxX - minX; } } + public float Height { get { return maxY - minY; } } public SkeletonBounds () { BoundingBoxes = new List(); Polygons = new List(); } - public void Update (Skeleton skeleton) { - aabb = false; - + public void Update (Skeleton skeleton, bool updateAabb) { List boundingBoxes = BoundingBoxes; List polygons = Polygons; List slots = skeleton.slots; @@ -139,6 +85,8 @@ namespace Spine { if (polygon.Vertices.Length < count) polygon.Vertices = new float[count]; boundingBox.ComputeWorldVertices(x, y, slot.bone, polygon.Vertices); } + + if (updateAabb) aabbCompute(); } private void aabbCompute () { @@ -160,19 +108,16 @@ namespace Spine { this.minY = minY; this.maxX = maxX; this.maxY = maxY; - aabb = true; } /** Returns true if the axis aligned bounding box contains the point. */ public bool AabbContainsPoint (float x, float y) { - if (!aabb) aabbCompute(); return x >= minX && x <= maxX && y >= minY && y <= maxY; } /** Returns true if the axis aligned bounding box intersects the line segment. */ public bool AabbIntersectsSegment (float x1, float y1, float x2, float y2) { - if (!aabb) aabbCompute(); float minX = this.minX; float minY = this.minY; float maxX = this.maxX; @@ -193,14 +138,11 @@ namespace Spine { /** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */ public bool AabbIntersectsSkeleton (SkeletonBounds bounds) { - if (!aabb) aabbCompute(); - if (!bounds.aabb) bounds.aabbCompute(); return minX < bounds.maxX && maxX > bounds.minX && minY < bounds.maxY && maxY > bounds.minY; } - /** Returns true if the bounding box attachment contains the point. */ - public bool ContainsPoint (int index, float x, float y) { - Polygon polygon = Polygons[index]; + /** Returns true if the polygon contains the point. */ + public bool ContainsPoint (Polygon polygon, float x, float y) { float[] vertices = polygon.Vertices; int nn = polygon.Count; @@ -209,7 +151,7 @@ namespace Spine { for (int ii = 0; ii < nn; ii += 2) { float vertexY = vertices[ii + 1]; float prevY = vertices[prevIndex + 1]; - if (vertexY < y && prevY >= y || prevY < y && vertexY >= y) { + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { float vertexX = vertices[ii]; if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside; } @@ -219,40 +161,25 @@ namespace Spine { } /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more - * efficient to only call this method if {@link #aabbContainsPoint(float, float)} return true. */ + * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */ public BoundingBoxAttachment ContainsPoint (float x, float y) { - List boundingBoxes = BoundingBoxes; - for (int i = 0, n = boundingBoxes.Count; i < n; i++) - if (ContainsPoint(i, x, y)) return boundingBoxes[i]; + List polygons = Polygons; + for (int i = 0, n = polygons.Count; i < n; i++) + if (ContainsPoint(polygons[i], x, y)) return BoundingBoxes[i]; return null; } - /** Returns true if the bounding box attachment contains the point. The bounding box must be in the SkeletonBounds. */ - public bool containsPoint (BoundingBoxAttachment attachment, float x, float y) { - int index = BoundingBoxes.IndexOf(attachment); - return index == -1 ? false : ContainsPoint(index, x, y); - } - /** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually - * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} return true. */ + * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true. */ public BoundingBoxAttachment IntersectsSegment (float x1, float y1, float x2, float y2) { - List boundingBoxes = BoundingBoxes; - for (int i = 0, n = boundingBoxes.Count; i < n; i++) { - BoundingBoxAttachment attachment = boundingBoxes[i]; - if (IntersectsSegment(attachment, x1, y1, x2, y2)) return attachment; - } + List polygons = Polygons; + for (int i = 0, n = polygons.Count; i < n; i++) + if (IntersectsSegment(polygons[i], x1, y1, x2, y2)) return BoundingBoxes[i]; return null; } - /** Returns true if the bounding box attachment contains the line segment. */ - public bool IntersectsSegment (BoundingBoxAttachment attachment, float x1, float y1, float x2, float y2) { - int index = BoundingBoxes.IndexOf(attachment); - return index == -1 ? false : IntersectsSegment(index, x1, y1, x2, y2); - } - - /** Returns true if the bounding box attachment contains the line segment. */ - public bool IntersectsSegment (int index, float x1, float y1, float x2, float y2) { - Polygon polygon = Polygons[index]; + /** Returns true if the polygon contains the line segment. */ + public bool IntersectsSegment (Polygon polygon, float x1, float y1, float x2, float y2) { float[] vertices = polygon.Vertices; int nn = polygon.Count; @@ -274,6 +201,11 @@ namespace Spine { } return false; } + + public Polygon getPolygon (BoundingBoxAttachment attachment) { + int index = BoundingBoxes.IndexOf(attachment); + return index == -1 ? null : Polygons[index]; + } } } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java index a218c78cd..94a99d905 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java @@ -40,14 +40,11 @@ import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.FloatArray; public class SkeletonBounds { - private boolean aabb; private float minX, minY, maxX, maxY; private Array boundingBoxes = new Array(); private Array polygons = new Array(); - public void update (Skeleton skeleton) { - aabb = false; - + public void update (Skeleton skeleton, boolean updateAabb) { Array boundingBoxes = this.boundingBoxes; Array polygons = this.polygons; Array slots = skeleton.slots; @@ -75,6 +72,8 @@ public class SkeletonBounds { boundingBox.computeWorldVertices(x, y, slot.bone, polygon.items); } } + + if (updateAabb) aabbCompute(); } private void aabbCompute () { @@ -96,18 +95,15 @@ public class SkeletonBounds { this.minY = minY; this.maxX = maxX; this.maxY = maxY; - aabb = true; } /** Returns true if the axis aligned bounding box contains the point. */ public boolean aabbContainsPoint (float x, float y) { - if (!aabb) aabbCompute(); return x >= minX && x <= maxX && y >= minY && y <= maxY; } /** Returns true if the axis aligned bounding box intersects the line segment. */ public boolean aabbIntersectsSegment (float x1, float y1, float x2, float y2) { - if (!aabb) aabbCompute(); float minX = this.minX; float minY = this.minY; float maxX = this.maxX; @@ -128,14 +124,20 @@ public class SkeletonBounds { /** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */ public boolean aabbIntersectsSkeleton (SkeletonBounds bounds) { - if (!aabb) aabbCompute(); - if (!bounds.aabb) bounds.aabbCompute(); return minX < bounds.maxX && maxX > bounds.minX && minY < bounds.maxY && maxY > bounds.minY; } + /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more + * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */ + public BoundingBoxAttachment containsPoint (float x, float y) { + Array polygons = this.polygons; + for (int i = 0, n = polygons.size; i < n; i++) + if (containsPoint(polygons.get(i), x, y)) return boundingBoxes.get(i); + return null; + } + /** Returns true if the bounding box attachment contains the point. */ - public boolean containsPoint (int index, float x, float y) { - FloatArray polygon = polygons.get(index); + public boolean containsPoint (FloatArray polygon, float x, float y) { float[] vertices = polygon.items; int nn = polygon.size; @@ -144,7 +146,7 @@ public class SkeletonBounds { for (int ii = 0; ii < nn; ii += 2) { float vertexY = vertices[ii + 1]; float prevY = vertices[prevIndex + 1]; - if (vertexY < y && prevY >= y || prevY < y && vertexY >= y) { + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { float vertexX = vertices[ii]; if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside; } @@ -153,41 +155,17 @@ public class SkeletonBounds { return inside; } - /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more - * efficient to only call this method if {@link #aabbContainsPoint(float, float)} return true. */ - public BoundingBoxAttachment containsPoint (float x, float y) { - Array boundingBoxes = this.boundingBoxes; - for (int i = 0, n = boundingBoxes.size; i < n; i++) - if (containsPoint(i, x, y)) return boundingBoxes.get(i); - return null; - } - - /** Returns true if the bounding box attachment contains the point. The bounding box must be in the SkeletonBounds. */ - public boolean containsPoint (BoundingBoxAttachment attachment, float x, float y) { - int index = boundingBoxes.indexOf(attachment, true); - return index == -1 ? false : containsPoint(index, x, y); - } - /** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually - * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} return true. */ + * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true. */ public BoundingBoxAttachment intersectsSegment (float x1, float y1, float x2, float y2) { - Array boundingBoxes = this.boundingBoxes; - for (int i = 0, n = boundingBoxes.size; i < n; i++) { - BoundingBoxAttachment attachment = boundingBoxes.get(i); - if (intersectsSegment(attachment, x1, y1, x2, y2)) return attachment; - } + Array polygons = this.polygons; + for (int i = 0, n = polygons.size; i < n; i++) + if (intersectsSegment(polygons.get(i), x1, y1, x2, y2)) return boundingBoxes.get(i); return null; } /** Returns true if the bounding box attachment contains the line segment. */ - public boolean intersectsSegment (BoundingBoxAttachment attachment, float x1, float y1, float x2, float y2) { - int index = boundingBoxes.indexOf(attachment, true); - return index == -1 ? false : intersectsSegment(index, x1, y1, x2, y2); - } - - /** Returns true if the bounding box attachment contains the line segment. */ - public boolean intersectsSegment (int index, float x1, float y1, float x2, float y2) { - FloatArray polygon = polygons.get(index); + public boolean intersectsSegment (FloatArray polygon, float x1, float y1, float x2, float y2) { float[] vertices = polygon.items; int nn = polygon.size; @@ -211,32 +189,26 @@ public class SkeletonBounds { } public float getMinX () { - if (!aabb) aabbCompute(); return minX; } public float getMinY () { - if (!aabb) aabbCompute(); return minY; } public float getMaxX () { - if (!aabb) aabbCompute(); return maxX; } public float getMaxY () { - if (!aabb) aabbCompute(); return maxY; } public float getWidth () { - if (!aabb) aabbCompute(); return maxX - minX; } public float getHeight () { - if (!aabb) aabbCompute(); return maxY - minY; } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java index 4ff9ce604..ce91b9e8b 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java @@ -101,7 +101,7 @@ public class SkeletonRendererDebug { if (drawBoundingBoxes) { SkeletonBounds bounds = this.bounds; - bounds.update(skeleton); + bounds.update(skeleton, true); renderer.setColor(aabbColor); renderer.rect(bounds.getMinX(), bounds.getMinY(), bounds.getWidth(), bounds.getHeight()); renderer.setColor(boundingBoxColor); diff --git a/spine-xna/example/src/ExampleGame.cs b/spine-xna/example/src/ExampleGame.cs index 62168dfab..2185a2eb8 100644 --- a/spine-xna/example/src/ExampleGame.cs +++ b/spine-xna/example/src/ExampleGame.cs @@ -133,7 +133,7 @@ namespace Spine { skeletonRenderer.Draw(skeleton); skeletonRenderer.End(); - bounds.Update(skeleton); + bounds.Update(skeleton, true); MouseState mouse = Mouse.GetState(); headSlot.G = 1; headSlot.B = 1; diff --git a/spine-xna/src/SkeletonRenderer.cs b/spine-xna/src/SkeletonRenderer.cs index 2a294412e..d2adcf934 100644 --- a/spine-xna/src/SkeletonRenderer.cs +++ b/spine-xna/src/SkeletonRenderer.cs @@ -116,7 +116,7 @@ namespace Spine { item.vertexTR.Color.A = a; float[] vertices = this.vertices; - regionAttachment.ComputeVertices(x, y, slot.Bone, vertices); + regionAttachment.ComputeWorldVertices(x, y, slot.Bone, vertices); item.vertexTL.Position.X = vertices[RegionAttachment.X1]; item.vertexTL.Position.Y = vertices[RegionAttachment.Y1]; item.vertexTL.Position.Z = 0;