mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-31 15:41:58 +08:00
Merge branch 'ms_gcode_refactor'
This commit is contained in:
commit
631810abe4
@ -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
|
||||
|
@ -298,7 +298,7 @@ class ExtrusionLoop : public ExtrusionEntity
|
||||
{
|
||||
public:
|
||||
ExtrusionPaths paths;
|
||||
|
||||
|
||||
ExtrusionLoop() = default;
|
||||
ExtrusionLoop(ExtrusionLoopRole role) : m_loop_role(role) {}
|
||||
ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault) : paths(paths), m_loop_role(role) {}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
@ -92,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;
|
||||
@ -178,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) {}
|
||||
@ -197,7 +191,7 @@ private:
|
||||
|
||||
bool is_open() const { return f; }
|
||||
bool is_error() const;
|
||||
|
||||
|
||||
void flush();
|
||||
void close();
|
||||
|
||||
@ -237,6 +231,15 @@ private:
|
||||
const GCode::Impl::Travels::ElevatedTravelParams &elevation_params
|
||||
) const;
|
||||
|
||||
std::vector<GCode::ExtrusionOrder::ExtruderExtrusions> get_sorted_extrusions(
|
||||
const Print &print,
|
||||
const ObjectsLayerToPrint &layers,
|
||||
const LayerTools &layer_tools,
|
||||
const std::vector<InstanceToPrint> &instances_to_print,
|
||||
const GCode::SmoothPathCaches &smooth_path_caches,
|
||||
const bool first_layer
|
||||
);
|
||||
|
||||
LayerResult process_layer(
|
||||
const Print &print,
|
||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||
@ -249,6 +252,7 @@ private:
|
||||
// If set to size_t(-1), then print all copies of all objects.
|
||||
// Otherwise print a single copy of a single object.
|
||||
const size_t single_object_idx = size_t(-1));
|
||||
|
||||
// Process all layers of all objects (non-sequential mode) with a parallel pipeline:
|
||||
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
|
||||
// and export G-code into file.
|
||||
@ -275,27 +279,14 @@ private:
|
||||
std::string change_layer(
|
||||
coordf_t previous_layer_z,
|
||||
coordf_t print_z,
|
||||
bool vase_mode
|
||||
bool vase_mode,
|
||||
const Point &first_point,
|
||||
const bool first_layer
|
||||
);
|
||||
std::string extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.);
|
||||
std::string extrude_loop(const ExtrusionLoop &loop, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.);
|
||||
std::string extrude_skirt(const ExtrusionLoop &loop_src, const ExtrusionFlow &extrusion_flow_override,
|
||||
const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed);
|
||||
|
||||
std::string extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.);
|
||||
std::string extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.);
|
||||
|
||||
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;
|
||||
};
|
||||
std::string extrude_smooth_path(
|
||||
const GCode::SmoothPath &smooth_path, const bool is_loop, const std::string_view description, const double speed
|
||||
);
|
||||
std::string extrude_skirt(GCode::SmoothPath smooth_path, const ExtrusionFlow &extrusion_flow_override);
|
||||
|
||||
std::vector<InstanceToPrint> sort_print_object_instances(
|
||||
// Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances.
|
||||
@ -305,26 +296,32 @@ private:
|
||||
// For sequential print, the instance of the object to be printing has to be defined.
|
||||
const size_t single_object_instance_idx);
|
||||
|
||||
// This function will be called for each printing extruder, possibly twice: First for wiping extrusions, second for normal extrusions.
|
||||
void process_layer_single_object(
|
||||
// output
|
||||
std::string &gcode,
|
||||
// Index of the extruder currently active.
|
||||
const unsigned int extruder_id,
|
||||
// What object and instance is going to be printed.
|
||||
const InstanceToPrint &print_instance,
|
||||
// and the object & support layer of the above.
|
||||
const ObjectLayerToPrint &layer_to_print,
|
||||
// Container for extruder overrides (when wiping into object or infill).
|
||||
const LayerTools &layer_tools,
|
||||
// Optional smooth path interpolating extrusion polylines.
|
||||
const GCode::SmoothPathCache &smooth_path_cache,
|
||||
// Is any extrusion possibly marked as wiping extrusion?
|
||||
const bool is_anything_overridden,
|
||||
// Round 1 (wiping into object or infill) or round 2 (normal extrusions).
|
||||
const bool print_wipe_extrusions);
|
||||
std::string extrude_perimeters(
|
||||
const PrintRegion ®ion,
|
||||
const std::vector<GCode::ExtrusionOrder::Perimeter> &perimeters,
|
||||
const InstanceToPrint &print_instance
|
||||
);
|
||||
|
||||
std::string extrude_infill_ranges(
|
||||
const std::vector<InfillRange> &infill_ranges,
|
||||
const std::string &commment
|
||||
);
|
||||
|
||||
void initialize_instance(
|
||||
const InstanceToPrint &print_instance,
|
||||
const ObjectLayerToPrint &layer_to_print
|
||||
);
|
||||
|
||||
std::string extrude_slices(
|
||||
const InstanceToPrint &print_instance,
|
||||
const ObjectLayerToPrint &layer_to_print,
|
||||
const std::vector<SliceExtrusions> &slices_extrusions
|
||||
);
|
||||
|
||||
std::string extrude_support(
|
||||
const std::vector<GCode::ExtrusionOrder::SupportPath> &support_extrusions
|
||||
);
|
||||
|
||||
std::string extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache &smooth_path_cache);
|
||||
std::string generate_travel_gcode(
|
||||
const Points3& travel,
|
||||
const std::string& comment,
|
||||
@ -433,12 +430,6 @@ private:
|
||||
std::optional<Vec3d> m_previous_layer_last_position;
|
||||
std::optional<Vec3d> m_previous_layer_last_position_before_wipe;
|
||||
// This needs to be populated during the layer processing!
|
||||
std::optional<Vec3d> m_current_layer_first_position;
|
||||
std::optional<unsigned> m_layer_change_extruder_id;
|
||||
bool m_layer_change_used_external_mp{false};
|
||||
const Layer* m_layer_change_layer{nullptr};
|
||||
std::optional<Vec2d> m_layer_change_origin;
|
||||
bool m_already_unretracted{false};
|
||||
std::unique_ptr<CoolingBuffer> m_cooling_buffer;
|
||||
std::unique_ptr<SpiralVase> m_spiral_vase;
|
||||
std::unique_ptr<GCodeFindReplace> m_find_replace;
|
||||
|
739
src/libslic3r/GCode/ExtrusionOrder.cpp
Normal file
739
src/libslic3r/GCode/ExtrusionOrder.cpp
Normal file
@ -0,0 +1,739 @@
|
||||
#include "ExtrusionOrder.hpp"
|
||||
#include "GCode/SmoothPath.hpp"
|
||||
#include "libslic3r/ShortestPath.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;
|
||||
}
|
||||
SmoothPath path{smooth_path(&layer, 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});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)};
|
||||
SmoothPath path{smooth_path(&layer, 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})};
|
||||
SmoothPath path{smooth_path(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})};
|
||||
SmoothPath path{smooth_path(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) {
|
||||
if (is_toolchange_required(is_first_layer, layer_tools.extruders.back(), extruder_id, current_extruder_id)) {
|
||||
const WipeTower::ToolChangeResult tool_change{wipe_tower->get_toolchange(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})};
|
||||
SmoothPath path{smooth_path(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})};
|
||||
SmoothPath path{smooth_path(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
|
||||
);
|
||||
|
||||
if (is_empty(extruder_extrusions)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
extrusions.push_back(std::move(extruder_extrusions));
|
||||
}
|
||||
return extrusions;
|
||||
}
|
||||
|
||||
std::optional<InstancePoint> get_first_point(const SmoothPath &path) {
|
||||
for (const SmoothPathElement & element : path) {
|
||||
if (!element.path.empty()) {
|
||||
return InstancePoint{element.path.front().point};
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<InstancePoint> 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<InstancePoint> 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<InstancePoint> 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<InstancePoint> 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<InstancePoint> 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<Point> get_first_point(const ExtruderExtrusions &extrusions) {
|
||||
for (const auto&[_, path] : extrusions.skirt) {
|
||||
if (auto result = get_first_point(path)) {
|
||||
return result->local_point;
|
||||
};
|
||||
}
|
||||
for (const BrimPath &brim_path : extrusions.brim) {
|
||||
if (auto result = get_first_point(brim_path.path)) {
|
||||
return result->local_point;
|
||||
};
|
||||
}
|
||||
for (const OverridenExtrusions &overriden_extrusions : extrusions.overriden_extrusions) {
|
||||
if (auto result = get_first_point(overriden_extrusions.slices_extrusions)) {
|
||||
return result->local_point - overriden_extrusions.instance_offset;
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
return result->local_point + normal_extrusions.instance_offset;
|
||||
}
|
||||
}
|
||||
if (auto result = get_first_point(normal_extrusions.slices_extrusions)) {
|
||||
return result->local_point + normal_extrusions.instance_offset;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Point> 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;
|
||||
}
|
||||
|
||||
}
|
147
src/libslic3r/GCode/ExtrusionOrder.hpp
Normal file
147
src/libslic3r/GCode/ExtrusionOrder.hpp
Normal file
@ -0,0 +1,147 @@
|
||||
#ifndef slic3r_GCode_ExtrusionOrder_hpp_
|
||||
#define slic3r_GCode_ExtrusionOrder_hpp_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "libslic3r/GCode/SmoothPath.hpp"
|
||||
#include "libslic3r/GCode/WipeTowerIntegration.hpp"
|
||||
#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<SmoothPath> items;
|
||||
const PrintRegion *region;
|
||||
};
|
||||
|
||||
struct Perimeter {
|
||||
GCode::SmoothPath smooth_path;
|
||||
bool reversed;
|
||||
const ExtrusionEntity *extrusion_entity;
|
||||
};
|
||||
|
||||
struct IslandExtrusions {
|
||||
const PrintRegion *region;
|
||||
std::vector<Perimeter> perimeters;
|
||||
std::vector<InfillRange> infill_ranges;
|
||||
bool infill_first{false};
|
||||
};
|
||||
|
||||
struct SliceExtrusions {
|
||||
std::vector<IslandExtrusions> common_extrusions;
|
||||
std::vector<InfillRange> ironing_extrusions;
|
||||
};
|
||||
|
||||
struct SupportPath {
|
||||
SmoothPath path;
|
||||
bool is_interface;
|
||||
};
|
||||
|
||||
struct NormalExtrusions {
|
||||
Point instance_offset;
|
||||
std::vector<SupportPath> support_extrusions;
|
||||
std::vector<SliceExtrusions> slices_extrusions;
|
||||
};
|
||||
|
||||
struct OverridenExtrusions {
|
||||
Point instance_offset;
|
||||
std::vector<SliceExtrusions> slices_extrusions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Intentionally strong type representing a point in a coordinate system
|
||||
* of an instance. Introduced to avoid confusion between local and
|
||||
* global coordinate systems.
|
||||
*/
|
||||
struct InstancePoint {
|
||||
Point local_point;
|
||||
};
|
||||
|
||||
using PathSmoothingFunction = std::function<SmoothPath(
|
||||
const Layer *, const ExtrusionEntityReference &, const unsigned extruder_id, std::optional<InstancePoint> &previous_position
|
||||
)>;
|
||||
|
||||
struct BrimPath {
|
||||
SmoothPath path;
|
||||
bool is_loop;
|
||||
};
|
||||
|
||||
struct ExtruderExtrusions {
|
||||
unsigned extruder_id;
|
||||
std::vector<std::pair<std::size_t, GCode::SmoothPath>> skirt;
|
||||
std::vector<BrimPath> brim;
|
||||
std::vector<OverridenExtrusions> overriden_extrusions;
|
||||
std::vector<NormalExtrusions> normal_extrusions;
|
||||
std::optional<Point> wipe_tower_start;
|
||||
};
|
||||
|
||||
static constexpr const double min_gcode_segment_length = 0.002;
|
||||
|
||||
bool is_empty(const std::vector<SliceExtrusions> &extrusions);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
std::optional<Point> get_first_point(const std::vector<ExtruderExtrusions> &extrusions);
|
||||
|
||||
const PrintInstance * get_first_instance(
|
||||
const std::vector<ExtruderExtrusions> &extrusions,
|
||||
const std::vector<InstanceToPrint> &instances_to_print
|
||||
);
|
||||
}
|
||||
|
||||
#endif // slic3r_GCode_ExtrusionOrder_hpp_
|
@ -57,9 +57,6 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags = {
|
||||
"HEIGHT:",
|
||||
"WIDTH:",
|
||||
"LAYER_CHANGE",
|
||||
"LAYER_CHANGE_TRAVEL",
|
||||
"LAYER_CHANGE_RETRACTION_START",
|
||||
"LAYER_CHANGE_RETRACTION_END",
|
||||
"COLOR_CHANGE",
|
||||
"PAUSE_PRINT",
|
||||
"CUSTOM_GCODE",
|
||||
|
@ -181,9 +181,6 @@ namespace Slic3r {
|
||||
Height,
|
||||
Width,
|
||||
Layer_Change,
|
||||
Layer_Change_Travel,
|
||||
Layer_Change_Retraction_Start,
|
||||
Layer_Change_Retraction_End,
|
||||
Color_Change,
|
||||
Pause_Print,
|
||||
Custom_Code,
|
||||
|
@ -34,15 +34,6 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
|
||||
|
||||
std::string gcode;
|
||||
|
||||
// Toolchangeresult.gcode assumes the wipe tower corner is at the origin (except for priming lines)
|
||||
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
|
||||
float alpha = m_wipe_tower_rotation / 180.f * float(M_PI);
|
||||
|
||||
auto transform_wt_pt = [&alpha, this](const Vec2f& pt) -> Vec2f {
|
||||
Vec2f out = Eigen::Rotation2Df(alpha) * pt;
|
||||
out += m_wipe_tower_pos;
|
||||
return out;
|
||||
};
|
||||
|
||||
Vec2f start_pos = tcr.start_pos;
|
||||
Vec2f end_pos = tcr.end_pos;
|
||||
@ -52,7 +43,7 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
|
||||
}
|
||||
|
||||
Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos;
|
||||
float wipe_tower_rotation = tcr.priming ? 0.f : alpha;
|
||||
float wipe_tower_rotation = tcr.priming ? 0.f : this->get_alpha();
|
||||
|
||||
std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation);
|
||||
|
||||
@ -77,18 +68,13 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
|
||||
gcode += gcodegen.retract_and_wipe();
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
const std::string comment{"Travel to a Wipe Tower"};
|
||||
if (gcodegen.m_current_layer_first_position) {
|
||||
if (gcodegen.last_position) {
|
||||
gcode += gcodegen.travel_to(
|
||||
*gcodegen.last_position, xy_point, ExtrusionRole::Mixed, comment, [](){return "";}
|
||||
);
|
||||
} else {
|
||||
gcode += gcodegen.writer().travel_to_xy(gcodegen.point_to_gcode(xy_point), comment);
|
||||
gcode += gcodegen.writer().get_travel_to_z_gcode(z, comment);
|
||||
}
|
||||
if (gcodegen.last_position) {
|
||||
gcode += gcodegen.travel_to(
|
||||
*gcodegen.last_position, xy_point, ExtrusionRole::Mixed, comment, [](){return "";}
|
||||
);
|
||||
} else {
|
||||
const Vec3crd point = to_3d(xy_point, scaled(z));
|
||||
gcode += gcodegen.travel_to_first_position(point, current_z, ExtrusionRole::Mixed, [](){return "";});
|
||||
gcode += gcodegen.writer().travel_to_xy(gcodegen.point_to_gcode(xy_point), comment);
|
||||
gcode += gcodegen.writer().get_travel_to_z_gcode(z, comment);
|
||||
}
|
||||
gcode += gcodegen.unretract();
|
||||
} else {
|
||||
@ -145,7 +131,7 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
|
||||
Geometry::ArcWelder::Path path;
|
||||
path.reserve(tcr.wipe_path.size());
|
||||
std::transform(tcr.wipe_path.begin(), tcr.wipe_path.end(), std::back_inserter(path),
|
||||
[&gcodegen, &transform_wt_pt](const Vec2f &wipe_pt) {
|
||||
[&gcodegen, this](const Vec2f &wipe_pt) {
|
||||
return Geometry::ArcWelder::Segment{ wipe_tower_point_to_object_point(gcodegen, transform_wt_pt(wipe_pt)) };
|
||||
});
|
||||
// Pass to the wipe cache.
|
||||
|
@ -39,8 +39,23 @@ public:
|
||||
std::string tool_change(GCodeGenerator &gcodegen, int extruder_id, bool finish_layer);
|
||||
std::string finalize(GCodeGenerator &gcodegen);
|
||||
std::vector<float> used_filament_length() const;
|
||||
WipeTower::ToolChangeResult get_toolchange(std::size_t index) const {
|
||||
return m_tool_changes.at(m_layer_idx).at(index);
|
||||
}
|
||||
|
||||
Vec2f transform_wt_pt(const Vec2f& pt) const {
|
||||
Vec2f out = Eigen::Rotation2Df(this->get_alpha()) * pt;
|
||||
out += m_wipe_tower_pos;
|
||||
return out;
|
||||
};
|
||||
|
||||
private:
|
||||
// Toolchangeresult.gcode assumes the wipe tower corner is at the origin (except for priming lines)
|
||||
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
|
||||
float get_alpha() const {
|
||||
return m_wipe_tower_rotation / 180.f * float(M_PI);
|
||||
}
|
||||
|
||||
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
|
||||
std::string append_tcr(GCodeGenerator &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
|
||||
|
||||
|
@ -1484,6 +1484,21 @@ const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt) const
|
||||
return m_wipe_tower_data;
|
||||
}
|
||||
|
||||
bool is_toolchange_required(
|
||||
const bool first_layer,
|
||||
const unsigned last_extruder_id,
|
||||
const unsigned extruder_id,
|
||||
const unsigned current_extruder_id
|
||||
) {
|
||||
if (first_layer && extruder_id == last_extruder_id) {
|
||||
return true;
|
||||
}
|
||||
if (extruder_id != current_extruder_id) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Print::_make_wipe_tower()
|
||||
{
|
||||
m_wipe_tower_data.clear();
|
||||
@ -1552,10 +1567,11 @@ void Print::_make_wipe_tower()
|
||||
unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.all_extruders().back();
|
||||
for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
|
||||
if (!layer_tools.has_wipe_tower) continue;
|
||||
bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
|
||||
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id, false);
|
||||
for (const auto extruder_id : layer_tools.extruders) {
|
||||
if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
|
||||
const bool first_layer{&layer_tools == &m_wipe_tower_data.tool_ordering.front()};
|
||||
const unsigned last_extruder_id{m_wipe_tower_data.tool_ordering.all_extruders().back()};
|
||||
if (is_toolchange_required(first_layer, last_extruder_id, extruder_id, current_extruder_id)) {
|
||||
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
|
||||
// Not all of that can be used for infill purging:
|
||||
volume_to_wipe -= (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||||
|
@ -493,6 +493,13 @@ private:
|
||||
WipeTowerData &operator=(const WipeTowerData & /* rhs */) = delete;
|
||||
};
|
||||
|
||||
bool is_toolchange_required(
|
||||
const bool first_layer,
|
||||
const unsigned last_extruder_id,
|
||||
const unsigned extruder_id,
|
||||
const unsigned current_extruder_id
|
||||
);
|
||||
|
||||
struct PrintStatistics
|
||||
{
|
||||
PrintStatistics() { clear(); }
|
||||
|
Loading…
x
Reference in New Issue
Block a user