mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-23 17:02:39 +00:00
Reset the CAD document with the project, and track its changes
New Project and Open Project went through Plater::priv::reset, which drops ModelObjects but not the Model-level recipe, and never touched the Design panel's document at all — so the previous design stayed loaded and its next edit wrote itself into the new project. A design that has not been committed to the plate has no ModelObjects, so the project also read as clean: no autosave, and no unsaved-changes prompt before the reset threw the design away.
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include "libslic3r/CAD/GeometryEngine.hpp" // face_by_index for face-extrude gizmo anchor
|
||||
#include "libslic3r/TriangleMesh.hpp" // mesh import: STL/OBJ -> indexed_triangle_set
|
||||
#include "libslic3r/Format/OBJ.hpp"
|
||||
#include "libslic3r/Format/bbs_3mf.hpp" // put_other_changes: mark the project dirty outside the undo stack
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
@@ -4620,7 +4621,16 @@ void DesignPanel::sync_recipe_to_model()
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (plater == nullptr) return;
|
||||
// An empty document CLEARS it, so a non-CAD project never carries a stale recipe.
|
||||
plater->model().cad_recipe = m_doc.features.empty() ? std::string() : m_doc.serialize_recipe();
|
||||
std::string recipe = m_doc.features.empty() ? std::string() : m_doc.serialize_recipe();
|
||||
if (recipe == plater->model().cad_recipe)
|
||||
return; // no change — this is the rehydrate of a project that was just opened
|
||||
plater->model().cad_recipe = std::move(recipe);
|
||||
|
||||
// The plater's dirty flag rides its own undo/redo stack, which Design edits never touch, and
|
||||
// a design that has not been committed to the plate has no ModelObjects either — so without
|
||||
// this the project reads as clean: no autosave, and no "unsaved changes" prompt on quit.
|
||||
// Same hook the auxiliary-files panel uses for project data that lives outside the model.
|
||||
Slic3r::put_other_changes();
|
||||
}
|
||||
|
||||
bool DesignPanel::recompute_guarded(const wxString& message)
|
||||
@@ -7427,6 +7437,15 @@ void DesignPanel::on_new_design()
|
||||
_L("Erase all features and bodies and start a new design? This cannot be undone."),
|
||||
_L("New Design"), wxYES_NO | wxICON_EXCLAMATION);
|
||||
if (dlg.ShowModal() != wxID_YES) return;
|
||||
clear_document();
|
||||
}
|
||||
|
||||
// The teardown behind New Design, without the confirmation. Also what New Project / Open
|
||||
// Project run through Plater::priv::reset: the document lives here rather than in the Model,
|
||||
// so without this it survives the project that produced it and the next Design edit writes
|
||||
// the previous project's feature tree into the new one.
|
||||
void DesignPanel::clear_document()
|
||||
{
|
||||
tool_cancel(); // leave any active tool / sketch / constrain cleanly
|
||||
m_doc.clear(); // features + bodies + meshes + history
|
||||
m_edit_index = -1;
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
explicit DesignPanel(wxWindow* parent);
|
||||
void on_tab_shown(); // re-sync bed to the active printer when the Design tab is activated
|
||||
void on_tab_hidden(); // another tab took over: take the viewport status line down with us
|
||||
void clear_document(); // New Project / Open Project: drop the document with the project
|
||||
// Rebuild off the UI thread (progress dialog only if it turns out to be slow), so a feature
|
||||
// op on a heavy imported solid does not freeze the window. Returns m_doc.recompute()'s result.
|
||||
// Push the document's recipe into the Model so ANY save path persists it (snaporca-vjk5).
|
||||
|
||||
+36
-12
@@ -97,6 +97,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"
|
||||
@@ -8343,6 +8346,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();
|
||||
@@ -9290,11 +9296,15 @@ 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 (Metadata/orca_cad.bin) onto the plater model so the Design tab
|
||||
// can rehydrate the editable feature tree on reopen.
|
||||
if (!model.cad_recipe.empty())
|
||||
// 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));
|
||||
@@ -9512,12 +9522,12 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
// q->model().stl_design_country = "";
|
||||
//}
|
||||
|
||||
// A CAD project legitimately carries no mesh: the model lives in the feature tree
|
||||
// (Metadata/orca_cad.bin) 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 the recipe as geometry.
|
||||
if (tolal_model_count <= 0 && q->model().cad_recipe.empty() && !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);
|
||||
@@ -10056,6 +10066,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();
|
||||
|
||||
@@ -15508,8 +15528,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)
|
||||
|
||||
Reference in New Issue
Block a user