Follow

Follow

# Bézier Curve Fun! - Pt. 2.5

## A little optimization

·Jun 19, 2022·  Photo by Ricardo Gomez Angel on Unsplash

• Enter the Generator
• Updated Curve Drawer
• Current Status
• Please let me know what you think!

This is going to be a quick update to the last post I wrote on expanding my Bézier Curve implementation to a recursive one.

## Enter the Generator

I decided to first optimize the code organization. So I went ahead and separated the tasks of `generating` the Bézier curve and `drawing` it. This is why I created this curve generator class:

``````class BezierCurveGenerator
{
private readonly List<Vector3> m_iterationOfPointsBuffer = new();

private const float k_tValueStepIncrement = 0.025f;

public void GenerateBezierCurve(
List<Vector3> controlPoints,
List<Vector3> resultBezierCurvePoints)
{
if (controlPoints.Count <= 1)
{
return;
}

resultBezierCurvePoints.Clear();

CalculateBezierCurve(controlPoints, resultBezierCurvePoints);
}
private void CalculateBezierCurve(
List<Vector3> controlPoints,
List<Vector3> resultBezierCurvePoints)
{
for (float t = 0f; t <= 1.001f; t += k_tValueStepIncrement)
{
ResetControlPointIterationBuffer(controlPoints);

CalculateBezierCurvePointRecursively(
controlPoints.Count,
t,
resultBezierCurvePoints);
}
}
private void ResetControlPointIterationBuffer(List<Vector3> controlPoints)
{
m_iterationOfPointsBuffer.Clear();
foreach (var controlPoint in controlPoints)
{
}
}

private void CalculateBezierCurvePointRecursively(
int numberOfPointsInIteration,
float t,
List<Vector3> resultBezierCurvePoints)
{
if (numberOfPointsInIteration == 2)
{
var resultCurvePoint =
Vector3.Lerp(
m_iterationOfPointsBuffer,
m_iterationOfPointsBuffer,
t);

return;
}

ProcessNextIterationOfpoints(numberOfPointsInIteration, t);

CalculateBezierCurvePointRecursively(
numberOfPointsInIteration - 1,
t,
resultBezierCurvePoints);
}

private void ProcessNextIterationOfpoints(int numberOfPointsInIteration, float t)
{
for (int i = 1; i < numberOfPointsInIteration; i++)
{
var lhsValueForLerp = m_iterationOfPointsBuffer[i - 1];

m_iterationOfPointsBuffer[i - 1] =
Vector3.Lerp(
lhsValueForLerp,
m_iterationOfPointsBuffer[i],
t);
}
}
}
``````

This class has all of the curve creation code discussed in the previous posts, but now I'm using a memory buffer to avoid creating a new list on every iteration of points, when calculating a result curve point.

#### Why do this?

By isolating this functionality, it's no longer tied to any kind of component that must be owned by a `GameObject` . I can now use a `BezierCurveGenerator` anywhere I need. I could even run tests on this curve generation process to see how fast (or not) it may be, find out how much memory it uses, stress test it with large amounts of control points (20, 200, 10,000!), etc.

#### Quick Note:

C#'s List< T >.Clear does not actually erase the internal memory used by the list. In the example from the documentation, the internal memory remains the same after calling the clear method:

``````...
dinosaurs.Clear();
Console.WriteLine("\nClear()");
Console.WriteLine("Capacity: {0}", dinosaurs.Capacity);
Console.WriteLine("Count: {0}", dinosaurs.Count);

/*
...
Clear()
Capacity: 5
Count: 0
*/
``````

We can take advantage of this, knowing that the memory will only be re-allocated when more control points get added than the highest amount used, or when you reset the component. When you clear a list and re-add items, you're simply re-writing the memory, and inserting new values into the buffer.

## Updated Curve Drawer

Going back to our old code, our`BezierCurveDrawer` class now uses an instance of the `BezierCurveGenerator`:

``````public class BezierCurveDrawer : MonoBehaviour
{
public List<Vector3> m_controlPoints = new()
{
new(-10f, 10f, 0f),
new (-10f, 20f, 0f),
new (10f, 20f, 0f),
new (10f, 10f, 0f)
};

private readonly List<Vector3> m_resultBezierCurvePoints = new();

private readonly BezierCurveGenerator m_bezierCurveGenerator = new();

private const float k_controlPointRadius = 0.2f;

private const float k_bezierCurvePointRadius = 0.1f;

private void OnDrawGizmos()
{
DrawControlPoints();

DrawLineSegmentsOfControlPoints();

DrawBezierCurve();
}

private void DrawControlPoints()
{
foreach (var controlPoint in m_controlPoints)
{
}
}

private void DrawLineSegmentsOfControlPoints()
{
DrawLineSegmentsInBetweenPoints(m_controlPoints);
}

private void DrawBezierCurve()
{
if (m_controlPoints.Count <= 1)
{
return;
}

Gizmos.color = Color.cyan;
m_resultBezierCurvePoints.Clear();

m_bezierCurveGenerator.GenerateBezierCurve(m_controlPoints, m_resultBezierCurvePoints);

DrawResultBezierCurvePoints();

DrawLineSegmentsInBetweenPoints(m_resultBezierCurvePoints);
}

private void DrawResultBezierCurvePoints()
{
foreach (var curvePoint in m_resultBezierCurvePoints)
{
}
}

private void DrawLineSegmentsInBetweenPoints(List<Vector3> points)
{
for (int pointListIndex = 1; pointListIndex < points.Count; pointListIndex++)
{
Gizmos.DrawLine(
points[pointListIndex - 1],
points[pointListIndex]);
}
}
}
``````

Note that I added the list of points representing the Bézier Curve as a member variable. This way I don't create a new list every time the `OnDrawGizmos()` event is called:

``````public class BezierCurveDrawer : MonoBehaviour
{
public List<Vector3> m_controlPoints = new() { ... }

private readonly List<Vector3> m_resultBezierCurvePoints = new();
...
}
``````

## Current Status

No real changes have been made to the component's behavior. I just organized the code a bit, and also slightly improved the curve creation process: Also, this is pretty neat! I found out that the code still works well when I switch the order of the control points in the editor: 