|
前頁では、指定した通過点を通るカーディナルスプライン曲線の描画について見てみました。ここでは、通過点を通るベジェ曲線について考えてみます。
ベジェ曲線は既に記述したように、始点+制御点1+制御点+終点、を連続することによって描画を行います。ですので単純に、始点、終点、を指定するようにして、制御点1、制御点2を計算によって求める事にします。
ベジェ曲線の式は既に記述した通り
P(t) = (1-t)3*Q0 + 3t(1-t)2*Q1 + 3(1-t)t2*Q2 + t3*Q3
ですが、これを
P(t) = (1-t)3*Q1 + 3t(1-t)2*Q1s + 3(1-t)t2*Q1e + t3*Q2
Q1:第1点
Q2:第2点
Q1s:第1点〜第2点への制御点1
Q1e:第1点〜第2点への制御点2
と考えます。
次に、第3点が与えられた際、Q1〜Q2〜Q3は滑らかにつながる必要があります。下記のようになってしまっては意味がありません。
滑らかにつながる為には、Q1〜Q2のQ2点での接線ベクトル・曲率と Q2〜Q3のQ2点での接線ベクトル・曲率が同じである必要があります。なお、開曲線の場合は、Q1点・Q3点での曲率は0であるとします。閉曲線の場合は、Q4点=Q1点であれば、上記と同様、接線ベクトル・曲率が同じであれば良いです。
接線ベクトルは、1階微分 P'(t) であり、
曲率は、2階微分 P''(t) となります。
曲線1:P1(t) = (1-t)3*Q1 + 3t(1-t)2*Q1s + 3(1-t)t2*Q1e + t3*Q2
曲線2:P2(t) = (1-t)3*Q2 + 3t(1-t)2*Q2s + 3(1-t)t2*Q2e + t3*Q3
これを t の式に書き換えます。
P1(t)=(1-t)3*Q1+3t(1-t)2*Q1s+3(1-t)t2*Q1e+t3*Q2
=(1-3t+3t2-t3)*Q1+(3t-6t2+3t3)*Q1s+(3t2-3t3)*Q1e+t3*Q2
=(-Q1+3Q1s-3Q1e+Q2)t3+(3Q1-6Q1s+3Q1e)t2+(-3Q1+3Q1s)t+Q1
P2(t)=(-Q2+3Q2s-3Q2e+Q3)t3+(3Q2-6Q2s+3Q2e)t2+(-3Q2+3Q2s)t+Q2
となります。
接線=1階微分は
P1'(t)=3(-Q1+3Q1s-3Q1e+Q2)t2+2(3Q1-6Q1s+3Q1e)t+(-3Q1+3Q1s)
=(-3Q1+9Q1s-9Q1e+3Q2)t2+(6Q1-12Q1s+6Q1e)t+(-3Q1+3Q1s)
P2'(t)=(-3Q2+9Q2s-9Q2e+3Q3)t2+(6Q2-12Q2s+6Q2e)t+(-3Q2+3Q2s)
曲率=2階微分は
P1"(t)=2(-3Q1+9Q1s-9Q1e+3Q2)t+(6Q1-12Q1s+6Q1e)
=(-6Q1+18Q1s-18Q1e+6Q2)t+(6Q1-12Q1s+6Q1e)
P2"(t)=(-6Q2+18Q2s-18Q2e+6Q3)t+(6Q2-12Q2s+6Q2e)
となります。
曲線P1(t)でのQ1点は t=0、つまり P1(0)、Q2点は t=1、つまり P1(1)
曲線P2(t)でのQ2点は t=0、つまり P2(0)、Q3点は t=1、つまり P2(1)
です。
曲線P1上のQ2点での接線 P1'(1) は上記の式により
P1'(1)=(-3Q1+9Q1s-9Q1e+3Q2)+(6Q1-12Q1s+6Q1e)+(-3Q1+3Q1s)
=3Q2-3Q1s
曲率
P1"(1)=(-6Q1+18Q1s-18Q1e+6Q2)+(6Q1-12Q1s+6Q1e)
=6Q1s-12Q1e+6Q2
曲線P2上のQ2点での接線 P2'(0) は上記の式により
P2'(0)=-3Q2+3Q2s
曲率
P2"(0)=6Q2-12Q2s+6Q2e
となり、P1'(1)=P2'(0)、P1"(1)=P2"(0) ですから、
3Q2-3Q1s=-3Q2+3Q2s
→ Q1s+Q2s=2Q2 ……@
6Q1s-12Q1e+6Q2=6Q2-12Q2s+6Q2e
→ Q1s−2Q1e+2Q2s−Q2e=0 ……A
開曲線の場合は、Q1点・Q3点での曲率を0にしますから
P1"(0)=6Q1-12Q1s+6Q1e=0
→ 2Q1s−Q1e=Q1 ……B
P2"(1)=(-6Q2+18Q2s-18Q2e+6Q3)+(6Q2-12Q2s+6Q2e)=0
→ −Q2s+2Q2e=Q3 ……C
という訳で、@〜Cの4元1次連立方程式を解けば、Q1s・Q1e・Q2s・Q2eの値が分かります(Q1・Q2・Q3は座標値です)。これは3点の場合ですからそのまま解いても良いのですが、4点=6式、5点=8式、6点=10式、・・・と座標点が多くなればなるほど、式の数はおおくなりますので、手動で解いて、という事は現実、出来ません。ですのでこれを行列式と考えて、行列を解くプログラムを考えます。
例:3点の場合
┌
│
│
│
└ | 0 | 1 | 1 | 0 |
┐┌
││
││
││
┘└ | Q1s |
┐ ┌
│ │
│=│
│ │
┘ └ | 2Q2 |
┐
│
│
│
┘ |
1 | -2 | 2 | -1 | Q1e | 0 |
2 | -1 | 0 | 0 | Q2s | Q1 |
0 | 0 | -1 | 2 | Q2e | Q3 |
ですが、これを、以下の行列を解くと考えます。
┌
│
│
│
└ | 0 | 1 | 1 | 0 | 2Q2 |
┐
│
│
│
┘ |
1 | -2 | 2 | -1 | 0 |
2 | -1 | 0 | 0 | Q1 |
0 | 0 | -1 | 2 | Q3 |
これが4点の開曲線となると
┌
│
│
│
│
│
└ | 0 | 1 | 1 | 0 | 0 | 0 | 2Q2 |
┐
│
│
│
│
│
┘ |
1 | -2 | 2 | -1 | 0 | 0 | 0 |
0 | 0 | 0 | 1 | 1 | 0 | 2Q3 |
0 | 0 | 1 | -2 | 2 | -1 | 0 |
2 | -1 | 0 | 0 | 0 | 0 | Q1 |
0 | 0 | 0 | 0 | -1 | 2 | Q4 |
となります。点数が増えれば同様にして行列を大きくして値をずらせば良いです。また、4点の閉曲線の場合は
┌
│
│
│
│
│
└ | 0 | 1 | 1 | 0 | 0 | 0 | 2Q2 |
┐
│
│
│
│
│
┘ |
1 | -2 | 2 | -1 | 0 | 0 | 0 |
0 | 0 | 0 | 1 | 1 | 0 | 2Q3 |
0 | 0 | 1 | -2 | 2 | -1 | 0 |
1 | 0 | 0 | 0 | 0 | 1 | 2Q4 |
2 | -1 | 0 | 0 | 1 | -2 | 0 |
のようになります。ずらしてはみ出た分は最初の方に入ります。
パターン性が明確になれば、プログラミングも可能となります。
n元1次連立方程式の解法には、普通高校の数学の授業でも習いますが、ガウス・ジョルダン(Gauss-Jordan)法(掃き出し法)を利用します。それにより、行列の右端の列が解となります。
UnitFunc.pasに、手続き gauss() を入れます。
データ構造は、前回作成した TDataSpline型の変数 mSpl、dSpl をそのまま利用することとして、曲線タイプ Stype の値を「2」とする事で対応させます。データ登録は前回のものをそのまま利用します。データ項目 Aval値は未使用(無効値)となりますが、まぁ気にしない事とします。UnitDataGraph.pasでのスプライン曲線の表示を行う手続きのほうで、Stype値を参照して処理を分岐するようにします。
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 2:ベジェ補間)
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 ; // 数
・・・ |
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 > 2) 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; |
UnitDataGraph.pas |
// スプライン曲線の表示
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 ;
//
m1,m2 : integer ;
qx,qy : array of 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 ;
Case(stp)of
1:begin
// カーディナルスプライン曲線
・・・
end;
2:begin
// ベジェ補間
// 途中の制御点を算出するための行列を生成
m1 := (num-1)*2+1;
m2 := (num-1)*2;
SetLength(qx,m1*m2);
SetLength(qy,m1*m2);
for i:=0 to m1*m2-1 do
qx[i] := 0.0;
for i:=0 to num-3 do begin
qx[(i*2 )*m1+i*2+1] := 1.0;
qx[(i*2 )*m1+i*2+2] := 1.0;
qx[(i*2+1)*m1+i*2 ] := 1.0;
qx[(i*2+1)*m1+i*2+1] :=-2.0;
qx[(i*2+1)*m1+i*2+2] := 2.0;
qx[(i*2+1)*m1+i*2+3] :=-1.0;
end;
if (oc = 1) then begin
// 開曲線
qx[((num-2)*2 )*m1 ] := 2.0;
qx[((num-2)*2 )*m1+1] :=-1.0;
qx[((num-2)*2+1)*m1+(num-2)*2 ] :=-1.0;
qx[((num-2)*2+1)*m1+(num-2)*2+1] := 2.0;
end
else begin
// 閉曲線
qx[((num-2)*2 )*m1 ] := 1.0;
qx[((num-2)*2 )*m1+(num-2)*2+1] := 1.0;
qx[((num-2)*2+1)*m1 ] := 2.0;
qx[((num-2)*2+1)*m1+1] :=-1.0;
qx[((num-2)*2+1)*m1+(num-2)*2 ] := 1.0;
qx[((num-2)*2+1)*m1+(num-2)*2+1] :=-2.0;
end;
for i:=0 to m1*m2-1 do
qy[i] := qx[i] ;
// 行列に座標値をセット
for i:=0 to num-3 do begin
qx[(i*2+1)*m1-1] := 2.0*vx[i+1];
qy[(i*2+1)*m1-1] := 2.0*vy[i+1];
end;
if (oc = 1) then begin
// 開曲線
qx[((num-2)*2+1)*m1-1] := vx[0];
qy[((num-2)*2+1)*m1-1] := vy[0];
qx[((num-2)*2+2)*m1-1] := vx[num-1];
qy[((num-2)*2+2)*m1-1] := vy[num-1];
end
else begin
// 閉曲線
qx[((num-2)*2+1)*m1-1] := 2.0*vx[num-1];
qy[((num-2)*2+1)*m1-1] := 2.0*vy[num-1];
end;
// 行列を解く
gauss(m1,m2,qx);
gauss(m1,m2,qy);
// 作図
for i:=0 to num-2 do begin
x0 := vx[i];
y0 := vy[i];
x1 := qx[(i*2+1)*m1-1];
y1 := qy[(i*2+1)*m1-1];
x2 := qx[(i*2+2)*m1-1];
y2 := qy[(i*2+2)*m1-1];
x3 := vx[i+1];
y3 := vy[i+1];
t := 0.0;
DisplayBezierSub(px1,py1, t,x0,x1,x2,x3, y0,y1,y2,y3);
for j:=1 to sp do begin
t := t + 1.0/sp;
DisplayBezierSub(px2,py2, t,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;
qx := nil;
qy := nil;
end;
end;
end; |
簡単なテストを Unit1.pas に記述して、再構築(コンパイル)し実行してみます。点線で表示しているのは前回のカーディナルスプラインです。
それでは、ここまでのテストプログラムです。実行ファイル、gdiplus.dll、gdipフォルダは入っていません。ソースのみです。
|
|
CAD装置(1)
CAD装置(2)
メディア
AutoCADの
DIESELマクロ
CSV
DXF
PCES
IGES
STEP
数学とCAD
CAD作ろ!
CADを考える
▲PREV
▼NEXT
M7
Jw_cad
|