-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathstl.go
119 lines (108 loc) · 3.19 KB
/
stl.go
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
// A very crude ASCII STL parser.
// https://en.wikipedia.org/wiki/STL_(file_format)
//
// Limitations: can only parse triangle facets (vertex triplets).
//
//
// Copyright 2018 Erik van Zijst -- [email protected]
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"io"
"bufio"
"fmt"
"regexp"
"container/list"
"math"
)
var pat = regexp.MustCompile("\\s*vertex\\s+([^ ]+)\\s+([^ ]+)\\s+([^ ]+)\\s*")
type STLReader struct {
scanner *bufio.Scanner
}
func NewSTLReader(reader io.Reader) *STLReader {
return &STLReader{
scanner: bufio.NewScanner(reader),
}
}
func (r *STLReader) readVertex() *V4 {
for {
if !r.scanner.Scan() {
return nil
}
line := r.scanner.Text()
vertices := pat.FindStringSubmatch(line)
if len(vertices) != 4 {
continue
}
var x, y, z float64
fmt.Sscanf(vertices[1], "%f", &x)
fmt.Sscanf(vertices[2], "%f", &y)
fmt.Sscanf(vertices[3], "%f", &z)
return NewV4(x, y, z)
}
}
// ReadTriangle returns the next triangle (facet) from the stream.
// When the end of the file is reached, nil is returned.
func (r *STLReader) ReadTriangle() *Triangle {
v1 := r.readVertex()
v2 := r.readVertex()
v3 := r.readVertex()
if v1 == nil {
return nil
} else if v2 == nil || v3 == nil {
panic("Invalid number of vertices in STL file.")
}
return &Triangle{v1: *v1, v2: *v2, v3: *v3}
}
// ReadModel returns the model as defined in the loaded STL file.
// Models can be scaled to fit in the ((-1, -1, -1), ..., (1, 1, 1))
// bounding box using the `scale` parameter. Use `scale=false` to keep
// the STL file's original vertex values.
func (r *STLReader) ReadModel(scale bool) *Model {
var minV = func(v1 *V4, v2 *V4) *V4 {
return NewV4(math.Min(v1.x, v2.x), math.Min(v1.y, v2.y), math.Min(v1.z, v2.z))
}
var maxV = func(v1 *V4, v2 *V4) *V4 {
return NewV4(math.Max(v1.x, v2.x), math.Max(v1.y, v2.y), math.Max(v1.z, v2.z))
}
elements := list.New()
for t := r.ReadTriangle(); t != nil; t = r.ReadTriangle() {
elements.PushBack(t)
}
var min, max *V4
model := Model{make([]Triangle, elements.Len())}
for i, e := 0, elements.Front(); e != nil; e = e.Next() {
t := e.Value.(*Triangle)
if i == 0 {
min, max = &t.v1, &t.v1
}
model.triangles[i] = *t
for _, v := range []V4{t.v1, t.v2, t.v3} {
min = minV(min, &v)
max = maxV(max, &v)
}
i++
}
if scale && elements.Len() > 0 {
// translate model to the origin:
model.Move(
((max.x - min.x) / 2.) - max.x,
((max.y - min.y) / 2.) - max.y,
((max.z - min.z) / 2.) - max.z)
// and scale it to fill the ((-.5, -.5, -.5), (.5, .5, .5)) bounding box:
factor := 1. / math.Max(max.x - min.x, math.Max(max.y - min.y, max.z - min.z))
model.Apply(ScaleM(factor, factor, factor))
}
return &model
}