diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 66ec6e0d78..a5fc0d4fd4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1530,12 +1530,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato print.config().nozzle_temperature_initial_layer.get_at(0)); file.write("; CONFIG_BLOCK_END\n\n"); } else { - file.write_format("; hack-fix: write fake slicer info here so that " - "preprocess_cancellation can process.\n"); - file.write_format("; %s\n\n", std::string(std::string("generated by SuperSlicer " SoftFever_VERSION " on ") + - Slic3r::Utils::local_timestamp()) - .c_str()); - DoExport::export_thumbnails_to_file( thumbnail_cb, print.get_plate_index(), print.full_print_config().option("thumbnails")->values, [&file](const char *sz) { file.write(sz); }, @@ -1563,6 +1557,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } file.write_format("; EXECUTABLE_BLOCK_START\n"); + + // SoftFever + file.write(set_object_info(&print)); + // adds tags for time estimators file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str()); @@ -2474,7 +2472,7 @@ std::vector GCode::sort_print_object_instances( const PrintObject *print_object = layers[layer_id].original_object; //const PrintObject *print_object = layers[layer_id].object(); if (print_object) - out.emplace_back(object_by_extruder, layer_id, *print_object, single_object_instance_idx); + out.emplace_back(object_by_extruder, *print_object, single_object_instance_idx); } } else { // Create mapping from PrintObject* to ObjectByExtruder*. @@ -2502,7 +2500,7 @@ std::vector GCode::sort_print_object_instances( auto it = std::lower_bound(sorted.begin(), sorted.end(), key); if (it != sorted.end() && it->first == &print_object) // ObjectByExtruder for this PrintObject was found. - out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance - print_object.instances().data()); + out.emplace_back(*it->second, print_object, instance->id); } } } @@ -2670,6 +2668,14 @@ namespace Skirt { } // namespace Skirt +inline std::string get_instance_name(const PrintObject *object, const PrintInstance &inst) { + return (boost::format("%1%_id_%2%_copy_%3%") % object->model_object()->name % object->get_id() % inst.id).str(); +} + +inline std::string get_instance_name(const PrintObject *object, size_t inst_id) { + return (boost::format("%1%_id_%2%_copy_%3%") % object->model_object()->name % object->get_id() % inst_id).str(); +} + // In sequential mode, process_layer is called once per each object and its copy, // therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object. // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. @@ -3172,7 +3178,7 @@ GCode::LayerResult GCode::process_layer( if (is_anything_overridden && print_wipe_extrusions == 0) gcode+="; PURGING FINISHED\n"; for (InstanceToPrint &instance_to_print : instances_to_print) { - const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id]; + const LayerToPrint &layer_to_print = layers[instance_to_print.get_object_id()]; // To control print speed of the 1st object layer printed over raft interface. bool object_layer_over_raft = layer_to_print.object_layer && layer_to_print.object_layer->id() > 0 && instance_to_print.print_object.slicing_parameters().raft_layers() == layer_to_print.object_layer->id(); @@ -3181,11 +3187,19 @@ GCode::LayerResult GCode::process_layer( m_object_layer_over_raft = object_layer_over_raft; if (m_config.reduce_crossing_wall) m_avoid_crossing_perimeters.init_layer(*m_layer); + bool reset_e = false; if (this->config().gcode_label_objects) { - gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n"; - if (!m_config.use_relative_e_distances) - gcode += m_writer.reset_e(true); + gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.print_object.get_id()) + " copy " + std::to_string(instance_to_print.instance_id) + "\n"; + reset_e = true; } + if (this->config().exclude_object && print.config().gcode_flavor.value == gcfKlipper) { + gcode += std::string("EXCLUDE_OBJECT_START NAME=") + + get_instance_name(&instance_to_print.print_object, instance_to_print.instance_id) + "\n"; + reset_e = true; + } + // ref to: https://github.com/SoftFever/OrcaSlicer/pull/205/commits/7f1fe0bd544077626080aa1a9a0576aa735da1a4#r1083470162 + if (reset_e && !m_config.use_relative_e_distances) + gcode += m_writer.reset_e(true); m_extrusion_quality_estimator.set_current_object(&instance_to_print.print_object); // When starting a new object, use the external motion planner for the first travel move. @@ -3196,7 +3210,7 @@ GCode::LayerResult GCode::process_layer( m_last_obj_copy = this_object_copy; this->set_origin(unscale(offset)); if (instance_to_print.object_by_extruder.support != nullptr) { - m_layer = layers[instance_to_print.layer_id].support_layer; + m_layer = layers[instance_to_print.get_object_id()].support_layer; m_object_layer_over_raft = false; //BBS: print supports' brims first @@ -3274,10 +3288,16 @@ GCode::LayerResult GCode::process_layer( gcode += this->extrude_infill(print,by_region_specific, true); } if (this->config().gcode_label_objects) { - gcode += std::string("; stop printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n"; - if (!m_config.use_relative_e_distances) - gcode += m_writer.reset_e(true); + gcode += std::string("; stop printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.print_object.get_id()) + " copy " + std::to_string(instance_to_print.instance_id) + "\n"; + reset_e = true; } + if (this->config().exclude_object && print.config().gcode_flavor.value == gcfKlipper) { + gcode += std::string("EXCLUDE_OBJECT_END NAME=") + + get_instance_name(&instance_to_print.print_object, instance_to_print.instance_id) + "\n"; + reset_e = true; + } + if (reset_e && !m_config.use_relative_e_distances) + gcode += m_writer.reset_e(true); } } } @@ -4591,6 +4611,37 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) return gcode; } +inline std::string polygon_to_string(const Polygon &polygon) { + std::ostringstream gcode; + gcode << "["; + for (const Point &p : polygon.points) { + gcode << "[" << unscaled(p.x()) << "," << unscaled(p.y()) << "],"; + } + gcode << "[" << unscaled(polygon.points.front().x()) << "," << unscaled(polygon.points.front().y()) << "]"; + gcode << "]"; + return gcode.str(); +} + // this function iterator PrintObject and assign a seqential id to each object. +// this id is used to generate unique object id for each object. +std::string GCode::set_object_info(Print* print) { + std::ostringstream gcode; + size_t object_id = 0; + for (PrintObject* object : print->objects()) { + object->set_id(object_id++); + size_t inst_id = 0; + for (PrintInstance &inst : object->instances()) { + inst.id = inst_id; + if (this->config().exclude_object && print->config().gcode_flavor.value == gcfKlipper) { + auto bbox = inst.get_bounding_box(); + gcode << "EXCLUDE_OBJECT_DEFINE NAME=" << get_instance_name(object, inst) + << " CENTER=" << bbox.center().x() << "," << bbox.center().y() + << " POLYGON=" << polygon_to_string(inst.get_convex_hull_2d()) << "\n"; + } + } + } + return gcode.str(); +} + // convert a model-space scaled point into G-code coordinates Vec2d GCode::point_to_gcode(const Point &point) const { @@ -4714,4 +4765,10 @@ void GCode::ObjectByExtruder::Island::Region::append(const Type type, const Extr } } -} // namespace Slic3r + +// 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. + +inline size_t GCode::InstanceToPrint::get_object_id() const { return print_object.get_id(); } + +} // namespace Slic3r diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 9c5716499e..0fc4271758 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -202,6 +202,9 @@ public: std::string unretract() { return m_writer.unlift() + m_writer.unretract(); } std::string set_extruder(unsigned int extruder_id, double print_z); + // SoftFever + std::string set_object_info(Print* print); + // append full config to the given string static void append_full_config(const Print& print, std::string& str); @@ -377,13 +380,13 @@ private: 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) {} + InstanceToPrint(ObjectByExtruder &object_by_extruder, const PrintObject &print_object, size_t instance_id) : + object_by_extruder(object_by_extruder), 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; + size_t get_object_id() const; const PrintObject &print_object; // Instance idx of the copy of a print object. const size_t instance_id; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 3d4d26fb86..e26c136b18 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1286,18 +1286,21 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const } // This returns an accurate snug bounding box of the transformed object instance, without the translation applied. -BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_translate) const -{ - BoundingBoxf3 bb; - const Transform3d& inst_matrix = this->instances[instance_idx]->get_transformation().get_matrix(dont_translate); - for (ModelVolume *v : this->volumes) - { - if (v->is_model_part()) - bb.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix())); - } - return bb; +BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_translate) const { + return instance_bounding_box(*this->instances[instance_idx], dont_translate); } +BoundingBoxf3 ModelObject::instance_bounding_box(const ModelInstance &instance, bool dont_translate) const { + BoundingBoxf3 bbox; + const auto& inst_mat = instance.get_transformation().get_matrix(dont_translate); + for (auto vol : this->volumes) { + if (vol->is_model_part()) + bbox.merge(vol->mesh().transformed_bounding_box(inst_mat * vol->get_matrix())); + } + return bbox; +} + + //BBS: add convex bounding box BoundingBoxf3 ModelObject::instance_convex_hull_bounding_box(size_t instance_idx, bool dont_translate) const { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 6b53a45af0..551a9072ee 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -345,6 +345,8 @@ public: const BoundingBoxf3& raw_bounding_box() const; // A snug bounding box around the transformed non-modifier object volumes. BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const; + BoundingBoxf3 instance_bounding_box(const ModelInstance& instance, bool dont_translate = false) const; + // A snug bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes. const BoundingBoxf3& raw_mesh_bounding_box() const; // A snug bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes. diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 3acc9530bb..d627479111 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -755,7 +755,8 @@ static std::vector s_Preset_print_options { "role_based_wipe_speed", "wipe_speed", "accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", "bridge_density", "precise_outer_wall", "overhang_speed_classic", "bridge_acceleration", "sparse_infill_acceleration", "internal_solid_infill_acceleration", "tree_support_adaptive_layer_height", "tree_support_auto_brim", - "tree_support_brim_width", "gcode_comments", "gcode_label_objects", "initial_layer_travel_speed" + "tree_support_brim_width", "gcode_comments", "gcode_label_objects", + "initial_layer_travel_speed", "exclude_object" }; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 2f3a32acfb..b509b35dca 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -159,7 +159,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "accel_to_decel_factor", "wipe_on_loops", "gcode_comments", - "gcode_label_objects" + "gcode_label_objects", + "exclude_object" }; static std::unordered_set steps_ignore; @@ -3329,4 +3330,14 @@ int Print::load_cached_data(const std::string& directory) return ret; } +BoundingBoxf3 PrintInstance::get_bounding_box() { + return print_object->model_object()->instance_bounding_box(*model_instance, false); +} + +Polygon PrintInstance::get_convex_hull_2d() { + Polygon poly = print_object->model_object()->convex_hull_2d(model_instance->get_matrix()); + poly.douglas_peucker(0.1); + return poly; +} + } // namespace Slic3r diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 60704e8a38..413be11f05 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -191,6 +191,13 @@ struct PrintInstance const ModelInstance *model_instance; // Shift of this instance's center into the world coordinates. Point shift; + + BoundingBoxf3 get_bounding_box(); + Polygon get_convex_hull_2d(); + // SoftFever + // + // instance id + size_t id; }; typedef std::vector PrintInstances; @@ -292,6 +299,7 @@ public: Transform3d trafo_centered() const { Transform3d t = this->trafo(); t.pretranslate(Vec3d(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0)); return t; } const PrintInstances& instances() const { return m_instances; } + PrintInstances &instances() { return m_instances; } // Whoever will get a non-const pointer to PrintObject will be able to modify its layers. LayerPtrs& layers() { return m_layers; } @@ -417,7 +425,12 @@ public: // BBS: Boundingbox of the first layer BoundingBox firstLayerObjectBrimBoundingBox; -private: + + // SoftFever + size_t get_id() const { return m_id; } + void set_id(size_t id) { m_id = id; } + + private: // to be called from Print only. friend class Print; @@ -501,6 +514,12 @@ private: PrintObject* m_shared_object{ nullptr }; + + // SoftFever + // + // object id + size_t m_id; + public: //BBS: When printing multi-material objects, this settings will make slicer to clip the overlapping object parts one by the other. //(2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc). diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index cbf7060f94..8a06bbe0a4 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1813,7 +1813,13 @@ void PrintConfigDef::init_fff_params() "Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill."); def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(1)); - + + def = this->add("exclude_object", coBool); + def->label = L("Exclude objects"); + def->tooltip = L("Enable this option to add EXCLUDE OBJECT command in g-code"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(1)); + def = this->add("gcode_comments", coBool); def->label = L("Verbose G-code"); def->tooltip = L("Enable this to get a commented G-code file, with each line explained by a descriptive text. " diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 79a2dc4009..a7b4847f4d 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1003,6 +1003,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( // SoftFever ((ConfigOptionPercents, filament_shrink)) ((ConfigOptionBool, gcode_label_objects)) + ((ConfigOptionBool, exclude_object)) ((ConfigOptionBool, gcode_comments)) ) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index a970357337..3ba8806294 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -697,6 +697,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_line(el, gcflavor == gcfKlipper); if(gcflavor == gcfKlipper) toggle_field("accel_to_decel_factor", config->opt_bool("accel_to_decel_enable")); + + toggle_field("exclude_object", gcflavor == gcfKlipper); + } void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 40ba091a9d..0fd751dd7c 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2058,6 +2058,7 @@ void TabPrint::build() optgroup->append_single_option_line("gcode_add_line_number"); optgroup->append_single_option_line("gcode_comments"); optgroup->append_single_option_line("gcode_label_objects"); + optgroup->append_single_option_line("exclude_object"); Option option = optgroup->get_option("filename_format"); // option.opt.full_width = true; option.opt.is_code = true;