diff --git a/resources/data/hints.ini b/resources/data/hints.ini index c8ce51dff..a418c6a89 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -232,8 +232,28 @@ text = Fullscreen mode\nDid you know that you can switch PrusaSlicer to fullscre enabled_tags = Windows [hint:Printables integration] -text = Printables.com integration\nDid you know that when you are browsing Printables.com, you can send 3D model files to PrusaSlicer with a single click? Read more in the documentation. +text = Printables.com integration\nDid you know that when you are browsing Printables.com, you can send 3D model files to PrusaSlicer with a single click? Learn more in the documentation. documentation_link = https://help.prusa3d.com/article/prusaslicer-printables-com-integration_399198 +weight = 3 + +[hint:Cut tool] +text = Cut tool\nDid you know that you can cut a model at any angle and even create aligning pins with the updated Cut tool? Learn more in the documentation. +documentation_link = https://help.prusa3d.com/article/cut-tool_1779 +hypertext_type = gizmo +hypertext_gizmo_item = cut +weight = 3 + +[hint:Measurement tool] +text = Measurement tool\nDid you know that you can measure the distances between points, edges and planes, the radius of a hole or the angle between edges or planes? Learn more in the documentation. +documentation_link = https://help.prusa3d.com/article/measurement-tool_399451 +hypertext_type = gizmo +hypertext_gizmo_item = measure +weight = 3 + +[hint:Text tool] +text = Text tool\nDid you know that you can add custom text labels to your models or even use the text as a modifier? Learn more in the documentation. +documentation_link = https://help.prusa3d.com/article/text-tool_399460 +weight = 3 #[hint:] #text = diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 331fc6c78..08ae899f6 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -4343,7 +4343,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 ? + float interface_angle_delta = config.support_material_style.value == smsSnug || config.support_material_style.value == smsTree || config.support_material_style.value == smsOrganic ? (support_layer.interface_id() & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.) : 0; diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/TreeModelVolumes.cpp index 63b0ab7a3..8bf26c90b 100644 --- a/src/libslic3r/TreeModelVolumes.cpp +++ b/src/libslic3r/TreeModelVolumes.cpp @@ -214,7 +214,7 @@ TreeModelVolumes::TreeModelVolumes( #endif } -void TreeModelVolumes::precalculate(const coord_t max_layer) +void TreeModelVolumes::precalculate(const coord_t max_layer, std::function throw_on_cancel) { auto t_start = std::chrono::high_resolution_clock::now(); m_precalculated = true; @@ -241,6 +241,8 @@ void TreeModelVolumes::precalculate(const coord_t max_layer) m_ignorable_radii.emplace_back(radius_eval); } + throw_on_cancel(); + // it may seem that the required avoidance can be of a smaller radius when going to model (no initial layer diameter for to model branches) // but as for every branch going towards the bp, the to model avoidance is required to check for possible merges with to model branches, this assumption is in-fact wrong. std::unordered_map radius_until_layer; @@ -260,6 +262,8 @@ void TreeModelVolumes::precalculate(const coord_t max_layer) update_radius_until_layer(ceilRadius(config.recommendedMinRadius(current_layer) + m_current_min_xy_dist_delta)); } + throw_on_cancel(); + // Copy to deque to use in parallel for later. std::vector relevant_avoidance_radiis{ radius_until_layer.begin(), radius_until_layer.end() }; @@ -275,7 +279,7 @@ void TreeModelVolumes::precalculate(const coord_t max_layer) std::vector relevant_collision_radiis{ radius_until_layer.begin(), radius_until_layer.end() }; // Calculate the relevant collisions - calculateCollision(relevant_collision_radiis); + calculateCollision(relevant_collision_radiis, throw_on_cancel); // calculate a separate Collisions with all holes removed. These are relevant for some avoidances that try to avoid holes (called safe) std::vector relevant_hole_collision_radiis; @@ -284,18 +288,18 @@ void TreeModelVolumes::precalculate(const coord_t max_layer) relevant_hole_collision_radiis.emplace_back(key); // Calculate collisions without holes, built from regular collision - calculateCollisionHolefree(relevant_hole_collision_radiis); + calculateCollisionHolefree(relevant_hole_collision_radiis, throw_on_cancel); // Let placables be calculated from calculateAvoidance() for better parallelization. if (m_support_rests_on_model) - calculatePlaceables(relevant_avoidance_radiis); + calculatePlaceables(relevant_avoidance_radiis, throw_on_cancel); auto t_coll = std::chrono::high_resolution_clock::now(); // Calculate the relevant avoidances in parallel as far as possible { tbb::task_group task_group; - task_group.run([this, relevant_avoidance_radiis]{ calculateAvoidance(relevant_avoidance_radiis, true, m_support_rests_on_model); }); - task_group.run([this, relevant_avoidance_radiis]{ calculateWallRestrictions(relevant_avoidance_radiis); }); + task_group.run([this, relevant_avoidance_radiis, throw_on_cancel]{ calculateAvoidance(relevant_avoidance_radiis, true, m_support_rests_on_model, throw_on_cancel); }); + task_group.run([this, relevant_avoidance_radiis, throw_on_cancel]{ calculateWallRestrictions(relevant_avoidance_radiis, throw_on_cancel); }); task_group.wait(); } auto t_end = std::chrono::high_resolution_clock::now(); @@ -351,7 +355,7 @@ const Polygons& TreeModelVolumes::getCollision(const coord_t orig_radius, LayerI BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; tree_supports_show_error("Not precalculated Collision requested."sv, false); } - const_cast(this)->calculateCollision(radius, layer_idx); + const_cast(this)->calculateCollision(radius, layer_idx, {}); return getCollision(orig_radius, layer_idx, min_xy_dist); } @@ -407,7 +411,7 @@ const Polygons& TreeModelVolumes::getAvoidance(const coord_t orig_radius, LayerI return getAvoidance(orig_radius, layer_idx, type, to_model, min_xy_dist); } -const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, LayerIndex layer_idx) const +const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, LayerIndex layer_idx, std::function throw_on_cancel) const { if (orig_radius == 0) return this->getCollision(0, layer_idx, true); @@ -419,8 +423,8 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; tree_supports_show_error("Not precalculated Placeable areas requested."sv, false); } - const_cast(this)->calculatePlaceables(radius, layer_idx); - return getPlaceableAreas(orig_radius, layer_idx); + const_cast(this)->calculatePlaceables(radius, layer_idx, throw_on_cancel); + return getPlaceableAreas(orig_radius, layer_idx, throw_on_cancel); } const Polygons& TreeModelVolumes::getWallRestriction(const coord_t orig_radius, LayerIndex layer_idx, bool min_xy_dist) const @@ -450,7 +454,7 @@ const Polygons& TreeModelVolumes::getWallRestriction(const coord_t orig_radius, return getWallRestriction(orig_radius, layer_idx, min_xy_dist); // Retrieve failed and correct result was calculated. Now it has to be retrieved. } -void TreeModelVolumes::calculateCollision(const std::vector &keys) +void TreeModelVolumes::calculateCollision(const std::vector &keys, std::function throw_on_cancel) { tbb::parallel_for(tbb::blocked_range(0, keys.size()), [&](const tbb::blocked_range &range) { @@ -458,12 +462,12 @@ void TreeModelVolumes::calculateCollision(const std::vector &ke const LayerIndex radius = keys[ikey].first; const size_t max_layer_idx = keys[ikey].second; // recursive call to parallel_for. - calculateCollision(radius, max_layer_idx); + calculateCollision(radius, max_layer_idx, throw_on_cancel); } }); } -void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex max_layer_idx) +void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex max_layer_idx, std::function throw_on_cancel) { // assert(radius == this->ceilRadius(radius)); @@ -501,7 +505,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex // 1) Calculate offsets of collision areas in parallel. std::vector collision_areas_offsetted(max_required_layer + 1 - min_layer_bottom); tbb::parallel_for(tbb::blocked_range(min_layer_bottom, max_required_layer + 1), - [&outlines, &machine_border = m_machine_border, offset_value = radius + xy_distance, min_layer_bottom, &collision_areas_offsetted] + [&outlines, &machine_border = m_machine_border, offset_value = radius + xy_distance, min_layer_bottom, &collision_areas_offsetted, &throw_on_cancel] (const tbb::blocked_range &range) { for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) { Polygons collision_areas = machine_border; @@ -509,13 +513,14 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex // jtRound is not needed here, as the overshoot can not cause errors in the algorithm, because no assumptions are made about the model. // if a key does not exist when it is accessed it is added! collision_areas_offsetted[layer_idx - min_layer_bottom] = offset_value == 0 ? union_(collision_areas) : offset(union_ex(collision_areas), offset_value, ClipperLib::jtMiter, 1.2); + throw_on_cancel(); } }); // 2) Sum over top / bottom ranges. const bool last = outline_idx == layer_outline_indices.size(); tbb::parallel_for(tbb::blocked_range(min_layer_last + 1, max_layer_idx + 1), - [&collision_areas_offsetted, &anti_overhang = m_anti_overhang, min_layer_bottom, radius, z_distance_bottom_layers, z_distance_top_layers, min_resolution = m_min_resolution, &data, min_layer_last, last] + [&collision_areas_offsetted, &anti_overhang = m_anti_overhang, min_layer_bottom, radius, z_distance_bottom_layers, z_distance_top_layers, min_resolution = m_min_resolution, &data, min_layer_last, last, &throw_on_cancel] (const tbb::blocked_range& range) { for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++layer_idx) { Polygons collisions; @@ -532,6 +537,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex dst = polygons_simplify(collisions, min_resolution); } else append(dst, collisions); + throw_on_cancel(); } }); @@ -539,7 +545,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex if (calculate_placable) { // Calculating both the collision areas and placable areas. tbb::parallel_for(tbb::blocked_range(std::max(min_layer_last + 1, z_distance_bottom_layers + 1), max_layer_idx + 1), - [&collision_areas_offsetted, &anti_overhang = m_anti_overhang, min_layer_bottom, z_distance_bottom_layers, last, min_resolution = m_min_resolution, &data_placeable, min_layer_last] + [&collision_areas_offsetted, &anti_overhang = m_anti_overhang, min_layer_bottom, z_distance_bottom_layers, last, min_resolution = m_min_resolution, &data_placeable, min_layer_last, &throw_on_cancel] (const tbb::blocked_range& range) { for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) { LayerIndex layer_idx_below = layer_idx - (z_distance_bottom_layers + 1) - min_layer_bottom; @@ -554,6 +560,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex dst = polygons_simplify(placable, min_resolution); } else append(dst, placable); + throw_on_cancel(); } }); } else { @@ -569,12 +576,13 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex } } #endif + throw_on_cancel(); m_collision_cache.insert(std::move(data), min_layer_last + 1, radius); if (calculate_placable) m_placeable_areas_cache.insert(std::move(data_placeable), min_layer_last + 1, radius); } -void TreeModelVolumes::calculateCollisionHolefree(const std::vector &keys) +void TreeModelVolumes::calculateCollisionHolefree(const std::vector &keys, std::function throw_on_cancel) { LayerIndex max_layer = 0; for (long long unsigned int i = 0; i < keys.size(); i++) @@ -598,13 +606,14 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::vectorgetCollision(m_increase_until_radius, layer_idx, false)), 5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution), m_min_resolution)); + throw_on_cancel(); } } m_collision_cache_holefree.insert(std::move(data)); }); } -void TreeModelVolumes::calculateAvoidance(const std::vector &keys, bool to_build_plate, bool to_model) +void TreeModelVolumes::calculateAvoidance(const std::vector &keys, bool to_build_plate, bool to_model, std::function throw_on_cancel) { // For every RadiusLayer pair there are 3 avoidances that have to be calculated. // Prepare tasks for parallelization. @@ -639,14 +648,17 @@ void TreeModelVolumes::calculateAvoidance(const std::vector &ke avoidance_tasks.emplace_back(task); } + throw_on_cancel(); + tbb::parallel_for(tbb::blocked_range(0, avoidance_tasks.size(), 1), - [this, &avoidance_tasks](const tbb::blocked_range &range) { + [this, &avoidance_tasks, &throw_on_cancel](const tbb::blocked_range &range) { for (size_t task_idx = range.begin(); task_idx < range.end(); ++ task_idx) { const AvoidanceTask &task = avoidance_tasks[task_idx]; assert(! task.holefree() || task.radius < m_increase_until_radius + m_current_min_xy_dist_delta); if (task.to_model) // ensuring Placeableareas are calculated - getPlaceableAreas(task.radius, task.max_required_layer); + //FIXME pass throw_on_cancel + getPlaceableAreas(task.radius, task.max_required_layer, throw_on_cancel); // The following loop propagating avoidance regions bottom up is inherently serial. const bool collision_holefree = (task.slow() || task.holefree()) && task.radius < m_increase_until_radius + m_current_min_xy_dist_delta; const float max_move = task.slow() ? m_max_move_slow : m_max_move; @@ -678,9 +690,10 @@ void TreeModelVolumes::calculateAvoidance(const std::vector &ke istep + 1 == move_steps ? - last_move_step : - move_step, ClipperLib::jtRound, m_min_resolution)); if (task.to_model) - latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx)); + latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx, throw_on_cancel)); latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution); data.emplace_back(RadiusLayerPair{task.radius, layer_idx}, latest_avoidance); + throw_on_cancel(); } #ifdef SLIC3R_TREESUPPORTS_PROGRESS { @@ -699,16 +712,16 @@ void TreeModelVolumes::calculateAvoidance(const std::vector &ke } -void TreeModelVolumes::calculatePlaceables(const std::vector &keys) +void TreeModelVolumes::calculatePlaceables(const std::vector &keys, std::function throw_on_cancel) { tbb::parallel_for(tbb::blocked_range(0, keys.size()), [&, keys](const tbb::blocked_range& range) { for (size_t key_idx = range.begin(); key_idx < range.end(); ++ key_idx) - this->calculatePlaceables(keys[key_idx].first, keys[key_idx].second); + this->calculatePlaceables(keys[key_idx].first, keys[key_idx].second, throw_on_cancel); }); } -void TreeModelVolumes::calculatePlaceables(const coord_t radius, const LayerIndex max_required_layer) +void TreeModelVolumes::calculatePlaceables(const coord_t radius, const LayerIndex max_required_layer, std::function throw_on_cancel) { LayerIndex start_layer = 1 + m_placeable_areas_cache.getMaxCalculatedLayer(radius); if (start_layer > max_required_layer) { @@ -722,15 +735,17 @@ void TreeModelVolumes::calculatePlaceables(const coord_t radius, const LayerInde data[0] = diff(m_machine_border, getCollision(radius, 0, true)); tbb::parallel_for(tbb::blocked_range(std::max(1, start_layer), max_required_layer + 1), - [this, &data, radius, start_layer](const tbb::blocked_range& range) { - for (LayerIndex layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) + [this, &data, radius, start_layer, &throw_on_cancel](const tbb::blocked_range& range) { + for (LayerIndex layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { data[layer_idx - start_layer] = offset( - union_ex(getPlaceableAreas(0, layer_idx)), + union_ex(getPlaceableAreas(0, layer_idx, throw_on_cancel)), // As a placeable area is calculated by (collision of the layer below) - (collision of the current layer) and the collision is offset by xy_distance, // it can happen that a small line is considered a flat area to place something onto, even though it is mostly // xy_distance that cant support it. Making the area smaller by xy_distance fixes this. - (radius + m_current_min_xy_dist + m_current_min_xy_dist_delta), jtMiter, 1.2); + throw_on_cancel(); + } }); #ifdef SLIC3R_TREESUPPORTS_PROGRESS { @@ -744,7 +759,7 @@ void TreeModelVolumes::calculatePlaceables(const coord_t radius, const LayerInde m_placeable_areas_cache.insert(std::move(data), start_layer, radius); } -void TreeModelVolumes::calculateWallRestrictions(const std::vector &keys) +void TreeModelVolumes::calculateWallRestrictions(const std::vector &keys, std::function throw_on_cancel) { // Wall restrictions are mainly important when they represent actual walls that are printed, and not "just" the configured z_distance, because technically valid placement is no excuse for moving through a wall. // As they exist to prevent accidentially moving though a wall at high speed between layers like thie (x = wall,i = influence area,o= empty space,d = blocked area because of z distance) Assume maximum movement distance is two characters and maximum safe movement distance of one character @@ -792,7 +807,7 @@ void TreeModelVolumes::calculateWallRestrictions(const std::vector 0) data_min.assign(buffer_size, Polygons{}); tbb::parallel_for(tbb::blocked_range(min_layer_bottom, max_required_layer + 1), - [this, &data, &data_min, radius, min_layer_bottom](const tbb::blocked_range &range) { + [this, &data, &data_min, radius, min_layer_bottom, &throw_on_cancel](const tbb::blocked_range &range) { for (LayerIndex layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { data[layer_idx - min_layer_bottom] = polygons_simplify( // radius contains m_current_min_xy_dist_delta already if required @@ -803,6 +818,7 @@ void TreeModelVolumes::calculateWallRestrictions(const std::vector 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. @@ -266,7 +266,7 @@ public: * \param layer_idx The layer of interest * \return Polygons object */ - const Polygons& getPlaceableAreas(coord_t radius, LayerIndex layer_idx) const; + const Polygons& getPlaceableAreas(coord_t radius, LayerIndex layer_idx, std::function throw_on_cancel) const; /*! * \brief Provides the area that represents the walls, as in the printed area, of the model. This is an abstract representation not equal with the outline. See calculateWallRestrictions for better description. * \param radius The radius of the node of interest. @@ -431,8 +431,8 @@ private: * collide with the model. Result is saved in the cache. * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. */ - void calculateCollision(const std::vector &keys); - void calculateCollision(const coord_t radius, const LayerIndex max_layer_idx); + void calculateCollision(const std::vector &keys, std::function throw_on_cancel); + void calculateCollision(const coord_t radius, const LayerIndex max_layer_idx, std::function throw_on_cancel); /*! * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed. * @@ -441,7 +441,7 @@ private: * A Hole is defined as an area, in which a branch with m_increase_until_radius radius would collide with the wall. * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. */ - void calculateCollisionHolefree(const std::vector &keys); + void calculateCollisionHolefree(const std::vector &keys, std::function throw_on_cancel); /*! * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed. @@ -453,7 +453,7 @@ private: */ void calculateCollisionHolefree(RadiusLayerPair key) { - calculateCollisionHolefree(std::vector{ RadiusLayerPair(key) }); + calculateCollisionHolefree(std::vector{ RadiusLayerPair(key) }, {}); } /*! @@ -463,7 +463,7 @@ private: * collide with the model. Result is saved in the cache. * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. */ - void calculateAvoidance(const std::vector &keys, bool to_build_plate, bool to_model); + void calculateAvoidance(const std::vector &keys, bool to_build_plate, bool to_model, std::function throw_on_cancel); /*! * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model. @@ -474,7 +474,7 @@ private: */ void calculateAvoidance(RadiusLayerPair key, bool to_build_plate, bool to_model) { - calculateAvoidance(std::vector{ RadiusLayerPair(key) }, to_build_plate, to_model); + calculateAvoidance(std::vector{ RadiusLayerPair(key) }, to_build_plate, to_model, {}); } /*! @@ -482,7 +482,7 @@ private: * Result is saved in the cache. * \param key RadiusLayerPair of the requested areas. It will be calculated up to the provided layer. */ - void calculatePlaceables(const coord_t radius, const LayerIndex max_required_layer); + void calculatePlaceables(const coord_t radius, const LayerIndex max_required_layer, std::function throw_on_cancel); /*! @@ -490,7 +490,7 @@ private: * Result is saved in the cache. * \param keys RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer. */ - void calculatePlaceables(const std::vector &keys); + void calculatePlaceables(const std::vector &keys, std::function throw_on_cancel); /*! * \brief Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object). @@ -499,7 +499,7 @@ private: * * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. */ - void calculateWallRestrictions(const std::vector &keys); + void calculateWallRestrictions(const std::vector &keys, std::function throw_on_cancel); /*! * \brief Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object). @@ -508,7 +508,7 @@ private: */ void calculateWallRestrictions(RadiusLayerPair key) { - calculateWallRestrictions(std::vector{ RadiusLayerPair(key) }); + calculateWallRestrictions(std::vector{ RadiusLayerPair(key) }, {}); } /*! diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 5b729f34e..6bd39f4e3 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -222,7 +222,7 @@ 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) +[[nodiscard]] static const std::vector generate_overhangs(const PrintObject &print_object, std::function throw_on_cancel) { std::vector out(print_object.layer_count(), Polygons{}); @@ -241,7 +241,7 @@ void tree_supports_show_error(std::string_view message, bool critical) auto enforcer_overhang_offset = scaled(config.support_tree_tip_diameter.value); tbb::parallel_for(tbb::blocked_range(1, out.size()), - [&print_object, &enforcers_layers, &blockers_layers, support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, &out] + [&print_object, &enforcers_layers, &blockers_layers, support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, &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); @@ -307,6 +307,7 @@ void tree_supports_show_error(std::string_view message, bool critical) } } out[layer_id] = std::move(overhangs); + throw_on_cancel(); } }); @@ -319,7 +320,7 @@ void tree_supports_show_error(std::string_view message, bool critical) * \param storage[in] Background storage to access meshes. * \param currently_processing_meshes[in] Indexes of all meshes that are processed in this iteration */ -[[nodiscard]] static LayerIndex precalculate(const Print &print, const std::vector &overhangs, const TreeSupportSettings &config, const std::vector &object_ids, TreeModelVolumes &volumes) +[[nodiscard]] static LayerIndex precalculate(const Print &print, const std::vector &overhangs, const TreeSupportSettings &config, const std::vector &object_ids, TreeModelVolumes &volumes, std::function throw_on_cancel) { // calculate top most layer that is relevant for support LayerIndex max_layer = 0; @@ -333,7 +334,7 @@ void tree_supports_show_error(std::string_view message, bool critical) } if (max_layer > 0) // The actual precalculation happens in TreeModelVolumes. - volumes.precalculate(max_layer); + volumes.precalculate(max_layer, throw_on_cancel); return max_layer; } @@ -874,7 +875,8 @@ static void generate_initial_areas( std::vector &move_bounds, SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &top_interface_layers, - SupportGeneratorLayerStorage &layer_storage) + SupportGeneratorLayerStorage &layer_storage, + std::function throw_on_cancel) { using AvoidanceType = TreeModelVolumes::AvoidanceType; static constexpr const auto base_radius = scaled(0.01); @@ -932,7 +934,7 @@ static void generate_initial_areas( [&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, - &move_bounds](const tbb::blocked_range &range) { + &move_bounds, &throw_on_cancel](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { if (overhangs[layer_idx + z_distance_delta].empty()) continue; @@ -1142,6 +1144,8 @@ static void generate_initial_areas( } } + throw_on_cancel(); + Polygons overhang_roofs; std::vector> overhang_processing; if (roof_enabled) { @@ -1271,6 +1275,7 @@ static void generate_initial_areas( 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); + throw_on_cancel(); } } }); @@ -1602,7 +1607,8 @@ static void increase_areas_one_layer( // Layer elements above merging_areas. SupportElements &layer_elements, // If false, the merging_areas will not be merged for performance reasons. - const bool mergelayer) + const bool mergelayer, + std::function throw_on_cancel) { using AvoidanceType = TreeModelVolumes::AvoidanceType; @@ -1731,7 +1737,7 @@ static void increase_areas_one_layer( } order = new_order; } - if (elem.to_buildplate || (elem.to_model_gracious && intersection(parent.influence_area, volumes.getPlaceableAreas(radius, layer_idx)).empty())) { + if (elem.to_buildplate || (elem.to_model_gracious && intersection(parent.influence_area, volumes.getPlaceableAreas(radius, layer_idx, throw_on_cancel)).empty())) { // error case // it is normal that we wont be able to find a new area at some point in time if we wont be able to reach layer 0 aka have to connect with the model insertSetting({ AvoidanceType::Fast, fast_speed, !increase_radius, !no_error, elem.use_min_xy_dist, move }, true); @@ -1849,6 +1855,7 @@ static void increase_areas_one_layer( // 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(); } + throw_on_cancel(); } }); } @@ -2144,8 +2151,11 @@ 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 LayerIndex layer_idx, - std::vector &influence_areas) + const TreeModelVolumes &volumes, + const TreeSupportSettings &config, + const LayerIndex layer_idx, + std::vector &influence_areas, + std::function throw_on_cancel) { const size_t input_size = influence_areas.size(); if (input_size == 0) @@ -2164,6 +2174,8 @@ static void merge_influence_areas( // Sort influence_areas in place. tree.build_modify_input(influence_areas); + throw_on_cancel(); + // Prepare the initial buckets as ranges of influence areas. The initial buckets contain power of 2 influence areas to follow // the branching of the AABB tree. // Vectors of ranges of influence areas, following the branching of the AABB tree: @@ -2203,6 +2215,7 @@ static void merge_influence_areas( const size_t bucket_pair_idx = idx * 2; // Merge bucket_count adjacent to each other, merging uneven bucket numbers into even buckets buckets[idx].second = merge_influence_areas_leaves(volumes, config, layer_idx, buckets[idx].first, buckets[idx].second); + throw_on_cancel(); } }); @@ -2217,6 +2230,7 @@ static void merge_influence_areas( buckets[bucket_pair_idx].second = merge_influence_areas_two_sets(volumes, config, layer_idx, buckets[bucket_pair_idx].first, buckets[bucket_pair_idx].second, buckets[bucket_pair_idx + 1].first, buckets[bucket_pair_idx + 1].second); + throw_on_cancel(); } }); // Remove odd buckets, which were merged into even buckets. @@ -2232,7 +2246,7 @@ static void merge_influence_areas( * * \param move_bounds[in,out] All currently existing influence areas */ -static void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector &move_bounds) +static void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector &move_bounds, std::function throw_on_cancel) { #ifdef SLIC3R_TREESUPPORTS_PROGRESS const double data_size_inverse = 1 / double(move_bounds.size()); @@ -2269,7 +2283,7 @@ static void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupp parents.emplace_back(element_idx); influence_areas.push_back({ el.state, parents }); } - increase_areas_one_layer(volumes, config, influence_areas, layer_idx, prev_layer, merge_this_layer); + increase_areas_one_layer(volumes, config, influence_areas, layer_idx, prev_layer, merge_this_layer, throw_on_cancel); // Place already fully constructed elements to the output, remove them from influence_areas. SupportElements &this_layer = move_bounds[layer_idx - 1]; @@ -2298,7 +2312,7 @@ static void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupp bool reduced_by_merging = false; if (size_t count_before_merge = influence_areas.size(); count_before_merge > 1) { // ### Calculate which influence areas overlap, and merge them into a new influence area (simplified: an intersection of influence areas that have such an intersection) - merge_influence_areas(volumes, config, layer_idx, influence_areas); + merge_influence_areas(volumes, config, layer_idx, influence_areas, throw_on_cancel); reduced_by_merging = count_before_merge > influence_areas.size(); } last_merge_layer_idx = layer_idx; @@ -2323,6 +2337,7 @@ static void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupp progress_total += data_size_inverse * TREE_PROGRESS_AREA_CALC; Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); #endif + throw_on_cancel(); } BOOST_LOG_TRIVIAL(info) << "Time spent with creating influence areas' subtasks: Increasing areas " << dur_inc.count() / 1000000 << @@ -2382,7 +2397,8 @@ static void set_to_model_contact_to_model_gracious( const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector &move_bounds, - SupportElement &first_elem) + SupportElement &first_elem, + std::function throw_on_cancel) { SupportElement *last_successfull_layer = nullptr; @@ -2390,7 +2406,7 @@ static void set_to_model_contact_to_model_gracious( { SupportElement *elem = &first_elem; for (LayerIndex layer_check = elem->state.layer_idx; - ! intersection(elem->influence_area, volumes.getPlaceableAreas(config.getCollisionRadius(elem->state), layer_check)).empty(); + ! intersection(elem->influence_area, volumes.getPlaceableAreas(config.getCollisionRadius(elem->state), layer_check, throw_on_cancel)).empty(); elem = &move_bounds[++ layer_check][elem->parents.front()]) { assert(elem->state.layer_idx == layer_check); assert(! elem->state.deleted); @@ -2475,7 +2491,8 @@ static void remove_deleted_elements(std::vector &move_bounds) static void create_nodes_from_area( const TreeModelVolumes &volumes, const TreeSupportSettings &config, - std::vector &move_bounds) + std::vector &move_bounds, + std::function throw_on_cancel) { // 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. @@ -2490,6 +2507,8 @@ static void create_nodes_from_area( } } + throw_on_cancel(); + for (LayerIndex layer_idx = 1; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) { auto &layer = move_bounds[layer_idx]; auto *layer_above = layer_idx + 1 < move_bounds.size() ? &move_bounds[layer_idx + 1] : nullptr; @@ -2512,7 +2531,7 @@ static void create_nodes_from_area( } else { // set the point where the branch will be placed on the model if (elem.state.to_model_gracious) - set_to_model_contact_to_model_gracious(volumes, config, move_bounds, elem); + set_to_model_contact_to_model_gracious(volumes, config, move_bounds, elem, throw_on_cancel); else set_to_model_contact_simple(elem); } @@ -2531,6 +2550,7 @@ static void create_nodes_from_area( set_points_on_areas(elem, layer_above); } } + throw_on_cancel(); } #ifndef NDEBUG @@ -2601,7 +2621,12 @@ struct DrawArea * \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) +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; @@ -2614,7 +2639,7 @@ static void generate_branch_areas(const TreeModelVolumes &volumes, const TreeSup 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](const tbb::blocked_range &range) { + [&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; @@ -2717,6 +2742,7 @@ static void generate_branch_areas(const TreeModelVolumes &volumes, const TreeSup Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); } #endif + throw_on_cancel(); } }); } @@ -2730,7 +2756,8 @@ static void smooth_branch_areas( const TreeSupportSettings &config, std::vector &move_bounds, std::vector &linear_data, - const std::vector &linear_data_layers) + 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; @@ -2792,6 +2819,7 @@ static void smooth_branch_areas( } } } + throw_on_cancel(); } }); } @@ -2835,6 +2863,7 @@ static void smooth_branch_areas( draw_area.polygons = std::move(result); } } + throw_on_cancel(); } }); } @@ -2856,12 +2885,13 @@ static void smooth_branch_areas( static void drop_non_gracious_areas( const TreeModelVolumes &volumes, const std::vector &linear_data, - std::vector &support_layer_storage) + std::vector &support_layer_storage, + std::function throw_on_cancel) { 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) + 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; @@ -2871,6 +2901,8 @@ static void drop_non_gracious_areas( 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++) @@ -2896,7 +2928,9 @@ static void finalize_interface_and_support_areas( SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &intermediate_layers, - SupportGeneratorLayerStorage &layer_storage) + SupportGeneratorLayerStorage &layer_storage, + + std::function throw_on_cancel) { InterfacePreference interface_pref = config.interface_preference; // InterfacePreference::SupportLinesOverwriteInterface; @@ -3013,6 +3047,7 @@ static void finalize_interface_and_support_areas( storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, static_cast(layer_idx)); } #endif + throw_on_cancel(); } }); } @@ -3033,7 +3068,8 @@ static void draw_areas( SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &intermediate_layers, - SupportGeneratorLayerStorage &layer_storage) + SupportGeneratorLayerStorage &layer_storage, + std::function throw_on_cancel) { std::vector support_layer_storage(move_bounds.size()); std::vector support_roof_storage(move_bounds.size()); @@ -3071,6 +3107,8 @@ static void draw_areas( 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]; @@ -3082,7 +3120,7 @@ static void draw_areas( 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); + generate_branch_areas(volumes, config, move_bounds, linear_data, throw_on_cancel); #if 0 assert(linear_data_layers.size() == move_bounds.size() + 1); @@ -3115,7 +3153,7 @@ static void draw_areas( 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); + 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) { @@ -3133,7 +3171,7 @@ static void draw_areas( 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); + 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. @@ -3156,7 +3194,7 @@ static void draw_areas( } finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage, - bottom_contacts, top_contacts, intermediate_layers, layer_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(); @@ -3403,7 +3441,8 @@ static void organic_smooth_branches_avoid_collisions( const TreeSupportSettings &config, std::vector &move_bounds, const std::vector> &elements_with_link_down, - const std::vector &linear_data_layers) + const std::vector &linear_data_layers, + std::function throw_on_cancel) { struct LayerCollisionCache { coord_t min_element_radius{ std::numeric_limits::max() }; @@ -3426,6 +3465,9 @@ static void organic_smooth_branches_avoid_collisions( auto& l = layer_collision_cache[layer_idx]; l.min_element_radius = std::min(l.min_element_radius, config.getRadius(element.first->state)); } + + throw_on_cancel(); + for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(layer_collision_cache.size()); ++layer_idx) if (LayerCollisionCache& l = layer_collision_cache[layer_idx]; !l.min_element_radius_known()) l.min_element_radius = 0; @@ -3440,6 +3482,7 @@ static void organic_smooth_branches_avoid_collisions( for (const Line &line : alines) l.lines.push_back({ unscaled(line.a), unscaled(line.b) }); l.aabbtree_lines = AABBTreeLines::build_aabb_tree_over_indexed_lines(l.lines); + throw_on_cancel(); } struct CollisionSphere { @@ -3511,6 +3554,8 @@ static void organic_smooth_branches_avoid_collisions( collision_sphere.layer_end = std::max(collision_sphere.element.state.layer_idx, layer_idx_floor(slicing_params, collision_sphere.max_z)) + 1; } + throw_on_cancel(); + static constexpr const double collision_extra_gap = 0.1; static constexpr const double max_nudge_collision_avoidance = 0.2; static constexpr const double max_nudge_smoothing = 0.2; @@ -3521,7 +3566,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](const tbb::blocked_range range) { + [&collision_spheres, &layer_collision_cache, &slicing_params, &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. @@ -3579,6 +3624,8 @@ static void organic_smooth_branches_avoid_collisions( // Shift by maximum 1mm, less than the collision avoidance factor. double nudge_dist = std::min(std::max(0., nudge_dist_max), max_nudge_smoothing); collision_sphere.position.head<2>() += (shift.normalized() * nudge_dist).cast(); + + throw_on_cancel(); } }); // printf("iteration: %d, moved: %d\n", int(iter), int(num_moved)); @@ -3703,7 +3750,9 @@ static void draw_branches( SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &intermediate_layers, - SupportGeneratorLayerStorage &layer_storage) + SupportGeneratorLayerStorage &layer_storage, + + std::function throw_on_cancel) { static int irun = 0; @@ -3745,7 +3794,9 @@ static void draw_branches( } } - organic_smooth_branches_avoid_collisions(print_object, volumes, config, move_bounds, elements_with_link_down, linear_data_layers); + throw_on_cancel(); + + organic_smooth_branches_avoid_collisions(print_object, volumes, config, move_bounds, elements_with_link_down, linear_data_layers, throw_on_cancel); std::vector support_layer_storage(move_bounds.size()); std::vector support_roof_storage(move_bounds.size()); @@ -3812,6 +3863,7 @@ static void draw_branches( #endif its_merge(cummulative_mesh, partial_mesh); } + throw_on_cancel(); } } @@ -3855,7 +3907,7 @@ static void draw_branches( }); finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage, - bottom_contacts, top_contacts, intermediate_layers, layer_storage); + bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); } /*! @@ -3916,10 +3968,10 @@ 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())); + std::vector overhangs = generate_overhangs(*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); + size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel); if (num_support_layers == 0) continue; @@ -3936,7 +3988,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume SupportGeneratorLayerStorage layer_storage; for (size_t mesh_idx : processing.second) - generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs, move_bounds, top_contacts, top_interface_layers, layer_storage); + generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs, move_bounds, top_contacts, top_interface_layers, layer_storage, throw_on_cancel); auto t_gen = std::chrono::high_resolution_clock::now(); #ifdef TREESUPPORT_DEBUG_SVG @@ -3953,22 +4005,22 @@ 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. - create_layer_pathing(volumes, config, move_bounds); + create_layer_pathing(volumes, config, move_bounds, throw_on_cancel); auto t_path = std::chrono::high_resolution_clock::now(); // ### Set a point in each influence area - create_nodes_from_area(volumes, config, move_bounds); + create_nodes_from_area(volumes, config, move_bounds, throw_on_cancel); auto t_place = std::chrono::high_resolution_clock::now(); // ### draw these points as circles if (print_object.config().support_material_style == smsTree) draw_areas(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds, - bottom_contacts, top_contacts, intermediate_layers, layer_storage); + bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); else { assert(print_object.config().support_material_style == smsOrganic); draw_branches(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds, - bottom_contacts, top_contacts, intermediate_layers, layer_storage); + bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); } auto t_draw = std::chrono::high_resolution_clock::now(); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 4e087c98f..de61488ef 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1214,6 +1214,20 @@ void Choice::set_value(const boost::any& value, bool change_event) auto it = std::find(values.begin(), values.end(), key); val = it == values.end() ? 0 : it - values.begin(); } + else if (m_opt_id == "support_material_style") + { + std::string key; + const t_config_enum_values& map_names = ConfigOptionEnum::get_enum_values(); + for (auto it : map_names) + if (val == it.second) { + key = it.first; + break; + } + + const std::vector& values = m_opt.enum_values; + auto it = std::find(values.begin(), values.end(), key); + val = it == values.end() ? 0 : it - values.begin(); + } field->SetSelection(val); break; } @@ -1281,7 +1295,9 @@ boost::any& Choice::get_value() if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "fill_pattern") { const std::string& key = m_opt.enum_values[field->GetSelection()]; m_value = int(ConfigOptionEnum::get_enum_values().at(key)); - } + } else if (m_opt_id == "support_material_style") { + m_value = int(ConfigOptionEnum::get_enum_values().at(m_opt.enum_values[field->GetSelection()])); + } else m_value = field->GetSelection(); } diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index d4df48d50..199a1e8d7 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -286,7 +286,8 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio bool is_infill = def->opt_key == "top_fill_pattern" || def->opt_key == "bottom_fill_pattern" || - def->opt_key == "fill_pattern"; + def->opt_key == "fill_pattern" || + def->opt_key == "support_material_style"; // Each infill doesn't use all list of infill declared in PrintConfig.hpp. // So we should "convert" val to the correct one diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index da1446fd6..11517bf10 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1217,7 +1217,8 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& return get_string_from_enum(opt_key, config, opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || - opt_key == "fill_pattern"); + opt_key == "fill_pattern" || + opt_key == "support_material_style"); } case coPoints: { if (opt_key == "bed_shape") {