|
前頁では構造化要素の木構造を表現するのに 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= |
┌
│
│
│
└ | t11 | t12 | t13 |
┐
│
│
│
┘ |
t21 | t22 | t23 |
t31 | t32 | t33 |
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
|