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/FillRectilinear.cpp
|
||||
Fill/FillRectilinear.hpp
|
||||
Fill/FillContour.hpp
|
||||
Fill/FillContour.cpp
|
||||
Flow.cpp
|
||||
Flow.hpp
|
||||
Frustum.cpp
|
||||
|
@ -432,6 +432,29 @@ bool ExPolygon::remove_colinear_points() {
|
||||
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,
|
||||
// however their contours may be rotated.
|
||||
bool expolygons_match(const ExPolygon &l, const ExPolygon &r)
|
||||
|
@ -456,6 +456,9 @@ inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double toleranc
|
||||
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,
|
||||
// however their contours may be rotated.
|
||||
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 erOverhangPerimeter : return L("Overhang wall");
|
||||
case erInternalInfill : return L("Sparse infill");
|
||||
case erEnsureVertical : return L("Ensure vertical");
|
||||
case erSolidInfill : return L("Internal solid infill");
|
||||
case erTopSolidInfill : return L("Top surface");
|
||||
case erBottomSurface : return L("Bottom surface");
|
||||
@ -649,6 +650,8 @@ ExtrusionRole ExtrusionEntity::string_to_role(const std::string_view role)
|
||||
return erOverhangPerimeter;
|
||||
else if (role == L("Sparse infill"))
|
||||
return erInternalInfill;
|
||||
else if (role == L("Ensure vertical"))
|
||||
return erEnsureVertical;
|
||||
else if (role == L("Internal solid infill"))
|
||||
return erSolidInfill;
|
||||
else if (role == L("Top surface"))
|
||||
|
@ -48,6 +48,7 @@ enum ExtrusionRole : uint8_t {
|
||||
erOverhangPerimeter,
|
||||
erInternalInfill,
|
||||
erSolidInfill,
|
||||
erEnsureVertical,
|
||||
erTopSolidInfill,
|
||||
erBottomSurface,
|
||||
erIroning,
|
||||
@ -68,7 +69,8 @@ enum ExtrusionRole : uint8_t {
|
||||
|
||||
enum CustomizeFlag : uint8_t {
|
||||
cfNone,
|
||||
cfCircleCompensation // shaft hole tolerance compensation
|
||||
cfCircleCompensation, // shaft hole tolerance compensation
|
||||
cfEnsureVertical
|
||||
};
|
||||
|
||||
// Special flags describing loop
|
||||
@ -97,6 +99,7 @@ inline bool is_infill(ExtrusionRole role)
|
||||
return role == erBridgeInfill
|
||||
|| role == erInternalInfill
|
||||
|| role == erSolidInfill
|
||||
|| role == erEnsureVertical
|
||||
|| role == erTopSolidInfill
|
||||
|| role == erBottomSurface
|
||||
|| role == erIroning;
|
||||
@ -111,6 +114,7 @@ inline bool is_solid_infill(ExtrusionRole role)
|
||||
{
|
||||
return role == erBridgeInfill
|
||||
|| role == erSolidInfill
|
||||
|| role == erEnsureVertical
|
||||
|| role == erTopSolidInfill
|
||||
|| role == erBottomSurface
|
||||
|| role == erIroning;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "FillLightning.hpp"
|
||||
#include "FillConcentricInternal.hpp"
|
||||
#include "FillConcentric.hpp"
|
||||
#include "FillContour.hpp"
|
||||
|
||||
#define NARROW_INFILL_AREA_THRESHOLD 3
|
||||
|
||||
@ -134,16 +135,6 @@ struct SurfaceFill {
|
||||
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> surface_fills;
|
||||
@ -177,7 +168,9 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
if (surface.is_solid()) {
|
||||
params.density = 100.f;
|
||||
//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;
|
||||
else if (surface.is_external() && !is_bridge)
|
||||
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 ?
|
||||
erBridgeInfill :
|
||||
(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);
|
||||
params.bridge_angle = float(surface.bridge_angle);
|
||||
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));
|
||||
else if (params.extrusion_role == erSolidInfill)
|
||||
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.
|
||||
if (surface.is_solid() || is_bridge) {
|
||||
@ -363,29 +358,53 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
|
||||
// BBS: detect narrow internal solid infill area and use ipConcentricInternal pattern instead
|
||||
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();
|
||||
for (size_t i = 0; i < surface_fills_size; i++) {
|
||||
if (surface_fills[i].surface.surface_type != stInternalSolid)
|
||||
continue;
|
||||
|
||||
size_t expolygons_size = surface_fills[i].expolygons.size();
|
||||
std::vector<size_t> narrow_expolygons_index;
|
||||
narrow_expolygons_index.reserve(expolygons_size);
|
||||
std::vector<size_t> narrow_expoly_idx;
|
||||
std::vector<size_t> narrow_floating_expoly_idx;
|
||||
std::vector<bool> use_floating_filler;
|
||||
// BBS: get the index list of narrow expolygon
|
||||
for (size_t j = 0; j < expolygons_size; j++)
|
||||
if (is_narrow_infill_area(surface_fills[i].expolygons[j]))
|
||||
narrow_expolygons_index.push_back(j);
|
||||
for (size_t j = 0; j < expolygons_size; j++) {
|
||||
auto bbox = get_extents(surface_fills[i].expolygons[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
|
||||
continue;
|
||||
}
|
||||
else if (narrow_expolygons_index.size() == expolygons_size) {
|
||||
// BBS: all expolygons are narrow, directly change the fill pattern
|
||||
else if (narrow_floating_expoly_idx.size() == expolygons_size) {
|
||||
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;
|
||||
}
|
||||
else {
|
||||
// BBS: some expolygons are narrow, spilit surface_fills[i] and rearrange the expolygons
|
||||
if (!narrow_expoly_idx.empty()) {
|
||||
params = surface_fills[i].params;
|
||||
params.pattern = ipConcentricInternal;
|
||||
surface_fills.emplace_back(params);
|
||||
@ -394,13 +413,35 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
surface_fills.back().surface.thickness = surface_fills[i].surface.thickness;
|
||||
surface_fills.back().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_expolygons_index.size(); j++) {
|
||||
for (size_t j = 0; j < narrow_expoly_idx.size(); j++) {
|
||||
// BBS: move the narrow expolygons to new surface_fills.back();
|
||||
surface_fills.back().expolygons.emplace_back(std::move(surface_fills[i].expolygons[narrow_expolygons_index[j]]));
|
||||
surface_fills.back().expolygons.emplace_back(std::move(surface_fills[i].expolygons[narrow_expoly_idx[j]]));
|
||||
}
|
||||
for (int j = narrow_expolygons_index.size() - 1; j >= 0; j--) {
|
||||
}
|
||||
|
||||
if (!narrow_floating_expoly_idx.empty()) {
|
||||
params = surface_fills[i].params;
|
||||
params.pattern = ipEnsureVertical;
|
||||
params.extrusion_role = erEnsureVertical;
|
||||
surface_fills.emplace_back(params);
|
||||
surface_fills.back().region_id = surface_fills[i].region_id;
|
||||
surface_fills.back().surface.surface_type = stEnsureVertical;
|
||||
surface_fills.back().surface.thickness = surface_fills[i].surface.thickness;
|
||||
surface_fills.back().region_id_group = surface_fills[i].region_id_group;
|
||||
surface_fills.back().no_overlap_expolygons = surface_fills[i].no_overlap_expolygons;
|
||||
for (size_t j = 0; j < narrow_floating_expoly_idx.size(); j++) {
|
||||
// BBS: move the narrow expolygons to new surface_fills.back();
|
||||
surface_fills.back().expolygons.emplace_back(std::move(surface_fills[i].expolygons[narrow_floating_expoly_idx[j]]));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<size_t> to_be_delete = narrow_floating_expoly_idx;
|
||||
to_be_delete.insert(to_be_delete.end(), narrow_expoly_idx.begin(), narrow_expoly_idx.end());
|
||||
std::sort(to_be_delete.begin(), to_be_delete.end());
|
||||
|
||||
for (int j = to_be_delete.size() - 1; j >= 0; j--) {
|
||||
// BBS: delete the narrow expolygons from old surface_fills
|
||||
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)
|
||||
layerm->fills.clear();
|
||||
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
// this->export_region_fill_surfaces_to_svg_debug("10_fill-initial");
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
@ -476,9 +516,49 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
assert(fill_concentric != nullptr);
|
||||
fill_concentric->print_config = &this->object()->print()->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;
|
||||
}
|
||||
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
|
||||
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge;
|
||||
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_max = surface_fill.params.anchor_length_max;
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
}
|
||||
if (surface_fill.params.pattern == ipGrid)
|
||||
if (surface_fill.params.pattern == ipGrid || surface_fill.params.pattern == ipEnsureVertical)
|
||||
params.can_reverse = false;
|
||||
LayerRegion* layerm = this->m_regions[surface_fill.region_id];
|
||||
for (ExPolygon& expoly : surface_fill.expolygons) {
|
||||
|
@ -24,6 +24,7 @@
|
||||
// BBS: new infill pattern header
|
||||
#include "FillConcentricInternal.hpp"
|
||||
#include "FillCrossHatch.hpp"
|
||||
#include "FillContour.hpp"
|
||||
|
||||
// #define INFILL_DEBUG_OUTPUT
|
||||
|
||||
@ -58,6 +59,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
|
||||
case ipMonotonicLine: return new FillMonotonicLineWGapFill();
|
||||
case ipZigZag: return new FillZigZag();
|
||||
case ipCrossZag: return new FillCrossZag();
|
||||
case ipEnsureVertical: return new FillContour();
|
||||
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)
|
||||
{
|
||||
// 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)) {
|
||||
m_wipe.path = std::move(path.polyline);
|
||||
m_wipe.path.reverse();
|
||||
@ -5286,7 +5287,7 @@ void GCode::smooth_speed_discontinuity_area(ExtrusionPaths &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;
|
||||
|
||||
@ -5380,7 +5381,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
if (path.role() == erPerimeter) {
|
||||
speed = m_config.inner_wall_speed.get_at(cur_extruder_index());
|
||||
//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());
|
||||
}else if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && path.smooth_speed != 0)
|
||||
speed = path.smooth_speed;
|
||||
@ -5392,7 +5393,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
} else if (path.role() == erExternalPerimeter) {
|
||||
speed = m_config.outer_wall_speed.get_at(cur_extruder_index());
|
||||
// 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());
|
||||
} else if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && path.smooth_speed != 0)
|
||||
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());
|
||||
} else if (path.role() == erSolidInfill) {
|
||||
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());
|
||||
} else if (path.role() == erIroning) {
|
||||
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());
|
||||
|
||||
|
||||
if (set_holes_and_compensation_speed)
|
||||
if (use_seperate_speed)
|
||||
gcode += "; Slow Down Start\n";
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
|
@ -5506,8 +5506,13 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
|
||||
void GCodeProcessor::set_extrusion_role(ExtrusionRole role)
|
||||
{
|
||||
m_used_filaments.process_role_cache(this);
|
||||
if (role == erEnsureVertical) {
|
||||
m_extrusion_role = erSolidInfill;
|
||||
}
|
||||
else {
|
||||
m_extrusion_role = role;
|
||||
}
|
||||
}
|
||||
|
||||
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{
|
||||
ExpansionZone{std::move(shells), expansion_params_into_solid_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;
|
||||
@ -639,298 +639,6 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||
|
||||
#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
|
||||
|
||||
void LayerRegion::prepare_fill_surfaces()
|
||||
|
@ -618,6 +618,58 @@ ThickLines ThickPolyline::thicklines() const
|
||||
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 lines;
|
||||
|
@ -102,6 +102,8 @@ public:
|
||||
void append(const Polyline& src);
|
||||
void append(Polyline&& src);
|
||||
|
||||
Polyline rebase_at(size_t idx);
|
||||
|
||||
Point& operator[](Points::size_type idx) { return this->points[idx]; }
|
||||
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
|
||||
|
||||
@ -264,6 +266,8 @@ public:
|
||||
Polyline::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::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",
|
||||
"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",
|
||||
"vertical_shell_speed","detect_floating_vertical_shell",
|
||||
// calib
|
||||
"print_flow_ratio",
|
||||
//Orca
|
||||
|
@ -552,6 +552,7 @@ private:
|
||||
void bridge_over_infill();
|
||||
void clip_fill_surfaces();
|
||||
void discover_horizontal_shells();
|
||||
void merge_infill_types();
|
||||
void combine_infill();
|
||||
void _generate_support_material();
|
||||
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->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->label = L("Internal bridge support thickness");
|
||||
def->category = L("Strength");
|
||||
@ -5561,6 +5579,7 @@ std::set<std::string> print_options_with_variant = {
|
||||
"small_perimeter_threshold",
|
||||
"sparse_infill_speed",
|
||||
"internal_solid_infill_speed",
|
||||
"vertical_shell_speed",
|
||||
"top_surface_speed",
|
||||
"enable_overhang_speed", //coBools
|
||||
"overhang_1_4_speed",
|
||||
|
@ -54,7 +54,7 @@ enum AuthorizationType {
|
||||
enum InfillPattern : int {
|
||||
ipConcentric, ipRectilinear, ipGrid, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb,
|
||||
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal,
|
||||
ipLightning, ipCrossHatch, ipZigZag, ipCrossZag,
|
||||
ipLightning, ipCrossHatch, ipZigZag, ipCrossZag,ipEnsureVertical,
|
||||
ipCount,
|
||||
};
|
||||
|
||||
@ -854,6 +854,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, tree_support_branch_diameter_angle))
|
||||
((ConfigOptionInt, tree_support_wall_count))
|
||||
((ConfigOptionBool, detect_narrow_internal_solid_infill))
|
||||
((ConfigOptionBool, detect_floating_vertical_shell))
|
||||
// ((ConfigOptionBool, adaptive_layer_height))
|
||||
((ConfigOptionFloat, support_bottom_interface_spacing))
|
||||
((ConfigOptionFloat, internal_bridge_support_thickness))
|
||||
@ -946,6 +947,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloatsNullable, top_surface_speed))
|
||||
((ConfigOptionFloatsOrPercentsNullable, small_perimeter_speed))
|
||||
((ConfigOptionFloatsNullable, small_perimeter_threshold))
|
||||
((ConfigOptionFloatsOrPercentsNullable, vertical_shell_speed))
|
||||
((ConfigOptionInt, top_color_penetration_layers))
|
||||
((ConfigOptionInt, bottom_color_penetration_layers))
|
||||
//BBS
|
||||
|
@ -55,6 +55,8 @@ using namespace std::literals;
|
||||
#include <cassert>
|
||||
#endif
|
||||
|
||||
#define USE_TBB_IN_INFILL 1
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Constructor is called from the main thread, therefore all Model / ModelObject / ModelIntance data are valid.
|
||||
@ -614,7 +616,7 @@ void PrintObject::infill()
|
||||
const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first;
|
||||
const auto& 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::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) {
|
||||
@ -1087,7 +1089,8 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "sparse_infill_anchor"
|
||||
|| opt_key == "sparse_infill_anchor_max"
|
||||
|| 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);
|
||||
} else if (opt_key == "sparse_infill_pattern"
|
||||
|| opt_key == "symmetric_infill_y_axis"
|
||||
@ -1164,7 +1167,8 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "sparse_infill_speed"
|
||||
|| opt_key == "inner_wall_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);
|
||||
} else if (
|
||||
opt_key == "flush_into_infill"
|
||||
@ -1261,6 +1265,7 @@ void PrintObject::detect_surfaces_type()
|
||||
if (interface_shells)
|
||||
surfaces_new.assign(num_layers, Surfaces());
|
||||
|
||||
// interface_shell 启用与否,决定着是否区分不同材料。开启后,不同材料间的接触面都会被识别为顶面、底面
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0,
|
||||
spiral_mode ?
|
||||
@ -1322,7 +1327,7 @@ void PrintObject::detect_surfaces_type()
|
||||
surfaces_append(
|
||||
bottom,
|
||||
opening_ex(
|
||||
diff_ex(layerm->slices.surfaces, lower_layer->lslices, ApplySafetyOffset::Yes),
|
||||
diff_ex(layerm->slices.surfaces, lower_layer->lslices, ApplySafetyOffset::Yes),//完全悬空
|
||||
offset),
|
||||
surface_type_bottom_other);
|
||||
// if user requested internal shells, we need to identify surfaces
|
||||
@ -1334,8 +1339,8 @@ void PrintObject::detect_surfaces_type()
|
||||
bottom,
|
||||
opening_ex(
|
||||
diff_ex(
|
||||
intersection(layerm->slices.surfaces, lower_layer->lslices), // supported
|
||||
lower_layer->m_regions[region_id]->slices.surfaces,
|
||||
intersection(layerm->slices.surfaces, lower_layer->lslices), // 先扣掉完全悬空
|
||||
lower_layer->m_regions[region_id]->slices.surfaces,//再扣掉同材料的区域
|
||||
ApplySafetyOffset::Yes),
|
||||
offset),
|
||||
stBottom);
|
||||
@ -1355,9 +1360,6 @@ void PrintObject::detect_surfaces_type()
|
||||
// and top surfaces; let's do an intersection to discover them and consider them
|
||||
// as bottom surfaces (to allow for bridge detection)
|
||||
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));
|
||||
top.clear();
|
||||
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(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
|
||||
layerm->export_region_slices_to_svg_debug("detect_surfaces_type-final");
|
||||
#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";
|
||||
//FIXME Improve the heuristics for a grain size.
|
||||
size_t grain_size = std::max(num_layers / 16, size_t(1));
|
||||
// 关闭interface_shell,不区分不同材料,所以遍历顺序是按层遍历,层中遍历region
|
||||
// top区域包含墙,bottom区域包含墙,holes为稀疏填充区域
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
||||
[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.
|
||||
float perimeter_offset = 0.f;
|
||||
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) {
|
||||
LayerRegion& layerm = *layer.m_regions[region_id];
|
||||
float top_bottom_expansion = float(layerm.flow(frSolidInfill).scaled_spacing()) * top_bottom_expansion_coeff;
|
||||
// Top surfaces.
|
||||
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.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;
|
||||
for (const Surface& s : layerm.slices.surfaces)
|
||||
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));
|
||||
}
|
||||
// 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.
|
||||
if (perimeter_offset > 0.) {
|
||||
// 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));
|
||||
#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);
|
||||
}});
|
||||
m_print->throw_if_canceled();
|
||||
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) {
|
||||
//FIXME Improve the heuristics for a grain size.
|
||||
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));
|
||||
|
||||
// 开启了interface_shell,代表顶底面计算时只有同region可以视为covered
|
||||
// 所以此时,对于某一个region,先逐层计算cache的top,bottom,由于稀疏填充是共用的,所以算一次即可
|
||||
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
|
||||
// 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";
|
||||
grain_size = 1;
|
||||
// 从第低到高按层遍历
|
||||
#if USE_TBB_IN_INFILL
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
||||
[this, region_id, &cache_top_botom_regions]
|
||||
(const tbb::blocked_range<size_t>& range) {
|
||||
// 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) {
|
||||
#else
|
||||
for (size_t idx_layer = 0; idx_layer < num_layers; ++idx_layer) {
|
||||
#endif
|
||||
m_print->throw_if_canceled();
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
static size_t debug_idx = 0;
|
||||
@ -1692,28 +1684,6 @@ void PrintObject::discover_vertical_shells()
|
||||
ExPolygons shell_ex;
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
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);
|
||||
auto combine_holes = [&holes](const Polygons &holes2) {
|
||||
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))
|
||||
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
|
||||
shell_ex = union_safety_offset_ex(shell);
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
@ -1811,42 +1769,6 @@ void PrintObject::discover_vertical_shells()
|
||||
//if (shell.empty())
|
||||
// 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.
|
||||
const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types({ stInternal, stInternalVoid, stInternalSolid }));
|
||||
shell = intersection(shell, polygonsInternal, ApplySafetyOffset::Yes);
|
||||
@ -1912,10 +1834,12 @@ void PrintObject::discover_vertical_shells()
|
||||
}),
|
||||
regularized_shell.end());
|
||||
}
|
||||
|
||||
if (regularized_shell.empty())
|
||||
continue;
|
||||
|
||||
ExPolygons new_internal_solid = intersection_ex(polygonsInternal, regularized_shell);
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
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();
|
||||
}
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
|
||||
// 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_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_solid, stInternalSolid);
|
||||
} // for each layer
|
||||
#if USE_TBB_IN_INFILL
|
||||
});
|
||||
#endif
|
||||
m_print->throw_if_canceled();
|
||||
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());
|
||||
}
|
||||
|
||||
#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.
|
||||
void PrintObject::bridge_over_infill()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Start" << log_memory_info();
|
||||
|
||||
// CandidateSurface存放一个需要桥接的区域
|
||||
struct CandidateSurface
|
||||
{
|
||||
CandidateSurface(const Surface *original_surface,
|
||||
@ -2160,6 +1917,7 @@ void PrintObject::bridge_over_infill()
|
||||
double bridge_angle;
|
||||
};
|
||||
|
||||
// 按层存放surface,存放着待桥接的信息
|
||||
std::map<size_t, std::vector<CandidateSurface>> surfaces_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::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) {
|
||||
// 按层并行
|
||||
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
|
||||
const Layer *layer = po->get_layer(lidx);
|
||||
if (layer->lower_layer == nullptr) {
|
||||
@ -2174,8 +1933,9 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
double spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing();
|
||||
// unsupported area will serve as a filter for polygons worth bridging.
|
||||
Polygons unsupported_area;
|
||||
Polygons lower_layer_solids;
|
||||
Polygons unsupported_area; // 下一层不提供支撑的区域
|
||||
Polygons lower_layer_solids; // 下一层的实心区域,可以提供支撑
|
||||
// 取当前层和下一层的数据
|
||||
for (const LayerRegion *region : layer->lower_layer->regions()) {
|
||||
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
|
||||
@ -2197,9 +1957,9 @@ void PrintObject::bridge_over_infill()
|
||||
unsupported_area = diff(unsupported_area, lower_layer_solids);
|
||||
|
||||
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) {
|
||||
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.
|
||||
// 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;
|
||||
@ -2369,8 +2129,8 @@ void PrintObject::bridge_over_infill()
|
||||
std::vector<std::vector<size_t>> clustered_layers_for_threads;
|
||||
float target_flow_height_factor = 0.9f;
|
||||
{
|
||||
std::vector<size_t> layers_with_candidates;
|
||||
std::map<size_t, Polygons> layer_area_covered_by_candidates;
|
||||
std::vector<size_t> layers_with_candidates; // 存储所有需要生成桥接的层号
|
||||
std::map<size_t, Polygons> layer_area_covered_by_candidates; // 存储每一层,需要生成桥接区域的bbox的并集
|
||||
for (const auto& pair : surfaces_by_layer) {
|
||||
layers_with_candidates.push_back(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,
|
||||
&layer_area_covered_by_candidates](
|
||||
tbb::blocked_range<size_t> r) {
|
||||
// 按层并行
|
||||
for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
|
||||
size_t lidx = layers_with_candidates[job_idx];
|
||||
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
|
||||
for (auto pair : surfaces_by_layer) {
|
||||
for (const auto &pair : surfaces_by_layer) {
|
||||
// 初次操作 || z方向距离较远 || 桥接区域无交集, 那么就可以重新划分一个组,否则分配到前一个组
|
||||
if (clustered_layers_for_threads.empty() ||
|
||||
this->get_layer(clustered_layers_for_threads.back().back())->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 = closing_ex(layers_sparse_infill, float(SCALED_EPSILON));
|
||||
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 target_flow_height = surfaces_by_layer[lidx].front().region->bridging_flow(frSolidInfill, true).height() *
|
||||
target_flow_height_factor;
|
||||
// 收集当前层中可以应用thick_bridge的区域
|
||||
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);
|
||||
}
|
||||
|
||||
// 得到thick_bridge区域,bridge区域扩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
|
||||
Polygons lightning_area;
|
||||
Polygons expansion_area;
|
||||
Polygons total_fill_area;
|
||||
Polygons top_area;
|
||||
Polygons expansion_area; // 可以提供扩张的区域
|
||||
Polygons total_fill_area; // 所有填充区域
|
||||
Polygons top_area; // 顶面区域
|
||||
|
||||
for (LayerRegion *region : layer->regions()) {
|
||||
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());
|
||||
for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) {
|
||||
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);
|
||||
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(),
|
||||
@ -2819,7 +2584,7 @@ void PrintObject::bridge_over_infill()
|
||||
|
||||
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())
|
||||
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) {
|
||||
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())
|
||||
continue;
|
||||
Layer *layer = po->get_layer(lidx);
|
||||
|
||||
Polygons cut_from_infill{};
|
||||
Polygons cut_from_infill{}; // 桥接区域
|
||||
if (surfaces_by_layer.find(lidx) != surfaces_by_layer.end()) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Polygons additional_ensuring_areas{};
|
||||
Polygons additional_ensuring_areas{}; // 下一层为上一层桥接需要生成的区域
|
||||
if (surfaces_by_layer.find(lidx + 1) != surfaces_by_layer.end()) {
|
||||
for (const auto &surface : surfaces_by_layer.at(lidx + 1)) {
|
||||
auto additional_area = diff(surface.new_polys,
|
||||
@ -2922,12 +2688,12 @@ void PrintObject::bridge_over_infill()
|
||||
for (LayerRegion *region : layer->regions()) {
|
||||
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()));
|
||||
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);
|
||||
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);
|
||||
for (const ExPolygon &ep : new_internal_infills) {
|
||||
new_surfaces.emplace_back(stInternal, ep);
|
||||
@ -2949,8 +2715,8 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = union_safety_offset_ex(new_internal_solids);
|
||||
for (const ExPolygon &ep : new_internal_solids) {
|
||||
@ -2976,10 +2742,6 @@ void PrintObject::bridge_over_infill()
|
||||
|
||||
} // void PrintObject::bridge_over_infill()
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders)
|
||||
{
|
||||
if (opt.value > (int)num_extruders)
|
||||
@ -3316,16 +3078,6 @@ void PrintObject::discover_horizontal_shells()
|
||||
Layer* layer = m_layers[i];
|
||||
LayerRegion* layerm = layer->regions()[region_id];
|
||||
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 (region_config.ensure_vertical_shell_thickness.value!=EnsureVerticalThicknessLevel::evtDisabled)
|
||||
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 stBottomBridge: return "rgb(0,0,255)"; // "blue";
|
||||
case stInternal: return "rgb(255,255,128)"; // yellow
|
||||
case stEnsureVertical:
|
||||
case stInternalSolid: return "rgb(255,0,255)"; // magenta
|
||||
case stInternalBridge: return "rgb(0,255,255)";
|
||||
case stInternalVoid: return "rgb(128,128,128)";
|
||||
|
@ -15,6 +15,7 @@ enum SurfaceType {
|
||||
stBottomBridge,
|
||||
// Normal sparse infill.
|
||||
stInternal,
|
||||
stEnsureVertical,
|
||||
// Full infill, supporting the top surfaces and/or defining the verticall wall thickness.
|
||||
stInternalSolid,
|
||||
// 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_external() const { return this->is_top() || this->is_bottom(); }
|
||||
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; }
|
||||
};
|
||||
|
||||
|
@ -650,6 +650,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
toggle_line(el, 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
|
||||
toggle_line("bridge_no_support", !support_is_tree);
|
||||
// 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.69f, 0.19f, 0.16f, 1.0f }, // erInternalInfill
|
||||
{ 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.40f, 0.36f, 0.78f, 1.0f }, // erBottomSurface
|
||||
{ 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 << erOverhangPerimeter);
|
||||
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 << erInternalInfill);
|
||||
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)
|
||||
++seams_count;
|
||||
|
||||
|
||||
size_t move_id = i - seams_count;
|
||||
|
||||
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("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("detect_floating_vertical_shell","parameter/strength-advance-settings");
|
||||
//optgroup->append_single_option_line("internal_bridge_support_thickness","parameter/strength-advance-settings");
|
||||
|
||||
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("sparse_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("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") };
|
||||
|
Loading…
x
Reference in New Issue
Block a user