//Divide the curve into equal steps public void DivideSegmentIntoSteps(BezierSegment seg) { //Find the total length of the curve float totalLength = SS_Common.GetLengthSimpsons(0f, 1f, seg); //How many sections do we want to divide the curve into int parts = numSteps; //reset the curve steps Array <======= EDIT MIO segmentSteps.Clear(); segmentSteps.Add(transform.TransformPoint(seg.A)); //What's the length of one section? float sectionLength = totalLength / (float)parts; //Init the variables we need in the loop float currentDistance = 0f + sectionLength; //The curve's start position Vector3 lastPos = seg.A; for (int i = 1; i <= parts; i++) { //Use Newton–Raphsons method to find the t value from the start of the curve //to the end of the distance we have float t = SS_Common.FindTValue(currentDistance, totalLength, seg); //Get the coordinate on the Bezier curve at this t value Vector3 pos = SS_Common.DeCasteljausAlgorithm(t, seg); //Save the last position lastPos = pos; //Add current pos to vector list <======= EDIT MIO segmentSteps.Add(transform.TransformPoint(pos)); //Add to the distance traveled on the line so far currentDistance += sectionLength; } }
/// <summary> /// Gets the sum of all individuals segments in this curve /// </summary> /// <returns></returns> public static float GetCurveLength(ShapeElement element, Transform theT) { float TotalLenghtSum = 0; //Draw Each Element for (int i = 0; i < element.knots.Length - 1; i++) { //CREATE A BEZIER SEGMENT BezierSegment seg = new BezierSegment(); seg.A = element.knots[i].KWorldPos(theT); //START POINT seg.B = element.knots[i].KHandleOutWorldPos(theT); //START TANGENT seg.C = element.knots[i + 1].KHandleInWorldPos(theT); //END TANGENT seg.D = element.knots[i + 1].KWorldPos(theT); //END POINT float totalLength = GetLengthSimpsons(0f, 1f, seg); TotalLenghtSum = TotalLenghtSum + totalLength; } return(TotalLenghtSum); }
//The De Casteljau's Algorithm public static Vector3 DeCasteljausAlgorithm(float t, BezierSegment seg) { //Linear interpolation = lerp = (1 - t) * A + t * B //Could use Vector3.Lerp(A, B, t) //To make it faster float oneMinusT = 1f - t; //Layer 1 Vector3 Q = oneMinusT * seg.A + t * seg.B; Vector3 R = oneMinusT * seg.B + t * seg.C; Vector3 S = oneMinusT * seg.C + t * seg.D; //Layer 2 Vector3 P = oneMinusT * Q + t * R; Vector3 T = oneMinusT * R + t * S; //Final interpolated position Vector3 U = oneMinusT * P + t * T; return(U); }
//Use Newton–Raphsons method to find the t value at the end of this distance d public static float FindTValue(float d, float totalLength, BezierSegment seg) { //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 t = 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) { //Newton's method float tNext = t - ((GetLengthSimpsons(0f, t, seg) - d) / GetArcLengthIntegrand(t, seg)); //Have we reached the desired accuracy? if (Mathf.Abs(tNext - t) < error) { break; } t = tNext; iterations += 1; if (iterations > 1000) { break; } } return(t); }
//Get the length of the curve between two t values with Simpson's rule public static float GetLengthSimpsons(float tStart, float tEnd, BezierSegment seg) { //This is the resolution and has to be even int n = 20; //Now we need to divide the curve into sections float delta = (tEnd - tStart) / (float)n; //The main loop to calculate the length //Everything multiplied by 1 float endPoints = GetArcLengthIntegrand(tStart, seg) + GetArcLengthIntegrand(tEnd, seg); //Everything multiplied by 4 float x4 = 0f; for (int i = 1; i < n; i += 2) { float t = tStart + delta * i; x4 += GetArcLengthIntegrand(t, seg); } //Everything multiplied by 2 float x2 = 0f; for (int i = 2; i < n; i += 2) { float t = tStart + delta * i; x2 += GetArcLengthIntegrand(t, seg); } //The final length float length = (delta / 3f) * (endPoints + 4f * x4 + 2f * x2); return(length); }
//Divide the curve into equal steps public static List <Vector3> ResampleCurve(int numSteps, ShapeElement element, Transform theT) { float totalLenght = GetCurveLength(element, theT); float sectionLength = totalLenght / numSteps; List <Vector3> curveSteps = new List <Vector3>(); /* * 20 8 35 17 -segmentLenghts N * * X -------------X--------X-------------------X-----------X -theSegs N * * 0 20 28 63 80 -knotDistances N+1 * */ //Create A list of Bezier Segment List <BezierSegment> theSegs = new List <BezierSegment>(); //Create A list of Bezier Segment Lengths List <float> segmentLenghts = new List <float>(); for (int i = 0; i < element.knots.Length - 1; i++) { //CREATE A BEZIER SEGMENT BezierSegment seg = new BezierSegment(); seg.A = element.knots[i].KWorldPos(theT); //START POINT seg.B = element.knots[i].KHandleOutWorldPos(theT); //START TANGENT seg.C = element.knots[i + 1].KHandleInWorldPos(theT); //END TANGENT seg.D = element.knots[i + 1].KWorldPos(theT); //END POINT theSegs.Add(seg); segmentLenghts.Add(GetLengthSimpsons(0f, 1f, seg)); } //Create a list of knot Distances List <float> knotDistances = new List <float>(); float tempDist = 0; knotDistances.Add(tempDist); for (int i = 0; i < segmentLenghts.Count; i++) { tempDist = tempDist + segmentLenghts[i]; knotDistances.Add(tempDist); } //Add the first point curveSteps.Clear(); curveSteps.Add(theSegs[0].A); //For each step for (int i = 1; i < numSteps; i++) { //Get this point dist float distAlong = sectionLength * i; //Find out in wich segment it falls int currSegIndex = 0; for (int f = 0; f < knotDistances.Count - 1; f++) { if (distAlong > knotDistances[f]) { if (distAlong < knotDistances[f + 1]) { currSegIndex = f; } } } //Find out at which distance along that segment the point falls float segPointDist = distAlong - knotDistances[currSegIndex]; //Use Newton–Raphsons method to find the t value from the start of the curve //to the end of the distance we have float t = FindTValue(segPointDist, segmentLenghts[currSegIndex], theSegs[currSegIndex]); //Get the coordinate on the Bezier curve at this t value Vector3 pos = DeCasteljausAlgorithm(t, theSegs[currSegIndex]); //Add the next point to the curveSteps list curveSteps.Add(pos); } return(curveSteps); }
//////////////////////////////////////// /// DRAWING UI /// DRAWING UI /// DRAWING UI //////////////////////////////////////// //////////////////////////////////////// /// BEZIER CURVE DRAWING METHODS /// BEZIER CURVE DRAWING METHODS //////////////////////////////////////// void DrawBezierSegment(BezierSegment seg) { UnityEditor.Handles.DrawBezier(seg.A, seg.D, seg.B, seg.C, curveColor, null, 1); }