diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp index 27a79f84e..556035a5b 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.cpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp @@ -6,10 +6,19 @@ #include "FixModelByWin10.hpp" -#include -#include +#include +#include #include +#include +#include +#include #include + +#include +#include +#include + +#include // for ComPtr #include // from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ @@ -17,16 +26,13 @@ #include #include -#include -#include -#include - #include "libslic3r/Model.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/Format/3mf.hpp" #include "../GUI/GUI.hpp" #include "../GUI/PresetBundle.hpp" +#include #include extern "C"{ @@ -105,8 +111,11 @@ static HRESULT winrt_get_activation_factory(const std::wstring &class_name, TYPE return winrt_get_activation_factory(class_name, __uuidof(TYPE), reinterpret_cast(pinst)); } +// To be called often to test whether to cancel the operation. +typedef std::function ThrowOnCancelFn; + template -static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr &asyncAction, int blocking_tick_ms = 300) +static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr &asyncAction, ThrowOnCancelFn throw_on_cancel, int blocking_tick_ms = 100) { Microsoft::WRL::ComPtr asyncInfo; asyncAction.As(&asyncInfo); @@ -118,6 +127,7 @@ static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr &asyncActio asyncInfo->get_Status(&status); if (status != AsyncStatus::Started) return status; + throw_on_cancel(); ::Sleep(blocking_tick_ms); } } @@ -125,7 +135,8 @@ static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr &asyncActio static HRESULT winrt_open_file_stream( const std::wstring &path, ABI::Windows::Storage::FileAccessMode mode, - ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream) + ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream, + ThrowOnCancelFn throw_on_cancel) { // Get the file factory. Microsoft::WRL::ComPtr fileFactory; @@ -142,7 +153,7 @@ static HRESULT winrt_open_file_stream( (*s_WindowsDeleteString)(hstr_path); // Wait until the file gets open, get the actual file. - AsyncStatus status = winrt_async_await(fileOpenAsync); + AsyncStatus status = winrt_async_await(fileOpenAsync, throw_on_cancel); Microsoft::WRL::ComPtr storageFile; if (status == AsyncStatus::Completed) { hr = fileOpenAsync->GetResults(storageFile.GetAddressOf()); @@ -159,7 +170,7 @@ static HRESULT winrt_open_file_stream( hr = storageFile->OpenAsync(mode, fileStreamAsync.GetAddressOf()); if (FAILED(hr)) return hr; - status = winrt_async_await(fileStreamAsync); + status = winrt_async_await(fileStreamAsync, throw_on_cancel); if (status == AsyncStatus::Completed) { hr = fileStreamAsync->GetResults(fileStream); } else { @@ -189,21 +200,23 @@ bool is_windows10() return false; } -bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst) -{ - if (! is_windows10()) { - return false; - } +// Progress function, to be called regularly to update the progress. +typedef std::function ProgressFn; - if (! winrt_load_runtime_object_library()) { - printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n"); - return false; - } +void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst, ProgressFn on_progress, ThrowOnCancelFn throw_on_cancel) +{ + if (! is_windows10()) + throw std::runtime_error("fix_model_by_win10_sdk called on non Windows 10 system"); + + if (! winrt_load_runtime_object_library()) + throw std::runtime_error("Failed to initialize the WinRT library."); HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED); { + on_progress(L("Exporting the source model"), 20); + Microsoft::WRL::ComPtr fileStream; - hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf()); + hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf(), throw_on_cancel); Microsoft::WRL::ComPtr printing3d3mfpackage; hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf()); @@ -211,30 +224,29 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path Microsoft::WRL::ComPtr> modelAsync; hr = printing3d3mfpackage->LoadModelFromPackageAsync(fileStream.Get(), modelAsync.GetAddressOf()); - AsyncStatus status = winrt_async_await(modelAsync); + AsyncStatus status = winrt_async_await(modelAsync, throw_on_cancel); Microsoft::WRL::ComPtr model; - if (status == AsyncStatus::Completed) { + if (status == AsyncStatus::Completed) hr = modelAsync->GetResults(model.GetAddressOf()); - } else { - printf("Failed loading the input model. Exiting.\n"); - return false; - } + else + throw std::runtime_error(L("Failed loading the input model.")); Microsoft::WRL::ComPtr> meshes; hr = model->get_Meshes(meshes.GetAddressOf()); unsigned num_meshes = 0; hr = meshes->get_Size(&num_meshes); + on_progress(L("Repairing the model by the Netfabb service"), 40); + Microsoft::WRL::ComPtr repairAsync; hr = model->RepairAsync(repairAsync.GetAddressOf()); - status = winrt_async_await(repairAsync); - if (status != AsyncStatus::Completed) { - printf("Mesh repair failed. Exiting.\n"); - return false; - } - printf("Mesh repair finished successfully.\n"); + status = winrt_async_await(repairAsync, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Mesh repair failed.")); repairAsync->GetResults(); + on_progress(L("Loading the repaired model"), 60); + // Verify the number of meshes returned after the repair action. meshes.Reset(); hr = model->get_Meshes(meshes.GetAddressOf()); @@ -243,20 +255,16 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path // Save model to this class' Printing3D3MFPackage. Microsoft::WRL::ComPtr saveToPackageAsync; hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf()); - status = winrt_async_await(saveToPackageAsync); - if (status != AsyncStatus::Completed) { - printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return false; - } + status = winrt_async_await(saveToPackageAsync, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); hr = saveToPackageAsync->GetResults(); Microsoft::WRL::ComPtr> generatorStreamAsync; hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf()); - status = winrt_async_await(generatorStreamAsync); - if (status != AsyncStatus::Completed) { - printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return false; - } + status = winrt_async_await(generatorStreamAsync, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); Microsoft::WRL::ComPtr generatorStream; hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf()); @@ -286,11 +294,9 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path Microsoft::WRL::ComPtr> asyncRead; for (;;) { hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf()); - status = winrt_async_await(asyncRead); - if (status != AsyncStatus::Completed) { - printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return false; - } + status = winrt_async_await(asyncRead, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); hr = buffer->get_Length(&length); if (length == 0) break; @@ -300,38 +306,95 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path // Here all the COM objects will be released through the ComPtr destructors. } (*s_RoUninitialize)(); - return true; } +class RepairCanceledException : public std::exception { +public: + const char* what() const throw() { return "Model repair has been canceled"; } +}; + void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result) { - enum { PROGRESS_RANGE = 1000 }; + std::mutex mutex; + std::condition_variable condition; + std::unique_lock lock(mutex); + struct Progress { + std::string message; + int percent = 0; + bool updated = false; + } progress; + std::atomic canceled = false; + std::atomic finished = false; + + // Open a progress dialog. wxProgressDialog progress_dialog( _(L("Model fixing")), _(L("Exporting model...")), - PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); - progress_dialog.Pulse(); - + 100, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); // Executing the calculation in a background thread, so that the COM context could be created with its own threading model. // (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context). - auto worker = std::thread([&model_object, &print, &result, &progress_dialog](){ - boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - path_src += ".3mf"; - Model model; - model.add_object(model_object); - bool res = Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast(&print), false); - model.clear_objects(); - model.clear_materials(); - - boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - path_dst += ".3mf"; - res = fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string()); - boost::filesystem::remove(path_src); - PresetBundle bundle; - res = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result); - boost::filesystem::remove(path_dst); + bool success = false; + auto on_progress = [&mutex, &condition, &progress](const char *msg, unsigned prcnt) { + std::lock_guard lk(mutex); + progress.message = msg; + progress.percent = prcnt; + progress.updated = true; + condition.notify_all(); + }; + auto worker_thread = boost::thread([&model_object, &print, &result, on_progress, &success, &canceled, &finished]() { + try { + on_progress(L("Exporting the source model"), 0); + boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_src += ".3mf"; + Model model; + model.add_object(model_object); + if (! Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast(&print), false)) { + boost::filesystem::remove(path_src); + throw std::runtime_error(L("Export of a temporary 3mf file failed")); + } + model.clear_objects(); + model.clear_materials(); + boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_dst += ".3mf"; + fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string(), on_progress, + [&canceled]() { if (canceled) throw RepairCanceledException(); }); + boost::filesystem::remove(path_src); + PresetBundle bundle; + on_progress(L("Loading the repaired model"), 80); + bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result); + boost::filesystem::remove(path_dst); + if (! loaded) + throw std::runtime_error(L("Import of the repaired 3mf file failed")); + success = true; + finished = true; + on_progress(L("Model repair finished"), 100); + } catch (RepairCanceledException &ex) { + canceled = true; + finished = true; + on_progress(L("Model repair canceled"), 100); + } catch (std::exception &ex) { + success = false; + finished = true; + on_progress(ex.what(), 100); + } }); - worker.join(); + while (! finished) { + condition.wait_for(lock, std::chrono::milliseconds(500), [&progress]{ return progress.updated; }); + if (! progress_dialog.Update(progress.percent, _(progress.message))) + canceled = true; + progress.updated = false; + } + + if (canceled) { + // Nothing to show. + } else if (success) { + wxMessageDialog dlg(nullptr, _(L("Model repaired successfully")), _(L("Model Repair by the Netfabb service")), wxICON_INFORMATION | wxOK_DEFAULT); + dlg.ShowModal(); + } else { + wxMessageDialog dlg(nullptr, _(L("Model repair failed: \n")) + _(progress.message), _(L("Model Repair by the Netfabb service")), wxICON_ERROR | wxOK_DEFAULT); + dlg.ShowModal(); + } + worker_thread.join(); } } // namespace Slic3r diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp index 299c9b75b..c148a6970 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.hpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp @@ -12,13 +12,11 @@ class Print; #ifdef HAS_WIN10SDK extern bool is_windows10(); -extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst); extern void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result); #else /* HAS_WIN10SDK */ inline bool is_windows10() { return false; } -inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; } inline void fix_model_by_win10_sdk_gui(const ModelObject &, const Print &, Model &) {} #endif /* HAS_WIN10SDK */