Compare commits

..
Author SHA1 Message Date
Sergei 252bbbf9f5 Make the exclude-object outline tolerance a user setting (#16503)
* Make the exclude-object outline tolerance configurable

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>

* fix(config): move exclude_object_tolerance to developer mode

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>

* fix(config): move exclude_object_tolerance to expert mode

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>

* feat(ui): hide exclude_object_tolerance for non-Klipper firmwares

mend

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>

* Test that a larger exclude object tolerance writes fewer outline points

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>

---------

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>
2026-10-19 08:17:20 -03:00
Lam Wei Lun 4c2ae6d202 Keep Auto Drop off for projects saved by 2.4.x (#16633)
2.4.0 to 2.4.2 wrote auto_drop on each build item, and 2.4.x objects
carry no auto_drop attribute in model_settings.config. The importer only
read the object attribute and treated a missing one as on, so floating
objects were dropped to the bed on load and the flag was rewritten as on
on the next save.

Record object ids with an item auto_drop="0" and use that as the fallback
when the object has no attribute. The attribute's presence is now tracked
so the fallback applies only when it is absent. Any item set to off turns
auto drop off for the object, since the flag is shared by all instances.
2026-10-19 16:08:59 +08:00
13 changed files with 127 additions and 73 deletions

No files matched your search

+6
View File
@@ -15648,6 +15648,12 @@ msgstr ""
msgid "Enable this option to add EXCLUDE OBJECT command in G-code."
msgstr ""
msgid "Outline tolerance"
msgstr ""
msgid "Maximum deviation of the object outline written to EXCLUDE_OBJECT_DEFINE from the original outline. Larger values give fewer points, which helps firmware that stalls while parsing G-code with many objects. 0 keeps every point."
msgstr ""
msgid "Verbose G-code"
msgstr ""
+13 -3
View File
@@ -988,7 +988,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
MetadataList metadata;
VolumeMetadataList volumes;
bool auto_drop { true };
std::optional<bool> auto_drop;
};
struct CutObjectInfo
@@ -1208,6 +1208,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//IdToGeometryMap m_orig_geometries; // backup & restore
CurrentConfig m_curr_config;
IdToMetadataMap m_objects_metadata;
std::set<int> m_items_auto_drop_off; // object ids with a build item auto_drop="0" (2.4.x layout)
IdToCutObjectInfoMap m_cut_object_infos;
IdToLayerHeightsProfileMap m_layer_heights_profiles;
IdToLayerConfigRangesMap m_layer_config_ranges;
@@ -1463,6 +1464,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
m_objects.clear();
m_instances.clear();
m_objects_metadata.clear();
m_items_auto_drop_off.clear();
m_curr_metadata_name.clear();
m_curr_characters.clear();
@@ -1502,6 +1504,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
m_curr_config.object_id = -1;
m_curr_config.volume_id = -1;
m_objects_metadata.clear();
m_items_auto_drop_off.clear();
m_layer_heights_profiles.clear();
m_layer_config_ranges.clear();
m_brim_ear_points.clear();
@@ -2257,11 +2260,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
ObjectMetadata::VolumeMetadataList volumes;
ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
// 2.4.x stored auto_drop on build items, shared by all instances of an object; any "off" wins
const bool legacy_auto_drop = !m_items_auto_drop_off.count(object.first.second);
IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first.second);
if (obj_metadata != m_objects_metadata.end()) {
// config data has been found, this model was saved using slic3r pe
model_object->auto_drop = obj_metadata->second.auto_drop;
model_object->auto_drop = obj_metadata->second.auto_drop.value_or(legacy_auto_drop);
// apply object's name and config data
for (const Metadata& metadata : obj_metadata->second.metadata) {
@@ -2279,6 +2285,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
}
else {
// config data not found, this model was not saved using slic3r pe
model_object->auto_drop = legacy_auto_drop;
// add the entire geometry as the single volume to generate
//volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
@@ -4121,6 +4128,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
std::string path = bbs_get_attribute_value_string(attributes, num_attributes, PPATH_ATTR);
Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
int printable = bbs_get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);
if (const char* auto_drop = bbs_get_attribute_value_charptr(attributes, num_attributes, AUTO_DROP_ATTR); auto_drop != nullptr && !::atoi(auto_drop))
m_items_auto_drop_off.insert(object_id);
return !m_load_model || _create_object_instance(path, object_id, transform, printable, 1);
}
@@ -4489,7 +4498,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
// int instances_count_id = bbs_get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR);
ObjectMetadata metadata;
metadata.auto_drop = bbs_get_attribute_value_bool(attributes, num_attributes, AUTO_DROP_ATTR);
if (bbs_get_attribute_value_charptr(attributes, num_attributes, AUTO_DROP_ATTR) != nullptr)
metadata.auto_drop = bbs_get_attribute_value_bool(attributes, num_attributes, AUTO_DROP_ATTR);
m_objects_metadata.insert({ object_id, std::move(metadata) });
m_curr_config.object_id = object_id;
return true;
+1
View File
@@ -1421,6 +1421,7 @@ static std::vector<std::string> s_Preset_print_options{
"initial_layer_travel_acceleration",
"initial_layer_travel_jerk",
"exclude_object",
"exclude_object_tolerance",
"slow_down_layers",
"infill_anchor",
"infill_anchor_max",
+2 -1
View File
@@ -309,6 +309,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"gcode_comments",
"gcode_label_objects",
"exclude_object",
"exclude_object_tolerance",
"support_material_interface_fan_speed",
"internal_bridge_fan_speed", // ORCA: Add support for separate internal bridge fan speed control
"ironing_fan_speed",
@@ -6929,7 +6930,7 @@ BoundingBoxf3 PrintInstance::get_bounding_box() const {
Polygon PrintInstance::get_convex_hull_2d() {
Polygon poly = print_object->model_object()->convex_hull_2d(model_instance->get_matrix());
poly.douglas_peucker(scale_(0.1));
poly.douglas_peucker(scale_(print_object->print()->config().exclude_object_tolerance.value));
return poly;
}
+11
View File
@@ -4629,6 +4629,17 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("exclude_object_tolerance", coFloat);
def->label = L("Outline tolerance");
def->tooltip = L("Maximum deviation of the object outline written to EXCLUDE_OBJECT_DEFINE from the "
"original outline. Larger values give fewer points, which helps firmware that stalls "
"while parsing G-code with many objects. 0 keeps every point.");
def->sidetext = L("mm");
def->min = 0;
def->max = 5;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0.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. "
+1
View File
@@ -2130,6 +2130,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionPercents, filament_shrinkage_compensation_z))
((ConfigOptionBool, gcode_label_objects))
((ConfigOptionBool, exclude_object))
((ConfigOptionFloat, exclude_object_tolerance))
((ConfigOptionFloats, grab_length))
((ConfigOptionBool, gcode_comments))
((ConfigOptionInt, slow_down_layers))
+7 -15
View File
@@ -435,7 +435,7 @@ void CalibrationWizard::cache_preset_info(MachineObject *obj, float nozzle_dia,
if (obj->is_multi_extruders()) {
int ams_id, slot_id, tray_id;
get_tray_ams_and_slot_id(curr_obj, result.tray_id, ams_id, slot_id, tray_id);
result.extruder_id = preset_page->get_extruder_id_by_tray_id(result.tray_id);
result.extruder_id = preset_page->get_extruder_id(ams_id);
result.nozzle_diameter = preset_page->get_nozzle_diameter(result.extruder_id);
}
else {
@@ -753,7 +753,7 @@ void PressureAdvanceWizard::on_cali_start()
X1CCalibInfos::X1CCalibInfo calib_info;
get_tray_ams_and_slot_id(curr_obj, item.first, calib_info.ams_id, calib_info.slot_id, calib_info.tray_id);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(item.first);
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
@@ -792,7 +792,7 @@ void PressureAdvanceWizard::on_cali_start()
CalibInfo calib_info;
calib_info.dev_id = curr_obj->get_dev_id();
get_tray_ams_and_slot_id(curr_obj, selected_filaments.begin()->first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(selected_filaments.begin()->first);
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
@@ -877,7 +877,7 @@ void PressureAdvanceWizard::on_cali_start()
calib_info.dev_id = curr_obj->get_dev_id();
get_tray_ams_and_slot_id(curr_obj, item.first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
calib_info.index = preset_page->get_index_by_tray_id(item.first);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(item.first);
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
@@ -1252,7 +1252,7 @@ void FlowRateWizard::on_cali_start(CaliPresetStage stage, float cali_value, Flow
X1CCalibInfos::X1CCalibInfo calib_info;
calib_info.tray_id = item.first;
get_tray_ams_and_slot_id(curr_obj, item.first, calib_info.ams_id, calib_info.slot_id, calib_info.tray_id);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(item.first);
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
@@ -1308,15 +1308,7 @@ void FlowRateWizard::on_cali_start(CaliPresetStage stage, float cali_value, Flow
int selected_tray_id = 0;
get_tray_ams_and_slot_id(curr_obj, selected_filaments.begin()->first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
calib_info.select_ams = std::to_string(selected_tray_id);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(selected_filaments.begin()->first);
if (from_page == FlowRatioCaliSource::FROM_COARSE_PAGE) {
// The fine pass must run on the extruder the coarse pass used. The cache records it;
// the preset page's selection may have changed or been reset since.
const auto cached = get_cached_selected_filament_for_multi_extruder(curr_obj);
const auto it = cached.find(selected_filaments.begin()->first);
if (it != cached.end())
calib_info.extruder_id = it->second.extruder_id;
}
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
@@ -1732,7 +1724,7 @@ void MaxVolumetricSpeedWizard::on_cali_start()
int selected_tray_id = 0;
get_tray_ams_and_slot_id(curr_obj, selected_filaments.begin()->first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
calib_info.select_ams = std::to_string(selected_tray_id);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(selected_filaments.begin()->first);
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
+22 -53
View File
@@ -587,23 +587,8 @@ void CalibrationPresetPage::on_sys_color_changed()
m_ams_sync_button->msw_rescale();
}
int CalibrationPresetPage::get_extruder_id_by_tray_id(int tray_id)
int CalibrationPresetPage::get_extruder_id(int ams_id)
{
// An AMS behind a Filament Track Switch feeds both extruders, so the extruder is the one
// whose panel the filament was selected in.
if (curr_obj && curr_obj->is_multi_extruders()) {
for (FilamentComboBox *fcb : get_selected_filament_combobox()) {
if (fcb->get_tray_id() != tray_id)
continue;
const bool is_deputy = std::find(m_deputy_filament_comboBox_list.begin(), m_deputy_filament_comboBox_list.end(), fcb) !=
m_deputy_filament_comboBox_list.end();
return is_deputy ? DEPUTY_EXTRUDER_ID : MAIN_EXTRUDER_ID;
}
}
int ams_id = tray_id / 4, slot_id, unused_tray_id;
if (curr_obj)
get_tray_ams_and_slot_id(curr_obj, tray_id, ams_id, slot_id, unused_tray_id);
if (m_ams_id_to_extruder_id_map.find(ams_id) != m_ams_id_to_extruder_id_map.end()) {
return m_ams_id_to_extruder_id_map[ams_id];
}
@@ -881,18 +866,6 @@ static void fill_nozzle_volume_combo(ComboBox *combo)
select_nozzle_volume(combo, NozzleVolumeType::nvtStandard);
}
static DynamicPrintConfig empty_tray_config()
{
DynamicPrintConfig config;
config.set_key_value("filament_id", new ConfigOptionStrings{""});
config.set_key_value("tag_uid", new ConfigOptionStrings{""});
config.set_key_value("filament_type", new ConfigOptionStrings{""});
config.set_key_value("tray_name", new ConfigOptionStrings{""});
config.set_key_value("filament_colour", new ConfigOptionStrings{""});
config.set_key_value("filament_exist", new ConfigOptionBools{false});
return config;
}
void CalibrationPresetPage::init_selection_values()
{
// init nozzle diameter and nozzle volume
@@ -2299,36 +2272,27 @@ void CalibrationPresetPage::sync_ams_info(MachineObject* obj)
}
}
// init virtual tray info. With a Filament Track Switch installed the external spools feed
// through the switch, so they can't be calibrated as their own trays.
if (obj->GetFilaSwitch()->IsInstalled()) {
filament_ams_list[VIRTUAL_TRAY_MAIN_ID] = empty_tray_config();
filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID] = empty_tray_config();
} else {
if (full_filament_ams_list.find(VIRTUAL_TRAY_MAIN_ID) != full_filament_ams_list.end()) {
filament_ams_list[VIRTUAL_TRAY_MAIN_ID] = full_filament_ams_list[VIRTUAL_TRAY_MAIN_ID];
}
// init virtual tray info
if (full_filament_ams_list.find(VIRTUAL_TRAY_MAIN_ID) != full_filament_ams_list.end()) {
filament_ams_list[VIRTUAL_TRAY_MAIN_ID] = full_filament_ams_list[VIRTUAL_TRAY_MAIN_ID];
}
if (full_filament_ams_list.find(VIRTUAL_TRAY_DEPUTY_ID) != full_filament_ams_list.end()) {
filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID] = full_filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID];
}
if (full_filament_ams_list.find(VIRTUAL_TRAY_DEPUTY_ID) != full_filament_ams_list.end()) {
filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID] = full_filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID];
}
// update filament from panel, display only obj has ams
// update multi ams panel, display only obj has multi ams
if (obj->HasAms()) {
if (obj->is_multi_extruders()) {
// An AMS behind a Filament Track Switch is bound to both extruders and appears in both panels.
bool main_done = false;
bool deputy_done = false;
for (auto &ams_item : obj->GetFilaSystem()->GetAmsList()) {
const auto &extruder_set = ams_item.second->GetBindedExtruderSet();
if (!main_done && extruder_set.count(MAIN_EXTRUDER_ID) != 0) {
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), MAIN_EXTRUDER_ID);
if (ams_item.second->GetExtruderId() == 0 && !main_done) {
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), ams_item.second->GetExtruderId());
main_done = true;
}
if (!deputy_done && extruder_set.count(DEPUTY_EXTRUDER_ID) != 0) {
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), DEPUTY_EXTRUDER_ID);
} else if (ams_item.second->GetExtruderId() == 1 && !deputy_done) {
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), ams_item.second->GetExtruderId());
deputy_done = true;
}
}
@@ -2369,11 +2333,10 @@ void CalibrationPresetPage::sync_ams_info(MachineObject* obj)
if (ams->second->IsExist()
&& info.parse_ams_info(obj, ams->second, obj->GetFilaSystem()->IsDetectRemainEnabled(), obj->is_support_ams_humidity)) {
ams_info.push_back(info);
for (int extruder_id : ams->second->GetBindedExtruderSet()) {
if (extruder_id == MAIN_EXTRUDER_ID)
main_ams_info.push_back(info);
else
deputy_ams_info.push_back(info);
if (info.nozzle_id == 0) {
main_ams_info.push_back(info);
} else {
deputy_ams_info.push_back(info);
}
}
m_ams_id_to_extruder_id_map[stoi(info.ams_id)] = info.nozzle_id;
@@ -2621,7 +2584,13 @@ void CalibrationPresetPage::update_multi_extruder_filament_combobox(const std::s
}
}
DynamicPrintConfig empty_config = empty_tray_config();
DynamicPrintConfig empty_config;
empty_config.set_key_value("filament_id", new ConfigOptionStrings{""});
empty_config.set_key_value("tag_uid", new ConfigOptionStrings{""});
empty_config.set_key_value("filament_type", new ConfigOptionStrings{""});
empty_config.set_key_value("tray_name", new ConfigOptionStrings{""});
empty_config.set_key_value("filament_colour", new ConfigOptionStrings{""});
empty_config.set_key_value("filament_exist", new ConfigOptionBools{false});
if (filament_ams_list.empty()) return;
@@ -251,7 +251,7 @@ public:
void msw_rescale() override;
void on_sys_color_changed() override;
int get_extruder_id_by_tray_id(int tray_id);
int get_extruder_id(int ams_id);
float get_nozzle_diameter(int extruder_id) const;
NozzleVolumeType get_nozzle_volume_type(int extruder_id) const;
ExtruderType get_extruder_type(int extruder_id) const;
+2
View File
@@ -1245,6 +1245,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
if(gcf_is_klipper)
toggle_field("accel_to_decel_factor", config->opt_bool("accel_to_decel_enable"));
toggle_line("exclude_object_tolerance", gcf_is_klipper && config->opt_bool("exclude_object"));
bool have_make_overhang_printable = config->opt_bool("make_overhang_printable");
toggle_line("make_overhang_printable_angle", have_make_overhang_printable);
toggle_line("make_overhang_printable_hole_size", have_make_overhang_printable);
+1
View File
@@ -3269,6 +3269,7 @@ void TabPrint::build()
optgroup->append_single_option_line("gcode_comments", "others_settings_g_code_output#verbose-g-code");
optgroup->append_single_option_line("gcode_label_objects", "others_settings_g_code_output#label-objects");
optgroup->append_single_option_line("exclude_object", "others_settings_g_code_output#exclude-objects");
optgroup->append_single_option_line("exclude_object_tolerance", "others_settings_g_code_output#outline-tolerance");
option = optgroup->get_option("filename_format");
// option.opt.full_width = true;
option.opt.is_code = true;
+38
View File
@@ -51,6 +51,44 @@ TEST_CASE("Klipper object labels name each copy without the characters Klipper c
}
}
// The outline written to EXCLUDE_OBJECT_DEFINE is simplified with exclude_object_tolerance. A larger value must
// leave fewer points, which keeps the exclude-object array small on printers that stall while parsing it.
TEST_CASE("A larger exclude object tolerance writes fewer outline points", "[GCode]")
{
const auto slice_with_tolerance = [](const std::string &tolerance) {
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({{"gcode_flavor", "klipper"}, {"exclude_object", "1"},
{"exclude_object_tolerance", tolerance}});
// Default facet angle (2 degrees): 180 hull points, well inside either tolerance.
return Test::slice({make_cylinder(20., 5.)}, config);
};
// Points of the POLYGON=[[x,y],...] on the first EXCLUDE_OBJECT_DEFINE line.
const auto polygon_points = [](const std::string &gcode) {
const size_t define = gcode.find("EXCLUDE_OBJECT_DEFINE");
REQUIRE(define != std::string::npos);
const size_t begin = gcode.find("POLYGON=", define);
REQUIRE(begin != std::string::npos);
const std::string polygon = gcode.substr(begin, gcode.find('\n', begin) - begin);
size_t points = 1;
for (size_t pos = polygon.find("],["); pos != std::string::npos; pos = polygon.find("],[", pos + 1))
++points;
return points;
};
const size_t fine = polygon_points(slice_with_tolerance("0.1"));
const size_t coarse = polygon_points(slice_with_tolerance("0.4"));
INFO("points at 0.1 mm: " << fine << ", at 0.4 mm: " << coarse);
CHECK(coarse >= 4);
CHECK(coarse < fine);
}
// Existing profiles must keep writing the outlines they wrote when the tolerance was a constant 0.1 mm.
TEST_CASE("The exclude object tolerance defaults to the former fixed 0.1 mm", "[GCode]")
{
CHECK_THAT(DynamicPrintConfig::full_print_config().opt_float("exclude_object_tolerance"),
Catch::Matchers::WithinAbs(0.1, 1e-9));
}
// Firmwares read the compatibility header lines (Anycubic's GoKlipper heats the bed with
// first_layer_bed_temperature), so they must describe the filament the plate prints, not filament 1.
TEST_CASE("The G-code header names the first layer temperatures of the filament the plate prints", "[GCode]")
+22
View File
@@ -505,6 +505,28 @@ TEST_CASE("A project whose components reference themselves fails to load", "[3mf
REQUIRE_FALSE(loaded);
}
TEST_CASE("A project in the 2.4.x layout keeps auto drop off from its build item", "[3mf][Regression]")
{
// 2.4.x wrote auto_drop on the build item and left it off the object, so the object is re-laid out here.
ScopedTemporaryFile temp(".3mf");
{
Model model;
ModelObject* object = model.add_object();
object->add_volume(make_cube(10., 10., 10.));
object->add_instance();
object->auto_drop = false;
store_project(model, temp.string(), SaveStrategy::Zip64 | SaveStrategy::Silence);
}
REQUIRE(replace_in_3mf_entry(temp.string(), "model_settings.config", " auto_drop=\"0\"", ""));
REQUIRE(replace_in_3mf_entry(temp.string(), "3dmodel.model", "printable=\"1\"/>", "printable=\"1\" auto_drop=\"0\"/>"));
ScopedTemporaryDir backup_dir("orca_auto_drop_dst");
Model model;
REQUIRE(load_project(temp.string(), model, backup_dir));
REQUIRE(model.objects.size() == 1);
CHECK_FALSE(model.objects.front()->auto_drop);
}
TEST_CASE("An object loads up to the component reference budget and fails past it", "[3mf][Regression]")
{
// The importer queues at most 100000 component references per object. Every reference besides the