mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-30 15:12:01 +08:00
756 lines
31 KiB
C++
756 lines
31 KiB
C++
#include "ExtrusionOrder.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cinttypes>
|
|
|
|
#include "libslic3r/GCode/SmoothPath.hpp"
|
|
#include "libslic3r/ShortestPath.hpp"
|
|
#include "libslic3r/Config.hpp"
|
|
#include "libslic3r/ExtrusionEntity.hpp"
|
|
#include "libslic3r/ExtrusionEntityCollection.hpp"
|
|
#include "libslic3r/ExtrusionRole.hpp"
|
|
#include "libslic3r/GCode/WipeTowerIntegration.hpp"
|
|
#include "libslic3r/Geometry/ArcWelder.hpp"
|
|
#include "libslic3r/LayerRegion.hpp"
|
|
#include "libslic3r/Print.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;
|
|
}
|
|
|
|
Point get_gcode_point(const InstancePoint &point, const Point &offset) {
|
|
return point.local_point + offset;
|
|
}
|
|
|
|
InstancePoint get_instance_point(const Point &point, const Point &offset) {
|
|
return {point - offset};
|
|
}
|
|
|
|
std::optional<Point> get_gcode_point(const std::optional<InstancePoint> &point, const Point &offset) {
|
|
if (point) {
|
|
return get_gcode_point(*point, offset);
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<InstancePoint> get_instance_point(const std::optional<Point> &point, const Point &offset) {
|
|
if (point) {
|
|
return get_instance_point(*point, offset);
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
using ExtractEntityPredicate = std::function<bool(const ExtrusionEntityCollection&, const PrintRegion&)>;
|
|
|
|
ExtrusionEntitiesPtr extract_infill_extrusions(
|
|
const PrintRegion ®ion,
|
|
const ExtrusionEntityCollection &fills,
|
|
const LayerExtrusionRanges::const_iterator& begin,
|
|
const LayerExtrusionRanges::const_iterator& end,
|
|
const ExtractEntityPredicate &should_pick_extrusion
|
|
) {
|
|
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() || !should_pick_extrusion(*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<Perimeter> extract_perimeter_extrusions(
|
|
const Print &print,
|
|
const Layer &layer,
|
|
const LayerIsland &island,
|
|
const ExtractEntityPredicate &should_pick_extrusion,
|
|
const unsigned extruder_id,
|
|
const Point &offset,
|
|
std::optional<Point> &previous_position,
|
|
const PathSmoothingFunction &smooth_path
|
|
) {
|
|
std::vector<Perimeter> 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() || !should_pick_extrusion(*eec, region)) {
|
|
continue;
|
|
}
|
|
|
|
for (ExtrusionEntity *ee : *eec) {
|
|
if (ee != nullptr) {
|
|
std::optional<InstancePoint> last_position{get_instance_point(previous_position, offset)};
|
|
bool reverse_loop{false};
|
|
if (auto loop = dynamic_cast<const ExtrusionLoop *>(ee)) {
|
|
const bool is_hole = loop->is_clockwise();
|
|
reverse_loop = print.config().prefer_clockwise_movements ? !is_hole : is_hole;
|
|
}
|
|
auto [path, wipe_offset]{smooth_path(&layer, ®ion, ExtrusionEntityReference{*ee, reverse_loop}, extruder_id, last_position)};
|
|
previous_position = get_gcode_point(last_position, offset);
|
|
if (!path.empty()) {
|
|
result.push_back(Perimeter{std::move(path), reverse_loop, ee, wipe_offset});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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,
|
|
const Point &offset,
|
|
std::optional<Point> &previous_position,
|
|
const ExtractEntityPredicate &should_pick_extrusion,
|
|
const PathSmoothingFunction &smooth_path,
|
|
const unsigned extruder_id
|
|
) {
|
|
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());
|
|
|
|
ExtrusionEntitiesPtr extrusions{extract_infill_extrusions(
|
|
region,
|
|
layerm.fills(),
|
|
it,
|
|
it_end,
|
|
should_pick_extrusion
|
|
)};
|
|
|
|
const std::optional<InstancePoint> previous_instance_point{get_instance_point(previous_position, offset)};
|
|
const Point* start_near{previous_instance_point ? &(previous_instance_point->local_point) : nullptr};
|
|
const ExtrusionEntityReferences sorted_extrusions{sort_fill_extrusions(extrusions, start_near)};
|
|
|
|
std::vector<SmoothPath> paths;
|
|
for (const ExtrusionEntityReference &extrusion_reference : sorted_extrusions) {
|
|
std::optional<InstancePoint> last_position{get_instance_point(previous_position, offset)};
|
|
auto [path, _]{smooth_path(&layer, ®ion, extrusion_reference, extruder_id, last_position)};
|
|
if (!path.empty()) {
|
|
paths.push_back(std::move(path));
|
|
}
|
|
previous_position = get_gcode_point(last_position, offset);
|
|
}
|
|
if (!paths.empty()) {
|
|
result.push_back({std::move(paths), ®ion});
|
|
}
|
|
it = it_end;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<IslandExtrusions> extract_island_extrusions(
|
|
const LayerSlice &lslice,
|
|
const Print &print,
|
|
const Layer &layer,
|
|
const ExtractEntityPredicate &should_pick_extrusion,
|
|
const PathSmoothingFunction &smooth_path,
|
|
const Point &offset,
|
|
const unsigned extruder_id,
|
|
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 should_pick_infill = [&should_pick_extrusion](const ExtrusionEntityCollection &eec, const PrintRegion ®ion) {
|
|
return should_pick_extrusion(eec, region) && eec.role() != ExtrusionRole::Ironing;
|
|
};
|
|
|
|
result.push_back(IslandExtrusions{®ion});
|
|
IslandExtrusions &island_extrusions{result.back()};
|
|
island_extrusions.infill_first = print.config().infill_first;
|
|
|
|
if (print.config().infill_first) {
|
|
island_extrusions.infill_ranges = extract_infill_ranges(
|
|
print, layer, island, offset, previous_position, should_pick_infill, smooth_path, extruder_id
|
|
);
|
|
|
|
island_extrusions.perimeters = extract_perimeter_extrusions(print, layer, island, should_pick_extrusion, extruder_id, offset, previous_position, smooth_path);
|
|
} else {
|
|
island_extrusions.perimeters = extract_perimeter_extrusions(print, layer, island, should_pick_extrusion, extruder_id, offset, previous_position, smooth_path);
|
|
|
|
island_extrusions.infill_ranges = {extract_infill_ranges(
|
|
print, layer, island, offset, previous_position, should_pick_infill, smooth_path, extruder_id
|
|
)};
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<InfillRange> extract_ironing_extrusions(
|
|
const LayerSlice &lslice,
|
|
const Print &print,
|
|
const Layer &layer,
|
|
const ExtractEntityPredicate &should_pick_extrusion,
|
|
const PathSmoothingFunction &smooth_path,
|
|
const Point &offset,
|
|
const unsigned extruder_id,
|
|
std::optional<Point> &previous_position
|
|
) {
|
|
std::vector<InfillRange> result;
|
|
|
|
for (const LayerIsland &island : lslice.islands) {
|
|
const auto should_pick_ironing = [&should_pick_extrusion](const auto &eec, const auto ®ion) {
|
|
return should_pick_extrusion(eec, region) && eec.role() == ExtrusionRole::Ironing;
|
|
};
|
|
|
|
const std::vector<InfillRange> ironing_ranges{extract_infill_ranges(
|
|
print, layer, island, offset, previous_position, should_pick_ironing, smooth_path, extruder_id
|
|
)};
|
|
result.insert(
|
|
result.end(), ironing_ranges.begin(), ironing_ranges.end()
|
|
);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<SliceExtrusions> get_slices_extrusions(
|
|
const Print &print,
|
|
const Layer &layer,
|
|
const ExtractEntityPredicate &should_pick_extrusion,
|
|
const PathSmoothingFunction &smooth_path,
|
|
const Point &offset,
|
|
const unsigned extruder_id,
|
|
std::optional<Point> &previous_position
|
|
) {
|
|
// 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];
|
|
std::vector<IslandExtrusions> island_extrusions{extract_island_extrusions(
|
|
lslice, print, layer, should_pick_extrusion, smooth_path, offset, extruder_id, previous_position
|
|
)};
|
|
std::vector<InfillRange> ironing_extrusions{extract_ironing_extrusions(
|
|
lslice, print, layer, should_pick_extrusion, smooth_path, offset, extruder_id, previous_position
|
|
)};
|
|
if (!island_extrusions.empty() || !ironing_extrusions.empty()) {
|
|
result.emplace_back(
|
|
SliceExtrusions{std::move(island_extrusions), std::move(ironing_extrusions)}
|
|
);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
std::vector<SupportPath> get_support_extrusions(
|
|
const unsigned int extruder_id,
|
|
const GCode::ObjectLayerToPrint &layer_to_print,
|
|
unsigned int support_extruder,
|
|
unsigned int interface_extruder,
|
|
const PathSmoothingFunction &smooth_path,
|
|
std::optional<Point> &previous_position
|
|
) {
|
|
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);
|
|
}
|
|
std::vector<SupportPath> paths;
|
|
for (const ExtrusionEntityReference &entity_reference : chain_extrusion_references(entities)) {
|
|
auto collection{dynamic_cast<const ExtrusionEntityCollection *>(&entity_reference.extrusion_entity())};
|
|
const bool is_interface{entity_reference.extrusion_entity().role() != ExtrusionRole::SupportMaterial};
|
|
if (collection != nullptr) {
|
|
for (const ExtrusionEntity * sub_entity : *collection) {
|
|
std::optional<InstancePoint> last_position{get_instance_point(previous_position, {0, 0})};
|
|
auto [path, _]{smooth_path(nullptr, nullptr, {*sub_entity, entity_reference.flipped()}, extruder_id, last_position)};
|
|
if (!path.empty()) {
|
|
paths.push_back({std::move(path), is_interface});
|
|
}
|
|
previous_position = get_gcode_point(last_position, {0, 0});
|
|
}
|
|
} else {
|
|
std::optional<InstancePoint> last_position{get_instance_point(previous_position, {0, 0})};
|
|
auto [path, _]{smooth_path(nullptr, nullptr, entity_reference, extruder_id, last_position)};
|
|
if (!path.empty()) {
|
|
paths.push_back({std::move(path), is_interface});
|
|
}
|
|
previous_position = get_gcode_point(last_position, {0, 0});
|
|
}
|
|
}
|
|
return paths;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::vector<OverridenExtrusions> get_overriden_extrusions(
|
|
const Print &print,
|
|
const GCode::ObjectsLayerToPrint &layers,
|
|
const LayerTools &layer_tools,
|
|
const std::vector<InstanceToPrint> &instances_to_print,
|
|
const unsigned int extruder_id,
|
|
const PathSmoothingFunction &smooth_path,
|
|
std::optional<Point> &previous_position
|
|
) {
|
|
std::vector<OverridenExtrusions> result;
|
|
|
|
for (const InstanceToPrint &instance : instances_to_print) {
|
|
if (const Layer *layer = layers[instance.object_layer_to_print_id].object_layer; layer) {
|
|
const auto should_pick_extrusion = [&layer_tools, &instance, &extruder_id](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
|
|
) != static_cast<int>(extruder_id)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
const PrintObject &print_object{instance.print_object};
|
|
const Point &offset{print_object.instances()[instance.instance_id].shift};
|
|
|
|
std::vector<SliceExtrusions> slices_extrusions{get_slices_extrusions(
|
|
print, *layer, should_pick_extrusion, smooth_path, offset, extruder_id, previous_position
|
|
)};
|
|
if (!slices_extrusions.empty()) {
|
|
result.push_back({offset, std::move(slices_extrusions)});
|
|
}
|
|
}
|
|
}
|
|
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 unsigned int extruder_id,
|
|
const PathSmoothingFunction &smooth_path,
|
|
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]};
|
|
const PrintObject &print_object = instance.print_object;
|
|
const Point &offset = print_object.instances()[instance.instance_id].shift;
|
|
|
|
result.emplace_back();
|
|
result.back().instance_offset = offset;
|
|
|
|
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),
|
|
smooth_path,
|
|
previous_position
|
|
);
|
|
}
|
|
|
|
if (const Layer *layer = layers[instance.object_layer_to_print_id].object_layer; layer) {
|
|
const auto should_pick_extrusion{[&layer_tools, &instance, &extruder_id](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) != static_cast<int>(extruder_id)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}};
|
|
|
|
result.back().slices_extrusions = get_slices_extrusions(
|
|
print,
|
|
*layer,
|
|
should_pick_extrusion,
|
|
smooth_path,
|
|
offset,
|
|
extruder_id,
|
|
previous_position
|
|
);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool is_empty(const std::vector<SliceExtrusions> &extrusions) {
|
|
for (const SliceExtrusions &slice_extrusions : extrusions) {
|
|
for (const IslandExtrusions &island_extrusions : slice_extrusions.common_extrusions) {
|
|
if (!island_extrusions.perimeters.empty() || !island_extrusions.infill_ranges.empty()) {
|
|
return false;
|
|
}
|
|
}
|
|
if (!slice_extrusions.ironing_extrusions.empty()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool is_empty(const ExtruderExtrusions &extruder_extrusions) {
|
|
for (const OverridenExtrusions &overriden_extrusions : extruder_extrusions.overriden_extrusions) {
|
|
if (!is_empty(overriden_extrusions.slices_extrusions)) {
|
|
return false;
|
|
}
|
|
}
|
|
for (const NormalExtrusions &normal_extrusions : extruder_extrusions.normal_extrusions) {
|
|
if (!normal_extrusions.support_extrusions.empty()) {
|
|
return false;
|
|
}
|
|
if (!is_empty(normal_extrusions.slices_extrusions)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
std::vector<ExtruderExtrusions> get_extrusions(
|
|
const Print &print,
|
|
const GCode::WipeTowerIntegration *wipe_tower,
|
|
const GCode::ObjectsLayerToPrint &layers,
|
|
const bool is_first_layer,
|
|
const LayerTools &layer_tools,
|
|
const std::vector<InstanceToPrint> &instances_to_print,
|
|
const std::map<unsigned int, std::pair<size_t, size_t>> &skirt_loops_per_extruder,
|
|
unsigned current_extruder_id,
|
|
const PathSmoothingFunction &smooth_path,
|
|
bool get_brim,
|
|
std::optional<Point> previous_position
|
|
) {
|
|
unsigned toolchange_number{0};
|
|
|
|
std::vector<ExtruderExtrusions> extrusions;
|
|
for (const unsigned int extruder_id : layer_tools.extruders)
|
|
{
|
|
ExtruderExtrusions extruder_extrusions{extruder_id};
|
|
|
|
if (layer_tools.has_wipe_tower && wipe_tower != nullptr) {
|
|
const bool finish_wipe_tower{extruder_id == layer_tools.extruders.back()};
|
|
if (finish_wipe_tower || is_toolchange_required(is_first_layer, layer_tools.extruders.back(), extruder_id, current_extruder_id)) {
|
|
const bool ignore_sparse{print.config().wipe_tower_no_sparse_layers.value};
|
|
if (const auto tool_change{wipe_tower->get_toolchange(toolchange_number, ignore_sparse)}) {
|
|
toolchange_number++;
|
|
previous_position = Point::new_scale(wipe_tower->transform_wt_pt(tool_change->end_pos));
|
|
current_extruder_id = tool_change->new_tool;
|
|
extruder_extrusions.wipe_tower_start = Point::new_scale(wipe_tower->transform_wt_pt(tool_change->start_pos));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
|
|
const std::pair<size_t, size_t> loops = loops_it->second;
|
|
for (std::size_t i = loops.first; i < loops.second; ++i) {
|
|
bool reverse{false};
|
|
if (auto loop = dynamic_cast<const ExtrusionLoop *>(print.skirt().entities[i])) {
|
|
const bool is_hole = loop->is_clockwise();
|
|
reverse = print.config().prefer_clockwise_movements ? !is_hole : is_hole;
|
|
}
|
|
const ExtrusionEntityReference entity{*print.skirt().entities[i], reverse};
|
|
std::optional<InstancePoint> last_position{get_instance_point(previous_position, {0, 0})};
|
|
auto [path, _]{smooth_path(nullptr, nullptr, entity, extruder_id, last_position)};
|
|
previous_position = get_gcode_point(last_position, {0, 0});
|
|
extruder_extrusions.skirt.emplace_back(i, std::move(path));
|
|
}
|
|
}
|
|
|
|
// Extrude brim with the extruder of the 1st region.
|
|
if (get_brim) {
|
|
for (const ExtrusionEntity *entity : print.brim().entities) {
|
|
bool reverse{false};
|
|
bool is_loop{false};
|
|
if (auto loop = dynamic_cast<const ExtrusionLoop *>(entity)) {
|
|
const bool is_hole = loop->is_clockwise();
|
|
is_loop = true;
|
|
reverse = print.config().prefer_clockwise_movements ? !is_hole : is_hole;
|
|
}
|
|
const ExtrusionEntityReference entity_reference{*entity, reverse};
|
|
|
|
std::optional<InstancePoint> last_position{get_instance_point(previous_position, {0, 0})};
|
|
auto [path, _]{smooth_path(nullptr, nullptr, entity_reference, extruder_id, last_position)};
|
|
previous_position = get_gcode_point(last_position, {0, 0});
|
|
extruder_extrusions.brim.push_back({std::move(path), is_loop});
|
|
}
|
|
get_brim = false;
|
|
}
|
|
|
|
using GCode::ExtrusionOrder::get_overriden_extrusions;
|
|
bool is_anything_overridden = layer_tools.wiping_extrusions().is_anything_overridden();
|
|
if (is_anything_overridden) {
|
|
extruder_extrusions.overriden_extrusions = get_overriden_extrusions(
|
|
print, layers, layer_tools, instances_to_print, extruder_id, smooth_path,
|
|
previous_position
|
|
);
|
|
}
|
|
|
|
using GCode::ExtrusionOrder::get_normal_extrusions;
|
|
extruder_extrusions.normal_extrusions = get_normal_extrusions(
|
|
print, layers, layer_tools, instances_to_print, extruder_id, smooth_path,
|
|
previous_position
|
|
);
|
|
|
|
extrusions.push_back(std::move(extruder_extrusions));
|
|
}
|
|
return extrusions;
|
|
}
|
|
|
|
std::optional<Geometry::ArcWelder::Segment> get_first_point(const SmoothPath &path) {
|
|
for (const SmoothPathElement & element : path) {
|
|
if (!element.path.empty()) {
|
|
return element.path.front();
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Geometry::ArcWelder::Segment> get_first_point(const std::vector<SmoothPath> &smooth_paths) {
|
|
for (const SmoothPath &path : smooth_paths) {
|
|
if (auto result = get_first_point(path)) {
|
|
return result;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Geometry::ArcWelder::Segment> get_first_point(const std::vector<InfillRange> &infill_ranges) {
|
|
for (const InfillRange &infill_range : infill_ranges) {
|
|
if (auto result = get_first_point(infill_range.items)) {
|
|
return result;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Geometry::ArcWelder::Segment> get_first_point(const std::vector<Perimeter> &perimeters) {
|
|
for (const Perimeter &perimeter : perimeters) {
|
|
if (auto result = get_first_point(perimeter.smooth_path)) {
|
|
return result;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Geometry::ArcWelder::Segment> get_first_point(const std::vector<IslandExtrusions> &extrusions) {
|
|
for (const IslandExtrusions &island : extrusions) {
|
|
if (island.infill_first) {
|
|
if (auto result = get_first_point(island.infill_ranges)) {
|
|
return result;
|
|
}
|
|
if (auto result = get_first_point(island.perimeters)) {
|
|
return result;
|
|
}
|
|
} else {
|
|
if (auto result = get_first_point(island.perimeters)) {
|
|
return result;
|
|
}
|
|
if (auto result = get_first_point(island.infill_ranges)) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Geometry::ArcWelder::Segment> get_first_point(const std::vector<SliceExtrusions> &extrusions) {
|
|
for (const SliceExtrusions &slice : extrusions) {
|
|
if (auto result = get_first_point(slice.common_extrusions)) {
|
|
return result;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Geometry::ArcWelder::Segment> get_first_point(const ExtruderExtrusions &extrusions) {
|
|
for (const auto&[_, path] : extrusions.skirt) {
|
|
if (auto result = get_first_point(path)) {
|
|
return result;
|
|
};
|
|
}
|
|
for (const BrimPath &brim_path : extrusions.brim) {
|
|
if (auto result = get_first_point(brim_path.path)) {
|
|
return result;
|
|
};
|
|
}
|
|
for (const OverridenExtrusions &overriden_extrusions : extrusions.overriden_extrusions) {
|
|
if (auto result = get_first_point(overriden_extrusions.slices_extrusions)) {
|
|
result->point += overriden_extrusions.instance_offset;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
for (const NormalExtrusions &normal_extrusions : extrusions.normal_extrusions) {
|
|
for (const SupportPath &support_path : normal_extrusions.support_extrusions) {
|
|
if (auto result = get_first_point(support_path.path)) {
|
|
result->point += normal_extrusions.instance_offset;
|
|
return result;
|
|
}
|
|
}
|
|
if (auto result = get_first_point(normal_extrusions.slices_extrusions)) {
|
|
result->point += normal_extrusions.instance_offset;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Geometry::ArcWelder::Segment> get_first_point(const std::vector<ExtruderExtrusions> &extrusions) {
|
|
if (extrusions.empty()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (extrusions.front().wipe_tower_start) {
|
|
return {{*(extrusions.front().wipe_tower_start)}};
|
|
}
|
|
|
|
for (const ExtruderExtrusions &extruder_extrusions : extrusions) {
|
|
if (auto result = get_first_point(extruder_extrusions)) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
const PrintInstance * get_first_instance(
|
|
const std::vector<ExtruderExtrusions> &extrusions,
|
|
const std::vector<InstanceToPrint> &instances_to_print
|
|
) {
|
|
for (const ExtruderExtrusions &extruder_extrusions : extrusions) {
|
|
if (!extruder_extrusions.overriden_extrusions.empty()) {
|
|
for (std::size_t i{0}; i < instances_to_print.size(); ++i) {
|
|
const OverridenExtrusions &overriden_extrusions{extruder_extrusions.overriden_extrusions[i]};
|
|
if (!is_empty(overriden_extrusions.slices_extrusions)) {
|
|
const InstanceToPrint &instance{instances_to_print[i]};
|
|
return &instance.print_object.instances()[instance.instance_id];
|
|
}
|
|
}
|
|
}
|
|
for (std::size_t i{0}; i < instances_to_print.size(); ++i) {
|
|
const InstanceToPrint &instance{instances_to_print[i]};
|
|
const std::vector<SupportPath> &support_extrusions{extruder_extrusions.normal_extrusions[i].support_extrusions};
|
|
const std::vector<SliceExtrusions> &slices_extrusions{extruder_extrusions.normal_extrusions[i].slices_extrusions};
|
|
|
|
if (!support_extrusions.empty() || !is_empty(slices_extrusions)) {
|
|
return &instance.print_object.instances()[instance.instance_id];
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
}
|