Exemplo n.º 1
0
        /// <summary>
        /// Gets the tangent at a given point in the curve.
        /// </summary>
        protected VECTOR GetCenterTangent(int first, int last, int split)
        {
            using var _ = GetCenterTangentMarker.Auto();
            List <VECTOR> pts    = _pts;
            List <FLOAT>  arclen = _arclen;

            // because we want to maintain C1 continuity on the spline, the tangents on either side must be inverses of one another
            Debug.Assert(first < split && split < last);
            FLOAT  splitLen = arclen[split];
            VECTOR pSplit   = pts[split];

            // left side
            FLOAT  firstLen    = arclen[first];
            FLOAT  partLen     = splitLen - firstLen;
            VECTOR total       = default(VECTOR);
            FLOAT  weightTotal = 0;

            for (int i = Math.Max(first, split - MID_TANGENT_N_PTS); i < split; i++)
            {
                FLOAT  t      = (arclen[i] - firstLen) / partLen;
                FLOAT  weight = t * t * t;
                VECTOR v      = VectorHelper.Normalize(pts[i] - pSplit);
                total       += v * weight;
                weightTotal += weight;
            }
            VECTOR tanL = VectorHelper.Length(total) > EPSILON && weightTotal > EPSILON?
                          VectorHelper.Normalize(total / weightTotal) :
                              VectorHelper.Normalize(pts[split - 1] - pSplit);

            // right side
            partLen = arclen[last] - splitLen;
            int rMax = Math.Min(last, split + MID_TANGENT_N_PTS);

            total       = default(VECTOR);
            weightTotal = 0;
            for (int i = split + 1; i <= rMax; i++)
            {
                FLOAT  ti     = 1 - ((arclen[i] - splitLen) / partLen);
                FLOAT  weight = ti * ti * ti;
                VECTOR v      = VectorHelper.Normalize(pSplit - pts[i]);
                total       += v * weight;
                weightTotal += weight;
            }
            VECTOR tanR = VectorHelper.Length(total) > EPSILON && weightTotal > EPSILON?
                          VectorHelper.Normalize(total / weightTotal) :
                              VectorHelper.Normalize(pSplit - pts[split + 1]);

            // The reason we separate this into two halves is because we want the right and left tangents to be weighted
            // equally no matter the weights of the individual parts of them, so that one of the curves doesn't get screwed
            // for the pleasure of the other half
            total = tanL + tanR;

            // Since the points are never coincident, the vector between any two of them will be normalizable, however this can happen in some really
            // odd cases when the points are going directly opposite directions (therefore the tangent is undefined)
            if (VectorHelper.LengthSquared(total) < EPSILON)
            {
                // try one last time using only the three points at the center, otherwise just use one of the sides
                tanL  = VectorHelper.Normalize(pts[split - 1] - pSplit);
                tanR  = VectorHelper.Normalize(pSplit - pts[split + 1]);
                total = tanL + tanR;
                return(VectorHelper.LengthSquared(total) < EPSILON ? tanL : VectorHelper.Normalize(total / 2));
            }
            else
            {
                return(VectorHelper.Normalize(total / 2));
            }
        }