mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-21 09:52:11 +00:00
feat(engine): write per-variant filament values back for selector regroups
When the per-layer filament selector (enable_filament_dynamic_map) migrates a filament across nozzle variants (e.g. Standard -> High Flow), the config write-back only stored the derived extruder map; every per-variant filament value (retraction, nozzle temperature, flow, flush...) kept the numbers resolved from the pre-slice static mapping. Now both dynamic write-back sites (the by-layer branch and the sequential stitch) branch on the result's dynamic support. Migrating results run a mixed-filament expansion that regathers every filament_options_with_variant key from the pristine per-variant superset, giving a migrating filament one config slot per (extruder type x nozzle volume type) it lands on - filament_self_index, filament_extruder_variant, and all value arrays grow in lockstep - and recompute the retract overrides with per-slot machine indices so a nil slot falls back to its own variant's machine value. Non-migrating dynamic results take the merged three-map write-back so re-applies reproduce from the written maps. Unrouted filaments resolve from the result's own default map, so slot resolution never depends on filament_map round-tripping through the plate config. Print::apply reproduces the identical expansion from the persisted group result (shared dedupe helper, expansion function, and slot indices on both sides): the expanded keys sit in the psWipeTower / psGCodeExport invalidate lists, so without the reproduction every re-apply after a selector slice would diff non-empty and permanently invalidate. cal_non_support_filaments now resolves the extruder per layer from the published result for dynamic groupings. filament_map_2 keeps its apply-time static derivation; nothing on the dynamic path reads it (the per-slot machine indices key the override merge), and per-(extruder x volume-type) machine limits in the g-code processor remain a documented follow-up. Every change is gated behind is_dynamic_group_reorder() or a persisted result with dynamic support; no profile sets the flag, so the static fleet's instruction stream is unchanged (20/20 pinned-slice byte gate identical, incl. the sequential repro sliced twice, deterministic). Tests: expansion unit coverage (migrating slots, unrouted fallback via the default map, mis-sized volume map ignored, nullable retract keys in lockstep, slot machine index layout), an end-to-end stub-driven write-back asserting expanded slots, per-layer config-index resolution, the override merge incl. the nil-slot variant fallback, and re-apply stability, plus a real selector slice staying valid across re-apply. Suites green (libslic3r 48987/168, fff_print 633/60).
This commit is contained in:
@@ -1063,12 +1063,24 @@ bool ToolOrdering::cal_non_support_filaments(const PrintConfig &config,
|
||||
int find_count = 0;
|
||||
int find_first_filaments_count = 0;
|
||||
bool has_non_support = has_non_support_filament(config);
|
||||
for (const LayerTools &layer_tool : m_layer_tools) {
|
||||
for (const unsigned int &filament : layer_tool.extruders) {
|
||||
// The selector can move a filament between extruders per layer; resolve the extruder from
|
||||
// the published result then, so the first filament attributed to an extruder is one it
|
||||
// actually prints there. Static results keep the cross-layer filament_map arithmetic.
|
||||
const bool use_dynamic_map = m_nozzle_group_result.is_support_dynamic_nozzle_map() && m_nozzle_group_result.get_layer_count() > 0;
|
||||
auto extruder_for_filament = [&](unsigned int filament, size_t layer_idx) -> int {
|
||||
if (use_dynamic_map)
|
||||
return m_nozzle_group_result.get_extruder_id(static_cast<int>(filament), static_cast<int>(layer_idx));
|
||||
return config.filament_map.values[filament] - 1;
|
||||
};
|
||||
for (size_t layer_idx = 0; layer_idx < m_layer_tools.size(); ++layer_idx) {
|
||||
for (const unsigned int &filament : m_layer_tools[layer_idx].extruders) {
|
||||
//check first filament
|
||||
if (!config.filament_map.values.empty() && initial_filaments[config.filament_map.values[filament] - 1] == -1) {
|
||||
initial_filaments[config.filament_map.values[filament] - 1] = filament;
|
||||
find_first_filaments_count++;
|
||||
if (!config.filament_map.values.empty()) {
|
||||
const int extruder_id = extruder_for_filament(filament, layer_idx);
|
||||
if (extruder_id >= 0 && extruder_id < static_cast<int>(initial_filaments.size()) && initial_filaments[extruder_id] == -1) {
|
||||
initial_filaments[extruder_id] = filament;
|
||||
find_first_filaments_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_non_support) {
|
||||
@@ -1083,8 +1095,9 @@ bool ToolOrdering::cal_non_support_filaments(const PrintConfig &config,
|
||||
if (config.filament_map.values.empty())
|
||||
return true;
|
||||
|
||||
if (initial_non_support_filaments[config.filament_map.values[filament] - 1] == -1) {
|
||||
initial_non_support_filaments[config.filament_map.values[filament] - 1] = filament;
|
||||
const int extruder_id = extruder_for_filament(filament, layer_idx);
|
||||
if (extruder_id >= 0 && extruder_id < static_cast<int>(initial_non_support_filaments.size()) && initial_non_support_filaments[extruder_id] == -1) {
|
||||
initial_non_support_filaments[extruder_id] = filament;
|
||||
find_count++;
|
||||
}
|
||||
|
||||
@@ -2080,17 +2093,15 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
|
||||
auto result = MultiNozzleUtils::LayeredNozzleGroupResult::create(nozzle_map_per_layer, grouping_context.nozzle_info.nozzle_list, used_filaments, filament_sequences);
|
||||
grouping_result = result ? *result : MultiNozzleUtils::LayeredNozzleGroupResult();
|
||||
|
||||
// Derive the extruder-level map for the stats path + config write-back.
|
||||
// Orca: the dynamic (per-layer) result carries no single volume/nozzle map, so only the
|
||||
// extruder map is written back here; the full per-nozzle config write-back
|
||||
// (a dedicated dynamic-map path) is a follow-up behind the dev flag.
|
||||
// Derive the extruder-level map for the stats path; the write-back resolves the
|
||||
// per-variant slots from the full grouping result itself.
|
||||
std::vector<int> derived_maps = grouping_result.get_extruder_map(false); // 1-based
|
||||
if (!derived_maps.empty()) {
|
||||
filament_maps = derived_maps;
|
||||
// A sequential per-object plan must not write its own map: the objects' plans are
|
||||
// stitched print-wide afterwards and written back once from there.
|
||||
if (not_sequential)
|
||||
m_print->update_filament_maps_to_config(filament_maps);
|
||||
m_print->update_to_config_by_nozzle_group_result(grouping_result);
|
||||
}
|
||||
std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value - 1; });
|
||||
}
|
||||
|
||||
@@ -2603,12 +2603,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
stitched_layer_filaments, used_filaments, physical_unprintables,
|
||||
geometric_unprintables, filament_unprintable_volumes);
|
||||
this->set_nozzle_group_result(std::make_shared<MultiNozzleUtils::LayeredNozzleGroupResult>(stitched));
|
||||
// Orca: the dynamic (per-layer) result carries no single volume/nozzle map, so only
|
||||
// the extruder map is written back (matching the by-layer selector branch); the full
|
||||
// per-nozzle config write-back is a follow-up behind the dev flag.
|
||||
std::vector<int> derived_maps = stitched.get_extruder_map(false); // 1-based
|
||||
if (!derived_maps.empty())
|
||||
update_filament_maps_to_config(derived_maps);
|
||||
update_to_config_by_nozzle_group_result(stitched);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -3370,6 +3365,133 @@ void Print::update_filament_maps_to_config(std::vector<int> f_maps, std::vector<
|
||||
m_has_auto_filament_map_result = true;
|
||||
}
|
||||
|
||||
bool Print::collect_filament_variant_uses(const MultiNozzleUtils::LayeredNozzleGroupResult& group_result,
|
||||
const DynamicPrintConfig& config,
|
||||
std::unordered_map<int, std::vector<FilamentVariantUse>>& uses) const
|
||||
{
|
||||
auto opt_filament_type = config.option<ConfigOptionStrings>("filament_type");
|
||||
auto opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(config.option("extruder_type"));
|
||||
if (!opt_filament_type || !opt_extruder_type)
|
||||
return false;
|
||||
|
||||
const size_t filament_count = opt_filament_type->values.size();
|
||||
const size_t extruder_count = opt_extruder_type->values.size();
|
||||
auto add_use = [&](std::set<FilamentVariantUse> &variant_set, const MultiNozzleUtils::NozzleInfo &nozzle) {
|
||||
// Orca: a persisted result can outlive a printer swap; never index the extruder
|
||||
// arrays with a stale nozzle record.
|
||||
if (nozzle.extruder_id < 0 || static_cast<size_t>(nozzle.extruder_id) >= extruder_count)
|
||||
return;
|
||||
FilamentVariantUse use;
|
||||
use.extruder_type = static_cast<ExtruderType>(opt_extruder_type->get_at(nozzle.extruder_id));
|
||||
use.nozzle_volume_type = nozzle.volume_type;
|
||||
use.extruder_id = nozzle.extruder_id;
|
||||
variant_set.insert(use);
|
||||
};
|
||||
for (size_t f_index = 0; f_index < filament_count; ++f_index) {
|
||||
std::set<FilamentVariantUse> variant_set;
|
||||
for (const MultiNozzleUtils::NozzleInfo &nozzle : group_result.get_nozzles_for_filament(static_cast<int>(f_index)))
|
||||
add_use(variant_set, nozzle);
|
||||
// A filament the plan never routes (not printed) still needs a deterministic slot: take
|
||||
// its default-map assignment from the result itself, so both the slice-time write-back
|
||||
// and the apply-time reproduction resolve the same slot even when the surrounding
|
||||
// filament_map has not round-tripped through the plate config in between.
|
||||
if (variant_set.empty()) {
|
||||
if (auto default_nozzle = group_result.get_nozzle_for_filament(static_cast<int>(f_index), -1); default_nozzle.has_value())
|
||||
add_use(variant_set, *default_nozzle);
|
||||
}
|
||||
// Filaments still without a variant stay absent from the map: the slot rebuild then
|
||||
// resolves them from their static filament_map / filament_volume_map assignment.
|
||||
if (!variant_set.empty())
|
||||
uses[static_cast<int>(f_index)] = std::vector<FilamentVariantUse>(variant_set.begin(), variant_set.end());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Print::update_to_config_by_nozzle_group_result(const MultiNozzleUtils::LayeredNozzleGroupResult& group_result)
|
||||
{
|
||||
std::vector<int> derived_maps = group_result.get_extruder_map(false); // 1-based
|
||||
if (derived_maps.empty())
|
||||
return;
|
||||
|
||||
if (!group_result.is_support_dynamic_nozzle_map()) {
|
||||
// No filament actually migrated between nozzles, so the plan reduces to a single
|
||||
// grouping: write all maps like the static paths do, and the next apply re-derives
|
||||
// identical slots from the written maps.
|
||||
std::vector<int> base_filament_map = m_config.filament_map.values;
|
||||
if (base_filament_map.size() != derived_maps.size())
|
||||
base_filament_map.assign(derived_maps.size(), 1);
|
||||
std::vector<int> base_volume_map = m_config.filament_volume_map.values;
|
||||
if (base_volume_map.size() != derived_maps.size())
|
||||
base_volume_map.assign(derived_maps.size(), (int)nvtStandard);
|
||||
const std::vector<unsigned int> used_filaments = group_result.get_used_filaments();
|
||||
update_filament_maps_to_config(FilamentGroupUtils::update_used_filament_values(base_filament_map, derived_maps, used_filaments),
|
||||
FilamentGroupUtils::update_used_filament_values(base_volume_map, group_result.get_volume_map(), used_filaments),
|
||||
group_result.get_nozzle_map());
|
||||
return;
|
||||
}
|
||||
|
||||
// Orca: keep the coarse per-filament extruder map published even though the per-layer truth
|
||||
// lives in the grouping result: the pre-export consumers, the plate read-back after slicing
|
||||
// and the preview panel all key on filament_map. The write is direct — the full map
|
||||
// write-back's single-slot rebuild would undo the per-variant expansion below.
|
||||
m_ori_full_print_config.option<ConfigOptionInts>("filament_map", true)->values = derived_maps;
|
||||
m_config.filament_map.values = derived_maps;
|
||||
|
||||
std::unordered_map<int, std::vector<FilamentVariantUse>> filament_variant_uses;
|
||||
if (!collect_filament_variant_uses(group_result, m_ori_full_print_config, filament_variant_uses)) {
|
||||
// Degenerate config (no filament/extruder typing): fall back to the single-slot
|
||||
// write-back so the maps and overrides stay coherent.
|
||||
update_filament_maps_to_config(derived_maps);
|
||||
return;
|
||||
}
|
||||
|
||||
int extruder_count = 1, extruder_volume_type_count = 1;
|
||||
m_ori_full_print_config.support_different_extruders(extruder_count);
|
||||
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
||||
extruder_volume_type_count = m_ori_full_print_config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
||||
|
||||
// Note: filament_map_2 keeps its apply-time (static) derivation here; the per-slot machine
|
||||
// indices below key the override merge instead, so nothing on this path reads it. Its other
|
||||
// consumers are the three-map write-back (which recomputes it) and the diagnostic copy in
|
||||
// the g-code header; per-(extruder x volume-type) machine limits in the g-code processor
|
||||
// remain a follow-up (see GCodeProcessor::get_axis_max_feedrate).
|
||||
m_full_print_config = m_ori_full_print_config;
|
||||
std::set<std::string> filament_keys = filament_options_with_variant;
|
||||
filament_keys.insert("filament_self_index");
|
||||
std::vector<int> slot_machine_indices;
|
||||
m_full_print_config.update_filament_config_values_for_multiple_extruders(m_full_print_config, filament_variant_uses,
|
||||
extruder_count, extruder_volume_type_count,
|
||||
filament_keys, "filament_self_index", "filament_extruder_variant",
|
||||
&slot_machine_indices);
|
||||
|
||||
const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
|
||||
const std::string filament_prefix = "filament_";
|
||||
t_config_option_keys print_diff;
|
||||
DynamicPrintConfig filament_overrides;
|
||||
for (auto& opt_key: extruder_retract_keys)
|
||||
{
|
||||
const ConfigOption *opt_new_filament = m_full_print_config.option(filament_prefix + opt_key);
|
||||
const ConfigOption *opt_new_machine = m_full_print_config.option(opt_key);
|
||||
const ConfigOption *opt_old_machine = m_config.option(opt_key);
|
||||
|
||||
if (opt_new_filament)
|
||||
compute_filament_override_value(opt_key, opt_old_machine, opt_new_machine, opt_new_filament, m_full_print_config, print_diff, filament_overrides, slot_machine_indices);
|
||||
}
|
||||
|
||||
{
|
||||
t_config_option_keys keys(filament_options_with_variant.begin(), filament_options_with_variant.end());
|
||||
keys.push_back("filament_self_index");
|
||||
m_config.apply_only(m_full_print_config, keys, true);
|
||||
}
|
||||
if (!print_diff.empty()) {
|
||||
m_placeholder_parser.apply_config(filament_overrides);
|
||||
m_config.apply(filament_overrides);
|
||||
}
|
||||
|
||||
update_filament_self_index_cache();
|
||||
m_has_auto_filament_map_result = true;
|
||||
}
|
||||
|
||||
void Print::apply_config_for_render(const DynamicConfig &config)
|
||||
{
|
||||
m_config.apply(config);
|
||||
|
||||
@@ -1006,6 +1006,13 @@ public:
|
||||
const ToolOrdering& tool_ordering() const { return m_tool_ordering; }
|
||||
|
||||
void update_filament_maps_to_config(std::vector<int> f_maps, std::vector<int> f_volume_maps = std::vector<int>{}, std::vector<int> f_nozzle_maps = std::vector<int>{});
|
||||
// Write-back for a selector (per-layer planned) grouping result. When a filament actually
|
||||
// migrates between nozzle variants, rebuilds the per-slot filament arrays so it holds one
|
||||
// slot per variant and recomputes the extruder retract overrides against the expanded
|
||||
// slots — update_filament_maps_to_config's single-slot rebuild cannot represent a
|
||||
// migration. A result without migration reduces to a single grouping and takes the
|
||||
// three-map write-back like the static paths.
|
||||
void update_to_config_by_nozzle_group_result(const MultiNozzleUtils::LayeredNozzleGroupResult& group_result);
|
||||
void apply_config_for_render(const DynamicConfig &config);
|
||||
|
||||
// 1 based group ids
|
||||
@@ -1242,6 +1249,15 @@ private:
|
||||
void _make_wipe_tower();
|
||||
void finalize_first_layer_convex_hull();
|
||||
void update_filament_self_index_cache();
|
||||
// Deduplicates, per filament, the (extruder type x volume type) variants the grouping
|
||||
// result routes it through; filaments the plan never routes get their default-map
|
||||
// assignment so the slot resolution never depends on the (mutable) filament_map. config
|
||||
// must carry extruder_type; returns false when it does not. Both the slice-time write-back
|
||||
// and the apply-time reproduction call this with m_ori_full_print_config so the two
|
||||
// expansions resolve identical slots.
|
||||
bool collect_filament_variant_uses(const MultiNozzleUtils::LayeredNozzleGroupResult& group_result,
|
||||
const DynamicPrintConfig& config,
|
||||
std::unordered_map<int, std::vector<FilamentVariantUse>>& uses) const;
|
||||
|
||||
// Islands of objects and their supports extruded at the 1st layer.
|
||||
Polygons first_layer_islands() const;
|
||||
|
||||
@@ -224,7 +224,11 @@ static t_config_option_keys print_config_diffs(
|
||||
const DynamicPrintConfig &new_full_config,
|
||||
DynamicPrintConfig &filament_overrides,
|
||||
int plate_index,
|
||||
std::vector<int>& filament_maps)
|
||||
std::vector<int>& filament_maps,
|
||||
// Per-slot machine indices when the filament arrays hold the per-variant expansion of a
|
||||
// selector result (one slot per variant a filament migrates through); the per-filament
|
||||
// map cannot index the expanded override arrays. Null on the single-slot path.
|
||||
const std::vector<int>* dynamic_override_indices = nullptr)
|
||||
{
|
||||
const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
|
||||
const std::string filament_prefix = "filament_";
|
||||
@@ -240,9 +244,14 @@ static t_config_option_keys print_config_diffs(
|
||||
const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
|
||||
|
||||
if (opt_new_filament != nullptr) {
|
||||
std::vector<int> filament_map_indices(filament_maps.size(), 0);
|
||||
for (int i = 0; i < filament_maps.size(); i++)
|
||||
filament_map_indices[i] = filament_maps[i] - 1;
|
||||
std::vector<int> filament_map_indices;
|
||||
if (dynamic_override_indices)
|
||||
filament_map_indices = *dynamic_override_indices;
|
||||
else {
|
||||
filament_map_indices.assign(filament_maps.size(), 0);
|
||||
for (int i = 0; i < filament_maps.size(); i++)
|
||||
filament_map_indices[i] = filament_maps[i] - 1;
|
||||
}
|
||||
compute_filament_override_value(opt_key, opt_old, opt_new, opt_new_filament, new_full_config, print_diff, filament_overrides, filament_map_indices);
|
||||
} else if (*opt_new != *opt_old) {
|
||||
//BBS: add plate_index logic for wipe_tower_x/wipe_tower_y
|
||||
@@ -1171,6 +1180,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
||||
int extruder_count = 1, extruder_volume_type_count = 1;
|
||||
bool different_extruder = false;
|
||||
// Filled only when the filament arrays are rebuilt from a persisted selector result below;
|
||||
// print_config_diffs then keys the retract overrides per expanded slot.
|
||||
std::vector<int> dynamic_slot_indices;
|
||||
|
||||
different_extruder = new_full_config.support_different_extruders(extruder_count);
|
||||
extruder_volume_type_count = new_full_config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
||||
@@ -1189,7 +1201,20 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
|
||||
std::set<std::string> filament_keys = filament_options_with_variant;
|
||||
filament_keys.insert("filament_self_index");
|
||||
if ((extruder_count > 1) || different_extruder)
|
||||
// A persisted selector result with an actual migration means the last slice rebuilt the
|
||||
// per-slot filament arrays from it (one slot per variant a filament prints through).
|
||||
// Reproduce that exact expansion here so an unchanged config diffs empty — the expanded
|
||||
// keys invalidate the wipe tower / g-code export, and the placeholder parser aliases
|
||||
// the full config — instead of trimming back to one slot per filament.
|
||||
auto group_result = std::dynamic_pointer_cast<MultiNozzleUtils::LayeredNozzleGroupResult>(this->get_nozzle_group_result());
|
||||
std::unordered_map<int, std::vector<FilamentVariantUse>> filament_variant_uses;
|
||||
if (group_result && group_result->is_support_dynamic_nozzle_map()
|
||||
&& collect_filament_variant_uses(*group_result, m_ori_full_print_config, filament_variant_uses))
|
||||
new_full_config.update_filament_config_values_for_multiple_extruders(m_ori_full_print_config, filament_variant_uses,
|
||||
extruder_count, extruder_volume_type_count, filament_keys,
|
||||
"filament_self_index", "filament_extruder_variant",
|
||||
&dynamic_slot_indices);
|
||||
else if ((extruder_count > 1) || different_extruder)
|
||||
new_full_config.update_values_to_printer_extruders_for_multiple_filaments(m_ori_full_print_config, extruder_count, extruder_volume_type_count, filament_keys,
|
||||
"filament_self_index", "filament_extruder_variant");
|
||||
}
|
||||
@@ -1210,7 +1235,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
|
||||
DynamicPrintConfig filament_overrides;
|
||||
//BBS: add plate index
|
||||
t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides, this->m_plate_index, filament_maps);
|
||||
t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides, this->m_plate_index, filament_maps,
|
||||
dynamic_slot_indices.empty() ? nullptr : &dynamic_slot_indices);
|
||||
// Orca: filament_map_2 is engine-derived state, never a user input: the rebuild below
|
||||
// recomputes it from filament_map/filament_volume_map/the variant slots on every apply
|
||||
// (all of which are diffed and invalidation-listed on their own), and the grouping
|
||||
|
||||
@@ -10759,6 +10759,145 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
|
||||
}
|
||||
}
|
||||
|
||||
// Regathers a vector option's values through per-slot source indices (one input index per
|
||||
// output slot). Out-of-range indices keep the first value, matching get_at's fallback.
|
||||
template<typename OptType, typename ValueType>
|
||||
static void gather_option_values(const std::string &key, OptType *opt, const std::vector<int> &slot_param_indices)
|
||||
{
|
||||
if (!opt || opt->values.empty()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% not found or empty, skipping")%__LINE__%key;
|
||||
return;
|
||||
}
|
||||
std::vector<ValueType> new_values;
|
||||
new_values.reserve(slot_param_indices.size());
|
||||
for (int idx : slot_param_indices) {
|
||||
if (idx < 0 || static_cast<size_t>(idx) >= opt->values.size()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: option %2% slot index %3% out of range, keeping first value")%__LINE__%key%idx;
|
||||
new_values.emplace_back(opt->values.front());
|
||||
}
|
||||
else
|
||||
new_values.emplace_back(opt->values[idx]);
|
||||
}
|
||||
opt->values = std::move(new_values);
|
||||
}
|
||||
|
||||
void DynamicPrintConfig::update_filament_config_values_for_multiple_extruders(DynamicPrintConfig& printer_config,
|
||||
const std::unordered_map<int, std::vector<FilamentVariantUse>>& filament_variant_uses,
|
||||
int extruder_count, int extruder_nozzle_volume_count,
|
||||
std::set<std::string>& key_set, std::string id_name, std::string variant_name,
|
||||
std::vector<int>* slot_machine_indices)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: extruder_count %2%, extruder_nozzle_volume_count %3%")%__LINE__ %extruder_count %extruder_nozzle_volume_count;
|
||||
|
||||
auto opt_filament_map = printer_config.option<ConfigOptionInts>("filament_map");
|
||||
if (!opt_filament_map) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: filament_map option not found, skipping")%__LINE__;
|
||||
return;
|
||||
}
|
||||
std::vector<int> filament_maps = opt_filament_map->values;
|
||||
size_t filament_count = filament_maps.size();
|
||||
auto opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_config.option("extruder_type"));
|
||||
auto opt_nozzle_volume_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_config.option("nozzle_volume_type"));
|
||||
if (!opt_extruder_type || !opt_nozzle_volume_type) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: extruder_type or nozzle_volume_type option not found, skipping")%__LINE__;
|
||||
return;
|
||||
}
|
||||
|
||||
auto opt_filament_volume_maps = dynamic_cast<const ConfigOptionInts*>(printer_config.option("filament_volume_map"));
|
||||
std::vector<int> filament_volume_maps;
|
||||
// Orca: same sizing guard as the single-slot rebuild above — honour the per-filament volume
|
||||
// map only when a producer sized it to the filament count.
|
||||
if (opt_filament_volume_maps && opt_filament_volume_maps->values.size() == filament_count)
|
||||
filament_volume_maps = opt_filament_volume_maps->values;
|
||||
auto opt_ids = id_name.empty() ? nullptr : dynamic_cast<const ConfigOptionInts*>(this->option(id_name));
|
||||
|
||||
std::vector<int> slot_param_indices;
|
||||
slot_param_indices.reserve(filament_count * 2);
|
||||
if (slot_machine_indices) {
|
||||
slot_machine_indices->clear();
|
||||
slot_machine_indices->reserve(filament_count * 2);
|
||||
}
|
||||
|
||||
auto resolve_slot = [&](int f_index, ExtruderType extruder_type, NozzleVolumeType nozzle_volume_type, int extruder_id_1based) {
|
||||
int param_index = get_index_for_extruder(f_index + 1, id_name, extruder_type, nozzle_volume_type, variant_name);
|
||||
if (param_index < 0) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: could not found extruder_type %2%, nozzle_volume_type %3%, filament_index %4%, extruder index %5%")
|
||||
%__LINE__ %s_keys_names_ExtruderType[extruder_type] % s_keys_names_NozzleVolumeType[nozzle_volume_type] % (f_index+1) %extruder_id_1based;
|
||||
assert(false);
|
||||
//for some updates happens in a invalid state(caused by popup window)
|
||||
//we need to avoid crash
|
||||
param_index = 0;
|
||||
if (opt_ids) {
|
||||
for (int i = 0; i < opt_ids->values.size(); i++)
|
||||
if (opt_ids->values[i] == (f_index + 1)) {
|
||||
param_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
slot_param_indices.push_back(param_index);
|
||||
if (slot_machine_indices) {
|
||||
// Orca: key each output slot to the machine slot of its own variant (the same
|
||||
// derivation the per-filament slot map uses), so the retract-override nil fallback
|
||||
// stays aligned when a filament occupies more than one slot.
|
||||
int machine_index = printer_config.get_index_for_extruder(extruder_id_1based, "print_extruder_id", extruder_type, nozzle_volume_type, "print_extruder_variant");
|
||||
slot_machine_indices->push_back(std::max(machine_index, 0));
|
||||
}
|
||||
};
|
||||
|
||||
for (int f_index = 0; f_index < static_cast<int>(filament_count); f_index++) {
|
||||
auto uses_it = filament_variant_uses.find(f_index);
|
||||
if (uses_it != filament_variant_uses.end() && !uses_it->second.empty()) {
|
||||
for (const FilamentVariantUse &use : uses_it->second)
|
||||
resolve_slot(f_index, use.extruder_type, use.nozzle_volume_type, use.extruder_id + 1);
|
||||
}
|
||||
else {
|
||||
ExtruderType extruder_type = (ExtruderType)(opt_extruder_type->get_at(filament_maps[f_index] - 1));
|
||||
NozzleVolumeType nozzle_volume_type = (NozzleVolumeType)(opt_nozzle_volume_type->get_at(filament_maps[f_index] - 1));
|
||||
if ((extruder_nozzle_volume_count > extruder_count || nozzle_volume_type == nvtHybrid) && !filament_volume_maps.empty())
|
||||
nozzle_volume_type = (NozzleVolumeType)(filament_volume_maps[f_index]);
|
||||
resolve_slot(f_index, extruder_type, nozzle_volume_type, filament_maps[f_index]);
|
||||
}
|
||||
}
|
||||
|
||||
const ConfigDef *config_def = this->def();
|
||||
if (!config_def) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: can not find config define")%__LINE__;
|
||||
return;
|
||||
}
|
||||
bool has_id_key = !id_name.empty() && key_set.count(id_name) > 0;
|
||||
for (auto &key : key_set) {
|
||||
if (has_id_key && key == id_name)
|
||||
continue;
|
||||
const ConfigOptionDef *optdef = config_def->get(key);
|
||||
if (!optdef) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: can not find opt define for %2%")%__LINE__%key;
|
||||
continue;
|
||||
}
|
||||
switch (optdef->type) {
|
||||
case coStrings: gather_option_values<ConfigOptionStrings, std::string>(key, this->option<ConfigOptionStrings>(key), slot_param_indices); break;
|
||||
case coInts: gather_option_values<ConfigOptionInts, int>(key, this->option<ConfigOptionInts>(key), slot_param_indices); break;
|
||||
case coFloats: gather_option_values<ConfigOptionFloats, double>(key, this->option<ConfigOptionFloats>(key), slot_param_indices); break;
|
||||
case coPercents: gather_option_values<ConfigOptionPercents, double>(key, this->option<ConfigOptionPercents>(key), slot_param_indices); break;
|
||||
case coFloatsOrPercents: gather_option_values<ConfigOptionFloatsOrPercents, FloatOrPercent>(key, this->option<ConfigOptionFloatsOrPercents>(key), slot_param_indices); break;
|
||||
case coBools: gather_option_values<ConfigOptionBools, unsigned char>(key, this->option<ConfigOptionBools>(key), slot_param_indices); break;
|
||||
case coEnums: gather_option_values<ConfigOptionEnumsGeneric, int>(key, this->option<ConfigOptionEnumsGeneric>(key), slot_param_indices); break;
|
||||
default:
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: unsupported option type for %2%")%__LINE__%key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Orca: also require a non-empty id list; get_at on an empty vector is undefined.
|
||||
if (has_id_key && opt_ids && !opt_ids->values.empty()) {
|
||||
// remap the id list itself last: the slot resolution above still needs the original ids
|
||||
std::vector<int> new_values;
|
||||
new_values.reserve(slot_param_indices.size());
|
||||
for (int idx : slot_param_indices)
|
||||
new_values.push_back(opt_ids->get_at(idx));
|
||||
const_cast<ConfigOptionInts*>(opt_ids)->values = std::move(new_values);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Options in printer_options_with_variant_2 are stored as (normal,silent) pairs per printer variant.
|
||||
// Some legacy presets/projects carry a variant list but still store only one pair; normalize to avoid crashes.
|
||||
|
||||
@@ -683,6 +683,23 @@ class StaticPrintConfig;
|
||||
// Minimum object distance for arrangement, based on printer technology.
|
||||
double min_object_distance(const ConfigBase &cfg);
|
||||
|
||||
// One (extruder type x nozzle volume type) parameter variant a filament prints through, plus a
|
||||
// representative physical extruder observed using it. Ordering (and set-dedup identity) covers
|
||||
// the variant pair only, so the same variant reached through two extruders keeps one config slot.
|
||||
struct FilamentVariantUse
|
||||
{
|
||||
ExtruderType extruder_type{etDirectDrive};
|
||||
NozzleVolumeType nozzle_volume_type{nvtStandard};
|
||||
int extruder_id{0}; // 0-based, first extruder seen using this variant
|
||||
|
||||
bool operator<(const FilamentVariantUse &other) const
|
||||
{
|
||||
if (extruder_type != other.extruder_type)
|
||||
return extruder_type < other.extruder_type;
|
||||
return nozzle_volume_type < other.nozzle_volume_type;
|
||||
}
|
||||
};
|
||||
|
||||
// Slic3r dynamic configuration, used to override the configuration
|
||||
// per object, per modification volume or per printing material.
|
||||
// The dynamic configuration is also used to store user modifications of the print global parameters,
|
||||
@@ -748,6 +765,18 @@ public:
|
||||
std::vector<int> update_values_to_printer_extruders(DynamicPrintConfig& printer_config, int extruder_count, int extruder_nozzle_volume_count, std::vector<std::vector<NozzleVolumeType>>& nv_types,
|
||||
std::set<std::string>& key_set, std::string id_name, std::string variant_name, unsigned int stride = 1, unsigned int extruder_id = 0, NozzleVolumeType filament_nvt = nvtStandard);
|
||||
void update_values_to_printer_extruders_for_multiple_filaments(DynamicPrintConfig& printer_config, int extruder_count, int extruder_nozzle_volume_count, std::set<std::string>& key_set, std::string id_name, std::string variant_name);
|
||||
// Rebuilds the per-slot filament arrays from a per-layer grouping outcome: a filament that
|
||||
// prints through several (extruder x nozzle volume type) variants keeps one slot per variant
|
||||
// (unlike the single-slot rebuild above), so layer-aware consumers can resolve the slot the
|
||||
// current layer actually prints with. Filaments absent from filament_variant_uses keep a
|
||||
// single slot resolved from filament_map / filament_volume_map. When slot_machine_indices is
|
||||
// non-null it receives one machine-variant slot index per output slot (the nil-value fallback
|
||||
// keying for the extruder retract overrides; a per-filament map cannot index expanded arrays).
|
||||
void update_filament_config_values_for_multiple_extruders(DynamicPrintConfig& printer_config,
|
||||
const std::unordered_map<int, std::vector<FilamentVariantUse>>& filament_variant_uses,
|
||||
int extruder_count, int extruder_nozzle_volume_count,
|
||||
std::set<std::string>& key_set, std::string id_name, std::string variant_name,
|
||||
std::vector<int>* slot_machine_indices = nullptr);
|
||||
|
||||
void update_non_diff_values_to_base_config(DynamicPrintConfig& new_config, const t_config_option_keys& keys, const std::set<std::string>& different_keys, std::string extruder_id_name, std::string extruder_variant_name,
|
||||
std::set<std::string>& key_set1, std::set<std::string>& key_set2);
|
||||
|
||||
Reference in New Issue
Block a user