| 特性 | PolyBezier | PolyBezierTo |
| 最少點數 | 4 (起點 + 2控制點 + 終點) | 3 (2控制點 + 終點,以當前位置為起點) |
| 陣列索引 | 通常 0-based: pts[0..3] | 通常 1-based: pts[1..3] (0 留給當前位置) |
| 改變當前位置 | 否 | 是 (移至最後終點) |
| 多段曲線點數公式 | 3n + 1 | 3n |
單段曲線基礎範例
// PolyBezier:自行指定起點
procedure TForm1.Button1Click(Sender: TObject);
var
Pts: array[0..3] of TPoint;
begin
Canvas.Pen.Width := 2;
Canvas.Pen.Color := clRed;
Pts[0] := Point(10, 10); // 起點
Pts[1] := Point(60, 10); // 控制點 1
Pts[2] := Point(10, 100); // 控制點 2
Pts[3] := Point(60, 100); // 終點
Canvas.PolyBezier(Pts);
// 驗證當前位置未變
Canvas.Pen.Width := 1;
Canvas.Pen.Color := clWhite;
Canvas.LineTo(ClientWidth, ClientHeight); // 從 (0,0) 畫線
end;
// PolyBezierTo:以 MoveTo 設定起點
procedure TForm1.Button2Click(Sender: TObject);
var
Pts: array[1..3] of TPoint; // 索引從 1 開始
begin
Canvas.Pen.Width := 2;
Canvas.Pen.Color := clRed;
Canvas.MoveTo(10 + 82, 10); // 設定起點 (偏移 82 像素避開第一條)
// Pts[0] 隱式為當前位置
Pts[1] := Point(60 + 82, 10); // 控制點 1
Pts[2] := Point(10 + 82, 100); // 控制點 2
Pts[3] := Point(60 + 82, 100); // 終點
Canvas.PolyBezierTo(Pts);
// 當前位置已移至終點
Canvas.Pen.Width := 1;
Canvas.Pen.Color := clBlue;
Canvas.LineTo(ClientWidth, ClientHeight); // 從終點畫線
end;
多段連續貝塞爾曲線
// PolyBezier 多段:3n+1 個點,終點自動成下段起點
procedure TForm1.Button1Click(Sender: TObject);
var
Pts: array[0..6] of TPoint; // 2 段曲線:3*2+1=7
begin
Canvas.Pen.Width := 2;
Canvas.Pen.Color := clRed;
Pts[0] := Point(10, 10);
Pts[1] := Point(60, 10);
Pts[2] := Point(10, 100);
Pts[3] := Point(40, 100); // 第 1 段終點 = 第 2 段起點
Pts[4] := Point(100, 10);
Pts[5] := Point(55, 99);
Pts[6] := Point(70, 10); // 第 2 段終點
Canvas.PolyBezier(Pts);
end;
// PolyBezierTo 多段:3n 個點,MoveTo 設定第 1 段起點
procedure TForm1.Button2Click(Sender: TObject);
var
Pts: array[1..6] of TPoint; // 2 段:3*2=6
begin
Canvas.Pen.Width := 2;
Canvas.Pen.Color := clRed;
Canvas.MoveTo(10 + 82, 10); // 第 1 段起點
Pts[1] := Point(60 + 82, 10);
Pts[2] := Point(10 + 82, 100);
Pts[3] := Point(40 + 82, 100); // 第 1 段終點
Pts[4] := Point(100 + 82, 10);
Pts[5] := Point(55 + 82, 99);
Pts[6] := Point(70 + 82, 10); // 第 2 段終點
Canvas.PolyBezierTo(Pts);
end;
貝塞爾曲線數學原理速覽
三次貝塞爾曲線由參數方程定義 (t ∈ [0,1]):
B(t) = (1-t)³P₀ + 3(1-t)²t P₁ + 3(1-t)t² P₂ + t³ P₃
// P₀=起點, P₁=控制點1, P₂=控制點2, P₃=終點
- 曲線不經過控制點 P₁、P₂
- 切線方向:起點指向 P₁,終點來自 P₂
- 凸包性質:曲線完全包含在四點凸包內
進階應用:平滑折線圖 (樣條插值)
procedure DrawSmoothPolyline(ACanvas: TCanvas; const Points: array of TPoint);
var
i: Integer;
Pts: array of TPoint;
P0, P1, P2, P3: TPoint;
begin
if Length(Points) < 2 then Exit;
SetLength(Pts, (Length(Points) - 1) * 3 + 1);
// Catmull-Rom 轉貝塞爾控制點算法
Pts[0] := Points[0];
for i := 0 to Length(Points) - 2 do
begin
// 計算控制點 (簡化版)
P0 := Points[i];
P3 := Points[i+1];
if i = 0 then
P1 := Point(P0.X + (P3.X - P0.X) div 3, P0.Y)
else
P1 := Point(Points[i].X + (Points[i+1].X - Points[i-1].X) div 6, Points[i].Y);
if i = Length(Points) - 2 then
P2 := Point(P3.X - (P3.X - P0.X) div 3, P3.Y)
else
P2 := Point(Points[i+1].X - (Points[i+2].X - Points[i].X) div 6, Points[i+1].Y);
Pts[i*3+1] := P1;
Pts[i*3+2] := P2;
Pts[i*3+3] := P3;
end;
ACanvas.PolyBezier(Pts);
end;
常見陷阱與避坑
- PolyBezierTo 陣列必須從 1 開始:
array[1..N],array[0..N-1] 會導致第一段錯誤
- 多段點數計算錯誤:PolyBezier 需 3n+1,PolyBezierTo 需 3n
- 控制點過遠導致曲線過度彎曲:控制點距離建議不超過終點距離 1.5 倍
- Width > 1 時線條銳角處理:GDI 不自動圓滑連接,需自行處理
效能提示
PolyBezier 批次繪製比循環 PolyBezierTo 快 (單次 GDI 調用)
- 大量曲線考慮
Path API (BeginPath/EndPath/StrokePath)
- 動畫場景預計算控制點,幀間僅插值
精通 PolyBezier/PolyBezierTo 的控制點邏輯與多段連接規則,能繪製從簡單曲線到複雜樣條的各類平滑圖形。
- 上一篇文章: Delphi Canvas.TextOut 文字輸出實戰:逐字繪製古詩與度量應用
- 下一篇文章: Delphi 多邊形與折線繪製:Polygon、Polyline 完整指南
Copyright ©
程式碼之家 All Rights Reserved