From 825c954b44086deaad674c82b506161b3404dc6a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 23 Feb 2023 13:52:39 +0100 Subject: [PATCH] WIP: Organic Supports & Raft Object is lifted, trees are routed to the ground. --- src/libslic3r/PrintObject.cpp | 4 +- src/libslic3r/SupportMaterial.cpp | 16 +-- src/libslic3r/SupportMaterial.hpp | 2 +- src/libslic3r/TreeModelVolumes.cpp | 29 ++-- src/libslic3r/TreeModelVolumes.hpp | 5 +- src/libslic3r/TreeSupport.cpp | 209 +++++++++++++++++++++++------ src/libslic3r/TreeSupport.hpp | 64 +-------- 7 files changed, 203 insertions(+), 126 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c4a5946e85..14c76fefe5 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2226,9 +2226,11 @@ void PrintObject::combine_infill() void PrintObject::_generate_support_material() { - if (m_config.support_material_style == smsTree || m_config.support_material_style == smsOrganic) { + if (this->has_support() && (m_config.support_material_style == smsTree || m_config.support_material_style == smsOrganic)) { fff_tree_support_generate(*this, std::function([this](){ this->throw_if_canceled(); })); } else { + // If support style is set to Organic however only raft will be built but no support, + // build snug raft instead. PrintObjectSupportMaterial support_material(this, m_slicing_params); support_material.generate(*this); } diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 836dc8ac97..d254457099 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -799,10 +799,6 @@ public: ) { switch (m_style) { - case smsTree: - case smsOrganic: - assert(false); - [[fallthrough]]; case smsGrid: { #ifdef SUPPORT_USE_AGG_RASTERIZER @@ -893,6 +889,10 @@ public: polygons_rotate(out, m_support_angle); return out; } + case smsTree: + case smsOrganic: +// assert(false); + [[fallthrough]]; case smsSnug: // Merge the support polygons by applying morphological closing and inwards smoothing. auto closing_distance = scaled(m_support_material_closing_radius); @@ -1763,7 +1763,7 @@ static inline void fill_contact_layer( #endif // SLIC3R_DEBUG )); // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra. - bool reduce_interfaces = object_config.support_material_style.value != smsSnug && layer_id > 0 && !slicing_params.soluble_interface; + bool reduce_interfaces = object_config.support_material_style.value == smsGrid && layer_id > 0 && !slicing_params.soluble_interface; if (reduce_interfaces) { // Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions. Polygons dense_interface_polygons = diff(overhang_polygons, lower_layer_polygons_for_dense_interface()); @@ -3045,7 +3045,7 @@ std::pair PrintObjectSuppo m_object_config->support_material_interface_extruder.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_material_interface_extruder.value - 1) && // Base extruder: Either "print with active extruder" not soluble. (m_object_config->support_material_extruder.value == 0 || ! m_print_config->filament_soluble.get_at(m_object_config->support_material_extruder.value - 1)); - bool snug_supports = m_object_config->support_material_style.value == smsSnug; + bool snug_supports = m_object_config->support_material_style.value != smsGrid; int num_interface_layers_top = m_object_config->support_material_interface_layers; int num_interface_layers_bottom = m_object_config->support_material_bottom_interface_layers; if (num_interface_layers_bottom < 0) @@ -4227,7 +4227,7 @@ void generate_support_toolpaths( } // Insert the raft base layers. - size_t n_raft_layers = size_t(std::max(0, int(slicing_params.raft_layers()) - 1)); + auto n_raft_layers = std::min(support_layers.size(), std::max(0, int(slicing_params.raft_layers()) - 1)); tbb::parallel_for(tbb::blocked_range(0, n_raft_layers), [&support_layers, &raft_layers, &config, &support_params, &slicing_params, &bbox_object, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor] @@ -4356,7 +4356,7 @@ void generate_support_toolpaths( { SupportLayer &support_layer = *support_layers[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; - float interface_angle_delta = config.support_material_style.value == smsSnug || config.support_material_style.value == smsTree || config.support_material_style.value == smsOrganic ? + float interface_angle_delta = config.support_material_style.value != smsGrid ? (support_layer.interface_id() & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.) : 0; diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp index fc5588d82c..ecfb5a7617 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -13,7 +13,7 @@ class PrintObjectConfig; // Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information // about the support layer type than the final support layers stored in a PrintObject. -enum SupporLayerType { +enum class SupporLayerType { Unknown = 0, // Ratft base layer, to be printed with the support material. RaftBase, diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/TreeModelVolumes.cpp index 1cb8578780..53ac6534dd 100644 --- a/src/libslic3r/TreeModelVolumes.cpp +++ b/src/libslic3r/TreeModelVolumes.cpp @@ -158,14 +158,24 @@ TreeModelVolumes::TreeModelVolumes( { m_anti_overhang = print_object.slice_support_blockers(); TreeSupportMeshGroupSettings mesh_settings(print_object); - m_layer_outlines.emplace_back(mesh_settings, std::vector{}); + const TreeSupportSettings config{ mesh_settings, print_object.slicing_parameters() }; + m_current_min_xy_dist = config.xy_min_distance; + m_current_min_xy_dist_delta = config.xy_distance - m_current_min_xy_dist; + assert(m_current_min_xy_dist_delta >= 0); + m_increase_until_radius = config.increase_radius_until_radius; + m_radius_0 = config.getRadius(0); + m_raft_layers = config.raft_layers; m_current_outline_idx = 0; + + m_layer_outlines.emplace_back(mesh_settings, std::vector{}); std::vector &outlines = m_layer_outlines.front().second; - outlines.assign(print_object.layer_count(), Polygons{}); - tbb::parallel_for(tbb::blocked_range(0, print_object.layer_count(), std::min(1, std::max(16, print_object.layer_count() / (8 * tbb::this_task_arena::max_concurrency())))), + size_t num_raft_layers = m_raft_layers.size(); + size_t num_layers = print_object.layer_count() + num_raft_layers; + outlines.assign(num_layers, Polygons{}); + tbb::parallel_for(tbb::blocked_range(num_raft_layers, num_layers, std::min(1, std::max(16, num_layers / (8 * tbb::this_task_arena::max_concurrency())))), [&](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) - outlines[layer_idx] = to_polygons(expolygons_simplify(print_object.get_layer(layer_idx)->lslices, mesh_settings.resolution)); + outlines[layer_idx] = to_polygons(expolygons_simplify(print_object.get_layer(layer_idx - num_raft_layers)->lslices, mesh_settings.resolution)); }); } #endif @@ -177,13 +187,6 @@ TreeModelVolumes::TreeModelVolumes( m_min_resolution = std::min(m_min_resolution, data_pair.first.resolution); } - const TreeSupportSettings config{ m_layer_outlines[m_current_outline_idx].first }; - m_current_min_xy_dist = config.xy_min_distance; - m_current_min_xy_dist_delta = config.xy_distance - m_current_min_xy_dist; - assert(m_current_min_xy_dist_delta >= 0); - m_increase_until_radius = config.increase_radius_until_radius; - m_radius_0 = config.getRadius(0); - #if 0 for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) { SliceMeshStorage mesh = storage.meshes[mesh_idx]; @@ -214,7 +217,7 @@ TreeModelVolumes::TreeModelVolumes( #endif } -void TreeModelVolumes::precalculate(const coord_t max_layer, std::function throw_on_cancel) +void TreeModelVolumes::precalculate(const PrintObject& print_object, const coord_t max_layer, std::function throw_on_cancel) { auto t_start = std::chrono::high_resolution_clock::now(); m_precalculated = true; @@ -222,7 +225,7 @@ void TreeModelVolumes::precalculate(const coord_t max_layer, std::function throw_on_cancel); + void precalculate(const PrintObject& print_object, const coord_t max_layer, std::function throw_on_cancel); /*! * \brief Provides the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. @@ -618,6 +618,9 @@ private: */ coord_t m_radius_0; + // Z heights of the raft layers (additional layers below the object, last raft layer aligned with the bottom of the first object layer). + std::vector m_raft_layers; + /*! * \brief Caches for the collision, avoidance and areas on the model where support can be placed safely * at given radius and layer indices. diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 71dda3a6a5..c0938cfe32 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -59,6 +59,85 @@ namespace Slic3r namespace FFFTreeSupport { +TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings, const SlicingParameters &slicing_params) + : angle(mesh_group_settings.support_tree_angle), + angle_slow(mesh_group_settings.support_tree_angle_slow), + support_line_width(mesh_group_settings.support_line_width), + layer_height(mesh_group_settings.layer_height), + branch_radius(mesh_group_settings.support_tree_branch_diameter / 2), + min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance + maximum_move_distance((angle < M_PI / 2.) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits::max()), + maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits::max()), + support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0), + tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large + diameter_angle_scale_factor(sin(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height / branch_radius), + max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2), + min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)), + increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2), + increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / (branch_radius * diameter_angle_scale_factor)), + support_rests_on_model(! mesh_group_settings.support_material_buildplate_only), + xy_distance(mesh_group_settings.support_xy_distance), + xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)), + bp_radius(mesh_group_settings.support_tree_bp_diameter / 2), + diameter_scale_bp_radius(std::min(sin(0.7) * layer_height / branch_radius, 1.0 / (branch_radius / (support_line_width / 2.0)))), // Either 40? or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller. + z_distance_top_layers(round_up_divide(mesh_group_settings.support_top_distance, layer_height)), + z_distance_bottom_layers(round_up_divide(mesh_group_settings.support_bottom_distance, layer_height)), + performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, layer_height)), +// support_infill_angles(mesh_group_settings.support_infill_angles), + support_roof_angles(mesh_group_settings.support_roof_angles), + roof_pattern(mesh_group_settings.support_roof_pattern), + support_pattern(mesh_group_settings.support_pattern), + support_roof_line_width(mesh_group_settings.support_roof_line_width), + support_line_spacing(mesh_group_settings.support_line_spacing), + support_bottom_offset(mesh_group_settings.support_bottom_offset), + support_wall_count(mesh_group_settings.support_wall_count), + resolution(mesh_group_settings.resolution), + support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference. + settings(mesh_group_settings), + min_feature_size(mesh_group_settings.min_feature_size) +{ + layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius); + + if (TreeSupportSettings::soluble) { + // safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely + // When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size + // This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance. + xy_min_distance = std::max(xy_min_distance, scaled(0.1)); + xy_distance = std::max(xy_distance, xy_min_distance); + } + + +// const std::unordered_map interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SupportAreaOverwritesInterface }, { "interface_area_overwrite_support_area", InterfacePreference::InterfaceAreaOverwritesSupport }, { "support_lines_overwrite_interface_area", InterfacePreference::SupportLinesOverwriteInterface }, { "interface_lines_overwrite_support_area", InterfacePreference::InterfaceLinesOverwriteSupport }, { "nothing", InterfacePreference::Nothing } }; +// interface_preference = interface_map.at(mesh_group_settings.get("support_interface_priority")); +//FIXME this was the default +// interface_preference = InterfacePreference::SupportLinesOverwriteInterface; + //interface_preference = InterfacePreference::SupportAreaOverwritesInterface; + interface_preference = InterfacePreference::InterfaceAreaOverwritesSupport; + + if (slicing_params.raft_layers() > 0) { + // Fill in raft_layers with the heights of the layers below the first object layer. + double z = slicing_params.first_print_layer_height; + this->raft_layers.emplace_back(z); + for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) { + z += slicing_params.base_raft_layer_height; + this->raft_layers.emplace_back(z); + } + for (size_t i = 0; i + 1 < slicing_params.interface_raft_layers; ++ i) { + z += slicing_params.interface_raft_layer_height; + this->raft_layers.emplace_back(z); + } + assert(is_approx(z, slicing_params.raft_interface_top_z)); + double dist_to_go = slicing_params.object_print_z_min - z; + assert(dist_to_go > slicing_params.min_layer_height - EPSILON); + auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height)); + double step = dist_to_go / nsteps; + for (size_t i = 0; i < nsteps; ++ i) { + z += step; + this->raft_layers.emplace_back(z); + } + } +} + enum class LineStatus { INVALID, @@ -158,7 +237,7 @@ static std::vector>> group_me assert(object_config.support_material_style == smsTree || object_config.support_material_style == smsOrganic); bool found_existing_group = false; - TreeSupportSettings next_settings{ TreeSupportMeshGroupSettings{ print_object } }; + TreeSupportSettings next_settings{ TreeSupportMeshGroupSettings{ print_object }, print_object.slicing_parameters() }; //FIXME for now only a single object per group is enabled. #if 0 for (size_t idx = 0; idx < grouped_meshes.size(); ++ idx) @@ -222,9 +301,12 @@ void tree_supports_show_error(std::string_view message, bool critical) #endif // TREE_SUPPORT_SHOW_ERRORS_WIN32 } -[[nodiscard]] static const std::vector generate_overhangs(const PrintObject &print_object, std::function throw_on_cancel) +[[nodiscard]] static const std::vector generate_overhangs(const TreeSupportSettings &settings, const PrintObject &print_object, std::function throw_on_cancel) { - std::vector out(print_object.layer_count(), Polygons{}); + const size_t num_raft_layers = settings.raft_layers.size(); + const size_t num_object_layers = print_object.layer_count(); + const size_t num_layers = num_object_layers + num_raft_layers; + std::vector out(num_layers, Polygons{}); const PrintConfig &print_config = print_object.print()->config(); const PrintObjectConfig &config = print_object.config(); @@ -241,10 +323,10 @@ void tree_supports_show_error(std::string_view message, bool critical) //FIXME this is a fudge constant! auto enforcer_overhang_offset = scaled(config.support_tree_tip_diameter.value); - size_t num_overhang_layers = support_auto ? out.size() : std::max(size_t(support_enforce_layers), enforcers_layers.size()); + size_t num_overhang_layers = support_auto ? 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, &throw_on_cancel, &out] + support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, &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); @@ -314,11 +396,34 @@ void tree_supports_show_error(std::string_view message, bool critical) //check_self_intersections(overhangs, "generate_overhangs - enforcers"); } } - out[layer_id] = std::move(overhangs); + out[layer_id + num_raft_layers] = std::move(overhangs); throw_on_cancel(); } }); + if (num_raft_layers > 0) { + const Layer &first_layer = *print_object.get_layer(0); + // Final overhangs. + Polygons overhangs = + // Don't apply blockes on raft layer. + //(! blockers_layers.empty() && ! blockers_layers[layer_id].empty() ? + // diff(first_layer.lslices, blockers_layers[layer_id], ApplySafetyOffset::Yes) : + to_polygons(first_layer.lslices); +#if 0 + if (! enforcers_layers.empty() && ! enforcers_layers[layer_id].empty()) { + if (Polygons enforced_overhangs = intersection(first_layer.lslices, enforcers_layers[layer_id] /*, ApplySafetyOffset::Yes */); + ! enforced_overhangs.empty()) { + //FIXME this is a hack to make enforcers work on steep overhangs. + //FIXME enforcer_overhang_offset is a fudge constant! + enforced_overhangs = offset(union_ex(enforced_overhangs), enforcer_overhang_offset); + overhangs = overhangs.empty() ? std::move(enforced_overhangs) : union_(overhangs, enforced_overhangs); + } + } +#endif + out[num_raft_layers] = std::move(overhangs); + throw_on_cancel(); + } + return out; } @@ -333,16 +438,18 @@ void tree_supports_show_error(std::string_view message, bool critical) // calculate top most layer that is relevant for support LayerIndex max_layer = 0; for (size_t object_id : object_ids) { - const PrintObject &print_object = *print.get_object(object_id); - int max_support_layer_id = 0; - for (int layer_id = 1; layer_id < int(print_object.layer_count()); ++ layer_id) + const PrintObject &print_object = *print.get_object(object_id); + const int num_raft_layers = int(config.raft_layers.size()); + const int num_layers = int(print_object.layer_count()) + num_raft_layers; + int max_support_layer_id = 0; + for (int layer_id = std::max(num_raft_layers, 1); layer_id < num_layers; ++ layer_id) if (! overhangs[layer_id].empty()) max_support_layer_id = layer_id; max_layer = std::max(max_support_layer_id - int(config.z_distance_top_layers), 0); } if (max_layer > 0) // The actual precalculation happens in TreeModelVolumes. - volumes.precalculate(max_layer, throw_on_cancel); + volumes.precalculate(*print.get_object(object_ids.front()), max_layer, throw_on_cancel); return max_layer; } @@ -815,29 +922,38 @@ static std::optional> polyline_sample_next_point_at_dis return union_(ret); } -static double layer_z(const SlicingParameters &slicing_params, const size_t layer_idx) +static double layer_z(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx) { - return slicing_params.object_print_z_min + slicing_params.first_object_layer_height + layer_idx * slicing_params.layer_height; + return layer_idx >= config.raft_layers.size() ? + slicing_params.object_print_z_min + slicing_params.first_object_layer_height + (layer_idx - config.raft_layers.size()) * slicing_params.layer_height : + config.raft_layers[layer_idx]; } -static LayerIndex layer_idx_ceil(const SlicingParameters &slicing_params, const double z) +// Lowest collision layer +static LayerIndex layer_idx_ceil(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z) { - return LayerIndex(ceil((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height)); + return + LayerIndex(config.raft_layers.size()) + + std::max(0, ceil((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height)); } -static LayerIndex layer_idx_floor(const SlicingParameters &slicing_params, const double z) +// Highest collision layer +static LayerIndex layer_idx_floor(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z) { - return LayerIndex(floor((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height)); + return + LayerIndex(config.raft_layers.size()) + + std::max(0, floor((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height)); } static inline SupportGeneratorLayer& layer_initialize( SupportGeneratorLayer &layer_new, const SupporLayerType layer_type, const SlicingParameters &slicing_params, + const TreeSupportSettings &config, const size_t layer_idx) { layer_new.layer_type = layer_type; - layer_new.print_z = layer_z(slicing_params, layer_idx); - layer_new.height = layer_idx == 0 ? slicing_params.first_object_layer_height : slicing_params.layer_height; - layer_new.bottom_z = layer_idx == 0 ? slicing_params.object_print_z_min : layer_new.print_z - layer_new.height; + layer_new.print_z = layer_z(slicing_params, config, layer_idx); + layer_new.bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0; + layer_new.height = layer_new.print_z - layer_new.bottom_z; return layer_new; } @@ -846,11 +962,12 @@ inline SupportGeneratorLayer& layer_allocate( std::deque &layer_storage, SupporLayerType layer_type, const SlicingParameters &slicing_params, + const TreeSupportSettings &config, size_t layer_idx) { //FIXME take raft into account. layer_storage.push_back(SupportGeneratorLayer()); - return layer_initialize(layer_storage.back(), layer_type, slicing_params, layer_idx); + return layer_initialize(layer_storage.back(), layer_type, slicing_params, config, layer_idx); } inline SupportGeneratorLayer& layer_allocate( @@ -858,11 +975,12 @@ inline SupportGeneratorLayer& layer_allocate( tbb::spin_mutex& layer_storage_mutex, SupporLayerType layer_type, const SlicingParameters &slicing_params, + const TreeSupportSettings &config, size_t layer_idx) { tbb::spin_mutex::scoped_lock lock(layer_storage_mutex); layer_storage.push_back(SupportGeneratorLayer()); - return layer_initialize(layer_storage.back(), layer_type, slicing_params, layer_idx); + return layer_initialize(layer_storage.back(), layer_type, slicing_params, config, layer_idx); } using SupportElements = std::deque; @@ -890,7 +1008,7 @@ static void generate_initial_areas( static constexpr const auto base_radius = scaled(0.01); const Polygon base_circle = make_circle(base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION); TreeSupportMeshGroupSettings mesh_group_settings(print_object); - TreeSupportSettings mesh_config{ mesh_group_settings }; + TreeSupportSettings mesh_config{ mesh_group_settings, print_object.slicing_parameters() }; SupportParameters support_params(print_object); support_params.with_sheath = true; support_params.support_density = 0; @@ -936,11 +1054,13 @@ static void generate_initial_areas( 0; //FIXME - size_t num_support_layers = print_object.layer_count(); - std::vector> already_inserted(num_support_layers - z_distance_delta); + const size_t num_raft_layers = config.raft_layers.size(); + const size_t num_support_layers = size_t(std::max(0, int(print_object.layer_count()) + int(num_raft_layers) - int(z_distance_delta))); + const size_t first_support_layer = std::max(int(num_raft_layers) - int(z_distance_delta), 1); + std::vector> already_inserted(num_support_layers); std::mutex mutex_layer_storage, mutex_movebounds; - tbb::parallel_for(tbb::blocked_range(1, num_support_layers - z_distance_delta), + tbb::parallel_for(tbb::blocked_range(first_support_layer, num_support_layers), [&print_object, &volumes, &config, &overhangs, &mesh_config, &mesh_group_settings, &support_params, z_distance_delta, min_xy_dist, force_tip_to_roof, roof_enabled, support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length, max_overhang_insert_lag, &base_circle, &mutex_layer_storage, &mutex_movebounds, &top_contacts, &layer_storage, &already_inserted, @@ -1060,7 +1180,7 @@ static void generate_initial_areas( std::lock_guard lock(mutex_layer_storage); SupportGeneratorLayer *&l = top_contacts[insert_layer_idx - dtt_roof_tip]; if (l == nullptr) - l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), insert_layer_idx - dtt_roof_tip); + l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, insert_layer_idx - dtt_roof_tip); append(l->polygons, std::move(added_roofs)); } } @@ -1242,7 +1362,7 @@ static void generate_initial_areas( if (! added_roofs[idx].empty()) { SupportGeneratorLayer *&l = top_contacts[layer_idx - idx]; if (l == nullptr) - l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), layer_idx - idx); + l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, layer_idx - idx); // will be unioned in finalize_interface_and_support_areas() append(l->polygons, std::move(added_roofs[idx])); } @@ -1280,7 +1400,7 @@ static void generate_initial_areas( std::lock_guard lock(mutex_layer_storage); SupportGeneratorLayer*& l = top_contacts[0]; if (l == nullptr) - l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), 0); + l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, 0); append(l->polygons, std::move(overhang_outset)); } else // normal trees have to be generated addLinesAsInfluenceAreas(overhang_lines, force_tip_to_roof ? support_roof_layers - dtt_roof : 0, layer_idx - dtt_roof, dtt_roof > 0, roof_enabled ? support_roof_layers - dtt_roof : 0); @@ -3047,7 +3167,7 @@ static void finalize_interface_and_support_areas( } if (! floor_layer.empty()) { if (support_bottom == nullptr) - support_bottom = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::BottomContact, print_object.slicing_parameters(), layer_idx); + support_bottom = &layer_allocate(layer_storage, layer_storage_mutex, 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. } @@ -3055,11 +3175,11 @@ static void finalize_interface_and_support_areas( if (! support_roof_polygons.empty()) { if (support_roof == nullptr) - support_roof = top_contacts[layer_idx] = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::TopContact, print_object.slicing_parameters(), layer_idx); + support_roof = top_contacts[layer_idx] = &layer_allocate(layer_storage, layer_storage_mutex, 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, layer_storage_mutex, SupporLayerType::Base, print_object.slicing_parameters(), layer_idx); + SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx); base_layer->polygons = union_(base_layer_polygons); } @@ -3391,8 +3511,8 @@ static void extrude_branch( const SupportElement &prev = *path[ipath - 1]; const SupportElement ¤t = *path[ipath]; assert(prev.state.layer_idx + 1 == current.state.layer_idx); - p1 = to_3d(unscaled(prev .state.result_on_layer), layer_z(slicing_params, prev .state.layer_idx)); - p2 = to_3d(unscaled(current.state.result_on_layer), layer_z(slicing_params, current.state.layer_idx)); + p1 = to_3d(unscaled(prev .state.result_on_layer), layer_z(slicing_params, config, prev .state.layer_idx)); + p2 = to_3d(unscaled(current.state.result_on_layer), layer_z(slicing_params, config, current.state.layer_idx)); v1 = (p2 - p1).normalized(); if (ipath == 1) { nprev = v1; @@ -3439,7 +3559,7 @@ static void extrude_branch( } else { const SupportElement &next = *path[ipath + 1]; assert(current.state.layer_idx + 1 == next.state.layer_idx); - p3 = to_3d(unscaled(next.state.result_on_layer), layer_z(slicing_params, next.state.layer_idx)); + p3 = to_3d(unscaled(next.state.result_on_layer), layer_z(slicing_params, config, next.state.layer_idx)); v2 = (p3 - p2).normalized(); ncurrent = (v1 + v2).normalized(); float radius = unscaled(config.getRadius(current.state)); @@ -3549,7 +3669,7 @@ static void organic_smooth_branches_avoid_collisions( element.parents.empty() || (link_down == -1 && element.state.layer_idx > 0), unscaled(config.getRadius(element.state)), // 3D position - to_3d(unscaled(element.state.result_on_layer), float(layer_z(slicing_params, element.state.layer_idx))) + to_3d(unscaled(element.state.result_on_layer), float(layer_z(slicing_params, config, element.state.layer_idx))) }); // Update min_z coordinate to min_z of the tree below. CollisionSphere &collision_sphere = collision_spheres.back(); @@ -3580,8 +3700,9 @@ static void organic_smooth_branches_avoid_collisions( //FIXME limit the collision span by the tree slope. collision_sphere.min_z = std::max(collision_sphere.min_z, collision_sphere.position.z() - collision_sphere.radius); collision_sphere.max_z = std::min(collision_sphere.max_z, collision_sphere.position.z() + collision_sphere.radius); - collision_sphere.layer_begin = std::min(collision_sphere.element.state.layer_idx, layer_idx_ceil(slicing_params, collision_sphere.min_z)); - collision_sphere.layer_end = std::max(collision_sphere.element.state.layer_idx, layer_idx_floor(slicing_params, collision_sphere.max_z)) + 1; + collision_sphere.layer_begin = std::min(collision_sphere.element.state.layer_idx, layer_idx_ceil(slicing_params, config, collision_sphere.min_z)); + assert(collision_sphere.layer_begin < layer_collision_cache.size()); + collision_sphere.layer_end = std::min(LayerIndex(layer_collision_cache.size()), std::max(collision_sphere.element.state.layer_idx, layer_idx_floor(slicing_params, config, collision_sphere.max_z)) + 1); } throw_on_cancel(); @@ -3596,7 +3717,7 @@ static void organic_smooth_branches_avoid_collisions( collision_sphere.prev_position = collision_sphere.position; std::atomic num_moved{ 0 }; tbb::parallel_for(tbb::blocked_range(0, collision_spheres.size()), - [&collision_spheres, &layer_collision_cache, &slicing_params, &move_bounds, &linear_data_layers, &num_moved, &throw_on_cancel](const tbb::blocked_range range) { + [&collision_spheres, &layer_collision_cache, &slicing_params, &config, &move_bounds, &linear_data_layers, &num_moved, &throw_on_cancel](const tbb::blocked_range range) { for (size_t collision_sphere_id = range.begin(); collision_sphere_id < range.end(); ++ collision_sphere_id) if (CollisionSphere &collision_sphere = collision_spheres[collision_sphere_id]; ! collision_sphere.locked) { // Calculate collision of multiple 2D layers against a collision sphere. @@ -3613,7 +3734,7 @@ static void organic_smooth_branches_avoid_collisions( double collision_depth = sqrt(r2) - dist; if (collision_depth > collision_sphere.last_collision_depth) { collision_sphere.last_collision_depth = collision_depth; - collision_sphere.last_collision = to_3d(hit_point_out.cast(), float(layer_z(slicing_params, layer_id))); + collision_sphere.last_collision = to_3d(hit_point_out.cast(), float(layer_z(slicing_params, config, layer_id))); } } } @@ -3693,7 +3814,7 @@ static void organic_smooth_branches_avoid_collisions( std::vector pts, prev, projections; std::vector distances; for (const std::pair& element : elements_with_link_down) { - Vec3d pt = to_3d(unscaled(element.first->state.result_on_layer), layer_z(print_object.slicing_parameters(), element.first->state.layer_idx)) * scale; + Vec3d pt = to_3d(unscaled(element.first->state.result_on_layer), layer_z(print_object.slicing_parameters(), config, element.first->state.layer_idx)) * scale; pts.push_back({ pt.x(), pt.y(), pt.z() }); } @@ -3918,9 +4039,9 @@ static void slice_branches( const SlicingParameters &slicing_params = print_object.slicing_parameters(); std::vector slice_z; for (size_t layer_idx = 0; layer_idx < move_bounds.size(); ++ layer_idx) { - double print_z = slicing_params.object_print_z_min + slicing_params.first_object_layer_height + layer_idx * slicing_params.layer_height; - double layer_height = layer_idx == 0 ? slicing_params.first_object_layer_height : slicing_params.layer_height; - slice_z.emplace_back(float(print_z - layer_height * 0.5)); + const double print_z = layer_z(print_object.slicing_parameters(), config, layer_idx); + const double bottom_z = layer_idx > 0 ? layer_z(print_object.slicing_parameters(), config, layer_idx - 1) : 0.; + slice_z.emplace_back(float(0.5 * (bottom_z + print_z))); } // Remove the trailing slices. while (! slice_z.empty()) @@ -4009,7 +4130,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume //FIXME generating overhangs just for the furst mesh of the group. assert(processing.second.size() == 1); - std::vector overhangs = generate_overhangs(*print.get_object(processing.second.front()), throw_on_cancel); + std::vector overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel); // ### Precalculate avoidances, collision etc. size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel); diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/TreeSupport.hpp index df4dc36a01..ff3c0f8f3d 100644 --- a/src/libslic3r/TreeSupport.hpp +++ b/src/libslic3r/TreeSupport.hpp @@ -40,6 +40,7 @@ namespace Slic3r class Print; class PrintObject; class SupportGeneratorLayer; +struct SlicingParameters; using SupportGeneratorLayerStorage = std::deque; using SupportGeneratorLayersPtr = std::vector; @@ -255,64 +256,7 @@ struct SupportElement struct TreeSupportSettings { TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupportGenerator class. - - explicit TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings) - : angle(mesh_group_settings.support_tree_angle), - angle_slow(mesh_group_settings.support_tree_angle_slow), - support_line_width(mesh_group_settings.support_line_width), - layer_height(mesh_group_settings.layer_height), - branch_radius(mesh_group_settings.support_tree_branch_diameter / 2), - min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance - maximum_move_distance((angle < M_PI / 2.) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits::max()), - maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits::max()), - support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0), - tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large - diameter_angle_scale_factor(sin(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height / branch_radius), - max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2), - min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)), - increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2), - increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / (branch_radius * diameter_angle_scale_factor)), - support_rests_on_model(! mesh_group_settings.support_material_buildplate_only), - xy_distance(mesh_group_settings.support_xy_distance), - xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)), - bp_radius(mesh_group_settings.support_tree_bp_diameter / 2), - diameter_scale_bp_radius(std::min(sin(0.7) * layer_height / branch_radius, 1.0 / (branch_radius / (support_line_width / 2.0)))), // Either 40? or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller. - z_distance_top_layers(round_up_divide(mesh_group_settings.support_top_distance, layer_height)), - z_distance_bottom_layers(round_up_divide(mesh_group_settings.support_bottom_distance, layer_height)), - performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, layer_height)), -// support_infill_angles(mesh_group_settings.support_infill_angles), - support_roof_angles(mesh_group_settings.support_roof_angles), - roof_pattern(mesh_group_settings.support_roof_pattern), - support_pattern(mesh_group_settings.support_pattern), - support_roof_line_width(mesh_group_settings.support_roof_line_width), - support_line_spacing(mesh_group_settings.support_line_spacing), - support_bottom_offset(mesh_group_settings.support_bottom_offset), - support_wall_count(mesh_group_settings.support_wall_count), - resolution(mesh_group_settings.resolution), - support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference. - settings(mesh_group_settings), - min_feature_size(mesh_group_settings.min_feature_size) - - - { - layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius); - - if (TreeSupportSettings::soluble) { - // safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely - // When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size - // This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance. - xy_min_distance = std::max(xy_min_distance, scaled(0.1)); - xy_distance = std::max(xy_distance, xy_min_distance); - } - - -// const std::unordered_map interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SupportAreaOverwritesInterface }, { "interface_area_overwrite_support_area", InterfacePreference::InterfaceAreaOverwritesSupport }, { "support_lines_overwrite_interface_area", InterfacePreference::SupportLinesOverwriteInterface }, { "interface_lines_overwrite_support_area", InterfacePreference::InterfaceLinesOverwriteSupport }, { "nothing", InterfacePreference::Nothing } }; -// interface_preference = interface_map.at(mesh_group_settings.get("support_interface_priority")); -//FIXME this was the default -// interface_preference = InterfacePreference::SupportLinesOverwriteInterface; - //interface_preference = InterfacePreference::SupportAreaOverwritesInterface; - interface_preference = InterfacePreference::InterfaceAreaOverwritesSupport; - } + explicit TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params); private: double angle; @@ -466,6 +410,9 @@ public: */ coord_t min_feature_size; + // Extra raft layers below the object. + std::vector raft_layers; + public: bool operator==(const TreeSupportSettings& other) const { @@ -497,6 +444,7 @@ public: settings.get("meshfix_maximum_extrusion_area_deviation") == other.settings.get("meshfix_maximum_extrusion_area_deviation")) ) #endif + && raft_layers == other.raft_layers ; }