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

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

プログラミングについて
ホームページについて
キャドについて
電子カタログについて
書籍・雑誌
イベント
リンク集
CADを考える:複合図形定義と複合図形配置(2)
前頁では構造化要素の木構造を表現するのに TreeViewコンポーネントを使い、それに対して項目を追加していきましたが、これをすると TreeViewコンポーネントが必須となってしまうため、TreeViewコンポーネントがなくても独自手続き・関数で出来るようにしてみます。
type
 ・・・
 TFukugoZukeiTree = record  // 複合図形ツリー構造
  name : string ;      // 複合図形名
  level : integer ;     // 階層レベル(0:Root=用紙 1〜)
  par : integer ;      // 親No
 end;
・・・
 zFzt : array of TFukugoZukeiTree;  // 複合図形ツリー構造
 zFztN: Integer ;
// 複合図形ツリー構造 生成
procedure TDataClass.MakeFZukeiTree ;
var
 i,j,k : integer ;
 s : string ;
begin
 zFzt := nil;
 zFztN := 0;
 for i:=0 to dFzkN-1 do begin
  if not(dFzk[i].exf) then continue ;
  if (FZukeiNameCheck(0,dFzk[i].name) = 0) then continue;
  Inc(zFztN);
  if ((zFztN mod 200) = 1) then SetLength(zFzt,zFztN+199);
  with zFzt[zFztN-1] do begin
   name := dFzk[i].name;
   level := 1 ;
   par :=-1;
  end;
 end;
 //
 i := 0 ;
 while (i < zFztN) do begin
  s := zFzt[i].name ;
  j := FZukeiNameCheck(0,s);
  if (j > 0) then begin
   for k:=0 to fDef[j-1].mFzkN-1 do begin
    if not(fDef[j-1].mFzk[k].exf) then continue ;
    Inc(zFztN);
    if ((zFztN mod 200) = 1) then SetLength(zFzt,zFztN+199);
    with zFzt[zFztN-1] do begin
     name := fDef[j-1].mFzk[k].name;
     level := zFzt[i].level + 1;
     par := i;
    end;
   end;
  end;
  Inc(i);
 end;
 //
 SetLength(zFzt,zFztN);
end;
これは前回の TreeView操作をしていたものをそのまま別の手続きにした感じですが、この場合のツリーデータは
部分図A
部分図B
部分図C
 作図グループD(親は部分図A)
 作図グループE(親は部分図B)
 作図グループF(親は部分図C)
  作図部品G(親は作図グループD)
  作図部品H(親は作図グループE)
  作図部品I(親は作図グループF)
のようなイメージになります。これでも別段プログラムは作成できるのですが、これを TreeViewで表現しようとすると、
// 表示ツリー生成
CData.MakeFZukeiTree ;
TreeView1.Items.Clear ;
TreeView1.Items.Add(nil,'用紙');
for i:=0 to CData.zFztN-1 do begin
 j := Cdata.zFzt[i].par ;
 if (j = -1) then begin
  p := TreeView1.Items[0] ;
  TreeView1.Items.AddChild(p, IntToStr(i)+':'+Cdata.zFzt[i].name)
 end
 else begin
  for k:=0 to TreeView1.Items.Count-1 do begin
   s := TreeView1.Items[k].Text ;
   s := Copy(s,1,Pos(':',s)-1);
   if (s = IntToStr(j)) then begin
    p := TreeView1.Items[k] ;
    TreeView1.Items.AddChild(p, IntToStr(i)+':'+Cdata.zFzt[i].name);
    break;
   end;
  end;
 end;
end;
のような感じで TreeViewの項目の前に「(数字):」が付いてしまい余り綺麗ではありませんし、少し煩雑になってしまいます。
 
ツリーデータを
部分図A
 作図グループD(親は部分図A)
  作図部品G(親は作図グループD)
部分図B
 作図グループE(親は部分図B)
  作図部品H(親は作図グループE)
部分図C
 作図グループF(親は部分図C)
  作図部品I(親は作図グループF)
のようなイメージにしたい場合には、やはり、再帰処理で作るのが簡単でしょう。
// 複合図形ツリー構造 生成
// 複合図形内の複合図形を取得する再帰用サブ
// no : 親の複合図形定義 番号(1〜)
// lv : 階層レベル(0:root(用紙) 1〜)
// pa : 親のツリー番号(0〜)
procedure TDataClass.MakeFZukeiTreeSub(no,lv,pa:integer) ;
var
 i,j : integer ;
begin
 for i:=0 to fDef[no-1].mFzkN-1 do begin
  if not(fDef[no-1].mFzk[i].exf) then continue ;
  j := FZukeiNameCheck(0,fDef[no-1].mFzk[i].name) ;
  if (j = 0) then continue;
  Inc(zFztN);
  if ((zFztN mod 200) = 1) then SetLength(zFzt,zFztN+199);
  with zFzt[zFztN-1] do begin
   name := fDef[no-1].mFzk[i].name;
   level := lv;
   par := pa;
  end;
  MakeFZukeiTreeSub(j,lv+1,zFztN-1);
 end;
end;
 
// 複合図形ツリー構造 生成
//  zFztN、zFzt
procedure TDataClass.MakeFZukeiTree ;
var
 i,j : integer ;
begin
 zFzt := nil;
 zFztN := 0;
 for i:=0 to dFzkN-1 do begin
  if not(dFzk[i].exf) then continue ;
  j := FZukeiNameCheck(0,dFzk[i].name) ;
  if (j = 0) then continue;
  Inc(zFztN);
  if ((zFztN mod 200) = 1) then SetLength(zFzt,zFztN+199);
  with zFzt[zFztN-1] do begin
   name := dFzk[i].name;
   level := 1 ;
   par :=-1;
  end;
  MakeFZukeiTreeSub(j,2,zFztN-1);
 end;
 SetLength(zFzt,zFztN);
end;
のようになります。これを TreeViewで表現しようとすると、
CData.MakeFZukeiTree ;
TreeView1.Items.Clear ;
TreeView1.Items.Add(nil,'用紙');
for i:=0 to CData.zFztN-1 do begin
 j := Cdata.zFzt[i].par ;
 p := TreeView1.Items[j+1] ;
 TreeView1.Items.AddChild(p, Cdata.zFzt[i].name);
end;
のようになります。
 
しかし、再帰処理をしているため、もしも万が一、
AddDataFZukei('作図部品D',0,'作図部品E',0.0,0.0,0.0,1.0,1.0);
AddDataFZukei('作図部品E',0,'作図部品D',0.0,0.0,0.0,1.0,1.0);
のように作図部品Dの下に作図部品Eを、作図部品Eの下に作図部品Dを、というような通常有り得ない指定をしてしまうと、無限ループ状態となってしまい、プログラムは無限ループとなってフリーズするか、強制終了となってしまいます。そして最悪メモリリークを起こしてしまい、下手をするとリソース不足でWindowsが不安定になったりWindowsがブルーバックとなってしまったりする危険性があります。そのため、このプログラムでは、ツリーデータの階層レベルを最大500まで、としています。通常、そんな深いデータ構造というのは無いだろう、というアタリをつけているのですが、これを大きくすればするほど遅くなりますし、メモリも消費するだろうと思われます。後日、この数値はもっと小さくするかもしれません(100程度まで)。
 
 
次に、座標変換=変換行列を扱う事を考えてみます。
先の頁で書きましたように、2D-CADで変換行列を考える場合には、3×3の行列が必要となります。
T=



t11t12t13



t21t22t23
t31t32t33
UnitFunc.pas に、これを扱えるレコード型を用意しておきます。
type
 TMatrix = record // 変換行列
  t11,t12,t13 : double ;
  t21,t22,t23 : double ;
  t31,t32,t33 : double ;
 end;
そして変換行列用の関数を幾つか用意しておきます。
procedure MtxClear(var t:TMatrix);
procedure MtxSetE(var t:TMatrix);
procedure MtxSet(var t:TMatrix;a11,a12,a13, a21,a22,a23, a31,a32,a33:double);
procedure MtxMove(var t:TMatrix;dx,dy:double);
procedure MtxScale(var t:TMatrix;sx,sy:double);
procedure MtxRotD(var t:TMatrix;ang:double);
procedure MtxRot(var t:TMatrix;ang:double);
function MtxAdd(t1,t2:TMatrix) : TMatrix ;
function MtxMult(t1,t2:TMatrix) : TMatrix ;
procedure MtxXY(var x,y:double;xx,yy:double;t:TMatrix);
MtxRotDは[°]での回転用、MtxRotは[rad]での回転用です。
 
これを使ってテスト作図を行ってみます。
var
 ・・・
 t1,t2 : TMatrix ;
begin
 ・・・
 // テスト描画 ランダムの1本の線を回転描画してみます
 lx1 := Random(CData.zp.x) ;
 ly1 := Random(CData.zp.y) ;
 lx2 := Random(CData.zp.x) ;
 ly2 := Random(CData.zp.y) ;
 MtxMove(t1,-CData.zp.x/2.0, -CData.zp.y/2.0);
 MtxRotD(t2,360.0/72.0);
 t1 := MtxMult(t1,t2);
 MtxMove(t2,CData.zp.x/2.0, CData.zp.y/2.0);
 t1 := MtxMult(t1,t2);
 for i:=1 to 72 do begin
  MtxXY(lx1,ly1, lx1,ly1, t1);
  MtxXY(lx2,ly2, lx2,ly2, t1);
  x1 := Trunc(ox + (lx1 - CData.zp.x/2.0)*mm_dot) ;
  y1 := Trunc(oy - (ly1 - CData.zp.y/2.0)*mm_dot) ;
  x2 := Trunc(ox + (lx2 - CData.zp.x/2.0)*mm_dot) ;
  y2 := Trunc(oy - (ly2 - CData.zp.y/2.0)*mm_dot) ;
  gp.G_Line(x1,y1,x2,y2);
 end;
 // テスト描画 ランダムの1本の線を横に1.2倍ずつ描画してみます
 gp.G_SetPen(clRed);
 MtxMove(t1,-lx1, -ly1); // 始点を固定点にする
 MtxScale(t2,1.2,1.0);
 t1 := MtxMult(t1,t2);
 MtxMove(t2,lx1, ly1);
 t1 := MtxMult(t1,t2);
 for i:=1 to 50 do begin
  MtxXY(lx1,ly1, lx1,ly1, t1);
  MtxXY(lx2,ly2, lx2,ly2, t1);
  x1 := Trunc(ox + (lx1 - CData.zp.x/2.0)*mm_dot) ;
  y1 := Trunc(oy - (ly1 - CData.zp.y/2.0)*mm_dot) ;
  x2 := Trunc(ox + (lx2 - CData.zp.x/2.0)*mm_dot) ;
  y2 := Trunc(oy - (ly2 - CData.zp.y/2.0)*mm_dot) ;
  gp.G_Line(x1,y1,x2,y2);
 end;
 
最初の部分の回転処理は、画面中央を中心として回転させますが、回転処理用の変換行列は(0,0)を中心として処理しますので、中心点座標の分をマイナスして、回転させて、再度、中心点座標の分をプラスします。そのため、変換行列は、移動→回転→移動 を用意し、掛け算しています。その次の倍率の変換も、(0,0)点を基準として倍率を掛けるため、倍率の際の固定点を(0,0)位置へ移動する操作、つまり、固定点座標の分をマイナスする操作が必要となります。よって、倍率のための変換行列は、移動→倍率→移動 を用意して掛け算をします。
 
 
取り合えず、ここまでのテストプログラムです。実行ファイル、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.