mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-27 22:21:47 +00:00
Fix: show all print validation warnings instead of only the last (#14112)
This commit is contained in:
@@ -3,9 +3,12 @@
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
#include "test_data.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Slic3r;
|
||||
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