diff --git a/spine-as3/spine-as3/src/spine/Polygon.as b/spine-as3/spine-as3/src/spine/Polygon.as new file mode 100644 index 000000000..644f3f70c --- /dev/null +++ b/spine-as3/spine-as3/src/spine/Polygon.as @@ -0,0 +1,82 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Essential, Professional, Enterprise, or Education License must + * be purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer, in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package spine { + +public class Polygon { + public var vertices:Vector. = new Vector.(); + + /// Returns true if the polygon contains the line segment. + public function intersectsSegment (x1:Number, y1:Number, x2:Number, y2:Number) : Boolean { + var nn:int = vertices.length; + + var width12:Number = x1 - x2, height12:Number = y1 - y2; + var det1:Number = x1 * y2 - y1 * x2; + var x3:Number = vertices[nn - 2], y3:Number = vertices[nn - 1]; + for (var ii:int = 0; ii < nn; ii += 2) { + var x4:Number = vertices[ii], y4:Number = vertices[ii + 1]; + var det2:Number = x3 * y4 - y3 * x4; + var width34:Number = x3 - x4, height34:Number = y3 - y4; + var det3:Number = width12 * height34 - height12 * width34; + var x:Number = (det1 * width34 - width12 * det2) / det3; + if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) { + var y:Number = (det1 * height34 - height12 * det2) / det3; + if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true; + } + x3 = x4; + y3 = y4; + } + return false; + } + + /// Returns true if the polygon contains the point. + public function containsPoint (x:Number, y:Number) : Boolean { + var nn:int = vertices.length; + + var prevIndex:int = nn - 2; + var inside:Boolean = false; + for (var ii:int = 0; ii < nn; ii += 2) { + var vertexY:Number = vertices[ii + 1]; + var prevY:Number = vertices[prevIndex + 1]; + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { + var vertexX:Number = vertices[ii]; + if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside; + } + prevIndex = ii; + } + + return inside; + } +} + +} diff --git a/spine-as3/spine-as3/src/spine/SkeletonBounds.as b/spine-as3/spine-as3/src/spine/SkeletonBounds.as new file mode 100644 index 000000000..49307bdb5 --- /dev/null +++ b/spine-as3/spine-as3/src/spine/SkeletonBounds.as @@ -0,0 +1,153 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Essential, Professional, Enterprise, or Education License must + * be purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer, in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package spine { +import spine.attachments.Attachment; +import spine.attachments.BoundingBoxAttachment; + +public class SkeletonBounds { + private var polygonPool:Vector. = new Vector.(); + + public var boundingBoxes:Vector. = new Vector.(); + public var polygons:Vector. = new Vector.(); + public var minX:Number, minY:Number, maxX:Number, maxY:Number; + + public function update (skeleton:Skeleton, updateAabb:Boolean) : void { + var slots:Vector. = skeleton.slots; + var slotCount:int = slots.length; + var x:Number = skeleton.x, y:Number = skeleton.y; + + boundingBoxes.length = 0; + for each (var polygon:Polygon in polygons) + polygonPool.push(polygon); + polygons.length = 0; + + for (var i:int = 0; i < slotCount; i++) { + var slot:Slot = slots[i]; + var boundingBox:BoundingBoxAttachment = slot.attachment as BoundingBoxAttachment; + if (boundingBox == null) continue; + boundingBoxes.push(boundingBox); + + var poolCount:int = polygonPool.length; + if (poolCount > 0) { + polygon = polygonPool[poolCount - 1]; + polygonPool.splice(poolCount - 1, 1); + } else + polygon = new Polygon(); + polygons.push(polygon); + + polygon.vertices.length = boundingBox.vertices.length; + boundingBox.computeWorldVertices(x, y, slot.bone, polygon.vertices); + } + + if (updateAabb) aabbCompute(); + } + + private function aabbCompute () : void { + var minX:Number = int.MAX_VALUE, minY:Number = int.MAX_VALUE, maxX:Number = int.MIN_VALUE, maxY:Number = int.MIN_VALUE; + for (var i:int = 0, n:int = polygons.length; i < n; i++) { + var polygon:Polygon = polygons[i]; + var vertices:Vector. = polygon.vertices; + for (var ii:int = 0, nn:int = vertices.length; ii < nn; ii += 2) { + var x:Number = vertices[ii]; + var y:Number = vertices[ii + 1]; + minX = Math.min(minX, x); + minY = Math.min(minY, y); + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); + } + } + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + } + + + /// Returns true if the axis aligned bounding box contains the point. + public function aabbContainsPoint (x:Number, y:Number) : Boolean { + return x >= minX && x <= maxX && y >= minY && y <= maxY; + } + + /// Returns true if the axis aligned bounding box intersects the line segment. + public function aabbIntersectsSegment (x1:Number, y1:Number, x2:Number, y2:Number) : Boolean { + if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY)) + return false; + var m:Number = (y2 - y1) / (x2 - x1); + var y:Number = m * (minX - x1) + y1; + if (y > minY && y < maxY) return true; + y = m * (maxX - x1) + y1; + if (y > minY && y < maxY) return true; + var x:Number = (minY - y1) / m + x1; + if (x > minX && x < maxX) return true; + x = (maxY - y1) / m + x1; + if (x > minX && x < maxX) return true; + return false; + } + + /// Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. + public function aabbIntersectsSkeleton (bounds:SkeletonBounds) : Boolean { + 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 function containsPoint (x:Number, y:Number) : BoundingBoxAttachment { + for (var i:int = 0, n:int = polygons.length; i < n; i++) + if (polygons[i].containsPoint(x, y)) return boundingBoxes[i]; + return null; + } + + /// 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)} returns true. + public function intersectsSegment (x1:Number, y1:Number, x2:Number, y2:Number) : BoundingBoxAttachment { + for (var i:int = 0, n:int = polygons.length; i < n; i++) + if (polygons[i].intersectsSegment(x1, y1, x2, y2)) return boundingBoxes[i]; + return null; + } + + public function getPolygon (attachment:BoundingBoxAttachment) : Polygon { + var index:int = boundingBoxes.indexOf(attachment); + return index == -1 ? null : polygons[index]; + } + + public function get width () : Number { + return maxX - minX; + } + + public function get height () : Number { + return maxY - minY; + } +} + +} diff --git a/spine-as3/spine-as3/src/spine/SkeletonJson.as b/spine-as3/spine-as3/src/spine/SkeletonJson.as index f80628a63..126298d0a 100644 --- a/spine-as3/spine-as3/src/spine/SkeletonJson.as +++ b/spine-as3/spine-as3/src/spine/SkeletonJson.as @@ -47,6 +47,7 @@ import spine.animation.TranslateTimeline; import spine.attachments.Attachment; import spine.attachments.AttachmentLoader; import spine.attachments.AttachmentType; +import spine.attachments.BoundingBoxAttachment; import spine.attachments.RegionAttachment; public class SkeletonJson { @@ -181,6 +182,10 @@ public class SkeletonJson { regionAttachment.width = (map["width"] || 32) * scale; regionAttachment.height = (map["height"] || 32) * scale; regionAttachment.updateOffset(); + } else if (attachment is BoundingBoxAttachment) { + var box:BoundingBoxAttachment = attachment as BoundingBoxAttachment; + for each (var point:Number in map["vertices"]) + box.vertices.push(point * scale); } return attachment;