STL ファイル バイナリ形式読み込み

概要

STL(Stereolithography)は,3次元形状を三角形ポリゴンの集まりで表現するファイル形式である。テキスト形式(ASCII STL)とバイナリ形式(バイナリ STL)がある。 このページでは,STL ファイルのバイナリ形式を読み込む C プログラムを示し,読み込んだ三角形ポリゴンのデータから CSV ファイルと SQLite 3 データベースを生成する体験を行う。

バイナリ STL のファイル構造は次のとおりである。

作成するSQLite 3 データベース: stldb(テーブル名: stl

目次

サイト内の関連情報

第1章 STL ファイルの準備

バイナリ形式の STL ファイルを 1 つ用意する。 利用条件(ライセンス)を必ず確認すること。利用条件に同意できない場合は,利用(ダウンロード)しないこと。

第2章 CSV ファイル生成手順

◆ ソースコードのダウンロードとコンパイル手順

cd /tmp
rm -f readstl.c
wget https://www.kkaneko.jp/tools/od/readstl.c
g++ -o a.out readstl.c

◆ 上記の手順でダウンロードされるソースコード

#include <stdio.h>

const int BUFSIZE = 10000;

int main(int argc, char** argv)
{
  FILE *fp;
  char *fname = argv[1];
  unsigned char buf[BUFSIZE];
  unsigned int  size;
  unsigned int  i;

  unsigned int num;
  float nx;
  float ny;
  float nz;
  float x1;
  float y1;
  float z1;
  float x2;
  float y2;
  float z2;
  float x3;
  float y3;
  float z3;

  if ( argc < 2 ) {
    printf( "usage: %s filename.stl\n", argv[0] );
    return -1;
  }

  fp = fopen( fname, "rb" );
  if ( fp == NULL ) {
    printf( "cannot open %s \n", fname );
    return -1;
  }

  /* header (80 バイト) */
  size = fread( buf, sizeof( unsigned char ), 80, fp );

  /* 三角形の個数 (4 バイト) */
  size = fread( buf, sizeof( unsigned char ), 4, fp );
  num = *( (unsigned int*) buf );

  /* CSV のヘッダ行 */
  printf( "seq, nx, ny, nz, x1, y1, z1, x2, y2, z2, x3, y3, z3\n" );

  /* body (1 個あたり 50 バイト) */
  i = 1;
  while (1) {
    size = fread( buf, sizeof( unsigned char ), 50, fp );
    if ( size < 50 ) {
      break;
    }

    nx = *( (float*) (&(buf[0])) );
    ny = *( (float*) (&(buf[4])) );
    nz = *( (float*) (&(buf[8])) );

    x1 = *( (float*) (&(buf[12])) );
    y1 = *( (float*) (&(buf[16])) );
    z1 = *( (float*) (&(buf[20])) );

    x2 = *( (float*) (&(buf[24])) );
    y2 = *( (float*) (&(buf[28])) );
    z2 = *( (float*) (&(buf[32])) );

    x3 = *( (float*) (&(buf[36])) );
    y3 = *( (float*) (&(buf[40])) );
    z3 = *( (float*) (&(buf[44])) );

    printf( "%d, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f\n",
            i, nx, ny, nz, x1, y1, z1, x2, y2, z2, x3, y3, z3 );

    i++;
  }

  fclose( fp );
  return 0;
}

◆ 実行手順と実行結果の例

標準出力に CSV が書き出されるので,リダイレクトでファイルに保存する。

./a.out beetle_500000.stl > beetle_500000.stl.csv

◆ 確認表示

R プログラム(scatterplot3d を使用)

X <- read.table("/tmp/beetle_500000.stl.csv", header=TRUE, sep=",", na.strings="NA", dec=".", strip.white=TRUE);
library(scatterplot3d)
scatterplot3d( x = X$x1, y = X$y1, z = X$z1 )

R プログラム(rgl を使用)

X <- read.table("/tmp/beetle_500000.stl.csv", header=TRUE, sep=",", na.strings="NA", dec=".", strip.white=TRUE);
library(rgl)
plot3d( x = X$x1, y = X$y1, z = X$z1 )

SQLite 3 データベースの生成

SQLite 3

あらかじめ決めておく事項

このページでは,SQLite 3 データベースの生成を行う。 生成するSQLite 3 データベースのデータベース名を決めておくこと。 このページでは,次のように書く。

テーブルの作成

  1. テーブル定義

    作業用テーブル T と,本体のテーブル stl の 2 つを定義する。 CSV ファイルをいったん T に取り込み,モデル名(name)を付けて stl に挿入する,という流れである。

    • T(seq, nx, ny, nz, x1, y1, z1, x2, y2, z2, x3, y3, z3)
    • stl(name, seq, nx, ny, nz, x1, y1, z1, x2, y2, z2, x3, y3, z3)

    bash プログラム

    #!/bin/bash
    
    rm -f /tmp/stldb
    #
    cat >/tmp/a.$$.sql <<-SQL
    drop table if exists T;
    drop table if exists stl;
    SQL
    cat /tmp/a.$$.sql | sqlite3 /tmp/stldb
    #
    cat >/tmp/a.$$.sql <<-SQL
    create table T (
    seq           integer primary key not null,
    nx            real,
    ny            real,
    nz            real,
    x1            real,
    y1            real,
    z1            real,
    x2            real,
    y2            real,
    z2            real,
    x3            real,
    y3            real,
    z3            real
    );
    SQL
    cat /tmp/a.$$.sql | sqlite3 /tmp/stldb
    #
    cat >/tmp/a.$$.sql <<-SQL
    create table stl (
    name          TEXT not null,
    seq           integer not null,
    nx            real,
    ny            real,
    nz            real,
    x1            real,
    y1            real,
    z1            real,
    x2            real,
    y2            real,
    z2            real,
    x3            real,
    y3            real,
    z3            real
    );
    SQL
    cat /tmp/a.$$.sql | sqlite3 /tmp/stldb
    
  2. テーブルへのデータの取り込み

    CSV ファイルの先頭 1 行(ヘッダ行)を取り除いたうえで .import で T に取り込み,name 列を付けて stl に挿入する。

    bash プログラム

    #!/bin/bash
    
    cat >/tmp/a.$$.sql <<-SQL
    .mode csv
    .import /tmp/a.$$.csv T
    insert into stl select 'beetle_500000' as name, T.seq as seq, T.nx as nx, T.ny as ny, T.nz as nz, T.x1 as x1, T.y1 as y1, T.z1 as z1, T.x2 as x2, T.y2 as y2, T.z2 as z2, T.x3 as x3, T.y3 as y3, T.z3 as z3 from T;
    drop table T;
    vacuum;
    SQL
    #
    cd /tmp
    rm -f readstl.c
    wget https://www.kkaneko.jp/tools/od/readstl.c
    g++ -o a.out readstl.c
    #
    # STL ファイル(バイナリ形式)を /tmp/beetle_500000.stl として置いておくこと
    #
    ./a.out /tmp/beetle_500000.stl > /tmp/beetle_500000.stl.csv
    tail -n +2 /tmp/beetle_500000.stl.csv > /tmp/a.$$.csv
    cat /tmp/a.$$.sql | sqlite3 /tmp/stldb
    #
    echo 'select * from stl limit 10;' | sqlite3 /tmp/stldb
    echo 'select count(*) from stl;' | sqlite3 /tmp/stldb
    
  3. (オプション) データベースの再構成

    .dump でデータベース全体を SQL 文としてダンプし,.read で読み込み直すことで,データベースを再構成できる。

    bash プログラム

    #!/bin/bash
    
    cd /tmp
    ls -la stldb
    
    # dump
    cat >/tmp/a.$$.sql <<-SQL
    .output /tmp/stldb.sql
    .dump
    .exit
    SQL
    #
    cat /tmp/a.$$.sql | sqlite3 /tmp/stldb
    
    #
    rm /tmp/stldb
    
    # restore
    cat >/tmp/a.$$.sql <<-SQL
    .read /tmp/stldb.sql
    .exit
    SQL
    #
    cat /tmp/a.$$.sql | sqlite3 /tmp/stldb
    #
    cd /tmp
    ls -la stldb
    

    SQLiteman で確認

  4. R で使ってみる

    bash プログラム

    #!/bin/bash
    cat >/tmp/a.$$.r <<-RCOMMAND
    library(RSQLite)
    library(sqldf)
    library(rgl)
    dbname="/tmp/stldb"
    driver=dbDriver("SQLite")
    conn=dbConnect(driver,dbname)
    #
    T <- dbGetQuery(conn, "SELECT * from stl");
    #
    D <- sqldf("select x1, y1, z1 from T")
    plot3d( x = D\$x1, y = D\$y1, z = D\$z1 )
    Sys.sleep(100)
    RCOMMAND
    cat /tmp/a.$$.r | r
    

考察ポイント