Exemple #1
0
/*!
 * generate lines within the area of \p in_outline, at regular intervals of \p lineSpacing
 * 
 * idea:
 * intersect a regular grid of 'scanlines' with the area inside \p in_outline
 * 
 * we call the areas between two consecutive scanlines a 'scansegment'.
 * Scansegment x is the area between scanline x and scanline x+1
 * 
 * algorithm:
 * 1) for each line segment of each polygon:
 *      store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
 *      (zigzag): add boundary segments to result
 * 2) for each scanline:
 *      sort the associated intersections 
 *      and connect them using the even-odd rule
 * 
 */
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
{
    if (lineSpacing == 0) return;
    if (in_outline.size() == 0) return;
    Polygons outline = ((outlineOffset)? in_outline.offset(outlineOffset) : in_outline).offset(extrusionWidth * infillOverlap / 100);
    if (outline.size() == 0) return;
    
    PointMatrix matrix(rotation);
    
    outline.applyMatrix(matrix);

    
    AABB boundary(outline);
    
    int scanline_min_idx = boundary.min.X / lineSpacing;
    int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
  
    std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
    
    for(int n=0; n<lineCount; n++)
        cutList.push_back(std::vector<int64_t>());
    
    for(unsigned int poly_idx=0; poly_idx < outline.size(); poly_idx++)
    {
        Point p0 = outline[poly_idx][outline[poly_idx].size()-1];
        for(unsigned int i=0; i < outline[poly_idx].size(); i++)
        {
            Point p1 = outline[poly_idx][i];
            int64_t xMin = p1.X, xMax = p0.X;
            if (xMin == xMax) {
                p0 = p1;
                continue; 
            }
            if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
            
            int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1   ...
            int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
            int direction = 1;
            if (p0.X > p1.X) 
            { 
                direction = -1; 
                scanline_idx1 += 1; // only consider the scanlines in between the scansegments
            } else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
            
            for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
            {
                int x = scanline_idx * lineSpacing;
                int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
                cutList[scanline_idx - scanline_min_idx].push_back(y);
            }
            p0 = p1;
        }
    }
    
    addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
}
Exemple #2
0
void generateLineInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation)
{
    Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100);
    PointMatrix matrix(rotation);
    
    outline.applyMatrix(matrix);
    
    AABB boundary(outline);
    
    boundary.min.X = ((boundary.min.X / lineSpacing) - 1) * lineSpacing;
    int lineCount = (boundary.max.X - boundary.min.X + (lineSpacing - 1)) / lineSpacing;
    vector<vector<int64_t> > cutList;
    for(int n=0; n<lineCount; n++)
        cutList.push_back(vector<int64_t>());

    for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
    {
        Point p1 = outline[polyNr][outline[polyNr].size()-1];
        for(unsigned int i=0; i < outline[polyNr].size(); i++)
        {
            Point p0 = outline[polyNr][i];
            int idx0 = (p0.X - boundary.min.X) / lineSpacing;
            int idx1 = (p1.X - boundary.min.X) / lineSpacing;
            int64_t xMin = p0.X, xMax = p1.X;
            if (p0.X > p1.X) { xMin = p1.X; xMax = p0.X; }
            if (idx0 > idx1) { int tmp = idx0; idx0 = idx1; idx1 = tmp; }
            for(int idx = idx0; idx<=idx1; idx++)
            {
                int x = (idx * lineSpacing) + boundary.min.X + lineSpacing / 2;
                if (x < xMin) continue;
                if (x >= xMax) continue;
                int y = p0.Y + (p1.Y - p0.Y) * (x - p0.X) / (p1.X - p0.X);
                cutList[idx].push_back(y);
            }
            p1 = p0;
        }
    }
    
    int idx = 0;
    for(int64_t x = boundary.min.X + lineSpacing / 2; x < boundary.max.X; x += lineSpacing)
    {
        std::sort(cutList[idx].begin(), cutList[idx].end());
        for(unsigned int i = 0; i + 1 < cutList[idx].size(); i+=2)
        {
            if (cutList[idx][i+1] - cutList[idx][i] < extrusionWidth / 5)
                continue;
            PolygonRef p = result.newPoly();
            p.add(matrix.unapply(Point(x, cutList[idx][i])));
            p.add(matrix.unapply(Point(x, cutList[idx][i+1])));
        }
        idx += 1;
    }
}
Exemple #3
0
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
{
    if (in_outline.size() == 0) return;
    Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
    if (outline.size() == 0) return;
    
    PointMatrix matrix(rotation);
    
    outline.applyMatrix(matrix);
    
    auto addLine = [&](Point from, Point to)
    {            
        PolygonRef p = result.newPoly();
        p.add(matrix.unapply(from));
        p.add(matrix.unapply(to));
    };   
        
    AABB boundary(outline);
    
    int scanline_min_idx = boundary.min.X / lineSpacing;
    int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
    
    std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
    
    for(int n=0; n<lineCount; n++)
        cutList.push_back(std::vector<int64_t>());
    for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
    {
        std::vector<Point> firstBoundarySegment;
        std::vector<Point> boundarySegment;
        
        bool isFirstBoundarySegment = true;
        bool firstBoundarySegmentEndsInEven;
        
        bool isEvenScanSegment = false; 
        
        
        Point p0 = outline[polyNr][outline[polyNr].size()-1];
        for(unsigned int i=0; i < outline[polyNr].size(); i++)
        {
            Point p1 = outline[polyNr][i];
            int64_t xMin = p1.X, xMax = p0.X;
            if (xMin == xMax) {
                p0 = p1;
                continue; 
            }
            if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
            
            int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1   ...
            int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
            int direction = 1;
            if (p0.X > p1.X) 
            { 
                direction = -1; 
                scanline_idx1 += 1; // only consider the scanlines in between the scansegments
            } else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
            
            
            if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
            else boundarySegment.push_back(p0);
            for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
            {
                int x = scanline_idx * lineSpacing;
                int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
                cutList[scanline_idx - scanline_min_idx].push_back(y);
                
                
                bool last_isEvenScanSegment = isEvenScanSegment;
                if (scanline_idx % 2 == 0) isEvenScanSegment = true;
                else isEvenScanSegment = false;
                
                if (!isFirstBoundarySegment)
                {
                    if (last_isEvenScanSegment && !isEvenScanSegment)
                    { // add whole boundarySegment (including the just obtained point)
                        for (unsigned int p = 1; p < boundarySegment.size(); p++)
                        {
                            addLine(boundarySegment[p-1], boundarySegment[p]);
                        }
                        addLine(boundarySegment[boundarySegment.size()-1], Point(x,y));
                        boundarySegment.clear();
                    } 
                    else if (isEvenScanSegment) // we are either in an end piece or an uneven boundary segment
                    {
                        boundarySegment.clear();
                        boundarySegment.emplace_back(x,y);
                    } else
                        boundarySegment.clear();
                        
                }
                
                if (isFirstBoundarySegment) 
                {
                    firstBoundarySegment.emplace_back(x,y);
                    firstBoundarySegmentEndsInEven = isEvenScanSegment;
                    isFirstBoundarySegment = false;
                    boundarySegment.emplace_back(x,y);
                }
                
            }
            if (!isFirstBoundarySegment && isEvenScanSegment)
                boundarySegment.push_back(p1);
            
            
            p0 = p1;
        }
        
        if (!isFirstBoundarySegment && isEvenScanSegment && !firstBoundarySegmentEndsInEven)
        {
            for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
                addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
        }
    } 
    

    addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);

}
Exemple #4
0
/*!
 * adapted from generateLineInfill(.)
 * 
 * generate lines within the area of [in_outline], at regular intervals of [lineSpacing]
 * idea:
 * intersect a regular grid of 'scanlines' with the area inside [in_outline]
 * sigzag:
 * include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines    |_|^|_|
 * 
 * we call the areas between two consecutive scanlines a 'scansegment'
 * 
 * algorithm:
 * 1. for each line segment of each polygon:
 *      store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
 *      (zigzag): add boundary segments to result
 * 2. for each scanline:
 *      sort the associated intersections 
 *      and connect them using the even-odd rule
 * 
 * zigzag algorithm:
 * while walking around (each) polygon (1.)
 *  if polygon intersects with even scanline
 *      start boundary segment (add each following segment to the [result])
 *  when polygon intersects with a scanline again
 *      stop boundary segment (stop adding segments to the [result])
 *      if polygon intersects with even scanline again (instead of odd)
 *          dont add the last line segment to the boundary (unless [connect_zigzags])
 * 
 * 
 *     <--
 *     ___
 *    |   |   |
 *    |   |   |
 *    |   |___|
 *         -->
 * 
 *        ^ = even scanline
 * 
 * start boundary from even scanline! :D
 * 
 * 
 *          _____
 *   |     |     | ,
 *   |     |     |  |
 *   |_____|     |__/
 * 
 *   ^     ^     ^    scanlines
 *                 ^  disconnected end piece
 */
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags)
{
//     if (in_outline.size() == 0) return;
//     Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
    Polygons empty;
    Polygons outline = in_outline.difference(empty); // copy
    if (outline.size() == 0) return;
    
    PointMatrix matrix(rotation);
    
    outline.applyMatrix(matrix);
    
    auto addLine = [&](Point from, Point to)
    {            
        PolygonRef p = result.newPoly();
        p.add(matrix.unapply(from));
        p.add(matrix.unapply(to));
    };   
        
    AABB boundary(outline);
    
    int scanline_min_idx = boundary.min.X / lineSpacing;
    int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
    
    std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
    
    for(int n=0; n<lineCount; n++)
        cutList.push_back(std::vector<int64_t>());
    for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
    {
        std::vector<Point> firstBoundarySegment;
        std::vector<Point> unevenBoundarySegment; // stored cause for connected_zigzags a boundary segment which ends in an uneven scanline needs to be included
        
        bool isFirstBoundarySegment = true;
        bool firstBoundarySegmentEndsInEven;
        
        bool isEvenScanSegment = false; 
        
        
        Point p0 = outline[polyNr][outline[polyNr].size()-1];
        Point lastPoint = p0;
        for(unsigned int i=0; i < outline[polyNr].size(); i++)
        {
            Point p1 = outline[polyNr][i];
            int64_t xMin = p1.X, xMax = p0.X;
            if (xMin == xMax) {
                lastPoint = p1;
                p0 = p1;
                continue; 
            }
            if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
            
            int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1   ...
            int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
            int direction = 1;
            if (p0.X > p1.X) 
            { 
                direction = -1; 
                scanline_idx1 += 1; // only consider the scanlines in between the scansegments
            } else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
            
            
            if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
            for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
            {
                int x = scanline_idx * lineSpacing;
                int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
                cutList[scanline_idx - scanline_min_idx].push_back(y);
                
                
                bool last_isEvenScanSegment = isEvenScanSegment;
                if (scanline_idx % 2 == 0) isEvenScanSegment = true;
                else isEvenScanSegment = false;
                
                if (!isFirstBoundarySegment)
                {
                    if (last_isEvenScanSegment && (connect_zigzags || !isEvenScanSegment))
                        addLine(lastPoint, Point(x,y));
                    else if (connect_zigzags && !last_isEvenScanSegment && !isEvenScanSegment) // if we end an uneven boundary in an uneven segment
                    { // add whole unevenBoundarySegment (including the just obtained point)
                        for (unsigned int p = 1; p < unevenBoundarySegment.size(); p++)
                        {
                            addLine(unevenBoundarySegment[p-1], unevenBoundarySegment[p]);
                        }
                        addLine(unevenBoundarySegment[unevenBoundarySegment.size()-1], Point(x,y));
                        unevenBoundarySegment.clear();
                    } 
                    if (connect_zigzags && last_isEvenScanSegment && !isEvenScanSegment)
                        unevenBoundarySegment.push_back(Point(x,y));
                    else 
                        unevenBoundarySegment.clear();
                        
                }
                lastPoint = Point(x,y);
                
                if (isFirstBoundarySegment) 
                {
                    firstBoundarySegment.emplace_back(x,y);
                    firstBoundarySegmentEndsInEven = isEvenScanSegment;
                    isFirstBoundarySegment = false;
                }
                
            }
            if (!isFirstBoundarySegment)
            {
                if (isEvenScanSegment)
                    addLine(lastPoint, p1);
                else if (connect_zigzags)
                    unevenBoundarySegment.push_back(p1);
            }
            
            lastPoint = p1;
            p0 = p1;
        }
        
        if (isEvenScanSegment || isFirstBoundarySegment || connect_zigzags)
        {
            for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
            {
                if (i < firstBoundarySegment.size() - 1 || !firstBoundarySegmentEndsInEven || connect_zigzags) // only add last element if connect_zigzags or boundary segment ends in uneven scanline
                    addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
            }   
        }
        else if (!firstBoundarySegmentEndsInEven)
            addLine(firstBoundarySegment[firstBoundarySegment.size()-2], firstBoundarySegment[firstBoundarySegment.size()-1]);
    } 
    
    if (cutList.size() == 0) return;
    if (connect_zigzags && cutList.size() == 1 && cutList[0].size() <= 2) return;  // don't add connection if boundary already contains whole outline!
    
    addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
}
Exemple #5
0
  void generateTroctInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, int posZ)
  {
    Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100);
    PointMatrix matrix(rotation);
    outline.applyMatrix(matrix);
    AABB boundary(outline);

    // ignore infill for areas smaller than line spacing
    if((abs(boundary.min.X - boundary.max.X) + abs(boundary.min.Y - boundary.max.Y)) < lineSpacing){
      return;
    }

    // fix to normalise against diagonal infill
    lineSpacing = lineSpacing * 2;

    uint64_t Zscale = SQRT2MUL(lineSpacing);

    int offset = abs(posZ % (Zscale) - (Zscale/2)) - (Zscale/4);
    boundary.min.X = ((boundary.min.X / lineSpacing) - 1) * lineSpacing;
    boundary.min.Y = ((boundary.min.Y / lineSpacing) - 1) * lineSpacing;

    unsigned int lineCountX = (boundary.max.X - boundary.min.X + (lineSpacing - 1)) / lineSpacing;
    unsigned int lineCountY = (boundary.max.Y - boundary.min.Y + (lineSpacing - 1)) / lineSpacing;
    int rtMod = int(rotation / 90) % 2;
    // with an odd number of lines, sides need to be swapped around
    if(rtMod == 1){
      rtMod = (lineCountX + int(rotation / 90)) % 2;
    }

    // draw non-horizontal walls of octohedrons
    Polygons po;
    PolygonRef p = po.newPoly();
    for(unsigned int ly=0; ly < lineCountY;){
      for(size_t it = 0; it < 2; ly++, it++){
        int side = (2*((ly + it + rtMod) % 2) - 1);
        int y = (ly * lineSpacing) + boundary.min.Y + lineSpacing / 2 - (offset/2 * side);
        int x = boundary.min.X-(offset/2);
        if(it == 1){
          x = (lineCountX * (lineSpacing)) + boundary.min.X + lineSpacing / 2 - (offset/2);
        }
        p.add(Point(x,y));
        for(unsigned int lx=0; lx < lineCountX; lx++){
          if(it == 1){
            side = (2*((lx + ly + it + rtMod + lineCountX) % 2) - 1);
            y = (ly * lineSpacing) + boundary.min.Y + lineSpacing / 2 + (offset/2 * side);
            x = ((lineCountX - lx - 1) * lineSpacing) + boundary.min.X + lineSpacing / 2;
            p.add(Point(x+lineSpacing-abs(offset/2), y));
            p.add(Point(x+abs(offset/2), y));
          } else {
            side = (2*((lx + ly + it + rtMod) % 2) - 1);
            y = (ly * lineSpacing) + boundary.min.Y + lineSpacing / 2 + (offset/2 * side);
            x = (lx * lineSpacing) + boundary.min.X + lineSpacing / 2;
            p.add(Point(x+abs(offset/2), y));
            p.add(Point(x+lineSpacing-abs(offset/2), y));
          }
        }
        x = (lineCountX * lineSpacing) + boundary.min.X + lineSpacing / 2 - (offset/2);
        if(it == 1){
          x = boundary.min.X-(offset/2);
        }
        y = (ly * lineSpacing) + boundary.min.Y + lineSpacing / 2 - (offset/2 * side);
        p.add(Point(x,y));
      }
    }
    // Generate tops / bottoms of octohedrons
    if(abs((abs(offset) - Zscale/4)) < (extrusionWidth/2)){
      uint64_t startLine = (offset < 0) ? 0 : 1;
      uint64_t coverWidth = OCTSLEN(lineSpacing);
      vector<Point> points;
      for(size_t xi = 0; xi < (lineCountX+1); xi++){
        for(size_t yi = 0; yi < (lineCountY); yi += 2){
          points.push_back(Point(boundary.min.X + OCTDLEN(lineSpacing)
                                 + (xi - startLine + rtMod) * lineSpacing,
                                 boundary.min.Y + OCTDLEN(lineSpacing)
                                 + (yi + (xi%2)) * lineSpacing
                                 + extrusionWidth/2));
        }
      }
      uint64_t order = 0;
      for(Point pp : points){
        PolygonRef p = po.newPoly();
        for(size_t yi = 0; yi <= coverWidth; yi += extrusionWidth) {
          if(order == 0){
            p.add(Point(pp.X, pp.Y + yi));
            p.add(Point(pp.X + coverWidth + extrusionWidth, pp.Y + yi));
          } else {
            p.add(Point(pp.X + coverWidth + extrusionWidth, pp.Y + yi));
            p.add(Point(pp.X, pp.Y + yi));
          }
          order = (order + 1) % 2;
        }
      }
    }
    // intersect with outline polygon(s)
    Polygons pi = po.intersection(outline);
    // Hack to add intersection to result. There doesn't seem
    // to be a direct way to do this
    for(unsigned int polyNr=0; polyNr < pi.size(); polyNr++) {
      PolygonRef p = result.newPoly(); //  = result.newPoly()
      for(unsigned int i=0; i < pi[polyNr].size(); i++) {
        Point p0 = pi[polyNr][i];
        p.add(matrix.unapply(Point(p0.X,p0.Y)));
      }
    }
}
Exemple #6
0
/* 
 * algorithm:
 * 1. for each line segment of each polygon:
 *      store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
 *      (zigzag): add boundary segments to result
 * 2. for each scanline:
 *      sort the associated intersections 
 *      and connect them using the even-odd rule
 * 
 * rough explanation of the zigzag algorithm:
 * while walking around (each) polygon (1.)
 *  if polygon intersects with even scanline
 *      start boundary segment (add each following segment to the [result])
 *  when polygon intersects with a scanline again
 *      stop boundary segment (stop adding segments to the [result])
 *  (see infill/ZigzagConnectorProcessor.h for actual implementation details)
 * 
 * 
 * we call the areas between two consecutive scanlines a 'scansegment'.
 * Scansegment x is the area between scanline x and scanline x+1
 * Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment, 
 *  while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment.
 */
void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, int64_t extra_shift)
{
    if (line_distance == 0)
    {
        return;
    }
    if (in_outline.size() == 0)
    {
        return;
    }

    int shift = extra_shift + this->shift;

    Polygons outline;
    if (outline_offset != 0)
    {
        outline = in_outline.offset(outline_offset);
        if (perimeter_gaps)
        {
            perimeter_gaps->add(in_outline.difference(outline.offset(infill_line_width / 2 + perimeter_gaps_extra_offset)));
        }
    }
    else
    {
        outline = in_outline;
    }

    outline = outline.offset(infill_overlap);

    if (outline.size() == 0)
    {
        return;
    }

    outline.applyMatrix(rotation_matrix);

    if (shift < 0)
    {
        shift = line_distance - (-shift) % line_distance;
    }
    else
    {
        shift = shift % line_distance;
    }

    AABB boundary(outline);

    int scanline_min_idx = computeScanSegmentIdx(boundary.min.X - shift, line_distance);
    int line_count = computeScanSegmentIdx(boundary.max.X - shift, line_distance) + 1 - scanline_min_idx;

    std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments

    for(int scanline_idx = 0; scanline_idx < line_count; scanline_idx++)
    {
        cut_list.push_back(std::vector<int64_t>());
    }

    for(unsigned int poly_idx = 0; poly_idx < outline.size(); poly_idx++)
    {
        PolygonRef poly = outline[poly_idx];
        Point p0 = poly.back();
        zigzag_connector_processor.registerVertex(p0); // always adds the first point to ZigzagConnectorProcessorEndPieces::first_zigzag_connector when using a zigzag infill type
        for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
        {
            Point p1 = poly[point_idx];
            if (p1.X == p0.X)
            {
                zigzag_connector_processor.registerVertex(p1); 
                // TODO: how to make sure it always adds the shortest line? (in order to prevent overlap with the zigzag connectors)
                // note: this is already a problem for normal infill, but hasn't really cothered anyone so far.
                p0 = p1;
                continue; 
            }

            int scanline_idx0;
            int scanline_idx1;
            // this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
            // in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
            // otherwise only 1 will be created, counting as an actual intersection
            int direction = 1;
            if (p0.X < p1.X) 
            { 
                scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment
                scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance); // -1 cause the vertex point is handled in the next segment (or not in the case which looks like >)
            }
            else
            {
                direction = -1; 
                scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance); // -1 cause the vertex point is handled in the previous segment (or not in the case which looks like >)
                scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment
            }

            for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction)
            {
                int x = scanline_idx * line_distance + shift;
                int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
                assert(scanline_idx - scanline_min_idx >= 0 && scanline_idx - scanline_min_idx < int(cut_list.size()) && "reading infill cutlist index out of bounds!");
                cut_list[scanline_idx - scanline_min_idx].push_back(y);
                Point scanline_linesegment_intersection(x, y);
                zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0);
            }
            zigzag_connector_processor.registerVertex(p1);
            p0 = p1;
        }
        zigzag_connector_processor.registerPolyFinished();
    }

    if (cut_list.size() == 0)
    {
        return;
    }
    if (connected_zigzags && cut_list.size() == 1 && cut_list[0].size() <= 2)
    {
        return;  // don't add connection if boundary already contains whole outline!
    }

    addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list, shift);
}