ENH: buildvolume: add logic to support extruder_printable_height

jira: no-jira
Change-Id: I962c4aed8c536c0fd8b89ae090cd0463c5d645db
(cherry picked from commit 43773d77010492453473797e77e83e9a4630c25f)
This commit is contained in:
lane.wei
2024-11-23 12:26:54 +08:00
committed by Noisyfox
parent bc753a286b
commit 671dfa5f0c
18 changed files with 84 additions and 46 deletions

View File

@@ -251,8 +251,8 @@ void Bed3D::Axes::render()
}
//BBS: add part plate logic
bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_height, std::vector<Pointfs> extruder_areas, const std::string& custom_model, bool force_as_custom,
const Vec2d& position, bool with_reset)
bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_height, std::vector<Pointfs> extruder_areas, std::vector<double> extruder_heights, const std::string& custom_model, bool force_as_custom,
const Vec2d position, bool with_reset)
{
/*auto check_texture = [](const std::string& texture) {
boost::system::error_code ec; // so the exists call does not throw (e.g. after a permission problem)
@@ -289,7 +289,7 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig
}
//BBS: add position related logic
if (m_bed_shape == printable_area && m_build_volume.printable_height() == printable_height && m_type == type && m_model_filename == model_filename && position == m_position && m_extruder_shapes == extruder_areas)
if (m_bed_shape == printable_area && m_build_volume.printable_height() == printable_height && m_type == type && m_model_filename == model_filename && position == m_position && m_extruder_shapes == extruder_areas && m_extruder_heights == extruder_heights)
// No change, no need to update the UI.
return false;
@@ -298,6 +298,7 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig
m_position = position;
m_bed_shape = printable_area;
m_extruder_shapes = extruder_areas;
m_extruder_heights = extruder_heights;
if ((position(0) != 0) || (position(1) != 0)) {
Pointfs new_bed_shape;
for (const Vec2d& p : m_bed_shape) {
@@ -313,10 +314,10 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig
}
new_extruder_shapes.push_back(new_extruder_shape);
}
m_build_volume = BuildVolume { new_bed_shape, printable_height, new_extruder_shapes };
m_build_volume = BuildVolume { new_bed_shape, printable_height, new_extruder_shapes, m_extruder_heights };
}
else
m_build_volume = BuildVolume { printable_area, printable_height, m_extruder_shapes };
m_build_volume = BuildVolume { printable_area, printable_height, m_extruder_shapes, m_extruder_heights };
m_type = type;
//m_texture_filename = texture_filename;
m_model_filename = model_filename;
@@ -352,7 +353,7 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig
//BBS: add api to set position for partplate related bed
void Bed3D::set_position(Vec2d& position)
{
set_shape(m_bed_shape, m_build_volume.printable_height(), m_extruder_shapes, m_model_filename, false, position, false);
set_shape(m_bed_shape, m_build_volume.printable_height(), m_extruder_shapes, m_extruder_heights, m_model_filename, false, position, false);
}
void Bed3D::set_axes_mode(bool origin)

View File

@@ -116,6 +116,7 @@ private:
Vec2d m_position{ Vec2d::Zero() };
std::vector<Vec2d> m_bed_shape;
std::vector<std::vector<Vec2d>> m_extruder_shapes;
std::vector<double> m_extruder_heights;
bool m_is_dark = false;
public:
@@ -127,8 +128,8 @@ public:
//FIXME if the build volume max print height is updated, this function still returns zero
// as this class does not use it, thus there is no need to update the UI.
// BBS
bool set_shape(const Pointfs& printable_area, const double printable_height, std::vector<Pointfs> extruder_areas, const std::string& custom_model, bool force_as_custom = false,
const Vec2d& position = Vec2d::Zero(), bool with_reset = true);
bool set_shape(const Pointfs& printable_area, const double printable_height, std::vector<Pointfs> extruder_areas, std::vector<double> extruder_heights, const std::string& custom_model, bool force_as_custom = false,
const Vec2d position = Vec2d::Zero(), bool with_reset = true);
void set_position(Vec2d& position);
void set_axes_mode(bool origin);

View File

@@ -1077,7 +1077,7 @@ bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, Mo
GUI::PartPlate* curr_plate = GUI::wxGetApp().plater()->get_partplate_list().get_selected_plate();
const Pointfs& pp_bed_shape = curr_plate->get_shape();
BuildVolume plate_build_volume(pp_bed_shape, build_volume.printable_height(), build_volume.extruder_areas());
BuildVolume plate_build_volume(pp_bed_shape, build_volume.printable_height(), build_volume.extruder_areas(), build_volume.extruder_heights());
const std::vector<BoundingBoxf3>& exclude_areas = curr_plate->get_exclude_areas();
std::map<ModelObject*, std::map<int, std::set<int>>> objects_unprintable_filaments;
@@ -1287,6 +1287,7 @@ bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, Mo
if (filament_maps[filament - 1] == extruder_id)
{
object_filament_info.manual_filaments.emplace(filament, extruder_id);
object_results->filament_maps[filament] = extruder_id;
conflict_filaments_set.emplace(filament);
}
}

View File

@@ -69,6 +69,7 @@ struct ObjectFilamentInfo {
struct ObjectFilamentResults {
FilamentMapMode mode;
std::vector<int> filaments; //filaments has conflicts
std::map<int, int> filament_maps; //filament maps
std::vector<ModelObject*> partly_outside_objects; //partly outside objects
std::vector<ObjectFilamentInfo> object_filaments;

View File

@@ -25,7 +25,7 @@ namespace GUI {
BedShape::BedShape(const Pointfs& points)
{
m_build_volume = { points, 0.f, {} };
m_build_volume = { points, 0.f, {}, {} };
}
static std::string get_option_label(BedShape::Parameter param)

View File

@@ -1009,6 +1009,7 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr
//BBS: add bed exclude area
Pointfs bed_exclude_area = Pointfs();
std::vector<Pointfs> extruder_areas;
std::vector<double> extruder_heights;
std::string texture;
std::string model;
@@ -1030,8 +1031,10 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr
if (!gcode_result.extruder_areas.empty())
extruder_areas = gcode_result.extruder_areas;
if (!gcode_result.extruder_heights.empty())
extruder_heights = gcode_result.extruder_heights;
wxGetApp().plater()->set_bed_shape(printable_area, bed_exclude_area, gcode_result.printable_height, extruder_areas, texture, model, gcode_result.printable_area.empty());
wxGetApp().plater()->set_bed_shape(printable_area, bed_exclude_area, gcode_result.printable_height, extruder_areas, extruder_heights, texture, model, gcode_result.printable_area.empty());
}
/*else {
// adjust printbed size in dependence of toolpaths bbox

View File

@@ -1386,6 +1386,18 @@ void GLCanvas3D::construct_error_string(ObjectFilamentResults& object_result, st
error_string += _u8L("Please solve the problem by moving them within the build volume.\n");
}
else {
error_string += _u8L("In the Filament manual-matching mode, Following filament->extruder maps: \n");
for (auto& filament: object_result.filaments)
{
error_string += std::to_string(filament) + "->" + std::to_string(object_result.filament_maps[filament]) + "\n";
}
error_string += "cannot be printed as they are placed in the unprintable area of the corresponding extruder. This may be caused by the following objects:\n";
for(ObjectFilamentInfo& object_filament: object_result.object_filaments)
{
error_string += object_filament.object->name;
error_string += "\n";
}
error_string += _u8L("Please solve the problem by moving them within the build volume.\n");
}
}
}

View File

@@ -846,7 +846,7 @@ void MainFrame::update_layout()
{
m_main_sizer->Add(m_plater, 1, wxEXPAND);
//BBS: add bed exclude area
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, {}, 0.0, {}, {}, {}, true);
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, {}, 0.0, {}, {}, {}, {}, true);
m_plater->get_collapse_toolbar().set_enabled(false);
m_plater->enable_sidebar(false);
m_plater->Show();

View File

@@ -2121,7 +2121,7 @@ bool PartPlate::check_outside(int obj_id, int instance_id, BoundingBoxf3* boundi
// Orca: For sinking object, we use a more expensive algorithm so part below build plate won't be considered
if (plate_box.intersects(instance_box)) {
// TODO: FIXME: this does not take exclusion area into account
const BuildVolume build_volume(get_shape(), m_plater->build_volume().printable_height(), m_extruder_areas);
const BuildVolume build_volume(get_shape(), m_plater->build_volume().printable_height(), m_extruder_areas, m_extruder_heights);
const auto state = instance->calc_print_volume_state(build_volume);
outside = state == ModelInstancePVS_Partly_Outside;
}
@@ -2668,9 +2668,10 @@ void PartPlate::generate_exclude_polygon(ExPolygon &exclude_polygon)
exclude_polygon.contour.make_counter_clockwise();
}
bool PartPlate::set_shape(const Pointfs& shape, const Pointfs& exclude_areas, const std::vector<Pointfs>& extruder_areas, Vec2d position, float height_to_lid, float height_to_rod)
bool PartPlate::set_shape(const Pointfs& shape, const Pointfs& exclude_areas, const std::vector<Pointfs>& extruder_areas, const std::vector<double>& extruder_heights, Vec2d position, float height_to_lid, float height_to_rod)
{
Pointfs new_shape, new_exclude_areas;
m_extruder_heights = extruder_heights;
for (const Vec2d& p : shape) {
new_shape.push_back(Vec2d(p.x() + position.x(), p.y() + position.y()));
}
@@ -3675,7 +3676,7 @@ void PartPlateList::reset_size(int width, int depth, int height, bool reload_obj
m_plate_height = height;
update_all_plates_pos_and_size(false, false, true);
if (update_shapes) {
set_shapes(m_shape, m_exclude_areas, m_extruder_areas, m_logo_texture_filename, m_height_to_lid, m_height_to_rod);
set_shapes(m_shape, m_exclude_areas, m_extruder_areas, m_extruder_heights, m_logo_texture_filename, m_height_to_lid, m_height_to_rod);
}
if (reload_objects)
reload_all_objects();
@@ -3760,7 +3761,7 @@ void PartPlateList::reinit()
//reset plate 0's position
Vec2d pos = compute_shape_position(0, m_plate_cols);
m_plate_list[0]->set_shape(m_shape, m_exclude_areas, m_extruder_areas, pos, m_height_to_lid, m_height_to_rod);
m_plate_list[0]->set_shape(m_shape, m_exclude_areas, m_extruder_areas, m_extruder_heights, pos, m_height_to_lid, m_height_to_rod);
//reset unprintable plate's position
Vec3d origin2 = compute_origin_for_unprintable();
unprintable_plate.set_pos_and_size(origin2, m_plate_width, m_plate_depth, m_plate_height, false);
@@ -3777,7 +3778,7 @@ void PartPlateList::reinit()
void PartPlateList::update_plates()
{
update_all_plates_pos_and_size(true, false);
set_shapes(m_shape, m_exclude_areas, m_extruder_areas, m_logo_texture_filename, m_height_to_lid, m_height_to_rod);
set_shapes(m_shape, m_exclude_areas, m_extruder_areas, m_extruder_heights, m_logo_texture_filename, m_height_to_lid, m_height_to_rod);
}
int PartPlateList::create_plate(bool adjust_position)
@@ -3810,7 +3811,7 @@ int PartPlateList::create_plate(bool adjust_position)
plate->set_index(new_index);
Vec2d pos = compute_shape_position(new_index, cols);
plate->set_shape(m_shape, m_exclude_areas, m_extruder_areas, pos, m_height_to_lid, m_height_to_rod);
plate->set_shape(m_shape, m_exclude_areas, m_extruder_areas, m_extruder_heights, pos, m_height_to_lid, m_height_to_rod);
m_plate_list.emplace_back(plate);
update_plate_cols();
if (old_cols != cols)
@@ -3818,7 +3819,7 @@ int PartPlateList::create_plate(bool adjust_position)
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":old_cols %1% -> new_cols %2%") % old_cols % cols;
//update the origin of each plate
update_all_plates_pos_and_size(adjust_position, false);
set_shapes(m_shape, m_exclude_areas, m_extruder_areas, m_logo_texture_filename, m_height_to_lid, m_height_to_rod);
set_shapes(m_shape, m_exclude_areas, m_extruder_areas, m_extruder_heights, m_logo_texture_filename, m_height_to_lid, m_height_to_rod);
if (m_plater) {
Vec2d pos = compute_shape_position(m_current_plate, cols);
@@ -3989,7 +3990,7 @@ int PartPlateList::delete_plate(int index)
//update render shapes
Vec2d pos = compute_shape_position(i, m_plate_cols);
plate->set_shape(m_shape, m_exclude_areas, m_extruder_areas, pos, m_height_to_lid, m_height_to_rod);
plate->set_shape(m_shape, m_exclude_areas, m_extruder_areas, m_extruder_heights, pos, m_height_to_lid, m_height_to_rod);
}
//update current_plate if delete current
@@ -4012,7 +4013,7 @@ int PartPlateList::delete_plate(int index)
{
//update the origin of each plate
update_all_plates_pos_and_size();
set_shapes(m_shape, m_exclude_areas, m_extruder_areas, m_logo_texture_filename, m_height_to_lid, m_height_to_rod);
set_shapes(m_shape, m_exclude_areas, m_extruder_areas, m_extruder_heights, m_logo_texture_filename, m_height_to_lid, m_height_to_rod);
}
else
{
@@ -5109,12 +5110,13 @@ void PartPlateList::select_plate_view()
m_plater->get_camera().select_view("topfront");
}
bool PartPlateList::set_shapes(const Pointfs& shape, const Pointfs& exclude_areas, const std::vector<Pointfs>& extruder_areas, const std::string& texture_filename, float height_to_lid, float height_to_rod)
bool PartPlateList::set_shapes(const Pointfs& shape, const Pointfs& exclude_areas, const std::vector<Pointfs>& extruder_areas, const std::vector<double>& extruder_heights, const std::string& texture_filename, float height_to_lid, float height_to_rod)
{
const std::lock_guard<std::mutex> local_lock(m_plates_mutex);
m_shape = shape;
m_exclude_areas = exclude_areas;
m_extruder_areas = extruder_areas;
m_extruder_heights = extruder_heights;
m_height_to_lid = height_to_lid;
m_height_to_rod = height_to_rod;
@@ -5128,7 +5130,7 @@ bool PartPlateList::set_shapes(const Pointfs& shape, const Pointfs& exclude_area
Vec2d pos;
pos = compute_shape_position(i, m_plate_cols);
plate->set_shape(shape, exclude_areas, extruder_areas, pos, height_to_lid, height_to_rod);
plate->set_shape(shape, exclude_areas, extruder_areas, extruder_heights, pos, height_to_lid, height_to_rod);
}
is_load_bedtype_textures = false; //reload textures
is_load_extruder_only_area_textures = false; // reload textures
@@ -5308,7 +5310,7 @@ int PartPlateList::rebuild_plates_after_deserialize(std::vector<bool>& previous_
}
update_plate_cols();
update_all_plates_pos_and_size(false, false, false, false);
set_shapes(m_shape, m_exclude_areas, m_extruder_areas, m_logo_texture_filename, m_height_to_lid, m_height_to_rod);
set_shapes(m_shape, m_exclude_areas, m_extruder_areas, m_extruder_heights, m_logo_texture_filename, m_height_to_lid, m_height_to_rod);
for (unsigned int i = 0; i < (unsigned int)m_plate_list.size(); ++i)
{
bool need_reset_print = false;
@@ -5643,7 +5645,7 @@ int PartPlateList::load_gcode_files()
//BoundingBoxf3 print_volume = m_plate_list[i]->get_bounding_box(false);
//print_volume.max(2) = this->m_plate_height;
//print_volume.min(2) = -1e10;
m_model->update_print_volume_state({m_plate_list[i]->get_shape(), (double)this->m_plate_height, m_plate_list[i]->get_extruder_areas() });
m_model->update_print_volume_state({m_plate_list[i]->get_shape(), (double)this->m_plate_height, m_plate_list[i]->get_extruder_areas(), m_plate_list[i]->get_extruder_heights() });
if (!m_plate_list[i]->load_gcode_from_file(m_plate_list[i]->m_gcode_path_from_3mf))
ret ++;

View File

@@ -121,6 +121,7 @@ private:
Pointfs m_shape;
Pointfs m_exclude_area;
std::vector<Pointfs> m_extruder_areas;
std::vector<double> m_extruder_heights;
BoundingBoxf3 m_bounding_box;
BoundingBoxf3 m_extended_bounding_box;
mutable std::vector<BoundingBoxf3> m_exclude_bounding_box;
@@ -365,8 +366,9 @@ public:
/*rendering related functions*/
const Pointfs& get_shape() const { return m_shape; }
bool set_shape(const Pointfs& shape, const Pointfs& exclude_areas, const std::vector<Pointfs>& extruder_areas, Vec2d position, float height_to_lid, float height_to_rod);
bool set_shape(const Pointfs& shape, const Pointfs& exclude_areas, const std::vector<Pointfs>& extruder_areas, const std::vector<double>& extruder_heights, Vec2d position, float height_to_lid, float height_to_rod);
const std::vector<Pointfs>& get_extruder_areas() const { return m_extruder_areas; }
const std::vector<double>& get_extruder_heights() const { return m_extruder_heights; }
bool contains(const Vec3d& point) const;
bool contains(const GLVolume& v) const;
bool contains(const BoundingBoxf3& bb) const;
@@ -571,6 +573,7 @@ class PartPlateList : public ObjectBase
Pointfs m_shape;
Pointfs m_exclude_areas;
std::vector<Pointfs> m_extruder_areas;
std::vector<double> m_extruder_heights;
BoundingBoxf3 m_bounding_box;
bool m_intialized;
std::string m_logo_texture_filename;
@@ -830,7 +833,7 @@ public:
int select_plate_by_obj(int obj_index, int instance_index);
void calc_bounding_boxes();
void select_plate_view();
bool set_shapes(const Pointfs& shape, const Pointfs& exclude_areas, const std::vector<Pointfs>& extruder_areas, const std::string& custom_texture, float height_to_lid, float height_to_rod);
bool set_shapes(const Pointfs& shape, const Pointfs& exclude_areas, const std::vector<Pointfs>& extruder_areas, const std::vector<double>& extruder_heights, const std::string& custom_texture, float height_to_lid, float height_to_rod);
void set_hover_id(int id);
void reset_hover_id();
bool intersects(const BoundingBoxf3 &bb);

View File

@@ -2953,7 +2953,7 @@ struct Plater::priv
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
// Sets m_bed.m_polygon to limit the object placement.
//BBS: add bed exclude area
void set_bed_shape(const Pointfs& shape, const Pointfs& exclude_areas, const double printable_height, std::vector<Pointfs> extruder_areas, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
void set_bed_shape(const Pointfs& shape, const Pointfs& exclude_areas, const double printable_height, std::vector<Pointfs> extruder_areas, std::vector<double> extruder_heights, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
bool can_delete() const;
bool can_delete_all() const;
@@ -5622,7 +5622,7 @@ void Plater::priv::update_print_volume_state()
{
//BBS: use the plate's bounding box instead of the bed's
PartPlate* pp = partplate_list.get_curr_plate();
BuildVolume build_volume(pp->get_shape(), this->bed.build_volume().printable_height(), this->bed.build_volume().extruder_areas());
BuildVolume build_volume(pp->get_shape(), this->bed.build_volume().printable_height(), this->bed.build_volume().extruder_areas(), this->bed.build_volume().extruder_heights());
this->model.update_print_volume_state(build_volume);
}
@@ -8728,7 +8728,7 @@ bool Plater::priv::show_publish_dlg(bool show)
}
//BBS: add bed exclude area
void Plater::priv::set_bed_shape(const Pointfs& shape, const Pointfs& exclude_areas, const double printable_height, std::vector<Pointfs> extruder_areas, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
void Plater::priv::set_bed_shape(const Pointfs& shape, const Pointfs& exclude_areas, const double printable_height, std::vector<Pointfs> extruder_areas, std::vector<double> extruder_heights, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
{
//Orca: reduce resolution for large bed printer
BoundingBoxf bed_size = get_extents(shape);
@@ -8739,7 +8739,7 @@ void Plater::priv::set_bed_shape(const Pointfs& shape, const Pointfs& exclude_ar
//BBS: add shape position
Vec2d shape_position = partplate_list.get_current_shape_position();
bool new_shape = bed.set_shape(shape, printable_height, extruder_areas, custom_model, force_as_custom, shape_position);
bool new_shape = bed.set_shape(shape, printable_height, extruder_areas, extruder_heights, custom_model, force_as_custom, shape_position);
float prev_height_lid, prev_height_rod;
partplate_list.get_height_limits(prev_height_lid, prev_height_rod);
@@ -8763,11 +8763,11 @@ void Plater::priv::set_bed_shape(const Pointfs& shape, const Pointfs& exclude_ar
//Pointfs& exclude_areas = config->option<ConfigOptionPoints>("bed_exclude_area")->values;
partplate_list.reset_size(max.x() - min.x() - Bed3D::Axes::DefaultTipRadius, max.y() - min.y() - Bed3D::Axes::DefaultTipRadius, z);
partplate_list.set_shapes(shape, exclude_areas, extruder_areas, custom_texture, height_to_lid, height_to_rod);
partplate_list.set_shapes(shape, exclude_areas, extruder_areas, extruder_heights, custom_texture, height_to_lid, height_to_rod);
Vec2d new_shape_position = partplate_list.get_current_shape_position();
if (shape_position != new_shape_position)
bed.set_shape(shape, printable_height, extruder_areas, custom_model, force_as_custom, new_shape_position);
bed.set_shape(shape, printable_height, extruder_areas, extruder_heights, custom_model, force_as_custom, new_shape_position);
}
}
@@ -13880,14 +13880,15 @@ void Plater::set_bed_shape() const
p->config->option<ConfigOptionPoints>("bed_exclude_area")->values,
p->config->option<ConfigOptionFloat>("printable_height")->value,
p->config->option<ConfigOptionPointsGroups>("extruder_printable_area")->values,
p->config->option<ConfigOptionFloatsNullable>("extruder_printable_height")->values,
p->config->option<ConfigOptionString>("bed_custom_texture")->value.empty() ? texture_filename : p->config->option<ConfigOptionString>("bed_custom_texture")->value,
p->config->option<ConfigOptionString>("bed_custom_model")->value);
}
//BBS: add bed exclude area
void Plater::set_bed_shape(const Pointfs& shape, const Pointfs& exclude_area, const double printable_height, std::vector<Pointfs> extruder_areas, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) const
void Plater::set_bed_shape(const Pointfs& shape, const Pointfs& exclude_area, const double printable_height, std::vector<Pointfs> extruder_areas, std::vector<double> extruder_heights, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) const
{
p->set_bed_shape(make_counter_clockwise(shape), exclude_area, printable_height, extruder_areas, custom_texture, custom_model, force_as_custom);
p->set_bed_shape(make_counter_clockwise(shape), exclude_area, printable_height, extruder_areas, extruder_heights, custom_texture, custom_model, force_as_custom);
}
void Plater::force_filament_colors_update()

View File

@@ -674,7 +674,7 @@ public:
void update_flush_volume_matrix(size_t old_nozzle_size, size_t new_nozzle_size);
//BBS: add bed exclude area
void set_bed_shape() const;
void set_bed_shape(const Pointfs& shape, const Pointfs& exclude_area, const double printable_height, std::vector<Pointfs> extruder_areas, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
void set_bed_shape(const Pointfs& shape, const Pointfs& exclude_area, const double printable_height, std::vector<Pointfs> extruder_areas, std::vector<double> extruder_heights, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
const NotificationManager* get_notification_manager() const;
NotificationManager* get_notification_manager();