fix(imex): shorten the off-primary message and block blended filaments

The routing error ran to roughly 450 characters and explained the mechanism
before it got to the remedy. It also offered to "edit the mode in Printer
Settings so its Primary tool is one of %3%", which on a plate whose filaments
resolve to no head at all rendered as "one of no configured extruder". Cut it
to the mode, the tool it prints with, where the plate's filaments actually are,
and the two things the user can do about it.

The second msgid that named candidate modes went with it. It could only suggest
a mode whose primary is among the routed heads, and every mode on the printers
this fires for declares 0:P, so it had nothing to offer.

Blended filaments now return before that check rather than falling through it.
A blend is mixed at the nozzle by its component toolheads, and a parallel mode
is already using those toolheads to print copies or mirrors, so the two cannot
run at once regardless of where the components route -- including when a
component sits on the declared primary. Reaching the routing rule would also
have described them wrongly: mixed slots sit past the end of
physical_extruder_map, so they resolve to no head and read as merely unrouted.

Keeps the empty-list guard the shortening first dropped. validate() reads the
raw physical_extruder_map, whose registered default is a single entry, so a
profile that declares IMEX modes without authoring a map leaves every slot past
the first outside it -- and the sentence ended in a dangling "on .".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Clifford Garwood
2026-08-26 22:04:02 -04:00
co-authored by Claude Opus 5
parent b24733cea8
commit f0778ef5fa
2 changed files with 61 additions and 29 deletions
+31 -1
View File
@@ -923,11 +923,41 @@ TEST_CASE("An IMEX plate whose filament never routes to the Primary tool is bloc
const StringObjectException err = print.validate(&warnings);
REQUIRE_FALSE(err.string.empty());
CHECK(err.string.find("Primary tool") != std::string::npos);
CHECK(err.string.find("T0") != std::string::npos); // the declared primary
CHECK(err.string.find("T2") != std::string::npos); // where the filament actually lives
}
// A blended filament is mixed at the nozzle by its component toolheads, which a parallel
// mode is already using to print copies. Unsupported regardless of where the components
// route, so this must refuse even though component filament 1 sits on the declared primary
// T0 -- and it must refuse with the blended message, not the routing one. Mixed slots sit
// past the end of physical_extruder_map, so the routing rule would call them unrouted.
TEST_CASE("An IMEX plate using a blended filament is blocked", "[MultiFilament][IMEX]")
{
DynamicPrintConfig config = multifilament_config(8);
imex_7x4_printer(config);
all_regions_on_filament(config, 8);
config.set_deserialize_strict({
{ "imex_parallel_mode", "copy" },
{ "filament_is_mixed", "0,0,0,0,0,0,0,1" },
{ "filament_mixed_components", ";;;;;;;1,5" },
});
std::vector<TriangleMesh> meshes;
meshes.push_back(cube(20));
const std::vector<std::vector<ConfigBase::SetDeserializeItem>> overrides{ { { "extruder", "8" } } };
Slic3r::Model model;
Slic3r::Print print;
init_print(std::move(meshes), print, model, config, &overrides, false);
std::vector<StringObjectException> warnings;
const StringObjectException err = print.validate(&warnings);
REQUIRE_FALSE(err.string.empty());
CHECK(err.string.find("Blended filaments") != std::string::npos);
}
// Guard rail: the block must not fire on a well-formed plate. Filament 1 (slot 0) routes to
// head 0, which `copy` declares Primary, so the Primary tool has something to print with.
TEST_CASE("An IMEX plate whose filament routes to the Primary tool validates",