mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 06:35:58 +08:00
Separate extrusion order utilities to a file.
This commit is contained in:
parent
09453c3a1e
commit
02c615999b
@ -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
|
||||
|
@ -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 ®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<bool(const ExtrusionEntityCollection&, const PrintRegion&)>;
|
||||
|
||||
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<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 ®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<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 ®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<ExtrusionEntityReference> 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<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 ®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<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 ®ion,
|
||||
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 ®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<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 ®ion) {
|
||||
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 ®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<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 ®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<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 ®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<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 ®ion,
|
||||
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
|
||||
|
@ -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
|
||||
);
|
||||
|
474
src/libslic3r/GCode/ExtrusionOrder.cpp
Normal file
474
src/libslic3r/GCode/ExtrusionOrder.cpp
Normal 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 ®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<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 ®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<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 ®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<ExtrusionEntityReference> 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<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 ®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<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 ®ion) {
|
||||
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 ®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<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 ®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;
|
||||
}
|
||||
|
||||
}
|
187
src/libslic3r/GCode/ExtrusionOrder.hpp
Normal file
187
src/libslic3r/GCode/ExtrusionOrder.hpp
Normal 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 ®ion,
|
||||
const std::size_t instance_id
|
||||
);
|
||||
|
||||
using ExtractEntityPredicate = std::function<bool(const ExtrusionEntityCollection&, const PrintRegion&)>;
|
||||
|
||||
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<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_
|
Loading…
x
Reference in New Issue
Block a user