我们有一个起始点(x,y)和一个圆形半径。还存在一个可以从贝塞尔曲线点创建路径的引擎。
如何使用贝塞尔曲线创建圆?
我们有一个起始点(x,y)和一个圆形半径。还存在一个可以从贝塞尔曲线点创建路径的引擎。
如何使用贝塞尔曲线创建圆?
如前所述:使用贝塞尔曲线无法精确表示圆形。
为了补充其他答案:对于有n
个线段的贝塞尔曲线,控制点到圆的最佳距离是(4/3)*tan(pi/(2n))
,其中中心点位于圆上。
因此,对于4个点,它是 (4/3)*tan(pi/8) = 4*(sqrt(2)-1)/3 = 0.552284749831
。
包含于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,“连续曲率贝塞尔曲线的良好逼近圆”的计算几何设计辅助 (#7 1990 pp. 33-41)。http://www.sciencedirect.com/science/article/pii/016783969090019N(需付费阅读)
还可参见免费文章:http://spencermortensen.com/articles/bezier-circle/
请注意,一些浏览器在canvas绘制圆弧时使用贝塞尔曲线,Chrome使用(目前)四个扇区的方法,而Safari使用八个扇区的方法,这种差异仅在高分辨率下才能察觉到,因为只有0.0273%,并且只在平行和不同相位的弧线绘制时真正可见,您会注意到弧线会从真正的圆形波动。当弧线围绕其径向中心旋转时,效果也更加明显,通常半径为600像素时会有所差异。
某些绘图API没有真正的弧形渲染功能,因此它们也使用贝塞尔曲线。例如Flash平台没有弧形绘制API,所以任何提供弧线的框架通常都使用相同的贝塞尔曲线方法。
请注意,浏览器中的SVG引擎可能使用不同的绘图方法。
无论你试图使用哪个平台,最好检查一下弧形绘制是如何完成的,这样你就可以预测出类似的视觉错误并进行适应。
对于只是寻找代码的人: 四个部分的解决方案
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>
bezierCurveTo()
之后再加上另一个 ctx.lineTo(centerX, centerY);
就可以解决这个问题。 - drott(0,1),(c,1),(1,c),(1,0)
径向误差仅为0.019608%,因此我只需要将其添加到答案列表中即可。这是不可能的。贝塞尔曲线是三次曲线(至少是最常用的)。由于圆包含平方根,因此无法使用三次曲线精确表示圆的方程。因此,您必须进行逼近。
为了做到这一点,您需要将圆分成n个部分(例如四分之一、八分之一)。对于每个部分,您使用第一个和最后一个点作为贝塞尔曲线的起点和终点。贝塞尔多边形需要两个额外的点。为了快速计算,我会针对每个部分的极端点取切线,并选择两条切线的交点作为这两个额外的点(以便基本上您的贝塞尔多边形是一个三角形)。增加n的数量以适应您的精度。
我不确定是否应该开新问题,因为这是关于近似值的问题,但我对获得任何次数的Bezier曲线控制点的通用公式感兴趣,并且我相信它适用于此问题。
我在网上找到的所有解决方案都只适用于三次曲线或需要付费,或者我甚至无法理解(我数学不太好)。
所以我决定自己解决这个问题。我正在研究控制点与给定角度的圆心距离,到目前为止我发现:
其中N
是单个曲线的控制点数量,α
是圆弧角度。
对于二次曲线,它可以简化为l ≈ r + r * PI*0.1 * pow(α/90, 2)
PI*0.1
是一个猜测 - 我没有计算出完美的值,但它非常接近。
这对于具有1-2个控制点的曲线效果相当不错,对于三次曲线,半径误差约为0.2%。对于更高阶的曲线,精度损失是明显的。对于具有3个控制点的曲线,曲线看起来类似于二次曲线,所以显然我漏掉了什么,但我无法弄清楚,并且这种方法通常适合我的需求。
这里是演示。
如果您需要@NoOorZ24答案的纯JS版本。这将返回一个SVG路径:
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)
);
}
很抱歉从死亡区域中找回这篇文章,但我发现这篇文章和this页面一起对创建可伸缩公式有很大帮助。
基本上,你可以使用一个非常简单的公式创建一个接近圆形的形状,该公式允许您使用超过4个贝塞尔曲线:距离=半径*步角/3
其中距离
是贝塞尔控制点与弧线最近端之间的距离,半径
是圆的半径,而步角
则是表示由两个π/(曲线数量)表示的弧末端之间的角度。
因此,为了一次性完成它:距离=半径*2π/(曲线数量)/3
Distance = (4/3)*tan(pi/2n)
。对于大量的弧,它几乎相同,因为 tan(pi/2)~pi/2n
,但例如对于 n=4
(这是最常用的情况),你的公式给出了 Distance=0.5235...
,但最优的公式是 Distance=0.5522...
(所以你有约5%的误差)。 - Kpym