#ifndef slic3r_GCode_hpp_ #define slic3r_GCode_hpp_ #include "libslic3r.h" #include "ExPolygon.hpp" #include "GCodeWriter.hpp" #include "Layer.hpp" #include "Point.hpp" #include "PlaceholderParser.hpp" #include "PrintConfig.hpp" #include "GCode/AvoidCrossingPerimeters.hpp" #include "GCode/CoolingBuffer.hpp" #include "GCode/SpiralVase.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" #include "GCode/SeamPlacer.hpp" #include "GCode/GCodeProcessor.hpp" #include "EdgeGrid.hpp" #include "GCode/ThumbnailData.hpp" #include #include #include #ifdef HAS_PRESSURE_EQUALIZER #include "GCode/PressureEqualizer.hpp" #endif /* HAS_PRESSURE_EQUALIZER */ namespace Slic3r { // Forward declarations. class GCode; namespace { struct Item; } struct PrintInstance; class ConstPrintObjectPtrsAdaptor; class OozePrevention { public: bool enable; Points standby_points; OozePrevention() : enable(false) {} std::string pre_toolchange(GCode &gcodegen); std::string post_toolchange(GCode &gcodegen); private: int _get_temp(GCode &gcodegen); }; class Wipe { public: bool enable; Polyline path; Wipe() : enable(false) {} bool has_path() const { return !this->path.points.empty(); } void reset_path() { this->path = Polyline(); } std::string wipe(GCode &gcodegen, bool toolchange = false); }; class WipeTowerIntegration { public: WipeTowerIntegration( const PrintConfig &print_config, const std::vector &priming, const std::vector> &tool_changes, const WipeTower::ToolChangeResult &final_purge) : m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f), m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)), m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)), m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)), m_extruder_offsets(print_config.extruder_offset.values), m_priming(priming), m_tool_changes(tool_changes), m_final_purge(final_purge), m_layer_idx(-1), m_tool_change_idx(0) {} std::string prime(GCode &gcodegen); void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; } std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer); std::string finalize(GCode &gcodegen); std::vector used_filament_length() const; private: WipeTowerIntegration& operator=(const WipeTowerIntegration&); std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const; // Postprocesses gcode: rotates and moves G1 extrusions and returns result std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const; // Left / right edges of the wipe tower, for the planning of wipe moves. const float m_left; const float m_right; const Vec2f m_wipe_tower_pos; const float m_wipe_tower_rotation; const std::vector m_extruder_offsets; // Reference to cached values at the Printer class. const std::vector &m_priming; const std::vector> &m_tool_changes; const WipeTower::ToolChangeResult &m_final_purge; // Current layer index. int m_layer_idx; int m_tool_change_idx; double m_last_wipe_tower_print_z = 0.f; }; class ColorPrintColors { static const std::vector Colors; public: static const std::vector& get() { return Colors; } }; class GCode { public: GCode() : m_origin(Vec2d::Zero()), m_enable_loop_clipping(true), m_enable_cooling_markers(false), m_enable_extrusion_role_markers(false), m_last_processor_extrusion_role(erNone), m_layer_count(0), m_layer_index(-1), m_layer(nullptr), m_object_layer_over_raft(false), m_volumetric_speed(0), m_last_pos_defined(false), m_last_extrusion_role(erNone), m_last_width(0.0f), #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_last_mm3_per_mm(0.0), #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING m_brim_done(false), m_second_layer_things_done(false), m_silent_time_estimator_enabled(false), m_last_obj_copy(nullptr, Point(std::numeric_limits::max(), std::numeric_limits::max())) {} ~GCode() = default; // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). void do_export(Print* print, const char* path, GCodeProcessorResult* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. const Vec2d& origin() const { return m_origin; } void set_origin(const Vec2d &pointf); void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); } const Point& last_pos() const { return m_last_pos; } Vec2d point_to_gcode(const Point &point) const; Point gcode_to_point(const Vec2d &point) const; const FullPrintConfig &config() const { return m_config; } const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } const GCodeWriter& writer() const { return m_writer; } PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } // Process a template through the placeholder parser, collect error messages to be reported // inside the generated string and after the G-code export finishes. std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr); bool enable_cooling_markers() const { return m_enable_cooling_markers; } // For Perl bindings, to be used exclusively by unit tests. unsigned int layer_count() const { return m_layer_count; } void set_layer_count(unsigned int value) { m_layer_count = value; } void apply_print_config(const PrintConfig &print_config); // append full config to the given string static void append_full_config(const Print& print, std::string& str); // 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 LayerToPrint { LayerToPrint() : 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; } }; private: class GCodeOutputStream { public: GCodeOutputStream(FILE *f, GCodeProcessor &processor) : f(f), m_processor(processor) {} ~GCodeOutputStream() { this->close(); } bool is_open() const { return f; } bool is_error() const; void flush(); void close(); // Write a string into a file. void write(const std::string& what) { this->write(what.c_str()); } void write(const char* what); // Write a string into a file. // Add a newline, if the string does not end with a newline already. // Used to export a custom G-code section processed by the PlaceholderParser. void writeln(const std::string& what); // Formats and write into a file the given data. void write_format(const char* format, ...); private: FILE *f = nullptr; GCodeProcessor &m_processor; }; void _do_export(Print &print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb); static std::vector collect_layers_to_print(const PrintObject &object); static std::vector>> collect_layers_to_print(const Print &print); struct LayerResult { std::string gcode; size_t layer_id; // Is spiral vase post processing enabled for this layer? bool spiral_vase_enable { false }; // Should the cooling buffer content be flushed at the end of this layer? bool cooling_buffer_flush { false }; }; LayerResult process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, const LayerTools &layer_tools, const bool last_layer, // Pairs of PrintObject index and its instance index. const std::vector *ordering, // 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. void process_layers( const Print &print, const ToolOrdering &tool_ordering, const std::vector &print_object_instances_ordering, const std::vector>> &layers_to_print, GCodeOutputStream &output_stream); // Process all layers of a single object instance (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. void process_layers( const Print &print, const ToolOrdering &tool_ordering, std::vector layers_to_print, const size_t single_object_idx, GCodeOutputStream &output_stream); void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; } bool last_pos_defined() const { return m_last_pos_defined; } void set_extruders(const std::vector &extruder_ids); std::string preamble(); std::string change_layer(coordf_t print_z); std::string extrude_entity(const ExtrusionEntity &entity, std::string description = "", double speed = -1., std::unique_ptr *lower_layer_edge_grid = nullptr); std::string extrude_loop(ExtrusionLoop loop, std::string description, double speed = -1., std::unique_ptr *lower_layer_edge_grid = nullptr); std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.); std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.); // Extruding multiple objects with soluble / non-soluble / combined supports // on a multi-material printer, trying to minimize tool switches. // Following structures sort extrusions by the extruder ID, by an order of objects and object islands. struct ObjectByExtruder { ObjectByExtruder() : support(nullptr), support_extrusion_role(erNone) {} const ExtrusionEntityCollection *support; // erSupportMaterial / erSupportMaterialInterface or erMixed. ExtrusionRole support_extrusion_role; struct Island { struct Region { // Non-owned references to LayerRegion::perimeters::entities // std::vector would be better here, but there is no way in C++ to convert from std::vector std::vector without copying. ExtrusionEntitiesPtr perimeters; // Non-owned references to LayerRegion::fills::entities ExtrusionEntitiesPtr infills; std::vector infills_overrides; std::vector perimeters_overrides; enum Type { PERIMETERS, INFILL, }; // Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping void append(const Type type, const ExtrusionEntityCollection* eec, const WipingExtrusions::ExtruderPerCopy* copy_extruders); }; std::vector by_region; // all extrusions for this island, grouped by regions // Fills in by_region_per_copy_cache and returns its reference. const std::vector& by_region_per_copy(std::vector &by_region_per_copy_cache, unsigned int copy, unsigned int extruder, bool wiping_entities = false) const; }; std::vector islands; }; struct InstanceToPrint { InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) : object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {} // Repository ObjectByExtruder &object_by_extruder; // Index into std::vector, which contains Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances. const size_t layer_id; const PrintObject &print_object; // Instance idx of the copy of a print object. const size_t instance_id; }; std::vector sort_print_object_instances( std::vector &objects_by_extruder, // Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances. const std::vector &layers, // Ordering must be defined for normal (non-sequential print). const std::vector *ordering, // For sequential print, the instance of the object to be printing has to be defined. const size_t single_object_instance_idx); std::string extrude_perimeters(const Print &print, const std::vector &by_region, std::unique_ptr &lower_layer_edge_grid); std::string extrude_infill(const Print &print, const std::vector &by_region, bool ironing); std::string extrude_support(const ExtrusionEntityCollection &support_fills); std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone); std::string retract(bool toolchange = false); std::string unretract() { return m_writer.unlift() + m_writer.unretract(); } std::string set_extruder(unsigned int extruder_id, double print_z); // Cache for custom seam enforcers/blockers for each layer. SeamPlacer m_seam_placer; /* Origin of print coordinates expressed in unscaled G-code coordinates. This affects the input arguments supplied to the extrude*() and travel_to() methods. */ Vec2d m_origin; FullPrintConfig m_config; // scaled G-code resolution double m_scaled_resolution; GCodeWriter m_writer; PlaceholderParser m_placeholder_parser; // For random number generator etc. PlaceholderParser::ContextData m_placeholder_parser_context; // Collection of templates, on which the placeholder substitution failed. std::map m_placeholder_parser_failed_templates; OozePrevention m_ooze_prevention; Wipe m_wipe; AvoidCrossingPerimeters m_avoid_crossing_perimeters; bool m_enable_loop_clipping; // If enabled, the G-code generator will put following comments at the ends // of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END // Those comments are received and consumed (removed from the G-code) by the CoolingBuffer.pm Perl module. bool m_enable_cooling_markers; // Markers for the Pressure Equalizer to recognize the extrusion type. // The Pressure Equalizer removes the markers from the final G-code. bool m_enable_extrusion_role_markers; // Keeps track of the last extrusion role passed to the processor ExtrusionRole m_last_processor_extrusion_role; // How many times will change_layer() be called? // change_layer() will update the progress bar. unsigned int m_layer_count; // Progress bar indicator. Increments from -1 up to layer_count. int m_layer_index; // Current layer processed. In sequential printing mode, only a single copy will be printed. // In non-sequential mode, all its copies will be printed. const Layer* m_layer; // m_layer is an object layer and it is being printed over raft surface. bool m_object_layer_over_raft; double m_volumetric_speed; // Support for the extrusion role markers. Which marker is active? ExtrusionRole m_last_extrusion_role; // Support for G-Code Processor float m_last_height{ 0.0f }; float m_last_layer_z{ 0.0f }; float m_max_layer_z{ 0.0f }; float m_last_width{ 0.0f }; #if ENABLE_GCODE_VIEWER_DATA_CHECKING double m_last_mm3_per_mm; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING Point m_last_pos; bool m_last_pos_defined; std::unique_ptr m_cooling_buffer; std::unique_ptr m_spiral_vase; #ifdef HAS_PRESSURE_EQUALIZER std::unique_ptr m_pressure_equalizer; #endif /* HAS_PRESSURE_EQUALIZER */ std::unique_ptr m_wipe_tower; // Heights (print_z) at which the skirt has already been extruded. std::vector m_skirt_done; // Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print. bool m_brim_done; // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. bool m_second_layer_things_done; // Index of a last object copy extruded. std::pair m_last_obj_copy; bool m_silent_time_estimator_enabled; // Processor GCodeProcessor m_processor; std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1); void print_machine_envelope(GCodeOutputStream &file, Print &print); void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); // On the first printing layer. This flag triggers first layer speeds. bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; } // To control print speed of 1st object layer over raft interface. bool object_layer_over_raft() const { return m_object_layer_over_raft; } friend ObjectByExtruder& object_by_extruder( std::map> &by_extruder, unsigned int extruder_id, size_t object_idx, size_t num_objects); friend std::vector& object_islands_by_extruder( std::map> &by_extruder, unsigned int extruder_id, size_t object_idx, size_t num_objects, size_t num_islands); friend class Wipe; friend class WipeTowerIntegration; }; std::vector sort_object_instances_by_model_order(const Print& print); } #endif