mirror of
https://git.mirrors.martin98.com/https://github.com/bambulab/BambuStudio.git
synced 2025-09-28 21:03:12 +08:00
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:
parent
62a06d3351
commit
40c4f667d1
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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"))
|
||||||
|
@ -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;
|
||||||
|
@ -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,44 +358,90 @@ 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
|
||||||
params = surface_fills[i].params;
|
if (!narrow_expoly_idx.empty()) {
|
||||||
params.pattern = ipConcentricInternal;
|
params = surface_fills[i].params;
|
||||||
surface_fills.emplace_back(params);
|
params.pattern = ipConcentricInternal;
|
||||||
surface_fills.back().region_id = surface_fills[i].region_id;
|
surface_fills.emplace_back(params);
|
||||||
surface_fills.back().surface.surface_type = stInternalSolid;
|
surface_fills.back().region_id = surface_fills[i].region_id;
|
||||||
surface_fills.back().surface.thickness = surface_fills[i].surface.thickness;
|
surface_fills.back().surface.surface_type = stInternalSolid;
|
||||||
surface_fills.back().region_id_group = surface_fills[i].region_id_group;
|
surface_fills.back().surface.thickness = surface_fills[i].surface.thickness;
|
||||||
surface_fills.back().no_overlap_expolygons = surface_fills[i].no_overlap_expolygons;
|
surface_fills.back().region_id_group = surface_fills[i].region_id_group;
|
||||||
for (size_t j = 0; j < narrow_expolygons_index.size(); j++) {
|
surface_fills.back().no_overlap_expolygons = surface_fills[i].no_overlap_expolygons;
|
||||||
// BBS: move the narrow expolygons to new surface_fills.back();
|
for (size_t j = 0; j < narrow_expoly_idx.size(); j++) {
|
||||||
surface_fills.back().expolygons.emplace_back(std::move(surface_fills[i].expolygons[narrow_expolygons_index[j]]));
|
// BBS: move the narrow expolygons to new surface_fills.back();
|
||||||
|
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) {
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
887
src/libslic3r/Fill/FillContour.cpp
Normal file
887
src/libslic3r/Fill/FillContour.cpp
Normal 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
|
77
src/libslic3r/Fill/FillContour.hpp
Normal file
77
src/libslic3r/Fill/FillContour.hpp
Normal 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 ¶ms,
|
||||||
|
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
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5506,7 +5506,12 @@ 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);
|
||||||
m_extrusion_role = role;
|
if (role == erEnsureVertical) {
|
||||||
|
m_extrusion_role = erSolidInfill;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_extrusion_role = role;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCodeProcessor::set_skippable_type(const std::string_view type)
|
void GCodeProcessor::set_skippable_type(const std::string_view type)
|
||||||
|
@ -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()
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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,15 +616,15 @@ 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) {
|
||||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get());
|
m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - end";
|
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - end";
|
||||||
@ -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 ®ion = this->printing_region(region_id);
|
const PrintRegion ®ion = 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的top,bottom,由于稀疏填充是共用的,所以算一次即可
|
||||||
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 ®ion = 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,13 +2688,13 @@ 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;
|
||||||
|
@ -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)";
|
||||||
|
@ -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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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") };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user