From 1170b048e81a04b5a9700d2f3d3e9d5e41b06577 Mon Sep 17 00:00:00 2001 From: HanifKoh <76276251+HanifKoh@users.noreply.github.com> Date: Fri, 4 Sep 2026 23:24:59 +0800 Subject: [PATCH] [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 --- src/OrcaSlicer.cpp | 3 +- src/libslic3r/BuildVolume.cpp | 10 ++++-- src/slic3r/GUI/PartPlate.cpp | 53 ++++++++++++++-------------- src/slic3r/GUI/PartPlate.hpp | 14 ++++---- src/slic3r/GUI/Plater.cpp | 3 +- tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_buildvolume.cpp | 40 +++++++++++++++++++++ 7 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 tests/libslic3r/test_buildvolume.cpp diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index 3c45331c20..dcd5ec0082 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -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 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); float depth = wipe_tower_size(1); float margin = 15.f, wp_brim_width = 0.f; diff --git a/src/libslic3r/BuildVolume.cpp b/src/libslic3r/BuildVolume.cpp index 1ac200d9c8..15e6668bae 100644 --- a/src/libslic3r/BuildVolume.cpp +++ b/src/libslic3r/BuildVolume.cpp @@ -13,7 +13,6 @@ BuildVolume::BuildVolume(const std::vector &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) { assert(printable_height >= 0); - //assert(extruder_printable_heights.size() == extruder_areas.size()); m_polygon = Polygon::new_scale(printable_area); assert(m_polygon.is_counter_clockwise()); @@ -86,6 +85,9 @@ BuildVolume::BuildVolume(const std::vector &printable_area, const double m_shared_volume.data[2] = m_bboxf.max.x(); m_shared_volume.data[3] = m_bboxf.max.y(); 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++) { std::vector& extruder_shape = m_extruder_shapes[index]; @@ -100,7 +102,9 @@ BuildVolume::BuildVolume(const std::vector &printable_area, const double 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.type = m_type; extruder_volume.bbox = m_bbox; @@ -113,7 +117,7 @@ BuildVolume::BuildVolume(const std::vector &printable_area, const double double poly_area = poly.area(); extruder_volume.bbox = get_extents(poly); 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)) { diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index 59a857816e..849aa4e31a 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -157,7 +157,7 @@ PartPlate::PartPlate() 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) { init(); @@ -1757,26 +1757,25 @@ std::vector PartPlate::get_extruders_under_cli(bool conside_custom_gcode, D else obj_support = glb_support; - if (!obj_support) - continue; + if (obj_support) { + 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; - 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_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_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; 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*/ //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 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) { // 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. - if (m_plater && plate_box.intersects(instance_box)) { + // m_height mirrors the printer's printable height and is set in CLI mode too, unlike m_plater. + 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, 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); outside = state == ModelInstancePVS_Partly_Outside; } @@ -4099,7 +4098,7 @@ void PartPlate::on_filament_deleted(int filament_count, int filament_id) /* 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), 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 -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(":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); //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_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)) ret ++; diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp index 58c0f95b87..8ad2f4a7d1 100644 --- a/src/slic3r/GUI/PartPlate.hpp +++ b/src/slic3r/GUI/PartPlate.hpp @@ -96,7 +96,7 @@ private: Vec3d m_origin; int m_width; int m_depth; - int m_height; + double m_height; float m_height_to_lid; float m_height_to_rod; bool m_printable; @@ -227,7 +227,7 @@ public: static void load_render_colors(); 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(); bool operator<(PartPlate&) const; @@ -328,7 +328,7 @@ public: Vec3d get_center_origin(); /* size and position related functions*/ //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 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_depth; - int m_plate_height; + double m_plate_height; float m_height_to_lid; float m_height_to_rod; @@ -698,12 +698,12 @@ public: static bool is_load_cali_texture; 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(); //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 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 @@ -717,7 +717,7 @@ public: //get the plate stride double plate_stride_x(); 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; depth = m_plate_depth; height = m_plate_height; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0347803dbc..156cc2388c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -8278,7 +8278,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ bool dlg_cont = true; bool is_user_cancel = 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()) return std::vector(); diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index bf3981f519..5d3e301ea7 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -8,6 +8,7 @@ add_executable(${_TEST_NAME}_tests test_arachne_walls.cpp test_arrange.cpp test_bambu_networking.cpp + test_buildvolume.cpp test_calib.cpp test_clipper_offset.cpp test_clipper_utils.cpp diff --git a/tests/libslic3r/test_buildvolume.cpp b/tests/libslic3r/test_buildvolume.cpp new file mode 100644 index 0000000000..b2e8040d68 --- /dev/null +++ b/tests/libslic3r/test_buildvolume.cpp @@ -0,0 +1,40 @@ +#include + +#include "libslic3r/BuildVolume.hpp" + +using namespace Slic3r; + +static std::vector 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 bed = rect_area(200., 200.); + const std::vector> areas = { rect_area(200., 200.), rect_area(100., 200.) }; + const std::vector 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 bed = rect_area(200., 200.); + const std::vector> areas = { rect_area(120., 200.), rect_area(100., 200.) }; + const std::vector 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)); +}