/// <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)); } }