using System; using System.Collections.Generic; using System.Diagnostics; using Microsoft.Xna.Framework; namespace FarseerPhysics.Common { public static class PolygonTools { /// /// Build vertices to represent an axis-aligned box. /// /// the half-width. /// the half-height. public static Vertices CreateRectangle(float hx, float hy) { Vertices vertices = new Vertices(4); vertices.Add(new Vector2(-hx, -hy)); vertices.Add(new Vector2(hx, -hy)); vertices.Add(new Vector2(hx, hy)); vertices.Add(new Vector2(-hx, hy)); return vertices; } public static Vertices CreateTriangle(float hx, float hy) { Vertices vertices = new Vertices(3); vertices.Add(new Vector2(hx/2, hy)); vertices.Add(new Vector2(hx, 0)); vertices.Add(new Vector2(0, 0)); return vertices; } /// /// Build vertices to represent an oriented box. /// /// the half-width. /// the half-height. /// the center of the box in local coordinates. /// the rotation of the box in local coordinates. public static Vertices CreateRectangle(float hx, float hy, Vector2 center, float angle) { Vertices vertices = CreateRectangle(hx, hy); Transform xf = new Transform(); xf.Position = center; xf.R.Set(angle); // Transform vertices for (int i = 0; i < 4; ++i) { vertices[i] = MathUtils.Multiply(ref xf, vertices[i]); } return vertices; } //Rounded rectangle contributed by Jonathan Smars - jsmars@gmail.com /// /// Creates a rounded rectangle with the specified width and height. /// /// The width. /// The height. /// The rounding X radius. /// The rounding Y radius. /// The number of segments to subdivide the edges. /// public static Vertices CreateRoundedRectangle(float width, float height, float xRadius, float yRadius, int segments) { if (yRadius > height / 2 || xRadius > width / 2) throw new Exception("Rounding amount can't be more than half the height and width respectively."); if (segments < 0) throw new Exception("Segments must be zero or more."); //We need at least 8 vertices to create a rounded rectangle Debug.Assert(Settings.MaxPolygonVertices >= 8); Vertices vertices = new Vertices(); if (segments == 0) { vertices.Add(new Vector2(width * .5f - xRadius, -height * .5f)); vertices.Add(new Vector2(width * .5f, -height * .5f + yRadius)); vertices.Add(new Vector2(width * .5f, height * .5f - yRadius)); vertices.Add(new Vector2(width * .5f - xRadius, height * .5f)); vertices.Add(new Vector2(-width * .5f + xRadius, height * .5f)); vertices.Add(new Vector2(-width * .5f, height * .5f - yRadius)); vertices.Add(new Vector2(-width * .5f, -height * .5f + yRadius)); vertices.Add(new Vector2(-width * .5f + xRadius, -height * .5f)); } else { int numberOfEdges = (segments * 4 + 8); float stepSize = MathHelper.TwoPi / (numberOfEdges - 4); int perPhase = numberOfEdges / 4; Vector2 posOffset = new Vector2(width / 2 - xRadius, height / 2 - yRadius); vertices.Add(posOffset + new Vector2(xRadius, -yRadius + yRadius)); short phase = 0; for (int i = 1; i < numberOfEdges; i++) { if (i - perPhase == 0 || i - perPhase * 3 == 0) { posOffset.X *= -1; phase--; } else if (i - perPhase * 2 == 0) { posOffset.Y *= -1; phase--; } vertices.Add(posOffset + new Vector2(xRadius * (float)Math.Cos(stepSize * -(i + phase)), -yRadius * (float)Math.Sin(stepSize * -(i + phase)))); } } return vertices; } /// /// Set this as a single edge. /// /// The first point. /// The second point. public static Vertices CreateLine(Vector2 start, Vector2 end) { Vertices vertices = new Vertices(2); vertices.Add(start); vertices.Add(end); return vertices; } /// /// Creates a circle with the specified radius and number of edges. /// /// The radius. /// The number of edges. The more edges, the more it resembles a circle /// public static Vertices CreateCircle(float radius, int numberOfEdges) { return CreateEllipse(radius, radius, numberOfEdges); } /// /// Creates a ellipse with the specified width, height and number of edges. /// /// Width of the ellipse. /// Height of the ellipse. /// The number of edges. The more edges, the more it resembles an ellipse /// public static Vertices CreateEllipse(float xRadius, float yRadius, int numberOfEdges) { Vertices vertices = new Vertices(); float stepSize = MathHelper.TwoPi / numberOfEdges; vertices.Add(new Vector2(xRadius, 0)); for (int i = numberOfEdges - 1; i > 0; --i) vertices.Add(new Vector2(xRadius * (float)Math.Cos(stepSize * i), -yRadius * (float)Math.Sin(stepSize * i))); return vertices; } public static Vertices CreateArc(float radians, int sides, float radius) { Debug.Assert(radians > 0, "The arc needs to be larger than 0"); Debug.Assert(sides > 1, "The arc needs to have more than 1 sides"); Debug.Assert(radius > 0, "The arc needs to have a radius larger than 0"); Vertices vertices = new Vertices(); float stepSize = radians / sides; for (int i = sides - 1; i > 0; i--) { vertices.Add(new Vector2(radius * (float)Math.Cos(stepSize * i), radius * (float)Math.Sin(stepSize * i))); } return vertices; } //Capsule contributed by Yobiv /// /// Creates an capsule with the specified height, radius and number of edges. /// A capsule has the same form as a pill capsule. /// /// Height (inner height + 2 * radius) of the capsule. /// Radius of the capsule ends. /// The number of edges of the capsule ends. The more edges, the more it resembles an capsule /// public static Vertices CreateCapsule(float height, float endRadius, int edges) { if (endRadius >= height / 2) throw new ArgumentException( "The radius must be lower than height / 2. Higher values of radius would create a circle, and not a half circle.", "endRadius"); return CreateCapsule(height, endRadius, edges, endRadius, edges); } /// /// Creates an capsule with the specified height, radius and number of edges. /// A capsule has the same form as a pill capsule. /// /// Height (inner height + radii) of the capsule. /// Radius of the top. /// The number of edges of the top. The more edges, the more it resembles an capsule /// Radius of bottom. /// The number of edges of the bottom. The more edges, the more it resembles an capsule /// public static Vertices CreateCapsule(float height, float topRadius, int topEdges, float bottomRadius, int bottomEdges) { if (height <= 0) throw new ArgumentException("Height must be longer than 0", "height"); if (topRadius <= 0) throw new ArgumentException("The top radius must be more than 0", "topRadius"); if (topEdges <= 0) throw new ArgumentException("Top edges must be more than 0", "topEdges"); if (bottomRadius <= 0) throw new ArgumentException("The bottom radius must be more than 0", "bottomRadius"); if (bottomEdges <= 0) throw new ArgumentException("Bottom edges must be more than 0", "bottomEdges"); if (topRadius >= height / 2) throw new ArgumentException( "The top radius must be lower than height / 2. Higher values of top radius would create a circle, and not a half circle.", "topRadius"); if (bottomRadius >= height / 2) throw new ArgumentException( "The bottom radius must be lower than height / 2. Higher values of bottom radius would create a circle, and not a half circle.", "bottomRadius"); Vertices vertices = new Vertices(); float newHeight = (height - topRadius - bottomRadius) * 0.5f; // top vertices.Add(new Vector2(topRadius, newHeight)); float stepSize = MathHelper.Pi / topEdges; for (int i = 1; i < topEdges; i++) { vertices.Add(new Vector2(topRadius * (float)Math.Cos(stepSize * i), topRadius * (float)Math.Sin(stepSize * i) + newHeight)); } vertices.Add(new Vector2(-topRadius, newHeight)); // bottom vertices.Add(new Vector2(-bottomRadius, -newHeight)); stepSize = MathHelper.Pi / bottomEdges; for (int i = 1; i < bottomEdges; i++) { vertices.Add(new Vector2(-bottomRadius * (float)Math.Cos(stepSize * i), -bottomRadius * (float)Math.Sin(stepSize * i) - newHeight)); } vertices.Add(new Vector2(bottomRadius, -newHeight)); return vertices; } /// /// Creates a gear shape with the specified radius and number of teeth. /// /// The radius. /// The number of teeth. /// The tip percentage. /// Height of the tooth. /// public static Vertices CreateGear(float radius, int numberOfTeeth, float tipPercentage, float toothHeight) { Vertices vertices = new Vertices(); float stepSize = MathHelper.TwoPi / numberOfTeeth; tipPercentage /= 100f; MathHelper.Clamp(tipPercentage, 0f, 1f); float toothTipStepSize = (stepSize / 2f) * tipPercentage; float toothAngleStepSize = (stepSize - (toothTipStepSize * 2f)) / 2f; for (int i = numberOfTeeth - 1; i >= 0; --i) { if (toothTipStepSize > 0f) { vertices.Add( new Vector2(radius * (float)Math.Cos(stepSize * i + toothAngleStepSize * 2f + toothTipStepSize), -radius * (float)Math.Sin(stepSize * i + toothAngleStepSize * 2f + toothTipStepSize))); vertices.Add( new Vector2((radius + toothHeight) * (float)Math.Cos(stepSize * i + toothAngleStepSize + toothTipStepSize), -(radius + toothHeight) * (float)Math.Sin(stepSize * i + toothAngleStepSize + toothTipStepSize))); } vertices.Add(new Vector2((radius + toothHeight) * (float)Math.Cos(stepSize * i + toothAngleStepSize), -(radius + toothHeight) * (float)Math.Sin(stepSize * i + toothAngleStepSize))); vertices.Add(new Vector2(radius * (float)Math.Cos(stepSize * i), -radius * (float)Math.Sin(stepSize * i))); } return vertices; } /// /// Detects the vertices by analyzing the texture data. /// /// The texture data. /// The texture width. /// public static Vertices CreatePolygon(uint[] data, int width) { return TextureConverter.DetectVertices(data, width); } /// /// Detects the vertices by analyzing the texture data. /// /// The texture data. /// The texture width. /// if set to true it will perform hole detection. /// public static Vertices CreatePolygon(uint[] data, int width, bool holeDetection) { return TextureConverter.DetectVertices(data, width, holeDetection); } /// /// Detects the vertices by analyzing the texture data. /// /// The texture data. /// The texture width. /// The hull tolerance. /// The alpha tolerance. /// if set to true it will perform multi part detection. /// if set to true it will perform hole detection. /// public static List CreatePolygon(uint[] data, int width, float hullTolerance, byte alphaTolerance, bool multiPartDetection, bool holeDetection) { return TextureConverter.DetectVertices(data, width, hullTolerance, alphaTolerance, multiPartDetection, holeDetection); } } }