简体   繁体   English

用贝塞尔曲线绘制圆弧

[英]Drawing arc with bezier curves

I am trying to draw an arc using bezier curves. 我正在尝试使用贝塞尔曲线绘制圆弧。 I have learned that you can't draw a perfect circle using bezier curves but you can come close. 我了解到不能使用贝塞尔曲线绘制完美的圆,但是可以接近。 Unfortunately the math is too complicated and I can't personally figure it out. 不幸的是,数学太复杂了,我个人无法弄清楚。

I can create the A1 slice below as a triangle, but I can't figure out how to determine the control points. 我可以将下面的A1切片创建为三角形,但无法弄清楚如何确定控制点。 Also if I try drawing a slice out of a circle in the opposite direction, notice how the control points seem to point in the negative direction. 另外,如果我尝试沿相反的方向从圆中绘制切片,请注意控制点似乎指向负方向。

So if I want a slice of a circle with a radius R and I have already computed the anchor points, how can I calculate the position of control point 1 and control point 2??? 因此,如果我想要一个半径为R的圆的切片并且已经计算出锚点,那么如何计算控制点1和控制点2的位置?

例

Feng Yuan proposed simple method in his book Windows Graphics Programming : build an arc with radius 1, centered at OX axis, calculate Bezier approximation for it, and scale, translate and rotate control points for needed arc parameters. 冯元在他的《 Windows图形编程》一书中提出了一种简单的方法:建立一个以OX轴为中心的半径为1的圆弧,为其计算Bezier近似值,并缩放,平移和旋转所需圆弧参数的控制点。 Here is my implementation of this method (in Delphi), modified for large arcs. 这是我针对大型弧修改的该方法的实现(在Delphi中)。 C++ sources can be found somewhere in the Internet, but I hope that the logic is clear. C ++源代码可以在Internet上找到,但是我希望逻辑清楚。

  GenerateBezierArc(200, 200, 150, Pi / 4, 3 * Pi / 2, Pts);
  Canvas.PolyBezier(Pts);

result: 结果:

在此处输入图片说明

type
  TPointArray = array of TPoint;

//calculates array of Bezier control points
//for circle arc with center CX, CY and radius R
procedure GenerateBezierArc(CX, CY, R: Integer;
                            StartAngle, SweepAngle: Double;
                            var Pts: TPointArray);
// C-Pascal translation from Feng Yuan book, with correction of source errors
var
  iCurve, NCurves: Integer;
  i: Integer;
  x0, y0, tx, ty, sn, cs, ASweep, AStart: Double;
  Px, Py: array [0 .. 3] of Double;
begin
  if SweepAngle = 0 then
    Exit;
  // if SweepAngle is too large, divide arc to smaller ones
  NCurves := Ceil(Abs(SweepAngle) / (Pi/2));
  SetLength(Pts, 3 * NCurves + 1);
  ASweep := SweepAngle / NCurves;

  // calculates control points for Bezier approx. of arc with radius=1,
  // circle center at (0,0), middle of arc at (1,0)
  y0 := Sin(ASweep / 2);
  x0 := Cos(ASweep / 2);
  tx := (1 - x0) * 4 / 3;
  ty := y0 - tx * x0 / (y0 + 0.0001);
  Px[0] := x0;
  Py[0] := -y0;
  Px[1] := x0 + tx;
  Py[1] := -ty;
  Px[2] := x0 + tx;
  Py[2] := ty;
  Px[3] := x0;
  Py[3] := y0;

  // rotation and translation of control points
  sn := Sin(StartAngle + ASweep / 2);
  cs := Cos(StartAngle + ASweep / 2);
  Pts[0].X := CX + Round(R * (Px[0] * cs - Py[0] * sn));
  Pts[0].Y := CY + Round(R * (Px[0] * sn + Py[0] * cs));

  for iCurve := 0 to NCurves - 1 do begin
    AStart := StartAngle + ASweep * iCurve;
    sn := Sin(AStart + ASweep / 2);
    cs := Cos(AStart + ASweep / 2);
    for i := 1 to 3 do begin
      Pts[i + iCurve * 3].X := CX + Round(R * (Px[i] * cs - Py[i] * sn));
      Pts[i + iCurve * 3].Y := CY + Round(R * (Px[i] * sn + Py[i] * cs));
    end;
  end;
end;

The article referenced by Duncan's post is actually the result for 90 degree circular arc from a journal paper authored by Tor Dokken (the main author) and published in Computer Aided Geometric Design Vol 7 in 1990. It cited two approaches for approximating a 90 degree arc: a standard approach and a better approach. Duncan帖子引用的文章实际上是90度圆弧的结果,该结果来自Tor Dokken(主要作者)并于1990年在《计算机辅助几何设计》第7卷上发表。它列举了两种近似90度圆弧的方法。 :标准方法和更好的方法。 I will list the general formula for the "standard approach" below and leave out the general formula for the "better approach" as it requires a lot of typing: 我将在下面列出“标准方法”的一般公式,并省略“更好方法”的一般公式,因为它需要大量输入:

For a circular arc with angular span A and unit radius, described as C(t) = (cos(t), sin(t)), where t=[0, A], a good cubic Bezier curve approximation can be obtained with the following control points: 对于具有角跨度A和单位半径的圆弧,描述为C(t)=(cos(t),sin(t)),其中t = [0,A],可以得到良好的三次贝塞尔曲线近似以下控制点:

P(0) = (1, 0), P(0)=(1,0),
P(1) = (1, 0) + L(0,1), P(1)=(1,0)+ L(0,1),
P(2) = (cosA, sinA) - L (-sinA, cosA), P(2)=(cosA,sinA)-L(-sinA,cosA),
P(3) = (cosA, sinA) P(3)=(cosA,sinA)

where L is a scalar constant depending on A as 其中L是取决于A的标量常数

L = (4/3)*tan(A/4) L =(4/3)*棕褐色(A / 4)

Please note that the cubic Bezier curve approximation obtained this way always interpolates the two end points and the mid-point of the circular arc and the approximation error is always positive, which means the cubic Bezier curve is always "outside" the circular arc. 请注意,以这种方式获得的三次贝塞尔曲线近似值始终会插补圆弧的两个端点和中点,并且近似误差始终为正,这意味着三次贝塞尔曲线总是位于圆弧“外部”。

The maximum radial error (x(t)^2 + y(t)^2 - 1) from this simple formula is 这个简单公式得出的最大径向误差(x(t)^ 2 + y(t)^ 2-1-)

Error_max = (4/27) * ( power(sin(A/4),6)/power(cos(A/4),2) ) 误差最大值=(4/27)*(power(sin(A / 4),6)/ power(cos(A / 4),2))

When you want to approximate a general circular arc (any angle span and any radius radius) within a certain tolerance, you can use this formula to compute how many segments you need to break the circular arc into and approximate each arc segment with a cubic Bezier curve. 当您想要在一定的公差范围内近似一般的圆弧(任意角度范围和任何半径的半径)时,可以使用此公式计算将圆弧分成多少段并使用三次方贝塞尔曲线近似每个圆弧段曲线。 Since this cubic Bezier curve will honor the end points and end slopes, all cubic Bezier curves obtained will join smoothly together. 由于此三次贝塞尔曲线将遵循端点和结束斜率,因此所有获得的三次贝塞尔曲线将平滑地连接在一起。

This article gives a set of 4 bezier curves that generates a very close approximation of a circle. 本文给出了一组4个贝塞尔曲线,这些曲线生成非常接近的圆。 It divides the circle into 4 quarters and each curve generates 1/4 of the circle. 它将圆分成四个四分之一,每个曲线生成圆的1/4。

I don't know how you'd come up with the control points for an arbitrary arc along a circle. 我不知道您怎么想出沿圆弧的任意弧的控制点。 You'd use trig to find the start and end points, but the middle points would be harder. 您将使用Trig查找起点和终点,但中间点会更难。

The conclusion of the article: 文章结论:

The maximum radial drift is 0.019608% with this approximation. 通过这种近似,最大径向漂移为0.019608%。 This is 28% better than the standard approximation. 这比标准近似值好28%。 Here is the final result: 这是最终结果:

Figure 4. The Bézier approximation is almost indistinguishable from a circle. 图4.贝塞尔逼近与圆几乎无法区分。 Figure 4 was created using the Bézier curves: P_0 = (0,1), P_1 = (c,1), P_2 = (1,c), P_3 = (1,0) P_0 = (1,0), P_1 = (1,-c), P_2 = (c,-1), P_3 = (0,-1) P_0 = (0,-1), P_1 = (-c,-1), P_3 = (-1,-c), P_4 = (-1,0) P_0 = (-1,0), P_1 = (-1,c), P_2 = (-c,1), P_3 = (0,1) with c = 0.551915024494. 使用贝塞尔曲线创建图4:P_0 =(0,1),P_1 =(c,1),P_2 =(1,c),P_3 =(1,0)P_0 =(1,0),P_1 = (1,-c),P_2 =(c,-1),P_3 =(0,-1)P_0 =(0,-1),P_1 =(-c,-1),P_3 =(-1,- c),P_4 =(-1,0),P_0 =(-1,0),P_1 =(-1,c),P_2 =(-c,1),P_3 =(0,1),其中c = 0.551915024494。

That's for a unit circle (a circle on the origin with a radius of 1) You'd need to scale it for other radius values. 那是一个单位圆(原点上半径为1的圆),您需要将其缩放为其他半径值。

EDIT: 编辑:

If you assume that your arc will always be 1/4 of a circle or less, then you could use the Bezier curve for a 1/4 circle, and draw a portion of that arc by varying the range of the t parameter to a range less than t=0 -> t=1. 如果您假设圆弧总是小于等于1/4的圆,则可以将Bezier曲线用于1/4圆,并通过将t参数的范围更改为一个范围来绘制该圆弧的一部分小于t = 0-> t = 1。 You'd need to apply a rotation transform to your points to move them around the circle. 您需要对点应用旋转变换,以使其围绕圆移动。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM