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

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

プログラミングについて
ホームページについて
キャドについて
電子カタログについて
書籍・雑誌
イベント
リンク集
CADを考える:ハッチング(6)
前頁までは構造化要素のハッチング(既定義(外部定義))について述べましたので今回は、ハッチング(塗り)について考えてみます。
 
ハッチング要素データ(塗り)は、SXF仕様書では以下のように記載されています。
構造化要素|ハッチング(塗り)

※SXF Ver.3.1仕様書より
パラメータ説明範囲
LayerIntレイヤコード 
ColorInt色コード 
out_idInt外形の複合曲線のフィーチャコード 
NumberInt中抜きの閉領域数 0≦数<int(32bits)
の最大値
in_id CArray
<int,
int>
中抜きの複合曲線のフィーチャコード(配列) 
備考
 
 
ハッチング(塗り)要素データは、指定した領域を塗り潰す機能です。塗り潰しを行う方法としては、まず、WindowsGDIでのTCanvasのFloodFillメソッドを利用する手法があります。これは、ペイントソフトでよくある塗り潰し機能で、境界線色のところまで塗るような機能です。これは、境界線色で塗る箇所を囲わないといけない事、隙間があると無関係なところまで塗ってしまう事、描画が遅い事、GDI+やDirect2Dに同じ機能が無い事、等のデメリットがあるため、利用しません。CADでこの機能を使うものはおそらくほとんど無いのではないかと思います。
 
次に、水平線のハッチング線を、1ドット毎に作図していく方法です。これは前回のハッチング(既定義)をほぼそのまま使う事が出来ますので、簡単です。1ドット→mm値換算をして描画を行うようにすれば良いです。が、このドット→mm換算は画面倍率によって変化するため、画面描画毎にハッチング計算を行う必要が出てしまいます。それをハッチング線の線幅でカバーするような手法はあるかもしれませんけれども、この辺りは少しやっかいかもしれません。
 
次に、WindowsGDIでのPolygonメソッド、GDI+でのFillPolygonメソッド又はパスを生成してFillPathメソッド、Direct2DでのFillGeometryメソッド、を利用する手法です。既に UnitGraph.pasにて G_Polygon手続きを作成済みですのでこれが利用出来ます。但しこの手法は、クリッピング処理が難しくなります。また、中抜きをどのように処理するか?という事を解決する必要もあります。
 
次に、ハッチング領域を三角形分割(三角パッチ)して三角形の面の集合体を描画するという手法です。こちらも中抜き処理の問題はありますし、三角形分割を行う処理も必要ですが、クリッピング処理は三角形に絞れるため比較的容易になるかもしれません。
 
また、中抜きがある場合には、中抜きも含むような連続線状態=1筆描きを行うような連続線状態を作成する、という手法も可能かもしれません。
 
WindowsGDIの場合は特に、クリッピングを行わないと画面が乱れて描画されてしまいますので、クリッピングはちゃんと行えないとまずい事になってしまいます。これは、GDIでの画面描画のドット座標値の使用可能範囲は、-32768〜32767であり、例えば、1024×768のディスプレイを使っている場合、画面の拡大倍率を32倍以上にすると超えてしまいます。CADでの画面拡大は、32倍どころか、1000倍・10000倍、というのは普通に使います。まぁ、範囲オーバーするような状態になれば画面に塗り潰しを表示しない、という手法もあります。
GDI+、Direct2Dの場合は、画面座標値は実数値で指定出来るため、少し試してみた限りによれば、クリッピング処理がなくても画面が乱れて描画されてしまう、という事は無さそうな感じはあります。ですので、GDIの場合には GDI+で処理を行うという風にすれば、さほど気にしなくても良いかもしれません。
クリッピング処理をしない場合は、処理が遅くなる、という不都合もありますが、塗り潰しそのものは元々が処理が遅いのでそれは仕方が無いとしてしまえるかもしれません。
 
という訳で、まずは UnitGraph.pas でのポリゴン描画 G_Polygon 手続きにて、GDIを指定している場合は、GDI+で描画するように変更します。GDI+はGDIよりも若干描画速度は遅いですが、仕方がありません。
UnitGraph.pas
// 描画開始
// C : 描画先
// 描画を行う最初に必ず行う事
procedure TGraphClass.G_Start(C:TCanvas);
begin
 Ca := C ;
 case(DisplayMode)of
 0,1:begin // GDI,GDI+
  with Ca do begin
   Pen.Color := clBlack;
   Pen.Width := 1;
   Pen.Style := psSolid;
  end;
  // GDI+
  gra := TGPGraphics.Create(Ca.Handle);
  gra.SetSmoothingMode(SmoothingModeHighSpeed);
  ・・・
end;
 
// 描画終了
// 描画を終わる時に必ず行う事
procedure TGraphClass.G_End ;
begin
 if (Ca = nil) then exit ;
 case(DisplayMode)of
 0,1:begin // GDI+
  pen.Free; pen := nil;
  br0.Free; br0 := nil;
  gra.Free; gra := nil;
 end;
 2: begin // Direct2D
   with D2DCanvas do begin
    EndDraw;
    Free;
   end;
   D2DCanvas := nil;
  end;
 end;
end;
・・・
// ポリゴン描画
procedure TGraphClass.G_Polygon(n:integer;p:array of TPoint) ;
var
 i : integer ;
 pnts: array of TGPPointF ;
 geo: ID2D1PathGeometry;
 aSink: ID2D1GeometrySink;
 pntsD: array of TD2DPoint2f;
begin
 case(DisplayMode)of
 0,1:begin   // GDI→GDI+、GDI+
   if (DisplayMode = 0) then begin
    br0.SetColor(ColorRefToARGB(Ca.Pen.Color)) ;
   end;
   try
    SetLength(pnts, n);
    for i:=0 to n-1 do begin
     pnts[i].X := p[i].x ;
     pnts[i].Y := p[i].y ;
    end;
    gra.FillPolygon(br0, PGPPointF(@pnts[0]), n) ;
    pnts := nil ;
   except
    pnts := nil ;
   end;
   if (DisplayMode = 0) then begin
    // 描画の乱れを防止
    gra.Free ;
    gra := nil ;
    gra := TGPGraphics.Create(Ca.Handle);
   end;
  end;
 2: begin // Direct2D
   D2DFactory.CreatePathGeometry(geo);
   geo.Open(aSink);
   pntsD := nil ;
   try
    aSink.SetFillMode(D2D1_FILL_MODE_WINDING);
    aSink.BeginFigure(
     D2D1PointF(p[0].x,p[0].y),
     D2D1_FIGURE_BEGIN_FILLED
    );
    SetLength(pntsD, n);
    for i:=0 to n-1 do begin
     pntsD[i].X := p[i].x ;
     pntsD[i].Y := p[i].y ;
    end;
    aSink.AddLines(@pntsD[0], Length(pntsD));
    aSink.EndFigure(D2D1_FIGURE_END_CLOSED);
    aSink.Close;
    pntsD := nil ;
   except
    aSink.Close;
    pntsD := nil ;
   end;
   with D2DCanvas do begin
    RenderTarget.FillGeometry(geo,D2Dbr);
   end;
  end;
 end;
end;
 
それでは、ハッチング(塗り)のデータ構造は下記のようにします。
UnitData.pas
type
 ・・・
 TDataHatch2 = record     // 構造化要素|ハッチング(塗り)
  exf : Boolean ;      // 存在フラグ(True:有り False:無し)
  Layer : Integer ;     // レイヤ(1〜256)
  Color : Integer ;     // 色 (0:レイヤ色 1〜256)
  out_id : Integer ;     // 外形の複合曲線のフィーチャコード
  Number : Integer ;     // 中抜の閉領域数(0〜)
  in_id : array of Integer; // 中抜の複合曲線のフィーチャコード
 end;
 ・・・
 
ハッチング(塗り)のデータ登録は下記のようにします。内容は、ハッチング(既定義)とよく似ていますが、ハッチング線の登録が無い分、簡単です。
UnitData.pas
// ハッチング(塗り) データ項目の追加登録
function TDataClass.AddDataHatch2(s:string;lay,col,oid,num:integer;
 iid:array of integer) : Boolean ;
 
ハッチング(塗り)の描画は、恒例のように UnitDataGraph.pasにて行いますが、塗り部分は DisplayPolygon2 手続きで行い、外形・中抜き線はハッチング(既定義)の場合と同じ処理です。
UnitDataGraph.pas
// ハッチング(塗り)の表示
procedure DisplayHatch2(lay,cf,col,oid,num:integer;
 iid:array of integer;t:TMatrix);
var
 c,cc : integer ;
 i,j,co : integer ;
begin
 with CData do begin
  // 一応チェック
  j := CCrvIDCheck(oid); // ID番号が有効かどうかをチェック
  if (j = -1) then exit ; // ID番号無効
  for i:=0 to num-1 do begin
   j := CCrvIDCheck(iid[i]); // ID番号が有効かどうかをチェック
   if (j = -1) then exit ; // ID番号無効
  end;
  // ハッチング領域図形のセット
  zHArN := 1 + num ;
  SetLength(zHAr,zHArN);
  CCrvDataToHAr(oid,1); // 複合曲線定義 oid → 外郭部
  j := 2 ;
  for i:=0 to num-1 do begin
   CCrvDataToHAr(iid[i],j); // 複合曲線定義 iid → 中抜き
   Inc(j);
  end;
  // 塗りの表示
  DisplayLayCol1(lay,col,c,cc); // 色
  gp.G_SetWidth(1);
  DisplayPolygon2(t) ;
  // 外郭線の表示
  j := CCrvIDCheck(oid);
  if (cDef[j].invisibility = 1) then begin
   co := cDef[j].Color ; if (cf = 1) then co := col;
   DisplayHArLines(0,lay,co,cDef[j].Ltype,cDef[j].line_width,t);
  end;
  // 中抜き線の表示
  for i:=0 to num-1 do begin
   j := CCrvIDCheck(iid[i]);
   if (cDef[j].invisibility = 1) then begin
   co := cDef[j].Color ; if (cf = 1) then co := col;
   DisplayHArLines(i+1,lay,co,cDef[j].Ltype,cDef[j].line_width,t);
   end;
  end;
  // メモリ解放
  DataClearHAr ;
 end;
end;
 
点群データの受け渡し、メモリ確保・解放等がやっかいになりますので、手続き DisplayPolygon2 はハッチング領域図形の変数 zHAr,zHArN を直接参照するようにしています。また、実際の描画を行う UnitGraph.pas 側への値の受け渡しでも、点群データの受け渡し等が煩雑になってしまうため、UnitGraph.pas のクラス内に privateメンバ変数 PoAr,PoArN を用意し、これを扱うための手続き PoAr〜を作成しています。そのため、以前からある G_Polygon 手続き内容もこれに対応するよう変更し、これを呼び出す UnitDataGraph.pas内の DisplayPolygon 手続き内容も変更しています。
 
そして、ハッチング(塗り)の中抜きについてですが、GDIの場合には GDI+を利用するようにしましたから、GDI+の場合にはパスの利用、Direct2Dの場合にはパスジオメトリを D2D1_FILL_MODE_ALTERNATEモードで利用するようにして中抜きの描画を出来るようにしています。そのため、ハッチング領域図形の変数 zHAr,zHArN を変数 PoAr,PoArN を経由して一括で指定してポリゴン(塗り)を出来るようにするための手続き G_PolygonA を追加しています。そして UnitDataGraph.pas 内に、これを利用する手続き DisplayPolygon2 を追加し、上記の手続き DisplayHatch2 でこれを呼び出すようにしています。
 
それでは、ハッチング(塗り)の登録・描画のテストです。中抜きの線が見えにくいので線幅を少し太くしてあります。
Unit1.pas
・・・
SetLength(lx,5);
SetLength(ly,5);
CData.AddCCurveDef(1,1,1,1,1);   // 複合曲線定義 #1
lx[0] := 40; ly[0] := 40 ;
lx[1] := 140; ly[1] := 40 ;
lx[2] := 140; ly[2] := 140 ;
lx[3] := 40; ly[3] := 140 ;
lx[4] := 40; ly[4] := 40 ;
CData.AddCCrvDataLines(1,5,lx,ly); // 折線
 
CData.AddCCurveDef(2,2,2,6,1);   // 複合曲線定義 #2
CData.AddCCrvDataArc(2,36,0, 90,130,8,0,180); // 円は円弧2つで形成
CData.AddCCrvDataArc(2,36,0, 90,130,8,180,0);
 
CData.AddCCurveDef(3,4,4,7,1);   // 複合曲線定義 #3
lx[0] := 60; ly[0] := 60 ;
lx[1] := 90; ly[1] := 90 ;
lx[2] := 60; ly[2] := 120 ;
lx[3] := 60; ly[3] := 60 ;
CData.AddCCrvDataSpline(3,3,1000,4,0,lx,ly); // スプライン曲線
 
CData.AddCCurveDef(4,5,5,8,1);   // 複合曲線定義 #4
lx[0] := 120; ly[0] := 60 ;
lx[1] := 90; ly[1] := 90 ;
lx[2] := 120; ly[2] := 120 ;
lx[3] := 120; ly[3] := 60 ;
CData.AddCCrvDataSpline(4,3,1000,4,0,lx,ly); // スプライン曲線
 
lx := nil;
ly := nil;
 
// 塗り
CData.AddDataHatch2('' ,1,12,1,3,[2,3,4]);
CData.AddDataHatch2('部分図A',1,12,1,3,[2,3,4]);
 
DisplayAllData ; // 全データ表示
・・・
というような感じでハッチングデータの登録をして描画をすると下記のような感じになります。データ登録及び描画を行う[登録]ボタンと、データ登録済の場合には描画だけ可能な[描画]ボタン、の2つにしています。スプライン曲線の分割数を1000にして、登録の際の交差チェック等でわざと時間が掛かるようにしていますが、[描画]をすればすぐに描画されるのが分かると思います。

少し拡大します

 
 
それでは、ここまでのテストプログラムです。実行ファイル、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.