Beispiel #1
0
        public override void SolveVelocityConstraints(b2SolverData data)
        {
            b2Vec2 vB = data.velocities[m_indexB].v;
            float  wB = data.velocities[m_indexB].w;

            // Cdot = v + cross(w, r)
            b2Vec2 Cdot    = vB + b2Math.b2Cross(wB, m_rB);
            b2Vec2 impulse = b2Math.b2Mul(m_mass, -(Cdot + m_C + m_gamma * m_impulse));

            b2Vec2 oldImpulse = m_impulse;

            m_impulse += impulse;
            float maxImpulse = data.step.dt * m_maxForce;

            if (m_impulse.LengthSquared() > maxImpulse * maxImpulse)
            {
                m_impulse *= maxImpulse / m_impulse.Length();
            }
            impulse = m_impulse - oldImpulse;

            vB += m_invMassB * impulse;
            wB += m_invIB * b2Math.b2Cross(m_rB, impulse);

            data.velocities[m_indexB].v = vB;
            data.velocities[m_indexB].w = wB;
        }
        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;
        }
        public virtual void Set(b2Vec2[] vertices, int count)
        {
            m_vertexCount = count;

            // Copy vertices.
            for (int i = 0; i < m_vertexCount; ++i)
            {
                m_vertices[i] = vertices[i];
            }

            // Compute normals. Ensure the edges have non-zero length.
            for (int i = 0; i < m_vertexCount; ++i)
            {
                int    i1   = i;
                int    i2   = i + 1 < m_vertexCount ? i + 1 : 0;
                b2Vec2 edge = m_vertices[i2] - m_vertices[i1];
                Debug.Assert(edge.LengthSquared() > b2Settings.b2_epsilon * b2Settings.b2_epsilon);
                m_normals[i] = b2Math.b2Cross(edge, 1.0f);
                m_normals[i].Normalize();
            }

#if DEBUG
            // Ensure the polygon is convex and the interior
            // is to the left of each edge.
            for (int i = 0; i < m_vertexCount; ++i)
            {
                int    i1   = i;
                int    i2   = i + 1 < m_vertexCount ? i + 1 : 0;
                b2Vec2 edge = m_vertices[i2] - m_vertices[i1];

                for (int j = 0; j < m_vertexCount; ++j)
                {
                    // Don't check vertices on the current edge.
                    if (j == i1 || j == i2)
                    {
                        continue;
                    }

                    b2Vec2 r = m_vertices[j] - m_vertices[i1];

                    // If this crashes, your polygon is non-convex, has colinear edges,
                    // or the winding order is wrong.
                    float s = b2Math.b2Cross(edge, r);
                    if (s < 0f)
                    {
                        throw (new InvalidOperationException("ERROR: Please ensure your polygon is convex and has a CCW winding order"));
                    }
                }
            }
#endif

            // Compute the polygon centroid.
            m_centroid = ComputeCentroid(m_vertices, m_vertexCount);
        }
Beispiel #4
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;
                }
            }
        }
        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];
        }
Beispiel #6
0
    /// Create a convex hull from the given array of local points.
    /// The count must be in the range [3, b2_maxPolygonVertices].
    /// @warning the points may be re-ordered, even if they form a convex polygon
    /// @warning collinear points are handled but not removed. Collinear points
    /// may lead to poor stacking behavior.
    public b2PolygonShape Set(b2Vec2[] vertices)
    {
        Debug.Assert(3 <= vertices.Length && vertices.Length <= Settings.b2_maxPolygonVertices);
        if (vertices.Length < 3)
        {
            SetAsBox(1.0f, 1.0f);
            return(this);
        }

        int n = Utils.b2Min(vertices.Length, Settings.b2_maxPolygonVertices);

        // Perform welding and copy vertices into local buffer.
        b2Vec2[] ps        = Arrays.InitializeWithDefaultInstances <b2Vec2>(Settings.b2_maxPolygonVertices);
        int      tempCount = 0;

        for (int i = 0; i < n; ++i)
        {
            b2Vec2 v = vertices[i];

            bool unique = true;
            for (int j = 0; j < tempCount; ++j)
            {
                if (Utils.b2DistanceSquared(v, ps[j]) < ((0.5f * Settings.b2_linearSlop) * (0.5f * Settings.b2_linearSlop)))
                {
                    unique = false;
                    break;
                }
            }

            if (unique)
            {
                ps[tempCount++] = v;
            }
        }

        n = tempCount;
        if (n < 3)
        {
            // Polygon is degenerate.
            Debug.Assert(false);
            SetAsBox(1.0f, 1.0f);
            return(this);
        }

        // Create the convex hull using the Gift wrapping algorithm
        // http://en.wikipedia.org/wiki/Gift_wrapping_algorithm

        // Find the right most point on the hull
        int   i0 = 0;
        float x0 = ps[0].x;

        for (int i = 1; i < n; ++i)
        {
            float x = ps[i].x;
            if (x > x0 || (x == x0 && ps[i].y < ps[i0].y))
            {
                i0 = i;
                x0 = x;
            }
        }

        int[] hull = new int[Settings.b2_maxPolygonVertices];
        int   m    = 0;
        int   ih   = i0;

        for (;;)
        {
            Debug.Assert(m < Settings.b2_maxPolygonVertices);
            hull[m] = ih;

            int ie = 0;
            for (int j = 1; j < n; ++j)
            {
                if (ie == ih)
                {
                    ie = j;
                    continue;
                }

                b2Vec2 r = ps[ie] - ps[hull[m]];
                b2Vec2 v = ps[j] - ps[hull[m]];
                float  c = Utils.b2Cross(r, v);
                if (c < 0.0f)
                {
                    ie = j;
                }

                // Collinearity check
                if (c == 0.0f && v.LengthSquared() > r.LengthSquared())
                {
                    ie = j;
                }
            }

            ++m;
            ih = ie;

            if (ie == i0)
            {
                break;
            }
        }

        if (m < 3)
        {
            // Polygon is degenerate.
            Debug.Assert(false);
            SetAsBox(1.0f, 1.0f);
            return(this);
        }

        m_count = m;

        // Copy vertices.
        for (int i = 0; i < m; ++i)
        {
            m_vertices[i] = ps[hull[i]];
        }

        // Compute normals. Ensure the edges have non-zero length.
        for (int i = 0; i < m; ++i)
        {
            int    i1   = i;
            int    i2   = i + 1 < m ? i + 1 : 0;
            b2Vec2 edge = m_vertices[i2] - m_vertices[i1];
            Debug.Assert(edge.LengthSquared() > float.Epsilon * float.Epsilon);
            m_normals[i] = Utils.b2Cross(edge, 1.0f);
            m_normals[i].Normalize();
        }

        // Compute the polygon centroid.
        m_centroid = Utils.ComputeCentroid(m_vertices, m);

        return(this);
    }
Beispiel #7
0
        private void SolveC3()
        {
            int count3 = m_count - 2;

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

                float m1 = m_ims[i];
                float m2 = m_ims[i + 1];
                float m3 = m_ims[i + 2];

                b2Vec2 d1 = p2 - p1;
                b2Vec2 d2 = p3 - p2;

                float L1sqr = d1.LengthSquared();
                float L2sqr = d2.LengthSquared();

                if (L1sqr * L2sqr == 0.0f)
                {
                    continue;
                }

                float a = b2Math.b2Cross(d1, d2);
                float b = b2Math.b2Dot(d1, d2);

                float angle = b2Math.b2Atan2(a, b);

                b2Vec2 Jd1 = (-1.0f / L1sqr) * d1.Skew();
                b2Vec2 Jd2 = (1.0f / L2sqr) * d2.Skew();

                b2Vec2 J1 = -Jd1;
                b2Vec2 J2 = Jd1 - Jd2;
                b2Vec2 J3 = Jd2;

                float mass = m1 * b2Math.b2Dot(J1, J1) + m2 * b2Math.b2Dot(J2, J2) + m3 * b2Math.b2Dot(J3, J3);
                if (mass == 0.0f)
                {
                    continue;
                }

                mass = 1.0f / mass;

                float C = angle - m_as[i];

                while (C > b2Settings.b2_pi)
                {
                    angle -= 2f * (float)Math.PI;
                    C      = angle - m_as[i];
                }

                while (C < -(float)Math.PI)
                {
                    angle += 2.0f * (float)Math.PI;
                    C      = angle - m_as[i];
                }

                float impulse = -m_k3 * mass * C;

                p1 += (m1 * impulse) * J1;
                p2 += (m2 * impulse) * J2;
                p3 += (m3 * impulse) * J3;

                m_ps[i]     = p1;
                m_ps[i + 1] = p2;
                m_ps[i + 2] = p3;
            }
        }
Beispiel #8
0
        /**
         * Copy vertices. This assumes the vertices define a convex polygon.
         * It is assumed that the exterior is the the right of each edge.
         */
        public void SetAsVector(List <b2Vec2> vertices, int vertexCount = 0)
        {
            if (vertexCount == 0)
            {
                vertexCount = vertices.Count;
            }

            b2Settings.b2Assert(2 <= vertexCount);
            m_vertexCount = vertexCount;

            Reserve(vertexCount);

            int i;

            // Copy vertices
            for (i = 0; i < m_vertexCount; i++)
            {
                b2Vec2 v = vertices[i];
                m_vertices[(m_vertexCount - 1) - i].SetV(v);            //改为逆时针顺序添加

                /*if(Application.platform==RuntimePlatform.FlashPlayer){
                 *      v.y=-v.y;
                 *      m_vertices[i].SetV(v);
                 * }else{
                 *      m_vertices[(m_vertexCount-1)-i].SetV(v);//改为逆时针顺序添加
                 * }*/
            }

            // Compute normals. Ensure the edges have non-zero length.
            for (i = 0; i < m_vertexCount; ++i)
            {
                int    i1   = i;
                int    i2   = i + 1 < m_vertexCount ? i + 1 : 0;
                b2Vec2 edge = b2Math.SubtractVV(m_vertices[i2], m_vertices[i1]);
                b2Settings.b2Assert(edge.LengthSquared() > float.MinValue /* * Number.MIN_VALUE*/);
                m_normals[i].SetV(b2Math.CrossVF(edge, 1.0f));
                m_normals[i].Normalize();
            }

//#ifdef _DEBUG
            // Ensure the polygon is convex and the interior
            // is to the left of each edge.
            //for (int32 i = 0; i < m_vertexCount; ++i)
            //{
            //int32 i1 = i;
            //int32 i2 = i + 1 < m_vertexCount ? i + 1 : 0;
            //b2Vec2 edge = m_vertices[i2] - m_vertices[i1];
            //for (int32 j = 0; j < m_vertexCount; ++j)
            //{
            // Don't check vertices on the current edge.
            //if (j == i1 || j == i2)
            //{
            //continue;
            //}
            //
            //b2Vec2 r = m_vertices[j] - m_vertices[i1];
            // Your polygon is non-convex (it has an indentation) or
            // has colinear edges.
            //float32 s = b2Cross(edge, r);
            //b2Assert(s > 0.0f);
            //}
            //}
//#endif

            // Compute the polygon centroid

            m_centroid = ComputeCentroid(m_vertices, (uint)m_vertexCount);
        }
Beispiel #9
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.
    public void RayCast(b2BroadphaseRayCastCallback callback, b2RayCastInput input)
    {
        b2Vec2 p1 = new b2Vec2(input.p1);
        b2Vec2 p2 = new b2Vec2(input.p2);
        b2Vec2 r  = p2 - p1;

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

        // v is perpendicular to the segment.
        b2Vec2 v     = Utils.b2Cross(1.0f, r);
        b2Vec2 abs_v = Utils.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 = new b2AABB();
        {
            b2Vec2 t = p1 + maxFraction * (p2 - p1);
            segmentAABB.lowerBound = Utils.b2Min(p1, t);
            segmentAABB.upperBound = Utils.b2Max(p1, t);
        }

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

        stack.Push(m_root);

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

            b2TreeNode node = m_nodes[nodeId];

            if (Utils.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.GetCenter();
            b2Vec2 h          = node.aabb.GetExtents();
            float  separation = Utils.b2Abs(Utils.b2Dot(v, p1 - c)) - Utils.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(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.lowerBound = Utils.b2Min(p1, t);
                    segmentAABB.upperBound = Utils.b2Max(p1, t);
                }
            }
            else
            {
                stack.Push(node.child1);
                stack.Push(node.child2);
            }
        }
    }