-
Notifications
You must be signed in to change notification settings - Fork 17
/
goslice.go
185 lines (160 loc) · 5.95 KB
/
goslice.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package goslice
import (
"time"
"github.com/aligator/goslice/clip"
"github.com/aligator/goslice/data"
"github.com/aligator/goslice/gcode"
"github.com/aligator/goslice/gcode/renderer"
"github.com/aligator/goslice/handler"
"github.com/aligator/goslice/modifier"
"github.com/aligator/goslice/optimizer"
"github.com/aligator/goslice/reader"
"github.com/aligator/goslice/slicer"
"github.com/aligator/goslice/writer"
)
// GoSlice combines all logic needed to slice
// a model and generate a GCode file.
type GoSlice struct {
Options data.GoSliceOptions
Reader handler.ModelReader
Optimizer handler.ModelOptimizer
Slicer handler.ModelSlicer
Modifiers []handler.LayerModifier
Generator handler.GCodeGenerator
Writer handler.GCodeWriter
}
// NewGoSlice provides a GoSlice with all built in implementations.
func NewGoSlice(options data.Options) *GoSlice {
s := &GoSlice{
Options: options.GoSlice,
}
// create handlers
topBottomPatternFactory := func(min data.MicroPoint, max data.MicroPoint) clip.Pattern {
return clip.NewLinearPattern(options.Printer.ExtrusionWidth, options.Printer.ExtrusionWidth, min, max, options.Print.InfillRotationDegree, true, false)
}
s.Reader = reader.Reader(&options)
s.Optimizer = optimizer.NewOptimizer(&options)
s.Slicer = slicer.NewSlicer(&options)
s.Modifiers = []handler.LayerModifier{
modifier.NewPerimeterModifier(&options),
modifier.NewInfillModifier(&options),
modifier.NewInternalInfillModifier(&options),
modifier.NewBrimModifier(&options),
modifier.NewSupportDetectorModifier(&options),
modifier.NewSupportGeneratorModifier(&options),
}
patternSpacing := options.Print.Support.PatternSpacing.ToMicrometer()
s.Generator = gcode.NewGenerator(
&options,
gcode.WithRenderer(renderer.PreLayer{}),
gcode.WithRenderer(renderer.Skirt{}),
gcode.WithRenderer(renderer.Brim{}),
gcode.WithRenderer(renderer.Perimeter{}),
// Add infill for support generation.
gcode.WithRenderer(&renderer.Infill{
PatternSetup: func(min data.MicroPoint, max data.MicroPoint) clip.Pattern {
// make bounding box bigger to allow generation of support which has always at least two lines
min.SetX(min.X() - patternSpacing)
min.SetY(min.Y() - patternSpacing)
max.SetX(max.X() + patternSpacing)
max.SetY(max.Y() + patternSpacing)
return clip.NewLinearPattern(options.Printer.ExtrusionWidth, patternSpacing, min, max, 90, false, true)
},
AttrName: "support",
Comments: []string{"TYPE:SUPPORT"},
}),
// Interface pattern for support generation is generated by rotating 90° to the support and no spaces between the lines.
gcode.WithRenderer(&renderer.Infill{
PatternSetup: func(min data.MicroPoint, max data.MicroPoint) clip.Pattern {
// make bounding box bigger to allow generation of support which has always at least two lines
min.SetX(min.X() - patternSpacing)
min.SetY(min.Y() - patternSpacing)
max.SetX(max.X() + patternSpacing)
max.SetY(max.Y() + patternSpacing)
return clip.NewLinearPattern(options.Printer.ExtrusionWidth, options.Printer.ExtrusionWidth, min, max, 0, false, true)
},
AttrName: "supportInterface",
Comments: []string{"TYPE:SUPPORT"},
}),
gcode.WithRenderer(&renderer.Infill{
PatternSetup: topBottomPatternFactory,
AttrName: "bottom",
Comments: []string{"TYPE:FILL", "BOTTOM-FILL"},
}),
gcode.WithRenderer(&renderer.Infill{
PatternSetup: topBottomPatternFactory,
AttrName: "top",
Comments: []string{"TYPE:FILL", "TOP-FILL"},
}),
gcode.WithRenderer(&renderer.Infill{
PatternSetup: func(min data.MicroPoint, max data.MicroPoint) clip.Pattern {
// TODO: the calculation of the percentage is currently very basic and may not be correct.
if options.Print.InfillPercent != 0 {
mm10 := data.Millimeter(10).ToMicrometer()
linesPer10mmFor100Percent := mm10 / options.Printer.ExtrusionWidth
linesPer10mmForInfillPercent := float64(linesPer10mmFor100Percent) * float64(options.Print.InfillPercent) / 100.0
lineWidth := data.Micrometer(float64(mm10) / linesPer10mmForInfillPercent)
return clip.NewLinearPattern(options.Printer.ExtrusionWidth, lineWidth, min, max, options.Print.InfillRotationDegree, true, options.Print.InfillZigZag)
}
return nil
},
AttrName: "infill",
Comments: []string{"TYPE:FILL", "INTERNAL-FILL"},
}),
gcode.WithRenderer(renderer.PostLayer{}),
)
s.Writer = writer.Writer()
return s
}
func (s *GoSlice) Process() error {
startTime := time.Now()
// 1. Load model
s.Options.Logger.Printf("Load model %v\n", s.Options.InputFilePath)
models, err := s.Reader.Read(s.Options.InputFilePath)
if err != nil {
return err
}
s.Options.Logger.Printf("Model loaded.\nFace count: %v\nSize: min: %v max %v\n", models.FaceCount(), models.Min(), models.Max())
// 2. Optimize model
var optimizedModel data.OptimizedModel
optimizedModel, err = s.Optimizer.Optimize(models)
if err != nil {
return err
}
s.Options.Logger.Printf("Model optimized\n")
//err = optimizedModel.SaveDebugSTL("test.stl")
//if err != nil {
// return err
//}
// 3. Slice model into layers
layers, err := s.Slicer.Slice(optimizedModel)
if err != nil {
return err
}
s.Options.Logger.Printf("Model sliced to %v layers\n", len(layers))
// 4. Modify the layers
// e.g. generate perimeter paths,
// generate the parts which should be filled in, ...
for _, m := range s.Modifiers {
m.Init(optimizedModel)
err = m.Modify(layers)
if err != nil {
return err
}
s.Options.Logger.Printf("Modifier %s applied\n", m.GetName())
}
s.Options.Logger.Printf("Layers modified %v\n", len(layers))
// 5. generate gcode from the layers
s.Generator.Init(optimizedModel)
finalGcode, err := s.Generator.Generate(layers)
if err != nil {
return err
}
outputPath := s.Options.OutputFilePath
if outputPath == "" {
outputPath = s.Options.InputFilePath + ".gcode"
}
err = s.Writer.Write(finalGcode, outputPath)
s.Options.Logger.Println("full processing time:", time.Now().Sub(startTime))
return err
}