All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scaffold.libs_as.starling.geom.Polygon.as Maven / Gradle / Ivy

// =================================================================================================
//
//	Starling Framework
//	Copyright 2011-2015 Gamua. All Rights Reserved.
//
//	This program is free software. You can redistribute and/or modify it
//	in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================

package starling.geom
{
    import flash.geom.Point;
    import flash.utils.getQualifiedClassName;

    import starling.rendering.IndexData;
    import starling.rendering.VertexData;
    import starling.utils.MathUtil;
    import starling.utils.Pool;

    /** A polygon describes a closed two-dimensional shape bounded by a number of straight
     *  line segments.
     *
     *  

The vertices of a polygon form a closed path (i.e. the last vertex will be connected * to the first). It is recommended to provide the vertices in clockwise order. * Self-intersecting paths are not supported and will give wrong results on triangulation, * area calculation, etc.

*/ public class Polygon { private var _coords:Vector.; // Helper object private static var sRestIndices:Vector. = new []; /** Creates a Polygon with the given coordinates. * @param vertices an array that contains either 'Point' instances or * alternating 'x' and 'y' coordinates. */ public function Polygon(vertices:Array=null) { _coords = new []; addVertices.apply(this, vertices); } /** Creates a clone of this polygon. */ public function clone():Polygon { var clone:Polygon = new Polygon(); var numCoords:int = _coords.length; for (var i:int=0; i 0) { if (args[0] is Point) { for (i=0; i= 0 && index <= numVertices) { _coords[index * 2 ] = x; _coords[index * 2 + 1] = y; } else throw new RangeError("Invalid index: " + index); } /** Returns the coordinates of a certain vertex. */ public function getVertex(index:int, out:Point=null):Point { if (index >= 0 && index < numVertices) { out ||= new Point(); out.setTo(_coords[index * 2], _coords[index * 2 + 1]); return out; } else throw new RangeError("Invalid index: " + index); } /** Figures out if the given coordinates lie within the polygon. */ public function contains(x:Number, y:Number):Boolean { // Algorithm & implementation thankfully taken from: // -> http://alienryderflex.com/polygon/ var i:int, j:int = numVertices - 1; var oddNodes:uint = 0; for (i=0; i= y || jy < y && iy >= y) && (ix <= x || jx <= x)) oddNodes ^= uint(ix + (y - iy) / (jy - iy) * (jx - ix) < x); j = i; } return oddNodes != 0; } /** Figures out if the given point lies within the polygon. */ public function containsPoint(point:Point):Boolean { return contains(point.x, point.y); } /** Calculates a possible representation of the polygon via triangles. The resulting * IndexData instance will reference the polygon vertices as they are saved in this * Polygon instance, optionally incremented by the given offset. * *

If you pass an indexData object, the new indices will be appended to it. * Otherwise, a new instance will be created.

*/ public function triangulate(indexData:IndexData=null, offset:int=0):IndexData { // Algorithm "Ear clipping method" described here: // -> https://en.wikipedia.org/wiki/Polygon_triangulation // // Implementation inspired by: // -> http://polyk.ivank.net var numVertices:int = this.numVertices; var numTriangles:int = this.numTriangles; var i:int, restIndexPos:int, numRestIndices:int; if (indexData == null) indexData = new IndexData(numTriangles * 3); if (numTriangles == 0) return indexData; sRestIndices.length = numVertices; for (i=0; i 3) { // In each step, we look at 3 subsequent vertices. If those vertices spawn up // a triangle that is convex and does not contain any other vertices, it is an 'ear'. // We remove those ears until only one remains -> each ear is one of our wanted // triangles. var otherIndex:uint; var earFound:Boolean = false; var i0:uint = sRestIndices[ restIndexPos % numRestIndices]; var i1:uint = sRestIndices[(restIndexPos + 1) % numRestIndices]; var i2:uint = sRestIndices[(restIndexPos + 2) % numRestIndices]; a.setTo(_coords[2 * i0], _coords[2 * i0 + 1]); b.setTo(_coords[2 * i1], _coords[2 * i1 + 1]); c.setTo(_coords[2 * i2], _coords[2 * i2 + 1]); if (isConvexTriangle(a.x, a.y, b.x, b.y, c.x, c.y)) { earFound = true; for (i = 3; i < numRestIndices; ++i) { otherIndex = sRestIndices[(restIndexPos + i) % numRestIndices]; p.setTo(_coords[2 * otherIndex], _coords[2 * otherIndex + 1]); if (MathUtil.isPointInTriangle(p, a, b, c)) { earFound = false; break; } } } if (earFound) { indexData.addTriangle(i0 + offset, i1 + offset, i2 + offset); sRestIndices.removeAt((restIndexPos + 1) % numRestIndices); numRestIndices--; restIndexPos = 0; } else { restIndexPos++; if (restIndexPos == numRestIndices) break; // no more ears } } Pool.putPoint(a); Pool.putPoint(b); Pool.putPoint(c); Pool.putPoint(p); indexData.addTriangle(sRestIndices[0] + offset, sRestIndices[1] + offset, sRestIndices[2] + offset); return indexData; } /** Copies all vertices to a 'VertexData' instance, beginning at a certain target index. */ public function copyToVertexData(target:VertexData=null, targetVertexID:int=0, attrName:String="position"):void { var numVertices:int = this.numVertices; var requiredTargetLength:int = targetVertexID + numVertices; if (target.numVertices < requiredTargetLength) target.numVertices = requiredTargetLength; for (var i:int=0; i 0) result += "\n"; for (var i:int=0; ib->c is to on the right-hand side of a->b. */ [Inline] private static function isConvexTriangle(ax:Number, ay:Number, bx:Number, by:Number, cx:Number, cy:Number):Boolean { // dot product of [the normal of (a->b)] and (b->c) must be positive return (ay - by) * (cx - bx) + (bx - ax) * (cy - by) >= 0; } /** Finds out if the vector a->b intersects c->d. */ private static function areVectorsIntersecting(ax:Number, ay:Number, bx:Number, by:Number, cx:Number, cy:Number, dx:Number, dy:Number):Boolean { if ((ax == bx && ay == by) || (cx == dx && cy == dy)) return false; // length = 0 var abx:Number = bx - ax; var aby:Number = by - ay; var cdx:Number = dx - cx; var cdy:Number = dy - cy; var tDen:Number = cdy * abx - cdx * aby; if (tDen == 0.0) return false; // parallel or identical var t:Number = (aby * (cx - ax) - abx * (cy - ay)) / tDen; if (t < 0 || t > 1) return false; // outside c->d var s:Number = aby ? (cy - ay + t * cdy) / aby : (cx - ax + t * cdx) / abx; return s >= 0.0 && s <= 1.0; // inside a->b } // properties /** Indicates if the polygon's line segments are not self-intersecting. * Beware: this is a brute-force implementation with O(n^2). */ public function get isSimple():Boolean { var numCoords:int = _coords.length; if (numCoords <= 6) return true; for (var i:int=0; i= 6) { for (var i:int = 0; i < numCoords; i += 2) { area += _coords[i ] * _coords[(i+3) % numCoords]; area -= _coords[i+1] * _coords[(i+2) % numCoords]; } } return area / 2.0; } /** Returns the total number of vertices spawning up the polygon. Assigning a value * that's smaller than the current number of vertices will crop the path; a bigger * value will fill up the path with zeros. */ public function get numVertices():int { return _coords.length / 2; } public function set numVertices(value:int):void { var oldLength:int = numVertices; _coords.length = value * 2; if (oldLength < value) { for (var i:int=oldLength; i < value; ++i) _coords[i * 2] = _coords[i * 2 + 1] = 0.0; } } /** Returns the number of triangles that will be required when triangulating the polygon. */ public function get numTriangles():int { var numVertices:int = this.numVertices; return numVertices >= 3 ? numVertices - 2 : 0; } } } import flash.errors.IllegalOperationError; import flash.utils.getQualifiedClassName; import starling.geom.Polygon; import starling.rendering.IndexData; class ImmutablePolygon extends Polygon { private var _frozen:Boolean; public function ImmutablePolygon(vertices:Array) { super(vertices); _frozen = true; } override public function addVertices(...args):void { if (_frozen) throw getImmutableError(); else super.addVertices.apply(this, args); } override public function setVertex(index:int, x:Number, y:Number):void { if (_frozen) throw getImmutableError(); else super.setVertex(index, x, y); } override public function reverse():void { if (_frozen) throw getImmutableError(); else super.reverse(); } override public function set numVertices(value:int):void { if (_frozen) throw getImmutableError(); else super.reverse(); } private function getImmutableError():Error { var className:String = getQualifiedClassName(this).split("::").pop(); var msg:String = className + " cannot be modified. Call 'clone' to create a mutable copy."; return new IllegalOperationError(msg); } } class Ellipse extends ImmutablePolygon { private var _x:Number; private var _y:Number; private var _radiusX:Number; private var _radiusY:Number; public function Ellipse(x:Number, y:Number, radiusX:Number, radiusY:Number, numSides:int = -1) { _x = x; _y = y; _radiusX = radiusX; _radiusY = radiusY; super(getVertices(numSides)); } private function getVertices(numSides:int):Array { if (numSides < 0) numSides = Math.PI * (_radiusX + _radiusY) / 4.0; if (numSides < 6) numSides = 6; var vertices:Array = []; var angleDelta:Number = 2 * Math.PI / numSides; var angle:Number = 0; for (var i:int=0; i= _x && x <= _x + _width && y >= _y && y <= _y + _height; } override public function get area():Number { return _width * _height; } override public function get isSimple():Boolean { return true; } override public function get isConvex():Boolean { return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy