139

We have a start point (x, y) and a circle radius. There also exists an engine that can create a path from Bézier curve points.

How can I create a circle using Bézier curves?

Jon Adams
  • 24,464
  • 18
  • 82
  • 120
Rella
  • 65,003
  • 109
  • 363
  • 636
  • Closely related: [Geometrical Arc to Bezier Curve](http://stackoverflow.com/q/734076/183120) – legends2k Jan 06 '16 at 15:28
  • Some excellent info here: https://math.stackexchange.com/q/873224/207316 – PM 2Ring Aug 23 '23 at 19:49
  • This should be on one of the math-related sites instead, as a) it's being asked in a language-agnostic way and b) *the language-agnostic component of the problem is purely mathematical and not related to programming* - assuming we have the described "engine" and know how to make the necessary API call, the actual "programming" is trivial and the only thing of interest is the math. – Karl Knechtel Aug 27 '23 at 16:03

10 Answers10

194

As already said: there is no exact representation of the circle using Bezier curves.

To complete the other answers : for Bezier curve with n segments the optimal distance to the control points, in the sense that the middle of the curve lies on the circle itself, is (4/3)*tan(pi/(2n)).

formula for n segments

So for 4 points it is (4/3)*tan(pi/8) = 4*(sqrt(2)-1)/3 = 0.552284749831.

4 point case

Suma
  • 33,181
  • 16
  • 123
  • 191
Kpym
  • 3,743
  • 1
  • 21
  • 18
  • 6
    By optimal distance, what kind of metrics are you optimizing? As shown in [Approximate a circle with cubic Bézier curves](http://spencermortensen.com/articles/bezier-circle/), the lowest possible maximum drift is achieved by a different value. Can you provide some link defining what "optimal" means in your case, or how it the formula derived? – Suma Oct 07 '15 at 07:31
  • 1
    @Suma this is not optimal for some distance. It is *optimal* to have the middle of the curve on the circle. And certainly can be made better if you put another criteria. – Kpym Oct 07 '15 at 11:43
  • 4
    OK. I will try to rephrase: "the distance to the control points such that the middle of the curve lies on the circle itself". I see this as a valid decision (good enough and easy to calculate), but I would not call it optimal (at least not without writing in what sense it is optimal). – Suma Oct 07 '15 at 11:57
  • 1
    This helped me calculate my control points for a bezier curve approximating an eighth of a circle. – JstnPwll Dec 17 '15 at 22:49
  • 1
    Yeah, since this one has a max deviation of +0.027% and a min deviation of -0 vs the true circle. It's only ever bigger than the real circle the better improved approximation is done by moving C in by half of 0.027%. If you want the midpoints on the circle though, this is certainly the way to do it. – Tatarize Dec 29 '15 at 19:28
  • @Kpym The figures are well-made and from the looks of it seems to be vector graphics. Can you please tell us how you created them? – legends2k Jan 06 '16 at 15:33
  • 2
    @legends2k I use LaTeX with TikZ to generate a PDF that I convert to PNG then. – Kpym Jan 06 '16 at 23:00
  • Yeah, but if you wanted to do this with 1 control point rather than two what's your C value and error rate? – Tatarize Aug 01 '16 at 18:38
  • @Tatarize If you want to approximate the circle with quadratic in place of cubic bezier curve tangent at n points, you don't have a lot of choice for the control points : the n control points should be "in the middle" at distance from the center `1/cos(pi/n)`. – Kpym Aug 03 '16 at 19:19
  • 2
    Maybe useful to someone I scripted creating SVG like path commands to draw circle with bezier curves: http://codereview.stackexchange.com/questions/141491/how-can-i-do-this-small-haskell-script-better-calculating-a-circle-with-bezier I based it on this answer. – Dave Thomas Sep 16 '16 at 02:45
  • This should be the answer. Thank you so much for the explanation! – Mr. Polywhirl Nov 29 '17 at 13:05
  • Now this number `0.552284749831` is not magic anymore! Thanks :) – Bruno Apr 09 '20 at 04:30
  • 1
    How do you find out the "4/3*tan(π/2n)" ? – cuixiping Feb 25 '21 at 06:34
  • Could the answer be edited to add the radius parameter somewhere ? My circle does not have a radius of 1, and I am not sure where in the provided formula we can take the radius into account – FMaz008 Feb 03 '23 at 16:06
  • @Suma unfortunately, the page you link doesn't show how to generalize the calculation for angles other than pi/4. At any rate, the better approximations use values that are very close to this; and we can also "improve" this approximation by just slightly shrinking the radius, so that the result sometimes deviates inward instead of outward (this can reduce the maximum *absolute value* of the deviation by half). – Karl Knechtel Aug 27 '23 at 15:21
42

Covered in the comp.graphics.faq

Excerpt:

Subject 4.04: How do I fit a Bezier curve to a circle?

Interestingly enough, Bezier curves can approximate a circle but not perfectly fit a circle. A common approximation is to use four beziers to model a circle, each with control points a distance d=r*4*(sqrt(2)-1)/3 from the end points (where r is the circle radius), and in a direction tangent to the circle at the end points. This will ensure the mid-points of the Beziers are on the circle, and that the first derivative is continuous.
The radial error in this approximation will be about 0.0273% of the circle's radius.

Michael Goldapp, "Approximation of circular arcs by cubic polynomials" Computer Aided Geometric Design (#8 1991 pp.227-238)

Tor Dokken and Morten Daehlen, "Good Approximations of circles by curvature-continuous Bezier curves" Computer Aided Geometric Design (#7 1990 pp. 33-41). http://www.sciencedirect.com/science/article/pii/016783969090019N (non free article)

Also see the non-paywalled article at http://spencermortensen.com/articles/bezier-circle/

Browsers and Canvas Element.

Note that some browsers use Bezier curves to their canvas draw arc, Chrome uses (at the present time) a 4 sector approach and Safari uses an 8 sector approach, the difference is noticeable only at high resolution, because of that 0.0273%, and also only truly visible when arcs are drawn in parallel and out of phase, you'll notice the arcs oscillate from a true circle. The effect is also more noticeable when the curve is animating around it's radial center, 600px radius is usually the size where it will make a difference.

Certain drawing API's don't have true arc rendering so they also use Bezier curves, for example the Flash platform has no arc drawing api, so any frameworks that offer arcs are generally using the same Bezier curve approach.

Note that SVG engines within browsers may use a different drawing method.

Other platforms

Whatever platform you are trying to use, it's worth checking to see how arc drawing is done, so you can predict visual errors like this, and adapt.

ocodo
  • 29,401
  • 18
  • 105
  • 117
41

The answers to the question are very good, so there's little to add. Inspired by that I started to make an experiment to visually confirm the solution, starting with four Bézier curves, reducing the number of curves to one. Amazingly I found out that with three Bézier curves the circle looked good enough for me, but the construction is a bit tricky. Actually I used Inkscape to place the black 1-pixel-wide Bézier approximation over a red 3-pixel-wide circle (as produced by Inkscape). For clarification I added blue lines and surfaces showing the bounding boxes of the Bézier curves.

To see yourself, I'm presenting my results:

The 1-curve graph (which looks like a drop squeezed in a corner, just for completeness) :enter image description here

The 2-curve graph:enter image description here

The 3-curve graph:enter image description here

The 4-curve graph: enter image description here

(I wanted to put the SVG or PDF here, but that isn't supported)

John Difool
  • 5,572
  • 5
  • 45
  • 80
U. Windl
  • 3,480
  • 26
  • 54
  • 1
    By now, svg can be included as html code snippet. See for example this answer: https://stackoverflow.com/a/32162431 – T S Jul 21 '19 at 14:53
  • 1
    @T S: When I tried to replace the graphics with the SVGs I had, I realized that I lost those with an USB stick that had been stolen at the beginning of this year. If time permits, I'll try to recreate them soon. However if SVG can be added as XML code (and is not displayed as graphics) it does not make much sense here. – U. Windl Jul 23 '19 at 23:57
  • If your browser supports svg, then the images are rendered as soon as you click "Run Code Snippet" (apparently that button isn't available on the mobile version of stackoverflow...). See in the answer I linked. – T S Jul 25 '19 at 06:47
  • 1
    @TS: For longer files it's too ugly IMHO. – U. Windl Jul 26 '19 at 01:02
19

To people who are just looking for code:

https://jsfiddle.net/nooorz24/2u9forep/12/

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

function drawBezierOvalQuarter(centerX, centerY, sizeX, sizeY) {
    ctx.beginPath();
    ctx.moveTo(
        centerX - (sizeX),
        centerY - (0)
    );
    ctx.bezierCurveTo(
        centerX - (sizeX),
        centerY - (0.552 * sizeY),
        centerX - (0.552 * sizeX),
        centerY - (sizeY),
        centerX - (0),
        centerY - (sizeY)
    );
    ctx.stroke();
}

function drawBezierOval(centerX, centerY, sizeX, sizeY) {
    drawBezierOvalQuarter(centerX, centerY, -sizeX, sizeY);
    drawBezierOvalQuarter(centerX, centerY, sizeX, sizeY);
    drawBezierOvalQuarter(centerX, centerY, sizeX, -sizeY);
    drawBezierOvalQuarter(centerX, centerY, -sizeX, -sizeY);
}

function drawBezierCircle(centerX, centerY, size) {
    drawBezierOval(centerX, centerY, size, size)
}

drawBezierCircle(200, 200, 64)
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

This allows to draw circle that is made out of 4 Bezier curves. Written in JS but can easily be translated to any other language

Note

Don't use Bezier curves if you need to draw a circle using SVG path unless required to do so. In path you can use Arc to create 2 half circles.

Circle drawing with SVG's arc path

NoOorZ24
  • 2,914
  • 1
  • 14
  • 33
  • That is very helpful, thanks! What needs to be changed to bring the 4 segments in order? I need to write text along a path, but now it is scattered around the 4 segments – Alexa Aug 18 '20 at 11:05
  • The code above seems to be missing the inner part of the quadrants, it just closes the rounded outer shape. I think another `ctx.lineTo(centerX, centerY);` after the `bezierCurveTo()` would fix that. ` – drott Jan 17 '22 at 15:02
14

Many answers already but I found a small online article with a very good cubic bezier approximation of a circle. In terms of unit circle c = 0.55191502449 where c is the distance from the axis intercept points along the tangents to the control points.

As a single quadrant for the unit circle with the two middle coordinates being the control points. (0,1),(c,1),(1,c),(1,0)

The radial error is just 0.019608% so I just had to add it to this list of answers.

The article can be found here Approximate a circle with cubic Bézier curves

Blindman67
  • 51,134
  • 11
  • 73
  • 136
  • 9
    Have you read this [**excellent treatise**](http://pomax.github.io/bezierinfo/) on Bezier Curves by Stackoverflow's [Mike 'Pomax' Kamermans](http://stackoverflow.com/users/740553/mike-pomax-kamermans). It's well worth the read! :-) – markE May 02 '16 at 03:17
  • 1
    @markE Thank you very much for that link, that is one of the "most excellent" treatise I have seen on the subject ever. Can't wait to get a chance to go over it in detail.. :D thanks... – Blindman67 May 02 '16 at 03:42
  • 1
    So with 0.019608% error the graphics will get 4 pixels in error when the radius goes beyond 2551 pixels in a circle rather than that awful 0.027253% where we're a solid half-pixel of error (where the graphics engine will change the pixel) at 1835 px causing 2 pixels to be in error! – Tatarize Aug 01 '16 at 20:03
  • @Tatarize The article does not specify how the error was measured, it says maximum radial drift ? I presume the error is minimised along the curve 0<= t <= 1 to match the quadrant 0 <= pheta <= Pi/2 at t = 0 = 1/2 = 1 equals pheta = 0 = Pi/4 = Pi/4 the error is 0.019608% and the max error at t = ~0.1822 & t = ~ 0.8177 of 0.019608% (signs?) but at these points t does not equal pheta does the error include the angular drift? . 4pixels may or may not be correct. The error may be variance, thus error < 2pix for r = 2551. A lot of questions that will need investigation – Blindman67 Aug 01 '16 at 21:00
  • I'm pretty sure having looked at the error curve that the given adjustment simply moves the point down by enough to cause the max error above the arc-line to equal the max error below the arc-line. Which is to say we change the curve a bit down so all the error isn't positive. This adjustment means that we're crossing the arc line 4 times, with 4 points of maximum error. When the original spec'ed line had 2 points, namely at t=.25 and t=.75. With the adjustments it should be at t=.125, t=.375 t=.625 t=.875. This assumes we are using solid pixels and not anti-aliased which would change at 14px. – Tatarize Aug 01 '16 at 21:10
11

It is not possible. A Bezier is a cubic (at least... the most commonly used is). A circle cannot be expressed exactly with a cubic, because a circle contains a square root in its equation. As a consequence, you have to approximate.

To do this, you have to divide your circle in n-tants (e.g.quadrants, octants). For each n-tant, you use the first and last point as the first and last of the Bezier curve. The Bezier polygon requires two additional points. To be fast, I would take the tangents to the circle for each extreme point of the n-tant and choose the two points as the intersection of the two tangents (so that basically your Bezier polygon is a triangle). Increase the number of n-tants to fit your precision.

Stefano Borini
  • 138,652
  • 96
  • 297
  • 431
  • 4
    It's possible, so long as you use an infinite number of bezier curves, of zero length. Which is basically an infinite number of points, or rather just an arc curve. – Tatarize Dec 29 '15 at 19:47
  • "A circle cannot be expressed exactly with a cubic, because a circle contains a square root in its equation." This phrasing suggests that there's something special about cubics - would it work with any other degree of Bezier curve? (To my understanding, it would not.) – Karl Knechtel Aug 27 '23 at 15:59
8

The other answers have covered the fact that a true circle is not possible. This SVG file is an approximation using Quadratic Bezier curves, and is the closest thing you can get: http://en.wikipedia.org/wiki/File:Circle_and_quadratic_bezier.svg

Here's one with Cubic Bezier curves: http://en.wikipedia.org/wiki/File:Circle_and_cubic_bezier.svg

Jaffer
  • 745
  • 1
  • 9
  • 20
1

In case you need a pure JS version of @NoOorZ24's answer. This will return a SVG path:

function drawBezierOvalQuarter(centerX, centerY, sizeX, sizeY) {
  return `
  M ${centerX - sizeX} ${centerY}
  C ${centerX - sizeX} ${centerY - 0.552 * sizeY},
    ${centerX - 0.552 * sizeX} ${centerY - sizeY},
    ${centerX} ${centerY - sizeY}
  `;
}

function drawBezierOval(centerX, centerY, sizeX, sizeY) {
  return (
    drawBezierOvalQuarter(centerX, centerY, -sizeX, sizeY) +
    drawBezierOvalQuarter(centerX, centerY, sizeX, sizeY) +
    drawBezierOvalQuarter(centerX, centerY, sizeX, -sizeY) +
    drawBezierOvalQuarter(centerX, centerY, -sizeX, -sizeY)
  );
}
Pål Thingbø
  • 1,211
  • 1
  • 17
  • 17
-1

Sorry to bring this one back from the dead, but I found this post very helpful along with this page in coming up with an expandable formula.

Basically, you can create a near circle using an incredibly simple formula that allows you to use any number of Bezier curves over 4: Distance = radius * stepAngle / 3

Where Distance is the distance between a Bezier control point and the closest end of the arc, radius is the radius of the circle, and stepAngle is the angle between the 2 ends of the arc as represented by 2π / (the number of curves).

So to hit it in one shot: Distance = radius * 2π / (the number of curves) / 3

bubbinator
  • 487
  • 2
  • 7
  • 3
    This is not the best approximation of a circle. The best one is `Distance = (4/3)*tan(pi/2n)`. For big number of arcs it is almost the same because `tan(pi/2)~pi/2n`, but for example for `n=4` (which is the most used case) your formula gives `Distance=0.5235...` but the optimal one is `Distance=0.5522...` (so you have ~5% error). – Kpym Jan 09 '15 at 13:37
-3

It's a heavy approximation that will look reasonable or terrible depending on the resolution and precision but I use sqrt(2)/2 x radius as my control points. I read a rather long text how that number is derived and it's worth reading but the formula above is the quick and dirty.

  • Aside from contributing an admittedly worse approximation without giving a derivation or citation... this doesn't even properly explain how to determine the points - in particular, it proposes a single formula in terms of the radius (a scalar value) in order to compute two different control points (two-dimensional Cartesian coordinates). That is clearly inadequate. – Karl Knechtel Aug 27 '23 at 16:01