Reading STL formatted CAD files

I had to process some CAD files for my rapid prototyping machine project so I came up with the following as a starting point. An STL files describe a set of triangular facets that form a mesh that approximates any three dimensional shape. I wrote this code to allow me to read a file and generate an array of facet data from the binary STL format.

We need a few helper classes.

An ordered triplet of 32 bit floats.

public class Triplet{
	public float x,y,z;
	public Triplet(float x, float y, float z){
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	public String toString(){
		return "x:" + x + " y:" + y + " z:" + z;
	}
	
	public boolean equals(Triplet other){
		if(this.x != other.x)
			return false;
		if(this.y != other.y)
			return false;
		if(this.z != other.z)
			return false;
		return true;
	}
}

A Vector class sub-classed from Triplet.

public class Vector extends Triplet{
	public Vector(float x, float y, float z){
		super(x,y,z);
	}
	public String toString(){
		return "Vector " + super.toString();
	}
}

A Vertex class sub-classed from Triplet.

public class Vertex extends Triplet{
	public Vertex(float x, float y, float z){
		super(x,y,z);
	}
	public String toString(){
		return "Vertex " + super.toString();
	}
}

A Facet class to represent the triangles in the STL mesh.

public class Facet {
	/*	While the ASCII STL specification allows for n-sided facets
	 * 	where n > 2, the binary specification only allows triangular
	 *  facets.  This class will also only represent triangular 
	 *  facets.
	 */
	
	Vector normalVector;
	Vertex v1, v2, v3;
	
	Edge edge1, edge2, edge3;
	
	public Facet(Vector normalVector, Vertex v1, Vertex v2, Vertex v3){
		/*	TODO make sure all vertices are used in the correct order as
		 *	described in the file.  Order matters! Same with edges.
		*/
		
		this.normalVector = normalVector;
		this.v1 = v1;
		this.v2 = v2;		
		this.v3 = v3;
		
		edge1 = new Edge(v1,v2);
		edge2 = new Edge(v2,v3);
		edge3 = new Edge(v3,v1);
	}
	
	public String toString(){
		StringBuilder s = new StringBuilder();
		s.append("normal");
		s.append(normalVector.toString());
		s.append("\n");
		s.append(v1.toString());
		s.append("\n");
		s.append(v2.toString());
		s.append("\n");
		s.append(v3.toString());
		return s.toString();
	}
}

And here is the class that does all the work: the STL_Reader class.

import java.io.File;
import java.io.FileInputStream;
import java.io.DataInputStream;
import java.io.IOException;

The pre tag breaks on this block of code; so here it is without indentation.


public class STL_Reader {

private String fileName;
private File file;
private FileInputStream fileStream;
private DataInputStream dataStream;

private int facetCount;
private Facet[] facets;
private byte[] header; //this needs to become an object

/**
* Try to open and represent a binary STL file.
*/
public STL_Reader(String fileName) throws STL_ReaderException {
this.fileName = fileName;
header = new byte[80];
//TODO: boolean flag doesn't really say what went went wrong
boolean itWorked = parse();
if(!itWorked)
throw new STL_ReaderException();

}

/**
* Get all of the triangular facets contained in the file.
*/
public Facet[] getFacets(){
return facets;
}

private boolean parse(){
try{
file = new File(fileName);
fileStream = new FileInputStream(file);
dataStream = new DataInputStream(fileStream);

/*read the 80 byte header*/
for(int i=0;i header[i] = dataStream.readByte();

/*next is a 4 byte unsigned int with the number of facets*/
byte[] facetCountBytes = new byte[4];
dataStream.read(facetCountBytes);
facetCount = Conversions.packInteger(facetCountBytes);

/*make facets hold of size facetCount*/
facets = new Facet[facetCount];

/*read in each facet*/
for(int i = 0;i

float x,y,z;
byte[] byteBuffer = new byte[4];

/* TODO: all of these vector reads need to be rolled
* into an inner loop.
*/

//normal vector
dataStream.read(byteBuffer);
x = Conversions.packFloat(byteBuffer);
dataStream.read(byteBuffer);
y = Conversions.packFloat(byteBuffer);
dataStream.read(byteBuffer);
z = Conversions.packFloat(byteBuffer);
Vector normal = new Vector(x,y,z);
//vertex 1
dataStream.read(byteBuffer);
x = Conversions.packFloat(byteBuffer);
dataStream.read(byteBuffer);
y = Conversions.packFloat(byteBuffer);
dataStream.read(byteBuffer);
z = Conversions.packFloat(byteBuffer);
Vertex v1 = new Vertex(x,y,z);
//vertex 2
dataStream.read(byteBuffer);
x = Conversions.packFloat(byteBuffer);
dataStream.read(byteBuffer);
y = Conversions.packFloat(byteBuffer);
dataStream.read(byteBuffer);
z = Conversions.packFloat(byteBuffer);
Vertex v2 = new Vertex(x,y,z);
//vertex 3
dataStream.read(byteBuffer);
x = Conversions.packFloat(byteBuffer);
dataStream.read(byteBuffer);
y = Conversions.packFloat(byteBuffer);
dataStream.read(byteBuffer);
z = Conversions.packFloat(byteBuffer);
Vertex v3 = new Vertex(x,y,z);

facets[i] = new Facet(normal, v1, v2, v3);

//System.out.println(facets[i].toString());

//need to throw out two padding bytes
dataStream.read();
dataStream.read();
}
}catch(IOException e){
e.printStackTrace();
return false;
}

return true;
}
}


So using this class I can now do something like:

    Facet[] faces = (new STL_Reader(filename)).getFacets();
    for(Facet facet : faces)
        System.out.println(facet);

To see all the facets contained in the file.