# 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?

• What determines the length / rotation of the control point handles in the modified curve? They don't seem to relate in an obvious way to either the inner nor the outer circle. Feb 8, 2019 at 22:55
• @DMGregory Interesting question. I haven't been able to find a precise relation between the curve I desire and the circles beyond my above description. All control point handles have the same length so I guess several types of symmetric curves will qualify for my above description, but I think some factor that can the determine the length of the handle as it reduces consistently from a circle to an arc should do. I guess something similar should apply to the rotation.
– Zizo
Feb 8, 2019 at 23:30
• Try this from another angle then: why do you want to transform the curve in this way? What gameplay function does it serve? That may let us identify options that are best suited to that function. Feb 8, 2019 at 23:34
• I want a character to be enclosed within the circle or ride on arcs. I thought of using a semi-circle instead of an arc but I feel it will water done the effect I desire.
– Zizo
Feb 8, 2019 at 23:47
• @DMGregory I thought my earlier suggestion might work where some factor determines how much the handles rotate and reduce in length over the period of transitioning from a circle to an arc whiles preserving symmetry. I know it sounds easier than implementing.
– Zizo
Feb 12, 2019 at 13:30

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) {