STLバイナリファイルエクスポート処理の追加
C#にてSTLバイナリ出力機能を追加しました。基本的にはASCIIファイルで出力しなければデータの劣化が起きてしまうのですが、旧来からのCAD/CAMソフトには必ずある機能で相互のデータの受け渡しに必要なので追加しました。バイナリ出力フォーマットの解説もしています。コードの書き方やバイナリファイルの扱い方が知りたい方は必見です。
解説と結果画面の紹介
バイナリファイルのテスト処理を追加必須なのですが、バイナリエディタで出力を確認しながら開発しました。古典的ですが、一定の効果はあります。
結果としては「まだ出力内容に問題があるので、テストコード必要だよね。」という状態です。
エクスポートメニューのファイルの種類に「STLバイナリファイル」が追加されました。
STLバイナリファイルフォーマット
出力されるフォーマットは以下のようになります。
バイト数 | データ型 | データ内容 |
---|---|---|
80 | char[ ] | 任意の文字列 |
4 | unsigned int | 三角形の枚数 |
- | - | 1つ目の三角形データ |
4 | float | 三角形の法線ベクトルのX成分値 |
4 | float | 三角形の法線ベクトルのY成分値 |
4 | float | 三角形の法線ベクトルのZ成分値 |
4 | float | 三角形の1点目の頂点のX座標値 |
4 | float | 三角形の1点目の頂点のY座標値 |
4 | float | 三角形の1点目の頂点のZ座標値 |
4 | float | 三角形の2点目の頂点のX座標値 |
4 | float | 三角形の2点目の頂点のY座標値 |
4 | float | 三角形の2点目の頂点のZ座標値 |
4 | float | 三角形の3点目の頂点のX座標値 |
4 | float | 三角形の3点目の頂点のY座標値 |
4 | float | 三角形の3点目の頂点のZ座標値 |
2 | - | 未使用データ |
- | - | 2つ目の三角形データ |
4 | float | 三角形の法線ベクトルのX成分値 |
4 | float | 三角形の法線ベクトルのY成分値 |
4 | float | 三角形の法線ベクトルのZ成分値 |
4 | float | 三角形の1点目の頂点のX座標値 |
4 | float | 三角形の1点目の頂点のY座標値 |
4 | float | 三角形の1点目の頂点のZ座標値 |
4 | float | 三角形の2点目の頂点のX座標値 |
4 | float | 三角形の2点目の頂点のY座標値 |
4 | float | 三角形の2点目の頂点のZ座標値 |
4 | float | 三角形の3点目の頂点のX座標値 |
4 | float | 三角形の3点目の頂点のY座標値 |
4 | float | 三角形の3点目の頂点のZ座標値 |
2 | - | 未使用データ |
・・・ | - | (三角形の法線ベクトルのX成分値から未使用データまでを1枚の三角形データとして三角形枚数分のデータが続く) |
実装コードの解説
C#のコードは以下の通りです。
/// <summary>
/// バイナリ(Binary)形式でのSTLファイル書き込み
/// </summary>
/// <param name="triangleMeshes">三角形メッシュ</param>
/// <param name="filePath">ファイルパス</param>
/// <returns></returns>
public static async Task WriteBinaryAsync(IEnumerable<TriangleMesh> triangleMeshes, string filePath)
{
// filePath が入っていない場合はエラーとする
if (filePath == null)
throw new ArgumentNullException(nameof(filePath));
// ファセットデータが無い場合はエラー
if (triangleMeshes == null)
throw new ArgumentNullException(nameof(triangleMeshes));
try
{
// 上書きモードでファイルを開く
using (var writer = new StreamWriter(filePath, false, Encoding.Unicode))
{
await Task.Run(() =>
{
byte[] header = new byte[80];
// ヘッダ書き込み
writer.Write(header);
// ファセットの枚数書き込み
uint size = 0;
foreach (var triangleMesh in triangleMeshes)
{
size += (uint)triangleMesh.VertexIndices.Count();
}
writer.Write(size);
// 全ファセット書き込み
ushort buff = 0;
// 全ファセットデータ書き込み
foreach (var triangleMesh in triangleMeshes)
{
int i = 0;
foreach (var vertexIndces in triangleMesh.VertexIndices)
{
writer.Write(triangleMesh.VertexNormals.ElementAtOrDefault(i).X);
writer.Write(triangleMesh.VertexNormals.ElementAtOrDefault(i).Y);
writer.Write(triangleMesh.VertexNormals.ElementAtOrDefault(i).Z);
writer.Write((float)triangleMesh.Vertices.ElementAtOrDefault(vertexIndces.Item1).X);
writer.Write((float)triangleMesh.Vertices.ElementAtOrDefault(vertexIndces.Item1).Y);
writer.Write((float)triangleMesh.Vertices.ElementAtOrDefault(vertexIndces.Item1).Z);
writer.Write((float)triangleMesh.Vertices.ElementAtOrDefault(vertexIndces.Item2).X);
writer.Write((float)triangleMesh.Vertices.ElementAtOrDefault(vertexIndces.Item2).X);
writer.Write((float)triangleMesh.Vertices.ElementAtOrDefault(vertexIndces.Item2).X);
writer.Write((float)triangleMesh.Vertices.ElementAtOrDefault(vertexIndces.Item3).X);
writer.Write((float)triangleMesh.Vertices.ElementAtOrDefault(vertexIndces.Item3).Y);
writer.Write((float)triangleMesh.Vertices.ElementAtOrDefault(vertexIndces.Item3).Z);
writer.Write(buff);
i++;
}
}
});
}
}
catch (Exception)
{
throw new Exception();
}
}
参考のGitHubリポジトリの紹介
ソースコードはGitHubに公開していますので、テストコード作成やバグフィックスにご協力していただく方募集中です。