ENH: full code of ensure vertical enhance

1.Mark ensure vertical part as a seperate part
2.Detect floating line in vertical part and use bridge speed to print
3.Slow down the vertical part to 80% solid infill speed

jira:NONE

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: I59678b3530c6cbb9565ac038349a0a2181e16dc8
This commit is contained in:
xun.zhang 2025-03-05 22:01:02 +08:00 committed by lane.wei
parent 62a06d3351
commit 40c4f667d1
24 changed files with 1300 additions and 655 deletions

View File

@ -112,6 +112,8 @@ set(lisbslic3r_sources
Fill/Lightning/TreeNode.hpp Fill/Lightning/TreeNode.hpp
Fill/FillRectilinear.cpp Fill/FillRectilinear.cpp
Fill/FillRectilinear.hpp Fill/FillRectilinear.hpp
Fill/FillContour.hpp
Fill/FillContour.cpp
Flow.cpp Flow.cpp
Flow.hpp Flow.hpp
Frustum.cpp Frustum.cpp

View File

@ -432,6 +432,29 @@ bool ExPolygon::remove_colinear_points() {
return removed; return removed;
} }
double get_expolygons_area(const ExPolygons& expolys)
{
return std::accumulate(expolys.begin(), expolys.end(), (double)(0), [](double val, const ExPolygon& expoly) {
return val + expoly.area();
});
}
bool is_narrow_expolygon(const ExPolygon& expolygon, double min_width, double min_area, double remain_area_ratio_thres)
{
double original_area = expolygon.area();
if (original_area < min_area)
return true;
ExPolygons offsets = offset_ex(expolygon, -min_width / 2);
if (offsets.empty())
return true;
if (get_expolygons_area(offsets) / (original_area + EPSILON) < remain_area_ratio_thres)
return true;
return false;
}
// Do expolygons match? If they match, they must have the same topology, // Do expolygons match? If they match, they must have the same topology,
// however their contours may be rotated. // however their contours may be rotated.
bool expolygons_match(const ExPolygon &l, const ExPolygon &r) bool expolygons_match(const ExPolygon &l, const ExPolygon &r)

View File

@ -456,6 +456,9 @@ inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double toleranc
return out; return out;
} }
double get_expolygons_area(const ExPolygons& expolys);
bool is_narrow_expolygon(const ExPolygon& expolygon, double min_width, double min_area = scale_(1) * scale_(1), double remain_area_ratio_thres = 0.1);
// Do expolygons match? If they match, they must have the same topology, // Do expolygons match? If they match, they must have the same topology,
// however their contours may be rotated. // however their contours may be rotated.
bool expolygons_match(const ExPolygon &l, const ExPolygon &r); bool expolygons_match(const ExPolygon &l, const ExPolygon &r);

View File

@ -619,6 +619,7 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
case erExternalPerimeter : return L("Outer wall"); case erExternalPerimeter : return L("Outer wall");
case erOverhangPerimeter : return L("Overhang wall"); case erOverhangPerimeter : return L("Overhang wall");
case erInternalInfill : return L("Sparse infill"); case erInternalInfill : return L("Sparse infill");
case erEnsureVertical : return L("Ensure vertical");
case erSolidInfill : return L("Internal solid infill"); case erSolidInfill : return L("Internal solid infill");
case erTopSolidInfill : return L("Top surface"); case erTopSolidInfill : return L("Top surface");
case erBottomSurface : return L("Bottom surface"); case erBottomSurface : return L("Bottom surface");
@ -649,6 +650,8 @@ ExtrusionRole ExtrusionEntity::string_to_role(const std::string_view role)
return erOverhangPerimeter; return erOverhangPerimeter;
else if (role == L("Sparse infill")) else if (role == L("Sparse infill"))
return erInternalInfill; return erInternalInfill;
else if (role == L("Ensure vertical"))
return erEnsureVertical;
else if (role == L("Internal solid infill")) else if (role == L("Internal solid infill"))
return erSolidInfill; return erSolidInfill;
else if (role == L("Top surface")) else if (role == L("Top surface"))

View File

@ -48,6 +48,7 @@ enum ExtrusionRole : uint8_t {
erOverhangPerimeter, erOverhangPerimeter,
erInternalInfill, erInternalInfill,
erSolidInfill, erSolidInfill,
erEnsureVertical,
erTopSolidInfill, erTopSolidInfill,
erBottomSurface, erBottomSurface,
erIroning, erIroning,
@ -68,7 +69,8 @@ enum ExtrusionRole : uint8_t {
enum CustomizeFlag : uint8_t { enum CustomizeFlag : uint8_t {
cfNone, cfNone,
cfCircleCompensation // shaft hole tolerance compensation cfCircleCompensation, // shaft hole tolerance compensation
cfEnsureVertical
}; };
// Special flags describing loop // Special flags describing loop
@ -97,6 +99,7 @@ inline bool is_infill(ExtrusionRole role)
return role == erBridgeInfill return role == erBridgeInfill
|| role == erInternalInfill || role == erInternalInfill
|| role == erSolidInfill || role == erSolidInfill
|| role == erEnsureVertical
|| role == erTopSolidInfill || role == erTopSolidInfill
|| role == erBottomSurface || role == erBottomSurface
|| role == erIroning; || role == erIroning;
@ -111,6 +114,7 @@ inline bool is_solid_infill(ExtrusionRole role)
{ {
return role == erBridgeInfill return role == erBridgeInfill
|| role == erSolidInfill || role == erSolidInfill
|| role == erEnsureVertical
|| role == erTopSolidInfill || role == erTopSolidInfill
|| role == erBottomSurface || role == erBottomSurface
|| role == erIroning; || role == erIroning;

View File

@ -14,6 +14,7 @@
#include "FillLightning.hpp" #include "FillLightning.hpp"
#include "FillConcentricInternal.hpp" #include "FillConcentricInternal.hpp"
#include "FillConcentric.hpp" #include "FillConcentric.hpp"
#include "FillContour.hpp"
#define NARROW_INFILL_AREA_THRESHOLD 3 #define NARROW_INFILL_AREA_THRESHOLD 3
@ -134,16 +135,6 @@ struct SurfaceFill {
ExPolygons no_overlap_expolygons; ExPolygons no_overlap_expolygons;
}; };
// BBS: used to judge whether the internal solid infill area is narrow
static bool is_narrow_infill_area(const ExPolygon& expolygon)
{
ExPolygons offsets = offset_ex(expolygon, -scale_(NARROW_INFILL_AREA_THRESHOLD));
if (offsets.empty())
return true;
return false;
}
std::vector<SurfaceFill> group_fills(const Layer &layer) std::vector<SurfaceFill> group_fills(const Layer &layer)
{ {
std::vector<SurfaceFill> surface_fills; std::vector<SurfaceFill> surface_fills;
@ -177,7 +168,9 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
if (surface.is_solid()) { if (surface.is_solid()) {
params.density = 100.f; params.density = 100.f;
//FIXME for non-thick bridges, shall we allow a bottom surface pattern? //FIXME for non-thick bridges, shall we allow a bottom surface pattern?
if (surface.is_solid_infill()) if (surface.is_ensure_vertical())
params.pattern = InfillPattern::ipEnsureVertical;
else if (surface.is_solid_infill())
params.pattern = region_config.internal_solid_infill_pattern.value; params.pattern = region_config.internal_solid_infill_pattern.value;
else if (surface.is_external() && !is_bridge) else if (surface.is_external() && !is_bridge)
params.pattern = surface.is_top() ? region_config.top_surface_pattern.value : region_config.bottom_surface_pattern.value; params.pattern = surface.is_top() ? region_config.top_surface_pattern.value : region_config.bottom_surface_pattern.value;
@ -191,7 +184,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
is_bridge ? is_bridge ?
erBridgeInfill : erBridgeInfill :
(surface.is_solid() ? (surface.is_solid() ?
(surface.is_top() ? erTopSolidInfill : (surface.is_bottom()? erBottomSurface : erSolidInfill)) : (surface.is_top() ? erTopSolidInfill : (surface.is_bottom()? erBottomSurface : surface.is_ensure_vertical()?erEnsureVertical:erSolidInfill)) :
erInternalInfill); erInternalInfill);
params.bridge_angle = float(surface.bridge_angle); params.bridge_angle = float(surface.bridge_angle);
params.angle = float(Geometry::deg2rad(region_config.infill_direction.value)); params.angle = float(Geometry::deg2rad(region_config.infill_direction.value));
@ -210,6 +203,8 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
params.top_surface_speed = region_config.top_surface_speed.get_at(layer.get_extruder_id(params.extruder)); params.top_surface_speed = region_config.top_surface_speed.get_at(layer.get_extruder_id(params.extruder));
else if (params.extrusion_role == erSolidInfill) else if (params.extrusion_role == erSolidInfill)
params.solid_infill_speed = region_config.internal_solid_infill_speed.get_at(layer.get_extruder_id(params.extruder)); params.solid_infill_speed = region_config.internal_solid_infill_speed.get_at(layer.get_extruder_id(params.extruder));
else if (params.extrusion_role == erEnsureVertical)
params.solid_infill_speed = region_config.bridge_speed.get_at(layer.get_extruder_id(params.extruder));
} }
// Calculate flow spacing for infill pattern generation. // Calculate flow spacing for infill pattern generation.
if (surface.is_solid() || is_bridge) { if (surface.is_solid() || is_bridge) {
@ -363,29 +358,53 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
// BBS: detect narrow internal solid infill area and use ipConcentricInternal pattern instead // BBS: detect narrow internal solid infill area and use ipConcentricInternal pattern instead
if (layer.object()->config().detect_narrow_internal_solid_infill) { if (layer.object()->config().detect_narrow_internal_solid_infill) {
ExPolygons lower_internal_areas;
BoundingBox lower_internal_bbox;
if (layer.lower_layer) {
for (auto layerm : layer.lower_layer->regions()) {
auto internal_surfaces = layerm->fill_surfaces.filter_by_types({ stInternal,stInternalVoid });
for (auto surface : internal_surfaces)
lower_internal_areas.push_back(surface->expolygon);
}
lower_internal_bbox = get_extents(lower_internal_areas);
}
size_t surface_fills_size = surface_fills.size(); size_t surface_fills_size = surface_fills.size();
for (size_t i = 0; i < surface_fills_size; i++) { for (size_t i = 0; i < surface_fills_size; i++) {
if (surface_fills[i].surface.surface_type != stInternalSolid) if (surface_fills[i].surface.surface_type != stInternalSolid)
continue; continue;
size_t expolygons_size = surface_fills[i].expolygons.size(); size_t expolygons_size = surface_fills[i].expolygons.size();
std::vector<size_t> narrow_expolygons_index; std::vector<size_t> narrow_expoly_idx;
narrow_expolygons_index.reserve(expolygons_size); std::vector<size_t> narrow_floating_expoly_idx;
std::vector<bool> use_floating_filler;
// BBS: get the index list of narrow expolygon // BBS: get the index list of narrow expolygon
for (size_t j = 0; j < expolygons_size; j++) for (size_t j = 0; j < expolygons_size; j++) {
if (is_narrow_infill_area(surface_fills[i].expolygons[j])) auto bbox = get_extents(surface_fills[i].expolygons[j]);
narrow_expolygons_index.push_back(j); if (is_narrow_expolygon(surface_fills[i].expolygons[j], scale_(NARROW_INFILL_AREA_THRESHOLD) * 2)) {
if (bbox.overlap(lower_internal_bbox) && !intersection_ex(offset_ex(surface_fills[i].expolygons[j],SCALED_EPSILON), lower_internal_areas).empty()) {
narrow_floating_expoly_idx.emplace_back(j);
}
else {
narrow_expoly_idx.emplace_back(j);
}
}
}
if (narrow_expolygons_index.size() == 0) { if (narrow_expoly_idx.empty() && narrow_floating_expoly_idx.empty()) {
// BBS: has no narrow expolygon // BBS: has no narrow expolygon
continue; continue;
} }
else if (narrow_expolygons_index.size() == expolygons_size) { else if (narrow_floating_expoly_idx.size() == expolygons_size) {
// BBS: all expolygons are narrow, directly change the fill pattern surface_fills[i].params.pattern = ipEnsureVertical;
surface_fills[i].params.extrusion_role = erEnsureVertical;
surface_fills[i].surface.surface_type = stEnsureVertical;
}
else if (narrow_expoly_idx.size() == expolygons_size) {
surface_fills[i].params.pattern = ipConcentricInternal; surface_fills[i].params.pattern = ipConcentricInternal;
} }
else { else {
// BBS: some expolygons are narrow, spilit surface_fills[i] and rearrange the expolygons // BBS: some expolygons are narrow, spilit surface_fills[i] and rearrange the expolygons
if (!narrow_expoly_idx.empty()) {
params = surface_fills[i].params; params = surface_fills[i].params;
params.pattern = ipConcentricInternal; params.pattern = ipConcentricInternal;
surface_fills.emplace_back(params); surface_fills.emplace_back(params);
@ -394,13 +413,35 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
surface_fills.back().surface.thickness = surface_fills[i].surface.thickness; surface_fills.back().surface.thickness = surface_fills[i].surface.thickness;
surface_fills.back().region_id_group = surface_fills[i].region_id_group; surface_fills.back().region_id_group = surface_fills[i].region_id_group;
surface_fills.back().no_overlap_expolygons = surface_fills[i].no_overlap_expolygons; surface_fills.back().no_overlap_expolygons = surface_fills[i].no_overlap_expolygons;
for (size_t j = 0; j < narrow_expolygons_index.size(); j++) { for (size_t j = 0; j < narrow_expoly_idx.size(); j++) {
// BBS: move the narrow expolygons to new surface_fills.back(); // BBS: move the narrow expolygons to new surface_fills.back();
surface_fills.back().expolygons.emplace_back(std::move(surface_fills[i].expolygons[narrow_expolygons_index[j]])); surface_fills.back().expolygons.emplace_back(std::move(surface_fills[i].expolygons[narrow_expoly_idx[j]]));
} }
for (int j = narrow_expolygons_index.size() - 1; j >= 0; j--) { }
if (!narrow_floating_expoly_idx.empty()) {
params = surface_fills[i].params;
params.pattern = ipEnsureVertical;
params.extrusion_role = erEnsureVertical;
surface_fills.emplace_back(params);
surface_fills.back().region_id = surface_fills[i].region_id;
surface_fills.back().surface.surface_type = stEnsureVertical;
surface_fills.back().surface.thickness = surface_fills[i].surface.thickness;
surface_fills.back().region_id_group = surface_fills[i].region_id_group;
surface_fills.back().no_overlap_expolygons = surface_fills[i].no_overlap_expolygons;
for (size_t j = 0; j < narrow_floating_expoly_idx.size(); j++) {
// BBS: move the narrow expolygons to new surface_fills.back();
surface_fills.back().expolygons.emplace_back(std::move(surface_fills[i].expolygons[narrow_floating_expoly_idx[j]]));
}
}
std::vector<size_t> to_be_delete = narrow_floating_expoly_idx;
to_be_delete.insert(to_be_delete.end(), narrow_expoly_idx.begin(), narrow_expoly_idx.end());
std::sort(to_be_delete.begin(), to_be_delete.end());
for (int j = to_be_delete.size() - 1; j >= 0; j--) {
// BBS: delete the narrow expolygons from old surface_fills // BBS: delete the narrow expolygons from old surface_fills
surface_fills[i].expolygons.erase(surface_fills[i].expolygons.begin() + narrow_expolygons_index[j]); surface_fills[i].expolygons.erase(surface_fills[i].expolygons.begin() + to_be_delete[j]);
} }
} }
} }
@ -436,7 +477,6 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
for (LayerRegion *layerm : m_regions) for (LayerRegion *layerm : m_regions)
layerm->fills.clear(); layerm->fills.clear();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
// this->export_region_fill_surfaces_to_svg_debug("10_fill-initial"); // this->export_region_fill_surfaces_to_svg_debug("10_fill-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
@ -476,9 +516,49 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
assert(fill_concentric != nullptr); assert(fill_concentric != nullptr);
fill_concentric->print_config = &this->object()->print()->config(); fill_concentric->print_config = &this->object()->print()->config();
fill_concentric->print_object_config = &this->object()->config(); fill_concentric->print_object_config = &this->object()->config();
} else if (surface_fill.params.pattern == ipLightning) } else if (surface_fill.params.pattern == ipLightning){
dynamic_cast<FillLightning::Filler*>(f.get())->generator = lightning_generator; dynamic_cast<FillLightning::Filler*>(f.get())->generator = lightning_generator;
}
else if (surface_fill.params.pattern == ipEnsureVertical) {
FillContour* fill_contour = dynamic_cast<FillContour*>(f.get());
assert(fill_contour != nullptr);
ExPolygons lower_unsuporrt_expolys;
Polygons lower_sparse_polys;
if (lower_layer) {
for (LayerRegion* layerm : lower_layer->regions()) {
auto surfaces = layerm->fill_surfaces.filter_by_types({ stInternal,stInternalVoid });
ExPolygons sexpolys;
for (auto surface : surfaces) {
sexpolys.push_back(surface->expolygon);
}
sexpolys = union_ex(sexpolys);
lower_unsuporrt_expolys = union_ex(lower_unsuporrt_expolys, sexpolys);
}
lower_unsuporrt_expolys = shrink_ex(lower_unsuporrt_expolys, SCALED_EPSILON);
std::vector<SurfaceFill> lower_fills = group_fills(*lower_layer);
bool detect_lower_sparse_lines = true;
for (auto& fill : lower_fills) {
if (fill.params.pattern == ipAdaptiveCubic || fill.params.pattern == ipLightning || fill.params.pattern == ipSupportCubic) {
detect_lower_sparse_lines = false;
break;
}
}
if (detect_lower_sparse_lines) {
float internal_infill_width = 0;
for (auto layerm : lower_layer->regions())
internal_infill_width += layerm->flow(frInfill).scaled_width();
internal_infill_width /= lower_layer->m_regions.size();
Polylines lower_sparse_lines = lower_layer->generate_sparse_infill_polylines_for_anchoring(nullptr, nullptr, nullptr);
lower_sparse_polys = offset(lower_sparse_lines, internal_infill_width / 2);
lower_sparse_polys = union_(lower_sparse_polys);
}
}
fill_contour->lower_sparse_polys = lower_sparse_polys;
fill_contour->lower_layer_unsupport_areas = lower_unsuporrt_expolys;
fill_contour->print_config = &this->object()->print()->config();
fill_contour->print_object_config = &this->object()->config();
}
// calculate flow spacing for infill pattern generation // calculate flow spacing for infill pattern generation
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge; bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge;
double link_max_length = 0.; double link_max_length = 0.;
@ -504,7 +584,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.anchor_length = surface_fill.params.anchor_length; params.anchor_length = surface_fill.params.anchor_length;
params.anchor_length_max = surface_fill.params.anchor_length_max; params.anchor_length_max = surface_fill.params.anchor_length_max;
params.resolution = resolution; params.resolution = resolution;
params.use_arachne = surface_fill.params.pattern == ipConcentric; params.use_arachne = surface_fill.params.pattern == ipConcentric || surface_fill.params.pattern == ipEnsureVertical;
params.layer_height = m_regions[surface_fill.region_id]->layer()->height; params.layer_height = m_regions[surface_fill.region_id]->layer()->height;
// BBS // BBS
@ -522,7 +602,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.symmetric_infill_y_axis = surface_fill.params.symmetric_infill_y_axis; params.symmetric_infill_y_axis = surface_fill.params.symmetric_infill_y_axis;
} }
if (surface_fill.params.pattern == ipGrid) if (surface_fill.params.pattern == ipGrid || surface_fill.params.pattern == ipEnsureVertical)
params.can_reverse = false; params.can_reverse = false;
LayerRegion* layerm = this->m_regions[surface_fill.region_id]; LayerRegion* layerm = this->m_regions[surface_fill.region_id];
for (ExPolygon& expoly : surface_fill.expolygons) { for (ExPolygon& expoly : surface_fill.expolygons) {

View File

@ -24,6 +24,7 @@
// BBS: new infill pattern header // BBS: new infill pattern header
#include "FillConcentricInternal.hpp" #include "FillConcentricInternal.hpp"
#include "FillCrossHatch.hpp" #include "FillCrossHatch.hpp"
#include "FillContour.hpp"
// #define INFILL_DEBUG_OUTPUT // #define INFILL_DEBUG_OUTPUT
@ -58,6 +59,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipMonotonicLine: return new FillMonotonicLineWGapFill(); case ipMonotonicLine: return new FillMonotonicLineWGapFill();
case ipZigZag: return new FillZigZag(); case ipZigZag: return new FillZigZag();
case ipCrossZag: return new FillCrossZag(); case ipCrossZag: return new FillCrossZag();
case ipEnsureVertical: return new FillContour();
default: throw Slic3r::InvalidArgument("unknown type"); default: throw Slic3r::InvalidArgument("unknown type");
} }
} }

View File

@ -0,0 +1,887 @@
#include "../ClipperUtils.hpp"
#include "../Clipper2Utils.hpp"
#include "../ClipperZUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "../VariableWidth.hpp"
#include "FillContour.hpp"
#include <boost/log/trivial.hpp>
namespace Slic3r {
using ZPath = ClipperLib_Z::Path;
using ZPaths = ClipperLib_Z::Paths;
FloatingPolyline FloatingPolyline::rebase_at(size_t idx)
{
if (!this->is_closed())
return {};
FloatingPolyline ret = *this;
static_cast<Polyline&>(ret) = Polyline::rebase_at(idx);
size_t n = this->points.size();
ret.is_floating.resize(n);
for (size_t j = 0; j < n - 1; ++j) {
ret.is_floating[j] = this->is_floating[(idx + j) % (n-1)];
}
ret.is_floating.emplace_back(ret.is_floating.front());
return ret;
}
FloatingThickPolyline FloatingThickPolyline::rebase_at(size_t idx)
{
if (!this->is_closed())
return {};
FloatingThickPolyline ret = *this;
static_cast<ThickPolyline&>(ret) = ThickPolyline::rebase_at(idx);
size_t n = this->points.size();
ret.is_floating.resize(n);
for (size_t j = 0; j < n - 1; ++j) {
ret.is_floating[j] = this->is_floating[(idx + j) % (n - 1)];
}
ret.is_floating.emplace_back(ret.is_floating.front());
return ret;
}
FloatingThicklines FloatingThickPolyline::floating_thicklines() const
{
FloatingThicklines lines;
if (this->points.size() >= 2) {
lines.reserve(this->points.size() - 1);
for (size_t i = 0; i + 1 < this->points.size(); ++i)
lines.emplace_back(this->points[i], this->points[i + 1], this->width[2 * i], this->width[2 * i + 1], this->is_floating[i], this->is_floating[i + 1]);
}
return lines;
}
//BBS: new function to filter width to avoid too fragmented segments
static ExtrusionPaths floating_thick_polyline_to_extrusion_paths(const FloatingThickPolyline& floating_polyline, ExtrusionRole role, const Flow& flow, const float tolerance)
{
ExtrusionPaths paths;
ExtrusionPath path(role);
FloatingThicklines lines = floating_polyline.floating_thicklines();
size_t start_index = 0;
double max_width, min_width;
auto set_flow_for_path = [&flow](ExtrusionPath& path, double width) {
Flow new_flow = flow.with_width(unscale<float>(width) + flow.height() * float(1. - 0.25 * PI));
path.mm3_per_mm = new_flow.mm3_per_mm();
path.width = new_flow.width();
path.height = new_flow.height();
};
auto append_path_and_reset = [set_flow_for_path, role, &paths](double& length, double& sum, ExtrusionPath& path){
length = sum = 0;
paths.emplace_back(std::move(path));
path = ExtrusionPath(role);
};
for (int i = 0; i < (int)lines.size(); ++i) {
const FloatingThickline& line = lines[i];
if (i == 0) {
max_width = min_width = line.a_width;
}
const coordf_t line_len = line.length();
if (line_len < SCALED_EPSILON) continue;
double thickness_delta = std::max(fabs(max_width - line.b_width), fabs(min_width - line.b_width));
//BBS: has large difference in width
if (thickness_delta > tolerance) {
//BBS: 1 generate path from start_index to i(not included)
if (start_index != i) {
path = ExtrusionPath(role);
double length = 0, sum = 0;
bool is_floating = false;
for (int idx = start_index; idx < i; idx++) {
bool curr_floating = lines[idx].is_a_floating && lines[idx].is_b_floating;
if (curr_floating != is_floating && length != 0) {
path.polyline.append(lines[idx].a);
if (is_floating)
path.set_customize_flag(CustomizeFlag::cfEnsureVertical);
set_flow_for_path(path, sum / length);
append_path_and_reset(length, sum, path);
}
is_floating = curr_floating;
double line_length = lines[idx].length();
length += line_length;
sum += line_length * (lines[idx].a_width + lines[idx].b_width) * 0.5;
path.polyline.append(lines[idx].a);
}
path.polyline.append(lines[i].a);
if (length > SCALED_EPSILON) {
if (lines[i].is_a_floating && lines[i].is_b_floating)
path.set_customize_flag(CustomizeFlag::cfEnsureVertical);
set_flow_for_path(path, sum / length);
paths.emplace_back(std::move(path));
}
}
start_index = i;
max_width = line.a_width;
min_width = line.a_width;
//BBS: 2 handle the i-th segment
thickness_delta = fabs(line.a_width - line.b_width);
if (thickness_delta > tolerance) {
const unsigned int segments = (unsigned int)ceil(thickness_delta / tolerance);
const coordf_t seg_len = line_len / segments;
Points pp;
std::vector<coordf_t> width;
{
pp.push_back(line.a);
width.push_back(line.a_width);
for (size_t j = 1; j < segments; ++j) {
pp.push_back((line.a.cast<double>() + (line.b - line.a).cast<double>().normalized() * (j * seg_len)).cast<coord_t>());
coordf_t w = line.a_width + (j * seg_len) * (line.b_width - line.a_width) / line_len;
width.push_back(w);
width.push_back(w);
}
pp.push_back(line.b);
width.push_back(line.b_width);
assert(pp.size() == segments + 1u);
assert(width.size() == segments * 2);
}
// delete this line and insert new ones
lines.erase(lines.begin() + i);
for (size_t j = 0; j < segments; ++j) {
FloatingThickline new_line(pp[j], pp[j + 1],width[2 * j],width[2 * j+1], line.is_a_floating,line.is_b_floating);
lines.insert(lines.begin() + i + j, new_line);
}
--i;
continue;
}
}
//BBS: just update the max and min width and continue
else {
max_width = std::max(max_width, std::max(line.a_width, line.b_width));
min_width = std::min(min_width, std::min(line.a_width, line.b_width));
}
}
//BBS: handle the remaining segment
size_t final_size = lines.size();
if (start_index < final_size) {
path = ExtrusionPath(role);
double length = 0, sum = 0;
bool is_floating = false;
for (int idx = start_index; idx < final_size; idx++) {
bool curr_floating = lines[idx].is_a_floating && lines[idx].is_b_floating;
if (curr_floating!= is_floating && length != 0) {
path.polyline.append(lines[idx].a);
if(is_floating)
path.set_customize_flag(CustomizeFlag::cfEnsureVertical);
set_flow_for_path(path, sum / length);
append_path_and_reset(length, sum, path);
}
is_floating = curr_floating;
double line_length = lines[idx].length();
length += line_length;
sum += line_length * (lines[idx].a_width + lines[idx].b_width) * 0.5;
path.polyline.append(lines[idx].a);
}
path.polyline.append(lines[final_size - 1].b);
if (length > SCALED_EPSILON) {
if (lines[final_size - 1].is_a_floating && lines[final_size - 1].is_b_floating)
path.set_customize_flag(CustomizeFlag::cfEnsureVertical);
set_flow_for_path(path, sum / length);
paths.emplace_back(std::move(path));
}
}
return paths;
}
double interpolate_width(const ZPath& path,
const ThickPolyline& line,
const int subject_idx_range,
const int default_width,
size_t idx)
{
int prev_idx = idx;
while (prev_idx >= 0 && (path[prev_idx].z() < 0 || path[prev_idx].z() >= subject_idx_range))
--prev_idx;
int next_idx = idx;
while (next_idx < path.size() && (path[next_idx].z() < 0 || path[next_idx].z() >= subject_idx_range))
++next_idx;
double width_prev;
double width_next;
if (prev_idx < 0) {
width_prev = default_width;
}
else {
size_t prev_z_idx = path[prev_idx].z();
width_prev = line.get_width_at(prev_z_idx);
}
if (next_idx >= path.size()) {
width_next = default_width;
}
else {
size_t next_z_idx = path[next_idx].z();
width_next = line.get_width_at(next_z_idx);
}
Point prev(path[prev_idx].x(), path[prev_idx].y());
Point next(path[next_idx].x(), path[next_idx].y());
Point curr(path[idx].x(), path[idx].y());
double d_total = (next - prev).cast<double>().norm();
double d_curr = (curr - prev).cast<double>().norm();
double t = (d_total > 0) ? (d_curr / d_total) : 0.0;
return (1 - t) * width_prev + t * width_next;
}
FloatingThickPolyline merge_lines(ZPaths lines, const std::vector<bool>& mark_flags, const ThickPolyline& line, const int subject_idx_range ,const int default_width)
{
using PathFlag = std::vector<bool>;
using PathFlags = std::vector<PathFlag>;
std::vector<bool>used(lines.size(), false);
ZPaths merged_paths;
PathFlags merged_marks;
auto update_path_flag = [](PathFlag& mark_flags, const ZPath& path, bool mark) {
for (auto p : path)
mark_flags.emplace_back(mark);
};
std::unordered_map<int64_t, std::unordered_set<size_t>> start_z_map;
std::unordered_map<int64_t, std::unordered_set<size_t>> end_z_map;
for (size_t idx = 0; idx < lines.size(); ++idx) {
if (lines[idx].empty()) {
used[idx] = true;
continue;
}
start_z_map[lines[idx].front().z()].insert(idx);
end_z_map[lines[idx].back().z()].insert(idx);
}
auto remove_from_map = [&start_z_map, &end_z_map, &lines](size_t idx) {
if (lines[idx].empty())
return;
int64_t start_z = lines[idx].front().z();
int64_t end_z = lines[idx].back().z();
start_z_map[start_z].erase(idx);
if (start_z_map[start_z].empty())
start_z_map.erase(start_z);
end_z_map[end_z].erase(idx);
if (end_z_map[end_z].empty())
end_z_map.erase(end_z);
};
for (size_t idx = 0; idx < lines.size(); ++idx) {
if (used[idx])
continue;
ZPath curr_path = lines[idx];
PathFlag curr_mark;
update_path_flag(curr_mark, curr_path, mark_flags[idx]);
used[idx] = true;
remove_from_map(idx);
bool merged;
do {
merged = false;
int64_t curr_end = curr_path.back().z();
int64_t curr_start = curr_path.front().z();
// search after
{
if (auto start_iter = start_z_map.find(curr_end);start_iter != start_z_map.end()) {
size_t j = *start_iter->second.begin();
remove_from_map(j);
curr_path.insert(curr_path.end(), lines[j].begin(), lines[j].end());
update_path_flag(curr_mark, lines[j], mark_flags[j]);
used[j] = true;
merged = true;
}
else if (auto end_iter = end_z_map.find(curr_end); end_iter != end_z_map.end()) {
size_t j = *end_iter->second.begin();
remove_from_map(j);
std::reverse(lines[j].begin(), lines[j].end());
curr_path.insert(curr_path.end(), lines[j].begin(), lines[j].end());
update_path_flag(curr_mark, lines[j], mark_flags[j]);
used[j] = true;
merged = true;
}
}
if (merged)
continue;
//search before
{
if (auto end_iter = end_z_map.find(curr_start);end_iter != end_z_map.end()) {
size_t j = *end_iter->second.begin();
remove_from_map(j);
ZPath new_path = lines[j];
PathFlag new_mark;
update_path_flag(new_mark, new_path, mark_flags[j]);
new_path.insert(new_path.end(), curr_path.begin(), curr_path.end());
new_mark.insert(new_mark.end(), curr_mark.begin(), curr_mark.end());
curr_path = std::move(new_path);
curr_mark = std::move(new_mark);
used[j] = true;
merged = true;
}
else if (auto start_iter = start_z_map.find(curr_start); start_iter != start_z_map.end()) {
size_t j = *start_iter->second.begin();
remove_from_map(j);
ZPath new_path = lines[j];
std::reverse(new_path.begin(), new_path.end());
PathFlag new_mark;
update_path_flag(new_mark, new_path, mark_flags[j]);
new_path.insert(new_path.end(), curr_path.begin(), curr_path.end());
new_mark.insert(new_mark.end(), curr_mark.begin(), curr_mark.end());
curr_path = std::move(new_path);
curr_mark = std::move(new_mark);
used[j] = true;
merged = true;
}
}
} while (merged);
merged_paths.emplace_back(curr_path);
merged_marks.emplace_back(curr_mark);
}
assert(merged_marks.size() == 1);
FloatingThickPolyline res;
auto& valid_path = merged_paths.front();
auto& valid_mark = merged_marks.front();
for (size_t idx = 0; idx < valid_path.size(); ++idx) {
int zvalue = valid_path[idx].z();
res.points.emplace_back(valid_path[idx].x(), valid_path[idx].y());
res.is_floating.emplace_back(valid_mark[idx]);
if (0 <= zvalue && zvalue < subject_idx_range) {
res.width.emplace_back(line.get_width_at(prev_idx_modulo(zvalue, line.points)));
res.width.emplace_back(line.get_width_at(zvalue));
}
else {
double width = interpolate_width(valid_path, line, subject_idx_range, default_width, idx);
res.width.emplace_back(width);
res.width.emplace_back(width);
}
}
res.width = std::vector<coordf_t>(res.width.begin() + 1, res.width.end()-1);
assert(res.width.size() == 2 * res.points.size() - 2);
return res;
}
FloatingThickPolyline detect_floating_line(const ThickPolyline& line, const ExPolygons& floating_areas, const double default_width ,bool force_no_detect)
{
{
Polyline polyline = line;
auto bbox_line = get_extents(polyline);
auto bbox_area = get_extents(floating_areas);
if (force_no_detect || !bbox_area.overlap(bbox_line) || intersection_pl(polyline, floating_areas).empty()) {
FloatingThickPolyline res;
res.width = line.width;
res.points = line.points;
res.is_floating.resize(res.points.size(), false);
return res;
}
}
using ZPoint = ClipperLib_Z::IntPoint;
auto hash_function = [](const int a1, const int b1, const int a2, const int b2)->int32_t {
int32_t hash_val = 1000 * (a1 * 13 + b1) + (a2 * 17 + b2) + 1;
hash_val &= 0x7fffffff;
return hash_val;
};
int idx = 0;
int subject_idx_range;
ZPaths subject_paths;
{
subject_paths.emplace_back();
for (auto p : line)
subject_paths.back().emplace_back(p.x(), p.y(), idx++);
}
subject_idx_range = idx;
ZPaths clip_paths;
{
Polygons floating_polygons = to_polygons(floating_areas);
for (auto& poly : floating_polygons) {
clip_paths.emplace_back();
for (const auto& p : poly)
clip_paths.back().emplace_back(p.x(), p.y(), idx++);
}
}
ClipperLib_Z::ZFillCallback z_filler = [hash_function, subject_idx_range](const ZPoint& e1_a, const ZPoint& e1_b, const ZPoint& e2_a, const ZPoint& e2_b, ZPoint& d) {
if (e1_a.z() < subject_idx_range && e1_b.z() < subject_idx_range && e2_a.z() < subject_idx_range && e2_b.z() < subject_idx_range) {
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: both point in subject : %d, %d, %d, %d ", e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
}
if (e1_a.z() >= subject_idx_range && e1_b.z() >= subject_idx_range && e2_a.z() >= subject_idx_range && e2_b.z() >= subject_idx_range) {
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: both point in clip : %d, %d, %d, %d ", e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
}
if (e1_a.z() < 0 || e1_b.z() < 0 || e2_a.z() < 0 || e2_b.z() < 0)
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: Encounter negative z : %d, %d, %d, %d ", e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
if (e1_a.z() == e1_b.z() || e2_a.z() == e2_b.z()) {
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: Encounter same z in one line : %d, %d, %d, %d ", e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
}
if (e1_a.z() == e1_b.z() && e1_b.z() == e2_a.z() && e2_a.z() == e2_b.z()) {
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: Encounter same z in both line : %d, %d, %d, %d ", e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
// the intersect is generated by two lines in subject
d.z() = e1_a.z();
return;
} // the intersect is generate by two line from subject and clip
d.z() = -hash_function(e1_a.z(), e1_b.z(), e2_a.z(), e2_b.z());
if (d.z() >= 0)
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: hash function generate postive value : %d", d.z());
};
ZPaths intersect_out;
{
ClipperLib_Z::Clipper c;
ClipperLib_Z::PolyTree polytree;
c.ZFillFunction(z_filler);
c.AddPaths(subject_paths, ClipperLib_Z::ptSubject, false);
c.AddPaths(clip_paths, ClipperLib_Z::ptClip, true);
c.Execute(ClipperLib_Z::ctIntersection, polytree, ClipperLib_Z::pftNonZero);
ClipperLib_Z::PolyTreeToPaths(std::move(polytree), intersect_out);
}
ZPaths diff_out;
{
ClipperLib_Z::Clipper c;
ClipperLib_Z::PolyTree polytree;
c.ZFillFunction(z_filler);
c.AddPaths(subject_paths, ClipperLib_Z::ptSubject, false);
c.AddPaths(clip_paths, ClipperLib_Z::ptClip, true);
c.Execute(ClipperLib_Z::ctDifference, polytree, ClipperLib_Z::pftNonZero);
ClipperLib_Z::PolyTreeToPaths(std::move(polytree), diff_out);
}
ZPaths to_merge = diff_out;
to_merge.insert(to_merge.end(), intersect_out.begin(), intersect_out.end());
std::vector<bool>floating_flags(to_merge.size(), false);
for (size_t idx = diff_out.size(); idx < diff_out.size() + intersect_out.size(); ++idx)
floating_flags[idx] = true;
for (size_t idx = 0; idx < to_merge.size(); ++idx) {
for (auto iter = to_merge[idx].begin(); iter != to_merge[idx].end();++iter) {
if (iter->z() >= subject_idx_range) {
BOOST_LOG_TRIVIAL(error) << Slic3r::format("ZFiller: encounter idx from clip: %d",iter->z());
}
}
}
return merge_lines(to_merge,floating_flags,line,subject_idx_range,default_width);
}
int start_none_floating_idx(int idx, const std::vector<int>& none_floating_count)
{
int backtrace_idx = idx - none_floating_count[idx] + 1;
if (backtrace_idx >= 0)
return backtrace_idx;
else
return none_floating_count.size() + backtrace_idx;
}
template<typename PointContainer>
void get_none_floating_prefix(const PointContainer& container, const ExPolygons& floating_areas, const Polygons& sparse_polys, std::vector<double>& none_floating_length, std::vector<int>& none_floating_count)
{
std::vector<double>(container.points.size(), 0).swap(none_floating_length);
std::vector<int>(container.points.size(), 0).swap(none_floating_count);
std::vector<BoundingBox> floating_bboxs;
for (size_t idx = 0; idx < floating_areas.size(); ++idx)
floating_bboxs.emplace_back(get_extents(floating_areas[idx]));
std::vector<BoundingBox> sparse_bboxs;
for (size_t idx = 0; idx < sparse_polys.size(); ++idx)
sparse_bboxs.emplace_back(get_extents(sparse_polys[idx]));
auto point_in_floating_area = [&floating_bboxs, &sparse_bboxs, &floating_areas, &sparse_polys](const Point& p)->bool {
for (size_t idx = 0; idx < sparse_polys.size(); ++idx) {
if (!sparse_bboxs[idx].contains(p))
continue;
if (sparse_polys[idx].contains(p))
return false;
}
for (size_t idx = 0; idx < floating_areas.size(); ++idx) {
if (!floating_bboxs[idx].contains(p))
continue;
if (floating_areas[idx].contains(p))
return true;
}
return false;
};
for (size_t idx = 0; idx < container.points.size(); ++idx) {
const Point& p = container.points[idx];
if (!point_in_floating_area(p)) {
if (idx == 0)
none_floating_count[idx] = 1;
else
none_floating_count[idx] = none_floating_count[idx - 1] + 1;
if (none_floating_count[idx] > 1)
none_floating_length[idx] = none_floating_length[idx - 1] + ((Point)(container.points[prev_idx_modulo(idx, container.points)] - p)).cast<double>().norm();
else
none_floating_length[idx] = 0;
}
else {
none_floating_length[idx] = 0;
none_floating_count[idx] = 0;
}
}
if (none_floating_count.back() > 0) {
for (size_t idx = 0; idx < container.points.size(); ++idx) {
if (none_floating_count[idx] == 0)
break;
none_floating_count[idx] = none_floating_count[prev_idx_modulo(idx, container.points)] + 1;
none_floating_length[idx] = none_floating_length[prev_idx_modulo(idx, container.points)] + ((Point)(container.points[prev_idx_modulo(idx, container.points)] - container.points[idx])).cast<double>().norm();
}
}
}
template<typename PointContainer>
int get_best_loop_start(const PointContainer& container, const ExPolygons& floating_areas, const Polygons& sparse_polys) {
std::vector<double> none_floating_length;
std::vector<int> none_floating_count;
BoundingBox floating_bbox = get_extents(floating_areas);
BoundingBox poly_bbox(container.points);
if (!poly_bbox.overlap(floating_bbox))
return 0;
Polygons clipped_sparse_polys = ClipperUtils::clip_clipper_polygons_with_subject_bbox(sparse_polys, poly_bbox);
get_none_floating_prefix(container, floating_areas, clipped_sparse_polys, none_floating_length, none_floating_count);
int best_idx = std::distance(none_floating_length.begin(), std::max_element(none_floating_length.begin(), none_floating_length.end()));
return start_none_floating_idx(best_idx, none_floating_count);
}
template<typename PointContainer>
std::vector<int> get_loop_start_candidates(const PointContainer& container, const ExPolygons& floating_areas, const Polygons& sparse_polys)
{
std::vector<double> none_floating_length;
std::vector<int> none_floating_count;
BoundingBox floating_bbox = get_extents(floating_areas);
BoundingBox poly_bbox(container.points);
std::vector<int> candidate_list;
if (!poly_bbox.overlap(floating_bbox)) {
candidate_list.resize(container.points.size());
std::iota(candidate_list.begin(), candidate_list.end(), 0);
return candidate_list;
}
Polygons clipped_sparse_polys = ClipperUtils::clip_clipper_polygons_with_subject_bbox(sparse_polys, poly_bbox);
get_none_floating_prefix(container, floating_areas, clipped_sparse_polys, none_floating_length, none_floating_count);
for (size_t idx = 0; idx < none_floating_length.size(); ++idx) {
if (none_floating_length[idx] > 0)
candidate_list.emplace_back(start_none_floating_idx(idx, none_floating_count));
}
return candidate_list;
}
void smooth_floating_line(FloatingThickPolyline& line,coord_t max_gap_threshold, coord_t min_floating_threshold)
{
if (line.empty())
return;
struct LineParts {
int start;
int end;
bool is_floating;
};
auto build_line_parts = [&](const FloatingThickPolyline& line)->std::vector<LineParts> {
std::vector<LineParts> line_parts;
bool current_val = line.is_floating.front();
int start = 0;
for (size_t idx = 1; idx < line.is_floating.size(); ++idx) {
if (line.is_floating[idx] != current_val) {
line_parts.push_back({ start,(int)(idx - 1),current_val });
current_val = line.is_floating[idx];
start = idx;
}
}
line_parts.push_back({ start,(int)(line.is_floating.size() - 1),current_val });
return line_parts;
};
std::vector<double> distance_prefix(line.points.size(),0);
for (size_t idx = 0; idx < line.points.size();++idx) {
if (idx == 0)
distance_prefix[idx] = 0;
else {
distance_prefix[idx] = distance_prefix[idx - 1] + (line.points[idx] - line.points[idx - 1]).cast<double>().norm();
}
}
{
// remove too small gaps
std::vector<LineParts> line_parts = build_line_parts(line);
std::vector<std::pair<int, int>> gaps_to_merge;
for (size_t i = 1; i + 1 < line_parts.size(); ++i) {
const auto& curr = line_parts[i];
if (!curr.is_floating) {
const auto& prev = line_parts[i - 1];
const auto& next = line_parts[i + 1];
if (prev.is_floating && next.is_floating) {
double total_length = distance_prefix[next.start] - distance_prefix[prev.end];
if (total_length < max_gap_threshold) {
gaps_to_merge.emplace_back(curr.start, curr.end);
}
}
}
}
for (const auto& gap : gaps_to_merge) {
for (int i = gap.first; i <= gap.second; ++i) {
line.is_floating[i] = true;
}
}
}
{
std::vector<LineParts> line_parts = build_line_parts(line);
std::vector<std::pair<int, int>> segments_to_remove;
for (auto& part : line_parts) {
if (part.is_floating && distance_prefix[part.end] - distance_prefix[part.start] < min_floating_threshold) {
segments_to_remove.emplace_back(part.start, part.end);
}
}
for (const auto& seg : segments_to_remove) {
for (int i = seg.first; i <= seg.second; ++i) {
line.is_floating[i] = false;
}
}
}
}
// nearest neibour排序但是取点时只能取get_loop_start_candidates得到的点
FloatingThickPolylines FillContour::resplit_order_loops(Point curr_point, std::vector<const Arachne::ExtrusionLine*> all_extrusions, const ExPolygons& floating_areas, const Polygons& sparse_polys, const coord_t default_width)
{
FloatingThickPolylines result;
for (size_t idx = 0; idx < all_extrusions.size(); ++idx) {
if (all_extrusions[idx]->empty())
continue;
ThickPolyline thick_polyline = Arachne::to_thick_polyline(*all_extrusions[idx]);
FloatingThickPolyline thick_line_with_floating = detect_floating_line(thick_polyline, floating_areas, default_width, print_object_config->detect_floating_vertical_shell.value);
smooth_floating_line(thick_line_with_floating, scale_(2), scale_(2));
int split_idx = 0;
if (!floating_areas.empty() && all_extrusions[idx]->is_closed && thick_line_with_floating.points.front() == thick_line_with_floating.points.back()) {
if (idx == 0)
split_idx = get_best_loop_start(thick_line_with_floating, floating_areas,sparse_polys);
else {
auto candidates = get_loop_start_candidates(thick_line_with_floating, floating_areas,sparse_polys);
double min_dist = std::numeric_limits<double>::max();
for (auto candidate : candidates) {
double dist = (curr_point - thick_line_with_floating.points[candidate]).cast<double>().norm();
if (min_dist > dist) {
min_dist = dist;
split_idx = candidate;
}
}
}
FloatingThickPolyline new_line = thick_line_with_floating.rebase_at(split_idx);
assert(new_line.width.size() == 2 * new_line.points.size() - 2);
result.emplace_back(thick_line_with_floating.rebase_at(split_idx));
}
else {
assert(thick_line_with_floating.width.size() == 2 * thick_line_with_floating.points.size() - 2);
result.emplace_back(thick_line_with_floating);
}
curr_point = result.back().last_point();
}
return result;
}
#if 0
Polylines FillContour::resplit_order_loops(Point curr_point, Polygons loops, const ExPolygons& floating_areas)
{
Polylines result;
for (size_t idx = 0; idx < loops.size(); ++idx) {
const Polygon& loop = loops[idx];
int split_idx = 0;
if (!floating_areas.empty()) {
if (idx == 0)
split_idx = get_best_loop_start(loop, floating_areas);
else {
auto candidates = get_loop_start_candidates(loop, floating_areas);
double min_dist = std::numeric_limits<double>::max();
for (auto candidate : candidates) {
double dist = (curr_point - loop.points[candidate]).cast<double>().norm();
if (min_dist > dist) {
min_dist = dist;
split_idx = candidate;
}
}
}
result.emplace_back(loop.split_at_index(split_idx));
}
else {
result.emplace_back(loop.split_at_index(curr_point.nearest_point_index(loop.points)));
}
curr_point = result.back().last_point();
}
return result;
};
void FillContour::_fill_surface_single(
const FillParams& params,
unsigned int thickness_layers,
const std::pair<float, Point>& direction,
ExPolygon expolygon,
FloatingLines& polylines_out
)
{
auto expoly_bbox = get_extents(expolygon);
coord_t min_spacing = scale_(this->spacing);
coord_t distance = coord_t(min_spacing / params.density);
distance = this->_adjust_solid_spacing(expoly_bbox.size()(0), distance);
this->spacing = unscale<double>(distance);
Polygons loops = to_polygons(expolygon);
ExPolygons offseted_expolys{ std::move(expolygon) };
while (!offseted_expolys.empty()) {
offseted_expolys = offset2_ex(offseted_expolys, -(distance + min_spacing / 2), min_spacing / 2);
append(loops, to_polygons(offseted_expolys));
}
// generate paths from outermost to the inner most
loops = union_pt_chained_outside_in(loops);
auto reordered_polylines = resplit_order_loops({ 0,0 }, loops, lower_layer_unsupport_areas);
size_t i = polylines_out.size();
for (auto& polyline : reordered_polylines) {
polylines_out.emplace_back(std::move(polyline));
}
size_t j = polylines_out.size();
for (; i < polylines_out.size(); ++i) {
polylines_out[i].clip_end(this->loop_clipping);
if (polylines_out[i].is_valid()) {
if (j < i)
polylines_out[j] = std::move(polylines_out[i]);
++j;
}
}
if (j < polylines_out.size())
polylines_out.erase(polylines_out.begin() + j, polylines_out.end());
}
#endif
void FillContour::_fill_surface_single(const FillParams& params,
unsigned int thickness_layers,
const std::pair<float, Point>& direction,
ExPolygon expolygon,
FloatingThickPolylines& thick_polylines_out)
{
Point bbox_size = expolygon.contour.bounding_box().size();
coord_t min_spacing = params.flow.scaled_spacing();
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
Polygons polygons = to_polygons(expolygon);
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
Arachne::WallToolPathsParams input_params;
input_params.min_bead_width = 0.85 * min_nozzle_diameter;
input_params.min_feature_size = 0.25 * min_nozzle_diameter;
input_params.wall_transition_length = 0.4;
input_params.wall_transition_angle = 10;
input_params.wall_transition_filter_deviation = 0.25 * min_nozzle_diameter;
input_params.wall_distribution_count = 1;
Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, params.layer_height, input_params);
std::vector<Arachne::VariableWidthLines> loops = wallToolPaths.getToolPaths();
std::vector<const Arachne::ExtrusionLine*> all_extrusions;
for (Arachne::VariableWidthLines& loop : loops) {
if (loop.empty())
continue;
for (const Arachne::ExtrusionLine& wall : loop)
all_extrusions.emplace_back(&wall);
}
// Split paths using a nearest neighbor search.
size_t firts_poly_idx = thick_polylines_out.size();
auto thick_polylines = resplit_order_loops({ 0,0 }, all_extrusions, this->lower_layer_unsupport_areas, this->lower_sparse_polys,min_spacing);
append(thick_polylines_out, thick_polylines);
// clip the paths to prevent the extruder from getting exactly on the first point of the loop
// Keep valid paths only.
size_t j = firts_poly_idx;
for (size_t i = firts_poly_idx; i < thick_polylines_out.size(); ++i) {
thick_polylines_out[i].clip_end(this->loop_clipping);
if (thick_polylines_out[i].is_valid()) {
if (j < i)
thick_polylines_out[j] = std::move(thick_polylines_out[i]);
++j;
}
}
if (j < thick_polylines_out.size())
thick_polylines_out.erase(thick_polylines_out.begin() + int(j), thick_polylines_out.end());
}
FloatingThickPolylines FillContour::fill_surface_arachne_floating(const Surface* surface, const FillParams& params)
{
// Create the infills for each of the regions.
FloatingThickPolylines floating_thick_polylines_out;
for (ExPolygon& expoly : no_overlap_expolygons)
_fill_surface_single(params, surface->thickness_layers, _infill_direction(surface), std::move(expoly), floating_thick_polylines_out);
return floating_thick_polylines_out;
}
void FillContour::fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out)
{
FloatingThickPolylines floating_lines = this->fill_surface_arachne_floating(surface, params);
if (floating_lines.empty())
return;
Flow new_flow = params.flow.with_spacing(this->spacing);
double flow_mm3_per_mm = new_flow.mm3_per_mm();
double flow_width = new_flow.width();
ExtrusionEntityCollection* ecc = new ExtrusionEntityCollection();
ecc->no_sort = true;
out.push_back(ecc);
size_t idx = ecc->entities.size();
const float tolerance = float(scale_(0.05));
for (const auto& line : floating_lines) {
ExtrusionPaths paths = floating_thick_polyline_to_extrusion_paths(line, params.extrusion_role, new_flow, tolerance);
// Append paths to collection.
assert(!paths.empty());
if (!paths.empty()) {
if (paths.front().first_point() == paths.back().last_point())
ecc->entities.emplace_back(new ExtrusionLoop(std::move(paths)));
else {
for (ExtrusionPath& path : paths) {
assert(!path.empty());
ecc->entities.emplace_back(new ExtrusionPath(std::move(path)));
}
}
}
}
}
} // namespace Slic3r

View File

@ -0,0 +1,77 @@
#ifndef SLIC3R_FILLCONTOUR_HPP
#define SLIC3R_FILLCONTOUR_HPP
#include "FillBase.hpp"
#include "FillConcentric.hpp"
#include "Arachne/WallToolPaths.hpp"
namespace Slic3r{
struct FloatingThickline : public ThickLine
{
FloatingThickline(const Point& a, const Point& b, double wa, double wb, bool a_floating, bool b_floating) :ThickLine(a, b, wa, wb)
{
is_a_floating = a_floating;
is_b_floating = b_floating;
}
bool is_a_floating;
bool is_b_floating;
};
using FloatingThicklines = std::vector<FloatingThickline>;
struct FloatingPolyline : public Polyline
{
std::vector<bool> is_floating;
FloatingPolyline rebase_at(size_t idx);
};
using FloatingPolylines = std::vector<FloatingPolyline>;
struct FloatingThickPolyline :public ThickPolyline
{
std::vector<bool> is_floating;
FloatingThickPolyline rebase_at(size_t idx);
FloatingThicklines floating_thicklines()const;
};
using FloatingThickPolylines = std::vector<FloatingThickPolyline>;
class FillContour : public FillConcentric
{
public:
~FillContour() override = default;
ExPolygons lower_layer_unsupport_areas;
Polygons lower_sparse_polys;
protected:
Fill* clone() const override { return new FillContour(*this); }
#if 0
void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon expolygon,
FloatingLines &polylines_out) ;
#endif
void _fill_surface_single(const FillParams& params,
unsigned int thickness_layers,
const std::pair<float, Point>& direction,
ExPolygon expolygon,
FloatingThickPolylines& thick_polylines_out);
FloatingThickPolylines fill_surface_arachne_floating(const Surface* surface, const FillParams& params);
void fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out);
FloatingThickPolylines resplit_order_loops(Point curr_point, std::vector<const Arachne::ExtrusionLine*> all_extrusions, const ExPolygons& floating_areas, const Polygons& sparse_polys, const coord_t default_width);
#if 0
Polylines resplit_order_loops(Point curr_point, Polygons loops, const ExPolygons& floating_areas);
#endif
friend class Layer;
};
}
#endif

View File

@ -4870,7 +4870,8 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des
std::string GCode::extrude_path(ExtrusionPath path, std::string description, double speed) std::string GCode::extrude_path(ExtrusionPath path, std::string description, double speed)
{ {
// description += ExtrusionEntity::role_to_string(path.role()); // description += ExtrusionEntity::role_to_string(path.role());
std::string gcode = this->_extrude(path, description, speed); bool flag = path.get_customize_flag() == CustomizeFlag::cfEnsureVertical;
std::string gcode = this->_extrude(path, description, speed,flag);
if (m_wipe.enable && FILAMENT_CONFIG(wipe)) { if (m_wipe.enable && FILAMENT_CONFIG(wipe)) {
m_wipe.path = std::move(path.polyline); m_wipe.path = std::move(path.polyline);
m_wipe.path.reverse(); m_wipe.path.reverse();
@ -5286,7 +5287,7 @@ void GCode::smooth_speed_discontinuity_area(ExtrusionPaths &paths) {
paths = std::move(inter_paths); paths = std::move(inter_paths);
} }
std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed, bool set_holes_and_compensation_speed, bool is_first_slope) std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed, bool use_seperate_speed, bool is_first_slope)
{ {
std::string gcode; std::string gcode;
@ -5380,7 +5381,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (path.role() == erPerimeter) { if (path.role() == erPerimeter) {
speed = m_config.inner_wall_speed.get_at(cur_extruder_index()); speed = m_config.inner_wall_speed.get_at(cur_extruder_index());
//reset speed by auto compensation speed //reset speed by auto compensation speed
if(set_holes_and_compensation_speed) { if(use_seperate_speed) {
speed = m_config.circle_compensation_speed.get_at(cur_extruder_index()); speed = m_config.circle_compensation_speed.get_at(cur_extruder_index());
}else if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && path.smooth_speed != 0) }else if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && path.smooth_speed != 0)
speed = path.smooth_speed; speed = path.smooth_speed;
@ -5392,7 +5393,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
} else if (path.role() == erExternalPerimeter) { } else if (path.role() == erExternalPerimeter) {
speed = m_config.outer_wall_speed.get_at(cur_extruder_index()); speed = m_config.outer_wall_speed.get_at(cur_extruder_index());
// reset speed by auto compensation speed // reset speed by auto compensation speed
if (set_holes_and_compensation_speed) { if (use_seperate_speed) {
speed = m_config.circle_compensation_speed.get_at(cur_extruder_index()); speed = m_config.circle_compensation_speed.get_at(cur_extruder_index());
} else if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && path.smooth_speed != 0) } else if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && path.smooth_speed != 0)
speed = path.smooth_speed; speed = path.smooth_speed;
@ -5409,7 +5410,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
speed = m_config.sparse_infill_speed.get_at(cur_extruder_index()); speed = m_config.sparse_infill_speed.get_at(cur_extruder_index());
} else if (path.role() == erSolidInfill) { } else if (path.role() == erSolidInfill) {
speed = m_config.internal_solid_infill_speed.get_at(cur_extruder_index()); speed = m_config.internal_solid_infill_speed.get_at(cur_extruder_index());
} else if (path.role() == erTopSolidInfill) { } else if (path.role() == erEnsureVertical){
if(use_seperate_speed){
speed = m_config.bridge_speed.get_at(cur_extruder_index());
}
else{
speed = m_config.vertical_shell_speed.get_at(cur_extruder_index()).get_abs_value(m_config.internal_solid_infill_speed.get_at(cur_extruder_index()));
}
}
else if (path.role() == erTopSolidInfill) {
speed = m_config.top_surface_speed.get_at(cur_extruder_index()); speed = m_config.top_surface_speed.get_at(cur_extruder_index());
} else if (path.role() == erIroning) { } else if (path.role() == erIroning) {
speed = m_config.get_abs_value("ironing_speed"); speed = m_config.get_abs_value("ironing_speed");
@ -5485,7 +5494,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
assert(is_decimal_separator_point()); assert(is_decimal_separator_point());
if (set_holes_and_compensation_speed) if (use_seperate_speed)
gcode += "; Slow Down Start\n"; gcode += "; Slow Down Start\n";
if (path.role() != m_last_processor_extrusion_role) { if (path.role() != m_last_processor_extrusion_role) {
@ -5639,7 +5648,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
} }
} }
if (set_holes_and_compensation_speed) { if (use_seperate_speed) {
gcode += "; Slow Down End\n"; gcode += "; Slow Down End\n";
} }

View File

@ -5506,8 +5506,13 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
void GCodeProcessor::set_extrusion_role(ExtrusionRole role) void GCodeProcessor::set_extrusion_role(ExtrusionRole role)
{ {
m_used_filaments.process_role_cache(this); m_used_filaments.process_role_cache(this);
if (role == erEnsureVertical) {
m_extrusion_role = erSolidInfill;
}
else {
m_extrusion_role = role; m_extrusion_role = role;
} }
}
void GCodeProcessor::set_skippable_type(const std::string_view type) void GCodeProcessor::set_skippable_type(const std::string_view type)
{ {

View File

@ -558,7 +558,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
std::vector<ExpansionZone> expansion_zones{ std::vector<ExpansionZone> expansion_zones{
ExpansionZone{std::move(shells), expansion_params_into_solid_infill}, ExpansionZone{std::move(shells), expansion_params_into_solid_infill},
ExpansionZone{std::move(sparse), expansion_params_into_sparse_infill}, ExpansionZone{std::move(sparse), expansion_params_into_sparse_infill},
ExpansionZone{std::move(top_expolygons), expansion_params_into_solid_infill}, ExpansionZone{std::move(top_expolygons), expansion_params_into_solid_infill}
}; };
SurfaceCollection bridges; SurfaceCollection bridges;
@ -639,298 +639,6 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
#else #else
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
{
const bool has_infill = this->region().config().sparse_infill_density.value > 0.;
//BBS
auto nozzle_diameter = this->region().nozzle_dmr_avg(this->layer()->object()->print()->config());
const float margin = float(scale_(EXTERNAL_INFILL_MARGIN));
const float bridge_margin = std::min(float(scale_(BRIDGE_INFILL_MARGIN)), float(scale_(nozzle_diameter * BRIDGE_INFILL_MARGIN / 0.4)));
// BBS
const PrintObjectConfig& object_config = this->layer()->object()->config();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// 1) Collect bottom and bridge surfaces, each of them grown by a fixed 3mm offset
// for better anchoring.
// Bottom surfaces, grown.
Surfaces bottom;
// Bridge surfaces, initialy not grown.
Surfaces bridges;
// Top surfaces, grown.
Surfaces top;
// Internal surfaces, not grown.
Surfaces internal;
// Areas, where an infill of various types (top, bottom, bottom bride, sparse, void) could be placed.
Polygons fill_boundaries = to_polygons(this->fill_expolygons);
Polygons lower_layer_covered_tmp;
// Collect top surfaces and internal surfaces.
// Collect fill_boundaries: If we're slicing with no infill, we can't extend external surfaces over non-existent infill.
// This loop destroys the surfaces (aliasing this->fill_surfaces.surfaces) by moving into top/internal/fill_boundaries!
{
// Voids are sparse infills if infill rate is zero.
Polygons voids;
double max_grid_area = -1;
if (this->layer()->lower_layer != nullptr)
max_grid_area = this->layer()->lower_layer->get_sparse_infill_max_void_area();
for (const Surface &surface : this->fill_surfaces.surfaces) {
if (surface.is_top()) {
// Collect the top surfaces, inflate them and trim them by the bottom surfaces.
// This gives the priority to bottom surfaces.
if (max_grid_area < 0 || surface.expolygon.area() < max_grid_area)
surfaces_append(top, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
else
//BBS: Don't need to expand too much in this situation. Expand 3mm to eliminate hole and 1mm for contour
surfaces_append(top, intersection_ex(offset(surface.expolygon.contour, margin / 3.0, EXTERNAL_SURFACES_OFFSET_PARAMETERS),
offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS)), surface);
} else if (surface.surface_type == stBottom || (surface.surface_type == stBottomBridge && lower_layer == nullptr)) {
// Grown by 3mm.
surfaces_append(bottom, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
} else if (surface.surface_type == stBottomBridge) {
if (! surface.empty())
bridges.emplace_back(surface);
}
if (surface.is_internal()) {
assert(surface.surface_type == stInternal || surface.surface_type == stInternalSolid);
if (! has_infill && lower_layer != nullptr)
polygons_append(voids, surface.expolygon);
internal.emplace_back(std::move(surface));
}
}
if (! has_infill && lower_layer != nullptr && ! voids.empty()) {
// Remove voids from fill_boundaries, that are not supported by the layer below.
if (lower_layer_covered == nullptr) {
lower_layer_covered = &lower_layer_covered_tmp;
lower_layer_covered_tmp = to_polygons(lower_layer->lslices);
}
if (! lower_layer_covered->empty())
voids = diff(voids, *lower_layer_covered);
fill_boundaries = diff(fill_boundaries, voids);
}
}
#if 0
{
static int iRun = 0;
bridges.export_to_svg(debug_out_path("bridges-before-grouping-%d.svg", iRun ++), true);
}
#endif
if (bridges.empty())
{
fill_boundaries = union_safety_offset(fill_boundaries);
} else
{
// 1) Calculate the inflated bridge regions, each constrained to its island.
ExPolygons fill_boundaries_ex = union_safety_offset_ex(fill_boundaries);
std::vector<Polygons> bridges_grown;
std::vector<BoundingBox> bridge_bboxes;
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static int iRun = 0;
SVG svg(debug_out_path("3_process_external_surfaces-fill_regions-%d.svg", iRun ++).c_str(), get_extents(fill_boundaries_ex));
svg.draw(fill_boundaries_ex);
svg.draw_outline(fill_boundaries_ex, "black", "blue", scale_(0.05));
svg.Close();
}
// export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
{
// Bridge expolygons, grown, to be tested for intersection with other bridge regions.
std::vector<BoundingBox> fill_boundaries_ex_bboxes = get_extents_vector(fill_boundaries_ex);
bridges_grown.reserve(bridges.size());
bridge_bboxes.reserve(bridges.size());
for (size_t i = 0; i < bridges.size(); ++ i) {
// Find the island of this bridge.
const Point pt = bridges[i].expolygon.contour.points.front();
int idx_island = -1;
for (int j = 0; j < int(fill_boundaries_ex.size()); ++ j)
if (fill_boundaries_ex_bboxes[j].contains(pt) &&
fill_boundaries_ex[j].contains(pt)) {
idx_island = j;
break;
}
// Grown by 3mm.
//BBS: eliminate too narrow area to avoid generating bridge on top layer when wall loop is 1
//Polygons polys = offset(bridges[i].expolygon, bridge_margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
Polygons polys = offset2({ bridges[i].expolygon }, -scale_(nozzle_diameter * 0.1), bridge_margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
if (idx_island == -1) {
BOOST_LOG_TRIVIAL(trace) << "Bridge did not fall into the source region!";
} else {
// Found an island, to which this bridge region belongs. Trim it,
polys = intersection(polys, fill_boundaries_ex[idx_island]);
}
bridge_bboxes.push_back(get_extents(polys));
bridges_grown.push_back(std::move(polys));
}
}
// 2) Group the bridge surfaces by overlaps.
std::vector<size_t> bridge_group(bridges.size(), (size_t)-1);
size_t n_groups = 0;
for (size_t i = 0; i < bridges.size(); ++ i) {
// A grup id for this bridge.
size_t group_id = (bridge_group[i] == size_t(-1)) ? (n_groups ++) : bridge_group[i];
bridge_group[i] = group_id;
// For all possibly overlaping bridges:
for (size_t j = i + 1; j < bridges.size(); ++ j) {
if (! bridge_bboxes[i].overlap(bridge_bboxes[j]))
continue;
if (intersection(bridges_grown[i], bridges_grown[j]).empty())
continue;
// The two bridge regions intersect. Give them the same group id.
if (bridge_group[j] != size_t(-1)) {
// The j'th bridge has been merged with some other bridge before.
size_t group_id_new = bridge_group[j];
for (size_t k = 0; k < j; ++ k)
if (bridge_group[k] == group_id)
bridge_group[k] = group_id_new;
group_id = group_id_new;
}
bridge_group[j] = group_id;
}
}
// 3) Merge the groups with the same group id, detect bridges.
{
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges. layer" << this->layer()->print_z << ", bridge groups: " << n_groups;
for (size_t group_id = 0; group_id < n_groups; ++ group_id) {
size_t n_bridges_merged = 0;
size_t idx_last = (size_t)-1;
for (size_t i = 0; i < bridges.size(); ++ i) {
if (bridge_group[i] == group_id) {
++ n_bridges_merged;
idx_last = i;
}
}
if (n_bridges_merged == 0)
// This group has no regions assigned as these were moved into another group.
continue;
// Collect the initial ungrown regions and the grown polygons.
ExPolygons initial;
Polygons grown;
for (size_t i = 0; i < bridges.size(); ++ i) {
if (bridge_group[i] != group_id)
continue;
initial.push_back(std::move(bridges[i].expolygon));
polygons_append(grown, bridges_grown[i]);
}
// detect bridge direction before merging grown surfaces otherwise adjacent bridges
// would get merged into a single one while they need different directions
// also, supply the original expolygon instead of the grown one, because in case
// of very thin (but still working) anchors, the grown expolygon would go beyond them
double custom_angle = Geometry::deg2rad(this->region().config().bridge_angle.value);
if (custom_angle > 0.0) {
bridges[idx_last].bridge_angle = custom_angle;
} else {
auto [bridging_dir, unsupported_dist] = detect_bridging_direction(to_polygons(initial), to_polygons(lower_layer->lslices));
bridges[idx_last].bridge_angle = PI + std::atan2(bridging_dir.y(), bridging_dir.x());
}
/*
BridgeDetector bd(initial, lower_layer->lslices, this->bridging_flow(frInfill, object_config.thick_bridges).scaled_width());
#ifdef SLIC3R_DEBUG
printf("Processing bridge at layer %zu:\n", this->layer()->id());
#endif
double custom_angle = Geometry::deg2rad(this->region().config().bridge_angle.value);
if (bd.detect_angle(custom_angle)) {
bridges[idx_last].bridge_angle = bd.angle;
if (this->layer()->object()->has_support()) {
// polygons_append(this->bridged, bd.coverage());
append(this->unsupported_bridge_edges, bd.unsupported_edges());
}
} else if (custom_angle > 0) {
// Bridge was not detected (likely it is only supported at one side). Still it is a surface filled in
// using a bridging flow, therefore it makes sense to respect the custom bridging direction.
bridges[idx_last].bridge_angle = custom_angle;
}
*/
// without safety offset, artifacts are generated (GH #2494)
surfaces_append(bottom, union_safety_offset_ex(grown), bridges[idx_last]);
}
fill_boundaries = to_polygons(fill_boundaries_ex);
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done";
}
#if 0
{
static int iRun = 0;
bridges.export_to_svg(debug_out_path("bridges-after-grouping-%d.svg", iRun ++), true);
}
#endif
}
Surfaces new_surfaces;
{
// Intersect the grown surfaces with the actual fill boundaries.
Polygons bottom_polygons = to_polygons(bottom);
// Merge top and bottom in a single collection.
surfaces_append(top, std::move(bottom));
for (size_t i = 0; i < top.size(); ++ i) {
Surface &s1 = top[i];
if (s1.empty())
continue;
Polygons polys;
polygons_append(polys, to_polygons(std::move(s1)));
for (size_t j = i + 1; j < top.size(); ++ j) {
Surface &s2 = top[j];
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, to_polygons(std::move(s2)));
s2.clear();
}
}
if (s1.is_top())
// Trim the top surfaces by the bottom surfaces. This gives the priority to the bottom surfaces.
polys = diff(polys, bottom_polygons);
surfaces_append(
new_surfaces,
// Don't use a safety offset as fill_boundaries were already united using the safety offset.
intersection_ex(polys, fill_boundaries),
s1);
}
}
// Subtract the new top surfaces from the other non-top surfaces and re-add them.
Polygons new_polygons = to_polygons(new_surfaces);
for (size_t i = 0; i < internal.size(); ++ i) {
Surface &s1 = internal[i];
if (s1.empty())
continue;
Polygons polys;
polygons_append(polys, to_polygons(std::move(s1)));
for (size_t j = i + 1; j < internal.size(); ++ j) {
Surface &s2 = internal[j];
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, to_polygons(std::move(s2)));
s2.clear();
}
}
ExPolygons new_expolys = diff_ex(polys, new_polygons);
polygons_append(new_polygons, to_polygons(new_expolys));
surfaces_append(new_surfaces, std::move(new_expolys), s1);
}
this->fill_surfaces.surfaces = std::move(new_surfaces);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-final");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
#endif #endif
void LayerRegion::prepare_fill_surfaces() void LayerRegion::prepare_fill_surfaces()

View File

@ -618,6 +618,58 @@ ThickLines ThickPolyline::thicklines() const
return lines; return lines;
} }
Polyline Polyline::rebase_at(size_t idx)
{
if (!this->is_closed())
return {};
Polyline ret = *this;
size_t n = this->points.size();
for (size_t j = 0; j < n - 1; ++j) {
ret.points[j] = this->points[(idx + j) % (n - 1)];
}
ret.points[n - 1] = ret.points.front();
return ret;
}
ThickPolyline ThickPolyline::rebase_at(size_t idx)
{
if (!this->is_closed())
return {};
ThickPolyline ret = *this;
static_cast<Polyline&>(ret) = Polyline::rebase_at(idx);
size_t n = this->points.size();
ret.width.resize(2 * n - 2, 0);
auto get_in_width = [&](size_t i)->double {
if (i == 0) return this->width[0];
if (i == n - 1) return this->width.back();
return this->width[2 * i - 1];
};
auto get_out_width = [&](size_t i)->double {
if (i == 0) return this->width[0];
if (i == n - 1) return this->width.back();
return this->width[2 * i];
};
ret.width[0] = get_out_width(idx % (n-1));
for (size_t j = 1; j < n - 1; ++j) {
size_t i = (idx + j) % (n-1);
ret.width[2 * j - 1] = get_in_width(i);
ret.width[2 * j] = get_out_width(i);
}
ret.width[2 * n - 3] = ret.width.front();
return ret;
}
coordf_t ThickPolyline::get_width_at(size_t point_idx) const
{
if (point_idx < 2)
return width[point_idx];
return width[2 * point_idx - 1];
}
Lines3 Polyline3::lines() const Lines3 Polyline3::lines() const
{ {
Lines3 lines; Lines3 lines;

View File

@ -102,6 +102,8 @@ public:
void append(const Polyline& src); void append(const Polyline& src);
void append(Polyline&& src); void append(Polyline&& src);
Polyline rebase_at(size_t idx);
Point& operator[](Points::size_type idx) { return this->points[idx]; } Point& operator[](Points::size_type idx) { return this->points[idx]; }
const Point& operator[](Points::size_type idx) const { return this->points[idx]; } const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
@ -264,6 +266,8 @@ public:
Polyline::clear(); Polyline::clear();
width.clear(); width.clear();
} }
ThickPolyline rebase_at(size_t idx);
coordf_t get_width_at(size_t point_idx) const;
std::vector<coordf_t> width; std::vector<coordf_t> width;
std::pair<bool,bool> endpoints; std::pair<bool,bool> endpoints;

View File

@ -898,6 +898,7 @@ static std::vector<std::string> s_Preset_print_options {
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk", "travel_jerk", "default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk", "travel_jerk",
"filter_out_gap_fill", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", "filter_out_gap_fill", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth",
"small_perimeter_speed", "small_perimeter_threshold", "z_direction_outwall_speed_continuous", "small_perimeter_speed", "small_perimeter_threshold", "z_direction_outwall_speed_continuous",
"vertical_shell_speed","detect_floating_vertical_shell",
// calib // calib
"print_flow_ratio", "print_flow_ratio",
//Orca //Orca

View File

@ -552,6 +552,7 @@ private:
void bridge_over_infill(); void bridge_over_infill();
void clip_fill_surfaces(); void clip_fill_surfaces();
void discover_horizontal_shells(); void discover_horizontal_shells();
void merge_infill_types();
void combine_infill(); void combine_infill();
void _generate_support_material(); void _generate_support_material();
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> prepare_adaptive_infill_data( std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> prepare_adaptive_infill_data(

View File

@ -1362,6 +1362,24 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Enabled")); def->enum_labels.push_back(L("Enabled"));
def->set_default_value(new ConfigOptionEnum<EnsureVerticalThicknessLevel>(EnsureVerticalThicknessLevel::evtEnabled)); def->set_default_value(new ConfigOptionEnum<EnsureVerticalThicknessLevel>(EnsureVerticalThicknessLevel::evtEnabled));
def = this->add("vertical_shell_speed",coFloatsOrPercents);
def->label = L("Vertical shell speed");
def->tooltip = L("Speed for vertical shell area. If expressed as percentage (for example: 80%) it will be calculated on"
"the internal solid infill speed above");
def->category = L("Speed");
def->sidetext = L("mm/s or %");
def->ratio_over = "internal_solid_infill_speed";
def->min = 0;
def->mode = comAdvanced;
def->nullable = true;
def->set_default_value(new ConfigOptionFloatsOrPercentsNullable{FloatOrPercent(80, true)});
def = this->add("detect_floating_vertical_shell", coBool);
def->label = L("Detect floating vertical shells");
def->tooltip = L("Detect floating vertical shells and slow them by using bridge speed.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool{false});
def = this->add("internal_bridge_support_thickness", coFloat); def = this->add("internal_bridge_support_thickness", coFloat);
def->label = L("Internal bridge support thickness"); def->label = L("Internal bridge support thickness");
def->category = L("Strength"); def->category = L("Strength");
@ -5561,6 +5579,7 @@ std::set<std::string> print_options_with_variant = {
"small_perimeter_threshold", "small_perimeter_threshold",
"sparse_infill_speed", "sparse_infill_speed",
"internal_solid_infill_speed", "internal_solid_infill_speed",
"vertical_shell_speed",
"top_surface_speed", "top_surface_speed",
"enable_overhang_speed", //coBools "enable_overhang_speed", //coBools
"overhang_1_4_speed", "overhang_1_4_speed",

View File

@ -54,7 +54,7 @@ enum AuthorizationType {
enum InfillPattern : int { enum InfillPattern : int {
ipConcentric, ipRectilinear, ipGrid, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb, ipConcentric, ipRectilinear, ipGrid, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb,
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal,
ipLightning, ipCrossHatch, ipZigZag, ipCrossZag, ipLightning, ipCrossHatch, ipZigZag, ipCrossZag,ipEnsureVertical,
ipCount, ipCount,
}; };
@ -854,6 +854,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, tree_support_branch_diameter_angle)) ((ConfigOptionFloat, tree_support_branch_diameter_angle))
((ConfigOptionInt, tree_support_wall_count)) ((ConfigOptionInt, tree_support_wall_count))
((ConfigOptionBool, detect_narrow_internal_solid_infill)) ((ConfigOptionBool, detect_narrow_internal_solid_infill))
((ConfigOptionBool, detect_floating_vertical_shell))
// ((ConfigOptionBool, adaptive_layer_height)) // ((ConfigOptionBool, adaptive_layer_height))
((ConfigOptionFloat, support_bottom_interface_spacing)) ((ConfigOptionFloat, support_bottom_interface_spacing))
((ConfigOptionFloat, internal_bridge_support_thickness)) ((ConfigOptionFloat, internal_bridge_support_thickness))
@ -946,6 +947,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloatsNullable, top_surface_speed)) ((ConfigOptionFloatsNullable, top_surface_speed))
((ConfigOptionFloatsOrPercentsNullable, small_perimeter_speed)) ((ConfigOptionFloatsOrPercentsNullable, small_perimeter_speed))
((ConfigOptionFloatsNullable, small_perimeter_threshold)) ((ConfigOptionFloatsNullable, small_perimeter_threshold))
((ConfigOptionFloatsOrPercentsNullable, vertical_shell_speed))
((ConfigOptionInt, top_color_penetration_layers)) ((ConfigOptionInt, top_color_penetration_layers))
((ConfigOptionInt, bottom_color_penetration_layers)) ((ConfigOptionInt, bottom_color_penetration_layers))
//BBS //BBS

View File

@ -55,6 +55,8 @@ using namespace std::literals;
#include <cassert> #include <cassert>
#endif #endif
#define USE_TBB_IN_INFILL 1
namespace Slic3r { namespace Slic3r {
// Constructor is called from the main thread, therefore all Model / ModelObject / ModelIntance data are valid. // Constructor is called from the main thread, therefore all Model / ModelObject / ModelIntance data are valid.
@ -614,7 +616,7 @@ void PrintObject::infill()
const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first; const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first;
const auto& support_fill_octree = this->m_adaptive_fill_octrees.second; const auto& support_fill_octree = this->m_adaptive_fill_octrees.second;
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; //BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) { [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) {
@ -1087,7 +1089,8 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "sparse_infill_anchor" || opt_key == "sparse_infill_anchor"
|| opt_key == "sparse_infill_anchor_max" || opt_key == "sparse_infill_anchor_max"
|| opt_key == "top_surface_line_width" || opt_key == "top_surface_line_width"
|| opt_key == "initial_layer_line_width") { || opt_key == "initial_layer_line_width"
|| opt_key == "detect_floating_vertical_shell") {
steps.emplace_back(posInfill); steps.emplace_back(posInfill);
} else if (opt_key == "sparse_infill_pattern" } else if (opt_key == "sparse_infill_pattern"
|| opt_key == "symmetric_infill_y_axis" || opt_key == "symmetric_infill_y_axis"
@ -1164,7 +1167,8 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "sparse_infill_speed" || opt_key == "sparse_infill_speed"
|| opt_key == "inner_wall_speed" || opt_key == "inner_wall_speed"
|| opt_key == "internal_solid_infill_speed" || opt_key == "internal_solid_infill_speed"
|| opt_key == "top_surface_speed") { || opt_key == "top_surface_speed"
|| opt_key == "vertical_shell_speed") {
invalidated |= m_print->invalidate_step(psGCodeExport); invalidated |= m_print->invalidate_step(psGCodeExport);
} else if ( } else if (
opt_key == "flush_into_infill" opt_key == "flush_into_infill"
@ -1261,6 +1265,7 @@ void PrintObject::detect_surfaces_type()
if (interface_shells) if (interface_shells)
surfaces_new.assign(num_layers, Surfaces()); surfaces_new.assign(num_layers, Surfaces());
// interface_shell 启用与否,决定着是否区分不同材料。开启后,不同材料间的接触面都会被识别为顶面、底面
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, tbb::blocked_range<size_t>(0,
spiral_mode ? spiral_mode ?
@ -1322,7 +1327,7 @@ void PrintObject::detect_surfaces_type()
surfaces_append( surfaces_append(
bottom, bottom,
opening_ex( opening_ex(
diff_ex(layerm->slices.surfaces, lower_layer->lslices, ApplySafetyOffset::Yes), diff_ex(layerm->slices.surfaces, lower_layer->lslices, ApplySafetyOffset::Yes),//完全悬空
offset), offset),
surface_type_bottom_other); surface_type_bottom_other);
// if user requested internal shells, we need to identify surfaces // if user requested internal shells, we need to identify surfaces
@ -1334,8 +1339,8 @@ void PrintObject::detect_surfaces_type()
bottom, bottom,
opening_ex( opening_ex(
diff_ex( diff_ex(
intersection(layerm->slices.surfaces, lower_layer->lslices), // supported intersection(layerm->slices.surfaces, lower_layer->lslices), // 先扣掉完全悬空
lower_layer->m_regions[region_id]->slices.surfaces, lower_layer->m_regions[region_id]->slices.surfaces,//再扣掉同材料的区域
ApplySafetyOffset::Yes), ApplySafetyOffset::Yes),
offset), offset),
stBottom); stBottom);
@ -1355,9 +1360,6 @@ void PrintObject::detect_surfaces_type()
// and top surfaces; let's do an intersection to discover them and consider them // and top surfaces; let's do an intersection to discover them and consider them
// as bottom surfaces (to allow for bridge detection) // as bottom surfaces (to allow for bridge detection)
if (! top.empty() && ! bottom.empty()) { if (! top.empty() && ! bottom.empty()) {
// Polygons overlapping = intersection(to_polygons(top), to_polygons(bottom));
// Slic3r::debugf " layer %d contains %d membrane(s)\n", $layerm->layer->id, scalar(@$overlapping)
// if $Slic3r::debug;
Polygons top_polygons = to_polygons(std::move(top)); Polygons top_polygons = to_polygons(std::move(top));
top.clear(); top.clear();
surfaces_append(top, diff_ex(top_polygons, bottom), stTop); surfaces_append(top, diff_ex(top_polygons, bottom), stTop);
@ -1393,9 +1395,6 @@ void PrintObject::detect_surfaces_type()
surfaces_append(surfaces_out, std::move(top)); surfaces_append(surfaces_out, std::move(top));
surfaces_append(surfaces_out, std::move(bottom)); surfaces_append(surfaces_out, std::move(bottom));
// Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n",
// $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("detect_surfaces_type-final"); layerm->export_region_slices_to_svg_debug("detect_surfaces_type-final");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
@ -1556,6 +1555,8 @@ void PrintObject::discover_vertical_shells()
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom";
//FIXME Improve the heuristics for a grain size. //FIXME Improve the heuristics for a grain size.
size_t grain_size = std::max(num_layers / 16, size_t(1)); size_t grain_size = std::max(num_layers / 16, size_t(1));
// 关闭interface_shell不区分不同材料所以遍历顺序是按层遍历层中遍历region
// top区域包含墙bottom区域包含墙holes为稀疏填充区域
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) { [this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
@ -1568,21 +1569,12 @@ void PrintObject::discover_vertical_shells()
// Simulate single set of perimeters over all merged regions. // Simulate single set of perimeters over all merged regions.
float perimeter_offset = 0.f; float perimeter_offset = 0.f;
float perimeter_min_spacing = FLT_MAX; float perimeter_min_spacing = FLT_MAX;
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static size_t debug_idx = 0;
++debug_idx;
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
for (size_t region_id = 0; region_id < num_regions; ++region_id) { for (size_t region_id = 0; region_id < num_regions; ++region_id) {
LayerRegion& layerm = *layer.m_regions[region_id]; LayerRegion& layerm = *layer.m_regions[region_id];
float top_bottom_expansion = float(layerm.flow(frSolidInfill).scaled_spacing()) * top_bottom_expansion_coeff; float top_bottom_expansion = float(layerm.flow(frSolidInfill).scaled_spacing()) * top_bottom_expansion_coeff;
// Top surfaces. // Top surfaces.
append(cache.top_surfaces, offset(layerm.slices.filter_by_type(stTop), top_bottom_expansion)); append(cache.top_surfaces, offset(layerm.slices.filter_by_type(stTop), top_bottom_expansion));
// append(cache.top_surfaces, offset(layerm.fill_surfaces().filter_by_type(stTop), top_bottom_expansion));
// Bottom surfaces.
append(cache.bottom_surfaces, offset(layerm.slices.filter_by_types(surfaces_bottom), top_bottom_expansion)); append(cache.bottom_surfaces, offset(layerm.slices.filter_by_types(surfaces_bottom), top_bottom_expansion));
// append(cache.bottom_surfaces, offset(layerm.fill_surfaces().filter_by_types(surfaces_bottom), top_bottom_expansion));
// Calculate the maximum perimeter offset as if the slice was extruded with a single extruder only.
// First find the maxium number of perimeters per region slice.
unsigned int perimeters = 0; unsigned int perimeters = 0;
for (const Surface& s : layerm.slices.surfaces) for (const Surface& s : layerm.slices.surfaces)
perimeters = std::max<unsigned int>(perimeters, s.extra_perimeters); perimeters = std::max<unsigned int>(perimeters, s.extra_perimeters);
@ -1597,29 +1589,22 @@ void PrintObject::discover_vertical_shells()
} }
polygons_append(cache.holes, to_polygons(layerm.fill_expolygons)); polygons_append(cache.holes, to_polygons(layerm.fill_expolygons));
} }
// Save some computing time by reducing the number of polygons.
cache.top_surfaces = union_(cache.top_surfaces);
cache.bottom_surfaces = union_(cache.bottom_surfaces);
// For a multi-material print, simulate perimeter / infill split as if only a single extruder has been used for the whole print. // For a multi-material print, simulate perimeter / infill split as if only a single extruder has been used for the whole print.
if (perimeter_offset > 0.) { if (perimeter_offset > 0.) {
// The layer.lslices are forced to merge by expanding them first. // The layer.lslices are forced to merge by expanding them first.
// 对于多材料,按照最大墙宽度,再算一次稀疏区域
polygons_append(cache.holes, offset2(layer.lslices, 0.3f * perimeter_min_spacing, -perimeter_offset - 0.3f * perimeter_min_spacing)); polygons_append(cache.holes, offset2(layer.lslices, 0.3f * perimeter_min_spacing, -perimeter_offset - 0.3f * perimeter_min_spacing));
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.lslices));
svg.draw(layer.lslices, "blue");
svg.draw(union_ex(cache.holes), "red");
svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05));
svg.Close();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} }
// Save some computing time by reducing the number of polygons.
cache.top_surfaces = union_(cache.top_surfaces);
cache.bottom_surfaces = union_(cache.bottom_surfaces);
cache.holes = union_(cache.holes); cache.holes = union_(cache.holes);
}}); }});
m_print->throw_if_canceled(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom";
} }
// 逐region遍历
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
//FIXME Improve the heuristics for a grain size. //FIXME Improve the heuristics for a grain size.
const PrintRegion &region = this->printing_region(region_id); const PrintRegion &region = this->printing_region(region_id);
@ -1629,6 +1614,8 @@ void PrintObject::discover_vertical_shells()
size_t grain_size = std::max(num_layers / 16, size_t(1)); size_t grain_size = std::max(num_layers / 16, size_t(1));
// 开启了interface_shell代表顶底面计算时只有同region可以视为covered
// 所以此时对于某一个region先逐层计算cache的topbottom由于稀疏填充是共用的所以算一次即可
if (! top_bottom_surfaces_all_regions) { if (! top_bottom_surfaces_all_regions) {
// This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness // This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness
// is calculated over a single material. // is calculated over a single material.
@ -1662,12 +1649,17 @@ void PrintObject::discover_vertical_shells()
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness";
grain_size = 1; grain_size = 1;
// 从第低到高按层遍历
#if USE_TBB_IN_INFILL
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, region_id, &cache_top_botom_regions] [this, region_id, &cache_top_botom_regions]
(const tbb::blocked_range<size_t>& range) { (const tbb::blocked_range<size_t>& range) {
// printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
#else
for (size_t idx_layer = 0; idx_layer < num_layers; ++idx_layer) {
#endif
m_print->throw_if_canceled(); m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static size_t debug_idx = 0; static size_t debug_idx = 0;
@ -1692,28 +1684,6 @@ void PrintObject::discover_vertical_shells()
ExPolygons shell_ex; ExPolygons shell_ex;
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
float min_perimeter_infill_spacing = float(infill_line_spacing) * 1.05f; float min_perimeter_infill_spacing = float(infill_line_spacing) * 1.05f;
#if 0
// #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
Slic3r::SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", debug_idx), this->bounding_box());
for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) {
if (n < 0 || n >= (int)m_layers.size())
continue;
ExPolygons &expolys = m_layers[n]->perimeter_expolygons;
for (size_t i = 0; i < expolys.size(); ++ i) {
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", debug_idx, n, i), get_extents(expolys[i]));
svg.draw(expolys[i]);
svg.draw_outline(expolys[i].contour, "black", scale_(0.05));
svg.draw_outline(expolys[i].holes, "blue", scale_(0.05));
svg.Close();
svg_cummulative.draw(expolys[i]);
svg_cummulative.draw_outline(expolys[i].contour, "black", scale_(0.05));
svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05));
}
}
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
polygons_append(holes, cache_top_botom_regions[idx_layer].holes); polygons_append(holes, cache_top_botom_regions[idx_layer].holes);
auto combine_holes = [&holes](const Polygons &holes2) { auto combine_holes = [&holes](const Polygons &holes2) {
if (holes.empty() || holes2.empty()) if (holes.empty() || holes2.empty())
@ -1792,18 +1762,6 @@ void PrintObject::discover_vertical_shells()
(i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_shell_thickness - EPSILON)) (i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_shell_thickness - EPSILON))
combine_holes(cache_top_botom_regions[i].holes); combine_holes(cache_top_botom_regions[i].holes);
} }
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell));
svg.draw(shell);
svg.draw_outline(shell, "black", scale_(0.05));
svg.Close();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
#if 0
// shell = union_(shell, true);
shell = union_(shell, false);
#endif
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
shell_ex = union_safety_offset_ex(shell); shell_ex = union_safety_offset_ex(shell);
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
@ -1811,42 +1769,6 @@ void PrintObject::discover_vertical_shells()
//if (shell.empty()) //if (shell.empty())
// continue; // continue;
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell));
svg.draw(shell_ex);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces().filter_by_type(stInternal), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces().filter_by_type(stInternal), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
}
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces().filter_by_type(stInternalVoid), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces().filter_by_type(stInternalVoid), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
}
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalsolid-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces().filter_by_type(stInternalSolid), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces().filter_by_type(stInternalSolid), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Trim the shells region by the internal & internal void surfaces. // Trim the shells region by the internal & internal void surfaces.
const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types({ stInternal, stInternalVoid, stInternalSolid })); const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types({ stInternal, stInternalVoid, stInternalSolid }));
shell = intersection(shell, polygonsInternal, ApplySafetyOffset::Yes); shell = intersection(shell, polygonsInternal, ApplySafetyOffset::Yes);
@ -1912,10 +1834,12 @@ void PrintObject::discover_vertical_shells()
}), }),
regularized_shell.end()); regularized_shell.end());
} }
if (regularized_shell.empty()) if (regularized_shell.empty())
continue; continue;
ExPolygons new_internal_solid = intersection_ex(polygonsInternal, regularized_shell); ExPolygons new_internal_solid = intersection_ex(polygonsInternal, regularized_shell);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{ {
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", debug_idx), get_extents(shell_before)); Slic3r::SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", debug_idx), get_extents(shell_before));
@ -1928,7 +1852,6 @@ void PrintObject::discover_vertical_shells()
svg.Close(); svg.Close();
} }
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Trim the internal & internalvoid by the shell. // Trim the internal & internalvoid by the shell.
Slic3r::ExPolygons new_internal = diff_ex(layerm->fill_surfaces.filter_by_type(stInternal), regularized_shell); Slic3r::ExPolygons new_internal = diff_ex(layerm->fill_surfaces.filter_by_type(stInternal), regularized_shell);
Slic3r::ExPolygons new_internal_void = diff_ex(layerm->fill_surfaces.filter_by_type(stInternalVoid), regularized_shell); Slic3r::ExPolygons new_internal_void = diff_ex(layerm->fill_surfaces.filter_by_type(stInternalVoid), regularized_shell);
@ -1947,7 +1870,9 @@ void PrintObject::discover_vertical_shells()
layerm->fill_surfaces.append(new_internal_void, stInternalVoid); layerm->fill_surfaces.append(new_internal_void, stInternalVoid);
layerm->fill_surfaces.append(new_internal_solid, stInternalSolid); layerm->fill_surfaces.append(new_internal_solid, stInternalSolid);
} // for each layer } // for each layer
#if USE_TBB_IN_INFILL
}); });
#endif
m_print->throw_if_canceled(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end";
@ -1965,181 +1890,13 @@ void PrintObject::discover_vertical_shells()
// PROFILE_OUTPUT(debug_out_path("discover_vertical_shells-profile.txt").c_str()); // PROFILE_OUTPUT(debug_out_path("discover_vertical_shells-profile.txt").c_str());
} }
#if 0
/* This method applies bridge flow to the first internal solid layer above
sparse infill */
void PrintObject::bridge_over_infill()
{
BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info();
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
const PrintRegion &region = this->printing_region(region_id);
// skip bridging in case there are no voids
if (region.config().sparse_infill_density.value == 100)
continue;
for (LayerPtrs::iterator layer_it = m_layers.begin(); layer_it != m_layers.end(); ++ layer_it) {
// skip first layer
if (layer_it == m_layers.begin())
continue;
Layer *layer = *layer_it;
LayerRegion *layerm = layer->m_regions[region_id];
const PrintObjectConfig& object_config = layer->object()->config();
//BBS: enable thick bridge for internal bridge only
Flow bridge_flow = layerm->bridging_flow(frSolidInfill, true);
// extract the stInternalSolid surfaces that might be transformed into bridges
Polygons internal_solid;
layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid);
// check whether the lower area is deep enough for absorbing the extra flow
// (for obvious physical reasons but also for preventing the bridge extrudates
// from overflowing in 3D preview)
ExPolygons to_bridge;
{
Polygons to_bridge_pp = internal_solid;
// iterate through lower layers spanned by bridge_flow
double bottom_z = layer->print_z - bridge_flow.height() - EPSILON;
for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) {
const Layer* lower_layer = m_layers[i];
// stop iterating if layer is lower than bottom_z
if (lower_layer->print_z < bottom_z) break;
// iterate through regions and collect internal surfaces
Polygons lower_internal;
for (LayerRegion *lower_layerm : lower_layer->m_regions)
lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal);
// intersect such lower internal surfaces with the candidate solid surfaces
to_bridge_pp = intersection(to_bridge_pp, lower_internal);
}
// BBS: expand to make avoid gap between bridge and inner wall
to_bridge_pp = expand(to_bridge_pp, bridge_flow.scaled_width());
to_bridge_pp = intersection(to_bridge_pp, internal_solid);
// there's no point in bridging too thin/short regions
//FIXME Vojtech: The offset2 function is not a geometric offset,
// therefore it may create 1) gaps, and 2) sharp corners, which are outside the original contour.
// The gaps will be filled by a separate region, which makes the infill less stable and it takes longer.
{
float min_width = float(bridge_flow.scaled_width()) * 3.f;
to_bridge_pp = opening(to_bridge_pp, min_width);
}
if (to_bridge_pp.empty()) continue;
// convert into ExPolygons
to_bridge = union_ex(to_bridge_pp);
}
#ifdef SLIC3R_DEBUG
printf("Bridging %zu internal areas at layer %zu\n", to_bridge.size(), layer->id());
#endif
// compute the remaning internal solid surfaces as difference
ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, ApplySafetyOffset::Yes);
to_bridge = intersection_ex(to_bridge, internal_solid, ApplySafetyOffset::Yes);
// build the new collection of fill_surfaces
layerm->fill_surfaces.remove_type(stInternalSolid);
for (ExPolygon &ex : to_bridge) {
layerm->fill_surfaces.surfaces.push_back(Surface(stInternalBridge, ex));
// BBS: detect angle for internal bridge infill
InternalBridgeDetector ibd(ex, layerm->fill_no_overlap_expolygons, bridge_flow.scaled_spacing());
if (ibd.detect_angle()) {
(layerm->fill_surfaces.surfaces.end() - 1)->bridge_angle = ibd.angle;
}
}
for (ExPolygon &ex : not_to_bridge)
layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, ex));
//BBS: modify stInternal to be stInternalWithLoop to give better support to internal bridge
if (!to_bridge.empty()){
float internal_loop_thickness = object_config.internal_bridge_support_thickness.value;
double bottom_z = layer->print_z - layer->height - internal_loop_thickness + EPSILON;
//BBS: lighting infill doesn't support this feature. Don't need to add loop when infill density is high than 50%
if (region.config().sparse_infill_pattern != InfillPattern::ipLightning && region.config().sparse_infill_density.value < 50)
for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) {
const Layer* lower_layer = m_layers[i];
if (lower_layer->print_z < bottom_z) break;
for (LayerRegion* lower_layerm : lower_layer->m_regions) {
Polygons lower_internal;
lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal);
ExPolygons internal_with_loop = intersection_ex(lower_internal, to_bridge);
ExPolygons internal = diff_ex(lower_internal, to_bridge);
if (internal_with_loop.empty()) {
//BBS: don't need to do anything
}
else if (internal.empty()) {
lower_layerm->fill_surfaces.change_to_new_type(stInternal, stInternalWithLoop);
}
else {
lower_layerm->fill_surfaces.remove_type(stInternal);
for (ExPolygon& ex : internal_with_loop)
lower_layerm->fill_surfaces.surfaces.push_back(Surface(stInternalWithLoop, ex));
for (ExPolygon& ex : internal)
lower_layerm->fill_surfaces.surfaces.push_back(Surface(stInternal, ex));
}
}
}
}
/*
# exclude infill from the layers below if needed
# see discussion at https://github.com/alexrj/Slic3r/issues/240
# Update: do not exclude any infill. Sparse infill is able to absorb the excess material.
if (0) {
my $excess = $layerm->extruders->{infill}->bridge_flow->width - $layerm->height;
for (my $i = $layer_id-1; $excess >= $self->get_layer($i)->height; $i--) {
Slic3r::debugf " skipping infill below those areas at layer %d\n", $i;
foreach my $lower_layerm (@{$self->get_layer($i)->regions}) {
my @new_surfaces = ();
# subtract the area from all types of surfaces
foreach my $group (@{$lower_layerm->fill_surfaces->group}) {
push @new_surfaces, map $group->[0]->clone(expolygon => $_),
@{diff_ex(
[ map $_->p, @$group ],
[ map @$_, @$to_bridge ],
)};
push @new_surfaces, map Slic3r::Surface->new(
expolygon => $_,
surface_type => stInternalVoid,
), @{intersection_ex(
[ map $_->p, @$group ],
[ map @$_, @$to_bridge ],
)};
}
$lower_layerm->fill_surfaces->clear;
$lower_layerm->fill_surfaces->append($_) for @new_surfaces;
}
$excess -= $self->get_layer($i)->height;
}
}
*/
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("7_bridge_over_infill");
layerm->export_region_fill_surfaces_to_svg_debug("7_bridge_over_infill");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
m_print->throw_if_canceled();
}
}
}
#else
// This method applies bridge flow to the first internal solid layer above sparse infill. // This method applies bridge flow to the first internal solid layer above sparse infill.
// This method applies bridge flow to the first internal solid layer above sparse infill. // This method applies bridge flow to the first internal solid layer above sparse infill.
void PrintObject::bridge_over_infill() void PrintObject::bridge_over_infill()
{ {
BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Start" << log_memory_info(); BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Start" << log_memory_info();
// CandidateSurface存放一个需要桥接的区域
struct CandidateSurface struct CandidateSurface
{ {
CandidateSurface(const Surface *original_surface, CandidateSurface(const Surface *original_surface,
@ -2160,6 +1917,7 @@ void PrintObject::bridge_over_infill()
double bridge_angle; double bridge_angle;
}; };
// 按层存放surface存放着待桥接的信息
std::map<size_t, std::vector<CandidateSurface>> surfaces_by_layer; std::map<size_t, std::vector<CandidateSurface>> surfaces_by_layer;
// SECTION to gather and filter surfaces for expanding, and then cluster them by layer // SECTION to gather and filter surfaces for expanding, and then cluster them by layer
@ -2167,6 +1925,7 @@ void PrintObject::bridge_over_infill()
tbb::concurrent_vector<CandidateSurface> candidate_surfaces; tbb::concurrent_vector<CandidateSurface> candidate_surfaces;
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this), tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this),
&candidate_surfaces](tbb::blocked_range<size_t> r) { &candidate_surfaces](tbb::blocked_range<size_t> r) {
// 按层并行
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
const Layer *layer = po->get_layer(lidx); const Layer *layer = po->get_layer(lidx);
if (layer->lower_layer == nullptr) { if (layer->lower_layer == nullptr) {
@ -2174,8 +1933,9 @@ void PrintObject::bridge_over_infill()
} }
double spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing(); double spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing();
// unsupported area will serve as a filter for polygons worth bridging. // unsupported area will serve as a filter for polygons worth bridging.
Polygons unsupported_area; Polygons unsupported_area; // 下一层不提供支撑的区域
Polygons lower_layer_solids; Polygons lower_layer_solids; // 下一层的实心区域,可以提供支撑
// 取当前层和下一层的数据
for (const LayerRegion *region : layer->lower_layer->regions()) { for (const LayerRegion *region : layer->lower_layer->regions()) {
Polygons fill_polys = to_polygons(region->fill_expolygons); Polygons fill_polys = to_polygons(region->fill_expolygons);
// initially consider the whole layer unsupported, but also gather solid layers to later cut off supported parts // initially consider the whole layer unsupported, but also gather solid layers to later cut off supported parts
@ -2197,9 +1957,9 @@ void PrintObject::bridge_over_infill()
unsupported_area = diff(unsupported_area, lower_layer_solids); unsupported_area = diff(unsupported_area, lower_layer_solids);
for (LayerRegion *region : layer->regions()) { for (LayerRegion *region : layer->regions()) {
SurfacesPtr region_internal_solids = region->fill_surfaces.filter_by_type(stInternalSolid); auto region_internal_solids = region->fill_surfaces.filter_by_types({ stInternalSolid,stEnsureVertical }); // 取当前层的实心区域
for (const Surface *s : region_internal_solids) { for (const Surface *s : region_internal_solids) {
Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); // 当前层需要生成桥接的区域,通过当前层的实心区域与下一层的非实心区域求交得到
// The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported. // The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported.
// These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs // These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs
bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON;
@ -2369,8 +2129,8 @@ void PrintObject::bridge_over_infill()
std::vector<std::vector<size_t>> clustered_layers_for_threads; std::vector<std::vector<size_t>> clustered_layers_for_threads;
float target_flow_height_factor = 0.9f; float target_flow_height_factor = 0.9f;
{ {
std::vector<size_t> layers_with_candidates; std::vector<size_t> layers_with_candidates; // 存储所有需要生成桥接的层号
std::map<size_t, Polygons> layer_area_covered_by_candidates; std::map<size_t, Polygons> layer_area_covered_by_candidates; // 存储每一层需要生成桥接区域的bbox的并集
for (const auto& pair : surfaces_by_layer) { for (const auto& pair : surfaces_by_layer) {
layers_with_candidates.push_back(pair.first); layers_with_candidates.push_back(pair.first);
layer_area_covered_by_candidates[pair.first] = {}; layer_area_covered_by_candidates[pair.first] = {};
@ -2381,6 +2141,7 @@ void PrintObject::bridge_over_infill()
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer, tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer,
&layer_area_covered_by_candidates]( &layer_area_covered_by_candidates](
tbb::blocked_range<size_t> r) { tbb::blocked_range<size_t> r) {
// 按层并行
for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
size_t lidx = layers_with_candidates[job_idx]; size_t lidx = layers_with_candidates[job_idx];
for (const auto &candidate : surfaces_by_layer.at(lidx)) { for (const auto &candidate : surfaces_by_layer.at(lidx)) {
@ -2392,7 +2153,8 @@ void PrintObject::bridge_over_infill()
}); });
// note: surfaces_by_layer is ordered map // note: surfaces_by_layer is ordered map
for (auto pair : surfaces_by_layer) { for (const auto &pair : surfaces_by_layer) {
// 初次操作 || z方向距离较远 || 桥接区域无交集, 那么就可以重新划分一个组,否则分配到前一个组
if (clustered_layers_for_threads.empty() || if (clustered_layers_for_threads.empty() ||
this->get_layer(clustered_layers_for_threads.back().back())->print_z < this->get_layer(clustered_layers_for_threads.back().back())->print_z <
this->get_layer(pair.first)->print_z - this->get_layer(pair.first)->print_z -
@ -2442,6 +2204,7 @@ void PrintObject::bridge_over_infill()
} }
} }
} }
// 收集一定z范围内的稀疏和实心区域判断有没有交集如果有交集则不能使用thick bridge(thick bridge的流量会侵占实心区域)
layers_sparse_infill = union_ex(layers_sparse_infill); layers_sparse_infill = union_ex(layers_sparse_infill);
layers_sparse_infill = closing_ex(layers_sparse_infill, float(SCALED_EPSILON)); layers_sparse_infill = closing_ex(layers_sparse_infill, float(SCALED_EPSILON));
not_sparse_infill = union_ex(not_sparse_infill); not_sparse_infill = union_ex(not_sparse_infill);
@ -2749,6 +2512,7 @@ void PrintObject::bridge_over_infill()
coordf_t spacing = surfaces_by_layer[lidx].front().region->bridging_flow(frSolidInfill, true).scaled_spacing(); coordf_t spacing = surfaces_by_layer[lidx].front().region->bridging_flow(frSolidInfill, true).scaled_spacing();
coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->bridging_flow(frSolidInfill, true).height() * coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->bridging_flow(frSolidInfill, true).height() *
target_flow_height_factor; target_flow_height_factor;
// 收集当前层中可以应用thick_bridge的区域
Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height); Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height);
{ {
@ -2770,16 +2534,17 @@ void PrintObject::bridge_over_infill()
} }
} }
} }
// 再减去别的层已经生成的桥接区域
deep_infill_area = diff(deep_infill_area, filled_polyons_on_lower_layers); deep_infill_area = diff(deep_infill_area, filled_polyons_on_lower_layers);
} }
// 得到thick_bridge区域bridge区域扩1.5倍
deep_infill_area = expand(deep_infill_area, spacing * 1.5); deep_infill_area = expand(deep_infill_area, spacing * 1.5);
// Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors
Polygons lightning_area; Polygons lightning_area;
Polygons expansion_area; Polygons expansion_area; // 可以提供扩张的区域
Polygons total_fill_area; Polygons total_fill_area; // 所有填充区域
Polygons top_area; Polygons top_area; // 顶面区域
for (LayerRegion *region : layer->regions()) { for (LayerRegion *region : layer->regions()) {
Polygons internal_polys = to_polygons(region->fill_surfaces.filter_by_types({stInternal, stInternalSolid})); Polygons internal_polys = to_polygons(region->fill_surfaces.filter_by_types({stInternal, stInternalSolid}));
@ -2808,7 +2573,7 @@ void PrintObject::bridge_over_infill()
expanded_surfaces.reserve(surfaces_by_layer[lidx].size()); expanded_surfaces.reserve(surfaces_by_layer[lidx].size());
for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) {
const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true);
Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing()); Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing()); // 待生成桥接区域
area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area); area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area);
ExPolygons area_to_be_bridge_ex = union_ex(area_to_be_bridge); ExPolygons area_to_be_bridge_ex = union_ex(area_to_be_bridge);
area_to_be_bridge_ex.erase(std::remove_if(area_to_be_bridge_ex.begin(), area_to_be_bridge_ex.end(), area_to_be_bridge_ex.erase(std::remove_if(area_to_be_bridge_ex.begin(), area_to_be_bridge_ex.end(),
@ -2819,7 +2584,7 @@ void PrintObject::bridge_over_infill()
area_to_be_bridge = to_polygons(area_to_be_bridge_ex); area_to_be_bridge = to_polygons(area_to_be_bridge_ex);
Polygons limiting_area = union_(area_to_be_bridge, expansion_area); Polygons limiting_area = union_(area_to_be_bridge, expansion_area); // 桥接区域 + 可扩张区域
if (area_to_be_bridge.empty()) if (area_to_be_bridge.empty())
continue; continue;
@ -2899,18 +2664,19 @@ void PrintObject::bridge_over_infill()
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = this, &surfaces_by_layer](tbb::blocked_range<size_t> r) { tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = this, &surfaces_by_layer](tbb::blocked_range<size_t> r) {
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
// 如果既不需要生成桥接,也不是桥接的下一层,不处理
if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end() && surfaces_by_layer.find(lidx + 1) == surfaces_by_layer.end()) if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end() && surfaces_by_layer.find(lidx + 1) == surfaces_by_layer.end())
continue; continue;
Layer *layer = po->get_layer(lidx); Layer *layer = po->get_layer(lidx);
Polygons cut_from_infill{}; Polygons cut_from_infill{}; // 桥接区域
if (surfaces_by_layer.find(lidx) != surfaces_by_layer.end()) { if (surfaces_by_layer.find(lidx) != surfaces_by_layer.end()) {
for (const auto &surface : surfaces_by_layer.at(lidx)) { for (const auto &surface : surfaces_by_layer.at(lidx)) {
cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end()); cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end());
} }
} }
Polygons additional_ensuring_areas{}; Polygons additional_ensuring_areas{}; // 下一层为上一层桥接需要生成的区域
if (surfaces_by_layer.find(lidx + 1) != surfaces_by_layer.end()) { if (surfaces_by_layer.find(lidx + 1) != surfaces_by_layer.end()) {
for (const auto &surface : surfaces_by_layer.at(lidx + 1)) { for (const auto &surface : surfaces_by_layer.at(lidx + 1)) {
auto additional_area = diff(surface.new_polys, auto additional_area = diff(surface.new_polys,
@ -2922,12 +2688,12 @@ void PrintObject::bridge_over_infill()
for (LayerRegion *region : layer->regions()) { for (LayerRegion *region : layer->regions()) {
Surfaces new_surfaces; Surfaces new_surfaces;
Polygons near_perimeters = to_polygons(union_safety_offset_ex(to_polygons(region->fill_surfaces.surfaces))); Polygons near_perimeters = to_polygons(union_safety_offset_ex(to_polygons(region->fill_surfaces.surfaces))); // 填充区域中,紧靠着外墙的区域
near_perimeters = diff(near_perimeters, shrink(near_perimeters, region->flow(frSolidInfill).scaled_spacing())); near_perimeters = diff(near_perimeters, shrink(near_perimeters, region->flow(frSolidInfill).scaled_spacing()));
ExPolygons additional_ensuring = intersection_ex(additional_ensuring_areas, near_perimeters); ExPolygons additional_ensuring = intersection_ex(additional_ensuring_areas, near_perimeters); // 紧靠着外墙,能够给上一层的桥接提供支撑的区域
SurfacesPtr internal_infills = region->fill_surfaces.filter_by_type(stInternal); SurfacesPtr internal_infills = region->fill_surfaces.filter_by_type(stInternal);
ExPolygons new_internal_infills = diff_ex(internal_infills, cut_from_infill); ExPolygons new_internal_infills = diff_ex(internal_infills, cut_from_infill); // 新的稀疏填充区域,去掉生成的桥接区域
new_internal_infills = diff_ex(new_internal_infills, additional_ensuring); new_internal_infills = diff_ex(new_internal_infills, additional_ensuring);
for (const ExPolygon &ep : new_internal_infills) { for (const ExPolygon &ep : new_internal_infills) {
new_surfaces.emplace_back(stInternal, ep); new_surfaces.emplace_back(stInternal, ep);
@ -2949,8 +2715,8 @@ void PrintObject::bridge_over_infill()
} }
} }
} }
ExPolygons new_internal_solids = to_expolygons(internal_solids); ExPolygons new_internal_solids = to_expolygons(internal_solids);
new_internal_solids.insert(new_internal_solids.end(), additional_ensuring.begin(), additional_ensuring.end());
new_internal_solids = diff_ex(new_internal_solids, cut_from_infill); new_internal_solids = diff_ex(new_internal_solids, cut_from_infill);
new_internal_solids = union_safety_offset_ex(new_internal_solids); new_internal_solids = union_safety_offset_ex(new_internal_solids);
for (const ExPolygon &ep : new_internal_solids) { for (const ExPolygon &ep : new_internal_solids) {
@ -2976,10 +2742,6 @@ void PrintObject::bridge_over_infill()
} // void PrintObject::bridge_over_infill() } // void PrintObject::bridge_over_infill()
#endif
static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders) static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders)
{ {
if (opt.value > (int)num_extruders) if (opt.value > (int)num_extruders)
@ -3316,16 +3078,6 @@ void PrintObject::discover_horizontal_shells()
Layer* layer = m_layers[i]; Layer* layer = m_layers[i];
LayerRegion* layerm = layer->regions()[region_id]; LayerRegion* layerm = layer->regions()[region_id];
const PrintRegionConfig& region_config = layerm->region().config(); const PrintRegionConfig& region_config = layerm->region().config();
#if 0
if (region_config.solid_infill_every_layers.value > 0 && region_config.sparse_infill_density.value > 0 &&
(i % region_config.solid_infill_every_layers) == 0) {
// Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge.
SurfaceType type = (region_config.sparse_infill_density == 100 || region_config.solid_infill_every_layers == 1) ? stInternalSolid : stInternalBridge;
for (Surface& surface : layerm->fill_surfaces.surfaces)
if (surface.surface_type == stInternal)
surface.surface_type = type;
}
#endif
// If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells(). // If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells().
if (region_config.ensure_vertical_shell_thickness.value!=EnsureVerticalThicknessLevel::evtDisabled) if (region_config.ensure_vertical_shell_thickness.value!=EnsureVerticalThicknessLevel::evtDisabled)
continue; continue;

View File

@ -38,6 +38,7 @@ const char* surface_type_to_color_name(const SurfaceType surface_type)
case stBottom: return "rgb(0,255,0)"; // "green"; case stBottom: return "rgb(0,255,0)"; // "green";
case stBottomBridge: return "rgb(0,0,255)"; // "blue"; case stBottomBridge: return "rgb(0,0,255)"; // "blue";
case stInternal: return "rgb(255,255,128)"; // yellow case stInternal: return "rgb(255,255,128)"; // yellow
case stEnsureVertical:
case stInternalSolid: return "rgb(255,0,255)"; // magenta case stInternalSolid: return "rgb(255,0,255)"; // magenta
case stInternalBridge: return "rgb(0,255,255)"; case stInternalBridge: return "rgb(0,255,255)";
case stInternalVoid: return "rgb(128,128,128)"; case stInternalVoid: return "rgb(128,128,128)";

View File

@ -15,6 +15,7 @@ enum SurfaceType {
stBottomBridge, stBottomBridge,
// Normal sparse infill. // Normal sparse infill.
stInternal, stInternal,
stEnsureVertical,
// Full infill, supporting the top surfaces and/or defining the verticall wall thickness. // Full infill, supporting the top surfaces and/or defining the verticall wall thickness.
stInternalSolid, stInternalSolid,
// 1st layer of dense infill over sparse infill, printed with a bridging extrusion flow. // 1st layer of dense infill over sparse infill, printed with a bridging extrusion flow.
@ -106,7 +107,8 @@ public:
bool is_bridge() const { return this->surface_type == stBottomBridge || this->surface_type == stInternalBridge; } bool is_bridge() const { return this->surface_type == stBottomBridge || this->surface_type == stInternalBridge; }
bool is_external() const { return this->is_top() || this->is_bottom(); } bool is_external() const { return this->is_top() || this->is_bottom(); }
bool is_internal() const { return ! this->is_external(); } bool is_internal() const { return ! this->is_external(); }
bool is_solid() const { return this->is_external() || this->surface_type == stInternalSolid || this->surface_type == stInternalBridge; } bool is_ensure_vertical() const { return this->surface_type == stEnsureVertical; }
bool is_solid() const { return this->is_external() || this->is_ensure_vertical() || this->surface_type == stInternalSolid || this->surface_type == stInternalBridge; }
bool is_solid_infill() const { return this->surface_type == stInternalSolid; } bool is_solid_infill() const { return this->surface_type == stInternalSolid; }
}; };

View File

@ -650,6 +650,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
toggle_line(el, support_is_tree); toggle_line(el, support_is_tree);
toggle_line("support_critical_regions_only", is_auto(support_type) && support_is_tree); toggle_line("support_critical_regions_only", is_auto(support_type) && support_is_tree);
for (auto el : { "detect_floating_vertical_shell", "vertical_shell_speed" })
toggle_line(el, config->option<ConfigOptionEnum<EnsureVerticalThicknessLevel>>("ensure_vertical_shell_thickness")->value != EnsureVerticalThicknessLevel::evtDisabled);
// tree support use max_bridge_length instead of bridge_no_support // tree support use max_bridge_length instead of bridge_no_support
toggle_line("bridge_no_support", !support_is_tree); toggle_line("bridge_no_support", !support_is_tree);
// only normal support has bottom interfaces // only normal support has bottom interfaces

View File

@ -745,6 +745,7 @@ const std::vector<GCodeViewer::Color> GCodeViewer::Extrusion_Role_Colors {{
{ 0.12f, 0.12f, 1.00f, 1.0f }, // erOverhangPerimeter { 0.12f, 0.12f, 1.00f, 1.0f }, // erOverhangPerimeter
{ 0.69f, 0.19f, 0.16f, 1.0f }, // erInternalInfill { 0.69f, 0.19f, 0.16f, 1.0f }, // erInternalInfill
{ 0.59f, 0.33f, 0.80f, 1.0f }, // erSolidInfill { 0.59f, 0.33f, 0.80f, 1.0f }, // erSolidInfill
{ 0.90f, 0.70f, 0.70f, 1.0f }, // erEnsureVertical
{ 0.94f, 0.25f, 0.25f, 1.0f }, // erTopSolidInfill { 0.94f, 0.25f, 0.25f, 1.0f }, // erTopSolidInfill
{ 0.40f, 0.36f, 0.78f, 1.0f }, // erBottomSurface { 0.40f, 0.36f, 0.78f, 1.0f }, // erBottomSurface
{ 1.00f, 0.55f, 0.41f, 1.0f }, // erIroning { 1.00f, 0.55f, 0.41f, 1.0f }, // erIroning
@ -1724,6 +1725,7 @@ void GCodeViewer::render_calibration_thumbnail(ThumbnailData& thumbnail_data, un
m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erExternalPerimeter); m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erExternalPerimeter);
m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erOverhangPerimeter); m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erOverhangPerimeter);
m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erSolidInfill); m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erSolidInfill);
m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erEnsureVertical);
m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erTopSolidInfill); m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erTopSolidInfill);
m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erInternalInfill); m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erInternalInfill);
m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erBottomSurface); m_extrusions.role_visibility_flags = m_extrusions.role_visibility_flags | (1 << erBottomSurface);
@ -3119,6 +3121,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const
if (move.type == EMoveType::Seam) if (move.type == EMoveType::Seam)
++seams_count; ++seams_count;
size_t move_id = i - seams_count; size_t move_id = i - seams_count;
if (move.type == EMoveType::Extrude) { if (move.type == EMoveType::Extrude) {

View File

@ -2108,6 +2108,7 @@ void TabPrint::build()
optgroup->append_single_option_line("infill_combination","parameter/strength-advance-settings"); optgroup->append_single_option_line("infill_combination","parameter/strength-advance-settings");
optgroup->append_single_option_line("detect_narrow_internal_solid_infill","parameter/strength-advance-settings"); optgroup->append_single_option_line("detect_narrow_internal_solid_infill","parameter/strength-advance-settings");
optgroup->append_single_option_line("ensure_vertical_shell_thickness","parameter/strength-advance-settings"); optgroup->append_single_option_line("ensure_vertical_shell_thickness","parameter/strength-advance-settings");
optgroup->append_single_option_line("detect_floating_vertical_shell","parameter/strength-advance-settings");
//optgroup->append_single_option_line("internal_bridge_support_thickness","parameter/strength-advance-settings"); //optgroup->append_single_option_line("internal_bridge_support_thickness","parameter/strength-advance-settings");
page = add_options_page(L("Speed"), "empty"); page = add_options_page(L("Speed"), "empty");
@ -2121,6 +2122,7 @@ void TabPrint::build()
optgroup->append_single_option_line("small_perimeter_threshold", "", 0); optgroup->append_single_option_line("small_perimeter_threshold", "", 0);
optgroup->append_single_option_line("sparse_infill_speed", "", 0); optgroup->append_single_option_line("sparse_infill_speed", "", 0);
optgroup->append_single_option_line("internal_solid_infill_speed", "", 0); optgroup->append_single_option_line("internal_solid_infill_speed", "", 0);
optgroup->append_single_option_line("vertical_shell_speed", "", 0);
optgroup->append_single_option_line("top_surface_speed", "", 0); optgroup->append_single_option_line("top_surface_speed", "", 0);
optgroup->append_single_option_line("enable_overhang_speed", "slow-down-for-overhang", 0); optgroup->append_single_option_line("enable_overhang_speed", "slow-down-for-overhang", 0);
Line line = { L("Overhang speed"), L("This is the speed for various overhang degrees. Overhang degrees are expressed as a percentage of line width. 0 speed means no slowing down for the overhang degree range and wall speed is used") }; Line line = { L("Overhang speed"), L("This is the speed for various overhang degrees. Overhang degrees are expressed as a percentage of line width. 0 speed means no slowing down for the overhang degree range and wall speed is used") };