mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-02 15:52:14 +00:00
Fix: show all print validation warnings instead of only the last (#14112)
This commit is contained in:
@@ -6058,9 +6058,9 @@ int CLI::run(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
(dynamic_cast<Print*>(print))->is_BBL_printer() = is_bbl_vendor_preset;
|
(dynamic_cast<Print*>(print))->is_BBL_printer() = is_bbl_vendor_preset;
|
||||||
|
|
||||||
StringObjectException warning;
|
std::vector<StringObjectException> warnings;
|
||||||
print_fff->set_check_multi_filaments_compatibility(!allow_mix_temp);
|
print_fff->set_check_multi_filaments_compatibility(!allow_mix_temp);
|
||||||
auto err = print->validate(&warning);
|
auto err = print->validate(&warnings);
|
||||||
if (!err.string.empty()) {
|
if (!err.string.empty()) {
|
||||||
if ((STRING_EXCEPT_LAYER_HEIGHT_EXCEEDS_LIMIT == err.type) && no_check) {
|
if ((STRING_EXCEPT_LAYER_HEIGHT_EXCEEDS_LIMIT == err.type) && no_check) {
|
||||||
BOOST_LOG_TRIVIAL(warning) << "got warnings: "<< err.string << std::endl;
|
BOOST_LOG_TRIVIAL(warning) << "got warnings: "<< err.string << std::endl;
|
||||||
@@ -6094,8 +6094,9 @@ int CLI::run(int argc, char **argv)
|
|||||||
flush_and_exit(validate_error);
|
flush_and_exit(validate_error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!warning.string.empty()) {
|
else if (!warnings.empty()) {
|
||||||
BOOST_LOG_TRIVIAL(warning) << "got warnings: "<< warning.string << std::endl;
|
for (const auto& w : warnings)
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "got warnings: "<< w.string << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (print->empty()) {
|
if (print->empty()) {
|
||||||
@@ -6115,8 +6116,14 @@ int CLI::run(int argc, char **argv)
|
|||||||
BOOST_LOG_TRIVIAL(info) << "set print's callback to cli_status_callback.";
|
BOOST_LOG_TRIVIAL(info) << "set print's callback to cli_status_callback.";
|
||||||
print->set_status_callback(cli_status_callback);
|
print->set_status_callback(cli_status_callback);
|
||||||
g_cli_callback_mgr.set_plate_info(index+1, (plate_to_slice== 0)?partplate_list.get_plate_count():1);
|
g_cli_callback_mgr.set_plate_info(index+1, (plate_to_slice== 0)?partplate_list.get_plate_count():1);
|
||||||
if (!warning.string.empty()) {
|
if (!warnings.empty()) {
|
||||||
PrintBase::SlicingStatus slicing_status{4, warning.string, 0, 0};
|
std::string warning_text;
|
||||||
|
for (const auto& w : warnings) {
|
||||||
|
if (!warning_text.empty())
|
||||||
|
warning_text += "\n";
|
||||||
|
warning_text += w.string;
|
||||||
|
}
|
||||||
|
PrintBase::SlicingStatus slicing_status{4, warning_text, 0, 0};
|
||||||
cli_status_callback(slicing_status);
|
cli_status_callback(slicing_status);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -1256,9 +1256,22 @@ StringObjectException Print::check_multi_filament_valid(const Print& print)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
||||||
//BBS: refine seq-print validation logic.....FIXME:StringObjectException *warning can only contain one warning, but there might be many warnings, need a vector<StringObjectException>
|
//BBS: refine seq-print validation logic
|
||||||
StringObjectException Print::validate(StringObjectException *warning, Polygons* collison_polygons, std::vector<std::pair<Polygon, float>>* height_polygons) const
|
StringObjectException Print::validate(std::vector<StringObjectException> *warnings, Polygons* collison_polygons, std::vector<std::pair<Polygon, float>>* height_polygons) const
|
||||||
{
|
{
|
||||||
|
auto add_warning = [warnings](StringObjectException w) {
|
||||||
|
w.is_warning = true;
|
||||||
|
if (warnings != nullptr)
|
||||||
|
warnings->push_back(std::move(w));
|
||||||
|
};
|
||||||
|
auto warn = [&](std::string msg, std::string opt_key = "", const ObjectBase* object = nullptr) {
|
||||||
|
StringObjectException w;
|
||||||
|
w.string = std::move(msg);
|
||||||
|
w.opt_key = std::move(opt_key);
|
||||||
|
w.object = object;
|
||||||
|
add_warning(std::move(w));
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<unsigned int> extruders = this->extruders();
|
std::vector<unsigned int> extruders = this->extruders();
|
||||||
unsigned int nozzles = m_config.nozzle_diameter.size();
|
unsigned int nozzles = m_config.nozzle_diameter.size();
|
||||||
|
|
||||||
@@ -1273,9 +1286,8 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
if (!ret.string.empty())
|
if (!ret.string.empty())
|
||||||
{
|
{
|
||||||
ret.type = STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP;
|
ret.type = STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP;
|
||||||
if (ret.is_warning && warning != nullptr) {
|
if (ret.is_warning) {
|
||||||
*warning = ret;
|
add_warning(ret);
|
||||||
//return {};
|
|
||||||
}else
|
}else
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1301,32 +1313,26 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//BBS
|
//BBS
|
||||||
auto ret = layered_print_cleareance_valid(*this, warning);
|
StringObjectException layer_warning;
|
||||||
|
auto ret = layered_print_cleareance_valid(*this, &layer_warning);
|
||||||
if (!ret.string.empty()) {
|
if (!ret.string.empty()) {
|
||||||
ret.type = STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT;
|
ret.type = STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
if (!layer_warning.string.empty())
|
||||||
|
add_warning(layer_warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_config.enable_prime_tower) {
|
if (m_config.enable_prime_tower) {
|
||||||
for (const PrintObject* object : m_objects) {
|
for (const PrintObject* object : m_objects) {
|
||||||
if (object->config().precise_z_height.value && warning != nullptr) {
|
if (object->config().precise_z_height.value) {
|
||||||
StringObjectException warningtemp;
|
warn(L("Enabling both precise Z height and the prime tower may cause slicing errors."), "precise_z_height");
|
||||||
warningtemp.string = L("Enabling both precise Z height and the prime tower may cause slicing errors.");
|
|
||||||
warningtemp.opt_key = "precise_z_height";
|
|
||||||
warningtemp.is_warning = true;
|
|
||||||
*warning = warningtemp;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (m_config.enable_wrapping_detection && warning!=nullptr) {
|
if (m_config.enable_wrapping_detection)
|
||||||
StringObjectException warningtemp;
|
warn(L("A prime tower is required for clumping detection; otherwise, there may be flaws on the model."), "enable_prime_tower");
|
||||||
warningtemp.string = L("A prime tower is required for clumping detection; otherwise, there may be flaws on the model.");
|
|
||||||
warningtemp.opt_key = "enable_prime_tower";
|
|
||||||
warningtemp.is_warning = true;
|
|
||||||
*warning = warningtemp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_config.spiral_mode) {
|
if (m_config.spiral_mode) {
|
||||||
@@ -1422,8 +1428,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
|
if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
|
||||||
|| std::abs((filament_diam - first_filament_diam) / first_filament_diam) > 0.1) {
|
|| std::abs((filament_diam - first_filament_diam) / first_filament_diam) > 0.1) {
|
||||||
// return { L("Different nozzle diameters and different filament diameters may not work well when prime tower is enabled. It's very experimental, please proceed with caucious.") };
|
// return { L("Different nozzle diameters and different filament diameters may not work well when prime tower is enabled. It's very experimental, please proceed with caucious.") };
|
||||||
warning->string = L("Different nozzle diameters and different filament diameters may not work well when the prime tower is enabled. It's very experimental, so please proceed with caution.");
|
warn(L("Different nozzle diameters and different filament diameters may not work well when the prime tower is enabled. It's very experimental, so please proceed with caution."), "nozzle_diameter");
|
||||||
warning->opt_key = "nozzle_diameter";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1580,22 +1585,15 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
// Orca: use organic as default
|
// Orca: use organic as default
|
||||||
object->config().support_style == smsDefault) {
|
object->config().support_style == smsDefault) {
|
||||||
|
|
||||||
if (warning) {
|
// Orca: check the support wall count and the base pattern
|
||||||
// Orca: check the support wall count and the base pattern
|
if (object->config().tree_support_wall_count > 1 &&
|
||||||
if (object->config().tree_support_wall_count > 1 &&
|
object->config().support_base_pattern != SupportMaterialPattern::smpNone &&
|
||||||
object->config().support_base_pattern != SupportMaterialPattern::smpNone &&
|
object->config().support_base_pattern != SupportMaterialPattern::smpDefault)
|
||||||
object->config().support_base_pattern != SupportMaterialPattern::smpDefault) {
|
warn(L("For Organic supports, two walls are supported only with the Hollow/Default base pattern."), "support_base_pattern");
|
||||||
warning->string = L("For Organic supports, two walls are supported only with the Hollow/Default base pattern.");
|
|
||||||
warning->opt_key = "support_base_pattern";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Orca: check if the Lightning base pattern selected
|
// Orca: check if the Lightning base pattern selected
|
||||||
if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning) {
|
if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning)
|
||||||
warning->string = L(
|
warn(L("The Lightning base pattern is not supported by this support type; Rectilinear will be used instead."), "support_base_pattern");
|
||||||
"The Lightning base pattern is not supported by this support type; Rectilinear will be used instead.");
|
|
||||||
warning->opt_key = "support_base_pattern";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float extrusion_width = std::min(
|
float extrusion_width = std::min(
|
||||||
support_material_flow(object).width(),
|
support_material_flow(object).width(),
|
||||||
@@ -1607,28 +1605,23 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
if (object->config().tree_support_branch_diameter_organic < object->config().tree_support_tip_diameter)
|
if (object->config().tree_support_branch_diameter_organic < object->config().tree_support_tip_diameter)
|
||||||
return { L("Organic support branch diameter must not be smaller than support tree tip diameter."), object, "tree_support_branch_diameter_organic" };
|
return { L("Organic support branch diameter must not be smaller than support tree tip diameter."), object, "tree_support_branch_diameter_organic" };
|
||||||
}
|
}
|
||||||
} else if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning && warning) {
|
} else if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning) {
|
||||||
// Orca: check if the Lightning base pattern selected
|
// Orca: check if the Lightning base pattern selected
|
||||||
warning->string = L("The Lightning base pattern is not supported by this support type; Rectilinear will be used instead.");
|
warn(L("The Lightning base pattern is not supported by this support type; Rectilinear will be used instead."), "support_base_pattern");
|
||||||
warning->opt_key = "support_base_pattern";
|
} else if (object->config().support_base_pattern == SupportMaterialPattern::smpNone) {
|
||||||
} else if (object->config().support_base_pattern == SupportMaterialPattern::smpNone && warning) {
|
|
||||||
// Orca: check if the Hollow base pattern selected
|
// Orca: check if the Hollow base pattern selected
|
||||||
warning->string = L("The Hollow base pattern is not supported by this support type; Rectilinear will be used instead.");
|
warn(L("The Hollow base pattern is not supported by this support type; Rectilinear will be used instead."), "support_base_pattern");
|
||||||
warning->opt_key = "support_base_pattern";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we have custom support data that would not be used?
|
// Do we have custom support data that would not be used?
|
||||||
// Notify the user in that case.
|
// Notify the user in that case.
|
||||||
if (! object->has_support() && warning) {
|
if (! object->has_support()) {
|
||||||
for (const ModelVolume* mv : object->model_object()->volumes) {
|
for (const ModelVolume* mv : object->model_object()->volumes) {
|
||||||
bool has_enforcers = mv->is_support_enforcer() ||
|
bool has_enforcers = mv->is_support_enforcer() ||
|
||||||
(mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
|
(mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
|
||||||
if (has_enforcers) {
|
if (has_enforcers) {
|
||||||
StringObjectException warningtemp;
|
warn(L("Support enforcers are used but support is not enabled. Please enable support."), "", object);
|
||||||
warningtemp.string = L("Support enforcers are used but support is not enabled. Please enable support.");
|
|
||||||
warningtemp.object = object;
|
|
||||||
*warning = warningtemp;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1780,7 +1773,11 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if print speed/accel/jerk is higher than the maximum speed of the printer
|
// check if print speed/accel/jerk is higher than the maximum speed of the printer
|
||||||
if (warning) {
|
if (warnings) {
|
||||||
|
// The motion-ability checks are mutually exclusive (gated on warning_key), so collect the
|
||||||
|
// single one that fires into a local and push it once - separate from the precise-wall and
|
||||||
|
// shrinkage warnings below.
|
||||||
|
StringObjectException motion_warning;
|
||||||
try {
|
try {
|
||||||
auto check_motion_ability_object_setting = [&](const std::vector<std::string>& keys_to_check, double limit) -> std::string {
|
auto check_motion_ability_object_setting = [&](const std::vector<std::string>& keys_to_check, double limit) -> std::string {
|
||||||
std::string warning_key;
|
std::string warning_key;
|
||||||
@@ -1811,7 +1808,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
if (!ignore_jerk_validation) {
|
if (!ignore_jerk_validation) {
|
||||||
if (m_default_object_config.default_jerk == 1 || m_default_object_config.outer_wall_jerk == 1 ||
|
if (m_default_object_config.default_jerk == 1 || m_default_object_config.outer_wall_jerk == 1 ||
|
||||||
m_default_object_config.inner_wall_jerk == 1) {
|
m_default_object_config.inner_wall_jerk == 1) {
|
||||||
warning->string = L("Setting the jerk speed too low could lead to artifacts on curved surfaces");
|
motion_warning.string = L("Setting the jerk speed too low could lead to artifacts on curved surfaces");
|
||||||
if (m_default_object_config.outer_wall_jerk == 1)
|
if (m_default_object_config.outer_wall_jerk == 1)
|
||||||
warning_key = "outer_wall_jerk";
|
warning_key = "outer_wall_jerk";
|
||||||
else if (m_default_object_config.inner_wall_jerk == 1)
|
else if (m_default_object_config.inner_wall_jerk == 1)
|
||||||
@@ -1819,7 +1816,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
else
|
else
|
||||||
warning_key = "default_jerk";
|
warning_key = "default_jerk";
|
||||||
|
|
||||||
warning->opt_key = warning_key;
|
motion_warning.opt_key = warning_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (warning_key.empty() && m_default_object_config.default_jerk > 0) {
|
if (warning_key.empty() && m_default_object_config.default_jerk > 0) {
|
||||||
@@ -1829,20 +1826,20 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
warning_key.clear();
|
warning_key.clear();
|
||||||
warning_key = check_motion_ability_object_setting(jerk_to_check, max_jerk);
|
warning_key = check_motion_ability_object_setting(jerk_to_check, max_jerk);
|
||||||
if (!warning_key.empty()) {
|
if (!warning_key.empty()) {
|
||||||
warning->string = L(
|
motion_warning.string = L(
|
||||||
"The jerk setting exceeds the printer's maximum jerk (machine_max_jerk_x/machine_max_jerk_y).\n"
|
"The jerk setting exceeds the printer's maximum jerk (machine_max_jerk_x/machine_max_jerk_y).\n"
|
||||||
"Orca will automatically cap the jerk speed to ensure it doesn't surpass the printer's capabilities.\n"
|
"Orca will automatically cap the jerk speed to ensure it doesn't surpass the printer's capabilities.\n"
|
||||||
"You can adjust the maximum jerk setting in your printer's configuration to get higher speeds.");
|
"You can adjust the maximum jerk setting in your printer's configuration to get higher speeds.");
|
||||||
warning->opt_key = warning_key;
|
motion_warning.opt_key = warning_key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check junction deviation
|
// check junction deviation
|
||||||
else if (m_default_object_config.default_junction_deviation.value > max_junction_deviation) {
|
else if (m_default_object_config.default_junction_deviation.value > max_junction_deviation) {
|
||||||
warning->string = L( "Junction deviation setting exceeds the printer's maximum value (machine_max_junction_deviation).\n"
|
motion_warning.string = L( "Junction deviation setting exceeds the printer's maximum value (machine_max_junction_deviation).\n"
|
||||||
"Orca will automatically cap the junction deviation to ensure it doesn't surpass the printer's capabilities.\n"
|
"Orca will automatically cap the junction deviation to ensure it doesn't surpass the printer's capabilities.\n"
|
||||||
"You can adjust the machine_max_junction_deviation value in your printer's configuration to get higher limits.");
|
"You can adjust the machine_max_junction_deviation value in your printer's configuration to get higher limits.");
|
||||||
warning->opt_key = "default_junction_deviation";
|
motion_warning.opt_key = "default_junction_deviation";
|
||||||
}
|
}
|
||||||
|
|
||||||
// check acceleration
|
// check acceleration
|
||||||
@@ -1877,12 +1874,12 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
};
|
};
|
||||||
warning_key = check_motion_ability_object_setting(accel_to_check, max_accel);
|
warning_key = check_motion_ability_object_setting(accel_to_check, max_accel);
|
||||||
if (!warning_key.empty()) {
|
if (!warning_key.empty()) {
|
||||||
warning->string = L("The acceleration setting exceeds the printer's maximum acceleration "
|
motion_warning.string = L("The acceleration setting exceeds the printer's maximum acceleration "
|
||||||
"(machine_max_acceleration_extruding).\nOrca will "
|
"(machine_max_acceleration_extruding).\nOrca will "
|
||||||
"automatically cap the acceleration speed to ensure it doesn't surpass the printer's "
|
"automatically cap the acceleration speed to ensure it doesn't surpass the printer's "
|
||||||
"capabilities.\nYou can adjust the "
|
"capabilities.\nYou can adjust the "
|
||||||
"machine_max_acceleration_extruding value in your printer's configuration to get higher speeds.");
|
"machine_max_acceleration_extruding value in your printer's configuration to get higher speeds.");
|
||||||
warning->opt_key = warning_key;
|
motion_warning.opt_key = warning_key;
|
||||||
}
|
}
|
||||||
if (support_travel_acc) {
|
if (support_travel_acc) {
|
||||||
const auto max_travel = m_config.machine_max_acceleration_travel.values[0];
|
const auto max_travel = m_config.machine_max_acceleration_travel.values[0];
|
||||||
@@ -1892,13 +1889,13 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
};
|
};
|
||||||
warning_key = check_motion_ability_object_setting(accel_to_check, max_travel);
|
warning_key = check_motion_ability_object_setting(accel_to_check, max_travel);
|
||||||
if (!warning_key.empty()) {
|
if (!warning_key.empty()) {
|
||||||
warning->string = L(
|
motion_warning.string = L(
|
||||||
"The travel acceleration setting exceeds the printer's maximum travel acceleration "
|
"The travel acceleration setting exceeds the printer's maximum travel acceleration "
|
||||||
"(machine_max_acceleration_travel).\nOrca will "
|
"(machine_max_acceleration_travel).\nOrca will "
|
||||||
"automatically cap the travel acceleration speed to ensure it doesn't surpass the printer's "
|
"automatically cap the travel acceleration speed to ensure it doesn't surpass the printer's "
|
||||||
"capabilities.\nYou can adjust the "
|
"capabilities.\nYou can adjust the "
|
||||||
"machine_max_acceleration_travel value in your printer's configuration to get higher speeds.");
|
"machine_max_acceleration_travel value in your printer's configuration to get higher speeds.");
|
||||||
warning->opt_key = warning_key;
|
motion_warning.opt_key = warning_key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1924,19 +1921,17 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// check wall sequence and precise outer wall
|
// check wall sequence and precise outer wall
|
||||||
if (m_default_region_config.precise_outer_wall && m_default_region_config.wall_sequence != WallSequence::InnerOuter) {
|
if (m_default_region_config.precise_outer_wall && m_default_region_config.wall_sequence != WallSequence::InnerOuter)
|
||||||
warning->string = L("The precise wall option will be ignored for outer-inner or inner-outer-inner wall sequences.");
|
warn(L("The precise wall option will be ignored for outer-inner or inner-outer-inner wall sequences."), "precise_outer_wall");
|
||||||
warning->opt_key = "precise_outer_wall";
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Orca: validate motion ability failed: " << e.what() << std::endl;
|
BOOST_LOG_TRIVIAL(warning) << "Orca: validate motion ability failed: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
|
if (!motion_warning.string.empty())
|
||||||
|
add_warning(motion_warning);
|
||||||
}
|
}
|
||||||
if (!this->has_same_shrinkage_compensations()){
|
if (!this->has_same_shrinkage_compensations())
|
||||||
warning->string = L("Filament shrinkage will not be used because filament shrinkage for the used filaments does not match.");
|
warn(L("Filament shrinkage will not be used because filament shrinkage for the used filaments does not match."));
|
||||||
warning->opt_key = "";
|
|
||||||
}
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -932,7 +932,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns an empty string if valid, otherwise returns an error message.
|
// Returns an empty string if valid, otherwise returns an error message.
|
||||||
StringObjectException validate(StringObjectException *warning = nullptr, Polygons* collison_polygons = nullptr, std::vector<std::pair<Polygon, float>>* height_polygons = nullptr) const override;
|
StringObjectException validate(std::vector<StringObjectException> *warnings = nullptr, Polygons* collison_polygons = nullptr, std::vector<std::pair<Polygon, float>>* height_polygons = nullptr) const override;
|
||||||
double skirt_first_layer_height() const;
|
double skirt_first_layer_height() const;
|
||||||
Flow brim_flow() const;
|
Flow brim_flow() const;
|
||||||
Flow skirt_flow() const;
|
Flow skirt_flow() const;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ struct StringObjectException
|
|||||||
std::string string;
|
std::string string;
|
||||||
ObjectBase const *object = nullptr;
|
ObjectBase const *object = nullptr;
|
||||||
std::string opt_key;
|
std::string opt_key;
|
||||||
StringExceptionType type; // warning type for tips
|
StringExceptionType type = STRING_EXCEPT_NOT_DEFINED; // warning type for tips
|
||||||
bool is_warning = false;
|
bool is_warning = false;
|
||||||
std::vector<std::string> params; // warning params for tips
|
std::vector<std::string> params; // warning params for tips
|
||||||
};
|
};
|
||||||
@@ -396,7 +396,7 @@ public:
|
|||||||
|
|
||||||
// Validate the print, return empty string if valid, return error if process() cannot (or should not) be started.
|
// Validate the print, return empty string if valid, return error if process() cannot (or should not) be started.
|
||||||
//BBS: add more paremeters to validate
|
//BBS: add more paremeters to validate
|
||||||
virtual StringObjectException validate(StringObjectException *warning = nullptr, Polygons* collison_polygons = nullptr, std::vector<std::pair<Polygon, float>>* height_polygons = nullptr) const { return {}; }
|
virtual StringObjectException validate(std::vector<StringObjectException> *warnings = nullptr, Polygons* collison_polygons = nullptr, std::vector<std::pair<Polygon, float>>* height_polygons = nullptr) const { return {}; }
|
||||||
|
|
||||||
enum ApplyStatus {
|
enum ApplyStatus {
|
||||||
// No change after the Print::apply() call.
|
// No change after the Print::apply() call.
|
||||||
|
|||||||
@@ -613,7 +613,7 @@ std::string SLAPrint::output_filename(const std::string &filename_base) const
|
|||||||
return this->PrintBase::output_filename(m_print_config.filename_format.value, ".sl1", filename_base, &config);
|
return this->PrintBase::output_filename(m_print_config.filename_format.value, ".sl1", filename_base, &config);
|
||||||
}
|
}
|
||||||
|
|
||||||
StringObjectException SLAPrint::validate(StringObjectException *exception, Polygons *collison_polygons, std::vector<std::pair<Polygon, float>> *height_polygons) const
|
StringObjectException SLAPrint::validate(std::vector<StringObjectException> *warnings, Polygons *collison_polygons, std::vector<std::pair<Polygon, float>> *height_polygons) const
|
||||||
{
|
{
|
||||||
for(SLAPrintObject * po : m_objects) {
|
for(SLAPrintObject * po : m_objects) {
|
||||||
|
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ public:
|
|||||||
|
|
||||||
const SLAPrintStatistics& print_statistics() const { return m_print_statistics; }
|
const SLAPrintStatistics& print_statistics() const { return m_print_statistics; }
|
||||||
|
|
||||||
StringObjectException validate(StringObjectException * warning = nullptr,
|
StringObjectException validate(std::vector<StringObjectException> * warnings = nullptr,
|
||||||
Polygons * collison_polygons = nullptr,
|
Polygons * collison_polygons = nullptr,
|
||||||
std::vector<std::pair<Polygon, float>> *height_polygons = nullptr) const override;
|
std::vector<std::pair<Polygon, float>> *height_polygons = nullptr) const override;
|
||||||
|
|
||||||
|
|||||||
@@ -675,13 +675,13 @@ bool BackgroundSlicingProcess::empty() const
|
|||||||
return m_print->empty();
|
return m_print->empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
StringObjectException BackgroundSlicingProcess::validate(StringObjectException *warning, Polygons* collison_polygons, std::vector<std::pair<Polygon, float>>* height_polygons)
|
StringObjectException BackgroundSlicingProcess::validate(std::vector<StringObjectException> *warnings, Polygons* collison_polygons, std::vector<std::pair<Polygon, float>>* height_polygons)
|
||||||
{
|
{
|
||||||
assert(m_print != nullptr);
|
assert(m_print != nullptr);
|
||||||
assert(m_print == m_fff_print);
|
assert(m_print == m_fff_print);
|
||||||
|
|
||||||
m_fff_print->is_BBL_printer() = wxGetApp().preset_bundle->is_bbl_vendor();
|
m_fff_print->is_BBL_printer() = wxGetApp().preset_bundle->is_bbl_vendor();
|
||||||
return m_print->validate(warning, collison_polygons, height_polygons);
|
return m_print->validate(warnings, collison_polygons, height_polygons);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply config over the print. Returns false, if the new config values caused any of the already
|
// Apply config over the print. Returns false, if the new config values caused any of the already
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ public:
|
|||||||
bool empty() const;
|
bool empty() const;
|
||||||
// Validate the print. Returns an empty string if valid, returns an error message if invalid.
|
// Validate the print. Returns an empty string if valid, returns an error message if invalid.
|
||||||
// Call validate before calling start().
|
// Call validate before calling start().
|
||||||
StringObjectException validate(StringObjectException *warning = nullptr, Polygons* collison_polygons = nullptr, std::vector<std::pair<Polygon, float>>* height_polygons = nullptr);
|
StringObjectException validate(std::vector<StringObjectException> *warnings = nullptr, Polygons* collison_polygons = nullptr, std::vector<std::pair<Polygon, float>>* height_polygons = nullptr);
|
||||||
|
|
||||||
// Set the export path of the G-code.
|
// Set the export path of the G-code.
|
||||||
// Once the path is set, the G-code
|
// Once the path is set, the G-code
|
||||||
|
|||||||
@@ -1005,7 +1005,8 @@ private:
|
|||||||
NotificationType::PlaterWarning,
|
NotificationType::PlaterWarning,
|
||||||
NotificationType::ProgressBar,
|
NotificationType::ProgressBar,
|
||||||
NotificationType::PrintHostUpload,
|
NotificationType::PrintHostUpload,
|
||||||
NotificationType::SimplifySuggestion
|
NotificationType::SimplifySuggestion,
|
||||||
|
NotificationType::ValidateWarning
|
||||||
};
|
};
|
||||||
//prepared (basic) notifications
|
//prepared (basic) notifications
|
||||||
// non-static so its not loaded too early. If static, the translations wont load correctly.
|
// non-static so its not loaded too early. If static, the translations wont load correctly.
|
||||||
|
|||||||
@@ -4625,6 +4625,7 @@ struct Plater::priv
|
|||||||
}
|
}
|
||||||
|
|
||||||
void process_validation_warning(StringObjectException const &warning) const;
|
void process_validation_warning(StringObjectException const &warning) const;
|
||||||
|
void process_validation_warnings(const std::vector<StringObjectException> &warnings) const;
|
||||||
|
|
||||||
bool background_processing_enabled() const {
|
bool background_processing_enabled() const {
|
||||||
#ifdef SUPPORT_BACKGROUND_PROCESSING
|
#ifdef SUPPORT_BACKGROUND_PROCESSING
|
||||||
@@ -7949,6 +7950,15 @@ void Plater::priv::process_validation_warning(StringObjectException const &warni
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Plater::priv::process_validation_warnings(const std::vector<StringObjectException> &warnings) const
|
||||||
|
{
|
||||||
|
// ValidateWarning stacks by text (m_multiple_types), so clear the stale set before re-adding.
|
||||||
|
notification_manager->close_notification_of_type(NotificationType::ValidateWarning);
|
||||||
|
for (const StringObjectException &warning : warnings)
|
||||||
|
if (!warning.string.empty())
|
||||||
|
process_validation_warning(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Update background processing thread from the current config and Model.
|
// Update background processing thread from the current config and Model.
|
||||||
// Returns a bitmask of UpdateBackgroundProcessReturnState.
|
// Returns a bitmask of UpdateBackgroundProcessReturnState.
|
||||||
@@ -8029,14 +8039,14 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
|||||||
// The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors.
|
// The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors.
|
||||||
|
|
||||||
//BBS: add is_warning logic
|
//BBS: add is_warning logic
|
||||||
StringObjectException warning;
|
std::vector<StringObjectException> warnings;
|
||||||
//BBS: refine seq-print logic
|
//BBS: refine seq-print logic
|
||||||
Polygons polygons;
|
Polygons polygons;
|
||||||
std::vector<std::pair<Polygon, float>> height_polygons;
|
std::vector<std::pair<Polygon, float>> height_polygons;
|
||||||
StringObjectException err = background_process.validate(&warning, &polygons, &height_polygons);
|
StringObjectException err = background_process.validate(&warnings, &polygons, &height_polygons);
|
||||||
// update string by type
|
// update string by type
|
||||||
q->post_process_string_object_exception(err);
|
q->post_process_string_object_exception(err);
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": validate err=%1%, warning=%2%")%err.string%warning.string;
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": validate err=%1%, warnings=%2%")%err.string%warnings.size();
|
||||||
|
|
||||||
if (err.string.empty()) {
|
if (err.string.empty()) {
|
||||||
this->partplate_list.get_curr_plate()->update_apply_result_invalid(false);
|
this->partplate_list.get_curr_plate()->update_apply_result_invalid(false);
|
||||||
@@ -8047,9 +8057,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
|||||||
if (invalidated != Print::APPLY_STATUS_UNCHANGED && background_processing_enabled())
|
if (invalidated != Print::APPLY_STATUS_UNCHANGED && background_processing_enabled())
|
||||||
return_state |= UPDATE_BACKGROUND_PROCESS_RESTART;
|
return_state |= UPDATE_BACKGROUND_PROCESS_RESTART;
|
||||||
|
|
||||||
// Pass a warning from validation and either show a notification,
|
process_validation_warnings(warnings);
|
||||||
// or hide the old one.
|
|
||||||
process_validation_warning(warning);
|
|
||||||
if (printer_technology == ptFFF) {
|
if (printer_technology == ptFFF) {
|
||||||
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
||||||
view3D->get_canvas3d()->set_as_dirty();
|
view3D->get_canvas3d()->set_as_dirty();
|
||||||
@@ -8062,7 +8070,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
|||||||
// Show error as notification.
|
// Show error as notification.
|
||||||
notification_manager->push_validate_error_notification(err);
|
notification_manager->push_validate_error_notification(err);
|
||||||
//also update the warnings
|
//also update the warnings
|
||||||
process_validation_warning(warning);
|
process_validation_warnings(warnings);
|
||||||
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
|
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
|
||||||
if (printer_technology == ptFFF) {
|
if (printer_technology == ptFFF) {
|
||||||
const Print* print = background_process.fff_print();
|
const Print* print = background_process.fff_print();
|
||||||
@@ -17693,14 +17701,14 @@ void Plater::validate_current_plate(bool& model_fits, bool& validate_error)
|
|||||||
if (p->printer_technology == ptFFF) {
|
if (p->printer_technology == ptFFF) {
|
||||||
//std::string plater_text = _u8L("An object is laid over the boundary of plate or exceeds the height limit.\n"
|
//std::string plater_text = _u8L("An object is laid over the boundary of plate or exceeds the height limit.\n"
|
||||||
// "Please solve the problem by moving it totally on or off the plate, and confirming that the height is within the build volume.");;
|
// "Please solve the problem by moving it totally on or off the plate, and confirming that the height is within the build volume.");;
|
||||||
StringObjectException warning;
|
std::vector<StringObjectException> warnings;
|
||||||
Polygons polygons;
|
Polygons polygons;
|
||||||
std::vector<std::pair<Polygon, float>> height_polygons;
|
std::vector<std::pair<Polygon, float>> height_polygons;
|
||||||
p->background_process.fff_print()->set_check_multi_filaments_compatibility(wxGetApp().app_config->get("enable_high_low_temp_mixed_printing") == "false");
|
p->background_process.fff_print()->set_check_multi_filaments_compatibility(wxGetApp().app_config->get("enable_high_low_temp_mixed_printing") == "false");
|
||||||
StringObjectException err = p->background_process.validate(&warning, &polygons, &height_polygons);
|
StringObjectException err = p->background_process.validate(&warnings, &polygons, &height_polygons);
|
||||||
// update string by type
|
// update string by type
|
||||||
post_process_string_object_exception(err);
|
post_process_string_object_exception(err);
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": validate err=%1%, warning=%2%, model_fits %3%")%err.string%warning.string %model_fits;
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": validate err=%1%, warnings=%2%, model_fits %3%")%err.string%warnings.size() %model_fits;
|
||||||
|
|
||||||
if (err.string.empty()) {
|
if (err.string.empty()) {
|
||||||
p->partplate_list.get_curr_plate()->update_apply_result_invalid(false);
|
p->partplate_list.get_curr_plate()->update_apply_result_invalid(false);
|
||||||
@@ -17708,9 +17716,7 @@ void Plater::validate_current_plate(bool& model_fits, bool& validate_error)
|
|||||||
p->notification_manager->close_notification_of_type(NotificationType::ValidateError);
|
p->notification_manager->close_notification_of_type(NotificationType::ValidateError);
|
||||||
p->notification_manager->bbl_close_3mf_warn_notification();
|
p->notification_manager->bbl_close_3mf_warn_notification();
|
||||||
|
|
||||||
// Pass a warning from validation and either show a notification,
|
p->process_validation_warnings(warnings);
|
||||||
// or hide the old one.
|
|
||||||
p->process_validation_warning(warning);
|
|
||||||
p->view3D->get_canvas3d()->reset_sequential_print_clearance();
|
p->view3D->get_canvas3d()->reset_sequential_print_clearance();
|
||||||
p->view3D->get_canvas3d()->set_as_dirty();
|
p->view3D->get_canvas3d()->set_as_dirty();
|
||||||
p->view3D->get_canvas3d()->request_extra_frame();
|
p->view3D->get_canvas3d()->request_extra_frame();
|
||||||
@@ -17720,7 +17726,7 @@ void Plater::validate_current_plate(bool& model_fits, bool& validate_error)
|
|||||||
p->partplate_list.get_curr_plate()->update_apply_result_invalid(true);
|
p->partplate_list.get_curr_plate()->update_apply_result_invalid(true);
|
||||||
// Show error as notification.
|
// Show error as notification.
|
||||||
p->notification_manager->push_validate_error_notification(err);
|
p->notification_manager->push_validate_error_notification(err);
|
||||||
p->process_validation_warning(warning);
|
p->process_validation_warnings(warnings);
|
||||||
//model_fits = false;
|
//model_fits = false;
|
||||||
validate_error = true;
|
validate_error = true;
|
||||||
p->view3D->get_canvas3d()->set_sequential_print_clearance_visible(true);
|
p->view3D->get_canvas3d()->set_sequential_print_clearance_visible(true);
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
#include "libslic3r/Print.hpp"
|
#include "libslic3r/Print.hpp"
|
||||||
#include "libslic3r/Layer.hpp"
|
#include "libslic3r/Layer.hpp"
|
||||||
|
#include "libslic3r/Model.hpp"
|
||||||
|
|
||||||
#include "test_data.hpp"
|
#include "test_data.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
using namespace Slic3r::Test;
|
using namespace Slic3r::Test;
|
||||||
|
|
||||||
@@ -103,3 +106,166 @@ SCENARIO("Print: Brim generation", "[Print]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Print::validate() warning collection
|
||||||
|
//
|
||||||
|
// validate() returns its warnings in a vector. The warning paths deliberately
|
||||||
|
// differ in how many entries they produce; these tests pin down each behaviour:
|
||||||
|
// * independent checks -> stack (one entry each)
|
||||||
|
// * motion-ability -> coalesce into one (mutually exclusive, gated)
|
||||||
|
// * clumping detection -> one independent warning
|
||||||
|
// * layered clearance -> many collisions concatenated into one entry
|
||||||
|
// * null warnings pointer -> no-op, no crash, no blocking error
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Build `n` 20mm cubes (spread apart, or stacked at the origin when `overlap`) into
|
||||||
|
// `model`/`print` and apply `config`, leaving the print ready to validate(). No slicing needed.
|
||||||
|
void build_cubes(Slic3r::Model& model, Slic3r::Print& print,
|
||||||
|
DynamicPrintConfig config, int n, bool overlap)
|
||||||
|
{
|
||||||
|
config.set_key_value("layer_change_gcode", new ConfigOptionString("G92 E0\n")); // validate() relative-E reset
|
||||||
|
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
ModelObject* object = model.add_object();
|
||||||
|
object->add_volume(Slic3r::Test::mesh(TestMesh::cube_20x20x20));
|
||||||
|
ModelInstance* inst = object->add_instance();
|
||||||
|
inst->set_offset(Vec3d(overlap ? 0.0 : i * 60.0, 0.0, 0.0));
|
||||||
|
}
|
||||||
|
for (ModelObject* mo : model.objects) {
|
||||||
|
mo->ensure_on_bed();
|
||||||
|
print.auto_assign_extruders(mo);
|
||||||
|
}
|
||||||
|
print.apply(model, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build cubes and run validate(), collecting warnings; returns the blocking error.
|
||||||
|
StringObjectException validate_cubes(const DynamicPrintConfig& config,
|
||||||
|
std::vector<StringObjectException>& warnings,
|
||||||
|
int n = 1, bool overlap = false)
|
||||||
|
{
|
||||||
|
Slic3r::Model model;
|
||||||
|
Slic3r::Print print;
|
||||||
|
build_cubes(model, print, config, n, overlap);
|
||||||
|
return print.validate(&warnings);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count_opt_key(const std::vector<StringObjectException>& warnings, const std::string& key)
|
||||||
|
{
|
||||||
|
return std::count_if(warnings.begin(), warnings.end(),
|
||||||
|
[&](const StringObjectException& w) { return w.opt_key == key; });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make `default_acceleration` exceed the machine's extruding-acceleration limit.
|
||||||
|
void trigger_acceleration_warning(DynamicPrintConfig& c)
|
||||||
|
{
|
||||||
|
c.set_key_value("machine_max_acceleration_extruding", new ConfigOptionFloats{ 100. });
|
||||||
|
c.set_key_value("default_acceleration", new ConfigOptionFloat(100000.));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make `default_jerk` exceed the machine's jerk limit (junction deviation off so
|
||||||
|
// the jerk check is not skipped).
|
||||||
|
void trigger_jerk_warning(DynamicPrintConfig& c)
|
||||||
|
{
|
||||||
|
c.set_key_value("machine_max_junction_deviation", new ConfigOptionFloats{ 0. });
|
||||||
|
c.set_key_value("machine_max_jerk_x", new ConfigOptionFloats{ 1. });
|
||||||
|
c.set_key_value("machine_max_jerk_y", new ConfigOptionFloats{ 1. });
|
||||||
|
c.set_key_value("default_jerk", new ConfigOptionFloat(9999.));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precise outer wall is ignored unless the wall sequence is inner-outer.
|
||||||
|
void trigger_precise_wall_warning(DynamicPrintConfig& c)
|
||||||
|
{
|
||||||
|
c.set_key_value("precise_outer_wall", new ConfigOptionBool(true));
|
||||||
|
c.set_key_value("wall_sequence", new ConfigOptionEnum<WallSequence>(WallSequence::OuterInner));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("Print::validate stacks independent warnings", "[Print][validate]")
|
||||||
|
{
|
||||||
|
// Two unrelated checks (region precise-wall + machine acceleration) must each
|
||||||
|
// contribute their own entry.
|
||||||
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
|
trigger_precise_wall_warning(config);
|
||||||
|
trigger_acceleration_warning(config);
|
||||||
|
|
||||||
|
std::vector<StringObjectException> warnings;
|
||||||
|
StringObjectException err = validate_cubes(config, warnings);
|
||||||
|
|
||||||
|
CHECK(err.string.empty());
|
||||||
|
CHECK(warnings.size() >= 2);
|
||||||
|
CHECK(count_opt_key(warnings, "precise_outer_wall") == 1); // jump-to key is preserved
|
||||||
|
for (const auto& w : warnings)
|
||||||
|
CHECK(w.is_warning); // every collected entry is a warning
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Print::validate coalesces motion-ability warnings into one", "[Print][validate]")
|
||||||
|
{
|
||||||
|
// The jerk/junction/acceleration checks are mutually exclusive (gated on a shared
|
||||||
|
// key), so adding a second motion trigger must NOT add a second warning.
|
||||||
|
DynamicPrintConfig accel_only = DynamicPrintConfig::full_print_config();
|
||||||
|
trigger_acceleration_warning(accel_only);
|
||||||
|
std::vector<StringObjectException> w_accel;
|
||||||
|
CHECK(validate_cubes(accel_only, w_accel).string.empty());
|
||||||
|
|
||||||
|
DynamicPrintConfig accel_and_jerk = DynamicPrintConfig::full_print_config();
|
||||||
|
trigger_acceleration_warning(accel_and_jerk);
|
||||||
|
trigger_jerk_warning(accel_and_jerk);
|
||||||
|
std::vector<StringObjectException> w_both;
|
||||||
|
CHECK(validate_cubes(accel_and_jerk, w_both).string.empty());
|
||||||
|
|
||||||
|
CHECK(w_accel.size() >= 1);
|
||||||
|
CHECK(w_both.size() == w_accel.size()); // the extra motion trigger collapses into the same warning
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Print::validate reports the clumping-detection warning", "[Print][validate]")
|
||||||
|
{
|
||||||
|
// A distinct single-shot path: clumping/wrapping detection without a prime tower warns
|
||||||
|
// (and carries the enable_prime_tower jump-to key). enable_prime_tower must be off, as
|
||||||
|
// the warning lives in the no-prime-tower branch.
|
||||||
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
|
config.set_key_value("enable_prime_tower", new ConfigOptionBool(false));
|
||||||
|
config.set_key_value("enable_wrapping_detection", new ConfigOptionBool(true));
|
||||||
|
|
||||||
|
std::vector<StringObjectException> warnings;
|
||||||
|
StringObjectException err = validate_cubes(config, warnings);
|
||||||
|
|
||||||
|
CHECK(err.string.empty());
|
||||||
|
CHECK(count_opt_key(warnings, "enable_prime_tower") == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Print::validate concatenates layered-clearance collisions into one warning", "[Print][validate]")
|
||||||
|
{
|
||||||
|
// In by-layer mode, layered_print_cleareance_valid folds every too-close pair into a
|
||||||
|
// single warning entry (newline-joined), unlike the per-check stacking above. Isolate
|
||||||
|
// that entry by type so unrelated default-config warnings don't affect the assertion.
|
||||||
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
|
|
||||||
|
std::vector<StringObjectException> warnings;
|
||||||
|
StringObjectException err = validate_cubes(config, warnings, /*n=*/3, /*overlap=*/true);
|
||||||
|
|
||||||
|
CHECK(err.string.empty());
|
||||||
|
auto is_layered = [](const StringObjectException& w) {
|
||||||
|
return w.type == STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT; };
|
||||||
|
REQUIRE(std::count_if(warnings.begin(), warnings.end(), is_layered) == 1); // 3 objects, 2 collisions, 1 entry
|
||||||
|
auto it = std::find_if(warnings.begin(), warnings.end(), is_layered);
|
||||||
|
CHECK(it->string.find('\n') != std::string::npos); // the collisions were concatenated
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Print::validate tolerates a null warnings pointer", "[Print][validate]")
|
||||||
|
{
|
||||||
|
// Callers may pass no warnings sink: a warning-producing config must not crash
|
||||||
|
// and must still return without a blocking error.
|
||||||
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
|
trigger_precise_wall_warning(config);
|
||||||
|
trigger_acceleration_warning(config);
|
||||||
|
|
||||||
|
Slic3r::Model model;
|
||||||
|
Slic3r::Print print;
|
||||||
|
build_cubes(model, print, config, /*n=*/1, /*overlap=*/false);
|
||||||
|
|
||||||
|
StringObjectException err = print.validate(); // warnings == nullptr
|
||||||
|
CHECK(err.string.empty());
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user