A while back, I had the need to interpret an STL file (stereolithography), which is the general filetype of choice when dealing with rapid prototyping / 3D printing. My side business at the time had released a free 3D model viewer for the iPad, and we wanted to give estimates on prototyping costs whenever someone uploaded one of their own models. I couldn’t find any reasonable STL parsers that would work server-side, so I decided to make my own. The process of doing that, and the resulting files, are described below.
Stereolithography files are used by a slew of 3D modeling programs. There are two main types of STL file, ASCII and Binary. Most of the STL files I had interacted with in my rapid prototyping days were binary, so that’s what I enabled my PHP engine to parse. Binary STL files have the following format:
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
In order to offer the user an estimate on rapid prototyping without bogging down the php code by computing the exact volume of the part, I decided that using a bounding box approach would work fine. By stepping through each triangle in the file, and each vertex in each triangle, I only had to check if the X, Y and Z values of each vertex were larger (absolute value) than the preceding maximum value. Once I had to overall volume of the part, I could apply some calculations for our cost of material to offer the user an estimate on printing. Now, on to the code:
<?php $x_max = 0; $y_max = 0; $z_max = 0; $x_min = 0; $y_min = 0; $z_min = 0;
First thing first, we want to set all the minimum and maximum values for each axis to zero. This way, a larger positive/negative value will always overwrite the initialized value of 0.
$code = $_GET['code']; $filepath = "/apps/mod3d/uploads/stl/".$code; $fp = fopen($filepath, "rb"); $section = file_get_contents($filepath, NULL, NULL, 0, 79); fseek($fp, 80); $data = fread($fp, 4); $numOfFacets = unpack("I", $data);
The $code variable holds an identifying number passed in from a previous file. This is appended to the filepath to give the correct location of the file in question. The path is opened, and the $section variable gets the first 80 characters of the file. This part isn’t necessary for the actual calculation, but it does give you the file name as part of the header. Next, we seek to the 80th byte in the file and read the next 4 characters into the $data variable. Lastly, we unpack that data into the $numOfFacets variable. The ‘I’ format variable indicates that the $data is in unsigned integer format. Now we have the number of triangles that make up the 3D model, which will become the limiter for our logic loop, which comes next:
for ($i = 0; $i < $numOfFacets[1]; $i++){ //Start Normal Vector $data = fread($fp, 4); $hold = unpack("f", $data); $normalVectorsX[$i] = $hold[1]; $data = fread($fp, 4); $hold = unpack("f", $data); $normalVectorsY[$i] = $hold[1]; $data = fread($fp, 4); $hold = unpack("f", $data); $normalVectorsZ[$i] = $hold[1]; //End Normal Vector //Start Vertex1 $data = fread($fp, 4); $hold = unpack("f", $data); $vertex1X[$i] = $hold[1]; $data = fread($fp, 4); $hold = unpack("f", $data); $vertex1Y[$i] = $hold[1]; $data = fread($fp, 4); $hold = unpack("f", $data); $vertex1Z[$i] = $hold[1]; //End Vertex1 //Start Vertex2 $data = fread($fp, 4); $hold = unpack("f", $data); $vertex2X[$i] = $hold[1]; $data = fread($fp, 4); $hold = unpack("f", $data); $vertex2Y[$i] = $hold[1]; $data = fread($fp, 4); $hold = unpack("f", $data); $vertex2Z[$i] = $hold[1]; //End Vertex2 //Start Vertex3 $data = fread($fp, 4); $hold = unpack("f", $data); $vertex3X[$i] = $hold[1]; $data = fread($fp, 4); $hold = unpack("f", $data); $vertex3Y[$i] = $hold[1]; $data = fread($fp, 4); $hold = unpack("f", $data); $vertex3Z[$i] = $hold[1]; //End Vertex3 //Attribute Byte Count $data = fread($fp, 2); $hold = unpack("S", $data); $abc[$i] = $hold[1]; $x_vals = array($vertex1X[$i], $vertex2X[$i], $vertex3X[$i]); $y_vals = array($vertex1Y[$i], $vertex2Y[$i], $vertex3Y[$i]); $z_vals = array($vertex1Z[$i], $vertex2Z[$i], $vertex3Z[$i]);
The first chunk of the for loop code reads and unpacks the X, Y, and Z points of the normal vector. This normal vector is the vector orthogonal to the face of the triangle we’re currently operating on. It would be useful if you’re trying to display the model, but not necessary for determining volume. The next portion (after the //End Normal Vector comment) grabs the X, Y, and Z point values for each of the three vertices that make up the triangle. The last portion grabs the attribute byte count, which is useless for our purposes. We then put all the X point, Y point and Z point values into separate arrays. The last portion of the for() loop looks at these arrays to determine if the point is a maximum:
if(max($x_vals) > $x_max) { $x_max = max($x_vals); } if(max($y_vals) > $y_max) { $y_max = max($y_vals); } if(max($z_vals) > $z_max) { $z_max = max($z_vals); } if(min($x_vals) < $x_min) { $x_min = min($x_vals); } if(min($y_vals) < $y_min) { $y_min = min($y_vals); } if(min($z_vals) < $z_min) { $z_min = min($z_vals); }
This process repeats over and over for each of the triangles in the file. When it’s all said and done, x_max/x_min will hold the minimum and maximum values of the 3D model along the X axis. The same goes for the Y and Z axes. Lastly, we can get the actual width of the model in each axis direction by subtracting the minimum value from the maximum. Then it’s a simple matter of multiplying all the dimensions of our bounding box to get a volume estimate.
$x_dim = $x_max - $x_min; $y_dim = $y_max - $y_min; $z_dim = $z_max - $z_min; $volume = $x_dim*$y_dim*$z_dim;
Note that this will almost always be an overestimate of volume, unless the item is in fact a box. Hollow areas, large extrusions off the main portion of the model, etc, will change how much the estimated volume approximates the actual volume. But it’s at least a starting point.
Hopefully this exercise will help you understand how STL files store data about 3D models, and how PHP can be used to parse that data. Feel free to use the attached PHP file for your own purposes, but I’d appreciate you leaving my developer info in the file, and an email/comment letting me know that you found it helpful.
Right-click and Save File As… : Link to stlvolume source
Pavan
Andrew
MD Schmidt
Abraham
Will
MD Schmidt
hanlei
Peter
MD Schmidt
Alex
MD Schmidt
angelo
MD Schmidt
jothikannan
Peter
Damian
castleseven