从弧度计算三次Bézier曲线的控制点

时间:2015-12-25 22:19:45

标签: javascript math geometry bezier

我有一个由两个端点(P0 = x1,y1和P4 = x2,y2)定义的几何弧,以及半径R或弧的中心(C = xc,yc)。我需要计算javascript中立方Bézier曲线的两个控制点P2,P3。在我的特殊情况下,弧角将小于90度。

我已经搜索了互联网和stackoverflow,并且任何解决方案都是不完整的,没有针对不确定角度的弧度推广,或者太复杂,我无法理解。

有没有人有任何javascript代码或伪代码可以帮助?我之前曾问过一个类似的问题,但在需要立方Bézier曲线时,不正确地将Bézier曲线称为二次曲线。

1 个答案:

答案 0 :(得分:3)

圆弧的立方贝塞尔近似,由起点和终点的坐标,圆的中心和半径定义 - (x1,y1) = P0, (x2,y2) = P3, C = (cx,cy), R
(可以找到由角度定义的arс的近似值here

Bezier的控制点应位于圆上给定点的切线上。一种可能的近似方法 - (对称)曲线的中点应该放在圆上 (注意,对于良好的近似弧角不能很大)

给定点的半径矢量:

V1 = (x1-cx, y1 - cy)
V2 = (x2-cx, y2 - cy)

切线向量:

T1 = (cy – y1, x1 – cx)
T2 = (y2 - cy, cx – x2)

控制点的坐标(k - 未知因子):

P1 = P0 + k * T1
P2 = P3 + k * T2

Bezier的中点:

MB = B(1/2) = P0 * 1/8 + P1 * 3/8 + P2 * 3/8 + P3 * 1/8 = 
P0 * 1/8 + P0 * 3/8  + k * T1 * 3/8 + P3 * 3/8 + k * T2 * 3/8 +  P3 * 1/8 =
(P0 + P3)/2 + k * 3/8 * (T1 +T2)

现在求解k因子

的方程
(MB.X – cx)^2 + (MB.Y – cy)^2 = R^2

有两种可能的解决方案 - 如果输入点的顺序正确,我们需要正解决方案(通常情况下,弧线< Pi - 具有最小幅度)

Delphi代码(不检查输入数据,不处理额外的情况),结果如下: enter image description here

  procedure BezierArcByPoints(x1, y1, x2, y2, cx, cy, R: Integer;
    var Pts: array of TPoint);
  var
    t1x, t1y, t2x, t2y, dx, dy, k, tx, ty, D, a, b, c: Double;
  begin
    t1x := cy - y1;
    t1y := x1 - cx;
    t2x := y2 - cy;
    t2y := cx - x2;
    dx := (x1 + x2) / 2 - cx;
    dy := (y1 + y2) / 2 - cy;
    tx := 3 / 8 * (t1x + t2x);
    ty := 3 / 8 * (t1y + t2y);
    a := tx * tx + ty * ty;
    b := dx * tx + dy * ty;
    c := dx * dx + dy * dy - R * R;
    D := b * b - a * c;
    if D > 0 then begin
      k := (Sqrt(D) - b) / a;
      Pts[0] := Point(x1, y1);
      Pts[3] := Point(x2, y2);
      Pts[1] := Point(x1 + Round(k * t1x), y1 + Round(k * t1y));
      Pts[2] := Point(x2 + Round(k * t2x), y2 + Round(k * t2y));
    end;
  end;

var
  Pts: array [0 .. 3] of TPoint;
  an1, an2: Double;
begin
  an1 := 0;
  an2 := Pi / 2;
  Canvas.Pen.Color := clBlue;
  Canvas.Pen.Width := 1;
  Canvas.Ellipse(100, 100, 301, 301);
  BezierArcByPoints(200 + Round(100 * Cos(an1)), 
                   200 + Round(100 * Sin(an1)),
                   200 + Round(100 * Cos(an2)), 
                   200 + Round(100 * Sin(an2)), 
                   200, 200, 100, Pts);
  Canvas.Pen.Color := clRed;
  Canvas.Pen.Width := 3;
  Canvas.PolyBezier(Pts);
end;