如何用贝塞尔曲线创建圆?


Answers:


138

如前所述:没有使用贝塞尔曲线的确切表示。

要完成其他答案:对于曲线n段为Bezier的曲线,到控制点的最佳距离为,曲线的中心位于圆自身上(4/3)*tan(pi/(2n))

n个细分的公式

所以是4分(4/3)*tan(pi/8) = 4*(sqrt(2)-1)/3 = 0.552284749831

4点情况


2
通过最佳距离,您正在优化哪种指标?如用三次贝塞尔曲线近似一个圆所示,通过不同的值可以实现最低的最大漂移。您能否提供一些链接来定义“最优”在您的情况下的含义,或公式的推导方式?
2015年

1
@Suma对于某些距离来说不是最佳的。这是最佳会对圆曲线的中间。如果您提出其他标准,当然可以做得更好。
Kpym

2
好。我将尝试改写:“到控制点的距离,以使曲线的中心位于圆本身上”。我认为这是一个有效的决定(足够好并且易于计算),但我不会将其称为最佳决定(至少在没有写出最佳决定的意义上也是如此)。
苏马

1
是的,因为相对于真实圆,此偏差的最大偏差为+ 0.027%,最小偏差为-0。它仅比实圆大,通过将C移入0.027%的一半,可以更好地改善逼近度。如果您想要圆上的中点,那肯定是这样做的方法。
2015年

2
@ legends2k我将LaTeX和TikZ一起使用以生成PDF,然后将其转换为PNG。
Kpym'1

34

在comp.graphics.faq中涵盖

摘抄:

主题4.04:如何将贝塞尔曲线拟合到圆上?

有趣的是,贝塞尔曲线可以近似一个圆,但不能完美地拟合一个圆。一个常见的近似方法是使用四个贝塞尔曲线来建模一个圆,每个贝塞尔曲线的控制点到端点的距离为d = r * 4 *(sqrt(2)-1)/ 3(其中r是圆半径),而在在端点处与圆相切的方向。这样可以确保贝塞尔曲线的中点在圆上,并且一阶导数是连续的。
这种近似的径向误差约为圆半径的0.0273%。

Michael Goldapp,“三次多项式近似圆弧”,计算机辅助几何设计(#8 1991 pp.227-238)

Tor Dokken和Morten Daehlen,“曲率连续的贝塞尔曲线的圆的良好近似”,计算机辅助几何设计(1990年第7页,第33-41页)。http://www.sciencedirect.com/science/article/pii/016783969090019N(非免费文章)

另请参见http://spencermortensen.com/articles/bezier-circle/上的非付费文章

浏览器和画布元素。

请注意,某些浏览器使用Bezier曲线绘制其画布绘制弧,Chrome(目前)使用4扇区方法,而Safari使用8扇区方法,这种差异仅在高分辨率时才明显,因为该分辨率为0.0273%,并且只有在圆弧平行且异相绘制时才真正可见,您会注意到圆弧从真实圆中振荡。当曲线围绕其径向中心进行动画处理时,效果也更加明显,半径通常为600px,会产生变化。

某些绘图API没有真正的圆弧渲染,因此它们也使用Bezier曲线,例如Flash平台没有圆弧绘图api,因此任何提供圆弧的框架通常都使用相同的Bezier曲线方法。

请注意,浏览器中的SVG引擎可能使用不同的绘制方法。

其他平台

无论您尝试使用哪种平台,都值得检查一下圆弧绘制的完成方式,以便您可以预测这样的视觉错误并进行调整。


谢谢,我代替它。
ocodo

31

该问题的答案非常好,因此几乎没有补充。受此启发,我开始进行实验以目视确认解决方案,从4条Bézier曲线开始,将曲线数减少为1。令人惊讶的是,我发现三个Bézier曲线对我来说看起来不错,但是构造有些棘手。实际上,我使用Inkscape将黑色的1像素宽的贝塞尔近似值放置在红色的3像素的圆上(由Inkscape生产)。为了澄清起见,我添加了蓝线和曲面以显示贝塞尔曲线的边界框。

为了展示自己,我正在展示我的结果:

1曲线图(看起来像是挤在角落里的水滴,只是为了完整性):在此处输入图片说明

2曲线图:在此处输入图片说明

3曲线图:在此处输入图片说明

4曲线图: 在此处输入图片说明

(我想在此处放置SVG或PDF,但不支持)


1
到目前为止,svg可以作为html代码段包含在内。例如,请参见以下答案:stackoverflow.com/a/32162431
TS

1
@TS:当我试图用自己拥有的SVG替换图形时,我意识到我丢失了今年年初被盗的USB记忆棒。如果时间允许,我会尽快重新创建它们。但是,如果SVG可以作为XML代码添加(并且不显示为图形),则在这里没有太大意义。
U. Windl

如果您的浏览器支持svg,则在您单击“运行代码段”后就会渲染图像(显然,该按钮在stackoverflow的移动版本上不可用...)。请参阅我链接的答案。
TS

1
@TS:对于更长的文件,恕我直言。
U. Windl

9

已经有很多答案,但是我发现了一篇在线文章,其很好的三次方贝塞尔近似于一个圆。以单位圆为单位,c = 0.55191502449,其中c是沿切线从轴截距点到控制点的距离。

作为单位圆的一个象限,其中两个中间坐标为控制点。 (0,1),(c,1),(1,c),(1,0)

径向误差仅为0.019608%,因此我只需要将其添加到此答案列表中即可。

可以在此处找到该文章近似具有三次贝塞尔曲线的圆


5
您是否已阅读Stackoverflow的Mike'Pomax'Kamermans撰写的关于Bezier曲线的出色论文。值得一读!:-)
markE

1
@markE非常感谢您提供的链接,这是我在该主题上见过的“最出色的”论文之一。等不及要有机会详细介绍它了..:D谢谢...
Blindman67

1
因此,如果半径为0.019608%,则当半径超过一个圆圈的2551像素时,图形将获得4个像素的错误,而不是可怕的0.027253%,因为我们是一个固定的半像素错误(图形引擎将更改像素)在1835像素,导致2像素错误!
2015年

@Tata​​rize本文未指定如何测量误差,而是说最大径向漂移?我假设误差沿着曲线0 <= t <= 1最小化以匹配象限0 <= pheta <= Pi / 2在t = 0 = 1/2 = 1等于pheta = 0 = Pi / 4 = Pi / 4误差为0.019608%,t =〜0.1822&t =〜0.8177时的最大误差为0.019608%(符号?),但在这些点t不等于pheta时,误差是否包括角漂移?。4个像素可能正确,也可能不正确。该错误可能是方差,因此对于r = 2551,错误<2pix。很多问题需要调查
Blindman67

我很确定已经查看了误差曲线,即给定的调整只是将点向下移动了足够多的距离,以使弧线上方的最大误差等于弧线下方的最大误差。这就是说我们稍微降低了曲线,所以所有的误差都不是正的。这种调整意味着我们要越过弧线4次,最大误差为4点。当原始规格线有2个点时,即t = .25和t = .75。进行调整后,它应为t = .125,t = .375 t = .625 t = .875。假设我们使用的是实像素而不是反走样,像素会以14像素变化。
2013年

8

这不可能。贝塞尔曲线是立方的(至少...最常用的是)。圆不能精确地用三次方表示,因为圆的方程式中包含平方根。因此,您必须近似。

为此,您必须将圆分成n-tant(四分位数,八分位数)。对于每个正态,将第一个和最后一个点用作Bezier曲线的第一个和最后一个点。贝塞尔曲线多边形需要另外两个点。为了快速起见,我将正切线的每个极点的切线取到圆上,然后选择两个点作为两个切线的交点(这样,您的Bezier多边形基本上就是一个三角形)。增加n-tant的数量以适合您的精度。


4
只要您使用无限长且长度为零的贝塞尔曲线,就有可能。基本上,这是无数个点,或者仅仅是弧形曲线。
2015年


7

对于只在寻找代码的人:

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>

这样可以绘制由4个贝塞尔曲线构成的圆。用JS编写,但可以轻松翻译成任何其他语言


这非常有帮助,谢谢!需要进行哪些更改才能使4个细分市场井井有条?我需要沿路径写文本,但现在它分散在4个部分中
Alexa

1

我不确定是否应该提出新的问题,因为这是关于近似的问题,但是我对通用公式感兴趣,可以获取贝塞尔的任意程度的控制点,并且我相信它适合这个问题。我在网上找到的所有解决方案仅适用于三次曲线或已付款,或者我什至不了解(我不太擅长数学)。因此,我决定尝试自行解决此问题。我研究了控制点到圆心的距离(取决于给定角度),到目前为止,我发现:

在此处输入图片说明

其中N为单曲线的控制点数量,为α圆弧角。

对于二次曲线,可以将其简化为。l ≈ r + r * PI*0.1 * pow(α/90, 2)PI*0.1是一个猜测-我没有计算完美值,但是非常接近。这对于具有1-2个控制点的曲线相当有效,三次曲线的半径误差约为0.2%。对于更高程度的曲线,精度的损失是明显的。具有3个控制点的曲线看起来与二次曲线相似,因此很明显我想念一些东西,但是我无法弄清楚,这种方法通常可以满足我现在的需求。这是演示


您使用什么软件来创建此图像?
Qian Sijianhao

1
从截图我的演示+数学书写板从赢(或无论该名翻译)7 + MS画图
帕维尔Audionysos

0

对不起,把从死人此一回,但我发现这个职位连同很有帮助页面中想出一个可扩展的计算公式。

基本上,您可以使用一个非常简单的公式来创建一个近圆,该公式允许您使用超过4条的任意数量的贝塞尔曲线: Distance = radius * stepAngle / 3

其中Distance是贝塞尔曲线控制点与圆弧的最近端之间的距离,半径是radius圆的半径,并且stepAngle是圆弧的两个端点之间的角度,以2π/(曲线数)表示。

因此,一枪打中: Distance = radius * 2π / (the number of curves) / 3


1
这不是圆的最佳近似。最好的是Distance = (4/3)*tan(pi/2n)。对于大量弧来说,这几乎是相同的,因为tan(pi/2)~pi/2n,例如,对于n=4(这是最常用的情况),您的公式给出Distance=0.5235...的值是最优的Distance=0.5522... (因此,您有〜5%的误差)。
Kpym

-2

这是一个粗略的近似值,取决于分辨率和精度,看起来合理或可怕,但是我将sqrt(2)/ 2 x半径用作控制点。我读了一篇相当长的文章,该数字是如何得出的,值得一读,但是上面的公式又快又脏。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.