dgnlib: 要素の読み込み

DGNファイルはさまざまな要素の種類があり、それぞれにフォーマットが異なります。
以下では、要素種別ごとに要素を読み込んで、さらに情報を取り出す方法を列挙します。

読み込み用にファイルを開く方法はdgnlib概要を参照してください。

以下は、DGNElemCore型のpsElementという変数に要素を読み込んだとして、
情報の取り出しを解説します。

レイヤー、色、線幅、線種

これらは全要素に共通の属性として、DGNElemCore型に記録されています。
描画要素以外の要素(複合連結ヘッダ、セルヘッダ、TCB等)にもあります。

DGNReadElement()関数はDGNElemCore型のポインタを返すので、
各要素へキャストする前にこれらの情報は取り出すことが出来ます。
フィールド名は、それぞれlevelcolorweightstyleとなっています。

線分、連続線分、多角形

これらはすべてDGNElemMultiPoint型で読み込まれます。
内部的には、連続線分と多角形は同じフォーマットですが、線分はこれらと異なります。

点要素と線分要素を区別する方法は、始点と終点の座標がまったく同じかどうかを調べる以外に方法がありません。

以下の例は取り出しの概念的な例です。

DGNElemCore *psElement;
double dX, dY, dZ;
int i;

switch( psElement->stype )
{
case DGNST_MULTIPOINT:
  DGNElemMultiPoint *pMulti;
  
  // 要素種別の取得
  if ( psElement->type == DGNT_LINE ) // 線分要素
  else if ( psElement->type == DGNT_LINE_STRING ) // 連続線分要素
  else if ( psElement->type == DGNT_SHAPE ) // 多角形要素
  
  pMulti = (DGNElemMultiPoint *)psElement;
  if ( psElement->type == DGNT_LINE &&
    ( pMulti->vertices[0].x == pMulti->vertices[1].x && 
      pMulti->vertices[0].y == pMulti->vertices[1].y &&
      pMulti->vertices[0].z == pMulti->vertices[1].z )
  )
  {
	// 点要素
	
	// 点の座標の取り出しの例
	dX = pMulti->vertices[0].x;
	dY = pMulti->vertices[0].y;
	dZ = pMulti->vertices[0].z;
  }
  else
  {
	// 線分、連続線分、多角形
    *pnVertice = pMulti->num_vertices;
    for ( i = 0; i < pMulti->num_vertices; i++ )
    {
	  // 頂点列の取り出しの例
      dX = pMulti->vertices[i].x;
      dY = pMulti->vertices[i].y;
      dZ = pMulti->vertices[i].z;
    }
    // 要素種別の取り出し
  }
  break;

楕円、円弧

大体以下のようなコードで読めるはずです。

DGNElemCore *psElement;
POINT pnt;
int i;

switch( psElement->stype )
{
case DGNST_ARC:
  DGNElemArc *pArc;
  DGNPoint pnt;
  double dPrimaryAxis, dSecondaryAxis;
  double dRot;
  int nQuat[4];
  
  // 要素種別の取得
  if ( psElement->type == DGNT_ELLIPSE ) // 楕円要素
  else if ( psElement->type == DGNT_ARC ) // 円弧要素
  
  pArc = (DGNElemArc *)psElement;
  
  // 中心の座標を取得
  pnt.x = pArc->origin.x;
  pnt.y = pArc->origin.y;
  pnt.z = pArc->origin.z;
  
  // 長半径、短半径の取得
  dPrimaryAxis = pArc->primary_axis;
  dSecondaryAxis = pArc->secondary_axis;
  
  // 回転角を取り出す(2次元)
  dRot = pArc->rotation;
  
  // クォータニオンを取り出す(3次元)
  for ( int i = 0; i < 4; i++ )
  	nQuat[i] = pArc->quat[i];
  	
  // 円弧の場合、円弧の開始位置と円周の角度を取り出す
  double dStartAngle = pArc->startang;
  double dSweepAngle = pArc->sweepang;
  
  break;
}

角度

角度の基準は、長半径がX軸方向ならば0で、反時計回りが+方向と定義されています。

2次元のデザインファイルならば上記のように角度を取り出せばいいのですが、
3次元のデザインファイルの場合は、角度はクォータニオンとして保存されています。

クォータニオン

上記のとおり、3次元のデザインファイルの場合、楕円、円弧要素の回転はクォータニオンで保存されています。
普通クォータニオンは小数になりますが、デザインファイルには整数で保存されています。
実際のクォータニオンの値を求めるには、デザインファイルから取り出した値を2147483647(INT_MAX)で割ります。

dgnlibには便利なDGNQuaternionToMatrix()関数が用意されているので、読み込みのときはこれを使うといいでしょう。

円弧を点で近似

dgnlibを、デザインファイルを読み込んで描画するプログラムを作るのに使う場合、
dgnlibにはこれまた便利なDGNStrokeArc()という関数があります。
これはDGNArc要素のポインタと近似する点数、その点数分のDGNPoint配列を渡すと
円弧をその点数で近似した点列を計算して返すというものです。
返された点列を連続線分でつなげば円弧を近似することができます。

テキスト要素

大体以下のようにして読み込むことができるかと思います。

DGNElemCore *psElement;
POINT pnt;
char *pStr;
double dWX, dWY;
int nFont;
int nJust;
double dRot;

switch ( psElement->stype )
{
case DGNST_TEXT:
	DGNElemText *pText;
	pText = (DGNElemText *)psElement;
	
	// テキストの基準点の位置
	pnt.x = pText->origin.x;
	pnt.y = pText->origin.y;
	pnt.z = pText->origin.z;
	
	// テキストの取り出し
	pStr = pText->string;
	
	// テキストサイズ
	dWX = pText->length_mult;
	dWY = pText->height_mult;
	
	// テキストのアンカー位置
	nJust = pText->justification;
	
	// フォント
	nFont = pText->font_id;
	
	// 回転
	dRot = pText->rotation;
	
	break;
}

とこのように、このままでは情報として使えないものが多いです。

テキストサイズ

DGNでは、1文字のテキストサイズを縦横それぞれ主単位の何倍かという値で記録されています。
主単位がメートルならば、テキスト1文字のサイズはlength_multheight_multmということになります。

フォント

フォントもフォント名をそのまま使用するのではなくフォントのIDとして記録されています。
この記録番号に割り当てられているフォントはソフトによって(または設定によって)異なるので、
ほぼ意味がありません。

アンカー位置

これは、テキストの基準位置のことで、justificationというフィールドに記録されています。
この値は0~14まであり、dgnlibのなかでDGNJ_**という定数で記録されています。

回転

回転角の定義は楕円要素と同じですが、クォータニオンはありません。
DGNファイルフォーマットとしてはテキスト要素にもクォータニオンは定義されていますが、dgnlibでは対応していないようです。

複合連結ヘッダ

いくつかの要素でひとつの図形を定義する場合に、構成要素の先頭に書き込まれている要素です。
この要素には、共通情報のほかには構成要素の数numelemsと、
自身も含めた構成要素全体のWORD数-19WORDを格納するtotlengthがあります。
あと2つのフィールド(surftypeboundelems)は3D曲面などで使用されるもののようで、
ここでは触れません。

この要素で必要になるのはnumelemsだけです。
この要素に続くnumelems個の要素は構成要素であるということがわかります。
構成要素となっている要素は共通情報core->complex1以上になっているはずです。

DGNElemComplexHeader *pComplex;
int nComplex;

switch ( psElement->stype )
{
case DGNST_COMPLEX_HEADER:
	pComplex = (DGNElemComplexHeader *)psElement;
	nComplex = pComplex->numelems;

TCB(Terminal Control Block)

ターミナルコントロールブロック(TCB)はデザインフィアルの2次元/3次元の別、
UORと副単位の比率、副単位と主単位の比率、ビュー情報などの情報を持っています。
これらはdgnlib内で適切に処理されるので特に意識する必要は無いですが、
2次元/3次元で処理を分けたいときなんかはここを参照します。

出力ファイルにビュー情報を持ち越したいときなんかはビュー情報を読み込む必要があるでしょう。
ビューは8個まで持つことができ、それぞれ個別にビューの設定をすることができます。

ビュー情報には中心位置、表示画層、視界行列、現在の奥行きなどが記録されています。
ファイルフォーマットに詳細があるので、
これと照らし合わせてみるとわかるかと思います
(とは言うものの、dgnlibと完全には対応していないようで、詳細は良くわかりません)。

表示画層は1~63までありますが、表示/非表示をビットで表しているようなので、
unsigned char型8個の配列として記録されているようです。

以下の例は、とりあえず出力する例です。

  DGNElemTCB *pTCB;

  switch( psElement->stype )
  {
  case DGNST_TCB:
    pTCB = (DGNElemTCB *)psElement;
    printf( "Dimension = %dn", pTCB->dimension );
    printf( "X ORIG = %d Y ORIG = %dn", pTCB->origin_x, pTCB->origin_y );
    printf( "UOR-Sub = %d Sub-Master = %dn",
     pTCB->uor_per_subunit, pTCB->subunits_per_master );
    for ( i = 0; i < 8; i++ )
    {
      printf( "View %d: flags = %d levels = %s origin = [%.3f,%.3f,%.3f]
       delta = [%.3f,%.3f,%.3f]n",
        i, pTCB->views[i].flags, pTCB->views[i].levels,
         pTCB->views[i].origin.x, pTCB->views[i].origin.y, pTCB->views[i].origin.z,
        pTCB->views[i].delta.x, pTCB->views[i].delta.y, pTCB->views[i].delta.z );
      printf( "TransMatrixn" );
      for ( j = 0; j < 9; j++ )
      {
        printf( "%d", pTCB->views[i].transmatrx[j] );
      }
      printf( "n" );
      printf( "conversion = %.3fn", pTCB->views[i].conversion );
      printf( "activez = %.dn", pTCB->views[i].activez );
    }

ちょいちょい続く、、、

アーカイブ