|
前頁ではドット指定での線分の描画について見てみました。ここでは、mm指定での図形描画について考えてみます。
先の頁にて既に mm・ドット比(変数 mm_dot)について記述しました。
mx := (PaintBox1.Width - 4)/CData.zp.x ; // 用紙横フィット
my := (PaintBox1.Height- 4)/CData.zp.y ; // 用紙縦フィット
if (mx < my) then
mm_dot := mx
else
mm_dot := my ; // 用紙全体表示 |
ここでいう CData.zp.x・CData.zp.y は用紙サイズを示しますが、ここを、各図形の最小座標値 min.x・min.y、最大座標値 max.x・max.y を利用すれば図形全体表示が可能になります。イメージとしては下記の通り。
mx := (PaintBox1.Width - 4)/(max.x - min.x);
my := (PaintBox1.Height- 4)/(max.y - min.y);
if (mx < my) then
mm_dot := mx
else
mm_dot := my ; // 図形全体表示 |
例えば、ある部分図の図形だけ最大最小座標を取得した場合には、その部分図の表示、という事が可能でしょうし、ある作図グループ内、ある作図部品内、という事も可能になると思われます。
この mm_dotの値を2倍にすれば、2倍拡大表示となりますし、0.5倍にすれば、1/2倍縮小表示となります。マウスで範囲指定した部分の拡大表示を行う場合は、上記と同様にすれば良いでしょう。
最初に用紙全体表示を行った場合には、画面中央が用紙中央となるように処理されますから、mm値→ドット値の変換は下記のようになります。
ox := PaintBox1.Width div 2; // 画面中央
oy := PaintBox1.Height div 2;
Xdot座標値 := Trunc(ox + (Xmm座標値 - CData.zp.x/2.0)*mm_dot) ;
Ydot座標値 := Trunc(oy - (Ymm座標値 - CData.zp.y/2.0)*mm_dot) ; |
このx1,y1の式を逆にすると
Xmm座標値 := CData.zp.x/2.0 + (Xdot座標値 - ox)/mm_dot ;
Ymm座標値 := CData.zp.y/2.0 - (Ydot座標値 - oy)/mm_dot ; |
となります。ですので、画面左下〜画面右上のmm座標値は
// 画面左下点
Xmm座標値 := CData.zp.x/2.0 + (0 - ox)/mm_dot ;
Ymm座標値 := CData.zp.y/2.0 - ((PaintBox1.Height-1) - oy)/mm_dot ;
// 画面右上点
Xmm座標値 := CData.zp.x/2.0 + ((PaintBox1.Width-1) - ox)/mm_dot ;
Ymm座標値 := CData.zp.y/2.0 - (0 - oy)/mm_dot ; |
となります。
つまり、この画面左下点〜画面右上点の範囲でクリッピング処理を行うようにすれば、画面範囲外の線分等の作図は行われなくなるので安全な描画が出来るようになるという事になります。
SXF仕様には無いと思われますが、部分図の表現が AutoCADのビューポートのような四角い枠内だけでの描画を行う、という風にしたい場合には、そのビューポート枠内でクリッピング処理を行い、ビューポート位置で画面描画を行うという風にすれば実現は出来ます。本プログラムでは行いませんけれど。
以前「CAD作ろ!」の際に作成したクリッピング用のユニットを利用して組み込んでみます。
UnitClipping.pas |
function ClipSub(x,y,wx1,wy1,wx2,wy2:double):integer ;
function ClipAreaOut(var vx,vy:double;x,y,wx1,wy1,wx2,wy2:double):integer ;
function ClipPoint(x1,y1,wx1,wy1,wx2,wy2:double):Integer ;
function Clip2Point(x1,y1,x2,y2,wx1,wy1,wx2,wy2:double):Integer ;
function ClipPreLine(x1,y1,x2,y2:double;wx1,wy1,wx2,wy2:double):Integer ;
function ClipLine(var x1,y1,x2,y2:double;wx1,wy1,wx2,wy2:double):Integer ; |
Unit1.pas |
// 試しのウインドウ枠
wx1 := 0.1*CData.zp.x ;
wy1 := 0.2*CData.zp.y ;
wx2 := 0.9*CData.zp.x ;
wy2 := 0.8*CData.zp.y ;
gp.SetDisplayMode(RadioGroup1.ItemIndex);
gp.G_Start(PaintBox1.Canvas);
gp.G_Cls(clWhite);
// 用紙枠描画
gp.G_SetPen(clBlue);
gp.G_Line(x1,y1, x2,y1);
gp.G_Line(x2,y1, x2,y2);
gp.G_Line(x2,y2, x1,y2);
gp.G_Line(x1,y2, x1,y1);
// テスト描画 ランダムで10000本の線を描画してみます
for i:=1 to 10000 do begin
gp.G_SetPen(RGB(Random(256),Random(256),Random(256)));
lx1 := Random(CData.zp.x*4) - CData.zp.x*2 ;
ly1 := Random(CData.zp.y*4) - CData.zp.y*2 ;
lx2 := Random(CData.zp.x*4) - CData.zp.x*2 ;
ly2 := Random(CData.zp.y*4) - CData.zp.y*2 ;
if (ClipLine(lx1,ly1,lx2,ly2 , wx1,wy1,wx2,wy2) < 2) then begin
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;
end;
gp.G_End; |
これを実行すると、線分は「試しのウインドウ枠」の四角内に描画されることになります(※線幅を1よりも大きくすれば枠よりもはみ出る部分は出てくると思います)。
表記要素については、用紙に対して直接作図を行いますので、上記のような事で画面描画できるのですが、幾何要素、つまり、複合図形(部分図、作図グループ、作図部品)の上に作図される図形については、それら複合図形配置のパラメータによって移動・拡大縮小・回転の処理を施す必要があります(※作図グループは座標変換ありません)。
まずは、移動について。座標の移動は、
X = x + dx
Y = y + dy
で行われます。(dx:X方向移動量 dy:Y方向移動量)
これを行列式で表現すると、
[X Y] = [x y 1] |
┌
│
│
│
└ | 1 | 0 |
┐
│
│
│
┘ |
0 | 1 |
dx | dy |
のようになります。
次に、拡大縮小は
X = m * x
Y = n * y
で行われます。(m:X方向倍率 n:Y方向倍率)
これを行列式で表現すると、
[X Y] = [x y] |
┌
│
│
└ | m | 0 |
┐
│
│
┘ |
0 | n |
のようになります。
そして、原点を中心とする回転については
X = x*cosθ - y*sinθ
Y = x*sinθ + y*cosθ
で行われますので、これを行列式で表現すると
[X Y] = [x y] |
┌
│
│
└ | cosθ | sinθ |
┐
│
│
┘ |
-sinθ | cosθ |
となります。
部分図上の線分は、部分図の位置・倍率・回転・XY/YX軸の処理を施された上で用紙上に作図されます。処理の順番は、倍率→回転→YX軸の場合はXY入れ替え→移動配置 をすれば良いでしょう。この場合、
[X Y 1] = [x y 1] T
となる変換行列T は、倍率・回転・移動の処理を合算してしまう事が出来ます。それをアフィン変換と呼びます。アフィン変換は、処理の順序に行列を掛けていけばよいです。上記の場合、行と列の数が合っていないのでダミーを入れて3×3にする必要があります。
移動
|
┌
│
│
│
└ | 1 | 0 | 0 |
┐
│
│
│
┘ |
0 | 1 | 0 |
dx | dy | 1 |
倍率
|
┌
│
│
│
└ | m | 0 | 0 |
┐
│
│
│
┘ |
0 | n | 0 |
0 | 0 | 1 |
回転
|
┌
│
│
│
└ | cosθ | sinθ | 0 |
┐
│
│
│
┘ |
-sinθ | cosθ | 0 |
0 | 0 | 1 |
倍率→回転→移動の場合であれば
T = |
┌
│
│
│
└ | m | 0 | 0 |
┐┌
││
││
││
┘└ | cosθ | sinθ | 0 |
┐┌
││
││
││
┘└ | 1 | 0 | 0 |
┐
│
│
│
┘ |
0 | n | 0 | -sinθ | cosθ | 0 | 0 | 1 | 0 |
0 | 0 | 1 | 0 | 0 | 1 | dx | dy | 1 |
の計算をしておいて、
[X Y 1] = [x y 1] T
を計算すれば良いという事になります。
3×3行列と3×3行列の掛け算の結果は、3×3行列となります。
|
┌
│
│
│
└ | a | b | c |
┐┌
││
││
││
┘└ | A | B | C |
┐
│
│
│
┘ |
d | e | f | D | E | F |
g | h | i | G | H | I |
の計算は、
|
┌
│
│
│
└ | a | b | c |
┐┌
││
││
││
┘└ | A | B | C |
┐
│
│
│
┘ |
d | e | f | D | E | F |
g | h | i | G | H | I |
という風に計算しますので結果は下記のようになります。
|
┌
│
│
│
└ | a*A+b*D+c*G | a*B+b*E+c*H | a*C+b*F+c*I |
┐
│
│
│
┘ |
d*A+e*D+f*G | d*B+e*E+f*H | d*C+e*F+f*I |
g*A+h*D+i*G | g*B+h*E+i*H | g*C+h*F+i*I |
見た目はややこしいかもしれませんが、計算結果はそれぞれ数値1つずつですし、計算はプログラムで行うだけなので難しくはありません。
さて、部分図上の図形を作図させるだけであれば別段、行列とか、アフィン変換などを考える必要もないかもしれません。しかし、それだけではなく、部分図の中には作図グループ・作図部品があり、更に、作図グループの中には作図部品が、作図部品の中には更に作図部品が・・・とあるかもしれません。そうなってくると非常にややこしくなってしまいます。
作図グループには、座標変換はありませんが、作図部品は多重に行うことが出来ますから、注意は必要でしょう。けれどもアフィン変換を考えれば、
用紙−部分図A−作図グループ−作図部品B−作図部品C−図形
となっている場合、
T = 作図部品C配置行列・作図部品B配置行列・部分図A配置行列
の計算を行っておき
[X Y 1] = [x y 1] T
を計算すれば、用紙上に作図する座標[X Y] を得られる事になります。そうすれば、mm値→dot変換を行って画面上に描画すればOK、となります。
複合図形配置の状況をツリー構造で構成し、例えば
用紙
部分図A
作図グループB
作図部品C
作図部品D
作図部品E
作図部品F
作図部品G
作図部品H
作図部品C
作図部品D
作図部品I
部分図J
部分図K
作図部品H
作図部品C
作図部品D
作図部品I
作図部品L
のようになるとすれば、それぞれに変換行列を保持しておき、アフィン変換を行い、その中にある図形データの座標に変換行列を掛けてmm・dot変換をして画面に描画すれば全図形描画を行う事が出来ます。
ビューワーソフトであれば、それぞれの変換行列をアフィン変換後の値にする事も出来ますが、CADソフトの場合は、変換行列を変更する可能性がありますので、アフィン変換後の値を入れるのではなく、必要に応じてアフィン変換を行うほうが無難、かもしれません。まぁ、状況に依ると思いますので、それはまた後に考える事にします。
それではここまでのテストプログラムです。変換行列やアフィン変換などについてはまだコーディングしていません。実行ファイル、gdiplus.dll、gdipフォルダは入っていません。ソースのみです。
|
|
CAD装置(1)
CAD装置(2)
メディア
AutoCADの
DIESELマクロ
CSV
DXF
PCES
IGES
STEP
数学とCAD
CAD作ろ!
CADを考える
▲PREV
▼NEXT
M7
Jw_cad
|