mirror of
https://git.mirrors.martin98.com/https://github.com/bambulab/BambuStudio.git
synced 2025-08-06 04:47:16 +08:00
FIX: support wall count doesn't work
jira: STUDIO-7975 Change-Id: Ic580d298568fc6eab8b1a2c017fa182869b432bf (cherry picked from commit 82bcb099e139065cc00c133f507e955d9955b2f4)
This commit is contained in:
parent
499b39b133
commit
04756bf447
@ -3576,10 +3576,11 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("tree_support_wall_count", coInt);
|
||||
def->label = L("Support wall loops");
|
||||
def->category = L("Support");
|
||||
def->tooltip = L("This setting specifies the count of walls around support");
|
||||
def->tooltip = L("This setting specifies the min count of support walls in the range of [0,2]. Actual wall count may be larger than the specified value.");
|
||||
def->min = 0;
|
||||
def->max = 2;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(0));
|
||||
def->set_default_value(new ConfigOptionInt(1));
|
||||
|
||||
def = this->add("chamber_temperatures", coInts);
|
||||
def->label = L("Chamber temperature");
|
||||
|
@ -40,7 +40,7 @@ namespace Slic3r {
|
||||
|
||||
// how much we extend support around the actual contact area
|
||||
//FIXME this should be dependent on the nozzle diameter!
|
||||
#define SUPPORT_MATERIAL_MARGIN 1.5
|
||||
#define SUPPORT_MATERIAL_MARGIN 1.5
|
||||
|
||||
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
|
||||
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
|
||||
@ -69,7 +69,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
|
||||
const bool smooth_supports = config.support_style.value != smsGrid;
|
||||
SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first;
|
||||
SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second;
|
||||
|
||||
|
||||
// BBS: if support interface and support base do not use the same filament, add a base layer to improve their adhesion
|
||||
// Note: support materials (such as Supp.W) can't be used as support base now, so support interface and base are still using different filaments even if support_filament==0
|
||||
bool differnt_support_interface_filament = config.support_interface_filament != 0 && config.support_interface_filament.value != config.support_filament.value;
|
||||
@ -87,7 +87,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
|
||||
const auto closing_distance = smoothing_distance; // scaled<float>(config.support_material_closing_radius.value);
|
||||
// Insert a new layer into base_interface_layers, if intersection with base exists.
|
||||
auto insert_layer = [&layer_storage, smooth_supports, closing_distance, smoothing_distance, minimum_island_radius](
|
||||
SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer,
|
||||
SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer,
|
||||
const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* {
|
||||
bool has_top_interface = top_interface_layer && ! top_interface_layer->polygons.empty();
|
||||
assert(! bottom.empty() || ! top.empty() || has_top_interface);
|
||||
@ -129,7 +129,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
|
||||
};
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())),
|
||||
[&bottom_contacts, &top_contacts, &top_interface_layers, &top_base_interface_layers, &intermediate_layers, &insert_layer, &support_params,
|
||||
snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range<int>& range) {
|
||||
snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range<int>& range) {
|
||||
// Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below
|
||||
// this intermediate layer.
|
||||
// Index of the first top contact layer intersecting the current intermediate layer.
|
||||
@ -165,7 +165,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
|
||||
//FIXME maybe this adds one interface layer in excess?
|
||||
if (top_contact_layer.bottom_z - EPSILON > top_z)
|
||||
break;
|
||||
polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface,
|
||||
polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface,
|
||||
// For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers.
|
||||
// For grid supports, merging of support regions will be performed by the projection into grid.
|
||||
snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons);
|
||||
@ -177,7 +177,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
|
||||
coordf_t bottom_interface_z = - std::numeric_limits<coordf_t>::max();
|
||||
if (support_params.num_bottom_base_interface_layers > 0)
|
||||
// Some bottom base interface layers will be generated.
|
||||
bottom_interface_z = support_params.num_bottom_interface_layers_only() == 0 ?
|
||||
bottom_interface_z = support_params.num_bottom_interface_layers_only() == 0 ?
|
||||
// Only base interface layers to generate.
|
||||
std::numeric_limits<coordf_t>::max() :
|
||||
intermediate_layers[std::max(0, idx_intermediate_layer - int(support_params.num_bottom_interface_layers_only()))]->bottom_z;
|
||||
@ -242,7 +242,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
|
||||
base_interface_layers = merge_remove_empty(base_interface_layers, top_base_interface_layers);
|
||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end";
|
||||
}
|
||||
|
||||
|
||||
return base_and_interface_layers;
|
||||
}
|
||||
|
||||
@ -315,7 +315,7 @@ SupportGeneratorLayersPtr generate_raft_base(
|
||||
polygons_append(interface_polygons, expand(interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
if (base_interfaces != nullptr && ! base_interfaces->polygons.empty())
|
||||
polygons_append(interface_polygons, expand(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
|
||||
|
||||
// Output vector.
|
||||
SupportGeneratorLayersPtr raft_layers;
|
||||
|
||||
@ -339,7 +339,7 @@ SupportGeneratorLayersPtr generate_raft_base(
|
||||
}
|
||||
if (! interface_polygons.empty()) {
|
||||
// Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface.
|
||||
base = union_(base, interface_polygons);
|
||||
base = union_(base, interface_polygons);
|
||||
}
|
||||
// Do not add the raft contact layer, only add the raft layers below the contact layer.
|
||||
// Insert the 1st layer.
|
||||
@ -479,10 +479,10 @@ static Polylines draw_perimeters(const ExPolygon &expoly, double clip_length)
|
||||
return polylines;
|
||||
}
|
||||
|
||||
static inline void tree_supports_generate_paths(
|
||||
void tree_supports_generate_paths(
|
||||
ExtrusionEntitiesPtr &dst,
|
||||
const Polygons &polygons,
|
||||
const Flow &flow,
|
||||
const Flow &flow,
|
||||
const SupportParameters &support_params)
|
||||
{
|
||||
// Offset expolygon inside, returns number of expolygons collected (0 or 1).
|
||||
@ -549,7 +549,7 @@ static inline void tree_supports_generate_paths(
|
||||
// No hole remaining after an offset. Just copy the outer contour.
|
||||
append(out, std::move(contours));
|
||||
} else {
|
||||
// Negative offset. There is a chance, that the offsetted hole intersects the outer contour.
|
||||
// Negative offset. There is a chance, that the offsetted hole intersects the outer contour.
|
||||
// Subtract the offsetted holes from the offsetted contours.
|
||||
ClipperLib_Z::Clipper clipper;
|
||||
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) {
|
||||
@ -580,124 +580,126 @@ static inline void tree_supports_generate_paths(
|
||||
ClipperLib_Z::Paths anchor_candidates;
|
||||
for (ExPolygon& expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width()))) {
|
||||
std::unique_ptr<ExtrusionEntityCollection> eec;
|
||||
ExPolygons regions_to_draw_inner_wall{expoly};
|
||||
if (support_params.tree_branch_diameter_double_wall_area_scaled > 0)
|
||||
if (double area = expoly.area(); area > support_params.tree_branch_diameter_double_wall_area_scaled) {
|
||||
BOOST_LOG_TRIVIAL(debug)<< "TreeSupports: double wall area: " << area<< " > " << support_params.tree_branch_diameter_double_wall_area_scaled;
|
||||
eec = std::make_unique<ExtrusionEntityCollection>();
|
||||
// Don't reoder internal / external loops of the same island, always start with the internal loop.
|
||||
// Don't reorder internal / external loops of the same island, always start with the internal loop.
|
||||
eec->no_sort = true;
|
||||
// Make the tree branch stable by adding another perimeter.
|
||||
ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width());
|
||||
if (level2.size() == 1) {
|
||||
Polylines polylines;
|
||||
ExPolygons level2 = offset2_ex({expoly}, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width());
|
||||
if (level2.size() > 0) {
|
||||
regions_to_draw_inner_wall = level2;
|
||||
extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), ExtrusionRole::erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(),
|
||||
// Disable reversal of the path, always start with the anchor, always print CCW.
|
||||
false);
|
||||
// Disable reversal of the path, always start with the anchor, always print CCW.
|
||||
false);
|
||||
expoly = level2.front();
|
||||
}
|
||||
}
|
||||
for (ExPolygon &expoly : regions_to_draw_inner_wall)
|
||||
{
|
||||
// Try to produce one more perimeter to place the seam anchor.
|
||||
// First genrate a 2nd perimeter loop as a source for anchor candidates.
|
||||
// The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection.
|
||||
anchor_candidates.clear();
|
||||
shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates);
|
||||
// Orient all contours CW.
|
||||
for (auto &path : anchor_candidates)
|
||||
if (ClipperLib_Z::Area(path) > 0) std::reverse(path.begin(), path.end());
|
||||
|
||||
// Try to produce one more perimeter to place the seam anchor.
|
||||
// First genrate a 2nd perimeter loop as a source for anchor candidates.
|
||||
// The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection.
|
||||
anchor_candidates.clear();
|
||||
shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates);
|
||||
// Orient all contours CW.
|
||||
for (auto &path : anchor_candidates)
|
||||
if (ClipperLib_Z::Area(path) > 0)
|
||||
std::reverse(path.begin(), path.end());
|
||||
|
||||
// Draw the perimeters.
|
||||
Polylines polylines;
|
||||
polylines.reserve(expoly.holes.size() + 1);
|
||||
for (int idx_loop = 0; idx_loop < int(expoly.num_contours()); ++ idx_loop) {
|
||||
// Open the loop with a seam.
|
||||
const Polygon &loop = expoly.contour_or_hole(idx_loop);
|
||||
Polyline pl(loop.points);
|
||||
// Orient all contours CW, because the anchor will be added to the end of polyline while we want to start a loop with the anchor.
|
||||
if (idx_loop == 0)
|
||||
// It is an outer contour.
|
||||
pl.reverse();
|
||||
pl.points.emplace_back(pl.points.front());
|
||||
pl.clip_end(clip_length);
|
||||
if (pl.size() < 2)
|
||||
continue;
|
||||
// Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour.
|
||||
ClipperLib_Z::Path *closest_contour = nullptr;
|
||||
Vec2d closest_point;
|
||||
int closest_point_idx = -1;
|
||||
double closest_point_t = 0.;
|
||||
double d2min = std::numeric_limits<double>::max();
|
||||
Vec2d seam_pt = pl.back().cast<double>();
|
||||
for (ClipperLib_Z::Path &path : anchor_candidates)
|
||||
for (int i = 0; i < int(path.size()); ++ i) {
|
||||
int j = next_idx_modulo(i, path);
|
||||
if (path[i].z() == idx_loop || path[j].z() == idx_loop) {
|
||||
Vec2d pi(path[i].x(), path[i].y());
|
||||
Vec2d pj(path[j].x(), path[j].y());
|
||||
Vec2d v = pj - pi;
|
||||
Vec2d w = seam_pt - pi;
|
||||
auto l2 = v.squaredNorm();
|
||||
auto t = std::clamp((l2 == 0) ? 0 : v.dot(w) / l2, 0., 1.);
|
||||
if ((path[i].z() == idx_loop || t > EPSILON) && (path[j].z() == idx_loop || t < 1. - EPSILON)) {
|
||||
// Closest point.
|
||||
Vec2d fp = pi + v * t;
|
||||
double d2 = (fp - seam_pt).squaredNorm();
|
||||
if (d2 < d2min) {
|
||||
d2min = d2;
|
||||
closest_contour = &path;
|
||||
closest_point = fp;
|
||||
closest_point_idx = i;
|
||||
closest_point_t = t;
|
||||
// Draw the perimeters.
|
||||
Polylines polylines;
|
||||
polylines.reserve(expoly.holes.size() + 1);
|
||||
for (int idx_loop = 0; idx_loop < int(expoly.num_contours()); ++idx_loop) {
|
||||
// Open the loop with a seam.
|
||||
const Polygon &loop = expoly.contour_or_hole(idx_loop);
|
||||
Polyline pl(loop.points);
|
||||
// Orient all contours CW, because the anchor will be added to the end of polyline while we want to start a loop with the anchor.
|
||||
if (idx_loop == 0)
|
||||
// It is an outer contour.
|
||||
pl.reverse();
|
||||
pl.points.emplace_back(pl.points.front());
|
||||
pl.clip_end(clip_length);
|
||||
if (pl.size() < 2) continue;
|
||||
// Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour.
|
||||
ClipperLib_Z::Path *closest_contour = nullptr;
|
||||
Vec2d closest_point;
|
||||
int closest_point_idx = -1;
|
||||
double closest_point_t = 0.;
|
||||
double d2min = std::numeric_limits<double>::max();
|
||||
Vec2d seam_pt = pl.back().cast<double>();
|
||||
for (ClipperLib_Z::Path &path : anchor_candidates)
|
||||
for (int i = 0; i < int(path.size()); ++i) {
|
||||
int j = next_idx_modulo(i, path);
|
||||
if (path[i].z() == idx_loop || path[j].z() == idx_loop) {
|
||||
Vec2d pi(path[i].x(), path[i].y());
|
||||
Vec2d pj(path[j].x(), path[j].y());
|
||||
Vec2d v = pj - pi;
|
||||
Vec2d w = seam_pt - pi;
|
||||
auto l2 = v.squaredNorm();
|
||||
auto t = std::clamp((l2 == 0) ? 0 : v.dot(w) / l2, 0., 1.);
|
||||
if ((path[i].z() == idx_loop || t > EPSILON) && (path[j].z() == idx_loop || t < 1. - EPSILON)) {
|
||||
// Closest point.
|
||||
Vec2d fp = pi + v * t;
|
||||
double d2 = (fp - seam_pt).squaredNorm();
|
||||
if (d2 < d2min) {
|
||||
d2min = d2;
|
||||
closest_contour = &path;
|
||||
closest_point = fp;
|
||||
closest_point_idx = i;
|
||||
closest_point_t = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (d2min < sqr(flow.scaled_width() * 3.)) {
|
||||
// Try to cut an anchor from the closest_contour.
|
||||
// Both closest_contour and pl are CW oriented.
|
||||
pl.points.emplace_back(closest_point.cast<coord_t>());
|
||||
const ClipperLib_Z::Path &path = *closest_contour;
|
||||
double remaining_length = anchor_length - (seam_pt - closest_point).norm();
|
||||
int i = closest_point_idx;
|
||||
int j = next_idx_modulo(i, *closest_contour);
|
||||
Vec2d pi(path[i].x(), path[i].y());
|
||||
Vec2d pj(path[j].x(), path[j].y());
|
||||
Vec2d v = pj - pi;
|
||||
double l = v.norm();
|
||||
if (remaining_length < (1. - closest_point_t) * l) {
|
||||
// Just trim the current line.
|
||||
pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast<coord_t>());
|
||||
} else {
|
||||
// Take the rest of the current line, continue with the other lines.
|
||||
pl.points.emplace_back(path[j].x(), path[j].y());
|
||||
pi = pj;
|
||||
for (i = j; path[i].z() == idx_loop && remaining_length > 0; i = j, pi = pj) {
|
||||
j = next_idx_modulo(i, path);
|
||||
pj = Vec2d(path[j].x(), path[j].y());
|
||||
v = pj - pi;
|
||||
l = v.norm();
|
||||
if (i == closest_point_idx) {
|
||||
// Back at the first segment. Most likely this should not happen and we may end the anchor.
|
||||
break;
|
||||
}
|
||||
if (remaining_length <= l) {
|
||||
pl.points.emplace_back((pi + v * (remaining_length / l)).cast<coord_t>());
|
||||
break;
|
||||
}
|
||||
if (d2min < sqr(flow.scaled_width() * 3.)) {
|
||||
// Try to cut an anchor from the closest_contour.
|
||||
// Both closest_contour and pl are CW oriented.
|
||||
pl.points.emplace_back(closest_point.cast<coord_t>());
|
||||
const ClipperLib_Z::Path &path = *closest_contour;
|
||||
double remaining_length = anchor_length - (seam_pt - closest_point).norm();
|
||||
int i = closest_point_idx;
|
||||
int j = next_idx_modulo(i, *closest_contour);
|
||||
Vec2d pi(path[i].x(), path[i].y());
|
||||
Vec2d pj(path[j].x(), path[j].y());
|
||||
Vec2d v = pj - pi;
|
||||
double l = v.norm();
|
||||
if (remaining_length < (1. - closest_point_t) * l) {
|
||||
// Just trim the current line.
|
||||
pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast<coord_t>());
|
||||
} else {
|
||||
// Take the rest of the current line, continue with the other lines.
|
||||
pl.points.emplace_back(path[j].x(), path[j].y());
|
||||
remaining_length -= l;
|
||||
pi = pj;
|
||||
for (i = j; path[i].z() == idx_loop && remaining_length > 0; i = j, pi = pj) {
|
||||
j = next_idx_modulo(i, path);
|
||||
pj = Vec2d(path[j].x(), path[j].y());
|
||||
v = pj - pi;
|
||||
l = v.norm();
|
||||
if (i == closest_point_idx) {
|
||||
// Back at the first segment. Most likely this should not happen and we may end the anchor.
|
||||
break;
|
||||
}
|
||||
if (remaining_length <= l) {
|
||||
pl.points.emplace_back((pi + v * (remaining_length / l)).cast<coord_t>());
|
||||
break;
|
||||
}
|
||||
pl.points.emplace_back(path[j].x(), path[j].y());
|
||||
remaining_length -= l;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Start with the anchor.
|
||||
pl.reverse();
|
||||
polylines.emplace_back(std::move(pl));
|
||||
}
|
||||
// Start with the anchor.
|
||||
pl.reverse();
|
||||
polylines.emplace_back(std::move(pl));
|
||||
}
|
||||
|
||||
ExtrusionEntitiesPtr &out = eec ? eec->entities : dst;
|
||||
extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(),
|
||||
// Disable reversal of the path, always start with the anchor, always print CCW.
|
||||
false);
|
||||
ExtrusionEntitiesPtr &out = eec ? eec->entities : dst;
|
||||
extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(),
|
||||
// Disable reversal of the path, always start with the anchor, always print CCW.
|
||||
false);
|
||||
}
|
||||
if (eec) {
|
||||
std::reverse(eec->entities.begin(), eec->entities.end());
|
||||
dst.emplace_back(eec.release());
|
||||
@ -712,6 +714,7 @@ void fill_expolygons_with_sheath_generate_paths(
|
||||
float density,
|
||||
ExtrusionRole role,
|
||||
const Flow &flow,
|
||||
const SupportParameters& support_params,
|
||||
bool with_sheath,
|
||||
bool no_sort)
|
||||
{
|
||||
@ -720,7 +723,7 @@ void fill_expolygons_with_sheath_generate_paths(
|
||||
|
||||
if (with_sheath) {
|
||||
if (density == 0) {
|
||||
tree_supports_generate_paths(dst, polygons, flow, SupportParameters());
|
||||
tree_supports_generate_paths(dst, polygons, flow, support_params);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -768,8 +771,8 @@ struct SupportGeneratorLayerExtruded
|
||||
return layer == nullptr || layer->polygons.empty();
|
||||
}
|
||||
|
||||
void set_polygons_to_extrude(Polygons &&polygons) {
|
||||
if (m_polygons_to_extrude == nullptr)
|
||||
void set_polygons_to_extrude(Polygons &&polygons) {
|
||||
if (m_polygons_to_extrude == nullptr)
|
||||
m_polygons_to_extrude = std::make_unique<Polygons>(std::move(polygons));
|
||||
else
|
||||
*m_polygons_to_extrude = std::move(polygons);
|
||||
@ -778,9 +781,9 @@ struct SupportGeneratorLayerExtruded
|
||||
const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; }
|
||||
|
||||
bool could_merge(const SupportGeneratorLayerExtruded &other) const {
|
||||
return ! this->empty() && ! other.empty() &&
|
||||
return ! this->empty() && ! other.empty() &&
|
||||
std::abs(this->layer->height - other.layer->height) < EPSILON &&
|
||||
this->layer->bridging == other.layer->bridging;
|
||||
this->layer->bridging == other.layer->bridging;
|
||||
}
|
||||
|
||||
// Merge regions, perform boolean union over the merged polygons.
|
||||
@ -886,7 +889,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact
|
||||
const Point* operator()(const Point &pt) const { return &pt; }
|
||||
};
|
||||
typedef ClosestPointInRadiusLookup<Point, PointAccessor> ClosestPointLookupType;
|
||||
|
||||
|
||||
Polygons loops0;
|
||||
{
|
||||
// find centerline of the external loop of the contours
|
||||
@ -983,7 +986,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact
|
||||
for (int i = 1; i < n_contact_loops; ++ i)
|
||||
polygons_append(loop_polygons,
|
||||
opening(
|
||||
loops0,
|
||||
loops0,
|
||||
i * flow.scaled_spacing() + 0.5f * flow.scaled_spacing(),
|
||||
0.5f * flow.scaled_spacing()));
|
||||
// Clip such loops to the side oriented towards the object.
|
||||
@ -1043,7 +1046,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact
|
||||
// Remove empty lines.
|
||||
remove_degenerate(loop_lines);
|
||||
}
|
||||
|
||||
|
||||
// add the contact infill area to the interface area
|
||||
// note that growing loops by $circle_radius ensures no tiny
|
||||
// extrusions are left inside the circles; however it creates
|
||||
@ -1115,7 +1118,7 @@ static void modulate_extrusion_by_overlapping_layers(
|
||||
// Split the extrusions by the overlapping layers, reduce their extrusion rate.
|
||||
// The last path_fragment is from this_layer.
|
||||
std::vector<ExtrusionPathFragment> path_fragments(
|
||||
n_overlapping_layers + 1,
|
||||
n_overlapping_layers + 1,
|
||||
ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height));
|
||||
// Don't use it, it will be released.
|
||||
extrusion_path_template = nullptr;
|
||||
@ -1167,7 +1170,7 @@ static void modulate_extrusion_by_overlapping_layers(
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
||||
// End points of the original paths.
|
||||
std::vector<std::pair<Point, Point>> path_ends;
|
||||
std::vector<std::pair<Point, Point>> path_ends;
|
||||
// Collect the paths of this_layer.
|
||||
{
|
||||
Polylines &polylines = path_fragments.back().polylines;
|
||||
@ -1384,7 +1387,7 @@ SupportGeneratorLayersPtr generate_support_layers(
|
||||
height_min = std::min(height_min, layer.height);
|
||||
}
|
||||
if (! empty) {
|
||||
// Here the upper_layer and lower_layer pointers are left to null at the support layers,
|
||||
// Here the upper_layer and lower_layer pointers are left to null at the support layers,
|
||||
// as they are never used. These pointers are candidates for removal.
|
||||
bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces;
|
||||
size_t this_layer_id_interface = layer_id_interface;
|
||||
@ -1459,7 +1462,7 @@ void generate_support_toolpaths(
|
||||
|
||||
// Print the support base below the support columns, or the support base for the support columns plus the contacts.
|
||||
if (support_layer_id > 0) {
|
||||
const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ?
|
||||
const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ?
|
||||
raft_layer.polygons :
|
||||
//FIXME misusing contact_polygons for support columns.
|
||||
((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons);
|
||||
@ -1480,7 +1483,7 @@ void generate_support_toolpaths(
|
||||
filler, float(support_params.support_density),
|
||||
// Extrusion parameters
|
||||
ExtrusionRole::erSupportMaterial, flow,
|
||||
support_params.with_sheath, false);
|
||||
support_params, support_params.with_sheath, false);
|
||||
}
|
||||
if (! tree_polygons.empty())
|
||||
tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow, support_params);
|
||||
@ -1507,15 +1510,15 @@ void generate_support_toolpaths(
|
||||
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
|
||||
fill_expolygons_with_sheath_generate_paths(
|
||||
// Destination
|
||||
support_layer.support_fills.entities,
|
||||
support_layer.support_fills.entities,
|
||||
// Regions to fill
|
||||
tree_polygons.empty() ? raft_layer.polygons : diff(raft_layer.polygons, tree_polygons),
|
||||
// Filler and its parameters
|
||||
filler, density,
|
||||
// Extrusion parameters
|
||||
(support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::erSupportMaterial : ExtrusionRole::erSupportMaterialInterface, flow,
|
||||
(support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::erSupportMaterial : ExtrusionRole::erSupportMaterialInterface, flow,
|
||||
// sheath at first layer
|
||||
support_layer_id == 0, support_layer_id == 0);
|
||||
support_params, support_layer_id == 0, support_layer_id == 0);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1559,12 +1562,12 @@ void generate_support_toolpaths(
|
||||
// Pointer to the 1st layer interface filler.
|
||||
auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get();
|
||||
// Filler for the 1st layer interface, if different from filler_interface.
|
||||
auto filler_raft_contact_ptr = std::unique_ptr<Fill>(range.begin() == n_raft_layers && config.support_interface_top_layers.value == 0 ?
|
||||
auto filler_raft_contact_ptr = std::unique_ptr<Fill>(range.begin() == n_raft_layers && config.support_interface_top_layers.value == 0 ?
|
||||
Fill::new_from_type(support_params.raft_interface_fill_pattern) : nullptr);
|
||||
// Pointer to the 1st layer interface filler.
|
||||
auto filler_raft_contact = filler_raft_contact_ptr ? filler_raft_contact_ptr.get() : filler_interface.get();
|
||||
// Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer).
|
||||
auto filler_base_interface = std::unique_ptr<Fill>(base_interface_layers.empty() ? nullptr :
|
||||
auto filler_base_interface = std::unique_ptr<Fill>(base_interface_layers.empty() ? nullptr :
|
||||
Fill::new_from_type(support_params.interface_density > 0.95 || support_params.with_sheath ? ipRectilinear : ipSupportBase));
|
||||
auto filler_support = std::unique_ptr<Fill>(Fill::new_from_type(support_params.base_fill_pattern));
|
||||
filler_interface->set_bounding_box(bbox_object);
|
||||
@ -1627,7 +1630,7 @@ void generate_support_toolpaths(
|
||||
// to trim other layers.
|
||||
if (top_contact_layer.could_merge(interface_layer) && ! raft_layer)
|
||||
top_contact_layer.merge(std::move(interface_layer));
|
||||
}
|
||||
}
|
||||
if ((config.support_interface_top_layers == 0 || config.support_interface_bottom_layers == 0) && support_params.can_merge_support_regions) {
|
||||
if (base_layer.could_merge(bottom_contact_layer))
|
||||
base_layer.merge(std::move(bottom_contact_layer));
|
||||
@ -1661,14 +1664,14 @@ void generate_support_toolpaths(
|
||||
auto *filler = raft_contact ? filler_raft_contact : filler_interface.get();
|
||||
auto interface_flow = layer_ex.layer->bridging ?
|
||||
Flow::bridging_flow(layer_ex.layer->height, support_params.support_material_bottom_interface_flow.nozzle_diameter()) :
|
||||
(raft_contact ? &support_params.raft_interface_flow :
|
||||
(raft_contact ? &support_params.raft_interface_flow :
|
||||
interface_as_base ? &support_params.support_material_flow : &support_params.support_material_interface_flow)
|
||||
->with_height(float(layer_ex.layer->height));
|
||||
filler->angle = interface_as_base ?
|
||||
// If zero interface layers are configured, use the same angle as for the base layers.
|
||||
angles[support_layer_id % angles.size()] :
|
||||
// Use interface angle for the interface layers.
|
||||
raft_contact ?
|
||||
raft_contact ?
|
||||
support_params.raft_interface_angle(support_layer.interface_id()) :
|
||||
support_interface_angle;
|
||||
double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density;
|
||||
@ -1677,7 +1680,7 @@ void generate_support_toolpaths(
|
||||
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
|
||||
fill_expolygons_generate_paths(
|
||||
// Destination
|
||||
layer_ex.extrusions,
|
||||
layer_ex.extrusions,
|
||||
// Regions to fill
|
||||
union_safety_offset_ex(layer_ex.polygons_to_extrude()),
|
||||
// Filler and its parameters
|
||||
@ -1704,7 +1707,7 @@ void generate_support_toolpaths(
|
||||
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.interface_density));
|
||||
fill_expolygons_generate_paths(
|
||||
// Destination
|
||||
base_interface_layer.extrusions,
|
||||
base_interface_layer.extrusions,
|
||||
//base_layer_interface.extrusions,
|
||||
// Regions to fill
|
||||
union_safety_offset_ex(base_interface_layer.polygons_to_extrude()),
|
||||
@ -1755,7 +1758,7 @@ void generate_support_toolpaths(
|
||||
filler, density,
|
||||
// Extrusion parameters
|
||||
ExtrusionRole::erSupportMaterial, flow,
|
||||
sheath, no_sort);
|
||||
support_params, sheath, no_sort);
|
||||
}
|
||||
|
||||
// Merge base_interface_layers to base_layers to avoid unneccessary retractions
|
||||
@ -1859,7 +1862,7 @@ void PrintObjectSupportMaterial::clip_by_pillars(
|
||||
|
||||
coord_t pillar_size = scale_(PILLAR_SIZE);
|
||||
coord_t pillar_spacing = scale_(PILLAR_SPACING);
|
||||
|
||||
|
||||
// A regular grid of pillars, filling the 2D bounding box.
|
||||
Polygons grid;
|
||||
{
|
||||
@ -1869,7 +1872,7 @@ void PrintObjectSupportMaterial::clip_by_pillars(
|
||||
pillar.points.push_back(Point(pillar_size, 0));
|
||||
pillar.points.push_back(Point(pillar_size, pillar_size));
|
||||
pillar.points.push_back(Point(0, pillar_size));
|
||||
|
||||
|
||||
// 2D bounding box of the projection of all contact polygons.
|
||||
BoundingBox bbox;
|
||||
for (LayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it)
|
||||
@ -1883,30 +1886,30 @@ void PrintObjectSupportMaterial::clip_by_pillars(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// add pillars to every layer
|
||||
for my $i (0..n_support_z) {
|
||||
$shape->[$i] = [ @$grid ];
|
||||
}
|
||||
|
||||
|
||||
// build capitals
|
||||
for my $i (0..n_support_z) {
|
||||
my $z = $support_z->[$i];
|
||||
|
||||
|
||||
my $capitals = intersection(
|
||||
$grid,
|
||||
$contact->{$z} // [],
|
||||
);
|
||||
|
||||
|
||||
// work on one pillar at time (if any) to prevent the capitals from being merged
|
||||
// but store the contact area supported by the capital because we need to make
|
||||
// but store the contact area supported by the capital because we need to make
|
||||
// sure nothing is left
|
||||
my $contact_supported_by_capitals = [];
|
||||
foreach my $capital (@$capitals) {
|
||||
// enlarge capital tops
|
||||
$capital = offset([$capital], +($pillar_spacing - $pillar_size)/2);
|
||||
push @$contact_supported_by_capitals, @$capital;
|
||||
|
||||
|
||||
for (my $j = $i-1; $j >= 0; $j--) {
|
||||
my $jz = $support_z->[$j];
|
||||
$capital = offset($capital, -$self->interface_flow->scaled_width/2);
|
||||
@ -1914,7 +1917,7 @@ void PrintObjectSupportMaterial::clip_by_pillars(
|
||||
push @{ $shape->[$j] }, @$capital;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Capitals will not generally cover the whole contact area because there will be
|
||||
// remainders. For now we handle this situation by projecting such unsupported
|
||||
// areas to the ground, just like we would do with a normal support.
|
||||
@ -1932,10 +1935,10 @@ void PrintObjectSupportMaterial::clip_by_pillars(
|
||||
|
||||
sub clip_with_shape {
|
||||
my ($self, $support, $shape) = @_;
|
||||
|
||||
|
||||
foreach my $i (keys %$support) {
|
||||
// don't clip bottom layer with shape so that we
|
||||
// can generate a continuous base flange
|
||||
// don't clip bottom layer with shape so that we
|
||||
// can generate a continuous base flange
|
||||
// also don't clip raft layers
|
||||
next if $i == 0;
|
||||
next if $i < $self->object_config->raft_layers;
|
||||
|
@ -45,7 +45,10 @@ SupportGeneratorLayersPtr generate_raft_base(
|
||||
const SupportGeneratorLayersPtr &base_layers,
|
||||
SupportGeneratorLayerStorage &layer_storage);
|
||||
|
||||
void fill_expolygons_with_sheath_generate_paths(ExtrusionEntitiesPtr& dst, const Polygons& polygons, Fill* filler, float density, ExtrusionRole role, const Flow& flow, bool with_sheath, bool no_sort);
|
||||
void tree_supports_generate_paths(ExtrusionEntitiesPtr &dst, const Polygons &polygons, const Flow &flow, const SupportParameters &support_params);
|
||||
|
||||
void fill_expolygons_with_sheath_generate_paths(
|
||||
ExtrusionEntitiesPtr &dst, const Polygons &polygons, Fill *filler, float density, ExtrusionRole role, const Flow &flow, const SupportParameters& support_params, bool with_sheath, bool no_sort);
|
||||
|
||||
// returns sorted layers
|
||||
SupportGeneratorLayersPtr generate_support_layers(
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
namespace Slic3r {
|
||||
struct SupportParameters {
|
||||
SupportParameters() = default;
|
||||
SupportParameters() = delete;
|
||||
SupportParameters(const PrintObject& object)
|
||||
{
|
||||
const PrintConfig& print_config = object.print()->config();
|
||||
@ -149,9 +149,19 @@ struct SupportParameters {
|
||||
assert(slicing_params.interface_raft_layers == 0);
|
||||
assert(slicing_params.raft_layers() == 0);
|
||||
}
|
||||
|
||||
double tree_support_branch_diameter_double_wall = 3.0; // in organic support, Branches with area larger than the area of a circle of this diameter will be printed with double walls for stability
|
||||
this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(tree_support_branch_diameter_double_wall)) * M_PI;
|
||||
|
||||
support_extrusion_width = object_config.support_line_width.value > 0 ? object_config.support_line_width : object_config.line_width;
|
||||
// Check if set to zero, use default if so.
|
||||
if (support_extrusion_width <= 0.0) {
|
||||
const auto nozzle_diameter = print_config.nozzle_diameter.get_at(object_config.support_interface_filament - 1);
|
||||
support_extrusion_width = Flow::auto_extrusion_width(FlowRole::frSupportMaterial, (float) nozzle_diameter);
|
||||
}
|
||||
|
||||
independent_layer_height = print_config.independent_support_layer_height;
|
||||
|
||||
// force double walls everywhere if wall count is larger than 1
|
||||
tree_branch_diameter_double_wall_area_scaled = object_config.tree_support_wall_count.value > 1 ? 0.1 : 0.25 * sqr(scaled<double>(5.0)) * M_PI;
|
||||
|
||||
}
|
||||
// Both top / bottom contacts and interfaces are soluble.
|
||||
bool soluble_interface;
|
||||
@ -210,7 +220,7 @@ struct SupportParameters {
|
||||
InfillPattern contact_fill_pattern;
|
||||
bool with_sheath;
|
||||
// Branches of organic supports with area larger than this threshold will be extruded with double lines.
|
||||
double tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(3.0)) * M_PI;;
|
||||
double tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(5.0)) * M_PI;;
|
||||
|
||||
float raft_angle_1st_layer;
|
||||
float raft_angle_base;
|
||||
|
@ -617,12 +617,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
|
||||
|
||||
if(support_pattern == smpLightning)
|
||||
m_support_params.base_fill_pattern = ipLightning;
|
||||
m_support_params.support_extrusion_width = m_object_config->support_line_width.value > 0 ? m_object_config->support_line_width : m_object_config->line_width;
|
||||
// Check if set to zero, use default if so.
|
||||
if (m_support_params.support_extrusion_width <= 0.0) {
|
||||
const auto nozzle_diameter = object.print()->config().nozzle_diameter.get_at(object.config().support_interface_filament - 1);
|
||||
m_support_params.support_extrusion_width = Flow::auto_extrusion_width(FlowRole::frSupportMaterial, (float)nozzle_diameter);
|
||||
}
|
||||
|
||||
is_slim = is_tree_slim(support_type, support_style);
|
||||
is_strong = is_tree(support_type) && support_style == smsTreeStrong;
|
||||
base_radius = std::max(MIN_BRANCH_RADIUS, m_object_config->tree_support_branch_diameter.value / 2);
|
||||
@ -632,7 +627,6 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
|
||||
Vec3d plate_offset = m_object->print()->get_plate_origin();
|
||||
// align with the centered object in current plate (may not be the 1st plate, so need to add the plate offset)
|
||||
m_machine_border.translate(Point(scale_(plate_offset(0)), scale_(plate_offset(1))) - m_object->instances().front().shift);
|
||||
m_support_params.independent_layer_height = m_print_config->independent_support_layer_height;
|
||||
top_z_distance = m_object_config->support_top_z_distance.value;
|
||||
if (top_z_distance > EPSILON) top_z_distance = std::max(top_z_distance, float(m_slicing_params.min_layer_height));
|
||||
#if USE_TREESUPPRT3D
|
||||
@ -1538,7 +1532,8 @@ void TreeSupport::generate_toolpaths()
|
||||
Polygons loops = to_polygons(poly);
|
||||
if (layer_id == 0) {
|
||||
float density = float(m_object_config->raft_first_layer_density.value * 0.01);
|
||||
fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, filler_support.get(), density, erSupportMaterial, flow, true, false);
|
||||
fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, filler_support.get(), density, erSupportMaterial, flow,
|
||||
m_support_params, true, false);
|
||||
}
|
||||
else {
|
||||
if (need_infill && m_support_params.base_fill_pattern != ipLightning) {
|
||||
@ -1551,12 +1546,16 @@ void TreeSupport::generate_toolpaths()
|
||||
}
|
||||
else {
|
||||
size_t walls = wall_count;
|
||||
if (area_group.need_extra_wall && walls < 2) walls += 1;
|
||||
for (size_t i = 1; i < walls; i++) {
|
||||
Polygons contour_new = offset(poly.contour, -(i - 0.5f) * flow.scaled_spacing(), jtSquare);
|
||||
loops.insert(loops.end(), contour_new.begin(), contour_new.end());
|
||||
}
|
||||
fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, nullptr, 0, erSupportMaterial, flow, true, false);
|
||||
//if (area_group.need_extra_wall && walls < 2) walls += 1;
|
||||
//for (size_t i = 1; i < walls; i++) {
|
||||
// Polygons contour_new = offset(poly.contour, -(i - 0.5f) * flow.scaled_spacing(), jtSquare);
|
||||
// loops.insert(loops.end(), contour_new.begin(), contour_new.end());
|
||||
//}
|
||||
//fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, nullptr, 0, erSupportMaterial, flow, true, false);
|
||||
SupportParameters support_params = m_support_params;
|
||||
if(walls>1)
|
||||
support_params.tree_branch_diameter_double_wall_area_scaled=0.1;
|
||||
tree_supports_generate_paths(ts_layer->support_fills.entities, loops, flow, support_params);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1616,8 +1615,12 @@ void TreeSupport::generate_toolpaths()
|
||||
}
|
||||
|
||||
// sort extrusions to reduce travel, also make sure walls go before infills
|
||||
if(ts_layer->support_fills.no_sort==false)
|
||||
if (ts_layer->support_fills.no_sort == false) {
|
||||
// chain_and_reorder_extrusion_entities crashes if there are empty elements in entities
|
||||
auto &entities = ts_layer->support_fills.entities;
|
||||
entities.erase(std::remove_if(entities.begin(), entities.end(), [](ExtrusionEntity* entity) { return static_cast<ExtrusionEntityCollection*>(entity)->empty(); }), entities.end());
|
||||
chain_and_reorder_extrusion_entities(ts_layer->support_fills.entities);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -4215,6 +4215,7 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
|
||||
support_params.support_density = 0;
|
||||
}
|
||||
|
||||
|
||||
SupportGeneratorLayerStorage layer_storage;
|
||||
SupportGeneratorLayersPtr top_contacts;
|
||||
SupportGeneratorLayersPtr bottom_contacts;
|
||||
|
Loading…
x
Reference in New Issue
Block a user