-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
aligator
committed
Mar 25, 2021
1 parent
a5ffdc6
commit ae412c1
Showing
3 changed files
with
233 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
package GoSlice | ||
|
||
import ( | ||
"GoSlice/clip" | ||
"GoSlice/data" | ||
"GoSlice/gcode" | ||
"GoSlice/gcode/renderer" | ||
"GoSlice/handler" | ||
"GoSlice/modifier" | ||
"GoSlice/optimizer" | ||
"GoSlice/reader" | ||
"GoSlice/slicer" | ||
"GoSlice/writer" | ||
"fmt" | ||
"time" | ||
) | ||
|
||
// 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 | ||
models, err := s.Reader.Read(s.Options.InputFilePath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// 2. Optimize model | ||
var optimizedModel data.OptimizedModel | ||
|
||
optimizedModel, err = s.Optimizer.Optimize(models) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
//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 | ||
} | ||
|
||
// 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 | ||
} | ||
} | ||
|
||
// 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) | ||
fmt.Println("full processing time:", time.Now().Sub(startTime)) | ||
|
||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package GoSlice | ||
|
||
import ( | ||
"GoSlice/data" | ||
"GoSlice/util/test" | ||
"testing" | ||
) | ||
|
||
const ( | ||
folder = "./test_stl/" | ||
|
||
// The models are copied to the project just to avoid downloading them for each test. | ||
|
||
// 3DBenchy is the unmodified model from here: | ||
// https://www.thingiverse.com/thing:763622 | ||
// using the following license | ||
// https://creativecommons.org/licenses/by-nd/4.0/ | ||
benchy = "3DBenchy.stl" | ||
|
||
// Go Gopher mascot is the unmodified model from here: | ||
// https://www.thingiverse.com/thing:3413597 | ||
// using the following license | ||
// https://creativecommons.org/licenses/by/4.0/ | ||
gopher = "gopher_union.stl" | ||
) | ||
|
||
func TestWholeSlicer(t *testing.T) { | ||
o := data.DefaultOptions() | ||
// enable support so that it is tested also | ||
o.Print.Support.Enabled = true | ||
o.Print.BrimSkirt.BrimCount = 3 | ||
s := NewGoSlice(o) | ||
|
||
var tests = []struct { | ||
path string | ||
}{ | ||
{ | ||
path: benchy, | ||
}, | ||
{ | ||
path: gopher, | ||
}, | ||
} | ||
|
||
for _, testCase := range tests { | ||
t.Log("slice " + testCase.path) | ||
s.Options.InputFilePath = folder + testCase.path | ||
err := s.Process() | ||
test.Ok(t, err) | ||
} | ||
} |