diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 1c2b785e3..340449ec8 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -61,7 +61,7 @@ namespace Slic3r { //#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 #define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. -#ifdef SLIC3R_DEBUG +#if 1 //#ifdef SLIC3R_DEBUG const char* support_surface_type_to_color_name(const SupporLayerType surface_type) { switch (surface_type) { @@ -543,7 +543,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled. // There is also a 1st intermediate layer containing bases of support columns. // Inflate the bases of the support columns and create the raft base under the object. - SupportGeneratorLayersPtr raft_layers = this->generate_raft_base(object, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); + SupportGeneratorLayersPtr raft_layers = generate_raft_base(object, m_support_params, m_slicing_params, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); #ifdef SLIC3R_DEBUG for (const SupportGeneratorLayer *l : interface_layers) @@ -573,6 +573,9 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // intermediate_layers.clear(); // interface_layers.clear(); +#ifdef SLIC3R_DEBUG + SupportGeneratorLayersPtr layers_sorted = +#endif // SLIC3R_DEBUG generate_support_layers(object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); BOOST_LOG_TRIVIAL(info) << "Support generator - Generating tool paths"; @@ -585,7 +588,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. int j = i + 1; coordf_t zmax = layers_sorted[i]->print_z + EPSILON; - bool empty = true; + bool empty = layers_sorted[i]->polygons.empty(); for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) if (!layers_sorted[j]->polygons.empty()) empty = false; @@ -615,7 +618,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. int j = i + 1; coordf_t zmax = layers_sorted[i]->print_z + EPSILON; - bool empty = true; + bool empty = layers_sorted[i]->polygons.empty(); for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) if (! layers_sorted[j]->polygons.empty()) empty = false; @@ -2866,13 +2869,15 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end"; } -SupportGeneratorLayersPtr PrintObjectSupportMaterial::generate_raft_base( - const PrintObject &object, +SupportGeneratorLayersPtr generate_raft_base( + const PrintObject &object, + const SupportParameters &support_params, + const SlicingParameters &slicing_params, const SupportGeneratorLayersPtr &top_contacts, const SupportGeneratorLayersPtr &interface_layers, const SupportGeneratorLayersPtr &base_interface_layers, const SupportGeneratorLayersPtr &base_layers, - SupportGeneratorLayerStorage &layer_storage) const + SupportGeneratorLayerStorage &layer_storage) { // If there is brim to be generated, calculate the trimming regions. Polygons brim; @@ -2904,22 +2909,22 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::generate_raft_base( } // How much to inflate the support columns to be stable. This also applies to the 1st layer, if no raft layers are to be printed. - const float inflate_factor_fine = float(scale_((m_slicing_params.raft_layers() > 1) ? 0.5 : EPSILON)); + const float inflate_factor_fine = float(scale_((slicing_params.raft_layers() > 1) ? 0.5 : EPSILON)); const float inflate_factor_1st_layer = std::max(0.f, float(scale_(object.config().raft_first_layer_expansion)) - inflate_factor_fine); SupportGeneratorLayer *contacts = top_contacts .empty() ? nullptr : top_contacts .front(); SupportGeneratorLayer *interfaces = interface_layers .empty() ? nullptr : interface_layers .front(); SupportGeneratorLayer *base_interfaces = base_interface_layers.empty() ? nullptr : base_interface_layers.front(); SupportGeneratorLayer *columns_base = base_layers .empty() ? nullptr : base_layers .front(); - if (contacts != nullptr && contacts->print_z > std::max(m_slicing_params.first_print_layer_height, m_slicing_params.raft_contact_top_z) + EPSILON) + if (contacts != nullptr && contacts->print_z > std::max(slicing_params.first_print_layer_height, slicing_params.raft_contact_top_z) + EPSILON) // This is not the raft contact layer. contacts = nullptr; - if (interfaces != nullptr && interfaces->bottom_print_z() > m_slicing_params.raft_interface_top_z + EPSILON) + if (interfaces != nullptr && interfaces->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) // This is not the raft column base layer. interfaces = nullptr; - if (base_interfaces != nullptr && base_interfaces->bottom_print_z() > m_slicing_params.raft_interface_top_z + EPSILON) + if (base_interfaces != nullptr && base_interfaces->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) // This is not the raft column base layer. base_interfaces = nullptr; - if (columns_base != nullptr && columns_base->bottom_print_z() > m_slicing_params.raft_interface_top_z + EPSILON) + if (columns_base != nullptr && columns_base->bottom_print_z() > slicing_params.raft_interface_top_z + EPSILON) // This is not the raft interface layer. columns_base = nullptr; @@ -2934,7 +2939,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::generate_raft_base( // Output vector. SupportGeneratorLayersPtr raft_layers; - if (m_slicing_params.raft_layers() > 1) { + if (slicing_params.raft_layers() > 1) { Polygons base; Polygons columns; if (columns_base != nullptr) { @@ -2951,30 +2956,30 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::generate_raft_base( // Do not add the raft contact layer, only add the raft layers below the contact layer. // Insert the 1st layer. { - SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, (m_slicing_params.base_raft_layers > 0) ? SupporLayerType::RaftBase : SupporLayerType::RaftInterface); + SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, (slicing_params.base_raft_layers > 0) ? SupporLayerType::RaftBase : SupporLayerType::RaftInterface); raft_layers.push_back(&new_layer); - new_layer.print_z = m_slicing_params.first_print_layer_height; - new_layer.height = m_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.bottom_z = 0.; new_layer.polygons = inflate_factor_1st_layer > 0 ? expand(base, inflate_factor_1st_layer) : base; } // Insert the base layers. - for (size_t i = 1; i < m_slicing_params.base_raft_layers; ++ i) { + for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) { coordf_t print_z = raft_layers.back()->print_z; SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, SupporLayerType::RaftBase); raft_layers.push_back(&new_layer); - new_layer.print_z = print_z + m_slicing_params.base_raft_layer_height; - new_layer.height = m_slicing_params.base_raft_layer_height; + new_layer.print_z = print_z + slicing_params.base_raft_layer_height; + new_layer.height = slicing_params.base_raft_layer_height; new_layer.bottom_z = print_z; new_layer.polygons = base; } // Insert the interface layers. - for (size_t i = 1; i < m_slicing_params.interface_raft_layers; ++ i) { + for (size_t i = 1; i < slicing_params.interface_raft_layers; ++ i) { coordf_t print_z = raft_layers.back()->print_z; SupportGeneratorLayer &new_layer = layer_allocate(layer_storage, SupporLayerType::RaftInterface); raft_layers.push_back(&new_layer); - new_layer.print_z = print_z + m_slicing_params.interface_raft_layer_height; - new_layer.height = m_slicing_params.interface_raft_layer_height; + new_layer.print_z = print_z + slicing_params.interface_raft_layer_height; + new_layer.height = slicing_params.interface_raft_layer_height; new_layer.bottom_z = print_z; new_layer.polygons = interface_polygons; //FIXME misusing contact_polygons for support columns. @@ -2982,12 +2987,12 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::generate_raft_base( } } else { 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 trimming = offset(m_object->layers().front()->lslices, (float)scale_(m_support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS); + Polygons trimming = offset(object.layers().front()->lslices, (float)scale_(support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS); if (inflate_factor_1st_layer > SCALED_EPSILON) { // 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 / m_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()))); float step = inflate_factor_1st_layer / nsteps; for (int i = 0; i < nsteps; ++ i) raft = diff(expand(raft, step), trimming); @@ -3847,7 +3852,7 @@ void modulate_extrusion_by_overlapping_layers( extrusion_entities_append_paths(extrusions_in_out, std::move(it_fragment->polylines), extrusion_role, it_fragment->mm3_per_mm, it_fragment->width, it_fragment->height); } -void generate_support_layers( +SupportGeneratorLayersPtr generate_support_layers( PrintObject &object, const SupportGeneratorLayersPtr &raft_layers, const SupportGeneratorLayersPtr &bottom_contacts, @@ -3921,6 +3926,7 @@ void generate_support_layers( } i = j; } + return layers_sorted; } void generate_support_toolpaths( diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp index e9baa4dbd..1e6d12f80 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -147,7 +147,20 @@ struct SupportParameters { bool with_sheath; }; -void generate_support_layers( +// 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 SupportParameters &support_params, + const SlicingParameters &slicing_params, + const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &interface_layers, + const SupportGeneratorLayersPtr &base_interface_layers, + const SupportGeneratorLayersPtr &base_layers, + SupportGeneratorLayerStorage &layer_storage); + +// returns sorted layers +SupportGeneratorLayersPtr generate_support_layers( PrintObject &object, const SupportGeneratorLayersPtr &raft_layers, const SupportGeneratorLayersPtr &bottom_contacts, @@ -170,6 +183,9 @@ void generate_support_toolpaths( const SupportGeneratorLayersPtr &interface_layers, const SupportGeneratorLayersPtr &base_interface_layers); +void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers); +void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer); + // This class manages raft and supports for a single PrintObject. // Instantiated by Slic3r::Print::Object->_support_material() // This class is instantiated before the slicing starts as Object.pm will query @@ -226,16 +242,6 @@ private: SupportGeneratorLayersPtr &intermediate_layers, const std::vector &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. diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/TreeModelVolumes.cpp index 57ef150d5..c94edca72 100644 --- a/src/libslic3r/TreeModelVolumes.cpp +++ b/src/libslic3r/TreeModelVolumes.cpp @@ -43,7 +43,7 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr } this->layer_height = scaled(config.layer_height.value); - this->resolution = scaled(print_config.resolution.value); + this->resolution = scaled(print_config.gcode_resolution.value); this->min_feature_size = scaled(config.min_feature_size.value); this->support_angle = M_PI / 2. - config.support_material_angle * M_PI / 180.; this->support_line_width = support_material_flow(&print_object, config.layer_height).scaled_width(); @@ -127,12 +127,6 @@ TreeModelVolumes::TreeModelVolumes( } m_current_outline_idx = mesh_to_layeroutline_idx[current_mesh_idx]; - m_support_rests_on_model = false; - m_min_resolution = std::numeric_limits::max(); - for (auto data_pair : m_layer_outlines) { - m_support_rests_on_model |= ! data_pair.first.support_material_buildplate_only; - m_min_resolution = std::min(m_min_resolution, data_pair.first.resolution); - } #else { m_anti_overhang = print_object.slice_support_blockers(); @@ -141,13 +135,23 @@ TreeModelVolumes::TreeModelVolumes( m_layer_outlines.emplace_back(mesh_settings, std::vector{}); m_current_outline_idx = 0; std::vector &outlines = m_layer_outlines.front().second; - outlines.reserve(print_object.layer_count()); - for (const Layer *layer : print_object.layers()) - outlines.emplace_back(to_polygons(expolygons_simplify(layer->lslices, mesh_settings.resolution))); + outlines.assign(print_object.layer_count(), Polygons{}); + tbb::parallel_for(tbb::blocked_range(0, print_object.layer_count(), std::min(1, std::max(16, print_object.layer_count() / (8 * tbb::this_task_arena::max_concurrency())))), + [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) + outlines[layer_idx] = to_polygons(expolygons_simplify(print_object.get_layer(layer_idx)->lslices, mesh_settings.resolution)); + }); } #endif - const TreeSupport::TreeSupportSettings &config = m_layer_outlines[m_current_outline_idx].first; + m_support_rests_on_model = false; + m_min_resolution = std::numeric_limits::max(); + for (auto data_pair : m_layer_outlines) { + m_support_rests_on_model |= ! data_pair.first.support_material_buildplate_only; + m_min_resolution = std::min(m_min_resolution, data_pair.first.resolution); + } + + const TreeSupport::TreeSupportSettings config{ m_layer_outlines[m_current_outline_idx].first }; if (! config.support_xy_overrides_z) { m_current_min_xy_dist = config.xy_min_distance; if (TreeSupport::TreeSupportSettings::has_to_rely_on_min_xy_dist_only) @@ -492,12 +496,6 @@ coord_t TreeModelVolumes::getRadiusNextCeil(coord_t radius, bool min_xy_dist) co return ceiled_radius; } -[[nodiscard]] static inline Polygons simplify(const Polygons &polygons, coord_t resolution) -{ - //FIXME - return polygons; -} - #if 0 Polygons TreeModelVolumes::extractOutlineFromMesh(const PrintObject &print_object, LayerIndex layer_idx) const { @@ -535,13 +533,13 @@ void TreeModelVolumes::calculateCollision(std::deque keys) { tbb::parallel_for(tbb::blocked_range(0, keys.size()), [&](const tbb::blocked_range &range) { - for (size_t i = range.begin(); i < range.end(); ++ i) { - coord_t radius = keys[i].first; + for (size_t i = range.begin(); i != range.end(); ++ i) { + const coord_t radius = keys[i].first; + const size_t layer_idx = keys[i].second; RadiusLayerPair key(radius, 0); RadiusLayerPolygonCache data_outer; RadiusLayerPolygonCache data_placeable_outer; - for (size_t outline_idx = 0; outline_idx < m_layer_outlines.size(); outline_idx++) - { + for (size_t outline_idx = 0; outline_idx < m_layer_outlines.size(); ++outline_idx) { RadiusLayerPolygonCache data; RadiusLayerPolygonCache data_placeable; @@ -550,7 +548,7 @@ void TreeModelVolumes::calculateCollision(std::deque keys) const coord_t z_distance_bottom = m_layer_outlines[outline_idx].first.support_bottom_distance; const size_t z_distance_bottom_layers = round_up_divide(z_distance_bottom, layer_height); const coord_t z_distance_top_layers = round_up_divide(m_layer_outlines[outline_idx].first.support_top_distance, layer_height); - const LayerIndex max_required_layer = keys[i].second + std::max(coord_t(1), z_distance_top_layers); + const LayerIndex max_required_layer = layer_idx + std::max(coord_t(1), z_distance_top_layers); const coord_t xy_distance = outline_idx == m_current_outline_idx ? m_current_min_xy_dist : m_layer_outlines[outline_idx].first.support_xy_distance; // technically this causes collision for the normal xy_distance to be larger by m_current_min_xy_dist_delta for all not currently processing meshes as this delta will be added at request time. // avoiding this would require saving each collision for each outline_idx separately. @@ -564,6 +562,7 @@ void TreeModelVolumes::calculateCollision(std::deque keys) if (min_layer_bottom < 0) min_layer_bottom = 0; + //FIXME parallel_for for (LayerIndex layer_idx = min_layer_bottom; layer_idx <= max_required_layer; layer_idx++) { key.second = layer_idx; Polygons collision_areas = m_machine_border; @@ -607,10 +606,10 @@ void TreeModelVolumes::calculateCollision(std::deque keys) } for (auto pair : data) - data_outer[pair.first] = union_(data_outer[pair.first], simplify(pair.second, m_min_resolution)); + data_outer[pair.first] = union_(data_outer[pair.first], polygons_simplify(pair.second, m_min_resolution)); if (radius == 0) { for (auto pair : data_placeable) - data_placeable_outer[pair.first] = union_(data_placeable_outer[pair.first], simplify(pair.second, m_min_resolution)); + data_placeable_outer[pair.first] = union_(data_placeable_outer[pair.first], polygons_simplify(pair.second, m_min_resolution)); } } @@ -651,8 +650,7 @@ void TreeModelVolumes::calculateCollisionHolefree(std::deque ke coord_t radius = key.first; coord_t increase_radius_ceil = ceilRadius(m_increase_until_radius, false) - ceilRadius(radius, true); // this union is important as otherwise holes(in form of lines that will increase to holes in a later step) can get unioned onto the area. - Polygons col = offset(union_ex(getCollision(m_increase_until_radius, layer_idx, false)), 5 - increase_radius_ceil, ClipperLib::jtRound, scaled(0.01)); - col = simplify(col, m_min_resolution); + Polygons col = polygons_simplify(offset(union_ex(getCollision(m_increase_until_radius, layer_idx, false)), 5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution), m_min_resolution); data[RadiusLayerPair(radius, layer_idx)] = col; } @@ -662,17 +660,6 @@ void TreeModelVolumes::calculateCollisionHolefree(std::deque ke }); } -// ensures offsets are only done in sizes with a max step size per offset while adding the collision offset after each step, this ensures that areas cannot glitch through walls defined by the collision when offsetting to fast -static Polygons safeOffset(const Polygons& me, coord_t distance, ClipperLib::JoinType jt, coord_t max_safe_step_distance, const Polygons& collision) -{ - const size_t steps = std::abs(distance / max_safe_step_distance); - assert(int64_t(distance) * int64_t(max_safe_step_distance) >= 0); - ExPolygons ret = union_ex(me); - for (size_t i = 0; i < steps; ++ i) - ret = union_ex(union_(offset(ret, max_safe_step_distance, jt, jt == jtRound ? scaled(0.01) : 1.2), collision)); - return union_(offset(ret, distance % max_safe_step_distance, jt, jt == jtRound ? scaled(0.01) : 1.2), collision); -} - void TreeModelVolumes::calculateAvoidance(std::deque keys) { // For every RadiusLayer pair there are 3 avoidances that have to be calculate, calculated in the same paralell_for loop for better paralellisation. @@ -695,9 +682,7 @@ void TreeModelVolumes::calculateAvoidance(std::deque keys) continue; const coord_t offset_speed = slow ? m_max_move_slow : m_max_move; - const coord_t max_step_move = std::max(1.9 * radius, m_current_min_xy_dist * 1.9); RadiusLayerPair key(radius, 0); - Polygons latest_avoidance; LayerIndex start_layer; { std::lock_guard critical_section(*(slow ? m_critical_avoidance_cache_slow : holefree ? m_critical_avoidance_cache_holefree : m_critical_avoidance_cache)); @@ -707,19 +692,19 @@ void TreeModelVolumes::calculateAvoidance(std::deque keys) BOOST_LOG_TRIVIAL(debug) << "Requested calculation for value already calculated ?"; continue; } - start_layer = std::max(start_layer, LayerIndex(1)); // Ensure StartLayer is at least 1 as if no avoidance was calculated getMaxCalculatedLayer returns -1 std::vector> data(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); - latest_avoidance = getAvoidance(radius, start_layer - 1, type, false, true); // minDist as the delta was already added, also avoidance for layer 0 will return the collision. - + // Ensure StartLayer is at least 1 as if no avoidance was calculated getMaxCalculatedLayer returns -1 + start_layer = std::max(start_layer, LayerIndex(1)); + // minDist as the delta was already added, also avoidance for layer 0 will return the collision. + Polygons latest_avoidance = getAvoidance(radius, start_layer - 1, type, false, true); // ### main loop doing the calculation - for (LayerIndex layer = start_layer; layer <= max_required_layer; layer++) { + for (LayerIndex layer = start_layer; layer <= max_required_layer; ++ layer) { key.second = layer; - Polygons col = (slow && radius < m_increase_until_radius + m_current_min_xy_dist_delta) || holefree ? + const Polygons &col = (slow && radius < m_increase_until_radius + m_current_min_xy_dist_delta) || holefree ? getCollisionHolefree(radius, layer, true) : getCollision(radius, layer, true); - latest_avoidance = safeOffset(latest_avoidance, -offset_speed, ClipperLib::jtRound, -max_step_move, col); - latest_avoidance = simplify(latest_avoidance, m_min_resolution); + latest_avoidance = polygons_simplify(union_(offset(union_ex(latest_avoidance), -offset_speed, ClipperLib::jtRound, m_min_resolution), col), m_min_resolution); data[layer] = std::pair(key, latest_avoidance); } @@ -770,7 +755,7 @@ void TreeModelVolumes::calculatePlaceables(std::deque keys) for (LayerIndex layer = start_layer; layer <= max_required_layer; layer++) { key.second = layer; Polygons placeable = getPlaceableAreas(0, layer); - placeable = simplify(placeable, m_min_resolution); // it is faster to do this here in each thread than once in calculateCollision. + placeable = polygons_simplify(placeable, m_min_resolution); // it is faster to do this here in each thread than once in calculateCollision. placeable = offset(union_ex(placeable), - radius, jtMiter, 1.2); data[layer] = std::pair(key, placeable); } @@ -814,8 +799,6 @@ void TreeModelVolumes::calculateAvoidanceToModel(std::deque key getPlaceableAreas(radius, max_required_layer); // ensuring Placeableareas are calculated const coord_t offset_speed = slow ? m_max_move_slow : m_max_move; - const coord_t max_step_move = std::max(1.9 * radius, m_current_min_xy_dist * 1.9); - Polygons latest_avoidance; std::vector> data(max_required_layer + 1, std::pair(RadiusLayerPair(radius, -1), Polygons())); RadiusLayerPair key(radius, 0); @@ -829,27 +812,17 @@ void TreeModelVolumes::calculateAvoidanceToModel(std::deque key BOOST_LOG_TRIVIAL(debug) << "Requested calculation for value already calculated ?"; continue; } + // Ensure StartLayer is at least 1 as if no avoidance was calculated getMaxCalculatedLayer returns -1 start_layer = std::max(start_layer, LayerIndex(1)); - latest_avoidance = getAvoidance(radius, start_layer - 1, type, true, true); // minDist as the delta was already added, also avoidance for layer 0 will return the collision. - + // minDist as the delta was already added, also avoidance for layer 0 will return the collision. + Polygons latest_avoidance = getAvoidance(radius, start_layer - 1, type, true, true); // ### main loop doing the calculation - for (LayerIndex layer = start_layer; layer <= max_required_layer; layer++) - { + for (LayerIndex layer = start_layer; layer <= max_required_layer; ++ layer) { key.second = layer; - Polygons col = getCollision(radius, layer, true); - - if ((slow && radius < m_increase_until_radius + m_current_min_xy_dist_delta) || holefree) - { - col = getCollisionHolefree(radius, layer, true); - } - else - { - col = getCollision(radius, layer, true); - } - - latest_avoidance = diff(safeOffset(latest_avoidance, -offset_speed, ClipperLib::jtRound, -max_step_move, col), getPlaceableAreas(radius, layer)); - - latest_avoidance = simplify(latest_avoidance, m_min_resolution); + const Polygons &col = (slow && radius < m_increase_until_radius + m_current_min_xy_dist_delta) || holefree ? + getCollisionHolefree(radius, layer, true) : + getCollision(radius, layer, true); + latest_avoidance = polygons_simplify(diff(union_(offset(union_ex(latest_avoidance), -offset_speed, ClipperLib::jtRound, m_min_resolution), col), getPlaceableAreas(radius, layer)), m_min_resolution); data[layer] = std::pair(key, latest_avoidance); } @@ -929,12 +902,12 @@ void TreeModelVolumes::calculateWallRestrictions(std::deque key key.second = layer_idx; LayerIndex layer_idx_below = layer_idx - 1; Polygons wall_restriction = intersection(getCollision(0, layer_idx, false), getCollision(radius, layer_idx_below, true)); // radius contains m_current_min_xy_dist_delta already if required - wall_restriction = simplify(wall_restriction, m_min_resolution); + wall_restriction = polygons_simplify(wall_restriction, m_min_resolution); data.emplace(key, wall_restriction); if (m_current_min_xy_dist_delta > 0) { Polygons wall_restriction_min = intersection(getCollision(0, layer_idx, true), getCollision(radius, layer_idx_below, true)); - wall_restriction = simplify(wall_restriction_min, m_min_resolution); + wall_restriction = polygons_simplify(wall_restriction_min, m_min_resolution); data_min.emplace(key, wall_restriction_min); } } diff --git a/src/libslic3r/TreeModelVolumes.hpp b/src/libslic3r/TreeModelVolumes.hpp index cd1cc9722..c8b986a00 100644 --- a/src/libslic3r/TreeModelVolumes.hpp +++ b/src/libslic3r/TreeModelVolumes.hpp @@ -30,7 +30,7 @@ class PrintObject; struct TreeSupportMeshGroupSettings { TreeSupportMeshGroupSettings() = default; - TreeSupportMeshGroupSettings(const PrintObject &print_object); + explicit TreeSupportMeshGroupSettings(const PrintObject &print_object); /*********************************************************************/ /* Print parameters, not support specific: */ @@ -153,7 +153,7 @@ struct TreeSupportMeshGroupSettings { // Tree Support Branch Distance // How far apart the branches need to be when they touch the model. Making this distance small will cause // the tree support to touch the model at more points, causing better overhang but making support harder to remove. - coord_t support_tree_branch_distance { scaled(50.) }; + coord_t support_tree_branch_distance { scaled(1.) }; // Tree Support Branch Diameter // The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this. // minimum: 0.001, minimum warning: support_line_width * 2 @@ -204,7 +204,7 @@ class TreeModelVolumes { public: TreeModelVolumes() = default; - TreeModelVolumes(const PrintObject &print_object, const BuildVolume &build_volume, + explicit TreeModelVolumes(const PrintObject &print_object, const BuildVolume &build_volume, coord_t max_move, coord_t max_move_slow, size_t current_mesh_idx, double progress_multiplier, double progress_offset, const std::vector &additional_excluded_areas = {}); TreeModelVolumes(TreeModelVolumes&&) = default; TreeModelVolumes& operator=(TreeModelVolumes&&) = default; @@ -582,7 +582,7 @@ private: std::unique_ptr m_critical_progress { std::make_unique() }; }; -static Polygons safeOffset(const Polygons& me, coord_t distance, ClipperLib::JoinType jt, coord_t max_safe_step_distance, const Polygons& collision); +Polygons safeOffset(const Polygons& me, coord_t distance, ClipperLib::JoinType jt, coord_t max_safe_step_distance, const Polygons& collision); } diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index cf74d2ab8..dec094c6c 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -16,6 +16,7 @@ #include "MultiPoint.hpp" #include "Polygon.hpp" #include "Polyline.hpp" +#include "MutablePolygon.hpp" #include "SupportMaterial.hpp" #include @@ -92,6 +93,76 @@ static inline void validate_range(const LineInformations &lines) validate_range(l); } +static inline void clip_for_diff(const Polygon &src, const BoundingBox &bbox, Polygon &out) +{ + out.clear(); + const size_t cnt = src.points.size(); + if (cnt < 3) + return; + + enum class Side { + Left = 1, + Right = 2, + Top = 4, + Bottom = 8 + }; + + auto sides = [bbox](const Point &p) { + return int(p.x() < bbox.min.x()) * int(Side::Left) + + int(p.x() > bbox.max.x()) * int(Side::Right) + + int(p.y() < bbox.min.y()) * int(Side::Bottom) + + int(p.y() > bbox.max.y()) * int(Side::Top); + }; + + int sides_prev = sides(src.points.back()); + int sides_this = sides(src.points.front()); + const size_t last = cnt - 1; + for (size_t i = 0; i < last; ++ i) { + int sides_next = sides(src.points[i + 1]); + if (// This point is inside. Take it. + sides_this == 0 || + // Either this point is outside and previous or next is inside, or + // the edge possibly cuts corner of the bounding box. + (sides_prev & sides_this & sides_next) == 0) { + out.points.emplace_back(src.points[i]); + sides_prev = sides_this; + } else { + // All the three points (this, prev, next) are outside at the same side. + // Ignore this point. + } + sides_this = sides_next; + } + // For the last point, if src is completely outside bbox, then out.points will be empty. Just use the first point instead. + int sides_next = sides(out.points.empty() ? src.points.front() : out.points.front()); + if (// The last point is inside. Take it. + sides_this == 0 || + // Either this point is outside and previous or next is inside, or + // the edge possibly cuts corner of the bounding box. + (sides_prev & sides_this & sides_next) == 0) + out.points.emplace_back(src.points.back()); +} + +[[nodiscard]] static inline Polygon clip_for_diff(const Polygon &src, const BoundingBox &bbox) +{ + Polygon out; + clip_for_diff(src, bbox, out); + return out; +} + +[[nodiscard]] static inline Polygons clip_for_diff(const Polygons &src, const BoundingBox &bbox) +{ + Polygons out; + out.reserve(src.size()); + for (const Polygon &p : src) + out.emplace_back(clip_for_diff(p, bbox)); + return out; +} + +[[nodiscard]] static inline Polygons diff_clipped(const Polygons &src, const Polygons &clipping) +{ + return diff(src, clip_for_diff(clipping, get_extents(src).inflated(SCALED_EPSILON))); +} + static constexpr const auto tiny_area_threshold = sqr(scaled(0.001)); static std::vector>> group_meshes(const Print &print, const std::vector &print_object_ids) @@ -121,7 +192,7 @@ static std::vectorprint_z + EPSILON; + bool empty = layers_sorted[i]->polygons.empty(); + for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) + if (!layers_sorted[j]->polygons.empty()) + empty = false; + if (!empty) { + export_print_z_polygons_to_svg( + debug_out_path("support-%d-%lf.svg", iRun, layers_sorted[i]->print_z).c_str(), + layers_sorted.data() + i, j - i); + export_print_z_polygons_and_extrusions_to_svg( + debug_out_path("support-w-fills-%d-%lf.svg", iRun, layers_sorted[i]->print_z).c_str(), + layers_sorted.data() + i, j - i, + *print_object.support_layers()[layer_id]); + ++layer_id; + } + i = j; + } + } +#endif /* SLIC3R_DEBUG */ + ++ counter; } @@ -713,7 +818,7 @@ static std::optional> polyline_sample_next_point_at_dis (support_params.interface_angle + (layer_idx & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)) : support_params.base_angle; - fill_params.density = float(roof ? support_params.interface_density : scaled(filler->spacing) / float(support_infill_distance)); + fill_params.density = float(roof ? support_params.interface_density : scaled(filler->spacing) / (scaled(filler->spacing) + float(support_infill_distance))); fill_params.dont_adjust = true; Polylines out; @@ -786,6 +891,19 @@ static std::optional> polyline_sample_next_point_at_dis return result; } +// ensures offsets are only done in sizes with a max step size per offset while adding the collision offset after each step, this ensures that areas cannot glitch through walls defined by the collision when offsetting to fast +[[nodiscard]] Polygons safeOffset(const Polygons& me, coord_t distance, ClipperLib::JoinType jt, coord_t max_safe_step_distance, const Polygons& collision) +{ + const size_t steps = std::abs(distance / max_safe_step_distance); + assert(int64_t(distance) * int64_t(max_safe_step_distance) >= 0); + ExPolygons ret = union_ex(me); + Polygons collision_trimmed = clip_for_diff(collision, get_extents(ret).inflated(std::max(0, distance) + SCALED_EPSILON)); + + for (size_t i = 0; i < steps; ++ i) + ret = union_ex(union_(offset(ret, max_safe_step_distance, jt, jt == jtRound ? scaled(0.01) : 1.2), collision_trimmed)); + return union_(offset(ret, distance % max_safe_step_distance, jt, jt == jtRound ? scaled(0.01) : 1.2), collision_trimmed); +} + /*! * \brief Offsets (increases the area of) a polygons object in multiple steps to ensure that it does not lag through over a given obstacle. * \param me[in] Polygons object that has to be offset. @@ -799,12 +917,21 @@ static std::optional> polyline_sample_next_point_at_dis { bool do_final_difference = last_step_offset_without_check == 0; Polygons ret = safeUnion(me); // ensure sane input + + // Trim the collision polygons with the region of interest for diff() efficiency. + Polygons collision_trimmed_buffer; + auto collision_trimmed = [&collision_trimmed_buffer, &collision, &ret, distance]() -> const Polygons& { + if (collision_trimmed_buffer.empty() && ! collision.empty()) + collision_trimmed_buffer = clip_for_diff(collision, get_extents(ret).inflated(std::max(0, distance) + SCALED_EPSILON)); + return collision_trimmed_buffer; + }; + if (distance == 0) - return do_final_difference ? diff(ret, collision) : union_(ret); + return do_final_difference ? diff(ret, collision_trimmed()) : union_(ret); if (safe_step_size < 0 || last_step_offset_without_check < 0) { BOOST_LOG_TRIVIAL(error) << "Offset increase got invalid parameter!"; TreeSupport::showError("Negative offset distance... How did you manage this ?", true); - return do_final_difference ? diff(ret, collision) : union_(ret); + return do_final_difference ? diff(ret, collision_trimmed()) : union_(ret); } coord_t step_size = safe_step_size; @@ -829,7 +956,7 @@ static std::optional> polyline_sample_next_point_at_dis } // offset in steps for (size_t i = 0; i < steps; i++) { - ret = diff(offset(ret, step_size, ClipperLib::jtRound, scaled(0.01)), collision); + ret = diff(offset(ret, step_size, ClipperLib::jtRound, scaled(0.01)), collision_trimmed()); // ensure that if many offsets are done the performance does not suffer extremely by the new vertices of jtRound. if (i % 10 == 7) ret = polygons_simplify(ret, scaled(0.015)); @@ -841,10 +968,23 @@ static std::optional> polyline_sample_next_point_at_dis ret = polygons_simplify(ret, scaled(0.015)); if (do_final_difference) - ret = diff(ret, collision); + ret = diff(ret, collision_trimmed()); return union_(ret); } +static inline SupportGeneratorLayer& layer_initialize( + SupportGeneratorLayer &layer_new, + const SupporLayerType layer_type, + const SlicingParameters &slicing_params, + const size_t layer_idx) +{ + layer_new.layer_type = layer_type; + layer_new.print_z = slicing_params.object_print_z_min + slicing_params.first_object_layer_height + layer_idx * slicing_params.layer_height; + layer_new.height = layer_idx == 0 ? slicing_params.first_object_layer_height : slicing_params.layer_height; + layer_new.bottom_z = layer_idx == 0 ? slicing_params.object_print_z_min : layer_new.print_z - layer_new.height; + return layer_new; +} + // Using the std::deque as an allocator. inline SupportGeneratorLayer& layer_allocate( std::deque &layer_storage, @@ -852,13 +992,9 @@ inline SupportGeneratorLayer& layer_allocate( const SlicingParameters &slicing_params, size_t layer_idx) { + //FIXME take raft into account. layer_storage.push_back(SupportGeneratorLayer()); - SupportGeneratorLayer *layer_new = &layer_storage.back(); - layer_new->layer_type = layer_type; - layer_new->print_z = slicing_params.first_print_layer_height + std::max(0, int(layer_idx) - 1) * slicing_params.layer_height; - layer_new->height = slicing_params.layer_height; - layer_new->bottom_z = layer_idx == 0 ? 0. : layer_new->print_z - slicing_params.layer_height; - return *layer_new; + return layer_initialize(layer_storage.back(), layer_type, slicing_params, layer_idx); } inline SupportGeneratorLayer& layer_allocate( @@ -868,15 +1004,9 @@ inline SupportGeneratorLayer& layer_allocate( const SlicingParameters &slicing_params, size_t layer_idx) { - layer_storage_mutex.lock(); + tbb::spin_mutex::scoped_lock lock(layer_storage_mutex); layer_storage.push_back(SupportGeneratorLayer()); - SupportGeneratorLayer *layer_new = &layer_storage.back(); - layer_storage_mutex.unlock(); - layer_new->layer_type = layer_type; - layer_new->print_z = slicing_params.first_print_layer_height + std::max(0, int(layer_idx) - 1) * slicing_params.layer_height; - layer_new->height = slicing_params.layer_height; - layer_new->bottom_z = layer_idx == 0 ? 0. : layer_new->print_z - slicing_params.layer_height; - return *layer_new; + return layer_initialize(layer_storage.back(), layer_type, slicing_params, layer_idx); } void TreeSupport::generateInitialAreas( @@ -886,16 +1016,14 @@ void TreeSupport::generateInitialAreas( SupportGeneratorLayersPtr &top_interface_layers, SupportGeneratorLayerStorage &layer_storage) { - tbb::global_control(tbb::global_control::max_allowed_parallelism, 1); - Polygon base_circle; - const int base_radius = 10; + const auto base_radius = scaled(0.01); for (unsigned int i = 0; i < SUPPORT_TREE_CIRCLE_RESOLUTION; ++ i) { const AngleRadians angle = static_cast(i) / SUPPORT_TREE_CIRCLE_RESOLUTION * (2.0 * M_PI); base_circle.points.emplace_back(coord_t(cos(angle) * base_radius), coord_t(sin(angle) * base_radius)); } - TreeSupportSettings mesh_config(print_object); TreeSupportMeshGroupSettings mesh_group_settings(print_object); + TreeSupportSettings mesh_config{ mesh_group_settings }; SupportParameters support_params(print_object); const size_t z_distance_delta = mesh_config.z_distance_top_layers + 1; // To ensure z_distance_top_layers are left empty between the overhang (zeroth empty layer), the support has to be added z_distance_top_layers+1 layers below @@ -906,7 +1034,7 @@ void TreeSupport::generateInitialAreas( return; #endif - const coord_t connect_length = (mesh_config.support_line_width * 100. / mesh_group_settings.support_tree_top_rate) + std::max(2 * mesh_config.min_radius - 1.0 * mesh_config.support_line_width, 0.0); + const coord_t connect_length = (mesh_config.support_line_width * 100. / mesh_group_settings.support_tree_top_rate) + std::max(2. * mesh_config.min_radius - 1.0 * mesh_config.support_line_width, 0.0); const coord_t circle_length_to_half_linewidth_change = mesh_config.min_radius < mesh_config.support_line_width ? mesh_config.min_radius / 2 : sqrt(sqr(mesh_config.min_radius) - sqr(mesh_config.min_radius - mesh_config.support_line_width / 2)); // As r*r=x*x+y*y (circle equation): If a circle with center at (0,0) the top most point is at (0,r) as in y=r. This calculates how far one has to move on the x-axis so that y=r-support_line_width/2. In other words how far does one need to move on the x-axis to be support_line_width/2 away from the circle line. As a circle is round this length is identical for every axis as long as the 90� angle between both remains. const coord_t extra_outset = std::max(coord_t(0), mesh_config.min_radius - mesh_config.support_line_width) + (xy_overrides_z ? 0 : mesh_config.support_line_width / 2); // extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is better than not supporting it at all. const size_t support_roof_layers = mesh_group_settings.support_roof_enable ? (mesh_group_settings.support_roof_height + mesh_config.layer_height / 2) / mesh_config.layer_height : 0; @@ -1191,7 +1319,7 @@ void TreeSupport::generateInitialAreas( // as some support is better than none. Polygons reduced_overhang_outset = offset(union_ex(overhang_outset), -mesh_config.support_line_width / 2.2, jtMiter, 1.2); polylines = ensureMaximumDistancePolyline( - to_polylines(!reduced_overhang_outset.empty() && area(offset(diff_ex(overhang_outset, reduced_overhang_outset), std::max(mesh_config.support_line_width, connect_length), jtMiter, 1.2)) < 1 ? + to_polylines(!reduced_overhang_outset.empty() && area(offset(diff_ex(overhang_outset, reduced_overhang_outset), std::max(mesh_config.support_line_width, connect_length), jtMiter, 1.2)) < sqr(scaled(0.001)) ? reduced_overhang_outset : overhang_outset), connect_length, min_support_points); @@ -1466,7 +1594,7 @@ static void mergeHelper( erase.emplace_back(reduced_check_iter->first); erase.emplace_back(influence_iter->first); - Polygons merge = diff(offset(union_(intersect, intersect_sec), config.getRadius(key), ClipperLib::jtRound, scaled(0.01)), volumes.getCollision(0, layer_idx - 1)); // regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a negative area.). And if this area disappears because of rounding errors, the only downside is that it can not merge again on this layer. + Polygons merge = diff_clipped(offset(union_(intersect, intersect_sec), config.getRadius(key), ClipperLib::jtRound, scaled(0.01)), volumes.getCollision(0, layer_idx - 1)); // regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a negative area.). And if this area disappears because of rounding errors, the only downside is that it can not merge again on this layer. reduced_aabb.erase(reduced_check_iter->first); // this invalidates reduced_check_iter reduced_aabb.emplace(key, get_extents(merge)); @@ -1547,7 +1675,7 @@ static void mergeInfluenceAreas( for (size_t idx = range.begin(); idx < range.end(); ++ idx) { // +=2 as in the beginning only uneven buckets will be filled size_t bucket_idx = 2 * idx + 1; - for (const std::pair& input_pair : buckets_area[bucket_idx]) + for (const std::pair& input_pair : buckets_area[bucket_idx]) buckets_aabb[bucket_idx].emplace(input_pair.first, get_extents(input_pair.second).inflated(config.getRadius(input_pair.first))); } }); @@ -1577,11 +1705,11 @@ static void mergeInfluenceAreas( to_model_areas.erase(del); influence_areas.erase(del); } - for (const std::pair &tup : insert_main[i / 2]) + for (const std::pair &tup : insert_main[i / 2]) to_bp_areas.emplace(std::move(tup)); - for (const std::pair &tup : insert_secondary[i / 2]) + for (const std::pair &tup : insert_secondary[i / 2]) to_model_areas.emplace(std::move(tup)); - for (const std::pair &tup : insert_influence[i / 2]) + for (const std::pair &tup : insert_influence[i / 2]) influence_areas.emplace(std::move(tup)); } @@ -1614,7 +1742,7 @@ std::optional TreeSupport::increaseSingleArea(AreaI increased = *parent->area; if (mergelayer || current_elem.to_buildplate) { - to_bp_data = safeUnion(diff(increased, m_volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance))); + to_bp_data = safeUnion(diff_clipped(increased, m_volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance))); if (! current_elem.to_buildplate && area(to_bp_data) > tiny_area_threshold) { // mostly happening in the tip, but with merges one should check every time, just to be sure. current_elem.to_buildplate = true; // sometimes nodes that can reach the buildplate are marked as cant reach, tainting subtrees. This corrects it. @@ -1623,14 +1751,14 @@ std::optional TreeSupport::increaseSingleArea(AreaI } if (m_config.support_rests_on_model) { if (mergelayer || current_elem.to_model_gracious) - to_model_data = safeUnion(diff(increased, m_volumes.getAvoidance(radius, layer_idx - 1, settings.type, true, settings.use_min_distance))); + to_model_data = safeUnion(diff_clipped(increased, m_volumes.getAvoidance(radius, layer_idx - 1, settings.type, true, settings.use_min_distance))); if (!current_elem.to_model_gracious) { if (mergelayer && area(to_model_data) >= tiny_area_threshold) { current_elem.to_model_gracious = true; BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong non gracious value on layer " << layer_idx - 1 << " targeting " << current_elem.target_height << " with radius " << radius; } else - to_model_data = safeUnion(diff(increased, m_volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance))); + to_model_data = safeUnion(diff_clipped(increased, m_volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance))); } } @@ -1643,10 +1771,10 @@ std::optional TreeSupport::increaseSingleArea(AreaI Polygons to_bp_data_2; if (current_elem.to_buildplate) - to_bp_data_2 = diff(increased, m_volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)); // regular union as output will not be used later => this area should always be a subset of the safeUnion one (i think) + to_bp_data_2 = diff_clipped(increased, m_volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)); // regular union as output will not be used later => this area should always be a subset of the safeUnion one (i think) Polygons to_model_data_2; if (m_config.support_rests_on_model && !current_elem.to_buildplate) - to_model_data_2 = diff(increased, + to_model_data_2 = diff_clipped(increased, current_elem.to_model_gracious ? m_volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, true, settings.use_min_distance) : m_volumes.getCollision(next_radius, layer_idx - 1, settings.use_min_distance)); @@ -1683,9 +1811,9 @@ std::optional TreeSupport::increaseSingleArea(AreaI if (ceil_radius_before != m_volumes.ceilRadius(radius, settings.use_min_distance)) { if (current_elem.to_buildplate) - to_bp_data = safeUnion(diff(increased, m_volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance))); + to_bp_data = safeUnion(diff_clipped(increased, m_volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance))); if (m_config.support_rests_on_model && (!current_elem.to_buildplate || mergelayer)) - to_model_data = safeUnion(diff(increased, + to_model_data = safeUnion(diff_clipped(increased, current_elem.to_model_gracious ? m_volumes.getAvoidance(radius, layer_idx - 1, settings.type, true, settings.use_min_distance) : m_volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance) @@ -1854,8 +1982,12 @@ void TreeSupport::increaseAreas(std::unordered_map& to Polygons lines_offset = offset(to_polylines(*parent->area), scaled(0.005), jtMiter, 1.2); Polygons base_error_area = union_(*parent->area, lines_offset); result = increaseSingleArea(settings, layer_idx, parent, base_error_area, to_bp_data, to_model_data, inc_wo_collision, (m_config.maximum_move_distance + extra_speed) * 1.5, mergelayer); - BOOST_LOG_TRIVIAL(error) << - "Influence area could not be increased! Data about the Influence area: " +#ifdef TREE_SUPPORT_SHOW_ERRORS + BOOST_LOG_TRIVIAL(error) +#else // TREE_SUPPORT_SHOW_ERRORS + BOOST_LOG_TRIVIAL(warning) +#endif // TREE_SUPPORT_SHOW_ERRORS + << "Influence area could not be increased! Data about the Influence area: " "Radius: " << radius << " at layer: " << layer_idx - 1 << " NextTarget: " << elem.next_height << " Distance to top: " << elem.distance_to_top << " Elephant foot increases " << elem.elephant_foot_increases << " use_min_xy_dist " << elem.use_min_xy_dist << " to buildplate " << elem.to_buildplate << " gracious " << elem.to_model_gracious << " safe " << elem.can_use_safe_radius << " until move " << elem.dont_move_until << " \n " @@ -1883,7 +2015,12 @@ void TreeSupport::increaseAreas(std::unordered_map& to if (!settings.use_min_distance) elem.use_min_xy_dist = false; if (!settings.no_error) - BOOST_LOG_TRIVIAL(error) << "Trying to keep area by moving faster than intended: Success"; +#ifdef TREE_SUPPORT_SHOW_ERRORS + BOOST_LOG_TRIVIAL(error) +#else // TREE_SUPPORT_SHOW_ERRORS + BOOST_LOG_TRIVIAL(info) +#endif // TREE_SUPPORT_SHOW_ERRORS + << "Trying to keep area by moving faster than intended: Success"; break; } else if (!settings.no_error) @@ -1891,7 +2028,7 @@ void TreeSupport::increaseAreas(std::unordered_map& to } if (add) { - Polygons max_influence_area = safeUnion(diff(inc_wo_collision, m_volumes.getCollision(radius, layer_idx - 1, elem.use_min_xy_dist)), safeUnion(to_bp_data, to_model_data)); // union seems useless, but some rounding errors somewhere can cause to_bp_data to be slightly bigger than it should be + Polygons max_influence_area = safeUnion(diff_clipped(inc_wo_collision, m_volumes.getCollision(radius, layer_idx - 1, elem.use_min_xy_dist)), safeUnion(to_bp_data, to_model_data)); // union seems useless, but some rounding errors somewhere can cause to_bp_data to be slightly bigger than it should be { std::lock_guard critical_section_newLayer(critical_sections); if (bypass_merge) { @@ -1981,8 +2118,8 @@ void TreeSupport::createLayerPathing(std::vector>& mov new_element = !move_bounds[layer_idx - 1].empty(); // Save calculated elements to output, and allocate Polygons on heap, as they will not be changed again. - for (std::pair tup : influence_areas) { - const SupportElement elem = tup.first; + for (const std::pair &tup : influence_areas) { + const SupportElement &elem = tup.first; validate_range(tup.second); validate_range(safeUnion(tup.second)); Polygons* new_area = new Polygons(safeUnion(tup.second)); @@ -2172,7 +2309,10 @@ void TreeSupport::createNodesFromArea(std::vector>& mo } } -void TreeSupport::generateBranchAreas(std::vector>& linear_data, std::vector>& layer_tree_polygons, const std::map& inverse_tree_order) +void TreeSupport::generateBranchAreas( + std::vector> &linear_data, + std::vector> &layer_tree_polygons, + const std::map &inverse_tree_order) { #ifdef SLIC3R_TREESUPPORTS_PROGRESS double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC; @@ -2186,7 +2326,7 @@ void TreeSupport::generateBranchAreas(std::vector linear_inserts(linear_data.size()); - + #ifdef SLIC3R_TREESUPPORTS_PROGRESS const size_t progress_inserts_check_interval = linear_data.size() / progress_report_steps; #endif // SLIC3R_TREESUPPORTS_PROGRESS @@ -2195,28 +2335,30 @@ void TreeSupport::generateBranchAreas(std::vector(0, linear_data.size()), [&](const tbb::blocked_range &range) { for (size_t idx = range.begin(); idx < range.end(); ++ idx) { - - SupportElement* elem = linear_data[idx].second; - coord_t radius = m_config.getRadius(*elem); - bool parent_uses_min = false; - SupportElement* child_elem = inverse_tree_order.count(elem) ? inverse_tree_order.at(elem) : nullptr; + const LayerIndex layer_idx = linear_data[idx].first; + const SupportElement *elem = linear_data[idx].second; + const auto it_elem = inverse_tree_order.find(const_cast(elem)); + const SupportElement* child_elem = it_elem == inverse_tree_order.end() ? nullptr : it_elem->second; + const coord_t radius = m_config.getRadius(*elem); + bool parent_uses_min = false; // Calculate multiple ovalized circles, to connect with every parent and child. Also generate regular circle for the current layer. Merge all these into one area. std::vector> movement_directions{ std::pair(Point(0, 0), radius) }; if (!elem->skip_ovalisation) { if (child_elem != nullptr) { - Point movement = (child_elem->result_on_layer - elem->result_on_layer); + const Point movement = child_elem->result_on_layer - elem->result_on_layer; movement_directions.emplace_back(movement, radius); } - for (SupportElement* parent : elem->parents) { - Point movement = (parent->result_on_layer - elem->result_on_layer); - movement_directions.emplace_back(movement, std::max(m_config.getRadius(parent), m_config.support_line_width)); + for (SupportElement *parent : elem->parents) { + const Point movement = parent->result_on_layer - elem->result_on_layer; + movement_directions.emplace_back(movement, std::max(m_config.getRadius(*parent), m_config.support_line_width)); parent_uses_min |= parent->use_min_xy_dist; } } double max_speed = 0; - auto generateArea = [&](coord_t aoffset) { + auto generateArea = [&volumes = m_volumes, layer_idx, elem, &branch_circle, branch_radius = m_config.branch_radius, support_line_width = m_config.support_line_width, &movement_directions, &max_speed, parent_uses_min]( + coord_t aoffset) { Polygons poly; for (std::pair movement : movement_directions) { @@ -2224,10 +2366,10 @@ void TreeSupport::generateBranchAreas(std::vectorresult_on_layer + movement.first / 2; - const double moveX = movement.first.x() / (used_scale * m_config.branch_radius); - const double moveY = movement.first.y() / (used_scale * m_config.branch_radius); + const double moveX = movement.first.x() / (used_scale * branch_radius); + const double moveY = movement.first.y() / (used_scale * branch_radius); const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY)); double matrix[] = { @@ -2242,8 +2384,8 @@ void TreeSupport::generateBranchAreas(std::vectoruse_min_xy_dist)); // There seem to be some rounding errors, causing a branch to be a tiny bit further away from the model that it has to be. This can cause the tip to be slightly further away front the overhang (x/y wise) than optimal. This fixes it, and for every other part, 0.05mm will not be noticed. + poly = diff_clipped(offset(union_(poly), std::min(coord_t(50), support_line_width / 4), jtMiter, 1.2), + volumes.getCollision(0, layer_idx, parent_uses_min || elem->use_min_xy_dist)); // There seem to be some rounding errors, causing a branch to be a tiny bit further away from the model that it has to be. This can cause the tip to be slightly further away front the overhang (x/y wise) than optimal. This fixes it, and for every other part, 0.05mm will not be noticed. return poly; }; @@ -2277,7 +2419,7 @@ void TreeSupport::generateBranchAreas(std::vectoruse_min_xy_dist)); + linear_inserts[idx] = diff_clipped(linear_inserts[idx], m_volumes.getCollision(0, linear_data[idx].first, parent_uses_min || elem->use_min_xy_dist)); } } } @@ -2405,7 +2547,7 @@ void TreeSupport::dropNonGraciousAreas( Polygons rest_support = layer_tree_polygons[linear_data[idx].first][elem]; LayerIndex counter = 1; while (area(rest_support) > tiny_area_threshold && counter < linear_data[idx].first) { - rest_support = diff(rest_support, m_volumes.getCollision(0, linear_data[idx].first - counter)); + rest_support = diff_clipped(rest_support, m_volumes.getCollision(0, linear_data[idx].first - counter)); dropped_down_areas[idx].emplace_back(linear_data[idx].first - counter, rest_support); counter++; } @@ -2436,7 +2578,9 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( [&](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { // Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned. - support_layer_storage[layer_idx] = union_(support_layer_storage[layer_idx]); //FIXME .smooth(50); + support_layer_storage[layer_idx] = smooth_outward(union_(support_layer_storage[layer_idx]), m_config.support_line_width); //FIXME was .smooth(50); + //smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) : + // simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution. support_layer_storage[layer_idx] = polygons_simplify(support_layer_storage[layer_idx], std::min(scaled(0.03), double(m_config.resolution))); // Subtract support lines of the branches from the roof @@ -2488,7 +2632,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( // Subtract support floors from the support area and add them to the support floor instead. if (m_config.support_bottom_layers > 0 && !support_layer_storage[layer_idx].empty()) { SupportGeneratorLayer*& support_bottom = bottom_contacts[layer_idx]; - Polygons layer_outset = diff( + Polygons layer_outset = diff_clipped( m_config.support_bottom_offset > 0 ? offset(support_layer_storage[layer_idx], m_config.support_bottom_offset, jtMiter, 1.2) : support_layer_storage[layer_idx], m_volumes.getCollision(0, layer_idx, false)); Polygons floor_layer; @@ -2507,7 +2651,7 @@ void TreeSupport::finalizeInterfaceAndSupportAreas( if (support_bottom == nullptr) support_bottom = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::BottomContact, print_object.slicing_parameters(), layer_idx); support_bottom->polygons = union_(floor_layer, support_bottom->polygons); - support_layer_storage[layer_idx] = diff(support_layer_storage[layer_idx], offset(support_bottom->polygons, scaled(0.01), jtMiter, 1.2)); // Subtract the support floor from the normal support. + support_layer_storage[layer_idx] = diff_clipped(support_layer_storage[layer_idx], offset(support_bottom->polygons, scaled(0.01), jtMiter, 1.2)); // Subtract the support floor from the normal support. } } @@ -2579,9 +2723,21 @@ void TreeSupport::drawAreas( append(support_layer_storage[pair.first], std::move(pair.second)); // single threaded combining all support areas to the right layers. ONLY COPYS DATA! - for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(layer_tree_polygons.size()); ++ layer_idx) - for (std::pair data_pair : layer_tree_polygons[layer_idx]) - append(data_pair.first->missing_roof_layers > data_pair.first->distance_to_top ? support_roof_storage[layer_idx] : support_layer_storage[layer_idx], std::move(data_pair.second)); + for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(layer_tree_polygons.size()); ++ layer_idx) { + auto &this_layer_tree_polygons = layer_tree_polygons[layer_idx]; + auto &this_roofs = support_roof_storage[layer_idx]; + auto &this_layers = support_layer_storage[layer_idx]; + size_t cnt_roofs = 0; + size_t cnt_layers = 0; + for (const std::pair &data_pair : this_layer_tree_polygons) + ++ (data_pair.first->missing_roof_layers > data_pair.first->distance_to_top ? cnt_roofs : cnt_layers); + this_roofs.reserve(this_roofs.size() + cnt_roofs); + this_layers.reserve(this_layers.size() + cnt_layers); + for (const std::pair &data_pair : this_layer_tree_polygons) { + auto &src = const_cast(data_pair.second); + std::move(std::begin(src), std::end(src), std::back_inserter(data_pair.first->missing_roof_layers > data_pair.first->distance_to_top ? this_roofs : this_layers)); + } + } finalizeInterfaceAndSupportAreas(print_object, support_layer_storage, support_roof_storage, bottom_contacts, top_contacts, intermediate_layers, layer_storage); diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/TreeSupport.hpp index 221bf3a9b..3449a0f75 100644 --- a/src/libslic3r/TreeSupport.hpp +++ b/src/libslic3r/TreeSupport.hpp @@ -16,6 +16,8 @@ #include "BoundingBox.hpp" +// #define TREE_SUPPORT_SHOW_ERRORS + #define SUPPORT_TREE_CIRCLE_RESOLUTION 25 // The number of vertices in each circle. #ifdef SLIC3R_TREESUPPORTS_PROGRESS @@ -108,7 +110,7 @@ public: struct SupportElement { - SupportElement( + explicit SupportElement( coord_t distance_to_top, size_t target_height, Point target_position, bool to_buildplate, bool to_model_gracious, bool use_min_xy_dist, size_t dont_move_until, bool supports_roof, bool can_use_safe_radius, bool force_tips_to_roof, bool skip_ovalisation) : target_height(target_height), target_position(target_position), next_position(target_position), next_height(target_height), effective_radius_height(distance_to_top), @@ -119,7 +121,7 @@ public: } - SupportElement(const SupportElement& elem, Polygons* newArea = nullptr) + explicit SupportElement(const SupportElement& elem, Polygons* newArea = nullptr) : // copy constructor with possibility to set a new area target_height(elem.target_height), target_position(elem.target_position), @@ -149,7 +151,7 @@ public: * \brief Create a new Element for one layer below the element of the pointer supplied. */ - SupportElement(SupportElement* element_above) + explicit SupportElement(SupportElement* element_above) : target_height(element_above->target_height), target_position(element_above->target_position), next_position(element_above->next_position), @@ -174,7 +176,7 @@ public: } // ONLY to be called in merge as it assumes a few assurances made by it. - SupportElement(const SupportElement& first, const SupportElement& second, size_t next_height, Point next_position, coord_t increased_to_model_radius, const TreeSupportSettings& config) : next_position(next_position), next_height(next_height), area(nullptr), increased_to_model_radius(increased_to_model_radius), use_min_xy_dist(first.use_min_xy_dist || second.use_min_xy_dist), supports_roof(first.supports_roof || second.supports_roof), dont_move_until(std::max(first.dont_move_until, second.dont_move_until)), can_use_safe_radius(first.can_use_safe_radius || second.can_use_safe_radius), missing_roof_layers(std::min(first.missing_roof_layers, second.missing_roof_layers)), skip_ovalisation(false) + explicit SupportElement(const SupportElement& first, const SupportElement& second, size_t next_height, Point next_position, coord_t increased_to_model_radius, const TreeSupportSettings& config) : next_position(next_position), next_height(next_height), area(nullptr), increased_to_model_radius(increased_to_model_radius), use_min_xy_dist(first.use_min_xy_dist || second.use_min_xy_dist), supports_roof(first.supports_roof || second.supports_roof), dont_move_until(std::max(first.dont_move_until, second.dont_move_until)), can_use_safe_radius(first.can_use_safe_radius || second.can_use_safe_radius), missing_roof_layers(std::min(first.missing_roof_layers, second.missing_roof_layers)), skip_ovalisation(false) { if (first.target_height > second.target_height) @@ -353,7 +355,7 @@ public: { TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupport class. - TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings) + explicit TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings) : angle(mesh_group_settings.support_tree_angle), angle_slow(mesh_group_settings.support_tree_angle_slow), support_line_width(mesh_group_settings.support_line_width),