Skip to content

Instantly share code, notes, and snippets.

@xlab
Forked from phrozen/main.go
Created December 25, 2016 18:00
Show Gist options
  • Save xlab/f82f6f5cb6cb5d34b47b584e29a1e063 to your computer and use it in GitHub Desktop.
Save xlab/f82f6f5cb6cb5d34b47b584e29a1e063 to your computer and use it in GitHub Desktop.
STL reader
package main
import (
"bufio"
"bytes"
"encoding/binary"
"flag"
"fmt"
"github.com/spf13/nitro"
"io"
"io/ioutil"
"os"
)
type Model struct {
Header [80]byte
Length uint32
Triangles []Triangle
}
// 50 bytes
type Triangle struct {
Normal [3]float32
Vertex1 [3]float32
Vertex2 [3]float32
Vertex3 [3]float32
Attribute uint16 `"_"`
}
var filename string
func init() {
flag.StringVar(&filename, "file", "", "Filename of the STL file to parse.")
flag.Parse()
}
func ParseBufferedSTL(filename string, size int) *Model {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
defer file.Close()
m := new(Model)
b := bufio.NewReader(file)
data := make([]byte, 84)
b.Read(data)
// Parsing Header first 80 bytes.
err = binary.Read(bytes.NewBuffer(data[0:80]), binary.LittleEndian, &m.Header)
if err != nil {
panic(err)
}
// Parsing triangle count uint32 at byte 80
err = binary.Read(bytes.NewBuffer(data[80:84]), binary.LittleEndian, &m.Length)
if err != nil {
panic(err)
}
// Allocating enough memory for all the triangles in the slice
m.Triangles = make([]Triangle, m.Length)
buf := make([]byte, 50*size)
tris := make([]Triangle, size)
rs := io.ReadSeeker(file)
rs.Seek(84,0)
b = bufio.NewReaderSize(rs, 50*size)
i := 0
for {
n, err := b.Read(buf)
if err != nil {
if err == io.EOF {
break
}
panic(err)
}
offset := n/50
if i > int(m.Length) {
break
}
err = binary.Read(bytes.NewBuffer(buf), binary.LittleEndian, &tris)
if err != nil {
panic(err)
}
copy(m.Triangles[i:i+offset],tris[:offset])
i = i + offset
}
return m
}
func Compare(src, dst []Triangle) bool {
for i, _ := range src {
if src[i] != dst[i] {
return false
}
}
return true
}
func ParseSTL(filename string) *Model {
// Reading entire STL file into memory
data, err := ioutil.ReadFile(filename)
if err != nil {
panic(err)
}
m := new(Model)
// Parsing Header first 80 bytes.
err = binary.Read(bytes.NewBuffer(data[0:80]), binary.LittleEndian, &m.Header)
if err != nil {
panic(err)
}
// Parsing triangle count uint32 at byte 80
err = binary.Read(bytes.NewBuffer(data[80:84]), binary.LittleEndian, &m.Length)
if err != nil {
panic(err)
}
// Allocating enough memory for all the triangles in the slice
m.Triangles = make([]Triangle, m.Length)
// Parsing the Triangle slice on byte 84 onwards, 50 bytes per triangle
err = binary.Read(bytes.NewBuffer(data[84:]), binary.LittleEndian, &m.Triangles)
if err != nil {
panic(err)
}
return m
}
func SaveASCII(name string, model *Model) {
fo, err := os.Create(name+".stl")
if err != nil { panic(err) }
// close fo on exit and check for its returned error
defer func() {
if err := fo.Close(); err != nil {
panic(err)
}
}()
fmt.Fprintf(fo, "solid %v\n", name)
for _, tri := range model.Triangles {
fmt.Fprintf(fo, " facet normal %E %E %E\n", tri.Normal[0], tri.Normal[1], tri.Normal[2])
fmt.Fprintf(fo, " outer loop\n", )
fmt.Fprintf(fo, " vertex %E %E %E\n", tri.Vertex1[0], tri.Vertex1[1], tri.Vertex1[2] )
fmt.Fprintf(fo, " vertex %E %E %E\n", tri.Vertex2[0], tri.Vertex2[1], tri.Vertex2[2] )
fmt.Fprintf(fo, " vertex %E %E %E\n", tri.Vertex3[0], tri.Vertex3[1], tri.Vertex3[2] )
fmt.Fprintf(fo, " endloop\n" )
fmt.Fprintf(fo, " endfacet\n" )
}
fmt.Fprintf(fo, "endsolid %v", name)
}
func main() {
fmt.Println(filename)
timer := nitro.Initalize()
model := ParseSTL(filename)
timer.Step("Parsing STL")
fmt.Println("File has", model.Length, "triangles.")
model1 := ParseBufferedSTL(filename, 100)
timer.Step("Parsing STL (buffered) 100")
fmt.Println("File has", model1.Length, "triangles.")
model2 := ParseBufferedSTL(filename, 1000)
timer.Step("Parsing STL (buffered) 1000")
fmt.Println("File has", model2.Length, "triangles.")
model3 := ParseBufferedSTL(filename, 10000)
timer.Step("Parsing STL (buffered) 10000" )
fmt.Println("File has", model3.Length, "triangles.")
SaveASCII(filename+"-ascii", model)
timer.Step("Saving ASCII")
SaveASCII(filename+"-ascii-1", model1)
timer.Step("Saving ASCII")
SaveASCII(filename+"-ascii-2", model2)
timer.Step("Saving ASCII")
SaveASCII(filename+"-ascii-3", model3)
timer.Step("Saving ASCII")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment