# How to move bezier curve points along circle to form a symmetric curve at center

## How to move bezier curve points along circle to form a symmetric curve at center

From the image below, A is an open bezier curve with it's points positioned to represent a circle. AP3 and AP1 have the same position.
From B, How can I move the points (AP1 and AP3 along the bigger circle of radius r whiles the CP's reduce in length as CP1 and CP4 also rotate) till they form a symmetric bezier curve at the center of the bigger circle marked as the red curve?



To keep things simple & consistent, let’s stick with circular arcs throughout.

We’ll have our first circle with radius $$\r_1\$$, shown in red below.

Then we’ll use a second radius $$\r_2\$$ to define the new positions of our endpoints as the curve swings open by an angle $$\\alpha\$$, shown on the green circle below.

Then we’ll draw a third circle, through our moved anchor points $$\AP_1\$$, $$\AP_3\$$ and the one we left stationary $$\AP_2\$$, shown in blue below. This gives us a unique arc to follow.

Since we already know how to trace a circular arc with a cubic Bézier curve, once we find this circle we’ll be able to draw the arc along it trivially. So I’ll focus on finding the parameters of the circle below:

Let’s get down to placing the points. For simplicity I’m going to assume that the fixed point is always at the rightmost edge of the red circle, and that the centers & radii of the circles $$\ \vec C_1, r_1, \vec C_2,r_2\$$ are known, along with the angle $$\ \alpha > 0\$$ you want to open the initial curve by. (For $$\\alpha = 0\$$, we just draw the original curve as in your previous question)

Now, we know our top anchor point is at…

$$\vec {AP_1} = \vec C_2 + r_2 \cdot \left(-\cos \alpha, \sin \alpha \right)$$

and our fixed anchor point is at…

$$\vec {AP_2} = \vec C_1 + \left( r_1, 0 \right)$$

Then the midpoint of the line joining these is at…

$$\vec M = \frac 1 2 \cdot \left( \vec {AP_1} + \vec {AP_2} \right)$$

A perpendicular $$\\vec p\$$ from this midpoint will point the way to our third center $$\\vec C_3\$$

$$\vec p = \left( -M_y, M_x – {AP_2}_x \right)$$

(If $$\p_y = 0\$$ then we’ve opened the curve all the way to a straight line, so just draw a straight line from $$\AP_1\$$ to $$\AP_3\$$ and you’re done)

We can scale this by a factor $$\t\$$ to reach the horizontal axis and locate our center:

$$t = \frac {{AP_2}_y – M_y} {p_y}$$

$$\vec C_3 = \vec M + t \cdot \vec p$$

$$r_3 = {AP_2}_x – {C_3}_x$$

And now we can find our angle $$\\theta\$$ with:

$$\theta = atan2(\vec {AP_1} – \vec C_3)$$

(Just note that in many APIs, the first argument of atan2 is y not x, so be sure you put the components in the right order)

Now we have a center ($$\\vec C_3\$$), a radius ($$\r_3\$$), a start angle (0), and an end angle ($$\\theta\$$), so we can draw a cubic Bézier curve along this circular arc using the technique described in my earlier answer.

For the lower half, just flip the y coordinates along the horizontal.

Here’s an animation of this method in action:

With a little attention to our start points, we can even make this work starting from a less-than-full circle:

Here’s the code I used:

// Buffer for our final spline's anchor & control points.
Vector2[] _bezierPoints = new Vector2[7];

[Range(0,180)]
public float circleCoverageDegrees= 180f;

// Size & center of our inner circle (red).
public Vector2 firstCenter;

// Size & center of our second circle (green).
Vector2 _secondCenter;  // Computed from radius & coverage.

// How far should we swing the anchor points out around the green circle?
[Range(0, 180)]
public float swingOutDegrees = 0f;

// Size & center of our third circle (blue).
Vector2 _thirdCenter;

// Used to mark the line from the second circle's center
// to our inner anchor points before we've swung them outward.
Vector2 _secondToInnerStart;

void OnValidate()
{

// Place the anchor points of the original circle.
// (For now, I assume its center is (0, 0), and add it as an offset later)

// Place the second center so the green circle passes through the endpoints.
_secondCenter = new Vector2(innerArcStart.x + horizontalDeviation, 0f);

_secondToInnerStart = innerArcStart - _secondCenter;

float startDegrees = Mathf.Approximately(_secondToInnerStart.x, 0) ? 90f
: Mathf.Rad2Deg * Mathf.Atan(-_secondToInnerStart.y / _secondToInnerStart.x);

// Swing out our anchor points along the green circle.
Vector2 finalArcStart = _secondCenter + PointOnCircle(secondRadius, 180 - startDegrees - swingOutDegrees);

// Find the center of the blue circle joining these anchors.
Vector2 midpoint = (finalArcStart + fixedAnchor) / 2f;
Vector2 perpendicular = new Vector2(-midpoint.y, midpoint.x - fixedAnchor.x);

// If they're in a vertical line, abort and just draw a straight line.
if(Mathf.Approximately(perpendicular.y, 0f)) {

_bezierPoints[0] = firstCenter + finalArcStart;
_bezierPoints[3] = firstCenter + fixedAnchor;
_bezierPoints[6] = 2 * firstCenter - finalArcStart;

_bezierPoints[1] = Vector2.Lerp(_bezierPoints[0], _bezierPoints[3], 1f / 3f);
_bezierPoints[2] = Vector2.Lerp(_bezierPoints[0], _bezierPoints[3], 2f / 3f);
_bezierPoints[4] = Vector2.Lerp(_bezierPoints[3], _bezierPoints[6], 1f / 3f);
_bezierPoints[5] = Vector2.Lerp(_bezierPoints[3], _bezierPoints[6], 2f / 3f);
return;
}

// Phew, we have a non-infinite circle! Place its center & radius.
float t = -midpoint.y / perpendicular.y;

_thirdCenter = new Vector2(midpoint.x + t * perpendicular.x, 0f);

// Find the angle of the endpoints around this blue circle.
Vector2 thirdToFinalStart = finalArcStart - _thirdCenter;
float angle = Mathf.Rad2Deg * Mathf.Atan2(thirdToFinalStart.y, thirdToFinalStart.x);

// Handle reversing concavity correctly.
float pivot = _thirdCenter.x < fixedAnchor.x ? 0 : 180;

// Populate our Bezier curve buffers using code from previous answer.
BezierCircle(_thirdCenter, _thirdRadius, angle, pivot, _bezierPoints, 0);
BezierCircle(_thirdCenter, _thirdRadius, pivot, 2 * pivot - angle, _bezierPoints, 3);
}

// Convenience method for plotting a point in polar coordinates.
static Vector2 PointOnCircle( float radius, float angleDegrees) {