summaryrefslogtreecommitdiff
path: root/stl.go
blob: 60ada3e48c1d745ae706cf5c9f9256545a76e4b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// 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
}

type Vector3 [3]float32

// 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() (vertex_position []float32, vertex_normal []float32) {

	vertex_position = make([]float32, stl.numberOfTriangles * 9)
	vertex_normal = make([]float32, stl.numberOfTriangles * 9)

	var point0,point1,point2,edge0,edge1,normal Vector3;

	for triangleIndex,triangle := range(stl.surface.triangles) {

		for pointIndex,point := range(triangle.points) {

			for scalarIndex,scalar := range(point.scalars) {

				vertex_position[triangleIndex*9+pointIndex*3+scalarIndex] = scalar

			}

		}

		// calculate normal
		point0 = Vector3{triangle.points[0].scalars[0], triangle.points[0].scalars[1], triangle.points[0].scalars[2]}
		point1 = Vector3{triangle.points[1].scalars[0], triangle.points[1].scalars[1], triangle.points[1].scalars[2]}
		point2 = Vector3{triangle.points[2].scalars[0], triangle.points[2].scalars[1], triangle.points[2].scalars[2]}
		edge0 = point1.subtract(point0)
		edge1 = point2.subtract(point1)
		normal = edge0.crossProduct(edge1)
		normal.divideScalar(2.0)  // length of normal vector corresponds to triangle area

		// save normal to each vertex of the triangle
		for i := 0; i<3; i++ {
			vertex_normal[triangleIndex*9+i*3+0] = normal[0]
			vertex_normal[triangleIndex*9+i*3+1] = normal[1]
			vertex_normal[triangleIndex*9+i*3+2] = normal[2]
		}

	}

	return vertex_position, vertex_normal
}

func (vector *Vector3) divideScalar(scalar float32) {
	vector[0] = vector[0] / scalar
	vector[1] = vector[1] / scalar
	vector[2] = vector[2] / scalar
}

func (vectorA Vector3) subtract(vectorB Vector3) (vectorC Vector3) {

	vectorC[0] = vectorA[0] - vectorB[0]
	vectorC[1] = vectorA[1] - vectorB[1]
	vectorC[2] = vectorA[2] - vectorB[2]

	return vectorC
}

func (vectorA Vector3) crossProduct(vectorB Vector3) (vectorC Vector3) {

	vectorC[0] = vectorA[1] * vectorB[2] - vectorA[2] * vectorB[1]
	vectorC[1] = vectorA[2] * vectorB[0] - vectorA[0] * vectorB[2]
	vectorC[2] = vectorA[0] * vectorB[1] - vectorA[1] * vectorB[0]

	return vectorC
}