前頁では、幾何要素/表記要素フィーチャの円弧について見ましたので今回は楕円について見てみます。
幾何要素/表記要素|楕円 |
|
パラメータ | 型 | 説明 | 範囲 |
Layer | Int | レイヤコード |
|
Color | Int | 色コード |
|
Type | Int | 線種コード |
|
line_width | Int | 線幅コード |
|
Center_x | double | 中心X座標 |
double(64bits)の
範囲(有効桁15桁) |
Center_y | double | 中心Y座標 |
double(64bits)の
範囲(有効桁15桁) |
Radius_x | double | X方向半径 | 0<半径<1.0×1015 |
Radius_y | double | Y方向半径 | 0<半径<1.0×1015 |
rotation_angle | double | 回転角 | 0≦回転角<360 |
備考
|
SXF Ver.3.1実装規約では線分等同様、楕円について何も無さそうです。
それでは楕円のデータ構造を決めます。
UnitData.pas |
type
・・・
TDataEllipse = 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)
Center_x : double ; // 中心点X座標
Center_y : double ; // 中心点Y座標
Radius_x : double ; // X方向半径
Radius_y : double ; // Y方向半径
rotation_angle:double ; // 回転角[°]
end;
TFukugoZukeiDef = record // 構造化要素|複合図形定義
exf : Boolean ; // 存在フラグ(True:有り False:無し)
name : string ; // 複合図形名
Flag : Integer ; // 種別フラグ
cnt : Integer ; // 配置数
・・・
mEll : array of TDataEllipse; // 幾何要素|楕円
mEllN : Integer ; // 数
mFzk : array of TDataFukugoZukei; // 構造化要素|複合図形配置
mFzkN : Integer ; // 数
end;
TDataClass = class
public
{ Public 宣言 }
・・・
dEll : array of TDataEllipse; // 表記要素|楕円
dEllN: Integer ; // 数
・・・ |
データ追加用の関数は
function AddDataEllipse(s:string;lay,col,ltp,wid:integer;cx,cy,rx,ry,ra:double) : Boolean; |
のようにします。線分等と同様、最初に「データ追加先の複合図形名(幾何要素) null:用紙へ追加(表記要素)」を指定するようにしています。
UnitData.pas |
// 楕円 データ項目の追加登録
function TDataClass.AddDataEllipse(s:string;lay,col,ltp,wid:integer;cx,cy,rx,ry,ra:double) : Boolean;
var
m : 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 (rx < LIMIT10)or(ry < LIMIT10) then exit ; // 半径が <= 0
ra := SetAngle(ra);
if (s = '') then begin
// 用紙へ追加
if (AddDataOrder(-1,6,dEllN)) then begin
try
Inc(dEllN);
if ((dEllN mod 200) = 1) then SetLength(dEll, dEllN+199);
with dEll[dEllN-1] do begin
exf := True ;
Layer := lay ;
Color := col ;
Ltype := ltp ;
line_width := wid ;
Center_x := cx ;
Center_y := cy ;
Radius_x := rx ;
Radius_y := ry ;
rotation_angle := ra ;
end;
Result := True ;
AddLayerCnt(lay,col,ltp,wid,-1);
except
Dec(dEllN);
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,6,mEllN)) then begin
try
Inc(mEllN);
if ((mEllN mod 200) = 1) then SetLength(mEll, mEllN+199);
with mEll[mEllN-1] do begin
exf := True ;
Layer := lay ;
Color := col ;
Ltype := ltp ;
line_width := wid ;
Center_x := cx ;
Center_y := cy ;
Radius_x := rx ;
Radius_y := ry ;
rotation_angle := ra ;
end;
Result := True ;
AddLayerFCnt(lay,col,ltp,wid,-1);
except
Dec(mEllN);
Dec(mOrdN);
end;
end;
end;
end;
end; |
さて、これで楕円データの登録は出来るようになりました。
次は、楕円の描画についてです。
円の方程式は、
(x-cx)2+(y-cy)2=r2
ですが、プログラムで記述するには非常にやりにくいです。そのため通常は
x=cx+r*Cosθ
y=cy+r*Sinθ (θ:0〜2π)
の式を利用します。
同様に、楕円の方程式は
(x-cx)2/rx2+(y-cy)2/ry2=1
となりますが、これも同様に
x=cx+rx*Cosθ
y=cy+ry*Sinθ (θ:0〜2π)
という形で利用します。但しこれには回転角が掛かっていませんので、回転を掛けるようにします。
円の描画を利用して楕円の描画を作ってみます。
UnitDataGraph.pas |
// 楕円の表示
procedure DisplayEllipse(lay,col,ltp,wid:integer;px,py,rx,ry,ra:double;t:TMatrix);
const
ARC_LENGTH = 15.0 ; // 補間線分長さ[dot相当値]
ARC_MIN = 8 ;
ARC_MAX = 360 ;
var
c,cc,l,w, i,j : integer ;
ww, d1, a,aa,n, x1,y1,x2,y2,x3,y3,x4,y4 : double ;
fl : Boolean ;
tr,tr1 : TMatrix ;
begin
// 楕円・クリッピングチェック
MtxMove(tr, -px,-py);
MtxRotD(tr1,ra);
tr := MtxMult(tr,tr1) ;
MtxMove(tr1, px,py);
tr := MtxMult(tr,tr1) ;
t := MtxMult(tr,t) ;
x1 := px-rx; y1 := py-ry; MtxXY(x1,y1, x1,y1, t);
x2 := px+rx; y2 := py-ry; MtxXY(x2,y2, x2,y2, t);
x3 := px+rx; y3 := py+ry; MtxXY(x3,y3, x3,y3, t);
x4 := px-rx; y4 := py+ry; MtxXY(x4,y4, x4,y4, t);
if (Clip4Point(x1,y1,x2,y2,x3,y3,x4,y4, WndX1,WndY1,WndX2,WndY2) = 2) then exit;
x1 := 0.0 ;
y1 := 0.0 ;
x2 := rx*Cos(0.25*Pi) ;
y2 := ry*Sin(0.25*Pi) ;
MtxXY(x1,y1, x1,y1, t);
MtxXY(x2,y2, x2,y2, t);
d1 := Dist(x1,y1,x2,y2);
a := ARC_LENGTH / (d1 * mm_dot) ;
n := Int(2.0*Pi/a) ;
if (n < ARC_MIN) then n := ARC_MIN ;
if (n > ARC_MAX) then n := ARC_MAX ;
a := 2.0*Pi / n ;
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 ;
i := 0 ;
d1 := 0.0 ;
aa := 0.0 ;
x1 := px + rx*Cos(aa);
y1 := py + ry*Sin(aa);
MtxXY(x1,y1, x1,y1, t);
for j:=1 to Round(n) do begin
aa := aa + a;
x2 := px + rx*Cos(aa);
y2 := py + ry*Sin(aa);
MtxXY(x2,y2, x2,y2, t);
if (fl) then
DisplayLineSub1(x1,y1,x2,y2)
else
DisplayLineSub2(l,i,d1, x1,y1,x2,y2) ;
x1 := x2 ;
y1 := y2 ;
end;
end; |
半径 pr だった箇所を、X方向半径 rx、Y方向半径 ry に変更し、楕円の回転角については、この手続きでは元々円を変換行列で変形して楕円状態にするようになっているので、その変換行列に対して、楕円の回転を前から掛けるようにしています。楕円の回転は楕円の中心点を基準に回転しますので、中心点の分をマイナス→回転→中心点の分をプラス、しています。 その後に部分図以下の変換行列を掛けます。そうすれば、クリッピングチェック、および、各点の座標を算出する際に、回転処理(UnitFunc.pas のRev・dRev等)を行ってから変換行列を掛ける、という事をせずに済みます。
これをしない場合は
x:=px-rx; y:=py-ry; dRev(x1,y1,x,y,px,py,ra); MtxXY(x1,y1, x1,y1, t);
x:=px+rx; y:=py-ry; dRev(x2,y2,x,y,px,py,ra); MtxXY(x2,y2, x2,y2, t);
x:=px+rx; y:=py+ry; dRev(x3,y3,x,y,px,py,ra); MtxXY(x3,y3, x3,y3, t);
x:=px-rx; y:=py+ry; dRev(x4,y4,x,y,px,py,ra); MtxXY(x4,y4, x4,y4, t);
if (Clip4Point(x1,y1,x2,y2,x3,y3,x4,y4, WndX1,WndY1,WndX2,WndY2) = 2) then exit; |
aa := 0.0 ;
x1 := px + rx*Cos(aa);
y1 := py + ry*Sin(aa);
dRev(x3,y3,x1,y1,px,py,ra);
MtxXY(x1,y1, x3,y3, t);
for j:=1 to Round(n) do begin
aa := aa + a;
x2 := px + rx*Cos(aa);
y2 := py + ry*Sin(aa);
dRev(x3,y3,x2,y2,px,py,ra);
MtxXY(x2,y2, x3,y3, t);
if (fl) then
DisplayLineSub1(x1,y1,x2,y2)
else
DisplayLineSub2(l,i,d1, x1,y1,x2,y2) ;
x1 := x2 ;
y1 := y2 ;
end;
end; |
のような感じで、回転処理を行う dRev手続きを行うという形になります。それであれば事前に変換行列にその回転処理の計算も含んでおけば、dRev手続きを実行する手間や処理時間を軽減することが出来るという訳です。
では簡単な楕円描画のテストをしてみます。
Unit1.pas |
・・・
// 楕円データ登録テスト
col := 1 ;
ltp := 1 ;
wid := 1 ;
for i:=1 to 10 do begin
lx1 := i*10 ;
ly1 := i*5 ;
CData.AddDataEllipse('', 1,col,ltp,wid, 150.0,50.0,lx1,ly1,30.0);
CData.AddDataEllipse('部分図A', 1,col,ltp,wid, 150.0,50.0,lx1,ly1,30.0);
Inc(col);
if (col > CData.zColN) then col := 1 ;
Inc(ltp);
if (ltp > CData.zLtpN) then ltp := 1 ;
Inc(wid);
if (wid > CData.zWidN) then wid := 1 ;
end;
・・・ |
これを実行すると下図のようになります。
それでは、ここまでのテストプログラムです。実行ファイル、gdiplus.dll、gdipフォルダは入っていません。ソースのみです。
|