お知らせ

電子会議

ライブラリ

パレット

Delphi FAQ検索

Delphi FAQ一覧

サンプル蔵





FDelphi FAQ
16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル

"折れ線->ベジェスプライン変換法"



□2次元の折れ線データから、ベジェスプラインを作り出す関数です

  いわゆる3次スプラインは行列演算が入るので結構厄介ですし、
 全部の点列が得られないと計算に入れません。
 比べてこの方式は、点列が入手出来る度に計算出来る利点があります。
 その上、混合スプラインや雲形定規スプラインに比べて接続感が良好。
 また、滑らか度合いを係数 k で調整可能です。
  さらに、整数化するだけで、Win32APIにあるBezier系の関数を使って
 高速描画出来る特徴もあります。
  注)この方法は一応裏目小僧オリジナルの方法と信じていますが、
   オリジナル性の調査はしていません。

type TDPoint = record
x: double;
y: double;
end;
type TDp4 = record
x1,y1: double;
x2,y2: double;
x3,y3: double;
x4,y4: double;
end;
///////////////////////////////////////////////////
// 4点を与えてp0〜p1の範囲のベジェ点化して返す
function BezierCvt(pr,p0,p1,p2:TDPoint):TDp4;
var dx,dy,k,r,b:double;
begin
 k:=0.3;  //kは 0.1〜0.3の範囲 小さい程折線に近い
 with Result do
  begin
    x1:=p0.x;
    y1:=p0.y;
    x4:=p1.x;
    y4:=p1.y;
    dx:=x4-x1;
    dy:=y4-y1;
    r:=k*sqrt(dx*dx+dy*dy);
    //  x2,y2 は p0点からpr-p1直線延長上距離rの点
    dx:=p1.x-pr.x;
    dy:=p1.y-pr.y;
    b:=sqrt(dx*dx+dy*dy);
    x2:=x1+r*dx/b;
    y2:=y1+r*dy/b;

    //  x2,y2 は p1点からp2-p0直線延長上距離rの点
    dx:=p0.x-p2.x;
    dy:=p0.y-p2.y;
    b:=sqrt(dx*dx+dy*dy);
    x3:=x4+r*dx/b;
    y3:=y4+r*dy/b;
  end;
end;
///////////////////////////////////////////////////
//ベジェ点データqを与えてその区間 t=0〜1 で点を返す
function BezierData(q:TDp4;t:double):TDpoint;
var   v:double;
begin
 v:=1-t;
 with Result do with q do begin
 x:= v*v*v*x1+3*v*v*t*x2+3*v*t*t*x3+t*t*t*x4;
 y:= v*v*v*y1+3*v*v*t*y2+3*v*t*t*y3+t*t*t*y4;
 end;
end;


///////////////////////////////////////////////////
//呼び出し方のサンプル
//  始点、終点では 始点終点を重ねるだけで、まあまあの結果が得られます。
// なお、 閉曲線なら始点終点を互いに重ねるだけの事です。

procedure TForm1.FormPaint(Sender: TObject);
var i,j:integer;
    t:double;
    p:TDpoint;
    q:TDp4;
    var dd:array [0..10] of TDpoint;
begin
i:=0;
 dd[i].x:=100; dd[i].y:=10; inc(i); //始点を重ねる
 dd[i].x:=100; dd[i].y:=10; inc(i);
 dd[i].x:=150; dd[i].y:=200; inc(i);
 dd[i].x:=200; dd[i].y:=150; inc(i);
 dd[i].x:=250; dd[i].y:=300; inc(i);
 dd[i].x:=300; dd[i].y:=400; inc(i);
 dd[i].x:=350; dd[i].y:=300; inc(i);
 dd[i].x:=400; dd[i].y:=300; inc(i);
 dd[i].x:=400; dd[i].y:=300; inc(i);//終点も同じ点を重ねる
 with Canvas do begin
   Pen.color:=clRed;
                     MoveTo(round(dd[1].x),round(dd[1].y));
  for i:=2 to 7 do   LineTo(round(dd[i].x),round(dd[i].y));
   Pen.color:=clBlack;

 for i:=1 to 6 do
  begin
   q:= BezierCvt(dd[i-1],dd[i],dd[i+1],dd[i+2]) ;
   MoveTo(round(q.x1),round(q.y1));
   for j:=1 to 100 do 
   with  BezierData(q,j/100) do  LineTo(round(x),round(y));
  end;
  //なお このqの値から直接WIN32APIのベジェ系の関数を呼ぶのは簡単でしょう
 end;
end;


Original document by 裏目小僧        氏 ID:(GGA03463)


ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。

Copyright 1996-2002 Delphi Users' Forum