diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index e00ce2412..aa2ca0e51 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -87,6 +87,8 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/GCode/CoolingBuffer.hpp ${LIBDIR}/libslic3r/GCode/PressureEqualizer.cpp ${LIBDIR}/libslic3r/GCode/PressureEqualizer.hpp + ${LIBDIR}/libslic3r/GCode/PrintExtents.cpp + ${LIBDIR}/libslic3r/GCode/PrintExtents.hpp ${LIBDIR}/libslic3r/GCode/SpiralVase.cpp ${LIBDIR}/libslic3r/GCode/SpiralVase.hpp ${LIBDIR}/libslic3r/GCode/ToolOrdering.cpp diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 8549b7688..a2e2f5607 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -2,6 +2,7 @@ #include "ExtrusionEntity.hpp" #include "EdgeGrid.hpp" #include "Geometry.hpp" +#include "GCode/PrintExtents.hpp" #include "GCode/WipeTowerPrusaMM.hpp" #include @@ -686,10 +687,28 @@ bool GCode::_do_export(Print &print, FILE *file) // All extrusion moves with the same top layer height are extruded uninterrupted. std::vector>> layers_to_print = collect_layers_to_print(print); // Prusa Multi-Material wipe tower. - if (print.has_wipe_tower()) { + if (print.has_wipe_tower() && ! layers_to_print.empty()) { if (tool_ordering.has_wipe_tower()) { m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get())); write(file, m_wipe_tower->prime(*this)); + // Verify, whether the print overaps the priming extrusions. + BoundingBoxf bbox_print(get_print_extrusions_extents(print)); + coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON; + for (const PrintObject *print_object : print.objects) + bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz)); + bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz)); + BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print)); + bbox_prime.offset(0.5f); + // Beep for 500ms, tone 800Hz. Yet better, play some Morse. + fprintf(file, "M300 S800 P500\n"); + if (bbox_prime.overlap(bbox_print)) { + // Wait for the user to remove the priming extrusions, otherwise they would + // get covered by the print. + fprintf(file, "M1 Remove priming towers and click button.\n"); + } else { + // Just wait for a bit to let the user check, that the priming succeeded. + fprintf(file, "M0 S10\n"); + } } else write(file, WipeTowerIntegration::prime_single_color_print(print, initial_extruder_id, *this)); } diff --git a/xs/src/libslic3r/GCode/PrintExtents.cpp b/xs/src/libslic3r/GCode/PrintExtents.cpp new file mode 100644 index 000000000..98dc59f58 --- /dev/null +++ b/xs/src/libslic3r/GCode/PrintExtents.cpp @@ -0,0 +1,180 @@ +// Calculate extents of the extrusions assigned to Print / PrintObject. +// The extents are used for assessing collisions of the print with the priming towers, +// to decide whether to pause the print after the priming towers are extruded +// to let the operator remove them from the print bed. + +#include "../BoundingBox.hpp" +#include "../ExtrusionEntity.hpp" +#include "../ExtrusionEntityCollection.hpp" +#include "../Print.hpp" + +#include "PrintExtents.hpp" +#include "WipeTower.hpp" + +namespace Slic3r { + +static inline BoundingBox extrusion_polyline_extents(const Polyline &polyline, const coord_t radius) +{ + BoundingBox bbox; + if (! polyline.points.empty()) + bbox.merge(polyline.points.front()); + for (const Point &pt : polyline.points) { + bbox.min.x = std::min(bbox.min.x, pt.x - radius); + bbox.min.y = std::min(bbox.min.y, pt.y - radius); + bbox.max.x = std::max(bbox.max.x, pt.x + radius); + bbox.max.y = std::max(bbox.max.y, pt.y + radius); + } + return bbox; +} + +static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusion_path) +{ + BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width)); + BoundingBoxf bboxf; + if (! empty(bbox)) { + bboxf.min = Pointf::new_unscale(bbox.min); + bboxf.max = Pointf::new_unscale(bbox.max); + } + return bboxf; +} + +static inline BoundingBoxf extrusionentity_extents(const ExtrusionLoop &extrusion_loop) +{ + BoundingBox bbox; + for (const ExtrusionPath &extrusion_path : extrusion_loop.paths) + bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width))); + BoundingBoxf bboxf; + if (! empty(bbox)) { + bboxf.min = Pointf::new_unscale(bbox.min); + bboxf.max = Pointf::new_unscale(bbox.max); + } + return bboxf; +} + +static inline BoundingBoxf extrusionentity_extents(const ExtrusionMultiPath &extrusion_multi_path) +{ + BoundingBox bbox; + for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths) + bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width))); + BoundingBoxf bboxf; + if (! empty(bbox)) { + bboxf.min = Pointf::new_unscale(bbox.min); + bboxf.max = Pointf::new_unscale(bbox.max); + } + return bboxf; +} + +static BoundingBoxf extrusionentity_extents(const ExtrusionEntity *extrusion_entity); + +static inline BoundingBoxf extrusionentity_extents(const ExtrusionEntityCollection &extrusion_entity_collection) +{ + BoundingBoxf bbox; + for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities) + bbox.merge(extrusionentity_extents(extrusion_entity)); + return bbox; +} + +static BoundingBoxf extrusionentity_extents(const ExtrusionEntity *extrusion_entity) +{ + if (extrusion_entity == nullptr) + return BoundingBoxf(); + auto *extrusion_path = dynamic_cast(extrusion_entity); + if (extrusion_path != nullptr) + return extrusionentity_extents(*extrusion_path); + auto *extrusion_loop = dynamic_cast(extrusion_entity); + if (extrusion_loop != nullptr) + return extrusionentity_extents(*extrusion_loop); + auto *extrusion_multi_path = dynamic_cast(extrusion_entity); + if (extrusion_multi_path != nullptr) + return extrusionentity_extents(*extrusion_multi_path); + auto *extrusion_entity_collection = dynamic_cast(extrusion_entity); + if (extrusion_entity_collection != nullptr) + return extrusionentity_extents(*extrusion_entity_collection); + CONFESS("Unexpected extrusion_entity type in extrusionentity_extents()"); + return BoundingBoxf(); +} + +BoundingBoxf get_print_extrusions_extents(const Print &print) +{ + BoundingBoxf bbox(extrusionentity_extents(print.brim)); + bbox.merge(extrusionentity_extents(print.skirt)); + return bbox; +} + +BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object, const coord_t max_print_z) +{ + BoundingBoxf bbox; + for (const Layer *layer : print_object.layers) { + if (layer->print_z > max_print_z) + break; + BoundingBoxf bbox_this; + for (const LayerRegion *layerm : layer->regions) { + bbox_this.merge(extrusionentity_extents(layerm->perimeters)); + for (const ExtrusionEntity *ee : layerm->fills.entities) + // fill represents infill extrusions of a single island. + bbox_this.merge(extrusionentity_extents(*dynamic_cast(ee))); + } + const SupportLayer *support_layer = dynamic_cast(layer); + if (support_layer) + for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) + bbox_this.merge(extrusionentity_extents(extrusion_entity)); + for (const Point &offset : print_object._shifted_copies) { + BoundingBoxf bbox_translated(bbox_this); + bbox_translated.translate(Pointf::new_unscale(offset)); + bbox.merge(bbox_translated); + } + } + return bbox; +} + +// Returns a bounding box of a projection of the wipe tower for the layers <= max_print_z. +// The projection does not contain the priming regions. +BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coord_t max_print_z) +{ + BoundingBoxf bbox; + for (const std::vector &tool_changes : print.m_wipe_tower_tool_changes) { + if (! tool_changes.empty() && tool_changes.front().print_z > max_print_z) + break; + for (const WipeTower::ToolChangeResult &tcr : tool_changes) { + for (size_t i = 1; i < tcr.extrusions.size(); ++ i) { + const WipeTower::Extrusion &e = tcr.extrusions[i]; + if (e.width > 0) { + Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y); + Pointf p2(e.pos.x, e.pos.y); + bbox.merge(p1); + coordf_t radius = 0.5 * e.width; + bbox.min.x = std::min(bbox.min.x, std::min(p1.x, p2.x) - radius); + bbox.min.y = std::min(bbox.min.y, std::min(p1.y, p2.y) - radius); + bbox.max.x = std::max(bbox.max.x, std::max(p1.x, p2.x) + radius); + bbox.max.y = std::max(bbox.max.y, std::max(p1.y, p2.y) + radius); + } + } + } + } + return bbox; +} + +// Returns a bounding box of the wipe tower priming extrusions. +BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print) +{ + BoundingBoxf bbox; + if (print.m_wipe_tower_priming) { + const WipeTower::ToolChangeResult &tcr = *print.m_wipe_tower_priming.get(); + for (size_t i = 1; i < tcr.extrusions.size(); ++ i) { + const WipeTower::Extrusion &e = tcr.extrusions[i]; + if (e.width > 0) { + Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y); + Pointf p2(e.pos.x, e.pos.y); + bbox.merge(p1); + coordf_t radius = 0.5 * e.width; + bbox.min.x = std::min(bbox.min.x, std::min(p1.x, p2.x) - radius); + bbox.min.y = std::min(bbox.min.y, std::min(p1.y, p2.y) - radius); + bbox.max.x = std::max(bbox.max.x, std::max(p1.x, p2.x) + radius); + bbox.max.y = std::max(bbox.max.y, std::max(p1.y, p2.y) + radius); + } + } + } + return bbox; +} + +} diff --git a/xs/src/libslic3r/GCode/PrintExtents.hpp b/xs/src/libslic3r/GCode/PrintExtents.hpp new file mode 100644 index 000000000..2706e2cab --- /dev/null +++ b/xs/src/libslic3r/GCode/PrintExtents.hpp @@ -0,0 +1,30 @@ +// Measure extents of the planned extrusions. +// To be used for collision reporting. + +#ifndef slic3r_PrintExtents_hpp_ +#define slic3r_PrintExtents_hpp_ + +#include "libslic3r.h" + +namespace Slic3r { + +class Print; +class PrintObject; +class BoundingBoxf; + +// Returns a bounding box of a projection of the brim and skirt. +BoundingBoxf get_print_extrusions_extents(const Print &print); + +// Returns a bounding box of a projection of the object extrusions at z <= max_print_z. +BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object, const coord_t max_print_z); + +// Returns a bounding box of a projection of the wipe tower for the layers <= max_print_z. +// The projection does not contain the priming regions. +BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coord_t max_print_z); + +// Returns a bounding box of the wipe tower priming extrusions. +BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print); + +}; + +#endif /* slic3r_PrintExtents_hpp_ */