From 58d524d75f354f68edcb0583887531bc8af72e39 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 26 Jan 2025 11:16:02 +0800 Subject: [PATCH 01/72] Remove unused Organic tree code --- src/libslic3r/Support/TreeSupport3D.cpp | 643 +----------------------- 1 file changed, 4 insertions(+), 639 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 98dc41f486..88074ccac8 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -2776,633 +2776,6 @@ static void create_nodes_from_area( #endif // NDEBUG } -// For producing circular / elliptical areas from SupportElements (one DrawArea per one SupportElement) -// and for smoothing those areas along the tree branches. -struct DrawArea -{ - // Element to be processed. - SupportElement *element; - // Element below, if there is such an element. nullptr if element is a root of a tree. - SupportElement *child_element; - // Polygons to be extruded for this element. - Polygons polygons; -}; - -/*! - * \brief Draws circles around result_on_layer points of the influence areas - * - * \param linear_data[in] All currently existing influence areas with the layer they are on - * \param layer_tree_polygons[out] Resulting branch areas with the layerindex they appear on. layer_tree_polygons.size() has to be at least linear_data.size() as each Influence area in linear_data will save have at least one (that's why it's a vector) corresponding branch area in layer_tree_polygons. - * \param inverse_tree_order[in] A mapping that returns the child of every influence area. - */ -static void generate_branch_areas( - const TreeModelVolumes &volumes, - const TreeSupportSettings &config, - const std::vector &move_bounds, - std::vector &linear_data, - std::function throw_on_cancel) -{ -#ifdef SLIC3R_TREESUPPORTS_PROGRESS - double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC; - constexpr int progress_report_steps = 10; - const size_t progress_inserts_check_interval = linear_data.size() / progress_report_steps; - std::mutex critical_sections; -#endif // SLIC3R_TREESUPPORTS_PROGRESS - - // Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time. - const Polygon branch_circle = make_circle(config.branch_radius, SUPPORT_TREE_CIRCLE_RESOLUTION); - - tbb::parallel_for(tbb::blocked_range(0, linear_data.size()), - [&volumes, &config, &move_bounds, &linear_data, &branch_circle, &throw_on_cancel](const tbb::blocked_range &range) { - for (size_t idx = range.begin(); idx < range.end(); ++ idx) { - DrawArea &draw_area = linear_data[idx]; - const LayerIndex layer_idx = draw_area.element->state.layer_idx; - const coord_t radius = support_element_radius(config, *draw_area.element); - 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 (! draw_area.element->state.skip_ovalisation) { - if (draw_area.child_element != nullptr) { - const Point movement = draw_area.child_element->state.result_on_layer - draw_area.element->state.result_on_layer; - movement_directions.emplace_back(movement, radius); - } - const SupportElements *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr; - for (int32_t parent_idx : draw_area.element->parents) { - const SupportElement &parent = (*layer_above)[parent_idx]; - const Point movement = parent.state.result_on_layer - draw_area.element->state.result_on_layer; - //FIXME why max(..., config.support_line_width)? - movement_directions.emplace_back(movement, std::max(support_element_radius(config, parent), config.support_line_width)); - parent_uses_min |= parent.state.use_min_xy_dist; - } - } - - const Polygons &collision = volumes.getCollision(0, layer_idx, parent_uses_min || draw_area.element->state.use_min_xy_dist); - auto generateArea = [&collision, &draw_area, &branch_circle, branch_radius = config.branch_radius, support_line_width = config.support_line_width, &movement_directions] - (coord_t aoffset, double &max_speed) { - Polygons poly; - max_speed = 0; - for (std::pair movement : movement_directions) { - max_speed = std::max(max_speed, movement.first.cast().norm()); - - // Visualization: https://jsfiddle.net/0zvcq39L/2/ - // Ovalizes the circle to an ellipse, that contains both old center and new target position. - double used_scale = (movement.second + aoffset) / (1.0 * branch_radius); - Point center_position = draw_area.element->state.result_on_layer + movement.first / 2; - 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[] = { - used_scale * (1 + moveX * moveX * vsize_inv), - used_scale * (0 + moveX * moveY * vsize_inv), - used_scale * (0 + moveX * moveY * vsize_inv), - used_scale * (1 + moveY * moveY * vsize_inv), - }; - Polygon circle; - for (Point vertex : branch_circle) - circle.points.emplace_back(center_position + Point(matrix[0] * vertex.x() + matrix[1] * vertex.y(), matrix[2] * vertex.x() + matrix[3] * vertex.y())); - poly.emplace_back(std::move(circle)); - } - - // 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), collision); - return poly; - }; - - // Ensure branch area will not overlap with model/collision. This can happen because of e.g. ovalization or increase_until_radius. - double max_speed; - Polygons polygons = generateArea(0, max_speed); - const bool fast_relative_movement = max_speed > radius * 0.75; - - if (fast_relative_movement || support_element_radius(config, *draw_area.element) - support_element_collision_radius(config, draw_area.element->state) > config.support_line_width) { - // Simulate the path the nozzle will take on the outermost wall. - // If multiple parts exist, the outer line will not go all around the support part potentially causing support material to be printed mid air. - ExPolygons nozzle_path = offset_ex(polygons, - config.support_line_width / 2); - if (nozzle_path.size() > 1) { - // Just try to make the area a tiny bit larger. - polygons = generateArea(config.support_line_width / 2, max_speed); - nozzle_path = offset_ex(polygons, -config.support_line_width / 2); - // If larger area did not fix the problem, all parts off the nozzle path that do not contain the center point are removed, hoping for the best. - if (nozzle_path.size() > 1) { - ExPolygons polygons_with_correct_center; - for (ExPolygon &part : nozzle_path) { - bool drop = false; - if (! part.contains(draw_area.element->state.result_on_layer)) { - // try a fuzzy inside as sometimes the point should be on the border, but is not because of rounding errors... - Point pt = draw_area.element->state.result_on_layer; - move_inside(to_polygons(part), pt, 0); - drop = (draw_area.element->state.result_on_layer - pt).cast().norm() >= scaled(0.025); - } - if (! drop) - polygons_with_correct_center.emplace_back(std::move(part)); - } - // Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above. - assert(contains(polygons, draw_area.element->state.result_on_layer)); - polygons = diff_clipped(offset(polygons_with_correct_center, config.support_line_width / 2, jtMiter, 1.2), - //FIXME Vojtech: Clipping may split the region into multiple pieces again, reversing the fixing effort. - collision); - } - } - } - - draw_area.polygons = std::move(polygons); - -#ifdef SLIC3R_TREESUPPORTS_PROGRESS - if (idx % progress_inserts_check_interval == 0) { - std::lock_guard critical_section_progress(critical_sections); - progress_total += TREE_PROGRESS_GENERATE_BRANCH_AREAS / progress_report_steps; - Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); - } -#endif - throw_on_cancel(); - } - }); -} - -/*! - * \brief Applies some smoothing to the outer wall, intended to smooth out sudden jumps as they can happen when a branch moves though a hole. - * - * \param layer_tree_polygons[in,out] Resulting branch areas with the layerindex they appear on. - */ -static void smooth_branch_areas( - const TreeSupportSettings &config, - std::vector &move_bounds, - std::vector &linear_data, - const std::vector &linear_data_layers, - std::function throw_on_cancel) -{ -#ifdef SLIC3R_TREESUPPORTS_PROGRESS - double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS; -#endif // SLIC3R_TREESUPPORTS_PROGRESS - - const coord_t max_radius_change_per_layer = 1 + config.support_line_width / 2; // this is the upper limit a radius may change per layer. +1 to avoid rounding errors - - // smooth upwards - for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()) - 1; ++ layer_idx) { - const size_t processing_base = linear_data_layers[layer_idx]; - const size_t processing_base_above = linear_data_layers[layer_idx + 1]; - const SupportElements &layer_above = move_bounds[layer_idx + 1]; - tbb::parallel_for(tbb::blocked_range(0, processing_base_above - processing_base), - [&](const tbb::blocked_range &range) { - for (size_t processing_idx = range.begin(); processing_idx < range.end(); ++ processing_idx) { - DrawArea &draw_area = linear_data[processing_base + processing_idx]; - assert(draw_area.element->state.layer_idx == layer_idx); - double max_outer_wall_distance = 0; - bool do_something = false; - for (int32_t parent_idx : draw_area.element->parents) { - const SupportElement &parent = layer_above[parent_idx]; - assert(parent.state.layer_idx == layer_idx + 1); - if (support_element_radius(config, parent) != support_element_collision_radius(config, parent)) { - do_something = true; - max_outer_wall_distance = std::max(max_outer_wall_distance, - (draw_area.element->state.result_on_layer - parent.state.result_on_layer).cast().norm() - (support_element_radius(config, *draw_area.element) - support_element_radius(config, parent))); - } - } - max_outer_wall_distance += max_radius_change_per_layer; // As this change is a bit larger than what usually appears, lost radius can be slowly reclaimed over the layers. - if (do_something) { - assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer)); - Polygons max_allowed_area = offset(draw_area.polygons, float(max_outer_wall_distance), jtMiter, 1.2); - for (int32_t parent_idx : draw_area.element->parents) { - const SupportElement &parent = layer_above[parent_idx]; -#ifndef NDEBUG - assert(parent.state.layer_idx == layer_idx + 1); - assert(contains(linear_data[processing_base_above + parent_idx].polygons, parent.state.result_on_layer)); - double radius_increase = support_element_radius(config, *draw_area.element) - support_element_radius(config, parent); - assert(radius_increase >= 0); - double shift = (draw_area.element->state.result_on_layer - parent.state.result_on_layer).cast().norm(); - assert(shift < radius_increase + 2. * config.maximum_move_distance_slow); -#endif // NDEBUG - if (support_element_radius(config, parent) != support_element_collision_radius(config, parent)) { - // No other element on this layer than the current one may be connected to &parent, - // thus it is safe to update parent's DrawArea directly. - Polygons &dst = linear_data[processing_base_above + parent_idx].polygons; -// Polygons orig = dst; - if (! dst.empty()) { - dst = intersection(dst, max_allowed_area); -#if 0 - if (dst.empty()) { - static int irun = 0; - SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-smooth-error-%d.svg", irun ++), - { { { union_ex(max_allowed_area) }, { "max_allowed_area", "yellow", 0.5f } }, - { { union_ex(orig) }, { "orig", "red", "black", "", scaled(0.1f), 0.5f } } }); - ::MessageBoxA(nullptr, "TreeSupport smoothing bug", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING); - } -#endif - } - } - } - } - throw_on_cancel(); - } - }); - } - -#ifdef SLIC3R_TREESUPPORTS_PROGRESS - progress_total += TREE_PROGRESS_SMOOTH_BRANCH_AREAS / 2; - Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); // It is just assumed that both smoothing loops together are one third of the time spent in this function. This was guessed. As the whole function is only 10%, and the smoothing is hard to predict a progress report in the loop may be not useful. -#endif - - // smooth downwards - for (auto& element : move_bounds.back()) - element.state.marked = false; - for (int layer_idx = int(move_bounds.size()) - 2; layer_idx >= 0; -- layer_idx) { - const size_t processing_base = linear_data_layers[layer_idx]; - const size_t processing_base_above = linear_data_layers[layer_idx + 1]; - const SupportElements &layer_above = move_bounds[layer_idx + 1]; - tbb::parallel_for(tbb::blocked_range(0, processing_base_above - processing_base), - [&](const tbb::blocked_range &range) { - for (size_t processing_idx = range.begin(); processing_idx < range.end(); ++ processing_idx) { - DrawArea &draw_area = linear_data[processing_base + processing_idx]; - bool do_something = false; - Polygons max_allowed_area; - for (int32_t parent_idx : draw_area.element->parents) { - const SupportElement &parent = layer_above[parent_idx]; - coord_t max_outer_line_increase = max_radius_change_per_layer; - Polygons result = offset(linear_data[processing_base_above + parent_idx].polygons, max_outer_line_increase, jtMiter, 1.2); - Point direction = draw_area.element->state.result_on_layer - parent.state.result_on_layer; - // move the polygons object - for (auto &outer : result) - for (Point& p : outer) - p += direction; - append(max_allowed_area, std::move(result)); - do_something = do_something || parent.state.marked || support_element_collision_radius(config, parent) != support_element_radius(config, parent); - } - if (do_something) { - // Trim the current drawing areas with max_allowed_area. - Polygons result = intersection(max_allowed_area, draw_area.polygons); - if (area(result) < area(draw_area.polygons)) { - // Mark parent as modified to propagate down. - draw_area.element->state.marked = true; - draw_area.polygons = std::move(result); - } - } - throw_on_cancel(); - } - }); - } - -#ifdef SLIC3R_TREESUPPORTS_PROGRESS - progress_total += TREE_PROGRESS_SMOOTH_BRANCH_AREAS / 2; - Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); -#endif -} - -/*! - * \brief Drop down areas that do rest non-gracefully on the model to ensure the branch actually rests on something. - * - * \param layer_tree_polygons[in] Resulting branch areas with the layerindex they appear on. - * \param linear_data[in] All currently existing influence areas with the layer they are on - * \param dropped_down_areas[out] Areas that have to be added to support all non-graceful areas. - * \param inverse_tree_order[in] A mapping that returns the child of every influence area. - */ -static void drop_non_gracious_areas( - const TreeModelVolumes &volumes, - const std::vector &linear_data, - std::vector &support_layer_storage, - std::function throw_on_cancel) -{ - const auto _tiny_area_threshold = tiny_area_threshold(); - std::vector>> dropped_down_areas(linear_data.size()); - tbb::parallel_for(tbb::blocked_range(0, linear_data.size()), - [&](const tbb::blocked_range &range) { - for (size_t idx = range.begin(); idx < range.end(); ++ idx) { - // If a element has no child, it connects to whatever is below as no support further down for it will exist. - if (const DrawArea &draw_element = linear_data[idx]; ! draw_element.element->state.to_model_gracious && draw_element.child_element == nullptr) { - Polygons rest_support; - const LayerIndex layer_idx_first = draw_element.element->state.layer_idx - 1; - for (LayerIndex layer_idx = layer_idx_first; area(rest_support) > _tiny_area_threshold && layer_idx >= 0; -- layer_idx) { - rest_support = diff_clipped(layer_idx == layer_idx_first ? draw_element.polygons : rest_support, volumes.getCollision(0, layer_idx, false)); - dropped_down_areas[idx].emplace_back(layer_idx, rest_support); - } - } - throw_on_cancel(); - } - }); - - for (coord_t i = 0; i < static_cast(dropped_down_areas.size()); i++) - for (std::pair &pair : dropped_down_areas[i]) - append(support_layer_storage[pair.first], std::move(pair.second)); -} - -/*! - * \brief Generates Support Floor, ensures Support Roof can not cut of branches, and saves the branches as support to storage - * - * \param support_layer_storage[in] Areas where support should be generated. - * \param support_roof_storage[in] Areas where support was replaced with roof. - * \param storage[in,out] The storage where the support should be stored. - */ -static void finalize_interface_and_support_areas( - const PrintObject &print_object, - const TreeModelVolumes &volumes, - const TreeSupportSettings &config, - const std::vector &overhangs, - std::vector &support_layer_storage, - std::vector &support_roof_storage, - - SupportGeneratorLayersPtr &bottom_contacts, - SupportGeneratorLayersPtr &top_contacts, - SupportGeneratorLayersPtr &intermediate_layers, - SupportGeneratorLayerStorage &layer_storage, - - std::function throw_on_cancel) -{ - assert(std::all_of(bottom_contacts.begin(), bottom_contacts.end(), [](auto *p) { return p == nullptr; })); -// assert(std::all_of(top_contacts.begin(), top_contacts.end(), [](auto* p) { return p == nullptr; })); - assert(std::all_of(intermediate_layers.begin(), intermediate_layers.end(), [](auto* p) { return p == nullptr; })); - - InterfacePreference interface_pref = config.interface_preference; // InterfacePreference::InterfaceAreaOverwritesSupport; - -#ifdef SLIC3R_TREESUPPORTS_PROGRESS - double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS + TREE_PROGRESS_SMOOTH_BRANCH_AREAS; -#endif // SLIC3R_TREESUPPORTS_PROGRESS - - // Iterate over the generated circles in parallel and clean them up. Also add support floor. - tbb::parallel_for(tbb::blocked_range(0, support_layer_storage.size()), - [&](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - // Subtract support lines of the branches from the roof - SupportGeneratorLayer *support_roof = top_contacts[layer_idx]; - Polygons support_roof_polygons; - - if (Polygons &src = support_roof_storage[layer_idx]; ! src.empty()) { - if (support_roof != nullptr && ! support_roof->polygons.empty()) { - support_roof_polygons = union_(src, support_roof->polygons); - support_roof->polygons.clear(); - } else - support_roof_polygons = std::move(src); - } else if (support_roof != nullptr) { - support_roof_polygons = std::move(support_roof->polygons); - support_roof->polygons.clear(); - } - - assert(intermediate_layers[layer_idx] == nullptr); - Polygons base_layer_polygons = std::move(support_layer_storage[layer_idx]); - - if (! base_layer_polygons.empty()) { - // Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned. - base_layer_polygons = smooth_outward(union_(base_layer_polygons), 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. - base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled(0.03), double(config.resolution)), polygons_strictly_simple); - } - - if (! support_roof_polygons.empty() && ! base_layer_polygons.empty()) { -// if (area(intersection(base_layer_polygons, support_roof_polygons)) > tiny_area_threshold) - { - switch (interface_pref) { - case InterfacePreference::InterfaceAreaOverwritesSupport: - base_layer_polygons = diff(base_layer_polygons, support_roof_polygons); - break; - case InterfacePreference::SupportAreaOverwritesInterface: - support_roof_polygons = diff(support_roof_polygons, base_layer_polygons); - break; - //FIXME - #if 1 - case InterfacePreference::InterfaceLinesOverwriteSupport: - case InterfacePreference::SupportLinesOverwriteInterface: - assert(false); - [[fallthrough]]; - #else - case InterfacePreference::InterfaceLinesOverwriteSupport: - { - // Hatch the support roof interfaces, offset them by their line width and subtract them from support base. - Polygons interface_lines = offset(to_polylines( - generate_support_infill_lines(support_roof->polygons, true, layer_idx, config.support_roof_line_distance)), - config.support_roof_line_width / 2); - base_layer_polygons = diff(base_layer_polygons, interface_lines); - break; - } - case InterfacePreference::SupportLinesOverwriteInterface: - { - // Hatch the support roof interfaces, offset them by their line width and subtract them from support base. - Polygons tree_lines = union_(offset(to_polylines( - generate_support_infill_lines(base_layer_polygons, false, layer_idx, config.support_line_distance, true)), - config.support_line_width / 2)); - // do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below. - support_roof->polygons = diff(support_roof->polygons, tree_lines); - break; - } - #endif - case InterfacePreference::Nothing: - break; - } - } - } - - // Subtract support floors from the support area and add them to the support floor instead. - if (config.support_bottom_layers > 0 && ! base_layer_polygons.empty()) { - SupportGeneratorLayer*& support_bottom = bottom_contacts[layer_idx]; - Polygons layer_outset = diff_clipped( - config.support_bottom_offset > 0 ? offset(base_layer_polygons, config.support_bottom_offset, jtMiter, 1.2) : base_layer_polygons, - volumes.getCollision(0, layer_idx, false)); - Polygons floor_layer; - size_t layers_below = 0; - while (layers_below <= config.support_bottom_layers) { - // one sample at 0 layers below, another at config.support_bottom_layers. In-between samples at config.performance_interface_skip_layers distance from each other. - const size_t sample_layer = static_cast(std::max(0, (static_cast(layer_idx) - static_cast(layers_below)) - static_cast(config.z_distance_bottom_layers))); - //FIXME subtract the wipe tower - append(floor_layer, intersection(layer_outset, overhangs[sample_layer])); - if (layers_below < config.support_bottom_layers) - layers_below = std::min(layers_below + 1, config.support_bottom_layers); - else - break; - } - if (! floor_layer.empty()) { - if (support_bottom == nullptr) - support_bottom = &layer_allocate(layer_storage, SupporLayerType::BottomContact, print_object.slicing_parameters(), config, layer_idx); - support_bottom->polygons = union_(floor_layer, support_bottom->polygons); - base_layer_polygons = diff_clipped(base_layer_polygons, offset(support_bottom->polygons, scaled(0.01), jtMiter, 1.2)); // Subtract the support floor from the normal support. - } - } - - if (! support_roof_polygons.empty()) { - if (support_roof == nullptr) - support_roof = top_contacts[layer_idx] = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, layer_idx); - support_roof->polygons = union_(support_roof_polygons); - } - if (! base_layer_polygons.empty()) { - SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(layer_storage, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx); - base_layer->polygons = union_(base_layer_polygons); - } - -#ifdef SLIC3R_TREESUPPORTS_PROGRESS - { - std::lock_guard critical_section_progress(critical_sections); - progress_total += TREE_PROGRESS_FINALIZE_BRANCH_AREAS / support_layer_storage.size(); - Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); - } -#endif -#if 0 - { - std::lock_guard lock(critical_sections); - if (!storage.support.supportLayers[layer_idx].support_infill_parts.empty() || !storage.support.supportLayers[layer_idx].support_roof.empty()) - storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, static_cast(layer_idx)); - } -#endif - throw_on_cancel(); - } - }); -} - -/*! - * \brief Draws circles around result_on_layer points of the influence areas and applies some post processing. - * - * \param move_bounds[in] All currently existing influence areas - * \param storage[in,out] The storage where the support should be stored. - */ -static void draw_areas( - PrintObject &print_object, - const TreeModelVolumes &volumes, - const TreeSupportSettings &config, - const std::vector &overhangs, - std::vector &move_bounds, - - SupportGeneratorLayersPtr &bottom_contacts, - SupportGeneratorLayersPtr &top_contacts, - SupportGeneratorLayersPtr &intermediate_layers, - SupportGeneratorLayerStorage &layer_storage, - std::function throw_on_cancel) -{ - std::vector support_layer_storage(move_bounds.size()); - std::vector support_roof_storage(move_bounds.size()); - // All SupportElements are put into a layer independent storage to improve parallelization. - std::vector linear_data; - std::vector linear_data_layers; - { - std::vector> map_downwards_old; - std::vector> map_downwards_new; - for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) { - SupportElements *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr; - map_downwards_new.clear(); - linear_data_layers.emplace_back(linear_data.size()); - std::sort(map_downwards_old.begin(), map_downwards_old.end(), [](auto &l, auto &r) { return l.first < r.first; }); - for (SupportElement &elem : move_bounds[layer_idx]) { - SupportElement *child = nullptr; - if (layer_idx > 0) { - auto it = std::lower_bound(map_downwards_old.begin(), map_downwards_old.end(), &elem, [](auto &l, const SupportElement *r) { return l.first < r; }); - if (it != map_downwards_old.end() && it->first == &elem) { - child = it->second; - // Only one link points to a node above from below. - assert(! (++ it != map_downwards_old.end() && it->first == &elem)); - } - assert(child ? child->state.result_on_layer_is_set() : elem.state.target_height > layer_idx); - } - for (int32_t parent_idx : elem.parents) { - SupportElement &parent = (*layer_above)[parent_idx]; - if (parent.state.result_on_layer_is_set()) - map_downwards_new.emplace_back(&parent, &elem); - } - linear_data.push_back({ &elem, child }); - } - std::swap(map_downwards_old, map_downwards_new); - } - linear_data_layers.emplace_back(linear_data.size()); - } - - throw_on_cancel(); - -#ifndef NDEBUG - for (size_t i = 0; i < move_bounds.size(); ++ i) { - size_t begin = linear_data_layers[i]; - size_t end = linear_data_layers[i + 1]; - for (size_t j = begin; j < end; ++ j) - assert(linear_data[j].element == &move_bounds[i][j - begin]); - } -#endif // NDEBUG - - auto t_start = std::chrono::high_resolution_clock::now(); - // Generate the circles that will be the branches. - generate_branch_areas(volumes, config, move_bounds, linear_data, throw_on_cancel); - -#if 0 - assert(linear_data_layers.size() == move_bounds.size() + 1); - for (const auto &draw_area : linear_data) - assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer)); - for (size_t i = 0; i < move_bounds.size(); ++ i) { - size_t begin = linear_data_layers[i]; - size_t end = linear_data_layers[i + 1]; - for (size_t j = begin; j < end; ++ j) { - const auto &draw_area = linear_data[j]; - assert(draw_area.element == &move_bounds[i][j - begin]); - assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer)); - } - } -#endif - -#if 0 - for (size_t area_layer_idx = 0; area_layer_idx + 1 < linear_data_layers.size(); ++ area_layer_idx) { - size_t begin = linear_data_layers[area_layer_idx]; - size_t end = linear_data_layers[area_layer_idx + 1]; - Polygons polygons; - for (size_t area_idx = begin; area_idx < end; ++ area_idx) { - DrawArea &area = linear_data[area_idx]; - append(polygons, area.polygons); - } - SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-raw-%d.svg", area_layer_idx), - { { { union_ex(polygons) }, { "parent", "red", "black", "", scaled(0.1f), 0.5f } } }); - } -#endif - - auto t_generate = std::chrono::high_resolution_clock::now(); - // In some edgecases a branch may go though a hole, where the regular radius does not fit. This can result in an apparent jump in branch radius. As such this cases need to be caught and smoothed out. - smooth_branch_areas(config, move_bounds, linear_data, linear_data_layers, throw_on_cancel); - -#if 0 - for (size_t area_layer_idx = 0; area_layer_idx + 1 < linear_data_layers.size(); ++area_layer_idx) { - size_t begin = linear_data_layers[area_layer_idx]; - size_t end = linear_data_layers[area_layer_idx + 1]; - Polygons polygons; - for (size_t area_idx = begin; area_idx < end; ++area_idx) { - DrawArea& area = linear_data[area_idx]; - append(polygons, area.polygons); - } - SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-smooth-%d.svg", area_layer_idx), - { { { union_ex(polygons) }, { "parent", "red", "black", "", scaled(0.1f), 0.5f } } }); - } -#endif - - auto t_smooth = std::chrono::high_resolution_clock::now(); - // drop down all trees that connect non gracefully with the model - drop_non_gracious_areas(volumes, linear_data, support_layer_storage, throw_on_cancel); - auto t_drop = std::chrono::high_resolution_clock::now(); - - // Single threaded combining all support areas to the right layers. - { - auto begin = linear_data.begin(); - for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) { - size_t cnt_roofs = 0; - size_t cnt_layers = 0; - auto end = begin; - for (; end != linear_data.end() && end->element->state.layer_idx == layer_idx; ++ end) - ++ (end->element->state.missing_roof_layers > end->element->state.distance_to_top ? cnt_roofs : cnt_layers); - auto &this_roofs = support_roof_storage[layer_idx]; - auto &this_layers = support_layer_storage[layer_idx]; - this_roofs.reserve(this_roofs.size() + cnt_roofs); - this_layers.reserve(this_layers.size() + cnt_layers); - for (auto it = begin; it != end; ++ it) - std::move(std::begin(it->polygons), std::end(it->polygons), std::back_inserter(it->element->state.missing_roof_layers > it->element->state.distance_to_top ? this_roofs : this_layers)); - begin = end; - } - } - - finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage, - bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); - auto t_end = std::chrono::high_resolution_clock::now(); - - auto dur_gen_tips = 0.001 * std::chrono::duration_cast(t_generate - t_start).count(); - auto dur_smooth = 0.001 * std::chrono::duration_cast(t_smooth - t_generate).count(); - auto dur_drop = 0.001 * std::chrono::duration_cast(t_drop - t_smooth).count(); - auto dur_finalize = 0.001 * std::chrono::duration_cast(t_end - t_drop).count(); - - BOOST_LOG_TRIVIAL(info) << - "Time used for drawing subfuctions: generate_branch_areas: " << dur_gen_tips << " ms " - "smooth_branch_areas: " << dur_smooth << " ms " - "drop_non_gracious_areas: " << dur_drop << " ms " - "finalize_interface_and_support_areas " << dur_finalize << " ms"; -} - template void triangulate_fan(indexed_triangle_set &its, int ifan, int ibegin, int iend) { @@ -4065,18 +3438,10 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume auto t_place = std::chrono::high_resolution_clock::now(); // ### draw these points as circles - - if (print_object.config().support_style.value != smsOrganic && - // Orca: use organic as default - print_object.config().support_style.value != smsDefault) { - draw_areas(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds, - bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); - } else { - organic_draw_branches( - *print.get_object(processing.second.front()), volumes, config, move_bounds, - bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage, - throw_on_cancel); - } + organic_draw_branches( + *print.get_object(processing.second.front()), volumes, config, move_bounds, + bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage, + throw_on_cancel); remove_undefined_layers(); From 824f9efb69f1970e0c1da9b13cd87db46ce471bc Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 26 Jan 2025 15:36:07 +0800 Subject: [PATCH 02/72] Remove duplicated support code --- src/libslic3r/Support/SupportCommon.cpp | 256 ++-- src/libslic3r/Support/SupportMaterial.cpp | 1562 +-------------------- src/libslic3r/Support/SupportMaterial.hpp | 60 +- 3 files changed, 146 insertions(+), 1732 deletions(-) diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index d37324084c..01261bb6dc 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -329,19 +329,21 @@ SupportGeneratorLayersPtr generate_raft_base( const BrimType brim_type = object.config().brim_type; const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner; const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner; - const auto brim_separation = scaled(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(object.config().brim_object_gap.value); + //const auto brim_object_gap = scaled(object.config().brim_object_gap.value + object.config().brim_width.value); for (const ExPolygon &ex : object.layers().front()->lslices) { if (brim_outer && brim_inner) - polygons_append(brim, offset(ex, brim_separation)); + polygons_append(brim, offset(ex, brim_object_gap)); else { 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 brim.emplace_back(ex.contour); if (brim_inner) { Polygons holes = ex.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_append(brim, std::move(holes)); } else @@ -378,7 +380,7 @@ SupportGeneratorLayersPtr generate_raft_base( polygons_append(interface_polygons, expand(interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (base_interfaces != nullptr && ! base_interfaces->polygons.empty()) polygons_append(interface_polygons, expand(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - + // Output vector. SupportGeneratorLayersPtr raft_layers; @@ -402,12 +404,12 @@ SupportGeneratorLayersPtr generate_raft_base( } if (! interface_polygons.empty()) { // Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface. - base = union_(base, interface_polygons); + base = union_(base, interface_polygons); } // Do not add the raft contact layer, only add the raft layers below the contact layer. // Insert the 1st layer. { - 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); new_layer.print_z = 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) { // Expand the bases of the support columns in the 1st layer. 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) { // 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()))); @@ -536,10 +543,10 @@ static Polylines draw_perimeters(const ExPolygon &expoly, double clip_length) return polylines; } -static inline void tree_supports_generate_paths( +void tree_supports_generate_paths( ExtrusionEntitiesPtr &dst, const Polygons &polygons, - const Flow &flow, + const Flow &flow, const SupportParameters &support_params) { // Offset expolygon inside, returns number of expolygons collected (0 or 1). @@ -606,7 +613,7 @@ static inline void tree_supports_generate_paths( // No hole remaining after an offset. Just copy the outer contour. append(out, std::move(contours)); } else { - // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. + // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. // Subtract the offsetted holes from the offsetted contours. ClipperLib_Z::Clipper clipper; clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) { @@ -637,124 +644,126 @@ static inline void tree_supports_generate_paths( ClipperLib_Z::Paths anchor_candidates; for (ExPolygon& expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width()))) { std::unique_ptr eec; + ExPolygons regions_to_draw_inner_wall{expoly}; if (support_params.tree_branch_diameter_double_wall_area_scaled > 0) if (double area = expoly.area(); area > support_params.tree_branch_diameter_double_wall_area_scaled) { + BOOST_LOG_TRIVIAL(debug)<< "TreeSupports: double wall area: " << area<< " > " << support_params.tree_branch_diameter_double_wall_area_scaled; eec = std::make_unique(); - // Don't reoder internal / external loops of the same island, always start with the internal loop. + // Don't reorder internal / external loops of the same island, always start with the internal loop. eec->no_sort = true; // Make the tree branch stable by adding another perimeter. - ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); - if (level2.size() == 1) { - Polylines polylines; + ExPolygons level2 = offset2_ex({expoly}, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); + if (level2.size() > 0) { + regions_to_draw_inner_wall = level2; extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), ExtrusionRole::erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), - // Disable reversal of the path, always start with the anchor, always print CCW. - false); + // Disable reversal of the path, always start with the anchor, always print CCW. + false); expoly = level2.front(); } } + for (ExPolygon &expoly : regions_to_draw_inner_wall) + { + // Try to produce one more perimeter to place the seam anchor. + // First genrate a 2nd perimeter loop as a source for anchor candidates. + // The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection. + anchor_candidates.clear(); + shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates); + // Orient all contours CW. + for (auto &path : anchor_candidates) + if (ClipperLib_Z::Area(path) > 0) std::reverse(path.begin(), path.end()); - // Try to produce one more perimeter to place the seam anchor. - // First genrate a 2nd perimeter loop as a source for anchor candidates. - // The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection. - anchor_candidates.clear(); - shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates); - // Orient all contours CW. - for (auto &path : anchor_candidates) - if (ClipperLib_Z::Area(path) > 0) - std::reverse(path.begin(), path.end()); - - // Draw the perimeters. - Polylines polylines; - polylines.reserve(expoly.holes.size() + 1); - for (int idx_loop = 0; idx_loop < int(expoly.num_contours()); ++ idx_loop) { - // Open the loop with a seam. - const Polygon &loop = expoly.contour_or_hole(idx_loop); - Polyline pl(loop.points); - // Orient all contours CW, because the anchor will be added to the end of polyline while we want to start a loop with the anchor. - if (idx_loop == 0) - // It is an outer contour. - pl.reverse(); - pl.points.emplace_back(pl.points.front()); - pl.clip_end(clip_length); - if (pl.size() < 2) - continue; - // Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour. - ClipperLib_Z::Path *closest_contour = nullptr; - Vec2d closest_point; - int closest_point_idx = -1; - double closest_point_t = 0.; - double d2min = std::numeric_limits::max(); - Vec2d seam_pt = pl.back().cast(); - for (ClipperLib_Z::Path &path : anchor_candidates) - for (int i = 0; i < int(path.size()); ++ i) { - int j = next_idx_modulo(i, path); - if (path[i].z() == idx_loop || path[j].z() == idx_loop) { - Vec2d pi(path[i].x(), path[i].y()); - Vec2d pj(path[j].x(), path[j].y()); - Vec2d v = pj - pi; - Vec2d w = seam_pt - pi; - auto l2 = v.squaredNorm(); - auto t = std::clamp((l2 == 0) ? 0 : v.dot(w) / l2, 0., 1.); - if ((path[i].z() == idx_loop || t > EPSILON) && (path[j].z() == idx_loop || t < 1. - EPSILON)) { - // Closest point. - Vec2d fp = pi + v * t; - double d2 = (fp - seam_pt).squaredNorm(); - if (d2 < d2min) { - d2min = d2; - closest_contour = &path; - closest_point = fp; - closest_point_idx = i; - closest_point_t = t; + // Draw the perimeters. + Polylines polylines; + polylines.reserve(expoly.holes.size() + 1); + for (int idx_loop = 0; idx_loop < int(expoly.num_contours()); ++idx_loop) { + // Open the loop with a seam. + const Polygon &loop = expoly.contour_or_hole(idx_loop); + Polyline pl(loop.points); + // Orient all contours CW, because the anchor will be added to the end of polyline while we want to start a loop with the anchor. + if (idx_loop == 0) + // It is an outer contour. + pl.reverse(); + pl.points.emplace_back(pl.points.front()); + pl.clip_end(clip_length); + if (pl.size() < 2) continue; + // Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour. + ClipperLib_Z::Path *closest_contour = nullptr; + Vec2d closest_point; + int closest_point_idx = -1; + double closest_point_t = 0.; + double d2min = std::numeric_limits::max(); + Vec2d seam_pt = pl.back().cast(); + for (ClipperLib_Z::Path &path : anchor_candidates) + for (int i = 0; i < int(path.size()); ++i) { + int j = next_idx_modulo(i, path); + if (path[i].z() == idx_loop || path[j].z() == idx_loop) { + Vec2d pi(path[i].x(), path[i].y()); + Vec2d pj(path[j].x(), path[j].y()); + Vec2d v = pj - pi; + Vec2d w = seam_pt - pi; + auto l2 = v.squaredNorm(); + auto t = std::clamp((l2 == 0) ? 0 : v.dot(w) / l2, 0., 1.); + if ((path[i].z() == idx_loop || t > EPSILON) && (path[j].z() == idx_loop || t < 1. - EPSILON)) { + // Closest point. + Vec2d fp = pi + v * t; + double d2 = (fp - seam_pt).squaredNorm(); + if (d2 < d2min) { + d2min = d2; + closest_contour = &path; + closest_point = fp; + closest_point_idx = i; + closest_point_t = t; + } } } } - } - if (d2min < sqr(flow.scaled_width() * 3.)) { - // Try to cut an anchor from the closest_contour. - // Both closest_contour and pl are CW oriented. - pl.points.emplace_back(closest_point.cast()); - const ClipperLib_Z::Path &path = *closest_contour; - double remaining_length = anchor_length - (seam_pt - closest_point).norm(); - int i = closest_point_idx; - int j = next_idx_modulo(i, *closest_contour); - Vec2d pi(path[i].x(), path[i].y()); - Vec2d pj(path[j].x(), path[j].y()); - Vec2d v = pj - pi; - double l = v.norm(); - if (remaining_length < (1. - closest_point_t) * l) { - // Just trim the current line. - pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast()); - } else { - // Take the rest of the current line, continue with the other lines. - pl.points.emplace_back(path[j].x(), path[j].y()); - pi = pj; - for (i = j; path[i].z() == idx_loop && remaining_length > 0; i = j, pi = pj) { - j = next_idx_modulo(i, path); - pj = Vec2d(path[j].x(), path[j].y()); - v = pj - pi; - l = v.norm(); - if (i == closest_point_idx) { - // Back at the first segment. Most likely this should not happen and we may end the anchor. - break; - } - if (remaining_length <= l) { - pl.points.emplace_back((pi + v * (remaining_length / l)).cast()); - break; - } + if (d2min < sqr(flow.scaled_width() * 3.)) { + // Try to cut an anchor from the closest_contour. + // Both closest_contour and pl are CW oriented. + pl.points.emplace_back(closest_point.cast()); + const ClipperLib_Z::Path &path = *closest_contour; + double remaining_length = anchor_length - (seam_pt - closest_point).norm(); + int i = closest_point_idx; + int j = next_idx_modulo(i, *closest_contour); + Vec2d pi(path[i].x(), path[i].y()); + Vec2d pj(path[j].x(), path[j].y()); + Vec2d v = pj - pi; + double l = v.norm(); + if (remaining_length < (1. - closest_point_t) * l) { + // Just trim the current line. + pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast()); + } else { + // Take the rest of the current line, continue with the other lines. pl.points.emplace_back(path[j].x(), path[j].y()); - remaining_length -= l; + pi = pj; + for (i = j; path[i].z() == idx_loop && remaining_length > 0; i = j, pi = pj) { + j = next_idx_modulo(i, path); + pj = Vec2d(path[j].x(), path[j].y()); + v = pj - pi; + l = v.norm(); + if (i == closest_point_idx) { + // Back at the first segment. Most likely this should not happen and we may end the anchor. + break; + } + if (remaining_length <= l) { + pl.points.emplace_back((pi + v * (remaining_length / l)).cast()); + break; + } + pl.points.emplace_back(path[j].x(), path[j].y()); + remaining_length -= l; + } } } + // Start with the anchor. + pl.reverse(); + polylines.emplace_back(std::move(pl)); } - // Start with the anchor. - pl.reverse(); - polylines.emplace_back(std::move(pl)); - } - ExtrusionEntitiesPtr &out = eec ? eec->entities : dst; - extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), - // Disable reversal of the path, always start with the anchor, always print CCW. - false); + ExtrusionEntitiesPtr &out = eec ? eec->entities : dst; + extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + // Disable reversal of the path, always start with the anchor, always print CCW. + false); + } if (eec) { std::reverse(eec->entities.begin(), eec->entities.end()); dst.emplace_back(eec.release()); @@ -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, const Polygons &polygons, Fill *filler, float density, ExtrusionRole role, const Flow &flow, + const SupportParameters& support_params, bool with_sheath, bool no_sort) { if (polygons.empty()) 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); return; } @@ -1510,7 +1526,7 @@ void generate_support_toolpaths( // Print the support base below the support columns, or the support base for the support columns plus the contacts. if (support_layer_id > 0) { - const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ? + const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ? raft_layer.polygons : //FIXME misusing contact_polygons for support columns. ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); @@ -1531,7 +1547,7 @@ void generate_support_toolpaths( filler, float(support_params.support_density), // Extrusion parameters ExtrusionRole::erSupportMaterial, flow, - support_params.with_sheath, false); + support_params, support_params.with_sheath, false); } if (! tree_polygons.empty()) tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow, support_params); @@ -1558,15 +1574,15 @@ void generate_support_toolpaths( filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); fill_expolygons_with_sheath_generate_paths( // Destination - support_layer.support_fills.entities, + support_layer.support_fills.entities, // Regions to fill tree_polygons.empty() ? raft_layer.polygons : diff(raft_layer.polygons, tree_polygons), // Filler and its parameters filler, density, // Extrusion parameters - (support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::erSupportMaterial : ExtrusionRole::erSupportMaterialInterface, flow, + (support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::erSupportMaterial : ExtrusionRole::erSupportMaterialInterface, flow, // sheath at first layer - support_layer_id == 0, support_layer_id == 0); + support_params, support_layer_id == 0, support_layer_id == 0); } }); @@ -1734,7 +1750,7 @@ void generate_support_toolpaths( // Filler and its parameters filler, float(density), // 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; @@ -1808,7 +1824,7 @@ void generate_support_toolpaths( filler, density, // Extrusion parameters ExtrusionRole::erSupportMaterial, flow, - sheath, no_sort); + support_params, sheath, no_sort); } // Merge base_interface_layers to base_layers to avoid unneccessary retractions diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 3050bd7f6e..85c461b02b 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -346,83 +346,12 @@ static std::string get_svg_filename(std::string layer_nr_or_z, std::string tag } PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : - m_object (object), m_print_config (&object->print()->config()), m_object_config (&object->config()), - m_slicing_params (slicing_params) + m_slicing_params (slicing_params), + m_support_params (*object), + m_object (object) { - m_support_params.first_layer_flow = support_material_1st_layer_flow(object, float(slicing_params.first_print_layer_height)); - m_support_params.support_material_flow = support_material_flow(object, float(slicing_params.layer_height)); - m_support_params.support_material_interface_flow = support_material_interface_flow(object, float(slicing_params.layer_height)); - m_support_params.support_layer_height_min = 0.01; - - // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. - m_support_params.support_layer_height_min = 1000000.; - for (auto lh : m_print_config->min_layer_height.values) - m_support_params.support_layer_height_min = std::min(m_support_params.support_layer_height_min, std::max(0.01, lh)); - for (auto layer : m_object->layers()) - m_support_params.support_layer_height_min = std::min(m_support_params.support_layer_height_min, std::max(0.01, layer->height)); - - if (m_object_config->support_interface_top_layers.value == 0) { - // No interface layers allowed, print everything with the base support pattern. - m_support_params.support_material_interface_flow = m_support_params.support_material_flow; - } - - // Evaluate the XY gap between the object outer perimeters and the support structures. - // Evaluate the XY gap between the object outer perimeters and the support structures. - coordf_t external_perimeter_width = 0.; - coordf_t bridge_flow = 0; - for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) { - const PrintRegion ®ion = object->printing_region(region_id); - external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width())); - bridge_flow += region.config().bridge_flow; - } - m_support_params.gap_xy = m_object_config->support_object_xy_distance.value; - bridge_flow /= object->num_printing_regions(); - - m_support_params.support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ? - m_support_params.support_material_interface_flow.with_flow_ratio(bridge_flow) : - Flow::bridging_flow(bridge_flow * m_support_params.support_material_interface_flow.nozzle_diameter(), m_support_params.support_material_interface_flow.nozzle_diameter()); - - m_support_params.can_merge_support_regions = m_object_config->support_filament.value == m_object_config->support_interface_filament.value; - if (!m_support_params.can_merge_support_regions && (m_object_config->support_filament.value == 0 || m_object_config->support_interface_filament.value == 0)) { - // One of the support extruders is of "don't care" type. - auto object_extruders = m_object->object_extruders(); - if (object_extruders.size() == 1 && - *object_extruders.begin() == std::max(m_object_config->support_filament.value, m_object_config->support_interface_filament.value)) - // Object is printed with the same extruder as the support. - m_support_params.can_merge_support_regions = true; - } - - - m_support_params.base_angle = Geometry::deg2rad(float(m_object_config->support_angle.value)); - m_support_params.interface_angle = Geometry::deg2rad(float(m_object_config->support_angle.value + 90.)); - m_support_params.interface_spacing = m_object_config->support_interface_spacing.value + m_support_params.support_material_interface_flow.spacing(); - m_support_params.interface_density = std::min(1., m_support_params.support_material_interface_flow.spacing() / m_support_params.interface_spacing); - m_support_params.support_spacing = m_object_config->support_base_pattern_spacing.value + m_support_params.support_material_flow.spacing(); - m_support_params.support_density = std::min(1., m_support_params.support_material_flow.spacing() / m_support_params.support_spacing); - if (m_object_config->support_interface_top_layers.value == 0) { - // No interface layers allowed, print everything with the base support pattern. - m_support_params.interface_spacing = m_support_params.support_spacing; - m_support_params.interface_density = m_support_params.support_density; - } - - SupportMaterialPattern support_pattern = m_object_config->support_base_pattern; - m_support_params.with_sheath = support_with_sheath; - m_support_params.base_fill_pattern = - support_pattern == smpHoneycomb ? ipHoneycomb : - m_support_params.support_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : ipSupportBase; - m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); - if (m_object_config->support_interface_pattern == smipGrid) - m_support_params.contact_fill_pattern = ipGrid; - else if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) - m_support_params.contact_fill_pattern = ipRectilinear; - else - m_support_params.contact_fill_pattern = - (m_object_config->support_interface_pattern == smipAuto && m_slicing_params.soluble_interface) || - m_object_config->support_interface_pattern == smipConcentric ? - ipConcentric : - (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); } // Using the std::deque as an allocator. @@ -563,14 +492,15 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Propagate top / bottom contact layers to generate interface layers // and base interface layers (for soluble interface / non souble base only) - auto [interface_layers, base_interface_layers] = this->generate_interface_layers(bottom_contacts, top_contacts, intermediate_layers, layer_storage); + SupportGeneratorLayersPtr empty_layers; + auto [interface_layers, base_interface_layers] = generate_interface_layers(*m_object_config, m_support_params, bottom_contacts, top_contacts, empty_layers, empty_layers, intermediate_layers, layer_storage); BOOST_LOG_TRIVIAL(info) << "Support generator - Creating raft"; // 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); if (object.print()->canceled()) return; @@ -650,7 +580,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #endif /* SLIC3R_DEBUG */ // Generate the actual toolpaths and save them into each layer. - this->generate_toolpaths(object.support_layers(), raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); + generate_support_toolpaths(object.support_layers(), *m_object_config, m_support_params, m_slicing_params, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); #ifdef SLIC3R_DEBUG { @@ -2396,7 +2326,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( // Find the bottom contact layers above the top surfaces of this layer. static inline SupportGeneratorLayer* detect_bottom_contacts( const SlicingParameters &slicing_params, - const PrintObjectSupportMaterial::SupportParams &support_params, + const SupportParameters &support_params, const PrintObject &object, const Layer &layer, // Existing top contact layers, to which this newly created bottom contact layer will be snapped to guarantee a minimum layer height. @@ -3249,1482 +3179,6 @@ 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, - const SupportGeneratorLayersPtr &top_contacts, - const SupportGeneratorLayersPtr &interface_layers, - const SupportGeneratorLayersPtr &base_interface_layers, - const SupportGeneratorLayersPtr &base_layers, - SupportGeneratorLayerStorage &layer_storage) const -{ - // If there is brim to be generated, calculate the trimming regions. - Polygons brim; - if (object.has_brim()) { - // Calculate the area covered by the brim. - const BrimType brim_type = object.config().brim_type; - const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner; - const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner; - // 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(object.config().brim_object_gap.value); - //const auto brim_object_gap = scaled(object.config().brim_object_gap.value + object.config().brim_width.value); - for (const ExPolygon &ex : object.layers().front()->lslices) { - if (brim_outer && brim_inner) - polygons_append(brim, offset(ex, brim_object_gap)); - else { - if (brim_outer) - polygons_append(brim, offset(ex.contour, brim_object_gap, ClipperLib::jtRound, float(scale_(0.1)))); - else - brim.emplace_back(ex.contour); - if (brim_inner) { - Polygons holes = ex.holes; - polygons_reverse(holes); - holes = shrink(holes, brim_object_gap, ClipperLib::jtRound, float(scale_(0.1))); - polygons_reverse(holes); - polygons_append(brim, std::move(holes)); - } else - polygons_append(brim, ex.holes); - } - } - brim = union_(brim); - } - - // 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_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) - // This is not the raft contact layer. - contacts = nullptr; - if (interfaces != nullptr && interfaces->bottom_print_z() > m_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) - // 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) - // This is not the raft interface layer. - columns_base = nullptr; - - Polygons interface_polygons; - if (contacts != nullptr && ! contacts->polygons.empty()) - polygons_append(interface_polygons, expand(contacts->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - if (interfaces != nullptr && ! interfaces->polygons.empty()) - polygons_append(interface_polygons, expand(interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - if (base_interfaces != nullptr && ! base_interfaces->polygons.empty()) - polygons_append(interface_polygons, expand(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - - // Output vector. - SupportGeneratorLayersPtr raft_layers; - - if (m_slicing_params.raft_layers() > 1) { - Polygons base; - Polygons columns; - if (columns_base != nullptr) { - base = columns_base->polygons; - columns = base; - if (! interface_polygons.empty()) - // Trim the 1st layer columns with the inflated interface polygons. - columns = diff(columns, interface_polygons); - } - if (! interface_polygons.empty()) { - // Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface. - base = union_(base, interface_polygons); - } - // Do not add the raft contact layer, only add the raft layers below the contact layer. - // Insert the 1st layer. - { - SupportGeneratorLayer &new_layer = layer_storage.allocate((m_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.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) { - coordf_t print_z = raft_layers.back()->print_z; - SupportGeneratorLayer &new_layer = layer_storage.allocate(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.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) { - coordf_t print_z = raft_layers.back()->print_z; - SupportGeneratorLayer &new_layer = layer_storage.allocate(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.bottom_z = print_z; - new_layer.polygons = interface_polygons; - //FIXME misusing contact_polygons for support columns. - new_layer.contact_polygons = std::make_unique(columns); - } - } else { - if (columns_base != nullptr) { - // Expand the bases of the support columns in the 1st layer. - Polygons &raft = columns_base->polygons; - 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(m_object->layers().front()->lslices, (float)scale_(object.config().brim_object_gap.value), SUPPORT_SURFACES_OFFSET_PARAMETERS); - else - trimming = offset(m_object->layers().front()->lslices, (float)scale_(m_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()))); - float step = inflate_factor_1st_layer / nsteps; - for (int i = 0; i < nsteps; ++ i) - raft = diff(expand(raft, step), trimming); - } else - raft = diff(raft, trimming); - if (! interface_polygons.empty()) - columns_base->polygons = diff(columns_base->polygons, interface_polygons); - } - if (! brim.empty()) { - if (columns_base) - columns_base->polygons = diff(columns_base->polygons, brim); - if (contacts) - contacts->polygons = diff(contacts->polygons, brim); - if (interfaces) - interfaces->polygons = diff(interfaces->polygons, brim); - if (base_interfaces) - base_interfaces->polygons = diff(base_interfaces->polygons, brim); - } - } - - return raft_layers; -} - -// Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers. -std::pair PrintObjectSupportMaterial::generate_interface_layers( - const SupportGeneratorLayersPtr &bottom_contacts, - const SupportGeneratorLayersPtr &top_contacts, - SupportGeneratorLayersPtr &intermediate_layers, - SupportGeneratorLayerStorage &layer_storage) const -{ -// my $area_threshold = $self->interface_flow->scaled_spacing ** 2; - - std::pair base_and_interface_layers; - SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first; - SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second; - - // distinguish between interface and base interface layers - // Contact layer is considered an interface layer, therefore run the following block only if support_interface_top_layers > 1. - // Contact layer needs a base_interface layer, therefore run the following block if support_interface_top_layers > 0, has soluble support and extruders are different. - bool soluble_interface_non_soluble_base = - // Zero z-gap between the overhangs and the support interface. - m_slicing_params.soluble_interface && - // Interface extruder soluble. - m_object_config->support_interface_filament.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_interface_filament.value - 1) && - // Base extruder: Either "print with active extruder" not soluble. - (m_object_config->support_filament.value == 0 || ! m_print_config->filament_soluble.get_at(m_object_config->support_filament.value - 1)); - bool snug_supports = m_object_config->support_style.value == smsSnug; - // BBS: if support interface and support base do not use the same filament, add a base layer to improve their adhesion - bool differnt_support_interface_filament = m_object_config->support_interface_filament.value != m_object_config->support_filament.value; - int num_base_interface_layers_top = differnt_support_interface_filament ? 1 : 0; - int num_base_interface_layers_bottom = differnt_support_interface_filament ? 1 : 0; - int num_interface_layers_top = m_object_config->support_interface_top_layers + num_base_interface_layers_top; - int num_interface_layers_bottom = m_object_config->support_interface_bottom_layers + num_base_interface_layers_bottom; - if (num_interface_layers_bottom < 0) - num_interface_layers_bottom = num_interface_layers_top; - - if (! intermediate_layers.empty() && (num_interface_layers_top > 1 || num_interface_layers_bottom > 1)) { - // For all intermediate layers, collect top contact surfaces, which are not further than support_interface_top_layers. - BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start"; - // Since the intermediate layer index starts at zero the number of interface layer needs to be reduced by 1. - -- num_interface_layers_top; - -- num_interface_layers_bottom; - int num_interface_layers_only_top = num_interface_layers_top - num_base_interface_layers_top; - int num_interface_layers_only_bottom = num_interface_layers_bottom - num_base_interface_layers_bottom; - interface_layers.assign(intermediate_layers.size(), nullptr); - if (num_base_interface_layers_top || num_base_interface_layers_bottom) - base_interface_layers.assign(intermediate_layers.size(), nullptr); - auto smoothing_distance = m_support_params.support_material_interface_flow.scaled_spacing() * 1.5; - auto minimum_island_radius = m_support_params.support_material_interface_flow.scaled_spacing() / m_support_params.interface_density; - auto closing_distance = smoothing_distance; // scaled(m_object_config->support_closing_radius.value); - // Insert a new layer into base_interface_layers, if intersection with base exists. - auto insert_layer = [&layer_storage, snug_supports, closing_distance, smoothing_distance, minimum_island_radius]( - SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* { - assert(! bottom.empty() || ! top.empty()); - // Merge top into bottom, unite them with a safety offset. - append(bottom, std::move(top)); - // Merge top / bottom interfaces. For snug supports, merge using closing distance and regularize (close concave corners). - bottom = intersection( - snug_supports ? - smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) : - union_safety_offset(std::move(bottom)), - intermediate_layer.polygons); - if (! bottom.empty()) { - //FIXME Remove non-printable tiny islands, let them be printed using the base support. - //bottom = opening(std::move(bottom), minimum_island_radius); - if (! bottom.empty()) { - SupportGeneratorLayer &layer_new = layer_storage.allocate(type); - layer_new.polygons = std::move(bottom); - layer_new.print_z = intermediate_layer.print_z; - layer_new.bottom_z = intermediate_layer.bottom_z; - layer_new.height = intermediate_layer.height; - layer_new.bridging = intermediate_layer.bridging; - // Subtract the interface from the base regions. - intermediate_layer.polygons = diff(intermediate_layer.polygons, layer_new.polygons); - if (subtract) - // Trim the base interface layer with the interface layer. - layer_new.polygons = diff(std::move(layer_new.polygons), *subtract); - //FIXME filter layer_new.polygons islands by a minimum area? - // $interface_area = [ grep abs($_->area) >= $area_threshold, @$interface_area ]; - return &layer_new; - } - } - return nullptr; - }; - tbb::parallel_for(tbb::blocked_range(0, int(intermediate_layers.size())), - [&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer, - num_interface_layers_top, num_interface_layers_bottom, num_base_interface_layers_top, num_base_interface_layers_bottom, num_interface_layers_only_top, num_interface_layers_only_bottom, - snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range& range) { - // Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below - // this intermediate layer. - // Index of the first top contact layer intersecting the current intermediate layer. - auto idx_top_contact_first = -1; - // Index of the first bottom contact layer intersecting the current intermediate layer. - auto idx_bottom_contact_first = -1; - auto num_intermediate = int(intermediate_layers.size()); - for (int idx_intermediate_layer = range.begin(); idx_intermediate_layer < range.end(); ++ idx_intermediate_layer) { - SupportGeneratorLayer &intermediate_layer = *intermediate_layers[idx_intermediate_layer]; - Polygons polygons_top_contact_projected_interface; - Polygons polygons_top_contact_projected_base; - Polygons polygons_bottom_contact_projected_interface; - Polygons polygons_bottom_contact_projected_base; - if (num_interface_layers_top > 0) { - // Top Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces - coordf_t top_z = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_top - 1)]->print_z; - coordf_t top_inteface_z = std::numeric_limits::max(); - if (num_base_interface_layers_top > 0) - // Some top base interface layers will be generated. - top_inteface_z = num_interface_layers_only_top == 0 ? - // Only base interface layers to generate. - - std::numeric_limits::max() : - intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_only_top - 1)]->print_z; - // Move idx_top_contact_first up until above the current print_z. - idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const SupportGeneratorLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON - // Collect the top contact areas above this intermediate layer, below top_z. - for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(top_contacts.size()); ++ idx_top_contact) { - const SupportGeneratorLayer &top_contact_layer = *top_contacts[idx_top_contact]; - //FIXME maybe this adds one interface layer in excess? - if (top_contact_layer.bottom_z - EPSILON > top_z) - break; - polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, - // For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers. - // For grid supports, merging of support regions will be performed by the projection into grid. - snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons); - } - } - if (num_interface_layers_bottom > 0) { - // Bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces - coordf_t bottom_z = intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_bottom + 1)]->bottom_z; - coordf_t bottom_interface_z = - std::numeric_limits::max(); - if (num_base_interface_layers_bottom > 0) - // Some bottom base interface layers will be generated. - bottom_interface_z = num_interface_layers_only_bottom == 0 ? - // Only base interface layers to generate. - std::numeric_limits::max() : - intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_only_bottom)]->bottom_z; - // Move idx_bottom_contact_first up until touching bottom_z. - idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const SupportGeneratorLayer *layer){ return layer->print_z >= bottom_z - EPSILON; }); - // Collect the top contact areas above this intermediate layer, below top_z. - for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) { - const SupportGeneratorLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact]; - if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z) - break; - polygons_append(bottom_contact_layer.print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base, bottom_contact_layer.polygons); - } - } - SupportGeneratorLayer *interface_layer = nullptr; - if (! polygons_bottom_contact_projected_interface.empty() || ! polygons_top_contact_projected_interface.empty()) { - interface_layer = insert_layer( - intermediate_layer, polygons_bottom_contact_projected_interface, std::move(polygons_top_contact_projected_interface), nullptr, - polygons_top_contact_projected_interface.empty() ? SupporLayerType::BottomInterface : SupporLayerType::TopInterface); - interface_layers[idx_intermediate_layer] = interface_layer; - } - if (! polygons_bottom_contact_projected_base.empty() || ! polygons_top_contact_projected_base.empty()) - base_interface_layers[idx_intermediate_layer] = insert_layer( - intermediate_layer, polygons_bottom_contact_projected_base, std::move(polygons_top_contact_projected_base), - interface_layer ? &interface_layer->polygons : nullptr, SupporLayerType::Base); - } - }); - - // Compress contact_out, remove the nullptr items. - remove_nulls(interface_layers); - remove_nulls(base_interface_layers); - BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end"; - } - - return base_and_interface_layers; -} - -static inline void fill_expolygon_generate_paths( - ExtrusionEntitiesPtr &dst, - ExPolygon &&expolygon, - Fill *filler, - const FillParams &fill_params, - ExtrusionRole role, - const Flow &flow) -{ - Surface surface(stInternal, std::move(expolygon)); - Polylines polylines; - try { - polylines = filler->fill_surface(&surface, fill_params); - } catch (InfillFailedException &) { - } - extrusion_entities_append_paths( - dst, - std::move(polylines), - role, - flow.mm3_per_mm(), flow.width(), flow.height()); -} - -static inline void fill_expolygons_generate_paths( - ExtrusionEntitiesPtr &dst, - ExPolygons &&expolygons, - Fill *filler, - const FillParams &fill_params, - ExtrusionRole role, - const Flow &flow) -{ - for (ExPolygon &expoly : expolygons) - fill_expolygon_generate_paths(dst, std::move(expoly), filler, fill_params, role, flow); -} - -static inline void fill_expolygons_generate_paths( - ExtrusionEntitiesPtr &dst, - ExPolygons &&expolygons, - Fill *filler, - float density, - ExtrusionRole role, - const Flow &flow) -{ - FillParams fill_params; - fill_params.density = density; - fill_params.dont_adjust = true; - fill_expolygons_generate_paths(dst, std::move(expolygons), filler, fill_params, role, flow); -} - -static inline void fill_expolygons_with_sheath_generate_paths( - ExtrusionEntitiesPtr &dst, - const Polygons &polygons, - Fill *filler, - float density, - ExtrusionRole role, - const Flow &flow, - bool with_sheath, - bool no_sort) -{ - if (polygons.empty()) - return; - - if (! with_sheath) { - fill_expolygons_generate_paths(dst, closing_ex(polygons, float(SCALED_EPSILON)), filler, density, role, flow); - return; - } - - FillParams fill_params; - fill_params.density = density; - fill_params.dont_adjust = true; - - double spacing = flow.scaled_spacing(); - // Clip the sheath path to avoid the extruder to get exactly on the first point of the loop. - double clip_length = spacing * 0.15; - - for (ExPolygon &expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5*flow.scaled_width()))) { - // Don't reorder the skirt and its infills. - std::unique_ptr eec; - if (no_sort) { - eec = std::make_unique(); - eec->no_sort = true; - } - ExtrusionEntitiesPtr &out = no_sort ? eec->entities : dst; - // Draw the perimeters. - Polylines polylines; - polylines.reserve(expoly.holes.size() + 1); - for (size_t i = 0; i <= expoly.holes.size(); ++ i) { - Polyline pl(i == 0 ? expoly.contour.points : expoly.holes[i - 1].points); - pl.points.emplace_back(pl.points.front()); - pl.clip_end(clip_length); - polylines.emplace_back(std::move(pl)); - } - extrusion_entities_append_paths(out, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); - // Fill in the rest. - fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, role, flow); - if (no_sort && ! eec->empty()) - dst.emplace_back(eec.release()); - } -} - -// Support layers, partially processed. -struct MyLayerExtruded -{ - MyLayerExtruded& operator=(MyLayerExtruded &&rhs) { - this->layer = rhs.layer; - this->extrusions = std::move(rhs.extrusions); - m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude); - rhs.layer = nullptr; - return *this; - } - - bool empty() const { - return layer == nullptr || layer->polygons.empty(); - } - - void set_polygons_to_extrude(Polygons &&polygons) { - if (m_polygons_to_extrude == nullptr) - m_polygons_to_extrude = std::make_unique(std::move(polygons)); - else - *m_polygons_to_extrude = std::move(polygons); - } - Polygons& polygons_to_extrude() { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } - const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } - - bool could_merge(const MyLayerExtruded &other) const { - return ! this->empty() && ! other.empty() && - std::abs(this->layer->height - other.layer->height) < EPSILON && - this->layer->bridging == other.layer->bridging; - } - - // Merge regions, perform boolean union over the merged polygons. - void merge(MyLayerExtruded &&other) { - assert(this->could_merge(other)); - // 1) Merge the rest polygons to extrude, if there are any. - if (other.m_polygons_to_extrude != nullptr) { - if (m_polygons_to_extrude == nullptr) { - // This layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). - assert(this->extrusions.empty()); - m_polygons_to_extrude = std::make_unique(this->layer->polygons); - } - Slic3r::polygons_append(*m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude)); - *m_polygons_to_extrude = union_safety_offset(*m_polygons_to_extrude); - other.m_polygons_to_extrude.reset(); - } else if (m_polygons_to_extrude != nullptr) { - assert(other.m_polygons_to_extrude == nullptr); - // The other layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). - assert(other.extrusions.empty()); - Slic3r::polygons_append(*m_polygons_to_extrude, other.layer->polygons); - *m_polygons_to_extrude = union_safety_offset(*m_polygons_to_extrude); - } - // 2) Merge the extrusions. - this->extrusions.insert(this->extrusions.end(), other.extrusions.begin(), other.extrusions.end()); - other.extrusions.clear(); - // 3) Merge the infill polygons. - Slic3r::polygons_append(this->layer->polygons, std::move(other.layer->polygons)); - this->layer->polygons = union_safety_offset(this->layer->polygons); - other.layer->polygons.clear(); - } - - void polygons_append(Polygons &dst) const { - if (layer != NULL && ! layer->polygons.empty()) - Slic3r::polygons_append(dst, layer->polygons); - } - - // The source layer. It carries the height and extrusion type (bridging / non bridging, extrusion height). - SupportGeneratorLayer *layer { nullptr }; - // Collect extrusions. They will be exported sorted by the bottom height. - ExtrusionEntitiesPtr extrusions; - -private: - // In case the extrusions are non-empty, m_polygons_to_extrude may contain the rest areas yet to be filled by additional support. - // This is useful mainly for the loop interfaces, which are generated before the zig-zag infills. - std::unique_ptr m_polygons_to_extrude; -}; - -typedef std::vector MyLayerExtrudedPtrs; - -struct LoopInterfaceProcessor -{ - LoopInterfaceProcessor(coordf_t circle_r) : - n_contact_loops(0), - circle_radius(circle_r), - circle_distance(circle_r * 3.) - { - // Shape of the top contact area. - circle.points.reserve(6); - for (size_t i = 0; i < 6; ++ i) { - double angle = double(i) * M_PI / 3.; - circle.points.push_back(Point(circle_radius * cos(angle), circle_radius * sin(angle))); - } - } - - // Generate loop contacts at the top_contact_layer, - // trim the top_contact_layer->polygons with the areas covered by the loops. - void generate(MyLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const; - - int n_contact_loops; - coordf_t circle_radius; - coordf_t circle_distance; - Polygon circle; -}; - -void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const -{ - if (n_contact_loops == 0 || top_contact_layer.empty()) - return; - - Flow flow = interface_flow_src.with_height(top_contact_layer.layer->height); - - Polygons overhang_polygons; - if (top_contact_layer.layer->overhang_polygons != nullptr) - overhang_polygons = std::move(*top_contact_layer.layer->overhang_polygons); - - // Generate the outermost loop. - // Find centerline of the external loop (or any other kind of extrusions should the loop be skipped) - ExPolygons top_contact_expolygons = offset_ex(union_ex(top_contact_layer.layer->polygons), - 0.5f * flow.scaled_width()); - - // Grid size and bit shifts for quick and exact to/from grid coordinates manipulation. - coord_t circle_grid_resolution = 1; - coord_t circle_grid_powerof2 = 0; - { - // epsilon to account for rounding errors - coord_t circle_grid_resolution_non_powerof2 = coord_t(2. * circle_distance + 3.); - while (circle_grid_resolution < circle_grid_resolution_non_powerof2) { - circle_grid_resolution <<= 1; - ++ circle_grid_powerof2; - } - } - - struct PointAccessor { - const Point* operator()(const Point &pt) const { return &pt; } - }; - typedef ClosestPointInRadiusLookup ClosestPointLookupType; - - Polygons loops0; - { - // find centerline of the external loop of the contours - // Only consider the loops facing the overhang. - Polygons external_loops; - // Holes in the external loops. - Polygons circles; - Polygons overhang_with_margin = offset(union_ex(overhang_polygons), 0.5f * flow.scaled_width()); - for (ExPolygons::iterator it_contact_expoly = top_contact_expolygons.begin(); it_contact_expoly != top_contact_expolygons.end(); ++ it_contact_expoly) { - // Store the circle centers placed for an expolygon into a regular grid, hashed by the circle centers. - ClosestPointLookupType circle_centers_lookup(coord_t(circle_distance - SCALED_EPSILON)); - Points circle_centers; - Point center_last; - // For each contour of the expolygon, start with the outer contour, continue with the holes. - for (size_t i_contour = 0; i_contour <= it_contact_expoly->holes.size(); ++ i_contour) { - Polygon &contour = (i_contour == 0) ? it_contact_expoly->contour : it_contact_expoly->holes[i_contour - 1]; - const Point *seg_current_pt = nullptr; - coordf_t seg_current_t = 0.; - if (! intersection_pl(contour.split_at_first_point(), overhang_with_margin).empty()) { - // The contour is below the overhang at least to some extent. - //FIXME ideally one would place the circles below the overhang only. - // Walk around the contour and place circles so their centers are not closer than circle_distance from each other. - if (circle_centers.empty()) { - // Place the first circle. - seg_current_pt = &contour.points.front(); - seg_current_t = 0.; - center_last = *seg_current_pt; - circle_centers_lookup.insert(center_last); - circle_centers.push_back(center_last); - } - for (Points::const_iterator it = contour.points.begin() + 1; it != contour.points.end(); ++it) { - // Is it possible to place a circle on this segment? Is it not too close to any of the circles already placed on this contour? - const Point &p1 = *(it-1); - const Point &p2 = *it; - // Intersection of a ray (p1, p2) with a circle placed at center_last, with radius of circle_distance. - const Vec2d v_seg(coordf_t(p2(0)) - coordf_t(p1(0)), coordf_t(p2(1)) - coordf_t(p1(1))); - const Vec2d v_cntr(coordf_t(p1(0) - center_last(0)), coordf_t(p1(1) - center_last(1))); - coordf_t a = v_seg.squaredNorm(); - coordf_t b = 2. * v_seg.dot(v_cntr); - coordf_t c = v_cntr.squaredNorm() - circle_distance * circle_distance; - coordf_t disc = b * b - 4. * a * c; - if (disc > 0.) { - // The circle intersects a ray. Avoid the parts of the segment inside the circle. - coordf_t t1 = (-b - sqrt(disc)) / (2. * a); - coordf_t t2 = (-b + sqrt(disc)) / (2. * a); - coordf_t t0 = (seg_current_pt == &p1) ? seg_current_t : 0.; - // Take the lowest t in , excluding . - coordf_t t; - if (t0 <= t1) - t = t0; - else if (t2 <= 1.) - t = t2; - else { - // Try the following segment. - seg_current_pt = nullptr; - continue; - } - seg_current_pt = &p1; - seg_current_t = t; - center_last = Point(p1(0) + coord_t(v_seg(0) * t), p1(1) + coord_t(v_seg(1) * t)); - // It has been verified that the new point is far enough from center_last. - // Ensure, that it is far enough from all the centers. - std::pair circle_closest = circle_centers_lookup.find(center_last); - if (circle_closest.first != nullptr) { - -- it; - continue; - } - } else { - // All of the segment is outside the circle. Take the first point. - seg_current_pt = &p1; - seg_current_t = 0.; - center_last = p1; - } - // Place the first circle. - circle_centers_lookup.insert(center_last); - circle_centers.push_back(center_last); - } - external_loops.push_back(std::move(contour)); - for (const Point ¢er : circle_centers) { - circles.push_back(circle); - circles.back().translate(center); - } - } - } - } - // Apply a pattern to the external loops. - loops0 = diff(external_loops, circles); - } - - Polylines loop_lines; - { - // make more loops - Polygons loop_polygons = loops0; - for (int i = 1; i < n_contact_loops; ++ i) - polygons_append(loop_polygons, - opening( - loops0, - i * flow.scaled_spacing() + 0.5f * flow.scaled_spacing(), - 0.5f * flow.scaled_spacing())); - // Clip such loops to the side oriented towards the object. - // Collect split points, so they will be recognized after the clipping. - // At the split points the clipped pieces will be stitched back together. - loop_lines.reserve(loop_polygons.size()); - std::unordered_map map_split_points; - for (Polygons::const_iterator it = loop_polygons.begin(); it != loop_polygons.end(); ++ it) { - assert(map_split_points.find(it->first_point()) == map_split_points.end()); - map_split_points[it->first_point()] = -1; - loop_lines.push_back(it->split_at_first_point()); - } - loop_lines = intersection_pl(loop_lines, expand(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN))); - // Because a closed loop has been split to a line, loop_lines may contain continuous segments split to 2 pieces. - // Try to connect them. - for (int i_line = 0; i_line < int(loop_lines.size()); ++ i_line) { - Polyline &polyline = loop_lines[i_line]; - auto it = map_split_points.find(polyline.first_point()); - if (it != map_split_points.end()) { - // This is a stitching point. - // If this assert triggers, multiple source polygons likely intersected at this point. - assert(it->second != -2); - if (it->second < 0) { - // First occurence. - it->second = i_line; - } else { - // Second occurence. Join the lines. - Polyline &polyline_1st = loop_lines[it->second]; - assert(polyline_1st.first_point() == it->first || polyline_1st.last_point() == it->first); - if (polyline_1st.first_point() == it->first) - polyline_1st.reverse(); - polyline_1st.append(std::move(polyline)); - it->second = -2; - } - continue; - } - it = map_split_points.find(polyline.last_point()); - if (it != map_split_points.end()) { - // This is a stitching point. - // If this assert triggers, multiple source polygons likely intersected at this point. - assert(it->second != -2); - if (it->second < 0) { - // First occurence. - it->second = i_line; - } else { - // Second occurence. Join the lines. - Polyline &polyline_1st = loop_lines[it->second]; - assert(polyline_1st.first_point() == it->first || polyline_1st.last_point() == it->first); - if (polyline_1st.first_point() == it->first) - polyline_1st.reverse(); - polyline.reverse(); - polyline_1st.append(std::move(polyline)); - it->second = -2; - } - } - } - // Remove empty lines. - remove_degenerate(loop_lines); - } - - // add the contact infill area to the interface area - // note that growing loops by $circle_radius ensures no tiny - // extrusions are left inside the circles; however it creates - // a very large gap between loops and contact_infill_polygons, so maybe another - // solution should be found to achieve both goals - // Store the trimmed polygons into a separate polygon set, so the original infill area remains intact for - // "modulate by layer thickness". - top_contact_layer.set_polygons_to_extrude(diff(top_contact_layer.layer->polygons, offset(loop_lines, float(circle_radius * 1.1)))); - - // Transform loops into ExtrusionPath objects. - extrusion_entities_append_paths( - top_contact_layer.extrusions, - std::move(loop_lines), - erSupportMaterialInterface, flow.mm3_per_mm(), flow.width(), flow.height()); -} - -#ifdef SLIC3R_DEBUG -static std::string dbg_index_to_color(int idx) -{ - if (idx < 0) - return "yellow"; - idx = idx % 3; - switch (idx) { - case 0: return "red"; - case 1: return "green"; - default: return "blue"; - } -} -#endif /* SLIC3R_DEBUG */ - -// When extruding a bottom interface layer over an object, the bottom interface layer is extruded in a thin air, therefore -// it is being extruded with a bridging flow to not shrink excessively (the die swell effect). -// Tiny extrusions are better avoided and it is always better to anchor the thread to an existing support structure if possible. -// Therefore the bottom interface spots are expanded a bit. The expanded regions may overlap with another bottom interface layers, -// leading to over extrusion, where they overlap. The over extrusion is better avoided as it often makes the interface layers -// to stick too firmly to the object. -// -// Modulate thickness (increase bottom_z) of extrusions_in_out generated for this_layer -// if they overlap with overlapping_layers, whose print_z is above this_layer.bottom_z() and below this_layer.print_z. -void modulate_extrusion_by_overlapping_layers( - // Extrusions generated for this_layer. - ExtrusionEntitiesPtr &extrusions_in_out, - const SupportGeneratorLayer &this_layer, - // Multiple layers overlapping with this_layer, sorted bottom up. - const SupportGeneratorLayersPtr &overlapping_layers) -{ - size_t n_overlapping_layers = overlapping_layers.size(); - if (n_overlapping_layers == 0 || extrusions_in_out.empty()) - // The extrusions do not overlap with any other extrusion. - return; - - // Get the initial extrusion parameters. - ExtrusionPath *extrusion_path_template = dynamic_cast(extrusions_in_out.front()); - assert(extrusion_path_template != nullptr); - ExtrusionRole extrusion_role = extrusion_path_template->role(); - float extrusion_width = extrusion_path_template->width; - - struct ExtrusionPathFragment - { - ExtrusionPathFragment() : mm3_per_mm(-1), width(-1), height(-1) {}; - ExtrusionPathFragment(double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height) {}; - - Polylines polylines; - double mm3_per_mm; - float width; - float height; - }; - - // Split the extrusions by the overlapping layers, reduce their extrusion rate. - // The last path_fragment is from this_layer. - std::vector path_fragments( - n_overlapping_layers + 1, - ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height)); - // Don't use it, it will be released. - extrusion_path_template = nullptr; - -#ifdef SLIC3R_DEBUG - static int iRun = 0; - ++ iRun; - BoundingBox bbox; - for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { - const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; - bbox.merge(get_extents(overlapping_layer.polygons)); - } - for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) { - ExtrusionPath *path = dynamic_cast(*it); - assert(path != nullptr); - bbox.merge(get_extents(path->polyline)); - } - SVG svg(debug_out_path("support-fragments-%d-%lf.svg", iRun, this_layer.print_z).c_str(), bbox); - const float transparency = 0.5f; - // Filled polygons for the overlapping regions. - svg.draw(union_ex(this_layer.polygons), dbg_index_to_color(-1), transparency); - for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { - const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; - svg.draw(union_ex(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), transparency); - } - // Contours of the overlapping regions. - svg.draw(to_polylines(this_layer.polygons), dbg_index_to_color(-1), scale_(0.2)); - for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) { - const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; - svg.draw(to_polylines(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), scale_(0.1)); - } - // Fill extrusion, the source. - for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) { - ExtrusionPath *path = dynamic_cast(*it); - std::string color_name; - switch ((it - extrusions_in_out.begin()) % 9) { - case 0: color_name = "magenta"; break; - case 1: color_name = "deepskyblue"; break; - case 2: color_name = "coral"; break; - case 3: color_name = "goldenrod"; break; - case 4: color_name = "orange"; break; - case 5: color_name = "olivedrab"; break; - case 6: color_name = "blueviolet"; break; - case 7: color_name = "brown"; break; - default: color_name = "orchid"; break; - } - svg.draw(path->polyline, color_name, scale_(0.2)); - } -#endif /* SLIC3R_DEBUG */ - - // End points of the original paths. - std::vector> path_ends; - // Collect the paths of this_layer. - { - Polylines &polylines = path_fragments.back().polylines; - for (ExtrusionEntity *ee : extrusions_in_out) { - ExtrusionPath *path = dynamic_cast(ee); - assert(path != nullptr); - polylines.emplace_back(Polyline(std::move(path->polyline))); - path_ends.emplace_back(std::pair(polylines.back().points.front(), polylines.back().points.back())); - } - } - // Destroy the original extrusion paths, their polylines were moved to path_fragments already. - // This will be the destination for the new paths. - extrusions_in_out.clear(); - - // Fragment the path segments by overlapping layers. The overlapping layers are sorted by an increasing print_z. - // Trim by the highest overlapping layer first. - for (int i_overlapping_layer = int(n_overlapping_layers) - 1; i_overlapping_layer >= 0; -- i_overlapping_layer) { - const SupportGeneratorLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; - ExtrusionPathFragment &frag = path_fragments[i_overlapping_layer]; - Polygons polygons_trimming = offset(union_ex(overlapping_layer.polygons), float(scale_(0.5*extrusion_width))); - frag.polylines = intersection_pl(path_fragments.back().polylines, polygons_trimming); - path_fragments.back().polylines = diff_pl(path_fragments.back().polylines, polygons_trimming); - // Adjust the extrusion parameters for a reduced layer height and a non-bridging flow (nozzle_dmr = -1, does not matter). - assert(this_layer.print_z > overlapping_layer.print_z); - frag.height = float(this_layer.print_z - overlapping_layer.print_z); - frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f).mm3_per_mm(); -#ifdef SLIC3R_DEBUG - svg.draw(frag.polylines, dbg_index_to_color(i_overlapping_layer), scale_(0.1)); -#endif /* SLIC3R_DEBUG */ - } - -#ifdef SLIC3R_DEBUG - svg.draw(path_fragments.back().polylines, dbg_index_to_color(-1), scale_(0.1)); - svg.Close(); -#endif /* SLIC3R_DEBUG */ - - // Now chain the split segments using hashing and a nearly exact match, maintaining the order of segments. - // Create a single ExtrusionPath or ExtrusionEntityCollection per source ExtrusionPath. - // Map of fragment start/end points to a pair of - // Because a non-exact matching is used for the end points, a multi-map is used. - // As the clipper library may reverse the order of some clipped paths, store both ends into the map. - struct ExtrusionPathFragmentEnd - { - ExtrusionPathFragmentEnd(size_t alayer_idx, size_t apolyline_idx, bool ais_start) : - layer_idx(alayer_idx), polyline_idx(apolyline_idx), is_start(ais_start) {} - size_t layer_idx; - size_t polyline_idx; - bool is_start; - }; - class ExtrusionPathFragmentEndPointAccessor { - public: - ExtrusionPathFragmentEndPointAccessor(const std::vector &path_fragments) : m_path_fragments(path_fragments) {} - // Return an end point of a fragment, or nullptr if the fragment has been consumed already. - const Point* operator()(const ExtrusionPathFragmentEnd &fragment_end) const { - const Polyline &polyline = m_path_fragments[fragment_end.layer_idx].polylines[fragment_end.polyline_idx]; - return polyline.points.empty() ? nullptr : - (fragment_end.is_start ? &polyline.points.front() : &polyline.points.back()); - } - private: - ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) { - return *this; - } - - const std::vector &m_path_fragments; - }; - const coord_t search_radius = 7; - ClosestPointInRadiusLookup map_fragment_starts( - search_radius, ExtrusionPathFragmentEndPointAccessor(path_fragments)); - for (size_t i_overlapping_layer = 0; i_overlapping_layer <= n_overlapping_layers; ++ i_overlapping_layer) { - const Polylines &polylines = path_fragments[i_overlapping_layer].polylines; - for (size_t i_polyline = 0; i_polyline < polylines.size(); ++ i_polyline) { - // Map a starting point of a polyline to a pair of - if (polylines[i_polyline].points.size() >= 2) { - map_fragment_starts.insert(ExtrusionPathFragmentEnd(i_overlapping_layer, i_polyline, true)); - map_fragment_starts.insert(ExtrusionPathFragmentEnd(i_overlapping_layer, i_polyline, false)); - } - } - } - - // For each source path: - for (size_t i_path = 0; i_path < path_ends.size(); ++ i_path) { - const Point &pt_start = path_ends[i_path].first; - const Point &pt_end = path_ends[i_path].second; - Point pt_current = pt_start; - // Find a chain of fragments with the original / reduced print height. - ExtrusionMultiPath multipath; - for (;;) { - // Find a closest end point to pt_current. - std::pair end_and_dist2 = map_fragment_starts.find(pt_current); - // There may be a bug in Clipper flipping the order of two last points in a fragment? - // assert(end_and_dist2.first != nullptr); - assert(end_and_dist2.first == nullptr || end_and_dist2.second < search_radius * search_radius); - if (end_and_dist2.first == nullptr) { - // New fragment connecting to pt_current was not found. - // Verify that the last point found is close to the original end point of the unfragmented path. - //const double d2 = (pt_end - pt_current).cast.squaredNorm(); - //assert(d2 < coordf_t(search_radius * search_radius)); - // End of the path. - break; - } - const ExtrusionPathFragmentEnd &fragment_end_min = *end_and_dist2.first; - // Fragment to consume. - ExtrusionPathFragment &frag = path_fragments[fragment_end_min.layer_idx]; - Polyline &frag_polyline = frag.polylines[fragment_end_min.polyline_idx]; - // Path to append the fragment to. - ExtrusionPath *path = multipath.paths.empty() ? nullptr : &multipath.paths.back(); - if (path != nullptr) { - // Verify whether the path is compatible with the current fragment. - assert(this_layer.layer_type == SupporLayerType::BottomContact || path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm); - if (path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm) { - path = nullptr; - } - // Merging with the previous path. This can only happen if the current layer was reduced by a base layer, which was split into a base and interface layer. - } - if (path == nullptr) { - // Allocate a new path. - multipath.paths.push_back(ExtrusionPath(extrusion_role, frag.mm3_per_mm, frag.width, frag.height)); - path = &multipath.paths.back(); - } - // The Clipper library may flip the order of the clipped polylines arbitrarily. - // Reverse the source polyline, if connecting to the end. - if (! fragment_end_min.is_start) - frag_polyline.reverse(); - // Enforce exact overlap of the end points of successive fragments. - assert(frag_polyline.points.front() == pt_current); - frag_polyline.points.front() = pt_current; - // Don't repeat the first point. - if (! path->polyline.points.empty()) - path->polyline.points.pop_back(); - // Consume the fragment's polyline, remove it from the input fragments, so it will be ignored the next time. - path->polyline.append(std::move(frag_polyline)); - frag_polyline.points.clear(); - pt_current = path->polyline.points.back(); - if (pt_current == pt_end) { - // End of the path. - break; - } - } - if (!multipath.paths.empty()) { - if (multipath.paths.size() == 1) { - // This path was not fragmented. - extrusions_in_out.push_back(new ExtrusionPath(std::move(multipath.paths.front()))); - } else { - // This path was fragmented. Copy the collection as a whole object, so the order inside the collection will not be changed - // during the chaining of extrusions_in_out. - extrusions_in_out.push_back(new ExtrusionMultiPath(std::move(multipath))); - } - } - } - // If there are any non-consumed fragments, add them separately. - //FIXME this shall not happen, if the Clipper works as expected and all paths split to fragments could be re-connected. - for (auto it_fragment = path_fragments.begin(); it_fragment != path_fragments.end(); ++ it_fragment) - 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 PrintObjectSupportMaterial::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 -{ - // loop_interface_processor with a given circle radius. - LoopInterfaceProcessor loop_interface_processor(1.5 * m_support_params.support_material_interface_flow.scaled_width()); - loop_interface_processor.n_contact_loops = this->has_contact_loops() ? 1 : 0; - - std::vector angles { m_support_params.base_angle }; - if (m_object_config->support_base_pattern == smpRectilinearGrid) - angles.push_back(m_support_params.interface_angle); - - BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); - -// const coordf_t link_max_length_factor = 3.; - const coordf_t link_max_length_factor = 0.; - - float raft_angle_1st_layer = 0.f; - float raft_angle_base = 0.f; - float raft_angle_interface = 0.f; - if (m_slicing_params.base_raft_layers > 1) { - // There are all raft layer types (1st layer, base, interface & contact layers) available. - raft_angle_1st_layer = m_support_params.interface_angle; - raft_angle_base = m_support_params.base_angle; - raft_angle_interface = m_support_params.interface_angle; - } else if (m_slicing_params.base_raft_layers == 1 || m_slicing_params.interface_raft_layers > 1) { - // 1st layer, interface & contact layers available. - raft_angle_1st_layer = m_support_params.base_angle; - if (this->has_support()) - // Print 1st layer at 45 degrees from both the interface and base angles as both can land on the 1st layer. - raft_angle_1st_layer += 0.7854f; - raft_angle_interface = m_support_params.interface_angle; - } else if (m_slicing_params.interface_raft_layers == 1) { - // Only the contact raft layer is non-empty, which will be printed as the 1st layer. - assert(m_slicing_params.base_raft_layers == 0); - assert(m_slicing_params.interface_raft_layers == 1); - assert(m_slicing_params.raft_layers() == 1 && raft_layers.size() == 0); - } else { - // No raft. - assert(m_slicing_params.base_raft_layers == 0); - assert(m_slicing_params.interface_raft_layers == 0); - assert(m_slicing_params.raft_layers() == 0 && raft_layers.size() == 0); - } - - // Insert the raft base layers. - size_t n_raft_layers = size_t(std::max(0, int(m_slicing_params.raft_layers()) - 1)); - tbb::parallel_for(tbb::blocked_range(0, n_raft_layers), - [this, &support_layers, &raft_layers, - &bbox_object, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor] - (const tbb::blocked_range& range) { - for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) - { - assert(support_layer_id < raft_layers.size()); - SupportLayer &support_layer = *support_layers[support_layer_id]; - assert(support_layer.support_fills.entities.empty()); - SupportGeneratorLayer &raft_layer = *raft_layers[support_layer_id]; - - std::unique_ptr filler_interface = std::unique_ptr(Fill::new_from_type(m_support_params.interface_fill_pattern)); - std::unique_ptr filler_support = std::unique_ptr(Fill::new_from_type(m_support_params.base_fill_pattern)); - filler_interface->set_bounding_box(bbox_object); - filler_support->set_bounding_box(bbox_object); - - // Print the support base below the support columns, or the support base for the support columns plus the contacts. - if (support_layer_id > 0) { - const Polygons &to_infill_polygons = (support_layer_id < m_slicing_params.base_raft_layers) ? - raft_layer.polygons : - //FIXME misusing contact_polygons for support columns. - ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); - if (! to_infill_polygons.empty()) { - assert(! raft_layer.bridging); - Flow flow(float(m_support_params.support_material_flow.width()), float(raft_layer.height), m_support_params.support_material_flow.nozzle_diameter()); - Fill * filler = filler_support.get(); - filler->angle = raft_angle_base; - filler->spacing = m_support_params.support_material_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / m_support_params.support_density)); - fill_expolygons_with_sheath_generate_paths( - // Destination - support_layer.support_fills.entities, - // Regions to fill - to_infill_polygons, - // Filler and its parameters - filler, float(m_support_params.support_density), - // Extrusion parameters - erSupportMaterial, flow, - m_support_params.with_sheath, false); - } - } - - Fill *filler = filler_interface.get(); - Flow flow = m_support_params.first_layer_flow; - float density = 0.f; - if (support_layer_id == 0) { - // Base flange. - filler->angle = raft_angle_1st_layer; - filler->spacing = m_support_params.first_layer_flow.spacing(); - density = float(m_object_config->raft_first_layer_density.value * 0.01); - } else if (support_layer_id >= m_slicing_params.base_raft_layers) { - filler->angle = raft_angle_interface; - // We don't use $base_flow->spacing because we need a constant spacing - // value that guarantees that all layers are correctly aligned. - filler->spacing = m_support_params.support_material_flow.spacing(); - assert(! raft_layer.bridging); - flow = Flow(float(m_support_params.support_material_interface_flow.width()), float(raft_layer.height), m_support_params.support_material_flow.nozzle_diameter()); - density = float(m_support_params.interface_density); - } else - continue; - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); - fill_expolygons_with_sheath_generate_paths( - // Destination - support_layer.support_fills.entities, - // Regions to fill - raft_layer.polygons, - // Filler and its parameters - filler, density, - // Extrusion parameters - (support_layer_id < m_slicing_params.base_raft_layers) ? erSupportMaterial : erSupportMaterialInterface, flow, - // sheath at first layer - support_layer_id == 0, support_layer_id == 0); - } - }); - - struct LayerCacheItem { - LayerCacheItem(MyLayerExtruded *layer_extruded = nullptr) : layer_extruded(layer_extruded) {} - MyLayerExtruded *layer_extruded; - std::vector overlapping; - }; - struct LayerCache { - MyLayerExtruded bottom_contact_layer; - MyLayerExtruded top_contact_layer; - MyLayerExtruded base_layer; - MyLayerExtruded interface_layer; - MyLayerExtruded base_interface_layer; - boost::container::static_vector nonempty; - - void add_nonempty_and_sort() { - for (MyLayerExtruded *item : { &bottom_contact_layer, &top_contact_layer, &interface_layer, &base_interface_layer, &base_layer }) - if (! item->empty()) - this->nonempty.emplace_back(item); - // Sort the layers with the same print_z coordinate by their heights, thickest first. - std::stable_sort(this->nonempty.begin(), this->nonempty.end(), [](const LayerCacheItem &lc1, const LayerCacheItem &lc2) { return lc1.layer_extruded->layer->height > lc2.layer_extruded->layer->height; }); - } - }; - std::vector layer_caches(support_layers.size()); - - tbb::parallel_for(tbb::blocked_range(n_raft_layers, support_layers.size()), - [this, &support_layers, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &base_interface_layers, &layer_caches, &loop_interface_processor, - &bbox_object, &angles, link_max_length_factor] - (const tbb::blocked_range& range) { - // Indices of the 1st layer in their respective container at the support layer height. - size_t idx_layer_bottom_contact = size_t(-1); - size_t idx_layer_top_contact = size_t(-1); - size_t idx_layer_intermediate = size_t(-1); - size_t idx_layer_interface = size_t(-1); - size_t idx_layer_base_interface = size_t(-1); - // BBS - const auto fill_type_first_layer = ipConcentric; - auto filler_interface = std::unique_ptr(Fill::new_from_type(m_support_params.contact_fill_pattern)); - // Filler for the 1st layer interface, if different from filler_interface. - auto filler_first_layer_ptr = std::unique_ptr(range.begin() == 0 && m_support_params.contact_fill_pattern != fill_type_first_layer ? Fill::new_from_type(fill_type_first_layer) : nullptr); - // Pointer to the 1st layer interface filler. - auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get(); - // Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer). - auto filler_base_interface = std::unique_ptr(base_interface_layers.empty() ? nullptr : - Fill::new_from_type(m_support_params.interface_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : ipSupportBase)); - auto filler_support = std::unique_ptr(Fill::new_from_type(m_support_params.base_fill_pattern)); - filler_interface->set_bounding_box(bbox_object); - if (filler_first_layer_ptr) - filler_first_layer_ptr->set_bounding_box(bbox_object); - if (filler_base_interface) - filler_base_interface->set_bounding_box(bbox_object); - filler_support->set_bounding_box(bbox_object); - for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) - { - SupportLayer &support_layer = *support_layers[support_layer_id]; - LayerCache &layer_cache = layer_caches[support_layer_id]; - float interface_angle_delta = m_object_config->support_style.value == smsSnug ? - (support_layer.interface_id() & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.) : - 0; - - // Find polygons with the same print_z. - MyLayerExtruded &bottom_contact_layer = layer_cache.bottom_contact_layer; - MyLayerExtruded &top_contact_layer = layer_cache.top_contact_layer; - MyLayerExtruded &base_layer = layer_cache.base_layer; - MyLayerExtruded &interface_layer = layer_cache.interface_layer; - MyLayerExtruded &base_interface_layer = layer_cache.base_interface_layer; - // Increment the layer indices to find a layer at support_layer.print_z. - { - auto fun = [&support_layer](const SupportGeneratorLayer *l){ return l->print_z >= support_layer.print_z - EPSILON; }; - idx_layer_bottom_contact = idx_higher_or_equal(bottom_contacts, idx_layer_bottom_contact, fun); - idx_layer_top_contact = idx_higher_or_equal(top_contacts, idx_layer_top_contact, fun); - idx_layer_intermediate = idx_higher_or_equal(intermediate_layers, idx_layer_intermediate, fun); - idx_layer_interface = idx_higher_or_equal(interface_layers, idx_layer_interface, fun); - idx_layer_base_interface = idx_higher_or_equal(base_interface_layers, idx_layer_base_interface,fun); - } - // Copy polygons from the layers. - if (idx_layer_bottom_contact < bottom_contacts.size() && bottom_contacts[idx_layer_bottom_contact]->print_z < support_layer.print_z + EPSILON) - bottom_contact_layer.layer = bottom_contacts[idx_layer_bottom_contact]; - if (idx_layer_top_contact < top_contacts.size() && top_contacts[idx_layer_top_contact]->print_z < support_layer.print_z + EPSILON) - top_contact_layer.layer = top_contacts[idx_layer_top_contact]; - if (idx_layer_interface < interface_layers.size() && interface_layers[idx_layer_interface]->print_z < support_layer.print_z + EPSILON) - interface_layer.layer = interface_layers[idx_layer_interface]; - if (idx_layer_base_interface < base_interface_layers.size() && base_interface_layers[idx_layer_base_interface]->print_z < support_layer.print_z + EPSILON) - base_interface_layer.layer = base_interface_layers[idx_layer_base_interface]; - if (idx_layer_intermediate < intermediate_layers.size() && intermediate_layers[idx_layer_intermediate]->print_z < support_layer.print_z + EPSILON) - base_layer.layer = intermediate_layers[idx_layer_intermediate]; - - if (m_object_config->support_interface_top_layers == 0) { - // If no top interface layers were requested, we treat the contact layer exactly as a generic base layer. - if (m_support_params.can_merge_support_regions) { - if (base_layer.could_merge(top_contact_layer)) - base_layer.merge(std::move(top_contact_layer)); - else if (base_layer.empty()) - base_layer = std::move(top_contact_layer); - } - } else { - loop_interface_processor.generate(top_contact_layer, m_support_params.support_material_interface_flow); - // If no loops are allowed, we treat the contact layer exactly as a generic interface layer. - // Merge interface_layer into top_contact_layer, as the top_contact_layer is not synchronized and therefore it will be used - // to trim other layers. - if (top_contact_layer.could_merge(interface_layer)) - top_contact_layer.merge(std::move(interface_layer)); - } - if ((m_object_config->support_interface_top_layers == 0 || m_object_config->support_interface_bottom_layers == 0) && m_support_params.can_merge_support_regions) { - if (base_layer.could_merge(bottom_contact_layer)) - base_layer.merge(std::move(bottom_contact_layer)); - else if (base_layer.empty() && ! bottom_contact_layer.empty() && ! bottom_contact_layer.layer->bridging) - base_layer = std::move(bottom_contact_layer); - } else if (bottom_contact_layer.could_merge(top_contact_layer)) - top_contact_layer.merge(std::move(bottom_contact_layer)); - else if (bottom_contact_layer.could_merge(interface_layer)) - bottom_contact_layer.merge(std::move(interface_layer)); - -#if 0 - if ( ! interface_layer.empty() && ! base_layer.empty()) { - // turn base support into interface when it's contained in our holes - // (this way we get wider interface anchoring) - //FIXME The intention of the code below is unclear. One likely wanted to just merge small islands of base layers filling in the holes - // inside interface layers, but the code below fills just too much, see GH #4570 - Polygons islands = top_level_islands(interface_layer.layer->polygons); - polygons_append(interface_layer.layer->polygons, intersection(base_layer.layer->polygons, islands)); - base_layer.layer->polygons = diff(base_layer.layer->polygons, islands); - } -#endif - - // Calculate top interface angle - float angle_of_biggest_bridge = -1.f; - do - { - // Currently only works when thick_bridges is off - if (m_object->config().thick_bridges) - break; - - coordf_t object_layer_bottom_z = support_layer.print_z + m_slicing_params.gap_support_object; - const Layer* object_layer = m_object->get_layer_at_bottomz(object_layer_bottom_z, 10.0 * EPSILON); - if (object_layer == nullptr) - break; - - if (object_layer != nullptr) { - float biggest_bridge_area = 0.f; - const Polygons& top_contact_polys = top_contact_layer.polygons_to_extrude(); - for (auto layerm : object_layer->regions()) { - for (auto bridge_surface : layerm->fill_surfaces.filter_by_type(stBottomBridge)) { - float bs_area = bridge_surface->area(); - if (bs_area <= biggest_bridge_area || bridge_surface->bridge_angle < 0.f) - continue; - - angle_of_biggest_bridge = bridge_surface->bridge_angle; - biggest_bridge_area = bs_area; - } - } - } - } while (0); - - auto calc_included_angle_degree = [](int degree_a, int degree_b) { - int iad = std::abs(degree_b - degree_a); - return std::min(iad, 180 - iad); - }; - - // Top and bottom contacts, interface layers. - for (size_t i = 0; i < 3; ++ i) { - MyLayerExtruded &layer_ex = (i == 0) ? top_contact_layer : (i == 1 ? bottom_contact_layer : interface_layer); - if (layer_ex.empty() || layer_ex.polygons_to_extrude().empty()) - continue; - bool interface_as_base = m_object_config->support_interface_top_layers.value == 0 || - (m_object_config->support_interface_bottom_layers == 0 && &layer_ex == &bottom_contact_layer); - //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore - // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) - Flow interface_flow; - if (layer_ex.layer->bridging) - interface_flow = Flow::bridging_flow(layer_ex.layer->height, m_support_params.support_material_bottom_interface_flow.nozzle_diameter()); - else if (layer_ex.layer->bottom_z < EPSILON) { - interface_flow = m_support_params.first_layer_flow; - }else - interface_flow = (interface_as_base ? &m_support_params.support_material_flow : &m_support_params.support_material_interface_flow)->with_height(float(layer_ex.layer->height)); - filler_interface->angle = interface_as_base ? - // If zero interface layers are configured, use the same angle as for the base layers. - angles[support_layer_id % angles.size()] : - // Use interface angle for the interface layers. - m_support_params.interface_angle + interface_angle_delta; - - // BBS - bool can_adjust_top_interface_angle = (m_object_config->support_interface_top_layers.value > 1 && &layer_ex == &top_contact_layer); - if (can_adjust_top_interface_angle && angle_of_biggest_bridge >= 0.f) { - int bridge_degree = (int)Geometry::rad2deg(angle_of_biggest_bridge); - int support_intf_degree = (int)Geometry::rad2deg(filler_interface->angle); - int max_included_degree = 0; - int step = 90; - for (int add_on_degree = 0; add_on_degree < 180; add_on_degree += step) { - int degree_to_try = support_intf_degree + add_on_degree; - int included_degree = calc_included_angle_degree(bridge_degree, degree_to_try); - if (included_degree > max_included_degree) { - max_included_degree = included_degree; - filler_interface->angle = Geometry::deg2rad((float)degree_to_try); - } - } - } - double density = interface_as_base ? m_support_params.support_density : m_support_params.interface_density; - filler_interface->spacing = interface_as_base ? m_support_params.support_material_flow.spacing() : m_support_params.support_material_interface_flow.spacing(); - filler_interface->link_max_length = coord_t(scale_(filler_interface->spacing * link_max_length_factor / density)); - // BBS support more interface patterns - FillParams fill_params; - fill_params.density = density; - fill_params.dont_adjust = true; - if (m_object_config->support_interface_pattern == smipGrid) { - filler_interface->angle = Geometry::deg2rad(m_support_params.base_angle); - fill_params.dont_sort = true; - } - if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) - filler_interface->layer_id = support_layer.interface_id(); - fill_expolygons_generate_paths( - // Destination - layer_ex.extrusions, - // Regions to fill - union_safety_offset_ex(layer_ex.polygons_to_extrude()), - // Filler and its parameters - filler_interface.get(), fill_params, - // Extrusion parameters - interface_as_base ? erSupportMaterial : erSupportMaterialInterface, interface_flow); - } - - // Base interface layers under soluble interfaces - if ( ! base_interface_layer.empty() && ! base_interface_layer.polygons_to_extrude().empty()) { - Fill *filler = filler_base_interface.get(); - //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore - // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) - assert(! base_interface_layer.layer->bridging); - Flow interface_flow = m_support_params.support_material_flow.with_height(float(base_interface_layer.layer->height)); - filler->angle = m_support_params.interface_angle + interface_angle_delta; - filler->spacing = m_support_params.support_material_interface_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / m_support_params.interface_density)); - fill_expolygons_generate_paths( - // Destination - base_interface_layer.extrusions, - //base_layer_interface.extrusions, - // Regions to fill - union_safety_offset_ex(base_interface_layer.polygons_to_extrude()), - // Filler and its parameters - filler, float(m_support_params.interface_density), - // Extrusion parameters - erSupportMaterial, interface_flow); - } - - // Base support or flange. - if (! base_layer.empty() && ! base_layer.polygons_to_extrude().empty()) { - Fill *filler = filler_support.get(); - filler->angle = angles[support_layer_id % angles.size()]; - // We don't use $base_flow->spacing because we need a constant spacing - // value that guarantees that all layers are correctly aligned. - assert(! base_layer.layer->bridging); - auto flow = m_support_params.support_material_flow.with_height(float(base_layer.layer->height)); - filler->spacing = m_support_params.support_material_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / m_support_params.support_density)); - float density = float(m_support_params.support_density); - bool sheath = m_support_params.with_sheath; - bool no_sort = false; - if (base_layer.layer->bottom_z < EPSILON) { - // Base flange (the 1st layer). - filler = filler_first_layer; - // BBS: the 1st layer use the same fill direction as other layers(in rectilinear) to avoid - // that 2nd layer detaches from the 1st layer. - //filler->angle = Geometry::deg2rad(float(m_object_config->support_angle.value + 90.)); - density = float(m_object_config->raft_first_layer_density.value * 0.01); - flow = m_support_params.first_layer_flow; - // use the proper spacing for first layer as we don't need to align - // its pattern to the other layers - //FIXME When paralellizing, each thread shall have its own copy of the fillers. - filler->spacing = flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); - sheath = true; - no_sort = true; - } - fill_expolygons_with_sheath_generate_paths( - // Destination - base_layer.extrusions, - // Regions to fill - base_layer.polygons_to_extrude(), - // Filler and its parameters - filler, density, - // Extrusion parameters - erSupportMaterial, flow, - sheath, no_sort); - - } - - // Merge base_interface_layers to base_layers to avoid unneccessary retractions - if (! base_layer.empty() && ! base_interface_layer.empty() && ! base_layer.polygons_to_extrude().empty() && ! base_interface_layer.polygons_to_extrude().empty() && - base_layer.could_merge(base_interface_layer)) - base_layer.merge(std::move(base_interface_layer)); - - layer_cache.add_nonempty_and_sort(); - - // Collect the support areas with this print_z into islands, as there is no need - // for retraction over these islands. - Polygons polys; - // Collect the extrusions, sorted by the bottom extrusion height. - for (LayerCacheItem &layer_cache_item : layer_cache.nonempty) { - // Collect islands to polys. - layer_cache_item.layer_extruded->polygons_append(polys); - // The print_z of the top contact surfaces and bottom_z of the bottom contact surfaces are "free" - // in a sense that they are not synchronized with other support layers. As the top and bottom contact surfaces - // are inflated to achieve a better anchoring, it may happen, that these surfaces will at least partially - // overlap in Z with another support layers, leading to over-extrusion. - // Mitigate the over-extrusion by modulating the extrusion rate over these regions. - // The print head will follow the same print_z, but the layer thickness will be reduced - // where it overlaps with another support layer. - //FIXME When printing a briging path, what is an equivalent height of the squished extrudate of the same width? - // Collect overlapping top/bottom surfaces. - layer_cache_item.overlapping.reserve(20); - coordf_t bottom_z = layer_cache_item.layer_extruded->layer->bottom_print_z() + EPSILON; - auto add_overlapping = [&layer_cache_item, bottom_z](const SupportGeneratorLayersPtr &layers, size_t idx_top) { - for (int i = int(idx_top) - 1; i >= 0 && layers[i]->print_z > bottom_z; -- i) - layer_cache_item.overlapping.push_back(layers[i]); - }; - add_overlapping(top_contacts, idx_layer_top_contact); - if (layer_cache_item.layer_extruded->layer->layer_type == SupporLayerType::BottomContact) { - // Bottom contact layer may overlap with a base layer, which may be changed to interface layer. - add_overlapping(intermediate_layers, idx_layer_intermediate); - add_overlapping(interface_layers, idx_layer_interface); - add_overlapping(base_interface_layers, idx_layer_base_interface); - } - // Order the layers by lexicographically by an increasing print_z and a decreasing layer height. - std::stable_sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); - } - if (! polys.empty()) - expolygons_append(support_layer.support_islands, union_ex(polys)); - } // for each support_layer_id - }); - - // Now modulate the support layer height in parallel. - tbb::parallel_for(tbb::blocked_range(n_raft_layers, support_layers.size()), - [&support_layers, &layer_caches] - (const tbb::blocked_range& range) { - for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { - SupportLayer &support_layer = *support_layers[support_layer_id]; - LayerCache &layer_cache = layer_caches[support_layer_id]; - // For all extrusion types at this print_z, ordered by decreasing layer height: - for (LayerCacheItem &layer_cache_item : layer_cache.nonempty) { - // Trim the extrusion height from the bottom by the overlapping layers. - modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping); - support_layer.support_fills.append(std::move(layer_cache_item.layer_extruded->extrusions)); - } - } - }); - -#ifndef NDEBUG - struct Test { - static bool verify_nonempty(const ExtrusionEntityCollection *collection) { - for (const ExtrusionEntity *ee : collection->entities) { - if (const ExtrusionPath *path = dynamic_cast(ee)) - assert(! path->empty()); - else if (const ExtrusionMultiPath *multipath = dynamic_cast(ee)) - assert(! multipath->empty()); - else if (const ExtrusionEntityCollection *eecol = dynamic_cast(ee)) { - assert(! eecol->empty()); - return verify_nonempty(eecol); - } else - assert(false); - } - return true; - } - }; - for (const SupportLayer *support_layer : support_layers) - assert(Test::verify_nonempty(&support_layer->support_fills)); -#endif // NDEBUG -} - /* void PrintObjectSupportMaterial::clip_by_pillars( const PrintObject &object, diff --git a/src/libslic3r/Support/SupportMaterial.hpp b/src/libslic3r/Support/SupportMaterial.hpp index 809f9b1611..e489c2374a 100644 --- a/src/libslic3r/Support/SupportMaterial.hpp +++ b/src/libslic3r/Support/SupportMaterial.hpp @@ -6,6 +6,7 @@ #include "Slicing.hpp" #include "Fill/FillBase.hpp" #include "SupportLayer.hpp" +#include "SupportParameters.hpp" namespace Slic3r { class PrintObject; @@ -18,35 +19,6 @@ class PrintObjectConfig; // the parameters of the raft to determine the 1st layer height and thickness. 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: PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params); @@ -97,25 +69,7 @@ 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. - std::pair 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 // the support volume and the object. @@ -131,16 +85,6 @@ private: 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. const PrintObject *m_object; 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. SlicingParameters m_slicing_params; // Various precomputed support parameters to be shared with external functions. - SupportParams m_support_params; + SupportParameters m_support_params; }; } // namespace Slic3r From 4034ffea188fbb75ea145cef067bf551c72fe1ab Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 26 Jan 2025 17:08:07 +0800 Subject: [PATCH 03/72] Replace `TreeSupport::SupportParams` with `SupportParameters` (cherry picked from commit bambulab/BambuStudio@39ae64fc53abec794d740e36baaa13fd6fb35849) Co-authored-by: Arthur --- src/libslic3r/Support/SupportCommon.cpp | 10 ++--- src/libslic3r/Support/SupportParameters.hpp | 33 +++++++++++++--- src/libslic3r/Support/TreeSupport.cpp | 42 ++++++--------------- src/libslic3r/Support/TreeSupport.hpp | 33 +--------------- 4 files changed, 45 insertions(+), 73 deletions(-) diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index 01261bb6dc..4754ad47d1 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -140,8 +140,8 @@ std::pair generate_interfa if (! intermediate_layers.empty() && support_params.has_interfaces()) { // For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers. BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start"; - const bool snug_supports = config.support_style.value == smsSnug; - const bool smooth_supports = config.support_style.value != smsGrid; + const bool snug_supports = support_params.support_style == smsSnug; + const bool smooth_supports = support_params.support_style != smsGrid; SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first; SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second; interface_layers.assign(intermediate_layers.size(), nullptr); @@ -1646,7 +1646,7 @@ void generate_support_toolpaths( { SupportLayer &support_layer = *support_layers[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; - const float support_interface_angle = config.support_style.value == smsGrid ? + const float support_interface_angle = support_params.support_style == smsGrid ? support_params.interface_angle : support_params.raft_interface_angle(support_layer.interface_id()); // Find polygons with the same print_z. @@ -1808,9 +1808,7 @@ void generate_support_toolpaths( filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); sheath = true; no_sort = true; - } else if (config.support_style == SupportMaterialStyle::smsOrganic || - // Orca: use organic as default - config.support_style == smsDefault) { + } else if (support_params.support_style == SupportMaterialStyle::smsOrganic) { tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow, support_params); done = true; } diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index e240504c23..98b15dd747 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -104,7 +104,12 @@ struct SupportParameters { this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase; - this->contact_fill_pattern = + if (object_config.support_interface_pattern == smipGrid) + this->contact_fill_pattern = ipGrid; + else if (object_config.support_interface_pattern == smipRectilinearInterlaced) + this->contact_fill_pattern = ipRectilinear; + else + this->contact_fill_pattern = (object_config.support_interface_pattern == smipAuto && slicing_params.soluble_interface) || object_config.support_interface_pattern == smipConcentric ? ipConcentric : @@ -142,11 +147,25 @@ struct SupportParameters { assert(slicing_params.interface_raft_layers == 0); assert(slicing_params.raft_layers() == 0); } - - this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI; - } - // Both top / bottom contacts and interfaces are soluble. + const auto nozzle_diameter = print_config.nozzle_diameter.get_at(object_config.support_interface_filament - 1); + const coordf_t extrusion_width = object_config.line_width.get_abs_value(nozzle_diameter); + support_extrusion_width = object_config.support_line_width.get_abs_value(nozzle_diameter); + support_extrusion_width = support_extrusion_width > 0 ? support_extrusion_width : extrusion_width; + + tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI; + + support_style = object_config.support_style; + if (support_style == smsDefault) { + if (is_tree(object_config.support_type)) { + // Orca: use organic as default + support_style = smsOrganic; + } else { + support_style = smsGrid; + } + } + } + // Both top / bottom contacts and interfaces are soluble. bool soluble_interface; // Support contact & interface are soluble, but support base is non-soluble. bool soluble_interface_non_soluble_base; @@ -181,6 +200,7 @@ struct SupportParameters { Flow support_material_bottom_interface_flow; // Flow at raft inteface & contact layers. Flow raft_interface_flow; + coordf_t support_extrusion_width; // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder? bool can_merge_support_regions; @@ -198,6 +218,7 @@ struct SupportParameters { coordf_t raft_interface_density; // Density of the base support layers. coordf_t support_density; + SupportMaterialStyle support_style = smsDefault; // Pattern of the sparse infill including sparse raft layers. InfillPattern base_fill_pattern; @@ -219,6 +240,8 @@ struct SupportParameters { // Produce a raft interface angle for a given SupportLayer::interface_id() float raft_interface_angle(size_t interface_id) const { return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); } + + const double thresh_big_overhang = Slic3r::sqr(scale_(10)); }; } // namespace Slic3r diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 6d6e25ef2b..330c53910a 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -667,41 +667,21 @@ static Point bounding_box_middle(const BoundingBox &bbox) } TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_params) - : m_object(&object), m_slicing_params(slicing_params), m_object_config(&object.config()) + : m_object(&object), m_slicing_params(slicing_params), m_support_params(object), m_object_config(&object.config()) { m_print_config = &m_object->print()->config(); m_raft_layers = slicing_params.base_raft_layers + slicing_params.interface_raft_layers; support_type = m_object_config->support_type; - support_style = m_object_config->support_style; - if (support_style == smsDefault) - // Orca: use organic as default - support_style = smsOrganic; + SupportMaterialPattern support_pattern = m_object_config->support_base_pattern; - if (support_style == smsTreeHybrid && support_pattern == smpDefault) + if (m_support_params.support_style == smsTreeHybrid && support_pattern == smpDefault) support_pattern = smpRectilinear; - m_support_params.base_fill_pattern = - support_pattern == smpLightning ? ipLightning : - support_pattern == smpHoneycomb ? ipHoneycomb : - m_support_params.support_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : ipSupportBase; - m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); - if (m_object_config->support_interface_pattern == smipGrid) - m_support_params.contact_fill_pattern = ipGrid; - else if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) - m_support_params.contact_fill_pattern = ipRectilinear; - else - m_support_params.contact_fill_pattern = (m_object_config->support_interface_pattern == smipAuto && m_slicing_params.soluble_interface) || - m_object_config->support_interface_pattern == smipConcentric ? - ipConcentric : - (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); + if(support_pattern == smpLightning) + m_support_params.base_fill_pattern = ipLightning; - const auto nozzle_diameter = object.print()->config().nozzle_diameter.get_at(object.config().support_interface_filament-1); - const coordf_t extrusion_width = m_object_config->line_width.get_abs_value(nozzle_diameter); - const coordf_t support_extrusion_width = m_object_config->support_line_width.get_abs_value(nozzle_diameter); - - m_support_params.support_extrusion_width = support_extrusion_width > 0 ? support_extrusion_width : extrusion_width; - is_slim = is_tree_slim(support_type, support_style); - is_strong = is_tree(support_type) && support_style == smsTreeStrong; + is_slim = is_tree_slim(support_type, m_support_params.support_style); + is_strong = is_tree(support_type) && m_support_params.support_style == smsTreeStrong; MAX_BRANCH_RADIUS = 10.0; tree_support_branch_diameter_angle = 5.0;//is_slim ? 10.0 : 5.0; // by default tree support needs no infill, unless it's tree hybrid which contains normal nodes. @@ -1132,7 +1112,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) if (max_bridge_length > 0 && ts_layer->overhang_areas.size() > 0 && lower_layer) { // do not break bridge for normal part in TreeHybrid - bool break_bridge = !(support_style == smsTreeHybrid && area(ts_layer->overhang_areas) > m_support_params.thresh_big_overhang); + bool break_bridge = !(m_support_params.support_style == smsTreeHybrid && area(ts_layer->overhang_areas) > m_support_params.thresh_big_overhang); m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &ts_layer->overhang_areas, max_bridge_length, break_bridge); } @@ -1587,7 +1567,7 @@ void TreeSupport::generate_toolpaths() bool need_infill = with_infill; if(m_object_config->support_base_pattern==smpDefault) need_infill &= area_group.need_infill; - if (layer_id>0 && area_group.dist_to_top < 10 && !need_infill && support_style!=smsTreeHybrid) { + if (layer_id>0 && area_group.dist_to_top < 10 && !need_infill && m_support_params.support_style!=smsTreeHybrid) { if (area_group.dist_to_top < 5) // 1 wall at the top <5mm make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, 1, flow, erSupportMaterial); else // at least 2 walls for range [5,10) @@ -1880,7 +1860,7 @@ Polygons TreeSupport::contact_nodes_to_polygon(const std::vector& contact void TreeSupport::generate() { - if (support_style == smsOrganic) { + if (m_support_params.support_style == smsOrganic) { generate_tree_support_3D(*m_object, this, this->throw_on_cancel); return; } @@ -3438,7 +3418,7 @@ void TreeSupport::generate_contact_points(std::vector m_support_params.thresh_big_overhang) { + if (m_support_params.support_style==smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang) { Point candidate = overhang_bounds.center(); if (!overhang_part.contains(candidate)) move_inside_expoly(overhang_part, candidate); diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index 2ad8dc1cdb..bd6bc7e38b 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -11,6 +11,7 @@ #include "Flow.hpp" #include "PrintConfig.hpp" #include "Fill/Lightning/Generator.hpp" +#include "TreeSupport3D.hpp" #ifndef SQ #define SQ(x) ((x)*(x)) @@ -361,35 +362,6 @@ public: } }; - struct SupportParams - { - Flow first_layer_flow; - Flow support_material_flow; - Flow support_material_interface_flow; - Flow support_material_bottom_interface_flow; - coordf_t support_extrusion_width; - // 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 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; - const double thresh_big_overhang = SQ(scale_(10)); - }; - int avg_node_per_layer = 0; float nodes_angle = 0; bool has_overhangs = false; @@ -397,7 +369,6 @@ public: bool has_cantilever = false; double max_cantilever_dist = 0; SupportType support_type; - SupportMaterialStyle support_style; std::unique_ptr generator; std::unordered_map printZ_to_lightninglayer; @@ -422,7 +393,7 @@ private: const PrintObjectConfig *m_object_config; SlicingParameters m_slicing_params; // Various precomputed support parameters to be shared with external functions. - SupportParams m_support_params; + SupportParameters m_support_params; size_t m_raft_layers = 0; size_t m_highest_overhang_layer = 0; std::vector> m_spanning_trees; From 51290a853daa56f3653be10970a077f1d94624cd Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 26 Jan 2025 17:43:29 +0800 Subject: [PATCH 04/72] Clean up tree support code (cherry picked from commit bambulab/BambuStudio@39ae64fc53abec794d740e36baaa13fd6fb35849) Co-authored-by: Arthur --- src/libslic3r/Support/TreeSupport.cpp | 414 +++++--------------------- src/libslic3r/Support/TreeSupport.hpp | 286 +++++++++--------- 2 files changed, 223 insertions(+), 477 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 330c53910a..009fd35a89 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -39,15 +39,6 @@ namespace Slic3r { #define unscale_(val) ((val) * SCALING_FACTOR) -inline unsigned int round_divide(unsigned int dividend, unsigned int divisor) //!< Return dividend divided by divisor rounded to the nearest integer -{ - return (dividend + divisor / 2) / divisor; -} -inline unsigned int round_up_divide(unsigned int dividend, unsigned int divisor) //!< Return dividend divided by divisor rounded to the nearest integer -{ - return (dividend + divisor - 1) / divisor; -} - inline double dot_with_unscale(const Point a, const Point b) { return unscale_(a(0)) * unscale_(b(0)) + unscale_(a(1)) * unscale_(b(1)); @@ -206,8 +197,8 @@ static void draw_contours_and_nodes_to_svg const ExPolygons &overhangs, const ExPolygons &overhangs_after_offset, const ExPolygons &outlines_below, - const std::vector &layer_nodes, - const std::vector &lower_layer_nodes, + const std::vector &layer_nodes, + const std::vector &lower_layer_nodes, std::string name_prefix, std::vector legends = { "overhang","avoid","outlines" }, std::vector colors = { "blue","red","yellow" } ) @@ -216,7 +207,7 @@ static void draw_contours_and_nodes_to_svg bbox.merge(get_extents(overhangs_after_offset)); bbox.merge(get_extents(outlines_below)); Points layer_pts; - for (TreeSupport::Node* node : layer_nodes) { + for (SupportNode* node : layer_nodes) { layer_pts.push_back(node->position); } bbox.merge(get_extents(layer_pts)); @@ -255,14 +246,14 @@ static void draw_contours_and_nodes_to_svg #if 0 // lower layer points layer_pts.clear(); - for (TreeSupport::Node *node : lower_layer_nodes) { + for (SupportNode *node : lower_layer_nodes) { layer_pts.push_back(node->position); } svg.draw(layer_pts, "black", coord_t(scale_(0.1))); // higher layer points layer_pts.clear(); - for (TreeSupport::Node* node : layer_nodes) { + for (SupportNode* node : layer_nodes) { if(node->parent) layer_pts.push_back(node->parent->position); } @@ -1269,50 +1260,9 @@ static void _make_loops(ExtrusionEntitiesPtr& loops_entities, ExPolygons &suppor expoly_list.erase(first_iter); } - // draw connected loops - if (/*wall_count > 1 && wall_count<5*/0) { - // TODO this method may drop some contours - wall_count = std::min(wall_count, loops.size()); - Polylines polylines; - polylines.push_back(Polyline()); - Polyline& polyline = polylines.back(); - Point end_pt; - Point end_dir; - for (int wall_idx = 0; wall_idx < wall_count; wall_idx++) { - Polygon &loop = loops[wall_idx]; - if (loop.size()<3) continue; - // break the closed loop if this is not the last loop, so the next loop can attach to the end_pt - //if (wall_idx != wall_count - 1 && loop.first_point() == loop.last_point()) - // loop.points.pop_back(); + extrusion_entities_append_loops(loops_entities, std::move(loops), role, float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); - if (wall_idx == 0) { - polyline.append(loop.points); - } else { - double d = loop.distance_to(end_pt); - if (d < scale_(2)) { // if current loop is close to the previous one - polyline.append(end_pt); - ExtrusionPath expath; - expath.polyline.append(loop.points); - ExtrusionLoop extru_loop(expath); - extru_loop.split_at(end_pt, false); - polyline.append(extru_loop.as_polyline()); - }else{// create a new polylie if they are far away - polylines.push_back(Polyline()); - polyline = polylines.back(); - polyline.append(loop.points); - } - } - end_pt = polyline.points.back(); - end_dir = end_pt - polyline.points[polyline.points.size() - 2]; - Point perpendicular_dir = turn90_ccw(end_dir); - end_pt = end_pt + normal(perpendicular_dir, flow.scaled_spacing()); - } - - extrusion_entities_append_paths(loops_entities, polylines, role, float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); - } else { - extrusion_entities_append_loops(loops_entities, std::move(loops), role, float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); - } - } +} static void make_perimeter_and_inner_brim(ExtrusionEntitiesPtr &dst, const ExPolygon &support_area, size_t wall_count, const Flow &flow, ExtrusionRole role) { @@ -1654,210 +1604,6 @@ void TreeSupport::generate_toolpaths() ); } -Polygons TreeSupport::spanning_tree_to_polygon(const std::vector& spanning_trees, Polygons layer_contours, int layer_nr) -{ - Polygons polys; - auto& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr]; - for (MinimumSpanningTree mst : spanning_trees) { - std::vector points = mst.vertices(); - if (points.size() == 0) - continue; - std::map visited; - for (int i=0;i to_ignore; - for (int i = 0; i < points.size(); i++) { - if (visited[points[i]] == true) - continue; - - Polygon poly; - bool has_next = true; - Point pt1 = points[i]; - poly.points.push_back(pt1); - visited[pt1] = true; - - while (has_next) { - const std::vector& neighbours = mst.adjacent_nodes(pt1); - if (neighbours.empty()) - { - break; - } - - double min_ccw = std::numeric_limits::max(); - Point pt_selected = neighbours[0]; - has_next = false; - for (Point pt2 : neighbours) { - if (to_ignore.find(Line(pt1, pt2)) == to_ignore.end()) { - auto iter = mst_line_x_layer_contour_cache.find({ pt1,pt2 }); - if (iter != mst_line_x_layer_contour_cache.end()) { - if (iter->second) - continue; - } - else { - Polylines pls; - pls.emplace_back(pt1, pt2); - Polylines pls_intersect = intersection_pl(pls, layer_contours); - mst_line_x_layer_contour_cache.insert({ {pt1, pt2}, !pls_intersect.empty() }); - mst_line_x_layer_contour_cache.insert({ {pt2, pt1}, !pls_intersect.empty() }); - if (!pls_intersect.empty()) - continue; - } - - if (poly.points.size() < 2 || visited[pt2]==false) - { - pt_selected = pt2; - has_next = true; - break; - } - double curr_ccw = pt2.ccw(pt1, poly.points.back()); - if (curr_ccw < min_ccw) - { - min_ccw = curr_ccw; - pt_selected = pt2; - has_next = true; - } - } - } - if (has_next) { - poly.points.push_back(pt_selected); - to_ignore.insert(Line(pt1, pt_selected)); - visited[pt_selected] = true; - pt1 = pt_selected; - } - } - polys.emplace_back(std::move(poly)); - } - } - return polys; -} - -Polygons TreeSupport::contact_nodes_to_polygon(const std::vector& contact_nodes, Polygons layer_contours, int layer_nr, std::vector& radiis, std::vector& is_interface) -{ - Polygons polys; - std::vector spanning_trees; - std::vector radiis_mtree; - std::vector is_interface_mtree; - // generate minimum spanning trees - { - std::map visited; - for (int i = 0; i < contact_nodes.size(); i++) - visited.emplace(contact_nodes[i], false); - std::unordered_set to_ignore; - - // generate minimum spaning trees - for (int i = 0; i < contact_nodes.size(); i++) { - Node* node = contact_nodes[i]; - if (visited[node]) - continue; - - std::vector points_to_mstree; - double radius = 0; - Point pt1 = node->position; - points_to_mstree.push_back(pt1); - visited[node] = true; - radius += node->radius; - - for (int j = i + 1; j < contact_nodes.size(); j++) { - Node* node2 = contact_nodes[j]; - Point pt2 = node2->position; - // connect to this neighbor if: - // 1) both are interface or both are not - // 3) not readly added - // 4) won't cross perimeters: this is not right since we need to check all possible connections - if ((node->support_roof_layers_below > 0) == (node2->support_roof_layers_below > 0) - && to_ignore.find(Line(pt1, pt2)) == to_ignore.end()) - { - points_to_mstree.emplace_back(pt2); - visited[node2] = true; - radius += node2->radius; - } - } - - spanning_trees.emplace_back(points_to_mstree); - radiis_mtree.push_back(radius / points_to_mstree.size()); - is_interface_mtree.push_back(node->support_roof_layers_below > 0); - } - } - auto lines = spanning_tree_to_lines(spanning_trees); -#if 1 - // convert mtree to polygon - for (int k = 0; k < spanning_trees.size(); k++) { - auto& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr]; - MinimumSpanningTree mst = spanning_trees[k]; - std::vector points = mst.vertices(); - std::map visited; - for (int i = 0; i < points.size(); i++) - visited.emplace(points[i], false); - - std::unordered_set to_ignore; - for (int i = 0; i < points.size(); i++) { - if (visited[points[i]]) - continue; - - Polygon poly; - Point pt1 = points[i]; - poly.points.push_back(pt1); - visited[pt1] = true; - - bool has_next = true; - while (has_next) - { - const std::vector& neighbours = mst.adjacent_nodes(pt1); - double min_ccw = -std::numeric_limits::max(); - Point pt_selected; - has_next = false; - for (Point pt2 : neighbours) { - if (to_ignore.find(Line(pt1, pt2)) == to_ignore.end()) { - auto iter = mst_line_x_layer_contour_cache.find({ pt1,pt2 }); - if (iter != mst_line_x_layer_contour_cache.end()) { - if (iter->second) - continue; - } - else { - Polylines pls; - pls.emplace_back(pt1, pt2); - Polylines pls_intersect = intersection_pl(pls, layer_contours); - mst_line_x_layer_contour_cache.insert({ {pt1, pt2}, !pls_intersect.empty() }); - mst_line_x_layer_contour_cache.insert({ {pt2, pt1}, !pls_intersect.empty() }); - if (!pls_intersect.empty()) - continue; - } - if (poly.points.size() < 2) - { - pt_selected = pt2; - has_next = true; - break; - } - double curr_ccw = pt2.ccw(pt1, poly.points.rbegin()[1]); - if (curr_ccw > min_ccw) - { - has_next = true; - min_ccw = curr_ccw; - pt_selected = pt2; - } - } - } - if (!has_next) - break; - - poly.points.push_back(pt_selected); - to_ignore.insert(Line(pt1, pt_selected)); - visited[pt_selected] = true; - pt1 = pt_selected; - } - polys.emplace_back(std::move(poly)); - radiis.push_back(radiis_mtree[k]); - is_interface.push_back(is_interface_mtree[k]); - } - } -#else - polys = spanning_tree_to_polygon(spanning_trees, layer_contours, layer_nr, radiis); -#endif - return polys; -} - - void TreeSupport::generate() { if (m_support_params.support_style == smsOrganic) { @@ -1865,7 +1611,7 @@ void TreeSupport::generate() return; } - std::vector> contact_nodes(m_object->layers().size()); + std::vector> contact_nodes(m_object->layers().size()); profiler.stage_start(STAGE_total); @@ -1907,7 +1653,7 @@ if (!m_object->config().tree_support_adaptive_layer_height) for (auto& layer : contact_nodes) { - for (Node* p_node : layer) + for (SupportNode* p_node : layer) { delete p_node; } @@ -2061,7 +1807,7 @@ Polygons TreeSupport::get_trim_support_regions( return polygons_trimming; } -void TreeSupport::draw_circles(const std::vector>& contact_nodes) +void TreeSupport::draw_circles(const std::vector>& contact_nodes) { const PrintObjectConfig &config = m_object->config(); const Print* print = m_object->print(); @@ -2123,7 +1869,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no if (print->canceled()) break; - const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); assert(ts_layer != nullptr); @@ -2134,7 +1880,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no continue; } - Node* first_node = curr_layer_nodes.front(); + SupportNode* first_node = curr_layer_nodes.front(); ts_layer->print_z = first_node->print_z; ts_layer->height = first_node->height; if (ts_layer->height < EPSILON) { @@ -2155,12 +1901,12 @@ void TreeSupport::draw_circles(const std::vector>& contact_no BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size(); //Draw the support areas and add the roofs appropriately to the support roof instead of normal areas. ts_layer->lslices.reserve(contact_nodes[layer_nr].size()); - for (const Node* p_node : contact_nodes[layer_nr]) + for (const SupportNode* p_node : contact_nodes[layer_nr]) { if (print->canceled()) break; - const Node& node = *p_node; + const SupportNode& node = *p_node; ExPolygons area; // Generate directly from overhang polygon if one of the following is true: // 1) node is a normal part of hybrid support @@ -2347,7 +2093,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no std::vector overhangs; for (int layer_nr = 1; layer_nr < m_object->layer_count(); layer_nr++) { if (print->canceled()) break; - const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); assert(ts_layer != nullptr); @@ -2439,7 +2185,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no if (print->canceled()) break; m_object->print()->set_status(66, (boost::format(_L("Support: fix holes at layer %d")) % layer_nr).str()); - const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); assert(ts_layer != nullptr); @@ -2547,7 +2293,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no } } -void TreeSupport::drop_nodes(std::vector>& contact_nodes) +void TreeSupport::drop_nodes(std::vector>& contact_nodes) { const PrintObjectConfig &config = m_object->config(); // Use Minimum Spanning Tree to connect the points on each layer and move them while dropping them down. @@ -2573,7 +2319,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) if (config.tree_support_branch_angle.value < 30.0) return config.tree_support_branch_angle.value; return (radius - MIN_BRANCH_RADIUS) / (MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS) * (config.tree_support_branch_angle.value - 30.0) + 30.0; }; - auto get_max_move_dist = [this, &config, branch_radius, tip_layers, diameter_angle_scale_factor, wall_count, support_extrusion_width, support_line_width](const Node *node, int power = 1) { + auto get_max_move_dist = [this, &config, branch_radius, tip_layers, diameter_angle_scale_factor, wall_count, support_extrusion_width, support_line_width](const SupportNode *node, int power = 1) { double move_dist = node->max_move_dist; if (node->max_move_dist == 0) { if (node->radius == 0) node->radius = calc_branch_radius(branch_radius, node->dist_mm_to_top, diameter_angle_scale_factor); @@ -2594,7 +2340,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) std::vector &layer_heights = m_ts_data->layer_heights; if (layer_heights.empty()) return; - std::unordered_set to_free_node_set; + std::unordered_set to_free_node_set; m_spanning_trees.resize(contact_nodes.size()); //m_mst_line_x_layer_contour_caches.resize(contact_nodes.size()); @@ -2613,7 +2359,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) if (layer_heights[layer_nr].height < EPSILON) continue; auto& layer_radius = all_layer_radius[layer_nr]; auto& layer_node_dist = all_layer_node_dist[layer_nr]; - for (Node *p_node : contact_nodes[layer_nr]) { + for (SupportNode *p_node : contact_nodes[layer_nr]) { layer_node_dist.emplace(p_node->dist_mm_to_top); } size_t layer_nr_next = layer_heights[layer_nr].next_layer_nr; @@ -2655,7 +2401,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) coordf_t print_z_next = layer_heights[layer_nr_next].print_z; coordf_t height_next = layer_heights[layer_nr_next].height; - std::deque> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches. + std::deque> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches. const Layer* ts_layer = m_object->get_support_layer(layer_nr); m_object->print()->set_status(60, (boost::format(_L("Support: propagate branches at layer %d")) % layer_nr).str()); @@ -2685,14 +2431,14 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) //Group together all nodes for each part. const ExPolygons& parts = m_ts_data->get_avoidance(0, layer_nr); - std::vector> nodes_per_part(1 + parts.size()); //All nodes that aren't inside a part get grouped together in the 0th part. - for (Node* p_node : layer_contact_nodes) + std::vector> nodes_per_part(1 + parts.size()); //All nodes that aren't inside a part get grouped together in the 0th part. + for (SupportNode* p_node : layer_contact_nodes) { - const Node& node = *p_node; + const SupportNode& node = *p_node; if (node.distance_to_top < 0) { // gap nodes do not merge or move - Node* next_node = new Node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, p_node->to_buildplate, p_node, + SupportNode* next_node = new SupportNode(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, p_node->to_buildplate, p_node, print_z_next, height_next); get_max_move_dist(next_node); next_node->is_merged = false; @@ -2747,10 +2493,10 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) profiler.tic(); //std::vector& spanning_trees = m_spanning_trees[layer_nr]; std::vector spanning_trees; - for (const std::unordered_map& group : nodes_per_part) + for (const std::unordered_map& group : nodes_per_part) { std::vector points_to_buildplate; - for (const std::pair& entry : group) + for (const std::pair& entry : group) { points_to_buildplate.emplace_back(entry.first); //Just the position of the node. } @@ -2767,11 +2513,11 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) { const MinimumSpanningTree& mst = spanning_trees[group_index]; //In the first pass, merge all nodes that are close together. - std::unordered_set to_delete; - for (const std::pair& entry : nodes_per_part[group_index]) + std::unordered_set to_delete; + for (const std::pair& entry : nodes_per_part[group_index]) { - Node* p_node = entry.second; - Node& node = *p_node; + SupportNode* p_node = entry.second; + SupportNode& node = *p_node; if (to_delete.find(p_node) != to_delete.end()) { continue; //Delete this node (don't create a new node for it on the next layer). @@ -2780,7 +2526,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) if (node.type == ePolygon) { // Remove all neighbours that are completely inside the polygon and merge them into this node. for (const Point &neighbour : neighbours) { - Node * neighbour_node = nodes_per_part[group_index][neighbour]; + SupportNode * neighbour_node = nodes_per_part[group_index][neighbour]; coord_t neighbour_radius = scale_(calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top, diameter_angle_scale_factor)); Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius), pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0); @@ -2812,8 +2558,8 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) move_out_expolys(avoid_layer, next_position, radius_sample_resolution + EPSILON, max_move_between_samples); } - Node* neighbour = nodes_per_part[group_index][neighbours[0]]; - Node* node_; + SupportNode* neighbour = nodes_per_part[group_index][neighbours[0]]; + SupportNode* node_; if (p_node->parent && neighbour->parent) node_ = (node.dist_mm_to_top >= neighbour->dist_mm_to_top && p_node->parent) ? p_node : neighbour; else @@ -2821,7 +2567,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) // Make sure the next pass doesn't drop down either of these (since that already happened). node_->merged_neighbours.push_front(node_ == p_node ? neighbour : p_node); const bool to_buildplate = !is_inside_ex(m_ts_data->get_avoidance(0, layer_nr_next), next_position); - Node * next_node = new Node(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_, + SupportNode * next_node = new SupportNode(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_, print_z_next, height_next); next_node->movement = next_position - node.position; get_max_move_dist(next_node); @@ -2839,7 +2585,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) { if (vsize2_with_unscale(neighbour - node.position) < /*max_move_distance2*/get_max_move_dist(&node,2)) { - Node* neighbour_node = nodes_per_part[group_index][neighbour]; + SupportNode* neighbour_node = nodes_per_part[group_index][neighbour]; if (neighbour_node->type == ePolygon) continue; node.distance_to_top = std::max(node.distance_to_top, neighbour_node->distance_to_top); @@ -2855,10 +2601,10 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) } //In the second pass, move all middle nodes. - for (const std::pair& entry : nodes_per_part[group_index]) + for (const std::pair& entry : nodes_per_part[group_index]) { - Node* p_node = entry.second; - const Node& node = *p_node; + SupportNode* p_node = entry.second; + const SupportNode& node = *p_node; if (to_delete.find(p_node) != to_delete.end()) { continue; @@ -2866,7 +2612,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) if (node.type == ePolygon) { // polygon node do not merge or move const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], p_node->position); - Node * next_node = new Node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, + SupportNode * next_node = new SupportNode(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); next_node->max_move_dist = 0; next_node->is_merged = false; @@ -2887,7 +2633,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) unsupported_branch_leaves.push_front({ layer_nr, p_node }); } else { - Node* pn = p_node; + SupportNode* pn = p_node; for (int i = 0; i <= bottom_interface_layers && pn; i++, pn = pn->parent) pn->support_floor_layers_above = bottom_interface_layers - i + 1; // +1 so the parent node has support_floor_layers_above=2 to_delete.insert(p_node); @@ -2897,7 +2643,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) // if the link between parent and current is cut by contours, mark current as bottom contact node if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false) { - Node* pn = p_node->parent; + SupportNode* pn = p_node->parent; for (int i = 0; i <= bottom_interface_layers && pn; i++, pn = pn->parent) pn->support_floor_layers_above = bottom_interface_layers - i + 1; to_delete.insert(p_node); @@ -2919,7 +2665,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) Point sum_direction(0, 0); for (const Point &neighbour : neighbours) { // do not move to the neighbor to be deleted - Node *neighbour_node = nodes_per_part[group_index][neighbour]; + SupportNode *neighbour_node = nodes_per_part[group_index][neighbour]; if (to_delete.find(neighbour_node) != to_delete.end()) continue; Point direction = neighbour - node.position; @@ -3010,7 +2756,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) } const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], next_layer_vertex);// !is_inside_ex(m_ts_data->get_avoidance(m_ts_data->m_xy_distance, layer_nr - 1), next_layer_vertex); - Node * next_node = new Node(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, + SupportNode * next_node = new SupportNode(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); next_node->movement = movement; get_max_move_dist(next_node); @@ -3038,11 +2784,11 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) for (;! unsupported_branch_leaves.empty(); unsupported_branch_leaves.pop_back()) { const auto& entry = unsupported_branch_leaves.back(); - Node* i_node = entry.second; + SupportNode* i_node = entry.second; for (; i_node != nullptr; i_node = i_node->parent) { size_t i_layer = i_node->obj_layer_nr; - std::vector::iterator to_erase = std::find(contact_nodes[i_layer].begin(), contact_nodes[i_layer].end(), i_node); + std::vector::iterator to_erase = std::find(contact_nodes[i_layer].begin(), contact_nodes[i_layer].end(), i_node); if (to_erase != contact_nodes[i_layer].end()) { // update the parent-child chain @@ -3053,7 +2799,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) contact_nodes[i_layer].erase(to_erase); to_free_node_set.insert(i_node); - for (Node* neighbour : i_node->merged_neighbours) + for (SupportNode* neighbour : i_node->merged_neighbours) { unsupported_branch_leaves.push_front({ i_layer, neighbour }); } @@ -3064,32 +2810,32 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) BOOST_LOG_TRIVIAL(debug) << "after m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size(); - for (Node *node : to_free_node_set) + for (SupportNode *node : to_free_node_set) { delete node; } to_free_node_set.clear(); } -void TreeSupport::smooth_nodes(std::vector> &contact_nodes) +void TreeSupport::smooth_nodes(std::vector> &contact_nodes) { for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { - std::vector &curr_layer_nodes = contact_nodes[layer_nr]; + std::vector &curr_layer_nodes = contact_nodes[layer_nr]; if (curr_layer_nodes.empty()) continue; - for (Node *node : curr_layer_nodes) { + for (SupportNode *node : curr_layer_nodes) { node->is_processed = false; if (layer_nr == 0) node->is_merged = true; // nodes on plate are also merged nodes } } for (int layer_nr = 0; layer_nr< contact_nodes.size(); layer_nr++) { - std::vector &curr_layer_nodes = contact_nodes[layer_nr]; + std::vector &curr_layer_nodes = contact_nodes[layer_nr]; if (curr_layer_nodes.empty()) continue; - for (Node *node : curr_layer_nodes) { + for (SupportNode *node : curr_layer_nodes) { if (!node->is_processed) { std::vector pts; - std::vector branch; - Node * p_node = node; + std::vector branch; + SupportNode * p_node = node; // add a fixed head if (node->child) { pts.push_back(p_node->child->position); @@ -3125,11 +2871,11 @@ void TreeSupport::smooth_nodes(std::vector> &contact_nodes) } // save tree structure for viewing in python auto& tree_nodes = m_ts_data->tree_nodes; - std::map ptr2idx; - std::map idx2ptr; + std::map ptr2idx; + std::map idx2ptr; for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { - std::vector& curr_layer_nodes = contact_nodes[layer_nr]; - for (Node* node : curr_layer_nodes) { + std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + for (SupportNode* node : curr_layer_nodes) { ptr2idx.emplace(node, tree_nodes.size()); idx2ptr.emplace(tree_nodes.size(), node); tree_nodes.emplace_back(node->position, node->print_z); @@ -3137,7 +2883,7 @@ void TreeSupport::smooth_nodes(std::vector> &contact_nodes) } for (size_t i = 0; i < tree_nodes.size(); i++) { TreeNode& tree_node = tree_nodes[i]; - Node* p_node = idx2ptr[i]; + SupportNode* p_node = idx2ptr[i]; if (p_node->child) tree_node.children.push_back(ptr2idx[p_node->child]); if(p_node->parent) @@ -3159,7 +2905,7 @@ void TreeSupport::smooth_nodes(std::vector> &contact_nodes) #endif } -void TreeSupport::adjust_layer_heights(std::vector>& contact_nodes) +void TreeSupport::adjust_layer_heights(std::vector>& contact_nodes) { if (contact_nodes.empty()) return; @@ -3170,8 +2916,8 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ // TODO can we merge layers in a way that guaranttees smoothness? if (!print_config.independent_support_layer_height || is_slim) { for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { - std::vector& curr_layer_nodes = contact_nodes[layer_nr]; - for (Node* node : curr_layer_nodes) { + std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + for (SupportNode* node : curr_layer_nodes) { node->print_z = m_object->get_layer(layer_nr)->print_z; node->height = m_object->get_layer(layer_nr)->height; } @@ -3190,14 +2936,14 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ if (layer_height == max_layer_height) return; extremes.push_back(0); - for (Node* node : contact_nodes[0]) { + for (SupportNode* node : contact_nodes[0]) { node->print_z = m_object->get_layer(0)->print_z; node->height = m_object->get_layer(0)->height; } for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) { - std::vector& curr_layer_nodes = contact_nodes[layer_nr]; - for (Node* node : curr_layer_nodes) { + std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + for (SupportNode* node : curr_layer_nodes) { if (node->support_roof_layers_below >0 || node->support_floor_layers_above == bot_intf_layers) { extremes.push_back(layer_nr); break; @@ -3206,7 +2952,7 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ if (extremes.back() == layer_nr) { // contact layer use the same print_z and layer height with object layer - for (Node* node : curr_layer_nodes) { + for (SupportNode* node : curr_layer_nodes) { node->print_z = m_object->get_layer(layer_nr)->print_z; node->height = m_object->get_layer(layer_nr)->height; } @@ -3230,11 +2976,11 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ coordf_t print_z = extr1z + step; assert(step >= layer_height - EPSILON); for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; layer_nr++) { - std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + std::vector& curr_layer_nodes = contact_nodes[layer_nr]; if (curr_layer_nodes.empty()) continue; if (std::abs(print_z - curr_layer_nodes[0]->print_z) < step / 2 + EPSILON) { - for (Node* node : curr_layer_nodes) { + for (SupportNode* node : curr_layer_nodes) { node->print_z = print_z; node->height = step; } @@ -3242,7 +2988,7 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ } else { // can't clear curr_layer_nodes, or the model will have empty layers - for (Node* node : curr_layer_nodes) { + for (SupportNode* node : curr_layer_nodes) { node->print_z = 0.0; node->height = 0.0; } @@ -3251,7 +2997,7 @@ void TreeSupport::adjust_layer_heights(std::vector>& contact_ } } -std::vector TreeSupport::plan_layer_heights(std::vector> &contact_nodes) +std::vector TreeSupport::plan_layer_heights(std::vector> &contact_nodes) { const PrintObjectConfig& config = m_object->config(); const PrintConfig & print_config = m_object->print()->config(); @@ -3350,7 +3096,7 @@ std::vector TreeSupport::plan_layer_heights(std::vector>& contact_nodes) +void TreeSupport::generate_contact_points(std::vector>& contact_nodes) { const PrintObjectConfig &config = m_object->config(); const coordf_t point_spread = scale_(config.tree_support_branch_distance.value); @@ -3423,7 +3169,7 @@ void TreeSupport::generate_contact_points(std::vectorm_layer_outlines_below[layer_nr], candidate))) { - Node* contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z, + SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z, height, z_distance_top); contact_node->type = ePolygon; contact_node->overhang = &overhang_part; @@ -3452,8 +3198,8 @@ void TreeSupport::generate_contact_points(std::vectorget_collision(0, layer_nr), candidate)) { constexpr bool to_buildplate = true; - Node * contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, - Node::NO_PARENT, print_z, height, z_distance_top); + SupportNode * contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, + SupportNode::NO_PARENT, print_z, height, z_distance_top); contact_node->overhang = &overhang_part; curr_nodes.emplace_back(contact_node); added = true; @@ -3475,7 +3221,7 @@ void TreeSupport::generate_contact_points(std::vectoroverhang = &overhang_part; curr_nodes.emplace_back(contact_node); @@ -3491,7 +3237,7 @@ void TreeSupport::generate_contact_points(std::vector().normalized(); auto v2 = (pt - points[(i + 1) % nSize]).cast().normalized(); if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees - Node *contact_node = new Node(pt, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z, + SupportNode *contact_node = new SupportNode(pt, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z, height, z_distance_top); contact_node->overhang = &overhang_part; contact_node->is_corner = true; @@ -3551,16 +3297,16 @@ void TreeSupport::generate_contact_points(std::vector& nodes_layer, Node* p_node) +void TreeSupport::insert_dropped_node(std::vector& nodes_layer, SupportNode* p_node) { - std::vector::iterator conflicting_node_it = std::find(nodes_layer.begin(), nodes_layer.end(), p_node); + std::vector::iterator conflicting_node_it = std::find(nodes_layer.begin(), nodes_layer.end(), p_node); if (conflicting_node_it == nodes_layer.end()) //No conflict. { nodes_layer.emplace_back(p_node); return; } - Node* conflicting_node = *conflicting_node_it; + SupportNode* conflicting_node = *conflicting_node_it; conflicting_node->distance_to_top = std::max(conflicting_node->distance_to_top, p_node->distance_to_top); conflicting_node->support_roof_layers_below = std::max(conflicting_node->support_roof_layers_below, p_node->support_roof_layers_below); } diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index bd6bc7e38b..8a19acc485 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -41,6 +41,142 @@ struct TreeNode { } }; +enum TreeNodeType { + eCircle, + eSquare, + ePolygon +}; + + +/*! + * \brief Represents the metadata of a node in the tree. + */ +struct SupportNode +{ + static constexpr SupportNode* NO_PARENT = nullptr; + + SupportNode() + : distance_to_top(0) + , position(Point(0, 0)) + , obj_layer_nr(0) + , support_roof_layers_below(0) + , support_floor_layers_above(0) + , to_buildplate(true) + , parent(nullptr) + , print_z(0.0) + , height(0.0) + {} + + // when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height; + SupportNode(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode* parent, + coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0) + : distance_to_top(distance_to_top) + , position(position) + , obj_layer_nr(obj_layer_nr) + , support_roof_layers_below(support_roof_layers_below) + , support_floor_layers_above(0) + , to_buildplate(to_buildplate) + , parent(parent) + , print_z(print_z_) + , height(height_) + , dist_mm_to_top(dist_mm_to_top_) + { + if (parent) { + type = parent->type; + overhang = parent->overhang; + if (dist_mm_to_top==0) + dist_mm_to_top = parent->dist_mm_to_top + parent->height; + parent->child = this; + for (auto& neighbor : parent->merged_neighbours) + neighbor->child = this; + } + } + +#ifdef DEBUG // Clear the delete node's data so if there's invalid access after, we may get a clue by inspecting that node. + ~SupportNode() + { + parent = nullptr; + merged_neighbours.clear(); + } +#endif // DEBUG + + /*! + * \brief The number of layers to go to the top of this branch. + * Negative value means it's a virtual node between support and overhang, which doesn't need to be extruded. + */ + int distance_to_top; + coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm + + /*! + * \brief The position of this node on the layer. + */ + Point position; + Point movement; // movement towards neighbor center or outline + mutable double radius = 0.0; + mutable double max_move_dist = 0.0; + TreeNodeType type = eCircle; + bool is_merged = false; // this node is generated by merging upper nodes + bool is_corner = false; + bool is_processed = false; + const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area + + /*! + * \brief The direction of the skin lines above the tip of the branch. + * + * This determines in which direction we should reduce the width of the + * branch. + */ + bool skin_direction; + + /*! + * \brief The number of support roof layers below this one. + * + * When a contact point is created, it is determined whether the mesh + * needs to be supported with support roof or not, since that is a + * per-mesh setting. This is stored in this variable in order to track + * how far we need to extend that support roof downwards. + */ + int support_roof_layers_below; + int support_floor_layers_above; + int obj_layer_nr; + + /*! + * \brief Whether to try to go towards the build plate. + * + * If the node is inside the collision areas, it has no choice but to go + * towards the model. If it is not inside the collision areas, it must + * go towards the build plate to prevent a scar on the surface. + */ + bool to_buildplate; + + /*! + * \brief The originating node for this one, one layer higher. + * + * In order to prune branches that can't have any support (because they + * can't be on the model and the path to the buildplate isn't clear), + * the entire branch needs to be known. + */ + SupportNode* parent; + SupportNode* child = nullptr; + + /*! + * \brief All neighbours (on the same layer) that where merged into this node. + * + * In order to prune branches that can't have any support (because they + * can't be on the model and the path to the buildplate isn't clear), + * the entire branch needs to be known. + */ + std::list merged_neighbours; + + coordf_t print_z; + coordf_t height; + + bool operator==(const SupportNode& other) const + { + return position == other.position; + } +}; + /*! * \brief Lazily generates tree guidance volumes. * @@ -227,140 +363,6 @@ public: void detect_overhangs(bool detect_first_sharp_tail_only=false); - enum NodeType { - eCircle, - eSquare, - ePolygon - }; - - /*! - * \brief Represents the metadata of a node in the tree. - */ - struct Node - { - static constexpr Node* NO_PARENT = nullptr; - - Node() - : distance_to_top(0) - , position(Point(0, 0)) - , obj_layer_nr(0) - , support_roof_layers_below(0) - , support_floor_layers_above(0) - , to_buildplate(true) - , parent(nullptr) - , print_z(0.0) - , height(0.0) - {} - - // when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height; - Node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, Node* parent, - coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0) - : distance_to_top(distance_to_top) - , position(position) - , obj_layer_nr(obj_layer_nr) - , support_roof_layers_below(support_roof_layers_below) - , support_floor_layers_above(0) - , to_buildplate(to_buildplate) - , parent(parent) - , print_z(print_z_) - , height(height_) - , dist_mm_to_top(dist_mm_to_top_) - { - if (parent) { - type = parent->type; - overhang = parent->overhang; - if (dist_mm_to_top==0) - dist_mm_to_top = parent->dist_mm_to_top + parent->height; - parent->child = this; - for (auto& neighbor : parent->merged_neighbours) - neighbor->child = this; - } - } - -#ifdef DEBUG // Clear the delete node's data so if there's invalid access after, we may get a clue by inspecting that node. - ~Node() - { - parent = nullptr; - merged_neighbours.clear(); - } -#endif // DEBUG - - /*! - * \brief The number of layers to go to the top of this branch. - * Negative value means it's a virtual node between support and overhang, which doesn't need to be extruded. - */ - int distance_to_top; - coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm - - /*! - * \brief The position of this node on the layer. - */ - Point position; - Point movement; // movement towards neighbor center or outline - mutable double radius = 0.0; - mutable double max_move_dist = 0.0; - NodeType type = eCircle; - bool is_merged = false; // this node is generated by merging upper nodes - bool is_corner = false; - bool is_processed = false; - const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area - - /*! - * \brief The direction of the skin lines above the tip of the branch. - * - * This determines in which direction we should reduce the width of the - * branch. - */ - bool skin_direction; - - /*! - * \brief The number of support roof layers below this one. - * - * When a contact point is created, it is determined whether the mesh - * needs to be supported with support roof or not, since that is a - * per-mesh setting. This is stored in this variable in order to track - * how far we need to extend that support roof downwards. - */ - int support_roof_layers_below; - int support_floor_layers_above; - int obj_layer_nr; - - /*! - * \brief Whether to try to go towards the build plate. - * - * If the node is inside the collision areas, it has no choice but to go - * towards the model. If it is not inside the collision areas, it must - * go towards the build plate to prevent a scar on the surface. - */ - bool to_buildplate; - - /*! - * \brief The originating node for this one, one layer higher. - * - * In order to prune branches that can't have any support (because they - * can't be on the model and the path to the buildplate isn't clear), - * the entire branch needs to be known. - */ - Node *parent; - Node *child = nullptr; - - /*! - * \brief All neighbours (on the same layer) that where merged into this node. - * - * In order to prune branches that can't have any support (because they - * can't be on the model and the path to the buildplate isn't clear), - * the entire branch needs to be known. - */ - std::list merged_neighbours; - - coordf_t print_z; - coordf_t height; - - bool operator==(const Node& other) const - { - return position == other.position; - } - }; int avg_node_per_layer = 0; float nodes_angle = 0; @@ -418,7 +420,7 @@ private: * save the resulting support polygons to. * \param contact_nodes The nodes to draw as support. */ - void draw_circles(const std::vector>& contact_nodes); + void draw_circles(const std::vector>& contact_nodes); /*! * \brief Drops down the nodes of the tree support towards the build plate. @@ -432,18 +434,18 @@ private: * dropped down. The nodes are dropped to lower layers inside the same * vector of layers. */ - void drop_nodes(std::vector> &contact_nodes); + void drop_nodes(std::vector>& contact_nodes); - void smooth_nodes(std::vector> &contact_nodes); + void smooth_nodes(std::vector>& contact_nodes); - void adjust_layer_heights(std::vector>& contact_nodes); + void adjust_layer_heights(std::vector>& contact_nodes); /*! BBS: MusangKing: maximum layer height * \brief Optimize the generation of tree support by pre-planning the layer_heights * */ - std::vector plan_layer_heights(std::vector> &contact_nodes); + std::vector plan_layer_heights(std::vector>& contact_nodes); /*! * \brief Creates points where support contacts the model. * @@ -457,18 +459,16 @@ private: * \return For each layer, a list of points where the tree should connect * with the model. */ - void generate_contact_points(std::vector>& contact_nodes); + void generate_contact_points(std::vector>& contact_nodes); /*! * \brief Add a node to the next layer. * * If a node is already at that position in the layer, the nodes are merged. */ - void insert_dropped_node(std::vector& nodes_layer, Node* node); + void insert_dropped_node(std::vector& nodes_layer, SupportNode* node); void create_tree_support_layers(); void generate_toolpaths(); - Polygons spanning_tree_to_polygon(const std::vector& spanning_trees, Polygons layer_contours, int layer_nr); - Polygons contact_nodes_to_polygon(const std::vector& contact_nodes, Polygons layer_contours, int layer_nr, std::vector& radiis, std::vector& is_interface); coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor); coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor); From 1d1df4b0a51869430911930adeb605ece99a7ef7 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 26 Jan 2025 22:11:30 +0800 Subject: [PATCH 05/72] ENH: improve tree support 1. add a hook inside tree branches for improved strength 2. fix the issue that interface may fly as a mess (delete the logic where gap nodes can skip dropping down) 3. fix the issue that base nodes may fly as a mess (smoothing should skip polygon nodes, see Jira:STUDIO-4403) Change-Id: Ie9f2039813c2ca3127ed8913304cc455fec8e7ee (cherry picked from commit 83cef5f91d49ff3d275a89ec3df8b5f0fd573f8c) (cherry picked from commit bambulab/BambuStudio@76f876a3c6e9b1b46182408ab66bb1f9c25370f5) Co-authored-by: Arthur --- src/libslic3r/ClipperUtils.cpp | 2 + src/libslic3r/ClipperUtils.hpp | 1 + src/libslic3r/Support/SupportCommon.hpp | 3 + src/libslic3r/Support/SupportParameters.hpp | 5 +- src/libslic3r/Support/TreeSupport.cpp | 535 +++++++++----------- src/libslic3r/Support/TreeSupport.hpp | 28 +- src/libslic3r/Support/TreeSupport3D.hpp | 4 + 7 files changed, 268 insertions(+), 310 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index b9a63e37e7..9082382a54 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -664,6 +664,8 @@ Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &c { return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons diff_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) { return diff(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); } +Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) + { return diff_ex(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); } Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 167449dc21..bc79dca5d5 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -433,6 +433,7 @@ Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygon // Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox(). // To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon. Slic3r::Polygons diff_clipped(const Slic3r::Polygons &src, const Slic3r::Polygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons &src, const Slic3r::Polygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); diff --git a/src/libslic3r/Support/SupportCommon.hpp b/src/libslic3r/Support/SupportCommon.hpp index cdaa43c5db..d292c9cd8f 100644 --- a/src/libslic3r/Support/SupportCommon.hpp +++ b/src/libslic3r/Support/SupportCommon.hpp @@ -50,6 +50,9 @@ SupportGeneratorLayersPtr generate_raft_base( const SupportGeneratorLayersPtr &base_layers, SupportGeneratorLayerStorage &layer_storage); +void fill_expolygons_with_sheath_generate_paths( + ExtrusionEntitiesPtr &dst, const Polygons &polygons, Fill *filler, float density, ExtrusionRole role, const Flow &flow, const SupportParameters& support_params, bool with_sheath, bool no_sort); + // returns sorted layers SupportGeneratorLayersPtr generate_support_layers( PrintObject &object, diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index 98b15dd747..da615ab1fd 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -152,7 +152,9 @@ struct SupportParameters { const coordf_t extrusion_width = object_config.line_width.get_abs_value(nozzle_diameter); support_extrusion_width = object_config.support_line_width.get_abs_value(nozzle_diameter); support_extrusion_width = support_extrusion_width > 0 ? support_extrusion_width : extrusion_width; - + + independent_layer_height = print_config.independent_support_layer_height; + tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI; support_style = object_config.support_style; @@ -241,6 +243,7 @@ struct SupportParameters { float raft_interface_angle(size_t interface_id) const { return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); } + bool independent_layer_height = false; const double thresh_big_overhang = Slic3r::sqr(scale_(10)); }; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 009fd35a89..376589321e 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -29,6 +29,7 @@ #endif #define TAU (2.0 * M_PI) #define NO_INDEX (std::numeric_limits::max()) +#define USE_SUPPORT_3D 0 // #define SUPPORT_TREE_DEBUG_TO_SVG @@ -331,7 +332,8 @@ static void draw_polylines(SupportLayer* ts_layer, Polylines& polylines) // Move point from inside polygon if distance>0, outside if distance<0. // Special case: distance=0 means find the nearest point of from on the polygon contour. // The max move distance should not excceed max_move_distance. -static unsigned int move_inside_expoly(const ExPolygon &polygon, Point& from, double distance = 0, double max_move_distance = std::numeric_limits::max()) +// @return success(true) or not(false) +static bool move_inside_expoly(const ExPolygon &polygon, Point& from, double distance = 0, double max_move_distance = std::numeric_limits::max()) { //TODO: This is copied from the moveInside of Polygons. /* @@ -347,7 +349,7 @@ static unsigned int move_inside_expoly(const ExPolygon &polygon, Point& from, do if (contour.points.size() < 2) { - return 0; + return false; } Point p0 = contour.points[polygon.contour.size() - 2]; Point p1 = contour.points.back(); @@ -444,12 +446,14 @@ static unsigned int move_inside_expoly(const ExPolygon &polygon, Point& from, do { from = ret; } + return true; } else if (bestDist2 < max_move_distance * max_move_distance) { from = ret; + return true; } - return 0; + return false; } /* @@ -622,6 +626,7 @@ static bool is_inside_ex(const ExPolygons &polygons, const Point &pt) return false; } +// use project_onto which is more accurate but more expensive static bool move_out_expolys(const ExPolygons& polygons, Point& from, double distance, double max_move_distance) { Point from0 = from; @@ -810,6 +815,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) if (!is_tree(stype)) return; max_cantilever_dist = 0; + m_highest_overhang_layer = 0; // main part of overhang detection can be parallel tbb::parallel_for(tbb::blocked_range(0, m_object->layer_count()), @@ -1125,7 +1131,10 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) } } - if (!ts_layer->overhang_areas.empty()) has_overhangs = true; + if (!ts_layer->overhang_areas.empty()) { + has_overhangs = true; + m_highest_overhang_layer = std::max(m_highest_overhang_layer, size_t(layer_nr)); + } if (!layer->cantilevers.empty()) has_cantilever = true; } @@ -1383,7 +1392,7 @@ void TreeSupport::generate_toolpaths() { ExtrusionRole raft_contour_er = m_slicing_params.base_raft_layers > 0 ? erSupportMaterial : erSupportMaterialInterface; SupportLayer *ts_layer = m_object->support_layers().front(); - Flow flow = m_object->print()->brim_flow(); + Flow flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter); Polygons loops; for (const ExPolygon& expoly : raft_areas) { @@ -1399,7 +1408,7 @@ void TreeSupport::generate_toolpaths() SupportLayer *ts_layer = m_object->get_support_layer(layer_nr); coordf_t expand_offset = (layer_nr == 0 ? 0. : -1.); - Flow support_flow = layer_nr == 0 ? m_object->print()->brim_flow() : Flow(support_extrusion_width, ts_layer->height, nozzle_diameter); + Flow support_flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter); Fill* filler_interface = Fill::new_from_type(ipRectilinear); filler_interface->angle = layer_nr == 0 ? 90 : 0; filler_interface->spacing = support_extrusion_width; @@ -1517,27 +1526,34 @@ void TreeSupport::generate_toolpaths() bool need_infill = with_infill; if(m_object_config->support_base_pattern==smpDefault) need_infill &= area_group.need_infill; - if (layer_id>0 && area_group.dist_to_top < 10 && !need_infill && m_support_params.support_style!=smsTreeHybrid) { - if (area_group.dist_to_top < 5) // 1 wall at the top <5mm - make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, 1, flow, erSupportMaterial); - else // at least 2 walls for range [5,10) - make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, std::max(wall_count, size_t(2)), flow, erSupportMaterial); - } - else if (layer_id > 0 && need_infill && m_support_params.base_fill_pattern != ipLightning) { - std::shared_ptr filler_support = std::shared_ptr(Fill::new_from_type(m_support_params.base_fill_pattern)); - filler_support->set_bounding_box(bbox_object); - filler_support->spacing = object_config.support_base_pattern_spacing.value * support_density;// constant spacing to align support infill lines - filler_support->angle = Geometry::deg2rad(object_config.support_angle.value); - + size_t walls = wall_count; + if (layer_id == 0) walls = std::numeric_limits::max(); + else if (area_group.need_extra_wall && walls < 2) walls += 1; + std::shared_ptr filler_support = std::shared_ptr(Fill::new_from_type(layer_id == 0 ? ipConcentric : m_support_params.base_fill_pattern)); + filler_support->set_bounding_box(bbox_object); + filler_support->spacing = object_config.support_base_pattern_spacing.value * support_density;// constant spacing to align support infill lines + filler_support->angle = Geometry::deg2rad(object_config.support_angle.value); + if (need_infill && m_support_params.base_fill_pattern != ipLightning) { // allow infill-only mode if support is thick enough (so min_wall_count is 0); // otherwise must draw 1 wall size_t min_wall_count = offset(poly, -scale_(support_spacing * 1.5)).empty() ? 1 : 0; - make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(min_wall_count, wall_count), flow, + make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(min_wall_count, walls), flow, erSupportMaterial, filler_support.get(), support_density); } else { - make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, - layer_id > 0 ? wall_count : std::numeric_limits::max(), flow, erSupportMaterial); + Polygons loops; + loops.emplace_back(poly.contour); + if (layer_id == 0) { + float density = float(m_object_config->raft_first_layer_density.value * 0.01); + fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, filler_support.get(), density, erSupportMaterial, flow, m_support_params, true, false); + } + else { + for (size_t i = 1; i < walls; i++) { + Polygons contour_new = offset(poly.contour, -(i-0.5f) * flow.scaled_spacing(), jtSquare); + loops.insert(loops.end(), contour_new.begin(), contour_new.end()); + } + fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, nullptr, 0, erSupportMaterial, flow, m_support_params, true, false); + } } } @@ -1611,8 +1627,6 @@ void TreeSupport::generate() return; } - std::vector> contact_nodes(m_object->layers().size()); - profiler.stage_start(STAGE_total); // Generate overhang areas @@ -1627,23 +1641,22 @@ void TreeSupport::generate() m_ts_data->is_slim = is_slim; // Generate contact points of tree support + std::vector> contact_nodes(m_object->layers().size()); + + std::vector move_bounds(m_highest_overhang_layer + 1); profiler.stage_start(STAGE_GENERATE_CONTACT_NODES); m_object->print()->set_status(56, _L("Support: generate contact points")); - generate_contact_points(contact_nodes); + generate_contact_points(contact_nodes, move_bounds); profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES); + //Drop nodes to lower layers. profiler.stage_start(STAGE_DROP_DOWN_NODES); m_object->print()->set_status(60, _L("Support: propagate branches")); drop_nodes(contact_nodes); profiler.stage_finish(STAGE_DROP_DOWN_NODES); - smooth_nodes(contact_nodes); - -if (!m_object->config().tree_support_adaptive_layer_height) - // Adjust support layer heights - adjust_layer_heights(contact_nodes); - + smooth_nodes(contact_nodes); // , tree_support_3d_config); //Generate support areas. profiler.stage_start(STAGE_DRAW_CIRCLES); @@ -1691,7 +1704,7 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, size_t layers_to_ return radius; } -coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor) +coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor, bool use_min_distance) { double radius; coordf_t tip_height = base_radius;// this is a 45 degree tip @@ -1712,11 +1725,63 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_to return radius; } +ExPolygons TreeSupport::get_avoidance(coordf_t radius, size_t obj_layer_nr) +{ +#if USE_SUPPORT_3D + if (m_model_volumes) { + bool on_build_plate = m_object_config->support_on_build_plate_only.value; + const Polygons& avoid_polys = m_model_volumes->getAvoidance(radius, obj_layer_nr, TreeSupport3D::TreeModelVolumes::AvoidanceType::Fast, on_build_plate, true); + ExPolygons expolys; + for (auto& poly : avoid_polys) + expolys.emplace_back(std::move(poly)); + return expolys; + } + return ExPolygons(); +#else + return m_ts_data->get_avoidance(radius, obj_layer_nr); +#endif +} +ExPolygons TreeSupport::get_collision(coordf_t radius, size_t layer_nr) +{ +#if USE_SUPPORT_3D + if (m_model_volumes) { + bool on_build_plate = m_object_config->support_on_build_plate_only.value; + const Polygons& collision_polys = m_model_volumes->getCollision(radius, layer_nr, true); + ExPolygons expolys; + for (auto& poly : collision_polys) + expolys.emplace_back(std::move(poly)); + return expolys; + } +#else + return m_ts_data->get_collision(radius, layer_nr); +#endif + return ExPolygons(); +} +Polygons TreeSupport::get_collision_polys(coordf_t radius, size_t layer_nr) +{ +#if USE_SUPPORT_3D + if (m_model_volumes) { + bool on_build_plate = m_object_config->support_on_build_plate_only.value; + const Polygons& collision_polys = m_model_volumes->getCollision(radius, layer_nr, true); + return collision_polys; + } +#else + ExPolygons expolys = m_ts_data->get_collision(radius, layer_nr); + Polygons polys; + for (auto& expoly : expolys) + polys.emplace_back(std::move(expoly.contour)); + return polys; +#endif + return Polygons(); +} + template // RegionType could be ExPolygons or Polygons ExPolygons avoid_object_remove_extra_small_parts(ExPolygons &expolys, const RegionType&avoid_region) { ExPolygons expolys_out; + if(expolys.empty()) return expolys_out; + auto clipped_avoid_region=ClipperUtils::clip_clipper_polygons_with_subject_bbox(avoid_region, get_extents(expolys)); for (auto expoly : expolys) { - auto expolys_avoid = diff_ex(expoly, avoid_region); + auto expolys_avoid = diff_ex(expoly, clipped_avoid_region); int idx_max_area = -1; float max_area = 0; for (int i = 0; i < expolys_avoid.size(); ++i) { @@ -1815,7 +1880,7 @@ void TreeSupport::draw_circles(const std::vector>& con int bottom_gap_layers = round(m_slicing_params.gap_object_support / m_slicing_params.layer_height); const coordf_t branch_radius = config.tree_support_branch_diameter.value / 2; const coordf_t branch_radius_scaled = scale_(branch_radius); - bool on_buildplate_only = config.support_on_build_plate_only.value; + bool on_buildplate_only = m_object_config->support_on_build_plate_only.value; Polygon branch_circle; //Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time. // Use square support if there are too many nodes per layer because circle support needs much longer time to compute @@ -1861,7 +1926,7 @@ void TreeSupport::draw_circles(const std::vector>& con // coconut: previously std::unordered_map in m_collision_cache is not multi-thread safe which may cause programs stuck, here we change to tbb::concurrent_unordered_map tbb::parallel_for( - tbb::blocked_range(0, m_object->layer_count()), + tbb::blocked_range(0, m_object->layer_count(), m_object->layer_count()), [&](const tbb::blocked_range& range) { for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) @@ -1912,10 +1977,10 @@ void TreeSupport::draw_circles(const std::vector>& con // 1) node is a normal part of hybrid support // 2) node is virtual if (node.type == ePolygon || node.distance_to_top<0) { - if (node.overhang->contour.size() > 100 || node.overhang->holes.size()>1) - area.emplace_back(*node.overhang); + if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1) + area.emplace_back(node.overhang); else { - area = offset_ex({ *node.overhang }, scale_(m_ts_data->m_xy_distance)); + area = offset_ex({ node.overhang }, scale_(m_ts_data->m_xy_distance)); } if (node.type == ePolygon) has_polygon_node = true; @@ -1923,7 +1988,7 @@ void TreeSupport::draw_circles(const std::vector>& con else { Polygon circle; size_t layers_to_top = node.distance_to_top; - double scale = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor) / branch_radius; + double scale = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor, false) / branch_radius; if (/*is_slim*/1) { // draw ellipse along movement direction double moveX = node.movement.x() / (scale * branch_radius_scaled); @@ -1949,18 +2014,20 @@ void TreeSupport::draw_circles(const std::vector>& con ? layers_to_top * layer_height / (scale * branch_radius) * 0.5 : config.tree_support_brim_width; - circle = offset(circle, scale_(brim_width))[0]; + auto tmp=offset(circle, scale_(brim_width)); + if(!tmp.empty()) + circle = tmp[0]; } area.emplace_back(ExPolygon(circle)); // merge overhang to get a smoother interface surface // Do not merge when buildplate_only is on, because some underneath nodes may have been deleted. if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only) { ExPolygons overhang_expanded; - if (node.overhang->contour.size() > 100 || node.overhang->holes.size()>1) - overhang_expanded.emplace_back(*node.overhang); + if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1) + overhang_expanded.emplace_back(node.overhang); else { // 对于有缺陷的模型,overhang膨胀以后可能是空的! - overhang_expanded = offset_ex({ *node.overhang }, scale_(m_ts_data->m_xy_distance)); + overhang_expanded = offset_ex({ node.overhang }, scale_(m_ts_data->m_xy_distance)); } append(area, overhang_expanded); } @@ -2013,21 +2080,21 @@ void TreeSupport::draw_circles(const std::vector>& con roof_1st_layer = avoid_object_remove_extra_small_parts(roof_1st_layer, avoid_region_interface); } else { - roof_areas = std::move(diff_ex(roof_areas, avoid_region_interface)); - roof_1st_layer = std::move(diff_ex(roof_1st_layer, avoid_region_interface)); + roof_areas = diff_ex(roof_areas, ClipperUtils::clip_clipper_polygons_with_subject_bbox(avoid_region_interface, get_extents(roof_areas))); + roof_1st_layer = diff_ex(roof_1st_layer, ClipperUtils::clip_clipper_polygons_with_subject_bbox(avoid_region_interface, get_extents(roof_1st_layer))); } roof_areas = intersection_ex(roof_areas, m_machine_border); // roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer - roof_1st_layer = std::move(diff_ex(roof_1st_layer, roof_areas)); + roof_1st_layer = diff_ex(roof_1st_layer, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roof_areas,get_extents(roof_1st_layer))); roof_1st_layer = intersection_ex(roof_1st_layer, m_machine_border); // let supports touch objects when brim is on - auto avoid_region = m_ts_data->get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr); - base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region); - base_areas = std::move(diff_ex(base_areas, roof_areas)); - base_areas = std::move(diff_ex(base_areas, roof_1st_layer)); - base_areas = std::move(diff_ex(base_areas, roof_gap_areas)); + auto avoid_region = get_collision_polys((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr); + //base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region); + base_areas = diff_clipped(base_areas, avoid_region); + ExPolygons roofs; append(roofs, roof_1st_layer); append(roofs, roof_areas);append(roofs, roof_gap_areas); + base_areas = diff_ex(base_areas, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roofs, get_extents(base_areas))); base_areas = intersection_ex(base_areas, m_machine_border); if (SQUARE_SUPPORT) { @@ -2255,7 +2322,7 @@ void TreeSupport::draw_circles(const std::vector>& con // make sure 1) roof1 and object 2) roof1 and roof, won't intersect // Note: We can't replace roof1 directly, as we have recorded its address. // So instead we need to replace its members one by one. - auto tmp1 = diff_ex(roof1, m_ts_data->get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr)); + auto tmp1 = diff_ex(roof1, get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr)); tmp1 = diff_ex(tmp1, ts_layer->roof_areas); if (!tmp1.empty()) { roof1.contour = std::move(tmp1[0].contour); @@ -2327,8 +2394,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod if (angle > 30.0 && node->radius > MIN_BRANCH_RADIUS) angle = (node->radius - MIN_BRANCH_RADIUS) / (MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS) * (config.tree_support_branch_angle.value - 30.0) + 30.0; double tan_angle = tan(angle * M_PI / 180); - int wall_count_ = node->radius > 2 * support_line_width ? wall_count : 1; - node->max_move_dist = (angle < 90) ? (coordf_t) (tan_angle * node->height) * wall_count_ : std::numeric_limits::max(); + node->max_move_dist = (angle < 90) ? (coordf_t) (tan_angle * node->height) : std::numeric_limits::max(); node->max_move_dist = std::min(node->max_move_dist, support_extrusion_width); move_dist = node->max_move_dist; } @@ -2344,50 +2410,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod m_spanning_trees.resize(contact_nodes.size()); //m_mst_line_x_layer_contour_caches.resize(contact_nodes.size()); - if (0) - {// get outlines below and avoidance area using tbb - // This part only takes very little time, so we disable it. - typedef std::chrono::high_resolution_clock clock_; - typedef std::chrono::duration > second_; - std::chrono::time_point t0{ clock_::now() }; - - // get all the possible radiis - std::vector > all_layer_radius(m_highest_overhang_layer+1); - std::vector> all_layer_node_dist(m_highest_overhang_layer + 1); - for (size_t layer_nr = m_highest_overhang_layer; layer_nr > 0; layer_nr--) - { - if (layer_heights[layer_nr].height < EPSILON) continue; - auto& layer_radius = all_layer_radius[layer_nr]; - auto& layer_node_dist = all_layer_node_dist[layer_nr]; - for (SupportNode *p_node : contact_nodes[layer_nr]) { - layer_node_dist.emplace(p_node->dist_mm_to_top); - } - size_t layer_nr_next = layer_heights[layer_nr].next_layer_nr; - if (layer_nr_next <= m_highest_overhang_layer && layer_nr_next>0) { - for (auto node_dist : layer_node_dist) - all_layer_node_dist[layer_nr_next].emplace(node_dist + layer_heights[layer_nr].height); - } - for (auto node_dist : layer_node_dist) { - layer_radius.emplace(calc_branch_radius(branch_radius, node_dist, diameter_angle_scale_factor)); - } - } - // parallel pre-compute avoidance - //tbb::parallel_for(tbb::blocked_range(1, m_highest_overhang_layer), [&](const tbb::blocked_range &range) { - //for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { - for (size_t layer_nr = 0; layer_nr < all_layer_radius.size(); layer_nr++) { - BOOST_LOG_TRIVIAL(debug) << "pre calculate_avoidance layer=" << layer_nr; - for (auto node_radius : all_layer_radius[layer_nr]) { - m_ts_data->get_avoidance(0, layer_nr); - m_ts_data->get_avoidance(node_radius, layer_nr); - } - } - //}); - - double duration{ std::chrono::duration_cast(clock_::now() - t0).count() }; - BOOST_LOG_TRIVIAL(debug) << "before m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size() - << ", takes " << duration << " secs."; - } - for (size_t layer_nr = contact_nodes.size() - 1; layer_nr > 0; layer_nr--) // Skip layer 0, since we can't drop down the vertices there. { if (m_object->print()->canceled()) @@ -2436,15 +2458,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod { const SupportNode& node = *p_node; - if (node.distance_to_top < 0) { - // gap nodes do not merge or move - SupportNode* next_node = new SupportNode(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, p_node->to_buildplate, p_node, - print_z_next, height_next); - get_max_move_dist(next_node); - next_node->is_merged = false; - contact_nodes[layer_nr_next].emplace_back(next_node); - continue; - } if (support_on_buildplate_only && !node.to_buildplate) //Can't rest on model and unable to reach the build plate. Then we must drop the node and leave parts unsupported. { unsupported_branch_leaves.push_front({ layer_nr, p_node }); @@ -2530,14 +2543,13 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod coord_t neighbour_radius = scale_(calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top, diameter_angle_scale_factor)); Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius), pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0); - if (is_inside_ex(*node.overhang, neighbour) && is_inside_ex(*node.overhang, pt_north) && is_inside_ex(*node.overhang, pt_south) - && is_inside_ex(*node.overhang, pt_west) && is_inside_ex(*node.overhang, pt_east)){ + if (is_inside_ex(node.overhang, neighbour) && is_inside_ex(node.overhang, pt_north) && is_inside_ex(node.overhang, pt_south) + && is_inside_ex(node.overhang, pt_west) && is_inside_ex(node.overhang, pt_east)){ node.distance_to_top = std::max(node.distance_to_top, neighbour_node->distance_to_top); node.support_roof_layers_below = std::max(node.support_roof_layers_below, neighbour_node->support_roof_layers_below); node.dist_mm_to_top = std::max(node.dist_mm_to_top, neighbour_node->dist_mm_to_top); node.merged_neighbours.push_front(neighbour_node); node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end()); - node.is_merged = true; to_delete.insert(neighbour_node); } } @@ -2550,7 +2562,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor); - auto avoid_layer = m_ts_data->get_avoidance(branch_radius_node, layer_nr_next); + auto avoid_layer = get_avoidance(branch_radius_node, layer_nr_next); if (group_index == 0) { //Avoid collisions. @@ -2566,12 +2578,12 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod node_ = p_node->parent ? p_node : neighbour; // Make sure the next pass doesn't drop down either of these (since that already happened). node_->merged_neighbours.push_front(node_ == p_node ? neighbour : p_node); - const bool to_buildplate = !is_inside_ex(m_ts_data->get_avoidance(0, layer_nr_next), next_position); + //const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_position); + const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr_next], next_position); SupportNode * next_node = new SupportNode(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_, print_z_next, height_next); next_node->movement = next_position - node.position; get_max_move_dist(next_node); - next_node->is_merged = true; contact_nodes[layer_nr_next].push_back(next_node); @@ -2593,7 +2605,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod node.dist_mm_to_top = std::max(node.dist_mm_to_top, neighbour_node->dist_mm_to_top); node.merged_neighbours.push_front(neighbour_node); node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end()); - node.is_merged = true; to_delete.insert(neighbour_node); } } @@ -2615,7 +2626,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod SupportNode * next_node = new SupportNode(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); next_node->max_move_dist = 0; - next_node->is_merged = false; contact_nodes[layer_nr_next].emplace_back(next_node); continue; } @@ -2633,9 +2643,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod unsupported_branch_leaves.push_front({ layer_nr, p_node }); } else { - SupportNode* pn = p_node; - for (int i = 0; i <= bottom_interface_layers && pn; i++, pn = pn->parent) - pn->support_floor_layers_above = bottom_interface_layers - i + 1; // +1 so the parent node has support_floor_layers_above=2 to_delete.insert(p_node); } continue; @@ -2643,9 +2650,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod // if the link between parent and current is cut by contours, mark current as bottom contact node if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false) { - SupportNode* pn = p_node->parent; - for (int i = 0; i <= bottom_interface_layers && pn; i++, pn = pn->parent) - pn->support_floor_layers_above = bottom_interface_layers - i + 1; to_delete.insert(p_node); continue; } @@ -2703,7 +2707,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod branch_radius_temp = branch_radius_node; } #endif - auto avoid_layer = m_ts_data->get_avoidance(branch_radius_node, layer_nr_next); + auto avoid_layer = get_avoidance(branch_radius_node, layer_nr_next); Point to_outside = projection_onto(avoid_layer, node.position); Point direction_to_outer = to_outside - node.position; @@ -2755,27 +2759,26 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } } - const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], next_layer_vertex);// !is_inside_ex(m_ts_data->get_avoidance(m_ts_data->m_xy_distance, layer_nr - 1), next_layer_vertex); + const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], next_layer_vertex); SupportNode * next_node = new SupportNode(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); next_node->movement = movement; get_max_move_dist(next_node); - next_node->is_merged = false; contact_nodes[layer_nr_next].push_back(next_node); } } #ifdef SUPPORT_TREE_DEBUG_TO_SVG if (contact_nodes[layer_nr].empty() == false) { - draw_contours_and_nodes_to_svg((boost::format("%.2f") % contact_nodes[layer_nr][0]->print_z).str(), m_ts_data->get_avoidance(0, layer_nr), - m_ts_data->get_avoidance(branch_radius_temp, layer_nr), + draw_contours_and_nodes_to_svg((boost::format("%.2f") % contact_nodes[layer_nr][0]->print_z).str(), get_avoidance(0, layer_nr), + get_avoidance(branch_radius_temp, layer_nr), m_ts_data->m_layer_outlines_below[layer_nr], contact_nodes[layer_nr], contact_nodes[layer_nr_next], "contact_points", { "overhang","avoid","outline" }, { "blue","red","yellow" }); BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer " << layer_nr << ", print_z=" << ts_layer->print_z; for (size_t i = 0; i < std::min(size_t(5), contact_nodes[layer_nr].size()); i++) { auto &node = contact_nodes[layer_nr][i]; - BOOST_LOG_TRIVIAL(debug) << "\t node " << i << ", pos=" << node->position << ", move = " << node->movement << ", is_merged=" << node->is_merged; + BOOST_LOG_TRIVIAL(debug) << "\t node " << i << ", pos=" << node->position << ", move = " << node->movement; } } #endif @@ -2792,10 +2795,13 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod if (to_erase != contact_nodes[i_layer].end()) { // update the parent-child chain - if(i_node->parent) - i_node->parent->child = i_node->child; - if(i_node->child) - i_node->child->parent = i_node->parent; + for (SupportNode* parent : i_node->parents) { + if (parent->child==i_node) + parent->child = i_node->child; + } + if (i_node->child) { + i_node->child->parents= i_node->parents; + } contact_nodes[i_layer].erase(to_erase); to_free_node_set.insert(i_node); @@ -2824,47 +2830,66 @@ void TreeSupport::smooth_nodes(std::vector> &contact_ if (curr_layer_nodes.empty()) continue; for (SupportNode *node : curr_layer_nodes) { node->is_processed = false; - if (layer_nr == 0) node->is_merged = true; // nodes on plate are also merged nodes } } - + + float max_move = scale_(m_object_config->support_line_width / 2); for (int layer_nr = 0; layer_nr< contact_nodes.size(); layer_nr++) { std::vector &curr_layer_nodes = contact_nodes[layer_nr]; if (curr_layer_nodes.empty()) continue; for (SupportNode *node : curr_layer_nodes) { if (!node->is_processed) { std::vector pts; + std::vector radii; std::vector branch; SupportNode * p_node = node; - // add a fixed head - if (node->child) { + // add a fixed head if it's not a polygon node, see STUDIO-4403 + // Polygon node can't be added because the move distance might be huge, making the nodes in between jump and dangling + if (node->child && node->child->type!=ePolygon) { pts.push_back(p_node->child->position); + radii.push_back(p_node->child->radius); branch.push_back(p_node->child); } do { pts.push_back(p_node->position); + radii.push_back(p_node->radius); branch.push_back(p_node); p_node = p_node->parent; } while (p_node && !p_node->is_processed); if (pts.size() < 3) continue; std::vector pts1 = pts; + std::vector radii1 = radii; // TODO here we assume layer height gap is constant. If not true, need to consider height jump const int iterations = 100; for (size_t k = 0; k < iterations; k++) { for (size_t i = 1; i < pts.size() - 1; i++) { size_t i2 = i >= 2 ? i - 2 : 0; size_t i3 = i < pts.size() - 2 ? i + 2 : pts.size() - 1; - Point pt = (pts[i2] + pts[i - 1] + pts[i] + pts[i + 1] + pts[i3]) / 5; + Point pt = ( pts[i - 1] + pts[i] + pts[i + 1] ) / 3; pts1[i] = pt; + radii1[i] = (radii[i - 1] + radii[i] + radii[i + 1] ) / 3; if (k == iterations - 1) { branch[i]->position = pt; + branch[i]->radius = radii1[i]; branch[i]->movement = (pts[i + 1] - pts[i - 1]) / 2; branch[i]->is_processed = true; + if (branch[i]->parents.size()>1 || (branch[i]->movement.x() > max_move || branch[i]->movement.y() > max_move)) + branch[i]->need_extra_wall = true; + BOOST_LOG_TRIVIAL(info) << "smooth_nodes: layer_nr=" << layer_nr << ", i=" << i << ", pt=" << pt << ", movement=" << branch[i]->movement << ", radius=" << branch[i]->radius; } } - if (k < iterations - 1) + if (k < iterations - 1) { std::swap(pts, pts1); + std::swap(radii, radii1); + } + else { + // interpolate need_extra_wall in the end + for (size_t i = 1; i < branch.size() - 1; i++) { + if (branch[i - 1]->need_extra_wall && branch[i + 1]->need_extra_wall) + branch[i]->need_extra_wall = true; + } + } } } } @@ -2905,116 +2930,22 @@ void TreeSupport::smooth_nodes(std::vector> &contact_ #endif } -void TreeSupport::adjust_layer_heights(std::vector>& contact_nodes) -{ - if (contact_nodes.empty()) - return; - - const PrintConfig& print_config = m_object->print()->config(); - const PrintObjectConfig& config = m_object->config(); - // don't merge layers for Vine support, or the branches will be unsmooth - // TODO can we merge layers in a way that guaranttees smoothness? - if (!print_config.independent_support_layer_height || is_slim) { - for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { - std::vector& curr_layer_nodes = contact_nodes[layer_nr]; - for (SupportNode* node : curr_layer_nodes) { - node->print_z = m_object->get_layer(layer_nr)->print_z; - node->height = m_object->get_layer(layer_nr)->height; - } - } - return; - } - - // extreme layer_id - std::vector extremes; - const coordf_t layer_height = config.layer_height.value; - const coordf_t max_layer_height = m_slicing_params.max_layer_height; - const size_t bot_intf_layers = config.support_interface_bottom_layers.value; - const size_t top_intf_layers = config.support_interface_top_layers.value; - - // if already using max layer height, no need to adjust - if (layer_height == max_layer_height) return; - - extremes.push_back(0); - for (SupportNode* node : contact_nodes[0]) { - node->print_z = m_object->get_layer(0)->print_z; - node->height = m_object->get_layer(0)->height; - } - - for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) { - std::vector& curr_layer_nodes = contact_nodes[layer_nr]; - for (SupportNode* node : curr_layer_nodes) { - if (node->support_roof_layers_below >0 || node->support_floor_layers_above == bot_intf_layers) { - extremes.push_back(layer_nr); - break; - } - } - - if (extremes.back() == layer_nr) { - // contact layer use the same print_z and layer height with object layer - for (SupportNode* node : curr_layer_nodes) { - node->print_z = m_object->get_layer(layer_nr)->print_z; - node->height = m_object->get_layer(layer_nr)->height; - } - } - } - - // schedule new layer heights and print_z - for (size_t idx_extreme = 0; idx_extreme < extremes.size(); idx_extreme++) { - int extr2_layer_nr = extremes[idx_extreme]; - coordf_t extr2z = m_object->get_layer(extr2_layer_nr)->bottom_z(); - int extr1_layer_nr = idx_extreme == 0 ? -1 : extremes[idx_extreme - 1]; - coordf_t extr1z = idx_extreme == 0 ? 0.f : m_object->get_layer(extr1_layer_nr)->print_z; - coordf_t dist = extr2z - extr1z; - - // Insert intermediate layers. - size_t n_layers_extra = size_t(ceil(dist / m_slicing_params.max_suport_layer_height)); - if (n_layers_extra <= 1) - continue; - - coordf_t step = dist / coordf_t(n_layers_extra); - coordf_t print_z = extr1z + step; - assert(step >= layer_height - EPSILON); - for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; layer_nr++) { - std::vector& curr_layer_nodes = contact_nodes[layer_nr]; - if (curr_layer_nodes.empty()) continue; - - if (std::abs(print_z - curr_layer_nodes[0]->print_z) < step / 2 + EPSILON) { - for (SupportNode* node : curr_layer_nodes) { - node->print_z = print_z; - node->height = step; - } - print_z += step; - } - else { - // can't clear curr_layer_nodes, or the model will have empty layers - for (SupportNode* node : curr_layer_nodes) { - node->print_z = 0.0; - node->height = 0.0; - } - } - } - } -} - std::vector TreeSupport::plan_layer_heights(std::vector> &contact_nodes) { - const PrintObjectConfig& config = m_object->config(); - const PrintConfig & print_config = m_object->print()->config(); const coordf_t max_layer_height = m_slicing_params.max_layer_height; - const coordf_t layer_height = config.layer_height.value; + const coordf_t layer_height = m_object_config->layer_height.value; coordf_t z_distance_top = m_slicing_params.gap_support_object; // BBS: add extra distance if thick bridge is enabled // Note: normal support uses print_z, but tree support uses integer layers, so we need to subtract layer_height if (!m_slicing_params.soluble_interface && m_object_config->thick_bridges) { - z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(m_object->print()->config()) - layer_height; + z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(*m_print_config) - layer_height; } - const size_t support_roof_layers = config.support_interface_top_layers.value; + const size_t support_roof_layers = m_object_config->support_interface_top_layers.value; const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1; std::vector layer_heights(contact_nodes.size()); std::vector bounds; - if (!config.tree_support_adaptive_layer_height || layer_height == max_layer_height || !print_config.independent_support_layer_height) { + if (!m_object_config->tree_support_adaptive_layer_height || layer_height == max_layer_height || !m_support_params.independent_layer_height) { for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { layer_heights[layer_nr] = {m_object->get_layer(layer_nr)->print_z, m_object->get_layer(layer_nr)->height, layer_nr > 0 ? size_t(layer_nr - 1) : 0}; } @@ -3096,7 +3027,7 @@ std::vector TreeSupport::plan_layer_heights(std::vector>& contact_nodes) +void TreeSupport::generate_contact_points(std::vector>& contact_nodes, const std::vector& move_bounds) { const PrintObjectConfig &config = m_object->config(); const coordf_t point_spread = scale_(config.tree_support_branch_distance.value); @@ -3144,7 +3075,6 @@ void TreeSupport::generate_contact_points(std::vector> if (m_object->layers().size() <= z_distance_top_layers + 1) return; - m_highest_overhang_layer = 0; int nonempty_layers = 0; std::vector all_nodes; for (size_t layer_nr = 1; layer_nr < m_object->layers().size(); layer_nr++) @@ -3157,7 +3087,7 @@ void TreeSupport::generate_contact_points(std::vector> if (overhang.empty()) continue; - m_highest_overhang_layer = std::max(m_highest_overhang_layer, layer_nr); + std::unordered_set already_inserted; auto print_z = m_object->get_layer(layer_nr)->print_z; auto height = m_object->get_layer(layer_nr)->height; @@ -3172,92 +3102,90 @@ void TreeSupport::generate_contact_points(std::vector> SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z, height, z_distance_top); contact_node->type = ePolygon; - contact_node->overhang = &overhang_part; + contact_node->overhang = overhang_part; curr_nodes.emplace_back(contact_node); continue; } } - overhang_bounds.inflated(half_overhang_distance); - bool added = false; //Did we add a point this way? - for (Point candidate : grid_points) - { - if (overhang_bounds.contains(candidate)) - { - // BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported! - bool is_inside = is_inside_ex(overhang_part, candidate); - if (!is_inside) { - constexpr coordf_t distance_inside = 0; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that - // fall between overhang areas on constant surfaces. - move_inside_expoly(overhang_part, candidate, distance_inside, half_overhang_distance); - is_inside = is_inside_ex(overhang_part, candidate); - } - if (is_inside) - { - // collision radius has to be 0 or the supports are too few at curved slopes - //if (!is_inside_ex(m_ts_data->get_collision(0, layer_nr), candidate)) - { - constexpr bool to_buildplate = true; - SupportNode * contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, - SupportNode::NO_PARENT, print_z, height, z_distance_top); - contact_node->overhang = &overhang_part; - curr_nodes.emplace_back(contact_node); - added = true; - } - } - } - } - - if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported. - { - auto bbox = overhang_part.contour.bounding_box(); - Points candidates; - if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected) - candidates = {bbox.min, bounding_box_middle(bbox), bbox.max}; - else - candidates = {bounding_box_middle(bbox)}; - - for (Point candidate : candidates) { - if (!overhang_part.contains(candidate)) - move_inside_expoly(overhang_part, candidate); - constexpr bool to_buildplate = true; - SupportNode *contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, SupportNode::NO_PARENT, - print_z, height, z_distance_top); - contact_node->overhang = &overhang_part; - curr_nodes.emplace_back(contact_node); - } - } // add supports at corners for both auto and manual overhangs, github #2008 if (/*ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected*/1) { // add points at corners - auto &points = overhang_part.contour.points; + auto& points = overhang_part.contour.points; int nSize = points.size(); for (int i = 0; i < nSize; i++) { auto pt = points[i]; auto v1 = (pt - points[(i - 1 + nSize) % nSize]).cast().normalized(); auto v2 = (pt - points[(i + 1) % nSize]).cast().normalized(); if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees - SupportNode *contact_node = new SupportNode(pt, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z, - height, z_distance_top); - contact_node->overhang = &overhang_part; + SupportNode* contact_node = new SupportNode(pt, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z, + height, z_distance_top); + contact_node->overhang = overhang_part; contact_node->is_corner = true; curr_nodes.emplace_back(contact_node); + + Point hash_pos = pt / ((m_min_radius + 1) / 1); + already_inserted.emplace(hash_pos); + } + } + } + auto& move_bounds_layer = move_bounds[layer_nr]; + if (move_bounds_layer.empty()) { + overhang_bounds.inflated(half_overhang_distance); + bool added = false; //Did we add a point this way? + for (Point candidate : grid_points) { + if (overhang_bounds.contains(candidate)) { + // BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported! + bool is_inside = is_inside_ex(overhang_part, candidate); + if (!is_inside) { + constexpr coordf_t distance_inside = 0; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that + // fall between overhang areas on constant surfaces. + is_inside = move_inside_expoly(overhang_part, candidate, distance_inside, half_overhang_distance); + } + if (is_inside) { + // normalize the point a bit to also catch points which are so close that inserting it would achieve nothing + Point hash_pos = candidate / ((m_min_radius + 1) / 1); + if (!already_inserted.count(hash_pos)) { + already_inserted.emplace(hash_pos); + constexpr bool to_buildplate = true; + SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, + SupportNode::NO_PARENT, print_z, height, z_distance_top); + contact_node->overhang=overhang_part; + curr_nodes.emplace_back(contact_node); + added = true; + } + } + } + } + if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported. + { + auto bbox = overhang_part.contour.bounding_box(); + Points candidates; + if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected) + candidates = { bbox.min, bounding_box_middle(bbox), bbox.max }; + else + candidates = { bounding_box_middle(bbox) }; + for (Point candidate : candidates) { + if (!overhang_part.contains(candidate)) + move_inside_expoly(overhang_part, candidate); + constexpr bool to_buildplate = true; + SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, SupportNode::NO_PARENT, + print_z, height, z_distance_top); + contact_node->overhang=overhang_part; + curr_nodes.emplace_back(contact_node); } } - } - if(ts_layer->overhang_types[&overhang_part] == SupportLayer::Enforced || is_slim){ - // remove close points in Enforcers - // auto above_nodes = contact_nodes[layer_nr - 1]; - if (!curr_nodes.empty() /*&& !above_nodes.empty()*/) { + if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Enforced || is_slim) { + // remove close points for (auto it = curr_nodes.begin(); it != curr_nodes.end();) { bool is_duplicate = false; if (!(*it)->is_corner) { Slic3r::Vec3f curr_pt((*it)->position(0), (*it)->position(1), scale_((*it)->print_z)); - for (auto &pt : all_nodes) { + for (auto& pt : all_nodes) { auto dif = curr_pt - pt; if (dif.norm() < point_spread / 2) { delete (*it); - it = curr_nodes.erase(it); + it = curr_nodes.erase(it); is_duplicate = true; break; } @@ -3267,6 +3195,14 @@ void TreeSupport::generate_contact_points(std::vector> } } } + else { + for (auto& elem:move_bounds_layer) { + SupportNode* contact_node = new SupportNode(elem.state.result_on_layer, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, elem.state.to_buildplate, + SupportNode::NO_PARENT, print_z, height, z_distance_top); + contact_node->overhang = ExPolygon(elem.influence_area.front()); + curr_nodes.emplace_back(contact_node); + } + } } if (!curr_nodes.empty()) nonempty_layers++; for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); } @@ -3395,6 +3331,7 @@ const ExPolygons& TreeSupportData::calculate_collision(const RadiusLayerPair& ke assert(key.layer_nr < m_layer_outlines.size()); ExPolygons collision_areas = offset_ex(m_layer_outlines[key.layer_nr], scale_(key.radius)); + collision_areas = expolygons_simplify(collision_areas, scale_(m_radius_sample_resolution)); const auto ret = m_collision_cache.insert({ key, std::move(collision_areas) }); return ret.first->second; } @@ -3434,7 +3371,7 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke //assert(ret.second); return ret.first->second; } else { - ExPolygons avoidance_areas = offset_ex(m_layer_outlines_below[layer_nr], scale_(m_xy_distance + radius)); + ExPolygons avoidance_areas = get_collision(radius, layer_nr);// std::move(offset_ex(m_layer_outlines_below[layer_nr], scale_(m_xy_distance + radius))); auto ret = m_avoidance_cache.insert({ key, std::move(avoidance_areas) }); assert(ret.second); return ret.first->second; diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index 8a19acc485..e371a5e6bc 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -11,6 +11,7 @@ #include "Flow.hpp" #include "PrintConfig.hpp" #include "Fill/Lightning/Generator.hpp" +#include "TreeModelVolumes.hpp" #include "TreeSupport3D.hpp" #ifndef SQ @@ -60,7 +61,6 @@ struct SupportNode , position(Point(0, 0)) , obj_layer_nr(0) , support_roof_layers_below(0) - , support_floor_layers_above(0) , to_buildplate(true) , parent(nullptr) , print_z(0.0) @@ -74,7 +74,6 @@ struct SupportNode , position(position) , obj_layer_nr(obj_layer_nr) , support_roof_layers_below(support_roof_layers_below) - , support_floor_layers_above(0) , to_buildplate(to_buildplate) , parent(parent) , print_z(print_z_) @@ -82,13 +81,16 @@ struct SupportNode , dist_mm_to_top(dist_mm_to_top_) { if (parent) { + parents.push_back(parent); type = parent->type; overhang = parent->overhang; if (dist_mm_to_top==0) dist_mm_to_top = parent->dist_mm_to_top + parent->height; parent->child = this; - for (auto& neighbor : parent->merged_neighbours) + for (auto& neighbor : parent->merged_neighbours) { neighbor->child = this; + parents.push_back(neighbor); + } } } @@ -115,10 +117,10 @@ struct SupportNode mutable double radius = 0.0; mutable double max_move_dist = 0.0; TreeNodeType type = eCircle; - bool is_merged = false; // this node is generated by merging upper nodes bool is_corner = false; bool is_processed = false; - const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area + bool need_extra_wall = false; + ExPolygon overhang; // when type==ePolygon, set this value to get original overhang area /*! * \brief The direction of the skin lines above the tip of the branch. @@ -137,7 +139,6 @@ struct SupportNode * how far we need to extend that support roof downwards. */ int support_roof_layers_below; - int support_floor_layers_above; int obj_layer_nr; /*! @@ -157,6 +158,7 @@ struct SupportNode * the entire branch needs to be known. */ SupportNode* parent; + std::vector parents; SupportNode* child = nullptr; /*! @@ -391,6 +393,8 @@ private: * \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks */ std::shared_ptr m_ts_data; + + std::unique_ptr m_model_volumes; PrintObject *m_object; const PrintObjectConfig *m_object_config; SlicingParameters m_slicing_params; @@ -400,10 +404,12 @@ private: size_t m_highest_overhang_layer = 0; std::vector> m_spanning_trees; std::vector< std::unordered_map> m_mst_line_x_layer_contour_caches; + float DO_NOT_MOVER_UNDER_MM = 0.0; coordf_t MAX_BRANCH_RADIUS = 10.0; coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0; coordf_t MIN_BRANCH_RADIUS = 0.5; float tree_support_branch_diameter_angle = 5.0; + coord_t m_min_radius = scale_(1); // in mm bool is_strong = false; bool is_slim = false; bool with_infill = false; @@ -438,8 +444,6 @@ private: void smooth_nodes(std::vector>& contact_nodes); - void adjust_layer_heights(std::vector>& contact_nodes); - /*! BBS: MusangKing: maximum layer height * \brief Optimize the generation of tree support by pre-planning the layer_heights * @@ -459,7 +463,7 @@ private: * \return For each layer, a list of points where the tree should connect * with the model. */ - void generate_contact_points(std::vector>& contact_nodes); + void generate_contact_points(std::vector>& contact_nodes, const std::vector& move_bounds); /*! * \brief Add a node to the next layer. @@ -470,7 +474,11 @@ private: void create_tree_support_layers(); void generate_toolpaths(); coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor); - coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor); + coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor, bool use_min_distance=true); + ExPolygons get_avoidance(coordf_t radius, size_t obj_layer_nr); + ExPolygons get_collision(coordf_t radius, size_t layer_nr); + // get Polygons instead of ExPolygons + Polygons get_collision_polys(coordf_t radius, size_t layer_nr); // similar to SupportMaterial::trim_support_layers_by_object Polygons get_trim_support_regions( diff --git a/src/libslic3r/Support/TreeSupport3D.hpp b/src/libslic3r/Support/TreeSupport3D.hpp index 311df504b7..ba543fbc81 100644 --- a/src/libslic3r/Support/TreeSupport3D.hpp +++ b/src/libslic3r/Support/TreeSupport3D.hpp @@ -139,6 +139,10 @@ struct SupportElementStateBits { struct SupportElementState : public SupportElementStateBits { + int type = 0; + coordf_t radius = 0; + float print_z = 0; + /*! * \brief The layer this support elements wants reach */ From 2205ad0069e116c9b6638e18f321476fb4d362de Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 26 Jan 2025 23:11:53 +0800 Subject: [PATCH 06/72] FIX: improve tree support generation speed 1. Improve generation speed by removing unnecessary get_avoidance. 2. Fix a bug of hybrid support's interface (Jira: STUDIO-4878, STUDIO-4726, Github#2614) 3. Fix a bug of tree support pass through objects (Jira: STUDIO-4252, STUDIO-4608 STUDIO-4298) 4. Fix a bug with first layer + Arachne (Jira: STUDIO-4281, Github #2423) Change-Id: I40978c93ab93fa6964483514dad552d73a66c710 (cherry picked from commit 2ccbbe49c74d4aab4f086e79a6f8262b7fc80f15) (cherry picked from commit bambulab/BambuStudio@d7a4623380d524a1878a62d14da32c93d0b2fa66) Co-authored-by: Arthur --- src/libslic3r/Support/TreeSupport.cpp | 86 +++++++++++++-------------- src/libslic3r/Support/TreeSupport.hpp | 2 + 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 376589321e..262c2d2c06 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -31,7 +31,7 @@ #define NO_INDEX (std::numeric_limits::max()) #define USE_SUPPORT_3D 0 -// #define SUPPORT_TREE_DEBUG_TO_SVG +//#define SUPPORT_TREE_DEBUG_TO_SVG #ifdef SUPPORT_TREE_DEBUG_TO_SVG #include "nlohmann/json.hpp" @@ -1533,29 +1533,29 @@ void TreeSupport::generate_toolpaths() filler_support->set_bounding_box(bbox_object); filler_support->spacing = object_config.support_base_pattern_spacing.value * support_density;// constant spacing to align support infill lines filler_support->angle = Geometry::deg2rad(object_config.support_angle.value); - if (need_infill && m_support_params.base_fill_pattern != ipLightning) { - // allow infill-only mode if support is thick enough (so min_wall_count is 0); - // otherwise must draw 1 wall - size_t min_wall_count = offset(poly, -scale_(support_spacing * 1.5)).empty() ? 1 : 0; - make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(min_wall_count, walls), flow, - erSupportMaterial, filler_support.get(), support_density); + + Polygons loops = to_polygons(poly); + if (layer_id == 0) { + float density = float(m_object_config->raft_first_layer_density.value * 0.01); + fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, filler_support.get(), density, erSupportMaterial, flow, + m_support_params, true, false); } else { - Polygons loops; - loops.emplace_back(poly.contour); - if (layer_id == 0) { - float density = float(m_object_config->raft_first_layer_density.value * 0.01); - fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, filler_support.get(), density, erSupportMaterial, flow, m_support_params, true, false); + if (need_infill && m_support_params.base_fill_pattern != ipLightning) { + // allow infill-only mode if support is thick enough (so min_wall_count is 0); + // otherwise must draw 1 wall + size_t min_wall_count = offset(poly, -scale_(support_spacing * 1.5)).empty() ? 1 : 0; + make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(min_wall_count, walls), flow, + erSupportMaterial, filler_support.get(), support_density); } else { for (size_t i = 1; i < walls; i++) { - Polygons contour_new = offset(poly.contour, -(i-0.5f) * flow.scaled_spacing(), jtSquare); + Polygons contour_new = offset(poly.contour, -(i - 0.5f) * flow.scaled_spacing(), jtSquare); loops.insert(loops.end(), contour_new.begin(), contour_new.end()); } fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, nullptr, 0, erSupportMaterial, flow, m_support_params, true, false); } } - } } if (m_support_params.base_fill_pattern == ipLightning) @@ -1730,7 +1730,7 @@ ExPolygons TreeSupport::get_avoidance(coordf_t radius, size_t obj_layer_nr) #if USE_SUPPORT_3D if (m_model_volumes) { bool on_build_plate = m_object_config->support_on_build_plate_only.value; - const Polygons& avoid_polys = m_model_volumes->getAvoidance(radius, obj_layer_nr, TreeSupport3D::TreeModelVolumes::AvoidanceType::Fast, on_build_plate, true); + const Polygons& avoid_polys = m_model_volumes->getAvoidance(radius, obj_layer_nr, TreeSupport3D::TreeModelVolumes::AvoidanceType::FastSafe, on_build_plate, true); ExPolygons expolys; for (auto& poly : avoid_polys) expolys.emplace_back(std::move(poly)); @@ -1769,7 +1769,8 @@ Polygons TreeSupport::get_collision_polys(coordf_t radius, size_t layer_nr) ExPolygons expolys = m_ts_data->get_collision(radius, layer_nr); Polygons polys; for (auto& expoly : expolys) - polys.emplace_back(std::move(expoly.contour)); + for (int i = 0; i < expoly.num_contours(); i++) + polys.emplace_back(std::move(expoly.contour_or_hole(i))); return polys; #endif return Polygons(); @@ -1987,12 +1988,13 @@ void TreeSupport::draw_circles(const std::vector>& con } else { Polygon circle; - size_t layers_to_top = node.distance_to_top; - double scale = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor, false) / branch_radius; + double scale = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor) / branch_radius; - if (/*is_slim*/1) { // draw ellipse along movement direction - double moveX = node.movement.x() / (scale * branch_radius_scaled); - double moveY = node.movement.y() / (scale * branch_radius_scaled); + double moveX = node.movement.x() / (scale * branch_radius_scaled); + double moveY = node.movement.y() / (scale * branch_radius_scaled); + //BOOST_LOG_TRIVIAL(debug) << format("scale,moveX,moveY: %.3f,%.3f,%.3f", scale, moveX, moveY); + + if (!SQUARE_SUPPORT && std::abs(moveX)>0.001 && std::abs(moveY)>0.001) { // draw ellipse along movement direction const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY)); double matrix[2*2] = { scale * (1 + moveX * moveX * vsize_inv),scale * (0 + moveX * moveY * vsize_inv), @@ -2011,7 +2013,7 @@ void TreeSupport::draw_circles(const std::vector>& con if (layer_nr == 0 && m_raft_layers == 0) { double brim_width = config.tree_support_auto_brim - ? layers_to_top * layer_height / + ? node.dist_mm_to_top / (scale * branch_radius) * 0.5 : config.tree_support_brim_width; auto tmp=offset(circle, scale_(brim_width)); @@ -2091,8 +2093,8 @@ void TreeSupport::draw_circles(const std::vector>& con // let supports touch objects when brim is on auto avoid_region = get_collision_polys((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr); - //base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region); - base_areas = diff_clipped(base_areas, avoid_region); + base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region); + //base_areas = diff_clipped(base_areas, avoid_region); ExPolygons roofs; append(roofs, roof_1st_layer); append(roofs, roof_areas);append(roofs, roof_gap_areas); base_areas = diff_ex(base_areas, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roofs, get_extents(base_areas))); base_areas = intersection_ex(base_areas, m_machine_border); @@ -2452,7 +2454,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod }; //Group together all nodes for each part. - const ExPolygons& parts = m_ts_data->get_avoidance(0, layer_nr); + const ExPolygons& parts = m_ts_data->m_layer_outlines_below[layer_nr];// m_ts_data->get_avoidance(0, layer_nr); std::vector> nodes_per_part(1 + parts.size()); //All nodes that aren't inside a part get grouped together in the 0th part. for (SupportNode* p_node : layer_contact_nodes) { @@ -2537,9 +2539,10 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } const std::vector& neighbours = mst.adjacent_nodes(node.position); if (node.type == ePolygon) { - // Remove all neighbours that are completely inside the polygon and merge them into this node. + // Remove all circle neighbours that are completely inside the polygon and merge them into this node. for (const Point &neighbour : neighbours) { SupportNode * neighbour_node = nodes_per_part[group_index][neighbour]; + if (neighbour_node->type == ePolygon) continue; coord_t neighbour_radius = scale_(calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top, diameter_angle_scale_factor)); Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius), pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0); @@ -2631,10 +2634,10 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } //If the branch falls completely inside a collision area (the entire branch would be removed by the X/Y offset), delete it. - if (group_index > 0 && is_inside_ex(m_ts_data->get_collision(m_ts_data->m_xy_distance, layer_nr), node.position)) + if (group_index > 0 && is_inside_ex(get_collision(m_ts_data->m_xy_distance, layer_nr), node.position)) { const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor); - Point to_outside = projection_onto(m_ts_data->get_collision(m_ts_data->m_xy_distance, layer_nr), node.position); + Point to_outside = projection_onto(get_collision(m_ts_data->m_xy_distance, layer_nr), node.position); double dist2_to_outside = vsize2_with_unscale(node.position - to_outside); if (dist2_to_outside >= branch_radius_node * branch_radius_node) //Too far inside. { @@ -2707,20 +2710,21 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod branch_radius_temp = branch_radius_node; } #endif - auto avoid_layer = get_avoidance(branch_radius_node, layer_nr_next); + auto avoidance_next = get_avoidance(branch_radius_node, layer_nr_next); - Point to_outside = projection_onto(avoid_layer, node.position); + Point to_outside = projection_onto(avoidance_next, node.position); Point direction_to_outer = to_outside - node.position; double dist2_to_outer = vsize2_with_unscale(direction_to_outer); // don't move if // 1) line of node and to_outside is cut by contour (means supports may intersect with object) // 2) it's impossible to move to build plate if (is_line_cut_by_contour(node.position, to_outside) || dist2_to_outer > max_move_distance2 * SQ(layer_nr) || - !is_inside_ex(avoid_layer, node.position)) { + !is_inside_ex(avoidance_next, node.position)) { // try move to outside of lower layer instead Point candidate_vertex = node.position; const coordf_t max_move_between_samples = max_move_distance + radius_sample_resolution + EPSILON; // 100 micron extra for rounding errors. - bool is_outside = move_out_expolys(avoid_layer, candidate_vertex, max_move_between_samples, max_move_between_samples); + // use get_collision instead of get_avoidance here (See STUDIO-4252) + bool is_outside = move_out_expolys(get_collision(branch_radius_node,layer_nr_next), candidate_vertex, max_move_between_samples, max_move_between_samples); if (is_outside) { direction_to_outer = candidate_vertex - node.position; dist2_to_outer = vsize2_with_unscale(direction_to_outer); @@ -2743,18 +2747,15 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod if (vsize2_with_unscale(movement) > get_max_move_dist(&node,2)) movement = normal(movement, scale_(get_max_move_dist(&node))); - // add momentum to force smooth movement - //movement = movement * 0.5 + p_node->movement * 0.5; - next_layer_vertex += movement; if (group_index == 0) { // Avoid collisions. const coordf_t max_move_between_samples = get_max_move_dist(&node, 1) + radius_sample_resolution + EPSILON; // 100 micron extra for rounding errors. - bool is_outside = move_out_expolys(avoid_layer, next_layer_vertex, radius_sample_resolution + EPSILON, max_move_between_samples); + bool is_outside = move_out_expolys(avoidance_next, next_layer_vertex, radius_sample_resolution + EPSILON, max_move_between_samples); if (!is_outside) { Point candidate_vertex = node.position; - is_outside = move_out_expolys(avoid_layer, candidate_vertex, radius_sample_resolution + EPSILON, max_move_between_samples); + is_outside = move_out_expolys(avoidance_next, candidate_vertex, radius_sample_resolution + EPSILON, max_move_between_samples); if (is_outside) { next_layer_vertex = candidate_vertex; } } } @@ -2770,9 +2771,9 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod #ifdef SUPPORT_TREE_DEBUG_TO_SVG if (contact_nodes[layer_nr].empty() == false) { - draw_contours_and_nodes_to_svg((boost::format("%.2f") % contact_nodes[layer_nr][0]->print_z).str(), get_avoidance(0, layer_nr), + draw_contours_and_nodes_to_svg((boost::format("%.2f") % contact_nodes[layer_nr][0]->print_z).str(), get_collision(0,layer_nr_next), get_avoidance(branch_radius_temp, layer_nr), - m_ts_data->m_layer_outlines_below[layer_nr], + m_ts_data->m_layer_outlines[layer_nr], contact_nodes[layer_nr], contact_nodes[layer_nr_next], "contact_points", { "overhang","avoid","outline" }, { "blue","red","yellow" }); BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer " << layer_nr << ", print_z=" << ts_layer->print_z; @@ -3103,6 +3104,7 @@ void TreeSupport::generate_contact_points(std::vector> height, z_distance_top); contact_node->type = ePolygon; contact_node->overhang = overhang_part; + contact_node->radius = unscale_(overhang_bounds.radius()); curr_nodes.emplace_back(contact_node); continue; } @@ -3311,7 +3313,6 @@ Polygons TreeSupportData::get_contours_with_holes(size_t layer_nr) const coordf_t TreeSupportData::ceil_radius(coordf_t radius) const { -#if 1 size_t factor = (size_t)(radius / m_radius_sample_resolution); coordf_t remains = radius - m_radius_sample_resolution * factor; if (remains > EPSILON) { @@ -3320,10 +3321,6 @@ coordf_t TreeSupportData::ceil_radius(coordf_t radius) const else { return radius; } -#else - coordf_t resolution = m_radius_sample_resolution; - return ceil(radius / resolution) * resolution; -#endif } const ExPolygons& TreeSupportData::calculate_collision(const RadiusLayerPair& key) const @@ -3371,6 +3368,7 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke //assert(ret.second); return ret.first->second; } else { + BOOST_LOG_TRIVIAL(debug) << "calculate_avoidance exceeds max_recursion_depth*2 on radius=" << radius << ", layer=" << layer_nr << ", recursion=" << key.recursions; ExPolygons avoidance_areas = get_collision(radius, layer_nr);// std::move(offset_ex(m_layer_outlines_below[layer_nr], scale_(m_xy_distance + radius))); auto ret = m_avoidance_cache.insert({ key, std::move(avoidance_areas) }); assert(ret.second); diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index e371a5e6bc..322ef66da8 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -473,7 +473,9 @@ private: void insert_dropped_node(std::vector& nodes_layer, SupportNode* node); void create_tree_support_layers(); void generate_toolpaths(); + // get unscaled radius of node coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor); + // get unscaled radius(mm) of node based on the distance mm to top coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor, bool use_min_distance=true); ExPolygons get_avoidance(coordf_t radius, size_t obj_layer_nr); ExPolygons get_collision(coordf_t radius, size_t layer_nr); From 70c4961df385e9149e7a9f00fa741e92f8ed90b8 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 26 Jan 2025 23:19:13 +0800 Subject: [PATCH 07/72] FIX: tree support generates floating hybrid supports Jira: STUDIO-4763 Github: #2660 Change-Id: I13d5a1443af8bc82f0cadd177e0db3fc3db971f1 (cherry picked from commit bambulab/BambuStudio@0964b5bb0aef5e3b04d634fd78d5ddc268c4501f) Co-authored-by: Arthur --- src/libslic3r/Support/TreeSupport.cpp | 38 ++++++++++++++++++--------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 262c2d2c06..c88225c91b 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1927,7 +1927,7 @@ void TreeSupport::draw_circles(const std::vector>& con // coconut: previously std::unordered_map in m_collision_cache is not multi-thread safe which may cause programs stuck, here we change to tbb::concurrent_unordered_map tbb::parallel_for( - tbb::blocked_range(0, m_object->layer_count(), m_object->layer_count()), + tbb::blocked_range(0, m_object->layer_count()), [&](const tbb::blocked_range& range) { for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) @@ -2581,8 +2581,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod node_ = p_node->parent ? p_node : neighbour; // Make sure the next pass doesn't drop down either of these (since that already happened). node_->merged_neighbours.push_front(node_ == p_node ? neighbour : p_node); - //const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_position); - const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr_next], next_position); + const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_position); SupportNode * next_node = new SupportNode(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_, print_z_next, height_next); next_node->movement = next_position - node.position; @@ -2625,11 +2624,26 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } if (node.type == ePolygon) { // polygon node do not merge or move - const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], p_node->position); - SupportNode * next_node = new SupportNode(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, - p_node, print_z_next, height_next); - next_node->max_move_dist = 0; - contact_nodes[layer_nr_next].emplace_back(next_node); + const bool to_buildplate = true; + // keep only the part that won't be removed by the next layer + ExPolygons overhangs_next = diff_clipped({ node.overhang }, get_collision_polys(0, layer_nr_next)); + // find the biggest overhang if there are many + float area_biggest = -1; + int index_biggest = -1; + for (int i = 0; i < overhangs_next.size(); i++) { + float a=area(overhangs_next[i]); + if (a > area_biggest) { + area_biggest = a; + index_biggest = i; + } + } + if (index_biggest >= 0) { + SupportNode* next_node = new SupportNode(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, + p_node, print_z_next, height_next); + next_node->max_move_dist = 0; + next_node->overhang = std::move(overhangs_next[index_biggest]); + contact_nodes[layer_nr_next].emplace_back(next_node); + } continue; } @@ -2760,7 +2774,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } } - const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], next_layer_vertex); + const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_layer_vertex); SupportNode * next_node = new SupportNode(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); next_node->movement = movement; @@ -3096,10 +3110,8 @@ void TreeSupport::generate_contact_points(std::vector> { BoundingBox overhang_bounds = get_extents(overhang_part); if (m_support_params.support_style==smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang) { - Point candidate = overhang_bounds.center(); - if (!overhang_part.contains(candidate)) - move_inside_expoly(overhang_part, candidate); - if (!(config.support_on_build_plate_only && is_inside_ex(m_ts_data->m_layer_outlines_below[layer_nr], candidate))) { + if (!overlaps({ overhang_part }, m_ts_data->m_layer_outlines_below[layer_nr-1])) { + Point candidate = overhang_bounds.center(); SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z, height, z_distance_top); contact_node->type = ePolygon; From 5fb0e01943e2c049a1a22859e31aaa2d7ea03481 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Mon, 27 Jan 2025 15:32:38 +0800 Subject: [PATCH 08/72] ENH: improve auto-arranging objects with tree support We decide to set brim width of all objects to MAX_BRANCH_RADIUS_FIRST_LAYER if there is an object with tree support after discussion. Jira: MAK-2009 Change-Id: I4c4940800632c433235966b01c44ac910e33a51c (cherry picked from commit bambulab/BambuStudio@2bd6b1150565b2345d9517ea10b8f2ea621c635b) Co-authored-by: Arthur --- src/libslic3r/Support/TreeSupport.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index c88225c91b..a1e983cb67 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1920,7 +1920,7 @@ void TreeSupport::draw_circles(const std::vector>& con const bool with_lightning_infill = m_support_params.base_fill_pattern == ipLightning; coordf_t support_extrusion_width = m_support_params.support_extrusion_width; - const size_t wall_count = config.tree_support_wall_count.value; + const float tree_brim_width = config.tree_support_brim_width.value; const PrintObjectConfig& object_config = m_object->config(); BOOST_LOG_TRIVIAL(info) << "draw_circles for object: " << m_object->model_object()->name; @@ -2011,11 +2011,7 @@ void TreeSupport::draw_circles(const std::vector>& con } } if (layer_nr == 0 && m_raft_layers == 0) { - double brim_width = - config.tree_support_auto_brim - ? node.dist_mm_to_top / - (scale * branch_radius) * 0.5 - : config.tree_support_brim_width; + double brim_width = !config.tree_support_auto_brim ? tree_brim_width : std::max(0.0, std::min(node.radius + node.dist_mm_to_top / (scale * branch_radius) * 0.5, MAX_BRANCH_RADIUS_FIRST_LAYER) - node.radius); auto tmp=offset(circle, scale_(brim_width)); if(!tmp.empty()) circle = tmp[0]; From 8a7cc7912939cd89c4ebf3fa77dd67062dc77e91 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 31 Oct 2023 10:33:42 +0800 Subject: [PATCH 09/72] ENH: improve first layer tree support First layer support can't be top interface, and min brim width of auto mode should be larger than 0. Jira: STUDIO-5010 Change-Id: I02f8b017b535f8a47965387e8679f692b1966e04 (cherry picked from commit 3e7d54abe352e8ab5f9d6492b5a86a96f9067f94) (cherry picked from commit 7efebe6bc682d66953a33704829140369b3ee7b8) --- src/libslic3r/Support/TreeSupport.cpp | 8 ++++---- src/libslic3r/Support/TreeSupport.hpp | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index a1e983cb67..9b199adc45 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -2011,7 +2011,7 @@ void TreeSupport::draw_circles(const std::vector>& con } } if (layer_nr == 0 && m_raft_layers == 0) { - double brim_width = !config.tree_support_auto_brim ? tree_brim_width : std::max(0.0, std::min(node.radius + node.dist_mm_to_top / (scale * branch_radius) * 0.5, MAX_BRANCH_RADIUS_FIRST_LAYER) - node.radius); + double brim_width = !config.tree_support_auto_brim ? tree_brim_width : std::max(MIN_BRANCH_RADIUS_FIRST_LAYER, std::min(node.radius + node.dist_mm_to_top / (scale * branch_radius) * 0.5, MAX_BRANCH_RADIUS_FIRST_LAYER) - node.radius); auto tmp=offset(circle, scale_(brim_width)); if(!tmp.empty()) circle = tmp[0]; @@ -2032,14 +2032,14 @@ void TreeSupport::draw_circles(const std::vector>& con has_circle_node = true; } - if (node.distance_to_top < 0) + if (layer_nr>0 && node.distance_to_top < 0) append(roof_gap_areas, area); - else if (node.support_roof_layers_below == 1) + else if (layer_nr > 0 && node.support_roof_layers_below == 1) { append(roof_1st_layer, area); max_layers_above_roof1 = std::max(max_layers_above_roof1, node.dist_mm_to_top); } - else if (node.support_roof_layers_below > 0) + else if (layer_nr > 0 && node.support_roof_layers_below > 0) { append(roof_areas, area); max_layers_above_roof = std::max(max_layers_above_roof, node.dist_mm_to_top); diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index 322ef66da8..62cce6ffea 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -406,8 +406,9 @@ private: std::vector< std::unordered_map> m_mst_line_x_layer_contour_caches; float DO_NOT_MOVER_UNDER_MM = 0.0; coordf_t MAX_BRANCH_RADIUS = 10.0; - coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0; coordf_t MIN_BRANCH_RADIUS = 0.5; + coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0; + coordf_t MIN_BRANCH_RADIUS_FIRST_LAYER = 2.0; float tree_support_branch_diameter_angle = 5.0; coord_t m_min_radius = scale_(1); // in mm bool is_strong = false; From 14ecfac5921bc0a6b98311c3b88f9c367bf52984 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 16 Nov 2023 21:05:26 +0800 Subject: [PATCH 10/72] ENH: open support wall count for normal support 1. open support wall count for normal support Enabling this option makes normal support stronger and gives better overhang quality, but also more difficult to removal. Jira: STUDIO-5192 2. fix a bug where tree support (hybrid style) may get overlapped extrusions near the walls. 3. fix a bug where raft layers can't be 1 in tree support Jira: STUDIO-5261 Change-Id: Iadc0c67a9b50b5b221c8e83d5aa22ed282018cf8 (cherry picked from commit c0bb0084e386cb70ed6e16edf93190e4b38f5b90) (cherry picked from commit bf93fd02fb92912563796a2eec1ccfb4b0ed7fa9) --- src/libslic3r/Support/SupportParameters.hpp | 6 +- src/libslic3r/Support/TreeSupport.cpp | 121 +++++++++++--------- src/slic3r/GUI/ConfigManipulation.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 2 +- 4 files changed, 69 insertions(+), 62 deletions(-) diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index da615ab1fd..1318925849 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -96,10 +96,10 @@ struct SupportParameters { // No interface layers allowed, print everything with the base support pattern. this->interface_density = this->support_density; } - + SupportMaterialPattern support_pattern = object_config.support_base_pattern; - this->with_sheath = false;//object_config.support_material_with_sheath; - this->base_fill_pattern = + this->with_sheath = /*is_tree(object_config.support_type) &&*/ object_config.tree_support_wall_count > 0; + this->base_fill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 9b199adc45..18c9f8e0e4 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -17,10 +17,6 @@ #include #include -#define _L(s) Slic3r::I18N::translate(s) - -#define USE_PLAN_LAYER_HEIGHTS 1 - #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 #endif @@ -176,7 +172,7 @@ Lines spanning_tree_to_lines(const std::vector& spanning_tr #ifdef SUPPORT_TREE_DEBUG_TO_SVG -static std::string get_svg_filename(std::string layer_nr_or_z, std::string tag = "bbl_ts") +static std::string get_svg_filename(const std::string& tag = "bbl_ts", const std::string& layer_nr_or_z="") { static bool rand_init = false; @@ -186,10 +182,13 @@ static std::string get_svg_filename(std::string layer_nr_or_z, std::string tag } int rand_num = rand() % 1000000; - //makedir("./SVG"); - std::string prefix = "./SVG/"; + std::string prefix = "C:/Users/arthur.tang/AppData/Roaming/BambuStudioInternal/SVG/"; + //makedir(prefix); std::string suffix = ".svg"; - return prefix + tag + "_" + layer_nr_or_z /*+ "_" + std::to_string(rand_num)*/ + suffix; + std::string name = prefix + tag; + if(!layer_nr_or_z.empty()) + name+= "_" + layer_nr_or_z; + return name + suffix; } static void draw_contours_and_nodes_to_svg @@ -217,10 +216,7 @@ static void draw_contours_and_nodes_to_svg bbox.max.y() = std::max(bbox.max.y(), (coord_t)scale_(10)); SVG svg; - if(!layer_nr_or_z.empty()) - svg.open(get_svg_filename(layer_nr_or_z, name_prefix), bbox); - else - svg.open(name_prefix, bbox); + svg.open(get_svg_filename(name_prefix, layer_nr_or_z), bbox); if (!svg.is_opened()) return; // draw grid @@ -276,7 +272,7 @@ static void draw_layer_mst bbox.merge(bb); } - SVG svg(get_svg_filename(layer_nr_or_z, "mstree").c_str(), bbox); + SVG svg(get_svg_filename("mstree",layer_nr_or_z).c_str(), bbox); if (!svg.is_opened()) return; svg.draw(lines, "blue", coord_t(scale_(0.05))); @@ -293,7 +289,7 @@ static void draw_two_overhangs_to_svg(SupportLayer* ts_layer, const ExPolygons& BoundingBox bbox2 = get_extents(overhangs2); bbox1.merge(bbox2); - SVG svg(get_svg_filename(std::to_string(ts_layer->print_z), "two_overhangs"), bbox1); + SVG svg(get_svg_filename("two_overhangs", std::to_string(ts_layer->print_z)), bbox1); if (!svg.is_opened()) return; svg.draw(union_ex(overhangs1), "blue"); @@ -306,7 +302,7 @@ static void draw_polylines(SupportLayer* ts_layer, Polylines& polylines) return; BoundingBox bbox = get_extents(polylines); - SVG svg(get_svg_filename(std::to_string(ts_layer->print_z), "lightnings"), bbox); + SVG svg(get_svg_filename("lightnings", std::to_string(ts_layer->print_z)), bbox); if (!svg.is_opened()) return; int id = 0; @@ -687,7 +683,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p // align with the centered object in current plate (may not be the 1st plate, so need to add the plate offset) m_machine_border.translate(Point(scale_(plate_offset(0)), scale_(plate_offset(1))) - m_object->instances().front().shift); #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg("SVG/machine_boarder.svg", m_object->bounding_box()); + SVG svg(get_svg_filename("machine_boarder"), m_object->bounding_box()); if (svg.is_opened()) svg.draw(m_machine_border, "yellow"); #endif } @@ -889,7 +885,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) if (!overhang.empty()) { has_sharp_tails = true; #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg(format("SVG/sharp_tail_orig_%.02f.svg", layer->print_z), m_object->bounding_box()); + SVG svg(get_svg_filename(format("sharp_tail_orig_%.02f", layer->print_z)), m_object->bounding_box()); if (svg.is_opened()) svg.draw(overhang, "red"); #endif } @@ -1007,7 +1003,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) if (!overhang.empty()) has_sharp_tails = true; #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg(format("SVG/sharp_tail_%.02f.svg",layer->print_z), m_object->bounding_box()); + SVG svg(get_svg_filename(format("sharp_tail_%.02f",layer->print_z)), m_object->bounding_box()); if (svg.is_opened()) svg.draw(overhang, "red"); #endif } @@ -1020,6 +1016,8 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { if (m_object->print()->canceled()) break; + if(layer_nr+m_raft_layers>=m_object->support_layer_count()) + break; SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); Layer* layer = m_object->get_layer(layer_nr); for (auto& overhang : ts_layer->overhang_areas) { @@ -1060,9 +1058,9 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) const Layer* layer1 = m_object->get_layer(cluster.min_layer); BoundingBox bbox = cluster.merged_bbox; bbox.merge(get_extents(layer1->lslices)); - SVG svg(format("SVG/overhangCluster_%s-%s_%s-%s_tail=%s_cantilever=%s_small=%s.svg", + SVG svg(get_svg_filename(format("overhangCluster_%s-%s_%s-%s_tail=%s_cantilever=%s_small=%s", cluster.min_layer, cluster.max_layer, layer1->print_z, m_object->get_layer(cluster.max_layer)->print_z, - cluster.is_sharp_tail, cluster.is_cantilever, cluster.is_small_overhang), bbox); + cluster.is_sharp_tail, cluster.is_cantilever, cluster.is_small_overhang)), bbox); if (svg.is_opened()) { svg.draw(layer1->lslices, "red"); svg.draw(cluster.merged_poly, "blue"); @@ -1108,8 +1106,9 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) } if (max_bridge_length > 0 && ts_layer->overhang_areas.size() > 0 && lower_layer) { - // do not break bridge for normal part in TreeHybrid - bool break_bridge = !(m_support_params.support_style == smsTreeHybrid && area(ts_layer->overhang_areas) > m_support_params.thresh_big_overhang); + // do not break bridge for normal part in TreeHybrid, nor Tree Strong + bool break_bridge = !(m_support_params.support_style == smsTreeHybrid && area(ts_layer->overhang_areas) > m_support_params.thresh_big_overhang) + && !(m_support_params.support_style==smsTreeStrong); m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &ts_layer->overhang_areas, max_bridge_length, break_bridge); } @@ -1143,7 +1142,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) if (layer->overhang_areas.empty() && (blockers.size()<=layer->id() || blockers[layer->id()].empty())) continue; - SVG svg(format("SVG/overhang_areas_%s.svg", layer->print_z), m_object->bounding_box()); + SVG svg(get_svg_filename("overhang_areas", std::to_string(layer->print_z)), m_object->bounding_box()); if (svg.is_opened()) { svg.draw_outline(m_object->get_layer(layer->id())->lslices, "yellow"); svg.draw(layer->overhang_areas, "orange"); @@ -1186,18 +1185,20 @@ void TreeSupport::create_tree_support_layers() } for (Layer *layer : m_object->layers()) { - SupportLayer* ts_layer = m_object->add_tree_support_layer(layer->id(), layer->height, layer->print_z, layer->slice_z); + SupportLayer* ts_layer = m_object->add_tree_support_layer(layer_id++, layer->height, layer->print_z, layer->slice_z); if (ts_layer->id() > m_raft_layers) { SupportLayer* lower_layer = m_object->get_support_layer(ts_layer->id() - 1); - lower_layer->upper_layer = ts_layer; - ts_layer->lower_layer = lower_layer; + if (lower_layer) { + lower_layer->upper_layer = ts_layer; + ts_layer->lower_layer = lower_layer; + } } } } static inline BoundingBox fill_expolygon_generate_paths( ExtrusionEntitiesPtr &dst, - ExPolygon &&expolygon, + ExPolygon &expolygon, Fill *filler, const FillParams &fill_params, ExtrusionRole role, @@ -1224,7 +1225,7 @@ static inline BoundingBox fill_expolygon_generate_paths( static inline std::vector fill_expolygons_generate_paths( ExtrusionEntitiesPtr &dst, - ExPolygons &&expolygons, + ExPolygons &expolygons, Fill *filler, const FillParams &fill_params, ExtrusionRole role, @@ -1390,16 +1391,9 @@ void TreeSupport::generate_toolpaths() // generate raft tool path if (m_raft_layers > 0) { - ExtrusionRole raft_contour_er = m_slicing_params.base_raft_layers > 0 ? erSupportMaterial : erSupportMaterialInterface; SupportLayer *ts_layer = m_object->support_layers().front(); Flow flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter); - - Polygons loops; - for (const ExPolygon& expoly : raft_areas) { - loops.push_back(expoly.contour); - loops.insert(loops.end(), expoly.holes.begin(), expoly.holes.end()); - } - extrusion_entities_append_loops(ts_layer->support_fills.entities, std::move(loops), raft_contour_er, + extrusion_entities_append_loops(ts_layer->support_fills.entities, to_polygons(raft_areas), erSupportMaterial, float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); raft_areas = offset_ex(raft_areas, -flow.scaled_spacing() / 2.); } @@ -1407,6 +1401,7 @@ void TreeSupport::generate_toolpaths() for (size_t layer_nr = 0; layer_nr < m_slicing_params.base_raft_layers; layer_nr++) { SupportLayer *ts_layer = m_object->get_support_layer(layer_nr); coordf_t expand_offset = (layer_nr == 0 ? 0. : -1.); + raft_areas = offset_ex(raft_areas, scale_(expand_offset)); Flow support_flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter); Fill* filler_interface = Fill::new_from_type(ipRectilinear); @@ -1417,16 +1412,25 @@ void TreeSupport::generate_toolpaths() fill_params.density = object_config.raft_first_layer_density * 0.01; fill_params.dont_adjust = true; - fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(offset_ex(raft_areas, scale_(expand_offset))), + fill_expolygons_generate_paths(ts_layer->support_fills.entities, raft_areas, filler_interface, fill_params, erSupportMaterial, support_flow); } + // subtract the non-raft support bases, otherwise we'll get support base on top of raft interfaces which is not stable + SupportLayer* first_non_raft_layer = m_object->get_support_layer(m_raft_layers); + ExPolygons first_non_raft_base; + for (auto& area_group : first_non_raft_layer->area_groups) { + if (area_group.type == SupportLayer::BaseType) + first_non_raft_base.emplace_back(*area_group.area); + } + ExPolygons raft_base_areas = intersection_ex(raft_areas, first_non_raft_base); + ExPolygons raft_interface_areas = diff_ex(raft_areas, raft_base_areas); + for (size_t layer_nr = m_slicing_params.base_raft_layers; layer_nr < m_slicing_params.base_raft_layers + m_slicing_params.interface_raft_layers; layer_nr++) { SupportLayer *ts_layer = m_object->get_support_layer(layer_nr); - coordf_t expand_offset = (layer_nr == 0 ? 0. : -1.); Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); Fill* filler_interface = Fill::new_from_type(ipRectilinear); @@ -1437,8 +1441,12 @@ void TreeSupport::generate_toolpaths() fill_params.density = interface_density; fill_params.dont_adjust = true; - fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(offset_ex(raft_areas, scale_(expand_offset))), + fill_expolygons_generate_paths(ts_layer->support_fills.entities, raft_interface_areas, filler_interface, fill_params, erSupportMaterialInterface, support_flow); + + fill_params.density = object_config.raft_first_layer_density * 0.01; + fill_expolygons_generate_paths(ts_layer->support_fills.entities, raft_base_areas, + filler_interface, fill_params, erSupportMaterial, support_flow); } BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); @@ -1459,7 +1467,7 @@ void TreeSupport::generate_toolpaths() if (m_object->print()->canceled()) break; - m_object->print()->set_status(70, (boost::format(_L("Support: generate toolpath at layer %d")) % layer_id).str()); + m_object->print()->set_status(70, (boost::format(_u8L("Support: generate toolpath at layer %d")) % layer_id).str()); SupportLayer* ts_layer = m_object->get_support_layer(layer_id); Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); @@ -1526,9 +1534,6 @@ void TreeSupport::generate_toolpaths() bool need_infill = with_infill; if(m_object_config->support_base_pattern==smpDefault) need_infill &= area_group.need_infill; - size_t walls = wall_count; - if (layer_id == 0) walls = std::numeric_limits::max(); - else if (area_group.need_extra_wall && walls < 2) walls += 1; std::shared_ptr filler_support = std::shared_ptr(Fill::new_from_type(layer_id == 0 ? ipConcentric : m_support_params.base_fill_pattern)); filler_support->set_bounding_box(bbox_object); filler_support->spacing = object_config.support_base_pattern_spacing.value * support_density;// constant spacing to align support infill lines @@ -1544,11 +1549,14 @@ void TreeSupport::generate_toolpaths() if (need_infill && m_support_params.base_fill_pattern != ipLightning) { // allow infill-only mode if support is thick enough (so min_wall_count is 0); // otherwise must draw 1 wall + // Don't need extra walls if we have infill. Extra walls may overlap with the infills. size_t min_wall_count = offset(poly, -scale_(support_spacing * 1.5)).empty() ? 1 : 0; - make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(min_wall_count, walls), flow, + make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(min_wall_count, wall_count), flow, erSupportMaterial, filler_support.get(), support_density); } else { + size_t walls = wall_count; + if (area_group.need_extra_wall && walls < 2) walls += 1; for (size_t i = 1; i < walls; i++) { Polygons contour_new = offset(poly.contour, -(i - 0.5f) * flow.scaled_spacing(), jtSquare); loops.insert(loops.end(), contour_new.begin(), contour_new.end()); @@ -1600,7 +1608,7 @@ void TreeSupport::generate_toolpaths() float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); #ifdef SUPPORT_TREE_DEBUG_TO_SVG - std::string name = "./SVG/trees_polyline_" + std::to_string(ts_layer->print_z) /*+ "_" + std::to_string(rand_num)*/ + ".svg"; + std::string name = get_svg_filename("trees_polyline", std::to_string(ts_layer->print_z)); BoundingBox bbox = get_extents(ts_layer->base_areas); SVG svg(name, bbox); if (svg.is_opened()) { @@ -1631,7 +1639,7 @@ void TreeSupport::generate() // Generate overhang areas profiler.stage_start(STAGE_DETECT_OVERHANGS); - m_object->print()->set_status(55, _L("Support: detect overhangs")); + m_object->print()->set_status(55, _u8L("Support: detect overhangs")); detect_overhangs(); profiler.stage_finish(STAGE_DETECT_OVERHANGS); @@ -1645,14 +1653,14 @@ void TreeSupport::generate() std::vector move_bounds(m_highest_overhang_layer + 1); profiler.stage_start(STAGE_GENERATE_CONTACT_NODES); - m_object->print()->set_status(56, _L("Support: generate contact points")); + m_object->print()->set_status(56, _u8L("Support: generate contact points")); generate_contact_points(contact_nodes, move_bounds); profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES); //Drop nodes to lower layers. profiler.stage_start(STAGE_DROP_DOWN_NODES); - m_object->print()->set_status(60, _L("Support: propagate branches")); + m_object->print()->set_status(60, _u8L("Support: propagate branches")); drop_nodes(contact_nodes); profiler.stage_finish(STAGE_DROP_DOWN_NODES); @@ -1660,7 +1668,7 @@ void TreeSupport::generate() //Generate support areas. profiler.stage_start(STAGE_DRAW_CIRCLES); - m_object->print()->set_status(65, _L("Support: draw polygons")); + m_object->print()->set_status(65, _u8L("Support: draw polygons")); draw_circles(contact_nodes); profiler.stage_finish(STAGE_DRAW_CIRCLES); @@ -1675,7 +1683,7 @@ void TreeSupport::generate() contact_nodes.clear(); profiler.stage_start(STAGE_GENERATE_TOOLPATHS); - m_object->print()->set_status(69, _L("Support: generate toolpath")); + m_object->print()->set_status(69, _u8L("Support: generate toolpath")); generate_toolpaths(); profiler.stage_finish(STAGE_GENERATE_TOOLPATHS); @@ -2063,7 +2071,7 @@ void TreeSupport::draw_circles(const std::vector>& con ts_layer->lslices_bboxes.emplace_back(get_extents(expoly)); ts_layer->backup_untyped_slices(); - m_object->print()->set_status(65, (boost::format( _L("Support: generate polygons at layer %d")) % layer_nr).str()); + m_object->print()->set_status(65, (boost::format( _u8L("Support: generate polygons at layer %d")) % layer_nr).str()); // join roof segments double contact_dist_scaled = scale_(0.5);// scale_(m_slicing_params.gap_support_object); @@ -2248,7 +2256,7 @@ void TreeSupport::draw_circles(const std::vector>& con std::map> holePropagationInfos; for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { if (print->canceled()) break; - m_object->print()->set_status(66, (boost::format(_L("Support: fix holes at layer %d")) % layer_nr).str()); + m_object->print()->set_status(66, (boost::format(_u8L("Support: fix holes at layer %d")) % layer_nr).str()); const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); @@ -2342,10 +2350,9 @@ void TreeSupport::draw_circles(const std::vector>& con ExPolygons& base_areas = ts_layer->base_areas; ExPolygons& roof_areas = ts_layer->roof_areas; ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer; - ExPolygons& floor_areas = ts_layer->floor_areas; + ExPolygons roofs = roof_areas; append(roofs, roof_1st_layer); if (base_areas.empty() && roof_areas.empty() && roof_1st_layer.empty()) continue; - char fname[10]; sprintf(fname, "%d_%.2f", layer_nr, ts_layer->print_z); - draw_contours_and_nodes_to_svg("", base_areas, roof_areas, roof_1st_layer, {}, {}, get_svg_filename(fname, "circles"), {"base", "roof", "roof1st"}); + draw_contours_and_nodes_to_svg(format("%d_%.2f", layer_nr, ts_layer->print_z), base_areas, roofs, ts_layer->lslices, {}, {}, "circles", {"base", "roof", "lslices"}, {"blue","red","black"}); } #endif // SUPPORT_TREE_DEBUG_TO_SVG @@ -2424,9 +2431,9 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod std::deque> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches. const Layer* ts_layer = m_object->get_support_layer(layer_nr); - m_object->print()->set_status(60, (boost::format(_L("Support: propagate branches at layer %d")) % layer_nr).str()); + m_object->print()->set_status(60, (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str()); - Polygons layer_contours = m_ts_data->get_contours_with_holes(layer_nr); + Polygons layer_contours = std::move(m_ts_data->get_contours_with_holes(layer_nr)); //std::unordered_map& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr]; std::unordered_map mst_line_x_layer_contour_cache; auto is_line_cut_by_contour = [&mst_line_x_layer_contour_cache,&layer_contours](Point a, Point b) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index a376aa7f1f..a8e77677d4 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -611,7 +611,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tree_support_branch_diameter" }) toggle_line(el, support_is_normal_tree); // settings specific to normal trees - for (auto el : {"tree_support_wall_count", "tree_support_auto_brim", "tree_support_brim_width", "tree_support_adaptive_layer_height"}) + for (auto el : {"tree_support_auto_brim", "tree_support_brim_width", "tree_support_adaptive_layer_height"}) toggle_line(el, support_is_normal_tree); // settings specific to organic trees for (auto el : {"tree_support_branch_angle_organic", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic","tree_support_angle_slow","tree_support_tip_diameter", "tree_support_top_rate", "tree_support_branch_diameter_angle", "tree_support_branch_diameter_double_wall"}) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index fea8908d75..67863846d2 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2250,6 +2250,7 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); optgroup->append_single_option_line("support_top_z_distance", "support#top-z-distance"); optgroup->append_single_option_line("support_bottom_z_distance", "support#bottom-z-distance"); + optgroup->append_single_option_line("tree_support_wall_count"); optgroup->append_single_option_line("support_base_pattern", "support#base-pattern"); optgroup->append_single_option_line("support_base_pattern_spacing", "support#base-pattern"); optgroup->append_single_option_line("support_angle"); @@ -2278,7 +2279,6 @@ void TabPrint::build() optgroup->append_single_option_line("tree_support_branch_angle_organic", "support#tree-support-only-options"); optgroup->append_single_option_line("tree_support_angle_slow"); optgroup->append_single_option_line("tree_support_branch_diameter_double_wall"); - optgroup->append_single_option_line("tree_support_wall_count"); optgroup->append_single_option_line("tree_support_adaptive_layer_height"); optgroup->append_single_option_line("tree_support_auto_brim"); optgroup->append_single_option_line("tree_support_brim_width"); From 5f450923c9526c6ff8d1c278587889a2ae278123 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 17 Nov 2023 11:38:55 +0800 Subject: [PATCH 11/72] FIX: compiling error on linux jira: none Change-Id: I1a4563503b5ddf74a1979cc0cee7a15b8aced904 (cherry picked from commit de52c6ca62c9f3a6314ddf5a856c1d8534329886) (cherry picked from commit 4bd2b7f96dee8862cf2ae87807e33f4255070d2d) --- src/libslic3r/Support/TreeSupport.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 18c9f8e0e4..51df15c0e7 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1233,7 +1233,7 @@ static inline std::vector fill_expolygons_generate_paths( { std::vector fill_boxes; for (ExPolygon& expoly : expolygons) { - auto box = fill_expolygon_generate_paths(dst, std::move(expoly), filler, fill_params, role, flow); + auto box = fill_expolygon_generate_paths(dst, expoly, filler, fill_params, role, flow); fill_boxes.emplace_back(box); } return fill_boxes; @@ -1291,7 +1291,7 @@ static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const Print& pr fill_params.density = support_density; fill_params.dont_adjust = true; ExPolygons to_infill = support_area_new; - std::vector fill_boxes = fill_expolygons_generate_paths(dst, std::move(to_infill), filler_support, fill_params, role, flow); + std::vector fill_boxes = fill_expolygons_generate_paths(dst, to_infill, filler_support, fill_params, role, flow); // allow wall_count to be zero, which means only draw infill if (wall_count == 0) { @@ -1513,7 +1513,7 @@ void TreeSupport::generate_toolpaths() // floor_areas fill_params.density = bottom_interface_density; filler_interface->spacing = m_support_material_interface_flow.spacing(); - fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys), + fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys, filler_interface.get(), fill_params, erSupportMaterialInterface, m_support_material_interface_flow); } else if (area_group.type == SupportLayer::RoofType) { // roof_areas @@ -1525,7 +1525,7 @@ void TreeSupport::generate_toolpaths() } if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) filler_interface->layer_id = round(area_group.dist_to_top / ts_layer->height); - fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys), filler_interface.get(), fill_params, erSupportMaterialInterface, + fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys, filler_interface.get(), fill_params, erSupportMaterialInterface, m_support_material_interface_flow); } else { From e8d4291e02c6c4320a0f3f9e90853715fff28d8c Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 13 Sep 2023 21:00:04 +0800 Subject: [PATCH 12/72] FIX: adaptive layer height may mess up support layers We must ensure when independent support layer height is enabled, the support layers are strictly synced with object layers. Otherwise, the wipe tower toolchange may be messed up. Jira: STUDIO-4097 Change-Id: I6208653f9665b15d028940d5e130c9e895629fc2 (cherry picked from commit 41d35c8af152c91cb356a68d88a879a115b44778) (cherry picked from commit 2b593ce378e7294c52f4d96b2403004972ed2a18) --- src/libslic3r/Support/SupportMaterial.cpp | 84 +++++++++++++++++++---- 1 file changed, 71 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 85c461b02b..b64862f60e 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -1685,6 +1685,48 @@ static inline std::tuple detect_contacts( return std::make_tuple(std::move(contact_polygons), std::move(enforcer_polygons), no_interface_offset); } +// find the object layer that is closest to the {layer.bottom_z-gap_support_object} for top contact, +// or {layer.print_z+gap_object_support} for bottom contact +Layer* sync_gap_with_object_layer(const Layer& layer, const coordf_t gap_support_object, bool is_top_contact) +{ + // sync gap with the object layer height + float gap_synced = 0; + if (is_top_contact) { + Layer* lower_layer = layer.lower_layer, * last_valid_gap_layer = layer.lower_layer; + while (lower_layer && gap_synced < gap_support_object) { + last_valid_gap_layer = lower_layer; + gap_synced += lower_layer->height; + lower_layer = lower_layer->lower_layer; + + } + // maybe gap_synced is too large, find the nearest object layer (one layer above may be better) + if (std::abs(gap_synced - last_valid_gap_layer->height - gap_support_object) < std::abs(gap_synced - gap_support_object)) { + gap_synced -= last_valid_gap_layer->height; + last_valid_gap_layer = last_valid_gap_layer->upper_layer; + } + lower_layer = last_valid_gap_layer; // layer just below the last valid gap layer + if (last_valid_gap_layer->lower_layer) + lower_layer = last_valid_gap_layer->lower_layer; + return lower_layer; + }else{ + Layer* upper_layer = layer.upper_layer, * last_valid_gap_layer = layer.upper_layer; + while (upper_layer && gap_synced < gap_support_object) { + last_valid_gap_layer = upper_layer; + gap_synced += upper_layer->height; + upper_layer = upper_layer->upper_layer; + } + // maybe gap_synced is too large, find the nearest object layer (one layer above may be better) + if (std::abs(gap_synced - last_valid_gap_layer->height - gap_support_object) < std::abs(gap_synced - gap_support_object)) { + gap_synced -= last_valid_gap_layer->height; + last_valid_gap_layer = last_valid_gap_layer->lower_layer; + } + upper_layer = last_valid_gap_layer; // layer just above the last valid gap layer + if (last_valid_gap_layer->upper_layer) + upper_layer = last_valid_gap_layer->upper_layer; + return upper_layer; + } +} + // Allocate one, possibly two support contact layers. // For "thick" overhangs, one support layer will be generated to support normal extrusions, the other to support the "thick" extrusions. static inline std::pair new_contact_layer( @@ -1712,9 +1754,18 @@ static inline std::pair new_cont print_z = layer.bottom_z(); height = layer.lower_layer->height; bottom_z = (layer_id == 1) ? slicing_params.object_print_z_min : layer.lower_layer->lower_layer->print_z; - } else { - print_z = layer.bottom_z() - slicing_params.gap_support_object; - height = print_config.independent_support_layer_height ? 0. : layer.lower_layer->height/*object_config.layer_height*/; // BBS: need to consider adaptive layer heights + } + else { + // BBS: need to consider adaptive layer heights + if (print_config.independent_support_layer_height) { + print_z = layer.bottom_z() - slicing_params.gap_support_object; + height = 0; + } + else { + Layer* synced_layer = sync_gap_with_object_layer(layer, slicing_params.gap_support_object, true); + print_z = synced_layer->print_z; + height = synced_layer->height; + } bottom_z = print_z - height; // Ignore this contact area if it's too low. // Don't want to print a layer below the first layer height as it may not stick well. @@ -1739,7 +1790,7 @@ static inline std::pair new_cont // Contact layer will be printed with a normal flow, but // it will support layers printed with a bridging flow. - if (object_config.thick_bridges && SupportMaterialInternal::has_bridging_extrusions(layer)) { + if (object_config.thick_bridges && SupportMaterialInternal::has_bridging_extrusions(layer) && print_config.independent_support_layer_height) { coordf_t bridging_height = 0.; for (const LayerRegion* region : layer.regions()) bridging_height += region->region().bridging_height_avg(print_config); @@ -2373,15 +2424,22 @@ static inline SupportGeneratorLayer* detect_bottom_contacts( // Grow top surfaces so that interface and support generation are generated // with some spacing from object - it looks we don't need the actual // top shapes so this can be done here - //FIXME calculate layer height based on the actual thickness of the layer: - // If the layer is extruded with no bridging flow, support just the normal extrusions. - layer_new.height = slicing_params.soluble_interface || !object.print()->config().independent_support_layer_height ? - // Align the interface layer with the object's layer height. - layer.upper_layer->height : - // Place a bridge flow interface layer or the normal flow interface layer over the top surface. - support_params.support_material_bottom_interface_flow.height(); - layer_new.print_z = slicing_params.soluble_interface ? layer.upper_layer->print_z : - layer.print_z + layer_new.height + slicing_params.gap_object_support; + if (object.print()->config().independent_support_layer_height) { + // If the layer is extruded with no bridging flow, support just the normal extrusions. + layer_new.height = slicing_params.soluble_interface? + // Align the interface layer with the object's layer height. + layer.upper_layer->height : + // Place a bridge flow interface layer or the normal flow interface layer over the top surface. + support_params.support_material_bottom_interface_flow.height(); + layer_new.print_z = slicing_params.soluble_interface ? layer.upper_layer->print_z : + layer.print_z + layer_new.height + slicing_params.gap_object_support; + } + else { + Layer* synced_layer = sync_gap_with_object_layer(layer, slicing_params.gap_object_support, false); + // If the layer is extruded with no bridging flow, support just the normal extrusions. + layer_new.height = synced_layer->height; + layer_new.print_z = synced_layer->print_z; + } layer_new.bottom_z = layer.print_z; layer_new.idx_object_layer_below = layer_id; layer_new.bridging = !slicing_params.soluble_interface && object.config().thick_bridges; From c443951f30f7dd1739a3d0f902395024c6378877 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 28 Nov 2023 17:17:11 +0800 Subject: [PATCH 13/72] FIX: organic support not work with raft only There is no raft generated when only raft enabled but no support needed. jira: none Change-Id: Ic0c9269e2f98038d85c9bc54e4a85f892dc5d764 (cherry picked from commit 1106ff825312b3f192de84019dfee1453afbc1cc) --- src/libslic3r/Support/TreeSupport3D.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 88074ccac8..60a66546dc 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -3367,6 +3367,9 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume bool has_raft = config.raft_layers.size() > 0; num_support_layers = std::max(num_support_layers, config.raft_layers.size()); + if (num_support_layers == 0) + continue; + SupportParameters support_params(print_object); support_params.with_sheath = true; // Don't override the support density of tree supports, as the support density is used for raft. From 8c51f3e693592ab5f219d3586cfba79a1b6b6acc Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 1 Dec 2023 10:12:22 +0800 Subject: [PATCH 14/72] FIX: crash when support type and style are inconsistent jira: STUDIO-5428 Change-Id: Ib1e79c71736810099e15282c30524e55e8f60f34 (cherry picked from commit aefb7fbaf25146c03bd2eb336f58ed2eb0e83ea6) (cherry picked from commit 697a7bbee327901ddff22369b89c8d752ef7e8d7) --- src/libslic3r/Support/SupportMaterial.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index b64862f60e..2975c2c3fb 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -677,7 +677,9 @@ public: m_extrusion_width(params.extrusion_width), m_support_material_closing_radius(params.support_closing_radius) { - if (m_style != smsSnug) m_style = smsGrid; + if (m_style == smsDefault) m_style = smsGrid; + if (std::set{smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsOrganic}.count(m_style)) + m_style = smsGrid; switch (m_style) { case smsGrid: { @@ -769,6 +771,13 @@ public: ) { switch (m_style) { + case smsTreeSlim: + case smsTreeStrong: + case smsTreeHybrid: + case smsOrganic: + assert(false); + //[[fallthrough]]; + return Polygons(); case smsGrid: { #ifdef SUPPORT_USE_AGG_RASTERIZER From 1c498664a59a54637692e02e8ecd699cde2c92f8 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 20 Dec 2023 12:09:27 +0800 Subject: [PATCH 15/72] FIX: several support bugs 1. interlaced rectilinear interface pattern not working with tree supports 2. infill overlaps with walls when wall count>1 3. support blockers can't block sharp tail detection in normal support jira: STUDIO-5663 4. bottom z distance=0 not working for normal support. jira: STUDIO-5676 github: #3203 Change-Id: I025eff2aaad90ad565661aa656c59c82ff969bbf (cherry picked from commit 5aaf7ead0fd697043f673161e0ede0145ec49f4d) (cherry picked from commit f3bd5ff87021b5c26794751a1f1da4349b603102) --- src/libslic3r/Layer.hpp | 1 + src/libslic3r/Support/SupportMaterial.cpp | 27 +++++++++++++---------- src/libslic3r/Support/TreeSupport.cpp | 12 +++++++--- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index a277aca29e..747e943179 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -311,6 +311,7 @@ protected: { ExPolygon *area; int type; + int interface_id = 0; coordf_t dist_to_top; // mm dist to top bool need_infill = false; bool need_extra_wall = false; diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 2975c2c3fb..16156965fb 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -1523,8 +1523,9 @@ static inline ExPolygons detect_overhangs( // spanning just the projection between the two slices. // Subtracting them as they are may leave unwanted narrow // residues of diff_polygons that would then be supported. - diff_polygons = diff(diff_polygons, - expand(union_(annotations.blockers_layers[layer_id]), float(1000. * SCALED_EPSILON))); + auto blocker = expand(union_(annotations.blockers_layers[layer_id]), float(1000. * SCALED_EPSILON)); + diff_polygons = diff(diff_polygons, blocker); + layer.sharp_tails = diff_ex(layer.sharp_tails, blocker); } if (bridge_no_support) { @@ -1729,9 +1730,11 @@ Layer* sync_gap_with_object_layer(const Layer& layer, const coordf_t gap_support gap_synced -= last_valid_gap_layer->height; last_valid_gap_layer = last_valid_gap_layer->lower_layer; } - upper_layer = last_valid_gap_layer; // layer just above the last valid gap layer - if (last_valid_gap_layer->upper_layer) - upper_layer = last_valid_gap_layer->upper_layer; + if (gap_support_object > 0) { + upper_layer = last_valid_gap_layer; // layer just above the last valid gap layer + if (last_valid_gap_layer->upper_layer) + upper_layer = last_valid_gap_layer->upper_layer; + } return upper_layer; } } @@ -2433,21 +2436,21 @@ static inline SupportGeneratorLayer* detect_bottom_contacts( // Grow top surfaces so that interface and support generation are generated // with some spacing from object - it looks we don't need the actual // top shapes so this can be done here + Layer* upper_layer = layer.upper_layer; if (object.print()->config().independent_support_layer_height) { // If the layer is extruded with no bridging flow, support just the normal extrusions. - layer_new.height = slicing_params.soluble_interface? + layer_new.height = slicing_params.soluble_interface ? // Align the interface layer with the object's layer height. - layer.upper_layer->height : + upper_layer->height : // Place a bridge flow interface layer or the normal flow interface layer over the top surface. support_params.support_material_bottom_interface_flow.height(); - layer_new.print_z = slicing_params.soluble_interface ? layer.upper_layer->print_z : + layer_new.print_z = slicing_params.soluble_interface ? upper_layer->print_z : layer.print_z + layer_new.height + slicing_params.gap_object_support; } else { - Layer* synced_layer = sync_gap_with_object_layer(layer, slicing_params.gap_object_support, false); - // If the layer is extruded with no bridging flow, support just the normal extrusions. - layer_new.height = synced_layer->height; - layer_new.print_z = synced_layer->print_z; + upper_layer = sync_gap_with_object_layer(layer, slicing_params.gap_object_support, false); + layer_new.height = upper_layer->height; + layer_new.print_z = upper_layer->print_z; } layer_new.bottom_z = layer.print_z; layer_new.idx_object_layer_below = layer_id; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 51df15c0e7..ab5366dfa5 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1290,7 +1290,7 @@ static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const Print& pr FillParams fill_params; fill_params.density = support_density; fill_params.dont_adjust = true; - ExPolygons to_infill = support_area_new; + ExPolygons to_infill = offset_ex(support_area, -float(wall_count) * float(flow.scaled_spacing()), jtSquare); std::vector fill_boxes = fill_expolygons_generate_paths(dst, to_infill, filler_support, fill_params, role, flow); // allow wall_count to be zero, which means only draw infill @@ -1524,7 +1524,8 @@ void TreeSupport::generate_toolpaths() fill_params.dont_sort = true; } if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) - filler_interface->layer_id = round(area_group.dist_to_top / ts_layer->height); + filler_interface->layer_id = area_group.interface_id; + fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys, filler_interface.get(), fill_params, erSupportMaterialInterface, m_support_material_interface_flow); } @@ -1969,6 +1970,7 @@ void TreeSupport::draw_circles(const std::vector>& con coordf_t max_layers_above_base = 0; coordf_t max_layers_above_roof = 0; coordf_t max_layers_above_roof1 = 0; + int interface_id = 0; bool has_polygon_node = false; bool has_circle_node = false; @@ -2051,6 +2053,7 @@ void TreeSupport::draw_circles(const std::vector>& con { append(roof_areas, area); max_layers_above_roof = std::max(max_layers_above_roof, node.dist_mm_to_top); + interface_id = node.obj_layer_nr % top_interface_layers; } else { @@ -2140,7 +2143,10 @@ void TreeSupport::draw_circles(const std::vector>& con area_groups.emplace_back(&area, SupportLayer::BaseType, max_layers_above_base); area_groups.back().need_infill = has_polygon_node; } - for (auto &area : ts_layer->roof_areas) area_groups.emplace_back(&area, SupportLayer::RoofType, max_layers_above_roof); + for (auto& area : ts_layer->roof_areas) { + area_groups.emplace_back(&area, SupportLayer::RoofType, max_layers_above_roof); + area_groups.back().interface_id = interface_id; + } for (auto &area : ts_layer->floor_areas) area_groups.emplace_back(&area, SupportLayer::FloorType, 10000); for (auto &area : ts_layer->roof_1st_layer) area_groups.emplace_back(&area, SupportLayer::Roof1stLayer, max_layers_above_roof1); From cc9f29c46302d993269d0ada99bf62ac438c70cc Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Mon, 27 Jan 2025 17:09:32 +0800 Subject: [PATCH 16/72] Rename `smsOrganic` to `smsTreeOrganic` --- src/libslic3r/Print.cpp | 4 ++-- src/libslic3r/PrintConfig.cpp | 2 +- src/libslic3r/PrintConfig.hpp | 2 +- src/libslic3r/Support/SupportCommon.cpp | 2 +- src/libslic3r/Support/SupportMaterial.cpp | 4 ++-- src/libslic3r/Support/SupportParameters.hpp | 2 +- src/libslic3r/Support/TreeSupport.cpp | 2 +- src/slic3r/GUI/ConfigManipulation.cpp | 4 ++-- src/slic3r/GUI/Tab.cpp | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 31f225cfa2..642cf79b5c 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1166,7 +1166,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* // Custom layering is not allowed for tree supports as of now. for (size_t print_object_idx = 0; print_object_idx < m_objects.size(); ++ print_object_idx) if (const PrintObject &print_object = *m_objects[print_object_idx]; - print_object.has_support_material() && is_tree(print_object.config().support_type.value) && (print_object.config().support_style.value == smsOrganic || + print_object.has_support_material() && is_tree(print_object.config().support_type.value) && (print_object.config().support_style.value == smsTreeOrganic || // Orca: use organic as default print_object.config().support_style.value == smsDefault) && print_object.model_object()->has_custom_layering()) { @@ -1339,7 +1339,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* // Prusa: Fixing crashes with invalid tip diameter or branch diameter // https://github.com/prusa3d/PrusaSlicer/commit/96b3ae85013ac363cd1c3e98ec6b7938aeacf46d - if (is_tree(object->config().support_type.value) && (object->config().support_style == smsOrganic || + if (is_tree(object->config().support_type.value) && (object->config().support_style == smsTreeOrganic || // Orca: use organic as default object->config().support_style == smsDefault)) { float extrusion_width = std::min( diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 8fb93e3024..7c6be8edab 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -230,7 +230,7 @@ static t_config_enum_values s_keys_map_SupportMaterialStyle { { "tree_slim", smsTreeSlim }, { "tree_strong", smsTreeStrong }, { "tree_hybrid", smsTreeHybrid }, - { "organic", smsOrganic } + { "organic", smsTreeOrganic } }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialStyle) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 33a0e7eabf..6e1f2701d6 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -132,7 +132,7 @@ enum SupportMaterialPattern { }; enum SupportMaterialStyle { - smsDefault, smsGrid, smsSnug, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsOrganic, + smsDefault, smsGrid, smsSnug, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsTreeOrganic, }; enum LongRectrationLevel diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index 4754ad47d1..edc71e76df 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -1808,7 +1808,7 @@ void generate_support_toolpaths( filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); sheath = true; no_sort = true; - } else if (support_params.support_style == SupportMaterialStyle::smsOrganic) { + } else if (support_params.support_style == SupportMaterialStyle::smsTreeOrganic) { tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow, support_params); done = true; } diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 16156965fb..cbf331fd78 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -678,7 +678,7 @@ public: m_support_material_closing_radius(params.support_closing_radius) { if (m_style == smsDefault) m_style = smsGrid; - if (std::set{smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsOrganic}.count(m_style)) + if (std::set{smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsTreeOrganic}.count(m_style)) m_style = smsGrid; switch (m_style) { case smsGrid: @@ -774,7 +774,7 @@ public: case smsTreeSlim: case smsTreeStrong: case smsTreeHybrid: - case smsOrganic: + case smsTreeOrganic: assert(false); //[[fallthrough]]; return Polygons(); diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index 1318925849..f36709fb53 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -161,7 +161,7 @@ struct SupportParameters { if (support_style == smsDefault) { if (is_tree(object_config.support_type)) { // Orca: use organic as default - support_style = smsOrganic; + support_style = smsTreeOrganic; } else { support_style = smsGrid; } diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index ab5366dfa5..945cef143f 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1631,7 +1631,7 @@ void TreeSupport::generate_toolpaths() void TreeSupport::generate() { - if (m_support_params.support_style == smsOrganic) { + if (m_support_params.support_style == smsTreeOrganic) { generate_tree_support_3D(*m_object, this, this->throw_on_cancel); return; } diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index a8e77677d4..3425890d70 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -426,7 +426,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con auto support_type = config->opt_enum("support_type"); auto support_style = config->opt_enum("support_style"); std::set enum_set_normal = { smsDefault, smsGrid, smsSnug }; - std::set enum_set_tree = { smsDefault, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsOrganic }; + std::set enum_set_tree = { smsDefault, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsTreeOrganic }; auto & set = is_tree(support_type) ? enum_set_tree : enum_set_normal; if (set.find(support_style) == set.end()) { DynamicPrintConfig new_conf = *config; @@ -603,7 +603,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co //toggle_field("support_closing_radius", have_support_material && support_style == smsSnug); bool support_is_tree = config->opt_bool("enable_support") && is_tree(support_type); - bool support_is_normal_tree = support_is_tree && support_style != smsOrganic && + bool support_is_normal_tree = support_is_tree && support_style != smsTreeOrganic && // Orca: use organic as default support_style != smsDefault; bool support_is_organic = support_is_tree && !support_is_normal_tree; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 67863846d2..916cb38ce6 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2441,7 +2441,7 @@ void TabPrint::toggle_options() if (auto choice = dynamic_cast(field)) { auto def = print_config_def.get("support_style"); std::vector enum_set_normal = {smsDefault, smsGrid, smsSnug }; - std::vector enum_set_tree = { smsDefault, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsOrganic }; + std::vector enum_set_tree = { smsDefault, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsTreeOrganic }; auto & set = is_tree(support_type) ? enum_set_tree : enum_set_normal; auto & opt = const_cast(field->m_opt); auto cb = dynamic_cast(choice->window); From b3be5bb1618cf685be3872a40104fb00f1d72c19 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 14 Sep 2023 17:20:58 +0800 Subject: [PATCH 17/72] ENH: tree support uses accurate lslices For tree support, use lslices as tree support island when generating brim, as this is faster and more accurate. For normal support, still use "support_fills.polygons_covered_by_spacing()" as support island when generating brim; Jira: studio 4332 Change-Id: Ibfadd3a166606f824e5780b57112fff221470aaf (cherry picked from commit 64960b19818c7029eaaaf3d8a89804aeaa26f11d) (cherry picked from commit 181b05c236d251ce29a7730966012c5402061899) --- src/libslic3r/Support/TreeSupport.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 945cef143f..64fd58d315 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1973,6 +1973,7 @@ void TreeSupport::draw_circles(const std::vector>& con int interface_id = 0; bool has_polygon_node = false; bool has_circle_node = false; + bool need_extra_wall = false; BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size(); //Draw the support areas and add the roofs appropriately to the support roof instead of normal areas. @@ -2039,7 +2040,6 @@ void TreeSupport::draw_circles(const std::vector>& con } append(area, overhang_expanded); } - has_circle_node = true; } if (layer_nr>0 && node.distance_to_top < 0) @@ -2061,19 +2061,8 @@ void TreeSupport::draw_circles(const std::vector>& con max_layers_above_base = std::max(max_layers_above_base, node.dist_mm_to_top); } - if (layer_nr < brim_skirt_layers) - append(ts_layer->lslices, area); } - ts_layer->lslices = std::move(union_ex(ts_layer->lslices)); - - //Must update bounding box which is used in avoid crossing perimeter - ts_layer->lslices_bboxes.clear(); - ts_layer->lslices_bboxes.reserve(ts_layer->lslices.size()); - for (const ExPolygon &expoly : ts_layer->lslices) - ts_layer->lslices_bboxes.emplace_back(get_extents(expoly)); - ts_layer->backup_untyped_slices(); - m_object->print()->set_status(65, (boost::format( _u8L("Support: generate polygons at layer %d")) % layer_nr).str()); // join roof segments @@ -2142,6 +2131,7 @@ void TreeSupport::draw_circles(const std::vector>& con for (auto& area : ts_layer->base_areas) { area_groups.emplace_back(&area, SupportLayer::BaseType, max_layers_above_base); area_groups.back().need_infill = has_polygon_node; + // area_groups.back().need_extra_wall = need_extra_wall; } for (auto& area : ts_layer->roof_areas) { area_groups.emplace_back(&area, SupportLayer::RoofType, max_layers_above_roof); @@ -2158,8 +2148,19 @@ void TreeSupport::draw_circles(const std::vector>& con return bbox_size[0] < scale_(2) && bbox_size[1] < scale_(2); }), expoly->holes.end()); + + if (layer_nr < brim_skirt_layers) + ts_layer->lslices.emplace_back(*expoly); } + ts_layer->lslices = std::move(union_ex(ts_layer->lslices)); + //Must update bounding box which is used in avoid crossing perimeter + ts_layer->lslices_bboxes.clear(); + ts_layer->lslices_bboxes.reserve(ts_layer->lslices.size()); + for (const ExPolygon& expoly : ts_layer->lslices) + ts_layer->lslices_bboxes.emplace_back(get_extents(expoly)); + ts_layer->backup_untyped_slices(); + } }); From de24d0ac2fa0619290bba35fbf4cf8965ae7334d Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 4 Aug 2023 22:29:56 +0800 Subject: [PATCH 18/72] FIX: tree support bottom interface layers were not correct The bottom interface layers were not right when "independent support layer height" is enabled. This commit ensures there are always 2 bottom interface layers and the gap is not less than specified. However, the gap may be slightly larger. Jira: STUDIO-3842, STUDIO-2138 Github: #2127 Change-Id: Ifd8fbc4c7bc6dd92f2534fdd0179458a9e93c79a (cherry picked from commit edcdad162ef68ab94f8d1e66c225c2617f43e00e) --- src/libslic3r/Support/TreeSupport.cpp | 35 ++++++++++++++++----------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 64fd58d315..16127d95c5 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1471,6 +1471,7 @@ void TreeSupport::generate_toolpaths() SupportLayer* ts_layer = m_object->get_support_layer(layer_id); Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); + m_support_material_interface_flow = support_material_interface_flow(m_object, ts_layer->height); // update flow using real support layer height coordf_t support_spacing = object_config.support_base_pattern_spacing.value + support_flow.spacing(); coordf_t support_density = std::min(1., support_flow.spacing() / support_spacing); ts_layer->support_fills.no_sort = false; @@ -1934,9 +1935,7 @@ void TreeSupport::draw_circles(const std::vector>& con const PrintObjectConfig& object_config = m_object->config(); BOOST_LOG_TRIVIAL(info) << "draw_circles for object: " << m_object->model_object()->name; - // coconut: previously std::unordered_map in m_collision_cache is not multi-thread safe which may cause programs stuck, here we change to tbb::concurrent_unordered_map - tbb::parallel_for( - tbb::blocked_range(0, m_object->layer_count()), + tbb::parallel_for(tbb::blocked_range(0, m_object->layer_count()), [&](const tbb::blocked_range& range) { for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) @@ -1998,7 +1997,7 @@ void TreeSupport::draw_circles(const std::vector>& con has_polygon_node = true; } else { - Polygon circle; + Polygon circle(branch_circle); double scale = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor) / branch_radius; double moveX = node.movement.x() / (scale * branch_radius_scaled); @@ -2011,14 +2010,14 @@ void TreeSupport::draw_circles(const std::vector>& con scale * (1 + moveX * moveX * vsize_inv),scale * (0 + moveX * moveY * vsize_inv), scale * (0 + moveX * moveY * vsize_inv),scale * (1 + moveY * moveY * vsize_inv), }; + int i = 0; for (auto vertex: branch_circle.points) { vertex = Point(matrix[0] * vertex.x() + matrix[1] * vertex.y(), matrix[2] * vertex.x() + matrix[3] * vertex.y()); - circle.append(node.position + vertex); + circle.points[i++] = node.position + vertex; } } else { - for (auto iter = branch_circle.points.begin(); iter != branch_circle.points.end(); iter++) { - Point corner = (*iter) * scale; - circle.append(node.position + corner); + for (int i = 0;i< circle.points.size(); i++) { + circle.points[i] = circle.points[i] * scale + node.position; } } if (layer_nr == 0 && m_raft_layers == 0) { @@ -2107,16 +2106,22 @@ void TreeSupport::draw_circles(const std::vector>& con { if (layer_nr >= bottom_interface_layers + bottom_gap_layers) { - for (size_t i = 0; i <= bottom_gap_layers; i++) + // find the lowest interface layer + // TODO the gap may not be exact when "independent support layer height" is enabled + size_t layer_nr_next = layer_nr; + for (size_t i = 0; i < bottom_interface_layers && layer_nr_next>0; i++) { + layer_nr_next = m_ts_data->layer_heights[layer_nr_next].next_layer_nr; + } + for (size_t i = 0; i <= bottom_gap_layers && i<=layer_nr_next; i++) { - const Layer* below_layer = m_object->get_layer(layer_nr - bottom_interface_layers - i); + const Layer* below_layer = m_object->get_layer(layer_nr_next - i); ExPolygons bottom_interface = intersection_ex(base_areas, below_layer->lslices); floor_areas.insert(floor_areas.end(), bottom_interface.begin(), bottom_interface.end()); } } if (floor_areas.empty() == false) { - floor_areas = std::move(diff_ex(floor_areas, avoid_region_interface)); - floor_areas = std::move(offset2_ex(floor_areas, contact_dist_scaled, -contact_dist_scaled)); + //floor_areas = std::move(diff_ex(floor_areas, avoid_region_interface)); + //floor_areas = std::move(offset2_ex(floor_areas, contact_dist_scaled, -contact_dist_scaled)); base_areas = std::move(diff_ex(base_areas, offset_ex(floor_areas, 10))); } } @@ -2238,6 +2243,8 @@ void TreeSupport::draw_circles(const std::vector>& con // check if poly's contour intersects with expoly's contour auto intersects_contour = [](Polygon poly, ExPolygon expoly, Point& pt_on_poly, Point& pt_on_expoly, Point& pt_far_on_poly, float dist_thresh = 0.01) { + Polylines pl_out = intersection_pl(to_polylines(expoly), ExPolygon(poly)); + if (pl_out.empty()) return false; float min_dist = std::numeric_limits::max(); float max_dist = 0; for (auto from : poly.points) { @@ -3045,8 +3052,8 @@ std::vector TreeSupport::plan_layer_heights(std::vectornext_layer_nr: " << layer_heights[i].print_z << " " << layer_heights[i].height << " " - << i << "->" << layer_heights[i].next_layer_nr << std::endl; + BOOST_LOG_TRIVIAL(info) << "plan_layer_heights print_z, height, layer_nr->next_layer_nr: " << layer_heights[i].print_z << " " << layer_heights[i].height << " " + << i << "->" << layer_heights[i].next_layer_nr; } return layer_heights; From d0868d6711682d6a2eee742407cc6be9a20f3261 Mon Sep 17 00:00:00 2001 From: Arthur Date: Sat, 6 Jan 2024 21:27:46 +0800 Subject: [PATCH 19/72] ENH: accurate top z distance for tree support 1. accurate top z distance for tree support Also fix a bug that bottom z and top z distances are misused. jira: STUDIO-3000, STUDIO-5990 github: #1827 2. Change interface pattern to interlaced rectilinear when using support material. 2. clean up tree support code Change-Id: Icc8ce1b6844c841a6fbd1d623df707fdc8ed0f7b (cherry picked from commit da7412a48dfb5767918ef125b9d0fb9718c03a61) (cherry picked from commit 39ae64fc53abec794d740e36baaa13fd6fb35849) --- localization/i18n/zh_CN/OrcaSlicer_zh_CN.po | 4 +- src/libslic3r/ClipperUtils.cpp | 4 + src/libslic3r/ClipperUtils.hpp | 1 + src/libslic3r/Layer.hpp | 2 +- src/libslic3r/Support/TreeSupport.cpp | 612 ++++++++++---------- src/libslic3r/Support/TreeSupport.hpp | 90 +-- src/libslic3r/utils.cpp | 18 +- src/slic3r/GUI/Tab.cpp | 6 +- 8 files changed, 365 insertions(+), 372 deletions(-) diff --git a/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po b/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po index 2e2d49487e..effc208b23 100644 --- a/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po +++ b/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po @@ -7315,11 +7315,11 @@ msgstr "" msgid "" "When using support material for the support interface, We recommend the " "following settings:\n" -"0 top z distance, 0 interface spacing, concentric pattern and disable " +"0 top z distance, 0 interface spacing, interlaced rectilinear pattern and disable " "independent support layer height" msgstr "" "当使用支持界面的支持材料时,我们推荐以下设置:\n" -"0顶层z距离,0接触层间距,同心图案,并且禁用独立支撑层高" +"0顶层z距离,0接触层间距,交叠直线图案,并且禁用独立支撑层高" msgid "" "Enabling this option will modify the model's shape. If your print requires " diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 9082382a54..7d893a3bf2 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -666,6 +666,10 @@ Slic3r::Polygons diff_clipped(const Slic3r::Polygons &subject, const Slic3r::Pol { return diff(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); } Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) { return diff_ex(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); } +Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons & subject, const Slic3r::ExPolygons & clip, ApplySafetyOffset do_safety_offset) +{ + return diff_ex(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); +} Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index bc79dca5d5..c6aebb5e4a 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -434,6 +434,7 @@ Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygon // To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon. Slic3r::Polygons diff_clipped(const Slic3r::Polygons &src, const Slic3r::Polygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons &src, const Slic3r::Polygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons &src, const Slic3r::ExPolygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 747e943179..45f185c425 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -317,7 +317,7 @@ protected: bool need_extra_wall = false; AreaGroup(ExPolygon *a, int t, coordf_t d) : area(a), type(t), dist_to_top(d) {} }; - enum OverhangType { Detected = 0, Enforced }; + enum OverhangType { Detected = 0, Enforced, SharpTail }; std::vector area_groups; std::map overhang_types; }; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 16127d95c5..bae9ee3de1 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -14,6 +14,7 @@ #include "TreeSupport3D.hpp" #include +#include #include #include @@ -80,7 +81,7 @@ enum TreeSupportStage { class TreeSupportProfiler { public: - uint32_t stage_durations[NUM_STAGES]; + uint32_t stage_durations[NUM_STAGES] = { 0 }; uint32_t stage_index = 0; boost::posix_time::ptime tic_time; boost::posix_time::ptime toc_time; @@ -110,14 +111,15 @@ public: } void tic() { tic_time = boost::posix_time::microsec_clock::local_time(); } - void toc() { toc_time = boost::posix_time::microsec_clock::local_time(); } - void stage_add(TreeSupportStage stage, bool do_toc = false) + uint32_t toc() { + toc_time = boost::posix_time::microsec_clock::local_time(); + return (toc_time - tic_time).total_milliseconds(); + } + void stage_add(TreeSupportStage stage) { if (stage > NUM_STAGES) return; - if(do_toc) - toc_time = boost::posix_time::microsec_clock::local_time(); - stage_durations[stage] += (toc_time - tic_time).total_milliseconds(); + stage_durations[stage] += toc(); } std::string report() @@ -172,35 +174,16 @@ Lines spanning_tree_to_lines(const std::vector& spanning_tr #ifdef SUPPORT_TREE_DEBUG_TO_SVG -static std::string get_svg_filename(const std::string& tag = "bbl_ts", const std::string& layer_nr_or_z="") -{ - static bool rand_init = false; - - if (!rand_init) { - srand(time(NULL)); - rand_init = true; - } - - int rand_num = rand() % 1000000; - std::string prefix = "C:/Users/arthur.tang/AppData/Roaming/BambuStudioInternal/SVG/"; - //makedir(prefix); - std::string suffix = ".svg"; - std::string name = prefix + tag; - if(!layer_nr_or_z.empty()) - name+= "_" + layer_nr_or_z; - return name + suffix; -} - static void draw_contours_and_nodes_to_svg ( - std::string layer_nr_or_z, + std::string fname, const ExPolygons &overhangs, const ExPolygons &overhangs_after_offset, const ExPolygons &outlines_below, - const std::vector &layer_nodes, - const std::vector &lower_layer_nodes, - std::string name_prefix, - std::vector legends = { "overhang","avoid","outlines" }, std::vector colors = { "blue","red","yellow" } + const std::vector& layer_nodes, + const std::vector& lower_layer_nodes, + std::vector legends = { "overhang","avoid","outlines" }, + std::vector colors = { "blue","red","yellow" } ) { BoundingBox bbox = get_extents(overhangs); @@ -216,34 +199,29 @@ static void draw_contours_and_nodes_to_svg bbox.max.y() = std::max(bbox.max.y(), (coord_t)scale_(10)); SVG svg; - svg.open(get_svg_filename(name_prefix, layer_nr_or_z), bbox); + svg.open(fname, bbox); if (!svg.is_opened()) return; // draw grid svg.draw_grid(bbox, "gray", coord_t(scale_(0.05))); // draw overhang areas - svg.draw_outline(union_ex(overhangs), colors[0]); - svg.draw_outline(union_ex(overhangs_after_offset), colors[1]); + svg.draw_outline(overhangs, colors[0]); + svg.draw_outline(overhangs_after_offset, colors[1]); svg.draw_outline(outlines_below, colors[2]); // draw legend - if (!lower_layer_nodes.empty()) { - svg.draw_text(bbox.min + Point(scale_(0), scale_(0)), format("nPoints: %1%->%2%",layer_nodes.size(), lower_layer_nodes.size()).c_str(), "green", 2); - } - else { - svg.draw_text(bbox.min + Point(scale_(0), scale_(0)), ("nPoints: " + std::to_string(layer_nodes.size())).c_str(), "green", 2); - } + svg.draw_text(bbox.min + Point(scale_(0), scale_(0)), format("nPoints: %1%->%2%",layer_nodes.size(), lower_layer_nodes.size()).c_str(), "green", 2); svg.draw_text(bbox.min + Point(scale_(0), scale_(2)), legends[0].c_str(), colors[0].c_str(), 2); svg.draw_text(bbox.min + Point(scale_(0), scale_(4)), legends[1].c_str(), colors[1].c_str(), 2); svg.draw_text(bbox.min + Point(scale_(0), scale_(6)), legends[2].c_str(), colors[2].c_str(), 2); // draw layer nodes svg.draw(layer_pts, "green", coord_t(scale_(0.1))); -#if 0 + // lower layer points layer_pts.clear(); - for (SupportNode *node : lower_layer_nodes) { + for (SupportNode* node : lower_layer_nodes) { layer_pts.push_back(node->position); } svg.draw(layer_pts, "black", coord_t(scale_(0.1))); @@ -255,11 +233,10 @@ static void draw_contours_and_nodes_to_svg layer_pts.push_back(node->parent->position); } svg.draw(layer_pts, "blue", coord_t(scale_(0.1))); -#endif } static void draw_layer_mst -(const std::string &layer_nr_or_z, +(const std::string &fname, const std::vector &spanning_trees, const ExPolygons& outline ) @@ -272,7 +249,7 @@ static void draw_layer_mst bbox.merge(bb); } - SVG svg(get_svg_filename("mstree",layer_nr_or_z).c_str(), bbox); + SVG svg(fname, bbox); if (!svg.is_opened()) return; svg.draw(lines, "blue", coord_t(scale_(0.05))); @@ -281,48 +258,6 @@ static void draw_layer_mst svg.draw(spanning_tree.vertices(), "black", coord_t(scale_(0.1))); } -static void draw_two_overhangs_to_svg(SupportLayer* ts_layer, const ExPolygons& overhangs1, const ExPolygons& overhangs2) -{ - if (overhangs1.empty() && overhangs2.empty()) - return; - BoundingBox bbox1 = get_extents(overhangs1); - BoundingBox bbox2 = get_extents(overhangs2); - bbox1.merge(bbox2); - - SVG svg(get_svg_filename("two_overhangs", std::to_string(ts_layer->print_z)), bbox1); - if (!svg.is_opened()) return; - - svg.draw(union_ex(overhangs1), "blue"); - svg.draw(union_ex(overhangs2), "red"); -} - -static void draw_polylines(SupportLayer* ts_layer, Polylines& polylines) -{ - if (polylines.empty()) - return; - BoundingBox bbox = get_extents(polylines); - - SVG svg(get_svg_filename("lightnings", std::to_string(ts_layer->print_z)), bbox); - if (!svg.is_opened()) return; - - int id = 0; - for (Polyline& pline : polylines) - { - int i1, i2; - for (size_t i = 0; i < pline.size() - 1; i++) - { - i1 = i; - i2 = i + 1; - svg.draw(Line(pline.points[i1], pline.points[i2]), "blue"); - svg.draw(pline.points[i1], "red"); - id++; - svg.draw_text(pline.points[i1], std::to_string(id).c_str(), "black", 1); - } - svg.draw(pline.points[i2], "red"); - id++; - svg.draw_text(pline.points[i2], std::to_string(id).c_str(), "black", 1); - } -} #endif // Move point from inside polygon if distance>0, outside if distance<0. @@ -683,7 +618,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p // align with the centered object in current plate (may not be the 1st plate, so need to add the plate offset) m_machine_border.translate(Point(scale_(plate_offset(0)), scale_(plate_offset(1))) - m_object->instances().front().shift); #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg(get_svg_filename("machine_boarder"), m_object->bounding_box()); + SVG svg(debug_out_path("machine_boarder.svg"), m_object->bounding_box()); if (svg.is_opened()) svg.draw(m_machine_border, "yellow"); #endif } @@ -885,8 +820,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) if (!overhang.empty()) { has_sharp_tails = true; #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg(get_svg_filename(format("sharp_tail_orig_%.02f", layer->print_z)), m_object->bounding_box()); - if (svg.is_opened()) svg.draw(overhang, "red"); + SVG::export_expolygons(debug_out_path("sharp_tail_orig_%.02f.svg", layer->print_z), { expoly }); #endif } } @@ -1003,8 +937,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) if (!overhang.empty()) has_sharp_tails = true; #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg(get_svg_filename(format("sharp_tail_%.02f",layer->print_z)), m_object->bounding_box()); - if (svg.is_opened()) svg.draw(overhang, "red"); + SVG::export_expolygons(debug_out_path("sharp_tail_%.02f.svg", layer->print_z), overhang); #endif } @@ -1056,17 +989,14 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) #ifdef SUPPORT_TREE_DEBUG_TO_SVG const Layer* layer1 = m_object->get_layer(cluster.min_layer); - BoundingBox bbox = cluster.merged_bbox; - bbox.merge(get_extents(layer1->lslices)); - SVG svg(get_svg_filename(format("overhangCluster_%s-%s_%s-%s_tail=%s_cantilever=%s_small=%s", + std::string fname = debug_out_path("overhangCluster_%d-%d_%.2f-%.2f_tail=%d_cantilever=%d_small=%d.svg", cluster.min_layer, cluster.max_layer, layer1->print_z, m_object->get_layer(cluster.max_layer)->print_z, - cluster.is_sharp_tail, cluster.is_cantilever, cluster.is_small_overhang)), bbox); - if (svg.is_opened()) { - svg.draw(layer1->lslices, "red"); - svg.draw(cluster.merged_poly, "blue"); - svg.draw_text(bbox.min + Point(scale_(0), scale_(2)), "lslices", "red", 2); - svg.draw_text(bbox.min + Point(scale_(0), scale_(2)), "overhang", "blue", 2); - } + cluster.is_sharp_tail, cluster.is_cantilever, cluster.is_small_overhang); + SVG::export_expolygons(fname, { + { layer1->lslices, {"min_layer_lslices","red",0.5} }, + { m_object->get_layer(cluster.max_layer)->lslices, {"max_layer_lslices","yellow",0.5} }, + { cluster.merged_poly,{"overhang", "blue", 0.5} }, + { cluster.is_cantilever? layer1->cantilevers: offset_ex(cluster.merged_poly, -1 * extrusion_width_scaled), {cluster.is_cantilever ? "cantilever":"erode1","green",0.5}} }); #endif if (!cluster.is_small_overhang) @@ -1112,9 +1042,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &ts_layer->overhang_areas, max_bridge_length, break_bridge); } - for (auto &area : ts_layer->overhang_areas) { - ts_layer->overhang_types.emplace(&area, SupportLayer::Detected); - } + int nDetected = ts_layer->overhang_areas.size(); // enforcers now follow same logic as normal support. See STUDIO-3692 if (layer_nr < enforcers.size() && lower_layer) { float no_interface_offset = std::accumulate(layer->regions().begin(), layer->regions().end(), FLT_MAX, @@ -1126,9 +1054,14 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) // Inflate just a tiny bit to avoid intersection of the overhang areas with the object. expand(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); append(ts_layer->overhang_areas, enforcer_polygons); - ts_layer->overhang_types.emplace(&ts_layer->overhang_areas.back(), SupportLayer::Enforced); } } + int nEnforced = ts_layer->overhang_areas.size(); + + // fill overhang_types + for (size_t i = 0; i < ts_layer->overhang_areas.size(); i++) + ts_layer->overhang_types.emplace(&ts_layer->overhang_areas[i], i < nDetected ? SupportLayer::Detected : + i < nEnforced ? SupportLayer::Enforced : SupportLayer::SharpTail); if (!ts_layer->overhang_areas.empty()) { has_overhangs = true; @@ -1142,13 +1075,11 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) if (layer->overhang_areas.empty() && (blockers.size()<=layer->id() || blockers[layer->id()].empty())) continue; - SVG svg(get_svg_filename("overhang_areas", std::to_string(layer->print_z)), m_object->bounding_box()); - if (svg.is_opened()) { - svg.draw_outline(m_object->get_layer(layer->id())->lslices, "yellow"); - svg.draw(layer->overhang_areas, "orange"); - if (blockers.size() > layer->id()) - svg.draw(blockers[layer->id()], "red"); - } + SVG::export_expolygons(debug_out_path("overhang_areas_%.2f.svg", layer->print_z), { + { m_object->get_layer(layer->id())->lslices, {"lslices","yellow",0.5} }, + { layer->overhang_areas, {"overhang","red",0.5} } + }); + if (enforcers.size() > layer->id()) { SVG svg(format("SVG/enforcer_%s.svg", layer->print_z), m_object->bounding_box()); if (svg.is_opened()) { @@ -1610,7 +1541,7 @@ void TreeSupport::generate_toolpaths() float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); #ifdef SUPPORT_TREE_DEBUG_TO_SVG - std::string name = get_svg_filename("trees_polyline", std::to_string(ts_layer->print_z)); + std::string name = debug_out_path("trees_polyline_%.2f.svg", ts_layer->print_z); BoundingBox bbox = get_extents(ts_layer->base_areas); SVG svg(name, bbox); if (svg.is_opened()) { @@ -1630,6 +1561,12 @@ void TreeSupport::generate_toolpaths() ); } +void deleteDirectoryContents(const std::filesystem::path& dir) +{ + for (const auto& entry : std::filesystem::directory_iterator(dir)) + std::filesystem::remove_all(entry.path()); +} + void TreeSupport::generate() { if (m_support_params.support_style == smsTreeOrganic) { @@ -1973,6 +1910,20 @@ void TreeSupport::draw_circles(const std::vector>& con bool has_polygon_node = false; bool has_circle_node = false; bool need_extra_wall = false; + ExPolygons collision_sharp_tails; + ExPolygons collision_base; + auto get_collision = [&](bool sharp_tail) -> ExPolygons& { + if (sharp_tail) { + if (collision_sharp_tails.empty()) + collision_sharp_tails = m_ts_data->get_collision(m_object_config->support_top_z_distance.value, layer_nr); + return collision_sharp_tails; + } + else { + if (collision_base.empty()) + collision_base = m_ts_data->get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr); + return collision_base; + } + }; BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size(); //Draw the support areas and add the roofs appropriately to the support roof instead of normal areas. @@ -1987,7 +1938,7 @@ void TreeSupport::draw_circles(const std::vector>& con // Generate directly from overhang polygon if one of the following is true: // 1) node is a normal part of hybrid support // 2) node is virtual - if (node.type == ePolygon || node.distance_to_top<0) { + if (node.type == ePolygon || (node.distance_to_top<0 && !node.is_sharp_tail)) { if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1) area.emplace_back(node.overhang); else { @@ -1998,8 +1949,7 @@ void TreeSupport::draw_circles(const std::vector>& con } else { Polygon circle(branch_circle); - double scale = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor) / branch_radius; - + double scale = node.radius / branch_radius; double moveX = node.movement.x() / (scale * branch_radius_scaled); double moveY = node.movement.y() / (scale * branch_radius_scaled); //BOOST_LOG_TRIVIAL(debug) << format("scale,moveX,moveY: %.3f,%.3f,%.3f", scale, moveX, moveY); @@ -2029,7 +1979,7 @@ void TreeSupport::draw_circles(const std::vector>& con area.emplace_back(ExPolygon(circle)); // merge overhang to get a smoother interface surface // Do not merge when buildplate_only is on, because some underneath nodes may have been deleted. - if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only) { + if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only && !node.is_sharp_tail) { ExPolygons overhang_expanded; if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1) overhang_expanded.emplace_back(node.overhang); @@ -2043,12 +1993,12 @@ void TreeSupport::draw_circles(const std::vector>& con if (layer_nr>0 && node.distance_to_top < 0) append(roof_gap_areas, area); - else if (layer_nr > 0 && node.support_roof_layers_below == 1) + else if (layer_nr > 0 && node.support_roof_layers_below == 1 && node.is_sharp_tail==false) { append(roof_1st_layer, area); max_layers_above_roof1 = std::max(max_layers_above_roof1, node.dist_mm_to_top); } - else if (layer_nr > 0 && node.support_roof_layers_below > 0) + else if (layer_nr > 0 && node.support_roof_layers_below > 0 && node.is_sharp_tail==false) { append(roof_areas, area); max_layers_above_roof = std::max(max_layers_above_roof, node.dist_mm_to_top); @@ -2070,8 +2020,9 @@ void TreeSupport::draw_circles(const std::vector>& con roof_1st_layer = std::move(offset2_ex(roof_1st_layer, contact_dist_scaled, -contact_dist_scaled)); // avoid object - //ExPolygons avoid_region_interface = m_ts_data->get_collision(m_ts_data->m_xy_distance, layer_nr); - Polygons avoid_region_interface = get_trim_support_regions(*m_object, ts_layer, m_slicing_params.gap_object_support, m_slicing_params.gap_support_object, m_ts_data->m_xy_distance); + // arthur: do not leave a gap for top interface if the top z distance is 0. See STUDIO-3991 + Polygons avoid_region_interface = get_trim_support_regions(*m_object, ts_layer, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, + m_slicing_params.gap_support_object == 0 ? 0 : m_ts_data->m_xy_distance); if (has_circle_node) { roof_areas = avoid_object_remove_extra_small_parts(roof_areas, avoid_region_interface); roof_1st_layer = avoid_object_remove_extra_small_parts(roof_1st_layer, avoid_region_interface); @@ -2087,8 +2038,8 @@ void TreeSupport::draw_circles(const std::vector>& con roof_1st_layer = intersection_ex(roof_1st_layer, m_machine_border); // let supports touch objects when brim is on - auto avoid_region = get_collision_polys((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr); - base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region); + //auto avoid_region = get_collision_polys((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr); + //base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region); //base_areas = diff_clipped(base_areas, avoid_region); ExPolygons roofs; append(roofs, roof_1st_layer); append(roofs, roof_areas);append(roofs, roof_gap_areas); base_areas = diff_ex(base_areas, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roofs, get_extents(base_areas))); @@ -2225,10 +2176,6 @@ void TreeSupport::draw_circles(const std::vector>& con overhangs.emplace_back(to_polygons(overhang)); contours.emplace_back(to_polygons(base_areas_lower)); printZ_to_lightninglayer[lower_layer->print_z] = overhangs.size() - 1; - -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - draw_two_overhangs_to_svg(m_object->get_support_layer(layer_nr_lower + m_raft_layers), base_areas_lower, to_expolygons(overhangs.back())); -#endif } @@ -2366,7 +2313,7 @@ void TreeSupport::draw_circles(const std::vector>& con ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer; ExPolygons roofs = roof_areas; append(roofs, roof_1st_layer); if (base_areas.empty() && roof_areas.empty() && roof_1st_layer.empty()) continue; - draw_contours_and_nodes_to_svg(format("%d_%.2f", layer_nr, ts_layer->print_z), base_areas, roofs, ts_layer->lslices, {}, {}, "circles", {"base", "roof", "lslices"}, {"blue","red","black"}); + draw_contours_and_nodes_to_svg(debug_out_path("circles_%d_%.2f.svg", layer_nr, ts_layer->print_z), base_areas, roofs, ts_layer->lslices_extrudable, {}, {}, {"base", "roof", "lslices"}, {"blue","red","black"}); } #endif // SUPPORT_TREE_DEBUG_TO_SVG @@ -2379,6 +2326,8 @@ void TreeSupport::draw_circles(const std::vector>& con } } +double SupportNode::diameter_angle_scale_factor; + void TreeSupport::drop_nodes(std::vector>& contact_nodes) { const PrintObjectConfig &config = m_object->config(); @@ -2397,6 +2346,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod const bool support_on_buildplate_only = config.support_on_build_plate_only.value; const size_t bottom_interface_layers = config.support_interface_bottom_layers.value; const size_t top_interface_layers = config.support_interface_top_layers.value; + SupportNode::diameter_angle_scale_factor = diameter_angle_scale_factor; float DO_NOT_MOVER_UNDER_MM = is_slim ? 0 : 5; // do not move contact points under 5mm const auto nozzle_diameter = m_object->print()->config().nozzle_diameter.get_at(m_object->config().support_interface_filament-1); const auto support_line_width = config.support_line_width.get_abs_value(nozzle_diameter); @@ -2463,7 +2413,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod Lines pls_intersect = intersection_ln(ln, layer_contours); mst_line_x_layer_contour_cache.insert({ {a, b}, !pls_intersect.empty() }); mst_line_x_layer_contour_cache.insert({ ln, !pls_intersect.empty() }); - profiler.stage_add(STAGE_intersection_ln, true); + profiler.stage_add(STAGE_intersection_ln); if (!pls_intersect.empty()) return true; } @@ -2534,12 +2484,12 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } spanning_trees.emplace_back(points_to_buildplate); } - profiler.stage_add(STAGE_MinimumSpanningTree,true); + profiler.stage_add(STAGE_MinimumSpanningTree); #ifdef SUPPORT_TREE_DEBUG_TO_SVG coordf_t branch_radius_temp = 0; coordf_t max_y = std::numeric_limits::min(); - draw_layer_mst(std::to_string(ts_layer->print_z), spanning_trees, m_object->get_layer(layer_nr)->lslices); + draw_layer_mst(debug_out_path("mtree_%.2f.svg", print_z), spanning_trees, m_object->get_layer(obj_layer_nr)->lslices_extrudable); #endif for (size_t group_index = 0; group_index < nodes_per_part.size(); group_index++) { @@ -2560,7 +2510,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod for (const Point &neighbour : neighbours) { SupportNode * neighbour_node = nodes_per_part[group_index][neighbour]; if (neighbour_node->type == ePolygon) continue; - coord_t neighbour_radius = scale_(calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top, diameter_angle_scale_factor)); + coord_t neighbour_radius = scale_(neighbour_node->radius); Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius), pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0); if (is_inside_ex(node.overhang, neighbour) && is_inside_ex(node.overhang, pt_north) && is_inside_ex(node.overhang, pt_south) @@ -2599,7 +2549,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod // Make sure the next pass doesn't drop down either of these (since that already happened). node_->merged_neighbours.push_front(node_ == p_node ? neighbour : p_node); const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_position); - SupportNode * next_node = new SupportNode(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_, + SupportNode * next_node = m_ts_data->create_node(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_, print_z_next, height_next); next_node->movement = next_position - node.position; get_max_move_dist(next_node); @@ -2625,6 +2575,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod node.merged_neighbours.push_front(neighbour_node); node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end()); to_delete.insert(neighbour_node); + neighbour_node->valid = false; } } } @@ -2655,7 +2606,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } } if (index_biggest >= 0) { - SupportNode* next_node = new SupportNode(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, + SupportNode* next_node = m_ts_data->create_node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); next_node->max_move_dist = 0; next_node->overhang = std::move(overhangs_next[index_biggest]); @@ -2678,6 +2629,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } else { to_delete.insert(p_node); + p_node->valid = false; } continue; } @@ -2685,6 +2637,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false) { to_delete.insert(p_node); + p_node->valid = false; continue; } } @@ -2792,7 +2745,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_layer_vertex); - SupportNode * next_node = new SupportNode(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, + SupportNode * next_node = m_ts_data->create_node(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); next_node->movement = movement; get_max_move_dist(next_node); @@ -2802,12 +2755,13 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod #ifdef SUPPORT_TREE_DEBUG_TO_SVG if (contact_nodes[layer_nr].empty() == false) { - draw_contours_and_nodes_to_svg((boost::format("%.2f") % contact_nodes[layer_nr][0]->print_z).str(), get_collision(0,layer_nr_next), + draw_contours_and_nodes_to_svg(debug_out_path("contact_points_%.2f.svg", contact_nodes[layer_nr][0]->print_z), get_collision(0,layer_nr_next), get_avoidance(branch_radius_temp, layer_nr), m_ts_data->m_layer_outlines[layer_nr], - contact_nodes[layer_nr], contact_nodes[layer_nr_next], "contact_points", { "overhang","avoid","outline" }, { "blue","red","yellow" }); + contact_nodes[layer_nr], contact_nodes[layer_nr_next], { "overhang","avoid","outline" }, { "blue","red","yellow" }); - BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer " << layer_nr << ", print_z=" << ts_layer->print_z; + BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer->next " << layer_nr << "->" << layer_nr_next << ", print_z=" << ts_layer->print_z + << ", num points: " << contact_nodes[layer_nr].size() << "->" << contact_nodes[layer_nr_next].size(); for (size_t i = 0; i < std::min(size_t(5), contact_nodes[layer_nr].size()); i++) { auto &node = contact_nodes[layer_nr][i]; BOOST_LOG_TRIVIAL(debug) << "\t node " << i << ", pos=" << node->position << ", move = " << node->movement; @@ -2827,15 +2781,20 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod if (to_erase != contact_nodes[i_layer].end()) { // update the parent-child chain - for (SupportNode* parent : i_node->parents) { + if (i_node->parent) { + i_node->parent->child = i_node->child; + for (SupportNode* parent : i_node->parents) { if (parent->child==i_node) parent->child = i_node->child; + } } if (i_node->child) { - i_node->child->parents= i_node->parents; + i_node->child->parent = i_node->parent; + i_node->child->parents.erase(std::find(i_node->child->parents.begin(), i_node->child->parents.end(), i_node)); + append(i_node->child->parents, i_node->parents); } contact_nodes[i_layer].erase(to_erase); - to_free_node_set.insert(i_node); + i_node->valid = false; for (SupportNode* neighbour : i_node->merged_neighbours) { @@ -2847,12 +2806,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } BOOST_LOG_TRIVIAL(debug) << "after m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size(); - - for (SupportNode *node : to_free_node_set) - { - delete node; - } - to_free_node_set.clear(); } void TreeSupport::smooth_nodes(std::vector> &contact_nodes) @@ -2926,40 +2879,6 @@ void TreeSupport::smooth_nodes(std::vector> &contact_ } } } - // save tree structure for viewing in python - auto& tree_nodes = m_ts_data->tree_nodes; - std::map ptr2idx; - std::map idx2ptr; - for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { - std::vector& curr_layer_nodes = contact_nodes[layer_nr]; - for (SupportNode* node : curr_layer_nodes) { - ptr2idx.emplace(node, tree_nodes.size()); - idx2ptr.emplace(tree_nodes.size(), node); - tree_nodes.emplace_back(node->position, node->print_z); - } - } - for (size_t i = 0; i < tree_nodes.size(); i++) { - TreeNode& tree_node = tree_nodes[i]; - SupportNode* p_node = idx2ptr[i]; - if (p_node->child) - tree_node.children.push_back(ptr2idx[p_node->child]); - if(p_node->parent) - tree_node.parents.push_back(ptr2idx[p_node->parent]); - } -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - nlohmann::json jj; - for (size_t i = 0; i < tree_nodes.size(); i++) { - nlohmann::json j; - j["pos"] = tree_nodes[i].pos; - j["children"] = tree_nodes[i].children; - j["linked"] = !(tree_nodes[i].pos.z() > 0.205 && tree_nodes[i].children.empty()); - jj.push_back(j); - } - - std::ofstream ofs("tree_nodes.json"); - ofs << jj.dump(); - ofs.close(); -#endif } std::vector TreeSupport::plan_layer_heights(std::vector> &contact_nodes) @@ -2988,29 +2907,33 @@ std::vector TreeSupport::plan_layer_heights(std::vectorget_layer(0)->print_z, m_object->get_layer(0)->height, 0}; // Collect top contact layers + coordf_t print_z = layer_heights[0].print_z; for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) { - if (!contact_nodes[layer_nr].empty()) - for (int i = 0; i < support_roof_layers + z_distance_top_layers + 1; i++) { - if (layer_nr - i > 0) { - bounds.push_back(layer_nr - i); - layer_heights[layer_nr - i].print_z = m_object->get_layer(layer_nr - i)->print_z; - layer_heights[layer_nr - i].height = m_object->get_layer(layer_nr - i)->height; - } - else { - break; + if (!contact_nodes[layer_nr].empty()) { + bounds.push_back(layer_nr); + layer_heights[layer_nr].print_z = contact_nodes[layer_nr].front()->print_z; + layer_heights[layer_nr].height = contact_nodes[layer_nr].front()->height; + if (layer_heights[layer_nr].bottom_z() - print_z < m_slicing_params.min_layer_height) { + layer_heights[layer_nr].height = layer_heights[layer_nr].print_z - print_z; + for (auto& node : contact_nodes[layer_nr]) { + node->height = layer_heights[layer_nr].height; } + } + print_z = layer_heights[layer_nr].print_z; + BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights0 print_z, height, layer_nr: " << layer_heights[layer_nr].print_z << " " << layer_heights[layer_nr].height << " " + << layer_nr; } } std::set s(bounds.begin(), bounds.end()); bounds.assign(s.begin(), s.end()); - for (size_t idx_extreme = 0; idx_extreme < bounds.size(); idx_extreme++) { + for (size_t idx_extreme = 1; idx_extreme < bounds.size(); idx_extreme++) { int extr2_layer_nr = bounds[idx_extreme]; - coordf_t extr2z = m_object->get_layer(extr2_layer_nr)->bottom_z(); - int extr1_layer_nr = idx_extreme == 0 ? -1 : bounds[idx_extreme - 1]; - coordf_t extr1z = idx_extreme == 0 ? 0.f : m_object->get_layer(extr1_layer_nr)->print_z; + coordf_t extr2z = layer_heights[extr2_layer_nr].bottom_z(); + int extr1_layer_nr = bounds[idx_extreme - 1]; + coordf_t extr1z = layer_heights[extr1_layer_nr].print_z; coordf_t dist = extr2z - extr1z; // Insert intermediate layers. @@ -3022,7 +2945,8 @@ std::vector TreeSupport::plan_layer_heights(std::vector= layer_height - EPSILON); + //assert(step >= layer_height - EPSILON); + coordf_t extr2z_large_steps = extr2z; for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; layer_nr++) { // if (curr_layer_nodes.empty()) continue; if (std::abs(print_z - m_object->get_layer(layer_nr)->print_z) < step / 2 + EPSILON || extr_layers_left < 1) { @@ -3052,8 +2976,21 @@ std::vector TreeSupport::plan_layer_heights(std::vectornext_layer_nr: " << layer_heights[i].print_z << " " << layer_heights[i].height << " " - << i << "->" << layer_heights[i].next_layer_nr; + } + for (i = 0; i < layer_heights.size(); i++) { + // there might be gap between layers due to non-integer interfaces + if (size_t next_layer_nr = layer_heights[i].next_layer_nr; + next_layer_nr>0 && layer_heights[i].height + EPSILON < layer_heights[i].print_z - layer_heights[next_layer_nr].print_z) + { + layer_heights[i].height = layer_heights[i].print_z - layer_heights[next_layer_nr].print_z; + } + // update interfaces' height + for (auto& node : contact_nodes[i]) { + node->height = layer_heights[i].height; + } + if (layer_heights[i].height > EPSILON) + BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights print_z, height, lower_layer_nr->layer_nr: " << layer_heights[i].print_z << " " << layer_heights[i].height << " " + << layer_heights[i].next_layer_nr << "->" << i; } return layer_heights; @@ -3063,6 +3000,9 @@ void TreeSupport::generate_contact_points(std::vector> { const PrintObjectConfig &config = m_object->config(); const coordf_t point_spread = scale_(config.tree_support_branch_distance.value); + const coordf_t max_bridge_length = scale_(config.max_bridge_length.value); + coord_t radius = scale_(config.tree_support_branch_diameter.value / 2); + radius = std::max(m_min_radius, radius); //First generate grid points to cover the entire area of the print. BoundingBox bounding_box = m_object->bounding_box(); @@ -3077,8 +3017,9 @@ void TreeSupport::generate_contact_points(std::vector> bounding_box_size(0) * sin_angle + bounding_box_size(1) * cos_angle) / 2; std::vector grid_points; - for (auto x = -rotated_dims(0); x < rotated_dims(0); x += point_spread) { - for (auto y = -rotated_dims(1); y < rotated_dims(1); y += point_spread) { + coordf_t sample_step = std::max(point_spread, max_bridge_length / 2); + for (auto x = -rotated_dims(0); x < rotated_dims(0); x += sample_step) { + for (auto y = -rotated_dims(1); y < rotated_dims(1); y += sample_step) { Point pt(x, y); pt.rotate(cos_angle, sin_angle); pt += center; @@ -3090,12 +3031,18 @@ void TreeSupport::generate_contact_points(std::vector> const coordf_t layer_height = config.layer_height.value; coordf_t z_distance_top = m_slicing_params.gap_support_object; + if (!m_support_params.independent_layer_height) { + z_distance_top = round(z_distance_top / layer_height) * layer_height; // BBS: add extra distance if thick bridge is enabled // Note: normal support uses print_z, but tree support uses integer layers, so we need to subtract layer_height if (!m_slicing_params.soluble_interface && m_object_config->thick_bridges) { z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(m_object->print()->config()) - layer_height; + } } const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1; //Support must always be 1 layer below overhang. + // virtual layer with 0 height will be deleted + if (z_distance_top == 0) + z_distance_top = 0.001; size_t support_roof_layers = config.support_interface_top_layers.value; if (support_roof_layers > 0) @@ -3108,140 +3055,131 @@ void TreeSupport::generate_contact_points(std::vector> return; int nonempty_layers = 0; - std::vector all_nodes; - for (size_t layer_nr = 1; layer_nr < m_object->layers().size(); layer_nr++) - { - if (m_object->print()->canceled()) - break; - auto ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); - const ExPolygons &overhang = ts_layer->overhang_areas; - auto & curr_nodes = contact_nodes[layer_nr]; - if (overhang.empty()) - continue; - - std::unordered_set already_inserted; - auto print_z = m_object->get_layer(layer_nr)->print_z; - auto height = m_object->get_layer(layer_nr)->height; - - for (const ExPolygon &overhang_part : overhang) - { - BoundingBox overhang_bounds = get_extents(overhang_part); - if (m_support_params.support_style==smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang) { - if (!overlaps({ overhang_part }, m_ts_data->m_layer_outlines_below[layer_nr-1])) { - Point candidate = overhang_bounds.center(); - SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z, - height, z_distance_top); - contact_node->type = ePolygon; + tbb::concurrent_vector all_nodes; + tbb::parallel_for(tbb::blocked_range(1, m_object->layers().size()), [&](const tbb::blocked_range& range) { + for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { + if (m_object->print()->canceled()) + break; + auto ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); + Layer* layer = m_object->get_layer(layer_nr); + auto& curr_nodes = contact_nodes[layer_nr - 1]; + if (ts_layer->overhang_areas.empty()) continue; + std::unordered_set already_inserted; + auto print_z = m_object->get_layer(layer_nr)->print_z; + auto bottom_z = m_object->get_layer(layer_nr)->bottom_z(); + auto height = m_object->get_layer(layer_nr)->height; + bool added = false; // Did we add a point this way? + bool is_sharp_tail = false; + auto insert_point = [&](Point pt, const ExPolygon& overhang_part, bool force_add = false) { + Point hash_pos = pt / ((radius + 1) / 1); + SupportNode* contact_node = nullptr; + if (force_add || !already_inserted.count(hash_pos)) { + already_inserted.emplace(hash_pos); + bool to_buildplate = true; + // add a new node as a virtual node which acts as the invisible gap between support and object + // distance_to_top=-1: it's virtual + // print_z=object_layer->bottom_z: it directly contacts the bottom + // height=z_distance_top: it's height is exactly the gap distance + // dist_mm_to_top=0: it directly contacts the bottom + contact_node = m_ts_data->create_node(pt, -1, layer_nr - 1, support_roof_layers + 1, to_buildplate, SupportNode::NO_PARENT, bottom_z, z_distance_top, 0, + unscale_(radius)); contact_node->overhang = overhang_part; - contact_node->radius = unscale_(overhang_bounds.radius()); + contact_node->is_sharp_tail = is_sharp_tail; + if (is_sharp_tail) { + int ind = overhang_part.contour.closest_point_index(pt); + auto n1 = (overhang_part.contour[ind] - overhang_part.contour[ind - 1]).cast().normalized(); + auto n2 = (overhang_part.contour[ind] - overhang_part.contour[ind + 1]).cast().normalized(); + contact_node->skin_direction = scaled((n1 + n2).normalized()); + } curr_nodes.emplace_back(contact_node); - continue; - } - } + added = true; + }; + return contact_node; + }; - // add supports at corners for both auto and manual overhangs, github #2008 - if (/*ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected*/1) { - // add points at corners - auto& points = overhang_part.contour.points; - int nSize = points.size(); - for (int i = 0; i < nSize; i++) { - auto pt = points[i]; - auto v1 = (pt - points[(i - 1 + nSize) % nSize]).cast().normalized(); - auto v2 = (pt - points[(i + 1) % nSize]).cast().normalized(); - if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees - SupportNode* contact_node = new SupportNode(pt, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z, - height, z_distance_top); - contact_node->overhang = overhang_part; - contact_node->is_corner = true; + for (const auto& area_type : ts_layer->overhang_types) { + const auto& overhang_part = *area_type.first; + is_sharp_tail = area_type.second == SupportLayer::SharpTail; + BoundingBox overhang_bounds = get_extents(overhang_part); + if (m_support_params.support_style == smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang && !is_sharp_tail) { + if (!overlaps({ overhang_part }, m_ts_data->m_layer_outlines_below[layer_nr - 1])) { + Point candidate = overhang_bounds.center(); + SupportNode* contact_node = insert_point(candidate, overhang_part, true); + contact_node->type = ePolygon; + contact_node->radius = unscale_(overhang_bounds.radius()); curr_nodes.emplace_back(contact_node); - - Point hash_pos = pt / ((m_min_radius + 1) / 1); - already_inserted.emplace(hash_pos); + continue; } } - } - auto& move_bounds_layer = move_bounds[layer_nr]; - if (move_bounds_layer.empty()) { - overhang_bounds.inflated(half_overhang_distance); - bool added = false; //Did we add a point this way? - for (Point candidate : grid_points) { - if (overhang_bounds.contains(candidate)) { - // BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported! - bool is_inside = is_inside_ex(overhang_part, candidate); - if (!is_inside) { - constexpr coordf_t distance_inside = 0; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that - // fall between overhang areas on constant surfaces. - is_inside = move_inside_expoly(overhang_part, candidate, distance_inside, half_overhang_distance); - } - if (is_inside) { - // normalize the point a bit to also catch points which are so close that inserting it would achieve nothing - Point hash_pos = candidate / ((m_min_radius + 1) / 1); - if (!already_inserted.count(hash_pos)) { - already_inserted.emplace(hash_pos); - constexpr bool to_buildplate = true; - SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, - SupportNode::NO_PARENT, print_z, height, z_distance_top); - contact_node->overhang=overhang_part; - curr_nodes.emplace_back(contact_node); - added = true; - } - } - } - } - if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported. + + // add supports at corners for both auto and manual overhangs, github #2008 { - auto bbox = overhang_part.contour.bounding_box(); - Points candidates; - if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected) - candidates = { bbox.min, bounding_box_middle(bbox), bbox.max }; - else - candidates = { bounding_box_middle(bbox) }; - for (Point candidate : candidates) { - if (!overhang_part.contains(candidate)) - move_inside_expoly(overhang_part, candidate); - constexpr bool to_buildplate = true; - SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, SupportNode::NO_PARENT, - print_z, height, z_distance_top); - contact_node->overhang=overhang_part; + auto& points = overhang_part.contour.points; + int nSize = points.size(); + for (int i = 0; i < nSize; i++) { + auto pt = points[i]; + auto v1 = (pt - points[(i - 1 + nSize) % nSize]).cast().normalized(); + auto v2 = (pt - points[(i + 1) % nSize]).cast().normalized(); + if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees + SupportNode* contact_node = insert_point(pt, overhang_part); + if (contact_node) { + contact_node->is_corner = true; + } + } + } + } + auto& move_bounds_layer = move_bounds[layer_nr]; + if (move_bounds_layer.empty()) { + overhang_bounds.inflated(half_overhang_distance); + bool added = false; //Did we add a point this way? + for (Point candidate : grid_points) { + if (overhang_bounds.contains(candidate)) { + // BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported! + bool is_inside = is_inside_ex(overhang_part, candidate); + if (!is_inside) { + constexpr coordf_t distance_inside = 0; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that + // fall between overhang areas on constant surfaces. + is_inside = move_inside_expoly(overhang_part, candidate, distance_inside, half_overhang_distance); + } + if (is_inside) { + SupportNode* contact_node = insert_point(candidate, overhang_part); + if (contact_node) + added = true; + } + } + } + if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported. + { + auto bbox = overhang_part.contour.bounding_box(); + Points candidates; + if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected) + candidates = { bbox.min, bounding_box_middle(bbox), bbox.max }; + else + candidates = { bounding_box_middle(bbox) }; + for (Point candidate : candidates) { + if (!overhang_part.contains(candidate)) + move_inside_expoly(overhang_part, candidate); + insert_point(candidate, overhang_part); + } + } + } + else { + for (auto& elem : move_bounds_layer) { + SupportNode* contact_node = m_ts_data->create_node(elem.state.result_on_layer, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, elem.state.to_buildplate, + SupportNode::NO_PARENT, print_z, height, z_distance_top); + contact_node->overhang = ExPolygon(elem.influence_area.front()); curr_nodes.emplace_back(contact_node); } } - if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Enforced || is_slim) { - // remove close points - for (auto it = curr_nodes.begin(); it != curr_nodes.end();) { - bool is_duplicate = false; - if (!(*it)->is_corner) { - Slic3r::Vec3f curr_pt((*it)->position(0), (*it)->position(1), scale_((*it)->print_z)); - for (auto& pt : all_nodes) { - auto dif = curr_pt - pt; - if (dif.norm() < point_spread / 2) { - delete (*it); - it = curr_nodes.erase(it); - is_duplicate = true; - break; - } - } - } - if (!is_duplicate) it++; - } - } } - else { - for (auto& elem:move_bounds_layer) { - SupportNode* contact_node = new SupportNode(elem.state.result_on_layer, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, elem.state.to_buildplate, - SupportNode::NO_PARENT, print_z, height, z_distance_top); - contact_node->overhang = ExPolygon(elem.influence_area.front()); - curr_nodes.emplace_back(contact_node); - } - } - } - if (!curr_nodes.empty()) nonempty_layers++; - for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); } + if (!curr_nodes.empty()) nonempty_layers++; + for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); } #ifdef SUPPORT_TREE_DEBUG_TO_SVG - draw_contours_and_nodes_to_svg(std::to_string(print_z), overhang, m_ts_data->m_layer_outlines_below[layer_nr], {}, - contact_nodes[layer_nr], {}, "init_contact_points", { "overhang","outlines","" }); + draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", print_z), ts_layer->overhang_areas, m_ts_data->m_layer_outlines_below[layer_nr], {}, + contact_nodes[layer_nr], contact_nodes[layer_nr - 1], { "overhang","outlines","" }); #endif - } + }} + ); // end tbb::parallel_for int nNodes = all_nodes.size(); avg_node_per_layer = nodes_angle = 0; if (nNodes > 0) { @@ -3281,6 +3219,7 @@ void TreeSupport::insert_dropped_node(std::vector& nodes_layer, Su TreeSupportData::TreeSupportData(const PrintObject &object, coordf_t xy_distance, coordf_t max_move, coordf_t radius_sample_resolution) : m_xy_distance(xy_distance), m_max_move(max_move), m_radius_sample_resolution(radius_sample_resolution) { + clear_nodes(); for (std::size_t layer_nr = 0; layer_nr < object.layers().size(); ++layer_nr) { const Layer* layer = object.get_layer(layer_nr); @@ -3304,7 +3243,7 @@ const ExPolygons& TreeSupportData::get_collision(coordf_t radius, size_t layer_n RadiusLayerPair key{radius, layer_nr}; const auto it = m_collision_cache.find(key); const ExPolygons& collision = it != m_collision_cache.end() ? it->second : calculate_collision(key); - profiler.stage_add(STAGE_get_collision, true); + profiler.stage_add(STAGE_get_collision); return collision; } @@ -3316,7 +3255,7 @@ const ExPolygons& TreeSupportData::get_avoidance(coordf_t radius, size_t layer_n const auto it = m_avoidance_cache.find(key); const ExPolygons& avoidance = it != m_avoidance_cache.end() ? it->second : calculate_avoidance(key); - profiler.stage_add(STAGE_GET_AVOIDANCE, true); + profiler.stage_add(STAGE_GET_AVOIDANCE); return avoidance; } @@ -3340,6 +3279,35 @@ Polygons TreeSupportData::get_contours_with_holes(size_t layer_nr) const return contours; } +SupportNode* TreeSupportData::create_node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode* parent, coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_, coordf_t radius_) +{ + // this function may be called from multiple threads, need to lock + m_mutex.lock(); + SupportNode* node = new SupportNode(position, distance_to_top, obj_layer_nr, support_roof_layers_below, to_buildplate, parent, print_z_, height_, dist_mm_to_top_, radius_); + contact_nodes.emplace_back(node); + m_mutex.unlock(); + return node; +} +void TreeSupportData::clear_nodes() +{ + for (auto node : contact_nodes) { + delete node; + } + contact_nodes.clear(); +} +void TreeSupportData::remove_invalid_nodes() +{ + for (auto it = contact_nodes.begin(); it != contact_nodes.end();) { + if ((*it)->valid==false) { + delete (*it); + it = contact_nodes.erase(it); + } + else { + it++; + } + } +} + coordf_t TreeSupportData::ceil_radius(coordf_t radius) const { size_t factor = (size_t)(radius / m_radius_sample_resolution); diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index 62cce6ffea..da58aaf498 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -31,14 +31,8 @@ struct LayerHeightData size_t next_layer_nr = 0; LayerHeightData() = default; LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {} -}; - -struct TreeNode { - Vec3f pos; - std::vector children; // index of children in the storing vector - std::vector parents; // index of parents in the storing vector - TreeNode(Point pt, float z) { - pos = { float(unscale_(pt.x())),float(unscale_(pt.y())),z }; + coordf_t bottom_z() { + return print_z - height; } }; @@ -57,40 +51,45 @@ struct SupportNode static constexpr SupportNode* NO_PARENT = nullptr; SupportNode() - : distance_to_top(0) - , position(Point(0, 0)) - , obj_layer_nr(0) - , support_roof_layers_below(0) - , to_buildplate(true) - , parent(nullptr) - , print_z(0.0) - , height(0.0) + : distance_to_top(0) + , position(Point(0, 0)) + , obj_layer_nr(0) + , support_roof_layers_below(0) + , to_buildplate(true) + , parent(nullptr) + , print_z(0.0) + , height(0.0) {} // when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height; SupportNode(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode* parent, - coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0) - : distance_to_top(distance_to_top) - , position(position) - , obj_layer_nr(obj_layer_nr) - , support_roof_layers_below(support_roof_layers_below) - , to_buildplate(to_buildplate) - , parent(parent) - , print_z(print_z_) - , height(height_) - , dist_mm_to_top(dist_mm_to_top_) + coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_ = 0, coordf_t radius_ = 0) + : distance_to_top(distance_to_top) + , position(position) + , obj_layer_nr(obj_layer_nr) + , support_roof_layers_below(support_roof_layers_below) + , to_buildplate(to_buildplate) + , parent(parent) + , print_z(print_z_) + , height(height_) + , dist_mm_to_top(dist_mm_to_top_) + , radius(radius_) { if (parent) { parents.push_back(parent); - type = parent->type; + type = parent->type; overhang = parent->overhang; - if (dist_mm_to_top==0) + if (dist_mm_to_top == 0) dist_mm_to_top = parent->dist_mm_to_top + parent->height; + if (radius == 0) + radius = parent->radius + (dist_mm_to_top - parent->dist_mm_to_top) * diameter_angle_scale_factor; parent->child = this; for (auto& neighbor : parent->merged_neighbours) { neighbor->child = this; parents.push_back(neighbor); } + is_sharp_tail = parent->is_sharp_tail; + skin_direction = parent->skin_direction; } } @@ -109,18 +108,23 @@ struct SupportNode int distance_to_top; coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm + // all nodes will have same diameter_angle_scale_factor because it's defined by user + static double diameter_angle_scale_factor; + /*! * \brief The position of this node on the layer. */ - Point position; - Point movement; // movement towards neighbor center or outline - mutable double radius = 0.0; - mutable double max_move_dist = 0.0; - TreeNodeType type = eCircle; - bool is_corner = false; - bool is_processed = false; - bool need_extra_wall = false; - ExPolygon overhang; // when type==ePolygon, set this value to get original overhang area + Point position; + Point movement; // movement towards neighbor center or outline + mutable double radius = 0.0; + mutable double max_move_dist = 0.0; + TreeNodeType type = eCircle; + bool is_corner = false; + bool is_processed = false; + bool need_extra_wall = false; + bool is_sharp_tail = false; + bool valid = true; + ExPolygon overhang; // when type==ePolygon, set this value to get original overhang area /*! * \brief The direction of the skin lines above the tip of the branch. @@ -128,7 +132,7 @@ struct SupportNode * This determines in which direction we should reduce the width of the * branch. */ - bool skin_direction; + Point skin_direction; /*! * \brief The number of support roof layers below this one. @@ -199,6 +203,9 @@ public: * \param collision_resolution */ TreeSupportData(const PrintObject& object, coordf_t max_move, coordf_t radius_sample_resolution, coordf_t collision_resolution); + ~TreeSupportData() { + clear_nodes(); + } TreeSupportData(TreeSupportData&&) = default; TreeSupportData& operator=(TreeSupportData&&) = default; @@ -237,9 +244,13 @@ public: Polygons get_contours(size_t layer_nr) const; Polygons get_contours_with_holes(size_t layer_nr) const; + SupportNode* create_node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode* parent, + coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_ = 0, coordf_t radius_ = 0); + void clear_nodes(); + void remove_invalid_nodes(); std::vector layer_heights; - std::vector tree_nodes; + std::vector contact_nodes; private: /*! @@ -285,6 +296,7 @@ private: */ const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const; + tbb::spin_mutex m_mutex; public: bool is_slim = false; diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index b6900553ee..744eb98311 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "format.hpp" #include "Platform.hpp" @@ -298,12 +299,13 @@ static std::atomic debug_out_path_called(false); std::string debug_out_path(const char *name, ...) { - static constexpr const char *SLIC3R_DEBUG_OUT_PATH_PREFIX = "out/"; + //static constexpr const char *SLIC3R_DEBUG_OUT_PATH_PREFIX = "out/"; + auto svg_folder = boost::filesystem::path(g_data_dir) / "SVG/"; if (! debug_out_path_called.exchange(true)) { - std::string path = boost::filesystem::system_complete(SLIC3R_DEBUG_OUT_PATH_PREFIX).string(); - if (!boost::filesystem::exists(path)) { - boost::filesystem::create_directory(path); + if (!boost::filesystem::exists(svg_folder)) { + boost::filesystem::create_directory(svg_folder); } + std::string path = boost::filesystem::system_complete(svg_folder).string(); printf("Debugging output files will be written to %s\n", path.c_str()); } char buffer[2048]; @@ -311,7 +313,13 @@ std::string debug_out_path(const char *name, ...) va_start(args, name); std::vsprintf(buffer, name, args); va_end(args); - return std::string(SLIC3R_DEBUG_OUT_PATH_PREFIX) + std::string(buffer); + + std::string buf(buffer); + if (size_t pos = buf.find_first_of('/'); pos != std::string::npos) { + std::string sub_dir = buf.substr(0, pos); + std::filesystem::create_directory(svg_folder.string() + sub_dir); + } + return svg_folder.string() + std::string(buffer); } namespace logging = boost::log; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 916cb38ce6..8ed06d1d87 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1537,9 +1537,9 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (opt_key == "support_interface_filament") { int interface_filament_id = m_config->opt_int("support_interface_filament") - 1; // the displayed id is based from 1, while internal id is based from 0 if (is_support_filament(interface_filament_id) && !(m_config->opt_float("support_top_z_distance") == 0 && m_config->opt_float("support_interface_spacing") == 0 && - m_config->opt_enum("support_interface_pattern") == SupportMaterialInterfacePattern::smipConcentric)) { + m_config->opt_enum("support_interface_pattern") == SupportMaterialInterfacePattern::smipRectilinearInterlaced)) { wxString msg_text = _L("When using support material for the support interface, We recommend the following settings:\n" - "0 top z distance, 0 interface spacing, concentric pattern and disable independent support layer height"); + "0 top z distance, 0 interface spacing, interlaced rectilinear pattern and disable independent support layer height"); msg_text += "\n\n" + _L("Change these settings automatically? \n" "Yes - Change these settings automatically\n" "No - Do not change these settings for me"); @@ -1548,7 +1548,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (dialog.ShowModal() == wxID_YES) { new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0)); new_conf.set_key_value("support_interface_spacing", new ConfigOptionFloat(0)); - new_conf.set_key_value("support_interface_pattern", new ConfigOptionEnum(SupportMaterialInterfacePattern::smipConcentric)); + new_conf.set_key_value("support_interface_pattern", new ConfigOptionEnum(SupportMaterialInterfacePattern::smipRectilinearInterlaced)); new_conf.set_key_value("independent_support_layer_height", new ConfigOptionBool(false)); m_config_manipulation.apply(m_config, &new_conf); } From c7febad548e9b8667b8491c793d760ce8e592682 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 26 Jan 2024 16:19:59 +0800 Subject: [PATCH 20/72] FIX: raft_first_layer_expansion not working with tree support jira: STUDIO-6043 github: #3355 Change-Id: Idf2a0053026a6f232a5239c3f504191321feb6ee (cherry picked from commit 3eb35f5b1a53990d215ae4d3527917611f5083c6) (cherry picked from commit 0589348c3264e56b9e3a16afea95a5d44983498b) --- src/libslic3r/Print.hpp | 2 +- src/libslic3r/Support/TreeSupport.cpp | 56 +++++++++++++++++---------- src/libslic3r/Support/TreeSupport.hpp | 2 +- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 7b7d13e709..9618e69dfc 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -385,7 +385,7 @@ public: size_t support_layer_count() const { return m_support_layers.size(); } void clear_support_layers(); - SupportLayer* get_support_layer(int idx) { return m_support_layers[idx]; } + SupportLayer* get_support_layer(int idx) { return idxsupport_layer_count() >= m_object->layer_count()) return; + bool tree_support_enable = m_object_config->enable_support.value && is_tree(m_object_config->support_type.value); + // Clear and create Tree Support Layers m_object->clear_support_layers(); m_object->clear_tree_support_preview_cache(); - create_tree_support_layers(); + create_tree_support_layers(tree_support_enable); + if (!tree_support_enable) + return; const PrintObjectConfig& config = m_object->config(); SupportType stype = support_type; @@ -1098,7 +1102,9 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) #endif } -void TreeSupport::create_tree_support_layers() +// create support layers for raft and tree support +// if support is disabled, only raft layers are created +void TreeSupport::create_tree_support_layers(bool support_enabled) { int layer_id = 0; coordf_t raft_print_z = 0.f; @@ -1115,6 +1121,9 @@ void TreeSupport::create_tree_support_layers() m_object->add_tree_support_layer(layer_id, m_slicing_params.base_raft_layer_height, raft_print_z, raft_slice_z); } + if (!support_enabled) + return; + for (Layer *layer : m_object->layers()) { SupportLayer* ts_layer = m_object->add_tree_support_layer(layer_id++, layer->height, layer->print_z, layer->slice_z); if (ts_layer->id() > m_raft_layers) { @@ -1319,20 +1328,10 @@ void TreeSupport::generate_toolpaths() raft_areas = std::move(offset_ex(raft_areas, scale_(object_config.raft_first_layer_expansion))); - // generate raft tool path - if (m_raft_layers > 0) - { - SupportLayer *ts_layer = m_object->support_layers().front(); - Flow flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter); - extrusion_entities_append_loops(ts_layer->support_fills.entities, to_polygons(raft_areas), erSupportMaterial, - float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); - raft_areas = offset_ex(raft_areas, -flow.scaled_spacing() / 2.); - } - for (size_t layer_nr = 0; layer_nr < m_slicing_params.base_raft_layers; layer_nr++) { SupportLayer *ts_layer = m_object->get_support_layer(layer_nr); - coordf_t expand_offset = (layer_nr == 0 ? 0. : -1.); - raft_areas = offset_ex(raft_areas, scale_(expand_offset)); + coordf_t expand_offset = (layer_nr == 0 ? m_object_config->raft_first_layer_expansion.value : 0.); + auto raft_areas1 = offset_ex(raft_areas, scale_(expand_offset)); Flow support_flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter); Fill* filler_interface = Fill::new_from_type(ipRectilinear); @@ -1343,16 +1342,25 @@ void TreeSupport::generate_toolpaths() fill_params.density = object_config.raft_first_layer_density * 0.01; fill_params.dont_adjust = true; - fill_expolygons_generate_paths(ts_layer->support_fills.entities, raft_areas, + // wall of first layer raft + if (layer_nr == 0) { + Flow flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter); + extrusion_entities_append_loops(ts_layer->support_fills.entities, to_polygons(raft_areas1), erSupportMaterial, + float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); + raft_areas1 = offset_ex(raft_areas1, -flow.scaled_spacing() / 2.); + } + fill_expolygons_generate_paths(ts_layer->support_fills.entities, raft_areas1, filler_interface, fill_params, erSupportMaterial, support_flow); } // subtract the non-raft support bases, otherwise we'll get support base on top of raft interfaces which is not stable - SupportLayer* first_non_raft_layer = m_object->get_support_layer(m_raft_layers); ExPolygons first_non_raft_base; - for (auto& area_group : first_non_raft_layer->area_groups) { - if (area_group.type == SupportLayer::BaseType) - first_non_raft_base.emplace_back(*area_group.area); + SupportLayer* first_non_raft_layer = m_object->get_support_layer(m_raft_layers); + if (first_non_raft_layer) { + for (auto& area_group : first_non_raft_layer->area_groups) { + if (area_group.type == SupportLayer::BaseType) + first_non_raft_base.emplace_back(*area_group.area); + } } ExPolygons raft_base_areas = intersection_ex(raft_areas, first_non_raft_base); ExPolygons raft_interface_areas = diff_ex(raft_areas, raft_base_areas); @@ -1380,6 +1388,9 @@ void TreeSupport::generate_toolpaths() filler_interface, fill_params, erSupportMaterial, support_flow); } + if (m_object->support_layer_count() <= m_raft_layers) + return; + BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); std::shared_ptr filler_interface = std::shared_ptr(Fill::new_from_type(m_support_params.contact_fill_pattern)); @@ -1869,7 +1880,8 @@ void TreeSupport::draw_circles(const std::vector>& con coordf_t support_extrusion_width = m_support_params.support_extrusion_width; const float tree_brim_width = config.tree_support_brim_width.value; - const PrintObjectConfig& object_config = m_object->config(); + if (m_object->support_layer_count() <= m_raft_layers) + return; BOOST_LOG_TRIVIAL(info) << "draw_circles for object: " << m_object->model_object()->name; tbb::parallel_for(tbb::blocked_range(0, m_object->layer_count()), @@ -2180,7 +2192,7 @@ void TreeSupport::draw_circles(const std::vector>& con auto m_support_material_flow = support_material_flow(m_object, m_slicing_params.layer_height); - coordf_t support_spacing = object_config.support_base_pattern_spacing.value + m_support_material_flow.spacing(); + coordf_t support_spacing = m_object_config->support_base_pattern_spacing.value + m_support_material_flow.spacing(); coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing * 2); // for lightning infill the density is defined differently, so need to double it generator = std::make_unique(m_object, contours, overhangs, []() {}, support_density); } @@ -3053,6 +3065,8 @@ void TreeSupport::generate_contact_points(std::vector> // fix bug of generating support for very thin objects if (m_object->layers().size() <= z_distance_top_layers + 1) return; + if (m_object->support_layer_count() <= m_raft_layers) + return; int nonempty_layers = 0; tbb::concurrent_vector all_nodes; diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index da58aaf498..8c4a12f43b 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -484,7 +484,7 @@ private: * If a node is already at that position in the layer, the nodes are merged. */ void insert_dropped_node(std::vector& nodes_layer, SupportNode* node); - void create_tree_support_layers(); + void create_tree_support_layers(bool support_enabled=true); void generate_toolpaths(); // get unscaled radius of node coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor); From a2e5332eeff05bcf8f8695bddbdf18ded58fdf03 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 31 Jan 2024 14:05:09 +0800 Subject: [PATCH 21/72] FIX: z_distance_top==0 not working jira: STUDIO-6144 Change-Id: I9ddaa237e2d71c8697a41bbbdb86f70d0b1e1452 (cherry picked from commit 38ba45e9cf7f59d2c1de658a56d8f2b109721bcd) (cherry picked from commit 9d37a31338cf42a285b39daab84af5504e51d009) --- src/libslic3r/Support/TreeSupport.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 775e1f1282..fa8ee64367 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -3052,6 +3052,7 @@ void TreeSupport::generate_contact_points(std::vector> } } const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1; //Support must always be 1 layer below overhang. + int gap_layers = z_distance_top == 0 ? 0 : 1; // virtual layer with 0 height will be deleted if (z_distance_top == 0) z_distance_top = 0.001; @@ -3095,7 +3096,7 @@ void TreeSupport::generate_contact_points(std::vector> // print_z=object_layer->bottom_z: it directly contacts the bottom // height=z_distance_top: it's height is exactly the gap distance // dist_mm_to_top=0: it directly contacts the bottom - contact_node = m_ts_data->create_node(pt, -1, layer_nr - 1, support_roof_layers + 1, to_buildplate, SupportNode::NO_PARENT, bottom_z, z_distance_top, 0, + contact_node = m_ts_data->create_node(pt, -gap_layers, layer_nr - 1, support_roof_layers + 1, to_buildplate, SupportNode::NO_PARENT, bottom_z, z_distance_top, 0, unscale_(radius)); contact_node->overhang = overhang_part; contact_node->is_sharp_tail = is_sharp_tail; @@ -3179,7 +3180,7 @@ void TreeSupport::generate_contact_points(std::vector> } else { for (auto& elem : move_bounds_layer) { - SupportNode* contact_node = m_ts_data->create_node(elem.state.result_on_layer, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, elem.state.to_buildplate, + SupportNode* contact_node = m_ts_data->create_node(elem.state.result_on_layer, -gap_layers, layer_nr-1, support_roof_layers + 1, elem.state.to_buildplate, SupportNode::NO_PARENT, print_z, height, z_distance_top); contact_node->overhang = ExPolygon(elem.influence_area.front()); curr_nodes.emplace_back(contact_node); From 31a5f0d523f47e200de50cf5d4d91d765e245b88 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 1 Feb 2024 17:39:37 +0800 Subject: [PATCH 22/72] FIX: Checking support necessity not working Only early stop detect_overhangs if support is disabled AND not checking support necessity. jira: STUDIO-6158 Change-Id: I2d9662231d941827d6392d57344f7d911f641e09 (cherry picked from commit b811a6031d1afe0e7b1cb73050fe6391a65411ef) (cherry picked from commit fa61ee6abdb4b04a2141b3f8ea9e2e8a789b7881) --- src/libslic3r/PrintObject.cpp | 14 ++++++++------ src/libslic3r/Support/TreeSupport.cpp | 15 +++++++-------- src/libslic3r/Support/TreeSupport.hpp | 4 ++-- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b76eaa4574..d9c76ba849 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -633,13 +633,9 @@ void PrintObject::generate_support_material() if (this->set_started(posSupportMaterial)) { this->clear_support_layers(); - if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) { - m_print->set_status(50, L("Generating support")); - - this->_generate_support_material(); - m_print->throw_if_canceled(); - } else if(!m_print->get_no_check_flag()) { + if(!has_support() && !m_print->get_no_check_flag()) { // BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled + // Note: we also need to pop warning if support is disabled and only raft is enabled m_print->set_status(50, L("Checking support necessity")); typedef std::chrono::high_resolution_clock clock_; typedef std::chrono::duration > second_; @@ -669,6 +665,12 @@ void PrintObject::generate_support_material() #endif } + if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && !m_layers.empty())) { + m_print->set_status(50, L("Generating support")); + + this->_generate_support_material(); + m_print->throw_if_canceled(); + } this->set_done(posSupportMaterial); } } diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index fa8ee64367..66e2f57c5d 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -625,7 +625,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p #define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. -void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) +void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) { // overhangs are already detected if (m_object->support_layer_count() >= m_object->layer_count()) @@ -636,8 +636,8 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) // Clear and create Tree Support Layers m_object->clear_support_layers(); m_object->clear_tree_support_preview_cache(); - create_tree_support_layers(tree_support_enable); - if (!tree_support_enable) + create_tree_support_layers(); + if (!tree_support_enable && !check_support_necessity) return; const PrintObjectConfig& config = m_object->config(); @@ -863,9 +863,11 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) ); // end tbb::parallel_for BOOST_LOG_TRIVIAL(info) << "max_cantilever_dist=" << max_cantilever_dist; + if (check_support_necessity) + return; // check if the sharp tails should be extended higher - if (is_auto(stype) && g_config_support_sharp_tails && !detect_first_sharp_tail_only) { + if (is_auto(stype) && g_config_support_sharp_tails) { for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { if (m_object->print()->canceled()) break; @@ -1104,7 +1106,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) // create support layers for raft and tree support // if support is disabled, only raft layers are created -void TreeSupport::create_tree_support_layers(bool support_enabled) +void TreeSupport::create_tree_support_layers() { int layer_id = 0; coordf_t raft_print_z = 0.f; @@ -1121,9 +1123,6 @@ void TreeSupport::create_tree_support_layers(bool support_enabled) m_object->add_tree_support_layer(layer_id, m_slicing_params.base_raft_layer_height, raft_print_z, raft_slice_z); } - if (!support_enabled) - return; - for (Layer *layer : m_object->layers()) { SupportLayer* ts_layer = m_object->add_tree_support_layer(layer_id++, layer->height, layer->print_z, layer->slice_z); if (ts_layer->id() > m_raft_layers) { diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index 8c4a12f43b..d5e4d0fe2f 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -375,7 +375,7 @@ public: */ void generate(); - void detect_overhangs(bool detect_first_sharp_tail_only=false); + void detect_overhangs(bool check_support_necessity = false); int avg_node_per_layer = 0; @@ -484,7 +484,7 @@ private: * If a node is already at that position in the layer, the nodes are merged. */ void insert_dropped_node(std::vector& nodes_layer, SupportNode* node); - void create_tree_support_layers(bool support_enabled=true); + void create_tree_support_layers(); void generate_toolpaths(); // get unscaled radius of node coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor); From 562dc76ab02316abd5ad1cd32846c526dd58eef9 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 6 Mar 2024 09:50:15 +0800 Subject: [PATCH 23/72] FIX: raft of tree support was incorrect 1. Raft was not generated when tree support is selected but enable_support is false. 2. Raft angle and density of tree support was incorrect github: #3675 Change-Id: Ifd78bf619a28eb03a908e75ad56af4934b6b08b7 (cherry picked from commit 2a448095a2fb4a2abebb0a5c8082a2ddbb635f16) (cherry picked from commit e7ffe31cb3c6b526268adb4c2349a2623b181c53) --- src/libslic3r/Support/TreeSupport.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 66e2f57c5d..3f78d5b091 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1333,12 +1333,13 @@ void TreeSupport::generate_toolpaths() auto raft_areas1 = offset_ex(raft_areas, scale_(expand_offset)); Flow support_flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter); - Fill* filler_interface = Fill::new_from_type(ipRectilinear); - filler_interface->angle = layer_nr == 0 ? 90 : 0; - filler_interface->spacing = support_extrusion_width; + Fill* filler_raft = Fill::new_from_type(ipRectilinear); + filler_raft->angle = layer_nr == 0 ? PI/2 : 0; + filler_raft->spacing = support_flow.spacing(); FillParams fill_params; - fill_params.density = object_config.raft_first_layer_density * 0.01; + coordf_t raft_density = std::min(1., support_flow.spacing() / (object_config.support_base_pattern_spacing.value + support_flow.spacing())); + fill_params.density = layer_nr == 0 ? object_config.raft_first_layer_density * 0.01 : raft_density; fill_params.dont_adjust = true; // wall of first layer raft @@ -1349,7 +1350,7 @@ void TreeSupport::generate_toolpaths() raft_areas1 = offset_ex(raft_areas1, -flow.scaled_spacing() / 2.); } fill_expolygons_generate_paths(ts_layer->support_fills.entities, raft_areas1, - filler_interface, fill_params, erSupportMaterial, support_flow); + filler_raft, fill_params, erSupportMaterial, support_flow); } // subtract the non-raft support bases, otherwise we'll get support base on top of raft interfaces which is not stable @@ -1372,8 +1373,8 @@ void TreeSupport::generate_toolpaths() Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); Fill* filler_interface = Fill::new_from_type(ipRectilinear); - filler_interface->angle = 0; - filler_interface->spacing = support_extrusion_width; + filler_interface->angle = PI / 2; // interface should be perpendicular to base + filler_interface->spacing = support_flow.spacing(); FillParams fill_params; fill_params.density = interface_density; From 715e8e1c32155eb5c0cba7fe8752f9a709c1009d Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 7 Mar 2024 09:26:48 +0800 Subject: [PATCH 24/72] FIX: improve cantilever detection of tree support github: #3667 Change-Id: I5fd6d7d5a6e80aaa563f7d033381e6b8cb9093e0 (cherry picked from commit 3f5fdf82dcaa8636d295794a1113aab78f7327af) (cherry picked from commit 58bba45bc2294ac80c624c0032bfa284228eaa2f) --- src/libslic3r/Support/TreeSupport.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 3f78d5b091..f80564c02c 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -778,6 +778,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) Layer* lower_layer = layer->lower_layer; coordf_t lower_layer_offset = layer_nr < enforce_support_layers ? -0.15 * extrusion_width : (float)lower_layer->height / tan(threshold_rad); + //lower_layer_offset = std::min(lower_layer_offset, extrusion_width); coordf_t support_offset_scaled = scale_(lower_layer_offset); // Filter out areas whose diameter that is smaller than extrusion_width. Do not use offset2() for this purpose! ExPolygons lower_polys; @@ -838,7 +839,8 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) // check cantilever { - auto cluster_boundary_ex = intersection_ex(poly, offset_ex(lower_layer->lslices, scale_(0.5))); + // lower_layer_offset may be very small, so we need to do max and then add 0.1 + auto cluster_boundary_ex = intersection_ex(poly, offset_ex(lower_layer->lslices, scale_(std::max(extrusion_width,lower_layer_offset)+0.1))); Polygons cluster_boundary = to_polygons(cluster_boundary_ex); if (cluster_boundary.empty()) continue; double dist_max = 0; From c199730004cc0ca2c2a91dadc63ad859d028800e Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 14 Sep 2023 14:11:12 +0800 Subject: [PATCH 25/72] FIX: tree support detect overhang inaccurate Jira: STUDIO-3657 Change-Id: I1ef4ca3ec299b121eb467afc12acee68e0f12b39 (cherry picked from commit 36f8937ae0d0d79c7558662903fffef20b528cb8) (cherry picked from commit a631e6cb99ee3f9b2f47aaa4d90831df623a0018) --- src/libslic3r/Support/TreeSupport.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index f80564c02c..fe1ad5f053 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -800,20 +800,15 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); ExPolygons overhang_areas = diff_ex(curr_polys, lower_layer_offseted); - overhang_areas.erase(std::remove_if(overhang_areas.begin(), overhang_areas.end(), - [extrusion_width_scaled](ExPolygon& area) { return offset_ex(area, -0.1 * extrusion_width_scaled).empty(); }), - overhang_areas.end()); - - if (is_auto(stype) && g_config_support_sharp_tails) { // BBS detect sharp tail for (const ExPolygon* expoly : curr_poly_ptrs) { bool is_sharp_tail = false; // 1. nothing below - // this is a sharp tail region if it's small but non-ignorable + // this is a sharp tail region if it's floating and non-ignorable if (!overlaps(offset_ex(*expoly, 0.5 * extrusion_width_scaled), lower_polys)) { - is_sharp_tail = expoly->area() < area_thresh_well_supported && !offset_ex(*expoly, -0.1 * extrusion_width_scaled).empty(); + is_sharp_tail = !offset_ex(*expoly, -0.1 * extrusion_width_scaled).empty(); } if (is_sharp_tail) { @@ -834,7 +829,6 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); for (ExPolygon& poly : overhang_areas) { - if (offset_ex(poly, -0.1 * extrusion_width_scaled).empty()) continue; ts_layer->overhang_areas.emplace_back(poly); // check cantilever From 107551f7b494ea1a418f00bd94fc0f4f45a30d3b Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 7 Mar 2024 16:14:46 +0800 Subject: [PATCH 26/72] ENH: improve the support of blade-shape overhangs 1. reduce the gap between blade-shape overhang and support github: #3667 2. improve sharp tail detection of blade-shape overhangs by introducing lslices_extrudable. github: #2786, #3367 jira: STUDIO-5065, STUDIO-6038 Change-Id: I4e899eace1aa28b100a6f4ce2b8d740c317f5530 (cherry picked from commit 08328b848e39c345b6c7b64021d1e0f04df24d08) (cherry picked from commit 22056d5f7e7bef5574b53fc0453781cd365bf0e1) --- src/libslic3r/Layer.hpp | 3 +- src/libslic3r/Support/SupportMaterial.cpp | 16 +++---- src/libslic3r/Support/TreeSupport.cpp | 56 +++++++++++------------ 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 45f185c425..b9be3f3d8e 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -139,7 +139,7 @@ public: // BBS mutable ExPolygons sharp_tails; mutable ExPolygons cantilevers; - mutable std::map sharp_tails_height; + mutable std::vector sharp_tails_height; // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry // (with possibly differing extruder ID and slicing parameters) and merged. @@ -150,6 +150,7 @@ public: // These lslices are also used to detect overhangs and overlaps between successive layers, therefore it is important // that the 1st lslice is not compensated by the Elephant foot compensation algorithm. ExPolygons lslices; + ExPolygons lslices_extrudable; // BBS: the extrudable part of lslices used for tree support std::vector lslices_bboxes; // BBS diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index cbf331fd78..57567b90d8 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -1412,6 +1412,7 @@ static inline ExPolygons detect_overhangs( const coordf_t max_bridge_length = scale_(object_config.max_bridge_length.value); const bool bridge_no_support = object_config.bridge_no_support.value; const coordf_t xy_expansion = scale_(object_config.support_expansion.value); + float lower_layer_offset = 0; if (layer_id == 0) { @@ -1424,7 +1425,7 @@ static inline ExPolygons detect_overhangs( !(bbox_size.x() > length_thresh_well_supported && bbox_size.y() > length_thresh_well_supported)) { layer.sharp_tails.push_back(slice); - layer.sharp_tails_height.insert({ &slice, layer.height }); + layer.sharp_tails_height.push_back(layer.height); } } } @@ -1444,7 +1445,6 @@ static inline ExPolygons detect_overhangs( } } - float lower_layer_offset = 0; for (LayerRegion *layerm : layer.regions()) { // Extrusion width accounts for the roundings of the extrudates. // It is the maximum widh of the extrudate. @@ -1506,7 +1506,7 @@ static inline ExPolygons detect_overhangs( if (is_sharp_tail) { ExPolygons overhang = diff_ex({ expoly }, lower_layer_expolys); layer.sharp_tails.push_back(expoly); - layer.sharp_tails_height.insert({ &expoly, accum_height }); + layer.sharp_tails_height.push_back(accum_height); overhang = offset_ex(overhang, 0.05 * fw); polygons_append(diff_polygons, to_polygons(overhang)); } @@ -1546,7 +1546,7 @@ static inline ExPolygons detect_overhangs( if (layer.lower_layer) { for (ExPolygon& poly : overhang_areas) { float fw = float(layer.regions().front()->flow(frExternalPerimeter).scaled_width()); - auto cluster_boundary_ex = intersection_ex(poly, offset_ex(layer.lower_layer->lslices, scale_(0.5))); + auto cluster_boundary_ex = intersection_ex(poly, offset_ex(layer.lower_layer->lslices, std::max(fw, lower_layer_offset) + scale_(0.1))); Polygons cluster_boundary = to_polygons(cluster_boundary_ex); if (cluster_boundary.empty()) continue; double dist_max = 0; @@ -2216,9 +2216,9 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( } // 2.3 check whether sharp tail exceed the max height - for (const auto& lower_sharp_tail_height : lower_layer_sharptails_height) { - if (lower_sharp_tail_height.first->overlaps(expoly)) { - accum_height += lower_sharp_tail_height.second; + for (size_t i = 0; i < lower_layer_sharptails_height.size();i++) { + if (lower_layer_sharptails[i].overlaps(expoly)) { + accum_height += lower_layer_sharptails_height[i]; break; } } @@ -2243,7 +2243,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( if (is_sharp_tail) { ExPolygons overhang = diff_ex({ expoly }, lower_layer->lslices); layer->sharp_tails.push_back(expoly); - layer->sharp_tails_height.insert({ &expoly, accum_height }); + layer->sharp_tails_height.push_back( accum_height ); append(overhangs_per_layers[layer_nr], overhang); #ifdef SUPPORT_TREE_DEBUG_TO_SVG SVG svg(get_svg_filename(std::to_string(layer->print_z), "sharp_tail"), object.bounding_box()); diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index fe1ad5f053..cf7634814d 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -752,6 +752,17 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) max_cantilever_dist = 0; m_highest_overhang_layer = 0; + // calc the extrudable expolygons of each layer + tbb::parallel_for(tbb::blocked_range(0, m_object->layer_count()), + [&](const tbb::blocked_range& range) { + for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { + if (m_object->print()->canceled()) + break; + Layer* layer = m_object->get_layer(layer_nr); + // Filter out areas whose diameter that is smaller than extrusion_width, but we don't want to lose any details. + layer->lslices_extrudable = intersection_ex(layer->lslices, offset2_ex(layer->lslices, -extrusion_width_scaled / 2, extrusion_width_scaled)); + } + }); // main part of overhang detection can be parallel tbb::parallel_for(tbb::blocked_range(0, m_object->layer_count()), [&](const tbb::blocked_range& range) { @@ -770,7 +781,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) if (!((bbox_size.x() > length_thresh_well_supported && bbox_size.y() > length_thresh_well_supported)) && g_config_support_sharp_tails) { layer->sharp_tails.push_back(slice); - layer->sharp_tails_height.insert({ &slice, layer->height }); + layer->sharp_tails_height.push_back(layer->height); } } continue; @@ -780,21 +791,8 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) coordf_t lower_layer_offset = layer_nr < enforce_support_layers ? -0.15 * extrusion_width : (float)lower_layer->height / tan(threshold_rad); //lower_layer_offset = std::min(lower_layer_offset, extrusion_width); coordf_t support_offset_scaled = scale_(lower_layer_offset); - // Filter out areas whose diameter that is smaller than extrusion_width. Do not use offset2() for this purpose! - ExPolygons lower_polys; - for (const ExPolygon& expoly : lower_layer->lslices) { - if (!offset_ex(expoly, -extrusion_width_scaled / 2).empty()) { - lower_polys.emplace_back(expoly); - } - } - ExPolygons curr_polys; - std::vector curr_poly_ptrs; - for (const ExPolygon& expoly : layer->lslices) { - if (!offset_ex(expoly, -extrusion_width_scaled / 2).empty()) { - curr_polys.emplace_back(expoly); - curr_poly_ptrs.emplace_back(&expoly); - } - } + ExPolygons& curr_polys = layer->lslices_extrudable; + ExPolygons& lower_polys = lower_layer->lslices_extrudable; // normal overhang ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); @@ -803,24 +801,24 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) if (is_auto(stype) && g_config_support_sharp_tails) { // BBS detect sharp tail - for (const ExPolygon* expoly : curr_poly_ptrs) { + for (const ExPolygon& expoly : curr_polys) { bool is_sharp_tail = false; // 1. nothing below // this is a sharp tail region if it's floating and non-ignorable - if (!overlaps(offset_ex(*expoly, 0.5 * extrusion_width_scaled), lower_polys)) { - is_sharp_tail = !offset_ex(*expoly, -0.1 * extrusion_width_scaled).empty(); + if (!overlaps(offset_ex(expoly, 0.5 * extrusion_width_scaled), lower_polys)) { + is_sharp_tail = !offset_ex(expoly, -0.1 * extrusion_width_scaled).empty(); } if (is_sharp_tail) { - ExPolygons overhang = diff_ex({ *expoly }, lower_polys); - layer->sharp_tails.push_back(*expoly); - layer->sharp_tails_height.insert({ expoly, layer->height }); + ExPolygons overhang = diff_ex({ expoly }, lower_polys); + layer->sharp_tails.push_back(expoly); + layer->sharp_tails_height.push_back(layer->height); append(overhang_areas, overhang); if (!overhang.empty()) { has_sharp_tails = true; #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG::export_expolygons(debug_out_path("sharp_tail_orig_%.02f.svg", layer->print_z), { expoly }); + SVG::export_expolygons(debug_out_path("sharp_tail_orig_%.02f.svg", layer->print_z), { expoly }); #endif } } @@ -834,7 +832,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) // check cantilever { // lower_layer_offset may be very small, so we need to do max and then add 0.1 - auto cluster_boundary_ex = intersection_ex(poly, offset_ex(lower_layer->lslices, scale_(std::max(extrusion_width,lower_layer_offset)+0.1))); + auto cluster_boundary_ex = intersection_ex(poly, offset_ex(lower_polys, scale_(std::max(extrusion_width,lower_layer_offset)+0.1))); Polygons cluster_boundary = to_polygons(cluster_boundary_ex); if (cluster_boundary.empty()) continue; double dist_max = 0; @@ -877,7 +875,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) // BBS detect sharp tail const ExPolygons& lower_layer_sharptails = lower_layer->sharp_tails; const auto& lower_layer_sharptails_height = lower_layer->sharp_tails_height; - for (ExPolygon& expoly : layer->lslices) { + for (ExPolygon& expoly : layer->lslices_extrudable) { bool is_sharp_tail = false; float accum_height = layer->height; do { @@ -907,9 +905,9 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } // 2.3 check whether sharp tail exceed the max height - for (const auto& lower_sharp_tail_height : lower_layer_sharptails_height) { - if (lower_sharp_tail_height.first->overlaps(expoly)) { - accum_height += lower_sharp_tail_height.second; + for(size_t i=0;ilslices); layer->sharp_tails.push_back(expoly); - layer->sharp_tails_height.insert({ &expoly, accum_height }); + layer->sharp_tails_height.push_back( accum_height); append(ts_layer->overhang_areas, overhang); if (!overhang.empty()) From 242a36a26299895b722688e115d23d087b495327 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 8 Mar 2024 16:02:47 +0800 Subject: [PATCH 27/72] FIX: several issue of organic support 1. raft under organic trees are not generated 2. Studio may crash when generating organic supports for some objects. jira: STUDIO-6407 Change-Id: I6e7ff2423b9fee95e4a4a85ccc2844549142f0c8 (cherry picked from commit 00db6c241270f5524bf1618109a2b45872073fd0) (cherry picked from commit c7d12b703e23a83840f480e2cbb3a80fb00e059d) --- src/libslic3r/Support/TreeSupport3D.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 60a66546dc..2a4b4b6396 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -195,7 +195,7 @@ static std::vector>> group_me } #endif -[[nodiscard]] static const std::vector generate_overhangs(const TreeSupportSettings &settings, const PrintObject &print_object, std::function throw_on_cancel) +[[nodiscard]] static const std::vector generate_overhangs(const TreeSupportSettings &settings, PrintObject &print_object, std::function throw_on_cancel) { const size_t num_raft_layers = settings.raft_layers.size(); const size_t num_object_layers = print_object.layer_count(); @@ -217,6 +217,20 @@ static std::vector>> group_me //FIXME this is a fudge constant! auto enforcer_overhang_offset = scaled(config.tree_support_tip_diameter.value); + // calc the extrudable expolygons of each layer + const coordf_t extrusion_width = config.line_width.value; + const coordf_t extrusion_width_scaled = scale_(extrusion_width); + tbb::parallel_for(tbb::blocked_range(0, print_object.layer_count()), + [&](const tbb::blocked_range& range) { + for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { + if (print_object.print()->canceled()) + break; + Layer* layer = print_object.get_layer(layer_nr); + // Filter out areas whose diameter that is smaller than extrusion_width, but we don't want to lose any details. + layer->lslices_extrudable = intersection_ex(layer->lslices, offset2_ex(layer->lslices, -extrusion_width_scaled / 2, extrusion_width_scaled)); + } + }); + size_t num_overhang_layers = support_auto ? num_object_layers : std::min(num_object_layers, std::max(size_t(support_enforce_layers), enforcers_layers.size())); tbb::parallel_for(tbb::blocked_range(1, num_overhang_layers), [&print_object, &config, &print_config, &enforcers_layers, &blockers_layers, @@ -244,9 +258,8 @@ static std::vector>> group_me lower_layer_offset = external_perimeter_width - float(scale_(config.support_threshold_overlap.get_abs_value(unscale_(external_perimeter_width)))); } else lower_layer_offset = scaled(lower_layer.height / tan_threshold); - overhangs = lower_layer_offset == 0 ? - diff(current_layer.lslices, lower_layer.lslices) : - diff(current_layer.lslices, offset(lower_layer.lslices, lower_layer_offset)); + Polygons lower_layer_offseted = offset(lower_layer.lslices_extrudable, lower_layer_offset); + overhangs = diff(current_layer.lslices_extrudable, lower_layer_offseted); if (lower_layer_offset == 0) { raw_overhangs = overhangs; raw_overhangs_calculated = true; @@ -3441,6 +3454,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume auto t_place = std::chrono::high_resolution_clock::now(); // ### draw these points as circles + // this new function give correct result when raft is also enabled organic_draw_branches( *print.get_object(processing.second.front()), volumes, config, move_bounds, bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage, From 4ee6d636c7b99a2c6e624b79958418b418e5c3ff Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 8 Mar 2024 16:35:05 +0800 Subject: [PATCH 28/72] FIX: blockers are not working correctly with organic supports. jira: STUDIO-6278 Change-Id: If74e611821db25241dd67dfc4a3e44fac557a10d (cherry picked from commit 76b9b2b7e856f648082cd3a09386775695c6accb) (cherry picked from commit cf35d0268fce05308d56b87bc2c50eb822451c02) --- src/libslic3r/Support/TreeSupport3D.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 2a4b4b6396..6d3f041207 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -216,6 +216,7 @@ static std::vector>> group_me double tan_threshold = support_threshold_auto ? 0. : tan(M_PI * double(support_threshold + 1) / 180.); //FIXME this is a fudge constant! auto enforcer_overhang_offset = scaled(config.tree_support_tip_diameter.value); + const coordf_t radius_sample_resolution = g_config_tree_support_collision_resolution; // calc the extrudable expolygons of each layer const coordf_t extrusion_width = config.line_width.value; @@ -234,7 +235,7 @@ static std::vector>> group_me size_t num_overhang_layers = support_auto ? num_object_layers : std::min(num_object_layers, std::max(size_t(support_enforce_layers), enforcers_layers.size())); tbb::parallel_for(tbb::blocked_range(1, num_overhang_layers), [&print_object, &config, &print_config, &enforcers_layers, &blockers_layers, - support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, &throw_on_cancel, &out] + support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, radius_sample_resolution, &throw_on_cancel, &out] (const tbb::blocked_range &range) { for (LayerIndex layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { const Layer ¤t_layer = *print_object.get_layer(layer_id); @@ -264,12 +265,8 @@ static std::vector>> group_me raw_overhangs = overhangs; raw_overhangs_calculated = true; } - if (! (enforced_layer || blockers_layers.empty() || blockers_layers[layer_id].empty())) { - Polygons &blocker = blockers_layers[layer_id]; - // Arthur: union_ is a must because after mirroring, the blocker polygons are in left-hand coordinates, ie clockwise, - // which are not valid polygons, and will be removed by offset. union_ can make these polygons right. - overhangs = diff(overhangs, offset(union_(blocker), scale_(g_config_tree_support_collision_resolution)), ApplySafetyOffset::Yes); - } + if (! (enforced_layer || blockers_layers.empty() || blockers_layers[layer_id].empty())) + overhangs = diff(overhangs, offset_ex(union_(blockers_layers[layer_id]), scale_(radius_sample_resolution)), ApplySafetyOffset::Yes); if (config.bridge_no_support) { for (const LayerRegion *layerm : current_layer.regions()) remove_bridges_from_contacts(print_config, lower_layer, *layerm, From fd3ed419c2c7c98c4a53c8fb867bfc7b1fab8d52 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 6 Mar 2024 08:44:32 +0800 Subject: [PATCH 29/72] NEW: add support_object_first_layer_gap option jira: STUDIO-6202 github: #3521 Change-Id: I6f1ce9f5312e9482c0f5bf6ac3215861c501106c (cherry picked from commit 83027d923a6e67fa0013e5d9f627283c68e996de) (cherry picked from commit 29da3bc441a194d5b83ce267fe24ebf3f3811133) --- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 11 +++++++++++ src/libslic3r/PrintConfig.hpp | 1 + src/libslic3r/PrintObject.cpp | 1 + src/libslic3r/Support/SupportCommon.cpp | 9 +++++---- src/libslic3r/Support/SupportParameters.hpp | 10 ++++++---- src/libslic3r/Support/TreeSupport.cpp | 6 +++--- src/libslic3r/Support/TreeSupportCommon.hpp | 2 ++ src/slic3r/GUI/ConfigManipulation.cpp | 2 +- src/slic3r/GUI/GUI_Factories.cpp | 8 +++++--- src/slic3r/GUI/Tab.cpp | 1 + 11 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 134538dde5..3051d88fb0 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -792,7 +792,7 @@ static std::vector s_Preset_print_options { "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence", "max_volumetric_extrusion_rate_slope", "max_volumetric_extrusion_rate_slope_segment_length","extrusion_rate_smoothing_external_perimeter_only", "inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed", - "top_surface_speed", "support_speed", "support_object_xy_distance", "support_interface_speed", + "top_surface_speed", "support_speed", "support_object_xy_distance", "support_object_first_layer_gap", "support_interface_speed", "bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed", "outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", "skirt_height", "draft_shield", "brim_width", "brim_object_gap", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","enforce_support_layers", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 7c6be8edab..31ab6fcbff 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4493,6 +4493,17 @@ void PrintConfigDef::init_fff_params() //Support with too small spacing may touch the object and difficult to remove. def->set_default_value(new ConfigOptionFloat(0.35)); + def = this->add("support_object_first_layer_gap", coFloat); + def->label = L("Support/object first layer gap"); + def->category = L("Support"); + def->tooltip = L("XY separation between an object and its support at the first layer."); + def->sidetext = L("mm"); + def->min = 0; + def->max = 10; + def->mode = comAdvanced; + //Support with too small spacing may touch the object and difficult to remove. + def->set_default_value(new ConfigOptionFloat(0.2)); + def = this->add("support_angle", coFloat); def->label = L("Pattern angle"); def->category = L("Support"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 6e1f2701d6..b0c1d910fa 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -831,6 +831,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionInt, support_threshold_angle)) ((ConfigOptionFloatOrPercent, support_threshold_overlap)) ((ConfigOptionFloat, support_object_xy_distance)) + ((ConfigOptionFloat, support_object_first_layer_gap)) ((ConfigOptionFloat, xy_hole_compensation)) ((ConfigOptionFloat, xy_contour_compensation)) ((ConfigOptionBool, flush_into_objects)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d9c76ba849..a25df3451a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1017,6 +1017,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "support_base_pattern" || opt_key == "support_style" || opt_key == "support_object_xy_distance" + || opt_key == "support_object_first_layer_gap" || opt_key == "support_base_pattern_spacing" || opt_key == "support_expansion" //|| opt_key == "independent_support_layer_height" // BBS diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index edc71e76df..6d6553b12f 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -445,10 +445,11 @@ SupportGeneratorLayersPtr generate_raft_base( Polygons &raft = columns_base->polygons; 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); + // brim_object_gap is changed to 0 by default, it's no longer appropriate to use it to determine the gap of first layer support. + //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_first_layer), 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 / support_params.first_layer_flow.scaled_width()))); diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index f36709fb53..e4c0dcd00e 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -69,10 +69,11 @@ struct SupportParameters { external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width())); bridge_flow_ratio += region.config().bridge_flow; } - this->gap_xy = object_config.support_object_xy_distance;//.get_abs_value(external_perimeter_width); + this->gap_xy = object_config.support_object_xy_distance.value; + this->gap_xy_first_layer = object_config.support_object_first_layer_gap.value; bridge_flow_ratio /= object.num_printing_regions(); - - this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ? + + this->support_material_bottom_interface_flow = slicing_params.soluble_interface || !object_config.thick_bridges ? this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter()); @@ -209,7 +210,8 @@ struct SupportParameters { coordf_t support_layer_height_min; // coordf_t support_layer_height_max; - coordf_t gap_xy; + coordf_t gap_xy; + coordf_t gap_xy_first_layer; float base_angle; float interface_angle; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index cf7634814d..d7873491b3 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1926,7 +1926,7 @@ void TreeSupport::draw_circles(const std::vector>& con } else { if (collision_base.empty()) - collision_base = m_ts_data->get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr); + collision_base = m_ts_data->get_collision((layer_nr == 0) ? config.support_object_first_layer_gap : m_ts_data->m_xy_distance, layer_nr); return collision_base; } }; @@ -2044,7 +2044,7 @@ void TreeSupport::draw_circles(const std::vector>& con roof_1st_layer = intersection_ex(roof_1st_layer, m_machine_border); // let supports touch objects when brim is on - //auto avoid_region = get_collision_polys((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr); + //auto avoid_region = get_collision_polys((layer_nr == 0) ? config.support_object_xy_distance : m_ts_data->m_xy_distance, layer_nr); //base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region); //base_areas = diff_clipped(base_areas, avoid_region); ExPolygons roofs; append(roofs, roof_1st_layer); append(roofs, roof_areas);append(roofs, roof_gap_areas); @@ -2295,7 +2295,7 @@ void TreeSupport::draw_circles(const std::vector>& con // make sure 1) roof1 and object 2) roof1 and roof, won't intersect // Note: We can't replace roof1 directly, as we have recorded its address. // So instead we need to replace its members one by one. - auto tmp1 = diff_ex(roof1, get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr)); + auto tmp1 = diff_ex(roof1, get_collision((layer_nr == 0) ? config.support_object_xy_distance : m_ts_data->m_xy_distance, layer_nr)); tmp1 = diff_ex(tmp1, ts_layer->roof_areas); if (!tmp1.empty()) { roof1.contour = std::move(tmp1[0].contour); diff --git a/src/libslic3r/Support/TreeSupportCommon.hpp b/src/libslic3r/Support/TreeSupportCommon.hpp index 50ca626fa3..b382f6526c 100644 --- a/src/libslic3r/Support/TreeSupportCommon.hpp +++ b/src/libslic3r/Support/TreeSupportCommon.hpp @@ -69,6 +69,7 @@ struct TreeSupportMeshGroupSettings { 0; this->support_material_buildplate_only = config.support_on_build_plate_only; this->support_xy_distance = scaled(config.support_object_xy_distance.value); + this->support_xy_distance_1st_layer = scaled(config.support_object_first_layer_gap.value); // Separation of interfaces, it is likely smaller than support_xy_distance. this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled(0.5 * external_perimeter_width)); this->support_top_distance = scaled(slicing_params.gap_support_object); @@ -158,6 +159,7 @@ struct TreeSupportMeshGroupSettings { // Distance of the support structure from the print in the X/Y directions. // minimum: 0, maximum warning: 1.5 * machine_nozzle_tip_outer_diameter coord_t support_xy_distance { scaled(0.7) }; + coord_t support_xy_distance_1st_layer { scaled(0.7) }; // Minimum Support X/Y Distance // Distance of the support structure from the overhang in the X/Y directions. // minimum_value: 0, minimum warning": support_xy_distance - support_line_width * 2, maximum warning: support_xy_distance diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 3425890d70..4e13f7972c 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -596,7 +596,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co "support_interface_pattern", "support_interface_top_layers", "support_interface_bottom_layers", "bridge_no_support", "max_bridge_length", "support_top_z_distance", "support_bottom_z_distance", "support_type", "support_on_build_plate_only", "support_critical_regions_only","support_interface_not_for_body", - "support_object_xy_distance"/*, "independent_support_layer_height"*/}) + "support_object_xy_distance","support_object_first_layer_gap"/*, "independent_support_layer_height"*/}) toggle_field(el, have_support_material); toggle_field("support_threshold_angle", have_support_material && is_auto(support_type)); toggle_field("support_threshold_overlap", config->opt_int("support_threshold_angle") == 0 && have_support_material && is_auto(support_type)); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 416c269905..096a83a512 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -91,9 +91,10 @@ std::map> SettingsFactory::OBJECT_C {"enable_support", "",4},{"support_type", "",5},{"support_threshold_angle", "",6}, {"support_threshold_overlap", "",6}, {"support_on_build_plate_only", "",7}, {"support_filament", "",8},{"support_interface_filament", "",9},{"support_expansion", "",24},{"support_style", "",25}, {"tree_support_brim_width", "",26}, {"tree_support_branch_angle", "",10},{"tree_support_branch_angle_organic","",10}, {"tree_support_wall_count", "",11},//tree support - {"support_top_z_distance", "",13},{"support_bottom_z_distance", "",12},{"support_base_pattern", "",14},{"support_base_pattern_spacing", "",15}, - {"support_interface_top_layers", "",16},{"support_interface_bottom_layers", "",17},{"support_interface_spacing", "",18},{"support_bottom_interface_spacing", "",19}, - {"support_object_xy_distance", "",20}, {"bridge_no_support", "",21},{"max_bridge_length", "",22},{"support_critical_regions_only", "",23},{"support_remove_small_overhang","",27} + {"support_top_z_distance", "",13},{"support_bottom_z_distance", "",12},{"support_base_pattern", "",14},{"support_base_pattern_spacing", "",15}, + {"support_interface_top_layers", "",16},{"support_interface_bottom_layers", "",17},{"support_interface_spacing", "",18},{"support_bottom_interface_spacing", "",19}, + {"support_object_xy_distance", "",20}, {"bridge_no_support", "",21},{"max_bridge_length", "",22},{"support_critical_regions_only", "",23},{"support_remove_small_overhang","",27}, + {"support_object_first_layer_gap","",28} }}, { L("Speed"), {{"support_speed", "",12}, {"support_interface_speed", "",13} }} @@ -155,6 +156,7 @@ std::vector SettingsFactory::get_visible_options(const std::s "tree_support_wall_count", //support "support_top_z_distance", "support_base_pattern", "support_base_pattern_spacing", "support_interface_top_layers", "support_interface_bottom_layers", "support_interface_spacing", "support_bottom_interface_spacing", "support_object_xy_distance", + "support_object_first_layer_gap", //adhesion "brim_type", "brim_width", "brim_object_gap", "raft_layers" };*/ diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 8ed06d1d87..05822fdfdb 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2263,6 +2263,7 @@ void TabPrint::build() //optgroup->append_single_option_line("support_interface_loop_pattern"); optgroup->append_single_option_line("support_object_xy_distance", "support"); + optgroup->append_single_option_line("support_object_first_layer_gap", "support"); optgroup->append_single_option_line("bridge_no_support", "support#base-pattern"); optgroup->append_single_option_line("max_bridge_length", "support#base-pattern"); optgroup->append_single_option_line("independent_support_layer_height", "support"); From da7eea49c943fe797ee43e6134753cc3c547d43f Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 14 Mar 2024 17:57:32 +0800 Subject: [PATCH 30/72] FIX: reduce unnecessary small parts of tree supports 1. reduce unnecessary small parts of tree supports jira: STUDIO-6506 2. fix the bug that zero height tree support may be generated with synced support layer height. 3. fix the bug that tree nodes' radii may change abruptly jira: STUDIO-6326 Change-Id: I38153d136e46bf9d797881cc6ca8803767f46736 (cherry picked from commit 59af0a6c2643169463cf18010ffd75f3aa19b704) (cherry picked from commit 47440f040cae70f79e430b02d44f6093208ac066) --- src/libslic3r/Support/TreeSupport.cpp | 186 ++++++++++++-------------- src/libslic3r/Support/TreeSupport.hpp | 2 +- 2 files changed, 86 insertions(+), 102 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index d7873491b3..61fede21eb 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1952,6 +1952,7 @@ void TreeSupport::draw_circles(const std::vector>& con } if (node.type == ePolygon) has_polygon_node = true; + area = diff_clipped(area, get_collision(node.is_sharp_tail && node.distance_to_top <= 0)); } else { Polygon circle(branch_circle); @@ -1982,7 +1983,7 @@ void TreeSupport::draw_circles(const std::vector>& con if(!tmp.empty()) circle = tmp[0]; } - area.emplace_back(ExPolygon(circle)); + area = avoid_object_remove_extra_small_parts(ExPolygons{ ExPolygon(circle) }, get_collision(node.is_sharp_tail && node.distance_to_top <= 0)); // merge overhang to get a smoother interface surface // Do not merge when buildplate_only is on, because some underneath nodes may have been deleted. if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only && !node.is_sharp_tail) { @@ -2025,28 +2026,10 @@ void TreeSupport::draw_circles(const std::vector>& con roof_areas = std::move(offset2_ex(roof_areas, contact_dist_scaled, -contact_dist_scaled)); roof_1st_layer = std::move(offset2_ex(roof_1st_layer, contact_dist_scaled, -contact_dist_scaled)); - // avoid object - // arthur: do not leave a gap for top interface if the top z distance is 0. See STUDIO-3991 - Polygons avoid_region_interface = get_trim_support_regions(*m_object, ts_layer, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, - m_slicing_params.gap_support_object == 0 ? 0 : m_ts_data->m_xy_distance); - if (has_circle_node) { - roof_areas = avoid_object_remove_extra_small_parts(roof_areas, avoid_region_interface); - roof_1st_layer = avoid_object_remove_extra_small_parts(roof_1st_layer, avoid_region_interface); - } - else { - roof_areas = diff_ex(roof_areas, ClipperUtils::clip_clipper_polygons_with_subject_bbox(avoid_region_interface, get_extents(roof_areas))); - roof_1st_layer = diff_ex(roof_1st_layer, ClipperUtils::clip_clipper_polygons_with_subject_bbox(avoid_region_interface, get_extents(roof_1st_layer))); - } - roof_areas = intersection_ex(roof_areas, m_machine_border); - // roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer roof_1st_layer = diff_ex(roof_1st_layer, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roof_areas,get_extents(roof_1st_layer))); roof_1st_layer = intersection_ex(roof_1st_layer, m_machine_border); - // let supports touch objects when brim is on - //auto avoid_region = get_collision_polys((layer_nr == 0) ? config.support_object_xy_distance : m_ts_data->m_xy_distance, layer_nr); - //base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region); - //base_areas = diff_clipped(base_areas, avoid_region); ExPolygons roofs; append(roofs, roof_1st_layer); append(roofs, roof_areas);append(roofs, roof_gap_areas); base_areas = diff_ex(base_areas, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roofs, get_extents(base_areas))); base_areas = intersection_ex(base_areas, m_machine_border); @@ -2549,7 +2532,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod SupportNode* neighbour = nodes_per_part[group_index][neighbours[0]]; SupportNode* node_; if (p_node->parent && neighbour->parent) - node_ = (node.dist_mm_to_top >= neighbour->dist_mm_to_top && p_node->parent) ? p_node : neighbour; + node_ = (node.dist_mm_to_top >= neighbour->dist_mm_to_top) ? p_node : neighbour; else node_ = p_node->parent ? p_node : neighbour; // Make sure the next pass doesn't drop down either of these (since that already happened). @@ -2557,7 +2540,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_position); SupportNode * next_node = m_ts_data->create_node(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_, print_z_next, height_next); - next_node->movement = next_position - node.position; get_max_move_dist(next_node); contact_nodes[layer_nr_next].push_back(next_node); @@ -2574,10 +2556,9 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod { SupportNode* neighbour_node = nodes_per_part[group_index][neighbour]; if (neighbour_node->type == ePolygon) continue; + // only allow bigger node to merge smaller nodes. See STUDIO-6326 + if(node.dist_mm_to_top < neighbour_node->dist_mm_to_top) continue; - node.distance_to_top = std::max(node.distance_to_top, neighbour_node->distance_to_top); - node.support_roof_layers_below = std::max(node.support_roof_layers_below, neighbour_node->support_roof_layers_below); - node.dist_mm_to_top = std::max(node.dist_mm_to_top, neighbour_node->dist_mm_to_top); node.merged_neighbours.push_front(neighbour_node); node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end()); to_delete.insert(neighbour_node); @@ -2753,7 +2734,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_layer_vertex); SupportNode * next_node = m_ts_data->create_node(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); - next_node->movement = movement; get_max_move_dist(next_node); contact_nodes[layer_nr_next].push_back(next_node); } @@ -2906,91 +2886,94 @@ std::vector TreeSupport::plan_layer_heights(std::vectorget_layer(layer_nr)->print_z, m_object->get_layer(layer_nr)->height, layer_nr > 0 ? size_t(layer_nr - 1) : 0}; } - return layer_heights; } - - bounds.push_back(0); - // Keep first layer still - layer_heights[0] = {m_object->get_layer(0)->print_z, m_object->get_layer(0)->height, 0}; - // Collect top contact layers - coordf_t print_z = layer_heights[0].print_z; - for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) - { - if (!contact_nodes[layer_nr].empty()) { - bounds.push_back(layer_nr); - layer_heights[layer_nr].print_z = contact_nodes[layer_nr].front()->print_z; - layer_heights[layer_nr].height = contact_nodes[layer_nr].front()->height; - if (layer_heights[layer_nr].bottom_z() - print_z < m_slicing_params.min_layer_height) { - layer_heights[layer_nr].height = layer_heights[layer_nr].print_z - print_z; - for (auto& node : contact_nodes[layer_nr]) { - node->height = layer_heights[layer_nr].height; + else { + bounds.push_back(0); + // Keep first layer still + layer_heights[0] = { m_object->get_layer(0)->print_z, m_object->get_layer(0)->height, 0 }; + // Collect top contact layers + coordf_t print_z = layer_heights[0].print_z; + for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) { + if (!contact_nodes[layer_nr].empty()) { + bounds.push_back(layer_nr); + layer_heights[layer_nr].print_z = contact_nodes[layer_nr].front()->print_z; + layer_heights[layer_nr].height = contact_nodes[layer_nr].front()->height; + if (layer_heights[layer_nr].bottom_z() - print_z < m_slicing_params.min_layer_height) { + layer_heights[layer_nr].height = layer_heights[layer_nr].print_z - print_z; + for (auto& node : contact_nodes[layer_nr]) { + node->height = layer_heights[layer_nr].height; + } } + print_z = layer_heights[layer_nr].print_z; + + BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights0 print_z, height, layer_nr: " << layer_heights[layer_nr].print_z << " " << layer_heights[layer_nr].height << " " + << layer_nr; + } + } + std::set s(bounds.begin(), bounds.end()); + bounds.assign(s.begin(), s.end()); + + for (size_t idx_extreme = 1; idx_extreme < bounds.size(); idx_extreme++) { + int extr2_layer_nr = bounds[idx_extreme]; + coordf_t extr2z = layer_heights[extr2_layer_nr].bottom_z(); + int extr1_layer_nr = bounds[idx_extreme - 1]; + coordf_t extr1z = layer_heights[extr1_layer_nr].print_z; + coordf_t dist = extr2z - extr1z; + + // Insert intermediate layers. + size_t n_layers_extra = size_t(ceil(dist / (m_slicing_params.max_suport_layer_height + EPSILON))); + int actual_internel_layers = extr2_layer_nr - extr1_layer_nr - 1; + int extr_layers_left = extr2_layer_nr - extr1_layer_nr - n_layers_extra - 1; + if (n_layers_extra < 1) + continue; + + coordf_t step = dist / coordf_t(n_layers_extra); + coordf_t print_z = extr1z + step; + //assert(step >= layer_height - EPSILON); + coordf_t extr2z_large_steps = extr2z; + for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; layer_nr++) { + // if (curr_layer_nodes.empty()) continue; + if (std::abs(print_z - m_object->get_layer(layer_nr)->print_z) < step / 2 + EPSILON || extr_layers_left < 1) { + layer_heights[layer_nr].print_z = print_z; + layer_heights[layer_nr].height = step; + print_z += step; + } + else { + // can't clear curr_layer_nodes, or the model will have empty layers + layer_heights[layer_nr].print_z = 0.0; + layer_heights[layer_nr].height = 0.0; + extr_layers_left--; } - print_z = layer_heights[layer_nr].print_z; - BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights0 print_z, height, layer_nr: " << layer_heights[layer_nr].print_z << " " << layer_heights[layer_nr].height << " " - << layer_nr; - } - } - std::set s(bounds.begin(), bounds.end()); - bounds.assign(s.begin(), s.end()); + } - for (size_t idx_extreme = 1; idx_extreme < bounds.size(); idx_extreme++) { - int extr2_layer_nr = bounds[idx_extreme]; - coordf_t extr2z = layer_heights[extr2_layer_nr].bottom_z(); - int extr1_layer_nr = bounds[idx_extreme - 1]; - coordf_t extr1z = layer_heights[extr1_layer_nr].print_z; - coordf_t dist = extr2z - extr1z; - - // Insert intermediate layers. - size_t n_layers_extra = size_t(ceil(dist / (m_slicing_params.max_suport_layer_height + EPSILON))); - int actual_internel_layers = extr2_layer_nr - extr1_layer_nr - 1; - int extr_layers_left = extr2_layer_nr - extr1_layer_nr - n_layers_extra - 1; - if (n_layers_extra < 1) - continue; - - coordf_t step = dist / coordf_t(n_layers_extra); - coordf_t print_z = extr1z + step; - //assert(step >= layer_height - EPSILON); - coordf_t extr2z_large_steps = extr2z; - for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; layer_nr++) { - // if (curr_layer_nodes.empty()) continue; - if (std::abs(print_z - m_object->get_layer(layer_nr)->print_z) < step / 2 + EPSILON || extr_layers_left < 1) { - layer_heights[layer_nr].print_z = print_z; - layer_heights[layer_nr].height = step; - print_z += step; + // fill in next_layer_nr + int i = layer_heights.size() - 1, j = i; + for (; j >= 0; i = j) { + if (layer_heights[i].height < EPSILON) { + j--; + continue; } - else { - // can't clear curr_layer_nodes, or the model will have empty layers - layer_heights[layer_nr].print_z = 0.0; - layer_heights[layer_nr].height = 0.0; - extr_layers_left--; + for (j = i - 1; j >= 0; j--) { + if (layer_heights[j].height > EPSILON) { + layer_heights[i].next_layer_nr = j; + break; + } } } + + for (i = 0; i < layer_heights.size(); i++) { + // there might be gap between layers due to non-integer interfaces + if (size_t next_layer_nr = layer_heights[i].next_layer_nr; + next_layer_nr > 0 && layer_heights[i].height + EPSILON < layer_heights[i].print_z - layer_heights[next_layer_nr].print_z) { + layer_heights[i].height = layer_heights[i].print_z - layer_heights[next_layer_nr].print_z; + } + + } } - // fill in next_layer_nr - int i = layer_heights.size() - 1, j = i; - for (; j >= 0; i = j) { - if (layer_heights[i].height < EPSILON) { - j--; - continue; - } - for (j = i - 1; j >= 0; j--) { - if (layer_heights[j].height > EPSILON) { - layer_heights[i].next_layer_nr = j; - break; - } - } - } - for (i = 0; i < layer_heights.size(); i++) { - // there might be gap between layers due to non-integer interfaces - if (size_t next_layer_nr = layer_heights[i].next_layer_nr; - next_layer_nr>0 && layer_heights[i].height + EPSILON < layer_heights[i].print_z - layer_heights[next_layer_nr].print_z) - { - layer_heights[i].height = layer_heights[i].print_z - layer_heights[next_layer_nr].print_z; - } - // update interfaces' height + // update interfaces' height + for (size_t i = 0; i < layer_heights.size(); i++) { for (auto& node : contact_nodes[i]) { node->height = layer_heights[i].height; } @@ -2998,7 +2981,6 @@ std::vector TreeSupport::plan_layer_heights(std::vectorlayer_nr: " << layer_heights[i].print_z << " " << layer_heights[i].height << " " << layer_heights[i].next_layer_nr << "->" << i; } - return layer_heights; } @@ -3295,6 +3277,8 @@ SupportNode* TreeSupportData::create_node(const Point position, const int distan SupportNode* node = new SupportNode(position, distance_to_top, obj_layer_nr, support_roof_layers_below, to_buildplate, parent, print_z_, height_, dist_mm_to_top_, radius_); contact_nodes.emplace_back(node); m_mutex.unlock(); + if (parent) + node->movement = position - parent->position; return node; } void TreeSupportData::clear_nodes() diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index d5e4d0fe2f..9606df1c5e 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -81,7 +81,7 @@ struct SupportNode overhang = parent->overhang; if (dist_mm_to_top == 0) dist_mm_to_top = parent->dist_mm_to_top + parent->height; - if (radius == 0) + if (radius == 0 && parent->radius>0) radius = parent->radius + (dist_mm_to_top - parent->dist_mm_to_top) * diameter_angle_scale_factor; parent->child = this; for (auto& neighbor : parent->merged_neighbours) { From 19107b586940389e7ccd2948474edfe586617dbe Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 15 Mar 2024 10:11:54 +0800 Subject: [PATCH 31/72] FIX: compiling error due to template function deduction jira: none Change-Id: Ib2a3f03468b7992defef3ff2298882b4435cabd1 (cherry picked from commit 9efa2ee2e2dcd4d1322afa03c7b7ce05b1d69872) (cherry picked from commit 40a468be3df9abc35054185ce780ada20902970b) --- src/libslic3r/Support/TreeSupport.cpp | 30 +++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 61fede21eb..07586d0006 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1728,24 +1728,22 @@ Polygons TreeSupport::get_collision_polys(coordf_t radius, size_t layer_nr) return Polygons(); } -template // RegionType could be ExPolygons or Polygons -ExPolygons avoid_object_remove_extra_small_parts(ExPolygons &expolys, const RegionType&avoid_region) { +ExPolygons avoid_object_remove_extra_small_parts(const ExPolygon &expoly, const ExPolygons& avoid_region) { ExPolygons expolys_out; - if(expolys.empty()) return expolys_out; - auto clipped_avoid_region=ClipperUtils::clip_clipper_polygons_with_subject_bbox(avoid_region, get_extents(expolys)); - for (auto expoly : expolys) { - auto expolys_avoid = diff_ex(expoly, clipped_avoid_region); - int idx_max_area = -1; - float max_area = 0; - for (int i = 0; i < expolys_avoid.size(); ++i) { - auto a = expolys_avoid[i].area(); - if (a > max_area) { - max_area = a; - idx_max_area = i; - } + if(expoly.empty()) return expolys_out; + auto clipped_avoid_region=ClipperUtils::clip_clipper_polygons_with_subject_bbox(avoid_region, get_extents(expoly)); + auto expolys_avoid = diff_ex(expoly, clipped_avoid_region); + int idx_max_area = -1; + float max_area = 0; + for (int i = 0; i < expolys_avoid.size(); ++i) { + auto a = expolys_avoid[i].area(); + if (a > max_area) { + max_area = a; + idx_max_area = i; } - if (idx_max_area >= 0) expolys_out.emplace_back(std::move(expolys_avoid[idx_max_area])); } + if (idx_max_area >= 0) expolys_out.emplace_back(std::move(expolys_avoid[idx_max_area])); + return expolys_out; } @@ -1983,7 +1981,7 @@ void TreeSupport::draw_circles(const std::vector>& con if(!tmp.empty()) circle = tmp[0]; } - area = avoid_object_remove_extra_small_parts(ExPolygons{ ExPolygon(circle) }, get_collision(node.is_sharp_tail && node.distance_to_top <= 0)); + area = avoid_object_remove_extra_small_parts(ExPolygon(circle), get_collision(node.is_sharp_tail && node.distance_to_top <= 0)); // merge overhang to get a smoother interface surface // Do not merge when buildplate_only is on, because some underneath nodes may have been deleted. if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only && !node.is_sharp_tail) { From 0b671e8852707dcb6c5efa9664995c31ff6e6428 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 15 Mar 2024 11:53:06 +0800 Subject: [PATCH 32/72] ENH: speedup tree support generation 1. speedup detect_overhangs by skipping sharp tail and cantilever detection if there are too many overhangs. jira: STUDIO-3584, STUDIO-2592 2. drop_nodes with precalculation of avoidance and tbb parallel_for_each for all nodes in each layer. jira: STUDIO-1814, STUDIO-2381, STUDIO-2639, STUDIO-5020, 3. don't show too many progress messages Change-Id: Ia4897089c69c235fb7cd8e5fdcf4690086048b31 (cherry picked from commit 9c08e28b5b5342dfdde2c939fc953f143a42a59b) (cherry picked from commit 9de69035a029374be477b74e67c96dd8235daafa) --- src/libslic3r/Support/TreeSupport.cpp | 262 +++++++++++++++----------- src/libslic3r/Support/TreeSupport.hpp | 1 + 2 files changed, 158 insertions(+), 105 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 07586d0006..d0dfbbf9cc 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -15,8 +15,10 @@ #include #include +#include #include #include +#include #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 @@ -649,7 +651,8 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) const coordf_t max_bridge_length = scale_(config.max_bridge_length.value); const bool bridge_no_support = max_bridge_length > 0; const bool support_critical_regions_only = config.support_critical_regions_only.value; - const bool config_remove_small_overhangs = config.support_remove_small_overhang.value; + bool config_remove_small_overhangs = config.support_remove_small_overhang.value; + bool config_detect_sharp_tails = g_config_support_sharp_tails; const int enforce_support_layers = config.enforce_support_layers.value; const double area_thresh_well_supported = SQ(scale_(6)); const double length_thresh_well_supported = scale_(6); @@ -763,6 +766,10 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) layer->lslices_extrudable = intersection_ex(layer->lslices, offset2_ex(layer->lslices, -extrusion_width_scaled / 2, extrusion_width_scaled)); } }); + + typedef std::chrono::high_resolution_clock clock_; + typedef std::chrono::duration > second_; + std::chrono::time_point t0{ clock_::now() }; // main part of overhang detection can be parallel tbb::parallel_for(tbb::blocked_range(0, m_object->layer_count()), [&](const tbb::blocked_range& range) { @@ -795,10 +802,18 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) ExPolygons& lower_polys = lower_layer->lslices_extrudable; // normal overhang + SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); - ExPolygons overhang_areas = diff_ex(curr_polys, lower_layer_offseted); + ts_layer->overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted)); - if (is_auto(stype) && g_config_support_sharp_tails) + double duration{ std::chrono::duration_cast(clock_::now() - t0).count() }; + if (duration > 30 || ts_layer->overhang_areas.size() > 100) { + BOOST_LOG_TRIVIAL(info) << "detect_overhangs takes more than 30 secs, skip cantilever and sharp tails detection: layer_nr=" << layer_nr << " duration=" << duration; + config_detect_sharp_tails = false; + config_remove_small_overhangs = false; + continue; + } + if (is_auto(stype) && config_detect_sharp_tails) { // BBS detect sharp tail for (const ExPolygon& expoly : curr_polys) { @@ -813,7 +828,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) ExPolygons overhang = diff_ex({ expoly }, lower_polys); layer->sharp_tails.push_back(expoly); layer->sharp_tails_height.push_back(layer->height); - append(overhang_areas, overhang); + append(ts_layer->overhang_areas, overhang); if (!overhang.empty()) { has_sharp_tails = true; @@ -825,31 +840,28 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } } - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); - for (ExPolygon& poly : overhang_areas) { - ts_layer->overhang_areas.emplace_back(poly); - // check cantilever - { - // lower_layer_offset may be very small, so we need to do max and then add 0.1 - auto cluster_boundary_ex = intersection_ex(poly, offset_ex(lower_polys, scale_(std::max(extrusion_width,lower_layer_offset)+0.1))); - Polygons cluster_boundary = to_polygons(cluster_boundary_ex); - if (cluster_boundary.empty()) continue; - double dist_max = 0; - for (auto& pt : poly.contour.points) { - double dist_pt = std::numeric_limits::max(); - for (auto& ply : cluster_boundary) { - double d = ply.distance_to(pt); - dist_pt = std::min(dist_pt, d); - } - dist_max = std::max(dist_max, dist_pt); - } - if (dist_max > scale_(3)) { // is cantilever if the farmost point is larger than 3mm away from base - max_cantilever_dist = std::max(max_cantilever_dist, dist_max); - layer->cantilevers.emplace_back(poly); - BOOST_LOG_TRIVIAL(debug) << "found a cantilever cluster. layer_nr=" << layer_nr << dist_max; - has_cantilever = true; + // check cantilever + // lower_layer_offset may be very small, so we need to do max and then add 0.1 + lower_layer_offseted = offset_ex(lower_layer_offseted, scale_(std::max(extrusion_width - lower_layer_offset, 0.) + 0.1)); + for (ExPolygon& poly : ts_layer->overhang_areas) { + auto cluster_boundary_ex = intersection_ex(poly, lower_layer_offseted); + Polygons cluster_boundary = to_polygons(cluster_boundary_ex); + if (cluster_boundary.empty()) continue; + double dist_max = 0; + for (auto& pt : poly.contour.points) { + double dist_pt = std::numeric_limits::max(); + for (auto& ply : cluster_boundary) { + double d = ply.distance_to(pt); + dist_pt = std::min(dist_pt, d); } + dist_max = std::max(dist_max, dist_pt); + } + if (dist_max > scale_(3)) { // is cantilever if the farmost point is larger than 3mm away from base + max_cantilever_dist = std::max(max_cantilever_dist, dist_max); + layer->cantilevers.emplace_back(poly); + BOOST_LOG_TRIVIAL(debug) << "found a cantilever cluster. layer_nr=" << layer_nr << dist_max; + has_cantilever = true; } } } @@ -861,7 +873,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) return; // check if the sharp tails should be extended higher - if (is_auto(stype) && g_config_support_sharp_tails) { + if (is_auto(stype) && config_detect_sharp_tails) { for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { if (m_object->print()->canceled()) break; @@ -945,21 +957,6 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } } - // group overhang clusters - for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { - if (m_object->print()->canceled()) - break; - if(layer_nr+m_raft_layers>=m_object->support_layer_count()) - break; - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); - Layer* layer = m_object->get_layer(layer_nr); - for (auto& overhang : ts_layer->overhang_areas) { - OverhangCluster* cluster = find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled); - if (overlaps({ overhang },layer->cantilevers)) - cluster->is_cantilever = true; - } - } - auto enforcers = m_object->slice_support_enforcers(); auto blockers = m_object->slice_support_blockers(); m_object->project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers); @@ -967,6 +964,21 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) if (is_auto(stype) && config_remove_small_overhangs) { if (blockers.size() < m_object->layer_count()) blockers.resize(m_object->layer_count()); + // group overhang clusters + for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { + if (m_object->print()->canceled()) + break; + if (layer_nr + m_raft_layers >= m_object->support_layer_count()) + break; + SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); + Layer* layer = m_object->get_layer(layer_nr); + for (auto& overhang : ts_layer->overhang_areas) { + OverhangCluster* cluster = find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled); + if (overlaps({ overhang }, layer->cantilevers)) + cluster->is_cantilever = true; + } + } + // remove small overhangs for (auto& cluster : overhangClusters) { // 3. check whether the small overhang is sharp tail cluster.is_sharp_tail = false; @@ -1403,7 +1415,7 @@ void TreeSupport::generate_toolpaths() if (m_object->print()->canceled()) break; - m_object->print()->set_status(70, (boost::format(_u8L("Support: generate toolpath at layer %d")) % layer_id).str()); + //m_object->print()->set_status(70, (boost::format(_u8L("Support: generate toolpath at layer %d")) % layer_id).str()); SupportLayer* ts_layer = m_object->get_support_layer(layer_id); Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); @@ -1583,7 +1595,7 @@ void TreeSupport::generate() // Generate overhang areas profiler.stage_start(STAGE_DETECT_OVERHANGS); - m_object->print()->set_status(55, _u8L("Support: detect overhangs")); + m_object->print()->set_status(55, _u8L("Generating supports")); detect_overhangs(); profiler.stage_finish(STAGE_DETECT_OVERHANGS); @@ -1604,7 +1616,7 @@ void TreeSupport::generate() //Drop nodes to lower layers. profiler.stage_start(STAGE_DROP_DOWN_NODES); - m_object->print()->set_status(60, _u8L("Support: propagate branches")); + m_object->print()->set_status(60, _u8L("Generating supports")); drop_nodes(contact_nodes); profiler.stage_finish(STAGE_DROP_DOWN_NODES); @@ -1612,22 +1624,14 @@ void TreeSupport::generate() //Generate support areas. profiler.stage_start(STAGE_DRAW_CIRCLES); - m_object->print()->set_status(65, _u8L("Support: draw polygons")); + m_object->print()->set_status(65, _u8L("Generating supports")); draw_circles(contact_nodes); profiler.stage_finish(STAGE_DRAW_CIRCLES); - for (auto& layer : contact_nodes) - { - for (SupportNode* p_node : layer) - { - delete p_node; - } - layer.clear(); - } - contact_nodes.clear(); + profiler.stage_start(STAGE_GENERATE_TOOLPATHS); - m_object->print()->set_status(69, _u8L("Support: generate toolpath")); + m_object->print()->set_status(69, _u8L("Generating supports")); generate_toolpaths(); profiler.stage_finish(STAGE_GENERATE_TOOLPATHS); @@ -1677,6 +1681,13 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_to return radius; } +coordf_t TreeSupport::get_radius(const SupportNode* node, coordf_t base_radius) +{ + if (node->radius == 0) + node->radius = calc_branch_radius(base_radius, node->dist_mm_to_top, node->diameter_angle_scale_factor); + return node->radius; +} + ExPolygons TreeSupport::get_avoidance(coordf_t radius, size_t obj_layer_nr) { #if USE_SUPPORT_3D @@ -2017,7 +2028,7 @@ void TreeSupport::draw_circles(const std::vector>& con } - m_object->print()->set_status(65, (boost::format( _u8L("Support: generate polygons at layer %d")) % layer_nr).str()); + //m_object->print()->set_status(65, (boost::format( _u8L("Support: generate polygons at layer %d")) % layer_nr).str()); // join roof segments double contact_dist_scaled = scale_(0.5);// scale_(m_slicing_params.gap_support_object); @@ -2204,7 +2215,7 @@ void TreeSupport::draw_circles(const std::vector>& con std::map> holePropagationInfos; for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { if (print->canceled()) break; - m_object->print()->set_status(66, (boost::format(_u8L("Support: fix holes at layer %d")) % layer_nr).str()); + //m_object->print()->set_status(66, (boost::format(_u8L("Support: fix holes at layer %d")) % layer_nr).str()); const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); @@ -2338,14 +2349,10 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod const auto nozzle_diameter = m_object->print()->config().nozzle_diameter.get_at(m_object->config().support_interface_filament-1); const auto support_line_width = config.support_line_width.get_abs_value(nozzle_diameter); - auto get_branch_angle = [this,&config](coordf_t radius) { - if (config.tree_support_branch_angle.value < 30.0) return config.tree_support_branch_angle.value; - return (radius - MIN_BRANCH_RADIUS) / (MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS) * (config.tree_support_branch_angle.value - 30.0) + 30.0; - }; auto get_max_move_dist = [this, &config, branch_radius, tip_layers, diameter_angle_scale_factor, wall_count, support_extrusion_width, support_line_width](const SupportNode *node, int power = 1) { double move_dist = node->max_move_dist; if (node->max_move_dist == 0) { - if (node->radius == 0) node->radius = calc_branch_radius(branch_radius, node->dist_mm_to_top, diameter_angle_scale_factor); + node->radius = get_radius(node, branch_radius); double angle = config.tree_support_branch_angle.value; if (angle > 30.0 && node->radius > MIN_BRANCH_RADIUS) angle = (node->radius - MIN_BRANCH_RADIUS) / (MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS) * (config.tree_support_branch_angle.value - 30.0) + 30.0; @@ -2362,7 +2369,47 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod std::vector &layer_heights = m_ts_data->layer_heights; if (layer_heights.empty()) return; - std::unordered_set to_free_node_set; + // precalculate avoidance of all possible radii. + // This will cause computing more (radius, layer_nr) pairs, but it's worth to do so since we are doning this in parallel. + if (1) { + typedef std::chrono::high_resolution_clock clock_; + typedef std::chrono::duration > second_; + std::chrono::time_point t0{ clock_::now() }; + + // get all the possible radiis + std::vector > all_layer_radius(contact_nodes.size()); + std::vector> all_layer_node_dist(contact_nodes.size()); + for (size_t layer_nr = contact_nodes.size() - 1; layer_nr > 0; layer_nr--) { + if (layer_heights[layer_nr].height < EPSILON) continue; + auto& layer_radius = all_layer_radius[layer_nr]; + auto& layer_node_dist = all_layer_node_dist[layer_nr]; + for (auto* p_node : contact_nodes[layer_nr]) { + layer_node_dist.emplace(p_node->dist_mm_to_top); + } + size_t layer_nr_next = layer_heights[layer_nr].next_layer_nr; + if (layer_nr_next <= contact_nodes.size() - 1 && layer_nr_next > 0) { + for (auto node_dist : layer_node_dist) + all_layer_node_dist[layer_nr_next].emplace(node_dist + layer_heights[layer_nr].height); + } + for (auto node_dist : layer_node_dist) { + layer_radius.emplace(calc_branch_radius(branch_radius, node_dist, diameter_angle_scale_factor)); + } + } + // parallel pre-compute avoidance + tbb::parallel_for(tbb::blocked_range(0, contact_nodes.size() - 1), [&](const tbb::blocked_range &range) { + for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { + for (auto node_radius : all_layer_radius[layer_nr]) { + m_ts_data->get_avoidance(node_radius, layer_nr); + get_collision(m_ts_data->m_xy_distance, layer_nr); + } + } + }); + + double duration{ std::chrono::duration_cast(clock_::now() - t0).count() }; + BOOST_LOG_TRIVIAL(debug) << "finish pre calculate_avoidance. before m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size() + << ", takes " << duration << " secs."; + } + m_spanning_trees.resize(contact_nodes.size()); //m_mst_line_x_layer_contour_caches.resize(contact_nodes.size()); @@ -2382,11 +2429,11 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod std::deque> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches. const Layer* ts_layer = m_object->get_support_layer(layer_nr); - m_object->print()->set_status(60, (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str()); + m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating supports"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str()); Polygons layer_contours = std::move(m_ts_data->get_contours_with_holes(layer_nr)); //std::unordered_map& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr]; - std::unordered_map mst_line_x_layer_contour_cache; + tbb::concurrent_unordered_map mst_line_x_layer_contour_cache; auto is_line_cut_by_contour = [&mst_line_x_layer_contour_cache,&layer_contours](Point a, Point b) { auto iter = mst_line_x_layer_contour_cache.find({ a, b }); @@ -2408,7 +2455,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod }; //Group together all nodes for each part. - const ExPolygons& parts = m_ts_data->m_layer_outlines_below[layer_nr];// m_ts_data->get_avoidance(0, layer_nr); + const ExPolygons& parts = m_ts_data->m_layer_outlines_below[layer_nr]; std::vector> nodes_per_part(1 + parts.size()); //All nodes that aren't inside a part get grouped together in the 0th part. for (SupportNode* p_node : layer_contact_nodes) { @@ -2480,23 +2527,24 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod #endif for (size_t group_index = 0; group_index < nodes_per_part.size(); group_index++) { + auto& nodes_this_part = nodes_per_part[group_index]; const MinimumSpanningTree& mst = spanning_trees[group_index]; //In the first pass, merge all nodes that are close together. - std::unordered_set to_delete; - for (const std::pair& entry : nodes_per_part[group_index]) - { + tbb::concurrent_unordered_set to_delete; + std::vector> nodes_vec(nodes_this_part.begin(), nodes_this_part.end()); + tbb::parallel_for_each(nodes_vec.begin(), nodes_vec.end(), [&](const std::pair& entry) { SupportNode* p_node = entry.second; SupportNode& node = *p_node; if (to_delete.find(p_node) != to_delete.end()) { - continue; //Delete this node (don't create a new node for it on the next layer). + return; //Delete this node (don't create a new node for it on the next layer). } const std::vector& neighbours = mst.adjacent_nodes(node.position); if (node.type == ePolygon) { // Remove all circle neighbours that are completely inside the polygon and merge them into this node. for (const Point &neighbour : neighbours) { - SupportNode * neighbour_node = nodes_per_part[group_index][neighbour]; - if (neighbour_node->type == ePolygon) continue; + SupportNode * neighbour_node = nodes_this_part[neighbour]; + if(neighbour_node->type==ePolygon) return; coord_t neighbour_radius = scale_(neighbour_node->radius); Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius), pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0); @@ -2512,14 +2560,12 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } } else if (neighbours.size() == 1 && vsize2_with_unscale(neighbours[0] - node.position) < max_move_distance2 && mst.adjacent_nodes(neighbours[0]).size() == 1 && - nodes_per_part[group_index][neighbours[0]]->type!=ePolygon) // We have just two nodes left, and they're very close, and the only neighbor is not ePolygon + nodes_this_part[neighbours[0]]->type!=ePolygon) // We have just two nodes left, and they're very close, and the only neighbor is not ePolygon { //Insert a completely new node and let both original nodes fade. Point next_position = (node.position + neighbours[0]) / 2; //Average position of the two nodes. - - const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor); - - auto avoid_layer = get_avoidance(branch_radius_node, layer_nr_next); + coordf_t next_radius = calc_branch_radius(branch_radius, node.dist_mm_to_top+height_next, diameter_angle_scale_factor); + auto avoid_layer = get_avoidance(next_radius, layer_nr_next); if (group_index == 0) { //Avoid collisions. @@ -2527,23 +2573,23 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod move_out_expolys(avoid_layer, next_position, radius_sample_resolution + EPSILON, max_move_between_samples); } - SupportNode* neighbour = nodes_per_part[group_index][neighbours[0]]; - SupportNode* node_; + SupportNode* neighbour = nodes_this_part[neighbours[0]]; + SupportNode* node_parent; if (p_node->parent && neighbour->parent) - node_ = (node.dist_mm_to_top >= neighbour->dist_mm_to_top) ? p_node : neighbour; + node_parent = (node.dist_mm_to_top >= neighbour->dist_mm_to_top) ? p_node : neighbour; else - node_ = p_node->parent ? p_node : neighbour; + node_parent = p_node->parent ? p_node : neighbour; // Make sure the next pass doesn't drop down either of these (since that already happened). - node_->merged_neighbours.push_front(node_ == p_node ? neighbour : p_node); + node_parent->merged_neighbours.push_front(node_parent == p_node ? neighbour : p_node); const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_position); - SupportNode * next_node = m_ts_data->create_node(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_, - print_z_next, height_next); + SupportNode* next_node = m_ts_data->create_node(next_position, node_parent->distance_to_top + 1, layer_nr_next, node_parent->support_roof_layers_below - 1, to_buildplate, node_parent, + print_z_next, height_next); get_max_move_dist(next_node); + m_ts_data->m_mutex.lock(); contact_nodes[layer_nr_next].push_back(next_node); - - to_delete.insert(neighbour); to_delete.insert(p_node); + m_ts_data->m_mutex.unlock(); } else if (neighbours.size() > 1) //Don't merge leaf nodes because we would then incur movement greater than the maximum move distance. { @@ -2552,7 +2598,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod { if (vsize2_with_unscale(neighbour - node.position) < /*max_move_distance2*/get_max_move_dist(&node,2)) { - SupportNode* neighbour_node = nodes_per_part[group_index][neighbour]; + SupportNode* neighbour_node = nodes_this_part[neighbour]; if (neighbour_node->type == ePolygon) continue; // only allow bigger node to merge smaller nodes. See STUDIO-6326 if(node.dist_mm_to_top < neighbour_node->dist_mm_to_top) continue; @@ -2565,15 +2611,16 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } } } + ); //In the second pass, move all middle nodes. - for (const std::pair& entry : nodes_per_part[group_index]) - { + tbb::parallel_for_each(nodes_vec.begin(), nodes_vec.end(), [&](const std::pair& entry) { + SupportNode* p_node = entry.second; const SupportNode& node = *p_node; if (to_delete.find(p_node) != to_delete.end()) { - continue; + return; } if (node.type == ePolygon) { // polygon node do not merge or move @@ -2595,15 +2642,19 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod p_node, print_z_next, height_next); next_node->max_move_dist = 0; next_node->overhang = std::move(overhangs_next[index_biggest]); + m_ts_data->m_mutex.lock(); contact_nodes[layer_nr_next].emplace_back(next_node); + m_ts_data->m_mutex.unlock(); + } - continue; + return; } //If the branch falls completely inside a collision area (the entire branch would be removed by the X/Y offset), delete it. if (group_index > 0 && is_inside_ex(get_collision(m_ts_data->m_xy_distance, layer_nr), node.position)) { - const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor); + std::scoped_lock lock(m_ts_data->m_mutex); + const coordf_t branch_radius_node = get_radius(p_node, branch_radius); Point to_outside = projection_onto(get_collision(m_ts_data->m_xy_distance, layer_nr), node.position); double dist2_to_outside = vsize2_with_unscale(node.position - to_outside); if (dist2_to_outside >= branch_radius_node * branch_radius_node) //Too far inside. @@ -2616,21 +2667,21 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod to_delete.insert(p_node); p_node->valid = false; } - continue; + return; } // if the link between parent and current is cut by contours, mark current as bottom contact node if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false) { to_delete.insert(p_node); p_node->valid = false; - continue; + return; } } Point next_layer_vertex = node.position; Point move_to_neighbor_center; std::vector moves; std::vector weights; - const std::vector neighbours = mst.adjacent_nodes(node.position); + const std::vector& neighbours = mst.adjacent_nodes(node.position); // 1. do not merge neighbors under 5mm // 2. Only merge node with single neighbor in distance between [max_move_distance, 10mm/layer_height] float dist2_to_first_neighbor = neighbours.empty() ? 0 : vsize2_with_unscale(neighbours[0] - node.position); @@ -2641,7 +2692,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod Point sum_direction(0, 0); for (const Point &neighbour : neighbours) { // do not move to the neighbor to be deleted - SupportNode *neighbour_node = nodes_per_part[group_index][neighbour]; + SupportNode *neighbour_node = nodes_this_part[neighbour]; if (to_delete.find(neighbour_node) != to_delete.end()) continue; Point direction = neighbour - node.position; @@ -2672,14 +2723,14 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod } } - const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top/*+node.print_z*/, diameter_angle_scale_factor); #ifdef SUPPORT_TREE_DEBUG_TO_SVG if (node.position(1) > max_y) { max_y = node.position(1); - branch_radius_temp = branch_radius_node; + branch_radius_temp = get_radius(p_node, branch_radius); } #endif - auto avoidance_next = get_avoidance(branch_radius_node, layer_nr_next); + coordf_t next_radius = calc_branch_radius(branch_radius, node.dist_mm_to_top + height_next, diameter_angle_scale_factor); + auto avoidance_next = get_avoidance(next_radius, layer_nr_next); Point to_outside = projection_onto(avoidance_next, node.position); Point direction_to_outer = to_outside - node.position; @@ -2693,7 +2744,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod Point candidate_vertex = node.position; const coordf_t max_move_between_samples = max_move_distance + radius_sample_resolution + EPSILON; // 100 micron extra for rounding errors. // use get_collision instead of get_avoidance here (See STUDIO-4252) - bool is_outside = move_out_expolys(get_collision(branch_radius_node,layer_nr_next), candidate_vertex, max_move_between_samples, max_move_between_samples); + bool is_outside = move_out_expolys(get_collision(next_radius,layer_nr_next), candidate_vertex, max_move_between_samples, max_move_between_samples); if (is_outside) { direction_to_outer = candidate_vertex - node.position; dist2_to_outer = vsize2_with_unscale(direction_to_outer); @@ -2733,8 +2784,11 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod SupportNode * next_node = m_ts_data->create_node(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); get_max_move_dist(next_node); + m_ts_data->m_mutex.lock(); contact_nodes[layer_nr_next].push_back(next_node); + m_ts_data->m_mutex.unlock(); } + ); } #ifdef SUPPORT_TREE_DEBUG_TO_SVG @@ -2833,8 +2887,6 @@ void TreeSupport::smooth_nodes(std::vector> &contact_ const int iterations = 100; for (size_t k = 0; k < iterations; k++) { for (size_t i = 1; i < pts.size() - 1; i++) { - size_t i2 = i >= 2 ? i - 2 : 0; - size_t i3 = i < pts.size() - 2 ? i + 2 : pts.size() - 1; Point pt = ( pts[i - 1] + pts[i] + pts[i + 1] ) / 3; pts1[i] = pt; radii1[i] = (radii[i - 1] + radii[i] + radii[i + 1] ) / 3; @@ -3325,7 +3377,7 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke { const auto& radius = key.radius; const auto& layer_nr = key.layer_nr; - BOOST_LOG_TRIVIAL(debug) << "calculate_avoidance on radius=" << radius << ", layer=" << layer_nr<<", recursion="< +#include #include "TreeSupport3D.hpp" #include @@ -629,18 +630,15 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p #define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) { - // overhangs are already detected - if (m_object->support_layer_count() >= m_object->layer_count()) - return; - bool tree_support_enable = m_object_config->enable_support.value && is_tree(m_object_config->support_type.value); + if (!tree_support_enable && !check_support_necessity) { + BOOST_LOG_TRIVIAL(info) << "Tree support is disabled."; + return; + } // Clear and create Tree Support Layers m_object->clear_support_layers(); m_object->clear_tree_support_preview_cache(); - create_tree_support_layers(); - if (!tree_support_enable && !check_support_necessity) - return; const PrintObjectConfig& config = m_object->config(); SupportType stype = support_type; @@ -764,6 +762,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) Layer* layer = m_object->get_layer(layer_nr); // Filter out areas whose diameter that is smaller than extrusion_width, but we don't want to lose any details. layer->lslices_extrudable = intersection_ex(layer->lslices, offset2_ex(layer->lslices, -extrusion_width_scaled / 2, extrusion_width_scaled)); + layer->loverhangs.clear(); } }); @@ -771,6 +770,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) typedef std::chrono::duration > second_; std::chrono::time_point t0{ clock_::now() }; // main part of overhang detection can be parallel + tbb::concurrent_vector overhangs_all_layers(m_object->layer_count()); tbb::parallel_for(tbb::blocked_range(0, m_object->layer_count()), [&](const tbb::blocked_range& range) { for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { @@ -802,12 +802,11 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) ExPolygons& lower_polys = lower_layer->lslices_extrudable; // normal overhang - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); - ts_layer->overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted)); + overhangs_all_layers[layer_nr] = std::move(diff_ex(curr_polys, lower_layer_offseted)); double duration{ std::chrono::duration_cast(clock_::now() - t0).count() }; - if (duration > 30 || ts_layer->overhang_areas.size() > 100) { + if (duration > 30 || overhangs_all_layers[layer_nr].size() > 100) { BOOST_LOG_TRIVIAL(info) << "detect_overhangs takes more than 30 secs, skip cantilever and sharp tails detection: layer_nr=" << layer_nr << " duration=" << duration; config_detect_sharp_tails = false; config_remove_small_overhangs = false; @@ -825,18 +824,14 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } if (is_sharp_tail) { - ExPolygons overhang = diff_ex({ expoly }, lower_polys); layer->sharp_tails.push_back(expoly); - layer->sharp_tails_height.push_back(layer->height); - append(ts_layer->overhang_areas, overhang); + layer->sharp_tails_height.push_back(0); - if (!overhang.empty()) { - has_sharp_tails = true; + has_sharp_tails = true; #ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG::export_expolygons(debug_out_path("sharp_tail_orig_%.02f.svg", layer->print_z), { expoly }); + SVG::export_expolygons(debug_out_path("sharp_tail_orig_%.02f.svg", layer->print_z), { expoly }); #endif - } - } + } } } @@ -844,7 +839,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) // check cantilever // lower_layer_offset may be very small, so we need to do max and then add 0.1 lower_layer_offseted = offset_ex(lower_layer_offseted, scale_(std::max(extrusion_width - lower_layer_offset, 0.) + 0.1)); - for (ExPolygon& poly : ts_layer->overhang_areas) { + for (ExPolygon& poly : overhangs_all_layers[layer_nr]) { auto cluster_boundary_ex = intersection_ex(poly, lower_layer_offseted); Polygons cluster_boundary = to_polygons(cluster_boundary_ex); if (cluster_boundary.empty()) continue; @@ -879,7 +874,6 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) break; Layer* layer = m_object->get_layer(layer_nr); - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); Layer* lower_layer = layer->lower_layer; if (!lower_layer) continue; @@ -941,43 +935,32 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } while (0); if (is_sharp_tail) { - ExPolygons overhang = diff_ex({ expoly }, lower_layer->lslices); layer->sharp_tails.push_back(expoly); layer->sharp_tails_height.push_back( accum_height); - append(ts_layer->overhang_areas, overhang); - - if (!overhang.empty()) - has_sharp_tails = true; -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG::export_expolygons(debug_out_path("sharp_tail_%.02f.svg", layer->print_z), overhang); -#endif } } } } - + + // group overhang clusters + for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { + if (m_object->print()->canceled()) + break; + Layer* layer = m_object->get_layer(layer_nr); + for (auto& overhang : overhangs_all_layers[layer_nr]) { + OverhangCluster* cluster = find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled); + if (overlaps({ overhang }, layer->cantilevers)) + cluster->is_cantilever = true; + } + } + auto enforcers = m_object->slice_support_enforcers(); auto blockers = m_object->slice_support_blockers(); m_object->project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers); m_object->project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers); + if (is_auto(stype) && config_remove_small_overhangs) { - if (blockers.size() < m_object->layer_count()) - blockers.resize(m_object->layer_count()); - // group overhang clusters - for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { - if (m_object->print()->canceled()) - break; - if (layer_nr + m_raft_layers >= m_object->support_layer_count()) - break; - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); - Layer* layer = m_object->get_layer(layer_nr); - for (auto& overhang : ts_layer->overhang_areas) { - OverhangCluster* cluster = find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled); - if (overlaps({ overhang }, layer->cantilevers)) - cluster->is_cantilever = true; - } - } // remove small overhangs for (auto& cluster : overhangClusters) { // 3. check whether the small overhang is sharp tail @@ -1010,51 +993,66 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) { cluster.merged_poly,{"overhang", "blue", 0.5} }, { cluster.is_cantilever? layer1->cantilevers: offset_ex(cluster.merged_poly, -1 * extrusion_width_scaled), {cluster.is_cantilever ? "cantilever":"erode1","green",0.5}} }); #endif - - if (!cluster.is_small_overhang) - continue; - - for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) { - int layer_nr = it->first; - auto p_overhang = it->second; - blockers[layer_nr].push_back(p_overhang->contour); - } } } - has_overhangs = false; + for (auto& cluster : overhangClusters) { + if (cluster.is_small_overhang) continue; + // collect overhangs that's not small overhangs + for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) { + int layer_nr = it->first; + auto p_overhang = it->second; + m_object->get_layer(layer_nr)->loverhangs.emplace_back(*p_overhang); + } + } + + int layers_with_overhangs = 0; for (int layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { if (m_object->print()->canceled()) break; - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); auto layer = m_object->get_layer(layer_nr); auto lower_layer = layer->lower_layer; - if (support_critical_regions_only && is_auto(stype)) { - ts_layer->overhang_areas.clear(); - if (lower_layer == nullptr) - ts_layer->overhang_areas = layer->sharp_tails; - else - ts_layer->overhang_areas = diff_ex(layer->sharp_tails, lower_layer->lslices); - append(ts_layer->overhang_areas, layer->cantilevers); + // add support for every 1mm height for sharp tails + ExPolygons sharp_tail_overhangs; + if (lower_layer == nullptr) + sharp_tail_overhangs = layer->sharp_tails; + else { + ExPolygons lower_layer_expanded = offset_ex(lower_layer->lslices_extrudable, SCALED_RESOLUTION); + for (size_t i = 0; i < layer->sharp_tails_height.size();i++) { + ExPolygons areas = diff_clipped({ layer->sharp_tails[i]}, lower_layer_expanded); + float accum_height = layer->sharp_tails_height[i]; + if (!areas.empty() && int(accum_height * 10) % 5 == 0) { + append(sharp_tail_overhangs, areas); + has_sharp_tails = true; +#ifdef SUPPORT_TREE_DEBUG_TO_SVG + SVG::export_expolygons(debug_out_path("sharp_tail_%.02f.svg", layer->print_z), areas); +#endif + } + } + } + + if (support_critical_regions_only && is_auto(stype)) { + layer->loverhangs.clear(); // remove oridinary overhangs, only keep cantilevers and sharp tails (added later) + append(layer->loverhangs, layer->cantilevers); } if (layer_nr < blockers.size()) { Polygons& blocker = blockers[layer_nr]; // Arthur: union_ is a must because after mirroring, the blocker polygons are in left-hand coordinates, ie clockwise, // which are not valid polygons, and will be removed by offset_ex. union_ can make these polygons right. - ts_layer->overhang_areas = diff_ex(ts_layer->overhang_areas, offset_ex(union_(blocker), scale_(radius_sample_resolution))); + layer->loverhangs = diff_ex(layer->loverhangs, offset_ex(union_(blocker), scale_(radius_sample_resolution))); } - if (max_bridge_length > 0 && ts_layer->overhang_areas.size() > 0 && lower_layer) { + if (max_bridge_length > 0 && layer->loverhangs.size() > 0 && lower_layer) { // do not break bridge for normal part in TreeHybrid, nor Tree Strong - bool break_bridge = !(m_support_params.support_style == smsTreeHybrid && area(ts_layer->overhang_areas) > m_support_params.thresh_big_overhang) + bool break_bridge = !(m_support_params.support_style == smsTreeHybrid && area(layer->loverhangs) > m_support_params.thresh_big_overhang) && !(m_support_params.support_style==smsTreeStrong); - m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &ts_layer->overhang_areas, max_bridge_length, break_bridge); + m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &layer->loverhangs, max_bridge_length, break_bridge); } - int nDetected = ts_layer->overhang_areas.size(); + int nDetected = layer->loverhangs.size(); // enforcers now follow same logic as normal support. See STUDIO-3692 if (layer_nr < enforcers.size() && lower_layer) { float no_interface_offset = std::accumulate(layer->regions().begin(), layer->regions().end(), FLT_MAX, @@ -1065,31 +1063,36 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) ExPolygons enforcer_polygons = diff_ex(intersection_ex(layer->lslices, enforcer), // Inflate just a tiny bit to avoid intersection of the overhang areas with the object. expand(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - append(ts_layer->overhang_areas, enforcer_polygons); + append(layer->loverhangs, enforcer_polygons); } } - int nEnforced = ts_layer->overhang_areas.size(); + int nEnforced = layer->loverhangs.size(); + // add sharp tail overhangs + append(layer->loverhangs, sharp_tail_overhangs); + // fill overhang_types - for (size_t i = 0; i < ts_layer->overhang_areas.size(); i++) - ts_layer->overhang_types.emplace(&ts_layer->overhang_areas[i], i < nDetected ? SupportLayer::Detected : - i < nEnforced ? SupportLayer::Enforced : SupportLayer::SharpTail); + for (size_t i = 0; i < layer->loverhangs.size(); i++) + overhang_types.emplace(&layer->loverhangs[i], i < nDetected ? OverhangType::Detected : + i < nEnforced ? OverhangType::Enforced : OverhangType::SharpTail); - if (!ts_layer->overhang_areas.empty()) { - has_overhangs = true; + if (!layer->loverhangs.empty()) { + layers_with_overhangs++; m_highest_overhang_layer = std::max(m_highest_overhang_layer, size_t(layer_nr)); } if (!layer->cantilevers.empty()) has_cantilever = true; } + BOOST_LOG_TRIVIAL(info) << "Tree support overhang detection done. " << layers_with_overhangs << " layers with overhangs."; + #ifdef SUPPORT_TREE_DEBUG_TO_SVG - for (const SupportLayer* layer : m_object->support_layers()) { - if (layer->overhang_areas.empty() && (blockers.size()<=layer->id() || blockers[layer->id()].empty())) + for (const Layer* layer : m_object->layers()) { + if (layer->loverhangs.empty() && (blockers.size()<=layer->id() || blockers[layer->id()].empty())) continue; - SVG::export_expolygons(debug_out_path("overhang_areas_%.2f.svg", layer->print_z), { + SVG::export_expolygons(debug_out_path("overhang_areas_%d_%.2f.svg",layer->id(), layer->print_z), { { m_object->get_layer(layer->id())->lslices, {"lslices","yellow",0.5} }, - { layer->overhang_areas, {"overhang","red",0.5} } + { layer->loverhangs, {"overhang","red",0.5} } }); if (enforcers.size() > layer->id()) { @@ -1595,12 +1598,11 @@ void TreeSupport::generate() // Generate overhang areas profiler.stage_start(STAGE_DETECT_OVERHANGS); - m_object->print()->set_status(55, _u8L("Generating supports")); + m_object->print()->set_status(55, _u8L("Generating support")); detect_overhangs(); profiler.stage_finish(STAGE_DETECT_OVERHANGS); - if (!has_overhangs) return; - + create_tree_support_layers(); m_ts_data = m_object->alloc_tree_support_preview_cache(); m_ts_data->is_slim = is_slim; @@ -1610,13 +1612,13 @@ void TreeSupport::generate() std::vector move_bounds(m_highest_overhang_layer + 1); profiler.stage_start(STAGE_GENERATE_CONTACT_NODES); m_object->print()->set_status(56, _u8L("Support: generate contact points")); - generate_contact_points(contact_nodes, move_bounds); + generate_contact_points(contact_nodes); profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES); //Drop nodes to lower layers. profiler.stage_start(STAGE_DROP_DOWN_NODES); - m_object->print()->set_status(60, _u8L("Generating supports")); + m_object->print()->set_status(60, _u8L("Generating support")); drop_nodes(contact_nodes); profiler.stage_finish(STAGE_DROP_DOWN_NODES); @@ -1624,14 +1626,14 @@ void TreeSupport::generate() //Generate support areas. profiler.stage_start(STAGE_DRAW_CIRCLES); - m_object->print()->set_status(65, _u8L("Generating supports")); + m_object->print()->set_status(65, _u8L("Generating support")); draw_circles(contact_nodes); profiler.stage_finish(STAGE_DRAW_CIRCLES); profiler.stage_start(STAGE_GENERATE_TOOLPATHS); - m_object->print()->set_status(69, _u8L("Generating supports")); + m_object->print()->set_status(70, _u8L("Generating support")); generate_toolpaths(); profiler.stage_finish(STAGE_GENERATE_TOOLPATHS); @@ -1922,7 +1924,6 @@ void TreeSupport::draw_circles(const std::vector>& con coordf_t max_layers_above_roof = 0; coordf_t max_layers_above_roof1 = 0; int interface_id = 0; - bool has_polygon_node = false; bool has_circle_node = false; bool need_extra_wall = false; ExPolygons collision_sharp_tails; @@ -1943,6 +1944,7 @@ void TreeSupport::draw_circles(const std::vector>& con BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size(); //Draw the support areas and add the roofs appropriately to the support roof instead of normal areas. ts_layer->lslices.reserve(contact_nodes[layer_nr].size()); + ExPolygons area_poly; // the polygon node area which will be printed as normal support for (const SupportNode* p_node : contact_nodes[layer_nr]) { if (print->canceled()) @@ -1959,9 +1961,9 @@ void TreeSupport::draw_circles(const std::vector>& con else { area = offset_ex({ node.overhang }, scale_(m_ts_data->m_xy_distance)); } - if (node.type == ePolygon) - has_polygon_node = true; area = diff_clipped(area, get_collision(node.is_sharp_tail && node.distance_to_top <= 0)); + if (node.type == ePolygon) + area_poly = area; } else { Polygon circle(branch_circle); @@ -2084,8 +2086,8 @@ void TreeSupport::draw_circles(const std::vector>& con auto &area_groups = ts_layer->area_groups; for (auto& area : ts_layer->base_areas) { area_groups.emplace_back(&area, SupportLayer::BaseType, max_layers_above_base); - area_groups.back().need_infill = has_polygon_node; - // area_groups.back().need_extra_wall = need_extra_wall; + area_groups.back().need_infill = overlaps({ area }, area_poly); + area_groups.back().need_extra_wall = need_extra_wall; } for (auto& area : ts_layer->roof_areas) { area_groups.emplace_back(&area, SupportLayer::RoofType, max_layers_above_roof); @@ -2423,13 +2425,14 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod continue; int layer_nr_next = layer_heights[layer_nr].next_layer_nr; + coordf_t print_z = layer_heights[layer_nr].print_z; coordf_t print_z_next = layer_heights[layer_nr_next].print_z; coordf_t height_next = layer_heights[layer_nr_next].height; std::deque> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches. const Layer* ts_layer = m_object->get_support_layer(layer_nr); - m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating supports"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str()); + m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating support"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str()); Polygons layer_contours = std::move(m_ts_data->get_contours_with_holes(layer_nr)); //std::unordered_map& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr]; @@ -3034,7 +3037,7 @@ std::vector TreeSupport::plan_layer_heights(std::vector>& contact_nodes, const std::vector& move_bounds) +void TreeSupport::generate_contact_points(std::vector>& contact_nodes) { const PrintObjectConfig &config = m_object->config(); const coordf_t point_spread = scale_(config.tree_support_branch_distance.value); @@ -3092,8 +3095,8 @@ void TreeSupport::generate_contact_points(std::vector> // fix bug of generating support for very thin objects if (m_object->layers().size() <= z_distance_top_layers + 1) return; - if (m_object->support_layer_count() <= m_raft_layers) - return; + //if (m_object->support_layer_count() <= m_raft_layers) + // return; int nonempty_layers = 0; tbb::concurrent_vector all_nodes; @@ -3101,10 +3104,10 @@ void TreeSupport::generate_contact_points(std::vector> for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { if (m_object->print()->canceled()) break; - auto ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); Layer* layer = m_object->get_layer(layer_nr); auto& curr_nodes = contact_nodes[layer_nr - 1]; - if (ts_layer->overhang_areas.empty()) continue; + if (layer->loverhangs.empty()) continue; + std::unordered_set already_inserted; auto print_z = m_object->get_layer(layer_nr)->print_z; auto bottom_z = m_object->get_layer(layer_nr)->bottom_z(); @@ -3138,9 +3141,9 @@ void TreeSupport::generate_contact_points(std::vector> return contact_node; }; - for (const auto& area_type : ts_layer->overhang_types) { - const auto& overhang_part = *area_type.first; - is_sharp_tail = area_type.second == SupportLayer::SharpTail; + for (const auto& overhang_part : layer->loverhangs) { + const auto& overhang_type = this->overhang_types[&overhang_part]; + is_sharp_tail = overhang_type == OverhangType::SharpTail; BoundingBox overhang_bounds = get_extents(overhang_part); if (m_support_params.support_style == smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang && !is_sharp_tail) { if (!overlaps({ overhang_part }, m_ts_data->m_layer_outlines_below[layer_nr - 1])) { @@ -3169,55 +3172,42 @@ void TreeSupport::generate_contact_points(std::vector> } } } - auto& move_bounds_layer = move_bounds[layer_nr]; - if (move_bounds_layer.empty()) { - overhang_bounds.inflated(half_overhang_distance); - bool added = false; //Did we add a point this way? + + // add supports along contours + libnest2d::placers::EdgeCache edge_cache(overhang_part); + for (size_t i = 0; i < edge_cache.holeCount() + 1; i++) { + double step = point_spread / (i == 0 ? edge_cache.circumference() : edge_cache.circumference(i - 1)); + double distance = 0; + while (distance < 1) { + auto pt = i == 0 ? edge_cache.coords(distance) : edge_cache.coords(i - 1, distance); + SupportNode* contact_node = insert_point(pt, overhang_part); + distance += step; + } + } + + // don't add inner supports for sharp tails + if (is_sharp_tail) + continue; + + // add inner supports + overhang_bounds.inflated(-radius); + ExPolygons overhang_inner = offset_ex(overhang_part, -radius); for (Point candidate : grid_points) { if (overhang_bounds.contains(candidate)) { // BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported! - bool is_inside = is_inside_ex(overhang_part, candidate); - if (!is_inside) { - constexpr coordf_t distance_inside = 0; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that - // fall between overhang areas on constant surfaces. - is_inside = move_inside_expoly(overhang_part, candidate, distance_inside, half_overhang_distance); - } + bool is_inside = is_inside_ex(overhang_inner, candidate); if (is_inside) { SupportNode* contact_node = insert_point(candidate, overhang_part); - if (contact_node) - added = true; } } } - if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported. - { - auto bbox = overhang_part.contour.bounding_box(); - Points candidates; - if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected) - candidates = { bbox.min, bounding_box_middle(bbox), bbox.max }; - else - candidates = { bounding_box_middle(bbox) }; - for (Point candidate : candidates) { - if (!overhang_part.contains(candidate)) - move_inside_expoly(overhang_part, candidate); - insert_point(candidate, overhang_part); - } - } - } - else { - for (auto& elem : move_bounds_layer) { - SupportNode* contact_node = m_ts_data->create_node(elem.state.result_on_layer, -gap_layers, layer_nr-1, support_roof_layers + 1, elem.state.to_buildplate, - SupportNode::NO_PARENT, print_z, height, z_distance_top); - contact_node->overhang = ExPolygon(elem.influence_area.front()); - curr_nodes.emplace_back(contact_node); - } - } + } if (!curr_nodes.empty()) nonempty_layers++; for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); } #ifdef SUPPORT_TREE_DEBUG_TO_SVG - draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", print_z), ts_layer->overhang_areas, m_ts_data->m_layer_outlines_below[layer_nr], {}, - contact_nodes[layer_nr], contact_nodes[layer_nr - 1], { "overhang","outlines","" }); + draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", print_z), layer->loverhangs,layer->lslices, m_ts_data->m_layer_outlines_below[layer_nr], + contact_nodes[layer_nr], contact_nodes[layer_nr - 1], { "overhang","lslices","outlines_below"}); #endif }} ); // end tbb::parallel_for diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index c1aa6cb284..dc641669da 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -380,7 +380,6 @@ public: int avg_node_per_layer = 0; float nodes_angle = 0; - bool has_overhangs = false; bool has_sharp_tails = false; bool has_cantilever = false; double max_cantilever_dist = 0; @@ -397,6 +396,8 @@ public: */ ExPolygon m_machine_border; + enum OverhangType { Detected = 0, Enforced, SharpTail }; + std::map overhang_types; private: /*! * \brief Generator for model collision, avoidance and internal guide volumes @@ -416,6 +417,7 @@ private: size_t m_highest_overhang_layer = 0; std::vector> m_spanning_trees; std::vector< std::unordered_map> m_mst_line_x_layer_contour_caches; + float DO_NOT_MOVER_UNDER_MM = 0.0; coordf_t MAX_BRANCH_RADIUS = 10.0; coordf_t MIN_BRANCH_RADIUS = 0.5; @@ -476,7 +478,7 @@ private: * \return For each layer, a list of points where the tree should connect * with the model. */ - void generate_contact_points(std::vector>& contact_nodes, const std::vector& move_bounds); + void generate_contact_points(std::vector>& contact_nodes); /*! * \brief Add a node to the next layer. diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 6d3f041207..ece9eea2b5 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -3367,10 +3367,28 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume #endif // SLIC3R_TREESUPPORTS_PROGRESS /* additional_excluded_areas */{} }; - //FIXME generating overhangs just for the furst mesh of the group. + //FIXME generating overhangs just for the first mesh of the group. assert(processing.second.size() == 1); - std::vector overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel); +#if 1 + // use smart overhang detection + std::vector overhangs; + tree_support->detect_overhangs(); + const int num_raft_layers = int(config.raft_layers.size()); + const int num_layers = int(print_object.layer_count()) + num_raft_layers; + overhangs.resize(num_layers); + for (size_t i = 0; i < print_object.layer_count(); i++) { + for (ExPolygon& expoly : print_object.get_layer(i)->loverhangs) { + Polygons polys = to_polygons(expoly); + if (tree_support->overhang_types[&expoly] == TreeSupport::SharpTail) { + polys = offset(to_polygons(expoly), scale_(0.2)); + } + append(overhangs[i + num_raft_layers], polys); + } + } +#else + std::vector overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel); +#endif // ### Precalculate avoidances, collision etc. size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel); bool has_support = num_support_layers > 0; @@ -3443,6 +3461,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume #endif // TREESUPPORT_DEBUG_SVG // ### Propagate the influence areas downwards. This is an inherently serial operation. + print.set_status(60, _L("Generating support")); create_layer_pathing(volumes, config, move_bounds, throw_on_cancel); auto t_path = std::chrono::high_resolution_clock::now(); @@ -3496,6 +3515,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume }); // Don't fill in the tree supports, make them hollow with just a single sheath line. + print.set_status(69, _L("Generating support")); generate_support_toolpaths(print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(), raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); From 5a8612eb6317abac46b5dc8105e010815f8ecfe1 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 29 Mar 2024 20:31:14 +0800 Subject: [PATCH 34/72] FIX: tree supports may generate flying nodes Previous parallelization has a bug where two adjacent nodes may be deleted at the same time. jira: none Change-Id: I99a29dae9f72aa74ed2721eea4421b15eec10732 (cherry picked from commit 91efe67d723652d3f7e4484dd3cdf31638f769a4) (cherry picked from commit 734a70b493b0347870dc955021b0f7055c76f84b) --- src/libslic3r/Support/TreeSupport.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 029fc62107..36293df5ab 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -2547,7 +2547,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod // Remove all circle neighbours that are completely inside the polygon and merge them into this node. for (const Point &neighbour : neighbours) { SupportNode * neighbour_node = nodes_this_part[neighbour]; - if(neighbour_node->type==ePolygon) return; + if(neighbour_node->type==ePolygon) continue; coord_t neighbour_radius = scale_(neighbour_node->radius); Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius), pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0); @@ -2606,10 +2606,15 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod // only allow bigger node to merge smaller nodes. See STUDIO-6326 if(node.dist_mm_to_top < neighbour_node->dist_mm_to_top) continue; - node.merged_neighbours.push_front(neighbour_node); - node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end()); - to_delete.insert(neighbour_node); - neighbour_node->valid = false; + m_ts_data->m_mutex.lock(); + if (to_delete.find(p_node) == to_delete.end()) + { // since we are processing all nodes in parallel, p_node may have been deleted by another thread. In this case, we should not delete neighbour_node. + node.merged_neighbours.push_front(neighbour_node); + node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end()); + to_delete.insert(neighbour_node); + neighbour_node->valid = false; + } + m_ts_data->m_mutex.unlock(); } } } From 6e05d9ef84323f61fec9d0801e95107d90729cde Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 1 Apr 2024 10:03:30 +0800 Subject: [PATCH 35/72] FIX: overhang interface may overlap with object jira: STUDIO-6710 Change-Id: Ie13ec81e07326a2572d698607c03aeb793f119c8 (cherry picked from commit cc49c82793a877c2c4187e6254e4092de9285010) (cherry picked from commit 39839e37325bd93e73ba069a2ea18cb326911c42) --- src/libslic3r/Support/TreeSupport.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 36293df5ab..c4c01c3399 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -783,7 +783,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) Layer* layer = m_object->get_layer(layer_nr); if (layer->lower_layer == nullptr) { - for (auto& slice : layer->lslices) { + for (auto& slice : layer->lslices_extrudable) { auto bbox_size = get_extents(slice).size(); if (!((bbox_size.x() > length_thresh_well_supported && bbox_size.y() > length_thresh_well_supported)) && g_config_support_sharp_tails) { @@ -1057,10 +1057,10 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) if (layer_nr < enforcers.size() && lower_layer) { float no_interface_offset = std::accumulate(layer->regions().begin(), layer->regions().end(), FLT_MAX, [](float acc, const LayerRegion* layerm) { return std::min(acc, float(layerm->flow(frExternalPerimeter).scaled_width())); }); - Polygons lower_layer_polygons = (layer_nr == 0) ? Polygons() : to_polygons(lower_layer->lslices); + Polygons lower_layer_polygons = (layer_nr == 0) ? Polygons() : to_polygons(lower_layer->lslices_extrudable); Polygons& enforcer = enforcers[layer_nr]; if (!enforcer.empty()) { - ExPolygons enforcer_polygons = diff_ex(intersection_ex(layer->lslices, enforcer), + ExPolygons enforcer_polygons = diff_ex(intersection_ex(layer->lslices_extrudable, enforcer), // Inflate just a tiny bit to avoid intersection of the overhang areas with the object. expand(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); append(layer->loverhangs, enforcer_polygons); @@ -1091,21 +1091,21 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) continue; SVG::export_expolygons(debug_out_path("overhang_areas_%d_%.2f.svg",layer->id(), layer->print_z), { - { m_object->get_layer(layer->id())->lslices, {"lslices","yellow",0.5} }, + { m_object->get_layer(layer->id())->lslices_extrudable, {"lslices_extrudable","yellow",0.5} }, { layer->loverhangs, {"overhang","red",0.5} } }); if (enforcers.size() > layer->id()) { SVG svg(format("SVG/enforcer_%s.svg", layer->print_z), m_object->bounding_box()); if (svg.is_opened()) { - svg.draw_outline(m_object->get_layer(layer->id())->lslices, "yellow"); + svg.draw_outline(m_object->get_layer(layer->id())->lslices_extrudable, "yellow"); svg.draw(enforcers[layer->id()], "red"); } } if (blockers.size() > layer->id()) { SVG svg(format("SVG/blocker_%s.svg", layer->print_z), m_object->bounding_box()); if (svg.is_opened()) { - svg.draw_outline(m_object->get_layer(layer->id())->lslices, "yellow"); + svg.draw_outline(m_object->get_layer(layer->id())->lslices_extrudable, "yellow"); svg.draw(blockers[layer->id()], "red"); } } @@ -1994,7 +1994,6 @@ void TreeSupport::draw_circles(const std::vector>& con if(!tmp.empty()) circle = tmp[0]; } - area = avoid_object_remove_extra_small_parts(ExPolygon(circle), get_collision(node.is_sharp_tail && node.distance_to_top <= 0)); // merge overhang to get a smoother interface surface // Do not merge when buildplate_only is on, because some underneath nodes may have been deleted. if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only && !node.is_sharp_tail) { From 36f2df3339fa1ce37b65a39e7dfcb3f401c38ac7 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 12 Apr 2024 19:46:04 +0800 Subject: [PATCH 36/72] FIX: blockers not working for sharp tails of tree support also change default style to tree organic jira: STUDIO-6801 Change-Id: Iab1d8c6117139c9a7a4c1fa71de0a13bcb356dd5 (cherry picked from commit d2c4efad58f16b23bef49bd47d3b70bf322d6f55) (cherry picked from commit cd9305e3e061b67903ed8f5cd05d0136d608ee01) --- src/libslic3r/Support/TreeSupport.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index c4c01c3399..914a0ed667 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1033,18 +1033,20 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } } + if (layer_nr < blockers.size()) { + // Arthur: union_ is a must because after mirroring, the blocker polygons are in left-hand coordinates, ie clockwise, + // which are not valid polygons, and will be removed by offset_ex. union_ can make these polygons right. + ExPolygons blocker = offset_ex(union_(blockers[layer_nr]), scale_(radius_sample_resolution)); + layer->loverhangs = diff_ex(layer->loverhangs, blocker); + layer->cantilevers = diff_ex(layer->cantilevers, blocker); + sharp_tail_overhangs = diff_ex(sharp_tail_overhangs, blocker); + } + if (support_critical_regions_only && is_auto(stype)) { layer->loverhangs.clear(); // remove oridinary overhangs, only keep cantilevers and sharp tails (added later) append(layer->loverhangs, layer->cantilevers); } - if (layer_nr < blockers.size()) { - Polygons& blocker = blockers[layer_nr]; - // Arthur: union_ is a must because after mirroring, the blocker polygons are in left-hand coordinates, ie clockwise, - // which are not valid polygons, and will be removed by offset_ex. union_ can make these polygons right. - layer->loverhangs = diff_ex(layer->loverhangs, offset_ex(union_(blocker), scale_(radius_sample_resolution))); - } - if (max_bridge_length > 0 && layer->loverhangs.size() > 0 && lower_layer) { // do not break bridge for normal part in TreeHybrid, nor Tree Strong bool break_bridge = !(m_support_params.support_style == smsTreeHybrid && area(layer->loverhangs) > m_support_params.thresh_big_overhang) From 3d70bdc3e1224c9f9c0a6c0808af2003ae30d04d Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 23 May 2024 22:01:01 +0800 Subject: [PATCH 37/72] FIX: top z distance incorrect with adaptive layer height This bug causes supports difficult to remove. Rule to remmeber: never decrease the top z distance, you can only increase it SLIGHTLY. jira: STUDIO-7103, STUDIO-7001 Change-Id: I24f71cd67d182d4e2c0902f244a8ca8f4c3ee982 (cherry picked from commit 461af9e8f6f98a8e0b363436276f225183365998) --- src/libslic3r/Support/TreeSupport.cpp | 35 ++++++--------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 914a0ed667..1a8a86ea58 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1910,9 +1910,8 @@ void TreeSupport::draw_circles(const std::vector>& con continue; } - SupportNode* first_node = curr_layer_nodes.front(); - ts_layer->print_z = first_node->print_z; - ts_layer->height = first_node->height; + ts_layer->print_z = m_ts_data->layer_heights[layer_nr].print_z; + ts_layer->height = m_ts_data->layer_heights[layer_nr].height; if (ts_layer->height < EPSILON) { continue; } @@ -2951,20 +2950,11 @@ std::vector TreeSupport::plan_layer_heights(std::vectorget_layer(0)->print_z, m_object->get_layer(0)->height, 0 }; // Collect top contact layers - coordf_t print_z = layer_heights[0].print_z; for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) { if (!contact_nodes[layer_nr].empty()) { bounds.push_back(layer_nr); layer_heights[layer_nr].print_z = contact_nodes[layer_nr].front()->print_z; layer_heights[layer_nr].height = contact_nodes[layer_nr].front()->height; - if (layer_heights[layer_nr].bottom_z() - print_z < m_slicing_params.min_layer_height) { - layer_heights[layer_nr].height = layer_heights[layer_nr].print_z - print_z; - for (auto& node : contact_nodes[layer_nr]) { - node->height = layer_heights[layer_nr].height; - } - } - print_z = layer_heights[layer_nr].print_z; - BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights0 print_z, height, layer_nr: " << layer_heights[layer_nr].print_z << " " << layer_heights[layer_nr].height << " " << layer_nr; } @@ -3008,34 +2998,23 @@ std::vector TreeSupport::plan_layer_heights(std::vector= 0; i = j) { + for (; i >= 0; i--) { if (layer_heights[i].height < EPSILON) { - j--; continue; } for (j = i - 1; j >= 0; j--) { - if (layer_heights[j].height > EPSILON) { + if (layer_heights[j].height > EPSILON && layer_heights[j].print_z0.01) // there is a gap more than 0.01mm, increase the top z distance to fill the gap + layer_heights[i].height = layer_heights[i].print_z - layer_heights[j].print_z; break; } } } - - for (i = 0; i < layer_heights.size(); i++) { - // there might be gap between layers due to non-integer interfaces - if (size_t next_layer_nr = layer_heights[i].next_layer_nr; - next_layer_nr > 0 && layer_heights[i].height + EPSILON < layer_heights[i].print_z - layer_heights[next_layer_nr].print_z) { - layer_heights[i].height = layer_heights[i].print_z - layer_heights[next_layer_nr].print_z; - } - - } } - // update interfaces' height + // log layer_heights for (size_t i = 0; i < layer_heights.size(); i++) { - for (auto& node : contact_nodes[i]) { - node->height = layer_heights[i].height; - } if (layer_heights[i].height > EPSILON) BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights print_z, height, lower_layer_nr->layer_nr: " << layer_heights[i].print_z << " " << layer_heights[i].height << " " << layer_heights[i].next_layer_nr << "->" << i; From 2332caa84c61b02c91b9c29e6c87c064043d7a41 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 28 May 2024 18:07:44 +0800 Subject: [PATCH 38/72] FIX: empty first layer of tree support The raft gap layer should only exist if there are raft layers. jira: STUDIO-7184 Change-Id: Ia4d2a5b7ddf873fb4ef16c7087648214e6bde806 (cherry picked from commit f13144d6a9c20cfbad11c6907c30b10447d8f8a3) --- src/libslic3r/Support/TreeSupport.cpp | 36 ++++++++++++++++++--------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 1a8a86ea58..5b61f11120 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1120,18 +1120,30 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) void TreeSupport::create_tree_support_layers() { int layer_id = 0; - coordf_t raft_print_z = 0.f; - coordf_t raft_slice_z = 0.f; - for (; layer_id < m_slicing_params.base_raft_layers; layer_id++) { - raft_print_z += m_slicing_params.base_raft_layer_height; - raft_slice_z = raft_print_z - m_slicing_params.base_raft_layer_height / 2; - m_object->add_tree_support_layer(layer_id, m_slicing_params.base_raft_layer_height, raft_print_z, raft_slice_z); - } - - for (; layer_id < m_slicing_params.base_raft_layers + m_slicing_params.interface_raft_layers; layer_id++) { - raft_print_z += m_slicing_params.interface_raft_layer_height; - raft_slice_z = raft_print_z - m_slicing_params.interface_raft_layer_height / 2; - m_object->add_tree_support_layer(layer_id, m_slicing_params.base_raft_layer_height, raft_print_z, raft_slice_z); + if (m_raft_layers > 0) { //create raft layers + coordf_t raft_print_z = 0.f; + coordf_t raft_slice_z = 0.f; + { + // Do not add the raft contact layer, 1st layer should use first_print_layer_height + coordf_t height = m_slicing_params.first_print_layer_height; + raft_print_z += height; + raft_slice_z = raft_print_z - height / 2; + m_object->add_tree_support_layer(layer_id++, height, raft_print_z, raft_slice_z); + } + // Insert the base layers. + for (size_t i = 1; i < m_slicing_params.base_raft_layers; i++) { + coordf_t height = m_slicing_params.base_raft_layer_height; + raft_print_z += height; + raft_slice_z = raft_print_z - height / 2; + m_object->add_tree_support_layer(layer_id++, height, raft_print_z, raft_slice_z); + } + // Insert the interface layers. + for (size_t i = 0; i < m_slicing_params.interface_raft_layers; i++) { + coordf_t height = m_slicing_params.interface_raft_layer_height; + raft_print_z += height; + raft_slice_z = raft_print_z - height / 2; + m_object->add_tree_support_layer(layer_id++, height, raft_print_z, raft_slice_z); + } } for (Layer *layer : m_object->layers()) { From fa11957c17ae80eb9eaab3605a432ec788709360 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 25 Jun 2024 09:30:12 +0800 Subject: [PATCH 39/72] FIX: do not break bridges in tree support jira: STUDIO-7424 github: #4318 Change-Id: Icccf56b129c4910f3b0a49d69871b8df1375a6d9 (cherry picked from commit 99211cde5f2114fd64e2724069540577793f889a) (cherry picked from commit 6a130a19ef2e881a7853183123163ce67d63bc37) --- src/libslic3r/Support/TreeSupport.cpp | 5 ++--- src/libslic3r/Support/TreeSupport3D.cpp | 2 +- src/slic3r/GUI/GUI_App.cpp | 3 ++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 5b61f11120..47e7422b3c 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1048,9 +1048,8 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } if (max_bridge_length > 0 && layer->loverhangs.size() > 0 && lower_layer) { - // do not break bridge for normal part in TreeHybrid, nor Tree Strong - bool break_bridge = !(m_support_params.support_style == smsTreeHybrid && area(layer->loverhangs) > m_support_params.thresh_big_overhang) - && !(m_support_params.support_style==smsTreeStrong); + // do not break bridge as the interface will be poor, see #4318 + bool break_bridge = false; m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &layer->loverhangs, max_bridge_length, break_bridge); } diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index ece9eea2b5..8773cae565 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -3447,7 +3447,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume move_bounds, interface_placer, throw_on_cancel); auto t_gen = std::chrono::high_resolution_clock::now(); - #ifdef TREESUPPORT_DEBUG_SVG +#ifdef TREESUPPORT_DEBUG_SVG for (size_t layer_idx = 0; layer_idx < move_bounds.size(); ++layer_idx) { Polygons polys; for (auto& area : move_bounds[layer_idx]) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 287f72a082..a01c509b15 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2125,7 +2125,8 @@ bool GUI_App::OnInit() { try { return on_init_inner(); - } catch (const std::exception&) { + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(fatal) << "OnInit Got Fatal error: " << e.what(); generic_exception_handle(); return false; } From 0cfa5f47fee56388b4b3acae8a51ae09f28c8ae1 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 24 May 2024 18:23:29 +0800 Subject: [PATCH 40/72] FIX: missing support layers at raft gap jira: none Change-Id: I9a7f34c148ee0e228cf2e8e18c85136253f354ad (cherry picked from commit 615751538a65cdaeb8e398dcc941971c98ec11b8) (cherry picked from commit da7f1439ce78d92326726be961528d2755d9a6e2) --- src/libslic3r/Support/TreeSupport.cpp | 48 +++++++++++++++++++++------ src/libslic3r/Support/TreeSupport.hpp | 2 +- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 47e7422b3c..a8cfa9cbfe 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1143,6 +1143,17 @@ void TreeSupport::create_tree_support_layers() raft_slice_z = raft_print_z - height / 2; m_object->add_tree_support_layer(layer_id++, height, raft_print_z, raft_slice_z); } + + // Layers between the raft contacts and bottom of the object. + double dist_to_go = m_slicing_params.object_print_z_min - raft_print_z; + auto nsteps = int(ceil(dist_to_go / m_slicing_params.max_suport_layer_height)); + double height = dist_to_go / nsteps; + for (int i = 0; i < nsteps; ++i) { + raft_print_z += height; + raft_slice_z = raft_print_z - height / 2; + m_object->add_tree_support_layer(layer_id++, height, raft_print_z, raft_slice_z); + } + m_raft_layers = layer_id; } for (Layer *layer : m_object->layers()) { @@ -1242,7 +1253,7 @@ static void make_perimeter_and_inner_brim(ExtrusionEntitiesPtr &dst, const ExPol _make_loops(dst, support_area_new, role, wall_count, flow); } -static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const Print& print, const ExPolygon& support_area, size_t wall_count, const Flow& flow, ExtrusionRole role, Fill* filler_support, double support_density, bool infill_first=true) +static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const ExPolygon& support_area, size_t wall_count, const Flow& flow, ExtrusionRole role, Fill* filler_support, double support_density, bool infill_first=true) { Polygons loops; ExPolygons support_area_new = offset_ex(support_area, -0.5f * float(flow.scaled_spacing()), jtSquare); @@ -1349,7 +1360,8 @@ void TreeSupport::generate_toolpaths() raft_areas = std::move(offset_ex(raft_areas, scale_(object_config.raft_first_layer_expansion))); - for (size_t layer_nr = 0; layer_nr < m_slicing_params.base_raft_layers; layer_nr++) { + size_t layer_nr = 0; + for (; layer_nr < m_slicing_params.base_raft_layers; layer_nr++) { SupportLayer *ts_layer = m_object->get_support_layer(layer_nr); coordf_t expand_offset = (layer_nr == 0 ? m_object_config->raft_first_layer_expansion.value : 0.); auto raft_areas1 = offset_ex(raft_areas, scale_(expand_offset)); @@ -1384,10 +1396,13 @@ void TreeSupport::generate_toolpaths() first_non_raft_base.emplace_back(*area_group.area); } } + first_non_raft_base = offset_ex(first_non_raft_base, support_extrusion_width); ExPolygons raft_base_areas = intersection_ex(raft_areas, first_non_raft_base); ExPolygons raft_interface_areas = diff_ex(raft_areas, raft_base_areas); - for (size_t layer_nr = m_slicing_params.base_raft_layers; + + // raft interfaces + for (layer_nr = m_slicing_params.base_raft_layers; layer_nr < m_slicing_params.base_raft_layers + m_slicing_params.interface_raft_layers; layer_nr++) { @@ -1410,6 +1425,17 @@ void TreeSupport::generate_toolpaths() filler_interface, fill_params, erSupportMaterial, support_flow); } + // layers between raft and object + for (; layer_nr < m_raft_layers; layer_nr++) { + SupportLayer *ts_layer = m_object->get_support_layer(layer_nr); + Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); + Fill* filler_raft = Fill::new_from_type(ipRectilinear); + filler_raft->angle = PI / 2; + filler_raft->spacing = support_flow.spacing(); + for (auto& poly : first_non_raft_base) + make_perimeter_and_infill(ts_layer->support_fills.entities, poly, std::min(size_t(1), wall_count), support_flow, erSupportMaterial, filler_raft, interface_density, false); + } + if (m_object->support_layer_count() <= m_raft_layers) return; @@ -1435,7 +1461,7 @@ void TreeSupport::generate_toolpaths() SupportLayer* ts_layer = m_object->get_support_layer(layer_id); Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); - m_support_material_interface_flow = support_material_interface_flow(m_object, ts_layer->height); // update flow using real support layer height + Flow interface_flow = support_material_interface_flow(m_object, ts_layer->height); // update flow using real support layer height coordf_t support_spacing = object_config.support_base_pattern_spacing.value + support_flow.spacing(); coordf_t support_density = std::min(1., support_flow.spacing() / support_spacing); ts_layer->support_fills.no_sort = false; @@ -1464,10 +1490,10 @@ void TreeSupport::generate_toolpaths() // roof_1st_layer fill_params.density = interface_density; // Note: spacing means the separation between two lines as if they are tightly extruded - filler_Roof1stLayer->spacing = m_support_material_interface_flow.spacing(); + filler_Roof1stLayer->spacing = interface_flow.spacing(); // generate a perimeter first to support interface better ExtrusionEntityCollection* temp_support_fills = new ExtrusionEntityCollection(); - make_perimeter_and_infill(temp_support_fills->entities, *m_object->print(), poly, 1, m_support_material_interface_flow, erSupportMaterial, + make_perimeter_and_infill(temp_support_fills->entities, poly, 1, interface_flow, erSupportMaterial, filler_Roof1stLayer.get(), interface_density, false); temp_support_fills->no_sort = true; // make sure loops are first if (!temp_support_fills->entities.empty()) @@ -1477,13 +1503,13 @@ void TreeSupport::generate_toolpaths() } else if (area_group.type == SupportLayer::FloorType) { // floor_areas fill_params.density = bottom_interface_density; - filler_interface->spacing = m_support_material_interface_flow.spacing(); + filler_interface->spacing = interface_flow.spacing(); fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys, - filler_interface.get(), fill_params, erSupportMaterialInterface, m_support_material_interface_flow); + filler_interface.get(), fill_params, erSupportMaterialInterface, interface_flow); } else if (area_group.type == SupportLayer::RoofType) { // roof_areas fill_params.density = interface_density; - filler_interface->spacing = m_support_material_interface_flow.spacing(); + filler_interface->spacing = interface_flow.spacing(); if (m_object_config->support_interface_pattern == smipGrid) { filler_interface->angle = Geometry::deg2rad(object_config.support_angle.value); fill_params.dont_sort = true; @@ -1492,7 +1518,7 @@ void TreeSupport::generate_toolpaths() filler_interface->layer_id = area_group.interface_id; fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys, filler_interface.get(), fill_params, erSupportMaterialInterface, - m_support_material_interface_flow); + interface_flow); } else { // base_areas @@ -1517,7 +1543,7 @@ void TreeSupport::generate_toolpaths() // otherwise must draw 1 wall // Don't need extra walls if we have infill. Extra walls may overlap with the infills. size_t min_wall_count = offset(poly, -scale_(support_spacing * 1.5)).empty() ? 1 : 0; - make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(min_wall_count, wall_count), flow, + make_perimeter_and_infill(ts_layer->support_fills.entities, poly, std::max(min_wall_count, wall_count), flow, erSupportMaterial, filler_support.get(), support_density); } else { diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index dc641669da..b8d4bdbbba 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -413,7 +413,7 @@ private: SlicingParameters m_slicing_params; // Various precomputed support parameters to be shared with external functions. SupportParameters m_support_params; - size_t m_raft_layers = 0; + size_t m_raft_layers = 0; // number of raft layers, including raft base, raft interface, raft gap size_t m_highest_overhang_layer = 0; std::vector> m_spanning_trees; std::vector< std::unordered_map> m_mst_line_x_layer_contour_caches; From 9ee61bb3dd8762a1d78a6937c0dd870976d20d71 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 2 Jul 2024 18:41:29 +0800 Subject: [PATCH 41/72] FIX: top z distance inaccurate if it's too large The top z gap should be split if it's too large. Also we use same logic for both synced and independent support layer. jira: STUDIO-7232 github: #4191 Change-Id: Idca792e8fa51a83c2a09441ecac64d40b91d6390 (cherry picked from commit c262a7ea137db09e453c157115b3d5417a32886d) --- src/libslic3r/Support/TreeSupport.cpp | 122 +++++++++++++------------- 1 file changed, 63 insertions(+), 59 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index a8cfa9cbfe..9d6b780df0 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -748,7 +748,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) return cluster; }; - if (!is_tree(stype)) return; + if (!is_tree(stype)) return; max_cantilever_dist = 0; m_highest_overhang_layer = 0; @@ -852,7 +852,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } dist_max = std::max(dist_max, dist_pt); } - if (dist_max > scale_(3)) { // is cantilever if the farmost point is larger than 3mm away from base + if (dist_max > scale_(3)) { // is cantilever if the farmost point is larger than 3mm away from base max_cantilever_dist = std::max(max_cantilever_dist, dist_max); layer->cantilevers.emplace_back(poly); BOOST_LOG_TRIVIAL(debug) << "found a cantilever cluster. layer_nr=" << layer_nr << dist_max; @@ -939,7 +939,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) layer->sharp_tails_height.push_back( accum_height); } - } + } } } @@ -1034,7 +1034,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } if (layer_nr < blockers.size()) { - // Arthur: union_ is a must because after mirroring, the blocker polygons are in left-hand coordinates, ie clockwise, + // Arthur: union_ is a must because after mirroring, the blocker polygons are in left-hand coordinates, ie clockwise, // which are not valid polygons, and will be removed by offset_ex. union_ can make these polygons right. ExPolygons blocker = offset_ex(union_(blockers[layer_nr]), scale_(radius_sample_resolution)); layer->loverhangs = diff_ex(layer->loverhangs, blocker); @@ -1071,7 +1071,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) // add sharp tail overhangs append(layer->loverhangs, sharp_tail_overhangs); - + // fill overhang_types for (size_t i = 0; i < layer->loverhangs.size(); i++) overhang_types.emplace(&layer->loverhangs[i], i < nDetected ? OverhangType::Detected : @@ -1295,7 +1295,7 @@ static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const ExPolygon if (infill_first) dst.insert(dst.end(), loops_entities.begin(), loops_entities.end()); - else { // loops first + else { // loops first loops_entities.insert(loops_entities.end(), dst.begin(), dst.end()); dst = std::move(loops_entities); } @@ -1670,7 +1670,7 @@ void TreeSupport::generate() profiler.stage_finish(STAGE_DRAW_CIRCLES); - + profiler.stage_start(STAGE_GENERATE_TOOLPATHS); m_object->print()->set_status(70, _u8L("Generating support")); generate_toolpaths(); @@ -1795,7 +1795,7 @@ ExPolygons avoid_object_remove_extra_small_parts(const ExPolygon &expoly, const } } if (idx_max_area >= 0) expolys_out.emplace_back(std::move(expolys_avoid[idx_max_area])); - + return expolys_out; } @@ -1888,7 +1888,7 @@ void TreeSupport::draw_circles(const std::vector>& con // Use square support if there are too many nodes per layer because circle support needs much longer time to compute // Hower circle support can be printed faster, so we prefer circle for fewer nodes case. - const bool SQUARE_SUPPORT = avg_node_per_layer > 200; + const bool SQUARE_SUPPORT = avg_node_per_layer > 200; const int CIRCLE_RESOLUTION = SQUARE_SUPPORT ? 4 : 100; // The number of vertices in each circle. @@ -2312,7 +2312,7 @@ void TreeSupport::draw_circles(const std::vector>& con { // if roof1 interface is inside a hole, need to expand the interface for (auto& roof1 : ts_layer->roof_1st_layer) { - //if (hole.contains(roof1.contour.points.front()) && hole.contains(roof1.contour.bounding_box().center())) + //if (hole.contains(roof1.contour.points.front()) && hole.contains(roof1.contour.bounding_box().center())) bool is_inside_hole = std::all_of(roof1.contour.points.begin(), roof1.contour.points.end(), [&hole](Point& pt) { return hole.contains(pt); }); if (is_inside_hole) { Polygon hole_reoriented = hole; @@ -2460,7 +2460,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod auto& layer_contact_nodes = contact_nodes[layer_nr]; if (layer_contact_nodes.empty()) continue; - + int layer_nr_next = layer_heights[layer_nr].next_layer_nr; coordf_t print_z = layer_heights[layer_nr].print_z; coordf_t print_z_next = layer_heights[layer_nr_next].print_z; @@ -2660,7 +2660,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod //In the second pass, move all middle nodes. tbb::parallel_for_each(nodes_vec.begin(), nodes_vec.end(), [&](const std::pair& entry) { - + SupportNode* p_node = entry.second; const SupportNode& node = *p_node; if (to_delete.find(p_node) != to_delete.end()) @@ -2738,7 +2738,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod for (const Point &neighbour : neighbours) { // do not move to the neighbor to be deleted SupportNode *neighbour_node = nodes_this_part[neighbour]; - if (to_delete.find(neighbour_node) != to_delete.end()) continue; + if (to_delete.find(neighbour_node) != to_delete.end()) continue; Point direction = neighbour - node.position; // do not move to neighbor that's too far away (即使以最大速度移动,在接触热床之前都无法汇聚) @@ -2754,7 +2754,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod if (!is_strong) sum_direction += direction * (1 / dist2_to_neighbor); else - sum_direction += direction; + sum_direction += direction; } if (!is_strong) @@ -2964,71 +2964,72 @@ void TreeSupport::smooth_nodes(std::vector> &contact_ std::vector TreeSupport::plan_layer_heights(std::vector> &contact_nodes) { - const coordf_t max_layer_height = m_slicing_params.max_layer_height; - const coordf_t layer_height = m_object_config->layer_height.value; - coordf_t z_distance_top = m_slicing_params.gap_support_object; - // BBS: add extra distance if thick bridge is enabled - // Note: normal support uses print_z, but tree support uses integer layers, so we need to subtract layer_height - if (!m_slicing_params.soluble_interface && m_object_config->thick_bridges) { - z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(*m_print_config) - layer_height; - } - const size_t support_roof_layers = m_object_config->support_interface_top_layers.value; - const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1; std::vector layer_heights(contact_nodes.size()); - std::vector bounds; + std::map> bounds; // layer_nr:(print_z, height) - if (!m_object_config->tree_support_adaptive_layer_height || layer_height == max_layer_height || !m_support_params.independent_layer_height) { - for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { - layer_heights[layer_nr] = {m_object->get_layer(layer_nr)->print_z, m_object->get_layer(layer_nr)->height, layer_nr > 0 ? size_t(layer_nr - 1) : 0}; - } - } - else { - bounds.push_back(0); + { // Keep first layer still layer_heights[0] = { m_object->get_layer(0)->print_z, m_object->get_layer(0)->height, 0 }; + bounds[0] = { m_object->get_layer(0)->print_z, m_object->get_layer(0)->height}; // Collect top contact layers for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) { if (!contact_nodes[layer_nr].empty()) { - bounds.push_back(layer_nr); - layer_heights[layer_nr].print_z = contact_nodes[layer_nr].front()->print_z; - layer_heights[layer_nr].height = contact_nodes[layer_nr].front()->height; - BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights0 print_z, height, layer_nr: " << layer_heights[layer_nr].print_z << " " << layer_heights[layer_nr].height << " " - << layer_nr; + coordf_t print_z = contact_nodes[layer_nr].front()->print_z; + coordf_t height = contact_nodes[layer_nr].front()->height; + if(height>m_slicing_params.max_suport_layer_height){ + // split this layer into multiple layers if the gap is too big + int num_layers=std::ceil(height/m_slicing_params.max_suport_layer_height); + coordf_t new_height= height/num_layers; + for(auto& node: contact_nodes[layer_nr]) { + node->height = new_height; + node->distance_to_top = -num_layers; + node->support_roof_layers_below+=num_layers-1; + } + + for (int i=0; i s(bounds.begin(), bounds.end()); - bounds.assign(s.begin(), s.end()); - for (size_t idx_extreme = 1; idx_extreme < bounds.size(); idx_extreme++) { - int extr2_layer_nr = bounds[idx_extreme]; - coordf_t extr2z = layer_heights[extr2_layer_nr].bottom_z(); - int extr1_layer_nr = bounds[idx_extreme - 1]; - coordf_t extr1z = layer_heights[extr1_layer_nr].print_z; + auto it1 = bounds.begin(); + auto it2 = bounds.begin(); + it2++; + for (; it2 != bounds.end();it1++, it2++) { + int extr2_layer_nr = it2->first; + coordf_t extr2z = it2->second.first - it2->second.second; // bottom_z of upper bound + int extr1_layer_nr = it1->first; //bounds[idx_extreme - 1]; + coordf_t extr1z = it1->second.first;// print_z of lower bound coordf_t dist = extr2z - extr1z; + layer_heights[extr2_layer_nr].print_z = it2->second.first; + layer_heights[extr2_layer_nr].height = it2->second.second; + BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights0 print_z, height, layer_nr: " << layer_heights[extr2_layer_nr].print_z << " " << layer_heights[extr2_layer_nr].height << " " << extr2_layer_nr; + // Insert intermediate layers. - size_t n_layers_extra = size_t(ceil(dist / (m_slicing_params.max_suport_layer_height + EPSILON))); - int actual_internel_layers = extr2_layer_nr - extr1_layer_nr - 1; - int extr_layers_left = extr2_layer_nr - extr1_layer_nr - n_layers_extra - 1; + size_t n_layers_extra = m_support_params.independent_layer_height ? size_t(ceil(dist / (m_slicing_params.max_suport_layer_height))) : + size_t(ceil(dist / (m_slicing_params.min_layer_height))); if (n_layers_extra < 1) continue; coordf_t step = dist / coordf_t(n_layers_extra); - coordf_t print_z = extr1z + step; - //assert(step >= layer_height - EPSILON); - coordf_t extr2z_large_steps = extr2z; + coordf_t print_z = extr1z; for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; layer_nr++) { - // if (curr_layer_nodes.empty()) continue; - if (std::abs(print_z - m_object->get_layer(layer_nr)->print_z) < step / 2 + EPSILON || extr_layers_left < 1) { + Layer* layer = m_object->get_layer(layer_nr); + if (!m_support_params.independent_layer_height) step = layer->height; + if (std::abs((print_z+step) - layer->print_z) < step / 2 + EPSILON) { + print_z += step; layer_heights[layer_nr].print_z = print_z; layer_heights[layer_nr].height = step; - print_z += step; } else { // can't clear curr_layer_nodes, or the model will have empty layers layer_heights[layer_nr].print_z = 0.0; layer_heights[layer_nr].height = 0.0; - extr_layers_left--; } } } @@ -3152,9 +3153,12 @@ void TreeSupport::generate_contact_points(std::vector> contact_node->overhang = overhang_part; contact_node->is_sharp_tail = is_sharp_tail; if (is_sharp_tail) { - int ind = overhang_part.contour.closest_point_index(pt); - auto n1 = (overhang_part.contour[ind] - overhang_part.contour[ind - 1]).cast().normalized(); - auto n2 = (overhang_part.contour[ind] - overhang_part.contour[ind + 1]).cast().normalized(); + int ind = overhang_part.contour.closest_point_index(pt); + int nSize = overhang_part.contour.points.size(); + int ind_prev = (ind - 1 + nSize) % nSize; + int ind_next = (ind + 1) % nSize; + auto n1 = (overhang_part.contour[ind] - overhang_part.contour[ind_prev]).cast().normalized(); + auto n2 = (overhang_part.contour[ind] - overhang_part.contour[ind_next]).cast().normalized(); contact_node->skin_direction = scaled((n1 + n2).normalized()); } curr_nodes.emplace_back(contact_node); @@ -3223,7 +3227,7 @@ void TreeSupport::generate_contact_points(std::vector> } } } - + } if (!curr_nodes.empty()) nonempty_layers++; for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); } @@ -3250,7 +3254,7 @@ void TreeSupport::generate_contact_points(std::vector> mx2 += x * x; } nodes_angle = atan2(nNodes * mxy - mx * my, nNodes * mx2 - SQ(mx)); - + BOOST_LOG_TRIVIAL(info) << "avg_node_per_layer=" << avg_node_per_layer << ", nodes_angle=" << nodes_angle; } } From f00bdfeca88e438c35b8bac78c8ad76e374fcf07 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 3 Jul 2024 10:44:55 +0800 Subject: [PATCH 42/72] FIX: enforcers may not work with contour expansion To fix this we have to expand the enforcer areas just like organic support. jira: STUDIO-7538 Change-Id: I8e4e3fd18b0e77db9beb57347d8da895fc83f4b0 (cherry picked from commit 319b3e2247e01e545bb9e4cebea7950d875cd89a) --- src/libslic3r/Support/TreeSupport.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 9d6b780df0..2a410e443f 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -662,6 +662,9 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) double thresh_angle = config.support_threshold_angle.value > EPSILON ? config.support_threshold_angle.value + 1 : 30; thresh_angle = std::min(thresh_angle, 89.); // should be smaller than 90 const double threshold_rad = Geometry::deg2rad(thresh_angle); + // FIXME this is a fudge constant! + double support_tree_tip_diameter = 0.8; + auto enforcer_overhang_offset = scaled(support_tree_tip_diameter); // for small overhang removal struct OverhangCluster { @@ -1007,6 +1010,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) } int layers_with_overhangs = 0; + int layers_with_enforcers = 0; for (int layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { if (m_object->print()->canceled()) break; @@ -1056,15 +1060,11 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) int nDetected = layer->loverhangs.size(); // enforcers now follow same logic as normal support. See STUDIO-3692 if (layer_nr < enforcers.size() && lower_layer) { - float no_interface_offset = std::accumulate(layer->regions().begin(), layer->regions().end(), FLT_MAX, - [](float acc, const LayerRegion* layerm) { return std::min(acc, float(layerm->flow(frExternalPerimeter).scaled_width())); }); - Polygons lower_layer_polygons = (layer_nr == 0) ? Polygons() : to_polygons(lower_layer->lslices_extrudable); - Polygons& enforcer = enforcers[layer_nr]; - if (!enforcer.empty()) { - ExPolygons enforcer_polygons = diff_ex(intersection_ex(layer->lslices_extrudable, enforcer), - // Inflate just a tiny bit to avoid intersection of the overhang areas with the object. - expand(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - append(layer->loverhangs, enforcer_polygons); + ExPolygons enforced_overhangs = intersection_ex(diff_ex(layer->lslices_extrudable, lower_layer->lslices_extrudable), enforcers[layer_nr]); + if (!enforced_overhangs.empty()) { + // FIXME this is a hack to make enforcers work on steep overhangs. See STUDIO-7538. + enforced_overhangs = diff_ex(offset_ex(enforced_overhangs, enforcer_overhang_offset), lower_layer->lslices_extrudable); + append(layer->loverhangs, enforced_overhangs); } } int nEnforced = layer->loverhangs.size(); @@ -1081,10 +1081,11 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) layers_with_overhangs++; m_highest_overhang_layer = std::max(m_highest_overhang_layer, size_t(layer_nr)); } + if (nEnforced > 0) layers_with_enforcers++; if (!layer->cantilevers.empty()) has_cantilever = true; } - BOOST_LOG_TRIVIAL(info) << "Tree support overhang detection done. " << layers_with_overhangs << " layers with overhangs."; + BOOST_LOG_TRIVIAL(info) << "Tree support overhang detection done. " << layers_with_overhangs << " layers with overhangs. nEnforced=" << layers_with_enforcers; #ifdef SUPPORT_TREE_DEBUG_TO_SVG for (const Layer* layer : m_object->layers()) { @@ -2563,7 +2564,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod #ifdef SUPPORT_TREE_DEBUG_TO_SVG coordf_t branch_radius_temp = 0; coordf_t max_y = std::numeric_limits::min(); - draw_layer_mst(debug_out_path("mtree_%.2f.svg", print_z), spanning_trees, m_object->get_layer(obj_layer_nr)->lslices_extrudable); + draw_layer_mst(debug_out_path("mtree_%.2f.svg", print_z), spanning_trees, m_object->get_layer(layer_nr)->lslices); #endif for (size_t group_index = 0; group_index < nodes_per_part.size(); group_index++) { @@ -2843,7 +2844,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod m_ts_data->m_layer_outlines[layer_nr], contact_nodes[layer_nr], contact_nodes[layer_nr_next], { "overhang","avoid","outline" }, { "blue","red","yellow" }); - BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer->next " << layer_nr << "->" << layer_nr_next << ", print_z=" << ts_layer->print_z + BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer->next " << layer_nr << "->" << layer_nr_next << ", print_z=" << print_z << ", num points: " << contact_nodes[layer_nr].size() << "->" << contact_nodes[layer_nr_next].size(); for (size_t i = 0; i < std::min(size_t(5), contact_nodes[layer_nr].size()); i++) { auto &node = contact_nodes[layer_nr][i]; From 532dcae37a2a0c0edc4f995e10fd89805650766c Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 28 May 2024 20:11:45 +0800 Subject: [PATCH 43/72] ENH: add rectilinear interface pattern for organic support 1. add rectilinear interface pattern for organic support jira: STUDIO-7181 2. add tree support optgroup Change-Id: I94882bc34a61c6adc06b8ecbc9f2323f9b039aac (cherry picked from commit a8142ab3f37e0bd140a31a7e635b8475f471d7e3) (cherry picked from commit 69cf816b9431bc21ca0187c7db1148e2d2e898ab) --- src/libslic3r/CMakeLists.txt | 8 +-- src/libslic3r/Fill/FillBase.cpp | 2 +- src/libslic3r/Support/SupportMaterial.cpp | 62 ------------------ src/libslic3r/Support/SupportParameters.hpp | 31 ++++----- src/libslic3r/Support/TreeSupport.cpp | 68 +++++++++---------- src/libslic3r/Support/TreeSupport.hpp | 16 ++--- src/libslic3r/Support/TreeSupport3D.cpp | 72 +++++++++++++-------- src/libslic3r/Support/TreeSupport3D.hpp | 2 - src/libslic3r/Support/TreeSupportCommon.hpp | 62 +++++++----------- src/slic3r/GUI/Tab.cpp | 37 ----------- 10 files changed, 130 insertions(+), 230 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 7c673231e0..6db340051a 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -309,16 +309,16 @@ set(lisbslic3r_sources Support/SupportLayer.hpp Support/SupportMaterial.cpp Support/SupportMaterial.hpp - Support/SupportParameters.hpp Support/SupportSpotsGenerator.cpp Support/SupportSpotsGenerator.hpp Support/TreeSupport.hpp Support/TreeSupport.cpp - Support/TreeSupport3D.cpp Support/TreeSupport3D.hpp - Support/TreeSupportCommon.hpp - Support/TreeModelVolumes.cpp + Support/TreeSupport3D.cpp Support/TreeModelVolumes.hpp + Support/TreeModelVolumes.cpp + Support/TreeSupportCommon.hpp + Support/SupportParameters.hpp PrincipalComponents2D.cpp PrincipalComponents2D.hpp MinimumSpanningTree.hpp diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index d5ce03d3b4..d64ab81689 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -56,7 +56,7 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipOctagramSpiral: return new FillOctagramSpiral(); case ipAdaptiveCubic: return new FillAdaptive::Filler(); case ipSupportCubic: return new FillAdaptive::Filler(); - case ipSupportBase: return new FillSupportBase(); + case ipSupportBase: return new FillSupportBase(); // simply line fill case ipLightning: return new FillLightning::Filler(); // BBS: for internal solid infill only case ipConcentricInternal: return new FillConcentricInternal(); diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 57567b90d8..47c476564b 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -329,22 +329,6 @@ static Polygons contours_simplified(const Vec2i32 &grid_size, const double pixel } #endif // SUPPORT_USE_AGG_RASTERIZER -static std::string get_svg_filename(std::string layer_nr_or_z, std::string tag = "bbl_ts") -{ - static bool rand_init = false; - - if (!rand_init) { - srand(time(NULL)); - rand_init = true; - } - - int rand_num = rand() % 1000000; - //makedir("./SVG"); - std::string prefix = "./SVG/"; - std::string suffix = ".svg"; - return prefix + tag + "_" + layer_nr_or_z /*+ "_" + std::to_string(rand_num)*/ + suffix; -} - PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : m_print_config (&object->print()->config()), m_object_config (&object->config()), @@ -567,18 +551,6 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) } #endif /* SLIC3R_DEBUG */ -#if 0 // #ifdef SLIC3R_DEBUG - // check bounds - std::ofstream out; - out.open("./SVG/ns_support_layers.txt"); - if (out.is_open()) { - out << "### Support Layers ###" << std::endl; - for (auto& i : object.support_layers()) { - out << i->print_z << std::endl; - } - } -#endif /* SLIC3R_DEBUG */ - // Generate the actual toolpaths and save them into each layer. generate_support_toolpaths(object.support_layers(), *m_object_config, m_support_params, m_slicing_params, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); @@ -2245,10 +2217,6 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( layer->sharp_tails.push_back(expoly); layer->sharp_tails_height.push_back( accum_height ); append(overhangs_per_layers[layer_nr], overhang); -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg(get_svg_filename(std::to_string(layer->print_z), "sharp_tail"), object.bounding_box()); - if (svg.is_opened()) svg.draw(overhang, "yellow"); -#endif } } @@ -2959,36 +2927,6 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp for (size_t i = 0; i < top_contacts.size(); ++i) assert(top_contacts[i]->height > 0.); #endif /* _DEBUG */ - -#if 0 // #ifdef SLIC3R_DEBUG - // check bounds - std::ofstream out; - out.open("./SVG/ns_bounds.txt"); - if (out.is_open()) { - if (!top_contacts.empty()) { - out << "### Top Contacts ###" << std::endl; - for (auto& t : top_contacts) { - out << t->print_z << std::endl; - } - } - if (!bottom_contacts.empty()) { - out << "### Bottome Contacts ###" << std::endl; - for (auto& b : bottom_contacts) { - out << b->print_z << std::endl; - } - } - if (!intermediate_layers.empty()) { - out << "### Intermediate Layers ###" << std::endl; - for (auto& i : intermediate_layers) { - out << i->print_z << std::endl; - } - } - out << "### Slice Layers ###" << std::endl; - for (size_t j = 0; j < object.layers().size(); ++j) { - out << object.layers()[j]->print_z << std::endl; - } - } -#endif /* SLIC3R_DEBUG */ return intermediate_layers; } diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index e4c0dcd00e..1b1c11d953 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -5,11 +5,8 @@ #include "../Flow.hpp" namespace Slic3r { - -class PrintObject; -enum InfillPattern : int; - struct SupportParameters { + SupportParameters() = default; SupportParameters(const PrintObject &object) { const PrintConfig &print_config = object.print()->config(); @@ -86,20 +83,24 @@ struct SupportParameters { // Object is printed with the same extruder as the support. this->can_merge_support_regions = true; } - - double interface_spacing = object_config.support_interface_spacing.value + this->support_material_interface_flow.spacing(); - this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing); + + + this->base_angle = Geometry::deg2rad(float(object_config.support_angle.value)); + this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.)); + this->interface_spacing = object_config.support_interface_spacing.value + this->support_material_interface_flow.spacing(); + this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / this->interface_spacing); double raft_interface_spacing = object_config.support_interface_spacing.value + this->raft_interface_flow.spacing(); this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing); - double support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing(); - this->support_density = std::min(1., this->support_material_flow.spacing() / support_spacing); + this->support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing(); + this->support_density = std::min(1., this->support_material_flow.spacing() / this->support_spacing); if (object_config.support_interface_top_layers.value == 0) { // No interface layers allowed, print everything with the base support pattern. + this->interface_spacing = this->support_spacing; this->interface_density = this->support_density; } SupportMaterialPattern support_pattern = object_config.support_base_pattern; - this->with_sheath = /*is_tree(object_config.support_type) &&*/ object_config.tree_support_wall_count > 0; + this->with_sheath = object_config.tree_support_wall_count > 0; this->base_fill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; @@ -115,9 +116,7 @@ struct SupportParameters { object_config.support_interface_pattern == smipConcentric ? ipConcentric : (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); - - this->base_angle = Geometry::deg2rad(float(object_config.support_angle.value)); - this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.)); + this->raft_angle_1st_layer = 0.f; this->raft_angle_base = 0.f; this->raft_angle_interface = 0.f; @@ -215,11 +214,13 @@ struct SupportParameters { float base_angle; float interface_angle; - + coordf_t interface_spacing; + coordf_t support_expansion=0; // Density of the top / bottom interface and contact layers. coordf_t interface_density; // Density of the raft interface and contact layers. coordf_t raft_interface_density; + coordf_t support_spacing; // Density of the base support layers. coordf_t support_density; SupportMaterialStyle support_style = smsDefault; @@ -235,7 +236,7 @@ struct SupportParameters { // Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness? bool with_sheath; // Branches of organic supports with area larger than this threshold will be extruded with double lines. - double tree_branch_diameter_double_wall_area_scaled; + double tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled(3.0)) * M_PI;; float raft_angle_1st_layer; float raft_angle_base; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 2a410e443f..37613bcb8c 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1,23 +1,27 @@ #include -#include "MinimumSpanningTree.hpp" -#include "TreeSupport.hpp" -#include "Print.hpp" -#include "Layer.hpp" + +#include "ClipperUtils.hpp" +#include "Fill/FillBase.hpp" #include "Fill/FillBase.hpp" #include "Fill/FillConcentric.hpp" -#include "CurveAnalyzer.hpp" -#include "SVG.hpp" -#include "ShortestPath.hpp" #include "I18N.hpp" +#include "Layer.hpp" +#include "MinimumSpanningTree.hpp" +#include "Print.hpp" +#include "ShortestPath.hpp" +#include "SupportCommon.hpp" +#include "SVG.hpp" +#include "TreeSupportCommon.hpp" +#include "TreeSupport.hpp" +#include "TreeSupport3D.hpp" #include #include -#include "TreeSupport3D.hpp" -#include -#include -#include + #include +#include +#include #include #include @@ -1646,28 +1650,25 @@ void TreeSupport::generate() m_ts_data = m_object->alloc_tree_support_preview_cache(); m_ts_data->is_slim = is_slim; - // Generate contact points of tree support - std::vector> contact_nodes(m_object->layers().size()); - std::vector move_bounds(m_highest_overhang_layer + 1); profiler.stage_start(STAGE_GENERATE_CONTACT_NODES); m_object->print()->set_status(56, _u8L("Support: generate contact points")); - generate_contact_points(contact_nodes); + generate_contact_points(); profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES); //Drop nodes to lower layers. profiler.stage_start(STAGE_DROP_DOWN_NODES); m_object->print()->set_status(60, _u8L("Generating support")); - drop_nodes(contact_nodes); + drop_nodes(); profiler.stage_finish(STAGE_DROP_DOWN_NODES); - smooth_nodes(contact_nodes); // , tree_support_3d_config); + smooth_nodes();// , tree_support_3d_config); //Generate support areas. profiler.stage_start(STAGE_DRAW_CIRCLES); m_object->print()->set_status(65, _u8L("Generating support")); - draw_circles(contact_nodes); + draw_circles(); profiler.stage_finish(STAGE_DRAW_CIRCLES); @@ -1714,7 +1715,6 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_to { radius = mm_to_top;// this is a 45 degree tip } - radius = std::max(radius, MIN_BRANCH_RADIUS); radius = std::min(radius, MAX_BRANCH_RADIUS); // if have interface layers, radius should be larger @@ -1876,7 +1876,7 @@ Polygons TreeSupport::get_trim_support_regions( return polygons_trimming; } -void TreeSupport::draw_circles(const std::vector>& contact_nodes) +void TreeSupport::draw_circles() { const PrintObjectConfig &config = m_object->config(); const Print* print = m_object->print(); @@ -1980,11 +1980,11 @@ void TreeSupport::draw_circles(const std::vector>& con } }; - BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size(); + BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << curr_layer_nodes.size(); //Draw the support areas and add the roofs appropriately to the support roof instead of normal areas. - ts_layer->lslices.reserve(contact_nodes[layer_nr].size()); + ts_layer->lslices.reserve(curr_layer_nodes.size()); ExPolygons area_poly; // the polygon node area which will be printed as normal support - for (const SupportNode* p_node : contact_nodes[layer_nr]) + for (const SupportNode* p_node : curr_layer_nodes) { if (print->canceled()) break; @@ -2366,7 +2366,7 @@ void TreeSupport::draw_circles(const std::vector>& con double SupportNode::diameter_angle_scale_factor; -void TreeSupport::drop_nodes(std::vector>& contact_nodes) +void TreeSupport::drop_nodes() { const PrintObjectConfig &config = m_object->config(); // Use Minimum Spanning Tree to connect the points on each layer and move them while dropping them down. @@ -2405,7 +2405,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod return move_dist; }; - m_ts_data->layer_heights = plan_layer_heights(contact_nodes); + m_ts_data->layer_heights = plan_layer_heights(); std::vector &layer_heights = m_ts_data->layer_heights; if (layer_heights.empty()) return; @@ -2468,7 +2468,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod coordf_t height_next = layer_heights[layer_nr_next].height; std::deque> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches. - const Layer* ts_layer = m_object->get_support_layer(layer_nr); m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating support"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str()); @@ -2731,7 +2730,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod // 1. do not merge neighbors under 5mm // 2. Only merge node with single neighbor in distance between [max_move_distance, 10mm/layer_height] float dist2_to_first_neighbor = neighbours.empty() ? 0 : vsize2_with_unscale(neighbours[0] - node.position); - if (ts_layer->print_z > DO_NOT_MOVER_UNDER_MM && + if (node.print_z > DO_NOT_MOVER_UNDER_MM && (neighbours.size() > 1 || (neighbours.size() == 1 && dist2_to_first_neighbor >= max_move_distance2))) // Only nodes that aren't about to collapse. { // Move towards the average position of all neighbours. @@ -2747,7 +2746,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod coordf_t branch_bottom_radius = calc_branch_radius(branch_radius, node.dist_mm_to_top + node.print_z, diameter_angle_scale_factor); coordf_t neighbour_bottom_radius = calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top + neighbour_node->print_z, diameter_angle_scale_factor); - double max_converge_distance = tan_angle * (ts_layer->print_z - DO_NOT_MOVER_UNDER_MM) + std::max(branch_bottom_radius, neighbour_bottom_radius); + double max_converge_distance = tan_angle * (p_node->print_z - DO_NOT_MOVER_UNDER_MM) + std::max(branch_bottom_radius, neighbour_bottom_radius); if (dist2_to_neighbor > max_converge_distance * max_converge_distance) continue; if (is_line_cut_by_contour(node.position, neighbour)) continue; @@ -2892,7 +2891,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nod BOOST_LOG_TRIVIAL(debug) << "after m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size(); } -void TreeSupport::smooth_nodes(std::vector> &contact_nodes) +void TreeSupport::smooth_nodes() { for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { std::vector &curr_layer_nodes = contact_nodes[layer_nr]; @@ -2963,7 +2962,7 @@ void TreeSupport::smooth_nodes(std::vector> &contact_ } } -std::vector TreeSupport::plan_layer_heights(std::vector> &contact_nodes) +std::vector TreeSupport::plan_layer_heights() { std::vector layer_heights(contact_nodes.size()); std::map> bounds; // layer_nr:(print_z, height) @@ -3061,7 +3060,7 @@ std::vector TreeSupport::plan_layer_heights(std::vector>& contact_nodes) +void TreeSupport::generate_contact_points() { const PrintObjectConfig &config = m_object->config(); const coordf_t point_spread = scale_(config.tree_support_branch_distance.value); @@ -3119,8 +3118,11 @@ void TreeSupport::generate_contact_points(std::vector> // fix bug of generating support for very thin objects if (m_object->layers().size() <= z_distance_top_layers + 1) return; - //if (m_object->support_layer_count() <= m_raft_layers) - // return; + + contact_nodes.clear(); + contact_nodes.resize(m_object->layers().size()); + + tbb::spin_mutex mtx; int nonempty_layers = 0; tbb::concurrent_vector all_nodes; diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index b8d4bdbbba..f94fd57020 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -379,7 +379,7 @@ public: int avg_node_per_layer = 0; - float nodes_angle = 0; + float nodes_angle = 0; bool has_sharp_tails = false; bool has_cantilever = false; double max_cantilever_dist = 0; @@ -405,11 +405,11 @@ private: * Lazily computes volumes as needed. * \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks */ + std::vector> contact_nodes; std::shared_ptr m_ts_data; - std::unique_ptr m_model_volumes; PrintObject *m_object; - const PrintObjectConfig *m_object_config; + const PrintObjectConfig* m_object_config; SlicingParameters m_slicing_params; // Various precomputed support parameters to be shared with external functions. SupportParameters m_support_params; @@ -441,7 +441,7 @@ private: * save the resulting support polygons to. * \param contact_nodes The nodes to draw as support. */ - void draw_circles(const std::vector>& contact_nodes); + void draw_circles(); /*! * \brief Drops down the nodes of the tree support towards the build plate. @@ -455,16 +455,16 @@ private: * dropped down. The nodes are dropped to lower layers inside the same * vector of layers. */ - void drop_nodes(std::vector>& contact_nodes); + void drop_nodes(); - void smooth_nodes(std::vector>& contact_nodes); + void smooth_nodes(); /*! BBS: MusangKing: maximum layer height * \brief Optimize the generation of tree support by pre-planning the layer_heights * */ - std::vector plan_layer_heights(std::vector>& contact_nodes); + std::vector plan_layer_heights(); /*! * \brief Creates points where support contacts the model. * @@ -478,7 +478,7 @@ private: * \return For each layer, a list of points where the tree should connect * with the model. */ - void generate_contact_points(std::vector>& contact_nodes); + void generate_contact_points(); /*! * \brief Add a node to the next layer. diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 8773cae565..22505d4f92 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -7,22 +7,22 @@ // CuraEngine is released under the terms of the AGPLv3 or higher. #include "TreeSupport3D.hpp" - -#include "../AABBTreeIndirect.hpp" -#include "../BuildVolume.hpp" -#include "../ClipperUtils.hpp" -#include "../EdgeGrid.hpp" -#include "../Fill/Fill.hpp" -#include "../Layer.hpp" -#include "../Print.hpp" -#include "../MultiPoint.hpp" -#include "../Polygon.hpp" -#include "../Polyline.hpp" -#include "../MutablePolygon.hpp" -#include "TreeSupportCommon.hpp" +#include "AABBTreeIndirect.hpp" +#include "AABBTreeLines.hpp" +#include "BuildVolume.hpp" +#include "ClipperUtils.hpp" +#include "EdgeGrid.hpp" +#include "Fill/Fill.hpp" +#include "Layer.hpp" +#include "Print.hpp" +#include "MultiPoint.hpp" +#include "Polygon.hpp" +#include "Polyline.hpp" +#include "MutablePolygon.hpp" #include "SupportCommon.hpp" +#include "TriangleMeshSlicer.hpp" #include "TreeSupport.hpp" -#include "libslic3r.h" +#include "I18N.hpp" #include #include @@ -49,7 +49,11 @@ #include #endif // TREE_SUPPORT_ORGANIC_NUDGE_NEW -// #define TREESUPPORT_DEBUG_SVG +#ifndef _L +#define _L(s) Slic3r::I18N::translate(s) +#endif + + //#define TREESUPPORT_DEBUG_SVG namespace Slic3r { @@ -57,16 +61,6 @@ namespace Slic3r namespace TreeSupport3D { -enum class LineStatus -{ - INVALID, - TO_MODEL, - TO_MODEL_GRACIOUS, - TO_MODEL_GRACIOUS_SAFE, - TO_BP, - TO_BP_SAFE -}; - using LineInformation = std::vector>; using LineInformations = std::vector; using namespace std::literals; @@ -363,6 +357,28 @@ static std::vector>> group_me return max_layer; } +// picked from convert_lines_to_internal() +[[nodiscard]] LineStatus get_avoidance_status(const Point& p, coord_t radius, LayerIndex layer_idx, + const TreeModelVolumes& volumes, const TreeSupportSettings& config) +{ + const bool min_xy_dist = config.xy_distance > config.xy_min_distance; + + LineStatus type = LineStatus::INVALID; + + if (!contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, false, min_xy_dist), p)) + type = LineStatus::TO_BP_SAFE; + else if (!contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::Fast, false, min_xy_dist), p)) + type = LineStatus::TO_BP; + else if (config.support_rests_on_model && !contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, true, min_xy_dist), p)) + type = LineStatus::TO_MODEL_GRACIOUS_SAFE; + else if (config.support_rests_on_model && !contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::Fast, true, min_xy_dist), p)) + type = LineStatus::TO_MODEL_GRACIOUS; + else if (config.support_rests_on_model && !contains(volumes.getCollision(radius, layer_idx, min_xy_dist), p)) + type = LineStatus::TO_MODEL; + + return type; +} + /*! * \brief Converts a Polygons object representing a line into the internal format. * @@ -1942,7 +1958,7 @@ static void increase_areas_one_layer( inc_wo_collision.clear(); if (!settings.no_error) { // ERROR CASE - // if the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if if wrongly would be a line, it still actually has an area that can be increased + // if the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if it would be a line wrongly, it still actually has an area that can be increased Polygons lines_offset = offset(to_polylines(parent.influence_area), scaled(0.005), jtMiter, 1.2); Polygons base_error_area = union_(parent.influence_area, lines_offset); result = increase_single_area(volumes, config, settings, layer_idx, parent, @@ -3321,7 +3337,7 @@ static void organic_smooth_branches_avoid_collisions( * \param storage The data storage where the mesh data is gotten from and * where the resulting support areas are stored. */ -static void generate_support_areas(Print &print, const BuildVolume &build_volume, const std::vector &print_object_ids, std::function throw_on_cancel) +static void generate_support_areas(Print &print, TreeSupport* tree_support, const BuildVolume &build_volume, const std::vector &print_object_ids, std::function throw_on_cancel) { // Settings with the indexes of meshes that use these settings. std::vector>> grouped_meshes = group_meshes(print, print_object_ids); @@ -3985,7 +4001,7 @@ void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_suppo std::transform(bedpts.begin(), bedpts.end(), std::back_inserter(bedptsf), [](const Point &p) { return unscale(p); }); BuildVolume build_volume{ bedptsf, tree_support->m_print_config->printable_height }; - TreeSupport3D::generate_support_areas(*print_object.print(), build_volume, { idx }, throw_on_cancel); + TreeSupport3D::generate_support_areas(*print_object.print(), tree_support, build_volume, { idx }, throw_on_cancel); } } // namespace Slic3r diff --git a/src/libslic3r/Support/TreeSupport3D.hpp b/src/libslic3r/Support/TreeSupport3D.hpp index ba543fbc81..1e58743c25 100644 --- a/src/libslic3r/Support/TreeSupport3D.hpp +++ b/src/libslic3r/Support/TreeSupport3D.hpp @@ -47,8 +47,6 @@ struct SlicingParameters; namespace TreeSupport3D { -// The number of vertices in each circle. -static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25; struct AreaIncreaseSettings { diff --git a/src/libslic3r/Support/TreeSupportCommon.hpp b/src/libslic3r/Support/TreeSupportCommon.hpp index b382f6526c..3122a43d2d 100644 --- a/src/libslic3r/Support/TreeSupportCommon.hpp +++ b/src/libslic3r/Support/TreeSupportCommon.hpp @@ -17,10 +17,10 @@ namespace Slic3r { - + // The number of vertices in each circle. + static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25; namespace TreeSupport3D { - using LayerIndex = int; enum class InterfacePreference @@ -40,18 +40,18 @@ struct TreeSupportMeshGroupSettings { const PrintObjectConfig &config = print_object.config(); const SlicingParameters &slicing_params = print_object.slicing_parameters(); // const std::vector printing_extruders = print_object.object_extruders(); - + // Support must be enabled and set to Tree style. - assert(config.enable_support || config.enforce_support_layers > 0); - assert(is_tree(config.support_type)); - + //assert(config.support_material); + //assert(config.support_material_style == smsTree || config.support_material_style == smsOrganic); + // Calculate maximum external perimeter width over all printing regions, taking into account the default layer height. coordf_t external_perimeter_width = 0.; for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) { const PrintRegion ®ion = print_object.printing_region(region_id); external_perimeter_width = std::max(external_perimeter_width, region.flow(print_object, frExternalPerimeter, config.layer_height).width()); } - + this->layer_height = scaled(config.layer_height.value); this->resolution = scaled(print_config.resolution.value); // Arache feature @@ -74,24 +74,15 @@ struct TreeSupportMeshGroupSettings { this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled(0.5 * external_perimeter_width)); this->support_top_distance = scaled(slicing_params.gap_support_object); this->support_bottom_distance = scaled(slicing_params.gap_object_support); - // this->support_interface_skip_height = - // this->support_infill_angles = this->support_roof_enable = config.support_interface_top_layers.value > 0; - this->support_roof_layers = this->support_roof_enable ? config.support_interface_top_layers.value : 0; - this->support_floor_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value > 0; - this->support_floor_layers = this->support_floor_enable ? config.support_interface_bottom_layers.value : 0; - // this->minimum_roof_area = - // this->support_roof_angles = + this->support_roof_layers = config.support_interface_top_layers.value; + this->support_floor_enable = config.support_interface_bottom_layers.value > 0; + this->support_floor_layers = config.support_interface_bottom_layers.value; this->support_roof_pattern = config.support_interface_pattern; this->support_pattern = config.support_base_pattern; this->support_line_spacing = scaled(config.support_base_pattern_spacing.value); - // this->support_bottom_offset = - // this->support_wall_count = config.support_material_with_sheath ? 1 : 0; - this->support_wall_count = 1; + this->support_wall_count = std::max(1, config.tree_support_wall_count.value); // at least 1 wall for organic tree support this->support_roof_line_distance = scaled(config.support_interface_spacing.value) + this->support_roof_line_width; - // this->minimum_support_area = - // this->minimum_bottom_area = - // this->support_offset = this->support_tree_branch_distance = scaled(config.tree_support_branch_distance_organic.value); this->support_tree_angle = std::clamp(config.tree_support_branch_angle_organic * M_PI / 180., 0., 0.5 * M_PI - EPSILON); this->support_tree_angle_slow = std::clamp(config.tree_support_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON); @@ -100,26 +91,6 @@ struct TreeSupportMeshGroupSettings { this->support_tree_top_rate = config.tree_support_top_rate.value; // percent // this->support_tree_tip_diameter = this->support_line_width; this->support_tree_tip_diameter = std::clamp(scaled(config.tree_support_tip_diameter.value), (coord_t)0, this->support_tree_branch_diameter); - - std::cout << "\n---------------\n" - << "layer_height: " << layer_height << "\nresolution: " << resolution << "\nmin_feature_size: " << min_feature_size - << "\nsupport_angle: " << support_angle << "\nconfig.support_threshold_angle: " << config.support_threshold_angle << "\nsupport_line_width: " << support_line_width - << "\nsupport_roof_line_width: " << support_roof_line_width << "\nsupport_bottom_enable: " << support_bottom_enable - << "\nsupport_bottom_height: " << support_bottom_height - << "\nsupport_material_buildplate_only: " << support_material_buildplate_only - << "\nsupport_xy_distance: " << support_xy_distance << "\nsupport_xy_distance_overhang: " << support_xy_distance_overhang - << "\nsupport_top_distance: " << support_top_distance << "\nsupport_bottom_distance: " << support_bottom_distance - << "\nsupport_roof_enable: " << support_roof_enable << "\nsupport_roof_layers: " << support_roof_layers - << "\nsupport_floor_enable: " << support_floor_enable << "\nsupport_floor_layers: " << support_floor_layers - << "\nsupport_roof_pattern: " << support_roof_pattern << "\nsupport_pattern: " << support_pattern - << "\nsupport_line_spacing: " << support_line_spacing << "\nsupport_wall_count: " << support_wall_count - << "\nsupport_roof_line_distance: " << support_roof_line_distance - << "\nsupport_tree_branch_distance: " << support_tree_branch_distance - << "\nsupport_tree_angle_slow: " << support_tree_angle_slow - << "\nsupport_tree_branch_diameter: " << support_tree_branch_diameter - << "\nsupport_tree_branch_diameter_angle: " << support_tree_branch_diameter_angle - << "\nsupport_tree_top_rate: " << support_tree_top_rate << "\nsupport_tree_tip_diameter: " << support_tree_tip_diameter - << "\n---------------\n"; } /*********************************************************************/ @@ -768,6 +739,17 @@ private: std::mutex m_mutex_layer_storage; }; +enum class LineStatus +{ + INVALID, + TO_MODEL, + TO_MODEL_GRACIOUS, + TO_MODEL_GRACIOUS_SAFE, + TO_BP, + TO_BP_SAFE +}; + + } // namespace TreeSupport3D } // namespace Slic3r diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 05822fdfdb..4ae655c2cf 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1496,43 +1496,6 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) m_config_manipulation.apply(m_config, &new_conf); } - // BBS popup a message to ask the user to set optimum parameters for tree support - if (opt_key == "support_type" || opt_key == "support_style") { - if (is_tree_slim(m_config->opt_enum("support_type"), m_config->opt_enum("support_style")) && - !(m_config->opt_float("support_top_z_distance") == 0 && m_config->opt_int("support_interface_top_layers") == 0 && m_config->opt_int("tree_support_wall_count") == 2)) { - wxString msg_text = _L("We have added an experimental style \"Tree Slim\" that features smaller support volume but weaker strength.\n" - "We recommend using it with: 0 interface layers, 0 top distance, 2 walls."); - msg_text += "\n\n" + _L("Change these settings automatically? \n" - "Yes - Change these settings automatically\n" - "No - Do not change these settings for me"); - MessageDialog dialog(wxGetApp().plater(), msg_text, "Suggestion", wxICON_WARNING | wxYES | wxNO); - DynamicPrintConfig new_conf = *m_config; - if (dialog.ShowModal() == wxID_YES) { - new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0)); - new_conf.set_key_value("support_interface_top_layers", new ConfigOptionInt(0)); - new_conf.set_key_value("tree_support_wall_count", new ConfigOptionInt(2)); - m_config_manipulation.apply(m_config, &new_conf); - } - wxGetApp().plater()->update(); - } else if ((m_config->opt_enum("support_type")==stTreeAuto && (m_config->opt_enum("support_style")==smsTreeStrong || m_config->opt_enum("support_style") == smsTreeHybrid)) && - !((m_config->opt_float("support_top_z_distance") >=0.1 || is_support_filament(m_config->opt_int("support_interface_filament") - 1)) - && m_config->opt_int("support_interface_top_layers") >1) ) { - wxString msg_text = _L("For \"Tree Strong\" and \"Tree Hybrid\" styles, we recommend the following settings: at least 2 interface layers, at least 0.1mm top z distance or using support materials on interface."); - msg_text += "\n\n" + _L("Change these settings automatically? \n" - "Yes - Change these settings automatically\n" - "No - Do not change these settings for me"); - MessageDialog dialog(wxGetApp().plater(), msg_text, "Suggestion", wxICON_WARNING | wxYES | wxNO); - DynamicPrintConfig new_conf = *m_config; - if (dialog.ShowModal() == wxID_YES) { - if (!is_support_filament(m_config->opt_int("support_interface_filament") - 1) && m_config->opt_float("support_top_z_distance") < 0.1) - new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0.2)); - new_conf.set_key_value("support_interface_top_layers", new ConfigOptionInt(2)); - m_config_manipulation.apply(m_config, &new_conf); - } - wxGetApp().plater()->update(); - } - } - // BBS popup a message to ask the user to set optimum parameters for support interface if support materials are used if (opt_key == "support_interface_filament") { int interface_filament_id = m_config->opt_int("support_interface_filament") - 1; // the displayed id is based from 1, while internal id is based from 0 From 5054ee85082a42ea31982e718cf0ebe9c5853962 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 22 Jul 2024 10:52:37 +0800 Subject: [PATCH 44/72] ENH: improve hybrid tree support 1. do not add interface for small overhangs so supports are easier to remove 2. calculate avoidance more accurately using real layer height jira: STUDIO-6285 3. hybrid nodes won't collide with lower layers 4. calculate max move more accurately 5. do not increase radius if next layer has collision jira: STUDIO-2296, STUDIO-7883 6. rewrite plan_layer_heights to prevent support layers overlap. Now the tree support layers are completely independent to object layers. 6. increase collision areas for interface. The top layers may be too close to interface with adaptive layer heights and very small overhang angle Change-Id: I052c3f66e68afb7663e2d70c846dd09ed7086071 (cherry picked from commit aca511caebfdeec270d4fc0ec6bbbadde77cddc9) (cherry picked from commit f2fc996652b3b204b4e554f57afed8519feb0397) --- src/libslic3r/PrintObject.cpp | 8 +- src/libslic3r/Support/TreeSupport.cpp | 600 +++++++++++++----------- src/libslic3r/Support/TreeSupport.hpp | 52 +- src/libslic3r/Support/TreeSupport3D.cpp | 242 +++++----- src/libslic3r/Support/TreeSupport3D.hpp | 2 +- 5 files changed, 479 insertions(+), 425 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a25df3451a..7f3945042a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -845,14 +845,8 @@ void PrintObject::clear_support_layers() std::shared_ptr PrintObject::alloc_tree_support_preview_cache() { if (!m_tree_support_preview_cache) { - const coordf_t layer_height = m_config.layer_height.value; const coordf_t xy_distance = m_config.support_object_xy_distance.value; - const double angle = m_config.tree_support_branch_angle.value * M_PI / 180.; - const coordf_t max_move_distance - = (angle < M_PI / 2) ? (coordf_t)(tan(angle) * layer_height) : std::numeric_limits::max(); - const coordf_t radius_sample_resolution = g_config_tree_support_collision_resolution; - - m_tree_support_preview_cache = std::make_shared(*this, xy_distance, max_move_distance, radius_sample_resolution); + m_tree_support_preview_cache = std::make_shared(*this, xy_distance, g_config_tree_support_collision_resolution); } return m_tree_support_preview_cache; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 37613bcb8c..7265945b3b 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -3,8 +3,6 @@ #include "ClipperUtils.hpp" #include "Fill/FillBase.hpp" -#include "Fill/FillBase.hpp" -#include "Fill/FillConcentric.hpp" #include "I18N.hpp" #include "Layer.hpp" #include "MinimumSpanningTree.hpp" @@ -616,14 +614,15 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p is_slim = is_tree_slim(support_type, m_support_params.support_style); is_strong = is_tree(support_type) && m_support_params.support_style == smsTreeStrong; - MAX_BRANCH_RADIUS = 10.0; - tree_support_branch_diameter_angle = 5.0;//is_slim ? 10.0 : 5.0; + base_radius = std::max(MIN_BRANCH_RADIUS, m_object_config->tree_support_branch_diameter.value / 2); // by default tree support needs no infill, unless it's tree hybrid which contains normal nodes. with_infill = support_pattern != smpNone && support_pattern != smpDefault; m_machine_border.contour = get_bed_shape_with_excluded_area(*m_print_config); Vec3d plate_offset = m_object->print()->get_plate_origin(); // align with the centered object in current plate (may not be the 1st plate, so need to add the plate offset) m_machine_border.translate(Point(scale_(plate_offset(0)), scale_(plate_offset(1))) - m_object->instances().front().shift); + top_z_distance = m_object_config->support_top_z_distance.value; + if (top_z_distance > EPSILON) top_z_distance = std::max(top_z_distance, float(m_slicing_params.min_layer_height)); #ifdef SUPPORT_TREE_DEBUG_TO_SVG SVG svg(debug_out_path("machine_boarder.svg"), m_object->bounding_box()); if (svg.is_opened()) svg.draw(m_machine_border, "yellow"); @@ -661,7 +660,6 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) static const double sharp_tail_max_support_height = 16.f; // a region is considered well supported if the number of layers below it exceeds this threshold const int thresh_layers_below = 10 / config.layer_height; - double obj_height = m_object->size().z(); // +1 makes the threshold inclusive double thresh_angle = config.support_threshold_angle.value > EPSILON ? config.support_threshold_angle.value + 1 : 30; thresh_angle = std::min(thresh_angle, 89.); // should be smaller than 90 @@ -1160,17 +1158,6 @@ void TreeSupport::create_tree_support_layers() } m_raft_layers = layer_id; } - - for (Layer *layer : m_object->layers()) { - SupportLayer* ts_layer = m_object->add_tree_support_layer(layer_id++, layer->height, layer->print_z, layer->slice_z); - if (ts_layer->id() > m_raft_layers) { - SupportLayer* lower_layer = m_object->get_support_layer(ts_layer->id() - 1); - if (lower_layer) { - lower_layer->upper_layer = ts_layer; - ts_layer->lower_layer = lower_layer; - } - } - } } static inline BoundingBox fill_expolygon_generate_paths( @@ -1631,6 +1618,36 @@ void deleteDirectoryContents(const std::filesystem::path& dir) std::filesystem::remove_all(entry.path()); } + +void TreeSupport::move_bounds_to_contact_nodes(std::vector &move_bounds, + PrintObject &print_object, + const TreeSupport3D::TreeSupportSettings &config) +{ + m_ts_data = print_object.alloc_tree_support_preview_cache(); + // convert move_bounds back to Support Nodes for tree skeleton preview + this->contact_nodes.resize(move_bounds.size()); + for (int layer_nr = move_bounds.size() - 1; layer_nr >= 0; layer_nr--) { + TreeSupport3D::SupportElements &elements = move_bounds[layer_nr]; + auto &contact_nodes_layer = this->contact_nodes[layer_nr]; + for (size_t i = 0; i < elements.size(); i++) { + auto &elem = elements[i]; + auto &state = elem.state; + state.print_z = layer_z(print_object.slicing_parameters(), config, layer_nr); + auto node = this->create_node(state.result_on_layer, state.distance_to_top, layer_nr, 0, state.to_buildplate, nullptr, state.print_z, config.layer_height); + contact_nodes_layer.push_back(node); + if (layer_nr < move_bounds.size() - 1 && !elem.parents.empty()) { + node->parent = contact_nodes[layer_nr + 1][elem.parents.front()]; + for (int j : elem.parents) { + contact_nodes[layer_nr + 1][j]->child = node; + node->parents.push_back(contact_nodes[layer_nr + 1][j]); + } + } + + if (i == 0) { m_ts_data->layer_heights.emplace_back(state.print_z, config.layer_height, std::max(0, layer_nr - 1)); } + } + } +} + void TreeSupport::generate() { if (m_support_params.support_style == smsTreeOrganic) { @@ -1656,6 +1673,7 @@ void TreeSupport::generate() generate_contact_points(); profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES); + m_ts_data->layer_heights = plan_layer_heights(); //Drop nodes to lower layers. profiler.stage_start(STAGE_DROP_DOWN_NODES); @@ -1697,9 +1715,8 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, size_t layers_to_ } else { radius = base_radius * (layers_to_top + 1) / (tip_layers * 2); } - radius = std::max(radius, MIN_BRANCH_RADIUS); } - radius = std::min(radius, MAX_BRANCH_RADIUS); + radius = std::clamp(radius, MIN_BRANCH_RADIUS, MAX_BRANCH_RADIUS); return radius; } @@ -1715,18 +1732,22 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_to { radius = mm_to_top;// this is a 45 degree tip } - radius = std::max(radius, MIN_BRANCH_RADIUS); - radius = std::min(radius, MAX_BRANCH_RADIUS); + radius = std::clamp(radius, MIN_BRANCH_RADIUS, MAX_BRANCH_RADIUS); // if have interface layers, radius should be larger if (m_object_config->support_interface_top_layers.value > 0) radius = std::max(radius, base_radius); return radius; } -coordf_t TreeSupport::get_radius(const SupportNode* node, coordf_t base_radius) +coordf_t TreeSupport::calc_radius(coordf_t mm_to_top) +{ + return calc_branch_radius(base_radius, mm_to_top, diameter_angle_scale_factor); +} + +coordf_t TreeSupport::get_radius(const SupportNode* node) { if (node->radius == 0) - node->radius = calc_branch_radius(base_radius, node->dist_mm_to_top, node->diameter_angle_scale_factor); + node->radius = calc_radius(node->dist_mm_to_top); return node->radius; } @@ -1916,11 +1937,9 @@ void TreeSupport::draw_circles() const coordf_t layer_height = config.layer_height.value; const size_t top_interface_layers = config.support_interface_top_layers.value; const size_t bottom_interface_layers = config.support_interface_bottom_layers.value; - const double diameter_angle_scale_factor = tan(tree_support_branch_diameter_angle * M_PI / 180.);// * layer_height / branch_radius; //Scale factor per layer to produce the desired angle. const double nozzle_diameter = m_object->print()->config().nozzle_diameter.get_at(0); const coordf_t line_width = config.get_abs_value("support_line_width", nozzle_diameter); const coordf_t line_width_scaled = scale_(line_width); - const bool with_lightning_infill = m_support_params.base_fill_pattern == ipLightning; coordf_t support_extrusion_width = m_support_params.support_extrusion_width; const float tree_brim_width = config.tree_support_brim_width.value; @@ -1929,7 +1948,7 @@ void TreeSupport::draw_circles() return; BOOST_LOG_TRIVIAL(info) << "draw_circles for object: " << m_object->model_object()->name; - tbb::parallel_for(tbb::blocked_range(0, m_object->layer_count()), + tbb::parallel_for(tbb::blocked_range(0, contact_nodes.size()), [&](const tbb::blocked_range& range) { for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) @@ -1950,6 +1969,7 @@ void TreeSupport::draw_circles() ts_layer->print_z = m_ts_data->layer_heights[layer_nr].print_z; ts_layer->height = m_ts_data->layer_heights[layer_nr].height; + size_t obj_layer_nr = m_ts_data->layer_heights[layer_nr].obj_layer_nr; if (ts_layer->height < EPSILON) { continue; } @@ -1967,17 +1987,23 @@ void TreeSupport::draw_circles() bool need_extra_wall = false; ExPolygons collision_sharp_tails; ExPolygons collision_base; - auto get_collision = [&](bool sharp_tail) -> ExPolygons& { - if (sharp_tail) { - if (collision_sharp_tails.empty()) - collision_sharp_tails = m_ts_data->get_collision(m_object_config->support_top_z_distance.value, layer_nr); - return collision_sharp_tails; - } - else { - if (collision_base.empty()) - collision_base = m_ts_data->get_collision((layer_nr == 0) ? config.support_object_first_layer_gap : m_ts_data->m_xy_distance, layer_nr); - return collision_base; + auto get_collision = [&](bool sharp_tail) -> ExPolygons &{ + ExPolygons &collision = sharp_tail ? collision_sharp_tails : collision_base; + if (collision.empty()) { + collision = offset_ex(m_ts_data->m_layer_outlines[obj_layer_nr], + sharp_tail ? scale_(top_z_distance) : + scale_((obj_layer_nr == 0) ? config.support_object_first_layer_gap : m_ts_data->m_xy_distance)); + // the top layers may be too close to interface with adaptive layer heights and very small overhang angle + if (top_z_distance > EPSILON) { + float accum_height = 0; + for (size_t layer_id = obj_layer_nr + 1; + layer_id < m_ts_data->m_layer_outlines.size() && (accum_height += m_object->get_layer(layer_id)->height) && accum_height <= top_z_distance; + layer_id++) { + collision = union_ex(collision, offset_ex(m_ts_data->m_layer_outlines[layer_id], scale_(top_z_distance))); + } + } } + return collision; }; BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << curr_layer_nodes.size(); @@ -2027,7 +2053,7 @@ void TreeSupport::draw_circles() circle.points[i] = circle.points[i] * scale + node.position; } } - if (layer_nr == 0 && m_raft_layers == 0) { + if (obj_layer_nr == 0 && m_raft_layers == 0) { double brim_width = !config.tree_support_auto_brim ? tree_brim_width : std::max(MIN_BRANCH_RADIUS_FIRST_LAYER, std::min(node.radius + node.dist_mm_to_top / (scale * branch_radius) * 0.5, MAX_BRANCH_RADIUS_FIRST_LAYER) - node.radius); auto tmp=offset(circle, scale_(brim_width)); if(!tmp.empty()) @@ -2040,21 +2066,20 @@ void TreeSupport::draw_circles() if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1) overhang_expanded.emplace_back(node.overhang); else { - // 对于有缺陷的模型,overhang膨胀以后可能是空的! overhang_expanded = offset_ex({ node.overhang }, scale_(m_ts_data->m_xy_distance)); } append(area, overhang_expanded); } } - if (layer_nr>0 && node.distance_to_top < 0) + if (obj_layer_nr>0 && node.distance_to_top < 0) append(roof_gap_areas, area); - else if (layer_nr > 0 && node.support_roof_layers_below == 1 && node.is_sharp_tail==false) + else if (obj_layer_nr > 0 && node.support_roof_layers_below == 1 && node.is_sharp_tail==false) { append(roof_1st_layer, area); max_layers_above_roof1 = std::max(max_layers_above_roof1, node.dist_mm_to_top); } - else if (layer_nr > 0 && node.support_roof_layers_below > 0 && node.is_sharp_tail==false) + else if (obj_layer_nr > 0 && node.support_roof_layers_below > 0 && node.is_sharp_tail == false) { append(roof_areas, area); max_layers_above_roof = std::max(max_layers_above_roof, node.dist_mm_to_top); @@ -2071,9 +2096,8 @@ void TreeSupport::draw_circles() //m_object->print()->set_status(65, (boost::format( _u8L("Support: generate polygons at layer %d")) % layer_nr).str()); // join roof segments - double contact_dist_scaled = scale_(0.5);// scale_(m_slicing_params.gap_support_object); - roof_areas = std::move(offset2_ex(roof_areas, contact_dist_scaled, -contact_dist_scaled)); - roof_1st_layer = std::move(offset2_ex(roof_1st_layer, contact_dist_scaled, -contact_dist_scaled)); + roof_areas = diff_clipped(offset2_ex(roof_areas, line_width_scaled, -line_width_scaled), get_collision(false)); + roof_1st_layer = diff_clipped(offset2_ex(roof_1st_layer, line_width_scaled, -line_width_scaled), get_collision(false)); // roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer roof_1st_layer = diff_ex(roof_1st_layer, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roof_areas,get_extents(roof_1st_layer))); @@ -2097,13 +2121,11 @@ void TreeSupport::draw_circles() { // find the lowest interface layer // TODO the gap may not be exact when "independent support layer height" is enabled - size_t layer_nr_next = layer_nr; - for (size_t i = 0; i < bottom_interface_layers && layer_nr_next>0; i++) { - layer_nr_next = m_ts_data->layer_heights[layer_nr_next].next_layer_nr; - } - for (size_t i = 0; i <= bottom_gap_layers && i<=layer_nr_next; i++) + size_t layer_nr_next = layer_nr - bottom_interface_layers; + size_t obj_layer_nr_next = m_ts_data->layer_heights[layer_nr_next].obj_layer_nr; + for (size_t i = 0; i <= bottom_gap_layers && i <= obj_layer_nr_next; i++) { - const Layer* below_layer = m_object->get_layer(layer_nr_next - i); + const Layer *below_layer = m_object->get_layer(obj_layer_nr_next - i); ExPolygons bottom_interface = intersection_ex(base_areas, below_layer->lslices); floor_areas.insert(floor_areas.end(), bottom_interface.begin(), bottom_interface.end()); } @@ -2115,7 +2137,7 @@ void TreeSupport::draw_circles() } } if (bottom_gap_layers > 0 && layer_nr > bottom_gap_layers) { - const Layer* below_layer = m_object->get_layer(layer_nr - bottom_gap_layers); + const Layer* below_layer = m_object->get_layer(m_ts_data->layer_heights[layer_nr].obj_layer_nr - bottom_gap_layers); ExPolygons bottom_gap_area = intersection_ex(floor_areas, below_layer->lslices); if (!bottom_gap_area.empty()) { floor_areas = std::move(diff_ex(floor_areas, bottom_gap_area)); @@ -2165,7 +2187,7 @@ void TreeSupport::draw_circles() std::vector contours; std::vector overhangs; - for (int layer_nr = 1; layer_nr < m_object->layer_count(); layer_nr++) { + for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) { if (print->canceled()) break; const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); @@ -2253,7 +2275,7 @@ void TreeSupport::draw_circles() // polygon pointer: depth, direction, farPoint std::map> holePropagationInfos; - for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { + for (int layer_nr = contact_nodes.size() - 1; layer_nr > 0; layer_nr--) { if (print->canceled()) break; //m_object->print()->set_status(66, (boost::format(_u8L("Support: fix holes at layer %d")) % layer_nr).str()); @@ -2327,7 +2349,7 @@ void TreeSupport::draw_circles() // make sure 1) roof1 and object 2) roof1 and roof, won't intersect // Note: We can't replace roof1 directly, as we have recorded its address. // So instead we need to replace its members one by one. - auto tmp1 = diff_ex(roof1, get_collision((layer_nr == 0) ? config.support_object_xy_distance : m_ts_data->m_xy_distance, layer_nr)); + auto tmp1 = diff_ex(roof1, get_collision(0, m_ts_data->layer_heights[layer_nr].obj_layer_nr)); tmp1 = diff_ex(tmp1, ts_layer->roof_areas); if (!tmp1.empty()) { roof1.contour = std::move(tmp1[0].contour); @@ -2344,7 +2366,7 @@ void TreeSupport::draw_circles() #ifdef SUPPORT_TREE_DEBUG_TO_SVG - for (int layer_nr = m_object->layer_count() - 1; layer_nr >= 0; layer_nr--) { + for (int layer_nr = m_object->support_layer_count() - 1; layer_nr >= 0; layer_nr--) { SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); ExPolygons& base_areas = ts_layer->base_areas; ExPolygons& roof_areas = ts_layer->roof_areas; @@ -2377,35 +2399,24 @@ void TreeSupport::drop_nodes() double tan_angle = tan(angle); // when nodes are thick, they can move further. this is the max angle const coordf_t max_move_distance = (angle < M_PI / 2) ? (coordf_t)(tan_angle * layer_height)*wall_count : std::numeric_limits::max(); const double max_move_distance2 = max_move_distance * max_move_distance; - const coordf_t branch_radius = config.tree_support_branch_diameter.value / 2; - const size_t tip_layers = branch_radius / layer_height; //The number of layers to be shrinking the circle to create a tip. This produces a 45 degree angle. - const double diameter_angle_scale_factor = tan(tree_support_branch_diameter_angle * M_PI / 180.);//*layer_height / branch_radius; // Scale factor per layer to produce the desired angle. + const size_t tip_layers = base_radius / layer_height; //The number of layers to be shrinking the circle to create a tip. This produces a 45 degree angle. const coordf_t radius_sample_resolution = m_ts_data->m_radius_sample_resolution; const bool support_on_buildplate_only = config.support_on_build_plate_only.value; const size_t bottom_interface_layers = config.support_interface_bottom_layers.value; const size_t top_interface_layers = config.support_interface_top_layers.value; SupportNode::diameter_angle_scale_factor = diameter_angle_scale_factor; float DO_NOT_MOVER_UNDER_MM = is_slim ? 0 : 5; // do not move contact points under 5mm - const auto nozzle_diameter = m_object->print()->config().nozzle_diameter.get_at(m_object->config().support_interface_filament-1); - const auto support_line_width = config.support_line_width.get_abs_value(nozzle_diameter); - auto get_max_move_dist = [this, &config, branch_radius, tip_layers, diameter_angle_scale_factor, wall_count, support_extrusion_width, support_line_width](const SupportNode *node, int power = 1) { - double move_dist = node->max_move_dist; + auto get_max_move_dist = [this, &config, tan_angle, wall_count, support_extrusion_width](const SupportNode *node, int power = 1) { if (node->max_move_dist == 0) { - node->radius = get_radius(node, branch_radius); - double angle = config.tree_support_branch_angle.value; - if (angle > 30.0 && node->radius > MIN_BRANCH_RADIUS) - angle = (node->radius - MIN_BRANCH_RADIUS) / (MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS) * (config.tree_support_branch_angle.value - 30.0) + 30.0; - double tan_angle = tan(angle * M_PI / 180); - node->max_move_dist = (angle < 90) ? (coordf_t) (tan_angle * node->height) : std::numeric_limits::max(); - node->max_move_dist = std::min(node->max_move_dist, support_extrusion_width); - move_dist = node->max_move_dist; + node->radius = get_radius(node); + node->max_move_dist = std::min(tan_angle * node->height, support_extrusion_width); } + double move_dist = node->max_move_dist; if (power == 2) move_dist = SQ(move_dist); return move_dist; }; - m_ts_data->layer_heights = plan_layer_heights(); std::vector &layer_heights = m_ts_data->layer_heights; if (layer_heights.empty()) return; @@ -2420,27 +2431,28 @@ void TreeSupport::drop_nodes() std::vector > all_layer_radius(contact_nodes.size()); std::vector> all_layer_node_dist(contact_nodes.size()); for (size_t layer_nr = contact_nodes.size() - 1; layer_nr > 0; layer_nr--) { - if (layer_heights[layer_nr].height < EPSILON) continue; auto& layer_radius = all_layer_radius[layer_nr]; auto& layer_node_dist = all_layer_node_dist[layer_nr]; for (auto* p_node : contact_nodes[layer_nr]) { layer_node_dist.emplace(p_node->dist_mm_to_top); } - size_t layer_nr_next = layer_heights[layer_nr].next_layer_nr; + size_t layer_nr_next = layer_nr - 1; if (layer_nr_next <= contact_nodes.size() - 1 && layer_nr_next > 0) { for (auto node_dist : layer_node_dist) all_layer_node_dist[layer_nr_next].emplace(node_dist + layer_heights[layer_nr].height); } for (auto node_dist : layer_node_dist) { - layer_radius.emplace(calc_branch_radius(branch_radius, node_dist, diameter_angle_scale_factor)); + layer_radius.emplace(calc_radius(node_dist)); } } // parallel pre-compute avoidance tbb::parallel_for(tbb::blocked_range(0, contact_nodes.size() - 1), [&](const tbb::blocked_range &range) { for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { for (auto node_radius : all_layer_radius[layer_nr]) { - m_ts_data->get_avoidance(node_radius, layer_nr); - get_collision(m_ts_data->m_xy_distance, layer_nr); + size_t obj_layer_nr= layer_heights[layer_nr].obj_layer_nr; + m_ts_data->get_avoidance(node_radius, obj_layer_nr); + get_collision(0, obj_layer_nr); + get_collision(node_radius, obj_layer_nr); } } }); @@ -2462,16 +2474,18 @@ void TreeSupport::drop_nodes() if (layer_contact_nodes.empty()) continue; - int layer_nr_next = layer_heights[layer_nr].next_layer_nr; + int layer_nr_next = layer_nr - 1; coordf_t print_z = layer_heights[layer_nr].print_z; coordf_t print_z_next = layer_heights[layer_nr_next].print_z; - coordf_t height_next = layer_heights[layer_nr_next].height; + coordf_t height_next = layer_heights[layer_nr_next].height; + size_t obj_layer_nr = layer_heights[layer_nr].obj_layer_nr; + size_t obj_layer_nr_next = layer_heights[layer_nr_next].obj_layer_nr; std::deque> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches. m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating support"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str()); - Polygons layer_contours = std::move(m_ts_data->get_contours_with_holes(layer_nr)); + Polygons layer_contours = std::move(m_ts_data->get_contours_with_holes(obj_layer_nr)); //std::unordered_map& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr]; tbb::concurrent_unordered_map mst_line_x_layer_contour_cache; auto is_line_cut_by_contour = [&mst_line_x_layer_contour_cache,&layer_contours](Point a, Point b) @@ -2495,7 +2509,7 @@ void TreeSupport::drop_nodes() }; //Group together all nodes for each part. - const ExPolygons& parts = m_ts_data->m_layer_outlines_below[layer_nr]; + const ExPolygons& parts = m_ts_data->m_layer_outlines_below[obj_layer_nr]; std::vector> nodes_per_part(1 + parts.size()); //All nodes that aren't inside a part get grouped together in the 0th part. for (SupportNode* p_node : layer_contact_nodes) { @@ -2563,19 +2577,18 @@ void TreeSupport::drop_nodes() #ifdef SUPPORT_TREE_DEBUG_TO_SVG coordf_t branch_radius_temp = 0; coordf_t max_y = std::numeric_limits::min(); - draw_layer_mst(debug_out_path("mtree_%.2f.svg", print_z), spanning_trees, m_object->get_layer(layer_nr)->lslices); + draw_layer_mst(debug_out_path("mtree_%.2f.svg", print_z), spanning_trees, m_object->get_layer(obj_layer_nr)->lslices_extrudable); #endif for (size_t group_index = 0; group_index < nodes_per_part.size(); group_index++) { auto& nodes_this_part = nodes_per_part[group_index]; const MinimumSpanningTree& mst = spanning_trees[group_index]; //In the first pass, merge all nodes that are close together. - tbb::concurrent_unordered_set to_delete; std::vector> nodes_vec(nodes_this_part.begin(), nodes_this_part.end()); tbb::parallel_for_each(nodes_vec.begin(), nodes_vec.end(), [&](const std::pair& entry) { SupportNode* p_node = entry.second; SupportNode& node = *p_node; - if (to_delete.find(p_node) != to_delete.end()) + if (!p_node->valid) { return; //Delete this node (don't create a new node for it on the next layer). } @@ -2595,17 +2608,17 @@ void TreeSupport::drop_nodes() node.dist_mm_to_top = std::max(node.dist_mm_to_top, neighbour_node->dist_mm_to_top); node.merged_neighbours.push_front(neighbour_node); node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end()); - to_delete.insert(neighbour_node); + neighbour_node->valid = false; } } - } - else if (neighbours.size() == 1 && vsize2_with_unscale(neighbours[0] - node.position) < max_move_distance2 && mst.adjacent_nodes(neighbours[0]).size() == 1 && + } else if (neighbours.size() == 1 && vsize2_with_unscale(neighbours[0] - node.position) < get_max_move_dist(p_node, 2) && + mst.adjacent_nodes(neighbours[0]).size() == 1 && nodes_this_part[neighbours[0]]->type!=ePolygon) // We have just two nodes left, and they're very close, and the only neighbor is not ePolygon { //Insert a completely new node and let both original nodes fade. Point next_position = (node.position + neighbours[0]) / 2; //Average position of the two nodes. - coordf_t next_radius = calc_branch_radius(branch_radius, node.dist_mm_to_top+height_next, diameter_angle_scale_factor); - auto avoid_layer = get_avoidance(next_radius, layer_nr_next); + coordf_t next_radius = calc_radius(node.dist_mm_to_top+height_next); + auto avoid_layer = get_avoidance(next_radius, obj_layer_nr_next); if (group_index == 0) { //Avoid collisions. @@ -2621,14 +2634,14 @@ void TreeSupport::drop_nodes() node_parent = p_node->parent ? p_node : neighbour; // Make sure the next pass doesn't drop down either of these (since that already happened). node_parent->merged_neighbours.push_front(node_parent == p_node ? neighbour : p_node); - const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_position); - SupportNode* next_node = m_ts_data->create_node(next_position, node_parent->distance_to_top + 1, layer_nr_next, node_parent->support_roof_layers_below - 1, to_buildplate, node_parent, + const bool to_buildplate = !is_inside_ex(get_collision(0, obj_layer_nr_next), next_position); + SupportNode* next_node = m_ts_data->create_node(next_position, node_parent->distance_to_top + 1, obj_layer_nr_next, node_parent->support_roof_layers_below - 1, to_buildplate, node_parent, print_z_next, height_next); get_max_move_dist(next_node); m_ts_data->m_mutex.lock(); contact_nodes[layer_nr_next].push_back(next_node); - to_delete.insert(neighbour); - to_delete.insert(p_node); + neighbour->valid = false; + p_node->valid = false; m_ts_data->m_mutex.unlock(); } else if (neighbours.size() > 1) //Don't merge leaf nodes because we would then incur movement greater than the maximum move distance. @@ -2636,7 +2649,7 @@ void TreeSupport::drop_nodes() //Remove all neighbours that are too close and merge them into this node. for (const Point& neighbour : neighbours) { - if (vsize2_with_unscale(neighbour - node.position) < /*max_move_distance2*/get_max_move_dist(&node,2)) + if (vsize2_with_unscale(neighbour - node.position) < get_max_move_dist(&node,2)) { SupportNode* neighbour_node = nodes_this_part[neighbour]; if (neighbour_node->type == ePolygon) continue; @@ -2644,11 +2657,10 @@ void TreeSupport::drop_nodes() if(node.dist_mm_to_top < neighbour_node->dist_mm_to_top) continue; m_ts_data->m_mutex.lock(); - if (to_delete.find(p_node) == to_delete.end()) + if (p_node->valid) { // since we are processing all nodes in parallel, p_node may have been deleted by another thread. In this case, we should not delete neighbour_node. node.merged_neighbours.push_front(neighbour_node); node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end()); - to_delete.insert(neighbour_node); neighbour_node->valid = false; } m_ts_data->m_mutex.unlock(); @@ -2663,7 +2675,7 @@ void TreeSupport::drop_nodes() SupportNode* p_node = entry.second; const SupportNode& node = *p_node; - if (to_delete.find(p_node) != to_delete.end()) + if (!p_node->valid) { return; } @@ -2671,7 +2683,7 @@ void TreeSupport::drop_nodes() // polygon node do not merge or move const bool to_buildplate = true; // keep only the part that won't be removed by the next layer - ExPolygons overhangs_next = diff_clipped({ node.overhang }, get_collision_polys(0, layer_nr_next)); + ExPolygons overhangs_next = diff_clipped({ node.overhang }, get_collision(0, obj_layer_nr_next)); // find the biggest overhang if there are many float area_biggest = -1; int index_biggest = -1; @@ -2683,7 +2695,7 @@ void TreeSupport::drop_nodes() } } if (index_biggest >= 0) { - SupportNode* next_node = m_ts_data->create_node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, + SupportNode* next_node = m_ts_data->create_node(p_node->position, p_node->distance_to_top + 1, obj_layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); next_node->max_move_dist = 0; next_node->overhang = std::move(overhangs_next[index_biggest]); @@ -2696,11 +2708,11 @@ void TreeSupport::drop_nodes() } //If the branch falls completely inside a collision area (the entire branch would be removed by the X/Y offset), delete it. - if (group_index > 0 && is_inside_ex(get_collision(m_ts_data->m_xy_distance, layer_nr), node.position)) + if (group_index > 0 && is_inside_ex(get_collision(0, obj_layer_nr), node.position)) { std::scoped_lock lock(m_ts_data->m_mutex); - const coordf_t branch_radius_node = get_radius(p_node, branch_radius); - Point to_outside = projection_onto(get_collision(m_ts_data->m_xy_distance, layer_nr), node.position); + const coordf_t branch_radius_node = get_radius(p_node); + Point to_outside = projection_onto(get_collision(0, obj_layer_nr), node.position); double dist2_to_outside = vsize2_with_unscale(node.position - to_outside); if (dist2_to_outside >= branch_radius_node * branch_radius_node) //Too far inside. { @@ -2709,7 +2721,6 @@ void TreeSupport::drop_nodes() unsupported_branch_leaves.push_front({ layer_nr, p_node }); } else { - to_delete.insert(p_node); p_node->valid = false; } return; @@ -2717,7 +2728,6 @@ void TreeSupport::drop_nodes() // if the link between parent and current is cut by contours, mark current as bottom contact node if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false) { - to_delete.insert(p_node); p_node->valid = false; return; } @@ -2731,21 +2741,21 @@ void TreeSupport::drop_nodes() // 2. Only merge node with single neighbor in distance between [max_move_distance, 10mm/layer_height] float dist2_to_first_neighbor = neighbours.empty() ? 0 : vsize2_with_unscale(neighbours[0] - node.position); if (node.print_z > DO_NOT_MOVER_UNDER_MM && - (neighbours.size() > 1 || (neighbours.size() == 1 && dist2_to_first_neighbor >= max_move_distance2))) // Only nodes that aren't about to collapse. + (neighbours.size() > 1 || (neighbours.size() == 1 && dist2_to_first_neighbor >= get_max_move_dist(p_node, 2)))) // Only nodes that aren't about to collapse. { // Move towards the average position of all neighbours. Point sum_direction(0, 0); for (const Point &neighbour : neighbours) { // do not move to the neighbor to be deleted SupportNode *neighbour_node = nodes_this_part[neighbour]; - if (to_delete.find(neighbour_node) != to_delete.end()) continue; + if (!neighbour_node->valid) continue; Point direction = neighbour - node.position; // do not move to neighbor that's too far away (即使以最大速度移动,在接触热床之前都无法汇聚) float dist2_to_neighbor = vsize2_with_unscale(direction); - coordf_t branch_bottom_radius = calc_branch_radius(branch_radius, node.dist_mm_to_top + node.print_z, diameter_angle_scale_factor); - coordf_t neighbour_bottom_radius = calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top + neighbour_node->print_z, diameter_angle_scale_factor); + coordf_t branch_bottom_radius = calc_radius(node.dist_mm_to_top + node.print_z); + coordf_t neighbour_bottom_radius = calc_radius(neighbour_node->dist_mm_to_top + neighbour_node->print_z); double max_converge_distance = tan_angle * (p_node->print_z - DO_NOT_MOVER_UNDER_MM) + std::max(branch_bottom_radius, neighbour_bottom_radius); if (dist2_to_neighbor > max_converge_distance * max_converge_distance) continue; @@ -2760,10 +2770,10 @@ void TreeSupport::drop_nodes() if (!is_strong) move_to_neighbor_center = sum_direction; else { - if (vsize2_with_unscale(sum_direction) <= max_move_distance2) { + if (vsize2_with_unscale(sum_direction) <= get_max_move_dist(p_node, 2)) { move_to_neighbor_center = sum_direction; } else { - move_to_neighbor_center = normal(sum_direction, scale_(get_max_move_dist(&node))); + move_to_neighbor_center = normal(sum_direction, scale_(get_max_move_dist(p_node))); } } } @@ -2771,11 +2781,11 @@ void TreeSupport::drop_nodes() #ifdef SUPPORT_TREE_DEBUG_TO_SVG if (node.position(1) > max_y) { max_y = node.position(1); - branch_radius_temp = get_radius(p_node, branch_radius); + branch_radius_temp = get_radius(p_node); } #endif - coordf_t next_radius = calc_branch_radius(branch_radius, node.dist_mm_to_top + height_next, diameter_angle_scale_factor); - auto avoidance_next = get_avoidance(next_radius, layer_nr_next); + coordf_t next_radius = calc_radius(node.dist_mm_to_top + height_next); + auto avoidance_next = get_avoidance(next_radius, obj_layer_nr_next); Point to_outside = projection_onto(avoidance_next, node.position); Point direction_to_outer = to_outside - node.position; @@ -2783,13 +2793,13 @@ void TreeSupport::drop_nodes() // don't move if // 1) line of node and to_outside is cut by contour (means supports may intersect with object) // 2) it's impossible to move to build plate - if (is_line_cut_by_contour(node.position, to_outside) || dist2_to_outer > max_move_distance2 * SQ(layer_nr) || + if (is_line_cut_by_contour(node.position, to_outside) || dist2_to_outer > max_move_distance2 * SQ(obj_layer_nr) || !is_inside_ex(avoidance_next, node.position)) { // try move to outside of lower layer instead Point candidate_vertex = node.position; const coordf_t max_move_between_samples = max_move_distance + radius_sample_resolution + EPSILON; // 100 micron extra for rounding errors. // use get_collision instead of get_avoidance here (See STUDIO-4252) - bool is_outside = move_out_expolys(get_collision(next_radius,layer_nr_next), candidate_vertex, max_move_between_samples, max_move_between_samples); + bool is_outside = move_out_expolys(get_collision(next_radius,obj_layer_nr_next), candidate_vertex, max_move_between_samples, max_move_between_samples); if (is_outside) { direction_to_outer = candidate_vertex - node.position; dist2_to_outer = vsize2_with_unscale(direction_to_outer); @@ -2824,10 +2834,15 @@ void TreeSupport::drop_nodes() if (is_outside) { next_layer_vertex = candidate_vertex; } } } - - const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_layer_vertex); - SupportNode * next_node = m_ts_data->create_node(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, + auto next_collision = get_collision(0, obj_layer_nr_next); + const bool to_buildplate = !is_inside_ex(next_collision, next_layer_vertex); + SupportNode * next_node = m_ts_data->create_node(next_layer_vertex, node.distance_to_top + 1, obj_layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); + // don't increase radius if next node will collide partially with the object (STUDIO-7883) + to_outside = projection_onto(next_collision, next_node->position); + direction_to_outer = to_outside - node.position; + double dist_to_outer = unscale_(direction_to_outer.cast().norm()); + next_node->radius = std::max(node.radius, std::min(next_node->radius, dist_to_outer)); get_max_move_dist(next_node); m_ts_data->m_mutex.lock(); contact_nodes[layer_nr_next].push_back(next_node); @@ -2838,9 +2853,9 @@ void TreeSupport::drop_nodes() #ifdef SUPPORT_TREE_DEBUG_TO_SVG if (contact_nodes[layer_nr].empty() == false) { - draw_contours_and_nodes_to_svg(debug_out_path("contact_points_%.2f.svg", contact_nodes[layer_nr][0]->print_z), get_collision(0,layer_nr_next), - get_avoidance(branch_radius_temp, layer_nr), - m_ts_data->m_layer_outlines[layer_nr], + draw_contours_and_nodes_to_svg(debug_out_path("contact_points_%.2f.svg", contact_nodes[layer_nr][0]->print_z), get_collision(0,obj_layer_nr_next), + get_avoidance(branch_radius_temp, obj_layer_nr), + m_ts_data->m_layer_outlines[obj_layer_nr], contact_nodes[layer_nr], contact_nodes[layer_nr_next], { "overhang","avoid","outline" }, { "blue","red","yellow" }); BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer->next " << layer_nr << "->" << layer_nr_next << ", print_z=" << print_z @@ -2964,98 +2979,128 @@ void TreeSupport::smooth_nodes() std::vector TreeSupport::plan_layer_heights() { - std::vector layer_heights(contact_nodes.size()); - std::map> bounds; // layer_nr:(print_z, height) - - { + std::vector layer_heights; + std::map z_heights; // print_z:height + if (!m_support_params.independent_layer_height) { + layer_heights.resize(m_object->layer_count()); + for (int layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { + z_heights[m_object->get_layer(layer_nr)->print_z] = m_object->get_layer(layer_nr)->height; + layer_heights[layer_nr] = {m_object->get_layer(layer_nr)->print_z, m_object->get_layer(layer_nr)->height, size_t(layer_nr)}; + } + } else { + const coordf_t max_layer_height = m_slicing_params.max_suport_layer_height; + const coordf_t min_layer_height = m_slicing_params.min_layer_height; + std::map bounds; // print_z: height // Keep first layer still - layer_heights[0] = { m_object->get_layer(0)->print_z, m_object->get_layer(0)->height, 0 }; - bounds[0] = { m_object->get_layer(0)->print_z, m_object->get_layer(0)->height}; + bounds[m_object->get_layer(0)->print_z] = {m_object->get_layer(0)->height}; + std::vector obj_layer_zs; + obj_layer_zs.reserve(m_object->layer_count()); + for (const Layer *l : m_object->layers()) obj_layer_zs.emplace_back((float) l->print_z); + z_heights[m_object->get_layer(0)->print_z] = m_object->get_layer(0)->height; // Collect top contact layers for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) { if (!contact_nodes[layer_nr].empty()) { coordf_t print_z = contact_nodes[layer_nr].front()->print_z; coordf_t height = contact_nodes[layer_nr].front()->height; - if(height>m_slicing_params.max_suport_layer_height){ - // split this layer into multiple layers if the gap is too big - int num_layers=std::ceil(height/m_slicing_params.max_suport_layer_height); - coordf_t new_height= height/num_layers; - for(auto& node: contact_nodes[layer_nr]) { - node->height = new_height; - node->distance_to_top = -num_layers; - node->support_roof_layers_below+=num_layers-1; - } - - for (int i=0; ifirst; - coordf_t extr2z = it2->second.first - it2->second.second; // bottom_z of upper bound - int extr1_layer_nr = it1->first; //bounds[idx_extreme - 1]; - coordf_t extr1z = it1->second.first;// print_z of lower bound - coordf_t dist = extr2z - extr1z; + auto it2 = std::next(it1); + for (; it2 != bounds.end(); it2++) { + coordf_t z2 = it2->first; + coordf_t h2 = it2->second; + coordf_t z1 = it1->first; + coordf_t h1 = it1->second; + coordf_t dist = z2 - z1; + if (dist < min_layer_height - EPSILON) continue; - layer_heights[extr2_layer_nr].print_z = it2->second.first; - layer_heights[extr2_layer_nr].height = it2->second.second; - BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights0 print_z, height, layer_nr: " << layer_heights[extr2_layer_nr].print_z << " " << layer_heights[extr2_layer_nr].height << " " << extr2_layer_nr; + BOOST_LOG_TRIVIAL(trace) << format("plan_layer_heights0 (%.2f,%.2f)->(%.2f,%.2f): ", z1, h1, z2, h2); // Insert intermediate layers. - size_t n_layers_extra = m_support_params.independent_layer_height ? size_t(ceil(dist / (m_slicing_params.max_suport_layer_height))) : - size_t(ceil(dist / (m_slicing_params.min_layer_height))); - if (n_layers_extra < 1) - continue; - + size_t n_layers_extra = size_t(ceil(dist / max_layer_height)); coordf_t step = dist / coordf_t(n_layers_extra); - coordf_t print_z = extr1z; - for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; layer_nr++) { - Layer* layer = m_object->get_layer(layer_nr); - if (!m_support_params.independent_layer_height) step = layer->height; - if (std::abs((print_z+step) - layer->print_z) < step / 2 + EPSILON) { - print_z += step; - layer_heights[layer_nr].print_z = print_z; - layer_heights[layer_nr].height = step; - } - else { - // can't clear curr_layer_nodes, or the model will have empty layers - layer_heights[layer_nr].print_z = 0.0; - layer_heights[layer_nr].height = 0.0; - } + coordf_t print_z = z1 + step; + for (int i = 0; i < n_layers_extra; i++, print_z += step) { + z_heights[print_z] = step; + BOOST_LOG_TRIVIAL(debug) << "plan_layer_heights add entry print_z, height: " << print_z << " " << step; } + it1=it2; } - // fill in next_layer_nr - int i = layer_heights.size() - 1, j = i; - for (; i >= 0; i--) { - if (layer_heights[i].height < EPSILON) { - continue; + // map z_heights to layer_heights + int i = 0; + size_t obj_layer_nr = 0; + layer_heights.resize(z_heights.size()); + for (auto it = z_heights.begin(); it != z_heights.end(); it++, i++) { + coordf_t print_z = it->first; + coordf_t height = it->second; + while (obj_layer_nr < obj_layer_zs.size() && obj_layer_zs[obj_layer_nr] < print_z - height / 2) obj_layer_nr++; + layer_heights[i] = {print_z, height, obj_layer_nr}; + + } + } + + // add support layers according to layer_heights + int support_layer_nr = m_raft_layers; + for (size_t i = 0; i < layer_heights.size(); i++, support_layer_nr++) { + SupportLayer *ts_layer = m_object->add_tree_support_layer(support_layer_nr, layer_heights[i].print_z, layer_heights[i].height, layer_heights[i].print_z); + if (ts_layer->id() > m_raft_layers) { + SupportLayer *lower_layer = m_object->get_support_layer(ts_layer->id() - 1); + if (lower_layer) { + lower_layer->upper_layer = ts_layer; + ts_layer->lower_layer = lower_layer; } - for (j = i - 1; j >= 0; j--) { - if (layer_heights[j].height > EPSILON && layer_heights[j].print_z0.01) // there is a gap more than 0.01mm, increase the top z distance to fill the gap - layer_heights[i].height = layer_heights[i].print_z - layer_heights[j].print_z; - break; - } + } + } + + + // re-distribute contact_nodes to support layers + decltype(contact_nodes) contact_nodes2(support_layer_nr); + for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { + if (contact_nodes[layer_nr].empty()) continue; + SupportNode *node1 = contact_nodes[layer_nr].front(); + auto it = z_heights.lower_bound(node1->print_z - EPSILON); + if (it == z_heights.end()) it = std::prev(it); + int layer_nr2 = std::distance(z_heights.begin(), it); + contact_nodes2[layer_nr2].insert(contact_nodes2[layer_nr2].end(), contact_nodes[layer_nr].begin(), contact_nodes[layer_nr].end()); + } + contact_nodes = contact_nodes2; + + // adjust contact nodes' distance_to_top and support_roof_layers_below according to layer_heights + // In case of very large top z distance, one gap layer is not enough, we need to split it into multiple layers + for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { + if (contact_nodes[layer_nr].empty()) continue; + SupportNode *node1 = contact_nodes[layer_nr].front(); + BOOST_LOG_TRIVIAL(debug) << format("plan_layer_heights node1->layer_nr,printz,height,distance_to_top: %d, %.2f,%.2f, %d", layer_nr, node1->print_z, node1->height, node1->distance_to_top) + << ", object_layer_zs[" << layer_heights[layer_nr].obj_layer_nr << "]=" << m_object->get_layer(layer_heights[layer_nr].obj_layer_nr)->print_z; + coordf_t new_height = layer_heights[layer_nr].height; + if (std::abs(node1->height - new_height) < EPSILON) continue; + coordf_t accum_height = 0; + int num_layers = 0; + for (int i=layer_nr;i>=0;i--){ + if (layer_heights[i].height > EPSILON) { + accum_height += layer_heights[i].height; + num_layers++; + if (accum_height > node1->height - EPSILON) break; } } + BOOST_LOG_TRIVIAL(debug) << format("plan_layer_heights adjust node's height %d %.2f: (%.3f,%d)->(%.3f,%.3f,%d)", layer_nr, node1->print_z, + node1->height, node1->distance_to_top, new_height, accum_height, -num_layers); + for (SupportNode *node : contact_nodes[layer_nr]) { + node->height = new_height; + node->distance_to_top = -num_layers; + node->support_roof_layers_below += num_layers - 1; + } } // log layer_heights for (size_t i = 0; i < layer_heights.size(); i++) { - if (layer_heights[i].height > EPSILON) - BOOST_LOG_TRIVIAL(trace) << "plan_layer_heights print_z, height, lower_layer_nr->layer_nr: " << layer_heights[i].print_z << " " << layer_heights[i].height << " " - << layer_heights[i].next_layer_nr << "->" << i; + //if (layer_heights[i].height > EPSILON) + BOOST_LOG_TRIVIAL(trace) << format("plan_layer_heights[%d] print_z, height: %.2f, %.2f",i, layer_heights[i].print_z, layer_heights[i].height); } return layer_heights; } @@ -3065,8 +3110,10 @@ void TreeSupport::generate_contact_points() const PrintObjectConfig &config = m_object->config(); const coordf_t point_spread = scale_(config.tree_support_branch_distance.value); const coordf_t max_bridge_length = scale_(config.max_bridge_length.value); - coord_t radius = scale_(config.tree_support_branch_diameter.value / 2); - radius = std::max(m_min_radius, radius); + coord_t radius_scaled = scale_(base_radius); + bool on_buildplate_only = m_object_config->support_on_build_plate_only.value; + const bool roof_enabled = config.support_interface_top_layers.value > 0; + const bool force_tip_to_roof = roof_enabled && m_support_params.soluble_interface; //First generate grid points to cover the entire area of the print. BoundingBox bounding_box = m_object->bounding_box(); @@ -3094,15 +3141,15 @@ void TreeSupport::generate_contact_points() } const coordf_t layer_height = config.layer_height.value; - coordf_t z_distance_top = m_slicing_params.gap_support_object; - if (!m_support_params.independent_layer_height) { - z_distance_top = round(z_distance_top / layer_height) * layer_height; - // BBS: add extra distance if thick bridge is enabled - // Note: normal support uses print_z, but tree support uses integer layers, so we need to subtract layer_height - if (!m_slicing_params.soluble_interface && m_object_config->thick_bridges) { - z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(m_object->print()->config()) - layer_height; - } - } + coordf_t z_distance_top = this->top_z_distance; + // if (!m_support_params.independent_layer_height) { + // z_distance_top = round(z_distance_top / layer_height) * layer_height; + // // BBS: add extra distance if thick bridge is enabled + // // Note: normal support uses print_z, but tree support uses integer layers, so we need to subtract layer_height + // if (!m_slicing_params.soluble_interface && m_object_config->thick_bridges) { + // z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(m_object->print()->config()) - layer_height; + //} + // } const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1; //Support must always be 1 layer below overhang. int gap_layers = z_distance_top == 0 ? 0 : 1; // virtual layer with 0 height will be deleted @@ -3131,39 +3178,36 @@ void TreeSupport::generate_contact_points() if (m_object->print()->canceled()) break; Layer* layer = m_object->get_layer(layer_nr); - auto& curr_nodes = contact_nodes[layer_nr - 1]; + auto& curr_nodes = contact_nodes[layer_nr-1]; if (layer->loverhangs.empty()) continue; std::unordered_set already_inserted; - auto print_z = m_object->get_layer(layer_nr)->print_z; auto bottom_z = m_object->get_layer(layer_nr)->bottom_z(); - auto height = m_object->get_layer(layer_nr)->height; bool added = false; // Did we add a point this way? bool is_sharp_tail = false; - auto insert_point = [&](Point pt, const ExPolygon& overhang_part, bool force_add = false) { - Point hash_pos = pt / ((radius + 1) / 1); + + // take the least restrictive avoidance possible + ExPolygons relevant_forbidden = offset_ex(m_ts_data->m_layer_outlines[layer_nr - 1], scale_(MIN_BRANCH_RADIUS)); + // prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise. + relevant_forbidden = offset_ex(union_ex(relevant_forbidden), scaled(0.005), jtMiter, 1.2); + + + auto insert_point = [&](Point pt, const ExPolygon& overhang, double radius, bool force_add = false, bool add_interface=true) { + Point hash_pos = pt / ((radius_scaled + 1) / 1); SupportNode* contact_node = nullptr; if (force_add || !already_inserted.count(hash_pos)) { already_inserted.emplace(hash_pos); bool to_buildplate = true; + size_t roof_layers = add_interface ? support_roof_layers : 0; // add a new node as a virtual node which acts as the invisible gap between support and object // distance_to_top=-1: it's virtual // print_z=object_layer->bottom_z: it directly contacts the bottom // height=z_distance_top: it's height is exactly the gap distance // dist_mm_to_top=0: it directly contacts the bottom - contact_node = m_ts_data->create_node(pt, -gap_layers, layer_nr - 1, support_roof_layers + 1, to_buildplate, SupportNode::NO_PARENT, bottom_z, z_distance_top, 0, - unscale_(radius)); - contact_node->overhang = overhang_part; + contact_node = m_ts_data->create_node(pt, -gap_layers, layer_nr-1, roof_layers + 1, to_buildplate, SupportNode::NO_PARENT, bottom_z, z_distance_top, 0, + radius); + contact_node->overhang = overhang; contact_node->is_sharp_tail = is_sharp_tail; - if (is_sharp_tail) { - int ind = overhang_part.contour.closest_point_index(pt); - int nSize = overhang_part.contour.points.size(); - int ind_prev = (ind - 1 + nSize) % nSize; - int ind_next = (ind + 1) % nSize; - auto n1 = (overhang_part.contour[ind] - overhang_part.contour[ind_prev]).cast().normalized(); - auto n2 = (overhang_part.contour[ind] - overhang_part.contour[ind_next]).cast().normalized(); - contact_node->skin_direction = scaled((n1 + n2).normalized()); - } curr_nodes.emplace_back(contact_node); added = true; }; @@ -3173,69 +3217,73 @@ void TreeSupport::generate_contact_points() for (const auto& overhang_part : layer->loverhangs) { const auto& overhang_type = this->overhang_types[&overhang_part]; is_sharp_tail = overhang_type == OverhangType::SharpTail; - BoundingBox overhang_bounds = get_extents(overhang_part); + ExPolygons overhangs_regular; if (m_support_params.support_style == smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang && !is_sharp_tail) { - if (!overlaps({ overhang_part }, m_ts_data->m_layer_outlines_below[layer_nr - 1])) { - Point candidate = overhang_bounds.center(); - SupportNode* contact_node = insert_point(candidate, overhang_part, true); + overhangs_regular = offset_ex(intersection_ex({overhang_part}, m_ts_data->m_layer_outlines_below[layer_nr - 1]), radius_scaled); + overhangs_regular = diff_ex(overhangs_regular, relevant_forbidden); + ExPolygons overhangs_normal = diff_ex({overhang_part}, overhangs_regular); + for(auto& overhang:overhangs_normal) { + BoundingBox overhang_bounds = get_extents(overhang); + double radius = unscale_(overhang_bounds.radius()); + Point candidate = overhang_bounds.center(); + SupportNode *contact_node = insert_point(candidate, overhang, radius, true, true); contact_node->type = ePolygon; - contact_node->radius = unscale_(overhang_bounds.radius()); curr_nodes.emplace_back(contact_node); - continue; } + } else { + overhangs_regular = ExPolygons{overhang_part}; } - // add supports at corners for both auto and manual overhangs, github #2008 - { - auto& points = overhang_part.contour.points; + for (auto &overhang : overhangs_regular) { + bool add_interface = (force_tip_to_roof || area(overhang) > minimum_roof_area) && !is_sharp_tail; + BoundingBox overhang_bounds = get_extents(overhang); + double radius = std::clamp(unscale_(overhang_bounds.radius()), MIN_BRANCH_RADIUS, base_radius); + // add supports at corners for both auto and manual overhangs, github #2008 + auto &points = overhang.contour.points; int nSize = points.size(); for (int i = 0; i < nSize; i++) { auto pt = points[i]; auto v1 = (pt - points[(i - 1 + nSize) % nSize]).cast().normalized(); auto v2 = (pt - points[(i + 1) % nSize]).cast().normalized(); if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees - SupportNode* contact_node = insert_point(pt, overhang_part); + SupportNode *contact_node = insert_point(pt, overhang, radius, false, add_interface); if (contact_node) { contact_node->is_corner = true; } } } - } // add supports along contours - libnest2d::placers::EdgeCache edge_cache(overhang_part); + libnest2d::placers::EdgeCache edge_cache(overhang); for (size_t i = 0; i < edge_cache.holeCount() + 1; i++) { - double step = point_spread / (i == 0 ? edge_cache.circumference() : edge_cache.circumference(i - 1)); + double step = point_spread / (i == 0 ? edge_cache.circumference() : edge_cache.circumference(i - 1)); double distance = 0; while (distance < 1) { - auto pt = i == 0 ? edge_cache.coords(distance) : edge_cache.coords(i - 1, distance); - SupportNode* contact_node = insert_point(pt, overhang_part); + auto pt = i == 0 ? edge_cache.coords(distance) : edge_cache.coords(i - 1, distance); + SupportNode *contact_node = insert_point(pt, overhang,radius, false, add_interface); distance += step; } } // don't add inner supports for sharp tails - if (is_sharp_tail) - continue; + if (is_sharp_tail) continue; // add inner supports - overhang_bounds.inflated(-radius); - ExPolygons overhang_inner = offset_ex(overhang_part, -radius); + overhang_bounds.inflated(-radius_scaled); + ExPolygons overhang_inner = offset_ex(overhang, -radius_scaled); for (Point candidate : grid_points) { if (overhang_bounds.contains(candidate)) { // BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported! - bool is_inside = is_inside_ex(overhang_inner, candidate); - if (is_inside) { - SupportNode* contact_node = insert_point(candidate, overhang_part); - } + bool is_inside = is_inside_ex(overhang_inner, candidate); + if (is_inside) { SupportNode *contact_node = insert_point(candidate, overhang,radius, false, add_interface); } } } - + } } if (!curr_nodes.empty()) nonempty_layers++; for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); } #ifdef SUPPORT_TREE_DEBUG_TO_SVG - draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", print_z), layer->loverhangs,layer->lslices, m_ts_data->m_layer_outlines_below[layer_nr], + draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", bottom_z), layer->loverhangs,layer->lslices_extrudable, m_ts_data->m_layer_outlines_below[layer_nr], contact_nodes[layer_nr], contact_nodes[layer_nr - 1], { "overhang","lslices","outlines_below"}); #endif }} @@ -3276,13 +3324,16 @@ void TreeSupport::insert_dropped_node(std::vector& nodes_layer, Su conflicting_node->support_roof_layers_below = std::max(conflicting_node->support_roof_layers_below, p_node->support_roof_layers_below); } -TreeSupportData::TreeSupportData(const PrintObject &object, coordf_t xy_distance, coordf_t max_move, coordf_t radius_sample_resolution) - : m_xy_distance(xy_distance), m_max_move(max_move), m_radius_sample_resolution(radius_sample_resolution) +TreeSupportData::TreeSupportData(const PrintObject &object, coordf_t xy_distance, coordf_t radius_sample_resolution) + : m_xy_distance(xy_distance), m_radius_sample_resolution(radius_sample_resolution) { + branch_scale_factor = tan(object.config().tree_support_branch_angle.value * M_PI / 180.); clear_nodes(); + m_max_move_distances.resize(object.layers().size(), 0); for (std::size_t layer_nr = 0; layer_nr < object.layers().size(); ++layer_nr) { const Layer* layer = object.get_layer(layer_nr); + m_max_move_distances[layer_nr] = layer->height * branch_scale_factor; m_layer_outlines.push_back(ExPolygons()); ExPolygons& outline = m_layer_outlines.back(); for (const ExPolygon& poly : layer->lslices) { @@ -3386,7 +3437,7 @@ const ExPolygons& TreeSupportData::calculate_collision(const RadiusLayerPair& ke { assert(key.layer_nr < m_layer_outlines.size()); - ExPolygons collision_areas = offset_ex(m_layer_outlines[key.layer_nr], scale_(key.radius)); + ExPolygons collision_areas = offset_ex(m_layer_outlines[key.layer_nr], scale_(key.radius+m_xy_distance)); collision_areas = expolygons_simplify(collision_areas, scale_(m_radius_sample_resolution)); const auto ret = m_collision_cache.insert({ key, std::move(collision_areas) }); return ret.first->second; @@ -3394,45 +3445,32 @@ const ExPolygons& TreeSupportData::calculate_collision(const RadiusLayerPair& ke const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& key) const { - const auto& radius = key.radius; - const auto& layer_nr = key.layer_nr; - BOOST_LOG_TRIVIAL(debug) << "calculate_avoidance on (radius,layer)= (" << radius << "," << layer_nr<<"), recursion="< 0; layers_below++) { layer_nr_next = layer_heights[layer_nr_next].next_layer_nr; } - // Check if we would exceed the recursion limit by trying to process this layer - if (layers_below >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr_next}) == m_avoidance_cache.end()) { - // Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result. - get_avoidance(radius, layer_nr_next, key.recursions + 1); - } - - layer_nr_next = layer_heights[layer_nr].next_layer_nr; - ExPolygons avoidance_areas = offset_ex(get_avoidance(radius, layer_nr_next, key.recursions+1), scale_(-m_max_move)); - const ExPolygons &collision = get_collision(radius, layer_nr); - avoidance_areas.insert(avoidance_areas.end(), collision.begin(), collision.end()); - avoidance_areas = std::move(union_ex(avoidance_areas)); - auto ret = m_avoidance_cache.insert({ key, std::move(avoidance_areas) }); - //assert(ret.second); - return ret.first->second; - } else { - BOOST_LOG_TRIVIAL(debug) << "calculate_avoidance exceeds max_recursion_depth*2 on radius=" << radius << ", layer=" << layer_nr << ", recursion=" << key.recursions; - ExPolygons avoidance_areas = get_collision(radius, layer_nr);// std::move(offset_ex(m_layer_outlines_below[layer_nr], scale_(m_xy_distance + radius))); - auto ret = m_avoidance_cache.insert({ key, std::move(avoidance_areas) }); - assert(ret.second); - return ret.first->second; + const auto &radius = key.radius; + const auto &layer_nr = key.layer_nr; + if (layer_nr == 0) { + m_avoidance_cache[key] = get_collision(radius, 0); + return m_avoidance_cache[key]; } + + // Avoidance for a given layer depends on all layers beneath it so could have very deep recursion depths if + // called at high layer heights. We can limit the reqursion depth to N by checking if the layer N + // below the current one exists and if not, forcing the calculation of that layer. This may cause another recursion + // if the layer at 2N below the current one but we won't exceed our limit unless there are N*N uncalculated layers + // below our current one. + constexpr auto max_recursion_depth = 100; + // Check if we would exceed the recursion limit by trying to process this layer + if (layer_nr >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr - max_recursion_depth}) == m_avoidance_cache.end()) { + // Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result. + get_avoidance(radius, layer_nr - max_recursion_depth); + } + + ExPolygons avoidance_areas = offset_ex(get_avoidance(radius, layer_nr - 1), scale_(-m_max_move_distances[layer_nr-1])); + const ExPolygons &collision = get_collision(radius, layer_nr); + avoidance_areas.insert(avoidance_areas.end(), collision.begin(), collision.end()); + avoidance_areas = std::move(union_ex(avoidance_areas)); + auto ret = m_avoidance_cache.insert({key, std::move(avoidance_areas)}); + //assert(ret.second); + return ret.first->second; } } //namespace Slic3r diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index f94fd57020..ff5b4f201a 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -28,9 +28,9 @@ struct LayerHeightData { coordf_t print_z = 0; coordf_t height = 0; - size_t next_layer_nr = 0; + size_t obj_layer_nr = 0; LayerHeightData() = default; - LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {} + LayerHeightData(coordf_t z, coordf_t h, size_t obj_layer) : print_z(z), height(h), obj_layer_nr(obj_layer) {} coordf_t bottom_z() { return print_z - height; } @@ -202,7 +202,7 @@ public: * \param radius_sample_resolution Sample size used to round requested node radii. * \param collision_resolution */ - TreeSupportData(const PrintObject& object, coordf_t max_move, coordf_t radius_sample_resolution, coordf_t collision_resolution); + TreeSupportData(const PrintObject& object, coordf_t radius_sample_resolution, coordf_t collision_resolution); ~TreeSupportData() { clear_nodes(); } @@ -305,11 +305,7 @@ public: */ coordf_t m_xy_distance; - /*! - * \brief The maximum distance that the centrepoint of a tree branch may - * move in consequtive layers - */ - coordf_t m_max_move; + double branch_scale_factor = 1.0; // tan(45 degrees) /*! * \brief Sample resolution for radius values. @@ -328,6 +324,8 @@ public: // union contours of all layers below std::vector m_layer_outlines_below; + std::vector m_max_move_distances; + /*! * \brief Caches for the collision, avoidance and internal model polygons * at given radius and layer indices. @@ -366,6 +364,10 @@ public: */ TreeSupport(PrintObject& object, const SlicingParameters &slicing_params); + void move_bounds_to_contact_nodes(std::vector &move_bounds, + PrintObject &print_object, + const TreeSupport3D::TreeSupportSettings &config); + /*! * \brief Create the areas that need support. * @@ -377,6 +379,19 @@ public: void detect_overhangs(bool check_support_necessity = false); + SupportNode* create_node(const Point position, + const int distance_to_top, + const int obj_layer_nr, + const int support_roof_layers_below, + const bool to_buildplate, + SupportNode* parent, + coordf_t print_z_, + coordf_t height_, + coordf_t dist_mm_to_top_ = 0, + coordf_t radius_ = 0) + { + return m_ts_data->create_node(position, distance_to_top, obj_layer_nr, support_roof_layers_below, to_buildplate, parent, print_z_, height_, dist_mm_to_top_, radius_); + } int avg_node_per_layer = 0; float nodes_angle = 0; @@ -419,12 +434,17 @@ private: std::vector< std::unordered_map> m_mst_line_x_layer_contour_caches; float DO_NOT_MOVER_UNDER_MM = 0.0; - coordf_t MAX_BRANCH_RADIUS = 10.0; - coordf_t MIN_BRANCH_RADIUS = 0.5; - coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0; - coordf_t MIN_BRANCH_RADIUS_FIRST_LAYER = 2.0; - float tree_support_branch_diameter_angle = 5.0; - coord_t m_min_radius = scale_(1); // in mm + coordf_t base_radius = 0.0; + const coordf_t MAX_BRANCH_RADIUS = 10.0; + const coordf_t MIN_BRANCH_RADIUS = 0.4; + const coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0; + const coordf_t MIN_BRANCH_RADIUS_FIRST_LAYER = 2.0; + const double tree_support_branch_diameter_angle = 5.0; + const double diameter_angle_scale_factor = tan(tree_support_branch_diameter_angle*M_PI/180.0); + // minimum roof area (1 mm^2), area smaller than this value will not have interface + const double minimum_roof_area{SQ(scaled(1.))}; + float top_z_distance = 0.0; + bool is_strong = false; bool is_slim = false; bool with_infill = false; @@ -492,8 +512,10 @@ private: coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor); // get unscaled radius(mm) of node based on the distance mm to top coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor, bool use_min_distance=true); - coordf_t get_radius(const SupportNode* node, coordf_t base_radius); + coordf_t calc_radius(coordf_t mm_to_top); + coordf_t get_radius(const SupportNode* node); ExPolygons get_avoidance(coordf_t radius, size_t obj_layer_nr); + // layer's expolygon expanded by radius+m_xy_distance ExPolygons get_collision(coordf_t radius, size_t layer_nr); // get Polygons instead of ExPolygons Polygons get_collision_polys(coordf_t radius, size_t layer_nr); diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 22505d4f92..f5b588bf72 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -68,28 +68,28 @@ using namespace std::literals; static inline void validate_range(const Point &pt) { static constexpr const int32_t hi = 65536 * 16384; - if (pt.x() > hi || pt.y() > hi || -pt.x() > hi || -pt.y() > hi) - throw ClipperLib::clipperException("Coordinate outside allowed range"); + if (pt.x() > hi || pt.y() > hi || -pt.x() > hi || -pt.y() > hi) + throw ClipperLib::clipperException("Coordinate outside allowed range"); } -static inline void validate_range(const Points &points) +static inline void validate_range(const Points &points) { for (const Point &p : points) validate_range(p); } -static inline void validate_range(const MultiPoint &mp) +static inline void validate_range(const MultiPoint &mp) { validate_range(mp.points); } -static inline void validate_range(const Polygons &polygons) +static inline void validate_range(const Polygons &polygons) { for (const Polygon &p : polygons) validate_range(p); } -static inline void validate_range(const Polylines &polylines) +static inline void validate_range(const Polylines &polylines) { for (const Polyline &p : polylines) validate_range(p); @@ -136,7 +136,7 @@ static std::vector>> group_me size_t largest_printed_mesh_idx = 0; - // Group all meshes that can be processed together. NOTE this is different from mesh-groups! Only one setting object is needed per group, + // Group all meshes that can be processed together. NOTE this is different from mesh-groups! Only one setting object is needed per group, // as different settings in the same group may only occur in the tip, which uses the original settings objects from the meshes. for (size_t object_id : print_object_ids) { const PrintObject &print_object = *print.get_object(object_id); @@ -228,7 +228,7 @@ static std::vector>> group_me size_t num_overhang_layers = support_auto ? num_object_layers : std::min(num_object_layers, std::max(size_t(support_enforce_layers), enforcers_layers.size())); tbb::parallel_for(tbb::blocked_range(1, num_overhang_layers), - [&print_object, &config, &print_config, &enforcers_layers, &blockers_layers, + [&print_object, &config, &print_config, &enforcers_layers, &blockers_layers, support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, radius_sample_resolution, &throw_on_cancel, &out] (const tbb::blocked_range &range) { for (LayerIndex layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { @@ -263,7 +263,7 @@ static std::vector>> group_me overhangs = diff(overhangs, offset_ex(union_(blockers_layers[layer_id]), scale_(radius_sample_resolution)), ApplySafetyOffset::Yes); if (config.bridge_no_support) { for (const LayerRegion *layerm : current_layer.regions()) - remove_bridges_from_contacts(print_config, lower_layer, *layerm, + remove_bridges_from_contacts(print_config, lower_layer, *layerm, float(layerm->flow(frExternalPerimeter).scaled_width()), overhangs); } } @@ -284,7 +284,7 @@ static std::vector>> group_me enforced_overhangs = diff(offset(union_ex(enforced_overhangs), enforcer_overhang_offset), lower_layer.lslices); #ifdef TREESUPPORT_DEBUG_SVG -// if (! intersecting_edges(enforced_overhangs).empty()) +// if (! intersecting_edges(enforced_overhangs).empty()) { static int irun = 0; SVG::export_expolygons(debug_out_path("treesupport-self-intersections-%d.svg", ++irun), @@ -297,7 +297,7 @@ static std::vector>> group_me overhangs = overhangs.empty() ? std::move(enforced_overhangs) : union_(overhangs, enforced_overhangs); //check_self_intersections(overhangs, "generate_overhangs - enforcers"); } - } + } out[layer_id + num_raft_layers] = std::move(overhangs); throw_on_cancel(); } @@ -460,7 +460,7 @@ static std::vector>> group_me return true; if (config.support_rests_on_model && (p.second != LineStatus::TO_BP && p.second != LineStatus::TO_BP_SAFE)) return ! contains( - p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? + p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? volumes.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? AvoidanceType::FastSafe : AvoidanceType::Fast, true, min_xy_dist) : volumes.getCollision(config.getRadius(0), current_layer - 1, min_xy_dist), p.first); @@ -532,7 +532,7 @@ static std::optional> polyline_sample_next_point_at_dis // Squared distance of "start_pt" from the ray (p0, p1). double l2_from_line = xf.squaredNorm(); // Squared distance of an intersection point of a circle with center at the foot point. - if (double l2_intersection = dist2 - l2_from_line; + if (double l2_intersection = dist2 - l2_from_line; l2_intersection > - SCALED_EPSILON) { // The ray (p0, p1) touches or intersects a circle centered at "start_pt" with radius "dist". // Distance of the circle intersection point from the foot point. @@ -627,7 +627,7 @@ static std::optional> polyline_sample_next_point_at_dis } else { if (current_point == next_point->first) { // In case a fixpoint is encountered, better aggressively overcompensate so the code does not become stuck here... - BOOST_LOG_TRIVIAL(warning) << "Tree Support: Encountered a fixpoint in polyline_sample_next_point_at_distance. This is expected to happen if the distance (currently " << next_distance << + BOOST_LOG_TRIVIAL(warning) << "Tree Support: Encountered a fixpoint in polyline_sample_next_point_at_distance. This is expected to happen if the distance (currently " << next_distance << ") is smaller than 100"; tree_supports_show_error("Encountered issue while placing tips. Some tips may be missing."sv, true); if (next_distance > 2 * current_distance) @@ -693,9 +693,9 @@ static std::optional> polyline_sample_next_point_at_dis int divisor = static_cast(angles.size()); int index = ((layer_idx % divisor) + divisor) % divisor; const AngleRadians fill_angle = angles[index]; - Infill roof_computation(pattern, true /* zig_zaggify_infill */, connect_polygons, polygon, - roof ? config.support_roof_line_width : config.support_line_width, support_infill_distance, support_roof_overlap, infill_multiplier, - fill_angle, z, support_shift, config.resolution, wall_line_count, infill_origin, + Infill roof_computation(pattern, true /* zig_zaggify_infill */, connect_polygons, polygon, + roof ? config.support_roof_line_width : config.support_line_width, support_infill_distance, support_roof_overlap, infill_multiplier, + fill_angle, z, support_shift, config.resolution, wall_line_count, infill_origin, perimeter_gaps, connected_zigzags, use_endpieces, false /* skip_some_zags */, zag_skip_count, pocket_size); Polygons polygons; Polygons lines; @@ -712,7 +712,7 @@ static std::optional> polyline_sample_next_point_at_dis filler->layer_id = layer_idx; filler->spacing = flow.spacing(); - filler->angle = roof ? + filler->angle = roof ? //fixme support_layer.interface_id() instead of layer_idx (support_params.interface_angle + (layer_idx & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)) : support_params.base_angle; @@ -783,7 +783,7 @@ static std::optional> polyline_sample_next_point_at_dis result = union_(offset(to_polylines(first), scaled(0.002), jtMiter, 1.2), offset(to_polylines(second), scaled(0.002), jtMiter, 1.2)); } } - + return result; } @@ -800,7 +800,7 @@ static std::optional> polyline_sample_next_point_at_dis { bool do_final_difference = last_step_offset_without_check == 0; Polygons ret = safe_union(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& { @@ -882,7 +882,7 @@ public: // called by sample_overhang_area() void add_points_along_lines( // Insert points (tree tips or top contact interfaces) along these lines. - LineInformations lines, + LineInformations lines, // Start at this layer. LayerIndex insert_layer_idx, // Insert this number of interface layers. @@ -922,7 +922,7 @@ public: // add all points that would not be valid for (const LineInformation &line : points) for (const std::pair &point_data : line) - add_point_as_influence_area(point_data, this_layer_idx, + add_point_as_influence_area(point_data, this_layer_idx, // don't move until roof_tip_layers - dtt_roof_tip, // supports roof @@ -1009,7 +1009,7 @@ private: // Temps coord_t m_base_radius; Polygon m_base_circle; - + // Mutexes, guards std::mutex m_mutex_movebounds; std::vector> m_already_inserted; @@ -1093,10 +1093,10 @@ void finalize_raft_contact( // Produce // 1) Maximum num_support_roof_layers roof (top interface & contact) layers. // 2) Tree tips supporting either the roof layers or the object itself. -// num_support_roof_layers should always be respected: +// num_support_roof_layers should always be respected: // If num_support_roof_layers contact layers could not be produced, then the tree tip // is augmented with SupportElementState::missing_roof_layers -// and the top "missing_roof_layers" of such particular tree tips are supposed to be coverted to +// and the top "missing_roof_layers" of such particular tree tips are supposed to be coverted to // roofs aka interface layers by the tool path generator. void sample_overhang_area( // Area to support @@ -1108,18 +1108,18 @@ void sample_overhang_area( const size_t layer_idx, // Maximum number of roof (contact, interface) layers between the overhang and tree tips to be generated. const size_t num_support_roof_layers, - // + // const coord_t connect_length, // Configuration classes - const TreeSupportMeshGroupSettings &mesh_group_settings, + const TreeSupportMeshGroupSettings& mesh_group_settings, // Configuration & Output - RichInterfacePlacer &interface_placer) + RichInterfacePlacer& interface_placer) { - // Assumption is that roof will support roof further up to avoid a lot of unnecessary branches. Each layer down it is checked whether the roof area - // is still large enough to be a roof and aborted as soon as it is not. This part was already reworked a few times, and there could be an argument + // Assumption is that roof will support roof further up to avoid a lot of unnecessary branches. Each layer down it is checked whether the roof area + // is still large enough to be a roof and aborted as soon as it is not. This part was already reworked a few times, and there could be an argument // made to change it again if there are actual issues encountered regarding supporting roofs. - // Main problem is that some patterns change each layer, so just calculating points and checking if they are still valid an layer below is not useful, - // as the pattern may be different one layer below. Same with calculating which points are now no longer being generated as result from + // Main problem is that some patterns change each layer, so just calculating points and checking if they are still valid an layer below is not useful, + // as the pattern may be different one layer below. Same with calculating which points are now no longer being generated as result from // a decreasing roof, as there is no guarantee that a line will be above these points. Implementing a separate roof support behavior // for each pattern harms maintainability as it very well could be >100 LOC auto generate_roof_lines = [&interface_placer, &mesh_group_settings](const Polygons &area, LayerIndex layer_idx) -> Polylines { @@ -1186,10 +1186,10 @@ void sample_overhang_area( if (overhang_lines.empty()) { // support_line_width to form a line here as otherwise most will be unsupported. Technically this violates branch distance, but not only is this the only reasonable choice, - // but it ensures consistant behaviour as some infill patterns generate each line segment as its own polyline part causing a similar line forming behaviour. + // but it ensures consistant behaviour as some infill patterns generate each line segment as its own polyline part causing a similar line forming behaviour. // This is not doen when a roof is above as the roof will support the model and the trees only need to support the roof - bool supports_roof = dtt_roof > 0; - bool continuous_tips = ! supports_roof && large_horizontal_roof; + bool supports_roof = dtt_roof > 0; + bool continuous_tips = !supports_roof && large_horizontal_roof; Polylines polylines = ensure_maximum_distance_polyline( generate_support_infill_lines(overhang_area, interface_placer.support_parameters, supports_roof, layer_idx - layer_generation_dtt, supports_roof ? mesh_group_settings.support_roof_line_distance : mesh_group_settings.support_tree_branch_distance), @@ -1200,10 +1200,10 @@ void sample_overhang_area( const size_t min_support_points = std::max(coord_t(1), std::min(coord_t(3), coord_t(total_length(overhang_area) / connect_length))); if (point_count <= min_support_points) { // add the outer wall (of the overhang) to ensure it is correct supported instead. Try placing the support points in a way that they fully support the outer wall, instead of just the with half of the the support line width. - // I assume that even small overhangs are over one line width wide, so lets try to place the support points in a way that the full support area generated from them - // will support the overhang (if this is not done it may only be half). This WILL NOT be the case when supporting an angle of about < 60� so there is a fallback, + // I assume that even small overhangs are over one line width wide, so lets try to place the support points in a way that the full support area generated from them + // will support the overhang (if this is not done it may only be half). This WILL NOT be the case when supporting an angle of about < 60 degrees so there is a fallback, // as some support is better than none. - Polygons reduced_overhang_area = offset(union_ex(overhang_area), - interface_placer.config.support_line_width / 2.2, jtMiter, 1.2); + Polygons reduced_overhang_area = offset(union_ex(overhang_area), -interface_placer.config.support_line_width / 2.2, jtMiter, 1.2); polylines = ensure_maximum_distance_polyline( to_polylines( ! reduced_overhang_area.empty() && @@ -1269,11 +1269,11 @@ static void generate_initial_areas( const coord_t connect_length = (config.support_line_width * 100. / mesh_group_settings.support_tree_top_rate) + std::max(2. * config.min_radius - 1.0 * config.support_line_width, 0.0); // 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. + // 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 degrees angle between both remains. - const coord_t circle_length_to_half_linewidth_change = config.min_radius < config.support_line_width ? - config.min_radius / 2 : + const coord_t circle_length_to_half_linewidth_change = config.min_radius < config.support_line_width ? + config.min_radius / 2 : scale_(sqrt(sqr(unscale(config.min_radius)) - sqr(unscale(config.min_radius - 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. //FIXME Vojtech: This is not sufficient for support enforcers to work. @@ -1286,14 +1286,16 @@ static void generate_initial_areas( const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers; const bool roof_enabled = num_support_roof_layers > 0; const bool force_tip_to_roof = roof_enabled && (interface_placer.support_parameters.soluble_interface || sqr(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area); - // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point - // may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang - // does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it. + // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point + // may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang + // does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it. // The 2*z_distance_delta is only a catch for when the support angle is very high. // Used only if not min_xy_dist. coord_t max_overhang_insert_lag = 0; if (config.z_distance_top_layers > 0) { max_overhang_insert_lag = 2 * config.z_distance_top_layers; + + //FIXME if (mesh_group_settings.support_angle > EPSILON && mesh_group_settings.support_angle < 0.5 * M_PI - EPSILON) { //FIXME mesh_group_settings.support_angle does not apply to enforcers and also it does not apply to automatic support angle (by half the external perimeter width). //used by max_overhang_insert_lag, only if not min_xy_dist. @@ -1339,12 +1341,12 @@ static void generate_initial_areas( relevant_forbidden = offset(union_ex(relevant_forbidden_raw), scaled(0.005), jtMiter, 1.2); } - // every overhang has saved if a roof should be generated for it. This can NOT be done in the for loop as an area may NOT have a roof - // even if it is larger than the minimum_roof_area when it is only larger because of the support horizontal expansion and + // every overhang has saved if a roof should be generated for it. This can NOT be done in the for loop as an area may NOT have a roof + // even if it is larger than the minimum_roof_area when it is only larger because of the support horizontal expansion and // it would not have a roof if the overhang is offset by support roof horizontal expansion instead. (At least this is the current behavior of the regular support) Polygons overhang_regular; { - // When support_offset = 0 safe_offset_inc will only be the difference between overhang_raw and relevant_forbidden, that has to be calculated anyway. + // When support_offset = 0 safe_offset_inc will only be the difference between overhang_raw and relevant_forbidden, that has to be calculated anyway. overhang_regular = safe_offset_inc(overhang_raw, mesh_group_settings.support_offset, relevant_forbidden, config.min_radius * 1.75 + config.xy_min_distance, 0, 1); //check_self_intersections(overhang_regular, "overhang_regular1"); @@ -1363,7 +1365,7 @@ static void generate_initial_areas( for (coord_t extra_total_offset_acc = 0; ! remaining_overhang.empty() && extra_total_offset_acc + config.support_line_width / 8 < extra_outset; ) { const coord_t offset_current_step = std::min( extra_total_offset_acc + 2 * config.support_line_width > config.min_radius ? - config.support_line_width / 8 : + config.support_line_width / 8 : circle_length_to_half_linewidth_change, extra_outset - extra_total_offset_acc); extra_total_offset_acc += offset_current_step; @@ -1383,10 +1385,10 @@ static void generate_initial_areas( LineInformations overhang_lines; { //Vojtech: Generate support heads at support_tree_branch_distance spacing by producing a zig-zag infill at support_tree_branch_distance spacing, - // which is then resmapled - // support_line_width to form a line here as otherwise most will be unsupported. Technically this violates branch distance, - // mbut not only is this the only reasonable choice, but it ensures consistent behavior as some infill patterns generate - // each line segment as its own polyline part causing a similar line forming behavior. Also it is assumed that + // which is then resmapled + // support_line_width to form a line here as otherwise most will be unsupported. Technically this violates branch distance, + // mbut not only is this the only reasonable choice, but it ensures consistent behavior as some infill patterns generate + // each line segment as its own polyline part causing a similar line forming behavior. Also it is assumed that // the area that is valid a layer below is to small for support roof. Polylines polylines = ensure_maximum_distance_polyline( generate_support_infill_lines(remaining_overhang, support_params, false, layer_idx, mesh_group_settings.support_tree_branch_distance), @@ -1404,7 +1406,7 @@ static void generate_initial_areas( } for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag && !overhang_lines.empty() && layer_idx - coord_t(lag_ctr) >= 1; lag_ctr++) { // get least restricted avoidance for layer_idx-lag_ctr - const Polygons &relevant_forbidden_below = config.support_rests_on_model ? + const Polygons &relevant_forbidden_below = config.support_rests_on_model ? volumes.getCollision(config.getRadius(0), layer_idx - lag_ctr, min_xy_dist) : volumes.getAvoidance(config.getRadius(0), layer_idx - lag_ctr, AvoidanceType::Fast, false, min_xy_dist); // it is not required to offset the forbidden area here as the points wont change: If points here are not inside the forbidden area neither will they be later when placing these points, as these are the same points. @@ -1442,8 +1444,9 @@ static void generate_initial_areas( // or roof is enabled and these are the thin overhangs at object slopes (not horizontal overhangs). if (mesh_group_settings.minimum_support_area > 0) remove_small(overhang_regular, mesh_group_settings.minimum_support_area); + for (ExPolygon &support_part : union_ex(overhang_regular)) { - sample_overhang_area(to_polygons(std::move(support_part)), + sample_overhang_area(to_polygons(std::move(support_part)), false, layer_idx, num_support_roof_layers, connect_length, mesh_group_settings, rich_interface_placer); throw_on_cancel(); @@ -1485,7 +1488,7 @@ static unsigned int move_inside(const Polygons &polygons, Point &from, int dista } int64_t dot_prod = ab.dot(ap); if (dot_prod <= 0) { // x is projected to before ab - if (projected_p_beyond_prev_segment) { + if (projected_p_beyond_prev_segment) { // case which looks like: > . projected_p_beyond_prev_segment = false; Point& x = p1; @@ -1520,7 +1523,7 @@ static unsigned int move_inside(const Polygons &polygons, Point &from, int dista p0 = p1; p1 = p2; continue; - } else { + } else { // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | . projected_p_beyond_prev_segment = false; Point x = a + (ab.cast() * (double(dot_prod) / double(ab_length2))).cast(); @@ -1594,7 +1597,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di Polygons &to_bp_data, Polygons &to_model_data, Polygons &increased, - const coord_t overspeed, + const coord_t overspeed, const bool mergelayer) { SupportElementState current_elem{ SupportElementState::propagate_down(parent.state) }; @@ -1607,18 +1610,18 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di if (settings.move) { increased = relevant_offset; if (overspeed > 0) { - const coord_t safe_movement_distance = - (current_elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + + const coord_t safe_movement_distance = + (current_elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); // The difference to ensure that the result not only conforms to wall_restriction, but collision/avoidance is done later. // The higher last_safe_step_movement_distance comes exactly from the fact that the collision will be subtracted later. - increased = safe_offset_inc(increased, overspeed, volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist), + increased = safe_offset_inc(increased, overspeed, volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist), safe_movement_distance, safe_movement_distance + radius, 1); } if (settings.no_error && settings.move) // as ClipperLib::jtRound has to be used for offsets this simplify is VERY important for performance. polygons_simplify(increased, scaled(0.025), polygons_strictly_simple); - } else + } else // if no movement is done the areas keep parent area as no move == offset(0) increased = parent.influence_area; @@ -1627,7 +1630,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di 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. - BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong to model value on layer " << layer_idx - 1 << " targeting " << + BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong to model value on layer " << layer_idx - 1 << " targeting " << current_elem.target_height << " with radius " << radius; } } @@ -1638,7 +1641,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di 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 " << + 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 // Cannot route to gracious areas. Push the tree away from object and route it down anyways. @@ -1659,8 +1662,8 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di to_bp_data_2 = diff_clipped(increased, volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance)); Polygons to_model_data_2; if (config.support_rests_on_model && !current_elem.to_buildplate) - to_model_data_2 = diff_clipped(increased, - current_elem.to_model_gracious ? + to_model_data_2 = diff_clipped(increased, + current_elem.to_model_gracious ? volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, true, settings.use_min_distance) : volumes.getCollision(next_radius, layer_idx - 1, settings.use_min_distance)); Polygons check_layer_data_2 = current_elem.to_buildplate ? to_bp_data_2 : to_model_data_2; @@ -1675,8 +1678,8 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di while (current_ceil_radius < target_radius && validWithRadius(volumes.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance))) current_ceil_radius = volumes.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance); size_t resulting_eff_dtt = current_elem.effective_radius_height; - while (resulting_eff_dtt + 1 < current_elem.distance_to_top && - config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= current_ceil_radius && + while (resulting_eff_dtt + 1 < current_elem.distance_to_top && + config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= current_ceil_radius && config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= support_element_radius(config, current_elem)) ++ resulting_eff_dtt; current_elem.effective_radius_height = resulting_eff_dtt; @@ -1684,7 +1687,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di radius = support_element_collision_radius(config, current_elem); const coord_t foot_radius_increase = std::max(config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer, 0.0); - // Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius, + // Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius, // which could cause the radius to become bigger than precalculated. double planned_foot_increase = std::min(1.0, double(config.recommendedMinRadius(layer_idx - 1) - support_element_radius(config, current_elem)) / foot_radius_increase); //FIXME @@ -1701,14 +1704,14 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di if (current_elem.to_buildplate) to_bp_data = safe_union(diff_clipped(increased, volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance))); if (config.support_rests_on_model && (!current_elem.to_buildplate || mergelayer)) - to_model_data = safe_union(diff_clipped(increased, - current_elem.to_model_gracious ? + to_model_data = safe_union(diff_clipped(increased, + current_elem.to_model_gracious ? volumes.getAvoidance(radius, layer_idx - 1, settings.type, true, settings.use_min_distance) : volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance) )); check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; if (area(check_layer_data) < _tiny_area_threshold) { - BOOST_LOG_TRIVIAL(error) << "Lost area by doing catch up from " << ceil_radius_before << " to radius " << + BOOST_LOG_TRIVIAL(error) << "Lost area by doing catch up from " << ceil_radius_before << " to radius " << volumes.ceilRadius(support_element_collision_radius(config, current_elem), settings.use_min_distance); tree_supports_show_error("Area lost catching up radius. May not cause visible malformation."sv, true); } @@ -1746,7 +1749,7 @@ struct SupportElementMerging { const Eigen::AlignedBox& bbox() const { return bbox_data;} const Point centroid() const { return (bbox_data.min() + bbox_data.max()) / 2; } - void set_bbox(const BoundingBox& abbox) + void set_bbox(const BoundingBox& abbox) { Point eps { coord_t(SCALED_EPSILON), coord_t(SCALED_EPSILON) }; bbox_data = { abbox.min - eps, abbox.max + eps }; } // Called by the AABBTree builder to get an index into the vector of source elements. @@ -1778,7 +1781,7 @@ static void increase_areas_one_layer( // New areas at the layer below layer_idx std::vector &merging_areas, // Layer above merging_areas. - const LayerIndex layer_idx, + const LayerIndex layer_idx, // Layer elements above merging_areas. SupportElements &layer_elements, // If false, the merging_areas will not be merged for performance reasons. @@ -1794,7 +1797,7 @@ static void increase_areas_one_layer( assert(merging_area.parents.size() == 1); SupportElement &parent = layer_elements[merging_area.parents.front()]; SupportElementState elem = SupportElementState::propagate_down(parent.state); - const Polygons &wall_restriction = + const Polygons &wall_restriction = // Abstract representation of the model outline. If an influence area would move through it, it could teleport through a wall. volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist); @@ -1825,10 +1828,10 @@ static void increase_areas_one_layer( * layer z-1:dddddxxxxxxxxxx * For more detailed visualisation see calculateWallRestrictions */ - const coord_t safe_movement_distance = - (elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + + const coord_t safe_movement_distance = + (elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) + (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0); - if (ceiled_parent_radius == volumes.ceilRadius(projected_radius_increased, parent.state.use_min_xy_dist) || + if (ceiled_parent_radius == volumes.ceilRadius(projected_radius_increased, parent.state.use_min_xy_dist) || projected_radius_increased < config.increase_radius_until_radius) // If it is guaranteed possible to increase the radius, the maximum movement speed can be increased, as it is assumed that the maximum movement speed is the one of the slower moving wall extra_speed += projected_radius_delta; @@ -1837,7 +1840,7 @@ static void increase_areas_one_layer( // Ensure that the slow movement distance can not become larger than the fast one. extra_slow_speed += std::min(projected_radius_delta, (config.maximum_move_distance + extra_speed) - (config.maximum_move_distance_slow + extra_slow_speed)); - if (config.layer_start_bp_radius > layer_idx && + if (config.layer_start_bp_radius > layer_idx && config.recommendedMinRadius(layer_idx - 1) < config.getRadius(elem.effective_radius_height + 1, elem.elephant_foot_increases)) { // can guarantee elephant foot radius increase if (ceiled_parent_radius == volumes.ceilRadius(config.getRadius(parent.state.effective_radius_height + 1, parent.state.elephant_foot_increases + 1), parent.state.use_min_xy_dist)) @@ -1872,7 +1875,7 @@ static void increase_areas_one_layer( if (elem.last_area_increase.move && elem.last_area_increase.no_error && elem.can_use_safe_radius && !mergelayer && !avoidance_speed_mismatch && (elem.distance_to_top >= config.tip_layers || parent_moved_slow)) { // assume that the avoidance type that was best for the parent is best for me. Makes this function about 7% faster. - insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed, + insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed, increase_radius, elem.last_area_increase.no_error, !use_min_radius, elem.last_area_increase.move }, true); insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed, !increase_radius, elem.last_area_increase.no_error, !use_min_radius, elem.last_area_increase.move }, true); @@ -1892,7 +1895,7 @@ static void increase_areas_one_layer( insertSetting({ AvoidanceType::Fast, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true); } else { insertSetting({ AvoidanceType::Slow, slow_speed, increase_radius, no_error, !use_min_radius, move }, true); - // while moving fast to be able to increase the radius (b) may seems preferable (over a) this can cause the a sudden skip in movement, + // while moving fast to be able to increase the radius (b) may seems preferable (over a) this can cause the a sudden skip in movement, // which looks similar to a layer shift and can reduce stability. // as such idx have chosen to only use the user setting for radius increases as a friendly recommendation. insertSetting({ AvoidanceType::Slow, slow_speed, !increase_radius, no_error, !use_min_radius, move }, true); // a @@ -1929,9 +1932,9 @@ static void increase_areas_one_layer( for (const AreaIncreaseSettings &settings : order) { if (settings.move) { if (offset_slow.empty() && (settings.increase_speed == slow_speed || ! offset_independant_faster)) { - // offsetting in 2 steps makes our offsetted area rounder preventing (rounding) errors created by to pointy areas. At this point one can see that the Polygons class + // offsetting in 2 steps makes our offsetted area rounder preventing (rounding) errors created by to pointy areas. At this point one can see that the Polygons class // was never made for precision in the single digit micron range. - offset_slow = safe_offset_inc(parent.influence_area, extra_speed + extra_slow_speed + config.maximum_move_distance_slow, + offset_slow = safe_offset_inc(parent.influence_area, extra_speed + extra_slow_speed + config.maximum_move_distance_slow, wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 2); #ifdef TREESUPPORT_DEBUG_SVG SVG::export_expolygons(debug_out_path("treesupport-increase_areas_one_layer-slow-%d-%ld.svg", layer_idx, int(merging_area_idx)), @@ -1941,7 +1944,7 @@ static void increase_areas_one_layer( } if (offset_fast.empty() && settings.increase_speed != slow_speed) { if (offset_independant_faster) - offset_fast = safe_offset_inc(parent.influence_area, extra_speed + config.maximum_move_distance, + offset_fast = safe_offset_inc(parent.influence_area, extra_speed + config.maximum_move_distance, wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 1); else { const coord_t delta_slow_fast = config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed); @@ -1956,12 +1959,12 @@ static void increase_areas_one_layer( } std::optional result; inc_wo_collision.clear(); - if (!settings.no_error) { + if (!settings.no_error) { // ERROR CASE // if the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if it would be a line wrongly, it still actually has an area that can be increased Polygons lines_offset = offset(to_polylines(parent.influence_area), scaled(0.005), jtMiter, 1.2); Polygons base_error_area = union_(parent.influence_area, lines_offset); - result = increase_single_area(volumes, config, settings, layer_idx, parent, + result = increase_single_area(volumes, config, settings, layer_idx, parent, base_error_area, to_bp_data, to_model_data, inc_wo_collision, (config.maximum_move_distance + extra_speed) * 1.5, mergelayer); #ifdef TREE_SUPPORT_SHOW_ERRORS BOOST_LOG_TRIVIAL(error) @@ -1970,7 +1973,7 @@ static void increase_areas_one_layer( #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.layer_idx << " 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 << + " 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 " "Parent " << &parent << ": Radius: " << support_element_collision_radius(config, parent.state) << " at layer: " << layer_idx << " NextTarget: " << parent.state.layer_idx << " Distance to top: " << parent.state.distance_to_top << " Elephant foot increases " << parent.state.elephant_foot_increases << " use_min_xy_dist " << parent.state.use_min_xy_dist << @@ -2002,7 +2005,7 @@ static void increase_areas_one_layer( elem.use_min_xy_dist = false; if (!settings.no_error) #ifdef TREE_SUPPORT_SHOW_ERRORS - BOOST_LOG_TRIVIAL(error) + BOOST_LOG_TRIVIAL(error) #else // TREE_SUPPORT_SHOW_ERRORS BOOST_LOG_TRIVIAL(info) #endif // TREE_SUPPORT_SHOW_ERRORS @@ -2029,7 +2032,7 @@ static void increase_areas_one_layer( merging_area.areas.to_model_areas = std::move(to_model_data); } } else { - // If the bottom most point of a branch is set, later functions will assume that the position is valid, and ignore it. + // If the bottom most point of a branch is set, later functions will assume that the position is valid, and ignore it. // But as branches connecting with the model that are to small have to be culled, the bottom most point has to be not set. // A point can be set on the top most tip layer (maybe more if it should not move for a few layers). parent.state.result_on_layer_reset(); @@ -2073,7 +2076,7 @@ static void increase_areas_one_layer( out.elephant_foot_increases = 0; if (config.bp_radius_increase_per_layer > 0) { coord_t foot_increase_radius = std::abs(std::max(support_element_collision_radius(config, second), support_element_collision_radius(config, first)) - support_element_collision_radius(config, out)); - // elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch + // elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch // the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value. out.elephant_foot_increases = foot_increase_radius / (config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer); } @@ -2096,8 +2099,8 @@ static bool merge_influence_areas_two_elements( { // Don't merge gracious with a non gracious area as bad placement could negatively impact reliability of the whole subtree. const bool merging_gracious_and_non_gracious = dst.state.to_model_gracious != src.state.to_model_gracious; - // Could cause some issues with the increase of one area, as it is assumed that if the smaller is increased - // by the delta to the larger it is engulfed by it already. But because a different collision + // Could cause some issues with the increase of one area, as it is assumed that if the smaller is increased + // by the delta to the larger it is engulfed by it already. But because a different collision // may be removed from the in draw_area() generated circles, this assumption could be wrong. const bool merging_min_and_regular_xy = dst.state.use_min_xy_dist != src.state.use_min_xy_dist; @@ -2141,10 +2144,10 @@ static bool merge_influence_areas_two_elements( if (increased_to_model_radius > config.max_to_model_radius_increase) return false; } - // if a merge could place a stable branch on unstable ground, would be increasing the radius further - // than allowed to when merging to model and to_bp trees or would merge to model before it is known + // if a merge could place a stable branch on unstable ground, would be increasing the radius further + // than allowed to when merging to model and to_bp trees or would merge to model before it is known // they will even been drawn the merge is skipped - if (! dst.state.supports_roof && ! src.state.supports_roof && + if (! dst.state.supports_roof && ! src.state.supports_roof && std::max(src.state.distance_to_top, dst.state.distance_to_top) < config.min_dtt_to_model) return false; } @@ -2155,7 +2158,7 @@ static bool merge_influence_areas_two_elements( return false; // the bigger radius is used to verify that the area is still valid after the increase with the delta. - // If there were a point where the big influence area could be valid with can_use_safe_radius + // If there were a point where the big influence area could be valid with can_use_safe_radius // the element would already be can_use_safe_radius. // the smaller radius, which gets increased by delta may reach into the area where use_min_xy_dist is no longer required. const bool use_min_radius = bigger_rad.state.use_min_xy_dist && smaller_rad.state.use_min_xy_dist; @@ -2186,7 +2189,7 @@ static bool merge_influence_areas_two_elements( const auto _tiny_area_threshold = tiny_area_threshold(); // dont use empty as a line is not empty, but for this use-case it very well may be (and would be one layer down as union does not keep lines) - // check if the overlap is large enough (Small ares tend to attract rounding errors in clipper). + // check if the overlap is large enough (Small ares tend to attract rounding errors in clipper). if (area(intersect) <= _tiny_area_threshold) return false; @@ -2208,7 +2211,7 @@ static bool merge_influence_areas_two_elements( Point new_pos = move_inside_if_outside(intersect, dst.state.next_position); SupportElementState new_state = merge_support_element_states(dst.state, src.state, new_pos, layer_idx - 1, config); - new_state.increased_to_model_radius = increased_to_model_radius == 0 ? + new_state.increased_to_model_radius = increased_to_model_radius == 0 ? // increased_to_model_radius was not set yet. Propagate maximum. std::max(dst.state.increased_to_model_radius, src.state.increased_to_model_radius) : increased_to_model_radius; @@ -2296,7 +2299,7 @@ static SupportElementMerging* merge_influence_areas_two_sets( SupportElementMerging * const dst_begin, SupportElementMerging * dst_end, SupportElementMerging * src_begin, SupportElementMerging * const src_end) { - // Merging src into dst. + // Merging src into dst. // Areas of src should not overlap with areas of another elements of src. // Areas of dst should not overlap with areas of another elements of dst. // The memory from dst_begin to src_end is reserved for the merging operation, @@ -2349,8 +2352,8 @@ static SupportElementMerging* merge_influence_areas_two_sets( * \param layer_idx[in] The current layer. */ static void merge_influence_areas( - const TreeModelVolumes &volumes, - const TreeSupportSettings &config, + const TreeModelVolumes &volumes, + const TreeSupportSettings &config, const LayerIndex layer_idx, std::vector &influence_areas, std::function throw_on_cancel) @@ -2538,7 +2541,7 @@ static void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupp throw_on_cancel(); } - BOOST_LOG_TRIVIAL(info) << "Time spent with creating influence areas' subtasks: Increasing areas " << dur_inc.count() / 1000000 << + BOOST_LOG_TRIVIAL(info) << "Time spent with creating influence areas' subtasks: Increasing areas " << dur_inc.count() / 1000000 << " ms merging areas: " << (dur_total - dur_inc).count() / 1000000 << " ms"; } @@ -2567,7 +2570,7 @@ static void set_points_on_areas(const SupportElement &elem, SupportElements *lay // if the value was set somewhere else it it kept. This happens when a branch tries not to move after being unable to create a roof. if (! next_elem.state.result_on_layer_is_set()) { // Move inside has edgecases (see tests) so DONT use Polygons.inside to confirm correct move, Error with distance 0 is <= 1 - // it is not required to check if how far this move moved a point as is can be larger than maximum_movement_distance. + // it is not required to check if how far this move moved a point as is can be larger than maximum_movement_distance. // While this seems like a problem it may for example occur after merges. next_elem.state.result_on_layer = move_inside_if_outside(next_elem.influence_area, elem.state.result_on_layer); // do not call recursive because then amount of layers would be restricted by the stack size @@ -2592,9 +2595,9 @@ static void set_to_model_contact_simple(SupportElement &elem) * \param layer_idx[in] The current layer. */ static void set_to_model_contact_to_model_gracious( - const TreeModelVolumes &volumes, - const TreeSupportSettings &config, - std::vector &move_bounds, + const TreeModelVolumes &volumes, + const TreeSupportSettings &config, + std::vector &move_bounds, SupportElement &first_elem, std::function throw_on_cancel) { @@ -2663,7 +2666,7 @@ static void remove_deleted_elements(std::vector &move_bounds) assert(i == layer.size() || i + 1 < layer.size()); if (i + 1 < int32_t(layer.size())) { element = std::move(layer.back()); - layer.pop_back(); + layer.pop_back(); // Mark the current element as deleted. map_current[i] = -1; // Mark the moved element as moved to index i. @@ -2692,7 +2695,7 @@ static void create_nodes_from_area( std::vector &move_bounds, std::function throw_on_cancel) { - // Initialize points on layer 0, with a "random" point in the influence area. + // Initialize points on layer 0, with a "random" point in the influence area. // Point is chosen based on an inaccurate estimate where the branches will split into two, but every point inside the influence area would produce a valid result. { SupportElements *layer_above = move_bounds.size() > 1 ? &move_bounds[1] : nullptr; @@ -2912,11 +2915,11 @@ static std::pair discretize_circle(const Vec3f ¢er, const Vec3f &n // Returns Z span of the generated mesh. static std::pair extrude_branch( - const std::vector &path, - const TreeSupportSettings &config, - const SlicingParameters &slicing_params, - const std::vector &move_bounds, - indexed_triangle_set &result) + const std::vector&path, + const TreeSupportSettings &config, + const SlicingParameters &slicing_params, + const std::vector &move_bounds, + indexed_triangle_set &result) { Vec3d p1, p2, p3; Vec3d v1, v2; @@ -2925,10 +2928,6 @@ static std::pair extrude_branch( assert(path.size() >= 2); static constexpr const float eps = 0.015f; std::pair prev_strip; - -// char fname[2048]; -// static int irun = 0; - float zmin = 0; float zmax = 0; @@ -3074,7 +3073,7 @@ static void organic_smooth_branches_avoid_collisions( Vec3f position; // Previous position, for Laplacian smoothing. Vec3f prev_position; - // + // Vec3f last_collision; double last_collision_depth; // Minimum Z for which the sphere collision will be evaluated. @@ -3174,7 +3173,7 @@ static void organic_smooth_branches_avoid_collisions( // Collision detected to be removed. // Nudge the circle center away from the collision. if (collision_sphere.last_collision_depth > EPSILON) - // a little bit of hysteresis to detect end of + // a little bit of hysteresis to detect end of ++ num_moved; // Shift by maximum 2mm. double nudge_dist = std::min(std::max(0., collision_sphere.last_collision_depth + collision_extra_gap), max_nudge_collision_avoidance); @@ -3379,7 +3378,7 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons // Generator for model collision, avoidance and internal guide volumes. TreeModelVolumes volumes{ print_object, build_volume, config.maximum_move_distance, config.maximum_move_distance_slow, processing.second.front(), #ifdef SLIC3R_TREESUPPORTS_PROGRESS - m_progress_multiplier, m_progress_offset, + m_progress_multiplier, m_progress_offset, #endif // SLIC3R_TREESUPPORTS_PROGRESS /* additional_excluded_areas */{} }; @@ -3396,8 +3395,7 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons for (size_t i = 0; i < print_object.layer_count(); i++) { for (ExPolygon& expoly : print_object.get_layer(i)->loverhangs) { Polygons polys = to_polygons(expoly); - if (tree_support->overhang_types[&expoly] == TreeSupport::SharpTail) { - polys = offset(to_polygons(expoly), scale_(0.2)); + if (tree_support->overhang_types[&expoly] == TreeSupport::SharpTail) { polys = offset(polys, scale_(0.2)); } append(overhangs[i + num_raft_layers], polys); } @@ -3492,6 +3490,8 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage, throw_on_cancel); + //tree_support->move_bounds_to_contact_nodes(move_bounds, print_object, config); + remove_undefined_layers(); std::tie(interface_layers, base_interface_layers) = generate_interface_layers(print_object.config(), support_params, diff --git a/src/libslic3r/Support/TreeSupport3D.hpp b/src/libslic3r/Support/TreeSupport3D.hpp index 1e58743c25..2d50f73ac5 100644 --- a/src/libslic3r/Support/TreeSupport3D.hpp +++ b/src/libslic3r/Support/TreeSupport3D.hpp @@ -308,7 +308,7 @@ void organic_draw_branches( SupportGeneratorLayersPtr &intermediate_layers, SupportGeneratorLayerStorage &layer_storage, - std::function throw_on_cancel); + std::function throw_on_cancel); } // namespace TreeSupport3D From ae6fadda4da2722ecb8e9112a3791c4586fab57a Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 21 Mar 2024 16:43:45 +0800 Subject: [PATCH 45/72] ENH: add vertical support enforcer Previously painting support enforces on vertical faces doesn't work, as projecting the facets downwards will give empty polygons. Now we use a different mechanism to enable vertical paint-on enforces, by directly adding contact nodes. Note: this feature only works with tree support as only tree support has contact nodes. jira: none Change-Id: Id171b1665566d142a6427285baccb40c0aa00949 (cherry picked from commit 9c882f61eb37350a4486df58de48f0ae489f2d15) (cherry picked from commit 68625a6e601e2feef8e56693da1f58372b27b560) --- src/libslic3r/MultiMaterialSegmentation.cpp | 4 +-- src/libslic3r/Print.hpp | 2 +- src/libslic3r/PrintObject.cpp | 4 +-- src/libslic3r/Support/TreeSupport.cpp | 40 ++++++++++++++++++--- src/libslic3r/Support/TreeSupport.hpp | 9 ++--- src/libslic3r/Support/TreeSupport3D.cpp | 16 +++++++++ src/libslic3r/TriangleMeshSlicer.cpp | 11 ++++-- src/libslic3r/TriangleMeshSlicer.hpp | 1 + 8 files changed, 70 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 4fe0d6b4b1..492e70b730 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1363,7 +1363,7 @@ static inline std::vector> mmu_segmentation_top_and_bott if (!zs.empty() && is_volume_sinking(painted, volume_trafo)) { std::vector zs_sinking = {0.f}; Slic3r::append(zs_sinking, zs); - slice_mesh_slabs(painted, zs_sinking, volume_trafo, max_top_layers > 0 ? &top : nullptr, max_bottom_layers > 0 ? &bottom : nullptr, throw_on_cancel_callback); + slice_mesh_slabs(painted, zs_sinking, volume_trafo, max_top_layers > 0 ? &top : nullptr, max_bottom_layers > 0 ? &bottom : nullptr, nullptr, throw_on_cancel_callback); MeshSlicingParams slicing_params; slicing_params.trafo = volume_trafo; @@ -1374,7 +1374,7 @@ static inline std::vector> mmu_segmentation_top_and_bott bottom[0] = union_(bottom[0], bottom_slice); } else - slice_mesh_slabs(painted, zs, volume_trafo, max_top_layers > 0 ? &top : nullptr, max_bottom_layers > 0 ? &bottom : nullptr, throw_on_cancel_callback); + slice_mesh_slabs(painted, zs, volume_trafo, max_top_layers > 0 ? &top : nullptr, max_bottom_layers > 0 ? &bottom : nullptr, nullptr, throw_on_cancel_callback); auto merge = [](std::vector &&src, std::vector &dst) { auto it_src = find_if(src.begin(), src.end(), [](const Polygons &p){ return ! p.empty(); }); if (it_src != src.end()) { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 9618e69dfc..783987a021 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -427,7 +427,7 @@ public: std::vector slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); } // Helpers to project custom facets on slices - void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector& expolys) const; + void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector& expolys, std::vector>* vertical_points=nullptr) const; //BBS BoundingBox get_first_layer_bbox(float& area, float& layer_height, std::string& name); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 7f3945042a..247552a079 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -3965,7 +3965,7 @@ static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const index } void PrintObject::project_and_append_custom_facets( - bool seam, EnforcerBlockerType type, std::vector& out) const + bool seam, EnforcerBlockerType type, std::vector& out, std::vector>* vertical_points) const { for (const ModelVolume* mv : this->model_object()->volumes) if (mv->is_model_part()) { @@ -3980,7 +3980,7 @@ void PrintObject::project_and_append_custom_facets( else { std::vector projected; // Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane. - slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){}); + slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, vertical_points, [](){}); // Merge these projections with the output, layer by layer. assert(! projected.empty()); assert(out.empty() || out.size() == projected.size()); diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 7265945b3b..934b2922c4 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -962,7 +962,8 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) auto enforcers = m_object->slice_support_enforcers(); auto blockers = m_object->slice_support_blockers(); - m_object->project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers); + m_vertical_enforcer_points.clear(); + m_object->project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers, &m_vertical_enforcer_points); m_object->project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers); if (is_auto(stype) && config_remove_small_overhangs) { @@ -2819,12 +2820,17 @@ void TreeSupport::drop_nodes() movement = move_to_neighbor_center; // otherwise move to neighbor center first } - if (vsize2_with_unscale(movement) > get_max_move_dist(&node,2)) - movement = normal(movement, scale_(get_max_move_dist(&node))); + if (node.is_sharp_tail && node.dist_mm_to_top < 3) { + movement = normal(node.skin_direction, scale_(get_max_move_dist(&node))); + } + else if (dist2_to_outer > 0) + movement = normal(direction_to_outer, scale_(get_max_move_dist(&node))); + else + movement = normal(move_to_neighbor_center, scale_(get_max_move_dist(&node))); next_layer_vertex += movement; - if (group_index == 0) { + if (group_index == 0 && 0) { // Avoid collisions. const coordf_t max_move_between_samples = get_max_move_dist(&node, 1) + radius_sample_resolution + EPSILON; // 100 micron extra for rounding errors. bool is_outside = move_out_expolys(avoidance_next, next_layer_vertex, radius_sample_resolution + EPSILON, max_move_between_samples); @@ -3171,6 +3177,21 @@ void TreeSupport::generate_contact_points() tbb::spin_mutex mtx; + // add vertical enforcer points + std::vector zs = zs_from_layers(m_object->layers()); + std::vector>> vertical_enforcer_points_by_layers(m_object->layer_count()); + for (auto& pt_and_normal : m_vertical_enforcer_points) { + auto pt = pt_and_normal.first; + auto normal = pt_and_normal.second; // normal seems useless + auto iter = std::lower_bound(zs.begin(), zs.end(), pt.z()); + if (iter != zs.end()) { + size_t layer_nr = iter - zs.begin(); + if (layer_nr > 0 && layer_nr < contact_nodes.size()) { + vertical_enforcer_points_by_layers[layer_nr].push_back({ to_2d(pt).cast(),scaled(to_2d(normal)) }); + } + } + } + int nonempty_layers = 0; tbb::concurrent_vector all_nodes; tbb::parallel_for(tbb::blocked_range(1, m_object->layers().size()), [&](const tbb::blocked_range& range) { @@ -3179,7 +3200,6 @@ void TreeSupport::generate_contact_points() break; Layer* layer = m_object->get_layer(layer_nr); auto& curr_nodes = contact_nodes[layer_nr-1]; - if (layer->loverhangs.empty()) continue; std::unordered_set already_inserted; auto bottom_z = m_object->get_layer(layer_nr)->bottom_z(); @@ -3280,6 +3300,13 @@ void TreeSupport::generate_contact_points() } } } + for (auto& pt_and_normal : vertical_enforcer_points_by_layers[layer_nr]) { + is_sharp_tail = true;// fake it as sharp tail point so the contact distance will be 0 + auto vertical_enforcer_point= pt_and_normal.first; + auto node=insert_point(vertical_enforcer_point, ExPolygon(), false); + if (node) + node->skin_direction = pt_and_normal.second; + } if (!curr_nodes.empty()) nonempty_layers++; for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); } #ifdef SUPPORT_TREE_DEBUG_TO_SVG @@ -3288,6 +3315,9 @@ void TreeSupport::generate_contact_points() #endif }} ); // end tbb::parallel_for + + + int nNodes = all_nodes.size(); avg_node_per_layer = nodes_angle = 0; if (nNodes > 0) { diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index ff5b4f201a..c9908c01dd 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -260,7 +260,7 @@ private: coordf_t radius; size_t layer_nr; int recursions; - + }; struct RadiusLayerPairEquality { constexpr bool operator()(const RadiusLayerPair& _Left, const RadiusLayerPair& _Right) const { @@ -334,7 +334,7 @@ public: * generally considered OK as the functions are still logically const * (ie there is no difference in behaviour for the user betweeen * calculating the values each time vs caching the results). - * + * * coconut: previously stl::unordered_map is used which seems problematic with tbb::parallel_for. * So we change to tbb::concurrent_unordered_map */ @@ -413,6 +413,8 @@ public: enum OverhangType { Detected = 0, Enforced, SharpTail }; std::map overhang_types; + std::vector> m_vertical_enforcer_points; + private: /*! * \brief Generator for model collision, avoidance and internal guide volumes @@ -432,7 +434,6 @@ private: size_t m_highest_overhang_layer = 0; std::vector> m_spanning_trees; std::vector< std::unordered_map> m_mst_line_x_layer_contour_caches; - float DO_NOT_MOVER_UNDER_MM = 0.0; coordf_t base_radius = 0.0; const coordf_t MAX_BRANCH_RADIUS = 10.0; @@ -481,7 +482,7 @@ private: /*! BBS: MusangKing: maximum layer height * \brief Optimize the generation of tree support by pre-planning the layer_heights - * + * */ std::vector plan_layer_heights(); diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index f5b588bf72..f471fed021 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -3400,6 +3400,22 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons append(overhangs[i + num_raft_layers], polys); } } + // add vertical enforcer points + std::vector zs = zs_from_layers(print_object.layers()); + Polygon base_circle = make_circle(scale_(0.5), SUPPORT_TREE_CIRCLE_RESOLUTION); + for (auto &pt_and_normal :tree_support->m_vertical_enforcer_points) { + auto pt = pt_and_normal.first; + auto normal = pt_and_normal.second; // normal seems useless + auto iter = std::lower_bound(zs.begin(), zs.end(), pt.z()); + if (iter != zs.end()) { + size_t layer_nr = iter - zs.begin(); + if (layer_nr > 0 && layer_nr < print_object.layer_count()) { + Polygon circle = base_circle; + circle.translate(to_2d(pt).cast()); + overhangs[layer_nr + num_raft_layers].emplace_back(std::move(circle)); + } + } + } #else std::vector overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel); #endif diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 4e102887e3..c9a59e5b2f 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -957,7 +957,6 @@ inline std::pair slice_slabs_make_lines( } slice_facet_with_slabs(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_top, lines_mutex_top); } - // BBS: add vertical faces option if (bottom && (fo == FaceOrientation::Down || fo == FaceOrientation::Degenerate)) { Vec3i32 neighbors = face_neighbors[face_idx]; // Reset neighborship of this triangle in case the other triangle is oriented backwards from this one. @@ -2063,6 +2062,7 @@ void slice_mesh_slabs( const Transform3d &trafo, std::vector *out_top, std::vector *out_bottom, + std::vector> *vertical_points, std::function throw_on_cancel) { BOOST_LOG_TRIVIAL(debug) << "slice_mesh_slabs to polygons"; @@ -2133,6 +2133,11 @@ void slice_mesh_slabs( // Is the triangle vertical or degenerate? assert(d == 0); fo = fa == fb || fa == fc || fb == fc ? FaceOrientation::Degenerate : FaceOrientation::Vertical; + if(vertical_points && fo==FaceOrientation::Vertical) + { + Vec3f normal = (fb - fa).cross(fc - fa).normalized(); + vertical_points->push_back({ (fa + fb + fc) / 3,normal }); + } } face_orientation[&tri - mesh.indices.data()] = fo; } @@ -2297,7 +2302,7 @@ void project_mesh( { std::vector top, bottom; std::vector zs { -1e10, 1e10 }; - slice_mesh_slabs(mesh, zs, trafo, out_top ? &top : nullptr, out_bottom ? &bottom : nullptr, throw_on_cancel); + slice_mesh_slabs(mesh, zs, trafo, out_top ? &top : nullptr, out_bottom ? &bottom : nullptr, nullptr, throw_on_cancel); if (out_top) *out_top = std::move(top.front()); if (out_bottom) @@ -2311,7 +2316,7 @@ Polygons project_mesh( { std::vector top, bottom; std::vector zs { -1e10, 1e10 }; - slice_mesh_slabs(mesh, zs, trafo, &top, &bottom, throw_on_cancel); + slice_mesh_slabs(mesh, zs, trafo, &top, &bottom, nullptr, throw_on_cancel); return union_(top.front(), bottom.back()); } diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp index ea6a7262cc..1f7bba9d27 100644 --- a/src/libslic3r/TriangleMeshSlicer.hpp +++ b/src/libslic3r/TriangleMeshSlicer.hpp @@ -107,6 +107,7 @@ void slice_mesh_slabs( const Transform3d &trafo, std::vector *out_top, std::vector *out_bottom, + std::vector> *vertical_points, std::function throw_on_cancel); // Project mesh upwards pointing surfaces / downwards pointing surfaces into 2D polygons. From f76683e90e3078fd1f33779ab0224b2b7a7b60d2 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 30 Aug 2024 15:13:29 +0800 Subject: [PATCH 46/72] FIX: support wall count doesn't work jira: STUDIO-7975 Change-Id: Ic580d298568fc6eab8b1a2c017fa182869b432bf (cherry picked from commit 82bcb099e139065cc00c133f507e955d9955b2f4) (cherry picked from commit 04756bf447f690a071eace1500b150f0b7b4ce02) --- src/libslic3r/PrintConfig.cpp | 5 +- src/libslic3r/Support/SupportCommon.cpp | 71 ++++++++++--------- src/libslic3r/Support/SupportCommon.hpp | 2 + src/libslic3r/Support/SupportParameters.hpp | 78 ++++++++++----------- src/libslic3r/Support/TreeSupport.cpp | 22 ++++-- src/libslic3r/Support/TreeSupport3D.cpp | 1 + 6 files changed, 95 insertions(+), 84 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 31ab6fcbff..aea3beb4e4 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4955,10 +4955,11 @@ void PrintConfigDef::init_fff_params() def = this->add("tree_support_wall_count", coInt); def->label = L("Support wall loops"); def->category = L("Support"); - def->tooltip = L("This setting specify the count of walls around support"); + def->tooltip = L("This setting specifies the min count of support walls in the range of [0,2]. Actual wall count may be larger than the specified value."); def->min = 0; + def->max = 2; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(0)); + def->set_default_value(new ConfigOptionInt(1)); def = this->add("tree_support_with_infill", coBool); def->label = L("Tree support with infill"); diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index 6d6553b12f..7651b1c71b 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -36,7 +36,7 @@ namespace Slic3r { // how much we extend support around the actual contact area //FIXME this should be dependent on the nozzle diameter! -#define SUPPORT_MATERIAL_MARGIN 1.5 +#define SUPPORT_MATERIAL_MARGIN 1.5 //#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3. //#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 @@ -144,6 +144,7 @@ std::pair generate_interfa const bool smooth_supports = support_params.support_style != smsGrid; SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first; SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second; + interface_layers.assign(intermediate_layers.size(), nullptr); if (support_params.has_base_interfaces()) base_interface_layers.assign(intermediate_layers.size(), nullptr); @@ -152,7 +153,7 @@ std::pair generate_interfa const auto closing_distance = smoothing_distance; // scaled(config.support_material_closing_radius.value); // Insert a new layer into base_interface_layers, if intersection with base exists. auto insert_layer = [&layer_storage, smooth_supports, closing_distance, smoothing_distance, minimum_island_radius]( - SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer, + SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer, const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* { bool has_top_interface = top_interface_layer && ! top_interface_layer->polygons.empty(); assert(! bottom.empty() || ! top.empty() || has_top_interface); @@ -194,7 +195,7 @@ std::pair generate_interfa }; tbb::parallel_for(tbb::blocked_range(0, int(intermediate_layers.size())), [&bottom_contacts, &top_contacts, &top_interface_layers, &top_base_interface_layers, &intermediate_layers, &insert_layer, &support_params, - snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range& range) { + snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range& range) { // Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below // this intermediate layer. // Index of the first top contact layer intersecting the current intermediate layer. @@ -230,7 +231,7 @@ std::pair generate_interfa //FIXME maybe this adds one interface layer in excess? if (top_contact_layer.bottom_z - EPSILON > top_z) break; - polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, + polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, // For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers. // For grid supports, merging of support regions will be performed by the projection into grid. snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons); @@ -242,7 +243,7 @@ std::pair generate_interfa coordf_t bottom_interface_z = - std::numeric_limits::max(); if (support_params.num_bottom_base_interface_layers > 0) // Some bottom base interface layers will be generated. - bottom_interface_z = support_params.num_bottom_interface_layers_only() == 0 ? + bottom_interface_z = support_params.num_bottom_interface_layers_only() == 0 ? // Only base interface layers to generate. std::numeric_limits::max() : intermediate_layers[std::max(0, idx_intermediate_layer - int(support_params.num_bottom_interface_layers_only()))]->bottom_z; @@ -307,7 +308,7 @@ std::pair generate_interfa base_interface_layers = merge_remove_empty(base_interface_layers, top_base_interface_layers); BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end"; } - + return base_and_interface_layers; } @@ -836,8 +837,8 @@ struct SupportGeneratorLayerExtruded return layer == nullptr || layer->polygons.empty(); } - void set_polygons_to_extrude(Polygons &&polygons) { - if (m_polygons_to_extrude == nullptr) + void set_polygons_to_extrude(Polygons &&polygons) { + if (m_polygons_to_extrude == nullptr) m_polygons_to_extrude = std::make_unique(std::move(polygons)); else *m_polygons_to_extrude = std::move(polygons); @@ -846,9 +847,9 @@ struct SupportGeneratorLayerExtruded const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } bool could_merge(const SupportGeneratorLayerExtruded &other) const { - return ! this->empty() && ! other.empty() && + return ! this->empty() && ! other.empty() && std::abs(this->layer->height - other.layer->height) < EPSILON && - this->layer->bridging == other.layer->bridging; + this->layer->bridging == other.layer->bridging; } // Merge regions, perform boolean union over the merged polygons. @@ -954,7 +955,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact const Point* operator()(const Point &pt) const { return &pt; } }; typedef ClosestPointInRadiusLookup ClosestPointLookupType; - + Polygons loops0; { // find centerline of the external loop of the contours @@ -1051,7 +1052,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact for (int i = 1; i < n_contact_loops; ++ i) polygons_append(loop_polygons, opening( - loops0, + loops0, i * flow.scaled_spacing() + 0.5f * flow.scaled_spacing(), 0.5f * flow.scaled_spacing())); // Clip such loops to the side oriented towards the object. @@ -1111,7 +1112,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact // Remove empty lines. remove_degenerate(loop_lines); } - + // add the contact infill area to the interface area // note that growing loops by $circle_radius ensures no tiny // extrusions are left inside the circles; however it creates @@ -1183,7 +1184,7 @@ static void modulate_extrusion_by_overlapping_layers( // Split the extrusions by the overlapping layers, reduce their extrusion rate. // The last path_fragment is from this_layer. std::vector path_fragments( - n_overlapping_layers + 1, + n_overlapping_layers + 1, ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height)); // Don't use it, it will be released. extrusion_path_template = nullptr; @@ -1235,7 +1236,7 @@ static void modulate_extrusion_by_overlapping_layers( #endif /* SLIC3R_DEBUG */ // End points of the original paths. - std::vector> path_ends; + std::vector> path_ends; // Collect the paths of this_layer. { Polylines &polylines = path_fragments.back().polylines; @@ -1452,7 +1453,7 @@ SupportGeneratorLayersPtr generate_support_layers( height_min = std::min(height_min, layer.height); } if (! empty) { - // Here the upper_layer and lower_layer pointers are left to null at the support layers, + // Here the upper_layer and lower_layer pointers are left to null at the support layers, // as they are never used. These pointers are candidates for removal. bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces; size_t this_layer_id_interface = layer_id_interface; @@ -1627,12 +1628,12 @@ void generate_support_toolpaths( // Pointer to the 1st layer interface filler. auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get(); // Filler for the 1st layer interface, if different from filler_interface. - auto filler_raft_contact_ptr = std::unique_ptr(range.begin() == n_raft_layers && config.support_interface_top_layers.value == 0 ? + auto filler_raft_contact_ptr = std::unique_ptr(range.begin() == n_raft_layers && config.support_interface_top_layers.value == 0 ? Fill::new_from_type(support_params.raft_interface_fill_pattern) : nullptr); // Pointer to the 1st layer interface filler. auto filler_raft_contact = filler_raft_contact_ptr ? filler_raft_contact_ptr.get() : filler_interface.get(); // Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer). - auto filler_base_interface = std::unique_ptr(base_interface_layers.empty() ? nullptr : + auto filler_base_interface = std::unique_ptr(base_interface_layers.empty() ? nullptr : Fill::new_from_type(support_params.interface_density > 0.95 || support_params.with_sheath ? ipRectilinear : ipSupportBase)); auto filler_support = std::unique_ptr(Fill::new_from_type(support_params.base_fill_pattern)); filler_interface->set_bounding_box(bbox_object); @@ -1695,7 +1696,7 @@ void generate_support_toolpaths( // to trim other layers. if (top_contact_layer.could_merge(interface_layer) && ! raft_layer) top_contact_layer.merge(std::move(interface_layer)); - } + } if ((config.support_interface_top_layers == 0 || config.support_interface_bottom_layers == 0) && support_params.can_merge_support_regions) { if (base_layer.could_merge(bottom_contact_layer)) base_layer.merge(std::move(bottom_contact_layer)); @@ -1729,14 +1730,14 @@ void generate_support_toolpaths( auto *filler = raft_contact ? filler_raft_contact : filler_interface.get(); auto interface_flow = layer_ex.layer->bridging ? Flow::bridging_flow(layer_ex.layer->height, support_params.support_material_bottom_interface_flow.nozzle_diameter()) : - (raft_contact ? &support_params.raft_interface_flow : + (raft_contact ? &support_params.raft_interface_flow : interface_as_base ? &support_params.support_material_flow : &support_params.support_material_interface_flow) ->with_height(float(layer_ex.layer->height)); filler->angle = interface_as_base ? // If zero interface layers are configured, use the same angle as for the base layers. angles[support_layer_id % angles.size()] : // Use interface angle for the interface layers. - raft_contact ? + raft_contact ? support_params.raft_interface_angle(support_layer.interface_id()) : support_interface_angle; double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density; @@ -1745,7 +1746,7 @@ void generate_support_toolpaths( filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); fill_expolygons_generate_paths( // Destination - layer_ex.extrusions, + layer_ex.extrusions, // Regions to fill union_safety_offset_ex(layer_ex.polygons_to_extrude()), // Filler and its parameters @@ -1772,7 +1773,7 @@ void generate_support_toolpaths( filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.interface_density)); fill_expolygons_generate_paths( // Destination - base_interface_layer.extrusions, + base_interface_layer.extrusions, //base_layer_interface.extrusions, // Regions to fill union_safety_offset_ex(base_interface_layer.polygons_to_extrude()), @@ -1927,7 +1928,7 @@ void PrintObjectSupportMaterial::clip_by_pillars( coord_t pillar_size = scale_(PILLAR_SIZE); coord_t pillar_spacing = scale_(PILLAR_SPACING); - + // A regular grid of pillars, filling the 2D bounding box. Polygons grid; { @@ -1937,7 +1938,7 @@ void PrintObjectSupportMaterial::clip_by_pillars( pillar.points.push_back(Point(pillar_size, 0)); pillar.points.push_back(Point(pillar_size, pillar_size)); pillar.points.push_back(Point(0, pillar_size)); - + // 2D bounding box of the projection of all contact polygons. BoundingBox bbox; for (LayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it) @@ -1951,30 +1952,30 @@ void PrintObjectSupportMaterial::clip_by_pillars( } } } - + // add pillars to every layer for my $i (0..n_support_z) { $shape->[$i] = [ @$grid ]; } - + // build capitals for my $i (0..n_support_z) { my $z = $support_z->[$i]; - + my $capitals = intersection( $grid, $contact->{$z} // [], ); - + // work on one pillar at time (if any) to prevent the capitals from being merged - // but store the contact area supported by the capital because we need to make + // but store the contact area supported by the capital because we need to make // sure nothing is left my $contact_supported_by_capitals = []; foreach my $capital (@$capitals) { // enlarge capital tops $capital = offset([$capital], +($pillar_spacing - $pillar_size)/2); push @$contact_supported_by_capitals, @$capital; - + for (my $j = $i-1; $j >= 0; $j--) { my $jz = $support_z->[$j]; $capital = offset($capital, -$self->interface_flow->scaled_width/2); @@ -1982,7 +1983,7 @@ void PrintObjectSupportMaterial::clip_by_pillars( push @{ $shape->[$j] }, @$capital; } } - + // Capitals will not generally cover the whole contact area because there will be // remainders. For now we handle this situation by projecting such unsupported // areas to the ground, just like we would do with a normal support. @@ -2000,10 +2001,10 @@ void PrintObjectSupportMaterial::clip_by_pillars( sub clip_with_shape { my ($self, $support, $shape) = @_; - + foreach my $i (keys %$support) { - // don't clip bottom layer with shape so that we - // can generate a continuous base flange + // don't clip bottom layer with shape so that we + // can generate a continuous base flange // also don't clip raft layers next if $i == 0; next if $i < $self->object_config->raft_layers; diff --git a/src/libslic3r/Support/SupportCommon.hpp b/src/libslic3r/Support/SupportCommon.hpp index d292c9cd8f..6f5894fc1d 100644 --- a/src/libslic3r/Support/SupportCommon.hpp +++ b/src/libslic3r/Support/SupportCommon.hpp @@ -50,6 +50,8 @@ SupportGeneratorLayersPtr generate_raft_base( const SupportGeneratorLayersPtr &base_layers, SupportGeneratorLayerStorage &layer_storage); +void tree_supports_generate_paths(ExtrusionEntitiesPtr &dst, const Polygons &polygons, const Flow &flow, const SupportParameters &support_params); + void fill_expolygons_with_sheath_generate_paths( ExtrusionEntitiesPtr &dst, const Polygons &polygons, Fill *filler, float density, ExtrusionRole role, const Flow &flow, const SupportParameters& support_params, bool with_sheath, bool no_sort); diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index 1b1c11d953..2e3ec3a6da 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -6,45 +6,42 @@ namespace Slic3r { struct SupportParameters { - SupportParameters() = default; - SupportParameters(const PrintObject &object) + SupportParameters() = delete; + SupportParameters(const PrintObject& object) { - const PrintConfig &print_config = object.print()->config(); - const PrintObjectConfig &object_config = object.config(); - const SlicingParameters &slicing_params = object.slicing_parameters(); - - this->soluble_interface = slicing_params.soluble_interface; - this->soluble_interface_non_soluble_base = - // Zero z-gap between the overhangs and the support interface. - slicing_params.soluble_interface && - // Interface extruder soluble. - object_config.support_interface_filament.value > 0 && print_config.filament_soluble.get_at(object_config.support_interface_filament.value - 1) && - // Base extruder: Either "print with active extruder" not soluble. - (object_config.support_filament.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_filament.value - 1)); - - { - int num_top_interface_layers = std::max(0, object_config.support_interface_top_layers.value); - int num_bottom_interface_layers = object_config.support_interface_bottom_layers < 0 ? - num_top_interface_layers : object_config.support_interface_bottom_layers; - this->has_top_contacts = num_top_interface_layers > 0; - this->has_bottom_contacts = num_bottom_interface_layers > 0; - this->num_top_interface_layers = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0; - this->num_bottom_interface_layers = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0; - if (this->soluble_interface_non_soluble_base) { - // Try to support soluble dense interfaces with non-soluble dense interfaces. - this->num_top_base_interface_layers = size_t(std::min(num_top_interface_layers / 2, 2)); - this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2)); - } else { - this->num_top_base_interface_layers = 0; - this->num_bottom_base_interface_layers = 0; - } - } - - this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height)); - this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height)); - this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height)); - this->raft_interface_flow = support_material_interface_flow; - + const PrintConfig& print_config = object.print()->config(); + const PrintObjectConfig& object_config = object.config(); + const SlicingParameters& slicing_params = object.slicing_parameters(); + + this->soluble_interface = slicing_params.soluble_interface; + this->soluble_interface_non_soluble_base = + // Zero z-gap between the overhangs and the support interface. + slicing_params.soluble_interface && + // Interface extruder soluble. + object_config.support_interface_filament.value > 0 && print_config.filament_soluble.get_at(object_config.support_interface_filament.value - 1) && + // Base extruder: Either "print with active extruder" not soluble. + (object_config.support_filament.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_filament.value - 1)); + + { + this->num_top_interface_layers = std::max(0, object_config.support_interface_top_layers.value); + this->num_bottom_interface_layers = object_config.support_interface_bottom_layers < 0 ? + num_top_interface_layers : object_config.support_interface_bottom_layers; + this->has_top_contacts = num_top_interface_layers > 0; + this->has_bottom_contacts = num_bottom_interface_layers > 0; + if (this->soluble_interface_non_soluble_base) { + // Try to support soluble dense interfaces with non-soluble dense interfaces. + this->num_top_base_interface_layers = size_t(std::min(int(num_top_interface_layers) / 2, 2)); + this->num_bottom_base_interface_layers = size_t(std::min(int(num_bottom_interface_layers) / 2, 2)); + } else { + this->num_top_base_interface_layers = 0; + this->num_bottom_base_interface_layers = 0; + } + } + this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height)); + this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height)); + this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height)); + this->raft_interface_flow = support_material_interface_flow; + // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. this->support_layer_height_min = scaled(0.01); for (auto lh : print_config.min_layer_height.values) @@ -155,7 +152,8 @@ struct SupportParameters { independent_layer_height = print_config.independent_support_layer_height; - tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI; + // force double walls everywhere if wall count is larger than 1 + tree_branch_diameter_double_wall_area_scaled = object_config.tree_support_wall_count.value > 1 ? 0.1 : 0.25 * sqr(scaled(5.0)) * M_PI; support_style = object_config.support_style; if (support_style == smsDefault) { @@ -236,7 +234,7 @@ struct SupportParameters { // Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness? bool with_sheath; // Branches of organic supports with area larger than this threshold will be extruded with double lines. - double tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled(3.0)) * M_PI;; + double tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled(5.0)) * M_PI;; float raft_angle_1st_layer; float raft_angle_base; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 934b2922c4..87e50d66c1 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1541,12 +1541,16 @@ void TreeSupport::generate_toolpaths() } else { size_t walls = wall_count; - if (area_group.need_extra_wall && walls < 2) walls += 1; - for (size_t i = 1; i < walls; i++) { - Polygons contour_new = offset(poly.contour, -(i - 0.5f) * flow.scaled_spacing(), jtSquare); - loops.insert(loops.end(), contour_new.begin(), contour_new.end()); - } - fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, nullptr, 0, erSupportMaterial, flow, m_support_params, true, false); + //if (area_group.need_extra_wall && walls < 2) walls += 1; + //for (size_t i = 1; i < walls; i++) { + // Polygons contour_new = offset(poly.contour, -(i - 0.5f) * flow.scaled_spacing(), jtSquare); + // loops.insert(loops.end(), contour_new.begin(), contour_new.end()); + //} + //fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, nullptr, 0, erSupportMaterial, flow, true, false); + SupportParameters support_params = m_support_params; + if(walls>1) + support_params.tree_branch_diameter_double_wall_area_scaled=0.1; + tree_supports_generate_paths(ts_layer->support_fills.entities, loops, flow, support_params); } } } @@ -1606,8 +1610,12 @@ void TreeSupport::generate_toolpaths() } // sort extrusions to reduce travel, also make sure walls go before infills - if(ts_layer->support_fills.no_sort==false) + if (ts_layer->support_fills.no_sort == false) { + // chain_and_reorder_extrusion_entities crashes if there are empty elements in entities + auto &entities = ts_layer->support_fills.entities; + entities.erase(std::remove_if(entities.begin(), entities.end(), [](ExtrusionEntity* entity) { return static_cast(entity)->empty(); }), entities.end()); chain_and_reorder_extrusion_entities(ts_layer->support_fills.entities); + } } } ); diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index f471fed021..dad831076c 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -3434,6 +3434,7 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons // The trees will have the density zeroed in tree_supports_generate_paths() // support_params.support_density = 0; + SupportGeneratorLayerStorage layer_storage; SupportGeneratorLayersPtr top_contacts; SupportGeneratorLayersPtr bottom_contacts; From 59b756acb8aefc6270246cd4818175cc33e0b7a9 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 3 Sep 2024 17:25:36 +0800 Subject: [PATCH 47/72] FIX: chain_and_reorder_extrusion_entities crashes this function crashes if there are empty elements in entities. jira: STUDIO-7975 Change-Id: I0dbeb6b1151dd089be7617ebc3271691f64ac61e (cherry picked from commit df30728617a89891c68e36cce771fb6380355b82) (cherry picked from commit e42aabebb16253b0172fb80a58f58953aec8dda7) --- src/libslic3r/ShortestPath.cpp | 3 +++ src/libslic3r/Support/TreeSupport.cpp | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index 3aa99e2b76..2b017b709b 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -1030,6 +1030,9 @@ void reorder_extrusion_entities(std::vector &entities, const s void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near) { + // this function crashes if there are empty elements in entities + entities.erase(std::remove_if(entities.begin(), entities.end(), [](ExtrusionEntity *entity) { return static_cast(entity)->empty(); }), + entities.end()); reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near)); } diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 87e50d66c1..c9cb47a734 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1611,9 +1611,6 @@ void TreeSupport::generate_toolpaths() // sort extrusions to reduce travel, also make sure walls go before infills if (ts_layer->support_fills.no_sort == false) { - // chain_and_reorder_extrusion_entities crashes if there are empty elements in entities - auto &entities = ts_layer->support_fills.entities; - entities.erase(std::remove_if(entities.begin(), entities.end(), [](ExtrusionEntity* entity) { return static_cast(entity)->empty(); }), entities.end()); chain_and_reorder_extrusion_entities(ts_layer->support_fills.entities); } } From e78d50f733658069fe650a4583180449f6ef8bde Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 11 Sep 2024 17:13:03 +0800 Subject: [PATCH 48/72] ENH: improve hybrid tree support 1. keep all polygon nodes in drop_nodes 2. prevent generating too small polygon nodes jira: STUDIO-8107 Change-Id: I1311158ab15097eb10727a8d6884b0bcd8136ef1 (cherry picked from commit 038b92a536a56568b1c6f385ce19ff36331cd46a) --- src/libslic3r/Support/TreeSupport.cpp | 40 ++++++++++++--------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index c9cb47a734..2afb96ccf4 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -2690,21 +2690,12 @@ void TreeSupport::drop_nodes() const bool to_buildplate = true; // keep only the part that won't be removed by the next layer ExPolygons overhangs_next = diff_clipped({ node.overhang }, get_collision(0, obj_layer_nr_next)); - // find the biggest overhang if there are many - float area_biggest = -1; - int index_biggest = -1; - for (int i = 0; i < overhangs_next.size(); i++) { - float a=area(overhangs_next[i]); - if (a > area_biggest) { - area_biggest = a; - index_biggest = i; - } - } - if (index_biggest >= 0) { - SupportNode* next_node = m_ts_data->create_node(p_node->position, p_node->distance_to_top + 1, obj_layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate, - p_node, print_z_next, height_next); + for(auto& overhang:overhangs_next) { + Point next_pt = overhang.contour.centroid(); + SupportNode *next_node = m_ts_data->create_node(next_pt, p_node->distance_to_top + 1, obj_layer_nr_next, p_node->support_roof_layers_below - 1, + to_buildplate, p_node, print_z_next, height_next); next_node->max_move_dist = 0; - next_node->overhang = std::move(overhangs_next[index_biggest]); + next_node->overhang = std::move(overhang); m_ts_data->m_mutex.lock(); contact_nodes[layer_nr_next].emplace_back(next_node); m_ts_data->m_mutex.unlock(); @@ -3245,15 +3236,20 @@ void TreeSupport::generate_contact_points() ExPolygons overhangs_regular; if (m_support_params.support_style == smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang && !is_sharp_tail) { overhangs_regular = offset_ex(intersection_ex({overhang_part}, m_ts_data->m_layer_outlines_below[layer_nr - 1]), radius_scaled); - overhangs_regular = diff_ex(overhangs_regular, relevant_forbidden); ExPolygons overhangs_normal = diff_ex({overhang_part}, overhangs_regular); - for(auto& overhang:overhangs_normal) { - BoundingBox overhang_bounds = get_extents(overhang); - double radius = unscale_(overhang_bounds.radius()); - Point candidate = overhang_bounds.center(); - SupportNode *contact_node = insert_point(candidate, overhang, radius, true, true); - contact_node->type = ePolygon; - curr_nodes.emplace_back(contact_node); + if (area(overhangs_normal) > m_support_params.thresh_big_overhang) { + // if the outside area is still big, we can need normal nodes + for (auto &overhang : overhangs_normal) { + BoundingBox overhang_bounds = get_extents(overhang); + double radius = unscale_(overhang_bounds.radius()); + Point candidate = overhang_bounds.center(); + SupportNode *contact_node = insert_point(candidate, overhang, radius, true, true); + contact_node->type = ePolygon; + curr_nodes.emplace_back(contact_node); + } + }else{ + // otherwise, all nodes should be circle nodes + overhangs_regular = ExPolygons{overhang_part}; } } else { overhangs_regular = ExPolygons{overhang_part}; From 3e7e4df7cea2f28f9d3875aeb43b7f8f4e085633 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 12 Sep 2024 15:50:37 +0800 Subject: [PATCH 49/72] ENH: precise tree support wall count Change the behavior of "tree support wall count" option, let it control precisely. 0 means auto. jira: STUDIO-8068 github: 4780 Change-Id: I6d1a64cff9b121f5c0a3e910c5ddbfe6db198687 (cherry picked from commit a557bbe758cd352fa9bb48323995ed2c90737577) --- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 16 ++-------------- src/libslic3r/PrintConfig.hpp | 1 - src/libslic3r/PrintObject.cpp | 1 - src/libslic3r/Support/SupportParameters.hpp | 4 +++- src/libslic3r/Support/TreeSupport.cpp | 12 +----------- src/slic3r/GUI/ConfigManipulation.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 1 - 8 files changed, 8 insertions(+), 31 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 3051d88fb0..e633d094f8 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -812,7 +812,7 @@ static std::vector s_Preset_print_options { "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits", "flush_into_infill", "flush_into_objects", "flush_into_support", "tree_support_branch_angle", "tree_support_angle_slow", "tree_support_wall_count", "tree_support_top_rate", "tree_support_branch_distance", "tree_support_tip_diameter", - "tree_support_branch_diameter", "tree_support_branch_diameter_angle", "tree_support_branch_diameter_double_wall", + "tree_support_branch_diameter", "tree_support_branch_diameter_angle", "detect_narrow_internal_solid_infill", "gcode_add_line_number", "enable_arc_fitting", "precise_z_height", "infill_combination","infill_combination_max_layer_height", /*"adaptive_layer_height",*/ "support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index aea3beb4e4..6d95d4e943 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4940,26 +4940,14 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(5)); - def = this->add("tree_support_branch_diameter_double_wall", coFloat); - def->label = L("Branch Diameter with double walls"); - def->category = L("Support"); - // TRN PrintSettings: "Organic supports" > "Branch Diameter" - def->tooltip = L("Branches with area larger than the area of a circle of this diameter will be printed with double walls for stability. " - "Set this value to zero for no double walls."); - def->sidetext = L("mm"); - def->min = 0; - def->max = 100.f; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(3.)); - def = this->add("tree_support_wall_count", coInt); def->label = L("Support wall loops"); def->category = L("Support"); - def->tooltip = L("This setting specifies the min count of support walls in the range of [0,2]. Actual wall count may be larger than the specified value."); + def->tooltip = L("This setting specifies the count of support walls in the range of [0,2]. 0 means auto."); def->min = 0; def->max = 2; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionInt(1)); + def->set_default_value(new ConfigOptionInt(0)); def = this->add("tree_support_with_infill", coBool); def->label = L("Tree support with infill"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index b0c1d910fa..d7d4dd5087 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -843,7 +843,6 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, tree_support_tip_diameter)) ((ConfigOptionFloat, tree_support_branch_diameter)) ((ConfigOptionFloat, tree_support_branch_diameter_angle)) - ((ConfigOptionFloat, tree_support_branch_diameter_double_wall)) ((ConfigOptionFloat, tree_support_branch_angle)) ((ConfigOptionFloat, tree_support_angle_slow)) ((ConfigOptionInt, tree_support_wall_count)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 247552a079..a2deed0924 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1033,7 +1033,6 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "tree_support_branch_diameter" || opt_key == "tree_support_branch_diameter_organic" || opt_key == "tree_support_branch_diameter_angle" - || opt_key == "tree_support_branch_diameter_double_wall" || opt_key == "tree_support_branch_angle" || opt_key == "tree_support_branch_angle_organic" || opt_key == "tree_support_angle_slow" diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index 2e3ec3a6da..82ced55094 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -153,7 +153,9 @@ struct SupportParameters { independent_layer_height = print_config.independent_support_layer_height; // force double walls everywhere if wall count is larger than 1 - tree_branch_diameter_double_wall_area_scaled = object_config.tree_support_wall_count.value > 1 ? 0.1 : 0.25 * sqr(scaled(5.0)) * M_PI; + tree_branch_diameter_double_wall_area_scaled = object_config.tree_support_wall_count.value > 1 ? 0.1 : + object_config.tree_support_wall_count.value == 0 ? 0.25 * sqr(scaled(5.0)) * M_PI : + std::numeric_limits::max(); support_style = object_config.support_style; if (support_style == smsDefault) { diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 2afb96ccf4..781e50de2b 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1540,17 +1540,7 @@ void TreeSupport::generate_toolpaths() erSupportMaterial, filler_support.get(), support_density); } else { - size_t walls = wall_count; - //if (area_group.need_extra_wall && walls < 2) walls += 1; - //for (size_t i = 1; i < walls; i++) { - // Polygons contour_new = offset(poly.contour, -(i - 0.5f) * flow.scaled_spacing(), jtSquare); - // loops.insert(loops.end(), contour_new.begin(), contour_new.end()); - //} - //fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, nullptr, 0, erSupportMaterial, flow, true, false); - SupportParameters support_params = m_support_params; - if(walls>1) - support_params.tree_branch_diameter_double_wall_area_scaled=0.1; - tree_supports_generate_paths(ts_layer->support_fills.entities, loops, flow, support_params); + tree_supports_generate_paths(ts_layer->support_fills.entities, loops, flow, m_support_params); } } } diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 4e13f7972c..a10f1399c9 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -614,7 +614,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co for (auto el : {"tree_support_auto_brim", "tree_support_brim_width", "tree_support_adaptive_layer_height"}) toggle_line(el, support_is_normal_tree); // settings specific to organic trees - for (auto el : {"tree_support_branch_angle_organic", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic","tree_support_angle_slow","tree_support_tip_diameter", "tree_support_top_rate", "tree_support_branch_diameter_angle", "tree_support_branch_diameter_double_wall"}) + for (auto el : {"tree_support_branch_angle_organic", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic","tree_support_angle_slow","tree_support_tip_diameter", "tree_support_top_rate", "tree_support_branch_diameter_angle"}) toggle_line(el, support_is_organic); toggle_field("tree_support_brim_width", support_is_tree && !config->opt_bool("tree_support_auto_brim")); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4ae655c2cf..9e0b5eb8a5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2242,7 +2242,6 @@ void TabPrint::build() optgroup->append_single_option_line("tree_support_branch_angle", "support#tree-support-only-options"); optgroup->append_single_option_line("tree_support_branch_angle_organic", "support#tree-support-only-options"); optgroup->append_single_option_line("tree_support_angle_slow"); - optgroup->append_single_option_line("tree_support_branch_diameter_double_wall"); optgroup->append_single_option_line("tree_support_adaptive_layer_height"); optgroup->append_single_option_line("tree_support_auto_brim"); optgroup->append_single_option_line("tree_support_brim_width"); From 724d8a12b651583eba6f5c621c2f9e5a0c622ce8 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 19 Sep 2024 11:24:36 +0800 Subject: [PATCH 50/72] FIX: fix hybrid tree support may go outside plate 1. fix hybrid tree support may go outside plate github: #4769 2. fix false alarm of empty layer warning jira: STUDIO-8178 Change-Id: I7bcc3959b06184901cbec946e8840c7a94bc1cab (cherry picked from commit 647bd4213c363eff6258992f5f607c1f03cbc482) --- src/libslic3r/GCode.cpp | 17 +++++++++++++---- src/libslic3r/Support/TreeSupport.cpp | 5 +++++ src/libslic3r/Support/TreeSupport.hpp | 1 + 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a1351cb6f5..c391d2be3a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1202,14 +1202,23 @@ std::vector GCode::collect_layers_to_print(const PrintObjec // Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions. || (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) { double top_cd = object.config().support_top_z_distance; - double bottom_cd = object.config().support_bottom_z_distance; - + double bottom_cd = object.config().support_bottom_z_distance == 0. ? top_cd : object.config().support_bottom_z_distance; + //if (!object.print()->config().independent_support_layer_height) + { // the actual support gap may be larger than the configured one due to rounding to layer height for organic support, regardless of independent support layer height + top_cd = std::ceil(top_cd / object.config().layer_height) * object.config().layer_height; + bottom_cd = std::ceil(bottom_cd / object.config().layer_height) * object.config().layer_height; + } double extra_gap = (layer_to_print.support_layer ? bottom_cd : top_cd); // raft contact distance should not trigger any warning - if(last_extrusion_layer && last_extrusion_layer->support_layer) + if (last_extrusion_layer && last_extrusion_layer->support_layer) { + double raft_gap = object.config().raft_contact_distance.value; + //if (!object.print()->config().independent_support_layer_height) + { + raft_gap = std::ceil(raft_gap / object.config().layer_height) * object.config().layer_height; + } extra_gap = std::max(extra_gap, object.config().raft_contact_distance.value); - + } double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.) + layer_to_print.layer()->height + std::max(0., extra_gap); diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 781e50de2b..8cd1926378 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1662,6 +1662,9 @@ void TreeSupport::generate() create_tree_support_layers(); m_ts_data = m_object->alloc_tree_support_preview_cache(); m_ts_data->is_slim = is_slim; + // // get the ring of outside plate + // auto tmp= diff_ex(offset_ex(m_machine_border, scale_(100)), m_machine_border); + // if (!tmp.empty()) m_ts_data->m_machine_border = tmp[0]; std::vector move_bounds(m_highest_overhang_layer + 1); profiler.stage_start(STAGE_GENERATE_CONTACT_NODES); @@ -2093,6 +2096,7 @@ void TreeSupport::draw_circles() // join roof segments roof_areas = diff_clipped(offset2_ex(roof_areas, line_width_scaled, -line_width_scaled), get_collision(false)); + roof_areas = intersection_ex(roof_areas, m_machine_border); roof_1st_layer = diff_clipped(offset2_ex(roof_1st_layer, line_width_scaled, -line_width_scaled), get_collision(false)); // roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer @@ -3460,6 +3464,7 @@ const ExPolygons& TreeSupportData::calculate_collision(const RadiusLayerPair& ke ExPolygons collision_areas = offset_ex(m_layer_outlines[key.layer_nr], scale_(key.radius+m_xy_distance)); collision_areas = expolygons_simplify(collision_areas, scale_(m_radius_sample_resolution)); + // collision_areas.emplace_back(m_machine_border); const auto ret = m_collision_cache.insert({ key, std::move(collision_areas) }); return ret.first->second; } diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index c9908c01dd..3c630629b4 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -251,6 +251,7 @@ public: std::vector layer_heights; std::vector contact_nodes; + // ExPolygon m_machine_border; private: /*! From e6880a468bcfc2950c1c05685a913f9d51e9da34 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 26 Sep 2024 15:30:40 +0800 Subject: [PATCH 51/72] FIX: tree support crashes when it's too short jira: STUDIO-8277 Change-Id: I327d9fb7beb6cc2822131ca4954066217b1a5c9b (cherry picked from commit 00e5e84bbdfb680da74c4861a56ec8f5a867f58d) --- src/libslic3r/Support/TreeSupport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 8cd1926378..b25a53d5ba 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -2136,7 +2136,7 @@ void TreeSupport::draw_circles() base_areas = std::move(diff_ex(base_areas, offset_ex(floor_areas, 10))); } } - if (bottom_gap_layers > 0 && layer_nr > bottom_gap_layers) { + if (bottom_gap_layers > 0 && m_ts_data->layer_heights[layer_nr].obj_layer_nr > bottom_gap_layers) { const Layer* below_layer = m_object->get_layer(m_ts_data->layer_heights[layer_nr].obj_layer_nr - bottom_gap_layers); ExPolygons bottom_gap_area = intersection_ex(floor_areas, below_layer->lslices); if (!bottom_gap_area.empty()) { From 36970dd9a23652daf3724e5a34d8d6633fe784f9 Mon Sep 17 00:00:00 2001 From: Arthur Date: Sun, 29 Sep 2024 19:22:21 +0800 Subject: [PATCH 52/72] FIX: hybrid tree support crash in some case jira: STUDIO-8313 Change-Id: Ide03d8f666232f457305f3dd298bf8151ba9c57b (cherry picked from commit 35bf682f7938ea6ddfa9525249c6a4cbb1f16071) --- src/libslic3r/Support/TreeSupport.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index b25a53d5ba..5dca8692f4 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -3474,7 +3474,10 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke const auto &radius = key.radius; const auto &layer_nr = key.layer_nr; if (layer_nr == 0) { - m_avoidance_cache[key] = get_collision(radius, 0); + // avoid ExPolygons:~ExPolygons() in multi-threading case, as it's not thread-safe and may + // cause crash in some cases. See STUDIO-8313. + if (m_avoidance_cache.find(key) == m_avoidance_cache.end()) + m_avoidance_cache[key] = get_collision(radius, 0); return m_avoidance_cache[key]; } From 7a26dde977d55a68c1ae63fc24889817c05e099f Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 10 Oct 2024 12:05:20 +0800 Subject: [PATCH 53/72] ENH: improve shar tail detection of tree support The expansion was too large and may miss sharp tails near the object. jira: STUDIO-8400 Change-Id: Iee5bd15cc7c23f16d30365d5f1c9fbcc0a632c19 (cherry picked from commit 05174d07063d8296241de1d35f5b4196bc33a353) --- src/libslic3r/Support/TreeSupport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 5dca8692f4..083e41c112 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -824,7 +824,7 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/) bool is_sharp_tail = false; // 1. nothing below // this is a sharp tail region if it's floating and non-ignorable - if (!overlaps(offset_ex(expoly, 0.5 * extrusion_width_scaled), lower_polys)) { + if (!overlaps(offset_ex(expoly, 0.1 * extrusion_width_scaled), lower_polys)) { is_sharp_tail = !offset_ex(expoly, -0.1 * extrusion_width_scaled).empty(); } From 07c7e2f9108cf24e57025049fec1f97868bb1f18 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 21 Oct 2024 17:13:24 +0800 Subject: [PATCH 54/72] FIX: solve tree support crash jira: STUDIO-8509 Change-Id: I8658538d7919136efbbf0d48cbf3d366e0621ded (cherry picked from commit d5b14e5c094fd68426f1be4d78734f7b86888eb2) --- src/libslic3r/Support/TreeSupport.cpp | 79 +++++++++++---------------- src/libslic3r/Support/TreeSupport.hpp | 3 +- 2 files changed, 34 insertions(+), 48 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 083e41c112..9f5457d2f8 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1947,7 +1947,7 @@ void TreeSupport::draw_circles() return; BOOST_LOG_TRIVIAL(info) << "draw_circles for object: " << m_object->model_object()->name; - tbb::parallel_for(tbb::blocked_range(0, contact_nodes.size()), + tbb::parallel_for(tbb::blocked_range(0, m_ts_data->layer_heights.size()), [&](const tbb::blocked_range& range) { for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) @@ -2597,7 +2597,8 @@ void TreeSupport::drop_nodes() // Remove all circle neighbours that are completely inside the polygon and merge them into this node. for (const Point &neighbour : neighbours) { SupportNode * neighbour_node = nodes_this_part[neighbour]; - if(neighbour_node->type==ePolygon) continue; + if (neighbour_node->valid == false) continue; + if (neighbour_node->type == ePolygon) continue; coord_t neighbour_radius = scale_(neighbour_node->radius); Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius), pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0); @@ -2871,8 +2872,6 @@ void TreeSupport::drop_nodes() for (; i_node != nullptr; i_node = i_node->parent) { size_t i_layer = i_node->obj_layer_nr; - std::vector::iterator to_erase = std::find(contact_nodes[i_layer].begin(), contact_nodes[i_layer].end(), i_node); - if (to_erase != contact_nodes[i_layer].end()) { // update the parent-child chain if (i_node->parent) { @@ -2887,18 +2886,23 @@ void TreeSupport::drop_nodes() i_node->child->parents.erase(std::find(i_node->child->parents.begin(), i_node->child->parents.end(), i_node)); append(i_node->child->parents, i_node->parents); } - contact_nodes[i_layer].erase(to_erase); - i_node->valid = false; + i_node->is_processed = true; // mark to be deleted later for (SupportNode* neighbour : i_node->merged_neighbours) { - unsupported_branch_leaves.push_front({ i_layer, neighbour }); + if (neighbour && !neighbour->is_processed) + unsupported_branch_leaves.push_front({ i_layer, neighbour }); } } } } + for (auto &layer_contact_nodes : contact_nodes) { + if (!layer_contact_nodes.empty()) + layer_contact_nodes.erase(std::remove_if(layer_contact_nodes.begin(), layer_contact_nodes.end(), [](SupportNode *node) { return node->is_processed; }), + layer_contact_nodes.end()); + } } - + BOOST_LOG_TRIVIAL(debug) << "after m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size(); } @@ -3419,32 +3423,20 @@ SupportNode* TreeSupportData::create_node(const Point position, const int distan { // this function may be called from multiple threads, need to lock m_mutex.lock(); - SupportNode* node = new SupportNode(position, distance_to_top, obj_layer_nr, support_roof_layers_below, to_buildplate, parent, print_z_, height_, dist_mm_to_top_, radius_); - contact_nodes.emplace_back(node); + std::unique_ptr node = std::make_unique(position, distance_to_top, obj_layer_nr, support_roof_layers_below, to_buildplate, parent, print_z_, height_, dist_mm_to_top_, radius_); + SupportNode* raw_ptr = node.get(); + contact_nodes.emplace_back(std::move(node)); m_mutex.unlock(); if (parent) - node->movement = position - parent->position; - return node; + raw_ptr->movement = position - parent->position; + return raw_ptr; } + void TreeSupportData::clear_nodes() { - for (auto node : contact_nodes) { - delete node; - } + tbb::spin_mutex::scoped_lock guard(m_mutex); contact_nodes.clear(); } -void TreeSupportData::remove_invalid_nodes() -{ - for (auto it = contact_nodes.begin(); it != contact_nodes.end();) { - if ((*it)->valid==false) { - delete (*it); - it = contact_nodes.erase(it); - } - else { - it++; - } - } -} coordf_t TreeSupportData::ceil_radius(coordf_t radius) const { @@ -3473,27 +3465,22 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke { const auto &radius = key.radius; const auto &layer_nr = key.layer_nr; - if (layer_nr == 0) { - // avoid ExPolygons:~ExPolygons() in multi-threading case, as it's not thread-safe and may - // cause crash in some cases. See STUDIO-8313. - if (m_avoidance_cache.find(key) == m_avoidance_cache.end()) - m_avoidance_cache[key] = get_collision(radius, 0); - return m_avoidance_cache[key]; - } + ExPolygons avoidance_areas; + if (layer_nr > 0) { + // Avoidance for a given layer depends on all layers beneath it so could have very deep recursion depths if + // called at high layer heights. We can limit the reqursion depth to N by checking if the layer N + // below the current one exists and if not, forcing the calculation of that layer. This may cause another recursion + // if the layer at 2N below the current one but we won't exceed our limit unless there are N*N uncalculated layers + // below our current one. + constexpr auto max_recursion_depth = 100; + // Check if we would exceed the recursion limit by trying to process this layer + if (layer_nr >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr - max_recursion_depth}) == m_avoidance_cache.end()) { + // Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result. + get_avoidance(radius, layer_nr - max_recursion_depth); + } - // Avoidance for a given layer depends on all layers beneath it so could have very deep recursion depths if - // called at high layer heights. We can limit the reqursion depth to N by checking if the layer N - // below the current one exists and if not, forcing the calculation of that layer. This may cause another recursion - // if the layer at 2N below the current one but we won't exceed our limit unless there are N*N uncalculated layers - // below our current one. - constexpr auto max_recursion_depth = 100; - // Check if we would exceed the recursion limit by trying to process this layer - if (layer_nr >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr - max_recursion_depth}) == m_avoidance_cache.end()) { - // Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result. - get_avoidance(radius, layer_nr - max_recursion_depth); + avoidance_areas = offset_ex(get_avoidance(radius, layer_nr - 1), scale_(-m_max_move_distances[layer_nr-1])); } - - ExPolygons avoidance_areas = offset_ex(get_avoidance(radius, layer_nr - 1), scale_(-m_max_move_distances[layer_nr-1])); const ExPolygons &collision = get_collision(radius, layer_nr); avoidance_areas.insert(avoidance_areas.end(), collision.begin(), collision.end()); avoidance_areas = std::move(union_ex(avoidance_areas)); diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index 3c630629b4..86bd3a28df 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -247,10 +247,9 @@ public: SupportNode* create_node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode* parent, coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_ = 0, coordf_t radius_ = 0); void clear_nodes(); - void remove_invalid_nodes(); std::vector layer_heights; - std::vector contact_nodes; + std::vector> contact_nodes; // ExPolygon m_machine_border; private: From 1c686b3c04bb13bf9c04efd05854e6b47733e73f Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 15 Oct 2024 14:48:31 +0800 Subject: [PATCH 55/72] ENH: improve tree supports 1. speedup organic tree support by using parallel for intersection of bed area jira: STUDIO-8451 2. add extra wall for hybrid tree support's tall branches 3. disable circle fitting for tree support. This feature produces inconsistent circles for tree supports. 4. expose the option tree_support_branch_diameter_angle. Tree supports' strength can be improved by increasing this value. Change-Id: If3688ca895df98a77f6ca538077daf8fe94e53f1 (cherry picked from commit 7697eb3dc8f87204d28e6be9adaf55dfcdadbc74) --- src/libslic3r/PrintConfig.cpp | 26 +++--- src/libslic3r/PrintConfig.hpp | 2 +- src/libslic3r/PrintObject.cpp | 89 +-------------------- src/libslic3r/Support/SupportCommon.cpp | 14 +++- src/libslic3r/Support/TreeSupport.cpp | 15 +++- src/libslic3r/Support/TreeSupport3D.cpp | 5 +- src/libslic3r/Support/TreeSupportCommon.hpp | 2 +- src/slic3r/GUI/GUI_Factories.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 2 +- 9 files changed, 49 insertions(+), 108 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 6d95d4e943..bbaa7e6c97 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4809,7 +4809,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloatOrPercent(50., true)); def = this->add("tree_support_branch_angle", coFloat); - def->label = L("Tree support branch angle"); + def->label = L("Branch angle"); def->category = L("Support"); def->tooltip = L("This setting determines the maximum overhang angle that t he branches of tree support allowed to make." "If the angle is increased, the branches can be printed more horizontally, allowing them to reach farther."); @@ -4843,7 +4843,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloat(25)); def = this->add("tree_support_branch_distance", coFloat); - def->label = L("Tree support branch distance"); + def->label = L("Branch distance"); def->category = L("Support"); def->tooltip = L("This setting determines the distance between neighboring tree support nodes."); def->sidetext = L("mm"); @@ -4907,7 +4907,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloat(0.8)); def = this->add("tree_support_branch_diameter", coFloat); - def->label = L("Tree support branch diameter"); + def->label = L("Branch diameter"); def->category = L("Support"); def->tooltip = L("This setting determines the initial diameter of support nodes."); def->sidetext = L("mm"); @@ -4916,16 +4916,6 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(5.)); - def = this->add("tree_support_branch_diameter_organic", coFloat); - def->label = L("Tree support branch diameter"); - def->category = L("Support"); - def->tooltip = L("This setting determines the initial diameter of support nodes."); - def->sidetext = L("mm"); - def->min = 1.0; - def->max = 10; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(2.)); - def = this->add("tree_support_branch_diameter_angle", coFloat); // TRN PrintSettings: #lmFIXME def->label = L("Branch Diameter Angle"); @@ -4940,6 +4930,16 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(5)); + def = this->add("tree_support_branch_diameter_organic", coFloat); + def->label = L("Tree support branch diameter"); + def->category = L("Support"); + def->tooltip = L("This setting determines the initial diameter of support nodes."); + def->sidetext = L("mm"); + def->min = 1.0; + def->max = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(2.)); + def = this->add("tree_support_wall_count", coInt); def->label = L("Support wall loops"); def->category = L("Support"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index d7d4dd5087..06778e1ad1 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -842,8 +842,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, tree_support_branch_distance)) ((ConfigOptionFloat, tree_support_tip_diameter)) ((ConfigOptionFloat, tree_support_branch_diameter)) - ((ConfigOptionFloat, tree_support_branch_diameter_angle)) ((ConfigOptionFloat, tree_support_branch_angle)) + ((ConfigOptionFloat, tree_support_branch_diameter_angle)) ((ConfigOptionFloat, tree_support_angle_slow)) ((ConfigOptionInt, tree_support_wall_count)) ((ConfigOptionBool, tree_support_adaptive_layer_height)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a2deed0924..9f1fbdc1e2 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -733,7 +733,8 @@ void PrintObject::simplify_extrusion_path() } if (this->set_started(posSimplifySupportPath)) { - //BBS: share same progress + //BBS: disable circle simplification for support as it causes separation of support walls + #if 0 m_print->set_status(75, L("Optimizing toolpath")); BOOST_LOG_TRIVIAL(debug) << "Simplify extrusion path of support in parallel - start"; tbb::parallel_for( @@ -747,6 +748,7 @@ void PrintObject::simplify_extrusion_path() ); m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Simplify extrusion path of support in parallel - end"; + #endif this->set_done(posSimplifySupportPath); } } @@ -3680,91 +3682,8 @@ template void PrintObject::remove_bridges_from_contacts( SupportNecessaryType PrintObject::is_support_necessary() { - static const double super_overhang_area_threshold = SQ(scale_(5.0)); const double cantilevel_dist_thresh = scale_(6); -#if 0 - double threshold_rad = (m_config.support_threshold_angle.value < EPSILON ? 30 : m_config.support_threshold_angle.value + 1) * M_PI / 180.; - int enforce_support_layers = m_config.enforce_support_layers; - // not fixing in extrusion width % PR b/c never called - const coordf_t extrusion_width = m_config.line_width.value; - const coordf_t extrusion_width_scaled = scale_(extrusion_width); - float max_bridge_length = scale_(m_config.max_bridge_length.value); - const bool bridge_no_support = max_bridge_length > 0;// config.bridge_no_support.value; - for (size_t layer_nr = enforce_support_layers + 1; layer_nr < this->layer_count(); layer_nr++) { - Layer* layer = m_layers[layer_nr]; - Layer* lower_layer = layer->lower_layer; - - coordf_t support_offset_scaled = extrusion_width_scaled * 0.9; - ExPolygons lower_layer_offseted = offset_ex(lower_layer->lslices, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); - - // 1. check sharp tail - for (const LayerRegion* layerm : layer->regions()) { - for (const ExPolygon& expoly : layerm->raw_slices) { - // detect sharp tail - if (intersection_ex({ expoly }, lower_layer_offseted).empty()) - return SharpTail; - } - } - - // 2. check overhang area - ExPolygons super_overhang_expolys = std::move(diff_ex(layer->lslices, lower_layer_offseted)); - super_overhang_expolys.erase(std::remove_if( - super_overhang_expolys.begin(), - super_overhang_expolys.end(), - [extrusion_width_scaled](ExPolygon& area) { - return offset_ex(area, -0.1 * extrusion_width_scaled).empty(); - }), - super_overhang_expolys.end()); - - // remove bridge - if (bridge_no_support) - remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &super_overhang_expolys, max_bridge_length); - - Polygons super_overhang_polys = to_polygons(super_overhang_expolys); - - - super_overhang_polys.erase(std::remove_if( - super_overhang_polys.begin(), - super_overhang_polys.end(), - [extrusion_width_scaled](Polygon& area) { - return offset_ex(area, -0.1 * extrusion_width_scaled).empty(); - }), - super_overhang_polys.end()); - - double super_overhang_area = 0.0; - for (Polygon& poly : super_overhang_polys) { - bool is_ccw = poly.is_counter_clockwise(); - double area_ = poly.area(); - if (is_ccw) { - if (area_ > super_overhang_area_threshold) - return LargeOverhang; - super_overhang_area += area_; - } - else { - super_overhang_area -= area_; - } - } - - //if (super_overhang_area > super_overhang_area_threshold) - // return LargeOverhang; - - // 3. check overhang distance - const double distance_threshold_scaled = extrusion_width_scaled * 2; - ExPolygons lower_layer_offseted_2 = offset_ex(lower_layer->lslices, distance_threshold_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); - ExPolygons exceed_overhang = std::move(diff_ex(super_overhang_polys, lower_layer_offseted_2)); - exceed_overhang.erase(std::remove_if( - exceed_overhang.begin(), - exceed_overhang.end(), - [extrusion_width_scaled](ExPolygon& area) { - // tolerance for 1 extrusion width offset - return offset_ex(area, -0.5 * extrusion_width_scaled).empty(); - }), - exceed_overhang.end()); - if (!exceed_overhang.empty()) - return LargeOverhang; - } -#else TreeSupport tree_support(*this, m_slicing_params); tree_support.support_type = SupportType::stTreeAuto; // need to set support type to fully utilize the power of feature detection tree_support.detect_overhangs(true); @@ -3773,7 +3692,7 @@ SupportNecessaryType PrintObject::is_support_necessary() return SharpTail; else if (tree_support.has_cantilever && tree_support.max_cantilever_dist > cantilevel_dist_thresh) return Cantilever; -#endif + return NoNeedSupp; } diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index 7651b1c71b..f4611a175f 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -1417,6 +1417,10 @@ SupportGeneratorLayersPtr generate_support_layers( append(layers_sorted, intermediate_layers); append(layers_sorted, interface_layers); append(layers_sorted, base_interface_layers); + // remove dupliated layers + std::sort(layers_sorted.begin(), layers_sorted.end()); + layers_sorted.erase(std::unique(layers_sorted.begin(), layers_sorted.end()), layers_sorted.end()); + // Sort the layers lexicographically by a raising print_z and a decreasing height. std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); int layer_id = 0; @@ -1648,7 +1652,7 @@ void generate_support_toolpaths( { SupportLayer &support_layer = *support_layers[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; - const float support_interface_angle = support_params.support_style == smsGrid ? + const float support_interface_angle = (support_params.support_style == smsGrid || config.support_interface_pattern == smipRectilinear) ? support_params.interface_angle : support_params.raft_interface_angle(support_layer.interface_id()); // Find polygons with the same print_z. @@ -1785,7 +1789,7 @@ void generate_support_toolpaths( // Base support or flange. if (! base_layer.empty() && ! base_layer.polygons_to_extrude().empty()) { - Fill *filler = filler_support.get(); + Fill *filler = filler_support.get(); filler->angle = angles[support_layer_id % angles.size()]; // We don't use $base_flow->spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. @@ -1811,7 +1815,11 @@ void generate_support_toolpaths( sheath = true; no_sort = true; } else if (support_params.support_style == SupportMaterialStyle::smsTreeOrganic) { - tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow, support_params); + // if the tree supports are too tall, use double wall to make it stronger + SupportParameters support_params2 = support_params; + if (support_layer.print_z > 100.0) + support_params2.tree_branch_diameter_double_wall_area_scaled = 0.1; + tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow, support_params2); done = true; } if (! done) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 9f5457d2f8..efbb3b8191 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1540,7 +1540,10 @@ void TreeSupport::generate_toolpaths() erSupportMaterial, filler_support.get(), support_density); } else { - tree_supports_generate_paths(ts_layer->support_fills.entities, loops, flow, m_support_params); + SupportParameters support_params = m_support_params; + if (area_group.need_extra_wall && object_config.tree_support_wall_count.value == 0) + support_params.tree_branch_diameter_double_wall_area_scaled = 0.1; + tree_supports_generate_paths(ts_layer->support_fills.entities, loops, flow, support_params); } } } @@ -2917,6 +2920,10 @@ void TreeSupport::smooth_nodes() } float max_move = scale_(m_object_config->support_line_width / 2); + // if the branch is very tall, the tip also needs extra wall + float thresh_tall_branch = 100; + float thresh_dist_to_top = 30; + for (int layer_nr = 0; layer_nr< contact_nodes.size(); layer_nr++) { std::vector &curr_layer_nodes = contact_nodes[layer_nr]; if (curr_layer_nodes.empty()) continue; @@ -2926,17 +2933,20 @@ void TreeSupport::smooth_nodes() std::vector radii; std::vector branch; SupportNode * p_node = node; + float total_height = 0; // add a fixed head if it's not a polygon node, see STUDIO-4403 // Polygon node can't be added because the move distance might be huge, making the nodes in between jump and dangling if (node->child && node->child->type!=ePolygon) { pts.push_back(p_node->child->position); radii.push_back(p_node->child->radius); branch.push_back(p_node->child); + total_height += p_node->child->height; } do { pts.push_back(p_node->position); radii.push_back(p_node->radius); branch.push_back(p_node); + total_height += p_node->height; p_node = p_node->parent; } while (p_node && !p_node->is_processed); if (pts.size() < 3) continue; @@ -2955,7 +2965,8 @@ void TreeSupport::smooth_nodes() branch[i]->radius = radii1[i]; branch[i]->movement = (pts[i + 1] - pts[i - 1]) / 2; branch[i]->is_processed = true; - if (branch[i]->parents.size()>1 || (branch[i]->movement.x() > max_move || branch[i]->movement.y() > max_move)) + if (branch[i]->parents.size() > 1 || (branch[i]->movement.x() > max_move || branch[i]->movement.y() > max_move) || + (total_height > thresh_tall_branch && branch[i]->dist_mm_to_top < thresh_dist_to_top)) branch[i]->need_extra_wall = true; BOOST_LOG_TRIVIAL(info) << "smooth_nodes: layer_nr=" << layer_nr << ", i=" << i << ", pt=" << pt << ", movement=" << branch[i]->movement << ", radius=" << branch[i]->radius; } diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index dad831076c..8b8acd7b57 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -36,6 +36,7 @@ #include #include +#include #if defined(TREE_SUPPORT_SHOW_ERRORS) && defined(_WIN32) #define TREE_SUPPORT_SHOW_ERRORS_WIN32 @@ -3551,7 +3552,9 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons print.set_status(69, _L("Generating support")); generate_support_toolpaths(print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(), raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); - + + auto t_end = std::chrono::high_resolution_clock::now(); + BOOST_LOG_TRIVIAL(info) << "Total time of organic tree support: " << 0.001 * std::chrono::duration_cast(t_end - t_start).count() << " ms"; #if 0 //#ifdef SLIC3R_DEBUG { diff --git a/src/libslic3r/Support/TreeSupportCommon.hpp b/src/libslic3r/Support/TreeSupportCommon.hpp index 3122a43d2d..31e410838d 100644 --- a/src/libslic3r/Support/TreeSupportCommon.hpp +++ b/src/libslic3r/Support/TreeSupportCommon.hpp @@ -87,7 +87,7 @@ struct TreeSupportMeshGroupSettings { this->support_tree_angle = std::clamp(config.tree_support_branch_angle_organic * M_PI / 180., 0., 0.5 * M_PI - EPSILON); this->support_tree_angle_slow = std::clamp(config.tree_support_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON); this->support_tree_branch_diameter = scaled(config.tree_support_branch_diameter_organic.value); - this->support_tree_branch_diameter_angle = std::clamp(config.tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON); + this->support_tree_branch_diameter_angle = std::clamp(config.tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON); this->support_tree_top_rate = config.tree_support_top_rate.value; // percent // this->support_tree_tip_diameter = this->support_line_width; this->support_tree_tip_diameter = std::clamp(scaled(config.tree_support_tip_diameter.value), (coord_t)0, this->support_tree_branch_diameter); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 096a83a512..f3861c69aa 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -90,7 +90,7 @@ std::map> SettingsFactory::OBJECT_C { L("Support"), {{"brim_type", "",1},{"brim_width", "",2},{"brim_object_gap", "",3}, {"enable_support", "",4},{"support_type", "",5},{"support_threshold_angle", "",6}, {"support_threshold_overlap", "",6}, {"support_on_build_plate_only", "",7}, {"support_filament", "",8},{"support_interface_filament", "",9},{"support_expansion", "",24},{"support_style", "",25}, - {"tree_support_brim_width", "",26}, {"tree_support_branch_angle", "",10},{"tree_support_branch_angle_organic","",10}, {"tree_support_wall_count", "",11},//tree support + {"tree_support_brim_width", "",26}, {"tree_support_branch_angle", "",10},{"tree_support_branch_angle_organic","",10}, {"tree_support_wall_count", "",11},{"tree_support_branch_diameter_angle", "",11},//tree support {"support_top_z_distance", "",13},{"support_bottom_z_distance", "",12},{"support_base_pattern", "",14},{"support_base_pattern_spacing", "",15}, {"support_interface_top_layers", "",16},{"support_interface_bottom_layers", "",17},{"support_interface_spacing", "",18},{"support_bottom_interface_spacing", "",19}, {"support_object_xy_distance", "",20}, {"bridge_no_support", "",21},{"max_bridge_length", "",22},{"support_critical_regions_only", "",23},{"support_remove_small_overhang","",27}, diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9e0b5eb8a5..9abcbfb2de 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2228,7 +2228,7 @@ void TabPrint::build() optgroup->append_single_option_line("support_object_xy_distance", "support"); optgroup->append_single_option_line("support_object_first_layer_gap", "support"); optgroup->append_single_option_line("bridge_no_support", "support#base-pattern"); - optgroup->append_single_option_line("max_bridge_length", "support#base-pattern"); + optgroup->append_single_option_line("max_bridge_length", "support#tree-support-only-options"); optgroup->append_single_option_line("independent_support_layer_height", "support"); optgroup = page->new_optgroup(L("Tree supports"), L"param_support_tree"); From c1df01fd8ec93ea54bee54b1eb4d148e4a6445de Mon Sep 17 00:00:00 2001 From: Arthur Date: Sun, 27 Oct 2024 13:26:03 +0800 Subject: [PATCH 56/72] FIX: hybrid tree support may crash due to empty extrusion entities jira: none Change-Id: I521e27e7a4d12189efc77768d10d264d0d6db111 (cherry picked from commit 64ab78298bc0c9a32ea5bcec5beddfc103074f53) --- src/libslic3r/Support/TreeSupport.cpp | 3 ++- src/libslic3r/Support/TreeSupport.hpp | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index efbb3b8191..dbf462c113 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -611,7 +611,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p if(support_pattern == smpLightning) m_support_params.base_fill_pattern = ipLightning; - + diameter_angle_scale_factor = std::clamp(m_object_config->tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON); is_slim = is_tree_slim(support_type, m_support_params.support_style); is_strong = is_tree(support_type) && m_support_params.support_style == smsTreeStrong; base_radius = std::max(MIN_BRANCH_RADIUS, m_object_config->tree_support_branch_diameter.value / 2); @@ -1293,6 +1293,7 @@ static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const ExPolygon dst = std::move(loops_entities); } } + dst.erase(std::remove_if(dst.begin(), dst.end(), [](ExtrusionEntity *entity) { return static_cast(entity)->empty(); }), dst.end()); if (infill_first) { // sort regions to reduce travel Points ordering_points; diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index 86bd3a28df..8afc241926 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -440,8 +440,7 @@ private: const coordf_t MIN_BRANCH_RADIUS = 0.4; const coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0; const coordf_t MIN_BRANCH_RADIUS_FIRST_LAYER = 2.0; - const double tree_support_branch_diameter_angle = 5.0; - const double diameter_angle_scale_factor = tan(tree_support_branch_diameter_angle*M_PI/180.0); + double diameter_angle_scale_factor = tan(5.0*M_PI/180.0); // minimum roof area (1 mm^2), area smaller than this value will not have interface const double minimum_roof_area{SQ(scaled(1.))}; float top_z_distance = 0.0; From d54d81e89d1fc0fa9000d9c6c44c6e0f3dd3c2d6 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 29 Oct 2024 19:23:49 +0800 Subject: [PATCH 57/72] FIX: 0 top z distance of hybrid tree support not working 1. fix the issue that setting top z distance=0 not working 2. remove too small extrusions of tree support jira: STUDIO-8578 Change-Id: I8c3face9d6a756698a6fab876fdb1acc0686647c (cherry picked from commit 4d219266a1f520445bec6ac5a0274dcfec4050e8) --- src/libslic3r/Slicing.cpp | 2 +- src/libslic3r/Support/TreeSupport.cpp | 30 ++++++++++++++++----------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 6655e3911d..f3499b7d36 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -114,7 +114,7 @@ SlicingParameters SlicingParameters::create_from_config( params.min_layer_height = std::min(params.min_layer_height, params.layer_height); params.max_layer_height = std::max(params.max_layer_height, params.layer_height); - if (! soluble_interface || is_tree_slim(object_config.support_type.value, object_config.support_style.value)) { + if (! soluble_interface) { params.gap_raft_object = object_config.raft_contact_distance.value; //BBS params.gap_object_support = object_config.support_bottom_z_distance.value; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index dbf462c113..737334367c 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -2148,17 +2148,25 @@ void TreeSupport::draw_circles() } } auto &area_groups = ts_layer->area_groups; - for (auto& area : ts_layer->base_areas) { - area_groups.emplace_back(&area, SupportLayer::BaseType, max_layers_above_base); - area_groups.back().need_infill = overlaps({ area }, area_poly); - area_groups.back().need_extra_wall = need_extra_wall; + for (auto& expoly : ts_layer->base_areas) { + if (area(expoly) < SQ(scale_(1))) continue; + area_groups.emplace_back(&expoly, SupportLayer::BaseType, max_layers_above_base); + area_groups.back().need_infill = overlaps({ expoly }, area_poly); + area_groups.back().need_extra_wall = need_extra_wall && !area_groups.back().need_infill; } - for (auto& area : ts_layer->roof_areas) { - area_groups.emplace_back(&area, SupportLayer::RoofType, max_layers_above_roof); + for (auto& expoly : ts_layer->roof_areas) { + if (area(expoly) < SQ(scale_(1))) continue; + area_groups.emplace_back(&expoly, SupportLayer::RoofType, max_layers_above_roof); area_groups.back().interface_id = interface_id; } - for (auto &area : ts_layer->floor_areas) area_groups.emplace_back(&area, SupportLayer::FloorType, 10000); - for (auto &area : ts_layer->roof_1st_layer) area_groups.emplace_back(&area, SupportLayer::Roof1stLayer, max_layers_above_roof1); + for (auto &expoly : ts_layer->floor_areas) { + if (area(expoly) < SQ(scale_(1))) continue; + area_groups.emplace_back(&expoly, SupportLayer::FloorType, 10000); + } + for (auto &expoly : ts_layer->roof_1st_layer) { + if (area(expoly) < SQ(scale_(1))) continue; + area_groups.emplace_back(&expoly, SupportLayer::Roof1stLayer, max_layers_above_roof1); + } for (auto &area_group : area_groups) { auto& expoly = area_group.area; @@ -3075,7 +3083,7 @@ std::vector TreeSupport::plan_layer_heights() for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { if (contact_nodes[layer_nr].empty()) continue; SupportNode *node1 = contact_nodes[layer_nr].front(); - auto it = z_heights.lower_bound(node1->print_z - EPSILON); + auto it = z_heights.lower_bound(node1->print_z + EPSILON); if (it == z_heights.end()) it = std::prev(it); int layer_nr2 = std::distance(z_heights.begin(), it); contact_nodes2[layer_nr2].insert(contact_nodes2[layer_nr2].end(), contact_nodes[layer_nr].begin(), contact_nodes[layer_nr].end()); @@ -3091,6 +3099,7 @@ std::vector TreeSupport::plan_layer_heights() << ", object_layer_zs[" << layer_heights[layer_nr].obj_layer_nr << "]=" << m_object->get_layer(layer_heights[layer_nr].obj_layer_nr)->print_z; coordf_t new_height = layer_heights[layer_nr].height; if (std::abs(node1->height - new_height) < EPSILON) continue; + if (top_z_distance < EPSILON && node1->height < EPSILON) continue; // top_z_distance==0, this is soluable interface coordf_t accum_height = 0; int num_layers = 0; for (int i=layer_nr;i>=0;i--){ @@ -3164,9 +3173,6 @@ void TreeSupport::generate_contact_points() // } const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1; //Support must always be 1 layer below overhang. int gap_layers = z_distance_top == 0 ? 0 : 1; - // virtual layer with 0 height will be deleted - if (z_distance_top == 0) - z_distance_top = 0.001; size_t support_roof_layers = config.support_interface_top_layers.value; if (support_roof_layers > 0) From 9f4d9fb4636220c3cb24665d2bc1438dd17f9a63 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 6 Nov 2024 14:10:08 +0800 Subject: [PATCH 58/72] FIX: normal support missing base interface layers jira: STUDIO-8642 Change-Id: Ib104fdb195f8766d452138eb85d8b86cbac96981 (cherry picked from commit 4655ec449b9bc86747d4054bbfb20c60f784c8c9) --- src/libslic3r/Support/SupportParameters.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index 82ced55094..304f3a23af 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -33,8 +33,13 @@ struct SupportParameters { this->num_top_base_interface_layers = size_t(std::min(int(num_top_interface_layers) / 2, 2)); this->num_bottom_base_interface_layers = size_t(std::min(int(num_bottom_interface_layers) / 2, 2)); } else { - this->num_top_base_interface_layers = 0; - this->num_bottom_base_interface_layers = 0; + // BBS: if support interface and support base do not use the same filament, add a base layer to improve their adhesion + // Note: support materials (such as Supp.W) can't be used as support base now, so support interface and base are still using different filaments even if + // support_filament==0 + bool differnt_support_interface_filament = object_config.support_interface_filament != 0 && + object_config.support_interface_filament != object_config.support_filament; + this->num_top_base_interface_layers = differnt_support_interface_filament ? 1 : 0; + this->num_bottom_base_interface_layers = differnt_support_interface_filament ? 1 : 0; } } this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height)); From 9e1b856e99d09e21cf31a1c9a9b65d67e89703eb Mon Sep 17 00:00:00 2001 From: "jiaxi.chen" Date: Mon, 18 Nov 2024 10:29:22 +0800 Subject: [PATCH 59/72] FIX: protect when support_style doesnt match type jira: STUDIO-8800 Change-Id: I7d42f95ba43b4b3a47a83ae59afaaf9bc94baa5d (cherry picked from commit d4bc450af6d2381bd5eb891c1395ebd50ca1c322) --- src/libslic3r/Support/SupportParameters.hpp | 6 ++++++ src/libslic3r/Support/TreeSupport.cpp | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index 304f3a23af..da21895b34 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -163,6 +163,12 @@ struct SupportParameters { std::numeric_limits::max(); support_style = object_config.support_style; + if (support_style != smsDefault) { + if ((support_style == smsSnug || support_style == smsGrid) && is_tree(object_config.support_type)) support_style = smsDefault; + if ((support_style == smsTreeSlim || support_style == smsTreeStrong || support_style == smsTreeHybrid || support_style == smsTreeOrganic) && + !is_tree(object_config.support_type)) + support_style = smsDefault; + } if (support_style == smsDefault) { if (is_tree(object_config.support_type)) { // Orca: use organic as default diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 737334367c..7bb9364133 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1650,6 +1650,8 @@ void TreeSupport::move_bounds_to_contact_nodes(std::vectorsupport_type.value)) return; + if (m_support_params.support_style == smsTreeOrganic) { generate_tree_support_3D(*m_object, this, this->throw_on_cancel); return; From 35157c9421ed88d2f4bada08b3362341a57995f8 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 2 Dec 2024 15:56:10 +0800 Subject: [PATCH 60/72] FIX: apply xy_expansion after support blocker jira: STUDIO-9040 Change-Id: I30e09a67047f651a547082bff737524aba940f19 (cherry picked from commit 5e476a1385286246a312b76bdaa836f18715b523) --- src/libslic3r/Support/SupportMaterial.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 47c476564b..61be8d3912 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -1457,7 +1457,6 @@ static inline ExPolygons detect_overhangs( // This is done to increase size of the supporting columns below, as they are calculated by // propagating these contact surfaces downwards. diff_polygons = diff(intersection(expand(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), lower_layer_polygons); - if (xy_expansion != 0) { diff_polygons = expand(diff_polygons, xy_expansion, SUPPORT_SURFACES_OFFSET_PARAMETERS); } } //FIXME add user defined filtering here based on minimal area or minimum radius or whatever. @@ -1509,6 +1508,8 @@ static inline ExPolygons detect_overhangs( if (diff_polygons.empty() || offset(diff_polygons, -0.1 * fw).empty()) continue; + if (xy_expansion != 0) { diff_polygons = expand(diff_polygons, xy_expansion, SUPPORT_SURFACES_OFFSET_PARAMETERS); } + polygons_append(overhang_polygons, diff_polygons); } // for each layer.region } From d7cf4b227445a4bf68c967d51c75e3200ffb8f2b Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 3 Dec 2024 12:00:26 +0800 Subject: [PATCH 61/72] FIX: inconsistent infill in hybrid tree support jira: STUDIO-9058 Change-Id: I62f6225d33708e8a161600930c6e0dff229f5995 (cherry picked from commit f774fda6e67e25bedc89e8ef8b2dfb77436f95f2) --- src/libslic3r/Support/TreeSupport.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 7bb9364133..e847ed3054 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -2032,8 +2032,7 @@ void TreeSupport::draw_circles() area = offset_ex({ node.overhang }, scale_(m_ts_data->m_xy_distance)); } area = diff_clipped(area, get_collision(node.is_sharp_tail && node.distance_to_top <= 0)); - if (node.type == ePolygon) - area_poly = area; + if (node.type == ePolygon) append(area_poly, area); } else { Polygon circle(branch_circle); From ee1bd704efc39cf6d75e8cbe9ab944c41f4ebbf6 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 4 Dec 2024 17:14:07 +0800 Subject: [PATCH 62/72] FIX: merge support interfaces into a continuous large one jira: STUDIO-8611 github: #5132 Change-Id: I12ee4a9f88a78304a98f354bfaa92e2a05f19ec2 (cherry picked from commit b7c8c7e8feb199b01f52e1fbfbba4469657db3b7) (cherry picked from commit c331b61cabd521cacd42d010c2936f8280a2b9f3) --- src/libslic3r/Support/TreeSupport.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index e847ed3054..8df6055e4a 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1,6 +1,7 @@ +#include #include - +#include "format.hpp" #include "ClipperUtils.hpp" #include "Fill/FillBase.hpp" #include "I18N.hpp" @@ -223,6 +224,7 @@ static void draw_contours_and_nodes_to_svg // draw layer nodes svg.draw(layer_pts, "green", coord_t(scale_(0.1))); + for (SupportNode *node : layer_nodes) { svg.draw({node->overhang}, "green", 0.5); } // lower layer points layer_pts.clear(); @@ -231,13 +233,13 @@ static void draw_contours_and_nodes_to_svg } svg.draw(layer_pts, "black", coord_t(scale_(0.1))); - // higher layer points - layer_pts.clear(); - for (SupportNode* node : layer_nodes) { - if(node->parent) - layer_pts.push_back(node->parent->position); - } - svg.draw(layer_pts, "blue", coord_t(scale_(0.1))); + //// higher layer points + //layer_pts.clear(); + //for (SupportNode* node : layer_nodes) { + // if(node->parent) + // layer_pts.push_back(node->parent->position); + //} + //svg.draw(layer_pts, "blue", coord_t(scale_(0.1))); } static void draw_layer_mst @@ -2063,6 +2065,12 @@ void TreeSupport::draw_circles() if(!tmp.empty()) circle = tmp[0]; } + area = avoid_object_remove_extra_small_parts(ExPolygon(circle), get_collision(node.is_sharp_tail && node.distance_to_top <= 0)); + // area = diff_clipped({ ExPolygon(circle) }, get_collision(node.is_sharp_tail && node.distance_to_top <= 0)); + + if (!area.empty()) has_circle_node = true; + if (node.need_extra_wall) need_extra_wall = true; + // merge overhang to get a smoother interface surface // Do not merge when buildplate_only is on, because some underneath nodes may have been deleted. if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only && !node.is_sharp_tail) { @@ -3328,6 +3336,7 @@ void TreeSupport::generate_contact_points() if (!curr_nodes.empty()) nonempty_layers++; for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); } #ifdef SUPPORT_TREE_DEBUG_TO_SVG + if (!curr_nodes.empty()) draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", bottom_z), layer->loverhangs,layer->lslices_extrudable, m_ts_data->m_layer_outlines_below[layer_nr], contact_nodes[layer_nr], contact_nodes[layer_nr - 1], { "overhang","lslices","outlines_below"}); #endif From ed9ba7dcb77b7380477d68e1e9ee6b19746aed42 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 6 Dec 2024 20:36:04 +0800 Subject: [PATCH 63/72] FIX: sharp tail miss detection jira: STUDIO-7973 Change-Id: I76b0dc56fe6cf44bacad4c35874695efee29c2f9 (cherry picked from commit 3fe1728f40e2a09340b061681895e4691bd1a8f7) (cherry picked from commit 7665aeb69ce53d7f9e1c50857b49985cd6912534) --- src/libslic3r/Support/TreeSupport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 8df6055e4a..3dfe29d7db 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -2853,7 +2853,7 @@ void TreeSupport::drop_nodes() } } auto next_collision = get_collision(0, obj_layer_nr_next); - const bool to_buildplate = !is_inside_ex(next_collision, next_layer_vertex); + const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[obj_layer_nr_next], next_layer_vertex); SupportNode * next_node = m_ts_data->create_node(next_layer_vertex, node.distance_to_top + 1, obj_layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, print_z_next, height_next); // don't increase radius if next node will collide partially with the object (STUDIO-7883) From d41c0a812b7810bd2cef8cf891f0733d25c9b1f6 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 11 Dec 2024 16:39:36 +0800 Subject: [PATCH 64/72] FIX: top z distance disappears in some cases jira: STUDIO-8883 github: #5334 Change-Id: Ie70f6dada19cc17cd74fa6aa116d1ca4fb4a07d7 (cherry picked from commit 849d41576c64527c2b9c0bfffdec37f29ed9b68b) --- src/libslic3r/Support/TreeSupport.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 3dfe29d7db..933f5737bc 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -3092,9 +3092,11 @@ std::vector TreeSupport::plan_layer_heights() for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { if (contact_nodes[layer_nr].empty()) continue; SupportNode *node1 = contact_nodes[layer_nr].front(); - auto it = z_heights.lower_bound(node1->print_z + EPSILON); - if (it == z_heights.end()) it = std::prev(it); - int layer_nr2 = std::distance(z_heights.begin(), it); + auto it = std::min_element(layer_heights.begin(), layer_heights.end(), [node1](const LayerHeightData &l1, const LayerHeightData &l2) { + return std::abs(l1.print_z - node1->print_z) < std::abs(l2.print_z - node1->print_z); + }); + if (it == layer_heights.end()) it = std::prev(it); + int layer_nr2 = std::distance(layer_heights.begin(), it); contact_nodes2[layer_nr2].insert(contact_nodes2[layer_nr2].end(), contact_nodes[layer_nr].begin(), contact_nodes[layer_nr].end()); } contact_nodes = contact_nodes2; @@ -3118,7 +3120,7 @@ std::vector TreeSupport::plan_layer_heights() if (accum_height > node1->height - EPSILON) break; } } - BOOST_LOG_TRIVIAL(debug) << format("plan_layer_heights adjust node's height %d %.2f: (%.3f,%d)->(%.3f,%.3f,%d)", layer_nr, node1->print_z, + BOOST_LOG_TRIVIAL(debug) << format("plan_layer_heights adjust node's height print_z[%d]=%.2f: (%.3f,%d)->(%.3f,%.3f,%d)", layer_nr, node1->print_z, node1->height, node1->distance_to_top, new_height, accum_height, -num_layers); for (SupportNode *node : contact_nodes[layer_nr]) { node->height = new_height; From 393e2f4027953ab0978cb9d5dd6aba125a09d9a2 Mon Sep 17 00:00:00 2001 From: "jiaxi.chen" Date: Wed, 18 Dec 2024 16:51:21 +0800 Subject: [PATCH 65/72] FIX: fix the bug of missing layers in SlimTree and HybridTree jira: STUDIO-8756 Change-Id: I12c09ec2e3c1a2ee138472ff7c63675d0ee26ba0 (cherry picked from commit 36d80e7b24a4bdcce2d3957e0fd3ea61c8dc6bdc) (cherry picked from commit 0d1bdab97d7e400fc4dd91dde0b3e8d00d2ee7f7) --- src/libslic3r/Support/TreeSupport.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 933f5737bc..a1faf1d06d 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -2158,22 +2158,22 @@ void TreeSupport::draw_circles() } auto &area_groups = ts_layer->area_groups; for (auto& expoly : ts_layer->base_areas) { - if (area(expoly) < SQ(scale_(1))) continue; + //if (area(expoly) < SQ(scale_(1))) continue; area_groups.emplace_back(&expoly, SupportLayer::BaseType, max_layers_above_base); area_groups.back().need_infill = overlaps({ expoly }, area_poly); area_groups.back().need_extra_wall = need_extra_wall && !area_groups.back().need_infill; } for (auto& expoly : ts_layer->roof_areas) { - if (area(expoly) < SQ(scale_(1))) continue; + //if (area(expoly) < SQ(scale_(1))) continue; area_groups.emplace_back(&expoly, SupportLayer::RoofType, max_layers_above_roof); area_groups.back().interface_id = interface_id; } for (auto &expoly : ts_layer->floor_areas) { - if (area(expoly) < SQ(scale_(1))) continue; + //if (area(expoly) < SQ(scale_(1))) continue; area_groups.emplace_back(&expoly, SupportLayer::FloorType, 10000); } for (auto &expoly : ts_layer->roof_1st_layer) { - if (area(expoly) < SQ(scale_(1))) continue; + //if (area(expoly) < SQ(scale_(1))) continue; area_groups.emplace_back(&expoly, SupportLayer::Roof1stLayer, max_layers_above_roof1); } From dc3938e0ea8632df25816679f4e0f72e99ceccd6 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 20 Dec 2024 11:59:17 +0800 Subject: [PATCH 66/72] ENH: reduce organic tree support log level jira: none Change-Id: I17b876d88974e632c592bd36f3fea700a51f86be (cherry picked from commit 97de4e87cfbff84c04c247bdf525ac83119e177f) --- src/libslic3r/Support/TreeModelVolumes.cpp | 2 +- src/libslic3r/Support/TreeSupport3D.cpp | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Support/TreeModelVolumes.cpp b/src/libslic3r/Support/TreeModelVolumes.cpp index 9c49c813a9..9cf8b3df37 100644 --- a/src/libslic3r/Support/TreeModelVolumes.cpp +++ b/src/libslic3r/Support/TreeModelVolumes.cpp @@ -33,7 +33,7 @@ using namespace std::literals; // or warning // had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL() -#define error_level_not_in_cache error +#define error_level_not_in_cache debug //FIXME Machine border is currently ignored. static Polygons calculateMachineBorderCollision(Polygon machine_border) diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 8b8acd7b57..8b364b85f0 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -813,7 +813,7 @@ static std::optional> polyline_sample_next_point_at_dis if (distance == 0) 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!"; + BOOST_LOG_TRIVIAL(warning) << "Offset increase got invalid parameter!"; tree_supports_show_error("Negative offset distance... How did you manage this ?"sv, true); return do_final_difference ? diff(ret, collision_trimmed()) : union_(ret); } @@ -1711,8 +1711,8 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance) )); check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; - if (area(check_layer_data) < _tiny_area_threshold) { - BOOST_LOG_TRIVIAL(error) << "Lost area by doing catch up from " << ceil_radius_before << " to radius " << + if (area(check_layer_data) < tiny_area_threshold) { + BOOST_LOG_TRIVIAL(debug) << "Lost area by doing catch up from " << ceil_radius_before << " to radius " << volumes.ceilRadius(support_element_collision_radius(config, current_elem), settings.use_min_distance); tree_supports_show_error("Area lost catching up radius. May not cause visible malformation."sv, true); } @@ -2013,7 +2013,7 @@ static void increase_areas_one_layer( << "Trying to keep area by moving faster than intended: Success"; break; } else if (!settings.no_error) - BOOST_LOG_TRIVIAL(error) << "Trying to keep area by moving faster than intended: FAILURE! WRONG BRANCHES LIKLY!"; + BOOST_LOG_TRIVIAL(warning) << "Trying to keep area by moving faster than intended: FAILURE! WRONG BRANCHES LIKLY!"; } if (add) { @@ -2495,8 +2495,8 @@ static void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupp // This area was removed completely due to collisions. return true; if (elem.areas.to_bp_areas.empty() && elem.areas.to_model_areas.empty()) { - if (area(elem.areas.influence_areas) < _tiny_area_threshold) { - BOOST_LOG_TRIVIAL(error) << "Insert Error of Influence area bypass on layer " << layer_idx - 1; + if (area(elem.areas.influence_areas) < tiny_area_threshold) { + BOOST_LOG_TRIVIAL(warning) << "Insert Error of Influence area bypass on layer " << layer_idx - 1; tree_supports_show_error("Insert error of area after bypassing merge.\n"sv, true); } // Move the area to output. @@ -2528,8 +2528,8 @@ static void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupp for (SupportElementMerging &elem : influence_areas) if (! elem.areas.influence_areas.empty()) { Polygons new_area = safe_union(elem.areas.influence_areas); - if (area(new_area) < _tiny_area_threshold) { - BOOST_LOG_TRIVIAL(error) << "Insert Error of Influence area on layer " << layer_idx - 1 << ". Origin of " << elem.parents.size() << " areas. Was to bp " << elem.state.to_buildplate; + if (area(new_area) < tiny_area_threshold) { + BOOST_LOG_TRIVIAL(warning) << "Insert Error of Influence area on layer " << layer_idx - 1 << ". Origin of " << elem.parents.size() << " areas. Was to bp " << elem.state.to_buildplate; tree_supports_show_error("Insert error of area after merge.\n"sv, true); } this_layer.emplace_back(elem.state, std::move(elem.parents), std::move(new_area)); @@ -2558,7 +2558,7 @@ static void set_points_on_areas(const SupportElement &elem, SupportElements *lay // Based on the branch center point of the current layer, the point on the next (further up) layer is calculated. if (! elem.state.result_on_layer_is_set()) { - BOOST_LOG_TRIVIAL(error) << "Uninitialized support element"; + BOOST_LOG_TRIVIAL(warning) << "Uninitialized support element"; tree_supports_show_error("Uninitialized support element. A branch may be missing.\n"sv, true); return; } @@ -2726,7 +2726,8 @@ static void create_nodes_from_area( if (! elem.state.result_on_layer_is_set()) { if (elem.state.to_buildplate || (elem.state.distance_to_top < config.min_dtt_to_model && ! elem.state.supports_roof)) { if (elem.state.to_buildplate) { - BOOST_LOG_TRIVIAL(error) << "Uninitialized Influence area targeting " << elem.state.target_position.x() << "," << elem.state.target_position.y() << ") " + BOOST_LOG_TRIVIAL(warning) << "Uninitialized Influence area targeting " << elem.state.target_position.x() << "," << elem.state.target_position.y() + << ") " "at target_height: " << elem.state.target_height << " layer: " << layer_idx; tree_supports_show_error("Uninitialized support element! A branch could be missing or exist partially."sv, true); } From 98135299e86f80583383f1aa9794e5c373fa1315 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Mon, 27 Jan 2025 23:01:45 +0800 Subject: [PATCH 67/72] Fix compile error --- src/libslic3r/Support/TreeSupport3D.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 8b364b85f0..e1971e9b4c 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -1711,7 +1711,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance) )); check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data; - if (area(check_layer_data) < tiny_area_threshold) { + if (area(check_layer_data) < _tiny_area_threshold) { BOOST_LOG_TRIVIAL(debug) << "Lost area by doing catch up from " << ceil_radius_before << " to radius " << volumes.ceilRadius(support_element_collision_radius(config, current_elem), settings.use_min_distance); tree_supports_show_error("Area lost catching up radius. May not cause visible malformation."sv, true); @@ -2495,7 +2495,7 @@ static void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupp // This area was removed completely due to collisions. return true; if (elem.areas.to_bp_areas.empty() && elem.areas.to_model_areas.empty()) { - if (area(elem.areas.influence_areas) < tiny_area_threshold) { + if (area(elem.areas.influence_areas) < _tiny_area_threshold) { BOOST_LOG_TRIVIAL(warning) << "Insert Error of Influence area bypass on layer " << layer_idx - 1; tree_supports_show_error("Insert error of area after bypassing merge.\n"sv, true); } @@ -2528,7 +2528,7 @@ static void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupp for (SupportElementMerging &elem : influence_areas) if (! elem.areas.influence_areas.empty()) { Polygons new_area = safe_union(elem.areas.influence_areas); - if (area(new_area) < tiny_area_threshold) { + if (area(new_area) < _tiny_area_threshold) { BOOST_LOG_TRIVIAL(warning) << "Insert Error of Influence area on layer " << layer_idx - 1 << ". Origin of " << elem.parents.size() << " areas. Was to bp " << elem.state.to_buildplate; tree_supports_show_error("Insert error of area after merge.\n"sv, true); } From a3ac1fc4f4105cf230b44e852f3804bbe6b318e1 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Tue, 28 Jan 2025 00:50:08 +0800 Subject: [PATCH 68/72] Fix error "Coordinate outside allowed range" --- src/libslic3r/Support/TreeSupport.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index a1faf1d06d..3de1c5127c 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -65,6 +65,9 @@ inline Point turn90_ccw(const Point pt) inline Point normal(Point pt, double scale) { double length = scale_(sqrt(vsize2_with_unscale(pt))); + if (length < SCALED_EPSILON) { + return pt; + } return pt * (scale / length); } From 279b3ded14889e35e76739d26fd915c1a90f341f Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Mon, 28 Oct 2024 16:24:45 +0100 Subject: [PATCH 69/72] libslic3r: Fix BOOST_LOG_TRIVIAL declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /run/build/BambuStudio/src/libslic3r/Support/SupportParameters.hpp: In constructor ‘Slic3r::SupportParameters::SupportParameters(const Slic3r::PrintObject&)’: /run/build/BambuStudio/src/libslic3r/Support/SupportParameters.hpp:172:39: error: ‘warning’ was not declared in this scope 172 | BOOST_LOG_TRIVIAL(warning) << "tree support default to organic support"; | ^~~~~~~ /run/build/BambuStudio/src/libslic3r/Support/SupportParameters.hpp:172:21: error: ‘BOOST_LOG_TRIVIAL’ was not declared in this scope 172 | BOOST_LOG_TRIVIAL(warning) << "tree support default to organic support"; | ^~~~~~~~~~~~~~~~~ /run/build/BambuStudio/src/libslic3r/Support/SupportParameters.hpp:175:39: error: ‘warning’ was not declared in this scope 175 | BOOST_LOG_TRIVIAL(warning) << "tree support default to hybrid tree due to adaptive layer height"; | ^~~~~~~ /run/build/BambuStudio/src/libslic3r/Support/SupportParameters.hpp:175:21: error: ‘BOOST_LOG_TRIVIAL’ was not declared in this scope 175 | BOOST_LOG_TRIVIAL(warning) << "tree support default to hybrid tree due to adaptive layer height"; | ^~~~~~~~~~~~~~~~~ (cherry picked from commit a63070bd4cbe012315b9532f5c199f6d2664333a) --- src/libslic3r/Support/SupportParameters.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index da21895b34..bcc85e2202 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_SupportParameters_hpp_ #define slic3r_SupportParameters_hpp_ +#include #include "../libslic3r.h" #include "../Flow.hpp" From 0187e1baf349bf60c3857d95a95bdc663943fde0 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 27 Mar 2024 11:24:58 +0100 Subject: [PATCH 70/72] libslic3r: Fix missing BOOST_LOG_TRIVIAL declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FAILED: src/libslic3r/CMakeFiles/libslic3r.dir/Support/TreeSupport.cpp.o /usr/bin/c++ -DBOOST_ATOMIC_NO_LIB -DBOOST_CHRONO_NO_LIB -DBOOST_DATE_TIME_NO_LIB -DBOOST_FILESYSTEM_NO_LIB -DBOOST_IOSTREAMS_NO_LIB -DBOOST_LOCALE_NO_LIB -DBOOST_LOG_NO_LIB -DBOOST_REGEX_NO_LIB -DBOOST_SYSTEM_NO_LIB -DBOOST_THREAD_NO_LIB -DHAVE_FREETYPE -DHAVE_OPENGL_EXT -DHAVE_XLIB -DLIBNEST2D_GEOMETRIES_libslic3r -DLIBNEST2D_OPTIMIZER_nlopt -DLIBNEST2D_STATIC -DLIBNEST2D_THREADING_tbb -DOCC_CONVERT_SIGNALS -DOPENVDB_OPENEXR_STATICLIB -DOPENVDB_STATICLIB -DSLIC3R_GUI -DTBB_USE_CAPTURED_EXCEPTION=0 -DUNICODE -DUSE_TBB -DWXINTL_NO_GETTEXT_MACRO -D_UNICODE -DwxNO_UNSAFE_WXSTRING_CONV -DwxUSE_UNICODE -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -I/run/build/BambuStudio/src -I/run/build/BambuStudio/build/src/platform -I/run/build/BambuStudio/src/libslic3r -I/run/build/BambuStudio/build/src/libslic3r -I/run/build/BambuStudio/deps/build/destdir/usr/local/include/opencascade -I/run/build/BambuStudio/src/libnest2d/include -I/run/build/BambuStudio/src/miniz -I/run/build/BambuStudio/src/glu-libtess/include -I/run/build/BambuStudio/src/clipper2/Clipper2Lib/include -isystem /run/build/BambuStudio/src/eigen -isystem /run/build/BambuStudio/src/libigl -isystem /run/build/BambuStudio/deps/build/destdir/usr/local/include -isystem /run/build/BambuStudio/deps/build/destdir/usr/local/include/OpenEXR -std=gnu++20 -fext-numeric-literals -Wall -Wno-reorder -O3 -DNDEBUG -std=gnu++17 -fPIC -fsigned-char -Werror=return-type -Wno-ignored-attributes -Wno-unknown-pragmas -DOPENVDB_ABI_VERSION_NUMBER=8 -MD -MT src/libslic3r/CMakeFiles/libslic3r.dir/Support/TreeSupport.cpp.o -MF src/libslic3r/CMakeFiles/libslic3r.dir/Support/TreeSupport.cpp.o.d -o src/libslic3r/CMakeFiles/libslic3r.dir/Support/TreeSupport.cpp.o -c /run/build/BambuStudio/src/libslic3r/Support/TreeSupport.cpp /run/build/BambuStudio/src/libslic3r/Support/TreeSupport.cpp:832:39: error: ‘info’ was not declared in this scope; did you mean ‘tbb::v1::info’? 832 | BOOST_LOG_TRIVIAL(info) << "detect_overhangs takes more than 30 secs, skip cantilever and sharp tails detection: layer_nr=" << layer_nr << " duration=" << duration; | ^~~~ | tbb::v1::info In file included from /run/build/BambuStudio/deps/build/destdir/usr/local/include/oneapi/tbb/task_arena.h:31, from /run/build/BambuStudio/deps/build/destdir/usr/local/include/oneapi/tbb/partitioner.h:48, from /run/build/BambuStudio/deps/build/destdir/usr/local/include/oneapi/tbb/parallel_for.h:27, from /run/build/BambuStudio/deps/build/destdir/usr/local/include/tbb/parallel_for.h:17, from /run/build/BambuStudio/src/libslic3r/Support/TreeSupport.cpp:25: /run/build/BambuStudio/deps/build/destdir/usr/local/include/oneapi/tbb/info.h:125:11: note: ‘tbb::v1::info’ declared here 125 | namespace info { | ^~~~ /run/build/BambuStudio/src/libslic3r/Support/TreeSupport.cpp:832:21: error: ‘BOOST_LOG_TRIVIAL’ was not declared in this scope 832 | BOOST_LOG_TRIVIAL(info) << "detect_overhangs takes more than 30 secs, skip cantilever and sharp tails detection: layer_nr=" << layer_nr << " duration=" << duration; | ^~~~~~~~~~~~~~~~~ /run/build/BambuStudio/src/libslic3r/Support/TreeSupport.cpp:884:43: error: ‘debug’ was not declared in this scope 884 | BOOST_LOG_TRIVIAL(debug) << "found a cantilever cluster. layer_nr=" << layer_nr << dist_max; | ^~~~~ (cherry picked from commit c680128141416f1297a25824deb7443fc441892b) --- src/libslic3r/Support/TreeSupport.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 3de1c5127c..139ae42597 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -24,6 +24,8 @@ #include #include +#include + #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 #endif From 32547c8c3bb3052bea25b2f429016438178d89c6 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 9 Feb 2025 19:01:51 +0800 Subject: [PATCH 71/72] Update header file to match the impl --- src/libslic3r/Support/TreeSupport.hpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index 8afc241926..e0446ad5f1 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -197,12 +197,9 @@ public: * * \param xy_distance The required clearance between the model and the * tree branches. - * \param max_move The maximum allowable movement between nodes on - * adjacent layers * \param radius_sample_resolution Sample size used to round requested node radii. - * \param collision_resolution */ - TreeSupportData(const PrintObject& object, coordf_t radius_sample_resolution, coordf_t collision_resolution); + TreeSupportData(const PrintObject& object, coordf_t xy_distance, coordf_t radius_sample_resolution); ~TreeSupportData() { clear_nodes(); } From dc4007104558c900b69312373dcc8dd1718aad72 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Tue, 25 Feb 2025 21:08:55 +0800 Subject: [PATCH 72/72] Revert unnecessary text changes --- src/libslic3r/PrintConfig.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index ae77db55d6..7ba48799a9 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4934,7 +4934,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloatOrPercent(50., true)); def = this->add("tree_support_branch_angle", coFloat); - def->label = L("Branch angle"); + def->label = L("Tree support branch angle"); def->category = L("Support"); def->tooltip = L("This setting determines the maximum overhang angle that t he branches of tree support allowed to make." "If the angle is increased, the branches can be printed more horizontally, allowing them to reach farther."); @@ -4968,7 +4968,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloat(25)); def = this->add("tree_support_branch_distance", coFloat); - def->label = L("Branch distance"); + def->label = L("Tree support branch distance"); def->category = L("Support"); def->tooltip = L("This setting determines the distance between neighboring tree support nodes."); def->sidetext = L("mm"); @@ -5032,7 +5032,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloat(0.8)); def = this->add("tree_support_branch_diameter", coFloat); - def->label = L("Branch diameter"); + def->label = L("Tree support branch diameter"); def->category = L("Support"); def->tooltip = L("This setting determines the initial diameter of support nodes."); def->sidetext = L("mm");