mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-13 21:25:53 +08:00
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:
parent
a9609a6dd5
commit
3e04877571
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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 ¶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<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
|
||||||
|
@ -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 ¶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<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
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "../libslic3r.h"
|
#include "../libslic3r.h"
|
||||||
|
|
||||||
#include "FillBase.hpp"
|
#include "Fill.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -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 ¶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<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
|
|
@ -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 ¶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<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_
|
|
@ -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 {
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "../libslic3r.h"
|
#include "../libslic3r.h"
|
||||||
|
|
||||||
#include "FillBase.hpp"
|
#include "Fill.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "../libslic3r.h"
|
#include "../libslic3r.h"
|
||||||
|
|
||||||
#include "FillBase.hpp"
|
#include "Fill.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "../libslic3r.h"
|
#include "../libslic3r.h"
|
||||||
|
|
||||||
#include "FillBase.hpp"
|
#include "Fill.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "../libslic3r.h"
|
#include "../libslic3r.h"
|
||||||
|
|
||||||
#include "FillBase.hpp"
|
#include "Fill.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
285
xs/src/libslic3r/LayerRegionFill.cpp
Normal file
285
xs/src/libslic3r/LayerRegionFill.cpp
Normal 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
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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); %};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user