|
@@ -13,103 +13,200 @@
|
|
|
|
|
|
namespace Slic3r {
|
|
|
|
|
|
-struct SurfaceGroupAttrib
|
|
|
+struct SurfaceFillParams
|
|
|
{
|
|
|
- SurfaceGroupAttrib() : is_solid(false), flow_width(0.f), pattern(-1) {}
|
|
|
- bool operator==(const SurfaceGroupAttrib &other) const
|
|
|
- { return is_solid == other.is_solid && flow_width == other.flow_width && pattern == other.pattern; }
|
|
|
- bool is_solid;
|
|
|
- float flow_width;
|
|
|
- // pattern is of type InfillPattern, -1 for an unset pattern.
|
|
|
- int pattern;
|
|
|
+ SurfaceFillParams() : flow(0.f, 0.f, 0.f, false) { memset(this, 0, sizeof(*this)); }
|
|
|
+ // Zero based extruder ID.
|
|
|
+ unsigned int extruder;
|
|
|
+ // Infill pattern, adjusted for the density etc.
|
|
|
+ InfillPattern pattern;
|
|
|
+
|
|
|
+ // FillBase
|
|
|
+ // in unscaled coordinates
|
|
|
+ coordf_t spacing;
|
|
|
+ // infill / perimeter overlap, in unscaled coordinates
|
|
|
+ coordf_t overlap;
|
|
|
+ // Angle as provided by the region config, in radians.
|
|
|
+ float angle;
|
|
|
+ // Non-negative for a bridge.
|
|
|
+ float bridge_angle;
|
|
|
+
|
|
|
+ // FillParams
|
|
|
+ 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;
|
|
|
+
|
|
|
+ // width, height of extrusion, nozzle diameter, is bridge
|
|
|
+ // For the output, for fill generator.
|
|
|
+ Flow flow;
|
|
|
+
|
|
|
+ // For the output
|
|
|
+ ExtrusionRole extrusion_role;
|
|
|
+
|
|
|
+ // Various print settings?
|
|
|
+
|
|
|
+ // Index of this entry in a linear vector.
|
|
|
+ size_t idx;
|
|
|
+
|
|
|
+
|
|
|
+ bool operator<(const SurfaceFillParams &rhs) const {
|
|
|
+#define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false;
|
|
|
+#define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false;
|
|
|
+
|
|
|
+ // Sort first by decreasing bridging angle, so that the bridges are processed with priority when trimming one layer by the other.
|
|
|
+ if (this->bridge_angle > rhs.bridge_angle) return true;
|
|
|
+ if (this->bridge_angle < rhs.bridge_angle) return false;
|
|
|
+
|
|
|
+ RETURN_COMPARE_NON_EQUAL(extruder);
|
|
|
+ RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, pattern);
|
|
|
+ RETURN_COMPARE_NON_EQUAL(spacing);
|
|
|
+ RETURN_COMPARE_NON_EQUAL(overlap);
|
|
|
+ RETURN_COMPARE_NON_EQUAL(angle);
|
|
|
+ RETURN_COMPARE_NON_EQUAL(density);
|
|
|
+ RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_connect);
|
|
|
+ RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
|
|
|
+ RETURN_COMPARE_NON_EQUAL(flow.width);
|
|
|
+ RETURN_COMPARE_NON_EQUAL(flow.height);
|
|
|
+ RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter);
|
|
|
+ RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, flow.bridge);
|
|
|
+ RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, extrusion_role);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool operator==(const SurfaceFillParams &rhs) const {
|
|
|
+ return this->extruder == rhs.extruder &&
|
|
|
+ this->pattern == rhs.pattern &&
|
|
|
+ this->pattern == rhs.pattern &&
|
|
|
+ this->spacing == rhs.spacing &&
|
|
|
+ this->overlap == rhs.overlap &&
|
|
|
+ this->angle == rhs.angle &&
|
|
|
+ this->density == rhs.density &&
|
|
|
+ this->dont_connect == rhs.dont_connect &&
|
|
|
+ this->dont_adjust == rhs.dont_adjust &&
|
|
|
+ this->flow == rhs.flow &&
|
|
|
+ this->extrusion_role == rhs.extrusion_role;
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
-// Generate infills for Slic3r::Layer::Region.
|
|
|
-// The Slic3r::Layer::Region 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 Slic3r::ExtrusionPath::Collection objects containing the infills generaed now
|
|
|
-// and the thin fills generated by generate_perimeters().
|
|
|
-void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
|
|
|
-{
|
|
|
-// Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id;
|
|
|
-
|
|
|
- double fill_density = layerm.region()->config().fill_density;
|
|
|
- Flow infill_flow = layerm.flow(frInfill);
|
|
|
- Flow solid_infill_flow = layerm.flow(frSolidInfill);
|
|
|
- Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill);
|
|
|
-
|
|
|
- Surfaces 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::iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++ it)
|
|
|
- if (it->bridge_angle >= 0)
|
|
|
- polygons_append(polygons_bridged, *it);
|
|
|
-
|
|
|
- // group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle)
|
|
|
- // group is of type Slic3r::SurfaceCollection
|
|
|
- //FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions.
|
|
|
- std::vector<SurfacesPtr> groups;
|
|
|
- layerm.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() || layerm.layer()->id() == 0)) {
|
|
|
- group_attrib[i].is_solid = true;
|
|
|
- group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
|
|
|
- group_attrib[i].pattern = surface.is_external() ?
|
|
|
+struct SurfaceFill {
|
|
|
+ SurfaceFill(const SurfaceFillParams& params) : region_id(size_t(-1)), surface(stCount, ExPolygon()), params(params) {}
|
|
|
+
|
|
|
+ size_t region_id;
|
|
|
+ Surface surface;
|
|
|
+ ExPolygons expolygons;
|
|
|
+ SurfaceFillParams params;
|
|
|
+};
|
|
|
+
|
|
|
+std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|
|
+{
|
|
|
+ std::vector<SurfaceFill> surface_fills;
|
|
|
+
|
|
|
+ // Fill in a map of a region & surface to SurfaceFillParams.
|
|
|
+ std::set<SurfaceFillParams> set_surface_params;
|
|
|
+ std::vector<std::vector<const SurfaceFillParams*>> region_to_surface_params(layer.regions().size(), std::vector<const SurfaceFillParams*>());
|
|
|
+ SurfaceFillParams params;
|
|
|
+ bool has_internal_voids = false;
|
|
|
+ for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
|
|
|
+ const LayerRegion &layerm = *layer.regions()[region_id];
|
|
|
+ region_to_surface_params[region_id].assign(layerm.fill_surfaces.size(), nullptr);
|
|
|
+ for (const Surface &surface : layerm.fill_surfaces.surfaces)
|
|
|
+ if (surface.surface_type == stInternalVoid)
|
|
|
+ has_internal_voids = true;
|
|
|
+ else {
|
|
|
+ FlowRole extrusion_role = (surface.surface_type == stTop) ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill);
|
|
|
+ bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge();
|
|
|
+ params.extruder = layerm.region()->extruder(extrusion_role);
|
|
|
+ params.pattern = layerm.region()->config().fill_pattern.value;
|
|
|
+ params.density = float(layerm.region()->config().fill_density);
|
|
|
+
|
|
|
+ if (surface.is_solid()) {
|
|
|
+ params.density = 100.f;
|
|
|
+ params.pattern = (surface.is_external() && ! is_bridge) ?
|
|
|
(surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_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
|
|
|
- groups[i].insert(groups[i].end(), groups[j].begin(), groups[j].end());
|
|
|
- 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<SurfacesPtr>::iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) {
|
|
|
- const SurfacesPtr &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(*it_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!
|
|
|
- // Using group.front() as a template.
|
|
|
- surfaces_append(surfaces, diff_ex(union_p, to_polygons(surfaces), true), *group.front());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
+ ipRectilinear;
|
|
|
+ } else if (params.density <= 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ params.extrusion_role =
|
|
|
+ is_bridge ?
|
|
|
+ erBridgeInfill :
|
|
|
+ (surface.is_solid() ?
|
|
|
+ ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) :
|
|
|
+ erInternalInfill);
|
|
|
+ params.bridge_angle = float(surface.bridge_angle);
|
|
|
+ params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
|
|
|
+
|
|
|
+ // calculate the actual flow we'll be using for this infill
|
|
|
+ params.flow = layerm.region()->flow(
|
|
|
+ extrusion_role,
|
|
|
+ (surface.thickness == -1) ? layerm.layer()->height : surface.thickness, // extrusion height
|
|
|
+ is_bridge || Fill::use_bridge_flow(params.pattern), // bridge flow?
|
|
|
+ layerm.layer()->id() == 0, // first layer?
|
|
|
+ -1, // auto width
|
|
|
+ *layerm.layer()->object()
|
|
|
+ );
|
|
|
+
|
|
|
+ // Calculate flow spacing for infill pattern generation.
|
|
|
+ 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
|
|
|
+ params.spacing = 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
|
|
|
+ *layer.object()
|
|
|
+ ).spacing();
|
|
|
+ } else
|
|
|
+ params.spacing = params.flow.spacing();
|
|
|
+
|
|
|
+ auto it_params = set_surface_params.find(params);
|
|
|
+ if (it_params == set_surface_params.end())
|
|
|
+ it_params = set_surface_params.insert(it_params, params);
|
|
|
+ region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()] = &(*it_params);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ surface_fills.reserve(set_surface_params.size());
|
|
|
+ for (const SurfaceFillParams ¶ms : set_surface_params) {
|
|
|
+ const_cast<SurfaceFillParams&>(params).idx = surface_fills.size();
|
|
|
+ surface_fills.emplace_back(params);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
|
|
|
+ const LayerRegion &layerm = *layer.regions()[region_id];
|
|
|
+ for (const Surface &surface : layerm.fill_surfaces.surfaces)
|
|
|
+ if (surface.surface_type != stInternalVoid) {
|
|
|
+ const SurfaceFillParams *params = region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()];
|
|
|
+ if (params != nullptr) {
|
|
|
+ SurfaceFill &fill = surface_fills[params->idx];
|
|
|
+ if (fill.region_id = size_t(-1)) {
|
|
|
+ fill.region_id = region_id;
|
|
|
+ fill.surface = surface;
|
|
|
+ fill.expolygons.emplace_back(std::move(fill.surface.expolygon));
|
|
|
+ } else
|
|
|
+ fill.expolygons.emplace_back(surface.expolygon);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ Polygons all_polygons;
|
|
|
+ for (SurfaceFill &fill : surface_fills)
|
|
|
+ if (! fill.expolygons.empty() && (fill.expolygons.size() > 1 || ! all_polygons.empty())) {
|
|
|
+ Polygons polys = to_polygons(std::move(fill.expolygons));
|
|
|
+ // Make a union of polygons, use a safety offset, subtract the preceding polygons.
|
|
|
+ // Bridges are processed first (see SurfaceFill::operator<())
|
|
|
+ fill.expolygons = all_polygons.empty() ? union_ex(polys, true) : diff_ex(polys, all_polygons, true);
|
|
|
+ append(all_polygons, std::move(polys));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// we need to detect any narrow surfaces that might collapse
|
|
|
// when adding spacing below
|
|
|
// such narrow surfaces are often generated in sloping walls
|
|
@@ -119,155 +216,170 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
|
|
|
// 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 = to_polygons(surfaces);
|
|
|
- Polygons collapsed = diff(
|
|
|
- surfaces_polygons,
|
|
|
- offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2),
|
|
|
- true);
|
|
|
- Polygons to_subtract;
|
|
|
- to_subtract.reserve(collapsed.size() + number_polygons(surfaces));
|
|
|
- for (Surfaces::const_iterator it_surface = surfaces.begin(); it_surface != surfaces.end(); ++ it_surface)
|
|
|
- if (it_surface->surface_type == stInternalVoid)
|
|
|
- polygons_append(to_subtract, *it_surface);
|
|
|
- polygons_append(to_subtract, collapsed);
|
|
|
- surfaces_append(
|
|
|
- surfaces,
|
|
|
- intersection_ex(
|
|
|
- offset(collapsed, (float)distance_between_surfaces),
|
|
|
- to_subtract,
|
|
|
- true),
|
|
|
- stInternalSolid);
|
|
|
+ if (has_internal_voids) {
|
|
|
+ // Internal voids are generated only if "infill_only_where_needed" or "infill_every_layers" are active.
|
|
|
+ coord_t distance_between_surfaces = 0;
|
|
|
+ Polygons surfaces_polygons;
|
|
|
+ Polygons voids;
|
|
|
+ int region_internal_infill = -1;
|
|
|
+ int region_solid_infill = -1;
|
|
|
+ int region_some_infill = -1;
|
|
|
+ for (SurfaceFill &surface_fill : surface_fills)
|
|
|
+ if (! surface_fill.expolygons.empty()) {
|
|
|
+ distance_between_surfaces = std::max(distance_between_surfaces, surface_fill.params.flow.scaled_spacing());
|
|
|
+ append((surface_fill.surface.surface_type == stInternalVoid) ? voids : surfaces_polygons, to_polygons(surface_fill.expolygons));
|
|
|
+ if (surface_fill.surface.surface_type == stInternalSolid)
|
|
|
+ region_internal_infill = (int)surface_fill.region_id;
|
|
|
+ if (surface_fill.surface.is_solid())
|
|
|
+ region_solid_infill = (int)surface_fill.region_id;
|
|
|
+ if (surface_fill.surface.surface_type != stInternalVoid)
|
|
|
+ region_some_infill = (int)surface_fill.region_id;
|
|
|
+ }
|
|
|
+ if (! voids.empty() && ! surfaces_polygons.empty()) {
|
|
|
+ // First clip voids by the printing polygons, as the voids were ignored by the loop above during mutual clipping.
|
|
|
+ voids = diff(voids, surfaces_polygons);
|
|
|
+ // Corners of infill regions, which would not be filled with an extrusion path with a radius of distance_between_surfaces/2
|
|
|
+ Polygons collapsed = diff(
|
|
|
+ surfaces_polygons,
|
|
|
+ offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2),
|
|
|
+ true);
|
|
|
+ //FIXME why the voids are added to collapsed here? First it is expensive, second the result may lead to some unwanted regions being
|
|
|
+ // added if two offsetted void regions merge.
|
|
|
+ // polygons_append(voids, collapsed);
|
|
|
+ ExPolygons extensions = intersection_ex(offset(collapsed, (float)distance_between_surfaces), voids, true);
|
|
|
+ // Now find an internal infill SurfaceFill to add these extrusions to.
|
|
|
+ SurfaceFill *internal_solid_fill = nullptr;
|
|
|
+ unsigned int region_id = 0;
|
|
|
+ if (region_internal_infill != -1)
|
|
|
+ region_id = region_internal_infill;
|
|
|
+ else if (region_solid_infill != -1)
|
|
|
+ region_id = region_solid_infill;
|
|
|
+ else if (region_some_infill != -1)
|
|
|
+ region_id = region_some_infill;
|
|
|
+ const LayerRegion& layerm = *layer.regions()[region_id];
|
|
|
+ for (SurfaceFill &surface_fill : surface_fills)
|
|
|
+ if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layerm.layer()->height - surface_fill.params.flow.height) < EPSILON) {
|
|
|
+ internal_solid_fill = &surface_fill;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (internal_solid_fill == nullptr) {
|
|
|
+ // Produce another solid fill.
|
|
|
+ params.extruder = layerm.region()->extruder(frSolidInfill);
|
|
|
+ params.pattern = ipRectilinear;
|
|
|
+ params.density = 100.f;
|
|
|
+ params.extrusion_role = erInternalInfill;
|
|
|
+ params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
|
|
|
+ // calculate the actual flow we'll be using for this infill
|
|
|
+ params.flow = layerm.region()->flow(
|
|
|
+ frSolidInfill,
|
|
|
+ layerm.layer()->height, // extrusion height
|
|
|
+ false, // bridge flow?
|
|
|
+ layerm.layer()->id() == 0, // first layer?
|
|
|
+ -1, // auto width
|
|
|
+ *layer.object()
|
|
|
+ );
|
|
|
+ params.spacing = params.flow.spacing();
|
|
|
+ surface_fills.emplace_back(params);
|
|
|
+ surface_fills.back().surface.surface_type = stInternalSolid;
|
|
|
+ surface_fills.back().surface.thickness = layer.height;
|
|
|
+ surface_fills.back().expolygons = std::move(extensions);
|
|
|
+ } else {
|
|
|
+ append(extensions, std::move(internal_solid_fill->expolygons));
|
|
|
+ internal_solid_fill->expolygons = union_ex(extensions);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if (0) {
|
|
|
-// 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 ],
|
|
|
-// );
|
|
|
- }
|
|
|
+ return surface_fills;
|
|
|
+}
|
|
|
|
|
|
- for (const Surface &surface : surfaces) {
|
|
|
- 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);
|
|
|
- bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge();
|
|
|
-
|
|
|
- if (surface.is_solid()) {
|
|
|
- density = 100.;
|
|
|
- fill_pattern = (surface.is_external() && ! is_bridge) ?
|
|
|
- (surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) :
|
|
|
- ipRectilinear;
|
|
|
- } else if (density <= 0)
|
|
|
- continue;
|
|
|
-
|
|
|
- // get filler object
|
|
|
- std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(fill_pattern));
|
|
|
- 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();
|
|
|
- }
|
|
|
+// friend to Layer
|
|
|
+void Layer::make_fills()
|
|
|
+{
|
|
|
+ for (LayerRegion *layerm : m_regions)
|
|
|
+ layerm->fills.clear();
|
|
|
+
|
|
|
+ std::vector<SurfaceFill> surface_fills = group_fills(*this);
|
|
|
+ const Slic3r::BoundingBox bbox = this->object()->bounding_box();
|
|
|
|
|
|
+ for (SurfaceFill &surface_fill : surface_fills) {
|
|
|
+ // Create the filler object.
|
|
|
+ std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(surface_fill.params.pattern));
|
|
|
+ f->set_bounding_box(bbox);
|
|
|
+ f->layer_id = this->id();
|
|
|
+ f->z = this->print_z;
|
|
|
+ f->angle = surface_fill.params.angle;
|
|
|
+ f->spacing = surface_fill.params.spacing;
|
|
|
+
|
|
|
+ // calculate flow spacing for infill pattern generation
|
|
|
+ bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge;
|
|
|
double link_max_length = 0.;
|
|
|
- if (! is_bridge) {
|
|
|
+ if (! surface_fill.params.flow.bridge) {
|
|
|
#if 0
|
|
|
link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing());
|
|
|
// printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length);
|
|
|
#else
|
|
|
- if (density > 80.) // 80%
|
|
|
+ if (surface_fill.params.density > 80.) // 80%
|
|
|
link_max_length = 3. * f->spacing;
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
- f->layer_id = layerm.layer()->id();
|
|
|
- f->z = layerm.layer()->print_z;
|
|
|
- f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
|
|
|
// Maximum length of the perimeter segment linking two infill lines.
|
|
|
f->link_max_length = (coord_t)scale_(link_max_length);
|
|
|
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
|
|
|
- f->loop_clipping = coord_t(scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
|
|
|
-// f->layer_height = h;
|
|
|
+ f->loop_clipping = coord_t(scale_(surface_fill.params.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 = float(0.01 * density);
|
|
|
-// params.dont_adjust = true;
|
|
|
- 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->flow_spacing
|
|
|
- } else {
|
|
|
- flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, (float)h, is_bridge || f->use_bridge_flow());
|
|
|
- }
|
|
|
+ params.density = float(0.01 * surface_fill.params.density);
|
|
|
+ params.dont_adjust = surface_fill.params.dont_adjust; // false
|
|
|
|
|
|
- // Save into layer.
|
|
|
- auto *eec = new ExtrusionEntityCollection();
|
|
|
- out.entities.push_back(eec);
|
|
|
- // Only concentric fills are not sorted.
|
|
|
- eec->no_sort = f->no_sort();
|
|
|
- extrusion_entities_append_paths(
|
|
|
- eec->entities, std::move(polylines),
|
|
|
- is_bridge ?
|
|
|
- erBridgeInfill :
|
|
|
- (surface.is_solid() ?
|
|
|
- ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) :
|
|
|
- erInternalInfill),
|
|
|
- flow.mm3_per_mm(), flow.width, flow.height);
|
|
|
+ for (ExPolygon &expoly : surface_fill.expolygons) {
|
|
|
+ surface_fill.surface.expolygon = std::move(expoly);
|
|
|
+ Polylines polylines = f->fill_surface(&surface_fill.surface, params);
|
|
|
+ if (! polylines.empty()) {
|
|
|
+ // calculate actual flow from spacing (which might have been adjusted by the infill
|
|
|
+ // pattern generator)
|
|
|
+ double flow_mm3_per_mm = surface_fill.params.flow.mm3_per_mm();
|
|
|
+ double flow_width = surface_fill.params.flow.width;
|
|
|
+ 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 new_flow = Flow::new_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter, surface_fill.params.flow.height, surface_fill.params.flow.bridge);
|
|
|
+ flow_mm3_per_mm = new_flow.mm3_per_mm();
|
|
|
+ flow_width = new_flow.width;
|
|
|
+ }
|
|
|
+ // Save into layer.
|
|
|
+ auto *eec = new ExtrusionEntityCollection();
|
|
|
+ m_regions[surface_fill.region_id]->fills.entities.push_back(eec);
|
|
|
+ // Only concentric fills are not sorted.
|
|
|
+ eec->no_sort = f->no_sort();
|
|
|
+ extrusion_entities_append_paths(
|
|
|
+ eec->entities, std::move(polylines),
|
|
|
+ surface_fill.params.extrusion_role,
|
|
|
+ flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// add thin fill regions
|
|
|
- // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
|
|
|
// Unpacks the collection, creates multiple collections per path.
|
|
|
// The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection.
|
|
|
// Why the paths are unpacked?
|
|
|
- for (const ExtrusionEntity *thin_fill : layerm.thin_fills.entities) {
|
|
|
- ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
|
|
|
- out.entities.push_back(&collection);
|
|
|
- collection.entities.push_back(thin_fill->clone());
|
|
|
- }
|
|
|
+ for (LayerRegion *layerm : m_regions)
|
|
|
+ for (const ExtrusionEntity *thin_fill : layerm->thin_fills.entities) {
|
|
|
+ ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
|
|
|
+ layerm->fills.entities.push_back(&collection);
|
|
|
+ collection.entities.push_back(thin_fill->clone());
|
|
|
+ }
|
|
|
+
|
|
|
+#ifndef NDEBUG
|
|
|
+ for (LayerRegion *layerm : m_regions)
|
|
|
+ for (size_t i = 0; i < layerm->fills.entities.size(); ++ i)
|
|
|
+ assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != nullptr);
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
} // namespace Slic3r
|