mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-05 09:07:39 +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:
@@ -3,7 +3,9 @@
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <regex>
|
||||
#include "libslic3r/MultiNozzleUtils.hpp"
|
||||
#include <future>
|
||||
#include <glad/gl.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@@ -66,6 +68,8 @@ static const int PARTPLATE_PLATENAME_OFFSET_Y = 10;
|
||||
const float WIPE_TOWER_DEFAULT_X_POS = 165.;
|
||||
const float WIPE_TOWER_DEFAULT_Y_POS = 250.; // Max y
|
||||
|
||||
const float N9_WIPE_TOWER_DEFAULT_Y_POS = 160.;
|
||||
|
||||
const float I3_WIPE_TOWER_DEFAULT_X_POS = 0.;
|
||||
const float I3_WIPE_TOWER_DEFAULT_Y_POS = 250.; // Max y
|
||||
|
||||
@@ -4221,6 +4225,12 @@ void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx, bool ini
|
||||
y = I3_WIPE_TOWER_DEFAULT_Y_POS;
|
||||
}
|
||||
|
||||
std::string printer_type = wxGetApp().preset_bundle->printers.get_edited_preset().get_printer_type(wxGetApp().preset_bundle);
|
||||
// Note: printer_type == "N9" and printer_structure_opt->value == PrinterStructure::psI3 can both be true
|
||||
if (printer_type == "N9") {
|
||||
y = N9_WIPE_TOWER_DEFAULT_Y_POS;
|
||||
}
|
||||
|
||||
PartPlate *part_plate = get_plate(plate_idx);
|
||||
Vec3d plate_origin = part_plate->get_origin();
|
||||
BoundingBoxf3 plate_bbox = part_plate->get_bounding_box();
|
||||
@@ -6279,6 +6289,58 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list, int f
|
||||
m_plate_list[index]->slice_filaments_info = plate_data_list[i]->slice_filaments_info;
|
||||
gcode_result->warnings = plate_data_list[i]->warnings;
|
||||
gcode_result->filament_maps = plate_data_list[i]->filament_maps;
|
||||
|
||||
// Reconstruct the device-side nozzle grouping from the loaded 3mf so
|
||||
// the monitor/preview can map filaments to physical nozzles.
|
||||
// load_nozzle_infos_with_compatibility handles older single-nozzle 3mf (no <nozzle> tags) by
|
||||
// falling back to the per-filament group_id, then to the extruder volume types / nozzle diameters.
|
||||
{
|
||||
// nozzle_volume_types is space-separated ints; nozzle_diameters is comma/space-separated floats.
|
||||
auto parse_int_tokens = [](const std::string& str) -> std::vector<int> {
|
||||
std::vector<int> out;
|
||||
std::istringstream iss(str);
|
||||
std::string tok;
|
||||
while (iss >> tok) { try { out.push_back(std::stoi(tok)); } catch (...) {} }
|
||||
return out;
|
||||
};
|
||||
auto parse_double_tokens = [](const std::string& str) -> std::vector<double> {
|
||||
std::vector<double> out;
|
||||
std::string s = str;
|
||||
std::replace(s.begin(), s.end(), ',', ' ');
|
||||
std::istringstream iss(s);
|
||||
std::string tok;
|
||||
while (iss >> tok) { try { out.push_back(std::stod(tok)); } catch (...) {} }
|
||||
return out;
|
||||
};
|
||||
|
||||
std::vector<int> nozzle_volume_type_values = parse_int_tokens(plate_data_list[i]->nozzle_volume_types);
|
||||
std::vector<double> nozzle_diameter_values = parse_double_tokens(plate_data_list[i]->nozzle_diameters);
|
||||
|
||||
std::vector<NozzleVolumeType> extruder_volume_types(nozzle_volume_type_values.size(), NozzleVolumeType::nvtStandard);
|
||||
for (size_t idx = 0; idx < nozzle_volume_type_values.size(); ++idx)
|
||||
if (nozzle_volume_type_values[idx] >= 0 && nozzle_volume_type_values[idx] <= nvtMaxNozzleVolumeType)
|
||||
extruder_volume_types[idx] = static_cast<NozzleVolumeType>(nozzle_volume_type_values[idx]);
|
||||
|
||||
auto nozzle_infos = MultiNozzleUtils::load_nozzle_infos_with_compatibility(
|
||||
plate_data_list[i]->nozzles_info,
|
||||
plate_data_list[i]->slice_filaments_info,
|
||||
plate_data_list[i]->filament_maps,
|
||||
extruder_volume_types,
|
||||
nozzle_diameter_values);
|
||||
|
||||
std::vector<int> fil_seq(plate_data_list[i]->filament_change_sequence.begin(), plate_data_list[i]->filament_change_sequence.end());
|
||||
std::vector<int> noz_seq(plate_data_list[i]->nozzle_change_sequence.begin(), plate_data_list[i]->nozzle_change_sequence.end());
|
||||
bool enable_filament_dynamic_map = false;
|
||||
if (plate_data_list[i]->config.has("enable_filament_dynamic_map"))
|
||||
enable_filament_dynamic_map = plate_data_list[i]->config.option<ConfigOptionBool>("enable_filament_dynamic_map")->value;
|
||||
|
||||
auto group_result = MultiNozzleUtils::StaticNozzleGroupResult::create(
|
||||
plate_data_list[i]->slice_filaments_info, nozzle_infos, fil_seq, noz_seq, enable_filament_dynamic_map);
|
||||
if (group_result)
|
||||
gcode_result->nozzle_group_result = std::make_shared<MultiNozzleUtils::StaticNozzleGroupResult>(group_result.value());
|
||||
else
|
||||
gcode_result->nozzle_group_result = nullptr;
|
||||
}
|
||||
if (m_plater && !plate_data_list[i]->thumbnail_file.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load thumbnail from %2%.")%(i+1) %plate_data_list[i]->thumbnail_file;
|
||||
if (boost::filesystem::exists(plate_data_list[i]->thumbnail_file)) {
|
||||
@@ -6438,9 +6500,10 @@ void PartPlateList::init_bed_type_info()
|
||||
auto bed_texture_maps = wxGetApp().plater()->get_bed_texture_maps();
|
||||
std::string bottom_texture_end_name = bed_texture_maps.find("bottom_texture_end_name") != bed_texture_maps.end() ? bed_texture_maps["bottom_texture_end_name"] : "";
|
||||
std::string bottom_texture_rect_str = bed_texture_maps.find("bottom_texture_rect") != bed_texture_maps.end() ? bed_texture_maps["bottom_texture_rect"] : "";
|
||||
std::string bottom_texture_rect_longer_str = bed_texture_maps.find("bottom_texture_rect_longer") != bed_texture_maps.end() ? bed_texture_maps["bottom_texture_rect_longer"] : "";
|
||||
std::string middle_texture_rect_str = bed_texture_maps.find("middle_texture_rect") != bed_texture_maps.end() ? bed_texture_maps["middle_texture_rect"] : "";
|
||||
std::string use_double_extruder_default_texture = bed_texture_maps.find("use_double_extruder_default_texture") != bed_texture_maps.end() ? bed_texture_maps["use_double_extruder_default_texture"] : "";
|
||||
std::array<float, 4> bottom_texture_rect = {0, 0, 0, 0}, middle_texture_rect = {0, 0, 0, 0};
|
||||
std::array<float, 4> bottom_texture_rect = {0, 0, 0, 0}, bottom_texture_rect_longer = {0, 0, 0, 0}, middle_texture_rect = {0, 0, 0, 0};
|
||||
if (bottom_texture_rect_str.size() > 0) {
|
||||
std::vector<std::string> items;
|
||||
boost::algorithm::erase_all(bottom_texture_rect_str, " ");
|
||||
@@ -6451,6 +6514,16 @@ void PartPlateList::init_bed_type_info()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bottom_texture_rect_longer_str.size() > 0) {
|
||||
std::vector<std::string> items;
|
||||
boost::algorithm::erase_all(bottom_texture_rect_longer_str, " ");
|
||||
boost::split(items, bottom_texture_rect_longer_str, boost::is_any_of(","));
|
||||
if (items.size() == 4) {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
bottom_texture_rect_longer[i] = std::atof(items[i].c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (middle_texture_rect_str.size() > 0) {
|
||||
std::vector<std::string> items;
|
||||
boost::algorithm::erase_all(middle_texture_rect_str, " ");
|
||||
@@ -6471,6 +6544,7 @@ void PartPlateList::init_bed_type_info()
|
||||
}
|
||||
pte_part2 = BedTextureInfo::TexturePart(45, -14.5, 70, 8, "bbl_bed_pte_left_bottom.svg");
|
||||
auto &bottom_rect = bottom_texture_rect;
|
||||
auto &bottom_rect_longer = bottom_texture_rect_longer;
|
||||
if (bottom_texture_end_name.size() > 0 && bottom_rect[2] > 0.f) {
|
||||
std::string pte_part2_name = "bbl_bed_pte_bottom_" + bottom_texture_end_name + ".svg";
|
||||
pte_part2 = BedTextureInfo::TexturePart(bottom_rect[0], bottom_rect[1], bottom_rect[2], bottom_rect[3], pte_part2_name);
|
||||
@@ -6494,6 +6568,9 @@ void PartPlateList::init_bed_type_info()
|
||||
if (bottom_texture_end_name.size() > 0 && bottom_rect[2] > 0.f) {
|
||||
std::string st_part2_name = "bbl_bed_st_bottom_" + bottom_texture_end_name + ".svg";
|
||||
st_part2 = BedTextureInfo::TexturePart(bottom_rect[0], bottom_rect[1], bottom_rect[2], bottom_rect[3], st_part2_name);
|
||||
} else if (bottom_rect_longer[2] > 0.f) {
|
||||
// SuperTack bottom strip uses the wider "longer" rect.
|
||||
st_part2.update_pos(bottom_rect_longer[0], bottom_rect_longer[1], bottom_rect_longer[2], bottom_rect_longer[3]);
|
||||
}
|
||||
|
||||
ep_part1 = BedTextureInfo::TexturePart(57, 300, 236.12f, 10.f, "bbl_bed_ep_middle.svg");
|
||||
@@ -6504,6 +6581,9 @@ void PartPlateList::init_bed_type_info()
|
||||
if (bottom_texture_end_name.size() > 0 && bottom_rect[2] > 0.f) {
|
||||
std::string ep_part2_name = "bbl_bed_ep_bottom_" + bottom_texture_end_name + ".svg";
|
||||
ep_part2 = BedTextureInfo::TexturePart(bottom_rect[0], bottom_rect[1], bottom_rect[2], bottom_rect[3], ep_part2_name);
|
||||
} else if (bottom_rect_longer[2] > 0.f) {
|
||||
// Engineering-plate bottom strip uses the wider "longer" rect.
|
||||
ep_part2.update_pos(bottom_rect_longer[0], bottom_rect_longer[1], bottom_rect_longer[2], bottom_rect_longer[3]);
|
||||
}
|
||||
|
||||
pc_part1 = BedTextureInfo::TexturePart(57, 300, 236.12f, 10.f, "bbl_bed_pc_middle.svg");
|
||||
|
||||
Reference in New Issue
Block a user