/* * Farseer Physics Engine: * Copyright (c) 2012 Ian Qvist * * Original source Box2D: * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ using System.Diagnostics; using FarseerPhysics.Common; using Microsoft.Xna.Framework; namespace FarseerPhysics.Collision.Shapes { /// /// A chain shape is a free form sequence of line segments. /// The chain has two-sided collision, so you can use inside and outside collision. /// Therefore, you may use any winding order. /// Connectivity information is used to create smooth collisions. /// WARNING: The chain will not collide properly if there are self-intersections. /// public class ChainShape : Shape { /// /// The vertices. These are not owned/freed by the chain Shape. /// public Vertices Vertices; private Vector2 _prevVertex, _nextVertex; private bool _hasPrevVertex, _hasNextVertex; private static EdgeShape _edgeShape = new EdgeShape(); /// /// Constructor for ChainShape. By default have 0 in density. /// public ChainShape() : base(0) { ShapeType = ShapeType.Chain; _radius = Settings.PolygonRadius; } /// /// Create a new chainshape from the vertices. /// /// The vertices to use. Must contain 2 or more vertices. /// Set to true to create a closed loop. It connects the first vertice to the last, and automatically adjusts connectivity to create smooth collisions along the chain. public ChainShape(Vertices vertices, bool createLoop = false) : base(0) { ShapeType = ShapeType.Chain; _radius = Settings.PolygonRadius; Debug.Assert(vertices != null && vertices.Count >= 2); Debug.Assert(vertices[0] != vertices[vertices.Count - 1]); // FPE. See http://www.box2d.org/forum/viewtopic.php?f=4&t=7973&p=35363 for (int i = 1; i < vertices.Count; ++i) { Vector2 v1 = vertices[i - 1]; Vector2 v2 = vertices[i]; // If the code crashes here, it means your vertices are too close together. Debug.Assert(Vector2.DistanceSquared(v1, v2) > Settings.LinearSlop * Settings.LinearSlop); } Vertices = new Vertices(vertices); if (createLoop) { Vertices.Add(vertices[0]); PrevVertex = Vertices[Vertices.Count - 2]; //FPE: We use the properties instead of the private fields here. NextVertex = Vertices[1]; //FPE: We use the properties instead of the private fields here. } } public override int ChildCount { // edge count = vertex count - 1 get { return Vertices.Count - 1; } } /// /// Establish connectivity to a vertex that precedes the first vertex. /// Don't call this for loops. /// public Vector2 PrevVertex { get { return _prevVertex; } set { Debug.Assert(value != null); _prevVertex = value; _hasPrevVertex = true; } } /// /// Establish connectivity to a vertex that follows the last vertex. /// Don't call this for loops. /// public Vector2 NextVertex { get { return _nextVertex; } set { Debug.Assert(value != null); _nextVertex = value; _hasNextVertex = true; } } /// /// This method has been optimized to reduce garbage. /// /// The cached edge to set properties on. /// The index. internal void GetChildEdge(EdgeShape edge, int index) { Debug.Assert(0 <= index && index < Vertices.Count - 1); Debug.Assert(edge != null); edge.ShapeType = ShapeType.Edge; edge._radius = _radius; edge.Vertex1 = Vertices[index + 0]; edge.Vertex2 = Vertices[index + 1]; if (index > 0) { edge.Vertex0 = Vertices[index - 1]; edge.HasVertex0 = true; } else { edge.Vertex0 = _prevVertex; edge.HasVertex0 = _hasPrevVertex; } if (index < Vertices.Count - 2) { edge.Vertex3 = Vertices[index + 2]; edge.HasVertex3 = true; } else { edge.Vertex3 = _nextVertex; edge.HasVertex3 = _hasNextVertex; } } /// /// Get a child edge. /// /// The index. public EdgeShape GetChildEdge(int index) { EdgeShape edgeShape = new EdgeShape(); GetChildEdge(edgeShape, index); return edgeShape; } public override bool TestPoint(ref Transform transform, ref Vector2 point) { return false; } public override bool RayCast(out RayCastOutput output, ref RayCastInput input, ref Transform transform, int childIndex) { Debug.Assert(childIndex < Vertices.Count); int i1 = childIndex; int i2 = childIndex + 1; if (i2 == Vertices.Count) { i2 = 0; } _edgeShape.Vertex1 = Vertices[i1]; _edgeShape.Vertex2 = Vertices[i2]; return _edgeShape.RayCast(out output, ref input, ref transform, 0); } public override void ComputeAABB(out AABB aabb, ref Transform transform, int childIndex) { Debug.Assert(childIndex < Vertices.Count); int i1 = childIndex; int i2 = childIndex + 1; if (i2 == Vertices.Count) { i2 = 0; } Vector2 v1 = MathUtils.Mul(ref transform, Vertices[i1]); Vector2 v2 = MathUtils.Mul(ref transform, Vertices[i2]); aabb.LowerBound = Vector2.Min(v1, v2); aabb.UpperBound = Vector2.Max(v1, v2); } protected override void ComputeProperties() { //Does nothing. Chain shapes don't have properties. } public override float ComputeSubmergedArea(ref Vector2 normal, float offset, ref Transform xf, out Vector2 sc) { sc = Vector2.Zero; return 0; } /// /// Compare the chain to another chain /// /// The other chain /// True if the two chain shapes are the same public bool CompareTo(ChainShape shape) { if (Vertices.Count != shape.Vertices.Count) return false; for (int i = 0; i < Vertices.Count; i++) { if (Vertices[i] != shape.Vertices[i]) return false; } return PrevVertex == shape.PrevVertex && NextVertex == shape.NextVertex; } public override Shape Clone() { ChainShape clone = new ChainShape(); clone.ShapeType = ShapeType; clone._density = _density; clone._radius = _radius; clone.PrevVertex = _prevVertex; clone.NextVertex = _nextVertex; clone._hasNextVertex = _hasNextVertex; clone._hasPrevVertex = _hasPrevVertex; clone.Vertices = new Vertices(Vertices); clone.MassData = MassData; return clone; } } }