Merge upstream/main into belt/rebase/may-18

Reconciles the belt-printer branch with upstream PRs through #13723. Six
files had conflicts; three additional files needed manual follow-up fixes
where the auto-merge produced code that referenced upstream-renamed fields
or changed function signatures.

Notable reconciliations:
- TreeSupport.cpp: kept belt-floor early-exit branches around HEAD's
  drop-down logic, folded upstream's `(distance_to_top > 0 ? 1 : 0)`
  formula into the non-belt-floor path (upstream PR #11812). Dropped dead
  `roof_enabled`/`force_tip_to_roof` locals.
- TreeSupport3D.cpp: combined upstream's safety-offset + remove_small
  changes with HEAD's belt-floor clip in the per-slice trim loop. Dropped
  HEAD's `else` block (superseded by upstream's rewritten bottom-contact
  propagation) and re-added the belt-floor clip into the new propagation
  loop. Gated the propagation on belt printers to prevent OOM when
  belt-floor clipping produces empty initial slices.
- TriangleSelector.{cpp,hpp}: merged both new `select_patch` parameters
  (HEAD's `up_direction` and upstream's `select_partially`); body uses
  `dot(up_direction)` for the overhang angle check and forwards
  `select_partially` to `select_triangle`.
- SupportMaterial.cpp: `slicing_params.soluble_interface` →
  `zero_gap_interface_bottom` in HEAD's `detect_belt_floor_bottom_contacts`,
  matching upstream's same-purpose rename at line 2495.
- Custom.json, GCodeWriter.cpp: simple additive merges (kept entries /
  includes from both sides).

Verified by building OrcaSlicer (RelWithDebInfo) after a full deps
rebuild (Eigen v5.0.1, libigl v2.6.0 are now managed deps) and slicing
a scaled Benchy on the NORMALIZER belt-printer profile without OOM.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
harrierpigeon
2026-05-18 21:53:24 -05:00
2143 changed files with 130163 additions and 174147 deletions

View File

@@ -81,6 +81,7 @@
#include "GUI_Utils.hpp"
#include "GUI_Factories.hpp"
#include "wxExtensions.hpp"
#include "../Utils/PrintHost.hpp"
#include "MainFrame.hpp"
#include "format.hpp"
#include "3DScene.hpp"
@@ -2382,7 +2383,7 @@ void Sidebar::init_filament_combo(PlaterPresetComboBox **combo, const int filame
(*combo)->clr_picker->SetLabel(wxString::Format("%d", filament_idx + 1));
combo_and_btn_sizer->Add((*combo)->clr_picker, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(SidebarProps::ElementSpacing()) - FromDIP(2)); // ElementSpacing - 2 (from combo box))
combo_and_btn_sizer->Add(*combo, 1, wxALL | wxEXPAND, FromDIP(2))->SetMinSize({-1, FromDIP(30)});
combo_and_btn_sizer->Add(*combo, 1, wxALL | wxEXPAND, FromDIP(2))->SetMinSize({-1, 30 * wxGetApp().em_unit() / 10}); // ORCA ensure height matches with PlaterPresetComboBox
/* BBS hide del_btn
ScalableButton* del_btn = new ScalableButton(p->m_panel_filament_content, wxID_ANY, "delete_filament");
@@ -2402,13 +2403,23 @@ void Sidebar::init_filament_combo(PlaterPresetComboBox **combo, const int filame
edit_btn->SetToolTip(_L("Click to edit preset"));
PlaterPresetComboBox* combobox = (*combo);
edit_btn->Bind(wxEVT_BUTTON, [this, edit_btn, filament_idx](wxCommandEvent) {
auto menu = p->plater->filament_action_menu(filament_idx);
wxPoint pt { 0, edit_btn->GetSize().GetHeight() + 10 };
pt = edit_btn->ClientToScreen(pt);
pt = wxGetApp().mainframe->ScreenToClient(pt);
p->m_menu_filament_id = filament_idx;
p->plater->PopupMenu(menu, (int) pt.x, pt.y);
edit_btn->Bind(wxEVT_BUTTON, [this, edit_btn, combobox, filament_idx](wxCommandEvent) {
bool single_or_bbl = should_show_SEMM_buttons();
bool is_multi_material = p->combos_filament.size() > 1;
if(single_or_bbl && is_multi_material) {
// MULTI MATERIAL Show menu
auto menu = p->plater->filament_action_menu(filament_idx);
wxPoint pt { 0, edit_btn->GetSize().GetHeight() + FromDIP(2) };
pt = edit_btn->ClientToScreen(pt);
pt = wxGetApp().mainframe->ScreenToClient(pt);
p->m_menu_filament_id = filament_idx;
p->plater->PopupMenu(menu, (int) pt.x, pt.y);
}
else {
// SINGLE MATERIAL / MULTI EXTRUDER / TOOLCHANGER / IDEX Opens Dialog directly
p->editing_filament = filament_idx;
combobox->switch_to_tab();
}
});
combobox->edit_btn = edit_btn;
@@ -2483,13 +2494,11 @@ void Sidebar::update_all_preset_comboboxes()
p->m_bpButton_ams_filament->Hide();
auto print_btn_type = MainFrame::PrintSelectType::eExportGcode;
wxString url = cfg.opt_string("print_host_webui").empty() ? cfg.opt_string("print_host") : cfg.opt_string("print_host_webui");
wxString url = from_u8(PrintHost::get_print_host_webui(&cfg));
wxString apikey;
if(url.empty())
url = wxString::Format("file://%s/web/orca/missing_connection.html", from_u8(resources_dir()));
else {
if (!url.Lower().starts_with("http"))
url = wxString::Format("http://%s", url);
const auto host_type = cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
if (cfg.has("printhost_apikey") && (host_type != htSimplyPrint))
apikey = cfg.opt_string("printhost_apikey");
@@ -2511,7 +2520,7 @@ void Sidebar::update_all_preset_comboboxes()
p->m_filament_icon->SetBitmap_("filament");
}
show_SEMM_buttons(should_show_SEMM_buttons());
show_SEMM_buttons();
//p->m_staticText_filament_settings->Update();
@@ -3113,16 +3122,7 @@ void Sidebar::on_filament_count_change(size_t num_filaments)
// remove unused choices if any
remove_unused_filament_combos(num_filaments);
auto sizer = p->m_panel_filament_title->GetSizer();
if (p->m_flushing_volume_btn != nullptr && sizer != nullptr) {
if (num_filaments > 1) {
sizer->Show(p->m_flushing_volume_btn);
sizer->Show(p->m_bpButton_del_filament); // ORCA: Show delete filament button if multiple filaments
} else {
sizer->Hide(p->m_flushing_volume_btn);
sizer->Hide(p->m_bpButton_del_filament); // ORCA: Hide delete filament button if there is only one filament
}
}
show_SEMM_buttons(); // ORCA
update_filaments_area_height(); // ORCA
@@ -3169,16 +3169,7 @@ void Sidebar::on_filaments_delete(size_t filament_id)
}
}
auto sizer = p->m_panel_filament_title->GetSizer();
if (p->m_flushing_volume_btn != nullptr && sizer != nullptr) {
if (p->combos_filament.size() > 1) {
sizer->Show(p->m_flushing_volume_btn);
sizer->Show(p->m_bpButton_del_filament); // ORCA: Show delete filament button if multiple filaments
} else {
sizer->Hide(p->m_flushing_volume_btn);
sizer->Hide(p->m_bpButton_del_filament); // ORCA: Hide delete filament button if there is only one filament
}
}
show_SEMM_buttons(); // ORCA
for (size_t idx = filament_id ; idx < p->combos_filament.size(); ++idx) {
p->combos_filament[idx]->update();
@@ -3737,14 +3728,31 @@ bool Sidebar::should_show_SEMM_buttons()
return cfg.opt_bool("single_extruder_multi_material") || is_bbl_vendor;
}
void Sidebar::show_SEMM_buttons(bool bshow)
void Sidebar::show_SEMM_buttons()
{
if(p->m_bpButton_add_filament)
p->m_bpButton_add_filament->Show(bshow);
if (p->m_bpButton_del_filament && p->combos_filament.size() > 1) // ORCA add filament count as condition to prevent showing Flushing volumes and Del Filament icon visible while only 1 filament exist
p->m_bpButton_del_filament->Show(bshow);
if (p->m_flushing_volume_btn && p->combos_filament.size() > 1) // ORCA add filament count as condition to prevent showing Flushing volumes and Del Filament icon visible while only 1 filament exist
p->m_flushing_volume_btn->Show(bshow);
// ORCA
if (!p || p->combos_filament.empty() || !p->m_bpButton_add_filament || !p->m_bpButton_del_filament || !p->m_flushing_volume_btn)
return;
bool is_multi_material = p->combos_filament.size() > 1;
bool single_or_bbl = should_show_SEMM_buttons();
bool is_single = single_or_bbl && !is_multi_material; // SINGLE EXTRUDER / BBL WITH 1 MATERIAL
bool is_multi = single_or_bbl && is_multi_material; // MULTI MATERIAL WITH SINGLE EXTRUDER
bool is_fixed = !is_single && !is_multi; // MULTI EXTRUDER / TOOLCHANGER / IDEX WITH FIXED MATERIAL
p->m_bpButton_add_filament->Show(single_or_bbl);
p->m_bpButton_del_filament->Show(is_multi);
p->m_flushing_volume_btn->Show( is_multi);
if (is_multi) {
for (auto &c : p->combos_filament)
c->edit_btn->SetBitmap_("menu_filament");
}
else if (is_single || is_fixed) {
for (auto &c : p->combos_filament)
c->edit_btn->SetBitmap_("edit");
}
Layout();
}
@@ -7628,7 +7636,7 @@ void Plater::priv::split_object(int obj_idx, bool auto_drop /* = true */)
wxBusyCursor wait;
ModelObjectPtrs new_objects;
current_model_object->split(&new_objects);
current_model_object->split(&new_objects, wxGetApp().app_config->get_bool("keep_painting"));
if (new_objects.size() == 1)
// #ysFIXME use notification
Slic3r::GUI::warning_catcher(q, _L("The selected object couldn't be split."));
@@ -8376,10 +8384,20 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const
new_volume->convert_from_imperial_units();
else if (old_volume->source.is_converted_from_meters)
new_volume->convert_from_meters();
new_volume->supported_facets.assign(old_volume->supported_facets);
new_volume->seam_facets.assign(old_volume->seam_facets);
new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets);
new_volume->fuzzy_skin_facets.assign(old_volume->fuzzy_skin_facets);
if (wxGetApp().app_config->get_bool("keep_painting")) {
// Proper paint remapping
auto saved_painting = old_volume->save_painting();
if (saved_painting) {
saved_painting->mesh.transform(Geometry::translation_transform(new_volume->mesh().get_init_shift()));
new_volume->restore_painting(saved_painting);
}
} else {
// Won't work well if mesh changed, but kept for old behavior
new_volume->supported_facets.assign(old_volume->supported_facets);
new_volume->seam_facets.assign(old_volume->seam_facets);
new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets);
new_volume->fuzzy_skin_facets.assign(old_volume->fuzzy_skin_facets);
}
std::swap(old_model_object->volumes[volume_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
if (!sinking)
@@ -8905,6 +8923,16 @@ void Plater::priv::reload_from_disk()
new_volume->convert_from_imperial_units();
else if (old_volume->source.is_converted_from_meters)
new_volume->convert_from_meters();
// Remap paint
if (wxGetApp().app_config->get_bool("keep_painting")) {
auto saved_painting = old_volume->save_painting();
if (saved_painting) {
saved_painting->mesh.transform(Geometry::translation_transform(new_volume->mesh().get_init_shift()));
new_volume->restore_painting(saved_painting);
}
}
std::swap(old_model_object->volumes[vol_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
if (!sinking) old_model_object->ensure_on_bed();
@@ -13387,6 +13415,8 @@ void Plater::calib_VFA(const Calib_Params& params)
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{true});
printer_config->set_key_value("input_shaping_type", new ConfigOptionEnum<InputShaperType>(InputShaperType::Disable));
filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 0.0 });
print_config->set_key_value("enable_overhang_speed", new ConfigOptionBool { false });
print_config->set_key_value("timelapse_type", new ConfigOptionEnum<TimelapseType>(tlTraditional));
@@ -13450,10 +13480,12 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params)
}
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{false});
filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 0.0 });
filament_config->set_key_value("slow_down_min_speed", new ConfigOptionFloats { 0.0 });
filament_config->set_key_value("slow_down_for_layer_cooling", new ConfigOptionBools{false});
print_config->set_key_value("enable_overhang_speed", new ConfigOptionBool { false });
print_config->set_key_value("layer_height", new ConfigOptionFloat(0.2));
print_config->set_key_value("enable_overhang_speed", new ConfigOptionBool{false});
print_config->set_key_value("timelapse_type", new ConfigOptionEnum<TimelapseType>(tlTraditional));
print_config->set_key_value("wall_loops", new ConfigOptionInt(1));
print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0));
@@ -13511,6 +13543,7 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params)
}
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{false});
filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 0.0 });
filament_config->set_key_value("slow_down_min_speed", new ConfigOptionFloats { 0.0 });
filament_config->set_key_value("slow_down_for_layer_cooling", new ConfigOptionBools{false});
@@ -13573,6 +13606,8 @@ void Plater::Calib_Cornering(const Calib_Params& params)
}
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{true});
printer_config->set_key_value("input_shaping_type", new ConfigOptionEnum<InputShaperType>(InputShaperType::Disable));
filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 0.0 });
filament_config->set_key_value("slow_down_min_speed", new ConfigOptionFloats { 0.0 });
filament_config->set_key_value("slow_down_for_layer_cooling", new ConfigOptionBools{false});
@@ -14034,7 +14069,6 @@ public:
wxPanel * m_top_line;
wxStaticText *m_fname_title;
wxStaticText *m_fname_f;
wxStaticText *m_fname_s;
StaticBox * m_panel_select;
void on_select_ok(wxCommandEvent &event);
@@ -14044,7 +14078,6 @@ public:
void set_action(int index) { m_action = index; }
wxBoxSizer *create_remember_checkbox(wxString title, wxWindow* parent, wxString tooltip);
wxBoxSizer *create_item_radiobox(wxString title, wxWindow *parent, int select_id, int groupid);
protected:
void on_dpi_changed(const wxRect &suggested_rect) override;
@@ -14062,10 +14095,6 @@ ProjectDropDialog::ProjectDropDialog(const std::string &filename)
// def setting
SetBackgroundColour(m_def_color);
// icon
std::string icon_path = (boost::format("%1%/images/OrcaSlicerTitle.ico") % resources_dir()).str();
SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL);
m_top_line = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
@@ -14073,37 +14102,23 @@ ProjectDropDialog::ProjectDropDialog(const std::string &filename)
m_sizer_main->Add(m_top_line, 0, wxEXPAND, 0);
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, 20);
wxBoxSizer *m_sizer_name = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *m_sizer_fline = new wxBoxSizer(wxHORIZONTAL);
m_sizer_main->AddSpacer(FromDIP(15));
// ORCA use file name on new line to create room for longer names
m_fname_title = new wxStaticText(this, wxID_ANY, _L("Please select an action"), wxDefaultPosition, wxDefaultSize, 0);
m_fname_title->Wrap(-1);
m_fname_title->SetFont(::Label::Body_14);
m_fname_title->SetForegroundColour(wxColour(107, 107, 107));
m_fname_title->SetBackgroundColour(wxColour(255, 255, 255));
m_fname_title->SetForegroundColour(wxColour("#363636"));
m_sizer_fline->Add(m_fname_title, 0, wxALL, 0);
m_sizer_fline->Add(0, 0, 0, wxEXPAND | wxLEFT, 5);
m_fname_f = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
m_fname_f = new wxStaticText(this, wxID_ANY, filename);
m_fname_f->SetFont(::Label::Head_14);
m_fname_f->Wrap(-1);
m_fname_f->SetForegroundColour(wxColour(38, 46, 48));
m_fname_f->SetMaxSize(wxSize(FromDIP(300),-1));
m_fname_f->Wrap(FromDIP(300));
m_fname_f->SetForegroundColour(wxColour("#363636"));
m_sizer_fline->Add(m_fname_f, 1, wxALL, 0);
m_sizer_name->Add(m_sizer_fline, 1, wxEXPAND, 0);
m_fname_s = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
m_fname_s->SetFont(::Label::Head_14);
m_fname_s->Wrap(-1);
m_fname_s->SetForegroundColour(wxColour(38, 46, 48));
m_sizer_name->Add(m_fname_s, 1, wxALL, 0);
m_sizer_main->Add(m_sizer_name, 1, wxEXPAND | wxLEFT | wxRIGHT, 20);
m_sizer_main->Add(m_fname_title, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(20));
m_sizer_main->AddSpacer(FromDIP(10));
m_sizer_main->Add(m_fname_f , 1, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(20));
m_sizer_main->AddSpacer(FromDIP(10));
auto radio_group = new RadioGroup(this, {
_L("Open as project"), // 0
@@ -14115,9 +14130,9 @@ ProjectDropDialog::ProjectDropDialog(const std::string &filename)
set_action(radio_group->GetSelection() + 1);
});
m_sizer_main->Add(radio_group, 0, wxEXPAND | wxLEFT | wxRIGHT, 20);
m_sizer_main->Add(radio_group, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(20));
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, 10);
m_sizer_main->AddSpacer(FromDIP(10));
// wxBoxSizer *m_sizer_bottom = new wxBoxSizer(wxHORIZONTAL);
// Orca: hide the "Don't show again" checkbox, people keeps accidentally checked this then forgot
@@ -14141,29 +14156,6 @@ ProjectDropDialog::ProjectDropDialog(const std::string &filename)
Fit();
Centre(wxBOTH);
auto limit_width = m_fname_f->GetSize().GetWidth() - 2;
auto current_width = 0;
auto cut_index = 0;
auto fstring = wxString("");
auto bstring = wxString("");
//auto file_name = from_u8(filename.c_str());
auto file_name = wxString(filename);
for (int x = 0; x < file_name.length(); x++) {
current_width += m_fname_s->GetTextExtent(file_name[x]).GetWidth();
cut_index = x;
if (current_width > limit_width) {
bstring += file_name[x];
} else {
fstring += file_name[x];
}
}
m_fname_f->SetLabel(fstring);
m_fname_s->SetLabel(bstring);
wxGetApp().UpdateDlgDarkUI(this);
}
@@ -15129,7 +15121,7 @@ Preset *get_printer_preset(const MachineObject *obj)
return nullptr;
Preset *printer_preset = nullptr;
float machine_nozzle_diameter = obj->GetExtderSystem()->GetNozzleDiameter(0);
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
for (auto printer_it = preset_bundle->printers.begin(); printer_it != preset_bundle->printers.end(); printer_it++) {
// only use system printer preset
@@ -15142,7 +15134,8 @@ Preset *get_printer_preset(const MachineObject *obj)
std::string model_id = printer_it->get_current_printer_type(preset_bundle);
std::string printer_type = obj->get_show_printer_type();
if (model_id.compare(printer_type) == 0 && printer_nozzle_vals && abs(printer_nozzle_vals->get_at(0) - machine_nozzle_diameter) < 1e-3) {
bool nozzle_diameter_matches_or_unknown = printer_nozzle_vals && obj->GetExtderSystem()->NozzleDiameterMatchesOrUnknown(0, printer_nozzle_vals->get_at(0));
if (model_id.compare(printer_type) == 0 && nozzle_diameter_matches_or_unknown) {
printer_preset = &(*printer_it);
}
}
@@ -15158,12 +15151,12 @@ bool Plater::check_printer_initialized(MachineObject *obj, bool only_warning, bo
const auto& extruders = obj->GetExtderSystem()->GetExtruders();
for (const DevExtder& extruder : extruders) {
if (obj->is_multi_extruders()) {
if (extruder.GetNozzleFlowType() == NozzleFlowType::NONE_FLOWTYPE) {
has_been_initialized = false;
break;
}
// Skip check if nozzle type is unknown
if (extruder.GetNozzleType() == NozzleType::ntUndefine) {
continue;
}
if (extruder.GetNozzleFlowType() == NozzleFlowType::NONE_FLOWTYPE) {
has_been_initialized = false;
break;
@@ -15247,7 +15240,7 @@ TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, st
}
if (instance_id == -1) {
TriangleMesh vols_mesh(mesh);
TriangleMesh vols_mesh(std::move(mesh));
mesh = TriangleMesh();
for (const ModelInstance* i : mo.instances) {
TriangleMesh m = vols_mesh;