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

## Solutions/Answers:

### Answer 1:

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];
// How much of the inner circle should we start with?
[Range(0,180)]
public float circleCoverageDegrees= 180f;
// Size & center of our inner circle (red).
public float firstRadius = 1.0f;
public Vector2 firstCenter;
// Size & center of our second circle (green).
public float secondRadius = 2.0f;
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;
float _thirdRadius;
// 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()
{
if (secondRadius < firstRadius)
secondRadius = firstRadius;
// Place the anchor points of the original circle.
// (For now, I assume its center is (0, 0), and add it as an offset later)
Vector2 innerArcStart = PointOnCircle(firstRadius, circleCoverageDegrees);
Vector2 fixedAnchor = PointOnCircle(firstRadius, 0f);
// Place the second center so the green circle passes through the endpoints.
float horizontalDeviation = Mathf.Sqrt(secondRadius * secondRadius - innerArcStart.y * innerArcStart.y);
_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)) {
_thirdRadius = float.PositiveInfinity;
_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);
_thirdRadius = Mathf.Abs(_thirdCenter.x - fixedAnchor.x);
// 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) {
float angleRadians = angleDegrees * Mathf.Deg2Rad;
return radius * new Vector2(Mathf.Cos(angleRadians), Mathf.Sin(angleRadians));
}
```

## References

- Database Administration Tutorials
- Programming Tutorials & IT News
- Linux & DevOps World
- Entertainment & General News
- Games & eSport