贝塞尔曲线和椭圆的关系?

问题描述 投票:0回答:5

我想在 html5 canvas 中画一个椭圆形,我在 stackoverflow 中找到了一个很好的方法。但是我还有另一个问题。

function drawEllipse(ctx, x, y, w, h) {
  var kappa = 0.5522848;
      ox = (w / 2) * kappa, // control point offset horizontal
      oy = (h / 2) * kappa, // control point offset vertical
      xe = x + w,           // x-end
      ye = y + h,           // y-end
      xm = x + w / 2,       // x-middle
      ym = y + h / 2;       // y-middle

  ctx.beginPath();
  ctx.moveTo(x, ym);
  ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
  ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
  ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
  ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
  ctx.closePath();
  ctx.stroke();
}

上面链接中的方法使用了 bezierCurveTo 来绘制椭圆,但是它已经绘制了 bezierCurveTo 4 次。但我认为只需 2 bezierCurveTo 就可以画一个椭圆。像这样:

enter image description here

但是我数学很弱,有人可以告诉我“控制点”和“椭圆点”的关系吗?还是必须画4条贝塞尔曲线才能画椭圆?

谢谢大家

html canvas relationship bezier
5个回答
9
投票

我的背景不是数学,所以如果我错了,我相信有人会纠正我,但根据我的理解,我们可以画出一个非常好的椭圆的近似,只有两条三次贝塞尔曲线但是坐标会有点棘手。

要回答有关椭圆点和控制点之间关系的问题,我认为最好通过观看此视频来回答,如果您熟悉插值,则从我选择的点开始,或者如果您熟悉插值,则从头开始不是。别担心它很短。

我们可能会遇到的一个问题是,当我们从顶部开始,以矩形的角(具有相同的宽度和高度)作为控制点,做一条 bezierCurveTo 椭圆的底部时,椭圆的宽度将会比矩形小。 .75 倍我们想要的尺寸。所以我们可以相应地缩放控制点。

我们的控制点的 x 将像这样调整(假设宽度是椭圆的宽度,我们除以二以获得其相对于原点的偏移量)

var cpx = (width / .75) / 2;

将可视化放在一起,您可以在其中调整宽度和高度并查看绘制的椭圆。

红色椭圆是我们想要的绘制方式,内部椭圆是如果我们不重新定位控制点的话将如何绘制。这些线条说明了视频中显示的 De Casteljau 算法。

这是可视化的屏幕截图 enter image description here


2
投票

您只需要两条三次贝塞尔曲线即可绘制椭圆。这是 DerekR 代码的简化版本,它使用您提供的原始函数参数 - 假设您希望 x 和 y 成为椭圆的中心:

jsFiddle

function drawEllipse(ctx, x, y, w, h) {
    var width_over_2 = w / 2;
    var width_two_thirds = w * 2 / 3;
    var height_over_2 = h / 2;

    ctx.beginPath();
    ctx.moveTo(x, y - height_over_2);
    ctx.bezierCurveTo(x + width_two_thirds, y - height_over_2, x + width_two_thirds, y + height_over_2, x, y + height_over_2);
    ctx.bezierCurveTo(x - width_two_thirds, y + height_over_2, x - width_two_thirds, y - height_over_2, x, y -height_over_2);
    ctx.closePath();
    ctx.stroke();
}

2
投票

非常感谢 BKH。 我使用他的代码和两条贝塞尔曲线来完成任意旋转角度的椭圆绘制。此外,我还创建了贝塞尔曲线绘制的椭圆与本机 ellipse() 函数(目前仅在 Chrome 中实现)之间的比较 demo

function drawEllipseByBezierCurves(ctx, x, y, radiusX, radiusY, rotationAngle) {
var width_two_thirds = radiusX * 4 / 3;

var dx1 = Math.sin(rotationAngle) * radiusY;
var dy1 = Math.cos(rotationAngle) * radiusY;
var dx2 = Math.cos(rotationAngle) * width_two_thirds;
var dy2 = Math.sin(rotationAngle) * width_two_thirds;

var topCenterX = x - dx1;
var topCenterY = y + dy1;
var topRightX = topCenterX + dx2;
var topRightY = topCenterY + dy2;
var topLeftX = topCenterX - dx2;
var topLeftY = topCenterY - dy2;

var bottomCenterX = x + dx1;
var bottomCenterY = y - dy1;
var bottomRightX = bottomCenterX + dx2;
var bottomRightY = bottomCenterY + dy2;
var bottomLeftX = bottomCenterX - dx2;
var bottomLeftY = bottomCenterY - dy2;

ctx.beginPath();
ctx.moveTo(bottomCenterX, bottomCenterY);
ctx.bezierCurveTo(bottomRightX, bottomRightY, topRightX, topRightY, topCenterX, topCenterY);
ctx.bezierCurveTo(topLeftX, topLeftY, bottomLeftX, bottomLeftY, bottomCenterX, bottomCenterY);
ctx.closePath();
ctx.stroke();

}


2
投票

您会发现这在http://pomax.github.io/bezierinfo/#circles_cubic中进行了稍微基于数学的解释,但要点是使用三次贝塞尔曲线超过四分之一圈通常不是一个好的选择主意。值得庆幸的是,使用四条曲线使得找到所需的控制点变得相当容易。从一个圆开始,在这种情况下,每个四分之一圆是 (1,0)--(1,0.55178)--(0.0.55178,1)--(0,1) 以及椭圆的缩放坐标。绘制四次,交换 +/- 符号以实现完整的圆形,缩放尺寸以获得椭圆形,然后完成。

(您可能还会看到人们告诉您使用 0.55228,基于 4(√2 - 1)/3,但这并不是实际上最合适的。如果您无论如何都要使用常量,0.55178 会产生更好的结果曲线)

如果使用两条曲线,每个半球的坐标为(1,0)--(1,4/3)--(-1,4/3)--(-1,0),然后再次缩放为你的椭圆。一般来说,这看起来不太好,但它可能看起来仍然足够不错,具体取决于你的应用程序以及你的绘图最终有多大。


0
投票

从数学上可以证明,任何阶数的贝塞尔曲线都不能构成圆。你可以通过近似来制作“几乎是圆”。

假设您想围绕 [0,0] 绘制四分之一圆。三次贝塞尔坐标为:

[0   , 1   ]
[0.55, 1   ]
[1   , 0.55]
[1   , 0   ]

这是一个非常好的近似值。将其线性变换以获得椭圆。

© www.soinside.com 2019 - 2024. All rights reserved.