mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-24 03:12:07 +00:00
feat(gui): multi-nozzle UI for H2C/A2L
Multi-nozzle sync widget, AMS rack-nozzle mapping popup, calibration rework, send-dialog nozzle mapping and extruder-count UI. Includes the fix to persist the AMS sync badge on filament cards (H2C/A2L and direct-sync printers).
This commit is contained in:
@@ -122,6 +122,8 @@
|
||||
#include "NotificationManager.hpp"
|
||||
#include "PresetComboBoxes.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "Widgets/MultiNozzleSync.hpp" // NozzleOption, tryPopUpMultiNozzleDialog, setExtruderNozzleCount
|
||||
#include "DeviceCore/DevNozzleSystem.h" // DevNozzle, GetExtNozzles / GetRackNozzles
|
||||
#include "ProjectDirtyStateManager.hpp"
|
||||
#include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification
|
||||
#include "Gizmos/GLGizmoSVG.hpp" // Drop SVG file
|
||||
@@ -584,7 +586,11 @@ struct Sidebar::priv
|
||||
void jump_to_object(ObjectDataViewModelNode* item);
|
||||
void can_search();
|
||||
|
||||
bool sync_extruder_list(bool &only_external_material);
|
||||
bool sync_extruder_list(bool &only_external_material, bool is_manual = false);
|
||||
// Resolve the nozzle option for a multi-nozzle machine. Returns nullopt (and is a no-op) unless
|
||||
// extruder_count >= 2 && support_multi_nozzle. When is_manual, always pops the MultiNozzleSyncDialog;
|
||||
// otherwise reuses the app_config-cached option when the machine's nozzle config is unchanged.
|
||||
std::optional<NozzleOption> get_nozzle_options(MachineObject* obj, int extruder_count, bool support_multi_nozzle, bool is_manual);
|
||||
bool switch_diameter(bool single);
|
||||
void update_sync_status(const MachineObject* obj);
|
||||
|
||||
@@ -1323,7 +1329,288 @@ static bool is_skip_high_flow_printer(const std::string& printer)
|
||||
return invalidate_list.count(printer);
|
||||
};
|
||||
|
||||
bool Sidebar::priv::sync_extruder_list(bool &only_external_material)
|
||||
// ---- Multi-nozzle sync helpers ----------------------------
|
||||
// Serialize/deserialize the machine nozzle config + chosen NozzleOption for the app_config
|
||||
// "sync_extruder" reuse cache, and Sidebar::priv::get_nozzle_options (the nozzle picker).
|
||||
|
||||
static std::string serialize_nozzle_config(const std::map<int, std::vector<DevNozzle>>& nozzle_cfg_map)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
std::vector<DevNozzle> deputy_nozzles;
|
||||
auto deputy_it = nozzle_cfg_map.find(1);
|
||||
if (deputy_it != nozzle_cfg_map.end()) {
|
||||
deputy_nozzles = deputy_it->second;
|
||||
}
|
||||
|
||||
std::vector<DevNozzle> main_nozzles;
|
||||
auto main_it = nozzle_cfg_map.find(0);
|
||||
if (main_it != nozzle_cfg_map.end()) {
|
||||
main_nozzles = main_it->second;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < deputy_nozzles.size(); ++i) {
|
||||
if (i > 0) oss << ";";
|
||||
oss << std::fixed << std::setprecision(1) << deputy_nozzles[i].GetNozzleDiameter() << ","
|
||||
<< static_cast<int>(deputy_nozzles[i].GetNozzleFlowType());
|
||||
}
|
||||
|
||||
oss << "|";
|
||||
|
||||
for (size_t i = 0; i < main_nozzles.size(); ++i) {
|
||||
if (i > 0) oss << ";";
|
||||
oss << std::fixed << std::setprecision(1) << main_nozzles[i].GetNozzleDiameter() << ","
|
||||
<< static_cast<int>(main_nozzles[i].GetNozzleFlowType());
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static std::map<int, std::vector<DevNozzle>> deserialize_nozzle_config(const std::string &config_str)
|
||||
{
|
||||
std::map<int, std::vector<DevNozzle>> nozzle_cfg_map;
|
||||
if (config_str.empty()) return nozzle_cfg_map;
|
||||
|
||||
std::vector<std::string> extruder_parts;
|
||||
boost::split(extruder_parts, config_str, boost::is_any_of("|"));
|
||||
|
||||
auto get_nozzles_from_string = [](const std::string& part_str) -> std::vector<DevNozzle> {
|
||||
std::vector<DevNozzle> nozzles;
|
||||
std::vector<std::string> parts;
|
||||
boost::split(parts, part_str, boost::is_any_of(";"));
|
||||
for (const auto &part : parts) {
|
||||
std::vector<std::string> values;
|
||||
boost::split(values, part, boost::is_any_of(","));
|
||||
if (values.size() == 2) {
|
||||
DevNozzle nozzle;
|
||||
nozzle.m_diameter = std::stof(values[0]);
|
||||
nozzle.m_nozzle_flow = static_cast<NozzleFlowType>(std::stoi(values[1]));
|
||||
nozzles.push_back(nozzle);
|
||||
}
|
||||
}
|
||||
return nozzles;
|
||||
};
|
||||
|
||||
if (extruder_parts.size() != 2) {
|
||||
auto nozzles = get_nozzles_from_string(config_str);
|
||||
nozzle_cfg_map[MAIN_EXTRUDER_ID] = nozzles;
|
||||
nozzle_cfg_map[DEPUTY_EXTRUDER_ID] = { DevNozzle() };
|
||||
return nozzle_cfg_map;
|
||||
}
|
||||
|
||||
if (!extruder_parts[0].empty()) {
|
||||
auto nozzles = get_nozzles_from_string(extruder_parts[0]);
|
||||
nozzle_cfg_map[DEPUTY_EXTRUDER_ID] = nozzles;
|
||||
}
|
||||
if (!extruder_parts[1].empty()) {
|
||||
auto nozzles = get_nozzles_from_string(extruder_parts[1]);
|
||||
nozzle_cfg_map[MAIN_EXTRUDER_ID] = nozzles;
|
||||
}
|
||||
|
||||
return nozzle_cfg_map;
|
||||
}
|
||||
|
||||
static bool is_same_nozzle_config(const std::map<int, std::vector<DevNozzle>> &config1, const std::map<int, std::vector<DevNozzle>> &config2)
|
||||
{
|
||||
if (config1.size() != config2.size()) return false;
|
||||
|
||||
for (const auto& [eid, nozzles1] : config1) {
|
||||
auto it = config2.find(eid);
|
||||
if (it == config2.end()) return false;
|
||||
|
||||
const auto &nozzles2 = it->second;
|
||||
if (nozzles1.size() != nozzles2.size()) return false;
|
||||
|
||||
auto sorted_nozzles1 = nozzles1;
|
||||
auto sorted_nozzles2 = nozzles2;
|
||||
|
||||
auto compare_nozzle = [](const DevNozzle &a, const DevNozzle &b) {
|
||||
float dia_a = a.GetNozzleDiameter();
|
||||
float dia_b = b.GetNozzleDiameter();
|
||||
if (std::abs(dia_a - dia_b) > EPSILON) {
|
||||
return dia_a < dia_b;
|
||||
}
|
||||
return static_cast<int>(a.GetNozzleFlowType()) < static_cast<int>(b.GetNozzleFlowType());
|
||||
};
|
||||
|
||||
std::sort(sorted_nozzles1.begin(), sorted_nozzles1.end(), compare_nozzle);
|
||||
std::sort(sorted_nozzles2.begin(), sorted_nozzles2.end(), compare_nozzle);
|
||||
|
||||
for (size_t i = 0; i < sorted_nozzles1.size(); ++i) {
|
||||
if (std::abs(sorted_nozzles1[i].GetNozzleDiameter() - sorted_nozzles2[i].GetNozzleDiameter()) > EPSILON ||
|
||||
sorted_nozzles1[i].GetNozzleFlowType() != sorted_nozzles2[i].GetNozzleFlowType()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string serialize_nozzle_option(const NozzleOption& option) {
|
||||
std::ostringstream oss;
|
||||
oss << option.diameter << "|";
|
||||
|
||||
bool first = true;
|
||||
for (const auto& pair : option.extruder_nozzle_stats) {
|
||||
if (!first) oss << ";";
|
||||
first = false;
|
||||
oss << pair.first << ":";
|
||||
|
||||
bool first_stat = true;
|
||||
for (const auto& stat_pair : pair.second) {
|
||||
if (!first_stat) oss << ",";
|
||||
first_stat = false;
|
||||
oss << static_cast<int>(stat_pair.first) << "#" << stat_pair.second;
|
||||
}
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static std::optional<NozzleOption> deserialize_nozzle_option(const std::string& option_str) {
|
||||
if (option_str.empty()) return std::nullopt;
|
||||
|
||||
std::vector<std::string> parts;
|
||||
boost::split(parts, option_str, boost::is_any_of("|"));
|
||||
if (parts.size() != 2) return std::nullopt;
|
||||
|
||||
NozzleOption option;
|
||||
option.diameter = parts[0];
|
||||
|
||||
std::vector<std::string> extruder_parts;
|
||||
boost::split(extruder_parts, parts[1], boost::is_any_of(";"));
|
||||
|
||||
for (const auto& extruder_part : extruder_parts) {
|
||||
if (extruder_part.empty()) continue;
|
||||
|
||||
std::vector<std::string> extruder_data;
|
||||
boost::split(extruder_data, extruder_part, boost::is_any_of(":"));
|
||||
if (extruder_data.size() != 2) continue;
|
||||
|
||||
int extruder_id = std::stoi(extruder_data[0]);
|
||||
std::unordered_map<NozzleVolumeType, int> stats;
|
||||
|
||||
std::vector<std::string> stat_parts;
|
||||
boost::split(stat_parts, extruder_data[1], boost::is_any_of(","));
|
||||
|
||||
for (const auto& stat_part : stat_parts) {
|
||||
std::vector<std::string> kv;
|
||||
boost::split(kv, stat_part, boost::is_any_of("#"));
|
||||
if (kv.size() == 2) {
|
||||
NozzleVolumeType type = static_cast<NozzleVolumeType>(std::stoi(kv[0]));
|
||||
int count = std::stoi(kv[1]);
|
||||
stats[type] = count;
|
||||
}
|
||||
}
|
||||
|
||||
option.extruder_nozzle_stats[extruder_id] = stats;
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
std::optional<NozzleOption> Sidebar::priv::get_nozzle_options(MachineObject* obj, int extruder_count, bool support_multi_nozzle, bool is_manual)
|
||||
{
|
||||
if (extruder_count < 2 || !support_multi_nozzle) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!obj || !obj->GetNozzleSystem()) return std::nullopt;
|
||||
auto nozzle_system = obj->GetNozzleSystem();
|
||||
|
||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
||||
if (!preset_bundle) return std::nullopt;
|
||||
|
||||
std::string curr_dev_id = obj->get_dev_id();
|
||||
std::map<int, std::vector<DevNozzle>> curr_nozzle_cfg;
|
||||
|
||||
for (const auto& ext_nozzle : nozzle_system->GetExtNozzles()) {
|
||||
int extruder_id = ext_nozzle.first;
|
||||
curr_nozzle_cfg[extruder_id].emplace_back(ext_nozzle.second);
|
||||
}
|
||||
|
||||
for (const auto& rack_nozzle : nozzle_system->GetRackNozzles()) {
|
||||
curr_nozzle_cfg[MAIN_EXTRUDER_ID].emplace_back(rack_nozzle.second);
|
||||
}
|
||||
|
||||
AppConfig *app_config = wxGetApp().app_config;
|
||||
std::string saved_dev_id = app_config->get("sync_extruder", "dev_id");
|
||||
std::string saved_nozzle_config_str = app_config->get("sync_extruder", "nozzle_config");
|
||||
std::string saved_nozzle_option_str = app_config->get("sync_extruder", "nozzle_option");
|
||||
std::optional<NozzleOption> nozzle_option;
|
||||
|
||||
if (is_manual) {
|
||||
nozzle_option = tryPopUpMultiNozzleDialog(obj);
|
||||
} else {
|
||||
bool can_reuse_saved_option = false;
|
||||
if (!saved_dev_id.empty() && !saved_nozzle_config_str.empty() && !saved_nozzle_option_str.empty() && saved_dev_id == curr_dev_id) {
|
||||
auto saved_nozzle_config = deserialize_nozzle_config(saved_nozzle_config_str);
|
||||
if (is_same_nozzle_config(saved_nozzle_config, curr_nozzle_cfg)) {
|
||||
nozzle_option = deserialize_nozzle_option(saved_nozzle_option_str);
|
||||
can_reuse_saved_option = nozzle_option.has_value();
|
||||
}
|
||||
if (can_reuse_saved_option) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Reusing saved nozzle option for dev_id: " << curr_dev_id;
|
||||
|
||||
auto &project_config = preset_bundle->project_config;
|
||||
ConfigOptionEnumsGeneric *nozzle_volume_type_opt = project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type");
|
||||
|
||||
// Write to preset bundle config
|
||||
for (int extruder_id = 0; extruder_id < extruder_count; ++extruder_id) {
|
||||
NozzleVolumeType volume_type;
|
||||
int nozzle_count;
|
||||
bool clear_all = true;
|
||||
|
||||
if (!nozzle_option->extruder_nozzle_stats.count(extruder_id)) {
|
||||
nozzle_count = 0;
|
||||
// Reset the concrete volume types (Standard/High Flow); Hybrid stays a hidden match-any
|
||||
// sentinel and is left alone. TPU High Flow is a concrete variant shipped on 0.4/0.6
|
||||
// nozzles, so reset it too, but only there (mirrors the High-Flow-skip-for-0.2 guard).
|
||||
std::vector<NozzleVolumeType> reset_types{nvtStandard, nvtHighFlow};
|
||||
if (extruder_supports_tpu_high_flow(preset_bundle, extruder_id))
|
||||
reset_types.push_back(nvtTPUHighFlow);
|
||||
for (NozzleVolumeType vt : reset_types) {
|
||||
volume_type = vt;
|
||||
setExtruderNozzleCount(preset_bundle, extruder_id, volume_type, nozzle_count, clear_all);
|
||||
clear_all = false;
|
||||
}
|
||||
} else {
|
||||
for (auto &stat : nozzle_option->extruder_nozzle_stats[extruder_id]) {
|
||||
volume_type = stat.first;
|
||||
nozzle_count = stat.second;
|
||||
setExtruderNozzleCount(preset_bundle, extruder_id, volume_type, nozzle_count, clear_all);
|
||||
clear_all = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Orca: nozzle stats are persisted directly into the preset's `extruder_nozzle_stats` key;
|
||||
// there is no machine/user data-source flag on the stats.
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_reuse_saved_option) {
|
||||
nozzle_option = tryPopUpMultiNozzleDialog(obj);
|
||||
}
|
||||
}
|
||||
if (nozzle_option) {
|
||||
std::string current_nozzle_config = serialize_nozzle_config(curr_nozzle_cfg);
|
||||
std::string current_nozzle_option = serialize_nozzle_option(*nozzle_option);
|
||||
if (app_config->has_section("sync_extruder")) {
|
||||
app_config->set("sync_extruder", "dev_id", curr_dev_id);
|
||||
app_config->set("sync_extruder", "nozzle_config", current_nozzle_config);
|
||||
app_config->set("sync_extruder", "nozzle_option", current_nozzle_option);
|
||||
} else {
|
||||
std::map<std::string, std::string> data;
|
||||
data["dev_id"] = curr_dev_id;
|
||||
data["nozzle_config"] = current_nozzle_config;
|
||||
data["nozzle_option"] = current_nozzle_option;
|
||||
app_config->set_section("sync_extruder", data);
|
||||
}
|
||||
}
|
||||
|
||||
return nozzle_option;
|
||||
}
|
||||
|
||||
bool Sidebar::priv::sync_extruder_list(bool &only_external_material, bool is_manual)
|
||||
{
|
||||
MachineObject *obj = wxGetApp().getDeviceManager()->get_selected_machine();
|
||||
auto printer_name = plater->get_selected_printer_name_in_combox();
|
||||
@@ -1377,13 +1664,51 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material)
|
||||
}
|
||||
assert(obj->GetExtderSystem()->GetTotalExtderCount() == extruder_nums);
|
||||
|
||||
// Multi-nozzle: resolve the nozzle option (pops MultiNozzleSyncDialog when is_manual). No-op for every
|
||||
// existing printer — support_multi_nozzle is false unless some extruder_max_nozzle_count entry is a real
|
||||
// value > 1 (nil-guarded, matching the manual/ToolOrdering gates), which no shipping single-nozzle or
|
||||
// dual-extruder profile sets. When the machine is multi-nozzle but no option resolves (e.g. user cancelled),
|
||||
// abort the sync.
|
||||
const ConfigOptionIntsNullable *extruder_max_nozzle_count = cur_preset.config.option<ConfigOptionIntsNullable>("extruder_max_nozzle_count");
|
||||
bool support_multi_nozzle = extruder_max_nozzle_count != nullptr &&
|
||||
std::any_of(extruder_max_nozzle_count->values.begin(), extruder_max_nozzle_count->values.end(),
|
||||
[](int val) { return val > 1 && val != ConfigOptionIntsNullable::nil_value(); });
|
||||
auto nozzle_option = get_nozzle_options(obj, extruder_nums, support_multi_nozzle, is_manual);
|
||||
if (!nozzle_option && support_multi_nozzle)
|
||||
return false;
|
||||
|
||||
std::vector<float> nozzle_diameters;
|
||||
nozzle_diameters.resize(extruder_nums);
|
||||
std::vector<NozzleVolumeType> target_types(extruder_nums, NozzleVolumeType::nvtStandard);
|
||||
for (size_t index = 0; index < extruder_nums; ++index) {
|
||||
int extruder_id = extruder_map[index];
|
||||
nozzle_diameters[extruder_id] = obj->GetExtderSystem()->GetNozzleDiameter(index);
|
||||
nozzle_diameters[extruder_id] = nozzle_option ? atof(nozzle_option->diameter.c_str()) : obj->GetExtderSystem()->GetNozzleDiameter(index);
|
||||
NozzleVolumeType target_type = NozzleVolumeType::nvtStandard;
|
||||
auto printer_tab = dynamic_cast<TabPrinter *>(wxGetApp().get_tab(Preset::TYPE_PRINTER));
|
||||
std::optional<NozzleVolumeType> select_type;
|
||||
if (nozzle_option && nozzle_option->extruder_nozzle_stats.count(index)) {
|
||||
const auto &stats = nozzle_option->extruder_nozzle_stats[index];
|
||||
if (stats.size() > 1) {
|
||||
// Orca: for a mixed extruder, keep Hybrid out of the (still-deferred) nozzle-centric slicing
|
||||
// pipeline, so collapse to the dominant concrete volume type (tie -> Standard).
|
||||
// TPU High Flow is shipped on 0.4/0.6 nozzles, so only fold it in there (mirrors the
|
||||
// High-Flow-skip-for-0.2 guard). Uses the device-reported diameter, same predicate as the preset path.
|
||||
int std_cnt = 0, hf_cnt = 0, tpu_hf_cnt = 0;
|
||||
for (const auto &kv : stats) {
|
||||
if (kv.first == nvtStandard) std_cnt = kv.second;
|
||||
else if (kv.first == nvtHighFlow) hf_cnt = kv.second;
|
||||
else if (kv.first == nvtTPUHighFlow) tpu_hf_cnt = kv.second;
|
||||
}
|
||||
if (!nozzle_diameter_supports_tpu_high_flow(nozzle_diameters[extruder_id]))
|
||||
tpu_hf_cnt = 0;
|
||||
select_type = nvtStandard;
|
||||
if (hf_cnt > std_cnt && hf_cnt >= tpu_hf_cnt)
|
||||
select_type = nvtHighFlow;
|
||||
else if (tpu_hf_cnt > std_cnt && tpu_hf_cnt > hf_cnt)
|
||||
select_type = nvtTPUHighFlow;
|
||||
} else {
|
||||
select_type = stats.begin()->first;
|
||||
}
|
||||
}
|
||||
if (obj->is_nozzle_flow_type_supported()) {
|
||||
if (obj->GetExtderSystem()->GetNozzleFlowType(index) == NozzleFlowType::NONE_FLOWTYPE) {
|
||||
MessageDialog dlg(this->plater, _L("There are unset nozzle types. Please set the nozzle types of all extruders before synchronizing."),
|
||||
@@ -1393,9 +1718,13 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material)
|
||||
}
|
||||
// hack code, only use standard flow for 0.2
|
||||
if (std::fabs(nozzle_diameters[extruder_id] - 0.2) > EPSILON)
|
||||
target_type = NozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(extruder_id) - 1);
|
||||
// Map device flow->volume via the table, not `flowtype - 1`.
|
||||
// The arithmetic only aligns for S_FLOW/H_FLOW; U_FLOW(3)-1 would yield nvtHybrid(2), not nvtTPUHighFlow(3).
|
||||
target_type = DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(extruder_id));
|
||||
}
|
||||
printer_tab->set_extruder_volume_type(index, target_type);
|
||||
if (select_type)
|
||||
target_type = *select_type;
|
||||
target_types[index] = target_type;
|
||||
}
|
||||
|
||||
int deputy_4 = 0, main_4 = 0, deputy_1 = 0, main_1 = 0;
|
||||
@@ -1439,6 +1768,12 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material)
|
||||
is_switching_diameter = false;
|
||||
}
|
||||
|
||||
// set nozzle volume type after switching prset, so this value can override the old value stored in conf
|
||||
auto printer_tab = dynamic_cast<TabPrinter *>(wxGetApp().get_tab(Preset::TYPE_PRINTER));
|
||||
for (size_t idx = 0; idx < target_types.size(); ++idx) {
|
||||
printer_tab->set_extruder_volume_type(idx, target_types[idx]);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " finish sync_extruder_list";
|
||||
return true;
|
||||
}
|
||||
@@ -3745,15 +4080,13 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn)
|
||||
{ // badge ams filament
|
||||
clear_combos_filament_badge();
|
||||
if (sync_result.direct_sync) {
|
||||
auto& ams_list = wxGetApp().preset_bundle->filament_ams_list;
|
||||
size_t tray_idx = 0;
|
||||
for (auto& entry : ams_list) {
|
||||
if (tray_idx >= p->combos_filament.size()) break;
|
||||
auto filament_id = entry.second.opt_string("filament_id", 0u);
|
||||
if (!filament_id.empty()) {
|
||||
badge_combox_filament(p->combos_filament[tray_idx]);
|
||||
}
|
||||
tray_idx++;
|
||||
// Orca: PresetBundle::sync_ams_list rebuilds combos_filament
|
||||
// 1:1 from the AMS trays that produce a combo (loaded trays + placeholders; non-placeholder
|
||||
// empty trays are skipped), so every resulting combo is AMS-sourced and gets a badge. The
|
||||
// previous per-tray index walked the full filament_ams_list (including the skipped empties),
|
||||
// so an empty slot before a loaded one dropped the badge for the trailing filaments.
|
||||
for (auto &c : p->combos_filament) {
|
||||
badge_combox_filament(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3948,7 +4281,8 @@ bool Sidebar::is_multifilament()
|
||||
void Sidebar::deal_btn_sync() {
|
||||
m_begin_sync_printer_status = true;
|
||||
bool only_external_material;
|
||||
auto ok = p->sync_extruder_list(only_external_material);
|
||||
// Manual "sync machine" button: is_manual=true so an H2C pops the MultiNozzleSyncDialog to pick a nozzle option.
|
||||
auto ok = p->sync_extruder_list(only_external_material, true);
|
||||
if (ok) {
|
||||
pop_sync_nozzle_and_ams_dialog();
|
||||
}
|
||||
@@ -5641,6 +5975,9 @@ std::map<std::string, std::string> Plater::get_bed_texture_maps()
|
||||
if (pm->bottom_texture_rect.size() > 0) {
|
||||
maps["bottom_texture_rect"] = pm->bottom_texture_rect;
|
||||
}
|
||||
if (pm->bottom_texture_rect_longer.size() > 0) {
|
||||
maps["bottom_texture_rect_longer"] = pm->bottom_texture_rect_longer;
|
||||
}
|
||||
if (pm->middle_texture_rect.size() > 0) {
|
||||
maps["middle_texture_rect"] = pm->middle_texture_rect;
|
||||
}
|
||||
@@ -11112,8 +11449,9 @@ bool Plater::priv::check_ams_status_impl(bool is_slice_all)
|
||||
auto nozzle_volumes_values = preset_bundle->project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type")->values;
|
||||
assert(obj->GetExtderSystem()->GetTotalExtderCount() == 2 && nozzle_volumes_values.size() == 2);
|
||||
if (obj->GetExtderSystem()->GetTotalExtderCount() == 2 && nozzle_volumes_values.size() == 2) {
|
||||
NozzleVolumeType right_nozzle_type = NozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(0) - 1);
|
||||
NozzleVolumeType left_nozzle_type = NozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(1) - 1);
|
||||
// Map device flow->volume via the table, not `flowtype - 1` (which mis-maps U_FLOW to nvtHybrid).
|
||||
NozzleVolumeType right_nozzle_type = DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(0));
|
||||
NozzleVolumeType left_nozzle_type = DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(1));
|
||||
NozzleVolumeType preset_left_type = NozzleVolumeType(nozzle_volumes_values[0]);
|
||||
NozzleVolumeType preset_right_type = NozzleVolumeType(nozzle_volumes_values[1]);
|
||||
is_same_as_printer = (left_nozzle_type == preset_left_type && right_nozzle_type == preset_right_type);
|
||||
@@ -17317,6 +17655,9 @@ void Plater::suppress_background_process(const bool stop_background_process)
|
||||
this->p->suppressed_backround_processing_update = true;
|
||||
}
|
||||
|
||||
// Expose the slicing process to the device GUI.
|
||||
BackgroundSlicingProcess& Plater::background_process() { return p->background_process; }
|
||||
|
||||
void Plater::center_selection() { p->center_selection(); }
|
||||
void Plater::drop_selection() { p->drop_selection(); }
|
||||
void Plater::mirror(Axis axis) { p->mirror(axis); }
|
||||
|
||||
Reference in New Issue
Block a user