用Java保存二进制STL文件

时间:2013-08-29 10:36:31

标签: java binary save bytearray stl-format

我正在尝试将一些数据保存为STL文件,以便在3D打印机上使用。 STL文件有两种形式:ASCII和二进制。 ASCII格式相对易于理解和创建,但大多数3D打印服务要求它采用二进制格式。

有关STL Binary的信息在维基百科页面上有解释:http://en.wikipedia.org/wiki/STL_(file_format)

我知道我将要求数据位于字节数组中,但我不知道如何解释维基百科中的信息并创建字节数组。这就是我想要帮助的。

到目前为止我的代码只保存了一个空字节数组:

byte[] bytes = null;
FileOutputStream stream = new FileOutputStream("test.stl");
    try {
        stream.write(bytes);
    } finally {
        stream.close();
}

4 个答案:

答案 0 :(得分:1)

如果你在最新的Java版本上开始一个新项目,你不应该对OutputStreams感到麻烦。请改用Channels和ByteBuffers。

try(FileChannel ch=new RandomAccessFile("test.stl", "rw").getChannel())
{
  ByteBuffer bb=ByteBuffer.allocate(10000).order(ByteOrder.LITTLE_ENDIAN);
  // ...
  // e.g. store a vertex:
  bb.putFloat(0.0f).putFloat(1.0f).putFloat(42);
  bb.flip();
  ch.write(bb);
  bb.clear();
  // ...
}

这是唯一一个根据需要为您提供little-endian支持的API。然后匹配数据类型: UINT8表示无符号字节, UINT32表示unsigned int, REAL32表示浮动, UINT16表示无符号短, REAL32 [3]表示三个浮点数(即数组)

只要不超过相应签名Java类型的最大值,就不必担心数据类型的无符号性质。

答案 1 :(得分:1)

这不应太含糊。规范说:

UINT8[80] – Header
UINT32 – Number of triangles

foreach triangle
  REAL32[3] – Normal vector
  REAL32[3] – Vertex 1
  REAL32[3] – Vertex 2
  REAL32[3] – Vertex 3
  UINT16 – Attribute byte count
end     

这意味着文件总大小为:80 + 4 +三角形数*(4 * 3 * 4 + 2)。

例如,如果100个三角形(84 + 100 * 50)产生了5084个字节的文件。

您可以优化以下功能代码。 打开文件并写入标题:

        RandomAccessFile raf = new RandomAccessFile( fileName, "rw" );
        raf.setLength( 0L );
        FileChannel ch = raf.getChannel();

        ByteBuffer bb = ByteBuffer.allocate( 1024 ).order( ByteOrder.LITTLE_ENDIAN );

        byte titleByte[] = new byte[ 80 ];
        System.arraycopy( title.getBytes(), 0, titleByte, 0, title.length() );
        bb.put( titleByte );

        bb.putInt( nofTriangles );              // Number of triangles

        bb.flip();                              // prep for writing
        ch.write( bb );

在此代码中,点顶点和三角形索引位于这样的数组中:

Vector3 vertices[ index ]
int indices[ index ][ triangle point number ]

写点数据:

        for ( int i = 0; i < nofIndices; i++ )  // triangles
        {
            bb.clear();
            Vector3 normal = getNormal( indices[ i ][ 0 ], indices[ i ][ 1 ], indices[ i ][ 2 ] );
            bb.putFloat( normal[ k ].x );
            bb.putFloat( normal[ k ].y );
            bb.putFloat( normal[ k ].z );
                
            for ( int j = 0; j < 3; j++ )           // triangle indices
            {
                bb.putFloat( vertices[ indices[ i ][ j ] ].x );
                bb.putFloat( vertices[ indices[ i ][ j ] ].y );
                bb.putFloat( vertices[ indices[ i ][ j ] ].z );
            }
            bb.putShort( ( short ) 0 );             // number of attributes
            bb.flip();
            ch.write( bb );
        }

关闭文件:

        ch.close();

获取法线:

Vector3 getNormal( int ind1, int ind2, int ind3 )
{
    Vector3 p1 = vertices[ ind1 ];
    Vector3 p2 = vertices[ ind2 ];
    Vector3 p3 = vertices[ ind3 ];
    return p1.cpy().sub( p2 ).crs( p2.x - p3.x, p2.y - p3.y, p2.z - p3.z ) ).nor();
}

另请参见:

Vector3

答案 2 :(得分:0)

您应该使用ASCII生成此文件并使用ASCII到二进制STL转换器。

如果您自己无法回答这个问题,那么首先在ascii中执行此操作可能会更容易。

http://www.thingiverse.com/thing:39655

答案 3 :(得分:0)

由于您的问题基于编写要发送到3D打印机的文件,因此我建议您放弃STL格式文件,而改用OBJ格式文件。编写起来要简单得多,并且它产生的文件要小得多。没有OBJ的二进制风格,但是您仍然会看到它是一个非常紧凑的文件。

(缩写)规范说:

List all the geometric vertex coordinates as a "v", followed by x, y, z values, like:
    v 123.45 234.56 345.67

then List all the triangle as "f", followed by indices in a CCW order, like:
    f 1 2 3

Indices start with 1.
Use a # character to start a comment line. Don't append comments anywhere else in a line.
Blank lines are ok.

它支持很多其他功能,例如法线和纹理。但是,如果您要做的只是将几何图形写入文件以导入到3D打印机中,那么OBJ实际上是首选,并且这种简单的内容是有效且足够的。

以下是一个完美有效的文件示例,该文件包含一个1单位的多维数据集,已成功导入Microsoft 3D Viewer(包含在Win / 10中),AutoDesk MeshMixer(免费下载)和PrusaSlicers(免费下载)

# vertices
v 0 0 0
v 0 1 0
v 1 1 0
v 1 0 0
v 0 0 1
v 0 1 1
v 1 1 1
v 1 0 1
# triangle indices
f 1 3 4
f 1 2 3
f 1 6 2
f 1 5 6
f 1 8 5
f 1 4 8
f 3 7 8
f 3 8 4
f 3 6 7
f 2 6 3
f 5 8 7
f 5 7 6

如果您有多个网格中的数据,则应合并顶点以消除重复的点。但是因为文件是纯文本,所以可以使用PrintWriter()对象和println()方法编写整个内容。