ENH: support exclude objects for klipper printer

Thanks OrcaSlicer.

This is just function for klipper printer.
NEVER used for BBL printer.

Signed-off-by: salt.wei <salt.wei@bambulab.com>
Change-Id: I910abceb67f4dcb4260f74f5dd1b4fb614812670
This commit is contained in:
salt.wei 2023-06-19 11:07:21 +08:00 committed by Lane.Wei
parent 0daca9d329
commit e567afdcb5
11 changed files with 146 additions and 5 deletions

View file

@ -1586,6 +1586,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
#endif
file.write_format("; EXECUTABLE_BLOCK_START\n");
// SoftFever: Orca's implementation for skipping object, for klipper firmware printer only
if (this->config().exclude_object && print.config().gcode_flavor.value == gcfKlipper)
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());
@ -2656,6 +2661,18 @@ namespace Skirt {
} // namespace Skirt
inline std::string get_instance_name(const PrintObject* object, size_t inst_id) {
auto obj_name = object->model_object()->name;
// replace space in obj_name with '-'
std::replace(obj_name.begin(), obj_name.end(), ' ', '_');
return (boost::format("%1%_id_%2%_copy_%3%") % obj_name % object->get_klipper_object_id() % inst_id).str();
}
inline std::string get_instance_name(const PrintObject* object, const PrintInstance& inst) {
return get_instance_name(object, inst.id);
}
// 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.
@ -3156,6 +3173,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 auto& inst = instance_to_print.print_object.instances()[instance_to_print.instance_id];
const LayerToPrint &layer_to_print = layers[instance_to_print.layer_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 &&
@ -3173,6 +3191,16 @@ GCode::LayerResult GCode::process_layer(
}
m_writer.set_object_start_str(start_str);
}
//Orca's implementation for skipping object, for klipper firmware printer only
bool reset_e = false;
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, inst.id) + "\n";
reset_e = true;
}
if (reset_e && !RELATIVE_E_AXIS)
gcode += m_writer.reset_e(true);
// When starting a new object, use the external motion planner for the first travel move.
const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
@ -3267,7 +3295,14 @@ GCode::LayerResult GCode::process_layer(
end_str += "M625\n";
m_writer.set_object_end_str(end_str);
}
//Orca's implementation for skipping object, for klipper firmware printer only
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, inst.id) + "\n";
reset_e = true;
}
if (reset_e && !RELATIVE_E_AXIS)
gcode += m_writer.reset_e(true);
}
}
}
@ -4553,6 +4588,41 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
return gcode;
}
inline std::string polygon_to_string(const Polygon& polygon, Print* print) {
std::ostringstream gcode;
gcode << "[";
for (const Point& p : polygon.points) {
const auto v = print->translate_to_print_space(p);
gcode << "[" << v.x() << "," << v.y() << "],";
}
const auto first_v = print->translate_to_print_space(polygon.points.front());
gcode << "[" << first_v.x() << "," << first_v.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_klipper_object_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();
auto center = print->translate_to_print_space(Vec2d(bbox.center().x(), bbox.center().y()));
gcode << "EXCLUDE_OBJECT_DEFINE NAME=" << get_instance_name(object, inst) << " CENTER=" << center.x()
<< "," << center.y() << " POLYGON=" << polygon_to_string(inst.get_convex_hull_2d(), print)
<< "\n";
}
}
}
return gcode.str();
}
// convert a model-space scaled point into G-code coordinates
Vec2d GCode::point_to_gcode(const Point &point) const
{

View file

@ -195,6 +195,9 @@ public:
void set_layer_count(unsigned int value) { m_layer_count = value; }
void apply_print_config(const PrintConfig &print_config);
// 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);

View file

@ -1384,6 +1384,16 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
return bb;
}
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
{

View file

@ -423,6 +423,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.

View file

@ -785,7 +785,9 @@ static std::vector<std::string> s_Preset_print_options {
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk", "travel_jerk",
"filter_out_gap_fill",
// calib
"print_flow_ratio"
"print_flow_ratio",
//Orca
"exclude_object"
};
static std::vector<std::string> s_Preset_filament_options {

View file

@ -160,7 +160,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"initial_layer_jerk",
"travel_jerk",
"inner_wall_acceleration",
"sparse_infill_acceleration"
"sparse_infill_acceleration",
"exclude_object",
};
static std::unordered_set<std::string> steps_ignore;
@ -2004,6 +2005,16 @@ std::vector<Point> Print::first_layer_wipe_tower_corners(bool check_wipe_tower_e
return corners;
}
//SoftFever
Vec2d Print::translate_to_print_space(const Vec2d& point) const {
//const BoundingBoxf bed_bbox(config().printable_area.values);
return Vec2d(point(0) - m_origin(0), point(1) - m_origin(1));
}
Vec2d Print::translate_to_print_space(const Point& point) const {
return Vec2d(unscaled(point.x()) - m_origin(0), unscaled(point.y()) - m_origin(1));
}
void Print::finalize_first_layer_convex_hull()
{
append(m_first_layer_convex_hull.points, m_skirt_convex_hull);
@ -3495,4 +3506,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

View file

@ -194,6 +194,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<PrintInstance> PrintInstances;
@ -295,6 +302,7 @@ public:
Transform3d trafo_centered() const
{ Transform3d t = this->trafo(); t.pretranslate(Vec3d(- unscale<double>(m_center_offset.x()), - unscale<double>(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; }
@ -420,6 +428,11 @@ public:
// BBS: Boundingbox of the first layer
BoundingBox firstLayerObjectBrimBoundingBox;
// SoftFever
size_t get_klipper_object_id() const { return m_klipper_object_id; }
void set_klipper_object_id(size_t id) { m_klipper_object_id = id; }
private:
// to be called from Print only.
friend class Print;
@ -504,6 +517,11 @@ private:
PrintObject* m_shared_object{ nullptr };
// SoftFever
//
// object id for klipper firmware only
size_t m_klipper_object_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).
@ -789,11 +807,14 @@ public:
// Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim.
std::vector<Point> first_layer_wipe_tower_corners(bool check_wipe_tower_existance=true) const;
//SoftFever
CalibMode & calib_mode() { return m_calib_params.mode; }
const CalibMode& calib_mode() const { return m_calib_params.mode; }
void set_calib_params(const Calib_Params &params);
const Calib_Params& calib_params() const { return m_calib_params; }
Vec2d translate_to_print_space(const Vec2d& point) const;
// scaled point
Vec2d translate_to_print_space(const Point& point) const;
protected:
// Invalidates the step, and its depending steps in Print.

View file

@ -1704,6 +1704,13 @@ void PrintConfigDef::init_fff_params()
def->readonly = false;
def->set_default_value(new ConfigOptionEnum<GCodeFlavor>(gcfMarlinLegacy));
//OrcaSlicer
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 for klipper firmware printer");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(1));
//BBS
def = this->add("infill_combination", coBool);
def->label = L("Infill combination");

View file

@ -976,7 +976,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloat, travel_jerk))
// BBS: move from PrintObjectConfig
((ConfigOptionBool, independent_support_layer_height))
((ConfigOptionBool, independent_support_layer_height))
((ConfigOptionBool, exclude_object))
)
// This object is mapped to Perl as Slic3r::Config::Full.

View file

@ -703,6 +703,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_field("accel_to_decel_factor", config->opt_bool("accel_to_decel_enable"));
}
toggle_line("exclude_object", gcflavor == gcfKlipper);
}
void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/)

View file

@ -2050,6 +2050,7 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("G-code output"), L"param_gcode");
optgroup->append_single_option_line("reduce_infill_retraction");
optgroup->append_single_option_line("gcode_add_line_number");
optgroup->append_single_option_line("exclude_object");
Option option = optgroup->get_option("filename_format");
option.opt.full_width = true;
optgroup->append_single_option_line(option);
@ -3627,6 +3628,8 @@ void TabPrinter::toggle_options()
//}
if (m_active_page->title() == "Basic information") {
toggle_option("single_extruder_multi_material", have_multiple_extruders);
//BBS: gcode_flavore of BBL printer can't be edited and changed
toggle_option("gcode_flavor", !is_BBL_printer);
auto flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value;
bool is_marlin_flavor = flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware;