[CLI]: Fix Plate Config Reading and BuildVolume Height Checks (#15479)

* Fix incorrect early exit for CLI mode no-support preventing parameters from being read

* Use PartPlate's m_height to allow CLI to perform proper BuildVolume check

* Add safeguard against extruder_pintable_heights and extruder_areas vector size mismatch

* Preserve printable_height precision in PartPlate/PartPlateList

* Fixed multiple BuildVolume warning issue, and keep check_outside diff minimal
This commit is contained in:
HanifKoh
2026-09-04 23:24:59 +08:00
committed by GitHub
parent df30e22427
commit 1170b048e8
7 changed files with 85 additions and 39 deletions

View File

@@ -5199,7 +5199,8 @@ int CLI::run(int argc, char **argv)
Vec3d wipe_tower_size = cur_plate->estimate_wipe_tower_size(m_print_config, w, v, new_extruder_count, filaments_cnt, false, enable_wrapping); Vec3d wipe_tower_size = cur_plate->estimate_wipe_tower_size(m_print_config, w, v, new_extruder_count, filaments_cnt, false, enable_wrapping);
Vec3d plate_origin = cur_plate->get_origin(); Vec3d plate_origin = cur_plate->get_origin();
int plate_width, plate_depth, plate_height; int plate_width, plate_depth;
double plate_height;
partplate_list.get_plate_size(plate_width, plate_depth, plate_height); partplate_list.get_plate_size(plate_width, plate_depth, plate_height);
float depth = wipe_tower_size(1); float depth = wipe_tower_size(1);
float margin = 15.f, wp_brim_width = 0.f; float margin = 15.f, wp_brim_width = 0.f;

View File

@@ -13,7 +13,6 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
: m_bed_shape(printable_area), m_max_print_height(printable_height), m_extruder_shapes(extruder_areas), m_extruder_printable_height(extruder_printable_heights) : m_bed_shape(printable_area), m_max_print_height(printable_height), m_extruder_shapes(extruder_areas), m_extruder_printable_height(extruder_printable_heights)
{ {
assert(printable_height >= 0); assert(printable_height >= 0);
//assert(extruder_printable_heights.size() == extruder_areas.size());
m_polygon = Polygon::new_scale(printable_area); m_polygon = Polygon::new_scale(printable_area);
assert(m_polygon.is_counter_clockwise()); assert(m_polygon.is_counter_clockwise());
@@ -86,6 +85,9 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
m_shared_volume.data[2] = m_bboxf.max.x(); m_shared_volume.data[2] = m_bboxf.max.x();
m_shared_volume.data[3] = m_bboxf.max.y(); m_shared_volume.data[3] = m_bboxf.max.y();
m_shared_volume.zs[1] = m_bboxf.max.z(); m_shared_volume.zs[1] = m_bboxf.max.z();
if (extruder_printable_heights.size() < m_extruder_shapes.size())
BOOST_LOG_TRIVIAL(warning) << boost::format("extruder_printable_height has only %1% entries but extruder_printable_area has %2%, falling back to the bed printable_height for the missing ones")
% extruder_printable_heights.size() % m_extruder_shapes.size();
for (unsigned int index = 0; index < m_extruder_shapes.size(); index++) for (unsigned int index = 0; index < m_extruder_shapes.size(); index++)
{ {
std::vector<Vec2d>& extruder_shape = m_extruder_shapes[index]; std::vector<Vec2d>& extruder_shape = m_extruder_shapes[index];
@@ -100,7 +102,9 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
return; return;
} }
if ((extruder_shape == printable_area)&&(extruder_printable_heights[index] == printable_height)) { const double extruder_height = index < extruder_printable_heights.size() ? extruder_printable_heights[index] : printable_height;
if ((extruder_shape == printable_area)&&(extruder_height == printable_height)) {
extruder_volume.same_with_bed = true; extruder_volume.same_with_bed = true;
extruder_volume.type = m_type; extruder_volume.type = m_type;
extruder_volume.bbox = m_bbox; extruder_volume.bbox = m_bbox;
@@ -113,7 +117,7 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
double poly_area = poly.area(); double poly_area = poly.area();
extruder_volume.bbox = get_extents(poly); extruder_volume.bbox = get_extents(poly);
BoundingBoxf temp_bboxf = get_extents(extruder_shape); BoundingBoxf temp_bboxf = get_extents(extruder_shape);
extruder_volume.bboxf = BoundingBoxf3{ to_3d(temp_bboxf.min, 0.), to_3d(temp_bboxf.max, extruder_printable_heights[index]) }; extruder_volume.bboxf = BoundingBoxf3{ to_3d(temp_bboxf.min, 0.), to_3d(temp_bboxf.max, extruder_height) };
if (extruder_shape.size() >= 4 && std::abs((poly_area - double(extruder_volume.bbox.size().x()) * double(extruder_volume.bbox.size().y()))) < sqr(SCALED_EPSILON)) if (extruder_shape.size() >= 4 && std::abs((poly_area - double(extruder_volume.bbox.size().x()) * double(extruder_volume.bbox.size().y()))) < sqr(SCALED_EPSILON))
{ {

View File

@@ -157,7 +157,7 @@ PartPlate::PartPlate()
init(); init();
} }
PartPlate::PartPlate(PartPlateList *partplate_list, Vec3d origin, int width, int depth, int height, Plater* platerObj, Model* modelObj, bool printable, PrinterTechnology tech) PartPlate::PartPlate(PartPlateList *partplate_list, Vec3d origin, int width, int depth, double height, Plater* platerObj, Model* modelObj, bool printable, PrinterTechnology tech)
:m_partplate_list(partplate_list), m_plater(platerObj), m_model(modelObj), printer_technology(tech), m_origin(origin), m_width(width), m_depth(depth), m_height(height), m_printable(printable) :m_partplate_list(partplate_list), m_plater(platerObj), m_model(modelObj), printer_technology(tech), m_origin(origin), m_width(width), m_depth(depth), m_height(height), m_printable(printable)
{ {
init(); init();
@@ -1757,26 +1757,25 @@ std::vector<int> PartPlate::get_extruders_under_cli(bool conside_custom_gcode, D
else else
obj_support = glb_support; obj_support = glb_support;
if (!obj_support) if (obj_support) {
continue; int obj_support_intf_extr = 0;
const ConfigOption* support_intf_extr_opt = object->config.option("support_interface_filament");
if (support_intf_extr_opt != nullptr)
obj_support_intf_extr = support_intf_extr_opt->getInt();
if (obj_support_intf_extr != 0)
plate_extruders.push_back(obj_support_intf_extr);
else if (glb_support_intf_extr != 0)
plate_extruders.push_back(glb_support_intf_extr);
int obj_support_intf_extr = 0; int obj_support_extr = 0;
const ConfigOption* support_intf_extr_opt = object->config.option("support_interface_filament"); const ConfigOption* support_extr_opt = object->config.option("support_filament");
if (support_intf_extr_opt != nullptr) if (support_extr_opt != nullptr)
obj_support_intf_extr = support_intf_extr_opt->getInt(); obj_support_extr = support_extr_opt->getInt();
if (obj_support_intf_extr != 0) if (obj_support_extr != 0)
plate_extruders.push_back(obj_support_intf_extr); plate_extruders.push_back(obj_support_extr);
else if (glb_support_intf_extr != 0) else if (glb_support_extr != 0)
plate_extruders.push_back(glb_support_intf_extr); plate_extruders.push_back(glb_support_extr);
}
int obj_support_extr = 0;
const ConfigOption* support_extr_opt = object->config.option("support_filament");
if (support_extr_opt != nullptr)
obj_support_extr = support_extr_opt->getInt();
if (obj_support_extr != 0)
plate_extruders.push_back(obj_support_extr);
else if (glb_support_extr != 0)
plate_extruders.push_back(glb_support_extr);
int obj_outer_wall_extr = 0; int obj_outer_wall_extr = 0;
if (const ConfigOption* wall_opt = object->config.option("outer_wall_filament_id"); wall_opt != nullptr) if (const ConfigOption* wall_opt = object->config.option("outer_wall_filament_id"); wall_opt != nullptr)
@@ -2473,7 +2472,7 @@ void PartPlate::clear(bool clear_sliced_result)
/* size and position related functions*/ /* size and position related functions*/
//set position and size //set position and size
void PartPlate::set_pos_and_size(Vec3d& origin, int width, int depth, int height, bool with_instance_move, bool do_clear) void PartPlate::set_pos_and_size(Vec3d& origin, int width, int depth, double height, bool with_instance_move, bool do_clear)
{ {
bool size_changed = false; //size changed means the machine changed bool size_changed = false; //size changed means the machine changed
bool pos_changed = false; bool pos_changed = false;
@@ -2772,10 +2771,10 @@ bool PartPlate::check_outside(int obj_id, int instance_id, BoundingBoxf3* boundi
if (instance_box.min.z() < SINKING_Z_THRESHOLD) { if (instance_box.min.z() < SINKING_Z_THRESHOLD) {
// Orca: For sinking object, we use a more expensive algorithm so part below build plate won't be considered // Orca: For sinking object, we use a more expensive algorithm so part below build plate won't be considered
// m_plater is null in CLI mode. // m_height mirrors the printer's printable height and is set in CLI mode too, unlike m_plater.
if (m_plater && plate_box.intersects(instance_box)) { if (plate_box.intersects(instance_box)) {
// TODO: FIXME: this does not take exclusion area into account // 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, m_extruder_heights); const BuildVolume build_volume(get_shape(), m_height, m_extruder_areas, m_extruder_heights);
const auto state = instance->calc_print_volume_state(build_volume); const auto state = instance->calc_print_volume_state(build_volume);
outside = state == ModelInstancePVS_Partly_Outside; outside = state == ModelInstancePVS_Partly_Outside;
} }
@@ -4099,7 +4098,7 @@ void PartPlate::on_filament_deleted(int filament_count, int filament_id)
/* PartPlate List related functions*/ /* PartPlate List related functions*/
PartPlateList::PartPlateList(int width, int depth, int height, Plater* platerObj, Model* modelObj, PrinterTechnology tech) PartPlateList::PartPlateList(int width, int depth, double height, Plater* platerObj, Model* modelObj, PrinterTechnology tech)
:m_plate_width(width), m_plate_depth(depth), m_plate_height(height), m_plater(platerObj), m_model(modelObj), printer_technology(tech), :m_plate_width(width), m_plate_depth(depth), m_plate_height(height), m_plater(platerObj), m_model(modelObj), printer_technology(tech),
unprintable_plate(this, Vec3d(0.0 + width * (1. + LOGICAL_PART_PLATE_GAP), 0.0, 0.0), width, depth, height, platerObj, modelObj, false, tech) unprintable_plate(this, Vec3d(0.0 + width * (1. + LOGICAL_PART_PLATE_GAP), 0.0, 0.0), width, depth, height, platerObj, modelObj, false, tech)
{ {
@@ -4544,7 +4543,7 @@ void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx, bool ini
} }
//this may be happened after machine changed //this may be happened after machine changed
void PartPlateList::reset_size(int width, int depth, int height, bool reload_objects, bool update_shapes) void PartPlateList::reset_size(int width, int depth, double height, bool reload_objects, bool update_shapes)
{ {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":before size: plate_width %1%, plate_depth %2%, plate_height %3%") % m_plate_width % m_plate_depth % m_plate_height; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":before size: plate_width %1%, plate_depth %2%, plate_height %3%") % m_plate_width % m_plate_depth % m_plate_height;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":after size: plate_width %1%, plate_depth %2%, plate_height %3%") % width % depth % height; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":after size: plate_width %1%, plate_depth %2%, plate_height %3%") % width % depth % height;
@@ -6699,7 +6698,7 @@ int PartPlateList::load_gcode_files()
//BoundingBoxf3 print_volume = m_plate_list[i]->get_bounding_box(false); //BoundingBoxf3 print_volume = m_plate_list[i]->get_bounding_box(false);
//print_volume.max(2) = this->m_plate_height; //print_volume.max(2) = this->m_plate_height;
//print_volume.min(2) = -1e10; //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_plate_list[i]->get_extruder_heights() }); m_model->update_print_volume_state({m_plate_list[i]->get_shape(), 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)) if (!m_plate_list[i]->load_gcode_from_file(m_plate_list[i]->m_gcode_path_from_3mf))
ret ++; ret ++;

View File

@@ -96,7 +96,7 @@ private:
Vec3d m_origin; Vec3d m_origin;
int m_width; int m_width;
int m_depth; int m_depth;
int m_height; double m_height;
float m_height_to_lid; float m_height_to_lid;
float m_height_to_rod; float m_height_to_rod;
bool m_printable; bool m_printable;
@@ -227,7 +227,7 @@ public:
static void load_render_colors(); static void load_render_colors();
PartPlate(); PartPlate();
PartPlate(PartPlateList *partplate_list, Vec3d origin, int width, int depth, int height, Plater* platerObj, Model* modelObj, bool printable=true, PrinterTechnology tech = ptFFF); PartPlate(PartPlateList *partplate_list, Vec3d origin, int width, int depth, double height, Plater* platerObj, Model* modelObj, bool printable=true, PrinterTechnology tech = ptFFF);
~PartPlate(); ~PartPlate();
bool operator<(PartPlate&) const; bool operator<(PartPlate&) const;
@@ -328,7 +328,7 @@ public:
Vec3d get_center_origin(); Vec3d get_center_origin();
/* size and position related functions*/ /* size and position related functions*/
//set position and size //set position and size
void set_pos_and_size(Vec3d& origin, int width, int depth, int height, bool with_instance_move, bool do_clear = true); void set_pos_and_size(Vec3d& origin, int width, int depth, double height, bool with_instance_move, bool do_clear = true);
// BBS // BBS
Vec2d get_size() const { return Vec2d(m_width, m_depth); } Vec2d get_size() const { return Vec2d(m_width, m_depth); }
@@ -590,7 +590,7 @@ class PartPlateList : public ObjectBase
int m_plate_width; int m_plate_width;
int m_plate_depth; int m_plate_depth;
int m_plate_height; double m_plate_height;
float m_height_to_lid; float m_height_to_lid;
float m_height_to_rod; float m_height_to_rod;
@@ -698,12 +698,12 @@ public:
static bool is_load_cali_texture; static bool is_load_cali_texture;
static bool is_load_extruder_only_area_textures; static bool is_load_extruder_only_area_textures;
PartPlateList(int width, int depth, int height, Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF); PartPlateList(int width, int depth, double height, Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF);
PartPlateList(Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF); PartPlateList(Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF);
~PartPlateList(); ~PartPlateList();
//this may be happened after machine changed //this may be happened after machine changed
void reset_size(int width, int depth, int height, bool reload_objects = true, bool update_shapes = false); void reset_size(int width, int depth, double height, bool reload_objects = true, bool update_shapes = false);
//clear all the instances in the plate, but keep the plates //clear all the instances in the plate, but keep the plates
void clear(bool delete_plates = false, bool release_print_list = false, bool except_locked = false, int plate_index = -1); void clear(bool delete_plates = false, bool release_print_list = false, bool except_locked = false, int plate_index = -1);
//clear all the instances in the plate, and delete the plates, only keep the first default plate //clear all the instances in the plate, and delete the plates, only keep the first default plate
@@ -717,7 +717,7 @@ public:
//get the plate stride //get the plate stride
double plate_stride_x(); double plate_stride_x();
double plate_stride_y(); double plate_stride_y();
void get_plate_size(int& width, int& depth, int& height) { void get_plate_size(int& width, int& depth, double& height) {
width = m_plate_width; width = m_plate_width;
depth = m_plate_depth; depth = m_plate_depth;
height = m_plate_height; height = m_plate_height;

View File

@@ -8278,7 +8278,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
bool dlg_cont = true; bool dlg_cont = true;
bool is_user_cancel = false; bool is_user_cancel = false;
bool translate_old = false; bool translate_old = false;
int current_width = 0, current_depth = 0, current_height = 0, project_filament_count = 1; int current_width = 0, current_depth = 0, project_filament_count = 1;
double current_height = 0;
if (input_files.empty()) if (input_files.empty())
return std::vector<size_t>(); return std::vector<size_t>();

View File

@@ -8,6 +8,7 @@ add_executable(${_TEST_NAME}_tests
test_arachne_walls.cpp test_arachne_walls.cpp
test_arrange.cpp test_arrange.cpp
test_bambu_networking.cpp test_bambu_networking.cpp
test_buildvolume.cpp
test_calib.cpp test_calib.cpp
test_clipper_offset.cpp test_clipper_offset.cpp
test_clipper_utils.cpp test_clipper_utils.cpp

View File

@@ -0,0 +1,40 @@
#include <catch2/catch_all.hpp>
#include "libslic3r/BuildVolume.hpp"
using namespace Slic3r;
static std::vector<Vec2d> rect_area(double w, double d)
{
return { { 0., 0. }, { w, 0. }, { w, d }, { 0., d } };
}
// extruder_printable_height and extruder_printable_area are independent config options, so a
// profile can leave the heights short. BuildVolume must not index past the end of the heights.
TEST_CASE("BuildVolume falls back to the bed height when extruder_printable_height is short", "[BuildVolume]")
{
const std::vector<Vec2d> bed = rect_area(200., 200.);
const std::vector<std::vector<Vec2d>> areas = { rect_area(200., 200.), rect_area(100., 200.) };
const std::vector<double> heights = { 180. };
const BuildVolume build_volume(bed, 250., areas, heights);
REQUIRE(build_volume.get_extruder_area_count() == 2);
// The extruder with a height of its own keeps it, and differs from the bed, so it gets its own volume.
CHECK_THAT(build_volume.get_extruder_area_volume(0).bboxf.max.z(), Catch::Matchers::WithinAbs(180., 1e-6));
// The extruder without one falls back to the bed's printable_height instead of reading out of range.
CHECK_THAT(build_volume.get_extruder_area_volume(1).bboxf.max.z(), Catch::Matchers::WithinAbs(250., 1e-6));
}
TEST_CASE("BuildVolume keeps per-extruder heights when both vectors match", "[BuildVolume]")
{
const std::vector<Vec2d> bed = rect_area(200., 200.);
const std::vector<std::vector<Vec2d>> areas = { rect_area(120., 200.), rect_area(100., 200.) };
const std::vector<double> heights = { 180., 200.5 };
const BuildVolume build_volume(bed, 250., areas, heights);
REQUIRE(build_volume.get_extruder_area_count() == 2);
CHECK_THAT(build_volume.get_extruder_area_volume(0).bboxf.max.z(), Catch::Matchers::WithinAbs(180., 1e-6));
CHECK_THAT(build_volume.get_extruder_area_volume(1).bboxf.max.z(), Catch::Matchers::WithinAbs(200.5, 1e-6));
}