diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 4d70076ef..de2657894 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1417,9 +1417,9 @@ sub start_background_process { # Stop the background processing sub stop_background_process { my ($self) = @_; + $self->{background_slicing_process}->stop(); $self->{toolpaths2D}->reload_print if $self->{canvas3D}; $self->{preview3D}->reload_print if $self->{preview3D}; - $self->schedule_background_process; } # Called by the "Slice now" button, which is visible only if the background processing is disabled. @@ -1467,6 +1467,7 @@ sub export_gcode { eval { # this will throw errors if config is not valid $config->validate; + #FIXME it shall use the background processing! $self->{print}->apply_config($config); $self->{print}->validate; }; @@ -1508,6 +1509,8 @@ sub export_gcode { # this updates buttons status $self->object_list_changed; }); + + $self->{background_slicing_process}->set_output_path($self->{export_gcode_output_file}); # start background process, whose completion event handler # will detect $self->{export_gcode_output_file} and proceed with export @@ -1557,7 +1560,10 @@ sub on_process_completed { my $message; my $send_gcode = 0; my $do_print = 0; - if ($result) { +# print "Process completed, message: ", $message, "\n"; + if (defined($result)) { + $message = L("Export failed"); + } else { # G-code file exported successfully. if ($self->{print_file}) { $message = L("File added to print queue"); @@ -1565,14 +1571,13 @@ sub on_process_completed { } elsif ($self->{send_gcode_file}) { $message = L("Sending G-code file to the Printer Host ..."); $send_gcode = 1; - } else { + } elsif (defined $self->{export_gcode_output_file}) { $message = L("G-code file exported to ") . $self->{export_gcode_output_file}; + } else { + $message = L("Slicing complete"); } - } else { - $message = L("Export failed"); } $self->{export_gcode_output_file} = undef; - $self->statusbar->SetStatusText($message); wxTheApp->notify($message); $self->do_print if $do_print; @@ -1580,14 +1585,20 @@ sub on_process_completed { # Send $self->{send_gcode_file} to OctoPrint. if ($send_gcode) { my $host = Slic3r::PrintHost::get_print_host($self->{config}); - if ($host->send_gcode($self->{send_gcode_file})) { - $self->statusbar->SetStatusText(L("Upload to host finished.")); + $message = L("Upload to host finished."); } else { - $self->statusbar->SetStatusText(""); + $message = ""; } } + # As of now, the BackgroundProcessing thread posts status bar update messages to a queue on the MainFrame.pm, + # but the "Processing finished" message is posted to this window. + # Delay the following status bar update, so it will be called later than what is received by MainFrame.pm. + wxTheApp->CallAfter(sub { + $self->statusbar->SetStatusText($message); + }); + $self->{print_file} = undef; $self->{send_gcode_file} = undef; $self->print_info_box_show(1); diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 272a1ad71..7e35a27b1 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -602,12 +602,12 @@ if (WIN32 AND ";${PerlEmbed_CCFLAGS};" MATCHES ";[-/]Od;") message("Old CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}") message("Old CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELEASE}") message("Old CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}") - set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") - set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG /DWIN32") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG /DWIN32") - set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") - set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG /DWIN32") + set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") + set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") + set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") + set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG /DWIN32 /DTBB_USE_ASSERT") endif() # The following line will add -fPIC on Linux to make the XS.so rellocable. add_definitions(${PerlEmbed_CCCDLFLAGS}) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 0ade0de61..91c9d6b0f 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -418,6 +418,17 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ { PROFILE_CLEAR(); + if (print->is_step_done(psGCodeExport)) { + // Does the file exist? If so, we hope that it is still valid. + FILE *f = boost::nowide::fopen(path, "r"); + if (f != nullptr) { + ::fclose(f); + return; + } + } + + print->set_started(psGCodeExport); + BOOST_LOG_TRIVIAL(info) << "Exporting G-code..."; // Remove the old g-code if it exists. @@ -467,12 +478,13 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(msg); } - if (boost::nowide::rename(path_tmp.c_str(), path) != 0) + if (rename_file(path_tmp, path) != 0) throw std::runtime_error( std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "Is " + path_tmp + " locked?" + '\n'); BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished"; + print->set_done(psGCodeExport); // Write the profiler measurements to file PROFILE_UPDATE(); @@ -483,8 +495,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) { PROFILE_FUNC(); - print.set_started(psGCodeExport); - // resets time estimators m_normal_time_estimator.reset(); m_normal_time_estimator.set_dialect(print.config().gcode_flavor); @@ -1029,8 +1039,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // starts analizer calculations if (preview_data != nullptr) m_analyzer.calc_gcode_preview_data(*preview_data); - - print.set_done(psGCodeExport); } std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp index 7471367fe..f97265ee3 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp @@ -1,4 +1,5 @@ #include "GCodeTimeEstimator.hpp" +#include "Utils.hpp" #include #include @@ -367,8 +368,7 @@ namespace Slic3r { fclose(out); in.close(); - boost::nowide::remove(filename.c_str()); - if (boost::nowide::rename(path_tmp.c_str(), filename.c_str()) != 0) + if (rename_file(path_tmp, filename) != 0) throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + "Is " + path_tmp + " locked?" + '\n'); diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 19c474cad..dc21ab4ae 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -849,6 +849,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects) return; } +// Called by Print::validate() from the UI thread. void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume) { for (const ModelVolume* vol : this->volumes) diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 8a8af481c..8ca1d8ea9 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -129,6 +129,7 @@ public: void cut(coordf_t z, Model* model) const; void split(ModelObjectPtrs* new_objects); + // Called by Print::validate() from the UI thread. void check_instances_print_volume_state(const BoundingBoxf3& print_volume); // Print object statistics to console. diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index c8a50a6bb..a677d9e5a 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -72,6 +72,7 @@ void Print::reload_object(size_t /* idx */) // Returns true if the brim or skirt have been invalidated. bool Print::reload_model_instances() { + tbb::mutex::scoped_lock lock(m_mutex); bool invalidated = false; for (PrintObject *object : m_objects) invalidated |= object->reload_model_instances(); @@ -370,6 +371,7 @@ double Print::max_allowed_layer_height() const // and have explicit instance positions. void Print::add_model_object(ModelObject* model_object, int idx) { + tbb::mutex::scoped_lock lock(m_mutex); // Initialize a new print object and store it at the given position. PrintObject *object = new PrintObject(this, model_object, model_object->raw_bounding_box()); if (idx != -1) { @@ -378,6 +380,7 @@ void Print::add_model_object(ModelObject* model_object, int idx) } else m_objects.emplace_back(object); // Invalidate all print steps. + //FIXME lock mutex! this->invalidate_all_steps(); for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { @@ -434,6 +437,8 @@ void Print::add_model_object(ModelObject* model_object, int idx) bool Print::apply_config(DynamicPrintConfig config) { + tbb::mutex::scoped_lock lock(m_mutex); + // we get a copy of the config object so we can modify it safely config.normalize(); @@ -564,13 +569,17 @@ std::string Print::validate() const BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(m_config.max_print_height))); // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. print_volume.min(2) = -1e10; - unsigned int printable_count = 0; - for (PrintObject *po : m_objects) { - po->model_object()->check_instances_print_volume_state(print_volume); - po->reload_model_instances(); - if (po->is_printable()) - ++printable_count; - } + unsigned int printable_count = 0; + { + // Lock due to the po->reload_model_instances() + tbb::mutex::scoped_lock lock(m_mutex); + for (PrintObject *po : m_objects) { + po->model_object()->check_instances_print_volume_state(print_volume); + po->reload_model_instances(); + if (po->is_printable()) + ++ printable_count; + } + } if (printable_count == 0) return L("All objects are outside of the print volume."); diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index d72c1fef5..106640c4d 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -88,6 +88,11 @@ public: bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback &cancel) { bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID; if (invalidated) { +#if 0 + if (mtx.state != mtx.HELD) { + printf("Not held!\n"); + } +#endif mtx.unlock(); cancel(); mtx.lock(); @@ -283,18 +288,14 @@ private: SupportLayerPtrs m_support_layers; PrintState m_state; - // Mutex used for synchronization of the worker thread with the UI thread: - // The mutex will be used to guard the worker thread against entering a stage - // while the data influencing the stage is modified. - tbb::mutex m_mutex; // TODO: call model_object->get_bounding_box() instead of accepting // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); ~PrintObject() {} - void set_started(PrintObjectStep step) { m_state.set_started(step, m_mutex); } - void set_done(PrintObjectStep step) { m_state.set_done(step, m_mutex); } + void set_started(PrintObjectStep step); + void set_done(PrintObjectStep step); std::vector _slice_region(size_t region_id, const std::vector &z, bool modifier); }; @@ -446,8 +447,8 @@ public: const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } protected: - void set_started(PrintStep step) { m_state.set_started(step, m_mutex); } - void set_done(PrintStep step) { m_state.set_done(step, m_mutex); } + void set_started(PrintStep step) { m_state.set_started(step, m_mutex); throw_if_canceled(); } + void set_done(PrintStep step) { m_state.set_done(step, m_mutex); throw_if_canceled(); } bool invalidate_step(PrintStep step); bool invalidate_all_steps() { return m_state.invalidate_all(m_mutex, m_cancel_callback); } @@ -473,7 +474,7 @@ private: // Mutex used for synchronization of the worker thread with the UI thread: // The mutex will be used to guard the worker thread against entering a stage // while the data influencing the stage is modified. - tbb::mutex m_mutex; + mutable tbb::mutex m_mutex; // Has the calculation been canceled? tbb::atomic m_canceled; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 96c831157..7882c58a8 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -59,8 +59,19 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding this->layer_height_profile = model_object->layer_height_profile; } +void PrintObject::set_started(PrintObjectStep step) +{ + m_state.set_started(step, m_print->m_mutex); +} + +void PrintObject::set_done(PrintObjectStep step) +{ + m_state.set_done(step, m_print->m_mutex); +} + bool PrintObject::add_copy(const Vec2d &point) { + tbb::mutex::scoped_lock lock(m_print->m_mutex); Points points = m_copies; points.push_back(Point::new_scale(point(0), point(1))); return this->set_copies(points); @@ -68,6 +79,7 @@ bool PrintObject::add_copy(const Vec2d &point) bool PrintObject::delete_last_copy() { + tbb::mutex::scoped_lock lock(m_print->m_mutex); Points points = m_copies; points.pop_back(); return this->set_copies(points); @@ -158,7 +170,6 @@ void PrintObject::make_perimeters() m_print->throw_if_canceled(); } this->typed_slices = false; -// m_state.invalidate(posPrepareInfill); } // compare each layer to the one below, and mark those slices needing diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index f1390b8a2..dd05891dd 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -43,6 +43,14 @@ extern local_encoded_string encode_path(const char *src); extern std::string decode_path(const char *src); extern std::string normalize_utf8_nfc(const char *src); +// Safely rename a file even if the target exists. +// On Windows, the file explorer (or anti-virus or whatever else) often locks the file +// for a short while, so the file may not be movable. Retry while we see recoverable errors. +extern int rename_file(const std::string &from, const std::string &to); + +// Copy a file, adjust the access attributes, so that the target is writable. +extern int copy_file(const std::string &from, const std::string &to); + // File path / name / extension splitting utilities, working with UTF-8, // to be published to Perl. namespace PerlUtils { diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 95aaf5453..4c2d65605 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace Slic3r { @@ -139,6 +140,87 @@ const std::string& data_dir() return g_data_dir; } + +// borrowed from LVVM lib/Support/Windows/Path.inc +int rename_file(const std::string &from, const std::string &to) +{ + int ec = 0; + +#ifdef _WIN32 + + // Convert to utf-16. + std::wstring wide_from = boost::nowide::widen(from); + std::wstring wide_to = boost::nowide::widen(to); + + // Retry while we see recoverable errors. + // System scanners (eg. indexer) might open the source file when it is written + // and closed. + bool TryReplace = true; + + // This loop may take more than 2000 x 1ms to finish. + for (int i = 0; i < 2000; ++ i) { + if (i > 0) + // Sleep 1ms + ::Sleep(1); + if (TryReplace) { + // Try ReplaceFile first, as it is able to associate a new data stream + // with the destination even if the destination file is currently open. + if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL)) + return 0; + DWORD ReplaceError = ::GetLastError(); + ec = -1; // ReplaceError + // If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or + // ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW(). + if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT || + ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) { + TryReplace = false; + continue; + } + // If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry + // using ReplaceFileW(). + if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED) + continue; + // We get ERROR_FILE_NOT_FOUND if the destination file is missing. + // MoveFileEx can handle this case. + if (ReplaceError != ERROR_ACCESS_DENIED && ReplaceError != ERROR_FILE_NOT_FOUND && ReplaceError != ERROR_SHARING_VIOLATION) + break; + } + if (::MoveFileExW(wide_from.c_str(), wide_to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) + return 0; + DWORD MoveError = ::GetLastError(); + ec = -1; // MoveError + if (MoveError != ERROR_ACCESS_DENIED && MoveError != ERROR_SHARING_VIOLATION) + break; + } + +#else + + boost::nowide::remove(from.c_str()); + ec = boost::nowide::rename(from.c_str(), to.c_str()); + +#endif + + return ec; +} + +int copy_file(const std::string &from, const std::string &to) +{ + const boost::filesystem::path source(from); + const boost::filesystem::path target(to); + static const auto perms = boost::filesystem::owner_read | boost::filesystem::owner_write | boost::filesystem::group_read | boost::filesystem::others_read; // aka 644 + + // Make sure the file has correct permission both before and after we copy over it. + try { + if (boost::filesystem::exists(target)) + boost::filesystem::permissions(target, perms); + boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists); + boost::filesystem::permissions(target, perms); + } catch (std::exception & /* ex */) { + return -1; + } + return 0; +} + } // namespace Slic3r #include diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 47da3367c..2f88b25ca 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -3,6 +3,7 @@ #include #include +#include // Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. #include "../../libslic3r/Print.hpp" @@ -11,12 +12,28 @@ #include #include +#include +#include + namespace Slic3r { namespace GUI { extern wxPanel *g_wxPlater; }; +BackgroundSlicingProcess::BackgroundSlicingProcess() +{ + m_temp_output_path = wxStandardPaths::Get().GetTempDir().utf8_str().data(); + m_temp_output_path += (boost::format(".%1%.gcode") % get_current_pid()).str(); +} + +BackgroundSlicingProcess::~BackgroundSlicingProcess() +{ + this->stop(); + this->join_background_thread(); + boost::nowide::remove(m_temp_output_path.c_str()); +} + void BackgroundSlicingProcess::thread_proc() { std::unique_lock lck(m_mutex); @@ -41,7 +58,10 @@ void BackgroundSlicingProcess::thread_proc() m_print->process(); if (! m_print->canceled()) { wxQueueEvent(GUI::g_wxPlater, new wxCommandEvent(m_event_sliced_id)); - m_print->export_gcode(m_output_path, m_gcode_preview_data); + m_print->export_gcode(m_temp_output_path, m_gcode_preview_data); + if (! m_print->canceled() && ! m_output_path.empty() && + copy_file(m_temp_output_path, m_output_path) != 0) + throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); } } catch (CanceledException &ex) { // Canceled, this is all right. @@ -111,8 +131,10 @@ bool BackgroundSlicingProcess::start() bool BackgroundSlicingProcess::stop() { std::unique_lock lck(m_mutex); - if (m_state == STATE_INITIAL) + if (m_state == STATE_INITIAL) { + this->m_output_path.clear(); return false; + } assert(this->running()); if (m_state == STATE_STARTED || m_state == STATE_RUNNING) { m_print->cancel(); @@ -124,6 +146,7 @@ bool BackgroundSlicingProcess::stop() // In the "Finished" or "Canceled" state. Reset the state to "Idle". m_state = STATE_IDLE; } + this->m_output_path.clear(); return true; } diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 358e00ca9..cc7a6db30 100644 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -17,9 +17,9 @@ class Print; class BackgroundSlicingProcess { public: - BackgroundSlicingProcess() {} - // Stop the background processing and finalize the bacgkround processing thread. - ~BackgroundSlicingProcess() { this->stop(); this->join_background_thread(); } + BackgroundSlicingProcess(); + // Stop the background processing and finalize the bacgkround processing thread, remove temp files. + ~BackgroundSlicingProcess(); void set_print(Print *print) { m_print = print; } void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } @@ -31,6 +31,8 @@ public: // The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed. void set_finished_event(int event_id) { m_event_finished_id = event_id; } + // Set the output path of the G-code. + void set_output_path(const std::string &path) { m_output_path = path; } // Start the background processing. Returns false if the background processing was already running. bool start(); // Cancel the background processing. Returns false if the background processing was not running. @@ -68,6 +70,7 @@ private: Print *m_print = nullptr; // Data structure, to which the G-code export writes its annotations. GCodePreviewData *m_gcode_preview_data = nullptr; + std::string m_temp_output_path; std::string m_output_path; // Thread, on which the background processing is executed. The thread will always be present // and ready to execute the slicing process. diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 2aaa4aed9..ae42f014b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -5208,9 +5208,9 @@ void GLCanvas3D::_load_shells() // adds objects' volumes unsigned int object_id = 0; - for (PrintObject* obj : m_print->objects()) + for (const PrintObject* obj : m_print->objects()) { - ModelObject* model_obj = obj->model_object(); + const ModelObject* model_obj = obj->model_object(); std::vector instance_ids(model_obj->instances.size()); for (int i = 0; i < (int)model_obj->instances.size(); ++i) diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp index af57db8ed..0c7b7a5d8 100644 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp +++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp @@ -383,13 +383,10 @@ void update_after_moving() if (volume_id < 0) return; - Vec3d m = m_move_options; - Vec3d l = m_last_coords; - - auto d = Vec3d(m(0) - l(0), m(1) - l(1), m(2) - l(2)); - auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; + auto d = m_move_options - m_last_coords; + auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; volume->mesh.translate(d(0), d(1), d(2)); - m_last_coords = m; + m_last_coords = m_move_options; m_parts_changed = true; parts_changed(m_selected_object_id); diff --git a/xs/src/slic3r/GUI/ProgressIndicator.hpp b/xs/src/slic3r/GUI/ProgressIndicator.hpp new file mode 100644 index 000000000..106f5e7ea --- /dev/null +++ b/xs/src/slic3r/GUI/ProgressIndicator.hpp @@ -0,0 +1,72 @@ +#ifndef IPROGRESSINDICATOR_HPP +#define IPROGRESSINDICATOR_HPP + +#include +#include + +#include + +namespace Slic3r { + +/** + * @brief Generic progress indication interface. + */ +class ProgressIndicator { +public: + using CancelFn = std::function; // Cancel function signature. + +private: + float state_ = .0f, max_ = 1.f, step_; + CancelFn cancelfunc_ = [](){}; + +public: + + inline virtual ~ProgressIndicator() {} + + /// Get the maximum of the progress range. + float max() const { return max_; } + + /// Get the current progress state + float state() const { return state_; } + + /// Set the maximum of the progress range + virtual void max(float maxval) { max_ = maxval; } + + /// Set the current state of the progress. + virtual void state(float val) { state_ = val; } + + /** + * @brief Number of states int the progress. Can be used instead of giving a + * maximum value. + */ + virtual void states(unsigned statenum) { + step_ = max_ / statenum; + } + + /// Message shown on the next status update. + virtual void message(const wxString&) = 0; + + /// Title of the operation. + virtual void title(const wxString&) = 0; + + /// Formatted message for the next status update. Works just like sprintf. + virtual void message_fmt(const wxString& fmt, ...); + + /// Set up a cancel callback for the operation if feasible. + virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; } + + /** + * Explicitly shut down the progress indicator and call the associated + * callback. + */ + virtual void cancel() { cancelfunc_(); } + + /// Convenience function to call message and status update in one function. + void update(float st, const wxString& msg) { + message(msg); state(st); + } +}; + +} + +#endif // IPROGRESSINDICATOR_HPP diff --git a/xs/xsp/GUI_BackgroundSlicingProcess.xsp b/xs/xsp/GUI_BackgroundSlicingProcess.xsp index cef9f6d58..8452b8c31 100644 --- a/xs/xsp/GUI_BackgroundSlicingProcess.xsp +++ b/xs/xsp/GUI_BackgroundSlicingProcess.xsp @@ -15,6 +15,7 @@ void set_sliced_event(int event_id); void set_finished_event(int event_id); + void set_output_path(const char *path); bool start(); bool stop(); bool apply_config(DynamicPrintConfig *config)