diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dfd5ce3f3..e656d7aab 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,7 +47,6 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/ExtrusionEntityCollection.cpp ${LIBDIR}/libslic3r/Fill/Fill.cpp ${LIBDIR}/libslic3r/Fill/Fill3DHoneycomb.cpp - ${LIBDIR}/libslic3r/Fill/FillBase.cpp ${LIBDIR}/libslic3r/Fill/FillConcentric.cpp ${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp ${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp @@ -61,6 +60,7 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/IO.cpp ${LIBDIR}/libslic3r/Layer.cpp ${LIBDIR}/libslic3r/LayerRegion.cpp + ${LIBDIR}/libslic3r/LayerRegionFill.cpp ${LIBDIR}/libslic3r/Line.cpp ${LIBDIR}/libslic3r/Model.cpp ${LIBDIR}/libslic3r/MotionPlanner.cpp diff --git a/xs/MANIFEST b/xs/MANIFEST index e3c4b9665..9281abd93 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -30,8 +30,6 @@ src/libslic3r/ExtrusionEntityCollection.cpp src/libslic3r/ExtrusionEntityCollection.hpp src/libslic3r/Fill/Fill.cpp src/libslic3r/Fill/Fill.hpp -src/libslic3r/Fill/FillBase.cpp -src/libslic3r/Fill/FillBase.hpp src/libslic3r/Fill/FillConcentric.cpp src/libslic3r/Fill/FillConcentric.hpp src/libslic3r/Fill/FillHoneycomb.cpp diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index 5a0897413..7de4e1fdd 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -1,290 +1,138 @@ -#include +#include #include -#include #include "../ClipperUtils.hpp" -#include "../Geometry.hpp" -#include "../Layer.hpp" -#include "../Print.hpp" -#include "../PrintConfig.hpp" #include "../Surface.hpp" -#include "../SurfaceCollection.hpp" +#include "../PrintConfig.hpp" -#include "FillBase.hpp" +#include "Fill.hpp" +#include "FillConcentric.hpp" +#include "FillHoneycomb.hpp" +#include "Fill3DHoneycomb.hpp" +#include "FillPlanePath.hpp" +#include "FillRectilinear.hpp" +#include "FillRectilinear2.hpp" namespace Slic3r { -struct SurfaceGroupAttrib +Fill* +Fill::new_from_type(const InfillPattern type) { - SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {} - bool operator==(const SurfaceGroupAttrib &other) const - { return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; } - bool is_solid; - float fw; - // pattern is of type InfillPattern, -1 for an unset pattern. - int pattern; -}; - -// Generate infills for a LayerRegion. -// 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. -// Returns an array of ExtrusionPathCollection objects containing the infills generated now -// and the thin fills generated by generate_perimeters(). -void make_fill(const LayerRegion &layerm, ExtrusionEntityCollection* out) -{ -// Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id; - - double fill_density = layerm.region()->config.fill_density; - const Flow infill_flow = layerm.flow(frInfill); - const Flow solid_infill_flow = layerm.flow(frSolidInfill); - const Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill); - - 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(layerm.fill_surfaces.surfaces.size()); - for (Surfaces::const_iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++it) - if (it->bridge_angle >= 0) - append_to(polygons_bridged, (Polygons)*it); + switch (type) { + case ipConcentric: return new FillConcentric(); + case ipHoneycomb: return new FillHoneycomb(); + case ip3DHoneycomb: return new Fill3DHoneycomb(); - // 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 groups; - layerm.fill_surfaces.group(&groups); + case ipRectilinear: return new FillRectilinear(); + case ipLine: return new FillLine(); + case ipGrid: return new FillGrid(); + case ipAlignedRectilinear: return new FillAlignedRectilinear(); - // merge compatible 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 group_attrib(groups.size()); - for (size_t i = 0; i < groups.size(); ++i) { - // we can only merge solid non-bridge surfaces, so discard - // non-solid surfaces - const Surface &surface = *groups[i].front(); - if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) { - group_attrib[i].is_solid = true; - group_attrib[i].fw = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; - group_attrib[i].pattern = surface.is_external() ? layerm.region()->config.external_fill_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; - } - } - } - } + case ipRectilinear2: return new FillRectilinear2(); + case ipGrid2: return new FillGrid2(); + case ipTriangles: return new FillTriangles(); + case ipStars: return new FillStars(); + case ipCubic: return new FillCubic(); - // Give priority to 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::const_iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) { - const SurfacesConstPtr &group = *it_group; - bool is_bridge = group.front()->bridge_angle >= 0; - if (is_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 (!polygons_bridged.empty() && !is_bridge) - 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() - ); + case ipArchimedeanChords: return new FillArchimedeanChords(); + case ipHilbertCurve: return new FillHilbertCurve(); + case ipOctagramSpiral: return new FillOctagramSpiral(); - 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(stInternalVoid, &to_subtract); - - append_to(to_subtract, collapsed); - surfaces.append( - intersection_ex( - offset(collapsed, distance_between_surfaces), - to_subtract, - true - ), - stInternalSolid - ); - } - - 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 == stInternalVoid) - continue; - - InfillPattern fill_pattern = layerm.region()->config.fill_pattern.value; - double density = fill_density; - FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill - : surface.is_solid() ? frSolidInfill - : frInfill; - const bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge(); - - if (surface.is_solid()) { - density = 100.; - fill_pattern = (surface.is_external() && !is_bridge) - ? layerm.region()->config.external_fill_pattern.value - : ipRectilinear; - } else if (density <= 0) - continue; - - // get filler object - #if SLIC3R_CPPVER >= 11 - std::unique_ptr f = std::unique_ptr(Fill::new_from_type(fill_pattern)); - #else - std::auto_ptr f = std::auto_ptr(Fill::new_from_type(fill_pattern)); - #endif - f->set_bounding_box(layerm.layer()->object()->bounding_box()); - - // calculate the actual flow we'll be using for this infill - coordf_t h = (surface.thickness == -1) ? layerm.layer()->height : surface.thickness; - Flow flow = layerm.region()->flow( - role, - h, - is_bridge || f->use_bridge_flow(), // bridge flow? - layerm.layer()->id() == 0, // first layer? - -1, // auto width - *layerm.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 = layerm.region()->flow( - frInfill, - layerm.layer()->object()->config.layer_height.value, // TODO: handle infill_every_layers? - false, // no bridge - false, // no first layer - -1, // auto width - *layerm.layer()->object() - ); - f->spacing = internal_flow.spacing(); - using_internal_flow = true; - } else { - f->spacing = flow.spacing(); - } - - f->layer_id = layerm.layer()->id(); - f->z = layerm.layer()->print_z; - f->angle = Geometry::deg2rad(layerm.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->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 - FillParams params; - params.density = density/100; - params.dont_adjust = false; - Polylines polylines = f->fill_surface(surface, params); - 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(); - out->entities.push_back(coll); - - { - ExtrusionRole role; - if (is_bridge) { - role = erBridgeInfill; - } else if (surface.is_solid()) { - role = (surface.surface_type == stTop) ? 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 = layerm.thin_fills.entities.begin(); thin_fill != layerm.thin_fills.entities.end(); ++ thin_fill) { - ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(); - out->entities.push_back(coll); - coll->entities.push_back((*thin_fill)->clone()); + default: CONFESS("unknown type"); return NULL; } } +Fill* +Fill::new_from_type(const std::string &type) +{ + static t_config_enum_values enum_keys_map = ConfigOptionEnum::get_enum_values(); + t_config_enum_values::const_iterator it = enum_keys_map.find(type); + return (it == enum_keys_map.end()) ? NULL : new_from_type(InfillPattern(it->second)); +} + +Polylines +Fill::fill_surface(const Surface &surface, const FillParams ¶ms) +{ + // Perform offset. + ExPolygons expp = offset_ex(surface.expolygon, -scale_(this->spacing)/2); + + // Create the infills for each of the regions. + Polylines polylines_out; + for (size_t i = 0; i < expp.size(); ++i) + this->_fill_surface_single( + params, + surface.thickness_layers, + this->_infill_direction(surface), + expp[i], + &polylines_out + ); + return polylines_out; +} + +// Calculate a new spacing to fill width with possibly integer number of lines, +// the first and last line being centered at the interval ends. +// This function possibly increases the spacing, never decreases, +// and for a narrow width the increase in spacing may become severe, +// therefore the adjustment is limited to 20% increase. +coord_t +Fill::adjust_solid_spacing(const coord_t width, const coord_t distance) +{ + assert(width >= 0); + assert(distance > 0); + // floor(width / distance) + coord_t number_of_intervals = floor(width / distance); + coord_t distance_new = (number_of_intervals == 0) + ? distance + : (width / number_of_intervals); + + const coordf_t factor = coordf_t(distance_new) / coordf_t(distance); + assert(factor > 1. - 1e-5); + + // How much could the extrusion width be increased? By 20%. + const coordf_t factor_max = 1.2; + if (factor > factor_max) + distance_new = floor((double)distance * factor_max + 0.5); + + return distance_new; +} + +// Returns orientation of the infill and the reference point of the infill pattern. +// For a normal print, the reference point is the center of a bounding box of the STL. +std::pair +Fill::_infill_direction(const Surface &surface) const +{ + // set infill angle + float out_angle = this->angle; + + // Bounding box is the bounding box of a Slic3r::PrintObject + // The bounding box is only undefined in unit tests. + Point out_shift = this->bounding_box.defined + ? this->bounding_box.center() + : surface.expolygon.contour.bounding_box().center(); + + #if 0 + if (!this->bounding_box.defined) { + printf("Fill::_infill_direction: empty bounding box!"); + } else { + printf("Fill::_infill_direction: reference point %d, %d\n", out_shift.x, out_shift.y); + } + #endif + + if (surface.bridge_angle >= 0) { + // use bridge angle + //FIXME Vojtech: Add a debugf? + // Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle); + #ifdef SLIC3R_DEBUG + printf("Filling bridge with angle %f\n", surface.bridge_angle); + #endif + out_angle = surface.bridge_angle; + } else if (this->layer_id != size_t(-1)) { + // alternate fill direction + out_angle += this->_layer_angle(this->layer_id / surface.thickness_layers); + } + + out_angle += float(M_PI/2.); + return std::pair(out_angle, out_shift); +} + } // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/Fill.hpp b/xs/src/libslic3r/Fill/Fill.hpp index 58e446933..f14b93e7b 100644 --- a/xs/src/libslic3r/Fill/Fill.hpp +++ b/xs/src/libslic3r/Fill/Fill.hpp @@ -1,22 +1,110 @@ #ifndef slic3r_Fill_hpp_ #define slic3r_Fill_hpp_ +#include #include #include #include #include "../libslic3r.h" #include "../BoundingBox.hpp" +#include "../ExPolygon.hpp" +#include "../Polyline.hpp" #include "../PrintConfig.hpp" -#include "FillBase.hpp" - namespace Slic3r { -class ExtrusionEntityCollection; -class LayerRegion; +class Surface; -void make_fill(const LayerRegion &layerm, ExtrusionEntityCollection* out); +struct FillParams +{ + public: + FillParams() : density(0), dont_connect(false), dont_adjust(false), complete(false) {}; + + // Fill density, fraction in <0, 1> + float density; + + // Don't connect the fill lines around the inner perimeter. + bool dont_connect; + + // Don't adjust spacing to fill the space evenly. + bool dont_adjust; + + // For Honeycomb. + // we were requested to complete each loop; + // in this case we don't try to make more continuous paths + bool complete; +}; + +class Fill +{ +public: + // Index of the layer. + size_t layer_id; + + // Z coordinate of the top print surface, in unscaled coordinates + coordf_t z; + + // in unscaled coordinates + coordf_t spacing; + + // in radians, ccw, 0 = East + float angle; + + // In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines. + // Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic. + // If left to zero, the links will not be limited. + coord_t link_max_length; + + // In scaled coordinates. Used by the concentric infill pattern to clip the loops to create extrusion paths. + coord_t loop_clipping; + + // In scaled coordinates. Bounding box of the 2D projection of the object. + BoundingBox bounding_box; + +public: + virtual ~Fill() {} + + static Fill* new_from_type(const InfillPattern type); + static Fill* new_from_type(const std::string &type); + + void set_bounding_box(const BoundingBox &bb) { this->bounding_box = bb; } + + // Use bridge flow for the fill? + virtual bool use_bridge_flow() const { return false; } + + // Do not sort the fill lines to optimize the print head path? + virtual bool no_sort() const { return false; } + + // Perform the fill. + virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + + static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance); + +protected: + Fill() : + layer_id(size_t(-1)), + z(0.f), + spacing(0.f), + angle(0), + link_max_length(0), + loop_clipping(0) + {}; + + // The expolygon may be modified by the method to avoid a copy. + virtual void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out) {}; + + virtual float _layer_angle(size_t idx) const { + return (idx % 2) == 0 ? (M_PI/2.) : 0; + } + + std::pair _infill_direction(const Surface &surface) const; +}; } // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp index 89b781e1e..19a45818f 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp @@ -5,7 +5,7 @@ #include "../libslic3r.h" -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Fill/FillBase.cpp b/xs/src/libslic3r/Fill/FillBase.cpp deleted file mode 100644 index 987d51f3d..000000000 --- a/xs/src/libslic3r/Fill/FillBase.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include - -#include "../ClipperUtils.hpp" -#include "../Surface.hpp" -#include "../PrintConfig.hpp" - -#include "FillBase.hpp" -#include "FillConcentric.hpp" -#include "FillHoneycomb.hpp" -#include "Fill3DHoneycomb.hpp" -#include "FillPlanePath.hpp" -#include "FillRectilinear.hpp" -#include "FillRectilinear2.hpp" - -namespace Slic3r { - -Fill* -Fill::new_from_type(const InfillPattern type) -{ - switch (type) { - case ipConcentric: return new FillConcentric(); - case ipHoneycomb: return new FillHoneycomb(); - case ip3DHoneycomb: return new Fill3DHoneycomb(); - - case ipRectilinear: return new FillRectilinear(); - case ipLine: return new FillLine(); - case ipGrid: return new FillGrid(); - case ipAlignedRectilinear: return new FillAlignedRectilinear(); - - case ipRectilinear2: return new FillRectilinear2(); - case ipGrid2: return new FillGrid2(); - case ipTriangles: return new FillTriangles(); - case ipStars: return new FillStars(); - case ipCubic: return new FillCubic(); - - case ipArchimedeanChords: return new FillArchimedeanChords(); - case ipHilbertCurve: return new FillHilbertCurve(); - case ipOctagramSpiral: return new FillOctagramSpiral(); - - default: CONFESS("unknown type"); return NULL; - } -} - -Fill* -Fill::new_from_type(const std::string &type) -{ - static t_config_enum_values enum_keys_map = ConfigOptionEnum::get_enum_values(); - t_config_enum_values::const_iterator it = enum_keys_map.find(type); - return (it == enum_keys_map.end()) ? NULL : new_from_type(InfillPattern(it->second)); -} - -Polylines -Fill::fill_surface(const Surface &surface, const FillParams ¶ms) -{ - // Perform offset. - ExPolygons expp = offset_ex(surface.expolygon, -scale_(this->spacing)/2); - - // Create the infills for each of the regions. - Polylines polylines_out; - for (size_t i = 0; i < expp.size(); ++i) - this->_fill_surface_single( - params, - surface.thickness_layers, - this->_infill_direction(surface), - expp[i], - &polylines_out - ); - return polylines_out; -} - -// Calculate a new spacing to fill width with possibly integer number of lines, -// the first and last line being centered at the interval ends. -// This function possibly increases the spacing, never decreases, -// and for a narrow width the increase in spacing may become severe, -// therefore the adjustment is limited to 20% increase. -coord_t -Fill::adjust_solid_spacing(const coord_t width, const coord_t distance) -{ - assert(width >= 0); - assert(distance > 0); - // floor(width / distance) - coord_t number_of_intervals = floor(width / distance); - coord_t distance_new = (number_of_intervals == 0) - ? distance - : (width / number_of_intervals); - - const coordf_t factor = coordf_t(distance_new) / coordf_t(distance); - assert(factor > 1. - 1e-5); - - // How much could the extrusion width be increased? By 20%. - const coordf_t factor_max = 1.2; - if (factor > factor_max) - distance_new = floor((double)distance * factor_max + 0.5); - - return distance_new; -} - -// Returns orientation of the infill and the reference point of the infill pattern. -// For a normal print, the reference point is the center of a bounding box of the STL. -std::pair -Fill::_infill_direction(const Surface &surface) const -{ - // set infill angle - float out_angle = this->angle; - - // Bounding box is the bounding box of a Slic3r::PrintObject - // The bounding box is only undefined in unit tests. - Point out_shift = this->bounding_box.defined - ? this->bounding_box.center() - : surface.expolygon.contour.bounding_box().center(); - - #if 0 - if (!this->bounding_box.defined) { - printf("Fill::_infill_direction: empty bounding box!"); - } else { - printf("Fill::_infill_direction: reference point %d, %d\n", out_shift.x, out_shift.y); - } - #endif - - if (surface.bridge_angle >= 0) { - // use bridge angle - //FIXME Vojtech: Add a debugf? - // Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle); - #ifdef SLIC3R_DEBUG - printf("Filling bridge with angle %f\n", surface.bridge_angle); - #endif - out_angle = surface.bridge_angle; - } else if (this->layer_id != size_t(-1)) { - // alternate fill direction - out_angle += this->_layer_angle(this->layer_id / surface.thickness_layers); - } - - out_angle += float(M_PI/2.); - return std::pair(out_angle, out_shift); -} - -} // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillBase.hpp b/xs/src/libslic3r/Fill/FillBase.hpp deleted file mode 100644 index 75627d29e..000000000 --- a/xs/src/libslic3r/Fill/FillBase.hpp +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef slic3r_FillBase_hpp_ -#define slic3r_FillBase_hpp_ - -#include -#include -#include -#include - -#include "../libslic3r.h" -#include "../BoundingBox.hpp" -#include "../ExPolygon.hpp" -#include "../Polyline.hpp" -#include "../PrintConfig.hpp" - -namespace Slic3r { - -class Surface; - -struct FillParams -{ - public: - FillParams() : density(0), dont_connect(false), dont_adjust(false), complete(false) {}; - - // Fill density, fraction in <0, 1> - float density; - - // Don't connect the fill lines around the inner perimeter. - bool dont_connect; - - // Don't adjust spacing to fill the space evenly. - bool dont_adjust; - - // For Honeycomb. - // we were requested to complete each loop; - // in this case we don't try to make more continuous paths - bool complete; -}; - -class Fill -{ -public: - // Index of the layer. - size_t layer_id; - - // Z coordinate of the top print surface, in unscaled coordinates - coordf_t z; - - // in unscaled coordinates - coordf_t spacing; - - // in radians, ccw, 0 = East - float angle; - - // In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines. - // Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic. - // If left to zero, the links will not be limited. - coord_t link_max_length; - - // In scaled coordinates. Used by the concentric infill pattern to clip the loops to create extrusion paths. - coord_t loop_clipping; - - // In scaled coordinates. Bounding box of the 2D projection of the object. - BoundingBox bounding_box; - -public: - virtual ~Fill() {} - - static Fill* new_from_type(const InfillPattern type); - static Fill* new_from_type(const std::string &type); - - void set_bounding_box(const BoundingBox &bb) { this->bounding_box = bb; } - - // Use bridge flow for the fill? - virtual bool use_bridge_flow() const { return false; } - - // Do not sort the fill lines to optimize the print head path? - virtual bool no_sort() const { return false; } - - // Perform the fill. - virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); - - static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance); - -protected: - Fill() : - layer_id(size_t(-1)), - z(0.f), - spacing(0.f), - angle(0), - link_max_length(0), - loop_clipping(0) - {}; - - // The expolygon may be modified by the method to avoid a copy. - virtual void _fill_surface_single( - const FillParams ¶ms, - unsigned int thickness_layers, - const std::pair &direction, - ExPolygon &expolygon, - Polylines* polylines_out) {}; - - virtual float _layer_angle(size_t idx) const { - return (idx % 2) == 0 ? (M_PI/2.) : 0; - } - - std::pair _infill_direction(const Surface &surface) const; -}; - -} // namespace Slic3r - -#endif // slic3r_FillBase_hpp_ diff --git a/xs/src/libslic3r/Fill/FillConcentric.hpp b/xs/src/libslic3r/Fill/FillConcentric.hpp index a4e1ddb11..472898f6e 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.hpp +++ b/xs/src/libslic3r/Fill/FillConcentric.hpp @@ -1,7 +1,7 @@ #ifndef slic3r_FillConcentric_hpp_ #define slic3r_FillConcentric_hpp_ -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.hpp b/xs/src/libslic3r/Fill/FillHoneycomb.hpp index 70caaf7a0..b9b0851bb 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.hpp +++ b/xs/src/libslic3r/Fill/FillHoneycomb.hpp @@ -5,7 +5,7 @@ #include "../libslic3r.h" -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Fill/FillPlanePath.hpp b/xs/src/libslic3r/Fill/FillPlanePath.hpp index 33b3ec1db..3eae2fa86 100644 --- a/xs/src/libslic3r/Fill/FillPlanePath.hpp +++ b/xs/src/libslic3r/Fill/FillPlanePath.hpp @@ -5,7 +5,7 @@ #include "../libslic3r.h" -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Fill/FillRectilinear.hpp b/xs/src/libslic3r/Fill/FillRectilinear.hpp index 03c0826bc..7e7ce4df8 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.hpp @@ -3,7 +3,7 @@ #include "../libslic3r.h" -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.hpp b/xs/src/libslic3r/Fill/FillRectilinear2.hpp index f1f8973de..44d8297bb 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.hpp @@ -3,7 +3,7 @@ #include "../libslic3r.h" -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index 3b944c97e..102654be0 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -2,7 +2,6 @@ #include "ClipperUtils.hpp" #include "Geometry.hpp" #include "Print.hpp" -#include "Fill/Fill.hpp" namespace Slic3r { @@ -239,13 +238,11 @@ Layer::make_fills() #endif FOREACH_LAYERREGION(this, it_layerm) { - LayerRegion &layerm = **it_layerm; - layerm.fills.clear(); - make_fill(layerm, &layerm.fills); + (*it_layerm)->make_fill(); #ifndef NDEBUG - for (size_t i = 0; i < layerm.fills.entities.size(); ++i) - assert(dynamic_cast(layerm.fills.entities[i]) != NULL); + for (size_t i = 0; i < (*it_layerm)->fills.entities.size(); ++i) + assert(dynamic_cast((*it_layerm)->fills.entities[i]) != NULL); #endif } } diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp index 7355ed919..c0db224ec 100644 --- a/xs/src/libslic3r/Layer.hpp +++ b/xs/src/libslic3r/Layer.hpp @@ -59,6 +59,7 @@ class LayerRegion void merge_slices(); void prepare_fill_surfaces(); void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); + void make_fill(); void process_external_surfaces(const Layer* lower_layer); double infill_area_threshold() const; diff --git a/xs/src/libslic3r/LayerRegionFill.cpp b/xs/src/libslic3r/LayerRegionFill.cpp new file mode 100644 index 000000000..8319ab195 --- /dev/null +++ b/xs/src/libslic3r/LayerRegionFill.cpp @@ -0,0 +1,285 @@ +#include "Layer.hpp" +#include "ClipperUtils.hpp" +#include "Fill/Fill.hpp" +#include "Geometry.hpp" +#include "Print.hpp" +#include "PrintConfig.hpp" +#include "Surface.hpp" + +namespace Slic3r { + +struct SurfaceGroupAttrib +{ + SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {} + bool operator==(const SurfaceGroupAttrib &other) const + { return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; } + bool is_solid; + float fw; + // pattern is of type InfillPattern, -1 for an unset pattern. + int pattern; +}; + +// Generate infills for a LayerRegion. +// 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); + + 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->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 groups; + this->fill_surfaces.group(&groups); + + // merge compatible 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 group_attrib(groups.size()); + for (size_t i = 0; i < groups.size(); ++i) { + // we can only merge solid non-bridge surfaces, so discard + // non-solid surfaces + const Surface &surface = *groups[i].front(); + if (surface.is_solid() && (!surface.is_bridge() || this->layer()->id() == 0)) { + group_attrib[i].is_solid = true; + group_attrib[i].fw = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; + group_attrib[i].pattern = surface.is_external() ? this->region()->config.external_fill_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 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::const_iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) { + const SurfacesConstPtr &group = *it_group; + bool is_bridge = group.front()->bridge_angle >= 0; + if (is_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 (!polygons_bridged.empty() && !is_bridge) + 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(stInternalVoid, &to_subtract); + + append_to(to_subtract, collapsed); + surfaces.append( + intersection_ex( + offset(collapsed, distance_between_surfaces), + to_subtract, + true + ), + stInternalSolid + ); + } + + 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 == stInternalVoid) + continue; + + InfillPattern fill_pattern = this->region()->config.fill_pattern.value; + double density = fill_density; + FlowRole role = (surface.surface_type == stTop) ? 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_external() && !is_bridge) + ? this->region()->config.external_fill_pattern.value + : ipRectilinear; + } else if (density <= 0) + continue; + + // get filler object + #if SLIC3R_CPPVER >= 11 + std::unique_ptr f = std::unique_ptr(Fill::new_from_type(fill_pattern)); + #else + std::auto_ptr f = std::auto_ptr(Fill::new_from_type(fill_pattern)); + #endif + f->set_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, + this->layer()->object()->config.layer_height.value, // TODO: handle infill_every_layers? + false, // no bridge + false, // no first layer + -1, // auto width + *this->layer()->object() + ); + f->spacing = internal_flow.spacing(); + using_internal_flow = true; + } else { + f->spacing = flow.spacing(); + } + + 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->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 + FillParams params; + params.density = density/100; + params.dont_adjust = false; + Polylines polylines = f->fill_surface(surface, params); + 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.surface_type == stTop) ? 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); + } +} + +} // namespace Slic3r diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 77122057b..8762d180a 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -1,7 +1,7 @@ #include "SLAPrint.hpp" #include "ClipperUtils.hpp" #include "ExtrusionEntity.hpp" -#include "Fill/FillBase.hpp" +#include "Fill/Fill.hpp" #include "Geometry.hpp" #include "Surface.hpp" #include diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 86dbcad83..06724f875 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -54,7 +54,7 @@ extern "C" { #include #include #include -#include +#include #include #include #include diff --git a/xs/xsp/Filler.xsp b/xs/xsp/Filler.xsp index 85d83d549..1e9652a6c 100644 --- a/xs/xsp/Filler.xsp +++ b/xs/xsp/Filler.xsp @@ -71,8 +71,5 @@ new_from_type(CLASS, type) %package{Slic3r::Filler}; -void make_fill(LayerRegion* layerm, ExtrusionEntityCollection* out) - %code{% make_fill(*layerm, out); %}; - coord_t adjust_solid_spacing(coord_t width, coord_t distance) %code{% RETVAL = Fill::adjust_solid_spacing(width, distance); %};