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

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

プログラミングについて
ホームページについて
キャドについて
電子カタログについて
書籍・雑誌
イベント
リンク集
CADを考える:ハッチング(3)
前頁の続きで、ハッチングについて考えます。
SXF仕様の「複合曲線定義」にて
・構成要素は、接続し、閉じている必要があり、交差、重なり(中抜の複合曲線が、外形に1点で接する場合も含む)は許さない。
という規定があります。ハッチングそのものは自己交差していても描画は出来る場合も多いです。例えば、下記は折線の四角内に自己交差する閉じたスプライン曲線を描画する例です。
CData.AddCCurveDef(5,1,1,1,1); // 複合曲線定義 #5
lx[0] := 60; ly[0] := 60 ;
lx[1] := 120; ly[1] := 120 ;
lx[2] := 120; ly[2] := 60 ;
lx[3] := 60; ly[3] := 120 ;
lx[4] := 60; ly[4] := 60 ;
CData.AddCCrvDataSpline(5,3,10,5,0,lx,ly); // スプライン曲線
・・・
// ハッチング領域図形のセット
CData.zHArN := 2 ;
SetLength(CData.zHAr,CData.zHArN);
CData.CCrvDataToHAr(1,1); // 複合曲線定義 #1 → 外郭部
CData.CCrvDataToHAr(5,2); // 複合曲線定義 #5 → 中抜き
 

また、他の複合曲線定義と交差していてもハッチング処理やその描画はうまく行く場合も多いです。
しかし、後述する「塗り潰し」の場合にも正常に処理・描画が可能なのかどうか?という疑問はありますし、その他の各種計算等でも自己交差していると都合が悪いという場合もあるでしょう。例えば、ポリゴンの各頂点座標から面積を計算する手法では自己交差していると正常値を返さない、という事は分かっています。
本プログラムで可能だとしても、データの受け渡しをした際に、他のソフトで不都合が出てしまうとまずいでしょう。ですので本プログラムでも自己交差・ハッチングの際の他の複合曲線定義との交差についてのチェックを行い、交差している場合には処理を行わない(データ登録を行わない=描画しない)、という風にします。
 
まずは複合曲線定義を連続線分化すると変数 tLns,tLnsN を経由して TDataClass内の変数 zHAr 内に頂点座標・頂点数が入りますが、それが自己交差していないかどうかのチェックを行う関数を作成します。
 
UnitData.pas
// ハッチング領域図形 連続線分データ の自己交差チェック
//  no : ハッチング領域図形番号(1-)
// (ret) True:OK False:NG
// ※ハッチング領域図形 zHAr,zHArN は予めセット・メモリ確保
function TDataClass.HArDataSelfIntersectCheck(no:integer):Boolean;
var
 i,j,k : integer ;
 rx,ry:double ;
begin
 Result := False ;
 if (no < 1)or(no > zHArN) then exit ;
 Result := True ;
 with zHAr[no-1] do begin
  for i:=0 to an-3 do begin
   for j:=i+2 to an-2 do begin
    if (i = 0)and(j = (an-2)) then continue;
    k := GetCrossLL(a[i].x,a[i].y, a[i+1].x,a[i+1].y,
            a[j].x,a[j].y, a[j+1].x,a[j+1].y, rx,ry);
    if (k = 1) then begin
     Result := False ;
     exit ;
    end;
   end;
  end;
 end;
end;
隣同士の線は必ず接続=頂点で交差するので、隣同士はチェックしないようにしています。
 
例えば、分割数10程度のスプライン曲線を3本チェックする程度でしたらすぐに処理は終わるのですが、分割数1000のスプライン曲線を3本チェックすると当方のPCでは5秒程掛かってしまいます。データが沢山あるとこれでは実用的な速度になりませんので、交点計算を行う前に、座標が明らかに離れている場合には交点無しとして判断させるようにします。
 
そのためにまず、UnitFunc.pas にて関数 BoxAreaCheck を作成し、上記にこれを組み込みます。
UnitFunc.pas
// 2つの長方形が重なっていないかどうかをチェック
// x1,y1,x2,y2 : 第1長方形
// x3,y3,x4,y4 : 第2長方形
// out : True : 重なっていない False : 重なっている
function BoxAreaCheck(x1,y1,x2,y2, x3,y3,x4,y4:double) : Boolean ;
begin
 Result := True ;
 if (x1 > x2) then swapD(x1,x2);
 if (y1 > y2) then swapD(y1,y2);
 if (x3 > x4) then swapD(x3,x4);
 if (y3 > y4) then swapD(y3,y4);
 if (x1 > (x4+LIMIT10)) or (x2 < (x3-LIMIT10)) then exit;
 if (y1 > (y4+LIMIT10)) or (y2 < (y3-LIMIT10)) then exit;
 Result := False ;
end;
UnitData.pas
// ハッチング領域図形 連続線分データ の自己交差チェック
//  no : ハッチング領域図形番号(1-)
// (ret) True:OK False:NG
// ※ハッチング領域図形 zHAr,zHArN は予めセット・メモリ確保
function TDataClass.HArDataSelfIntersectCheck(no:integer):Boolean;
var
 i,j,k : integer ;
 rx,ry:double ;
begin
 Result := False ;
 if (no < 1)or(no > zHArN) then exit ;
 Result := True ;
 with zHAr[no-1] do begin
  for i:=0 to an-3 do begin
   for j:=i+2 to an-2 do begin
    if (i = 0)and(j = (an-2)) then continue;
    if (BoxAreaCheck(a[i].x,a[i].y, a[i+1].x,a[i+1].y,
             a[j].x,a[j].y, a[j+1].x,a[j+1].y)) then
     continue ;
    k := GetCrossLL(a[i].x,a[i].y, a[i+1].x,a[i+1].y,
            a[j].x,a[j].y, a[j+1].x,a[j+1].y, rx,ry);
    if (k = 1) then begin
     Result := False ;
     exit ;
    end;
   end;
  end;
 end;
end;
これでテスト動作をすると約5秒だったものが約1秒までに短縮出来ます。さて、交差・重なりのチェックはまだこれだけではありません。外形と各中抜き、及び、中抜きと中抜き、の交差チェックがあります。外形と中抜きは接する事も許されない(交点1つ以上がダメ)、との事ですが、中抜き同士は、接する事はOKだが重なってはダメ(交点2つ以上がダメ)、というように読めます。
 
※交差チェックは、分割数・頂点数指定によって連続線化された状態でのチェックとなります。本来の曲線そのものでのチェックではありませんので御注意下さい。そのため、曲線そのものは交差していたとしても、線分補間後での状態では交差していない、という状況になる可能性があります。分割数・頂点数を増やせば精度は増えますが線分数が増加し、計算速度が遅くなります。
 
それでは、外形と中抜きの重なりチェックです。上記同様、交点計算の総当りを行います。当然ながら、頂点数(線分数)が多くなればなるほど遅くなります。
UnitData.pas
// ハッチング領域図形 連続線分データ の重なりチェック
//  外形と中抜きの重なりチェックを行う
//  1つでも交点があるとその時点で終了します
//  (ret) True:重なっていません False:重なっています
// ※ハッチング領域図形 zHAr,zHArN は予めセット・メモリ確保
function TDataClass.HArDataOverlapCheck1 : Boolean ;
var
 h,i,j,k : integer ;
 x1,y1,x2,y2,x3,y3,x4,y4,rx,ry:double ;
begin
 Result := True ;
 for h:=1 to zHArN-1 do begin
  for i:=0 to zHAr[0].an-2 do begin
   x1 := zHAr[0].a[i].x ;
   y1 := zHAr[0].a[i].y ;
   x2 := zHAr[0].a[i+1].x ;
   y2 := zHAr[0].a[i+1].y ;
   for j:=0 to zHAr[h].an-2 do begin
    x3 := zHAr[h].a[j].x ;
    y3 := zHAr[h].a[j].y ;
    x4 := zHAr[h].a[j+1].x ;
    y4 := zHAr[h].a[j+1].y ;
    if (BoxAreaCheck(x1,y1,x2,y2, x3,y3,x4,y4)) then continue;
    k := GetCrossLL(x1,y1,x2,y2, x3,y3,x4,y4, rx,ry);
    if (k = 1) then begin
     Result := False ;
     exit ;
    end;
   end;
  end;
 end;
end;
次に、中抜きと中抜きの重なりチェックです。これも上記同様、交点計算の総当りを行いますが、2つの中抜き同士の交点が合計1つだけの場合は接している=セーフ、という風にします。
UnitData.pas
// ハッチング領域図形 連続線分データ の重なりチェック
//  中抜きと中抜きの重なりチェックを行う
//  交点が2つあるとその時点で終了します
//  (ret) True:重なっていません False:重なっています
// ※ハッチング領域図形 zHAr,zHArN は予めセット・メモリ確保
function TDataClass.HArDataOverlapCheck2 : Boolean ;
var
 g,h,i,j,k,l : integer ;
 x1,y1,x2,y2,x3,y3,x4,y4,rx,ry,sx,sy:double ;
begin
 Result := True ;
 sx := 0.0;
 sy := 0.0;
 for g:=1 to zHArN-2 do begin
  for h:=g+1 to zHArN-1 do begin
   l := 0;
   for i:=0 to zHAr[g].an-2 do begin
    x1 := zHAr[g].a[i].x ;
    y1 := zHAr[g].a[i].y ;
    x2 := zHAr[g].a[i+1].x ;
    y2 := zHAr[g].a[i+1].y ;
    for j:=0 to zHAr[h].an-2 do begin
     x3 := zHAr[h].a[j].x ;
     y3 := zHAr[h].a[j].y ;
     x4 := zHAr[h].a[j+1].x ;
     y4 := zHAr[h].a[j+1].y ;
     if (BoxAreaCheck(x1,y1,x2,y2, x3,y3,x4,y4)) then
      continue ;
     k := GetCrossLL(x1,y1,x2,y2, x3,y3,x4,y4, rx,ry);
     if (k = 1) then begin
      if (l = 0) then begin
       Inc(l);
       sx := rx ;
       sy := ry ;
      end
      else begin
       if not(SamePosCheck(rx,ry,sx,sy)) then begin
        Result := False ;
        exit ;
       end;
      end;
     end;
    end;
   end;
  end;
 end;
end;
 
それではハッチングのテスト実行結果です。
UnitData.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(6,1,1,1,1); // 複合曲線定義 #6
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(6,3,10,4,0,lx,ly); // スプライン曲線
 
CData.AddCCurveDef(7,1,1,1,1); // 複合曲線定義 #7
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(7,3,10,4,0,lx,ly); // スプライン曲線
 
lx := nil;
ly := nil;
 
s := '' ;
if not(CData.CCrvDataCheck(1)) then s := s + '#1 ' ;
if not(CData.CCrvDataCheck(6)) then s := s + '#6 ' ;
if not(CData.CCrvDataCheck(7)) then s := s + '#7 ' ;
if (s <> '') then showMessage('複合曲線定義 '+s+'がエラーです');
 
// ----- ハッチングテスト -----
 
// ハッチング領域図形のセット
CData.zHArN := 3 ;
SetLength(CData.zHAr,CData.zHArN);
CData.CCrvDataToHAr(1,1); // 複合曲線定義 #1 → 外郭部
CData.CCrvDataToHAr(6,2); // 複合曲線定義 #6 → 中抜き
CData.CCrvDataToHAr(7,3); // 複合曲線定義 #7 → 中抜き
if not(CData.HArDataSelfIntersectCheck(1)) then
 showMessage('複合曲線定義#1が自己交差エラーです');
if not(CData.HArDataSelfIntersectCheck(2)) then
 showMessage('複合曲線定義#2が自己交差エラーです');
if not(CData.HArDataSelfIntersectCheck(3)) then
 showMessage('複合曲線定義#3が自己交差エラーです');
if not(CData.HArDataOverlapCheck1) then
 showMessage('外形と中抜きが重なって/接しています。');
if not(CData.HArDataOverlapCheck2) then
 showMessage('中抜きと中抜きが重なっています。');
// min,maxを取得
・・・・
 

 
今回は取り合えずここまでにしておきます。
 
 
それでは、ここまでのテストプログラムです。実行ファイル、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.