mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-14 00:52:04 +00:00
collapse_printer_variants_to_extruders for bbl profiles
This commit is contained in:
@@ -225,9 +225,101 @@ static const std::unordered_map<std::string, std::string> pre_family_model_map {
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
||||||
|
// Orca: BBL printer profiles encode a per-(extruder × nozzle_volume_type) variant matrix,
|
||||||
|
// producing arrays of size n_extruders * n_variants_per_extruder for keys in
|
||||||
|
// printer_options_with_variant_1/_2. OrcaSlicer keeps one machine setting slot per
|
||||||
|
// physical extruder, so collapse these arrays in memory and intentionally keep the
|
||||||
|
// Standard flow variant for each physical extruder. Profile JSON on disk is untouched,
|
||||||
|
// so vendor profiles sync from Bambu Studio without edits.
|
||||||
|
//
|
||||||
|
// extruder_variant_list (one entry per physical extruder, each a comma-separated list of
|
||||||
|
// supported variants for that extruder) is the source of truth — a child profile may
|
||||||
|
// override printer_extruder_variant with the full matrix while inheriting an
|
||||||
|
// already-flattened printer_extruder_id from a parent this function previously processed.
|
||||||
|
static void collapse_printer_variants_to_extruders(DynamicPrintConfig& config)
|
||||||
|
{
|
||||||
|
auto* variant_opt = config.option<ConfigOptionStrings>("printer_extruder_variant");
|
||||||
|
auto* nd_opt = config.option<ConfigOptionFloats>("nozzle_diameter");
|
||||||
|
if (!variant_opt || !nd_opt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const size_t n_extruders = nd_opt->values.size();
|
||||||
|
const size_t orig_variants = variant_opt->values.size();
|
||||||
|
if (n_extruders == 0 || orig_variants <= n_extruders)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto* variant_list_opt = config.option<ConfigOptionStrings>("extruder_variant_list");
|
||||||
|
if (!variant_list_opt || variant_list_opt->values.size() != n_extruders)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Use cumulative offsets, not name search: symmetric printers (H2D) may have
|
||||||
|
// identical variant strings on multiple extruders and a name search would collapse
|
||||||
|
// them all to the first match.
|
||||||
|
const std::string standard_flow = get_nozzle_volume_type_string(NozzleVolumeType::nvtStandard);
|
||||||
|
std::vector<int> keep;
|
||||||
|
keep.reserve(n_extruders);
|
||||||
|
int cursor = 0;
|
||||||
|
for (size_t e = 0; e < n_extruders; ++e) {
|
||||||
|
std::vector<std::string> variants;
|
||||||
|
boost::split(variants, variant_list_opt->values[e], boost::is_any_of(","), boost::token_compress_on);
|
||||||
|
int variant_count = 0;
|
||||||
|
int standard_offset = -1;
|
||||||
|
for (auto& v : variants) {
|
||||||
|
boost::trim(v);
|
||||||
|
if (v.empty())
|
||||||
|
continue;
|
||||||
|
if (standard_offset < 0 && (v == standard_flow || boost::ends_with(v, " " + standard_flow)))
|
||||||
|
standard_offset = variant_count;
|
||||||
|
++variant_count;
|
||||||
|
}
|
||||||
|
if (variant_count == 0)
|
||||||
|
return;
|
||||||
|
keep.push_back(cursor + (standard_offset >= 0 ? standard_offset : 0));
|
||||||
|
cursor += variant_count;
|
||||||
|
}
|
||||||
|
if (size_t(cursor) != orig_variants)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto pick_with_stride = [&](const std::string& key, int stride) {
|
||||||
|
ConfigOption* opt = config.option(key);
|
||||||
|
if (!opt || !opt->is_vector())
|
||||||
|
return;
|
||||||
|
auto* vec = static_cast<ConfigOptionVectorBase*>(opt);
|
||||||
|
// Skip arrays not sized to the full variant grid; replace_nil_and_resize (called
|
||||||
|
// later from extend_default_config_length) handles those.
|
||||||
|
if (vec->size() != orig_variants * size_t(stride))
|
||||||
|
return;
|
||||||
|
// In-place compaction is safe because keep[] is monotonically non-decreasing
|
||||||
|
// (cursor only advances), so read_idx >= write_idx for every assignment.
|
||||||
|
for (size_t e = 0; e < keep.size(); ++e) {
|
||||||
|
for (int s = 0; s < stride; ++s) {
|
||||||
|
vec->set_at(vec, e * stride + s, keep[e] * stride + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec->resize(keep.size() * stride);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& key : printer_options_with_variant_1)
|
||||||
|
pick_with_stride(key, 1);
|
||||||
|
for (const auto& key : printer_options_with_variant_2)
|
||||||
|
pick_with_stride(key, 2);
|
||||||
|
|
||||||
|
// Rebuild rather than flatten: printer_extruder_id may have arrived parent-flattened,
|
||||||
|
// stretched by an earlier replace_nil_and_resize, or fresh from JSON.
|
||||||
|
if (auto* id_opt = config.option<ConfigOptionInts>("printer_extruder_id")) {
|
||||||
|
id_opt->values.clear();
|
||||||
|
id_opt->values.reserve(n_extruders);
|
||||||
|
for (size_t e = 1; e <= n_extruders; ++e)
|
||||||
|
id_opt->values.push_back(int(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 中间版本兼容性处理,如果是nil值,先改成default值,再进行扩展
|
// 中间版本兼容性处理,如果是nil值,先改成default值,再进行扩展
|
||||||
void extend_default_config_length(DynamicPrintConfig& config, const bool set_nil_to_default, const DynamicPrintConfig& defaults)
|
void extend_default_config_length(DynamicPrintConfig& config, const bool set_nil_to_default, const DynamicPrintConfig& defaults)
|
||||||
{
|
{
|
||||||
|
// Orca: flatten BBL variant arrays to one slot per physical extruder before sizing.
|
||||||
|
collapse_printer_variants_to_extruders(config);
|
||||||
|
|
||||||
constexpr int default_param_length = 1;
|
constexpr int default_param_length = 1;
|
||||||
int filament_variant_length = default_param_length;
|
int filament_variant_length = default_param_length;
|
||||||
int process_variant_length = default_param_length;
|
int process_variant_length = default_param_length;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <catch2/catch_all.hpp>
|
#include <catch2/catch_all.hpp>
|
||||||
|
|
||||||
|
#include "libslic3r/Preset.hpp"
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
#include "libslic3r/PrintConfigConstants.hpp"
|
#include "libslic3r/PrintConfigConstants.hpp"
|
||||||
#include "libslic3r/LocalesUtils.hpp"
|
#include "libslic3r/LocalesUtils.hpp"
|
||||||
@@ -315,6 +316,135 @@ SCENARIO("update_non_diff_values_to_base_config preserves child vectors when chi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SCENARIO("extend_default_config_length collapses selectable nozzle flow variants to Standard",
|
||||||
|
"[Config][Variant]") {
|
||||||
|
GIVEN("A single-extruder BBL-style profile with Standard and High Flow variants") {
|
||||||
|
Slic3r::DynamicPrintConfig config;
|
||||||
|
|
||||||
|
config.set_key_value("nozzle_diameter", new Slic3r::ConfigOptionFloats({0.4}));
|
||||||
|
config.set_key_value("extruder_type", new Slic3r::ConfigOptionEnumsGeneric({Slic3r::etDirectDrive}));
|
||||||
|
config.set_key_value("extruder_variant_list", new Slic3r::ConfigOptionStrings({
|
||||||
|
"Direct Drive Standard,Direct Drive High Flow"
|
||||||
|
}));
|
||||||
|
config.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 1}));
|
||||||
|
config.set_key_value("printer_extruder_variant", new Slic3r::ConfigOptionStrings({
|
||||||
|
"Direct Drive Standard", "Direct Drive High Flow"
|
||||||
|
}));
|
||||||
|
config.set_key_value("machine_max_speed_x", new Slic3r::ConfigOptionFloats({
|
||||||
|
500, 200, 200, 200
|
||||||
|
}));
|
||||||
|
|
||||||
|
WHEN("default config lengths are normalized") {
|
||||||
|
Slic3r::extend_default_config_length(config, false, {});
|
||||||
|
|
||||||
|
THEN("only the Standard nozzle flow variant remains") {
|
||||||
|
auto* variants = config.option<Slic3r::ConfigOptionStrings>("printer_extruder_variant");
|
||||||
|
REQUIRE(variants->values == std::vector<std::string>({
|
||||||
|
"Direct Drive Standard"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
THEN("Standard machine limits are kept") {
|
||||||
|
auto* speed_x = config.option<Slic3r::ConfigOptionFloats>("machine_max_speed_x");
|
||||||
|
REQUIRE(speed_x->values == std::vector<double>({500, 200}));
|
||||||
|
}
|
||||||
|
|
||||||
|
THEN("printer extruder ids are rebuilt per physical extruder") {
|
||||||
|
auto* ids = config.option<Slic3r::ConfigOptionInts>("printer_extruder_id");
|
||||||
|
REQUIRE(ids->values == std::vector<int>({1}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SCENARIO("extend_default_config_length collapses mixed physical extruder variants",
|
||||||
|
"[Config][Variant]") {
|
||||||
|
GIVEN("An X2D-style profile with one Direct Drive and one Bowden extruder") {
|
||||||
|
Slic3r::DynamicPrintConfig config;
|
||||||
|
|
||||||
|
config.set_key_value("nozzle_diameter", new Slic3r::ConfigOptionFloats({0.4, 0.4}));
|
||||||
|
config.set_key_value("extruder_type", new Slic3r::ConfigOptionEnumsGeneric({
|
||||||
|
Slic3r::etDirectDrive, Slic3r::etBowden
|
||||||
|
}));
|
||||||
|
config.set_key_value("extruder_variant_list", new Slic3r::ConfigOptionStrings({
|
||||||
|
"Direct Drive Standard,Direct Drive High Flow",
|
||||||
|
"Bowden Standard,Bowden High Flow"
|
||||||
|
}));
|
||||||
|
config.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 1, 2, 2}));
|
||||||
|
config.set_key_value("printer_extruder_variant", new Slic3r::ConfigOptionStrings({
|
||||||
|
"Direct Drive Standard", "Direct Drive High Flow",
|
||||||
|
"Bowden Standard", "Bowden High Flow"
|
||||||
|
}));
|
||||||
|
config.set_key_value("retraction_length", new Slic3r::ConfigOptionFloats({
|
||||||
|
0.8, 0.8, 2.0, 2.0
|
||||||
|
}));
|
||||||
|
config.set_key_value("machine_max_speed_e", new Slic3r::ConfigOptionFloats({
|
||||||
|
30, 30, 30, 30, 120, 120, 120, 120
|
||||||
|
}));
|
||||||
|
|
||||||
|
WHEN("default config lengths are normalized") {
|
||||||
|
Slic3r::extend_default_config_length(config, false, {});
|
||||||
|
|
||||||
|
THEN("one Standard variant is kept for each physical extruder") {
|
||||||
|
auto* variants = config.option<Slic3r::ConfigOptionStrings>("printer_extruder_variant");
|
||||||
|
REQUIRE(variants->values == std::vector<std::string>({
|
||||||
|
"Direct Drive Standard", "Bowden Standard"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
THEN("per-extruder retraction settings are aligned") {
|
||||||
|
auto* retraction = config.option<Slic3r::ConfigOptionFloats>("retraction_length");
|
||||||
|
REQUIRE(retraction->values == std::vector<double>({0.8, 2.0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
THEN("stride-2 machine limits are aligned") {
|
||||||
|
auto* speed_e = config.option<Slic3r::ConfigOptionFloats>("machine_max_speed_e");
|
||||||
|
REQUIRE(speed_e->values == std::vector<double>({30, 30, 120, 120}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SCENARIO("extend_default_config_length collapses symmetric physical extruder variants",
|
||||||
|
"[Config][Variant]") {
|
||||||
|
GIVEN("An H2D-style profile with matching Direct Drive extruder variant lists") {
|
||||||
|
Slic3r::DynamicPrintConfig config;
|
||||||
|
|
||||||
|
config.set_key_value("nozzle_diameter", new Slic3r::ConfigOptionFloats({0.4, 0.4}));
|
||||||
|
config.set_key_value("extruder_type", new Slic3r::ConfigOptionEnumsGeneric({
|
||||||
|
Slic3r::etDirectDrive, Slic3r::etDirectDrive
|
||||||
|
}));
|
||||||
|
config.set_key_value("extruder_variant_list", new Slic3r::ConfigOptionStrings({
|
||||||
|
"Direct Drive Standard,Direct Drive High Flow",
|
||||||
|
"Direct Drive Standard,Direct Drive High Flow"
|
||||||
|
}));
|
||||||
|
config.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 1, 2, 2}));
|
||||||
|
config.set_key_value("printer_extruder_variant", new Slic3r::ConfigOptionStrings({
|
||||||
|
"Direct Drive Standard", "Direct Drive High Flow",
|
||||||
|
"Direct Drive Standard", "Direct Drive High Flow"
|
||||||
|
}));
|
||||||
|
config.set_key_value("machine_max_speed_e", new Slic3r::ConfigOptionFloats({
|
||||||
|
50, 50, 60, 60, 70, 70, 80, 80
|
||||||
|
}));
|
||||||
|
|
||||||
|
WHEN("default config lengths are normalized") {
|
||||||
|
Slic3r::extend_default_config_length(config, false, {});
|
||||||
|
|
||||||
|
THEN("the Standard variant is kept for each physical extruder, not the first matching name globally") {
|
||||||
|
auto* variants = config.option<Slic3r::ConfigOptionStrings>("printer_extruder_variant");
|
||||||
|
REQUIRE(variants->values == std::vector<std::string>({
|
||||||
|
"Direct Drive Standard", "Direct Drive Standard"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
THEN("stride-2 machine limits are kept from each extruder's Standard slot") {
|
||||||
|
auto* speed_e = config.option<Slic3r::ConfigOptionFloats>("machine_max_speed_e");
|
||||||
|
REQUIRE(speed_e->values == std::vector<double>({50, 50, 70, 70}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SCENARIO("DynamicPrintConfig JSON serialization", "[Config]") {
|
// SCENARIO("DynamicPrintConfig JSON serialization", "[Config]") {
|
||||||
// WHEN("DynamicPrintConfig is serialized and deserialized") {
|
// WHEN("DynamicPrintConfig is serialized and deserialized") {
|
||||||
// auto now = std::chrono::high_resolution_clock::now();
|
// auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
|||||||
Reference in New Issue
Block a user