用Java保存二进制STL文件
我正在尝试将一些数据保存为STL文件,以便在3D打印机上使用。STL文件有两种形式:ASCII和二进制。ASCII格式相对容易理解和创建,但大多数3D打印服务要求其为二进制格式 有关STL二进制文件的信息在Wikipedia页面上解释如下: 我知道我会要求数据在字节数组中,但我不知道如何解释维基百科中的信息并创建字节数组。这就是我想要帮助的 到目前为止,我的代码只保存了一个空字节数组:用Java保存二进制STL文件,java,binary,save,bytearray,stl-format,Java,Binary,Save,Bytearray,Stl Format,我正在尝试将一些数据保存为STL文件,以便在3D打印机上使用。STL文件有两种形式:ASCII和二进制。ASCII格式相对容易理解和创建,但大多数3D打印服务要求其为二进制格式 有关STL二进制文件的信息在Wikipedia页面上解释如下: 我知道我会要求数据在字节数组中,但我不知道如何解释维基百科中的信息并创建字节数组。这就是我想要帮助的 到目前为止,我的代码只保存了一个空字节数组: byte[] bytes = null; FileOutputStream stream = new File
byte[] bytes = null;
FileOutputStream stream = new FileOutputStream("test.stl");
try {
stream.write(bytes);
} finally {
stream.close();
}
您应该以ASCII生成此文件,并使用ASCII到二进制STL转换器 如果您自己无法回答这个问题,那么首先使用ascii可能更容易
如果您在最新的Java版本上启动一个新项目,您不应该与OutputStreams发生冲突。改用通道和字节缓冲区
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表示无符号整数,
REAL32表示浮动,
UINT16表示无符号短符号,
REAL32[3]表示三个浮点(即一个数组)
只要不超过相应有符号Java类型的最大值,就不必担心数据类型的无符号性质。这不应该如此含糊不清。说明书上说:
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
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.
这意味着总文件大小为: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 );
}
获取法线:
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();
}
另见:
由于您的问题是基于编写一个文件发送到3D打印机,因此我建议您放弃STL格式文件,改用OBJ格式文件。它的编写要简单得多,并且生成的文件要小得多。OBJ没有二进制风格,但正如您将看到的,它仍然是一个非常紧凑的文件 (缩写)规范说明:
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
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(免费下载)中
如果在多个网格中有数据,则应合并顶点以消除重复点。但是因为文件是纯文本,所以可以使用PrintWriter()对象和println()方法来编写整个文件。不过我想学习如何编写。我正在寻找一些关于这个主题的一般性建议和教程-我不仅仅是在寻找解决方案。谢谢。因此,如果我理解正确,这可能是标题和三角形计数:
String title=“testing”;bb.put(title.getBytes(“UTF-16”);//标题(80字节)bb.putInt(32);//三角形数(UINT32)
可用于每个三角形:bb.putFloat(0)、putFloat(0)、putFloat(0);bb.putFloat(0)、putFloat(0)、putFloat(0);bb.putFloat(0)、putFloat(0)、putFloat(0);bb.putFloat(0)、putFloat(0)、putFloat(0);bb.putShort((short)12)代码>我已经弄明白了。我忘了将标题设置为固定字节[80]数组。顺便说一句,如果要对多个字符串进行编码,最好让编码器直接将结果放入缓冲区,而不是创建临时数组。例如,CharsetEncoder enc=StandardCharsets.UTF_16LE.newEncoder();enc.encode(CharBuffer.wrap(“测试”),bb,true)代码>我刚刚意识到我实际上不需要设置头,它可以是一个空字节数组,这样就不会有问题了。你知道我该如何把颜色信息添加进去吗?wiki上提到的第一种方法说,它使用“属性字节计数”值来存储15位数据。我该怎么做呢?