Refactoring: make_fills() is now a LayerRegion method, and we move the base Fill class to Fill.hpp, no need for FillBase.hpp anymore

This commit is contained in:
Alessandro Ranellucci 2016-11-30 18:07:44 +01:00
parent a9609a6dd5
commit 3e04877571
18 changed files with 512 additions and 547 deletions

View File

@ -47,7 +47,6 @@ add_library(libslic3r STATIC
${LIBDIR}/libslic3r/ExtrusionEntityCollection.cpp ${LIBDIR}/libslic3r/ExtrusionEntityCollection.cpp
${LIBDIR}/libslic3r/Fill/Fill.cpp ${LIBDIR}/libslic3r/Fill/Fill.cpp
${LIBDIR}/libslic3r/Fill/Fill3DHoneycomb.cpp ${LIBDIR}/libslic3r/Fill/Fill3DHoneycomb.cpp
${LIBDIR}/libslic3r/Fill/FillBase.cpp
${LIBDIR}/libslic3r/Fill/FillConcentric.cpp ${LIBDIR}/libslic3r/Fill/FillConcentric.cpp
${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp ${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp
${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp ${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp
@ -61,6 +60,7 @@ add_library(libslic3r STATIC
${LIBDIR}/libslic3r/IO.cpp ${LIBDIR}/libslic3r/IO.cpp
${LIBDIR}/libslic3r/Layer.cpp ${LIBDIR}/libslic3r/Layer.cpp
${LIBDIR}/libslic3r/LayerRegion.cpp ${LIBDIR}/libslic3r/LayerRegion.cpp
${LIBDIR}/libslic3r/LayerRegionFill.cpp
${LIBDIR}/libslic3r/Line.cpp ${LIBDIR}/libslic3r/Line.cpp
${LIBDIR}/libslic3r/Model.cpp ${LIBDIR}/libslic3r/Model.cpp
${LIBDIR}/libslic3r/MotionPlanner.cpp ${LIBDIR}/libslic3r/MotionPlanner.cpp

View File

@ -30,8 +30,6 @@ src/libslic3r/ExtrusionEntityCollection.cpp
src/libslic3r/ExtrusionEntityCollection.hpp src/libslic3r/ExtrusionEntityCollection.hpp
src/libslic3r/Fill/Fill.cpp src/libslic3r/Fill/Fill.cpp
src/libslic3r/Fill/Fill.hpp src/libslic3r/Fill/Fill.hpp
src/libslic3r/Fill/FillBase.cpp
src/libslic3r/Fill/FillBase.hpp
src/libslic3r/Fill/FillConcentric.cpp src/libslic3r/Fill/FillConcentric.cpp
src/libslic3r/Fill/FillConcentric.hpp src/libslic3r/Fill/FillConcentric.hpp
src/libslic3r/Fill/FillHoneycomb.cpp src/libslic3r/Fill/FillHoneycomb.cpp

View File

@ -1,290 +1,138 @@
#include <assert.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <memory>
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../Geometry.hpp"
#include "../Layer.hpp"
#include "../Print.hpp"
#include "../PrintConfig.hpp"
#include "../Surface.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 { namespace Slic3r {
struct SurfaceGroupAttrib Fill*
Fill::new_from_type(const InfillPattern type)
{ {
SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {} switch (type) {
bool operator==(const SurfaceGroupAttrib &other) const case ipConcentric: return new FillConcentric();
{ return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; } case ipHoneycomb: return new FillHoneycomb();
bool is_solid; case ip3DHoneycomb: return new Fill3DHoneycomb();
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);
// group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle) case ipRectilinear: return new FillRectilinear();
// group is of type SurfaceCollection case ipLine: return new FillLine();
// FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions. case ipGrid: return new FillGrid();
std::vector<SurfacesConstPtr> groups; case ipAlignedRectilinear: return new FillAlignedRectilinear();
layerm.fill_surfaces.group(&groups);
// merge compatible groups (we can generate continuous infill for them) case ipRectilinear2: return new FillRectilinear2();
{ case ipGrid2: return new FillGrid2();
// cache flow widths and patterns used for all solid groups case ipTriangles: return new FillTriangles();
// (we'll use them for comparing compatible groups) case ipStars: return new FillStars();
std::vector<SurfaceGroupAttrib> group_attrib(groups.size()); case ipCubic: return new FillCubic();
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;
}
}
}
}
// Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round. case ipArchimedeanChords: return new FillArchimedeanChords();
for (size_t round = 0; round < 2; ++ round) { case ipHilbertCurve: return new FillHilbertCurve();
for (std::vector<SurfacesConstPtr>::const_iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) { case ipOctagramSpiral: return new FillOctagramSpiral();
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; default: CONFESS("unknown type"); return NULL;
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<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
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());
} }
} }
Fill*
Fill::new_from_type(const std::string &type)
{
static t_config_enum_values enum_keys_map = ConfigOptionEnum<InfillPattern>::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 &params)
{
// 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<float, Point>
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<float, Point>(out_angle, out_shift);
}
} // namespace Slic3r } // namespace Slic3r

View File

@ -1,22 +1,110 @@
#ifndef slic3r_Fill_hpp_ #ifndef slic3r_Fill_hpp_
#define slic3r_Fill_hpp_ #define slic3r_Fill_hpp_
#include <assert.h>
#include <memory.h> #include <memory.h>
#include <float.h> #include <float.h>
#include <stdint.h> #include <stdint.h>
#include "../libslic3r.h" #include "../libslic3r.h"
#include "../BoundingBox.hpp" #include "../BoundingBox.hpp"
#include "../ExPolygon.hpp"
#include "../Polyline.hpp"
#include "../PrintConfig.hpp" #include "../PrintConfig.hpp"
#include "FillBase.hpp"
namespace Slic3r { namespace Slic3r {
class ExtrusionEntityCollection; class Surface;
class LayerRegion;
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 &params);
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 &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out) {};
virtual float _layer_angle(size_t idx) const {
return (idx % 2) == 0 ? (M_PI/2.) : 0;
}
std::pair<float, Point> _infill_direction(const Surface &surface) const;
};
} // namespace Slic3r } // namespace Slic3r

View File

@ -5,7 +5,7 @@
#include "../libslic3r.h" #include "../libslic3r.h"
#include "FillBase.hpp" #include "Fill.hpp"
namespace Slic3r { namespace Slic3r {

View File

@ -1,138 +0,0 @@
#include <math.h>
#include <stdio.h>
#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<InfillPattern>::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 &params)
{
// 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<float, Point>
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<float, Point>(out_angle, out_shift);
}
} // namespace Slic3r

View File

@ -1,111 +0,0 @@
#ifndef slic3r_FillBase_hpp_
#define slic3r_FillBase_hpp_
#include <assert.h>
#include <memory.h>
#include <float.h>
#include <stdint.h>
#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 &params);
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 &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines* polylines_out) {};
virtual float _layer_angle(size_t idx) const {
return (idx % 2) == 0 ? (M_PI/2.) : 0;
}
std::pair<float, Point> _infill_direction(const Surface &surface) const;
};
} // namespace Slic3r
#endif // slic3r_FillBase_hpp_

View File

@ -1,7 +1,7 @@
#ifndef slic3r_FillConcentric_hpp_ #ifndef slic3r_FillConcentric_hpp_
#define slic3r_FillConcentric_hpp_ #define slic3r_FillConcentric_hpp_
#include "FillBase.hpp" #include "Fill.hpp"
namespace Slic3r { namespace Slic3r {

View File

@ -5,7 +5,7 @@
#include "../libslic3r.h" #include "../libslic3r.h"
#include "FillBase.hpp" #include "Fill.hpp"
namespace Slic3r { namespace Slic3r {

View File

@ -5,7 +5,7 @@
#include "../libslic3r.h" #include "../libslic3r.h"
#include "FillBase.hpp" #include "Fill.hpp"
namespace Slic3r { namespace Slic3r {

View File

@ -3,7 +3,7 @@
#include "../libslic3r.h" #include "../libslic3r.h"
#include "FillBase.hpp" #include "Fill.hpp"
namespace Slic3r { namespace Slic3r {

View File

@ -3,7 +3,7 @@
#include "../libslic3r.h" #include "../libslic3r.h"
#include "FillBase.hpp" #include "Fill.hpp"
namespace Slic3r { namespace Slic3r {

View File

@ -2,7 +2,6 @@
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
#include "Print.hpp" #include "Print.hpp"
#include "Fill/Fill.hpp"
namespace Slic3r { namespace Slic3r {
@ -239,13 +238,11 @@ Layer::make_fills()
#endif #endif
FOREACH_LAYERREGION(this, it_layerm) { FOREACH_LAYERREGION(this, it_layerm) {
LayerRegion &layerm = **it_layerm; (*it_layerm)->make_fill();
layerm.fills.clear();
make_fill(layerm, &layerm.fills);
#ifndef NDEBUG #ifndef NDEBUG
for (size_t i = 0; i < layerm.fills.entities.size(); ++i) for (size_t i = 0; i < (*it_layerm)->fills.entities.size(); ++i)
assert(dynamic_cast<ExtrusionEntityCollection*>(layerm.fills.entities[i]) != NULL); assert(dynamic_cast<ExtrusionEntityCollection*>((*it_layerm)->fills.entities[i]) != NULL);
#endif #endif
} }
} }

View File

@ -59,6 +59,7 @@ class LayerRegion
void merge_slices(); void merge_slices();
void prepare_fill_surfaces(); void prepare_fill_surfaces();
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
void make_fill();
void process_external_surfaces(const Layer* lower_layer); void process_external_surfaces(const Layer* lower_layer);
double infill_area_threshold() const; double infill_area_threshold() const;

View File

@ -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<SurfacesConstPtr> 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<SurfaceGroupAttrib> 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<SurfacesConstPtr>::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<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
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

View File

@ -1,7 +1,7 @@
#include "SLAPrint.hpp" #include "SLAPrint.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "ExtrusionEntity.hpp" #include "ExtrusionEntity.hpp"
#include "Fill/FillBase.hpp" #include "Fill/Fill.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
#include "Surface.hpp" #include "Surface.hpp"
#include <iostream> #include <iostream>

View File

@ -54,7 +54,7 @@ extern "C" {
#include <ClipperUtils.hpp> #include <ClipperUtils.hpp>
#include <Config.hpp> #include <Config.hpp>
#include <ExPolygon.hpp> #include <ExPolygon.hpp>
#include <Fill/FillBase.hpp> #include <Fill/Fill.hpp>
#include <MultiPoint.hpp> #include <MultiPoint.hpp>
#include <Point.hpp> #include <Point.hpp>
#include <Polygon.hpp> #include <Polygon.hpp>

View File

@ -71,8 +71,5 @@ new_from_type(CLASS, type)
%package{Slic3r::Filler}; %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) coord_t adjust_solid_spacing(coord_t width, coord_t distance)
%code{% RETVAL = Fill::adjust_solid_spacing(width, distance); %}; %code{% RETVAL = Fill::adjust_solid_spacing(width, distance); %};