Example #1
0
        public override void Step(Settings settings)
        {
            base.Step(settings);

            // Traverse the contact results. Apply a force on shapes
            // that overlap the sensor.
            for (int i = 0; i < e_count; ++i)
            {
                if (m_touching[i] == false)
                {
                    continue;
                }

                b2Body body   = m_bodies[i];
                b2Body ground = m_sensor.Body;

                b2CircleShape circle = (b2CircleShape)m_sensor.Shape;
                b2Vec2        center = ground.GetWorldPoint(circle.Position);

                b2Vec2 position = body.Position;

                b2Vec2 d = center - position;
                if (d.LengthSquared < float.Epsilon * float.Epsilon)
                {
                    continue;
                }

                d.Normalize();
                b2Vec2 F = 100.0f * d;
                body.ApplyForce(F, position);
            }
        }
Example #2
0
        public override bool SolvePositionConstraints(b2SolverData data)
        {
            b2Vec2 cA = data.positions[m_indexA].c;
            float  aA = data.positions[m_indexA].a;
            b2Vec2 cB = data.positions[m_indexB].c;
            float  aB = data.positions[m_indexB].a;

            b2Rot qA = new b2Rot(aA);
            b2Rot qB = new b2Rot(aB);

            b2Vec2 rA = b2Math.b2Mul(qA, m_localAnchorA - m_localCenterA);
            b2Vec2 rB = b2Math.b2Mul(qB, m_localAnchorB - m_localCenterB);
            b2Vec2 u  = cB + rB - cA - rA;

            float length = u.Normalize();
            float C      = length - m_maxLength;

            C = b2Math.b2Clamp(C, 0.0f, b2Settings.b2_maxLinearCorrection);

            float  impulse = -m_mass * C;
            b2Vec2 P       = impulse * u;

            cA -= m_invMassA * P;
            aA -= m_invIA * b2Math.b2Cross(rA, P);
            cB += m_invMassB * P;
            aB += m_invIB * b2Math.b2Cross(rB, P);

            data.positions[m_indexA].c = cA;
            data.positions[m_indexA].a = aA;
            data.positions[m_indexB].c = cB;
            data.positions[m_indexB].a = aB;

            return(length - m_maxLength < b2Settings.b2_linearSlop);
        }
        private void SolveC2()
        {
            int count2 = m_count - 1;

            for (int i = 0; i < count2; ++i)
            {
                b2Vec2 p1 = m_ps[i];
                b2Vec2 p2 = m_ps[i + 1];

                b2Vec2 d = p2 - p1;
                float  L = d.Normalize();

                float im1 = m_ims[i];
                float im2 = m_ims[i + 1];

                if (im1 + im2 == 0.0f)
                {
                    continue;
                }

                float s1 = im1 / (im1 + im2);
                float s2 = im2 / (im1 + im2);

                p1 -= m_k2 * s1 * (m_Ls[i] - L) * d;
                p2 += m_k2 * s2 * (m_Ls[i] - L) * d;

                m_ps[i]     = p1;
                m_ps[i + 1] = p2;
            }
        }
Example #4
0
        public b2PrismaticJoint(b2PrismaticJointDef def)
            : base(def)
        {
            m_localAnchorA = def.localAnchorA;
            m_localAnchorB = def.localAnchorB;
            m_localXAxisA  = def.localAxisA;
            m_localXAxisA.Normalize();
            m_localYAxisA    = m_localXAxisA.NegUnitCross(); //  b2Math.b2Cross(1.0f, m_localXAxisA);
            m_referenceAngle = def.referenceAngle;

            m_impulse.SetZero();
            m_motorMass    = 0.0f;
            m_motorImpulse = 0.0f;

            m_lowerTranslation = def.lowerTranslation;
            m_upperTranslation = def.upperTranslation;
            m_maxMotorForce    = def.maxMotorForce;
            m_motorSpeed       = def.motorSpeed;
            m_enableLimit      = def.enableLimit;
            m_enableMotor      = def.enableMotor;
            m_limitState       = b2LimitState.e_inactiveLimit;

            m_axis.SetZero();
            m_perp.SetZero();
        }
        public override void SolveVelocityConstraints(b2SolverData data)
        {
            b2Vec2 vA = data.velocities[m_indexA].v;
            float  wA = data.velocities[m_indexA].w;
            b2Vec2 vB = data.velocities[m_indexB].v;
            float  wB = data.velocities[m_indexB].w;

            float mA = m_invMassA, mB = m_invMassB;
            float iA = m_invIA, iB = m_invIB;

            float h = data.step.dt;

            // Solve angular friction
            {
                float Cdot    = wB - wA;
                float impulse = -m_angularMass * Cdot;

                float oldImpulse = m_angularImpulse;
                float maxImpulse = h * m_maxTorque;
                m_angularImpulse = b2Math.b2Clamp(m_angularImpulse + impulse, -maxImpulse, maxImpulse);
                impulse          = m_angularImpulse - oldImpulse;

                wA -= iA * impulse;
                wB += iB * impulse;
            }

            // Solve linear friction
            {
                b2Vec2 Cdot = vB + b2Math.b2Cross(wB, m_rB) - vA - b2Math.b2Cross(wA, m_rA);

                b2Vec2 impulse    = -b2Math.b2Mul(m_linearMass, Cdot);
                b2Vec2 oldImpulse = m_linearImpulse;
                m_linearImpulse += impulse;

                float maxImpulse = h * m_maxForce;

                if (m_linearImpulse.LengthSquared() > maxImpulse * maxImpulse)
                {
                    m_linearImpulse.Normalize();
                    m_linearImpulse *= maxImpulse;
                }

                impulse = m_linearImpulse - oldImpulse;

                vA -= mA * impulse;
                wA -= iA * b2Math.b2Cross(m_rA, impulse);

                vB += mB * impulse;
                wB += iB * b2Math.b2Cross(m_rB, impulse);
            }

            data.velocities[m_indexA].v = vA;
            data.velocities[m_indexA].w = wA;
            data.velocities[m_indexB].v = vB;
            data.velocities[m_indexB].w = wB;
        }
Example #6
0
    internal override bool SolvePositionConstraints(b2SolverData data)
    {
        if (m_frequencyHz > 0.0f)
        {
            // There is no position correction for soft distance constraints.
            return(true);
        }

        b2Vec2 cA = data.positions[m_indexA].c;
        float  aA = data.positions[m_indexA].a;
        b2Vec2 cB = data.positions[m_indexB].c;
        float  aB = data.positions[m_indexB].a;

        b2Rot qA = new b2Rot(aA);
        b2Rot qB = new b2Rot(aB);

        b2Vec2 rA = Utils.b2Mul(qA, m_localAnchorA - m_localCenterA);
        b2Vec2 rB = Utils.b2Mul(qB, m_localAnchorB - m_localCenterB);
        b2Vec2 u  = cB + rB - cA - rA;

        float length = u.Normalize();
        float C      = length - m_length;

        C = Utils.b2Clamp(C, -Settings.b2_maxLinearCorrection, Settings.b2_maxLinearCorrection);

        float  impulse = -m_mass * C;
        b2Vec2 P       = impulse * u;

        cA -= m_invMassA * P;
        aA -= m_invIA * Utils.b2Cross(rA, P);
        cB += m_invMassB * P;
        aB += m_invIB * Utils.b2Cross(rB, P);



        data.positions[m_indexA].c = cA;
        data.positions[m_indexA].a = aA;


        data.positions[m_indexB].c = cB;
        data.positions[m_indexB].a = aB;

        return(Utils.b2Abs(C) < Settings.b2_linearSlop);
    }
Example #7
0
        public Prismatic()
        {
            b2Body ground = null;
            {
                b2BodyDef bd = new b2BodyDef();
                ground = m_world.CreateBody(bd);

                b2EdgeShape shape = new b2EdgeShape();
                shape.Set(new b2Vec2(-40.0f, 0.0f), new b2Vec2(40.0f, 0.0f));
                ground.CreateFixture(shape, 0.0f);
            }

            {
                b2PolygonShape shape = new b2PolygonShape();
                shape.SetAsBox(2.0f, 0.5f);

                b2BodyDef bd = new b2BodyDef();
                bd.type = b2BodyType.b2_dynamicBody;
                bd.position.Set(-10.0f, 10.0f);
                bd.angle      = 0.5f * b2Settings.b2_pi;
                bd.allowSleep = false;
                b2Body body = m_world.CreateBody(bd);
                body.CreateFixture(shape, 5.0f);

                b2PrismaticJointDef pjd = new b2PrismaticJointDef();

                // Bouncy limit
                b2Vec2 axis = new b2Vec2(2.0f, 1.0f);
                axis.Normalize();
                pjd.Initialize(ground, body, new b2Vec2(0.0f, 0.0f), axis);

                // Non-bouncy limit
                //pjd.Initialize(ground, body, b2Vec2(-10.0f, 10.0f), b2Vec2(1.0f, 0.0f));

                pjd.motorSpeed       = 10.0f;
                pjd.maxMotorForce    = 10000.0f;
                pjd.enableMotor      = true;
                pjd.lowerTranslation = 0.0f;
                pjd.upperTranslation = 20.0f;
                pjd.enableLimit      = true;

                m_joint = (b2PrismaticJoint)m_world.CreateJoint(pjd);
            }
        }
Example #8
0
        public override bool SolvePositionConstraints(b2SolverData data)
        {
            if (m_frequencyHz > 0.0f)
            {
                // There is no position correction for soft distance constraints.
                return(true);
            }

            b2Vec2 cA = m_bodyA.InternalPosition.c;
            float  aA = m_bodyA.InternalPosition.a;
            b2Vec2 cB = m_bodyB.InternalPosition.c;
            float  aB = m_bodyB.InternalPosition.a;

            b2Rot qA = new b2Rot(aA);
            b2Rot qB = new b2Rot(aB);

            b2Vec2 rA = b2Math.b2Mul(qA, m_localAnchorA - m_localCenterA);
            b2Vec2 rB = b2Math.b2Mul(qB, m_localAnchorB - m_localCenterB);
            b2Vec2 u  = cB + rB - cA - rA;

            float length = u.Normalize();
            float C      = length - m_length;

            C = b2Math.b2Clamp(C, -b2Settings.b2_maxLinearCorrection, b2Settings.b2_maxLinearCorrection);

            float  impulse = -m_mass * C;
            b2Vec2 P       = impulse * u;

            cA -= m_invMassA * P;
            aA -= m_invIA * b2Math.b2Cross(ref rA, ref P);
            cB += m_invMassB * P;
            aB += m_invIB * b2Math.b2Cross(ref rB, ref P);

            m_bodyA.InternalPosition.c = cA;
            m_bodyA.InternalPosition.a = aA;
            m_bodyB.InternalPosition.c = cB;
            m_bodyB.InternalPosition.a = aB;

            return(b2Math.b2Abs(C) < b2Settings.b2_linearSlop);
        }
Example #9
0
    /// Implement b2Shape.

    // p = p1 + t * d
    // v = v1 + s * e
    // p1 + t * d = v1 + s * e
    // s * e - t * d = p1 - v1
    public override bool RayCast(b2RayCastOutput output, b2RayCastInput input, b2Transform xf, int childIndex)
    {
        // Put the ray into the edge's frame of reference.
        b2Vec2 p1 = Utils.b2MulT(xf.q, input.p1 - xf.p);
        b2Vec2 p2 = Utils.b2MulT(xf.q, input.p2 - xf.p);
        b2Vec2 d  = p2 - p1;

        b2Vec2 v1     = new b2Vec2(m_vertex1);
        b2Vec2 v2     = new b2Vec2(m_vertex2);
        b2Vec2 e      = v2 - v1;
        b2Vec2 normal = new b2Vec2(e.y, -e.x);

        normal.Normalize();

        // q = p1 + t * d
        // dot(normal, q - v1) = 0
        // dot(normal, p1 - v1) + t * dot(normal, d) = 0
        float numerator   = Utils.b2Dot(normal, v1 - p1);
        float denominator = Utils.b2Dot(normal, d);

        if (denominator == 0.0f)
        {
            return(false);
        }

        float t = numerator / denominator;

        if (t < 0.0f || input.maxFraction < t)
        {
            return(false);
        }

        b2Vec2 q = p1 + t * d;

        // q = v1 + s * r
        // s = dot(q - v1, r) / dot(r, r)
        b2Vec2 r  = v2 - v1;
        float  rr = Utils.b2Dot(r, r);

        if (rr == 0.0f)
        {
            return(false);
        }

        float s = Utils.b2Dot(q - v1, r) / rr;

        if (s < 0.0f || 1.0f < s)
        {
            return(false);
        }

        output.fraction = t;
        if (numerator > 0.0f)
        {
            output.normal = -Utils.b2Mul(xf.q, normal);
        }
        else
        {
            output.normal = Utils.b2Mul(xf.q, normal);
        }
        return(true);
    }
Example #10
0
        public static void Distance(b2DistanceOutput output, b2SimplexCache cache, b2DistanceInput input)
        {
            ++b2_gjkCalls;

            b2DistanceProxy proxyA = input.proxyA;
            b2DistanceProxy proxyB = input.proxyB;

            b2Transform transformA = input.transformA;
            b2Transform transformB = input.transformB;

            // Initialize the simplex
            b2Simplex simplex = s_simplex;

            simplex.ReadCache(cache, proxyA, transformA, proxyB, transformB);

            // Get simplex vertices as an vector.
            b2SimplexVertex[] vertices   = simplex.m_vertices;
            const int         k_maxIters = 20;

            // These store the vertices of the last simplex so that we
            // can check for duplicates and preven cycling
            int[] saveA     = s_saveA;
            int[] saveB     = s_saveB;
            int   saveCount = 0;

            b2Vec2 closestPoint = simplex.GetClosestPoint();
            float  distanceSqr1 = closestPoint.LengthSquared();
            float  distanceSqr2 = distanceSqr1;

            int    i;
            b2Vec2 p;

            // Main iteration loop
            int iter = 0;

            while (iter < k_maxIters)
            {
                // Copy the simplex so that we can identify duplicates
                saveCount = simplex.m_count;
                for (i = 0; i < saveCount; i++)
                {
                    saveA[i] = vertices[i].indexA;
                    saveB[i] = vertices[i].indexB;
                }

                /*switch(simplex.m_count)
                 * {
                 *      case 1:
                 *              break;
                 *      case 2:
                 *              simplex.Solve2();
                 *              break;
                 *      case 3:
                 *              simplex.Solve3();
                 *              break;
                 *      default:
                 *              b2Settings.b2Assert(false);
                 * }*/
                if (simplex.m_count == 1)
                {
                }
                else if (simplex.m_count == 2)
                {
                    simplex.Solve2();
                }
                else if (simplex.m_count == 3)
                {
                    simplex.Solve3();
                }
                else
                {
                    b2Settings.b2Assert(false);
                }

                // If we have 3 points, then the origin is in the corresponding triangle.
                if (simplex.m_count == 3)
                {
                    break;
                }

                // Compute the closest point.
                p            = simplex.GetClosestPoint();
                distanceSqr2 = p.LengthSquared();

                // Ensure progress
                if (distanceSqr2 > distanceSqr1)
                {
                    //break;
                }
                distanceSqr1 = distanceSqr2;

                // Get search direction.
                b2Vec2 d = simplex.GetSearchDirection();

                // Ensure the search direction is numerically fit.
                if (d.LengthSquared() < 0.0f /*Number.MinValue * Number.MinValue*/)
                {
                    // THe origin is probably contained by a line segment or triangle.
                    // Thus the shapes are overlapped.

                    // We can't return zero here even though there may be overlap.
                    // In case the simplex is a point, segment or triangle it is very difficult
                    // to determine if the origin is contained in the CSO or very close to it
                    break;
                }

                // Compute a tentative new simplex vertex using support points
                b2SimplexVertex vertex = vertices[simplex.m_count];
                vertex.indexA = (int)proxyA.GetSupport(b2Math.MulTMV(transformA.R, d.GetNegative()));
                vertex.wA     = b2Math.MulX(transformA, proxyA.GetVertex(vertex.indexA));
                vertex.indexB = (int)proxyB.GetSupport(b2Math.MulTMV(transformB.R, d));
                vertex.wB     = b2Math.MulX(transformB, proxyB.GetVertex(vertex.indexB));
                vertex.w      = b2Math.SubtractVV(vertex.wB, vertex.wA);

                // Iteration count is equated to the number of support point calls.
                ++iter;
                ++b2_gjkIters;

                // Check for duplicate support points. This is the main termination criteria.
                bool duplicate = false;
                for (i = 0; i < saveCount; i++)
                {
                    if (vertex.indexA == saveA[i] && vertex.indexB == saveB[i])
                    {
                        duplicate = true;
                        break;
                    }
                }

                // If we found a duplicate support point we must exist to avoid cycling
                if (duplicate)
                {
                    break;
                }

                // New vertex is ok and needed.
                ++simplex.m_count;
            }

            b2_gjkMaxIters = (int)b2Math.Max(b2_gjkMaxIters, iter);

            // Prepare output
            simplex.GetWitnessPoints(output.pointA, output.pointB);
            output.distance   = b2Math.SubtractVV(output.pointA, output.pointB).Length();
            output.iterations = iter;

            // Cache the simplex
            simplex.WriteCache(cache);

            // Apply radii if requested.
            if (input.useRadii)
            {
                float rA = proxyA.m_radius;
                float rB = proxyB.m_radius;

                if (output.distance > rA + rB && output.distance > float.MinValue)
                {
                    // Shapes are still not overlapped.
                    // Move the witness points to the outer surface.
                    output.distance -= rA + rB;
                    b2Vec2 normal = b2Math.SubtractVV(output.pointB, output.pointA);
                    normal.Normalize();
                    output.pointA.x += rA * normal.x;
                    output.pointA.y += rA * normal.y;
                    output.pointB.x -= rB * normal.x;
                    output.pointB.y -= rB * normal.y;
                }
                else
                {
                    // Shapes are overlapped when radii are considered.
                    // Move the witness points to the middle.
                    p               = new b2Vec2();
                    p.x             = 0.5f * (output.pointA.x + output.pointB.x);
                    p.y             = 0.5f * (output.pointA.y + output.pointB.y);
                    output.pointA.x = output.pointB.x = p.x;
                    output.pointA.y = output.pointB.y = p.y;
                    output.distance = 0.0f;
                }
            }
        }
        /// Compute the collision manifold between an edge and a circle.
        public static void b2CollideEdgeAndCircle(ref b2Manifold manifold,
                                        b2EdgeShape edgeA, ref b2Transform xfA,
                                        b2CircleShape circleB, ref b2Transform xfB)
        {
            manifold.pointCount = 0;

            // Compute circle in frame of edge
            b2Vec2 Q = b2Math.b2MulT(xfA, b2Math.b2Mul(xfB, circleB.Position));

            b2Vec2 A = edgeA.Vertex1, B = edgeA.Vertex2;
            b2Vec2 e = B - A;
            b2Vec2 diff;

            // Barycentric coordinates
            diff = B - Q;
            float u = b2Math.b2Dot(ref e, ref diff); // B - Q);
            diff = Q - A;
            float v = b2Math.b2Dot(ref e, ref diff); // Q - A);

            float radius = edgeA.Radius + circleB.Radius;

            b2ContactFeature cf = b2ContactFeature.Zero;
            cf.indexB = 0;
            cf.typeB = b2ContactFeatureType.e_vertex;

            // Region A
            if (v <= 0.0f)
            {
                b2Vec2 P = A;
                b2Vec2 d = Q - P;
                float dd = d.LengthSquared; //  b2Math.b2Dot(d, d);
                if (dd > radius * radius)
                {
                    return;
                }

                // Is there an edge connected to A?
                if (edgeA.HasVertex0)
                {
                    b2Vec2 A1 = edgeA.Vertex0;
                    b2Vec2 B1 = A;
                    b2Vec2 e1 = B1 - A1;
                    diff = B1 - Q;
                    float u1 = b2Math.b2Dot(ref e1, ref diff);

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

                cf.indexA = 0;
                cf.typeA = b2ContactFeatureType.e_vertex;
                manifold.pointCount = 1;
                manifold.type = b2ManifoldType.e_circles;
                manifold.localNormal.SetZero();
                manifold.localPoint = P;
                manifold.points[0].id.key = 0;
                manifold.points[0].id.Set(cf);
                manifold.points[0].localPoint = circleB.Position;
                return;
            }

            // Region B
            if (u <= 0.0f)
            {
                b2Vec2 P = B;
                b2Vec2 d = Q - P;
                float dd = d.LengthSquared; //  b2Math.b2Dot(d, d);
                if (dd > radius * radius)
                {
                    return;
                }

                // Is there an edge connected to B?
                if (edgeA.HasVertex3)
                {
                    b2Vec2 B2 = edgeA.Vertex3;
                    b2Vec2 A2 = B;
                    b2Vec2 e2 = B2 - A2;
                    diff = Q - A2;
                    float v2 = b2Math.b2Dot(ref e2, ref diff);

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

                cf.indexA = 1;
                cf.typeA = b2ContactFeatureType.e_vertex;
                manifold.pointCount = 1;
                manifold.type = b2ManifoldType.e_circles;
                manifold.localNormal.SetZero();
                manifold.localPoint = P;
                manifold.points[0].id.key = 0;
                manifold.points[0].id.Set(cf);
                manifold.points[0].localPoint = circleB.Position;
                return;
            }

            // Region AB
            float den = e.Length; // b2Math.b2Dot(e, e);
            System.Diagnostics.Debug.Assert(den > 0.0f);
            b2Vec2 xP = (1.0f / den) * (u * A + v * B);
            b2Vec2 xd = Q - xP;
            float xdd = xd.LengthSquared; //  b2Math.b2Dot(xd, xd);
            if (xdd > radius * radius)
            {
                return;
            }

            b2Vec2 n = b2Vec2.Zero; // new b2Vec2(-e.y, e.x); 
            n.m_x = -e.y;
            n.m_y = e.x;
            diff = Q - A;
            if (b2Math.b2Dot(ref n, ref diff) < 0.0f)
            {
                // n.Set(-n.x, -n.y);
                n.Set(-n.m_x, -n.m_y);
            }
            n.Normalize();

            cf.indexA = 0;
            cf.typeA = b2ContactFeatureType.e_face;
            manifold.pointCount = 1;
            manifold.type = b2ManifoldType.e_faceA;
            manifold.localNormal = n;
            manifold.localPoint = A;
            manifold.points[0].id.key = 0;
            manifold.points[0].id.Set(cf);
            manifold.points[0].localPoint = circleB.Position;
        }
        /// Compute the collision manifold between two polygons.
        // Find edge normal of max separation on A - return if separating axis is found
        // Find edge normal of max separation on B - return if separation axis is found
        // Choose reference edge as min(minA, minB)
        // Find incident edge
        // Clip
        // The normal points from 1 to 2
        public static void b2CollidePolygons(ref b2Manifold manifold,
                                b2PolygonShape polyA, ref b2Transform xfA,
                                b2PolygonShape polyB, ref b2Transform xfB)
        {
            manifold.pointCount = 0;
            float totalRadius = polyA.Radius + polyB.Radius;

            int edgeA = 0;
            float separationA = b2FindMaxSeparation(out edgeA, polyA, ref xfA, polyB, ref xfB);
            if (separationA > totalRadius)
                return;

            int edgeB = 0;
            float separationB = b2FindMaxSeparation(out edgeB, polyB, ref xfB, polyA, ref xfA);
            if (separationB > totalRadius)
                return;

            b2PolygonShape poly1;	// reference polygon
            b2PolygonShape poly2;	// incident polygon
            b2Transform xf1, xf2;
            int edge1;		// reference edge
            byte flip;
            const float k_relativeTol = 0.98f;
            const float k_absoluteTol = 0.001f;

            if (separationB > k_relativeTol * separationA + k_absoluteTol)
            {
                poly1 = polyB;
                poly2 = polyA;
                xf1 = xfB;
                xf2 = xfA;
                edge1 = edgeB;
                manifold.type = b2ManifoldType.e_faceB;
                flip = 1;
            }
            else
            {
                poly1 = polyA;
                poly2 = polyB;
                xf1 = xfA;
                xf2 = xfB;
                edge1 = edgeA;
                manifold.type = b2ManifoldType.e_faceA;
                flip = 0;
            }

            b2ClipVertex[] incidentEdge = new b2ClipVertex[2];
            b2FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2);

            int count1 = poly1.VertexCount;
            b2Vec2[] vertices1 = poly1.Vertices;

            int iv1 = edge1;
            int iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0;

            b2Vec2 v11 = vertices1[iv1];
            b2Vec2 v12 = vertices1[iv2];

            b2Vec2 localTangent = v12 - v11;
            localTangent.Normalize();

            b2Vec2 localNormal = localTangent.UnitCross(); // b2Math.b2Cross(localTangent, 1.0f);
            b2Vec2 planePoint = 0.5f * (v11 + v12);

            b2Vec2 tangent = b2Math.b2Mul(xf1.q, localTangent);
            b2Vec2 normal = tangent.UnitCross(); //  b2Math.b2Cross(tangent, 1.0f);

            v11 = b2Math.b2Mul(xf1, v11);
            v12 = b2Math.b2Mul(xf1, v12);

            // Face offset.
            float frontOffset = b2Math.b2Dot(ref normal, ref v11);

            // Side offsets, extended by polytope skin thickness.
            float sideOffset1 = -b2Math.b2Dot(ref tangent, ref v11) + totalRadius;
            float sideOffset2 = b2Math.b2Dot(ref tangent, ref v12) + totalRadius;

            // Clip incident edge against extruded edge1 side edges.
            b2ClipVertex[] clipPoints1 = new b2ClipVertex[2];
            b2ClipVertex[] clipPoints2 = new b2ClipVertex[2];
            int np;

            // Clip to box side 1
            np = b2ClipSegmentToLine(clipPoints1, incidentEdge, -tangent, sideOffset1, (byte)iv1);

            if (np < 2)
                return;

            // Clip to negative box side 1
            np = b2ClipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2, (byte)iv2);

            if (np < 2)
            {
                return;
            }

            // Now clipPoints2 contains the clipped points.
            manifold.localNormal = localNormal;
            manifold.localPoint = planePoint;

            int pointCount = 0;
            for (int i = 0; i < b2Settings.b2_maxManifoldPoints; ++i)
            {
                float separation = b2Math.b2Dot(ref normal, ref clipPoints2[i].v) - frontOffset;

                if (separation <= totalRadius)
                {
                    b2ManifoldPoint cp = manifold.points[pointCount];
                    cp.localPoint = b2Math.b2MulT(xf2, clipPoints2[i].v);
                    cp.id = clipPoints2[i].id;
                    if (flip != 0)
                    {
                        // Swap features
                        b2ContactFeature cf = cp.id;
                        cp.id.indexA = cf.indexB;
                        cp.id.indexB = cf.indexA;
                        cp.id.typeA = cf.typeB;
                        cp.id.typeB = cf.typeA;
                    }
                    manifold.points[pointCount] = cp;
                    ++pointCount;
                }
            }

            manifold.pointCount = pointCount;
        }
Example #13
0
    // Algorithm:
    // 1. Classify v1 and v2
    // 2. Classify polygon centroid as front or back
    // 3. Flip normal if necessary
    // 4. Initialize normal range to [-pi, pi] about face normal
    // 5. Adjust normal range according to adjacent edges
    // 6. Visit each separating axes, only accept axes within the range
    // 7. Return if _any_ axis indicates separation
    // 8. Clip
    public void Collide(b2Manifold manifold, b2EdgeShape edgeA, b2Transform xfA, b2PolygonShape polygonB, b2Transform xfB)
    {
        m_xf = Utils.b2MulT(xfA, xfB);

        m_centroidB = Utils.b2Mul(m_xf, polygonB.m_centroid);

        m_v0 = edgeA.m_vertex0;
        m_v1 = edgeA.m_vertex1;
        m_v2 = edgeA.m_vertex2;
        m_v3 = edgeA.m_vertex3;

        bool hasVertex0 = edgeA.m_hasVertex0;
        bool hasVertex3 = edgeA.m_hasVertex3;

        b2Vec2 edge1 = m_v2 - m_v1;

        edge1.Normalize();
        m_normal1.Set(edge1.y, -edge1.x);
        float offset1 = Utils.b2Dot(m_normal1, m_centroidB - m_v1);
        float offset0 = 0.0f;
        float offset2 = 0.0f;
        bool  convex1 = false;
        bool  convex2 = false;

        // Is there a preceding edge?
        if (hasVertex0)
        {
            b2Vec2 edge0 = m_v1 - m_v0;
            edge0.Normalize();
            m_normal0.Set(edge0.y, -edge0.x);
            convex1 = Utils.b2Cross(edge0, edge1) >= 0.0f;
            offset0 = Utils.b2Dot(m_normal0, m_centroidB - m_v0);
        }

        // Is there a following edge?
        if (hasVertex3)
        {
            b2Vec2 edge2 = m_v3 - m_v2;
            edge2.Normalize();
            m_normal2.Set(edge2.y, -edge2.x);
            convex2 = Utils.b2Cross(edge1, edge2) > 0.0f;
            offset2 = Utils.b2Dot(m_normal2, m_centroidB - m_v2);
        }

        // Determine front or back collision. Determine collision normal limits.
        if (hasVertex0 && hasVertex3)
        {
            if (convex1 && convex2)
            {
                m_front = offset0 >= 0.0f || offset1 >= 0.0f || offset2 >= 0.0f;
                if (m_front)
                {
                    m_normal     = m_normal1;
                    m_lowerLimit = m_normal0;
                    m_upperLimit = m_normal2;
                }
                else
                {
                    m_normal     = -m_normal1;
                    m_lowerLimit = -m_normal1;
                    m_upperLimit = -m_normal1;
                }
            }
            else if (convex1)
            {
                m_front = offset0 >= 0.0f || (offset1 >= 0.0f && offset2 >= 0.0f);
                if (m_front)
                {
                    m_normal     = m_normal1;
                    m_lowerLimit = m_normal0;
                    m_upperLimit = m_normal1;
                }
                else
                {
                    m_normal     = -m_normal1;
                    m_lowerLimit = -m_normal2;
                    m_upperLimit = -m_normal1;
                }
            }
            else if (convex2)
            {
                m_front = offset2 >= 0.0f || (offset0 >= 0.0f && offset1 >= 0.0f);
                if (m_front)
                {
                    m_normal     = m_normal1;
                    m_lowerLimit = m_normal1;
                    m_upperLimit = m_normal2;
                }
                else
                {
                    m_normal     = -m_normal1;
                    m_lowerLimit = -m_normal1;
                    m_upperLimit = -m_normal0;
                }
            }
            else
            {
                m_front = offset0 >= 0.0f && offset1 >= 0.0f && offset2 >= 0.0f;
                if (m_front)
                {
                    m_normal     = m_normal1;
                    m_lowerLimit = m_normal1;
                    m_upperLimit = m_normal1;
                }
                else
                {
                    m_normal     = -m_normal1;
                    m_lowerLimit = -m_normal2;
                    m_upperLimit = -m_normal0;
                }
            }
        }
        else if (hasVertex0)
        {
            if (convex1)
            {
                m_front = offset0 >= 0.0f || offset1 >= 0.0f;
                if (m_front)
                {
                    m_normal     = m_normal1;
                    m_lowerLimit = m_normal0;
                    m_upperLimit = -m_normal1;
                }
                else
                {
                    m_normal     = -m_normal1;
                    m_lowerLimit = m_normal1;
                    m_upperLimit = -m_normal1;
                }
            }
            else
            {
                m_front = offset0 >= 0.0f && offset1 >= 0.0f;
                if (m_front)
                {
                    m_normal     = m_normal1;
                    m_lowerLimit = m_normal1;
                    m_upperLimit = -m_normal1;
                }
                else
                {
                    m_normal     = -m_normal1;
                    m_lowerLimit = m_normal1;
                    m_upperLimit = -m_normal0;
                }
            }
        }
        else if (hasVertex3)
        {
            if (convex2)
            {
                m_front = offset1 >= 0.0f || offset2 >= 0.0f;
                if (m_front)
                {
                    m_normal     = m_normal1;
                    m_lowerLimit = -m_normal1;
                    m_upperLimit = m_normal2;
                }
                else
                {
                    m_normal     = -m_normal1;
                    m_lowerLimit = -m_normal1;
                    m_upperLimit = m_normal1;
                }
            }
            else
            {
                m_front = offset1 >= 0.0f && offset2 >= 0.0f;
                if (m_front)
                {
                    m_normal     = m_normal1;
                    m_lowerLimit = -m_normal1;
                    m_upperLimit = m_normal1;
                }
                else
                {
                    m_normal     = -m_normal1;
                    m_lowerLimit = -m_normal2;
                    m_upperLimit = m_normal1;
                }
            }
        }
        else
        {
            m_front = offset1 >= 0.0f;
            if (m_front)
            {
                m_normal     = m_normal1;
                m_lowerLimit = -m_normal1;
                m_upperLimit = -m_normal1;
            }
            else
            {
                m_normal     = -m_normal1;
                m_lowerLimit = m_normal1;
                m_upperLimit = m_normal1;
            }
        }

        // Get polygonB in frameA
        m_polygonB.count = polygonB.m_count;
        for (int i = 0; i < polygonB.m_count; ++i)
        {
            m_polygonB.vertices[i] = Utils.b2Mul(m_xf, polygonB.m_vertices[i]);
            m_polygonB.normals[i]  = Utils.b2Mul(m_xf.q, polygonB.m_normals[i]);
        }

        m_radius = polygonB.m_radius + edgeA.m_radius;

        manifold.pointCount = 0;

        b2EPAxis edgeAxis = ComputeEdgeSeparation();

        // If no valid normal can be found than this edge should not collide.
        if (edgeAxis.type == b2EPAxis.Type.e_unknown)
        {
            return;
        }

        if (edgeAxis.separation > m_radius)
        {
            return;
        }

        b2EPAxis polygonAxis = ComputePolygonSeparation();

        if (polygonAxis.type != b2EPAxis.Type.e_unknown && polygonAxis.separation > m_radius)
        {
            return;
        }

        // Use hysteresis for jitter reduction.
        const float k_relativeTol = 0.98f;
        const float k_absoluteTol = 0.001f;

        b2EPAxis primaryAxis = new b2EPAxis();

        if (polygonAxis.type == b2EPAxis.Type.e_unknown)
        {
            primaryAxis = edgeAxis;
        }
        else if (polygonAxis.separation > k_relativeTol * edgeAxis.separation + k_absoluteTol)
        {
            primaryAxis = polygonAxis;
        }
        else
        {
            primaryAxis = edgeAxis;
        }

        b2ClipVertex[]  ie = Arrays.InitializeWithDefaultInstances <b2ClipVertex>(2);
        b2ReferenceFace rf = new b2ReferenceFace();

        if (primaryAxis.type == b2EPAxis.Type.e_edgeA)
        {
            manifold.type = b2Manifold.Type.e_faceA;

            // Search for the polygon normal that is most anti-parallel to the edge normal.
            int   bestIndex = 0;
            float bestValue = Utils.b2Dot(m_normal, m_polygonB.normals[0]);
            for (int i = 1; i < m_polygonB.count; ++i)
            {
                float value = Utils.b2Dot(m_normal, m_polygonB.normals[i]);
                if (value < bestValue)
                {
                    bestValue = value;
                    bestIndex = i;
                }
            }

            int i1 = bestIndex;
            int i2 = i1 + 1 < m_polygonB.count ? i1 + 1 : 0;

            ie[0].v            = m_polygonB.vertices[i1];
            ie[0].id.cf.indexA = 0;
            ie[0].id.cf.indexB = (byte)i1;
            ie[0].id.cf.typeA  = (int)b2ContactFeature.Type.e_face;
            ie[0].id.cf.typeB  = (int)b2ContactFeature.Type.e_vertex;

            ie[1].v            = m_polygonB.vertices[i2];
            ie[1].id.cf.indexA = 0;
            ie[1].id.cf.indexB = (byte)i2;
            ie[1].id.cf.typeA  = (int)b2ContactFeature.Type.e_face;
            ie[1].id.cf.typeB  = (int)b2ContactFeature.Type.e_vertex;

            if (m_front)
            {
                rf.i1     = 0;
                rf.i2     = 1;
                rf.v1     = m_v1;
                rf.v2     = m_v2;
                rf.normal = m_normal1;
            }
            else
            {
                rf.i1     = 1;
                rf.i2     = 0;
                rf.v1     = m_v2;
                rf.v2     = m_v1;
                rf.normal = -m_normal1;
            }
        }
        else
        {
            manifold.type = b2Manifold.Type.e_faceB;

            ie[0].v            = m_v1;
            ie[0].id.cf.indexA = 0;
            ie[0].id.cf.indexB = (byte)primaryAxis.index;
            ie[0].id.cf.typeA  = (int)b2ContactFeature.Type.e_vertex;
            ie[0].id.cf.typeB  = (int)b2ContactFeature.Type.e_face;

            ie[1].v            = m_v2;
            ie[1].id.cf.indexA = 0;
            ie[1].id.cf.indexB = (byte)primaryAxis.index;
            ie[1].id.cf.typeA  = (int)b2ContactFeature.Type.e_vertex;
            ie[1].id.cf.typeB  = (int)b2ContactFeature.Type.e_face;

            rf.i1     = primaryAxis.index;
            rf.i2     = rf.i1 + 1 < m_polygonB.count ? rf.i1 + 1 : 0;
            rf.v1     = m_polygonB.vertices[rf.i1];
            rf.v2     = m_polygonB.vertices[rf.i2];
            rf.normal = m_polygonB.normals[rf.i1];
        }

        rf.sideNormal1.Set(rf.normal.y, -rf.normal.x);
        rf.sideNormal2 = -rf.sideNormal1;
        rf.sideOffset1 = Utils.b2Dot(rf.sideNormal1, rf.v1);
        rf.sideOffset2 = Utils.b2Dot(rf.sideNormal2, rf.v2);

        // Clip incident edge against extruded edge1 side edges.
        b2ClipVertex[] clipPoints1 = Arrays.InitializeWithDefaultInstances <b2ClipVertex>(2);
        b2ClipVertex[] clipPoints2 = Arrays.InitializeWithDefaultInstances <b2ClipVertex>(2);
        int            np;

        // Clip to box side 1
        np = Utils.b2ClipSegmentToLine(clipPoints1, ie, rf.sideNormal1, rf.sideOffset1, rf.i1);

        if (np < Settings.b2_maxManifoldPoints)
        {
            return;
        }

        // Clip to negative box side 1
        np = Utils.b2ClipSegmentToLine(clipPoints2, clipPoints1, rf.sideNormal2, rf.sideOffset2, rf.i2);

        if (np < Settings.b2_maxManifoldPoints)
        {
            return;
        }

        // Now clipPoints2 contains the clipped points.
        if (primaryAxis.type == b2EPAxis.Type.e_edgeA)
        {
            manifold.localNormal = rf.normal;
            manifold.localPoint  = rf.v1;
        }
        else
        {
            manifold.localNormal = polygonB.m_normals[rf.i1];
            manifold.localPoint  = polygonB.m_vertices[rf.i1];
        }

        int pointCount = 0;

        for (int i = 0; i < Settings.b2_maxManifoldPoints; ++i)
        {
            float separation;

            separation = Utils.b2Dot(rf.normal, clipPoints2[i].v - rf.v1);

            if (separation <= m_radius)
            {
                b2ManifoldPoint cp = manifold.points[pointCount];

                if (primaryAxis.type == b2EPAxis.Type.e_edgeA)
                {
                    cp.localPoint = Utils.b2MulT(m_xf, clipPoints2[i].v);
                    cp.id         = clipPoints2[i].id;
                }
                else
                {
                    cp.localPoint   = clipPoints2[i].v;
                    cp.id.cf.typeA  = clipPoints2[i].id.cf.typeB;
                    cp.id.cf.typeB  = clipPoints2[i].id.cf.typeA;
                    cp.id.cf.indexA = clipPoints2[i].id.cf.indexB;
                    cp.id.cf.indexB = clipPoints2[i].id.cf.indexA;
                }

                ++pointCount;
            }
        }

        manifold.pointCount = pointCount;
    }
Example #14
0
        // p = p1 + t * d
        // v = v1 + s * e
        // p1 + t * d = v1 + s * e
        // s * e - t * d = p1 - v1
        public override bool RayCast(out b2RayCastOutput output, b2RayCastInput input,
                                     b2Transform xf, int childIndex)
        {
            output = b2RayCastOutput.Zero;

            // Put the ray into the edge's frame of reference.
            b2Vec2 p1 = b2Math.b2MulT(xf.q, input.p1 - xf.p);
            b2Vec2 p2 = b2Math.b2MulT(xf.q, input.p2 - xf.p);
            b2Vec2 d  = p2 - p1;

            b2Vec2 v1     = m_vertex1;
            b2Vec2 v2     = m_vertex2;
            b2Vec2 e      = v2 - v1;
            b2Vec2 normal = b2Vec2.Zero; // new b2Vec2(e.y, -e.x);

            normal.m_x = e.m_y;
            normal.m_y = -e.m_x;
            normal.Normalize();

            // q = p1 + t * d
            // dot(normal, q - v1) = 0
            // dot(normal, p1 - v1) + t * dot(normal, d) = 0
            b2Vec2 diff        = v1 - p1;
            float  numerator   = b2Math.b2Dot(ref normal, ref diff);
            float  denominator = b2Math.b2Dot(ref normal, ref d);

            if (denominator == 0.0f)
            {
                return(false);
            }

            float t = numerator / denominator;

            if (t < 0.0f || input.maxFraction < t)
            {
                return(false);
            }

            b2Vec2 q = p1 + t * d;

            // q = v1 + s * r
            // s = dot(q - v1, r) / dot(r, r)
            b2Vec2 r  = v2 - v1;
            float  rr = r.LengthSquared; // b2Math.b2Dot(r, r);

            if (rr == 0.0f)
            {
                return(false);
            }

            diff = q - v1;
            float s = b2Math.b2Dot(ref diff, ref r) / rr;

            if (s < 0.0f || 1.0f < s)
            {
                return(false);
            }

            output.fraction = t;
            if (numerator > 0.0f)
            {
                output.normal = -normal;
            }
            else
            {
                output.normal = normal;
            }
            return(true);
        }
Example #15
0
        // The normal points from 1 to 2
        static public void CollidePolygons(b2Manifold manifold,
                                           b2PolygonShape polyA, b2Transform xfA,
                                           b2PolygonShape polyB, b2Transform xfB)
        {
            ClipVertex cv;

            manifold.m_pointCount = 0;
            float totalRadius = polyA.m_radius + polyB.m_radius;

            int edgeA = 0;

            s_edgeAO[0] = edgeA;
            float separationA = FindMaxSeparation(s_edgeAO, polyA, xfA, polyB, xfB);

            edgeA = s_edgeAO[0];
            if (separationA > totalRadius)
            {
                return;
            }

            int edgeB = 0;

            s_edgeBO[0] = edgeB;
            float separationB = FindMaxSeparation(s_edgeBO, polyB, xfB, polyA, xfA);

            edgeB = s_edgeBO[0];
            if (separationB > totalRadius)
            {
                return;
            }

            b2PolygonShape poly1;       // reference poly
            b2PolygonShape poly2;       // incident poly
            b2Transform    xf1;
            b2Transform    xf2;
            int            edge1;       // reference edge
            uint           flip;
            const float    k_relativeTol = 0.98f;
            const float    k_absoluteTol = 0.001f;
            b2Mat22        tMat;

            if (separationB > k_relativeTol * separationA + k_absoluteTol)
            {
                poly1           = polyB;
                poly2           = polyA;
                xf1             = xfB;
                xf2             = xfA;
                edge1           = edgeB;
                manifold.m_type = b2Manifold.e_faceB;
                flip            = 1;
            }
            else
            {
                poly1           = polyA;
                poly2           = polyB;
                xf1             = xfA;
                xf2             = xfB;
                edge1           = edgeA;
                manifold.m_type = b2Manifold.e_faceA;
                flip            = 0;
            }

            ClipVertex[] incidentEdge = s_incidentEdge;
            FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2);

            int           count1    = poly1.m_vertexCount;
            List <b2Vec2> vertices1 = poly1.m_vertices;

            b2Vec2 local_v11 = vertices1[edge1];
            b2Vec2 local_v12;

            if (edge1 + 1 < count1)
            {
                local_v12 = vertices1[(int)(edge1 + 1)];
            }
            else
            {
                local_v12 = vertices1[0];
            }

            b2Vec2 localTangent = s_localTangent;

            localTangent.Set(local_v12.x - local_v11.x, local_v12.y - local_v11.y);
            localTangent.Normalize();

            b2Vec2 localNormal = s_localNormal;

            localNormal.x = localTangent.y;
            localNormal.y = -localTangent.x;

            b2Vec2 planePoint = s_planePoint;

            planePoint.Set(0.5f * (local_v11.x + local_v12.x), 0.5f * (local_v11.y + local_v12.y));

            b2Vec2 tangent = s_tangent;

            //tangent = b2Math.b2MulMV(xf1.R, localTangent);
            tMat      = xf1.R;
            tangent.x = (tMat.col1.x * localTangent.x + tMat.col2.x * localTangent.y);
            tangent.y = (tMat.col1.y * localTangent.x + tMat.col2.y * localTangent.y);
            b2Vec2 tangent2 = s_tangent2;

            tangent2.x = -tangent.x;
            tangent2.y = -tangent.y;
            b2Vec2 normal = s_normal;

            normal.x = tangent.y;
            normal.y = -tangent.x;

            //v11 = b2Math.MulX(xf1, local_v11);
            //v12 = b2Math.MulX(xf1, local_v12);
            b2Vec2 v11 = s_v11;
            b2Vec2 v12 = s_v12;

            v11.x = xf1.position.x + (tMat.col1.x * local_v11.x + tMat.col2.x * local_v11.y);
            v11.y = xf1.position.y + (tMat.col1.y * local_v11.x + tMat.col2.y * local_v11.y);
            v12.x = xf1.position.x + (tMat.col1.x * local_v12.x + tMat.col2.x * local_v12.y);
            v12.y = xf1.position.y + (tMat.col1.y * local_v12.x + tMat.col2.y * local_v12.y);

            // Face offset
            float frontOffset = normal.x * v11.x + normal.y * v11.y;
            // Side offsets, extended by polytope skin thickness
            float sideOffset1 = -tangent.x * v11.x - tangent.y * v11.y + totalRadius;
            float sideOffset2 = tangent.x * v12.x + tangent.y * v12.y + totalRadius;

            // Clip incident edge against extruded edge1 side edges.
            ClipVertex[] clipPoints1 = s_clipPoints1;
            ClipVertex[] clipPoints2 = s_clipPoints2;
            int          np;

            // Clip to box side 1
            //np = ClipSegmentToLine(clipPoints1, incidentEdge, -tangent, sideOffset1);
            np = ClipSegmentToLine(clipPoints1, incidentEdge, tangent2, sideOffset1);

            if (np < 2)
            {
                return;
            }

            // Clip to negative box side 1
            np = ClipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2);

            if (np < 2)
            {
                return;
            }

            // Now clipPoints2 contains the clipped points.
            manifold.m_localPlaneNormal.SetV(localNormal);
            manifold.m_localPoint.SetV(planePoint);

            int pointCount = 0;

            for (int i = 0; i < b2Settings.b2_maxManifoldPoints; ++i)
            {
                cv = clipPoints2[i];
                float separation = normal.x * cv.v.x + normal.y * cv.v.y - frontOffset;
                if (separation <= totalRadius)
                {
                    b2ManifoldPoint cp = manifold.m_points[pointCount];
                    //cp.m_localPoint = b2Math.b2MulXT(xf2, cv.v);
                    tMat = xf2.R;
                    float tX = cv.v.x - xf2.position.x;
                    float tY = cv.v.y - xf2.position.y;
                    cp.m_localPoint.x = (tX * tMat.col1.x + tY * tMat.col1.y);
                    cp.m_localPoint.y = (tX * tMat.col2.x + tY * tMat.col2.y);
                    cp.m_id.Set(cv.id);
                    cp.m_id.features.flip = (int)flip;
                    ++pointCount;
                }
            }

            manifold.m_pointCount = pointCount;
        }
Example #16
0
        public static void b2Distance(ref b2DistanceOutput output, ref b2SimplexCache cache, ref b2DistanceInput input)
        {
            ++b2DistanceProxy.b2_gjkCalls;

            b2DistanceProxy proxyA = input.proxyA.Copy();
            b2DistanceProxy proxyB = input.proxyB.Copy();

            b2Transform transformA = input.transformA;
            b2Transform transformB = input.transformB;

            // Initialize the simplex.
            b2Simplex simplex = new b2Simplex();

            simplex.ReadCache(ref cache, ref proxyA, ref transformA, ref proxyB, ref transformB);

            // Get simplex vertices as an array.
            b2SimplexVertex[] vertices = new b2SimplexVertex[] { simplex.m_vertices[0], simplex.m_vertices[1], simplex.m_vertices[2] };
            int k_maxIters             = 20;

            // These store the vertices of the last simplex so that we
            // can check for duplicates and prevent cycling.
            int[] saveA     = new int[3];
            int[] saveB     = new int[3];
            int   saveCount = 0;

            b2Vec2 closestPoint = simplex.GetClosestPoint();
            float  distanceSqr1 = closestPoint.LengthSquared();
            float  distanceSqr2 = distanceSqr1;

//            Console.WriteLine("Closest Point={0},{1}, distance={2}", closestPoint.x, closestPoint.y, distanceSqr1);


            // Main iteration loop.
            #region Main Iteration Loop
            int iter = 0;
            while (iter < k_maxIters)
            {
                // Copy simplex so we can identify duplicates.
                saveCount = simplex.m_count;
                for (int i = 0; i < saveCount; ++i)
                {
                    saveA[i] = vertices[i].indexA;
                    saveB[i] = vertices[i].indexB;
                }

                switch (simplex.m_count)
                {
                case 1:
                    break;

                case 2:
                    simplex.Solve2();
                    break;

                case 3:
                    simplex.Solve3();
                    break;

                default:
                    Debug.Assert(false);
                    break;
                }

                // If we have 3 points, then the origin is in the corresponding triangle.
                if (simplex.m_count == 3)
                {
                    break;
                }

                // Compute closest point.
                b2Vec2 p = simplex.GetClosestPoint();
                distanceSqr2 = p.LengthSquared();

                // Ensure progress
                if (distanceSqr2 >= distanceSqr1)
                {
                    //break;
                }
                distanceSqr1 = distanceSqr2;

                // Get search direction.
                b2Vec2 d = simplex.GetSearchDirection();

                // Ensure the search direction is numerically fit.
                if (d.LengthSquared() < b2Settings.b2_epsilon * b2Settings.b2_epsilon)
                {
                    // The origin is probably contained by a line segment
                    // or triangle. Thus the shapes are overlapped.

                    // We can't return zero here even though there may be overlap.
                    // In case the simplex is a point, segment, or triangle it is difficult
                    // to determine if the origin is contained in the CSO or very close to it.
                    break;
                }

                // Compute a tentative new simplex vertex using support points.
                b2SimplexVertex vertex = vertices[simplex.m_count];
                vertex.indexA = proxyA.GetSupport(b2Math.b2MulT(transformA.q, -d));
                vertex.wA     = b2Math.b2Mul(transformA, proxyA.GetVertex(vertex.indexA));
                //                b2Vec2 wBLocal = new b2Vec2();
                vertex.indexB             = proxyB.GetSupport(b2Math.b2MulT(transformB.q, d));
                vertex.wB                 = b2Math.b2Mul(transformB, proxyB.GetVertex(vertex.indexB));
                vertex.w                  = vertex.wB - vertex.wA;
                vertices[simplex.m_count] = vertex;

                // Iteration count is equated to the number of support point calls.
                ++iter;
                ++b2DistanceProxy.b2_gjkIters;

                // Check for duplicate support points. This is the main termination criteria.
                bool duplicate = false;
                for (int i = 0; i < saveCount; ++i)
                {
                    if (vertex.indexA == saveA[i] && vertex.indexB == saveB[i])
                    {
                        duplicate = true;
                        break;
                    }
                }

                // If we found a duplicate support point we must exit to avoid cycling.
                if (duplicate)
                {
                    break;
                }

                // New vertex is ok and needed.
                ++simplex.m_count;
            }
            #endregion

            b2DistanceProxy.b2_gjkMaxIters = Math.Max(b2DistanceProxy.b2_gjkMaxIters, iter);

            // Prepare output.
            simplex.GetWitnessPoints(ref output.pointA, ref output.pointB);
            output.distance   = b2Math.b2Distance(output.pointA, output.pointB);
            output.iterations = iter;

            // Cache the simplex.
            simplex.WriteCache(ref cache);

            // Apply radii if requested.
            if (input.useRadii)
            {
                float rA = proxyA.Radius;
                float rB = proxyB.Radius;

                if (output.distance > rA + rB && output.distance > b2Settings.b2_epsilon)
                {
                    // Shapes are still not overlapped.
                    // Move the witness points to the outer surface.
                    output.distance -= rA + rB;
                    b2Vec2 normal = output.pointB - output.pointA;
                    normal.Normalize();
                    output.pointA += rA * normal;
                    output.pointB -= rB * normal;
                }
                else
                {
                    // Shapes are overlapped when radii are considered.
                    // Move the witness points to the middle.
                    b2Vec2 p = 0.5f * (output.pointA + output.pointB);
                    output.pointA   = p;
                    output.pointB   = p;
                    output.distance = 0.0f;
                }
            }
            // Copy back the vertex changes because they are structs, but in C++ land they are reference values
            simplex.m_vertices[0] = vertices[0];
            simplex.m_vertices[1] = vertices[1];
            simplex.m_vertices[2] = vertices[2];
        }
Example #17
0
        public void RayCast(b2WorldRayCastWrapper callback, b2RayCastInput input)
        {
            b2Vec2 p1 = input.p1;
            b2Vec2 p2 = input.p2;
            b2Vec2 r  = p2 - p1;

            Debug.Assert(r.LengthSquared > 0.0f);
            r.Normalize();

            // v is perpendicular to the segment.
            b2Vec2 v     = r.NegUnitCross(); // b2Math.b2Cross(1.0f, r);
            b2Vec2 abs_v = b2Math.b2Abs(v);

            // Separating axis for segment (Gino, p80).
            // |dot(v, p1 - c)| > dot(|v|, h)

            float maxFraction = input.maxFraction;

            // Build a bounding box for the segment.
            b2AABB segmentAABB = b2AABB.Default;
            {
                b2Vec2 t = p1 + maxFraction * (p2 - p1);
                segmentAABB.Set(b2Math.b2Min(p1, t), b2Math.b2Max(p1, t));
            }

            Stack <int> stack = new Stack <int>();

            stack.Push(m_root);

            while (stack.Count > 0)
            {
                int nodeId = stack.Pop();
                if (nodeId == b2TreeNode.b2_nullNode)
                {
                    continue;
                }

                b2TreeNode node = m_nodes[nodeId];

                if (b2Collision.b2TestOverlap(ref node.aabb, ref segmentAABB) == false)
                {
                    continue;
                }

                // Separating axis for segment (Gino, p80).
                // |dot(v, p1 - c)| > dot(|v|, h)
                b2Vec2 c          = node.aabb.Center;
                b2Vec2 h          = node.aabb.Extents;
                float  separation = b2Math.b2Abs(b2Math.b2Dot(v, p1 - c)) - b2Math.b2Dot(abs_v, h);
                if (separation > 0.0f)
                {
                    continue;
                }

                if (node.IsLeaf())
                {
                    b2RayCastInput subInput = new b2RayCastInput();
                    subInput.p1          = input.p1;
                    subInput.p2          = input.p2;
                    subInput.maxFraction = maxFraction;

                    float value = callback.RayCastCallback(subInput, nodeId);

                    if (value == 0.0f)
                    {
                        // The client has terminated the ray cast.
                        return;
                    }

                    if (value > 0.0f)
                    {
                        // Update segment bounding box.
                        maxFraction = value;
                        b2Vec2 t = p1 + maxFraction * (p2 - p1);
                        segmentAABB.Set(b2Math.b2Min(p1, t), b2Math.b2Max(p1, t));
                    }
                }
                else
                {
                    stack.Push(node.child1);
                    stack.Push(node.child2);
                }
            }
        }
Example #18
0
        /**
         * Ray-cast against the proxies in the tree. This relies on the callback
         * to perform a exact ray-cast in the case were the proxy contains a shape.
         * The callback also performs the any collision filtering. This has performance
         * roughly equal to k * log(n), where k is the number of collisions and n is the
         * number of proxies in the tree.
         * @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
         * @param callback a callback class that is called for each proxy that is hit by the ray.
         * It should be of signature:
         * ----- <code>function callback(input:b2RayCastInput, proxy:*):void</code>
         * <code>function callback(input:b2RayCastInput, proxy:*):Number</code>
         */
        public void RayCast(BroadPhaseRayCastCallback callback, b2RayCastInput input)
        {
            if (m_root == null)
            {
                return;
            }

            b2Vec2 p1 = input.p1;
            b2Vec2 p2 = input.p2;
            b2Vec2 r  = b2Math.SubtractVV(p1, p2);

            //b2Settings.b2Assert(r.LengthSquared() > 0.0);
            r.Normalize();

            // v is perpendicular to the segment
            b2Vec2 v     = b2Math.CrossFV(1.0f, r);
            b2Vec2 abs_v = b2Math.AbsV(v);

            float maxFraction = input.maxFraction;

            // Build a bounding box for the segment
            b2AABB segmentAABB = new b2AABB();
            float  tX;
            float  tY;
            {
                tX = p1.x + maxFraction * (p2.x - p1.x);
                tY = p1.y + maxFraction * (p2.y - p1.y);
                segmentAABB.lowerBound.x = Mathf.Min(p1.x, tX);
                segmentAABB.lowerBound.y = Mathf.Min(p1.y, tY);
                segmentAABB.upperBound.x = Mathf.Max(p1.x, tX);
                segmentAABB.upperBound.y = Mathf.Max(p1.y, tY);
            }

            Dictionary <int, b2DynamicTreeNode> stack = new Dictionary <int, b2DynamicTreeNode>();

            int count = 0;

            stack[count++] = m_root;

            while (count > 0)
            {
                b2DynamicTreeNode node = stack[--count];

                if (node.aabb.TestOverlap(segmentAABB) == false)
                {
                    continue;
                }

                // Separating axis for segment (Gino, p80)
                // |dot(v, p1 - c)| > dot(|v|,h)

                b2Vec2 c          = node.aabb.GetCenter();
                b2Vec2 h          = node.aabb.GetExtents();
                float  separation = Mathf.Abs(v.x * (p1.x - c.x) + v.y * (p1.y - c.y))
                                    - abs_v.x * h.x - abs_v.y * h.y;
                if (separation > 0.0f)
                {
                    continue;
                }

                if (node.IsLeaf())
                {
                    b2RayCastInput subInput = new b2RayCastInput();
                    subInput.p1 = input.p1;
                    subInput.p2 = input.p2;
                    //*************by kingBook 2015/10/22 16:17*************
                    subInput.maxFraction = maxFraction;
                    float value = callback(subInput, node);
                    if (value == 0)
                    {
                        return;
                    }
                    if (value > 0)
                    {
                        //Update the segment bounding box
                        maxFraction = value;
                        //******************************************************
                        tX = p1.x + maxFraction * (p2.x - p1.x);
                        tY = p1.y + maxFraction * (p2.y - p1.y);
                        segmentAABB.lowerBound.x = Mathf.Min(p1.x, tX);
                        segmentAABB.lowerBound.y = Mathf.Min(p1.y, tY);
                        segmentAABB.upperBound.x = Mathf.Max(p1.x, tX);
                        segmentAABB.upperBound.y = Mathf.Max(p1.y, tY);
                    }
                }
                else
                {
                    // No stack limit, so no assert
                    stack[count++] = node.child1;
                    stack[count++] = node.child2;
                }
            }
        }