/// <summary>
        /// Compute contact points for edge versus circle.
        /// This accounts for edge connectivity.
        /// </summary>
        /// <param name="manifold">The manifold.</param>
        /// <param name="edgeA">The edge A.</param>
        /// <param name="transformA">The transform A.</param>
        /// <param name="circleB">The circle B.</param>
        /// <param name="transformB">The transform B.</param>
        public static void CollideEdgeAndCircle(ref Manifold manifold, EdgeShape edgeA, ref Transform transformA, CircleShape circleB, ref Transform transformB)
        {
            manifold.PointCount = 0;

            // Compute circle in frame of edge
            Vector2 Q = MathUtils.MulT(ref transformA, MathUtils.Mul(ref transformB, ref circleB._position));

            Vector2 A = edgeA.Vertex1, B = edgeA.Vertex2;
            Vector2 e = B - A;

            // Barycentric coordinates
            GGame.Math.Fix64 u = Vector2.Dot(e, B - Q);
            GGame.Math.Fix64 v = Vector2.Dot(e, Q - A);

            GGame.Math.Fix64 radius = edgeA.Radius + circleB.Radius;

            ContactFeature cf;

            cf.IndexB = 0;
            cf.TypeB  = ContactFeatureType.Vertex;

            // Region A
            if (v <= 0.0f)
            {
                Vector2          P1  = A;
                Vector2          d1  = Q - P1;
                GGame.Math.Fix64 dd1 = Vector2.Dot(d1, d1);
                if (dd1 > radius * radius)
                {
                    return;
                }

                // Is there an edge connected to A?
                if (edgeA.HasVertex0)
                {
                    Vector2          A1 = edgeA.Vertex0;
                    Vector2          B1 = A;
                    Vector2          e1 = B1 - A1;
                    GGame.Math.Fix64 u1 = Vector2.Dot(e1, B1 - Q);

                    // Is the circle in Region AB of the previous edge?
                    if (u1 > 0.0f)
                    {
                        return;
                    }
                }

                cf.IndexA                                = 0;
                cf.TypeA                                 = ContactFeatureType.Vertex;
                manifold.PointCount                      = 1;
                manifold.Type                            = ManifoldType.Circles;
                manifold.LocalNormal                     = Vector2.Zero;
                manifold.LocalPoint                      = P1;
                manifold.Points.Value0.Id.Key            = 0;
                manifold.Points.Value0.Id.ContactFeature = cf;
                manifold.Points.Value0.LocalPoint        = circleB.Position;
                return;
            }

            // Region B
            if (u <= 0.0f)
            {
                Vector2          P2  = B;
                Vector2          d2  = Q - P2;
                GGame.Math.Fix64 dd2 = Vector2.Dot(d2, d2);
                if (dd2 > radius * radius)
                {
                    return;
                }

                // Is there an edge connected to B?
                if (edgeA.HasVertex3)
                {
                    Vector2          B2 = edgeA.Vertex3;
                    Vector2          A2 = B;
                    Vector2          e2 = B2 - A2;
                    GGame.Math.Fix64 v2 = Vector2.Dot(e2, Q - A2);

                    // Is the circle in Region AB of the next edge?
                    if (v2 > 0.0f)
                    {
                        return;
                    }
                }

                cf.IndexA                                = 1;
                cf.TypeA                                 = (byte)ContactFeatureType.Vertex;
                manifold.PointCount                      = 1;
                manifold.Type                            = ManifoldType.Circles;
                manifold.LocalNormal                     = Vector2.Zero;
                manifold.LocalPoint                      = P2;
                manifold.Points.Value0.Id.Key            = 0;
                manifold.Points.Value0.Id.ContactFeature = cf;
                manifold.Points.Value0.LocalPoint        = circleB.Position;
                return;
            }

            // Region AB
            GGame.Math.Fix64 den = Vector2.Dot(e, e);
            Debug.Assert(den > 0.0f);
            Vector2 P = (1.0f / den) * (u * A + v * B);
            Vector2 d = Q - P;

            GGame.Math.Fix64 dd = Vector2.Dot(d, d);
            if (dd > radius * radius)
            {
                return;
            }

            Vector2 n = new Vector2(-e.Y, e.X);

            if (Vector2.Dot(n, Q - A) < 0.0f)
            {
                n = new Vector2(-n.X, -n.Y);
            }
            n.Normalize();

            cf.IndexA                                = 0;
            cf.TypeA                                 = ContactFeatureType.Face;
            manifold.PointCount                      = 1;
            manifold.Type                            = ManifoldType.FaceA;
            manifold.LocalNormal                     = n;
            manifold.LocalPoint                      = A;
            manifold.Points.Value0.Id.Key            = 0;
            manifold.Points.Value0.Id.ContactFeature = cf;
            manifold.Points.Value0.LocalPoint        = circleB.Position;
        }
        /// <summary>
        /// Compute the collision manifold between a polygon and a circle.
        /// </summary>
        /// <param name="manifold">The manifold.</param>
        /// <param name="polygonA">The polygon A.</param>
        /// <param name="xfA">The transform of A.</param>
        /// <param name="circleB">The circle B.</param>
        /// <param name="xfB">The transform of B.</param>
        public static void CollidePolygonAndCircle(ref Manifold manifold, PolygonShape polygonA, ref Transform xfA, CircleShape circleB, ref Transform xfB)
        {
            manifold.PointCount = 0;

            // Compute circle position in the frame of the polygon.
            Vector2 c      = MathUtils.Mul(ref xfB, circleB.Position);
            Vector2 cLocal = MathUtils.MulT(ref xfA, c);

            // Find the min separating edge.
            int      normalIndex = 0;
            float    separation  = -Settings.MaxFloat;
            float    radius      = polygonA.Radius + circleB.Radius;
            int      vertexCount = polygonA.Vertices.Count;
            Vertices vertices    = polygonA.Vertices;
            Vertices normals     = polygonA.Normals;

            for (int i = 0; i < vertexCount; ++i)
            {
                float s = Vector2.Dot(normals[i], cLocal - vertices[i]);

                if (s > radius)
                {
                    // Early out.
                    return;
                }

                if (s > separation)
                {
                    separation  = s;
                    normalIndex = i;
                }
            }

            // Vertices that subtend the incident face.
            int     vertIndex1 = normalIndex;
            int     vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0;
            Vector2 v1         = vertices[vertIndex1];
            Vector2 v2         = vertices[vertIndex2];

            // If the center is inside the polygon ...
            if (separation < Settings.Epsilon)
            {
                manifold.PointCount  = 1;
                manifold.Type        = ManifoldType.FaceA;
                manifold.LocalNormal = normals[normalIndex];
                manifold.LocalPoint  = 0.5f * (v1 + v2);
                manifold.Points.Value0.LocalPoint = circleB.Position;
                manifold.Points.Value0.Id.Key     = 0;
                return;
            }

            // Compute barycentric coordinates
            float u1 = Vector2.Dot(cLocal - v1, v2 - v1);
            float u2 = Vector2.Dot(cLocal - v2, v1 - v2);

            if (u1 <= 0.0f)
            {
                if (Vector2.DistanceSquared(cLocal, v1) > radius * radius)
                {
                    return;
                }

                manifold.PointCount  = 1;
                manifold.Type        = ManifoldType.FaceA;
                manifold.LocalNormal = cLocal - v1;
                manifold.LocalNormal.Normalize();
                manifold.LocalPoint = v1;
                manifold.Points.Value0.LocalPoint = circleB.Position;
                manifold.Points.Value0.Id.Key     = 0;
            }
            else if (u2 <= 0.0f)
            {
                if (Vector2.DistanceSquared(cLocal, v2) > radius * radius)
                {
                    return;
                }

                manifold.PointCount  = 1;
                manifold.Type        = ManifoldType.FaceA;
                manifold.LocalNormal = cLocal - v2;
                manifold.LocalNormal.Normalize();
                manifold.LocalPoint = v2;
                manifold.Points.Value0.LocalPoint = circleB.Position;
                manifold.Points.Value0.Id.Key     = 0;
            }
            else
            {
                Vector2 faceCenter = 0.5f * (v1 + v2);
                float   s          = Vector2.Dot(cLocal - faceCenter, normals[vertIndex1]);
                if (s > radius)
                {
                    return;
                }

                manifold.PointCount  = 1;
                manifold.Type        = ManifoldType.FaceA;
                manifold.LocalNormal = normals[vertIndex1];
                manifold.LocalPoint  = faceCenter;
                manifold.Points.Value0.LocalPoint = circleB.Position;
                manifold.Points.Value0.Id.Key     = 0;
            }
        }
 /// <summary>
 /// Collides and edge and a polygon, taking into account edge adjacency.
 /// </summary>
 public static void CollideEdgeAndPolygon(ref Manifold manifold, EdgeShape edgeA, ref Transform xfA, PolygonShape polygonB, ref Transform xfB)
 {
     EPCollider.Collide(ref manifold, edgeA, ref xfA, polygonB, ref xfB);
 }
Esempio n. 4
0
        public static void GetPointStates(out FixedArray2 <PointState> state1, out FixedArray2 <PointState> state2, ref Manifold manifold1, ref Manifold manifold2)
        {
            state1 = new FixedArray2 <PointState>();
            state2 = new FixedArray2 <PointState>();

            for (int i = 0; i < Settings.MaxManifoldPoints; ++i)
            {
                state1[i] = PointState.Null;
                state2[i] = PointState.Null;
            }

            // Detect persists and removes.
            for (int i = 0; i < manifold1.PointCount; ++i)
            {
                ContactID id = manifold1.Points[i].Id;

                state1[i] = PointState.Remove;

                for (int j = 0; j < manifold2.PointCount; ++j)
                {
                    if (manifold2.Points[j].Id.Key == id.Key)
                    {
                        state1[i] = PointState.Persist;
                        break;
                    }
                }
            }

            // Detect persists and adds.
            for (int i = 0; i < manifold2.PointCount; ++i)
            {
                ContactID id = manifold2.Points[i].Id;

                state2[i] = PointState.Add;

                for (int j = 0; j < manifold1.PointCount; ++j)
                {
                    if (manifold1.Points[j].Id.Key == id.Key)
                    {
                        state2[i] = PointState.Persist;
                        break;
                    }
                }
            }
        }