Exemple #1
0
            public static void ReadCache(
                out Simplex simplex,
                SimplexCache cache,
                WorldTransform xfA,
                DistanceProxy proxyA,
                WorldTransform xfB,
                DistanceProxy proxyB)
            {
                System.Diagnostics.Debug.Assert(cache.Count <= 3);

                // Copy data from cache.
                simplex = new Simplex {
                    Count = cache.Count
                };
                for (var i = 0; i < simplex.Count; ++i)
                {
                    SimplexVertex v;
                    v.IndexA  = cache.IndexA[i];
                    v.IndexB  = cache.IndexB[i];
                    v.VertexA = xfA.ToGlobal(proxyA.Vertices[v.IndexA]);
                    v.VertexB = xfB.ToGlobal(proxyB.Vertices[v.IndexB]);
// ReSharper disable RedundantCast Necessary for FarPhysics.
                    v.VertexDelta = (LocalPoint)(v.VertexB - v.VertexA);
// ReSharper restore RedundantCast
                    v.Alpha             = 0.0f;
                    simplex.Vertices[i] = v;
                }

                // Compute the new simplex metric, if it is substantially different than
                // old metric then flush the simplex.
                if (simplex.Count > 1)
                {
                    var metric1 = cache.Metric;
                    var metric2 = simplex.GetMetric();
                    if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < float.Epsilon)
                    {
                        // Reset the simplex, triggers computation below.
                        simplex.Count = 0;
                    }
                }

                // If the cache is empty or invalid ...
                if (simplex.Count == 0)
                {
                    SimplexVertex v;
                    v.IndexA  = 0;
                    v.IndexB  = 0;
                    v.VertexA = xfA.ToGlobal(proxyA.Vertices[0]);
                    v.VertexB = xfB.ToGlobal(proxyB.Vertices[0]);
// ReSharper disable RedundantCast Necessary for FarPhysics.
                    v.VertexDelta = (LocalPoint)(v.VertexB - v.VertexA);
// ReSharper restore RedundantCast
                    v.Alpha = 1.0f;
                    simplex.Vertices.Item1 = v;
                    simplex.Count          = 1;
                }
            }
Exemple #2
0
        /// <summary>Tests whether two shapes overlap, using their distance proxies.</summary>
        /// <param name="proxyA">The proxy for the first shape.</param>
        /// <param name="proxyB">The proxy for the second shape.</param>
        /// <param name="transformA">The transform of the first shape.</param>
        /// <param name="transformB">The transform of the second shape.</param>
        /// <returns></returns>
        internal static bool TestOverlap(
            DistanceProxy proxyA,
            DistanceProxy proxyB,
            WorldTransform transformA,
            WorldTransform transformB)
        {
            var cache = new SimplexCache {
                Count = 0
            };

            return(Distance(ref cache, proxyA, transformA, proxyB, transformB, true) < 10 * Settings.Epsilon);
        }
        public static float Evaluate(int indexA, int indexB, float t, DistanceProxy proxyA, ref Sweep sweepA,
                                     DistanceProxy proxyB, ref Sweep sweepB, ref Vector2 axis, ref Vector2 localPoint,
                                     SeparationFunctionType type)
        {
            Transform xfA, xfB;

            sweepA.GetTransform(out xfA, t);
            sweepB.GetTransform(out xfB, t);

            switch (type)
            {
            case SeparationFunctionType.Points:
            {
                var localPointA = proxyA.Vertices[indexA];
                var localPointB = proxyB.Vertices[indexB];

                var pointA     = MathUtils.Mul(ref xfA, localPointA);
                var pointB     = MathUtils.Mul(ref xfB, localPointB);
                var separation = Vector2.Dot(pointB - pointA, axis);

                return(separation);
            }

            case SeparationFunctionType.FaceA:
            {
                var normal = MathUtils.Mul(ref xfA.q, axis);
                var pointA = MathUtils.Mul(ref xfA, localPoint);

                var localPointB = proxyB.Vertices[indexB];
                var pointB      = MathUtils.Mul(ref xfB, localPointB);

                var separation = Vector2.Dot(pointB - pointA, normal);
                return(separation);
            }

            case SeparationFunctionType.FaceB:
            {
                var normal = MathUtils.Mul(ref xfB.q, axis);
                var pointB = MathUtils.Mul(ref xfB, localPoint);

                var localPointA = proxyA.Vertices[indexA];
                var pointA      = MathUtils.Mul(ref xfA, localPointA);

                var separation = Vector2.Dot(pointA - pointB, normal);
                return(separation);
            }

            default:
                Debug.Assert(false);
                return(0.0f);
            }
        }
Exemple #4
0
        /// <summary>
        ///     Reads the cache using the specified cache
        /// </summary>
        /// <param name="cache">The cache</param>
        /// <param name="proxyA">The proxy</param>
        /// <param name="transformA">The transform</param>
        /// <param name="proxyB">The proxy</param>
        /// <param name="transformB">The transform</param>
        internal void ReadCache(ref SimplexCache cache, ref DistanceProxy proxyA, ref Transform transformA,
                                ref DistanceProxy proxyB, ref Transform transformB)
        {
            Debug.Assert(cache.Count <= 3);

            // Copy data from cache.
            Count = cache.Count;
            for (int i = 0; i < Count; ++i)
            {
                SimplexVertex v = V[i];
                v.IndexA = cache.IndexA[i];
                v.IndexB = cache.IndexB[i];
                Vector2 wALocal = proxyA.Vertices[v.IndexA];
                Vector2 wBLocal = proxyB.Vertices[v.IndexB];
                v.Wa = MathUtils.Mul(ref transformA, wALocal);
                v.Wb = MathUtils.Mul(ref transformB, wBLocal);
                v.W  = v.Wb - v.Wa;
                v.A  = 0.0f;
                V[i] = v;
            }

            // Compute the new simplex metric, if it is substantially different than
            // old metric then flush the simplex.
            if (Count > 1)
            {
                float metric1 = cache.Metric;
                float metric2 = GetMetric();
                if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < MathConstants.Epsilon)
                {
                    // Reset the simplex.
                    Count = 0;
                }
            }

            // If the cache is empty or invalid ...
            if (Count == 0)
            {
                SimplexVertex v = V[0];
                v.IndexA = 0;
                v.IndexB = 0;
                Vector2 wALocal = proxyA.Vertices[0];
                Vector2 wBLocal = proxyB.Vertices[0];
                v.Wa  = MathUtils.Mul(ref transformA, wALocal);
                v.Wb  = MathUtils.Mul(ref transformB, wBLocal);
                v.W   = v.Wb - v.Wa;
                v.A   = 1.0f;
                V[0]  = v;
                Count = 1;
            }
        }
Exemple #5
0
            public void ReadCache(SimplexCache cache,
                                  DistanceProxy shapeA, ref Transform transformA,
                                  DistanceProxy shapeB, ref Transform transformB)
            {
                Box2DXDebug.Assert(0 <= cache.Count && cache.Count <= 3);

                // Copy data from cache.
                Count = cache.Count;

                for (int i = 0; i < Count; ++i)
                {
                    SimplexVertex v = Vertices[i];
                    v.IndexA = cache.IndexA[i];
                    v.IndexB = cache.IndexB[i];
                    Vec2 wALocal = shapeA.GetVertex(v.IndexA);
                    Vec2 wBLocal = shapeB.GetVertex(v.IndexB);
                    v.WA = Math.Mul(transformA, wALocal);
                    v.WB = Math.Mul(transformB, wBLocal);
                    v.W  = v.WB - v.WA;
                    v.A  = 0.0f;
                }

                // Compute the new simplex metric, if it is substantially different than
                // old metric then flush the simplex.
                if (Count > 1)
                {
                    float metric1 = cache.Metric;
                    float metric2 = GetMetric();
                    if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < Settings.FLT_EPSILON)
                    {
                        // Reset the simplex.
                        Count = 0;
                    }
                }

                // If the cache is empty or invalid ...
                if (Count == 0)
                {
                    SimplexVertex v = Vertices[0];
                    v.IndexA = 0;
                    v.IndexB = 0;
                    Vec2 wALocal = shapeA.GetVertex(0);
                    Vec2 wBLocal = shapeB.GetVertex(0);
                    v.WA  = Math.Mul(transformA, wALocal);
                    v.WB  = Math.Mul(transformB, wBLocal);
                    v.W   = v.WB - v.WA;
                    Count = 1;
                }
            }
Exemple #6
0
        internal static unsafe bool PointDistance <T>(PointDistanceInput input, Collider *collider, ref T collector) where T : struct, ICollector <DistanceHit>
        {
            if (!CollisionFilter.IsCollisionEnabled(input.Filter, collider->Filter))
            {
                return(false);
            }

            // Ensure the query context is initialized.
            input.QueryContext.EnsureIsInitialized();

            var           proxySource = new DistanceProxy(1, &input.Position, 0f);
            DistanceProxy proxyTarget;

            switch (collider->ColliderType)
            {
            case ColliderType.Box:
            case ColliderType.Polygon:
            case ColliderType.Capsule:
            case ColliderType.Circle:
            {
                var convexCollider = (ConvexCollider *)collider;
                proxyTarget = new DistanceProxy(ref convexCollider->m_ConvexHull);
                break;
            }

            case ColliderType.Compound:
                return(PointDistanceCompound(input, (PhysicsCompoundCollider *)collider, ref collector));

            default:
                SafetyChecks.ThrowNotImplementedException();
                return(default);
            }

            var hit = ColliderDistance(PhysicsTransform.Identity, ref proxySource, ref proxyTarget);

            if (hit.Distance < collector.MaxFraction)
            {
                hit.PhysicsBodyIndex = input.QueryContext.PhysicsBodyIndex;
                hit.ColliderKey      = input.QueryContext.ColliderKey;
                hit.Entity           = input.QueryContext.Entity;

                hit.PointA = PhysicsMath.mul(input.QueryContext.LocalToWorldTransform, hit.PointA);
                hit.PointB = PhysicsMath.mul(input.QueryContext.LocalToWorldTransform, hit.PointB);

                return(collector.AddHit(hit));
            }

            return(false);
        }
Exemple #7
0
            public void ReadCache(SimplexCache cache, DistanceProxy proxyA, Transform transformA, DistanceProxy proxyB, Transform transformB)
            {
                Debug.Assert(cache.Count <= 3);

                // Copy data from cache.
                Count = cache.Count;

                for (int i = 0; i < Count; ++i)
                {
                    SimplexVertex v = Vertices[i];
                    v.IndexA = cache.IndexA[i];
                    v.IndexB = cache.IndexB[i];
                    Vec2 wALocal = proxyA.GetVertex(v.IndexA);
                    Vec2 wBLocal = proxyB.GetVertex(v.IndexB);
                    Transform.MulToOutUnsafe(transformA, wALocal, v.WA);
                    Transform.MulToOutUnsafe(transformB, wBLocal, v.WB);
                    v.W.Set(v.WB).SubLocal(v.WA);
                    v.A = 0.0f;
                }

                // Compute the new simplex metric, if it is substantially different than
                // old metric then flush the simplex.
                if (Count > 1)
                {
                    float metric1 = cache.Metric;
                    float metric2 = Metric;
                    if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < Settings.EPSILON)
                    {
                        // Reset the simplex.
                        Count = 0;
                    }
                }

                // If the cache is empty or invalid ...
                if (Count == 0)
                {
                    SimplexVertex v = Vertices[0];
                    v.IndexA = 0;
                    v.IndexB = 0;
                    Vec2 wALocal = proxyA.GetVertex(0);
                    Vec2 wBLocal = proxyB.GetVertex(0);
                    Transform.MulToOutUnsafe(transformA, wALocal, v.WA);
                    Transform.MulToOutUnsafe(transformB, wBLocal, v.WB);
                    v.W.Set(v.WB).SubLocal(v.WA);
                    Count = 1;
                }
            }
Exemple #8
0
        static bool CastConvex(ref ColliderCastInput input, ref DistanceProxy proxyTarget, out ColliderCastHit hit)
        {
            DistanceProxy proxySource;

            var inputColliderBlob = input.Collider;

            switch (inputColliderBlob.Value.ColliderType)
            {
            case ColliderType.Box:
            case ColliderType.Polygon:
            case ColliderType.Capsule:
            case ColliderType.Circle:
            {
                ref var convexHull = ref inputColliderBlob.GetColliderRef <ConvexCollider>().m_ConvexHull;
                proxySource = new DistanceProxy(ref convexHull);
                break;
            }
Exemple #9
0
            void ReadCache(SimplexCache cache, DistanceProxy proxyA, XForm transformA, DistanceProxy proxyB, XForm transformB)
            {
                // Copy data from cache.
                m_count = cache.count;
                SimplexVertex[] vertices = m_v1;
                for (int i = 0; i < m_count; ++i)
                {
                    SimplexVertex v = vertices[i];
                    v.indexA = cache.indexA[i];
                    v.indexB = cache.indexB[i];
                    Vec2 wALocal = proxyA.GetVertex(v.indexA);
                    Vec2 wBLocal = proxyB.GetVertex(v.indexB);
                    v.wA = MathB2.Mul(transformA, wALocal);
                    v.wB = MathB2.Mul(transformB, wBLocal);
                    v.w  = v.wB - v.wA;
                    v.a  = 0.0f;
                }

                // Compute the new simplex metric, if it is substantially different than
                // old metric then flush the simplex.
                if (m_count > 1)
                {
                    float metric1 = cache.metric;
                    float metric2 = GetMetric();
                    if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < Settings.FLT_EPSILON)
                    {
                        // Reset the simplex.
                        m_count = 0;
                    }
                }

                // If the cache is empty or invalid ...
                if (m_count == 0)
                {
                    SimplexVertex v = vertices[0];
                    v.indexA = 0;
                    v.indexB = 0;
                    Vec2 wALocal = proxyA.GetVertex(0);
                    Vec2 wBLocal = proxyB.GetVertex(0);
                    v.wA    = MathB2.Mul(transformA, wALocal);
                    v.wB    = MathB2.Mul(transformB, wBLocal);
                    v.w     = v.wB - v.wA;
                    v.a     = 1.0f;
                    m_count = 1;
                }
            }
        private float distanceWithPhysicObj(PhysicObj obj)
        {
            if (obj == null)
            {
                return(-1);
            }
            Fixture proxyAfix = owner.getBoundsFixture();
            Fixture proxyBfix = obj.getBoundsFixture();

            if (proxyAfix == null || proxyBfix == null)
            {
                return(-1);
            }

            DistanceProxy proxyA = new DistanceProxy();

            proxyA.Set(proxyAfix.Shape, 0);
            DistanceProxy proxyB = new DistanceProxy();

            proxyB.Set(proxyBfix.Shape, 1);
            DistanceInput distInput = new DistanceInput();

            distInput.ProxyA = proxyA;
            distInput.ProxyB = proxyB;
            Transform transformA;

            owner.body.GetTransform(out transformA);
            Transform transformB;

            obj.body.GetTransform(out transformB);
            distInput.TransformA = transformA;
            distInput.TransformB = transformB;

            DistanceOutput distout      = new DistanceOutput();
            SimplexCache   simplexCache = new SimplexCache();

            Distance.ComputeDistance(out distout, out simplexCache, distInput);

            return(distout.Distance);
        }
Exemple #11
0
            public virtual void readCache(SimplexCache cache, DistanceProxy proxyA, Transform transformA, DistanceProxy proxyB, Transform transformB)
            {
                Debug.Assert(cache.count <= 3);

                // Copy data from cache.
                m_count = cache.count;

                for (int i = 0; i < m_count; ++i)
                {
                    SimplexVertex v = vertices[i];
                    v.indexA = cache.indexA[i];
                    v.indexB = cache.indexB[i];
                    Vec2 wALocal = proxyA.getVertex(v.indexA);
                    Vec2 wBLocal = proxyB.getVertex(v.indexB);
                    Transform.mulToOutUnsafe(transformA, wALocal, v.wA);
                    Transform.mulToOutUnsafe(transformB, wBLocal, v.wB);
                    v.w.set_Renamed(v.wB).subLocal(v.wA);
                    v.a = 0.0f;
                }

                // Compute the new simplex metric, if it is substantially different than
                // old metric then flush the simplex.
                if (m_count > 1)
                {
                    float metric1 = cache.metric;
                    float metric2 = Metric;
                    if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < Settings.EPSILON)
                    {
                        // Reset the simplex.
                        m_count = 0;
                    }
                }

                // If the cache is empty or invalid ...
                if (m_count == 0)
                {
                    SimplexVertex v = vertices[0];
                    v.indexA = 0;
                    v.indexB = 0;
                    Vec2 wALocal = proxyA.getVertex(0);
                    Vec2 wBLocal = proxyB.getVertex(0);
                    Transform.mulToOutUnsafe(transformA, wALocal, v.wA);
                    Transform.mulToOutUnsafe(transformB, wBLocal, v.wB);
                    v.w.set_Renamed(v.wB).subLocal(v.wA);
                    m_count = 1;
                }
            }
            public void Initialize(SimplexCache cache,
                DistanceProxy proxyA, Transform transformA,
                DistanceProxy proxyB, Transform transformB)
            {
                _proxyA = proxyA;
                _proxyB = proxyB;
                int count = cache.Count;
                Box2DXDebug.Assert(0 < count && count < 3);

                if (count == 1)
                {
                    _type = Type.Points;
                    Vec2 localPointA = _proxyA.GetVertex(cache.IndexA[0]);
                    Vec2 localPointB = _proxyB.GetVertex(cache.IndexB[0]);
                    Vec2 pointA = Math.Mul(transformA, localPointA);
                    Vec2 pointB = Math.Mul(transformB, localPointB);
                    _axis = pointB - pointA;
                    _axis.Normalize();
                }
                else if (cache.IndexB[0] == cache.IndexB[1])
                {
                    // Two points on A and one on B
                    _type = Type.FaceA;
                    Vec2 localPointA1 = _proxyA.GetVertex(cache.IndexA[0]);
                    Vec2 localPointA2 = _proxyA.GetVertex(cache.IndexA[1]);
                    Vec2 localPointB = _proxyB.GetVertex(cache.IndexB[0]);
                    _localPoint = 0.5f*(localPointA1 + localPointA2);
                    _axis = Vec2.Cross(localPointA2 - localPointA1, 1.0f);
                    _axis.Normalize();

                    Vec2 normal = Math.Mul(transformA.R, _axis);
                    Vec2 pointA = Math.Mul(transformA, _localPoint);
                    Vec2 pointB = Math.Mul(transformB, localPointB);

                    float s = Vec2.Dot(pointB - pointA, normal);
                    if (s < 0.0f)
                    {
                        _axis = -_axis;
                    }
                }
                else if (cache.IndexA[0] == cache.IndexA[1])
                {
                    // Two points on B and one on A.
                    _type = Type.FaceB;
                    Vec2 localPointA = proxyA.GetVertex(cache.IndexA[0]);
                    Vec2 localPointB1 = proxyB.GetVertex(cache.IndexB[0]);
                    Vec2 localPointB2 = proxyB.GetVertex(cache.IndexB[1]);
                    _localPoint = 0.5f*(localPointB1 + localPointB2);
                    _axis = Vec2.Cross(localPointB2 - localPointB1, 1.0f);
                    _axis.Normalize();

                    Vec2 normal = Math.Mul(transformB.R, _axis);
                    Vec2 pointB = Math.Mul(transformB, _localPoint);
                    Vec2 pointA = Math.Mul(transformA, localPointA);

                    float s = Vec2.Dot(pointA - pointB, normal);
                    if (s < 0.0f)
                    {
                        _axis = -_axis;
                    }
                }
                else
                {
                    // Two points on B and two points on A.
                    // The faces are parallel.
                    Vec2 localPointA1 = _proxyA.GetVertex(cache.IndexA[0]);
                    Vec2 localPointA2 = _proxyA.GetVertex(cache.IndexA[1]);
                    Vec2 localPointB1 = _proxyB.GetVertex(cache.IndexB[0]);
                    Vec2 localPointB2 = _proxyB.GetVertex(cache.IndexB[1]);

                    Vec2 pA = Math.Mul(transformA, localPointA1);
                    Vec2 dA = Math.Mul(transformA.R, localPointA2 - localPointA1);
                    Vec2 pB = Math.Mul(transformB, localPointB1);
                    Vec2 dB = Math.Mul(transformB.R, localPointB2 - localPointB1);

                    float a = Vec2.Dot(dA, dA);
                    float e = Vec2.Dot(dB, dB);
                    Vec2 r = pA - pB;
                    float c = Vec2.Dot(dA, r);
                    float f = Vec2.Dot(dB, r);

                    float b = Vec2.Dot(dA, dB);
                    float denom = a*e - b*b;

                    float s = 0.0f;
                    if (denom != 0.0f)
                    {
                        s = Math.Clamp((b*f - c*e)/denom, 0.0f, 1.0f);
                    }

                    float t = (b*s + f)/e;

                    if (t < 0.0f)
                    {
                        t = 0.0f;
                        s = Math.Clamp(-c/a, 0.0f, 1.0f);
                    }
                    else if (t > 1.0f)
                    {
                        t = 1.0f;
                        s = Math.Clamp((b - c)/a, 0.0f, 1.0f);
                    }

                    Vec2 localPointA = localPointA1 + s*(localPointA2 - localPointA1);
                    Vec2 localPointB = localPointB1 + t*(localPointB2 - localPointB1);

                    if (s == 0.0f || s == 1.0f)
                    {
                        _type = Type.FaceB;
                        _axis = Vec2.Cross(localPointB2 - localPointB1, 1.0f);
                        _axis.Normalize();

                        _localPoint = localPointB;

                        Vec2 normal = Math.Mul(transformB.R, _axis);
                        Vec2 pointA = Math.Mul(transformA, localPointA);
                        Vec2 pointB = Math.Mul(transformB, localPointB);

                        float sgn = Vec2.Dot(pointA - pointB, normal);
                        if (sgn < 0.0f)
                        {
                            _axis = -_axis;
                        }
                    }
                    else
                    {
                        _type = Type.FaceA;
                        _axis = Vec2.Cross(localPointA2 - localPointA1, 1.0f);
                        _axis.Normalize();

                        _localPoint = localPointA;

                        Vec2 normal = Math.Mul(transformA.R, _axis);
                        Vec2 pointA = Math.Mul(transformA, localPointA);
                        Vec2 pointB = Math.Mul(transformB, localPointB);

                        float sgn = Vec2.Dot(pointB - pointA, normal);
                        if (sgn < 0.0f)
                        {
                            _axis = -_axis;
                        }
                    }
                }
            }
Exemple #13
0
        /// <summary>
        /// Compute the closest points between two shapes. Supports any combination of:
        /// b2CircleShape, b2PolygonShape, b2EdgeShape. The simplex cache is input/output.
        /// On the first call set b2SimplexCache.count to zero.
        /// </summary>
        public static void Distance(out DistanceOutput output,
                                    SimplexCache cache,
                                    DistanceInput input)
        {
            ++GjkCalls;

            DistanceProxy proxyA = input.proxyA;
            DistanceProxy proxyB = input.proxyB;

            Transform transformA = input.TransformA;
            Transform transformB = input.TransformB;

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

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

            // Get simplex vertices as an array.
            SimplexVertex[] vertices   = simplex.Vertices;
            const 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], saveB = new int[3];
            int   saveCount = 0;

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

            // Main iteration loop.
            int iter = 0;

            while (iter < k_maxIters)
            {
                // Copy simplex so we can identify duplicates.
                saveCount = simplex.Count;
                for (int i = 0; i < saveCount; ++i)
                {
                    saveA[i] = vertices[i].IndexA;
                    saveB[i] = vertices[i].IndexB;
                }

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

                case 2:
                    simplex.Solve2();
                    break;

                case 3:
                    simplex.Solve3();
                    break;

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

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

                // Compute closest point.
                Vec2  p           = simplex.GetClosestPoint();
                float distanceSqr = p.LengthSquared();

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

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

                // Ensure the search direction is numerically fit.
                if (d.LengthSquared() < Settings.FLT_EPSILON * Settings.FLT_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.
                SimplexVertex vertex = vertices[simplex.Count];
                vertex.IndexA = proxyA.GetSupport(Math.MulT(transformA.R, -d));
                vertex.WA     = Math.Mul(transformA, proxyA.GetVertex(vertex.IndexA));

                vertex.IndexB = proxyB.GetSupport(Math.MulT(transformB.R, d));
                vertex.WB     = Math.Mul(transformB, proxyB.GetVertex(vertex.IndexB));
                vertex.W      = vertex.WB - vertex.WA;

                // Iteration count is equated to the number of support point calls.
                ++iter;
                ++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.Count;
            }

            GjkMaxIters = Math.Max(GjkMaxIters, iter);

            // Prepare output.
            simplex.GetWitnessPoints(out output.PointA, out output.PointB);
            output.Distance   = Vec2.Distance(output.PointA, output.PointB);
            output.Iterations = iter;

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

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

                if (output.Distance > rA + rB && output.Distance > Settings.FLT_EPSILON)
                {
                    // Shapes are still no overlapped.
                    // Move the witness points to the outer surface.
                    output.Distance -= rA + rB;
                    Vec2 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.
                    Vec2 p = 0.5f * (output.PointA + output.PointB);
                    output.PointA   = p;
                    output.PointB   = p;
                    output.Distance = 0.0f;
                }
            }
        }
Exemple #14
0
        /// <summary>
        ///     Computes the distance between two shapes represented by the specified proxies, positioned as defined by the
        ///     specified transforms. The cache is read-write and will be updated for future calls.
        /// </summary>
        private static float Distance(
            ref SimplexCache cache,
            DistanceProxy proxyA,
            WorldTransform xfA,
            DistanceProxy proxyB,
            WorldTransform xfB,
            bool useRadii = false)
        {
            // Initialize the simplex.
            Simplex simplex;

            Simplex.ReadCache(out simplex, cache, xfA, proxyA, xfB, proxyB);

            // These store the vertices of the last simplex so that we
            // can check for duplicates and prevent cycling.
            var saveA = new FixedArray3 <int>();
            var saveB = new FixedArray3 <int>();

            // Main iteration loop.
            for (var i = 0; i < 20; ++i)
            {
                // Copy simplex so we can identify duplicates.
                var saveCount = simplex.Count;
                for (var j = 0; j < saveCount; ++j)
                {
                    saveA[j] = simplex.Vertices[j].IndexA;
                    saveB[j] = simplex.Vertices[j].IndexB;
                }

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

                case 2:
                    simplex.Solve2();
                    break;

                case 3:
                    simplex.Solve3();
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }

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

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

                // Ensure the search direction is numerically fit.
                if (d.LengthSquared() < Settings.Epsilon * Settings.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.
                SimplexVertex v;
                v.IndexA  = proxyA.GetSupport(-xfA.Rotation * -d);
                v.IndexB  = proxyB.GetSupport(-xfB.Rotation * d);
                v.VertexA = xfA.ToGlobal(proxyA.Vertices[v.IndexA]);
                v.VertexB = xfB.ToGlobal(proxyB.Vertices[v.IndexB]);
// ReSharper disable RedundantCast Necessary for FarPhysics.
                v.VertexDelta = (LocalPoint)(v.VertexB - v.VertexA);
// ReSharper restore RedundantCast
                v.Alpha = 0;
                simplex.Vertices[simplex.Count] = v;

                // Check for duplicate support points. This is the main termination criteria.
                var duplicate = false;
                for (var j = 0; j < saveCount; ++j)
                {
                    if (v.IndexA == saveA[j] && v.IndexB == saveB[j])
                    {
                        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.Count;
            }

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

            // Prepare output.
            var distance = simplex.GetWitnessPointDistance();

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

                if (distance > rA + rB && distance > Settings.Epsilon)
                {
                    // Shapes are still not overlapped.
                    return(distance - (rA + rB));
                }
                // Shapes are overlapped when radii are considered.
                return(0.0f);
            }

            return(distance);
        }
            public void Initialize(SimplexCache cache,
                                   DistanceProxy proxyA, Transform transformA,
                                   DistanceProxy proxyB, Transform transformB)
            {
                _proxyA = proxyA;
                _proxyB = proxyB;
                int count = cache.Count;

                Box2DXDebug.Assert(0 < count && count < 3);

                if (count == 1)
                {
                    _type = Type.Points;
                    Vec2 localPointA = _proxyA.GetVertex(cache.IndexA[0]);
                    Vec2 localPointB = _proxyB.GetVertex(cache.IndexB[0]);
                    Vec2 pointA      = Math.Mul(transformA, localPointA);
                    Vec2 pointB      = Math.Mul(transformB, localPointB);
                    _axis = pointB - pointA;
                    _axis.Normalize();
                }
                else if (cache.IndexB[0] == cache.IndexB[1])
                {
                    // Two points on A and one on B
                    _type = Type.FaceA;
                    Vec2 localPointA1 = _proxyA.GetVertex(cache.IndexA[0]);
                    Vec2 localPointA2 = _proxyA.GetVertex(cache.IndexA[1]);
                    Vec2 localPointB  = _proxyB.GetVertex(cache.IndexB[0]);
                    _localPoint = 0.5f * (localPointA1 + localPointA2);
                    _axis       = Vec2.Cross(localPointA2 - localPointA1, 1.0f);
                    _axis.Normalize();

                    Vec2 normal = Math.Mul(transformA.R, _axis);
                    Vec2 pointA = Math.Mul(transformA, _localPoint);
                    Vec2 pointB = Math.Mul(transformB, localPointB);

                    float s = Vec2.Dot(pointB - pointA, normal);
                    if (s < 0.0f)
                    {
                        _axis = -_axis;
                    }
                }
                else if (cache.IndexA[0] == cache.IndexA[1])
                {
                    // Two points on B and one on A.
                    _type = Type.FaceB;
                    Vec2 localPointA  = proxyA.GetVertex(cache.IndexA[0]);
                    Vec2 localPointB1 = proxyB.GetVertex(cache.IndexB[0]);
                    Vec2 localPointB2 = proxyB.GetVertex(cache.IndexB[1]);
                    _localPoint = 0.5f * (localPointB1 + localPointB2);
                    _axis       = Vec2.Cross(localPointB2 - localPointB1, 1.0f);
                    _axis.Normalize();

                    Vec2 normal = Math.Mul(transformB.R, _axis);
                    Vec2 pointB = Math.Mul(transformB, _localPoint);
                    Vec2 pointA = Math.Mul(transformA, localPointA);

                    float s = Vec2.Dot(pointA - pointB, normal);
                    if (s < 0.0f)
                    {
                        _axis = -_axis;
                    }
                }
                else
                {
                    // Two points on B and two points on A.
                    // The faces are parallel.
                    Vec2 localPointA1 = _proxyA.GetVertex(cache.IndexA[0]);
                    Vec2 localPointA2 = _proxyA.GetVertex(cache.IndexA[1]);
                    Vec2 localPointB1 = _proxyB.GetVertex(cache.IndexB[0]);
                    Vec2 localPointB2 = _proxyB.GetVertex(cache.IndexB[1]);

                    Vec2 pA = Math.Mul(transformA, localPointA1);
                    Vec2 dA = Math.Mul(transformA.R, localPointA2 - localPointA1);
                    Vec2 pB = Math.Mul(transformB, localPointB1);
                    Vec2 dB = Math.Mul(transformB.R, localPointB2 - localPointB1);

                    float a = Vec2.Dot(dA, dA);
                    float e = Vec2.Dot(dB, dB);
                    Vec2  r = pA - pB;
                    float c = Vec2.Dot(dA, r);
                    float f = Vec2.Dot(dB, r);

                    float b     = Vec2.Dot(dA, dB);
                    float denom = a * e - b * b;

                    float s = 0.0f;
                    if (denom != 0.0f)
                    {
                        s = Math.Clamp((b * f - c * e) / denom, 0.0f, 1.0f);
                    }

                    float t = (b * s + f) / e;

                    if (t < 0.0f)
                    {
                        t = 0.0f;
                        s = Math.Clamp(-c / a, 0.0f, 1.0f);
                    }
                    else if (t > 1.0f)
                    {
                        t = 1.0f;
                        s = Math.Clamp((b - c) / a, 0.0f, 1.0f);
                    }

                    Vec2 localPointA = localPointA1 + s * (localPointA2 - localPointA1);
                    Vec2 localPointB = localPointB1 + t * (localPointB2 - localPointB1);

                    if (s == 0.0f || s == 1.0f)
                    {
                        _type = Type.FaceB;
                        _axis = Vec2.Cross(localPointB2 - localPointB1, 1.0f);
                        _axis.Normalize();

                        _localPoint = localPointB;

                        Vec2 normal = Math.Mul(transformB.R, _axis);
                        Vec2 pointA = Math.Mul(transformA, localPointA);
                        Vec2 pointB = Math.Mul(transformB, localPointB);

                        float sgn = Vec2.Dot(pointA - pointB, normal);
                        if (sgn < 0.0f)
                        {
                            _axis = -_axis;
                        }
                    }
                    else
                    {
                        _type = Type.FaceA;
                        _axis = Vec2.Cross(localPointA2 - localPointA1, 1.0f);
                        _axis.Normalize();

                        _localPoint = localPointA;

                        Vec2 normal = Math.Mul(transformA.R, _axis);
                        Vec2 pointA = Math.Mul(transformA, localPointA);
                        Vec2 pointB = Math.Mul(transformB, localPointB);

                        float sgn = Vec2.Dot(pointB - pointA, normal);
                        if (sgn < 0.0f)
                        {
                            _axis = -_axis;
                        }
                    }
                }
            }
        public static void Initialize(ref SimplexCache cache, DistanceProxy proxyA, ref Sweep sweepA, DistanceProxy proxyB, ref Sweep sweepB, float t1, out Vector2 axis, out Vector2 localPoint, out SeparationFunctionType type)
        {
            int count = cache.Count;

            Debug.Assert(0 < count && count < 3);

            sweepA.GetTransform(out Transform xfA, t1);
            sweepB.GetTransform(out Transform xfB, t1);

            if (count == 1)
            {
                localPoint = Vector2.Zero;
                type       = SeparationFunctionType.Points;
                Vector2 localPointA = proxyA._vertices[cache.IndexA[0]];
                Vector2 localPointB = proxyB._vertices[cache.IndexB[0]];
                Vector2 pointA      = MathUtils.Mul(ref xfA, localPointA);
                Vector2 pointB      = MathUtils.Mul(ref xfB, localPointB);
                axis = pointB - pointA;
                axis.Normalize();
            }
            else if (cache.IndexA[0] == cache.IndexA[1])
            {
                // Two points on B and one on A.
                type = SeparationFunctionType.FaceB;
                Vector2 localPointB1 = proxyB._vertices[cache.IndexB[0]];
                Vector2 localPointB2 = proxyB._vertices[cache.IndexB[1]];

                Vector2 a = localPointB2 - localPointB1;
                axis = new Vector2(a.Y, -a.X);
                axis.Normalize();
                Vector2 normal = MathUtils.Mul(ref xfB.q, axis);

                localPoint = 0.5f * (localPointB1 + localPointB2);
                Vector2 pointB = MathUtils.Mul(ref xfB, localPoint);

                Vector2 localPointA = proxyA._vertices[cache.IndexA[0]];
                Vector2 pointA      = MathUtils.Mul(ref xfA, localPointA);

                float s = Vector2.Dot(pointA - pointB, normal);
                if (s < 0.0f)
                {
                    axis = -axis;
                }
            }
            else
            {
                // Two points on A and one or two points on B.
                type = SeparationFunctionType.FaceA;
                Vector2 localPointA1 = proxyA._vertices[cache.IndexA[0]];
                Vector2 localPointA2 = proxyA._vertices[cache.IndexA[1]];

                Vector2 a = localPointA2 - localPointA1;
                axis = new Vector2(a.Y, -a.X);
                axis.Normalize();
                Vector2 normal = MathUtils.Mul(ref xfA.q, axis);

                localPoint = 0.5f * (localPointA1 + localPointA2);
                Vector2 pointA = MathUtils.Mul(ref xfA, localPoint);

                Vector2 localPointB = proxyB._vertices[cache.IndexB[0]];
                Vector2 pointB      = MathUtils.Mul(ref xfB, localPointB);

                float s = Vector2.Dot(pointB - pointA, normal);
                if (s < 0.0f)
                {
                    axis = -axis;
                }
            }

            //Velcro note: the returned value that used to be here has been removed, as it was not used.
        }
Exemple #17
0
        // CCD via the local separating axis method. This seeks progression
        // by computing the largest time at which separation is maintained.
        public static bool TimeOfImpact(
            DistanceProxy proxyA,
            DistanceProxy proxyB,
            Sweep sweepA,
            Sweep sweepB,
            float tMax,
            out float t)
        {
            // Large rotations can make the root finder fail, so we normalize the
            // sweep angles.
            sweepA.Normalize();
            sweepB.Normalize();

            var totalRadius = proxyA.Radius + proxyB.Radius;
            var target      = System.Math.Max(Settings.LinearSlop, totalRadius - 3.0f * Settings.LinearSlop);

            const float tolerance = 0.25f * Settings.LinearSlop;

            System.Diagnostics.Debug.Assert(target > tolerance);

            // Prepare input for distance query.
            var cache = new SimplexCache {
                Count = 0
            };

            // The outer loop progressively attempts to compute new separating axes.
            // This loop terminates when an axis is repeated (no progress is made).
            var t1 = 0.0f;

            for (var iter = 0; iter < 20; ++iter)
            {
                WorldTransform xfA, xfB;
                sweepA.GetTransform(out xfA, t1);
                sweepB.GetTransform(out xfB, t1);

                // Get the distance between shapes. We can also use the results
                // to get a separating axis.
                var distance = Distance(ref cache, proxyA, xfA, proxyB, xfB);

                // If the shapes are overlapped, we give up on continuous collision.
                if (distance <= 0.0f)
                {
                    // Failure!
                    t = 0.0f;
                    return(false);
                }

                if (distance < target + tolerance)
                {
                    // Victory!
                    t = t1;
                    return(true);
                }

                // Initialize the separating axis.
                SeparationFunction fcn;
                SeparationFunction.Initialize(out fcn, cache, proxyA, proxyB, sweepA, sweepB, t1);

                // Compute the TOI on the separating axis. We do this by successively
                // resolving the deepest point. This loop is bounded by the number of vertices.
                var t2 = tMax;
                for (var pushBackIter = 0; pushBackIter < Settings.MaxPolygonVertices; ++pushBackIter)
                {
                    // Find the deepest point at t2. Store the witness point indices.
                    int indexA, indexB;
                    var s2 = fcn.FindMinSeparation(out indexA, out indexB, t2);

                    // Is the final configuration separated?
                    if (s2 > target + tolerance)
                    {
                        // Victory!
                        t = tMax;
                        return(false);
                    }

                    // Has the separation reached tolerance?
                    if (s2 > target - tolerance)
                    {
                        // Advance the sweeps
                        t1 = t2;
                        break;
                    }

                    // Compute the initial separation of the witness points.
                    var s1 = fcn.Evaluate(indexA, indexB, t1);

                    // Check for initial overlap. This might happen if the root finder
                    // runs out of iterations.
                    if (s1 < target - tolerance)
                    {
                        t = t1;
                        return(false);
                    }

                    // Check for touching
                    if (s1 <= target + tolerance)
                    {
                        // Victory! t1 should hold the TOI (could be 0.0).
                        t = t1;
                        return(true);
                    }

                    // Compute 1D root of: f(x) - target = 0
                    float a1 = t1, a2 = t2;
                    for (var rootIterCount = 0; rootIterCount < 50; ++rootIterCount)
                    {
                        // Use a mix of the secant rule and bisection.
                        float u;
                        if ((rootIterCount & 1) != 0)
                        {
                            // Secant rule to improve convergence.
                            u = a1 + (target - s1) * (a2 - a1) / (s2 - s1);
                        }
                        else
                        {
                            // Bisection to guarantee progress.
                            u = 0.5f * (a1 + a2);
                        }

                        var s = fcn.Evaluate(indexA, indexB, u);

                        if (System.Math.Abs(s - target) < tolerance)
                        {
                            // t2 holds a tentative value for t1
                            t2 = u;
                            break;
                        }

                        // Ensure we continue to bracket the root.
                        if (s > target)
                        {
                            a1 = u;
                            s1 = s;
                        }
                        else
                        {
                            a2 = u;
                            s2 = s;
                        }
                    }
                }
            }

            // Root finder got stuck. Semi-victory.
            t = t1;
            return(false);
        }
Exemple #18
0
        internal static unsafe DistanceHit ColliderDistance(PhysicsTransform transformA, ref DistanceProxy proxyA, ref DistanceProxy proxyB)
        {
            var simplex = new Simplex();

            simplex.Reset(transformA, proxyA, proxyB);

            var inverseRotationA = math.transpose(transformA.Rotation);

            var vertices = &simplex.Vertex1;

            Simplex.VertexIndexTriple saveA;
            Simplex.VertexIndexTriple saveB;

            var iteration = 0;

            while (iteration < PhysicsSettings.Constants.MaxGJKInterations)
            {
                // Copy simplex so we can identify duplicates.
                var saveCount = simplex.Count;
                for (var i = 0; i < saveCount; ++i)
                {
                    saveA.Index[i] = vertices[i].IndexA;
                    saveB.Index[i] = vertices[i].IndexB;
                }

                switch (saveCount)
                {
                case 1:
                    break;

                case 2:
                    simplex.Solve2();
                    break;

                case 3:
                    simplex.Solve3();
                    break;

                default:
                    SafetyChecks.ThrowInvalidOperationException("Simplex has invalid count.");
                    return(default);
                }

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

                // Get search direction.
                var direction = simplex.GetSearchDirection();

                // Ensure the search direction is numerically fit.
                if (math.lengthsq(direction) < float.Epsilon * float.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.
                var vertex = vertices + simplex.Count;
                vertex->IndexA   = proxyA.GetSupport(PhysicsMath.mul(inverseRotationA, -direction));
                vertex->SupportA = PhysicsMath.mul(transformA, proxyA.Vertices[vertex->IndexA]);
                vertex->IndexB   = proxyB.GetSupport(direction);
                vertex->SupportB = proxyB.Vertices[vertex->IndexB];
                vertex->W        = vertex->SupportB - vertex->SupportA;

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

                // Check for duplicate support points. This is the main termination criteria.
                var duplicate = false;
                for (var i = 0; i < saveCount; ++i)
                {
                    if (vertex->IndexA == saveA.Index[i] && vertex->IndexB == saveB.Index[i])
                    {
                        duplicate = true;
                        break;
                    }
                }

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

                // New vertex is okay and needed.
                simplex.Count++;
            }

            // Prepare result.
            var pointA = float2.zero;
            var pointB = float2.zero;

            simplex.GetWitnessPoints(ref pointA, ref pointB);

            var distance = math.distance(pointA, pointB);
            var radiusA  = proxyA.ConvexRadius;
            var radiusB  = proxyB.ConvexRadius;

            if (distance > (radiusA + radiusB) && distance > float.Epsilon)
            {
                // Shapes not overlapped.
                // Move the witness points to the outer surface.
                distance -= radiusA + radiusB;
                var normal = math.normalize(pointB - pointA);
                pointA += radiusA * normal;
                pointB -= radiusB * normal;
            }
            else
            {
                // Shapes are overlapped.
                // Move the witness points to the middle.
                pointA   = pointB = 0.5f * (pointA + pointB);
                distance = 0f;
            }

            return(new DistanceHit
            {
                PointA = pointA,
                PointB = pointB,
                Fraction = distance
            });
        }
Exemple #19
0
            public void ReadCache(SimplexCache cache, DistanceProxy proxyA, Transform transformA, DistanceProxy proxyB, Transform transformB)
            {
                Debug.Assert(cache.Count <= 3);

                // Copy data from cache.
                Count = cache.Count;

                for (int i = 0; i < Count; ++i)
                {
                    SimplexVertex v = Vertices[i];
                    v.IndexA = cache.IndexA[i];
                    v.IndexB = cache.IndexB[i];
                    Vec2 wALocal = proxyA.GetVertex(v.IndexA);
                    Vec2 wBLocal = proxyB.GetVertex(v.IndexB);
                    Transform.MulToOutUnsafe(transformA, wALocal, v.WA);
                    Transform.MulToOutUnsafe(transformB, wBLocal, v.WB);
                    v.W.Set(v.WB).SubLocal(v.WA);
                    v.A = 0.0f;
                }

                // Compute the new simplex metric, if it is substantially different than
                // old metric then flush the simplex.
                if (Count > 1)
                {
                    float metric1 = cache.Metric;
                    float metric2 = Metric;
                    if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < Settings.EPSILON)
                    {
                        // Reset the simplex.
                        Count = 0;
                    }
                }

                // If the cache is empty or invalid ...
                if (Count == 0)
                {
                    SimplexVertex v = Vertices[0];
                    v.IndexA = 0;
                    v.IndexB = 0;
                    Vec2 wALocal = proxyA.GetVertex(0);
                    Vec2 wBLocal = proxyB.GetVertex(0);
                    Transform.MulToOutUnsafe(transformA, wALocal, v.WA);
                    Transform.MulToOutUnsafe(transformB, wBLocal, v.WB);
                    v.W.Set(v.WB).SubLocal(v.WA);
                    Count = 1;
                }
            }
Exemple #20
0
        /// <summary>
        /// Compute the closest points between two shapes. Supports any combination of: CircleShape and
        /// PolygonShape. The simplex cache is input/output. On the first call set SimplexCache.count to
        /// zero.
        /// </summary>
        /// <param name="output"></param>
        /// <param name="cache"></param>
        /// <param name="input"></param>
        public void GetDistance(DistanceOutput output, SimplexCache cache, DistanceInput input)
        {
            GJK_CALLS++;

            DistanceProxy proxyA = input.ProxyA;
            DistanceProxy proxyB = input.ProxyB;

            Transform transformA = input.TransformA;
            Transform transformB = input.TransformB;

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

            // Get simplex vertices as an array.
            SimplexVertex[] vertices = simplex.Vertices;

            // These store the vertices of the last simplex so that we
            // can check for duplicates and prevent cycling.
            // (pooled above)

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

            // Main iteration loop
            int iter = 0;

            while (iter < GJK_MAX_ITERS)
            {
                // Copy simplex so we can identify duplicates.
                int saveCount = simplex.Count;
                for (int i = 0; i < saveCount; i++)
                {
                    saveA[i] = vertices[i].IndexA;
                    saveB[i] = vertices[i].IndexB;
                }

                switch (simplex.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.Count == 3)
                {
                    break;
                }

                // Compute closest point.
                simplex.GetClosestPoint(closestPoint);
                float distanceSqr2 = closestPoint.LengthSquared();

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

                // get search direction;
                simplex.GetSearchDirection(d);

                // Ensure the search direction is numerically fit.
                if (d.LengthSquared() < Settings.EPSILON * Settings.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;
                }

                /*
                 * SimplexVertex* vertex = vertices + simplex.m_count; vertex.indexA =
                 * proxyA.GetSupport(MulT(transformA.R, -d)); vertex.wA = Mul(transformA,
                 * proxyA.GetVertex(vertex.indexA)); Vec2 wBLocal; vertex.indexB =
                 * proxyB.GetSupport(MulT(transformB.R, d)); vertex.wB = Mul(transformB,
                 * proxyB.GetVertex(vertex.indexB)); vertex.w = vertex.wB - vertex.wA;
                 */

                // Compute a tentative new simplex vertex using support points.
                SimplexVertex vertex = vertices[simplex.Count];

                Rot.MulTransUnsafe(transformA.Q, d.NegateLocal(), temp);
                vertex.IndexA = proxyA.GetSupport(temp);
                Transform.MulToOutUnsafe(transformA, proxyA.GetVertex(vertex.IndexA), vertex.WA);
                // Vec2 wBLocal;
                Rot.MulTransUnsafe(transformB.Q, d.NegateLocal(), temp);
                vertex.IndexB = proxyB.GetSupport(temp);
                Transform.MulToOutUnsafe(transformB, proxyB.GetVertex(vertex.IndexB), vertex.WB);
                vertex.W.Set(vertex.WB).SubLocal(vertex.WA);

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

                // 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.Count;
            }

            GJK_MAX_ITERS = MathUtils.Max(GJK_MAX_ITERS, iter);

            // Prepare output.
            simplex.GetWitnessPoints(output.PointA, output.PointB);
            output.Distance   = MathUtils.Distance(output.PointA, output.PointB);
            output.Iterations = iter;

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

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

                if (output.Distance > rA + rB && output.Distance > Settings.EPSILON)
                {
                    // Shapes are still no overlapped.
                    // Move the witness points to the outer surface.
                    output.Distance -= (rA + rB);
                    normal.Set(output.PointB).SubLocal(output.PointA);
                    normal.Normalize();
                    temp.Set(normal).MulLocal(rA);
                    output.PointA.AddLocal(temp);
                    temp.Set(normal).MulLocal(rB);
                    output.PointB.SubLocal(temp);
                }
                else
                {
                    // Shapes are overlapped when radii are considered.
                    // Move the witness points to the middle.
                    // Vec2 p = 0.5f * (output.pointA + output.pointB);
                    output.PointA.AddLocal(output.PointB).MulLocal(.5f);
                    output.PointB.Set(output.PointA);
                    output.Distance = 0.0f;
                }
            }
        }
            public void ReadCache(SimplexCache cache,
                            DistanceProxy shapeA, ref Transform transformA,
                            DistanceProxy shapeB, ref Transform transformB)
            {
                Box2DXDebug.Assert(0 <= cache.Count && cache.Count <= 3);

                // Copy data from cache.
                Count = cache.Count;

                for (int i = 0; i < Count; ++i)
                {
                    SimplexVertex v = Vertices[i];
                    v.IndexA = cache.IndexA[i];
                    v.IndexB = cache.IndexB[i];
                    Vec2 wALocal = shapeA.GetVertex(v.IndexA);
                    Vec2 wBLocal = shapeB.GetVertex(v.IndexB);
                    v.WA = Math.Mul(transformA, wALocal);
                    v.WB = Math.Mul(transformB, wBLocal);
                    v.W = v.WB - v.WA;
                    v.A = 0.0f;
                }

                // Compute the new simplex metric, if it is substantially different than
                // old metric then flush the simplex.
                if (Count > 1)
                {
                    float metric1 = cache.Metric;
                    float metric2 = GetMetric();
                    if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < Settings.FLT_EPSILON)
                    {
                        // Reset the simplex.
                        Count = 0;
                    }
                }

                // If the cache is empty or invalid ...
                if (Count == 0)
                {
                    SimplexVertex v = Vertices[0];
                    v.IndexA = 0;
                    v.IndexB = 0;
                    Vec2 wALocal = shapeA.GetVertex(0);
                    Vec2 wBLocal = shapeB.GetVertex(0);
                    v.WA = Math.Mul(transformA, wALocal);
                    v.WB = Math.Mul(transformB, wBLocal);
                    v.W = v.WB - v.WA;
                    Count = 1;
                }
            }
Exemple #22
0
        // TODO_ERIN might not need to return the separation

        public float Initialize(
            ref SimplexCache cache,
            DistanceProxy proxyA,
            in Sweep sweepA,
Exemple #23
0
            public static void Initialize(
                out SeparationFunction f,
                SimplexCache cache,
                DistanceProxy proxyA,
                DistanceProxy proxyB,
                Sweep sweepA,
                Sweep sweepB,
                float t1)
            {
                System.Diagnostics.Debug.Assert(0 < cache.Count && cache.Count < 3);

                f._proxyA = proxyA;
                f._proxyB = proxyB;

                f._sweepA = sweepA;
                f._sweepB = sweepB;

                WorldTransform xfA, xfB;

                f._sweepA.GetTransform(out xfA, t1);
                f._sweepB.GetTransform(out xfB, t1);

                if (cache.Count == 1)
                {
                    f._type = Type.Points;
                    var localPointA = f._proxyA.Vertices[cache.IndexA.Item1];
                    var localPointB = f._proxyB.Vertices[cache.IndexB.Item1];
                    var pointA      = xfA.ToGlobal(localPointA);
                    var pointB      = xfB.ToGlobal(localPointB);
// ReSharper disable RedundantCast Necessary for FarPhysics.
                    f._axis = (Vector2)(pointB - pointA);
// ReSharper restore RedundantCast
                    f._axis.Normalize();
                    f._localPoint = LocalPoint.Zero;
                }
                else if (cache.IndexA.Item1 == cache.IndexA.Item2)
                {
                    // Two points on B and one on A.
                    f._type = Type.FaceB;
                    var localPointB1 = proxyB.Vertices[cache.IndexB.Item1];
                    var localPointB2 = proxyB.Vertices[cache.IndexB.Item2];

                    f._axis = Vector2Util.Cross(localPointB2 - localPointB1, 1.0f);
                    f._axis.Normalize();
                    var normal = xfB.Rotation * f._axis;

                    f._localPoint = 0.5f * (localPointB1 + localPointB2);
                    var pointB = xfB.ToGlobal(f._localPoint);

                    var localPointA = proxyA.Vertices[cache.IndexA[0]];
                    var pointA      = xfA.ToGlobal(localPointA);

// ReSharper disable RedundantCast Necessary for FarPhysics.
                    var s = Vector2Util.Dot((Vector2)(pointA - pointB), normal);
// ReSharper restore RedundantCast
                    if (s < 0.0f)
                    {
                        f._axis = -f._axis;
                    }
                }
                else
                {
                    // Two points on A and one or two points on B.
                    f._type = Type.FaceA;
                    var localPointA1 = f._proxyA.Vertices[cache.IndexA.Item1];
                    var localPointA2 = f._proxyA.Vertices[cache.IndexA.Item2];

                    f._axis = Vector2Util.Cross(localPointA2 - localPointA1, 1.0f);
                    f._axis.Normalize();
                    var normal = xfA.Rotation * f._axis;

                    f._localPoint = 0.5f * (localPointA1 + localPointA2);
                    var pointA = xfA.ToGlobal(f._localPoint);

                    var localPointB = f._proxyB.Vertices[cache.IndexB[0]];
                    var pointB      = xfB.ToGlobal(localPointB);

// ReSharper disable RedundantCast Necessary for FarPhysics.
                    var s = Vector2Util.Dot((Vector2)(pointB - pointA), normal);
// ReSharper restore RedundantCast
                    if (s < 0.0f)
                    {
                        f._axis = -f._axis;
                    }
                }
            }
Exemple #24
0
        static unsafe bool CastCollider(
            Ray ray, ref float2x2 rotation,
            ref DistanceProxy proxySource, ref DistanceProxy proxyTarget,
            out ColliderCastHit hit)
        {
            hit = default;

            var transformSource = new PhysicsTransform
            {
                Translation = ray.Origin,
                Rotation    = rotation
            };

            // Check we're not initially overlapped.
            if ((proxySource.VertexCount < 3 || proxyTarget.VertexCount < 3) &&
                OverlapQueries.OverlapConvexConvex(ref transformSource, ref proxySource, ref proxyTarget))
            {
                return(false);
            }

            // B = Source
            // A = Target

            var radiusSource = proxySource.ConvexRadius;
            var radiusTarget = proxyTarget.ConvexRadius;
            var totalRadius  = radiusSource + radiusTarget;

            var invRotation = math.inverse(rotation);

            var sweepDirection = ray.Displacement;
            var normal         = float2.zero;
            var lambda         = 0.0f;

            // Initialize the simplex.
            var simplex = new Simplex();

            simplex.Count = 0;
            var vertices = &simplex.Vertex1;

            // Get a support point in the inverse direction.
            var indexTarget   = proxyTarget.GetSupport(-sweepDirection);
            var supportTarget = proxyTarget.Vertices[indexTarget];
            var indexSource   = proxySource.GetSupport(PhysicsMath.mul(invRotation, sweepDirection));
            var supportSource = PhysicsMath.mul(transformSource, proxySource.Vertices[indexSource]);
            var v             = supportTarget - supportSource;

            // Sigma is the target distance between polygons
            var         sigma     = math.max(PhysicsSettings.Constants.MinimumConvexRadius, totalRadius - PhysicsSettings.Constants.MinimumConvexRadius);
            const float tolerance = PhysicsSettings.Constants.LinearSlop * 0.5f;

            var iteration = 0;

            while (
                iteration++ < PhysicsSettings.Constants.MaxGJKInterations &&
                math.abs(math.length(v) - sigma) > tolerance
                )
            {
                if (simplex.Count >= 3)
                {
                    SafetyChecks.ThrowInvalidOperationException("ColliderCast Simplex must have less than 3 vertex.");
                }

                // Support in direction -supportV (Target - Source)
                indexTarget   = proxyTarget.GetSupport(-v);
                supportTarget = proxyTarget.Vertices[indexTarget];
                indexSource   = proxySource.GetSupport(PhysicsMath.mul(invRotation, v));
                supportSource = PhysicsMath.mul(transformSource, proxySource.Vertices[indexSource]);
                var p = supportTarget - supportSource;

                v = math.normalizesafe(v);

                // Intersect ray with plane.
                var vp = math.dot(v, p);
                var vr = math.dot(v, sweepDirection);
                if (vp - sigma > lambda * vr)
                {
                    if (vr <= 0.0f)
                    {
                        return(false);
                    }

                    lambda = (vp - sigma) / vr;
                    if (lambda > 1.0f)
                    {
                        return(false);
                    }

                    normal        = -v;
                    simplex.Count = 0;
                }

                // Reverse simplex since it works with B - A.
                // Shift by lambda * r because we want the closest point to the current clip point.
                // Note that the support point p is not shifted because we want the plane equation
                // to be formed in un-shifted space.
                var vertex = vertices + simplex.Count;
                vertex->IndexA   = indexSource;
                vertex->SupportA = supportSource + lambda * sweepDirection;
                vertex->IndexB   = indexTarget;
                vertex->SupportB = supportTarget;
                vertex->W        = vertex->SupportB - vertex->SupportA;
                vertex->A        = 1.0f;
                simplex.Count   += 1;

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

                case 2:
                    simplex.Solve2();
                    break;

                case 3:
                    simplex.Solve3();
                    break;

                default:
                    SafetyChecks.ThrowInvalidOperationException("Simplex has invalid count.");
                    return(default);
                }

                // If we have 3 points, then the origin is in the corresponding triangle.
                if (simplex.Count == 3)
                {
                    // Overlap.
                    return(false);
                }

                // Get search direction.
                v = simplex.GetClosestPoint();
            }

            // Ensure we don't process an empty simplex.
            if (simplex.Count == 0)
            {
                return(false);
            }

            // Prepare result.
            var pointSource = float2.zero;
            var pointTarget = float2.zero;

            simplex.GetWitnessPoints(ref pointSource, ref pointTarget);

            normal = math.normalizesafe(-v);

            hit = new ColliderCastHit
            {
                Position      = pointTarget + (normal * radiusTarget),
                SurfaceNormal = normal,
                Fraction      = lambda
            };

            return(true);
        }
        public static float FindMinSeparation(out int indexA, out int indexB, float t, DistanceProxy proxyA, ref Sweep sweepA, DistanceProxy proxyB, ref Sweep sweepB, ref Vector2 axis, ref Vector2 localPoint, SeparationFunctionType type)
        {
            sweepA.GetTransform(out Transform xfA, t);
            sweepB.GetTransform(out Transform xfB, t);

            switch (type)
            {
            case SeparationFunctionType.Points:
            {
                Vector2 axisA = MathUtils.MulT(ref xfA.q, axis);
                Vector2 axisB = MathUtils.MulT(ref xfB.q, -axis);

                indexA = proxyA.GetSupport(axisA);
                indexB = proxyB.GetSupport(axisB);

                Vector2 localPointA = proxyA._vertices[indexA];
                Vector2 localPointB = proxyB._vertices[indexB];

                Vector2 pointA = MathUtils.Mul(ref xfA, localPointA);
                Vector2 pointB = MathUtils.Mul(ref xfB, localPointB);

                float separation = Vector2.Dot(pointB - pointA, axis);
                return(separation);
            }

            case SeparationFunctionType.FaceA:
            {
                Vector2 normal = MathUtils.Mul(ref xfA.q, axis);
                Vector2 pointA = MathUtils.Mul(ref xfA, localPoint);

                Vector2 axisB = MathUtils.MulT(ref xfB.q, -normal);

                indexA = -1;
                indexB = proxyB.GetSupport(axisB);

                Vector2 localPointB = proxyB._vertices[indexB];
                Vector2 pointB      = MathUtils.Mul(ref xfB, localPointB);

                float separation = Vector2.Dot(pointB - pointA, normal);
                return(separation);
            }

            case SeparationFunctionType.FaceB:
            {
                Vector2 normal = MathUtils.Mul(ref xfB.q, axis);
                Vector2 pointB = MathUtils.Mul(ref xfB, localPoint);

                Vector2 axisA = MathUtils.MulT(ref xfA.q, -normal);

                indexB = -1;
                indexA = proxyA.GetSupport(axisA);

                Vector2 localPointA = proxyA._vertices[indexA];
                Vector2 pointA      = MathUtils.Mul(ref xfA, localPointA);

                float separation = Vector2.Dot(pointA - pointB, normal);
                return(separation);
            }

            default:
                Debug.Assert(false);
                indexA = -1;
                indexB = -1;
                return(0.0f);
            }
        }
        /// <summary>
        /// CCD via the secant method.
        /// Compute the time when two shapes begin to touch or touch at a closer distance.
        /// TOI considers the shape radii. It attempts to have the radii overlap by the tolerance.
        /// Iterations terminate with the overlap is within 0.5 * tolerance. The tolerance should be
        /// smaller than sum of the shape radii.
        /// @warning the sweeps must have the same time interval.
        /// fraction=0 means the shapes begin touching/overlapped, and fraction=1 means the shapes don't touch.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <param name="shapeA">The shape A.</param>
        /// <param name="shapeB">The shape B.</param>
        /// <returns>
        /// fraction between [0,1] in which the shapes first touch.
        /// </returns>
        public static float TimeOfImpact(TOIInput input)
        {
            ++ToiCalls;

            DistanceProxy proxyA = input.ProxyA;
            DistanceProxy proxyB = input.ProxyB;

            Sweep sweepA = input.SweepA;
            Sweep sweepB = input.SweepB;

            Box2DXDebug.Assert(sweepA.T0 == sweepB.T0);
            Box2DXDebug.Assert(1.0f - sweepA.T0 > Settings.FLT_EPSILON);

            float radius    = proxyA._radius + proxyB._radius;
            float tolerance = input.Tolerance;

            float alpha = 0.0f;

            const int k_maxIterations = 1000; // TODO_ERIN b2Settings
            int       iter            = 0;
            float     target          = 0.0f;

            // Prepare input for distance query.
            SimplexCache cache = new SimplexCache();

            cache.Count = 0;
            DistanceInput distanceInput = new DistanceInput();

            distanceInput.proxyA   = input.ProxyA;
            distanceInput.proxyB   = input.ProxyB;
            distanceInput.UseRadii = false;

            for (;;)
            {
                Transform xfA, xfB;
                sweepA.GetTransform(out xfA, alpha);
                sweepB.GetTransform(out xfB, alpha);

                // Get the distance between shapes.
                distanceInput.TransformA = xfA;
                distanceInput.TransformB = xfB;
                DistanceOutput distanceOutput;
                Distance(out distanceOutput, cache, distanceInput);

                if (distanceOutput.Distance <= 0.0f)
                {
                    alpha = 1.0f;
                    break;
                }

                SeparationFunction fcn = new SeparationFunction();
                fcn.Initialize(cache, proxyA, xfA, proxyB, xfB);

                float separation = fcn.Evaluate(xfA, xfB);
                if (separation <= 0.0f)
                {
                    alpha = 1.0f;
                    break;
                }

                if (iter == 0)
                {
                    // Compute a reasonable target distance to give some breathing room
                    // for conservative advancement. We take advantage of the shape radii
                    // to create additional clearance.
                    if (separation > radius)
                    {
                        target = Math.Max(radius - tolerance, 0.75f * radius);
                    }
                    else
                    {
                        target = Math.Max(separation - tolerance, 0.02f * radius);
                    }
                }

                if (separation - target < 0.5f * tolerance)
                {
                    if (iter == 0)
                    {
                        alpha = 1.0f;
                        break;
                    }

                    break;
                }

#if false
                // Dump the curve seen by the root finder
                {
                    const int N  = 100;
                    float     dx = 1.0f / N;
                    float     xs[N + 1];