Remove duplicated support code

This commit is contained in:
Noisyfox 2025-01-26 15:36:07 +08:00
parent 58d524d75f
commit 824f9efb69
3 changed files with 146 additions and 1732 deletions

View file

@ -329,19 +329,21 @@ SupportGeneratorLayersPtr generate_raft_base(
const BrimType brim_type = object.config().brim_type; const BrimType brim_type = object.config().brim_type;
const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner; const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner;
const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner; const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner;
const auto brim_separation = scaled<float>(object.config().brim_object_gap.value + object.config().brim_width.value); // BBS: the pattern of raft and brim are the same, thus the brim can be serpated by support raft.
const auto brim_object_gap = scaled<float>(object.config().brim_object_gap.value);
//const auto brim_object_gap = scaled<float>(object.config().brim_object_gap.value + object.config().brim_width.value);
for (const ExPolygon &ex : object.layers().front()->lslices) { for (const ExPolygon &ex : object.layers().front()->lslices) {
if (brim_outer && brim_inner) if (brim_outer && brim_inner)
polygons_append(brim, offset(ex, brim_separation)); polygons_append(brim, offset(ex, brim_object_gap));
else { else {
if (brim_outer) if (brim_outer)
polygons_append(brim, offset(ex.contour, brim_separation, ClipperLib::jtRound, float(scale_(0.1)))); polygons_append(brim, offset(ex.contour, brim_object_gap, ClipperLib::jtRound, float(scale_(0.1))));
else else
brim.emplace_back(ex.contour); brim.emplace_back(ex.contour);
if (brim_inner) { if (brim_inner) {
Polygons holes = ex.holes; Polygons holes = ex.holes;
polygons_reverse(holes); polygons_reverse(holes);
holes = shrink(holes, brim_separation, ClipperLib::jtRound, float(scale_(0.1))); holes = shrink(holes, brim_object_gap, ClipperLib::jtRound, float(scale_(0.1)));
polygons_reverse(holes); polygons_reverse(holes);
polygons_append(brim, std::move(holes)); polygons_append(brim, std::move(holes));
} else } else
@ -407,7 +409,7 @@ SupportGeneratorLayersPtr generate_raft_base(
// Do not add the raft contact layer, only add the raft layers below the contact layer. // Do not add the raft contact layer, only add the raft layers below the contact layer.
// Insert the 1st layer. // Insert the 1st layer.
{ {
SupportGeneratorLayer &new_layer = layer_storage.allocate_unguarded(slicing_params.base_raft_layers > 0 ? SupporLayerType::RaftBase : SupporLayerType::RaftInterface); SupportGeneratorLayer &new_layer = layer_storage.allocate(slicing_params.base_raft_layers > 0 ? SupporLayerType::RaftBase : SupporLayerType::RaftInterface);
raft_layers.push_back(&new_layer); raft_layers.push_back(&new_layer);
new_layer.print_z = slicing_params.first_print_layer_height; new_layer.print_z = slicing_params.first_print_layer_height;
new_layer.height = slicing_params.first_print_layer_height; new_layer.height = slicing_params.first_print_layer_height;
@ -441,7 +443,12 @@ SupportGeneratorLayersPtr generate_raft_base(
if (columns_base != nullptr) { if (columns_base != nullptr) {
// Expand the bases of the support columns in the 1st layer. // Expand the bases of the support columns in the 1st layer.
Polygons &raft = columns_base->polygons; Polygons &raft = columns_base->polygons;
Polygons trimming = offset(object.layers().front()->lslices, (float)scale_(support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS); Polygons trimming;
// BBS: if first layer of support is intersected with object island, it must have the same function as brim unless in nobrim mode.
if (object.has_brim())
trimming = offset(object.layers().front()->lslices, (float)scale_(object.config().brim_object_gap.value), SUPPORT_SURFACES_OFFSET_PARAMETERS);
else
trimming = offset(object.layers().front()->lslices, (float)scale_(support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (inflate_factor_1st_layer > SCALED_EPSILON) { if (inflate_factor_1st_layer > SCALED_EPSILON) {
// Inflate in multiple steps to avoid leaking of the support 1st layer through object walls. // Inflate in multiple steps to avoid leaking of the support 1st layer through object walls.
auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / support_params.first_layer_flow.scaled_width()))); auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / support_params.first_layer_flow.scaled_width())));
@ -536,7 +543,7 @@ static Polylines draw_perimeters(const ExPolygon &expoly, double clip_length)
return polylines; return polylines;
} }
static inline void tree_supports_generate_paths( void tree_supports_generate_paths(
ExtrusionEntitiesPtr &dst, ExtrusionEntitiesPtr &dst,
const Polygons &polygons, const Polygons &polygons,
const Flow &flow, const Flow &flow,
@ -637,124 +644,126 @@ static inline void tree_supports_generate_paths(
ClipperLib_Z::Paths anchor_candidates; ClipperLib_Z::Paths anchor_candidates;
for (ExPolygon& expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width()))) { for (ExPolygon& expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width()))) {
std::unique_ptr<ExtrusionEntityCollection> eec; std::unique_ptr<ExtrusionEntityCollection> eec;
ExPolygons regions_to_draw_inner_wall{expoly};
if (support_params.tree_branch_diameter_double_wall_area_scaled > 0) 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) { 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>(); 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; eec->no_sort = true;
// Make the tree branch stable by adding another perimeter. // Make the tree branch stable by adding another perimeter.
ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); ExPolygons level2 = offset2_ex({expoly}, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width());
if (level2.size() == 1) { if (level2.size() > 0) {
Polylines polylines; 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(), 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. // Disable reversal of the path, always start with the anchor, always print CCW.
false); false);
expoly = level2.front(); 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. // Draw the perimeters.
// First genrate a 2nd perimeter loop as a source for anchor candidates. Polylines polylines;
// The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection. polylines.reserve(expoly.holes.size() + 1);
anchor_candidates.clear(); for (int idx_loop = 0; idx_loop < int(expoly.num_contours()); ++idx_loop) {
shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates); // Open the loop with a seam.
// Orient all contours CW. const Polygon &loop = expoly.contour_or_hole(idx_loop);
for (auto &path : anchor_candidates) Polyline pl(loop.points);
if (ClipperLib_Z::Area(path) > 0) // 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.
std::reverse(path.begin(), path.end()); if (idx_loop == 0)
// It is an outer contour.
// Draw the perimeters. pl.reverse();
Polylines polylines; pl.points.emplace_back(pl.points.front());
polylines.reserve(expoly.holes.size() + 1); pl.clip_end(clip_length);
for (int idx_loop = 0; idx_loop < int(expoly.num_contours()); ++ idx_loop) { if (pl.size() < 2) continue;
// Open the loop with a seam. // Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour.
const Polygon &loop = expoly.contour_or_hole(idx_loop); ClipperLib_Z::Path *closest_contour = nullptr;
Polyline pl(loop.points); Vec2d closest_point;
// 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. int closest_point_idx = -1;
if (idx_loop == 0) double closest_point_t = 0.;
// It is an outer contour. double d2min = std::numeric_limits<double>::max();
pl.reverse(); Vec2d seam_pt = pl.back().cast<double>();
pl.points.emplace_back(pl.points.front()); for (ClipperLib_Z::Path &path : anchor_candidates)
pl.clip_end(clip_length); for (int i = 0; i < int(path.size()); ++i) {
if (pl.size() < 2) int j = next_idx_modulo(i, path);
continue; if (path[i].z() == idx_loop || path[j].z() == idx_loop) {
// Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour. Vec2d pi(path[i].x(), path[i].y());
ClipperLib_Z::Path *closest_contour = nullptr; Vec2d pj(path[j].x(), path[j].y());
Vec2d closest_point; Vec2d v = pj - pi;
int closest_point_idx = -1; Vec2d w = seam_pt - pi;
double closest_point_t = 0.; auto l2 = v.squaredNorm();
double d2min = std::numeric_limits<double>::max(); auto t = std::clamp((l2 == 0) ? 0 : v.dot(w) / l2, 0., 1.);
Vec2d seam_pt = pl.back().cast<double>(); if ((path[i].z() == idx_loop || t > EPSILON) && (path[j].z() == idx_loop || t < 1. - EPSILON)) {
for (ClipperLib_Z::Path &path : anchor_candidates) // Closest point.
for (int i = 0; i < int(path.size()); ++ i) { Vec2d fp = pi + v * t;
int j = next_idx_modulo(i, path); double d2 = (fp - seam_pt).squaredNorm();
if (path[i].z() == idx_loop || path[j].z() == idx_loop) { if (d2 < d2min) {
Vec2d pi(path[i].x(), path[i].y()); d2min = d2;
Vec2d pj(path[j].x(), path[j].y()); closest_contour = &path;
Vec2d v = pj - pi; closest_point = fp;
Vec2d w = seam_pt - pi; closest_point_idx = i;
auto l2 = v.squaredNorm(); closest_point_t = t;
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.)) {
if (d2min < sqr(flow.scaled_width() * 3.)) { // Try to cut an anchor from the closest_contour.
// Try to cut an anchor from the closest_contour. // Both closest_contour and pl are CW oriented.
// Both closest_contour and pl are CW oriented. pl.points.emplace_back(closest_point.cast<coord_t>());
pl.points.emplace_back(closest_point.cast<coord_t>()); const ClipperLib_Z::Path &path = *closest_contour;
const ClipperLib_Z::Path &path = *closest_contour; double remaining_length = anchor_length - (seam_pt - closest_point).norm();
double remaining_length = anchor_length - (seam_pt - closest_point).norm(); int i = closest_point_idx;
int i = closest_point_idx; int j = next_idx_modulo(i, *closest_contour);
int j = next_idx_modulo(i, *closest_contour); Vec2d pi(path[i].x(), path[i].y());
Vec2d pi(path[i].x(), path[i].y()); Vec2d pj(path[j].x(), path[j].y());
Vec2d pj(path[j].x(), path[j].y()); Vec2d v = pj - pi;
Vec2d v = pj - pi; double l = v.norm();
double l = v.norm(); if (remaining_length < (1. - closest_point_t) * l) {
if (remaining_length < (1. - closest_point_t) * l) { // Just trim the current line.
// Just trim the current line. pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast<coord_t>());
pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast<coord_t>()); } else {
} else { // Take the rest of the current line, continue with the other lines.
// 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;
}
pl.points.emplace_back(path[j].x(), path[j].y()); 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; ExtrusionEntitiesPtr &out = eec ? eec->entities : dst;
extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), 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. // Disable reversal of the path, always start with the anchor, always print CCW.
false); false);
}
if (eec) { if (eec) {
std::reverse(eec->entities.begin(), eec->entities.end()); std::reverse(eec->entities.begin(), eec->entities.end());
dst.emplace_back(eec.release()); dst.emplace_back(eec.release());
@ -762,20 +771,27 @@ static inline void tree_supports_generate_paths(
} }
} }
static inline void fill_expolygons_with_sheath_generate_paths( void fill_expolygons_with_sheath_generate_paths(
ExtrusionEntitiesPtr &dst, ExtrusionEntitiesPtr &dst,
const Polygons &polygons, const Polygons &polygons,
Fill *filler, Fill *filler,
float density, float density,
ExtrusionRole role, ExtrusionRole role,
const Flow &flow, const Flow &flow,
const SupportParameters& support_params,
bool with_sheath, bool with_sheath,
bool no_sort) bool no_sort)
{ {
if (polygons.empty()) if (polygons.empty())
return; return;
if (! with_sheath) { if (with_sheath) {
if (density == 0) {
tree_supports_generate_paths(dst, polygons, flow, support_params);
return;
}
}
else {
fill_expolygons_generate_paths(dst, closing_ex(polygons, float(SCALED_EPSILON)), filler, density, role, flow); fill_expolygons_generate_paths(dst, closing_ex(polygons, float(SCALED_EPSILON)), filler, density, role, flow);
return; return;
} }
@ -1531,7 +1547,7 @@ void generate_support_toolpaths(
filler, float(support_params.support_density), filler, float(support_params.support_density),
// Extrusion parameters // Extrusion parameters
ExtrusionRole::erSupportMaterial, flow, ExtrusionRole::erSupportMaterial, flow,
support_params.with_sheath, false); support_params, support_params.with_sheath, false);
} }
if (! tree_polygons.empty()) if (! tree_polygons.empty())
tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow, support_params); tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow, support_params);
@ -1566,7 +1582,7 @@ void generate_support_toolpaths(
// Extrusion parameters // 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 // sheath at first layer
support_layer_id == 0, support_layer_id == 0); support_params, support_layer_id == 0, support_layer_id == 0);
} }
}); });
@ -1734,7 +1750,7 @@ void generate_support_toolpaths(
// Filler and its parameters // Filler and its parameters
filler, float(density), filler, float(density),
// Extrusion parameters // Extrusion parameters
ExtrusionRole::erSupportMaterialInterface, interface_flow); interface_as_base ? ExtrusionRole::erSupportMaterial : ExtrusionRole::erSupportMaterialInterface, interface_flow);
} }
}; };
const bool top_interfaces = config.support_interface_top_layers.value != 0; const bool top_interfaces = config.support_interface_top_layers.value != 0;
@ -1808,7 +1824,7 @@ void generate_support_toolpaths(
filler, density, filler, density,
// Extrusion parameters // Extrusion parameters
ExtrusionRole::erSupportMaterial, flow, ExtrusionRole::erSupportMaterial, flow,
sheath, no_sort); support_params, sheath, no_sort);
} }
// Merge base_interface_layers to base_layers to avoid unneccessary retractions // Merge base_interface_layers to base_layers to avoid unneccessary retractions

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@
#include "Slicing.hpp" #include "Slicing.hpp"
#include "Fill/FillBase.hpp" #include "Fill/FillBase.hpp"
#include "SupportLayer.hpp" #include "SupportLayer.hpp"
#include "SupportParameters.hpp"
namespace Slic3r { namespace Slic3r {
class PrintObject; class PrintObject;
@ -18,35 +19,6 @@ class PrintObjectConfig;
// the parameters of the raft to determine the 1st layer height and thickness. // the parameters of the raft to determine the 1st layer height and thickness.
class PrintObjectSupportMaterial class PrintObjectSupportMaterial
{ {
public:
struct SupportParams {
Flow first_layer_flow;
Flow support_material_flow;
Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t support_expansion;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
InfillPattern base_fill_pattern;
InfillPattern interface_fill_pattern;
InfillPattern contact_fill_pattern;
bool with_sheath;
};
public: public:
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params); PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
@ -97,24 +69,6 @@ private:
SupportGeneratorLayersPtr &intermediate_layers, SupportGeneratorLayersPtr &intermediate_layers,
const std::vector<Polygons> &layer_support_areas) const; const std::vector<Polygons> &layer_support_areas) const;
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
SupportGeneratorLayersPtr generate_raft_base(
const PrintObject &object,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers,
const SupportGeneratorLayersPtr &base_layers,
SupportGeneratorLayerStorage &layer_storage) const;
// Turn some of the base layers into base interface layers.
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
// extruder to improve adhesion of the soluble filament to the base.
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage) const;
// Trim support layers by an object to leave a defined gap between // Trim support layers by an object to leave a defined gap between
@ -131,16 +85,6 @@ private:
void clip_with_shape(); void clip_with_shape();
*/ */
// Produce the actual G-code.
void generate_toolpaths(
SupportLayerPtrs &support_layers,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers) const;
// Following objects are not owned by SupportMaterial class. // Following objects are not owned by SupportMaterial class.
const PrintObject *m_object; const PrintObject *m_object;
const PrintConfig *m_print_config; const PrintConfig *m_print_config;
@ -149,7 +93,7 @@ private:
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc. // carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params; SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions. // Various precomputed support parameters to be shared with external functions.
SupportParams m_support_params; SupportParameters m_support_params;
}; };
} // namespace Slic3r } // namespace Slic3r