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();