Separate extrusion order utilities to a file.

This commit is contained in:
Martin Šach 2024-05-17 13:00:45 +02:00 committed by Lukas Matena
parent 09453c3a1e
commit 02c615999b
5 changed files with 740 additions and 586 deletions

View File

@ -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

View File

@ -1961,7 +1961,7 @@ void GCodeGenerator::_print_first_layer_extruder_temperatures(GCodeOutputStream
}
}
std::vector<InstanceToPrint> GCodeGenerator::sort_print_object_instances(
std::vector<GCode::InstanceToPrint> GCodeGenerator::sort_print_object_instances(
const std::vector<ObjectLayerToPrint> &object_layers,
// Ordering must be defined for normal (non-sequential print).
const std::vector<const PrintInstance*> *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 &region,
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<bool(const ExtrusionEntityCollection&, const PrintRegion&)>;
ExtrusionEntitiesPtr extract_infill_extrusions(
const Layer *layer,
const PrintRegion &region,
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<ExtrusionEntityCollection*>(fills.entities[fill_id]));
auto *eec{static_cast<ExtrusionEntityCollection*>(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<ExtrusionEntity *> extract_perimeter_extrusions(
const Print &print,
const Layer *layer,
const LayerIsland &island,
const ExtractEntityPredicate &predicate
) {
std::vector<ExtrusionEntity *> result;
const LayerRegion &layerm = *layer->get_region(island.perimeters.region());
const PrintRegion &region = 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<ExtrusionEntityCollection*>(layerm.perimeters().entities[perimeter_id]));
auto *eec = static_cast<ExtrusionEntityCollection*>(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<ExtrusionEntityReference> sort_fill_extrusions(const ExtrusionEntitiesPtr &fills, const Point* start_near) {
if (fills.empty()) {
return {};
}
std::vector<ExtrusionEntityReference> sorted_extrusions;
for (const ExtrusionEntityReference &fill : chain_extrusion_references(fills, start_near)) {
if (auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(&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<InfillRange> extract_infill_ranges(
const Print &print,
const Layer *layer,
const LayerIsland island,
std::optional<Point> previous_position,
const ExtractEntityPredicate &predicate
) {
std::vector<InfillRange> 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 &region = 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<ExtrusionEntityReference> sorted_extrusions{sort_fill_extrusions(extrusions, start_near)};
if (! sorted_extrusions.empty()) {
result.push_back({sorted_extrusions, &region});
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<ExtrusionEntity *> &perimeters, std::optional<Point> previous_position, const bool spiral_vase
) {
std::vector<const ExtrusionEntity *> result;
result.reserve(perimeters.size());
for (ExtrusionEntity* perimeter : perimeters) {
auto loop{static_cast<ExtrusionLoop *>(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<ExtrusionEntityReference> &sorted_extrusions,
const PrintRegion &region,
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<GCode::InfillRange> &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 &region,
const std::vector<ExtrusionEntity *> &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<ExtrusionEntity *> perimeters;
std::vector<GCode::InfillRange> infill_ranges;
};
struct SliceExtrusions {
std::vector<IslandExtrusions> common_extrusions;
std::vector<GCode::InfillRange> ironing_extrusions;
};
std::optional<Point> get_last_position(const std::vector<GCode::InfillRange> &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<Point> 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<Point> get_last_position(const std::vector<ExtrusionEntity *> &perimeters){
if (!perimeters.empty()) {
auto last_perimeter{static_cast<const ExtrusionLoop *>(perimeters.back())};
if (last_perimeter != nullptr) {
return last_perimeter->seam;
}
}
return std::nullopt;
}
std::optional<Point> get_last_position(const std::vector<SliceExtrusions> &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<IslandExtrusions> 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<Point> &previous_position
) {
std::vector<IslandExtrusions> 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 &region = print.get_print_region(layerm.region().print_region_id());
const auto infill_predicate = [&](const auto &eec, const auto &region) {
return predicate(eec, region) && eec.role() != ExtrusionRole::Ironing;
};
result.push_back(IslandExtrusions{&region});
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<GCode::InfillRange> extract_ironing_extrusions(
const LayerSlice &lslice,
const Print &print,
const Layer *layer,
const GCode::ExtractEntityPredicate &predicate,
std::optional<Point> &previous_position
) {
std::vector<GCode::InfillRange> result;
for (const LayerIsland &island : lslice.islands) {
const auto ironing_predicate = [&](const auto &eec, const auto &region) {
return predicate(eec, region) && eec.role() == ExtrusionRole::Ironing;
};
const std::vector<GCode::InfillRange> 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<SliceExtrusions> get_slices_extrusions(
const Print &print,
const Layer *layer,
const Seams::Placer &seam_placer,
std::optional<Point> 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<SliceExtrusions> 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<std::vector<SliceExtrusions>> get_overriden_extrusions(
const Print &print,
const GCode::ObjectsLayerToPrint &layers,
const LayerTools &layer_tools,
const std::vector<InstanceToPrint> &instances_to_print,
const Seams::Placer &seam_placer,
const bool spiral_vase,
const unsigned int extruder_id,
std::optional<Point> &previous_position
) {
std::vector<std::vector<SliceExtrusions>> 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 &region) {
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<SliceExtrusions> slices_extrusions;
};
std::vector<NormalExtrusions> get_normal_extrusions(
const Print &print,
const GCode::ObjectsLayerToPrint &layers,
const LayerTools &layer_tools,
const std::vector<InstanceToPrint> &instances_to_print,
const Seams::Placer &seam_placer,
const bool spiral_vase,
const unsigned int extruder_id,
std::optional<Point> &previous_position
) {
std::vector<NormalExtrusions> 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 &region){
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<std::vector<SliceExtrusions>> 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<NormalExtrusions> 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<ExtrusionEntityReference> &sorted_extrusions,
const PrintRegion &region,
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<InfillRange> &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 &region,
const std::vector<ExtrusionEntity *> &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

View File

@ -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<ExtrusionEntityReference> 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<ObjectLayerToPrint>, 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<std::pair<std::string, std::string>>& config);
using ObjectLayerToPrint = GCode::ObjectLayerToPrint;
using ObjectsLayerToPrint = std::vector<GCode::ObjectLayerToPrint>;
using ObjectsLayerToPrint = GCode::ObjectsLayerToPrint;
std::optional<Point> 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<GCode::InfillRange> &infill_ranges,
const std::vector<InfillRange> &infill_ranges,
const std::string &commment,
const GCode::SmoothPathCache &smooth_path_cache
);

View File

@ -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 &region,
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 &region,
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<ExtrusionEntityCollection*>(fills.entities[fill_id]));
auto *eec{static_cast<ExtrusionEntityCollection*>(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<ExtrusionEntity *> extract_perimeter_extrusions(
const Print &print,
const Layer *layer,
const LayerIsland &island,
const ExtractEntityPredicate &predicate
) {
std::vector<ExtrusionEntity *> result;
const LayerRegion &layerm = *layer->get_region(island.perimeters.region());
const PrintRegion &region = 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<ExtrusionEntityCollection*>(layerm.perimeters().entities[perimeter_id]));
auto *eec = static_cast<ExtrusionEntityCollection*>(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<ExtrusionEntityReference> sort_fill_extrusions(const ExtrusionEntitiesPtr &fills, const Point* start_near) {
if (fills.empty()) {
return {};
}
std::vector<ExtrusionEntityReference> sorted_extrusions;
for (const ExtrusionEntityReference &fill : chain_extrusion_references(fills, start_near)) {
if (auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(&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<InfillRange> extract_infill_ranges(
const Print &print,
const Layer *layer,
const LayerIsland island,
std::optional<Point> previous_position,
const ExtractEntityPredicate &predicate
) {
std::vector<InfillRange> 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 &region = 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<ExtrusionEntityReference> sorted_extrusions{sort_fill_extrusions(extrusions, start_near)};
if (! sorted_extrusions.empty()) {
result.push_back({sorted_extrusions, &region});
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<ExtrusionEntity *> &perimeters, std::optional<Point> previous_position, const bool spiral_vase
) {
std::vector<const ExtrusionEntity *> result;
result.reserve(perimeters.size());
for (ExtrusionEntity* perimeter : perimeters) {
auto loop{static_cast<ExtrusionLoop *>(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<Point> get_last_position(const std::vector<InfillRange> &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<Point> 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<Point> get_last_position(const std::vector<ExtrusionEntity *> &perimeters){
if (!perimeters.empty()) {
auto last_perimeter{static_cast<const ExtrusionLoop *>(perimeters.back())};
if (last_perimeter != nullptr) {
return last_perimeter->seam;
}
}
return std::nullopt;
}
std::optional<Point> get_last_position(const std::vector<SliceExtrusions> &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<IslandExtrusions> 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<Point> &previous_position
) {
std::vector<IslandExtrusions> 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 &region = print.get_print_region(layerm.region().print_region_id());
const auto infill_predicate = [&](const auto &eec, const auto &region) {
return predicate(eec, region) && eec.role() != ExtrusionRole::Ironing;
};
result.push_back(IslandExtrusions{&region});
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<InfillRange> extract_ironing_extrusions(
const LayerSlice &lslice,
const Print &print,
const Layer *layer,
const ExtractEntityPredicate &predicate,
std::optional<Point> &previous_position
) {
std::vector<InfillRange> result;
for (const LayerIsland &island : lslice.islands) {
const auto ironing_predicate = [&](const auto &eec, const auto &region) {
return predicate(eec, region) && eec.role() == ExtrusionRole::Ironing;
};
const std::vector<InfillRange> 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<SliceExtrusions> get_slices_extrusions(
const Print &print,
const Layer *layer,
const Seams::Placer &seam_placer,
std::optional<Point> 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<SliceExtrusions> 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<std::vector<SliceExtrusions>> get_overriden_extrusions(
const Print &print,
const GCode::ObjectsLayerToPrint &layers,
const LayerTools &layer_tools,
const std::vector<InstanceToPrint> &instances_to_print,
const Seams::Placer &seam_placer,
const bool spiral_vase,
const unsigned int extruder_id,
std::optional<Point> &previous_position
) {
std::vector<std::vector<SliceExtrusions>> 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 &region) {
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<NormalExtrusions> get_normal_extrusions(
const Print &print,
const GCode::ObjectsLayerToPrint &layers,
const LayerTools &layer_tools,
const std::vector<InstanceToPrint> &instances_to_print,
const Seams::Placer &seam_placer,
const bool spiral_vase,
const unsigned int extruder_id,
std::optional<Point> &previous_position
) {
std::vector<NormalExtrusions> 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 &region){
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;
}
}

View File

@ -0,0 +1,187 @@
#ifndef slic3r_GCode_ExtrusionOrder_hpp_
#define slic3r_GCode_ExtrusionOrder_hpp_
#include <vector>
#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<GCode::ObjectLayerToPrint>;
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<ObjectLayerToPrint>, 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<ExtrusionEntityReference> items;
const PrintRegion *region;
};
struct IslandExtrusions {
const PrintRegion *region;
std::vector<ExtrusionEntity *> perimeters;
std::vector<InfillRange> infill_ranges;
};
struct SliceExtrusions {
std::vector<IslandExtrusions> common_extrusions;
std::vector<InfillRange> ironing_extrusions;
};
struct NormalExtrusions {
ExtrusionEntityReferences support_extrusions;
std::vector<SliceExtrusions> 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 &region,
const std::size_t instance_id
);
using ExtractEntityPredicate = std::function<bool(const ExtrusionEntityCollection&, const PrintRegion&)>;
ExtrusionEntitiesPtr extract_infill_extrusions(
const Layer *layer,
const PrintRegion &region,
const ExtrusionEntityCollection &fills,
LayerExtrusionRanges::const_iterator begin,
LayerExtrusionRanges::const_iterator end,
const ExtractEntityPredicate &predicate
);
std::vector<ExtrusionEntity *> extract_perimeter_extrusions(
const Print &print,
const Layer *layer,
const LayerIsland &island,
const ExtractEntityPredicate &predicate
);
std::vector<ExtrusionEntityReference> sort_fill_extrusions(const ExtrusionEntitiesPtr &fills, const Point* start_near);
std::vector<InfillRange> extract_infill_ranges(
const Print &print,
const Layer *layer,
const LayerIsland island,
std::optional<Point> previous_position,
const ExtractEntityPredicate &predicate
);
void place_seams(
const Layer *layer, const Seams::Placer &seam_placer, const std::vector<ExtrusionEntity *> &perimeters, std::optional<Point> previous_position, const bool spiral_vase
);
std::optional<Point> get_last_position(const std::vector<InfillRange> &infill_ranges);
std::optional<Point> get_last_position(const ExtrusionEntityReferences &extrusions);
std::optional<Point> get_last_position(const std::vector<ExtrusionEntity *> &perimeters);
std::optional<Point> get_last_position(const std::vector<SliceExtrusions> &slice_extrusions, const bool infill_first);
std::vector<IslandExtrusions> 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<Point> &previous_position
);
std::vector<InfillRange> extract_ironing_extrusions(
const LayerSlice &lslice,
const Print &print,
const Layer *layer,
const ExtractEntityPredicate &predicate,
std::optional<Point> &previous_position
);
std::vector<SliceExtrusions> get_slices_extrusions(
const Print &print,
const Layer *layer,
const Seams::Placer &seam_placer,
std::optional<Point> 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<std::vector<SliceExtrusions>> get_overriden_extrusions(
const Print &print,
const GCode::ObjectsLayerToPrint &layers,
const LayerTools &layer_tools,
const std::vector<InstanceToPrint> &instances_to_print,
const Seams::Placer &seam_placer,
const bool spiral_vase,
const unsigned int extruder_id,
std::optional<Point> &previous_position
);
std::vector<NormalExtrusions> get_normal_extrusions(
const Print &print,
const GCode::ObjectsLayerToPrint &layers,
const LayerTools &layer_tools,
const std::vector<InstanceToPrint> &instances_to_print,
const Seams::Placer &seam_placer,
const bool spiral_vase,
const unsigned int extruder_id,
std::optional<Point> &previous_position
);
}
#endif // slic3r_GCode_ExtrusionOrder_hpp_