Merge main

This commit is contained in:
Lam Wei Lun
2026-09-18 15:59:00 +08:00
2713 changed files with 238542 additions and 115600 deletions
+51 -3
View File
@@ -91,6 +91,9 @@
#include "wxExtensions.hpp"
#include "../Utils/PrintHost.hpp"
#include "MainFrame.hpp"
#ifdef SLIC3R_CAD
#include "slic3r/GUI/CAD/DesignPanel.hpp"
#endif
#include "format.hpp"
#include "3DScene.hpp"
#include "GLCanvas3D.hpp"
@@ -8328,6 +8331,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
int answer_convert_from_meters = wxOK_DEFAULT;
int answer_convert_from_imperial_units = wxOK_DEFAULT;
int tolal_model_count = 0;
// Whether one of the files being loaded here carried a CAD recipe. A statement about these
// files, not about the plater — q->model() may still hold the previous project's recipe.
bool loaded_cad_recipe = false;
int progress_percent = 0;
int total_files = input_files.size();
@@ -9453,6 +9459,16 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
auto loaded_idxs = load_model_objects(model.objects, is_project_file);
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
// load_model_objects only transfers ModelObjects; carry the Model-level CAD recipe
// onto the plater model so the Design tab can rehydrate the editable feature tree on
// reopen. Assigned unconditionally on the project-replacing path so that opening a
// project without a recipe clears whatever the previous one left behind; importing a
// plain model into the open project leaves the current recipe alone.
if (is_project_file) {
q->model().cad_recipe = model.cad_recipe;
loaded_cad_recipe = !model.cad_recipe.empty();
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", finished load_model_objects");
wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename));
dlg_cont = dlg.Update(progress_percent, msg);
@@ -9669,7 +9685,12 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// q->model().stl_design_country = "";
//}
if (tolal_model_count <= 0 && !q->m_exported_file) {
// A CAD project legitimately carries no mesh: the model lives in the feature tree until it is
// committed to the plate. Warning "no geometry data" for one is false, and it is the LAST thing
// a user sees after opening a design they spent an hour on — it reads as "your work is gone"
// when the recipe has in fact just been loaded and the Design tab will rehydrate it. Count a
// recipe that came from THESE files as geometry.
if (tolal_model_count <= 0 && !loaded_cad_recipe && !q->m_exported_file) {
dlg.Hide();
if (!is_user_cancel) {
MessageDialog msg(wxGetApp().mainframe, _L("The file does not contain any geometry data."), _L("Warning"), wxYES | wxICON_WARNING);
@@ -10216,6 +10237,16 @@ void Plater::priv::reset(bool apply_presets_change)
// Stop and reset the Print content.
this->background_process.reset();
model.clear_objects();
// clear_objects() only drops the ModelObjects; the CAD recipe is Model-level state and would
// otherwise be written into every project saved for the rest of the session.
model.cad_recipe.clear();
#ifdef SLIC3R_CAD
// Same reason, one level up: the Design tab keeps the editable document, not the Model, so
// clearing the recipe alone leaves the tab showing the previous project's feature tree —
// and its next edit syncs that tree straight back into the new project.
if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr)
wxGetApp().mainframe->m_design_panel->clear_document();
#endif
assemble_view->get_canvas3d()->reset_explosion_ratio();
update();
@@ -13709,6 +13740,14 @@ void Plater::priv::unbind_canvas_event_handlers()
if (assemble_view != nullptr)
assemble_view->get_canvas3d()->unbind_event_handlers();
#ifdef SLIC3R_CAD
// The Design tab's viewport is a fourth GLCanvas3D on the same shared GL context, owned by
// MainFrame rather than by us — same reach as reset() uses for clear_document(). Null until
// the tab has been opened once, so most sessions skip it.
if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr)
wxGetApp().mainframe->m_design_panel->unbind_canvas_event_handlers();
#endif
}
void Plater::priv::reset_canvas_volumes()
@@ -13718,6 +13757,11 @@ void Plater::priv::reset_canvas_volumes()
if (preview != nullptr)
preview->get_canvas3d()->reset_volumes();
#ifdef SLIC3R_CAD
if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr)
wxGetApp().mainframe->m_design_panel->reset_canvas_volumes();
#endif
}
bool Plater::priv::check_ams_status_impl(bool is_slice_all)
@@ -15669,8 +15713,12 @@ bool Plater::up_to_date(bool saved, bool backup)
Slic3r::clear_other_changes(backup);
return p->up_to_date(saved, backup);
}
return p->model.objects.empty() || (p->up_to_date(saved, backup) &&
!Slic3r::has_other_changes(backup));
// A Design-tab project is object-less until it is committed to the plate, but its feature
// tree is real work: treating it as an empty project skipped both the autosave and the
// "unsaved changes" prompt, so quitting threw it away without asking. Non-CAD projects
// never carry a recipe, so the empty-project shortcut is unchanged for them.
return (p->model.objects.empty() && p->model.cad_recipe.empty()) ||
(p->up_to_date(saved, backup) && !Slic3r::has_other_changes(backup));
}
bool Plater::add_model(bool imperial_units, std::string fname)