|
前頁では、3次のBスプライン曲線について見てみました。
3次Bスプライン曲線は、始点+制御点+終点、という指定方法を取りますが、CADソフトでは一般的に、制御点ではなく、通過点を取るスプライン曲線を描画する事が多いようです。
ですのでここでは、通過点を取るスプライン曲線で、比較的簡単な、カーディナルスプライン曲線(カーティシアンスプライン曲線)について考えてみます。このスプライン曲線は、位置と接線ベクトルから曲線の描画を行う、Ferguson/Coons(ファーガソン/クーンズ)曲線を元にします。
Ferguson/Coons(ファーガソン/クーンズ)曲線の式は
P(t) = H0(t)*Q0 + H1(t)*Q1 + H2(t)*Q0' + H3(t)*Q1'
H0(t)= 2t3 − 3t2 + 1
H1(t)=-2t3 + 3t2
H2(t)= t3 − 2t2 + t
H3(t)= t3 − t2
となります。
そして接線ベクトルをどうするか?という所で、カーディナルスプライン曲線は、2点(x1,y1)、(x2,y2)の前後の点(x0,y0)、(x3,y3)の利用し、
Q0'x = A×(x2-x0)
Q0'y = A×(y2-y0)
Q1'x = A×(x3-x1)
Q1'y = A×(y3-y1)
(A値は、0〜1の間の値、通常は 0.5を取る)
であるとして計算を行います。開曲線の第1区間では(x0,y0)=(x1,y1)、最終区間では(x3,y3)=(x2,y2)とします。
あとは、ベジェ曲線やBスプライン曲線と同様に描画を行います。
それではスプライン曲線のデータ構造を決めます。
UnitData.pas |
type
・・・
TDataSpline = record // 独自|スプライン曲線
exf : Boolean ; // 存在フラグ(True:有り False:無し)
Layer : Integer ; // レイヤ(1〜256)
Color : Integer ; // 色 (0:レイヤ色 1〜256)
Ltype : Integer ; // 線種 (0:レイヤ線種 1〜32)
line_width: Integer ; // 線幅 (0:レイヤ線幅 1〜16)
Stype : Integer ; // 曲線タイプ (1:Cスプライン)
Aval : double ; // CスプラインのA値
open_close : Integer; // 開閉区分:0:閉 1:開
sep : Integer ; // 頂点間分割数
Number : Integer ; // 頂点数
X : array of double ; // 頂点X座標
Y : array of double ; // 頂点Y座標
end;
TFukugoZukeiDef = record // 構造化要素|複合図形定義
exf : Boolean ; // 存在フラグ(True:有り False:無し)
name : string ; // 複合図形名
Flag : Integer ; // 種別フラグ
cnt : Integer ; // 配置数
・・・
mSpl : array of TDataSpline; // 幾何要素|スプライン曲線
mSplN : Integer ; // 数
mFzk : array of TDataFukugoZukei; // 構造化要素|複合図形配置
mFzkN : Integer ; // 数
end;
TDataClass = class
public
{ Public 宣言 }
・・・
dSpl : array of TDataSpline; // 表記要素|スプライン曲線
dSplN : Integer ; // 数
・・・ |
データ追加用の関数は
function AddDataSpline(s:string;lay,col,ltp,wid,stp,sp,num:integer;
av:double;vx,vy:array of double) : Boolean; |
のようにします。線分・折線等と同様、最初に「データ追加先の複合図形名(幾何要素) null:用紙へ追加(表記要素)」を指定するようにしています。open_close値は内部で確認・設定します。
UnitData.pas |
// スプライン曲線 データ項目の追加登録
function TDataClass.AddDataSpline(s:string;lay,col,ltp,wid,stp,sp,num:integer;av:double;vx,vy:array of double) : Boolean;
var
m,i : integer ;
oc : integer ;
begin
Result := False;
if (lay < 1) or (lay > zLayN) then lay := 1 ;
if (col < 0) or (col > zColN) then col := 0 ;
if (ltp < 0) or (ltp > zLtpN) then ltp := 0 ;
if (wid < 0) or (wid > zWidN) then wid := 0 ;
if (stp < 1) or (stp > 1) then stp := 1 ;
if (sp < 1) then sp := 1 ;
if (sp > 1000) then sp := 1000 ;
if (num <= 2) then exit ;
for i:=1 to num-1 do
if (Abs(vx[i]-vx[i-1]) < LIMIT10)
and(Abs(vy[i]-vy[i-1]) < LIMIT10) then exit ; // 同一点
oc := 1;
if (Abs(vx[0]-vx[num-1]) < LIMIT10)
and(Abs(vy[0]-vy[num-1]) < LIMIT10) then oc := 0; // 閉
if (s = '') then begin
// 用紙へ追加
if (AddDataOrder(-1,11,dSplN)) then begin
try
Inc(dSplN);
if ((dSplN mod 100) = 1) then SetLength(dSpl, dSplN+99);
with dSpl[dSplN-1] do begin
exf := True ;
Layer := lay ;
Color := col ;
Ltype := ltp ;
line_width := wid ;
Stype := stp ;
Aval := av ;
open_close := oc ;
sep := sp ;
Number := num ;
SetLength(X, num);
SetLength(Y, num);
for i:=0 to num-1 do begin
X[i] := vx[i];
Y[i] := vy[i];
end;
end;
Result := True ;
AddLayerCnt(lay,col,ltp,wid,-1);
except
Dec(dSplN);
Dec(dOrdN);
end;
end;
end
else begin
m := FZukeiNameCheck(0,s);
if (m = 0) then exit ; // 追加先の複合図形名は無いのでエラー
with fDef[m-1] do begin
if (AddDataOrder(m,11,mSplN)) then begin
try
Inc(mSplN);
if ((mSplN mod 100) = 1) then SetLength(mSpl, mSplN+99);
with mSpl[mSplN-1] do begin
exf := True ;
Layer := lay ;
Color := col ;
Ltype := ltp ;
line_width := wid ;
Stype := stp ;
Aval := av ;
open_close := oc ;
sep := sp ;
Number := num ;
SetLength(X, num);
SetLength(Y, num);
for i:=0 to num-1 do begin
X[i] := vx[i];
Y[i] := vy[i];
end;
end;
Result := True ;
AddLayerFCnt(lay,col,ltp,wid,-1);
except
Dec(mSplN);
Dec(mOrdN);
end;
end;
end;
end;
end; |
これでスプライン曲線データの登録は出来るようになりました。
次に、スプライン曲線の描画についてです。H0(t)〜H3(t)を変数 t0,t1,t2,t3 とすると
t = 0.0 → 1.0
t0 := 2.0*t*t*t - 3.0*t*t + 1.0;
t1 :=-2.0*t*t*t + 3.0*t*t;
t2 := t*t*t - 2.0*t*t + t;
t3 := t*t*t - t*t;
と計算すれば良く、
P(t) = H0(t)*Q0 + H1(t)*Q1 + H2(t)*Q0' + H3(t)*Q1'
ですので、座標計算は、
xp := (t0*x1) + (t1*x2) + (t2*dx1) + (t3*dx2);
yp := (t0*y1) + (t1*y2) + (t2*dy1) + (t3*dy2);
(x1,y1:第1点、x2,y2:第2点、x0,y0:第0点、x3,y3:第3点
dx1 := Aval*(x2-x0); dx2 := Aval*(x3-x1);
dy1 := Aval*(y2-y0); dy2 := Aval*(y3-y1);
)
のようになります。
UnitDataGraph.pas |
// スプライン曲線の表示 サブ1
procedure DisplaySplineSub1(var xp,yp:double;t,av,x0,x1,x2,x3, y0,y1,y2,y3:double);
var
t0,t1,t2,t3,dx1,dy1,dx2,dy2 : double ;
begin
t0 := 2.0*t*t*t - 3.0*t*t + 1.0;
t1 :=-2.0*t*t*t + 3.0*t*t;
t2 := t*t*t - 2.0*t*t + t;
t3 := t*t*t - t*t;
dx1 := av*(x2-x0);
dy1 := av*(y2-y0);
dx2 := av*(x3-x1);
dy2 := av*(y3-y1);
xp := t0*x1 + t1*x2 + t2*dx1 + t3*dx2 ;
yp := t0*y1 + t1*y2 + t2*dy1 + t3*dy2 ;
end;
// スプライン曲線の表示
procedure DisplaySpline(lay,col,ltp,wid,stp,oc,sp,num:integer;av:double;vx,vy:array of double);
var
fl : Boolean ;
c,cc,l,w, i,j,k : integer ;
ww, d1 ,t,x0,y0,x1,y1,x2,y2,x3,y3,px1,py1,px2,py2 : double ;
begin
DisplayLayCol2(lay,col,ltp,wid, c,cc,l,w,ww); // 色・線種・線幅
//
fl := False ;
if (CData.zLtp[l-1].Segment = 0)
or(CData.zLtp[l-1].SpaceMax * mm_dot < 2.0)
or(CData.zLtp[l-1].SpaceMax * mm_dot < ww ) then fl := True;
k := 0 ;
d1:= 0.0 ;
for i:=0 to num-2 do begin
if (i > 0) then begin
x0 := vx[i-1];
y0 := vy[i-1];
end
else begin
if (oc = 1) then begin
// 開曲線
x0 := vx[0];
y0 := vy[0];
end
else begin
// 閉曲線
x0 := vx[num-2];
y0 := vy[num-2];
end;
end;
x1 := vx[i];
y1 := vy[i];
x2 := vx[i+1];
y2 := vy[i+1];
if (i < (num-2)) then begin
x3 := vx[i+2];
y3 := vy[i+2];
end
else begin
if (oc = 1) then begin
// 開曲線
x3 := vx[i+1];
y3 := vy[i+1];
end
else begin
// 閉曲線
x3 := vx[1];
y3 := vy[1];
end;
end;
//
t := 0.0;
DisplaySplineSub1(px1,py1, t,av, x0,x1,x2,x3, y0,y1,y2,y3);
for j:=1 to sp do begin
t := t + 1.0/sp;
DisplaySplineSub1(px2,py2, t,av, x0,x1,x2,x3, y0,y1,y2,y3);
if (fl) then
DisplayLineSub1(px1,py1, px2,py2)
else
DisplayLineSub2(l,k,d1, px1,py1, px2,py2);
px1 := px2;
py1 := py2;
end;
end;
end; |
簡単なテストを Unit1.pas に記述して、再構築(コンパイル)し実行してみます。
「●」付近に緑色の折線と、それを頂点とするスプライン曲線のテストを作図しています。用紙サイズを小さくすると左下に作図しているスプライン曲線が拡大表示されますので確認してみて下さい。
分割数を少なくすると、スプライン曲線は粗くなります。分割数を多くするとスプライン曲線は綺麗に見えます。スプライン曲線要素として登録していますので、線分データが増える訳ではありませんので、分割数を多くしてもデータは巨大化しません。しかし、DXFで他CADへデータを受け渡す場合には、線分データの集まりとしてファイル保存を行うためにファイルサイズは大きくなるでしょうし、交点計算等を行う場合は各補間線分との交点計算をせざるを得ないため分割数が多いと計算量も多くなるため、その分、遅くなるであろうと考えられます。
それでは、ここまでのテストプログラムです。実行ファイル、gdiplus.dll、gdipフォルダは入っていません。ソースのみです。
|
|
CAD装置(1)
CAD装置(2)
メディア
AutoCADの
DIESELマクロ
CSV
DXF
PCES
IGES
STEP
数学とCAD
CAD作ろ!
CADを考える
▲PREV
▼NEXT
M7
Jw_cad
|