Example #1
0
        /// <summary>
        /// Find the closest point on 'spline' to 'pt'
        /// Note: the analytic solution to this problem involves solving a 5th order polynomial
        /// This method uses Newton's method and relies on a "good" initial estimate of the nearest point
        /// Should have quadratic convergence</summary>
        public static float ClosestPoint(Spline spline, v4 pt, float initial_estimate, bool bound01 = true, int iterations = 5)
        {
            // The distance (sqr'd) from 'pt' to the spline is: Dist(t) = |pt - S(t)|^2.    (S(t) = spline at t)
            // At the closest point, Dist'(t) = 0.
            // Dist'(t) = -2(pt - S(t)).S'(t)
            // So we want to find 't' such that Dist'(t) = 0
            // Newton's method of iteration = t_next = t_current - f(x)/f'(x)
            //	f(x) = Dist'(t)
            //	f'(x) = Dist''(t) = 2S'(t).S'(t) - 2(pt - S(t)).S''(t)
            float time = initial_estimate;

            for (int iter = 0; iter != iterations; ++iter)
            {
                v4 S   = spline.Position(time);
                v4 dS  = spline.Velocity(time);
                v4 ddS = spline.Acceleration(time);
                v4 R   = pt - S;
                time += Math_.Dot(R, dS) / (Math_.Dot(dS, dS) - Math_.Dot(R, ddS));
                if (bound01 && time <= 0.0f || time >= 1.0f)
                {
                    return(Math_.Clamp(time, 0.0f, 1.0f));
                }
            }
            return(time);
        }
Example #2
0
        /// <summary>Return the closest point on 'rect' to 'pt'</summary>
        public static v2 ClosestPoint(BRect rect, v2 pt)
        {
            v2 lower = rect.Lower;
            v2 upper = rect.Upper;
            v2 closest;

            if (rect.IsWithin(pt))
            {
                // if pt.x/pt.y > rect.sizeX/rect.sizeY then the point
                // is closer to the Y edge of the rectangle
                if (Math_.Abs(pt.x * rect.SizeY) > Math_.Abs(pt.y * rect.SizeX))
                {
                    closest = new v2(pt.x, Math_.Sign(pt.y) * rect.SizeY);
                }
                else
                {
                    closest = new v2(Math_.Sign(pt.x) * rect.SizeX, pt.y);
                }
            }
            else
            {
                closest = new v2(
                    Math_.Clamp(pt.x, lower.x, upper.x),
                    Math_.Clamp(pt.y, lower.y, upper.y));
            }
            return(closest);
        }
Example #3
0
        /// <summary>Return the closest point between two line segments</summary>
        public static void ClosestPoint(v4 s0, v4 e0, v4 s1, v4 e1, out float t0, out float t1)
        {
            Debug.Assert(s0.w == 1f && e0.w == 1f && s1.w == 1f && e1.w == 1f);

            v4    line0           = e0 - s0;
            v4    line1           = e1 - s1;
            v4    separation      = s0 - s1;
            float f               = Math_.Dot(line1, separation);
            float c               = Math_.Dot(line0, separation);
            float line0_length_sq = line0.LengthSq;
            float line1_length_sq = line1.LengthSq;

            // Check if either or both segments are degenerate
            if (Math_.FEql(line0_length_sq, 0f) && Math_.FEql(line1_length_sq, 0f))
            {
                t0 = 0.0f; t1 = 0.0f; return;
            }
            if (Math_.FEql(line0_length_sq, 0f))
            {
                t0 = 0.0f; t1 = Math_.Clamp(f / line1_length_sq, 0.0f, 1.0f); return;
            }
            if (Math_.FEql(line1_length_sq, 0f))
            {
                t1 = 0.0f; t0 = Math_.Clamp(-c / line0_length_sq, 0.0f, 1.0f); return;
            }

            // The general nondegenerate case starts here
            float b     = Math_.Dot(line0, line1);
            float denom = line0_length_sq * line1_length_sq - b * b;             // Always non-negative

            // If segments not parallel, calculate closest point on infinite line 'line0'
            // to infinite line 'line1', and clamp to segment 1. Otherwise pick arbitrary t0
            t0 = denom != 0.0f ? Math_.Clamp((b * f - c * line1_length_sq) / denom, 0.0f, 1.0f) : 0.0f;

            // Calculate point on infinite line 'line1' closest to segment 'line0' at t0
            // using t1 = Dot3(pt0 - s1, line1) / line1_length_sq = (b*t0 + f) / line1_length_sq
            t1 = (b * t0 + f) / line1_length_sq;

            // If t1 in [0,1] then done. Otherwise, clamp t1, recompute t0 for the new value
            // of t1 using t0 = Dot3(pt1 - s0, line0) / line0_length_sq = (b*t1 - c) / line0_length_sq
            // and clamped t0 to [0, 1]
            if (t1 < 0.0f)
            {
                t1 = 0.0f; t0 = Math_.Clamp((-c) / line0_length_sq, 0.0f, 1.0f);
            }
            else if (t1 > 1.0f)
            {
                t1 = 1.0f; t0 = Math_.Clamp((b - c) / line0_length_sq, 0.0f, 1.0f);
            }
        }