diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index a69e7b48b5..937d4db91c 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -213,6 +213,8 @@ set(SLIC3R_SOURCES GCode/AvoidCrossingPerimeters.hpp GCode/Travels.cpp GCode/Travels.hpp + GCode/ExtrusionOrder.cpp + GCode/ExtrusionOrder.hpp GCode.cpp GCode.hpp GCodeReader.cpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 82e06d08b3..10b19a5d1b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1961,7 +1961,7 @@ void GCodeGenerator::_print_first_layer_extruder_temperatures(GCodeOutputStream } } -std::vector GCodeGenerator::sort_print_object_instances( +std::vector GCodeGenerator::sort_print_object_instances( const std::vector &object_layers, // Ordering must be defined for normal (non-sequential print). const std::vector *ordering, @@ -2283,558 +2283,6 @@ std::string GCodeGenerator::generate_ramping_layer_change_gcode( return travel_gcode; } -namespace GCode { - -bool is_overriden(const ExtrusionEntityCollection &eec, const LayerTools &layer_tools, const std::size_t instance_id) { - return layer_tools.wiping_extrusions().get_extruder_override(&eec, instance_id) > -1; -} - -int get_extruder_id( - const ExtrusionEntityCollection &eec, - const LayerTools &layer_tools, - const PrintRegion ®ion, - const std::size_t instance_id -) { - if (is_overriden(eec, layer_tools, instance_id)) { - return layer_tools.wiping_extrusions().get_extruder_override(&eec, instance_id); - } - - const int extruder_id = layer_tools.extruder(eec, region); - if (! layer_tools.has_extruder(extruder_id)) { - // Extruder is not in layer_tools - we'll print it by last extruder on this layer (could - // happen e.g. when a wiping object is taller than others - dontcare extruders are - // eradicated from layer_tools) - return layer_tools.extruders.back(); - } - return extruder_id; -} - -using ExtractEntityPredicate = std::function; - -ExtrusionEntitiesPtr extract_infill_extrusions( - const Layer *layer, - const PrintRegion ®ion, - const ExtrusionEntityCollection &fills, - LayerExtrusionRanges::const_iterator begin, - LayerExtrusionRanges::const_iterator end, - const ExtractEntityPredicate &predicate -) { - ExtrusionEntitiesPtr result; - for (auto it = begin; it != end; ++ it) { - assert(it->region() == begin->region()); - const LayerExtrusionRange &range{*it}; - for (uint32_t fill_id : range) { - assert(dynamic_cast(fills.entities[fill_id])); - - auto *eec{static_cast(fills.entities[fill_id])}; - if (eec == nullptr || eec->empty() || !predicate(*eec, region)) { - continue; - } - - if (eec->can_reverse()) { - // Flatten the infill collection for better path planning. - for (auto *ee : eec->entities) { - result.emplace_back(ee); - } - } else { - result.emplace_back(eec); - } - } - } - return result; -} - -std::vector extract_perimeter_extrusions( - const Print &print, - const Layer *layer, - const LayerIsland &island, - const ExtractEntityPredicate &predicate -) { - std::vector result; - - const LayerRegion &layerm = *layer->get_region(island.perimeters.region()); - const PrintRegion ®ion = print.get_print_region(layerm.region().print_region_id()); - - for (uint32_t perimeter_id : island.perimeters) { - // Extrusions inside islands are expected to be ordered already. - // Don't reorder them. - assert(dynamic_cast(layerm.perimeters().entities[perimeter_id])); - auto *eec = static_cast(layerm.perimeters().entities[perimeter_id]); - if (eec == nullptr || eec->empty() || !predicate(*eec, region)) { - continue; - } - - for (ExtrusionEntity *ee : *eec) { - result.push_back(ee); - } - } - - return result; -} - -std::vector sort_fill_extrusions(const ExtrusionEntitiesPtr &fills, const Point* start_near) { - if (fills.empty()) { - return {}; - } - std::vector sorted_extrusions; - - for (const ExtrusionEntityReference &fill : chain_extrusion_references(fills, start_near)) { - if (auto *eec = dynamic_cast(&fill.extrusion_entity()); eec) { - for (const ExtrusionEntityReference &ee : chain_extrusion_references(*eec, start_near, fill.flipped())) { - sorted_extrusions.push_back(ee); - } - } else { - sorted_extrusions.push_back(fill); - } - } - return sorted_extrusions; -} - -std::vector extract_infill_ranges( - const Print &print, - const Layer *layer, - const LayerIsland island, - std::optional previous_position, - const ExtractEntityPredicate &predicate -) { - std::vector result; - for (auto it = island.fills.begin(); it != island.fills.end();) { - // Gather range of fill ranges with the same region. - auto it_end = it; - for (++ it_end; it_end != island.fills.end() && it->region() == it_end->region(); ++ it_end) ; - const LayerRegion &layerm = *layer->get_region(it->region()); - // PrintObjects own the PrintRegions, thus the pointer to PrintRegion would be unique to a PrintObject, they would not - // identify the content of PrintRegion accross the whole print uniquely. Translate to a Print specific PrintRegion. - const PrintRegion ®ion = print.get_print_region(layerm.region().print_region_id()); - - const Point* start_near = previous_position ? &(*(previous_position)) : nullptr; - - ExtrusionEntitiesPtr extrusions{extract_infill_extrusions( - layer, - region, - layerm.fills(), - it, - it_end, - predicate - )}; - - const std::vector sorted_extrusions{sort_fill_extrusions(extrusions, start_near)}; - - if (! sorted_extrusions.empty()) { - result.push_back({sorted_extrusions, ®ion}); - previous_position = sorted_extrusions.back().flipped() ? - sorted_extrusions.back().extrusion_entity().first_point() : - sorted_extrusions.back().extrusion_entity().last_point(); - } - it = it_end; - } - return result; -} -} // namespace GCode - -void place_seams( - const Layer *layer, const Seams::Placer &seam_placer, const std::vector &perimeters, std::optional previous_position, const bool spiral_vase -) { - std::vector result; - result.reserve(perimeters.size()); - - for (ExtrusionEntity* perimeter : perimeters) { - auto loop{static_cast(perimeter)}; - - Point seam_point{previous_position ? *previous_position : Point::Zero()}; - if (!spiral_vase && loop != nullptr) { - assert(m_layer != nullptr); - seam_point = seam_placer.place_seam(layer, *loop, seam_point); - loop->seam = seam_point; - } - previous_position = seam_point; - } -} - -std::string GCodeGenerator::extrude_infill_range( - const std::vector &sorted_extrusions, - const PrintRegion ®ion, - const std::string &extrusion_name, - const GCode::SmoothPathCache &smooth_path_cache -) { - std::string gcode{}; - if (!sorted_extrusions.empty()) { - this->m_config.apply(region.config()); - - for (const ExtrusionEntityReference &ee : sorted_extrusions) { - gcode += this->extrude_entity(ee, smooth_path_cache, extrusion_name); - } - } - return gcode; -}; - -std::string GCodeGenerator::extrude_infill_ranges( - const std::vector &infill_ranges, - const std::string &comment, - const GCode::SmoothPathCache &smooth_path_cache -) { - std::string gcode{}; - for (const GCode::InfillRange &infill_range : infill_ranges) { - gcode += this->extrude_infill_range( - infill_range.items, *infill_range.region, comment, smooth_path_cache - ); - } - return gcode; -} - -static const auto comment_perimeter = "perimeter"sv; -// Comparing string_view pointer & length for speed. -static inline bool comment_is_perimeter(const std::string_view comment) { - return comment.data() == comment_perimeter.data() && comment.size() == comment_perimeter.size(); -} - -std::string GCodeGenerator::extrude_perimeters( - const Print &print, - const PrintRegion ®ion, - const std::vector &perimeters, - const InstanceToPrint &print_instance, - const GCode::SmoothPathCache &smooth_path_cache -) { - if (!perimeters.empty()) { - m_config.apply(region.config()); - } - - std::string gcode{}; - - for (const ExtrusionEntity *ee : perimeters) { - // Don't reorder, don't flip. - gcode += this->extrude_entity( - {*ee, false}, smooth_path_cache, comment_perimeter, -1. - ); - this->m_travel_obstacle_tracker.mark_extruded( - ee, print_instance.object_layer_to_print_id, print_instance.instance_id - ); - } - return gcode; -}; - -struct IslandExtrusions { - const PrintRegion *region; - std::vector perimeters; - std::vector infill_ranges; -}; - -struct SliceExtrusions { - std::vector common_extrusions; - std::vector ironing_extrusions; -}; - -std::optional get_last_position(const std::vector &infill_ranges) { - if (!infill_ranges.empty() && !infill_ranges.back().items.empty()) { - const ExtrusionEntityReference &last_infill{infill_ranges.back().items.back()}; - return last_infill.flipped() ? last_infill.extrusion_entity().first_point() : - last_infill.extrusion_entity().last_point(); - } - return std::nullopt; -} - -std::optional get_last_position(const ExtrusionEntityReferences &extrusions) { - if (!extrusions.empty()) { - const ExtrusionEntityReference &last_extrusion{extrusions.back()}; - return last_extrusion.flipped() ? last_extrusion.extrusion_entity().first_point() : - last_extrusion.extrusion_entity().last_point(); - } - return std::nullopt; -} - -std::optional get_last_position(const std::vector &perimeters){ - if (!perimeters.empty()) { - auto last_perimeter{static_cast(perimeters.back())}; - if (last_perimeter != nullptr) { - return last_perimeter->seam; - } - } - return std::nullopt; -} - -std::optional get_last_position(const std::vector &slice_extrusions, const bool infill_first) { - if (slice_extrusions.empty()) { - return {}; - } - const SliceExtrusions &last_slice{slice_extrusions.back()}; - if (!last_slice.ironing_extrusions.empty()) { - return get_last_position(slice_extrusions.back().ironing_extrusions); - } - if (last_slice.common_extrusions.empty()) { - return {}; - } - const IslandExtrusions last_island{last_slice.common_extrusions.back()}; - if (infill_first) { - if (!last_island.perimeters.empty()) { - return get_last_position(last_island.perimeters); - } - return get_last_position(last_island.infill_ranges); - } else { - if (!last_island.infill_ranges.empty()) { - return get_last_position(last_island.infill_ranges); - } - return get_last_position(last_island.perimeters); - } -} - -std::vector extract_island_extrusions( - const LayerSlice &lslice, - const Print &print, - const Layer *layer, - const Seams::Placer &seam_placer, - const bool spiral_vase, - const GCode::ExtractEntityPredicate &predicate, - std::optional &previous_position -) { - std::vector result; - for (const LayerIsland &island : lslice.islands) { - const LayerRegion &layerm = *layer->get_region(island.perimeters.region()); - // PrintObjects own the PrintRegions, thus the pointer to PrintRegion would be - // unique to a PrintObject, they would not identify the content of PrintRegion - // accross the whole print uniquely. Translate to a Print specific PrintRegion. - const PrintRegion ®ion = print.get_print_region(layerm.region().print_region_id()); - - const auto infill_predicate = [&](const auto &eec, const auto ®ion) { - return predicate(eec, region) && eec.role() != ExtrusionRole::Ironing; - }; - - result.push_back(IslandExtrusions{®ion}); - IslandExtrusions &island_extrusions{result.back()}; - - island_extrusions.perimeters = GCode::extract_perimeter_extrusions(print, layer, island, predicate); - - if (print.config().infill_first) { - island_extrusions.infill_ranges = GCode::extract_infill_ranges( - print, layer, island, previous_position, infill_predicate - ); - if (const auto last_position = get_last_position(island_extrusions.infill_ranges)) { - previous_position = last_position; - } - - place_seams(layer, seam_placer, island_extrusions.perimeters, previous_position, spiral_vase); - - if (const auto last_position = get_last_position(island_extrusions.perimeters)) { - previous_position = last_position; - } - } else { - place_seams(layer, seam_placer, island_extrusions.perimeters, previous_position, spiral_vase); - - if (const auto last_position = get_last_position(island_extrusions.perimeters)) { - previous_position = last_position; - } - island_extrusions.infill_ranges = {GCode::extract_infill_ranges( - print, layer, island, previous_position, infill_predicate - )}; - if (const auto last_position = get_last_position(island_extrusions.infill_ranges)) { - previous_position = last_position; - } - } - } - return result; -} - -std::vector extract_ironing_extrusions( - const LayerSlice &lslice, - const Print &print, - const Layer *layer, - const GCode::ExtractEntityPredicate &predicate, - std::optional &previous_position -) { - std::vector result; - - for (const LayerIsland &island : lslice.islands) { - const auto ironing_predicate = [&](const auto &eec, const auto ®ion) { - return predicate(eec, region) && eec.role() == ExtrusionRole::Ironing; - }; - - const std::vector ironing_ranges{GCode::extract_infill_ranges( - print, layer, island, previous_position, ironing_predicate - )}; - result.insert( - result.end(), ironing_ranges.begin(), ironing_ranges.end() - ); - - if (const auto last_position = get_last_position(ironing_ranges)) { - previous_position = last_position; - } - } - return result; -} - -std::vector get_slices_extrusions( - const Print &print, - const Layer *layer, - const Seams::Placer &seam_placer, - std::optional previous_position, - const bool spiral_vase, - const GCode::ExtractEntityPredicate &predicate -) { - // Note: ironing. - // FIXME move ironing into the loop above over LayerIslands? - // First Ironing changes extrusion rate quickly, second single ironing may be done over - // multiple perimeter regions. Ironing in a second phase is safer, but it may be less - // efficient. - - std::vector result; - - for (size_t idx : layer->lslice_indices_sorted_by_print_order) { - const LayerSlice &lslice = layer->lslices_ex[idx]; - result.emplace_back(SliceExtrusions{ - extract_island_extrusions( - lslice, print, layer, seam_placer, spiral_vase, predicate, previous_position - ), - extract_ironing_extrusions(lslice, print, layer, predicate, previous_position) - }); - } - return result; -} - -unsigned translate_support_extruder( - const int configured_extruder, - const LayerTools &layer_tools, - const ConfigOptionBools &is_soluable -) { - if (configured_extruder <= 0) { - // Some support will be printed with "don't care" material, preferably non-soluble. - // Is the current extruder assigned a soluble filament? - auto it_nonsoluble = std::find_if(layer_tools.extruders.begin(), layer_tools.extruders.end(), - [&is_soluable](unsigned int extruder_id) { return ! is_soluable.get_at(extruder_id); }); - // There should be a non-soluble extruder available. - assert(it_nonsoluble != layer_tools.extruders.end()); - return it_nonsoluble == layer_tools.extruders.end() ? layer_tools.extruders.front() : *it_nonsoluble; - } else { - return configured_extruder - 1; - } -} - -ExtrusionEntityReferences get_support_extrusions( - const unsigned int extruder_id, - const GCode::ObjectLayerToPrint &layer_to_print, - unsigned int support_extruder, - unsigned int interface_extruder -) { - if (const SupportLayer &support_layer = *layer_to_print.support_layer; - !support_layer.support_fills.entities.empty()) { - ExtrusionRole role = support_layer.support_fills.role(); - bool has_support = role.is_mixed() || role.is_support_base(); - bool has_interface = role.is_mixed() || role.is_support_interface(); - - bool extrude_support = has_support && support_extruder == extruder_id; - bool extrude_interface = has_interface && interface_extruder == extruder_id; - - if (extrude_support || extrude_interface) { - ExtrusionEntitiesPtr entities_cache; - const ExtrusionEntitiesPtr &entities = extrude_support && extrude_interface ? - support_layer.support_fills.entities : - entities_cache; - if (!extrude_support || !extrude_interface) { - auto role = extrude_support ? ExtrusionRole::SupportMaterial : - ExtrusionRole::SupportMaterialInterface; - entities_cache.reserve(support_layer.support_fills.entities.size()); - for (ExtrusionEntity *ee : support_layer.support_fills.entities) - if (ee->role() == role) - entities_cache.emplace_back(ee); - } - return chain_extrusion_references(entities); - } - } - return {}; -} - -std::vector> get_overriden_extrusions( - const Print &print, - const GCode::ObjectsLayerToPrint &layers, - const LayerTools &layer_tools, - const std::vector &instances_to_print, - const Seams::Placer &seam_placer, - const bool spiral_vase, - const unsigned int extruder_id, - std::optional &previous_position -) { - std::vector> result; - - for (const InstanceToPrint &instance : instances_to_print) { - if (const Layer *layer = layers[instance.object_layer_to_print_id].object_layer; layer) { - const auto predicate = [&](const ExtrusionEntityCollection &entity_collection, - const PrintRegion ®ion) { - if (!GCode::is_overriden(entity_collection, layer_tools, instance.instance_id)) { - return false; - } - - if (GCode::get_extruder_id( - entity_collection, layer_tools, region, instance.instance_id - ) != extruder_id) { - return false; - } - return true; - }; - - result.emplace_back(get_slices_extrusions( - print, layer, seam_placer, previous_position, spiral_vase, predicate - )); - previous_position = get_last_position(result.back(), print.config().infill_first); - } - } - return result; -} - -struct NormalExtrusions { - ExtrusionEntityReferences support_extrusions; - std::vector slices_extrusions; -}; - -std::vector get_normal_extrusions( - const Print &print, - const GCode::ObjectsLayerToPrint &layers, - const LayerTools &layer_tools, - const std::vector &instances_to_print, - const Seams::Placer &seam_placer, - const bool spiral_vase, - const unsigned int extruder_id, - std::optional &previous_position -) { - std::vector result; - - for (std::size_t i{0}; i < instances_to_print.size(); ++i) { - const InstanceToPrint &instance{instances_to_print[i]}; - result.emplace_back(); - - if (layers[instance.object_layer_to_print_id].support_layer != nullptr) { - result.back().support_extrusions = get_support_extrusions( - extruder_id, - layers[instance.object_layer_to_print_id], - translate_support_extruder(instance.print_object.config().support_material_extruder.value, layer_tools, print.config().filament_soluble), - translate_support_extruder(instance.print_object.config().support_material_interface_extruder.value, layer_tools, print.config().filament_soluble) - ); - previous_position = get_last_position(result.back().support_extrusions); - } - - if (const Layer *layer = layers[instance.object_layer_to_print_id].object_layer; layer) { - const auto predicate = [&](const ExtrusionEntityCollection &entity_collection, const PrintRegion ®ion){ - if (GCode::is_overriden(entity_collection, layer_tools, instance.instance_id)) { - return false; - } - - if (GCode::get_extruder_id(entity_collection, layer_tools, region, instance.instance_id) != extruder_id) { - return false; - } - return true; - }; - - result.back().slices_extrusions = get_slices_extrusions( - print, - layer, - seam_placer, - previous_position, - spiral_vase, - predicate - ); - previous_position = get_last_position(result.back().slices_extrusions, print.config().infill_first); - } - } - return result; -} - // In sequential mode, process_layer is called once per each object and its copy, // therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object. // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. @@ -3041,12 +2489,15 @@ LayerResult GCodeGenerator::process_layer( skirt.emplace_back(i, print.skirt().entities[i]); } } + + using GCode::ExtrusionOrder::get_last_position; ExtrusionEntitiesPtr brim; if (!m_brim_done) { brim = print.brim().entities; previous_position = get_last_position(brim); } + using GCode::ExtrusionOrder::get_overriden_extrusions; bool is_anything_overridden = layer_tools.wiping_extrusions().is_anything_overridden(); std::vector> overriden_extrusions; if (is_anything_overridden) { @@ -3056,6 +2507,8 @@ LayerResult GCodeGenerator::process_layer( ); } + using GCode::ExtrusionOrder::get_normal_extrusions; + using GCode::ExtrusionOrder::NormalExtrusions; const std::vector normal_extrusions{get_normal_extrusions( print, layers, layer_tools, instances_to_print, this->m_seam_placer, this->m_config.spiral_vase, extruder_id, previous_position @@ -3212,6 +2665,12 @@ LayerResult GCodeGenerator::process_layer( return result; } +static const auto comment_perimeter = "perimeter"sv; +// Comparing string_view pointer & length for speed. +static inline bool comment_is_perimeter(const std::string_view comment) { + return comment.data() == comment_perimeter.data() && comment.size() == comment_perimeter.size(); +} + void GCodeGenerator::process_layer_single_object( std::string &gcode, const InstanceToPrint &print_instance, @@ -3510,6 +2969,62 @@ std::string GCodeGenerator::extrude_skirt( return gcode; } +std::string GCodeGenerator::extrude_infill_range( + const std::vector &sorted_extrusions, + const PrintRegion ®ion, + const std::string &extrusion_name, + const GCode::SmoothPathCache &smooth_path_cache +) { + std::string gcode{}; + if (!sorted_extrusions.empty()) { + this->m_config.apply(region.config()); + + for (const ExtrusionEntityReference &ee : sorted_extrusions) { + gcode += this->extrude_entity(ee, smooth_path_cache, extrusion_name); + } + } + return gcode; +}; + +std::string GCodeGenerator::extrude_infill_ranges( + const std::vector &infill_ranges, + const std::string &comment, + const GCode::SmoothPathCache &smooth_path_cache +) { + std::string gcode{}; + for (const InfillRange &infill_range : infill_ranges) { + gcode += this->extrude_infill_range( + infill_range.items, *infill_range.region, comment, smooth_path_cache + ); + } + return gcode; +} + +std::string GCodeGenerator::extrude_perimeters( + const Print &print, + const PrintRegion ®ion, + const std::vector &perimeters, + const InstanceToPrint &print_instance, + const GCode::SmoothPathCache &smooth_path_cache +) { + if (!perimeters.empty()) { + m_config.apply(region.config()); + } + + std::string gcode{}; + + for (const ExtrusionEntity *ee : perimeters) { + // Don't reorder, don't flip. + gcode += this->extrude_entity( + {*ee, false}, smooth_path_cache, comment_perimeter, -1. + ); + this->m_travel_obstacle_tracker.mark_extruded( + ee, print_instance.object_layer_to_print_id, print_instance.instance_id + ); + } + return gcode; +}; + std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { #ifndef NDEBUG diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 1bf1aad818..3dafeb09b6 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -15,6 +15,7 @@ #ifndef slic3r_GCode_hpp_ #define slic3r_GCode_hpp_ +#include "libslic3r/GCode/ExtrusionOrder.hpp" #include "libslic3r/GCode/ExtrusionProcessor.hpp" #include "JumpPointSearch.hpp" #include "libslic3r.h" @@ -54,7 +55,6 @@ namespace Slic3r { // Forward declarations. class GCodeGenerator; struct WipeTowerData; -struct SliceExtrusions; namespace { struct Item; } struct PrintInstance; @@ -93,18 +93,6 @@ struct LayerResult { }; namespace GCode { -// Object and support extrusions of the same PrintObject at the same print_z. -// public, so that it could be accessed by free helper functions from GCode.cpp -struct ObjectLayerToPrint -{ - ObjectLayerToPrint() : object_layer(nullptr), support_layer(nullptr) {} - const Layer* object_layer; - const SupportLayer* support_layer; - const Layer* layer() const { return (object_layer != nullptr) ? object_layer : support_layer; } - const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; } - coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; } -}; - struct PrintObjectInstance { const PrintObject *print_object = nullptr; @@ -114,25 +102,8 @@ struct PrintObjectInstance bool operator!=(const PrintObjectInstance &other) const { return !(*this == other); } }; -struct InfillRange { - std::vector items; - const PrintRegion *region; -}; - } // namespace GCode -struct InstanceToPrint -{ - InstanceToPrint(size_t object_layer_to_print_id, const PrintObject &print_object, size_t instance_id) : - object_layer_to_print_id(object_layer_to_print_id), print_object(print_object), instance_id(instance_id) {} - - // Index into std::vector, which contains Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances. - const size_t object_layer_to_print_id; - const PrintObject &print_object; - // Instance idx of the copy of a print object. - const size_t instance_id; -}; - class GCodeGenerator { public: @@ -196,11 +167,16 @@ public: static void encode_full_config(const Print& print, std::vector>& config); using ObjectLayerToPrint = GCode::ObjectLayerToPrint; - using ObjectsLayerToPrint = std::vector; + using ObjectsLayerToPrint = GCode::ObjectsLayerToPrint; std::optional last_position; private: + using InstanceToPrint = GCode::InstanceToPrint; + using InfillRange = GCode::ExtrusionOrder::InfillRange; + using SliceExtrusions = GCode::ExtrusionOrder::SliceExtrusions; + using IslandExtrusions = GCode::ExtrusionOrder::IslandExtrusions; + class GCodeOutputStream { public: GCodeOutputStream(FILE *f, GCodeProcessor &processor) : f(f), m_processor(processor) {} @@ -215,7 +191,7 @@ private: bool is_open() const { return f; } bool is_error() const; - + void flush(); void close(); @@ -327,7 +303,7 @@ private: ); std::string extrude_infill_ranges( - const std::vector &infill_ranges, + const std::vector &infill_ranges, const std::string &commment, const GCode::SmoothPathCache &smooth_path_cache ); diff --git a/src/libslic3r/GCode/ExtrusionOrder.cpp b/src/libslic3r/GCode/ExtrusionOrder.cpp new file mode 100644 index 0000000000..af92cb6b2d --- /dev/null +++ b/src/libslic3r/GCode/ExtrusionOrder.cpp @@ -0,0 +1,474 @@ +#include "ExtrusionOrder.hpp" + +namespace Slic3r::GCode::ExtrusionOrder { + +bool is_overriden(const ExtrusionEntityCollection &eec, const LayerTools &layer_tools, const std::size_t instance_id) { + return layer_tools.wiping_extrusions().get_extruder_override(&eec, instance_id) > -1; +} + +int get_extruder_id( + const ExtrusionEntityCollection &eec, + const LayerTools &layer_tools, + const PrintRegion ®ion, + const std::size_t instance_id +) { + if (is_overriden(eec, layer_tools, instance_id)) { + return layer_tools.wiping_extrusions().get_extruder_override(&eec, instance_id); + } + + const int extruder_id = layer_tools.extruder(eec, region); + if (! layer_tools.has_extruder(extruder_id)) { + // Extruder is not in layer_tools - we'll print it by last extruder on this layer (could + // happen e.g. when a wiping object is taller than others - dontcare extruders are + // eradicated from layer_tools) + return layer_tools.extruders.back(); + } + return extruder_id; +} + +ExtrusionEntitiesPtr extract_infill_extrusions( + const Layer *layer, + const PrintRegion ®ion, + const ExtrusionEntityCollection &fills, + LayerExtrusionRanges::const_iterator begin, + LayerExtrusionRanges::const_iterator end, + const ExtractEntityPredicate &predicate +) { + ExtrusionEntitiesPtr result; + for (auto it = begin; it != end; ++ it) { + assert(it->region() == begin->region()); + const LayerExtrusionRange &range{*it}; + for (uint32_t fill_id : range) { + assert(dynamic_cast(fills.entities[fill_id])); + + auto *eec{static_cast(fills.entities[fill_id])}; + if (eec == nullptr || eec->empty() || !predicate(*eec, region)) { + continue; + } + + if (eec->can_reverse()) { + // Flatten the infill collection for better path planning. + for (auto *ee : eec->entities) { + result.emplace_back(ee); + } + } else { + result.emplace_back(eec); + } + } + } + return result; +} + +std::vector extract_perimeter_extrusions( + const Print &print, + const Layer *layer, + const LayerIsland &island, + const ExtractEntityPredicate &predicate +) { + std::vector result; + + const LayerRegion &layerm = *layer->get_region(island.perimeters.region()); + const PrintRegion ®ion = print.get_print_region(layerm.region().print_region_id()); + + for (uint32_t perimeter_id : island.perimeters) { + // Extrusions inside islands are expected to be ordered already. + // Don't reorder them. + assert(dynamic_cast(layerm.perimeters().entities[perimeter_id])); + auto *eec = static_cast(layerm.perimeters().entities[perimeter_id]); + if (eec == nullptr || eec->empty() || !predicate(*eec, region)) { + continue; + } + + for (ExtrusionEntity *ee : *eec) { + result.push_back(ee); + } + } + + return result; +} + +std::vector sort_fill_extrusions(const ExtrusionEntitiesPtr &fills, const Point* start_near) { + if (fills.empty()) { + return {}; + } + std::vector sorted_extrusions; + + for (const ExtrusionEntityReference &fill : chain_extrusion_references(fills, start_near)) { + if (auto *eec = dynamic_cast(&fill.extrusion_entity()); eec) { + for (const ExtrusionEntityReference &ee : chain_extrusion_references(*eec, start_near, fill.flipped())) { + sorted_extrusions.push_back(ee); + } + } else { + sorted_extrusions.push_back(fill); + } + } + return sorted_extrusions; +} + +std::vector extract_infill_ranges( + const Print &print, + const Layer *layer, + const LayerIsland island, + std::optional previous_position, + const ExtractEntityPredicate &predicate +) { + std::vector result; + for (auto it = island.fills.begin(); it != island.fills.end();) { + // Gather range of fill ranges with the same region. + auto it_end = it; + for (++ it_end; it_end != island.fills.end() && it->region() == it_end->region(); ++ it_end) ; + const LayerRegion &layerm = *layer->get_region(it->region()); + // PrintObjects own the PrintRegions, thus the pointer to PrintRegion would be unique to a PrintObject, they would not + // identify the content of PrintRegion accross the whole print uniquely. Translate to a Print specific PrintRegion. + const PrintRegion ®ion = print.get_print_region(layerm.region().print_region_id()); + + const Point* start_near = previous_position ? &(*(previous_position)) : nullptr; + + ExtrusionEntitiesPtr extrusions{extract_infill_extrusions( + layer, + region, + layerm.fills(), + it, + it_end, + predicate + )}; + + const std::vector sorted_extrusions{sort_fill_extrusions(extrusions, start_near)}; + + if (! sorted_extrusions.empty()) { + result.push_back({sorted_extrusions, ®ion}); + previous_position = sorted_extrusions.back().flipped() ? + sorted_extrusions.back().extrusion_entity().first_point() : + sorted_extrusions.back().extrusion_entity().last_point(); + } + it = it_end; + } + return result; +} + +void place_seams( + const Layer *layer, const Seams::Placer &seam_placer, const std::vector &perimeters, std::optional previous_position, const bool spiral_vase +) { + std::vector result; + result.reserve(perimeters.size()); + + for (ExtrusionEntity* perimeter : perimeters) { + auto loop{static_cast(perimeter)}; + + Point seam_point{previous_position ? *previous_position : Point::Zero()}; + if (!spiral_vase && loop != nullptr) { + assert(m_layer != nullptr); + seam_point = seam_placer.place_seam(layer, *loop, seam_point); + loop->seam = seam_point; + } + previous_position = seam_point; + } +} + +std::optional get_last_position(const std::vector &infill_ranges) { + if (!infill_ranges.empty() && !infill_ranges.back().items.empty()) { + const ExtrusionEntityReference &last_infill{infill_ranges.back().items.back()}; + return last_infill.flipped() ? last_infill.extrusion_entity().first_point() : + last_infill.extrusion_entity().last_point(); + } + return std::nullopt; +} + +std::optional get_last_position(const ExtrusionEntityReferences &extrusions) { + if (!extrusions.empty()) { + const ExtrusionEntityReference &last_extrusion{extrusions.back()}; + return last_extrusion.flipped() ? last_extrusion.extrusion_entity().first_point() : + last_extrusion.extrusion_entity().last_point(); + } + return std::nullopt; +} + +std::optional get_last_position(const std::vector &perimeters){ + if (!perimeters.empty()) { + auto last_perimeter{static_cast(perimeters.back())}; + if (last_perimeter != nullptr) { + return last_perimeter->seam; + } + } + return std::nullopt; +} + +std::optional get_last_position(const std::vector &slice_extrusions, const bool infill_first) { + if (slice_extrusions.empty()) { + return {}; + } + const SliceExtrusions &last_slice{slice_extrusions.back()}; + if (!last_slice.ironing_extrusions.empty()) { + return get_last_position(slice_extrusions.back().ironing_extrusions); + } + if (last_slice.common_extrusions.empty()) { + return {}; + } + const IslandExtrusions last_island{last_slice.common_extrusions.back()}; + if (infill_first) { + if (!last_island.perimeters.empty()) { + return get_last_position(last_island.perimeters); + } + return get_last_position(last_island.infill_ranges); + } else { + if (!last_island.infill_ranges.empty()) { + return get_last_position(last_island.infill_ranges); + } + return get_last_position(last_island.perimeters); + } +} + +std::vector extract_island_extrusions( + const LayerSlice &lslice, + const Print &print, + const Layer *layer, + const Seams::Placer &seam_placer, + const bool spiral_vase, + const ExtractEntityPredicate &predicate, + std::optional &previous_position +) { + std::vector result; + for (const LayerIsland &island : lslice.islands) { + const LayerRegion &layerm = *layer->get_region(island.perimeters.region()); + // PrintObjects own the PrintRegions, thus the pointer to PrintRegion would be + // unique to a PrintObject, they would not identify the content of PrintRegion + // accross the whole print uniquely. Translate to a Print specific PrintRegion. + const PrintRegion ®ion = print.get_print_region(layerm.region().print_region_id()); + + const auto infill_predicate = [&](const auto &eec, const auto ®ion) { + return predicate(eec, region) && eec.role() != ExtrusionRole::Ironing; + }; + + result.push_back(IslandExtrusions{®ion}); + IslandExtrusions &island_extrusions{result.back()}; + + island_extrusions.perimeters = extract_perimeter_extrusions(print, layer, island, predicate); + + if (print.config().infill_first) { + island_extrusions.infill_ranges = extract_infill_ranges( + print, layer, island, previous_position, infill_predicate + ); + if (const auto last_position = get_last_position(island_extrusions.infill_ranges)) { + previous_position = last_position; + } + + place_seams(layer, seam_placer, island_extrusions.perimeters, previous_position, spiral_vase); + + if (const auto last_position = get_last_position(island_extrusions.perimeters)) { + previous_position = last_position; + } + } else { + place_seams(layer, seam_placer, island_extrusions.perimeters, previous_position, spiral_vase); + + if (const auto last_position = get_last_position(island_extrusions.perimeters)) { + previous_position = last_position; + } + island_extrusions.infill_ranges = {extract_infill_ranges( + print, layer, island, previous_position, infill_predicate + )}; + if (const auto last_position = get_last_position(island_extrusions.infill_ranges)) { + previous_position = last_position; + } + } + } + return result; +} + +std::vector extract_ironing_extrusions( + const LayerSlice &lslice, + const Print &print, + const Layer *layer, + const ExtractEntityPredicate &predicate, + std::optional &previous_position +) { + std::vector result; + + for (const LayerIsland &island : lslice.islands) { + const auto ironing_predicate = [&](const auto &eec, const auto ®ion) { + return predicate(eec, region) && eec.role() == ExtrusionRole::Ironing; + }; + + const std::vector ironing_ranges{extract_infill_ranges( + print, layer, island, previous_position, ironing_predicate + )}; + result.insert( + result.end(), ironing_ranges.begin(), ironing_ranges.end() + ); + + if (const auto last_position = get_last_position(ironing_ranges)) { + previous_position = last_position; + } + } + return result; +} + +std::vector get_slices_extrusions( + const Print &print, + const Layer *layer, + const Seams::Placer &seam_placer, + std::optional previous_position, + const bool spiral_vase, + const ExtractEntityPredicate &predicate +) { + // Note: ironing. + // FIXME move ironing into the loop above over LayerIslands? + // First Ironing changes extrusion rate quickly, second single ironing may be done over + // multiple perimeter regions. Ironing in a second phase is safer, but it may be less + // efficient. + + std::vector result; + + for (size_t idx : layer->lslice_indices_sorted_by_print_order) { + const LayerSlice &lslice = layer->lslices_ex[idx]; + result.emplace_back(SliceExtrusions{ + extract_island_extrusions( + lslice, print, layer, seam_placer, spiral_vase, predicate, previous_position + ), + extract_ironing_extrusions(lslice, print, layer, predicate, previous_position) + }); + } + return result; +} + +unsigned translate_support_extruder( + const int configured_extruder, + const LayerTools &layer_tools, + const ConfigOptionBools &is_soluable +) { + if (configured_extruder <= 0) { + // Some support will be printed with "don't care" material, preferably non-soluble. + // Is the current extruder assigned a soluble filament? + auto it_nonsoluble = std::find_if(layer_tools.extruders.begin(), layer_tools.extruders.end(), + [&is_soluable](unsigned int extruder_id) { return ! is_soluable.get_at(extruder_id); }); + // There should be a non-soluble extruder available. + assert(it_nonsoluble != layer_tools.extruders.end()); + return it_nonsoluble == layer_tools.extruders.end() ? layer_tools.extruders.front() : *it_nonsoluble; + } else { + return configured_extruder - 1; + } +} + +ExtrusionEntityReferences get_support_extrusions( + const unsigned int extruder_id, + const GCode::ObjectLayerToPrint &layer_to_print, + unsigned int support_extruder, + unsigned int interface_extruder +) { + if (const SupportLayer &support_layer = *layer_to_print.support_layer; + !support_layer.support_fills.entities.empty()) { + ExtrusionRole role = support_layer.support_fills.role(); + bool has_support = role.is_mixed() || role.is_support_base(); + bool has_interface = role.is_mixed() || role.is_support_interface(); + + bool extrude_support = has_support && support_extruder == extruder_id; + bool extrude_interface = has_interface && interface_extruder == extruder_id; + + if (extrude_support || extrude_interface) { + ExtrusionEntitiesPtr entities_cache; + const ExtrusionEntitiesPtr &entities = extrude_support && extrude_interface ? + support_layer.support_fills.entities : + entities_cache; + if (!extrude_support || !extrude_interface) { + auto role = extrude_support ? ExtrusionRole::SupportMaterial : + ExtrusionRole::SupportMaterialInterface; + entities_cache.reserve(support_layer.support_fills.entities.size()); + for (ExtrusionEntity *ee : support_layer.support_fills.entities) + if (ee->role() == role) + entities_cache.emplace_back(ee); + } + return chain_extrusion_references(entities); + } + } + return {}; +} + +std::vector> get_overriden_extrusions( + const Print &print, + const GCode::ObjectsLayerToPrint &layers, + const LayerTools &layer_tools, + const std::vector &instances_to_print, + const Seams::Placer &seam_placer, + const bool spiral_vase, + const unsigned int extruder_id, + std::optional &previous_position +) { + std::vector> result; + + for (const InstanceToPrint &instance : instances_to_print) { + if (const Layer *layer = layers[instance.object_layer_to_print_id].object_layer; layer) { + const auto predicate = [&](const ExtrusionEntityCollection &entity_collection, + const PrintRegion ®ion) { + if (!is_overriden(entity_collection, layer_tools, instance.instance_id)) { + return false; + } + + if (get_extruder_id( + entity_collection, layer_tools, region, instance.instance_id + ) != extruder_id) { + return false; + } + return true; + }; + + result.emplace_back(get_slices_extrusions( + print, layer, seam_placer, previous_position, spiral_vase, predicate + )); + previous_position = get_last_position(result.back(), print.config().infill_first); + } + } + return result; +} + +std::vector get_normal_extrusions( + const Print &print, + const GCode::ObjectsLayerToPrint &layers, + const LayerTools &layer_tools, + const std::vector &instances_to_print, + const Seams::Placer &seam_placer, + const bool spiral_vase, + const unsigned int extruder_id, + std::optional &previous_position +) { + std::vector result; + + for (std::size_t i{0}; i < instances_to_print.size(); ++i) { + const InstanceToPrint &instance{instances_to_print[i]}; + result.emplace_back(); + + if (layers[instance.object_layer_to_print_id].support_layer != nullptr) { + result.back().support_extrusions = get_support_extrusions( + extruder_id, + layers[instance.object_layer_to_print_id], + translate_support_extruder(instance.print_object.config().support_material_extruder.value, layer_tools, print.config().filament_soluble), + translate_support_extruder(instance.print_object.config().support_material_interface_extruder.value, layer_tools, print.config().filament_soluble) + ); + previous_position = get_last_position(result.back().support_extrusions); + } + + if (const Layer *layer = layers[instance.object_layer_to_print_id].object_layer; layer) { + const auto predicate = [&](const ExtrusionEntityCollection &entity_collection, const PrintRegion ®ion){ + if (is_overriden(entity_collection, layer_tools, instance.instance_id)) { + return false; + } + + if (get_extruder_id(entity_collection, layer_tools, region, instance.instance_id) != extruder_id) { + return false; + } + return true; + }; + + result.back().slices_extrusions = get_slices_extrusions( + print, + layer, + seam_placer, + previous_position, + spiral_vase, + predicate + ); + previous_position = get_last_position(result.back().slices_extrusions, print.config().infill_first); + } + } + return result; +} + +} diff --git a/src/libslic3r/GCode/ExtrusionOrder.hpp b/src/libslic3r/GCode/ExtrusionOrder.hpp new file mode 100644 index 0000000000..7578f40495 --- /dev/null +++ b/src/libslic3r/GCode/ExtrusionOrder.hpp @@ -0,0 +1,187 @@ +#ifndef slic3r_GCode_ExtrusionOrder_hpp_ +#define slic3r_GCode_ExtrusionOrder_hpp_ + +#include + +#include "libslic3r/ExtrusionEntity.hpp" +#include "libslic3r/GCode/SeamPlacer.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/ShortestPath.hpp" + +namespace Slic3r::GCode { +// Object and support extrusions of the same PrintObject at the same print_z. +// public, so that it could be accessed by free helper functions from GCode.cpp +struct ObjectLayerToPrint +{ + ObjectLayerToPrint() : object_layer(nullptr), support_layer(nullptr) {} + const Layer *object_layer; + const SupportLayer *support_layer; + const Layer *layer() const { return (object_layer != nullptr) ? object_layer : support_layer; } + const PrintObject *object() const { + return (this->layer() != nullptr) ? this->layer()->object() : nullptr; + } + coordf_t print_z() const { + return (object_layer != nullptr && support_layer != nullptr) ? + 0.5 * (object_layer->print_z + support_layer->print_z) : + this->layer()->print_z; + } +}; + +using ObjectsLayerToPrint = std::vector; + +struct InstanceToPrint +{ + InstanceToPrint( + size_t object_layer_to_print_id, const PrintObject &print_object, size_t instance_id + ) + : object_layer_to_print_id(object_layer_to_print_id) + , print_object(print_object) + , instance_id(instance_id) {} + + // Index into std::vector, which contains Object and Support layers for the + // current print_z, collected for a single object, or for possibly multiple objects with + // multiple instances. + const size_t object_layer_to_print_id; + const PrintObject &print_object; + // Instance idx of the copy of a print object. + const size_t instance_id; +}; +} // namespace Slic3r::GCode + +namespace Slic3r::GCode::ExtrusionOrder { + +struct InfillRange { + std::vector items; + const PrintRegion *region; +}; + +struct IslandExtrusions { + const PrintRegion *region; + std::vector perimeters; + std::vector infill_ranges; +}; + +struct SliceExtrusions { + std::vector common_extrusions; + std::vector ironing_extrusions; +}; + +struct NormalExtrusions { + ExtrusionEntityReferences support_extrusions; + std::vector slices_extrusions; +}; + +bool is_overriden(const ExtrusionEntityCollection &eec, const LayerTools &layer_tools, const std::size_t instance_id); + +int get_extruder_id( + const ExtrusionEntityCollection &eec, + const LayerTools &layer_tools, + const PrintRegion ®ion, + const std::size_t instance_id +); + +using ExtractEntityPredicate = std::function; + +ExtrusionEntitiesPtr extract_infill_extrusions( + const Layer *layer, + const PrintRegion ®ion, + const ExtrusionEntityCollection &fills, + LayerExtrusionRanges::const_iterator begin, + LayerExtrusionRanges::const_iterator end, + const ExtractEntityPredicate &predicate +); + +std::vector extract_perimeter_extrusions( + const Print &print, + const Layer *layer, + const LayerIsland &island, + const ExtractEntityPredicate &predicate +); + +std::vector sort_fill_extrusions(const ExtrusionEntitiesPtr &fills, const Point* start_near); + +std::vector extract_infill_ranges( + const Print &print, + const Layer *layer, + const LayerIsland island, + std::optional previous_position, + const ExtractEntityPredicate &predicate +); + +void place_seams( + const Layer *layer, const Seams::Placer &seam_placer, const std::vector &perimeters, std::optional previous_position, const bool spiral_vase +); + +std::optional get_last_position(const std::vector &infill_ranges); + +std::optional get_last_position(const ExtrusionEntityReferences &extrusions); + +std::optional get_last_position(const std::vector &perimeters); + +std::optional get_last_position(const std::vector &slice_extrusions, const bool infill_first); + +std::vector extract_island_extrusions( + const LayerSlice &lslice, + const Print &print, + const Layer *layer, + const Seams::Placer &seam_placer, + const bool spiral_vase, + const ExtractEntityPredicate &predicate, + std::optional &previous_position +); + +std::vector extract_ironing_extrusions( + const LayerSlice &lslice, + const Print &print, + const Layer *layer, + const ExtractEntityPredicate &predicate, + std::optional &previous_position +); + +std::vector get_slices_extrusions( + const Print &print, + const Layer *layer, + const Seams::Placer &seam_placer, + std::optional previous_position, + const bool spiral_vase, + const ExtractEntityPredicate &predicate +); + +unsigned translate_support_extruder( + const int configured_extruder, + const LayerTools &layer_tools, + const ConfigOptionBools &is_soluable +); + +ExtrusionEntityReferences get_support_extrusions( + const unsigned int extruder_id, + const GCode::ObjectLayerToPrint &layer_to_print, + unsigned int support_extruder, + unsigned int interface_extruder +); + +std::vector> get_overriden_extrusions( + const Print &print, + const GCode::ObjectsLayerToPrint &layers, + const LayerTools &layer_tools, + const std::vector &instances_to_print, + const Seams::Placer &seam_placer, + const bool spiral_vase, + const unsigned int extruder_id, + std::optional &previous_position +); + +std::vector get_normal_extrusions( + const Print &print, + const GCode::ObjectsLayerToPrint &layers, + const LayerTools &layer_tools, + const std::vector &instances_to_print, + const Seams::Placer &seam_placer, + const bool spiral_vase, + const unsigned int extruder_id, + std::optional &previous_position +); + +} + +#endif // slic3r_GCode_ExtrusionOrder_hpp_