Implemented raft for Organic supports. A multi-layer raft is only built

for 1st object layer; the trees go to the print bed and only
the 1st layer raft is built for both the multi-layer raft and the trees.
Fixes #9816 #9743 #9526
This commit is contained in:
Vojtech Bubnik 2023-02-27 11:03:44 +01:00
parent 825c954b44
commit c84c699a96
3 changed files with 136 additions and 43 deletions

View File

@ -680,6 +680,13 @@ Slic3r::Polygons union_(const Slic3r::ExPolygons &subject)
{ return _clipper(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No); } { return _clipper(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No); }
Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2) Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2)
{ return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(subject2), ApplySafetyOffset::No); } { return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(subject2), ApplySafetyOffset::No); }
Slic3r::Polygons union_(Slic3r::Polygons &&subject, const Slic3r::Polygons &subject2) {
if (subject.empty())
return subject2;
if (subject2.empty())
return std::move(subject);
return union_(subject, subject2);
}
Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &subject2) Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &subject2)
{ return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(subject2), ApplySafetyOffset::No); } { return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(subject2), ApplySafetyOffset::No); }

View File

@ -2011,11 +2011,11 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers(
// Find the bottom contact layers above the top surfaces of this layer. // Find the bottom contact layers above the top surfaces of this layer.
static inline SupportGeneratorLayer* detect_bottom_contacts( static inline SupportGeneratorLayer* detect_bottom_contacts(
const SlicingParameters &slicing_params, const SlicingParameters &slicing_params,
const SupportParameters &support_params, const SupportParameters &support_params,
const PrintObject &object, const PrintObject &object,
const Layer &layer, const Layer &layer,
// Existing top contact layers, to which this newly created bottom contact layer will be snapped to guarantee a minimum layer height. // Existing top contact layers, to which this newly created bottom contact layer will be snapped to guarantee a minimum layer height.
const SupportGeneratorLayersPtr &top_contacts, const SupportGeneratorLayersPtr &top_contacts,
// First top contact layer index overlapping with this new bottom interface layer. // First top contact layer index overlapping with this new bottom interface layer.
size_t contact_idx, size_t contact_idx,
// To allocate a new layer from. // To allocate a new layer from.
@ -2888,6 +2888,7 @@ SupportGeneratorLayersPtr generate_raft_base(
// If there is brim to be generated, calculate the trimming regions. // If there is brim to be generated, calculate the trimming regions.
Polygons brim; Polygons brim;
if (object.has_brim()) { if (object.has_brim()) {
// The object does not have a raft.
// Calculate the area covered by the brim. // Calculate the area covered by the brim.
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;
@ -2948,12 +2949,20 @@ SupportGeneratorLayersPtr generate_raft_base(
if (slicing_params.raft_layers() > 1) { if (slicing_params.raft_layers() > 1) {
Polygons base; Polygons base;
Polygons columns; Polygons columns;
Polygons first_layer;
if (columns_base != nullptr) { if (columns_base != nullptr) {
base = columns_base->polygons; if (columns_base->print_z > slicing_params.raft_contact_top_z - EPSILON) {
columns = base; // Classic supports with colums above the raft interface.
if (! interface_polygons.empty()) base = columns_base->polygons;
// Trim the 1st layer columns with the inflated interface polygons. columns = base;
columns = diff(columns, interface_polygons); if (! interface_polygons.empty())
// Trim the 1st layer columns with the inflated interface polygons.
columns = diff(columns, interface_polygons);
} else {
// Organic supports with raft on print bed.
assert(is_approx(columns_base->print_z, slicing_params.first_print_layer_height));
first_layer = columns_base->polygons;
}
} }
if (! interface_polygons.empty()) { if (! interface_polygons.empty()) {
// Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface. // Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface.
@ -2967,7 +2976,8 @@ SupportGeneratorLayersPtr generate_raft_base(
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;
new_layer.bottom_z = 0.; new_layer.bottom_z = 0.;
new_layer.polygons = inflate_factor_1st_layer > 0 ? expand(base, inflate_factor_1st_layer) : base; first_layer = union_(std::move(first_layer), base);
new_layer.polygons = inflate_factor_1st_layer > 0 ? expand(first_layer, inflate_factor_1st_layer) : first_layer;
} }
// Insert the base layers. // Insert the base layers.
for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) { for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) {
@ -3492,12 +3502,7 @@ static inline void fill_expolygons_with_sheath_generate_paths(
if (polygons.empty()) if (polygons.empty())
return; return;
if (with_sheath) { if (! with_sheath) {
if (density == 0) {
tree_supports_generate_paths(dst, polygons, flow);
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;
} }
@ -4229,7 +4234,7 @@ void generate_support_toolpaths(
// Insert the raft base layers. // Insert the raft base layers.
auto n_raft_layers = std::min<size_t>(support_layers.size(), std::max(0, int(slicing_params.raft_layers()) - 1)); auto n_raft_layers = std::min<size_t>(support_layers.size(), std::max(0, int(slicing_params.raft_layers()) - 1));
tbb::parallel_for(tbb::blocked_range<size_t>(0, n_raft_layers), tbb::parallel_for(tbb::blocked_range<size_t>(0, n_raft_layers),
[&support_layers, &raft_layers, &config, &support_params, &slicing_params, [&support_layers, &raft_layers, &intermediate_layers, &config, &support_params, &slicing_params,
&bbox_object, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor] &bbox_object, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor]
(const tbb::blocked_range<size_t>& range) { (const tbb::blocked_range<size_t>& range) {
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
@ -4244,16 +4249,24 @@ void generate_support_toolpaths(
filler_interface->set_bounding_box(bbox_object); filler_interface->set_bounding_box(bbox_object);
filler_support->set_bounding_box(bbox_object); filler_support->set_bounding_box(bbox_object);
// Print the tree supports cutting through the raft with the exception of the 1st layer, where a full support layer will be printed below
// both the raft and the trees.
// Trim the raft layers with the tree polygons.
const Polygons &tree_polygons =
support_layer_id > 0 && support_layer_id < intermediate_layers.size() && is_approx(intermediate_layers[support_layer_id]->print_z, support_layer.print_z) ?
intermediate_layers[support_layer_id]->polygons : Polygons();
// Print the support base below the support columns, or the support base for the support columns plus the contacts. // Print the support base below the support columns, or the support base for the support columns plus the contacts.
if (support_layer_id > 0) { 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 : raft_layer.polygons :
//FIXME misusing contact_polygons for support columns. //FIXME misusing contact_polygons for support columns.
((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons);
// Trees may cut through the raft layers down to a print bed.
Flow flow(float(support_params.support_material_flow.width()), float(raft_layer.height), support_params.support_material_flow.nozzle_diameter());
assert(!raft_layer.bridging);
if (! to_infill_polygons.empty()) { if (! to_infill_polygons.empty()) {
assert(! raft_layer.bridging); Fill *filler = filler_support.get();
Flow flow(float(support_params.support_material_flow.width()), float(raft_layer.height), support_params.support_material_flow.nozzle_diameter());
Fill * filler = filler_support.get();
filler->angle = raft_angle_base; filler->angle = raft_angle_base;
filler->spacing = support_params.support_material_flow.spacing(); filler->spacing = support_params.support_material_flow.spacing();
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.support_density)); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.support_density));
@ -4261,13 +4274,15 @@ void generate_support_toolpaths(
// Destination // Destination
support_layer.support_fills.entities, support_layer.support_fills.entities,
// Regions to fill // Regions to fill
to_infill_polygons, tree_polygons.empty() ? to_infill_polygons : diff(to_infill_polygons, tree_polygons),
// Filler and its parameters // Filler and its parameters
filler, float(support_params.support_density), filler, float(support_params.support_density),
// Extrusion parameters // Extrusion parameters
ExtrusionRole::SupportMaterial, flow, ExtrusionRole::SupportMaterial, flow,
support_params.with_sheath, false); support_params.with_sheath, false);
} }
if (! tree_polygons.empty())
tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow);
} }
Fill *filler = filler_interface.get(); Fill *filler = filler_interface.get();
@ -4293,7 +4308,7 @@ void generate_support_toolpaths(
// Destination // Destination
support_layer.support_fills.entities, support_layer.support_fills.entities,
// Regions to fill // Regions to fill
raft_layer.polygons, tree_polygons.empty() ? raft_layer.polygons : diff(raft_layer.polygons, tree_polygons),
// Filler and its parameters // Filler and its parameters
filler, density, filler, density,
// Extrusion parameters // Extrusion parameters
@ -4491,6 +4506,7 @@ void generate_support_toolpaths(
float density = float(support_params.support_density); float density = float(support_params.support_density);
bool sheath = support_params.with_sheath; bool sheath = support_params.with_sheath;
bool no_sort = false; bool no_sort = false;
bool done = false;
if (base_layer.layer->bottom_z < EPSILON) { if (base_layer.layer->bottom_z < EPSILON) {
// Base flange (the 1st layer). // Base flange (the 1st layer).
filler = filler_first_layer; filler = filler_first_layer;
@ -4504,18 +4520,21 @@ void generate_support_toolpaths(
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
sheath = true; sheath = true;
no_sort = true; no_sort = true;
} else if (config.support_material_style == SupportMaterialStyle::smsOrganic) {
tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow);
done = true;
} }
fill_expolygons_with_sheath_generate_paths( if (! done)
// Destination fill_expolygons_with_sheath_generate_paths(
base_layer.extrusions, // Destination
// Regions to fill base_layer.extrusions,
base_layer.polygons_to_extrude(), // Regions to fill
// Filler and its parameters base_layer.polygons_to_extrude(),
filler, density, // Filler and its parameters
// Extrusion parameters filler, density,
ExtrusionRole::SupportMaterial, flow, // Extrusion parameters
sheath, no_sort); ExtrusionRole::SupportMaterial, flow,
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
@ -4708,3 +4727,4 @@ sub clip_with_shape {
*/ */
} // namespace Slic3r } // namespace Slic3r

View File

@ -116,24 +116,30 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mes
if (slicing_params.raft_layers() > 0) { if (slicing_params.raft_layers() > 0) {
// Fill in raft_layers with the heights of the layers below the first object layer. // Fill in raft_layers with the heights of the layers below the first object layer.
// First layer
double z = slicing_params.first_print_layer_height; double z = slicing_params.first_print_layer_height;
this->raft_layers.emplace_back(z); this->raft_layers.emplace_back(z);
// Raft base layers
for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) { for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) {
z += slicing_params.base_raft_layer_height; z += slicing_params.base_raft_layer_height;
this->raft_layers.emplace_back(z); this->raft_layers.emplace_back(z);
} }
// Raft interface layers
for (size_t i = 0; i + 1 < slicing_params.interface_raft_layers; ++ i) { for (size_t i = 0; i + 1 < slicing_params.interface_raft_layers; ++ i) {
z += slicing_params.interface_raft_layer_height; z += slicing_params.interface_raft_layer_height;
this->raft_layers.emplace_back(z); this->raft_layers.emplace_back(z);
} }
assert(is_approx(z, slicing_params.raft_interface_top_z)); // Raft contact layer
double dist_to_go = slicing_params.object_print_z_min - z; z = slicing_params.raft_contact_top_z;
assert(dist_to_go > slicing_params.min_layer_height - EPSILON); this->raft_layers.emplace_back(z);
auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height)); if (double dist_to_go = slicing_params.object_print_z_min - z; dist_to_go > EPSILON) {
double step = dist_to_go / nsteps; // Layers between the raft contacts and bottom of the object.
for (size_t i = 0; i < nsteps; ++ i) { auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height));
z += step; double step = dist_to_go / nsteps;
this->raft_layers.emplace_back(z); for (size_t i = 0; i < nsteps; ++ i) {
z += step;
this->raft_layers.emplace_back(z);
}
} }
} }
} }
@ -401,6 +407,7 @@ void tree_supports_show_error(std::string_view message, bool critical)
} }
}); });
#if 0
if (num_raft_layers > 0) { if (num_raft_layers > 0) {
const Layer &first_layer = *print_object.get_layer(0); const Layer &first_layer = *print_object.get_layer(0);
// Final overhangs. // Final overhangs.
@ -423,6 +430,7 @@ void tree_supports_show_error(std::string_view message, bool critical)
out[num_raft_layers] = std::move(overhangs); out[num_raft_layers] = std::move(overhangs);
throw_on_cancel(); throw_on_cancel();
} }
#endif
return out; return out;
} }
@ -1053,13 +1061,30 @@ static void generate_initial_areas(
std::max<coord_t>(round_up_divide(mesh_config.xy_distance, max_overhang_speed / 2), 2 * mesh_config.z_distance_top_layers) : std::max<coord_t>(round_up_divide(mesh_config.xy_distance, max_overhang_speed / 2), 2 * mesh_config.z_distance_top_layers) :
0; 0;
//FIXME
const size_t num_raft_layers = config.raft_layers.size(); const size_t num_raft_layers = config.raft_layers.size();
const size_t num_support_layers = size_t(std::max(0, int(print_object.layer_count()) + int(num_raft_layers) - int(z_distance_delta))); const size_t num_support_layers = size_t(std::max(0, int(print_object.layer_count()) + int(num_raft_layers) - int(z_distance_delta)));
const size_t first_support_layer = std::max(int(num_raft_layers) - int(z_distance_delta), 1); const size_t first_support_layer = std::max(int(num_raft_layers) - int(z_distance_delta), 1);
std::vector<std::unordered_set<Point, PointHash>> already_inserted(num_support_layers); size_t first_tree_layer = 0;
size_t raft_contact_layer_idx = std::numeric_limits<size_t>::max();
if (num_raft_layers > 0 && print_object.layer_count() > 0) {
// Produce raft contact layer outside of the tree support loop, so that no trees will be generated for the raft contact layer.
// Raft layers supporting raft contact interface will be produced by the classic raft generator.
// Find the raft contact layer.
raft_contact_layer_idx = config.raft_layers.size() - 1;
while (raft_contact_layer_idx > 0 && config.raft_layers[raft_contact_layer_idx] > print_object.slicing_parameters().raft_contact_top_z + EPSILON)
-- raft_contact_layer_idx;
// Create the raft contact layer.
SupportGeneratorLayer &raft_contact_layer = layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, raft_contact_layer_idx);
top_contacts[raft_contact_layer_idx] = &raft_contact_layer;
const ExPolygons &lslices = print_object.get_layer(0)->lslices;
double expansion = print_object.config().raft_expansion.value;
raft_contact_layer.polygons = expansion > 0 ? expand(lslices, scaled<float>(expansion)) : to_polygons(lslices);
first_tree_layer = print_object.slicing_parameters().raft_layers() - 1;
}
std::mutex mutex_layer_storage, mutex_movebounds; std::mutex mutex_layer_storage, mutex_movebounds;
std::vector<std::unordered_set<Point, PointHash>> already_inserted(num_support_layers);
tbb::parallel_for(tbb::blocked_range<size_t>(first_support_layer, num_support_layers), tbb::parallel_for(tbb::blocked_range<size_t>(first_support_layer, num_support_layers),
[&print_object, &volumes, &config, &overhangs, &mesh_config, &mesh_group_settings, &support_params, [&print_object, &volumes, &config, &overhangs, &mesh_config, &mesh_group_settings, &support_params,
z_distance_delta, min_xy_dist, force_tip_to_roof, roof_enabled, support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length, max_overhang_insert_lag, z_distance_delta, min_xy_dist, force_tip_to_roof, roof_enabled, support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length, max_overhang_insert_lag,
@ -1408,6 +1433,49 @@ static void generate_initial_areas(
} }
} }
}); });
// Remove tree tips that start below the raft contact,
// remove interface layers below the raft contact.
for (size_t i = 0; i < first_tree_layer; ++i) {
top_contacts[i] = nullptr;
move_bounds[i].clear();
}
if (raft_contact_layer_idx != std::numeric_limits<coord_t>::max() && print_object.config().raft_expansion.value > 0) {
// If any tips at first_tree_layer now are completely inside the expanded raft layer, remove them as well before they are propagated to the ground.
Polygons &raft_polygons = top_contacts[raft_contact_layer_idx]->polygons;
EdgeGrid::Grid grid(get_extents(raft_polygons).inflated(SCALED_EPSILON));
grid.create(raft_polygons, Polylines{}, coord_t(scale_(10.)));
SupportElements &first_layer_move_bounds = move_bounds[first_tree_layer];
double threshold = scaled<double>(print_object.config().raft_expansion.value) * 2.;
first_layer_move_bounds.erase(std::remove_if(first_layer_move_bounds.begin(), first_layer_move_bounds.end(),
[&grid, threshold](const SupportElement &el) {
coordf_t dist;
if (grid.signed_distance_edges(el.state.result_on_layer, threshold, dist)) {
assert(std::abs(dist) < threshold + SCALED_EPSILON);
// Support point is inside the expanded raft, remove it.
return dist < - 0.;
}
return false;
}), first_layer_move_bounds.end());
#if 0
// Remove the remaining tips from the raft: Closing operation on tip circles.
if (! first_layer_move_bounds.empty()) {
const double eps = 0.1;
// All tips supporting this layer are expected to have the same radius.
double radius = config.getRadius(first_layer_move_bounds.front().state);
// Connect the tips with the following closing radius.
double closing_distance = radius;
Polygon circle = make_circle(radius + closing_distance, eps);
Polygons circles;
circles.reserve(first_layer_move_bounds.size());
for (const SupportElement &el : first_layer_move_bounds) {
circles.emplace_back(circle);
circles.back().translate(el.state.result_on_layer);
}
raft_polygons = diff(raft_polygons, offset(union_(circles), - closing_distance));
}
#endif
}
} }
static unsigned int move_inside(const Polygons &polygons, Point &from, int distance = 0, int64_t maxDist2 = std::numeric_limits<int64_t>::max()) static unsigned int move_inside(const Polygons &polygons, Point &from, int distance = 0, int64_t maxDist2 = std::numeric_limits<int64_t>::max())
@ -4217,8 +4285,6 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
// Produce the support G-code. // Produce the support G-code.
// Used by both classic and tree supports. // Used by both classic and tree supports.
SupportParameters support_params(print_object); SupportParameters support_params(print_object);
support_params.with_sheath = true;
support_params.support_density = 0;
SupportGeneratorLayersPtr interface_layers, base_interface_layers; SupportGeneratorLayersPtr interface_layers, base_interface_layers;
SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
#if 1 //#ifdef SLIC3R_DEBUG #if 1 //#ifdef SLIC3R_DEBUG