AFsoft WebSite(エーエフソフト・ウェブサイト)
 

オペレーティング・システムについて

プログラミングについて
ホームページについて
キャドについて
電子カタログについて
書籍・雑誌
イベント
リンク集
CADを考える:スプライン(2)
前頁では、幾何要素/表記要素フィーチャのスプラインについて見ました。が、SXF仕様のスプラインというのは制御点指定での3次ベジェ曲線であって、スプライン曲線ではありませんでした。CADソフトではスプライン曲線を実装しているものがほとんどですので、機能として無いとイマイチ感が生じてしまいます。また、スプライン曲線を再編集する事を考えてもデータとして実装していた方が後々便利でしょう。線分データ補間をしてしまうと曲線の再編集は事実上出来なくなり、曲線の再作図となってしまいます。
 
ここでは、よく基本とされる3次のBスプライン曲線の実装について考えてみます。
 
前頁にも記載していますが、3次のベジェ曲線は
  P(t) = B0(t)*Q0 + B1(t)*Q1 + B2(t)*Q2 + B3(t)*Q3
  B0(t)=(1-t)3
  B1(t)=3t(1-t)2
  B2(t)=3(1-t)t2
  B3(t)=t3
となりますが、3次のBスプライン曲線は
  P(t) = N0(t)*Q0 + N1(t)*Q1 + N2(t)*Q2 + N3(t)*Q3
  N0(t)=1/6*(1-t)3
  N1(t)=1/2*t3−t2+2/3
  N2(t)=-1/2*t3+1/2*t2+1/2*t+1/6
  N3(t)=1/6*t3
となります。
但しこれは、

という部分の表現となります。5点ある場合は、Q1・Q2・Q3・Q4の4点でQ2〜Q3間の描画を行います。閉曲線の場合は、Q4=Q0となり、Q0〜Q1間は Q3・Q0・Q1・Q2の4点で、Q2〜Q3間は Q1・Q2・Q3・Q0間の4点で描画を行います。
上記のような開曲線の場合、Q0〜Q1、Q2〜Q3は点が足らない為に描画できませんが、Q0・Q3は制御点ではなく通過点となりますので例外的な手法として、Q0及びQ3を重複させて取るようにしています。
 
このBスプライン曲線は、開曲線の場合は始点・終点が通過点となりますが、それ以外の点、及び閉曲線の全ての点は、制御点となります。
 
それではBスプライン曲線のデータ構造を決めます。
UnitData.pas
type
 ・・・
 TDataBspline = record  // 独自|Bスプライン曲線
  exf : Boolean ;    // 存在フラグ(True:有り False:無し)
  Layer : Integer ;   // レイヤ(1〜256)
  Color : Integer ;   // 色  (0:レイヤ色  1〜256)
  Ltype : Integer ;   // 線種 (0:レイヤ線種 1〜32)
  line_width: Integer ; // 線幅 (0:レイヤ線幅 1〜16)
  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 ;     // 配置数
  ・・・
  mBsp : array of TDataBspline; // 幾何要素|Bスプライン
  mBspN : Integer ;       // 数
  mFzk : array of TDataFukugoZukei; // 構造化要素|複合図形配置
  mFzkN : Integer ;         // 数
 end;
 
 TDataClass = class
  public
  { Public 宣言 }
  ・・・
  dBsp : array of TDataBspline; // 表記要素|Bスプライン
  dBspN : Integer ;       // 数
  ・・・
 
データ追加用の関数は
function AddDataBspline(s:string;lay,col,ltp,wid,sp,num:integer;
 vx,vy:array of double) : Boolean;
のようにします。線分・折線等と同様、最初に「データ追加先の複合図形名(幾何要素) null:用紙へ追加(表記要素)」を指定するようにしています。open_close値は内部で確認・設定します。
 
UnitData.pas
// Bスプライン曲線 データ項目の追加登録
function TDataClass.AddDataBspline(s:string;lay,col,ltp,wid,sp,num:integer;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 (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,10,dBspN)) then begin
   try
    Inc(dBspN);
    if ((dBspN mod 100) = 1) then SetLength(dBsp, dBspN+99);
    with dBsp[dBspN-1] do begin
     exf := True ;
     Layer := lay ;
     Color := col ;
     Ltype := ltp ;
     line_width := wid ;
     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(dBspN);
    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,10,mBspN)) then begin
    try
     Inc(mBspN);
     if ((mBspN mod 100) = 1) then SetLength(mBsp, mBspN+99);
     with mBsp[mBspN-1] do begin
      exf := True ;
      Layer := lay ;
      Color := col ;
      Ltype := ltp ;
      line_width := wid ;
      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(mBspN);
     Dec(mOrdN);
    end;
   end;
  end;
 end;
end;
 
これでBスプライン曲線データの登録は出来るようになりました。
 
次に、Bスプライン曲線の描画についてです。N0(t)〜N3(t)を変数 t0,t1,t2,t3 とすると
  t = 0.0 → 1.0
  t0 := (1.0-t)*(1.0-t)*(1.0-t)/6.0;
  t1 := 0.5*t*t*t - t*t + 2.0/3.0;
  t2 :=-0.5*t*t*t + 0.5*t*t + 0.5*t + 1.0/6.0;
  t3 := t*t*t/6.0;
と計算すれば良く、
  P(t) = N0(t)*Q0 + N1(t)*Q1 + N2(t)*Q2 + N3(t)*Q3
ですので、座標計算は、
  xp := (t0*x1) + (t1*x2) + (t2*x3) + (t3*x4);
  yp := (t0*y1) + (t1*y2) + (t2*y3) + (t3*y4);
  (x1,y1:第1点、x2,y2:第2点、x3,y3:第3点、x4,y4:第4点)
のようになります。これが1区間のBスプライン曲線になります。これを連続して作図していけば良いです。
この1区間内を何分割するかを決め、t値を 0 から 1 へ変えます。例えば10分割とした場合は、t値を 0、0.1、0.2、0.3、・・・、0.9、1.0、と変えてその都度、t0・t1・t2・t3、xp,yp値を求めて、xp,yp間を結んでいけば、その1区間のBスプライン曲線が作図出来ます。
 
UnitDataGraph.pas
// Bスプライン曲線の表示 サブ
procedure DisplayBsplineSub(var xp,yp:double;t,x1,x2,x3,x4, y1,y2,y3,y4:double);
var
 t0,t1,t2,t3 : double ;
begin
 t0 := (1.0-t)*(1.0-t)*(1.0-t)/6.0;
 t1 := 0.5*t*t*t - t*t + 2.0/3.0;
 t2 :=-0.5*t*t*t + 0.5*t*t + 0.5*t + 1.0/6.0;
 t3 := t*t*t/6.0;
 xp := t0*x1 + t1*x2 + t2*x3 + t3*x4 ;
 yp := t0*y1 + t1*y2 + t2*y3 + t3*y4 ;
end;
 
// Bスプライン曲線の表示
procedure DisplayBspline(lay,col,ltp,wid,oc,sp,num:integer;vx,vy:array of double);
var
 fl : Boolean ;
 c,cc,l,w, i,j,k : integer ;
 ww, d1 ,t,x1,y1,x2,y2,x3,y3,x4,y4,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 ;
 i := 0;
 while(i < num) do begin
  if (i = 0) then begin
   if (oc = 1) then begin
    // 開曲線
    x1 := vx[0];
    y1 := vy[0];
   end
   else begin
    // 閉曲線
    x1 := vx[num-2];
    y1 := vy[num-2];
   end;
  end
  else begin
   x1 := vx[i-1];
   y1 := vy[i-1];
  end;
  x2 := vx[i];
  y2 := vy[i];
  if (i >= num-1) then begin
   if (oc = 1)or(num < 2) then begin
    // 開曲線
    x3 := vx[num-1];
    y3 := vy[num-1];
   end
   else begin
    // 閉曲線
    x3 := vx[1];
    y3 := vy[1];
   end;
  end
  else begin
   x3 := vx[i+1];
   y3 := vy[i+1];
  end;
  if (i >= num-2) then begin
   if (oc = 1)or(num < 3) then begin
    // 開曲線
    x4 := vx[num-1];
    y4 := vy[num-1];
   end
   else begin
    // 閉曲線
    x4 := vx[i-num+3];
    y4 := vy[i-num+3];
   end;
  end
  else begin
   x4 := vx[i+2];
   y4 := vy[i+2];
  end;
  //
  t := 0.0;
  DisplayBsplineSub(px1,py1, t,x1,x2,x3,x4, y1,y2,y3,y4);
  if (i = 0)and(oc = 1) then begin
   px1 := vx[0];
   py1 := vy[0];
  end;
  for j:=1 to sp do begin
   t := t + 1.0/sp;
   DisplayBsplineSub(px2,py2, t,x1,x2,x3,x4, y1,y2,y3,y4);
   if (fl) then
    DisplayLineSub1(px1,py1, px2,py2)
   else
    DisplayLineSub2(l,k,d1, px1,py1, px2,py2);
   px1 := px2;
   py1 := py2;
  end;
  i := i + 1;
 end;
end;
 
簡単なテストを Unit1.pas に記述して、再構築(コンパイル)し実行してみます。

「●」付近に緑色の折線と、それを頂点とするBスプライン曲線のテストを作図しています。用紙サイズを小さくすると左下に作図しているBスプライン曲線が拡大表示されますので確認してみて下さい。
分割数を少なくすると、Bスプライン曲線は粗くなります。分割数を多くするとBスプライン曲線は綺麗に見えます。Bスプライン曲線要素として登録していますので、線分データが増える訳ではありませんので、分割数を多くしてもデータは巨大化しません。しかし、DXFで他CADへデータを受け渡す場合には、線分データの集まりとしてファイル保存を行うためにファイルサイズは大きくなるでしょうし、交点計算等を行う場合は各補間線分との交点計算をせざるを得ないため分割数が多いと計算量も多くなるため、その分、遅くなるであろうと考えられます。
 
それでは、ここまでのテストプログラムです。実行ファイル、gdiplus.dll、gdipフォルダは入っていません。ソースのみです。
 
CAD装置(1)
CAD装置(2)
メディア
AutoCADの
DIESELマクロ
CSV
DXF
PCES
IGES
STEP
数学とCAD
CAD作ろ!
CADを考える
 ▲PREV
 ▼NEXT
M7
Jw_cad
 
お問い合わせ 
本サイトはリンクフリーです
リンクバナー
(C)Copyright 1999-2015 By AFsoft All Rights Reserved.