複雑系科学演習1
コンピュータグラフィックス
担当 畔上秀幸
今日の話題
STLファイルを読み込んで表示する.
STLデータをどのようなデータ構造に格納しているか?
配列を用いる方法
構造体を用いる方法
読み込んだデータをどのように使うか?
lesson8_1.cの説明
solid NOTITLEfacet normal 0.000000e+00 0.0000000e+00 1.000000e+00 outer loop
vertex 0.000000e+00 1.000000e+00 0.000000e+00 vertex 0.000000e+00 0.000000e+00 0.000000e+00 vertex 1.000000e+00 0.000000e+00 0.000000e+00 endloop
endface
facet normal 0.0000000e+00 0.000000e+00 1.000000e+00 outer loop
vertex 0.000000e+00 1.000000e+00 0.000000e+00 vertex 1.000000e+00 0.000000e+00 0.000000e+00 vertex 1.000000e+00 1.000000e+00 0.000000e+00 endloop
endface endsolid NOTITLE
STLデータ,要素,節点
A
B
y
x
節点
(node)要素
(element)a
b
c
d
e
f
a
b
c
d
e
f
A
B
solid NOTITLEfacet normal 0.000000e+00 0.0000000e+00 1.000000e+00 outer loop
vertex 0.000000e+00 1.000000e+00 0.000000e+00 vertex 0.000000e+00 0.000000e+00 0.000000e+00 vertex 1.000000e+00 0.000000e+00 0.000000e+00 endloop
endface
facet normal 0.0000000e+00 0.000000e+00 1.000000e+00 outer loop
vertex 0.000000e+00 1.000000e+00 0.000000e+00 vertex 1.000000e+00 0.000000e+00 0.000000e+00 vertex 1.000000e+00 1.000000e+00 0.000000e+00 endloop
endface
endsolid NOTITLE
x
y
z
STLデータを読み込む
手順
データ構造を考える.
(fem_struct.h)
行単位で読み込むプログラムを作る.
(idc.c)
一行分のデータを
文字
,
数字
に分けて読み込む.
通常
,
gets系
,
scanf系
関数を使う.
lesson8_1.cではidc.cを関数を使っている.
バイナリデータの場合は方法が異なる.
データを代入するプログラムを作る.
(stl_utl.c)
読み込んだデータを確認しつつ,必要なデータを取捨選択
しながら読み込む.
想定外のデータがあった場合の対処
データ構造
:
配列を用いる方法
まず,データを見る.
facet normal 0.000000e+00 0.0000000e+00 1.000000e+00 outer loop
vertex 0.000000e+00 1.000000e+00 0.000000e+00
vertex 0.000000e+00 0.000000e+00 0.000000e+00
vertex 1.000000e+00 0.000000e+00 0.000000e+00
endloop endface
1要素につき,法線1つと節点3つ
OpenGLでポリゴン表示するには全部必要
しかし,まれに法線が間違っている
STLが存在する.
今回は法線を無視し,自力で計算する.
節点の並びが,時計回りか反時計回りかは表示してみないとわからない.
一つの要素を
double型の配列9個で格納できる
.
法線
節点
配列
定義
型 識別子[定数式];
node
という名前の
double型
の値が
9個
入る配列の宣言
double node[9];
配列の初期化
double node[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
値の代入
節点
aの値の代入
node[0] = 0.000000e+00; /* x */ node[1] = 1.000000e+00; /* y */ node[2] = 0.000000e+00; /* z */
節点
bの値の代入
node[3] = 0.000000e+00; /* x */ node[4] = 0.000000e+00; /* y */ node[5] = 0.000000e+00; /* z */配列
(Cont.)
二次元配列の
定義
型 識別子[定数式] [定数式];
node
という名前の
double型
の値が
3x3個
入る
配列の宣言
double node[3][3];
配列の初期化
double node[
3
][
3
] = {
{
0.0, 0.0, 0.0
}
,
{
0.0, 0.0, 0.0
}
,
{
0.0, 0.0, 0.0
}
};
配列
(Cont.)
値の代入
節点
aの値の代入
node[0][0] = 0.000000e+00; /* x */ node[0][1] = 1.000000e+00; /* y */ node[0][2] = 0.000000e+00; /* z */
節点
bの値の代入
node[1][0] = 0.000000e+00; /* x */ node[1][1] = 0.000000e+00; /* y */ node[1][2] = 0.000000e+00; /* z */
配列の値を出力
for(ii1=0; ii1<3; ii1++){
for(ii2=0; ii2<3; ii2++){
fprintf(stderr, “node[%d][%d] = %e¥n”, ii1, ii2,
node[
ii1
][
ii2
]
);
}
データ構造
:
配列を用いる方法
(Cont.)
3次元配列も同じ.
STLデータを3次元配列で読み込む事を考える.
double model[要素数][節点数][座標値];
double model[
?
][3][3];
要素数がわからない.
3次元形状計測装置で取得したデータ量(要素数)は人に
よってまちまち
解決策
要素数に巨大な数字を指定する.
スタックオーバーフローが起こる.
静的な配列を使わずに
動的に配列を確保する
.
配列の動的確保:1次元配列の場合の例
int allocate1D (int size, double **array) {
int ii1;
if(size <= 0) return(1); /* サイズのチェック */ /* 配列の動的確保 */
(*array) = (double *)malloc(size * sizeof(double)) );
if(*array == NULL) return(1); /* mallocが失敗してないかチェック */ /* 配列の初期化 */
for(ii1=0; ii1<size; ii1++){ (*array)[ii1] = 0.0; } return(0); }
データ構造
:
配列を用いる方法
(Cont.)
array[size]になると考えればよい.
データ構造再考
三次元配列を動的に確保すれば,データは格
納できる.
しかし,配列のデータ構造にはいくつかの問題
がある.
重複した節点を格納している.
a-d, c-eは同じ座標値
.格納効率が悪い.
節点が移動した場合
,その節点を含む要素を
探索し,修正する必要がある.
二次元三角形のSTLの場合はまだ良いが,有限要
素モデルのように内部にもメッシュ構造を持つ三次
元モデルの場合,
ほとんどが重複節点
である.
節点の番号がない場合,移動する節点と共通の座
標を持つ節点を
誤差計算をしながら全配列から探
査する
必要がある.
A
B
y
x
a
b
c
d
e
f
データ構造
:構造体
を用いる方法
要素と節点を分けて格納する.
この方法は有限要素法に用いることを前提にした
azlibの場合であって,必ずしも全ての状況におい
て最善というわけではない.
要素と節点に数字のラベルをつける.
A
B
y
x
a
b
c
d
e
f
3
5
y
x
11
12
13
14
データ構造
:構造体
を用いる方法
(Cont.)
要素と節点の持つべきデータを考える
.
要素
ラベル
[
3
]
構成されている節点
[
11
,
12
,
13
]
節点
ラベル
[
11
]
座標値
[
0.0000
,
1.0000
,
0.0000
]
3
y
x
11
12
13
データ構造
:構造体を用いる方法(Cont.)
要素と節点を分けて格納すると
…
要素ラベル
要素を構成
する節点
11
13
11
13
5
14
12
3
3
5
y
x
11
12
13
14
0.0000 0.0000 0.0000 0.0000 0.0000 1.0000 0.0000 12 13 0.0000 14 0.0000 1.0000 1.0000 1.0000 節点座標 11 節点ラベル要素のデータ構造配列
節点のデータ構造配列
構造体
定義
struct タグ名 {メンバ宣言子並び} 宣言子並び
通常
typedefを用いて型として宣言する.
VECT3D
という名前のベクトルもしくは3次元座標を格納する構造体の宣言
typedef struct
{
double x;
double y;
double z;
}
VECT3D;
点
pを宣言
VECT3D
p;
初期化はメンバ毎に行う.通常初期化する関数を作る.
p.x = 0.0;
p.y = 0.0;
p.z = 0.0;
構造体のメンバにアクセスするときは「
.」で指定する.
データ構造
:構造体
を用いる方法
(Cont.)
節点の構造体
typedef struct {
int label; /* label of node(if < 0 then unused) */
VECT3D p; /* x,y,z coordinate value */
int renum; /* for renumbering */
int i_info[NODE_INFO]; /* the other informations(int) */ double d_info[NODE_INFO]; /* the other informations(double) */
} NODE; 節点ラベル 節点座標 0.0000 1.0000 0.0000 11
y
x
11
p
label
データ構造
:構造体
を用いる方法
(Cont.)
要素の構造体
#define MAX_NODE 20
typedef struct {
int label; /* label of element(if < 0 then unused) */
ELEM_TYPE type; /* element type */
int material; /* material property label */ int physical; /* physical property label */
int node_num; /* number of node */
int node[MAX_NODE]; /* node labels */
double volume; /* volume or area of element */
int i_info[ELEMENT_INFO]; /* another information (int) */ double d_info[ELEMENT_INFO]; /* another information (double) */
} ELEMENT;
3
y
x
11
12
13
要素ラベル 要素を構成す る節点 11 13 12 3データ構造
:構造体
を用いる方法
(Cont.)
節点配列の構造体
typedef struct
{
int size;
int alloc_size;
DATA_TYPE source;
NODE *array;
RENUMBER_FLAG renum_flag;
ARRAY_SORT_FLAG sort_flag;
}
NODE_ARRAY
;
y
x
11
12
13
14
節点ラベル 節点座標 0.0000 1.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 12 13 1.0000 1.0000 1.0000 14 0.0000 11array[?]
NODE
データ構造
:構造体
を用いる方法
(Cont.)
要素配列の構造体
typedef struct
{
int size;
int alloc_size;
DATA_TYPE source;
ELEMENT *array;
ARRAY_SORT_FLAG sort_flag;
}
ELEMENT_ARRAY
;
要素ラベル
要素を構成
する節点
11
13
11
13
5
14
12
3
3
5
y
x
11
12
13
14
ELEMENT
lesson8_1.c
21行目から
/* グローバル変数 */
static GLint drag_state; static GLint begin_x = 0; static GLint begin_y = 0; static float quat[4];
static NODE_ARRAY node;
static ELEMENT_ARRAY element;
static VECT3D min, max, center; static double radius;
57
行目から
/* STLファイルからモデルデータを読み込む */
rc_fopen(argv[1], "r", &fp); /* ファイルを開く */
input_stl_3dface(fp, &element, &node); /* STLファイルの読み込み */
rc_fclose(fp); /* ファイルを閉じる */ /* 節点と要素の数 */
fprintf(stderr, "node.size = %d¥n", node.size);
lesson8_1.c(Cont.)
141行目から
/* STLモデルを描画 */
glPushMatrix();
/* 大きさを正規化して,STLモデルを原点に移動 */
glScalef(1.0/radius, 1.0/radius, 1.0/radius); glTranslatef(-center.x, -center.y, -center.z);
/* Bounding Box */ glDisable(GL_LIGHTING); /* 線,点を描画する時はライトOFF */ glColor3f(R(100), G(100), B(100)); draw_bounding_box(min, max); /* STLモデルを描画 */ glEnable(GL_LIGHTING); /* ライトON */ glColor3f(R(32), G(165), B(237)); draw_stl_model(GL_POLYGON); /* GL_LINE_LOOPにすると線で表示 */ glPopMatrix();
lesson8_1.c(Cont.)
290行目から
void draw_stl_model(GLenum mode)
{
int ii1;
……
for(ii1=0; ii1<element.size; ii1++){
/* 要素数で繰り返し */
/* 要素配列の labelが -1 の要素は使われていないのでスキップ */
if(element.array[ii1].label < 0){
continue;
}
……
}
……
}
要素ラベル 要素を構成 する節点 11 13 11 13 5 14 12 3lesson8_1.c(Cont.)
要素と節点を分けて格納したので節点を検索する必要が
ある.
303行目から
/* * 各要素を成す節点(三角形の頂点)の配列上の位置を探索する * node : 節点の配列などを含む「節点の構造体」 * element : 要素の配列などを含む「要素の構造体」 * element.array[??] : 要素配列中の一つの要素 * element.array[??].node[0] : 要素を成す節点(三角形の頂点)の一つ目 * index[?] : ある番号を持つ節点が節点配列の何番目にあるかを示す数字 */index[0] = search_node_label(node, element.array[ii1].node[0]); index[1] = search_node_label(node, element.array[ii1].node[1]); index[2] = search_node_label(node, element.array[ii1].node[2]); /* 何れかの節点が無い時はプログラム終了 */
if( (index[0] < 0) || (index[1] < 0) || (index[2] < 0)){ fprintf(stderr, "search_node_label[%d|?] < ", ii1); exit(1); } 要素ラベル 要素を構成 する節点 11 13 11 13 5 14 12 3
lesson8_1.c(Cont.)
配列の位置とラベルは違う.
index[0] = search_node_label(node, element.array[0].node[0]);
配列の
番号
要素ラ
ベル
要素を構成
する節点
11
3
13
11
13
1
5
14
12
0
14 13 12 11 節点ラベル 0.0000 0.0000 0.0000 0.0000 0.0000 1.0000 0.0000 1 2 0.0000 3 0.0000 1.0000 1.0000 1.0000 節点座標 0 配列の番号lesson8_1.c(Cont.)
法線ベクトルの計算
321
行目から
/* * 簡易法線計算 * node.array[index[0]].p : 節点配列の index[0]番目にある節点の座標点 p * sub_vect3d() : ベクトルの差 * outer_product3d() : 外積 */v1 = sub_vect3d( node.array[index[1]].p, node.array[index[0]].p ); v2 = sub_vect3d( node.array[index[2]].p, node.array[index[0]].p ); nvect = outer_product3d(v1, v2);