はじめに
CCTが提供している調達DXでは3D CADのデータを取り扱っています。
異なる3D CADで作成されたデータを読み込む場合、誤差や変換の処理によりエラーが発生する場合があります。
前回の「3D CADデータと誤差」では点と点の一致を判定する誤差を扱いました。
今回はそれを用いて3Dプリンターなどに使用されるSTLファイルにエラーがないかをチェックします。
STLファイルとは
STL(Standard Triangulated Language)(参考: Wikipedia Standard Triangulated Language)は、米スリーディー・システムズ社によって作成された3D CAD用のファイルフォーマットで、現在は3Dプリンターなどのラピッドプロトタイピング分野では標準フォーマットとして使用されています。
STL形式のファイルにはバイナリ形式とテキスト形式のファイルフォーマットがあり、3個の頂点と1つの法線ベクトルで定義された三角形(ファセット)の集合として記述します。
ファセットは頂点3個と法線1個で定義され、頂点の順番はファセットの表面から見て反時計回りです。
法線はCADのデータ変換時に計算されたもので、3個の頂点で定義される平面の法線とは必ずしも一致しません。
STLファイルのエラー
STLファイルは構造が簡単で形状の整合性がとれていなくても表現できるため、そのまま3Dプリンターで使用すると不具合を起こす場合があります。
代表的なエラーとしては、
- ファセットの間に隙間がある
- ファセットの向きが裏返っている
が、あります。
今回はSTLファイルにこの2つのエラーがあるかチェックする方法を考えます。
STLファイルのチェック
ファセットの頂点をP1、P2、P3、ファセットのエッジをE1、E2、E3と定義します。
エッジの定義は以下の頂点で構成される線分とします。
- E1:P1 → P2
- E2:P2 → P3
- E3:P3 → P1
ファセットの隙間をチェックする
STLのファセットを隙間なく並べて見てみます。
この図を見ると、隙間の無い場合は以下の条件があてはまりそうです。
- 隣接するファセット間では2点が一致している
- 隣接するファセット間のエッジは逆向きになる
- 隣接するファセットの無いエッジは存在しない
これらの条件から、
- エッジに逆向きに隣接しているエッジがファセットの集合内に存在しない
場合に「隙間がある」と判断することにします。
疑似コードで以下のように記述してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
ファセットの集合 = read(STLファイル); for(ファセットA:ファセットの集合){ for(エッジA:ファセットAのエッジ){ 隣接するエッジが存在 = false; //ファセットの集合からエッジAに隣接するエッジを検索する for(ファセットB:ファセットの集合){ //同一ファセットは比較しない if(ファセットA == ファセットB){ continue; } for(エッジB:ファセットBのエッジ){ //エッジAとエッジBが逆方向で一致しているか判定 if(エッジAの始点 == エッジBの終点 and エッジAの終点 == エッジBの始点){ 隣接するエッジが存在 = true; } } } if(隣接するエッジが存在 == false){ print("ファセットAのエッジAに隙間がある"); } } } |
多重のforループで読みづらいですが、各プログラム言語のクラスや関数で整理すると簡潔に記述できると思います。
また、隙間があるファセットとエッジを記録して3Dモデル上に表示することでエラーの箇所を視覚的に把握できます。
ファセットの裏返りをチェックする
表向きと裏向きのファセットを隣接させてみます。
正常な隣接と違うところは裏向きのファセットは
- エッジが同じ方向で隣接している
のがわかります。
この条件で以下のように疑似コードを記述しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
ファセットの集合 = read(STLファイル); for(ファセットA:ファセットの集合){ for(エッジA:ファセットAのエッジ){ 同方向のエッジが存在 = false; //ファセットの集合からエッジAに隣接するエッジを検索する for(ファセットB:ファセットの集合){ //同一ファセットは比較しない if(ファセットA == ファセットB){ continue; } for(エッジB:ファセットBのエッジ){ //エッジAとエッジBが同一方向で一致しているか判定 if(エッジAの始点 == エッジBの始点 and エッジAの終点 == エッジBの終点){ 同方向のエッジが存在 = true; } } } if(同方向のエッジが存在 == true){ print("ファセットAとファセットBは逆方向"); } } } |
隙間のチェックとif文の判定内容が違いますが、この判定により「裏返ったファセットがある」のがわかります。
また、どのエッジで逆向きと判定しているかを記録しておくことでエラーの位置を表示することも可能になります。
ですが、この判定ではどちらのファセットが正しい向きかは判断できません。また、すべてのファセットが裏返っている場合はエラーとして検出することができません。
まとめ
STLファイルのエラーのチェック方法を2例紹介しましたが、用途によってはさらに
- 交差しているファセット
- 重複しているファセット
- 2点以上が同じ座標のファセット
- ファセット集合全体の向き
- 内部に空間のある形状
などをチェック、修正する必要があります。
今回の方法はファセット集合内での総当たりの計算となるので、ファセットの数の2乗に比例して計算時間が増えてしまいます。
ファセットの数が数万~数十万に増えても現実的な計算時間にするためには
- 計算試行回数の削減
- 並列計算
- GPUでの演算
などの改善が考えられます。
今回は比較的単純なSTLファイルについてのエラーチェックをしてみましたが、以降も3D CADのデータ構造に関わる話題を続けていきます。