コード例 #1
0
ファイル: ClipperUtils.cpp プロジェクト: prusa3d/Slic3r
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input)
{
    ClipperLib::Paths retval;
    for (Polylines::const_iterator it = input.begin(); it != input.end(); ++it)
        retval.emplace_back(Slic3rMultiPoint_to_ClipperPath(*it));
    return retval;
}
コード例 #2
0
ファイル: FillGyroid.cpp プロジェクト: prusa3d/Slic3r
static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
{
    const double scaleFactor = scale_(line_spacing) / density_adjusted;
 //scale factor for 5% : 8 712 388
 // 1z = 10^-6 mm ?
    const double z     = gridZ / scaleFactor;
    const double z_sin = sin(z);
    const double z_cos = cos(z);

    bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
    double lower_bound = 0.;
    double upper_bound = height;
    bool flip = true;
    if (vertical) {
        flip = false;
        lower_bound = -M_PI;
        upper_bound = width - M_PI_2;
        std::swap(width,height);
    }

    std::vector<Vec2d> one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time
    Polylines result;

    for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI)           // creates odd polylines
            result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));

    flip = !flip;                                                                   // even polylines are a bit shifted
    one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // updates the one period sample
    for (double y0 = lower_bound + M_PI; y0 < upper_bound+EPSILON; y0 += 2*M_PI)    // creates even polylines
            result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));

    return result;
}
コード例 #3
0
ファイル: ExtrusionEntity.hpp プロジェクト: jiripech/Slic3r
inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
    dst.reserve(dst.size() + polylines.size());
    for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
        ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height);
        dst.push_back(extrusion_path);
        extrusion_path->polyline = *it_polyline;
    }
}
コード例 #4
0
ファイル: ExtrusionEntity.cpp プロジェクト: alexrj/Slic3r
void
ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const
{
    for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) {
        ExtrusionPath* path = this->clone();
        path->polyline = *it;
        collection->entities.push_back(path);
    }
}
コード例 #5
0
ファイル: PolylineCollection.cpp プロジェクト: alexrj/Slic3r
Polylines PolylineCollection::chained_path(const Polylines &src, bool no_reverse)
{
    return (src.empty() || src.front().points.empty()) ?
        Polylines() :
        _chained_path_from(src, src.front().first_point(), no_reverse
#if SLIC3R_CPPVER >= 11
        , false
#endif
    );
}
コード例 #6
0
ファイル: PolylineCollection.cpp プロジェクト: alexrj/Slic3r
Point PolylineCollection::leftmost_point(const Polylines &polylines)
{
    if (polylines.empty()) CONFESS("leftmost_point() called on empty PolylineCollection");
    Polylines::const_iterator it = polylines.begin();
    Point p = it->leftmost_point();
    for (++ it; it != polylines.end(); ++it) {
        Point p2 = it->leftmost_point();
        if (p2.x < p.x) 
            p = p2;
    }
    return p;
}
コード例 #7
0
ファイル: Polygon.hpp プロジェクト: jiripech/Slic3r
inline Polylines to_polylines(Polygons &&polys)
{
    Polylines polylines;
    polylines.assign(polys.size(), Polyline());
    size_t idx = 0;
    for (Polygons::const_iterator it = polys.begin(); it != polys.end(); ++ it) {
        Polyline &pl = polylines[idx ++];
        pl.points = std::move(it->points);
        pl.points.push_back(it->points.front());
    }
    assert(idx == polylines.size());
    return polylines;
}
コード例 #8
0
ファイル: PerimeterGenerator.cpp プロジェクト: 2bright/Slic3r
ExtrusionEntityCollection
PerimeterGenerator::_fill_gaps(double min, double max, double w,
    const Polygons &gaps) const
{
    ExtrusionEntityCollection coll;
    
    min *= (1 - INSET_OVERLAP_TOLERANCE);
    
    ExPolygons curr = diff_ex(
        offset2(gaps, -min/2, +min/2),
        offset2(gaps, -max/2, +max/2),
        true
    );
    
    Polylines polylines;
    for (ExPolygons::const_iterator ex = curr.begin(); ex != curr.end(); ++ex)
        ex->medial_axis(max, min/2, &polylines);
    if (polylines.empty())
        return coll;
    
    #ifdef SLIC3R_DEBUG
    if (!curr.empty())
        printf("  %zu gaps filled with extrusion width = %f\n", curr.size(), w);
    #endif
    
    //my $flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL, 0, $w);
    Flow flow(
        w, this->layer_height, this->solid_infill_flow.nozzle_diameter
    );
    
    double mm3_per_mm = flow.mm3_per_mm();
    
    for (Polylines::const_iterator p = polylines.begin(); p != polylines.end(); ++p) {
        ExtrusionPath path(erGapFill);
        path.polyline   = *p;
        path.mm3_per_mm = mm3_per_mm;
        path.width      = flow.width;
        path.height     = this->layer_height;
        
        if (p->is_valid() && p->first_point().coincides_with(p->last_point())) {
            // since medial_axis() now returns only Polyline objects, detect loops here
            ExtrusionLoop loop;
            loop.paths.push_back(path);
            coll.append(loop);
        } else {
            coll.append(path);
        }
    }
    
    return coll;
}
コード例 #9
0
ファイル: ExPolygon.hpp プロジェクト: jiripech/Slic3r
inline Polylines to_polylines(ExPolygon &&src)
{
    Polylines polylines;
    polylines.assign(src.holes.size() + 1, Polyline());
    size_t idx = 0;
    Polyline &pl = polylines[idx ++];
    pl.points = std::move(src.contour.points);
    pl.points.push_back(pl.points.front());
    for (Polygons::const_iterator ith = src.holes.begin(); ith != src.holes.end(); ++ith) {
        Polyline &pl = polylines[idx ++];
        pl.points = std::move(ith->points);
        pl.points.push_back(ith->points.front());
    }
    assert(idx == polylines.size());
    return polylines;
}
コード例 #10
0
Polylines PolylineCollection::_chained_path_from(
    const Polylines &src,
    Point start_near,
    bool  no_reverse, 
    bool  move_from_src)
{
    std::vector<Chaining> endpoints;
    endpoints.reserve(src.size());
    for (size_t i = 0; i < src.size(); ++ i) {
        Chaining c;
        c.first = src[i].first_point();
        if (! no_reverse)
            c.last = src[i].last_point();
        c.idx = i;
        endpoints.push_back(c);
    }
    Polylines retval;
    while (! endpoints.empty()) {
        // find nearest point
        int endpoint_index = nearest_point_index<double>(endpoints, start_near, no_reverse);
        assert(endpoint_index >= 0 && endpoint_index < endpoints.size() * 2);
        if (move_from_src) {
            retval.push_back(std::move(src[endpoints[endpoint_index/2].idx]));
        } else {
            retval.push_back(src[endpoints[endpoint_index/2].idx]);
        }
        if (endpoint_index & 1)
            retval.back().reverse();
        endpoints.erase(endpoints.begin() + endpoint_index/2);
        start_near = retval.back().last_point();
    }
    return retval;
}
コード例 #11
0
ファイル: ExPolygon.hpp プロジェクト: jiripech/Slic3r
inline Polylines to_polylines(const ExPolygons &src)
{
    Polylines polylines;
    polylines.assign(number_polygons(src), Polyline());
    size_t idx = 0;
    for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++it) {
        Polyline &pl = polylines[idx ++];
        pl.points = it->contour.points;
        pl.points.push_back(pl.points.front());
        for (Polygons::const_iterator ith = it->holes.begin(); ith != it->holes.end(); ++ith) {
            Polyline &pl = polylines[idx ++];
            pl.points = ith->points;
            pl.points.push_back(ith->points.front());
        }
    }
    assert(idx == polylines.size());
    return polylines;
}
コード例 #12
0
int main()
{
  // Domain (Warning: Sphere_3 constructor uses squared radius !)
  Mesh_domain domain(sphere_function,
                     K::Sphere_3(Point(1, 0, 0), 6.));

  // Mesh criteria
  Mesh_criteria criteria(edge_size = 0.15,
                         facet_angle = 25, facet_size = 0.15,
                         cell_radius_edge_ratio = 2, cell_size = 0.15);
  
  // Create edge that we want to preserve
  Polylines polylines (1);
  Polyline_3& polyline = polylines.front();
  
  for(int i = 0; i < 360; ++i)
  {
    Point p (1, std::cos(i*CGAL_PI/180), std::sin(i*CGAL_PI/180));
    polyline.push_back(p);
  }
  polyline.push_back(polyline.front()); // close the line

  // Insert edge in domain
  domain.add_features(polylines.begin(), polylines.end());
  
  // Mesh generation without feature preservation
  C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria,
                                      CGAL::parameters::no_features());

  std::ofstream medit_file("out-no-protection.mesh");
  c3t3.output_to_medit(medit_file);
  medit_file.close();
  c3t3.clear();

  // Mesh generation with feature preservation
  c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria);
  
  // Output
  medit_file.open("out-with-protection.mesh");
  c3t3.output_to_medit(medit_file);
  medit_file.close();

  return 0;
}
コード例 #13
0
ファイル: ClipperUtils.cpp プロジェクト: prusa3d/Slic3r
Lines
_clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons &clip,
    bool safety_offset_)
{
    // convert Lines to Polylines
    Polylines polylines;
    polylines.reserve(subject.size());
    for (const Line &line : subject)
        polylines.emplace_back(Polyline(line.a, line.b));
    
    // perform operation
    polylines = _clipper_pl(clipType, polylines, clip, safety_offset_);
    
    // convert Polylines to Lines
    Lines retval;
    for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline)
        retval.emplace_back(polyline->operator Line());
    return retval;
}
コード例 #14
0
ファイル: ClipperUtils.cpp プロジェクト: alpha6/Slic3r
Lines
_clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons &clip,
    bool safety_offset_)
{
    // convert Lines to Polylines
    Polylines polylines;
    polylines.reserve(subject.size());
    for (Lines::const_iterator line = subject.begin(); line != subject.end(); ++line)
        polylines.push_back(*line);
    
    // perform operation
    polylines = _clipper_pl(clipType, polylines, clip, safety_offset_);
    
    // convert Polylines to Lines
    Lines retval;
    for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline)
        retval.push_back(*polyline);
    return retval;
}
コード例 #15
0
int main()
{
  // Define functions
  Function f1 = cube_function_1;
  Function f2 = cube_function_2;

  Function_vector v;
  v.push_back(f1);
  v.push_back(f2);

  std::vector<std::string> vps;
  vps.push_back("--");

  // Domain (Warning: Sphere_3 constructor uses square radius !)
  Mesh_domain_with_features domain(Function_wrapper(v, vps), K::Sphere_3(CGAL::ORIGIN, 5.*5.));
  Polylines polylines;
  create_polylines(polylines);
  domain.add_features(polylines.begin(),polylines.end());

  // Set mesh criteria
  Mesh_criteria criteria(edge_size = 0.15,
      facet_angle = 30, facet_size = 0.2,
      cell_radius_edge_ratio = 2, cell_size = 0.4);

  // Mesh generation
  C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria, no_exude(), no_perturb());

  // Perturbation (maximum cpu time: 10s, targeted dihedral angle: default)
  CGAL::perturb_mesh_3(c3t3, domain, time_limit = 10);
  
  // Exudation
  CGAL::exude_mesh_3(c3t3,12);
  
  // Output
  std::ofstream medit_file("out_cubes_intersection_with_features.mesh");
  CGAL::output_to_medit(medit_file, c3t3);

  return 0;
}
コード例 #16
0
ファイル: FillGyroid.cpp プロジェクト: Sebastianv650/Slic3r
static Polylines make_gyroid_waves(double gridZ, double density, double layer_width, double width, double height)
{
    double scaleFactor = scale_(layer_width) / density;
    double segmentSize = 0.5 * density;
 //scale factor for 5% : 8 712 388
 // 1z = 10^-6 mm ?
    double z     = gridZ / scaleFactor;
    double z_sin = sin(z);
    double z_cos = cos(z);
    Polylines result;
    if (abs(z_sin) <= abs(z_cos)) {
        // Vertical wave
        double x0 = M_PI * (int)((- 0.5 * M_PI) / M_PI - 1.);
        bool   flip          = ((int)(x0 / M_PI + 1.) & 1) != 0;
        for (; x0 < width - 0.5 * M_PI; x0 += M_PI, flip = ! flip)
            result.emplace_back(make_wave_vertical(width, height, x0, segmentSize, scaleFactor, z_cos, z_sin, flip));
    } else {
        // Horizontal wave
        bool flip = true;
        for (double y0 = 0.; y0 < width; y0 += M_PI, flip = !flip)
            result.emplace_back(make_wave_horizontal(width, height, y0, segmentSize, scaleFactor, z_cos, z_sin, flip));
    }
    return result;
}
コード例 #17
0
ファイル: Octree2.cpp プロジェクト: edwardsjohnmartin/pgvd
void Octree2::build(const Polylines& lines,
                    const BoundingBox<float2>* customBB) {
  using namespace std;

  //------------------
  // Initialize OpenCL
  //------------------
// #ifdef __OPEN_CL_SUPPORT__
//   static bool initialized = false;
//   if (options.gpu && !initialized) {
//     OpenCLInit(2, o, options.opencl_log);
//     initialized = true;
//   }
// #endif

  karras_points.clear();
  bb = BoundingBox<float2>();
  extra_qpoints.clear();
  octree.clear();

  const vector<vector<float2>>& polygons = lines.getPolygons();
  if (polygons.empty()) {
    buildOctVertices();
    return;
  }

  // Get all vertices into a 1D array (karras_points).
  for (int i = 0; i < polygons.size(); ++i) {
    const vector<float2>& polygon = polygons[i];
    for (int j = 0; j < polygon.size()-1; ++j) {
      karras_points.push_back(polygon[j]);
    }
    karras_points.push_back(polygon.back());
  }

  // Compute bounding box
  if (customBB) {
    bb = *customBB;
  } else {
    for (int i = 0; i < karras_points.size(); ++i) {
      bb(karras_points[i]);
    }
  }
  
  // Karras iterations
  vector<intn> qpoints = Karras::Quantize(karras_points, resln);
  int iterations = 0;
  do {
    qpoints.insert(qpoints.end(), extra_qpoints.begin(), extra_qpoints.end());
    for (const intn& qp : extra_qpoints) {
      karras_points.push_back(oct2Obj(qp));
    }
    extra_qpoints.clear();
    if (qpoints.size() > 1) {
      octree = Karras::BuildOctreeInParallel(qpoints, resln, true);
    }
    else {
      octree.clear();
    }
    //FindMultiCells(lines);

    ++iterations;
  } while (iterations < options.karras_iterations && !extra_qpoints.empty());
  //cout << "Karras iterations: " << iterations << endl;

  // Count the number of cells with multiple intersections
  int count = 0;
  for (int i = 0; i < cell_intersections.size(); ++i) {
    for (int j = 0; j < 4; ++j) {
      if (cell_intersections[i].is_multi(j)) {
        ++count;
      }
    }
  }
  //cout << "Number of multi-intersection cells: " << count << endl;

  // todo: setup vertices on GPU for rendering
  buildOctVertices();
  
  //------------------
  // Cleanup OpenCL
  //------------------
// #ifdef __OPEN_CL_SUPPORT__
//   if (options.gpu) {
//     OpenCLCleanup();
//   }
// #endif
}
コード例 #18
0
ファイル: FillGyroid.cpp プロジェクト: Sebastianv650/Slic3r
void FillGyroid::_fill_surface_single(
    const FillParams                &params, 
    unsigned int                     thickness_layers,
    const std::pair<float, Point>   &direction, 
    ExPolygon                       &expolygon, 
    Polylines                       &polylines_out)
{
    // no rotation is supported for this infill pattern
    BoundingBox bb = expolygon.contour.bounding_box();
    coord_t     distance = coord_t(scale_(this->spacing) / (params.density*this->scaling));

    // align bounding box to a multiple of our grid module
    bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
    
    // generate pattern
    Polylines   polylines = make_gyroid_waves(
        scale_(this->z),
        params.density*this->scaling,
        this->spacing,
        ceil(bb.size().x / distance) + 1.,
        ceil(bb.size().y / distance) + 1.);
    
    // move pattern in place
    for (Polyline &polyline : polylines)
        polyline.translate(bb.min.x, bb.min.y);

    // clip pattern to boundaries
    polylines = intersection_pl(polylines, (Polygons)expolygon);

    // connect lines
    if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
        ExPolygon expolygon_off;
        {
            ExPolygons expolygons_off = offset_ex(expolygon, (float)SCALED_EPSILON);
            if (! expolygons_off.empty()) {
                // When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
                assert(expolygons_off.size() == 1);
                std::swap(expolygon_off, expolygons_off.front());
            }
        }
        Polylines chained = PolylineCollection::chained_path_from(
            std::move(polylines), 
            PolylineCollection::leftmost_point(polylines), false); // reverse allowed
        bool first = true;
        for (Polyline &polyline : chained) {
            if (! first) {
                // Try to connect the lines.
                Points &pts_end = polylines_out.back().points;
                const Point &first_point = polyline.points.front();
                const Point &last_point = pts_end.back();
                // TODO: we should also check that both points are on a fill_boundary to avoid 
                // connecting paths on the boundaries of internal regions
                // TODO: avoid crossing current infill path
                if (first_point.distance_to(last_point) <= 5 * distance && 
                    expolygon_off.contains(Line(last_point, first_point))) {
                    // Append the polyline.
                    pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
                    continue;
                }
            }
            // The lines cannot be connected.
            polylines_out.emplace_back(std::move(polyline));
            first = false;
        }
    }
}
コード例 #19
0
ファイル: SVG.cpp プロジェクト: 2bright/Slic3r
void
SVG::draw(const Polylines &polylines, std::string stroke)
{
    for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
        this->draw(*it, fill);
}
コード例 #20
0
ファイル: Octree2.cpp プロジェクト: edwardsjohnmartin/pgvd
void Octree2::FindMultiCells(const Polylines& lines) {
  using namespace Karras;

  cell_intersections.clear();
  cell_intersections.resize(octree.size(), CellIntersections());
  MCData data(cell_intersections);

  const vector<vector<float2>>& polygons = lines.getPolygons();;

  // For each line segment in each polygon do a cell walk, updating
  // the cell_intersections structure.
  intersections.clear();
  for (int j = 0; j < polygons.size(); ++j) {
    const std::vector<float2>& polygon = polygons[j];
    data.cur_label = j;
    for (int i = 0; i < polygon.size() - 1; ++i) {
      const floatn a = obj2Oct(polygon[i]);
      const floatn b = obj2Oct(polygon[i+1]);
      // Visit each octree cell intersected by segment a-b. The visitor
      // is MCCallback.
      CellWalk(a, b, octree, resln, MCCallback, &data);
      vector<OctreeUtils::CellIntersection> local = Walk(a, b);
      for (const OctreeUtils::CellIntersection& ci : local) {
        intersections.push_back(ci.p);
      }
    }
  }

  // Find points that we want to add in order to run a more effective
  // Karras octree construction.
  extra_qpoints.clear();
  _origins.clear();
  _lengths.clear();
  for (int i = 0; i < octree.size(); ++i) {
    const CellIntersections& intersection = cell_intersections[i];
    for (int octant = 0; octant < (1<<DIM); ++octant) {
      const int num_labels = intersection.num_labels(octant);
      // cout << "cell = " << i << "/" << octant << ": " << num_labels << endl;
      // if (intersection.is_multi(octant)) {
      //   {
      for (int j = 0; j < num_labels; ++j) {
      // for (int j = 0; j < 1; ++j) {
        for (int k = j+1; k < num_labels; ++k) {
        // for (int k = j+1; k < std::min(2, num_labels); ++k) {
          // We'll compare segments at j and k.
          FloatSegment segs[2] = { intersection.seg(j, octant),
                                intersection.seg(k, octant) };
          vector<floatn> samples;
          vector<floatn> origins;
          vector<float> lengths;
          if (!Geom::multi_intersection(segs[0], segs[1])) {
            try {
              if (i == fnode.get_parent_idx() && octant == fnode.get_octant()) {
                cout << "here in FitBoxes" << endl;
                write_seg(segs[0], "seg0.dat");
                write_seg(segs[1], "seg1.dat");
              }
              Geom::FitBoxes(segs[0], segs[1], 1, &samples, &origins, &lengths);
            } catch(logic_error& e) {
              cerr << "segments: " << segs[0] << " " << segs[1] << endl;
              cerr << "labels: " << intersection.label(0, octant)
                   << " " << intersection.label(1, octant) << endl;
              write_seg(segs[0], "seg0.dat");
              write_seg(segs[1], "seg1.dat");
              throw e;
            }
          }
          _origins.insert(_origins.end(), origins.begin(), origins.end());
          _lengths.insert(_lengths.end(), lengths.begin(), lengths.end());
          for (const floatn& sample : samples) {
            extra_qpoints.push_back(convert_intn(sample));
          }
        }
      }
    }
  }
}
コード例 #21
0
ファイル: PolylineCollection.cpp プロジェクト: alexrj/Slic3r
void
PolylineCollection::append(const Polylines &pp)
{
    this->polylines.insert(this->polylines.end(), pp.begin(), pp.end());
}
コード例 #22
0
ファイル: LayerRegionFill.cpp プロジェクト: alexrj/Slic3r
/// The LayerRegion at this point of time may contain
/// surfaces of various types (internal/bridge/top/bottom/solid).
/// The infills are generated on the groups of surfaces with a compatible type.
/// Fills an array of ExtrusionPathCollection objects containing the infills generated now
/// and the thin fills generated by generate_perimeters().
void
LayerRegion::make_fill()
{
    this->fills.clear();
    
    const double fill_density          = this->region()->config.fill_density;
    const Flow   infill_flow           = this->flow(frInfill);
    const Flow   solid_infill_flow     = this->flow(frSolidInfill);
    const Flow   top_solid_infill_flow = this->flow(frTopSolidInfill);
    const coord_t perimeter_spacing    = this->flow(frPerimeter).scaled_spacing();

    SurfaceCollection surfaces;
    
    // merge adjacent surfaces
    // in case of bridge surfaces, the ones with defined angle will be attached to the ones
    // without any angle (shouldn't this logic be moved to process_external_surfaces()?)
    {
        Polygons polygons_bridged;
        polygons_bridged.reserve(this->fill_surfaces.surfaces.size());
        for (Surfaces::const_iterator it = this->fill_surfaces.surfaces.begin(); it != this->fill_surfaces.surfaces.end(); ++it)
            if (it->is_bridge() && it->bridge_angle >= 0)
                append_to(polygons_bridged, (Polygons)*it);
        
        // group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle)
        // group is of type SurfaceCollection
        // FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions.
        std::vector<SurfacesConstPtr> groups;
        this->fill_surfaces.group(&groups);
        
        // merge compatible solid groups (we can generate continuous infill for them)
        {
            // cache flow widths and patterns used for all solid groups
            // (we'll use them for comparing compatible groups)
            std::vector<SurfaceGroupAttrib> group_attrib(groups.size());
            for (size_t i = 0; i < groups.size(); ++i) {
                const Surface &surface = *groups[i].front();
                // we can only merge solid non-bridge surfaces, so discard
                // non-solid or bridge surfaces
                if (!surface.is_solid() || surface.is_bridge()) continue;
                
                group_attrib[i].is_solid = true;
                group_attrib[i].fw = (surface.is_top()) ? top_solid_infill_flow.width : solid_infill_flow.width;
                group_attrib[i].pattern = surface.is_top() ? this->region()->config.top_infill_pattern.value
                    : surface.is_bottom() ? this->region()->config.bottom_infill_pattern.value
                    : ipRectilinear;
            }
            // Loop through solid groups, find compatible groups and append them to this one.
            for (size_t i = 0; i < groups.size(); ++i) {
                if (!group_attrib[i].is_solid)
                    continue;
                for (size_t j = i + 1; j < groups.size();) {
                    if (group_attrib[i] == group_attrib[j]) {
                        // groups are compatible, merge them
                        append_to(groups[i], groups[j]);
                        groups.erase(groups.begin() + j);
                        group_attrib.erase(group_attrib.begin() + j);
                    } else {
                        ++j;
                    }
                }
            }
        }
        
        // Give priority to oriented bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round.
        for (size_t round = 0; round < 2; ++ round) {
            for (std::vector<SurfacesConstPtr>::const_iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) {
                const SurfacesConstPtr &group = *it_group;
                const bool is_oriented_bridge = group.front()->is_bridge() && group.front()->bridge_angle >= 0;
                if (is_oriented_bridge != (round == 0))
                    continue;
                
                // Make a union of polygons defining the infiill regions of a group, use a safety offset.
                Polygons union_p = union_(to_polygons(group), true);
                
                // Subtract surfaces having a defined bridge_angle from any other, use a safety offset.
                if (!is_oriented_bridge && !polygons_bridged.empty())
                    union_p = diff(union_p, polygons_bridged, true);
                
                // subtract any other surface already processed
                //FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice!
                surfaces.append(
                    diff_ex(union_p, to_polygons(surfaces), true),
                    *group.front()  // template
                );
            }
        }
    }
    
    // we need to detect any narrow surfaces that might collapse
    // when adding spacing below
    // such narrow surfaces are often generated in sloping walls
    // by bridge_over_infill() and combine_infill() as a result of the
    // subtraction of the combinable area from the layer infill area,
    // which leaves small areas near the perimeters
    // we are going to grow such regions by overlapping them with the void (if any)
    // TODO: detect and investigate whether there could be narrow regions without
    // any void neighbors
    {
        coord_t distance_between_surfaces = std::max(
            std::max(infill_flow.scaled_spacing(), solid_infill_flow.scaled_spacing()),
            top_solid_infill_flow.scaled_spacing()
        );
        
        Polygons surfaces_polygons = (Polygons)surfaces;
        Polygons collapsed = diff(
            surfaces_polygons,
            offset2(surfaces_polygons, -distance_between_surfaces/2, +distance_between_surfaces/2),
            true
        );
            
        Polygons to_subtract;
        surfaces.filter_by_type((stInternal | stVoid), &to_subtract);
                
        append_to(to_subtract, collapsed);
        surfaces.append(
            intersection_ex(
                offset(collapsed, distance_between_surfaces),
                to_subtract,
                true
            ),
            (stInternal | stSolid)
        );
    }

    if (false) {
//        require "Slic3r/SVG.pm";
//        Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg",
//            expolygons      => [ map $_->expolygon, grep !$_->is_solid, @surfaces ],
//            red_expolygons  => [ map $_->expolygon, grep  $_->is_solid, @surfaces ],
//        );
    }

    for (Surfaces::const_iterator surface_it = surfaces.surfaces.begin();
        surface_it != surfaces.surfaces.end(); ++surface_it) {
        
        const Surface &surface = *surface_it;
        if (surface.surface_type == (stInternal | stVoid))
            continue;
        
        InfillPattern fill_pattern = this->region()->config.fill_pattern.value;
        double density = fill_density;
        FlowRole role = (surface.is_top()) ? frTopSolidInfill
            : surface.is_solid() ? frSolidInfill
            : frInfill;
        const bool is_bridge = this->layer()->id() > 0 && surface.is_bridge();
        
        if (surface.is_solid()) {
            density = 100.;
            fill_pattern = (surface.is_top()) ? this->region()->config.top_infill_pattern.value
                : (surface.is_bottom() && !is_bridge) ? this->region()->config.bottom_infill_pattern.value
                : ipRectilinear;
        } else if (density <= 0)
            continue;
        
        // get filler object
        #if SLIC3R_CPPVER >= 11
            std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(fill_pattern));
        #else
            std::auto_ptr<Fill> f = std::auto_ptr<Fill>(Fill::new_from_type(fill_pattern));
        #endif
        
        // switch to rectilinear if this pattern doesn't support solid infill
        if (density > 99 && !f->can_solid())
            #if SLIC3R_CPPVER >= 11
                f = std::unique_ptr<Fill>(Fill::new_from_type(ipRectilinear));
            #else
                f = std::auto_ptr<Fill>(Fill::new_from_type(ipRectilinear));
            #endif
        
        f->bounding_box = this->layer()->object()->bounding_box();
        
        // calculate the actual flow we'll be using for this infill
        coordf_t h = (surface.thickness == -1) ? this->layer()->height : surface.thickness;
        Flow flow = this->region()->flow(
            role,
            h,
            is_bridge || f->use_bridge_flow(),  // bridge flow?
            this->layer()->id() == 0,           // first layer?
            -1,                                 // auto width
            *this->layer()->object()
        );
        
        // calculate flow spacing for infill pattern generation
        bool using_internal_flow = false;
        if (!surface.is_solid() && !is_bridge) {
            // it's internal infill, so we can calculate a generic flow spacing
            // for all layers, for avoiding the ugly effect of
            // misaligned infill on first layer because of different extrusion width and
            // layer height
            Flow internal_flow = this->region()->flow(
                frInfill,
                h,  // use the calculated surface thickness here for internal infill instead of the layer height to account for infill_every_layers
                false,  // no bridge
                false,  // no first layer
                -1,     // auto width
                *this->layer()->object()
            );
            f->min_spacing = internal_flow.spacing();
            using_internal_flow = true;
        } else {
            f->min_spacing = flow.spacing();
        }
        
        f->endpoints_overlap = scale_(this->region()->config.get_abs_value("infill_overlap",
            (unscale(perimeter_spacing) + (f->min_spacing))/2));
        f->layer_id = this->layer()->id();
        f->z        = this->layer()->print_z;
        f->angle    = Geometry::deg2rad(this->region()->config.fill_angle.value);
        
        // Maximum length of the perimeter segment linking two infill lines.
        f->link_max_length = (!is_bridge && density > 80)
            ? scale_(3 * f->min_spacing)
            : 0;
        
        // Used by the concentric infill pattern to clip the loops to create extrusion paths.
        f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
        
        // apply half spacing using this flow's own spacing and generate infill
        f->density = density/100;
        f->dont_adjust = false;
        /*
        std::cout << surface.expolygon.dump_perl() << std::endl
            << " layer_id: " << f->layer_id << " z: " << f->z
            << " angle: " << f->angle << " min-spacing: " << f->min_spacing
            << " endpoints_overlap: " << f->endpoints_overlap << std::endl << std::endl;
        */
        Polylines polylines = f->fill_surface(surface);
        if (polylines.empty())
            continue;

        // calculate actual flow from spacing (which might have been adjusted by the infill
        // pattern generator)
        if (using_internal_flow) {
            // if we used the internal flow we're not doing a solid infill
            // so we can safely ignore the slight variation that might have
            // been applied to f->spacing()
        } else {
            flow = Flow::new_from_spacing(f->spacing(), flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow());
        }

        // Save into layer.
        ExtrusionEntityCollection* coll = new ExtrusionEntityCollection();
        coll->no_sort = f->no_sort();
        this->fills.entities.push_back(coll);
        
        {
            ExtrusionRole role;
            if (is_bridge) {
                role = erBridgeInfill;
            } else if (surface.is_solid()) {
                role = (surface.is_top()) ? erTopSolidInfill : erSolidInfill;
            } else {
                role = erInternalInfill;
            }
            
            ExtrusionPath templ(role);
            templ.mm3_per_mm    = flow.mm3_per_mm();
            templ.width         = flow.width;
            templ.height        = flow.height;
            
            coll->append(STDMOVE(polylines), templ);
        }
    }

    // add thin fill regions
    // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
    // Unpacks the collection, creates multiple collections per path so that they will
    // be individually included in the nearest neighbor search.
    // The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection.
    for (ExtrusionEntitiesPtr::const_iterator thin_fill = this->thin_fills.entities.begin(); thin_fill != this->thin_fills.entities.end(); ++ thin_fill) {
        ExtrusionEntityCollection* coll = new ExtrusionEntityCollection();
        this->fills.entities.push_back(coll);
        coll->append(**thin_fill);
    }
}
コード例 #23
0
ファイル: ClipperUtils.cpp プロジェクト: alpha6/Slic3r
Polylines
_clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, 
    const Polygons &clip, bool safety_offset_)
{
    // transform input polygons into polylines
    Polylines polylines;
    polylines.reserve(subject.size());
    for (Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon)
        polylines.push_back(*polygon);  // implicit call to split_at_first_point()
    
    // perform clipping
    Polylines retval = _clipper_pl(clipType, polylines, clip, safety_offset_);
    
    /* If the split_at_first_point() call above happens to split the polygon inside the clipping area
       we would get two consecutive polylines instead of a single one, so we go through them in order
       to recombine continuous polylines. */
    for (size_t i = 0; i < retval.size(); ++i) {
        for (size_t j = i+1; j < retval.size(); ++j) {
            if (retval[i].points.back().coincides_with(retval[j].points.front())) {
                /* If last point of i coincides with first point of j,
                   append points of j to i and delete j */
                retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end());
                retval.erase(retval.begin() + j);
                --j;
            } else if (retval[i].points.front().coincides_with(retval[j].points.back())) {
                /* If first point of i coincides with last point of j,
                   prepend points of j to i and delete j */
                retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
                retval.erase(retval.begin() + j);
                --j;
            } else if (retval[i].points.front().coincides_with(retval[j].points.front())) {
                /* Since Clipper does not preserve orientation of polylines, 
                   also check the case when first point of i coincides with first point of j. */
                retval[j].reverse();
                retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
                retval.erase(retval.begin() + j);
                --j;
            } else if (retval[i].points.back().coincides_with(retval[j].points.back())) {
                /* Since Clipper does not preserve orientation of polylines, 
                   also check the case when last point of i coincides with last point of j. */
                retval[j].reverse();
                retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end());
                retval.erase(retval.begin() + j);
                --j;
            }
        }
    }
    return retval;
}
コード例 #24
0
void create_polylines (Polylines& polylines)
{
  {
    Polyline_3 polyline;
    polyline.push_back(Point(1,1,1));
    polyline.push_back(Point(2,1,1));
    polylines.push_back(polyline);
  }
  {
    Polyline_3 polyline;
    polyline.push_back(Point(2,1,1));
    polyline.push_back(Point(2,2,1));
    polylines.push_back(polyline);
  }
  {
    Polyline_3 polyline;
    polyline.push_back(Point(2,2,1));
    polyline.push_back(Point(1,2,1));
    polylines.push_back(polyline);
  }
  {
    Polyline_3 polyline;
    polyline.push_back(Point(1,2,1));
    polyline.push_back(Point(1,1,1));
    polylines.push_back(polyline);
  }
//----------
  {
    Polyline_3 polyline;
    polyline.push_back(Point(1,1,2));
    polyline.push_back(Point(2,1,2));
    polylines.push_back(polyline);
  }
  {
    Polyline_3 polyline;
    polyline.push_back(Point(2,1,2));
    polyline.push_back(Point(2,2,2));
    polylines.push_back(polyline);
  }
  {
    Polyline_3 polyline;
    polyline.push_back(Point(2,2,2));
    polyline.push_back(Point(1,2,2));
    polylines.push_back(polyline);
  }
  {
    Polyline_3 polyline;
    polyline.push_back(Point(1,2,2));
    polyline.push_back(Point(1,1,2));
    polylines.push_back(polyline);
  }
  //----------
  {
    Polyline_3 polyline;
    polyline.push_back(Point(1,1,1));
    polyline.push_back(Point(1,1,2));
    polylines.push_back(polyline);
  }
  {
    Polyline_3 polyline;
    polyline.push_back(Point(2,1,1));
    polyline.push_back(Point(2,1,2));
    polylines.push_back(polyline);
  }
  {
    Polyline_3 polyline;
    polyline.push_back(Point(2,2,1));
    polyline.push_back(Point(2,2,2));
    polylines.push_back(polyline);
  }
  {
    Polyline_3 polyline;
    polyline.push_back(Point(1,2,1));
    polyline.push_back(Point(1,2,2));
    polylines.push_back(polyline);
  }
}
コード例 #25
0
ファイル: PerimeterGenerator.cpp プロジェクト: 2bright/Slic3r
ExtrusionEntityCollection
PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,
    Polylines &thin_walls) const
{
    // loops is an arrayref of ::Loop objects
    // turn each one into an ExtrusionLoop object
    ExtrusionEntityCollection coll;
    for (PerimeterGeneratorLoops::const_iterator loop = loops.begin();
        loop != loops.end(); ++loop) {
        bool is_external = loop->is_external();
        
        ExtrusionRole role;
        ExtrusionLoopRole loop_role;
        role = is_external ? erExternalPerimeter : erPerimeter;
        if (loop->is_internal_contour()) {
            // Note that we set loop role to ContourInternalPerimeter
            // also when loop is both internal and external (i.e.
            // there's only one contour loop).
            loop_role = elrContourInternalPerimeter;
        } else {
            loop_role = elrDefault;
        }
        
        // detect overhanging/bridging perimeters
        ExtrusionPaths paths;
        if (this->config->overhangs && this->layer_id > 0
            && !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) {
            // get non-overhang paths by intersecting this loop with the grown lower slices
            {
                Polylines polylines;
                intersection((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
                
                for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
                    ExtrusionPath path(role);
                    path.polyline   = *polyline;
                    path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm           : this->_mm3_per_mm;
                    path.width      = is_external ? this->ext_perimeter_flow.width  : this->perimeter_flow.width;
                    path.height     = this->layer_height;
                    paths.push_back(path);
                }
            }
            
            // get overhang paths by checking what parts of this loop fall 
            // outside the grown lower slices (thus where the distance between
            // the loop centerline and original lower slices is >= half nozzle diameter
            {
                Polylines polylines;
                diff((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
                
                for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
                    ExtrusionPath path(erOverhangPerimeter);
                    path.polyline   = *polyline;
                    path.mm3_per_mm = this->_mm3_per_mm_overhang;
                    path.width      = this->overhang_flow.width;
                    path.height     = this->overhang_flow.height;
                    paths.push_back(path);
                }
            }
            
            // reapply the nearest point search for starting point
            // We allow polyline reversal because Clipper may have randomly
            // reversed polylines during clipping.
            paths = ExtrusionEntityCollection(paths).chained_path();
        } else {
            ExtrusionPath path(role);
            path.polyline   = loop->polygon.split_at_first_point();
            path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm           : this->_mm3_per_mm;
            path.width      = is_external ? this->ext_perimeter_flow.width  : this->perimeter_flow.width;
            path.height     = this->layer_height;
            paths.push_back(path);
        }
        
        coll.append(ExtrusionLoop(paths, loop_role));
    }
    
    // append thin walls to the nearest-neighbor search (only for first iteration)
    for (Polylines::const_iterator polyline = thin_walls.begin(); polyline != thin_walls.end(); ++polyline) {
        ExtrusionPath path(erExternalPerimeter);
        path.polyline   = *polyline;
        path.mm3_per_mm = this->_mm3_per_mm;
        path.width      = this->perimeter_flow.width;
        path.height     = this->layer_height;
        coll.append(path);
    }
    thin_walls.clear();
    
    // sort entities into a new collection using a nearest-neighbor search,
    // preserving the original indices which are useful for detecting thin walls
    ExtrusionEntityCollection sorted_coll;
    coll.chained_path(&sorted_coll, false, &sorted_coll.orig_indices);
    
    // traverse children and build the final collection
    ExtrusionEntityCollection entities;
    for (std::vector<size_t>::const_iterator idx = sorted_coll.orig_indices.begin();
        idx != sorted_coll.orig_indices.end();
        ++idx) {
        
        if (*idx >= loops.size()) {
            // this is a thin wall
            // let's get it from the sorted collection as it might have been reversed
            size_t i = idx - sorted_coll.orig_indices.begin();
            entities.append(*sorted_coll.entities[i]);
        } else {
            const PerimeterGeneratorLoop &loop = loops[*idx];
            ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[*idx]);
            
            ExtrusionEntityCollection children = this->_traverse_loops(loop.children, thin_walls);
            if (loop.is_contour) {
                eloop.make_counter_clockwise();
                entities.append(children.entities);
                entities.append(eloop);
            } else {
                eloop.make_clockwise();
                entities.append(eloop);
                entities.append(children.entities);
            }
        }
    }
    return entities;
}
コード例 #26
0
ファイル: Polyline.cpp プロジェクト: JoeyKramer1/Slic3r
Polyline::operator Polylines() const
{
    Polylines polylines;
    polylines.push_back(*this);
    return polylines;
}
コード例 #27
0
ファイル: PerimeterGenerator.cpp プロジェクト: 2bright/Slic3r
void
PerimeterGenerator::process()
{
    // other perimeters
    this->_mm3_per_mm           = this->perimeter_flow.mm3_per_mm();
    coord_t pwidth              = this->perimeter_flow.scaled_width();
    coord_t pspacing            = this->perimeter_flow.scaled_spacing();
    
    // external perimeters
    this->_ext_mm3_per_mm       = this->ext_perimeter_flow.mm3_per_mm();
    coord_t ext_pwidth          = this->ext_perimeter_flow.scaled_width();
    coord_t ext_pspacing        = this->ext_perimeter_flow.scaled_spacing();
    coord_t ext_pspacing2       = this->ext_perimeter_flow.scaled_spacing(this->perimeter_flow);
    
    // overhang perimeters
    this->_mm3_per_mm_overhang  = this->overhang_flow.mm3_per_mm();
    
    // solid infill
    coord_t ispacing            = this->solid_infill_flow.scaled_spacing();
    coord_t gap_area_threshold  = pwidth * pwidth;
    
    // Calculate the minimum required spacing between two adjacent traces.
    // This should be equal to the nominal flow spacing but we experiment
    // with some tolerance in order to avoid triggering medial axis when
    // some squishing might work. Loops are still spaced by the entire
    // flow spacing; this only applies to collapsing parts.
    // For ext_min_spacing we use the ext_pspacing calculated for two adjacent
    // external loops (which is the correct way) instead of using ext_pspacing2
    // which is the spacing between external and internal, which is not correct
    // and would make the collapsing (thus the details resolution) dependent on 
    // internal flow which is unrelated.
    coord_t min_spacing         = pspacing      * (1 - INSET_OVERLAP_TOLERANCE);
    coord_t ext_min_spacing     = ext_pspacing  * (1 - INSET_OVERLAP_TOLERANCE);
    
    // prepare grown lower layer slices for overhang detection
    if (this->lower_slices != NULL && this->config->overhangs) {
        // We consider overhang any part where the entire nozzle diameter is not supported by the
        // lower layer, so we take lower slices and offset them by half the nozzle diameter used 
        // in the current layer
        double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->perimeter_extruder-1);
        
        this->_lower_slices_p = offset(*this->lower_slices, scale_(+nozzle_diameter/2));
    }
    
    // we need to process each island separately because we might have different
    // extra perimeters for each one
    for (Surfaces::const_iterator surface = this->slices->surfaces.begin();
        surface != this->slices->surfaces.end(); ++surface) {
        // detect how many perimeters must be generated for this island
        signed short loop_number = this->config->perimeters + surface->extra_perimeters;
        loop_number--;  // 0-indexed loops
        
        Polygons gaps;
        
        Polygons last = surface->expolygon.simplify_p(SCALED_RESOLUTION);
        if (loop_number >= 0) {  // no loops = -1
            
            std::vector<PerimeterGeneratorLoops> contours(loop_number+1);    // depth => loops
            std::vector<PerimeterGeneratorLoops> holes(loop_number+1);       // depth => loops
            Polylines thin_walls;
            
            // we loop one time more than needed in order to find gaps after the last perimeter was applied
            for (signed short i = 0; i <= loop_number+1; ++i) {  // outer loop is 0
                Polygons offsets;
                if (i == 0) {
                    // the minimum thickness of a single loop is:
                    // ext_width/2 + ext_spacing/2 + spacing/2 + width/2
                    if (this->config->thin_walls) {
                        offsets = offset2(
                            last,
                            -(ext_pwidth/2 + ext_min_spacing/2 - 1),
                            +(ext_min_spacing/2 - 1)
                        );
                    } else {
                        offsets = offset(last, -ext_pwidth/2);
                    }
                    
                    // look for thin walls
                    if (this->config->thin_walls) {
                        Polygons diffpp = diff(
                            last,
                            offset(offsets, +ext_pwidth/2),
                            true  // medial axis requires non-overlapping geometry
                        );
                        
                        // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
                        // (actually, something larger than that still may exist due to mitering or other causes)
                        coord_t min_width = ext_pwidth / 2;
                        ExPolygons expp = offset2_ex(diffpp, -min_width/2, +min_width/2);
                        
                        // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
                        Polylines pp;
                        for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex)
                            ex->medial_axis(ext_pwidth + ext_pspacing2, min_width, &pp);
                        
                        double threshold = ext_pwidth * 2;
                        for (Polylines::const_iterator p = pp.begin(); p != pp.end(); ++p) {
                            if (p->length() > threshold) {
                                thin_walls.push_back(*p);
                            }
                        }
                        
                        #ifdef DEBUG
                        printf("  %zu thin walls detected\n", thin_walls.size());
                        #endif
                        
                        /*
                        if (false) {
                            require "Slic3r/SVG.pm";
                            Slic3r::SVG::output(
                                "medial_axis.svg",
                                no_arrows       => 1,
                                #expolygons      => \@expp,
                                polylines       => \@thin_walls,
                            );
                        }
                        */
                    }
                } else {
                    coord_t distance = (i == 1) ? ext_pspacing2 : pspacing;
                    
                    if (this->config->thin_walls) {
                        offsets = offset2(
                            last,
                            -(distance + min_spacing/2 - 1),
                            +(min_spacing/2 - 1)
                        );
                    } else {
                        offsets = offset(
                            last,
                            -distance
                        );
                    }
                    
                    // look for gaps
                    if (this->config->gap_fill_speed.value > 0 && this->config->fill_density.value > 0) {
                        // not using safety offset here would "detect" very narrow gaps
                        // (but still long enough to escape the area threshold) that gap fill
                        // won't be able to fill but we'd still remove from infill area
                        ExPolygons diff_expp = diff_ex(
                            offset(last, -0.5*distance),
                            offset(offsets, +0.5*distance + 10)  // safety offset
                        );
                        for (ExPolygons::const_iterator ex = diff_expp.begin(); ex != diff_expp.end(); ++ex) {
                            if (fabs(ex->area()) >= gap_area_threshold) {
                                Polygons pp = *ex;
                                gaps.insert(gaps.end(), pp.begin(), pp.end());
                            }
                        }
                    }
                }
                
                if (offsets.empty()) break;
                if (i > loop_number) break; // we were only looking for gaps this time
                
                last = offsets;
                for (Polygons::const_iterator polygon = offsets.begin(); polygon != offsets.end(); ++polygon) {
                    PerimeterGeneratorLoop loop(*polygon, i);
                    loop.is_contour = polygon->is_counter_clockwise();
                    if (loop.is_contour) {
                        contours[i].push_back(loop);
                    } else {
                        holes[i].push_back(loop);
                    }
                }
            }
            
            // nest loops: holes first
            for (signed short d = 0; d <= loop_number; ++d) {
                PerimeterGeneratorLoops &holes_d = holes[d];
                
                // loop through all holes having depth == d
                for (signed short i = 0; i < holes_d.size(); ++i) {
                    const PerimeterGeneratorLoop &loop = holes_d[i];
                    
                    // find the hole loop that contains this one, if any
                    for (signed short t = d+1; t <= loop_number; ++t) {
                        for (signed short j = 0; j < holes[t].size(); ++j) {
                            PerimeterGeneratorLoop &candidate_parent = holes[t][j];
                            if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
                                candidate_parent.children.push_back(loop);
                                holes_d.erase(holes_d.begin() + i);
                                --i;
                                goto NEXT_LOOP;
                            }
                        }
                    }
                    
                    // if no hole contains this hole, find the contour loop that contains it
                    for (signed short t = loop_number; t >= 0; --t) {
                        for (signed short j = 0; j < contours[t].size(); ++j) {
                            PerimeterGeneratorLoop &candidate_parent = contours[t][j];
                            if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
                                candidate_parent.children.push_back(loop);
                                holes_d.erase(holes_d.begin() + i);
                                --i;
                                goto NEXT_LOOP;
                            }
                        }
                    }
                    NEXT_LOOP: ;
                }
            }
        
            // nest contour loops
            for (signed short d = loop_number; d >= 1; --d) {
                PerimeterGeneratorLoops &contours_d = contours[d];
                
                // loop through all contours having depth == d
                for (signed short i = 0; i < contours_d.size(); ++i) {
                    const PerimeterGeneratorLoop &loop = contours_d[i];
                
                    // find the contour loop that contains it
                    for (signed short t = d-1; t >= 0; --t) {
                        for (signed short j = 0; j < contours[t].size(); ++j) {
                            PerimeterGeneratorLoop &candidate_parent = contours[t][j];
                            if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
                                candidate_parent.children.push_back(loop);
                                contours_d.erase(contours_d.begin() + i);
                                --i;
                                goto NEXT_CONTOUR;
                            }
                        }
                    }
                    
                    NEXT_CONTOUR: ;
                }
            }
        
            // at this point, all loops should be in contours[0]
            
            ExtrusionEntityCollection entities = this->_traverse_loops(contours.front(), thin_walls);
            
            // if brim will be printed, reverse the order of perimeters so that
            // we continue inwards after having finished the brim
            // TODO: add test for perimeter order
            if (this->config->external_perimeters_first
                || (this->layer_id == 0 && this->print_config->brim_width.value > 0))
                    entities.reverse();
            
            // append perimeters for this slice as a collection
            if (!entities.empty())
                this->loops->append(entities);
        }
        
        // fill gaps
        if (!gaps.empty()) {
            /*
            if (false) {
                require "Slic3r/SVG.pm";
                Slic3r::SVG::output(
                    "gaps.svg",
                    expolygons => union_ex(\@gaps),
                );
            }
            */
            
            // where $pwidth < thickness < 2*$pspacing, infill with width = 2*$pwidth
            // where 0.1*$pwidth < thickness < $pwidth, infill with width = 1*$pwidth
            std::vector<PerimeterGeneratorGapSize> gap_sizes;
            gap_sizes.push_back(PerimeterGeneratorGapSize(pwidth, 2*pspacing, 2*pwidth));
            gap_sizes.push_back(PerimeterGeneratorGapSize(0.1*pwidth, pwidth, 1*pwidth));
            
            for (std::vector<PerimeterGeneratorGapSize>::const_iterator gap_size = gap_sizes.begin();
                gap_size != gap_sizes.end(); ++gap_size) {
                ExtrusionEntityCollection gap_fill = this->_fill_gaps(gap_size->min, 
                    gap_size->max, unscale(gap_size->width), gaps);
                this->gap_fill->append(gap_fill.entities);
                
                // Make sure we don't infill narrow parts that are already gap-filled
                // (we only consider this surface's gaps to reduce the diff() complexity).
                // Growing actual extrusions ensures that gaps not filled by medial axis
                // are not subtracted from fill surfaces (they might be too short gaps
                // that medial axis skips but infill might join with other infill regions
                // and use zigzag).
                coord_t dist = gap_size->width/2;
                Polygons filled;
                for (ExtrusionEntitiesPtr::const_iterator it = gap_fill.entities.begin();
                    it != gap_fill.entities.end(); ++it) {
                    Polygons f;
                    offset((*it)->as_polyline(), &f, dist);
                    filled.insert(filled.end(), f.begin(), f.end());
                }
                last = diff(last, filled);
                gaps = diff(gaps, filled);  // prevent more gap fill here
            }
        }
        
        // create one more offset to be used as boundary for fill
        // we offset by half the perimeter spacing (to get to the actual infill boundary)
        // and then we offset back and forth by half the infill spacing to only consider the
        // non-collapsing regions
        coord_t inset = 0;
        if (loop_number == 0) {
            // one loop
            inset += ext_pspacing2/2;
        } else if (loop_number > 0) {
            // two or more loops
            inset += pspacing/2;
        }
        
        // only apply infill overlap if we actually have one perimeter
        if (inset > 0)
            inset -= this->config->get_abs_value("infill_overlap", inset + ispacing/2);
        
        {
            ExPolygons expp = union_ex(last);
            
            // simplify infill contours according to resolution
            Polygons pp;
            for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex)
                ex->simplify_p(SCALED_RESOLUTION, &pp);
            
            // collapse too narrow infill areas
            coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE);
            expp = offset2_ex(
                pp,
                -inset -min_perimeter_infill_spacing/2,
                +min_perimeter_infill_spacing/2
            );
            
            // append infill areas to fill_surfaces
            for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex)
                this->fill_surfaces->surfaces.push_back(Surface(stInternal, *ex));  // use a bogus surface type
        }
    }
}
コード例 #28
0
ファイル: SVG.cpp プロジェクト: Sebastianv650/Slic3r
void
SVG::draw(const Polylines &polylines, std::string stroke, coordf_t stroke_width)
{
    for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
        this->draw(*it, stroke, stroke_width);
}