diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index ce4ba357c89..4bcde8e488b 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -1041,10 +1041,14 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z)); // Find the 1st layer above lt_new. for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z - EPSILON; ++ j); - if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) { + if (j < m_layer_tools.size() && std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) { m_layer_tools[j].has_wipe_tower = true; - } else { - LayerTools <_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new); + } else if (j < m_layer_tools.size() && ! m_layer_tools[j].extruders.empty()) { + // The layer right above the inserted one may carry no extruders, e.g. when + // support generation was toggled off after a slice that had it enabled: the + // layer plan for the raft gap then contains no extrusions for some layers. + // lt_next.extruders.front() would dereference a null begin() and crash. + LayerTools <_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new); //LayerTools <_prev = m_layer_tools[j]; LayerTools <_next = m_layer_tools[j + 1]; assert(! m_layer_tools[j - 1].extruders.empty() && ! lt_next.extruders.empty()); diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index 2b017b709b6..09d672a1f52 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -6,6 +6,7 @@ #include "clipper.hpp" #include "ShortestPath.hpp" +#include "ExtrusionEntityCollection.hpp" #include "KDTreeIndirect.hpp" #include "MutablePriorityQueue.hpp" #include "Print.hpp" @@ -15,6 +16,30 @@ namespace Slic3r { +// Orca: Some support entities may contain empty nested paths, which cannot be reordered safely. +static bool extrusion_entity_has_endpoints(const ExtrusionEntity *entity) +{ + auto paths_have_endpoints = [](const ExtrusionPaths &paths) { + return !paths.empty() && + !paths.front().polyline.points.empty() && + !paths.back().polyline.points.empty(); + }; + + if (entity == nullptr) + return false; + if (const auto *collection = dynamic_cast(entity)) + return !collection->entities.empty() && + extrusion_entity_has_endpoints(collection->entities.front()) && + extrusion_entity_has_endpoints(collection->entities.back()); + if (const auto *path = dynamic_cast(entity)) + return !path->polyline.points.empty(); + if (const auto *multipath = dynamic_cast(entity)) + return paths_have_endpoints(multipath->paths); + if (const auto *loop = dynamic_cast(entity)) + return paths_have_endpoints(loop->paths); + return true; +} + // Naive implementation of the Traveling Salesman Problem, it works by always taking the next closest neighbor. // This implementation will always produce valid result even if some segments cannot reverse. template @@ -1030,8 +1055,10 @@ void reorder_extrusion_entities(std::vector &entities, const s void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near) { - // this function crashes if there are empty elements in entities - entities.erase(std::remove_if(entities.begin(), entities.end(), [](ExtrusionEntity *entity) { return static_cast(entity)->empty(); }), + // Orca: Reordering queries first_point() / last_point(); drop entities that cannot provide valid endpoints. + entities.erase(std::remove_if(entities.begin(), entities.end(), [](ExtrusionEntity *entity) { + return !extrusion_entity_has_endpoints(entity); + }), entities.end()); reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near)); } diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 17e7fb044f0..67c54011c29 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1300,7 +1300,12 @@ static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const ExPolygon dst = std::move(loops_entities); } } - dst.erase(std::remove_if(dst.begin(), dst.end(), [](ExtrusionEntity *entity) { return static_cast(entity)->empty(); }), dst.end()); + + // Orca: Some entities are direct paths, so check the type before testing for an empty collection. + dst.erase(std::remove_if(dst.begin(), dst.end(), [](ExtrusionEntity *entity) { + return entity != nullptr && entity->is_collection() && static_cast(entity)->empty(); + }), dst.end()); + if (infill_first) { // sort regions to reduce travel Points ordering_points; diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 03c9f0c9bb5..4a54ea5cb12 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1654,8 +1654,15 @@ boost::any& Choice::get_value() m_opt_id == "ironing_pattern" || m_opt_id == "support_ironing_pattern" || m_opt_id == "support_style" || m_opt_id == "curr_bed_type") { - const std::string &key = m_opt.enum_values[field->GetSelection()]; - m_value = int(m_opt.enum_keys_map->at(key)); + // Selection can be invalid when the current value is not present in the rebuilt + // enum list (e.g. stale support_style vs support_type); fall back to the first + // entry instead of indexing out of bounds. + const int selection = field->GetSelection(); + if (! m_opt.enum_values.empty()) { + const int index = (selection >= 0 && selection < static_cast(m_opt.enum_values.size())) ? selection : 0; + const std::string &key = m_opt.enum_values[index]; + m_value = static_cast(m_opt.enum_keys_map->at(key)); + } } // Support ThirdPartyPrinter else if (m_opt_id.compare("host_type") == 0 && m_opt.enum_values.size() > field->GetCount()) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6b0e7bda218..8c12a9ba234 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2766,6 +2766,11 @@ void TabPrint::toggle_options() cb->Append(_(def->enum_labels[i])); } cb->SetValue(n); + // The stale label (e.g. a tree style left over after support_type changed while support + // is disabled) may not exist in the rebuilt list; GetValue()/SetValue() then leaves the + // selection invalid, and Choice::get_value would index enum_values out of bounds. + if (cb->GetSelection() == wxNOT_FOUND && cb->GetCount() > 0) + cb->SetSelection(0); } // Keep plate bed-type list in sync with currently selected printer.