コード例 #1
0
        /// <summary>
        /// Generates a bezier curve for the segment using a least-squares approximation. for the derivation of this and why it works,
        /// see http://read.pudn.com/downloads141/ebook/610086/Graphics_Gems_I.pdf page 626 and beyond. tl;dr: math.
        /// </summary>
        protected CubicBezier GenerateBezier(int first, int last, VECTOR tanL, VECTOR tanR)
        {
#if OPTIMIZED_GENERATEBEZIERSIMD
            using var _ = GenerateBezierMarker.Auto();
            unsafe
            {
                using (_pts.ViewAsNativeArray(out var pts))
                    using (_u.ViewAsNativeArray(out var u))
                    {
                        OptimizedHelpers.GenerateBezier(first, last, tanL, tanR, (VECTOR *)pts.GetUnsafePtr(), (FLOAT *)u.GetUnsafePtr(), out var bezier);
                        return(bezier);
                    }
            }
#elif OPTIMIZED_GENERATEBEZIER
            using var _ = GenerateBezierMarker.Auto();
            unsafe
            {
                using (_pts.ViewAsNativeArray(out var pts))
                    using (_u.ViewAsNativeArray(out var u))
                    {
                        OptimizedHelpers.OptimizedGenerateBezier(first, last, tanL, tanR, (VECTOR *)pts.GetUnsafePtr(), (FLOAT *)u.GetUnsafePtr(), out var bezier);
                        return(bezier);
                    }
            }
#else
            using var _ = GenerateBezierMarker.Auto();
            List <VECTOR> pts = _pts;
            List <FLOAT>  u = _u;
            int           nPts = last - first + 1;
            VECTOR        p0 = pts[first], p3 = pts[last];           // first and last points of curve are actual points on data
            FLOAT         c00 = 0, c01 = 0, c11 = 0, x0 = 0, x1 = 0; // matrix members -- both C[0,1] and C[1,0] are the same, stored in c01
            for (int i = 1; i < nPts; i++)
            {
                // Calculate cubic bezier multipliers
                FLOAT t  = u[i];
                FLOAT ti = 1 - t;
                FLOAT t0 = ti * ti * ti;
                FLOAT t1 = 3 * ti * ti * t;
                FLOAT t2 = 3 * ti * t * t;
                FLOAT t3 = t * t * t;

                // For X matrix; moving this up here since profiling shows it's better up here (maybe a0/a1 not in registers vs only v not in regs)
                VECTOR s = (p0 * t0) + (p0 * t1) + (p3 * t2) + (p3 * t3); // NOTE: this would be Q(t) if p1=p0 and p2=p3
                VECTOR v = pts[first + i] - s;

                // C matrix
                VECTOR a0 = tanL * t1;
                VECTOR a1 = tanR * t2;
                c00 += VectorHelper.Dot(a0, a0);
                c01 += VectorHelper.Dot(a0, a1);
                c11 += VectorHelper.Dot(a1, a1);

                // X matrix
                x0 += VectorHelper.Dot(a0, v);
                x1 += VectorHelper.Dot(a1, v);
            }

            // determinents of X and C matrices
            FLOAT det_C0_C1 = c00 * c11 - c01 * c01;
            FLOAT det_C0_X  = c00 * x1 - c01 * x0;
            FLOAT det_X_C1  = x0 * c11 - x1 * c01;
            FLOAT alphaL    = det_X_C1 / det_C0_C1;
            FLOAT alphaR    = det_C0_X / det_C0_C1;

            // if alpha is negative, zero, or very small (or we can't trust it since C matrix is small), fall back to Wu/Barsky heuristic
            FLOAT linDist  = VectorHelper.Distance(p0, p3);
            FLOAT epsilon2 = EPSILON * linDist;
            if (Math.Abs(det_C0_C1) < EPSILON || alphaL < epsilon2 || alphaR < epsilon2)
            {
                FLOAT  alpha = linDist / 3;
                VECTOR p1    = (tanL * alpha) + p0;
                VECTOR p2    = (tanR * alpha) + p3;
                return(new CubicBezier(p0, p1, p2, p3));
            }
            else
            {
                VECTOR p1 = (tanL * alphaL) + p0;
                VECTOR p2 = (tanR * alphaR) + p3;
                return(new CubicBezier(p0, p1, p2, p3));
            }
#endif
        }