Skip to content

Commit

Permalink
#14 add basic zig zag line support for the infill (not very fast yet...)
Browse files Browse the repository at this point in the history
Also this optimizes the IsCrossingPerimeter method a bit.
  • Loading branch information
aligator committed Jul 7, 2020
1 parent c581a87 commit a763e17
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 26 deletions.
12 changes: 6 additions & 6 deletions clip/clip.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,8 @@ func (c clipperClipper) runClipper(clipType clipper.ClipType, parts []data.Layer
}

func (c clipperClipper) IsCrossingPerimeter(parts []data.LayerPart, line data.Path) (result, ok bool) {
// TODO: iIs there a more performant way to detect this?
cl := clipper.NewClipper(clipper.IoNone)
// TODO: Is there a more performant way to detect this?
cl := clipper.NewClipper(clipper.IoReverseSolution) // inverse solution so that it is basically LINE - PARTS

for _, part := range parts {
cl.AddPaths(clipperPaths(part.Holes()), clipper.PtClip, true)
Expand All @@ -260,13 +260,13 @@ func (c clipperClipper) IsCrossingPerimeter(parts []data.LayerPart, line data.Pa

cl.AddPath(clipperPath(line), clipper.PtSubject, false)

// calculate the intersection of the line and the parts, then look if the result is split into more paths than one.
// If yes, the line crossed a perimeter.
tree, ok := cl.Execute2(clipper.CtIntersection, clipper.PftEvenOdd, clipper.PftEvenOdd)
// calculate the difference of the parts and the line, then look if the (inverted) result contains any left path which would be a line not inside of the parts.
// If any part is left, the line crossed a perimeter.
tree, ok := cl.Execute2(clipper.CtDifference, clipper.PftEvenOdd, clipper.PftEvenOdd)

if !ok {
return false, ok
}

return len(polyTreeToLayerParts(tree)) > 1, true
return tree.Total() > 0, true
}
84 changes: 66 additions & 18 deletions clip/linear.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@ type linear struct {
lineWidth data.Micrometer
degree int
min, max data.MicroPoint
rectlinear bool
zigZag bool
}

// NewLinearPattern provides a simple linear infill pattern consisting of simple parallel lines.
// The direction of the lines is switching for each layer by 90°.
func NewLinearPattern(lineWidth data.Micrometer, lineDistance data.Micrometer, min data.MicroPoint, max data.MicroPoint, degree int) Pattern {
func NewLinearPattern(lineWidth data.Micrometer, lineDistance data.Micrometer, min data.MicroPoint, max data.MicroPoint, degree int, zigZag bool) Pattern {
return linear{
lineDistance: lineDistance,
lineWidth: lineWidth,
degree: degree,
min: min,
max: max,
zigZag: zigZag,
}
}

Expand Down Expand Up @@ -63,35 +66,46 @@ func (p linear) Fill(layerNr int, part data.LayerPart) data.Paths {
bounds.Rotate(rotation)
min, max := bounds.Bounds()

resultInfill := p.getInfill(min, max, clipperPath(outline), clipperPaths(holes), 0)
result := p.sortInfill(microPaths(resultInfill, false))
smallerLines := data.Micrometer(0)
if p.zigZag {
smallerLines = p.lineWidth
}

resultInfill := p.getInfill(min, max, clipperPath(outline), clipperPaths(holes), 0, smallerLines)

result := p.sortInfill(microPaths(resultInfill, false), p.zigZag, data.NewBasicLayerPart(outline, holes))

result.Rotate(-rotation)

return result
}

// sortInfill optimizes the order of the infill lines.
func (p linear) sortInfill(unsorted data.Paths) data.Paths {
func (p linear) sortInfill(unsorted data.Paths, zigZag bool, part data.LayerPart) data.Paths {
if len(unsorted) == 0 {
return unsorted
}

cl := NewClipper()

// Save all sorted paths here.
sorted := data.Paths{unsorted[0]}

// save the amount of lines already saved without the extra zigZag lines
savedPointsNum := 1

// Saves already used indices.
isUsed := make([]bool, len(unsorted))
isUsed[0] = true

// Saves the last path to know where to continue.
lastindex := 0
lastIndex := 0

// Save if the first or second point from the lastPath was the last point.
// Save if the first(0) or second(1) point from the lastPath was the last point.
lastPoint := 0

for len(sorted) < len(unsorted) {
point := unsorted[lastindex][lastPoint]
for savedPointsNum < len(unsorted) {
point := unsorted[lastIndex][lastPoint]

bestIndex := -1
bestDiff := data.Micrometer(-1)
Expand All @@ -113,13 +127,36 @@ func (p linear) sortInfill(unsorted data.Paths) data.Paths {
}

if bestIndex > -1 {
lastindex = bestIndex
sorted = append(sorted, unsorted[lastindex])
lastIndex = bestIndex

if zigZag {
p1 := sorted[len(sorted)-1][1]
p2 := unsorted[lastIndex][1-lastPoint]

if p1.Sub(p2).ShorterThanOrEqual(p.lineWidth + p.lineDistance*2) {

connectionLine := []data.MicroPoint{p1, p2}

isCrossing, ok := cl.IsCrossingPerimeter([]data.LayerPart{part}, connectionLine)

if !ok {
// TODO: return error
panic("could not calculate the difference between the current layer and the non-extrusion-move")
}

if !isCrossing {
sorted = append(sorted, connectionLine)
}
}
}

sorted = append(sorted, unsorted[lastIndex])
savedPointsNum++
isUsed[bestIndex] = true
lastPoint = 1 - lastPoint
} else {
sorted = append(sorted, unsorted[lastindex])
isUsed[lastindex] = true
// should never go here
panic("there should always be a bestIndex > -1")
}

if lastPoint == 1 {
Expand All @@ -130,15 +167,11 @@ func (p linear) sortInfill(unsorted data.Paths) data.Paths {
}
}

if len(sorted) < len(unsorted) {
panic("the sorted lines should have the same amount as the unsorted lines")
}

return sorted
}

// getInfill fills a polygon (with holes)
func (p linear) getInfill(min data.MicroPoint, max data.MicroPoint, outline clipper.Path, holes clipper.Paths, overlap float32) clipper.Paths {
func (p linear) getInfill(min data.MicroPoint, max data.MicroPoint, outline clipper.Path, holes clipper.Paths, overlap float32, smallerLines data.Micrometer) clipper.Paths {
var result clipper.Paths

// clip the paths with the lines using intersection
Expand Down Expand Up @@ -189,7 +222,22 @@ func (p linear) getInfill(min data.MicroPoint, max data.MicroPoint, outline clip
}

for _, c := range tree.Childs() {
result = append(result, c.Contour())
if smallerLines != 0 {
// shorten the lines if smallerLines is set
p1 := c.Contour()[0]
p2 := c.Contour()[1]

// shorten them by the half value on each side
// only do this if the line is bigger than the smallerLines value
if p1.Y-clipper.CInt(smallerLines)/2 > p2.Y {
p1.Y = p1.Y - clipper.CInt(smallerLines)/2
p2.Y = p2.Y + clipper.CInt(smallerLines)/2
}

result = append(result, []*clipper.IntPoint{p1, p2})
} else {
result = append(result, c.Contour())
}
}

return result
Expand Down
4 changes: 4 additions & 0 deletions data/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ func (p Path) Bounds() (MicroPoint, MicroPoint) {

// Rotate rotates all points around (0|0) by the given degree.
func (p Path) Rotate(degree float64) {
if degree == 0 {
return
}

for i, point := range p {
p[i] = point.Rotate(degree)
}
Expand Down
5 changes: 5 additions & 0 deletions data/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ type PrintOptions struct {
// InfillRotationDegree is the rotation used for the infill.
InfillRotationDegree int

// InfillZigZig sets if the infill should use connected lines in zig zag form.
InfillZigZag bool

// NumberBottomLayers is the amount of layers the bottom layers should grow into the model.
NumberBottomLayers int

Expand Down Expand Up @@ -248,6 +251,7 @@ func DefaultOptions() Options {
AdditionalInternalInfillOverlapPercent: 400,
InfillPercent: 20,
InfillRotationDegree: 45,
InfillZigZag: false,
NumberBottomLayers: 3,
NumberTopLayers: 4,
},
Expand Down Expand Up @@ -300,6 +304,7 @@ func ParseFlags() Options {
flag.IntVar(&options.Print.AdditionalInternalInfillOverlapPercent, "additional-internal-infill-overlap-percent", options.Print.AdditionalInternalInfillOverlapPercent, "The percentage used to make the internal infill (infill not blocked by the perimeters) even bigger so that it grows a bit into the model.")
flag.IntVar(&options.Print.InfillPercent, "infill-percent", options.Print.InfillPercent, "The amount of infill which should be generated.")
flag.IntVar(&options.Print.InfillRotationDegree, "infill-rotation-degree", options.Print.InfillRotationDegree, "The rotation used for the infill.")
flag.BoolVar(&options.Print.InfillZigZag, "infill-zig-zag", options.Print.InfillZigZag, "Sets if the infill should use connected lines in zig zag form.")
flag.IntVar(&options.Print.NumberBottomLayers, "number-bottom-layers", options.Print.NumberBottomLayers, "The amount of layers the bottom layers should grow into the model.")
flag.IntVar(&options.Print.NumberTopLayers, "number-top-layers", options.Print.NumberTopLayers, "The amount of layers the bottom layers should grow into the model.")

Expand Down
4 changes: 2 additions & 2 deletions slicer.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func NewGoSlice(options data.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)
return clip.NewLinearPattern(options.Printer.ExtrusionWidth, options.Printer.ExtrusionWidth, min, max, options.Print.InfillRotationDegree, false)
}

s.reader = reader.Reader(&options)
Expand Down Expand Up @@ -71,7 +71,7 @@ func NewGoSlice(options data.Options) *GoSlice {

lineWidth := data.Micrometer(float64(mm10) / linesPer10mmForInfillPercent)

return clip.NewLinearPattern(options.Printer.ExtrusionWidth, lineWidth, min, max, options.Print.InfillRotationDegree)
return clip.NewLinearPattern(options.Printer.ExtrusionWidth, lineWidth, min, max, options.Print.InfillRotationDegree, options.Print.InfillZigZag)
}

return nil
Expand Down

0 comments on commit a763e17

Please sign in to comment.