// vim: shiftwidth=4 tabstop=4 noexpandtab /* Wikipedia about STL file format: https://en.wikipedia.org/wiki/STL_(file_format) */ package main import ( "io/ioutil" "log" "encoding/binary" "math" ) // representation of binary STL file content type StlModel struct { header []byte numberOfTriangles uint32 surface Surface } // read and parse a given binary STL file func ReadBinaryStlFile(filePath string) (StlModel, error) { fileContent, err := ioutil.ReadFile(filePath) if err != nil { return StlModel{}, err } model := StlModel{} model.surface = Surface{} model.header = fileContent[0:80] log.Printf("STL header: '%s'\n", string(model.header)) model.numberOfTriangles = binary.LittleEndian.Uint32(fileContent[80:84]) log.Printf("STL model has %d triangles\n", model.numberOfTriangles) var i uint32 for i = 0; i < model.numberOfTriangles; i++ { // for each expected triangle start := 84 + i*50 // 50 bytes is length of one triangle end := 84 + (i+1)*50 model.surface.triangles = append(model.surface.triangles, ParseBinaryStlTriangle(fileContent[start:end])) } return model,nil } // parse the 50 bytes of the STL file representing a triangle (surface normal is ignored) func ParseBinaryStlTriangle(data []byte) *Triangle { // FIXME: This function should only accept 50 byte slices/arrays // allocate a new triangle and three corner points on the heap triangle := new(Triangle) triangle.points[0] = new(Point) triangle.points[1] = new(Point) triangle.points[2] = new(Point) // parse x, y and z coordinate for corner point a triangle.points[0].scalars[0] = math.Float32frombits(binary.LittleEndian.Uint32(data[12:16])) triangle.points[0].scalars[1] = math.Float32frombits(binary.LittleEndian.Uint32(data[16:20])) triangle.points[0].scalars[2] = math.Float32frombits(binary.LittleEndian.Uint32(data[20:24])) // parse x, y and z coordinate for corner point b triangle.points[1].scalars[0] = math.Float32frombits(binary.LittleEndian.Uint32(data[24:28])) triangle.points[1].scalars[1] = math.Float32frombits(binary.LittleEndian.Uint32(data[28:32])) triangle.points[1].scalars[2] = math.Float32frombits(binary.LittleEndian.Uint32(data[32:36])) // parse x, y and z coordinate for corner point c triangle.points[2].scalars[0] = math.Float32frombits(binary.LittleEndian.Uint32(data[36:40])) triangle.points[2].scalars[1] = math.Float32frombits(binary.LittleEndian.Uint32(data[40:44])) triangle.points[2].scalars[2] = math.Float32frombits(binary.LittleEndian.Uint32(data[44:48])) return triangle } func (stl StlModel) toVertices() []float32 { retval := make([]float32, stl.numberOfTriangles * 9) for triangleIndex,triangle := range(stl.surface.triangles) { for pointIndex,point := range(triangle.points) { for scalarIndex,scalar := range(point.scalars) { retval[triangleIndex*9+pointIndex*3+scalarIndex] = scalar } } } return retval }