//Get the length by using Simpson's Rule (not related to the television show) //https://www.youtube.com/watch?v=J_a4PXI_nLY //The basic idea is that we cut the curve into sections and each section is approximated by a polynom public static float GetLength_SimpsonsRule(_Curve curve, float tStart, float tEnd) { //Divide the curve into sections //How many sections? int n = 10; //The width of each section float delta = (tEnd - tStart) / (float)n; //The main loop to calculate the length //Everything multiplied by 1 float derivativeStart = curve.GetDerivative(tStart); float derivativeEnd = curve.GetDerivative(tEnd); float endPoints = derivativeStart + derivativeEnd; //Everything multiplied by 4 float x4 = 0f; for (int i = 1; i < n; i += 2) { float t = tStart + delta * i; x4 += curve.GetDerivative(t); } //Everything multiplied by 2 float x2 = 0f; for (int i = 2; i < n; i += 2) { float t = tStart + delta * i; x2 += curve.GetDerivative(t); } //The final length float length = (delta / 3f) * (endPoints + 4f * x4 + 2f * x2); return(length); }
// // Divide the curve into constant steps // //The problem with the t-value and Bezier curves is that the t-value is NOT percentage along the curve //So sometimes we need to divide the curve into equal steps, for example if we generate a mesh along the curve //So we have a distance along the curve and we want to find the t-value that generates that distance //Alternative 1 //Use Newton–Raphsons method to find which t value we need to travel distance d on the curve //d is measured from the start of the curve in [m] so is not the same as paramete t which is [0, 1] //https://en.wikipedia.org/wiki/Newton%27s_method //https://www.youtube.com/watch?v=-mad4YPAn2U //TODO: We can use the lookup table from the lookup-method in the newton-raphson method! public static float Find_t_FromDistance_Iterative(_Curve curve, float d, float totalLength) { //Need a start value to make the method start //Should obviously be between 0 and 1 //We can say that a good starting point is the percentage of distance traveled //If this start value is not working you can use the Bisection Method to find a start value //https://en.wikipedia.org/wiki/Bisection_method float tGood = d / totalLength; //Need an error so we know when to stop the iteration float error = 0.001f; //We also need to avoid infinite loops int iterations = 0; while (true) { //The derivative and the length can be calculated in different ways //The derivative at point t float derivative = curve.GetDerivative(tGood); //The length of the curve to point t from the start //float lengthTo_t = GetLengthNaive_CubicBezier(pA, pB, hA, hB, steps: 20, tEnd: t); float lengthTo_t = GetLength_SimpsonsRule(curve, tStart: 0f, tEnd: tGood); //Calculate a better t with Newton's method: x_n+1 = x_n + (f(x_n) / f'(x_n)) //Our f(x) = lengthTo_t - d = 0. We want them to be equal because we want to find the t value //that generates a distance(t) which is the same as the d we want. So when f(x) is close to zero we are happy //When we take the derivative of f(x), d disappears which is why we are not subtracting it in the bottom float tNext = tGood - ((lengthTo_t - d) / derivative); //Have we reached the desired accuracy? float diff = tNext - tGood; tGood = tNext; //Have we found a t to get a distance which matches the distance we want? if (diff < error && diff > -error) { //Debug.Log("d: " + d + " t: " + t + " Distance: " + GetLengthSimpsonsCubicBezier(posA, posB, handleA, handleB, tStart: 0f, tEnd: tNext)); break; } //Safety so we don't get stuck in an infinite loop iterations += 1; if (iterations > 1000) { Debug.Log("Couldnt find a t value within the iteration limit"); break; } } return(tGood); }