From e65fac5e8484b7737f90948d40dfd05a49a35969 Mon Sep 17 00:00:00 2001 From: bubnikv <bubnikv@gmail.com> Date: Mon, 4 Jun 2018 17:27:33 +0200 Subject: [PATCH 1/3] Added initial implementation of fixing 3MFs through the Netfabb API provided through the Windows 10 Universal Windows Platform API. --- xs/CMakeLists.txt | 2 + xs/src/slic3r/Utils/FixModelByWin10.cpp | 287 ++++++++++++++++++++++++ xs/src/slic3r/Utils/FixModelByWin10.hpp | 18 ++ 3 files changed, 307 insertions(+) create mode 100644 xs/src/slic3r/Utils/FixModelByWin10.cpp create mode 100644 xs/src/slic3r/Utils/FixModelByWin10.hpp diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 77747cd07..64f4b9274 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -232,6 +232,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/FirmwareDialog.hpp ${LIBDIR}/slic3r/Utils/Http.cpp ${LIBDIR}/slic3r/Utils/Http.hpp + ${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp + ${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp ${LIBDIR}/slic3r/Utils/OctoPrint.hpp ${LIBDIR}/slic3r/Utils/Bonjour.cpp diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp new file mode 100644 index 000000000..e4b1952a4 --- /dev/null +++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp @@ -0,0 +1,287 @@ +#ifdef HAS_WIN10SDK + +#include "FixModelByWin10.hpp" + +#include <roapi.h> +#include <string> +#include <cstdint> +// for ComPtr +#include <wrl/client.h> +// from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ +#include <winrt/robuffer.h> +#include <winrt/windows.storage.provider.h> +#include <winrt/windows.graphics.printing3d.h> + +extern "C"{ + // from rapi.h + typedef HRESULT (__stdcall* FunctionRoInitialize)(int); + typedef HRESULT (__stdcall* FunctionRoUninitialize)(); + typedef HRESULT (__stdcall* FunctionRoActivateInstance)(HSTRING activatableClassId, IInspectable **instance); + typedef HRESULT (__stdcall* FunctionRoGetActivationFactory)(HSTRING activatableClassId, REFIID iid, void **factory); + // from winstring.h + typedef HRESULT (__stdcall* FunctionWindowsCreateString)(LPCWSTR sourceString, UINT32 length, HSTRING *string); + typedef HRESULT (__stdcall* FunctionWindowsDelteString)(HSTRING string); +} + +HMODULE s_hRuntimeObjectLibrary = nullptr; +FunctionRoInitialize s_RoInitialize = nullptr; +FunctionRoUninitialize s_RoUninitialize = nullptr; +FunctionRoActivateInstance s_RoActivateInstance = nullptr; +FunctionRoGetActivationFactory s_RoGetActivationFactory = nullptr; +FunctionWindowsCreateString s_WindowsCreateString = nullptr; +FunctionWindowsDelteString s_WindowsDeleteString = nullptr; + +bool winrt_load_runtime_object_library() +{ + if (s_hRuntimeObjectLibrary == nullptr) + s_hRuntimeObjectLibrary = LoadLibrary(L"ComBase.dll"); + if (s_hRuntimeObjectLibrary != nullptr) { + s_RoInitialize = (FunctionRoInitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoInitialize"); + s_RoUninitialize = (FunctionRoUninitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoUninitialize"); + s_RoActivateInstance = (FunctionRoActivateInstance) GetProcAddress(s_hRuntimeObjectLibrary, "RoActivateInstance"); + s_RoGetActivationFactory = (FunctionRoGetActivationFactory) GetProcAddress(s_hRuntimeObjectLibrary, "RoGetActivationFactory"); + s_WindowsCreateString = (FunctionWindowsCreateString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsCreateString"); + s_WindowsDeleteString = (FunctionWindowsDelteString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsDeleteString"); + } + return s_RoInitialize && s_RoUninitialize && s_RoActivateInstance && s_WindowsCreateString && s_WindowsDeleteString; +} + +static HRESULT winrt_activate_instance(const std::wstring &class_name, IInspectable **pinst) +{ + HSTRING hClassName; + HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName); + if (S_OK != hr) + return hr; + hr = (*s_RoActivateInstance)(hClassName, pinst); + (*s_WindowsDeleteString)(hClassName); + return hr; +} + +template<typename TYPE> +static HRESULT winrt_activate_instance(const std::wstring &class_name, TYPE **pinst) +{ + IInspectable *pinspectable = nullptr; + HRESULT hr = winrt_activate_instance(class_name, &pinspectable); + if (S_OK != hr) + return hr; + hr = pinspectable->QueryInterface(__uuidof(TYPE), (void**)pinst); + pinspectable->Release(); + return hr; +} + +static HRESULT winrt_get_activation_factory(const std::wstring &class_name, REFIID iid, void **pinst) +{ + HSTRING hClassName; + HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName); + if (S_OK != hr) + return hr; + hr = (*s_RoGetActivationFactory)(hClassName, iid, pinst); + (*s_WindowsDeleteString)(hClassName); + return hr; +} + +template<typename TYPE> +static HRESULT winrt_get_activation_factory(const std::wstring &class_name, TYPE **pinst) +{ + return winrt_get_activation_factory(class_name, __uuidof(TYPE), reinterpret_cast<void**>(pinst)); +} + +template<typename T> +static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncAction, int blocking_tick_ms = 300) +{ + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo; + asyncAction.As(&asyncInfo); + AsyncStatus status; + // Ugly blocking loop until the RepairAsync call finishes. +//FIXME replace with a callback. +// https://social.msdn.microsoft.com/Forums/en-US/a5038fb4-b7b7-4504-969d-c102faa389fb/trying-to-block-an-async-operation-and-wait-for-a-particular-time?forum=vclanguage + for (;;) { + asyncInfo->get_Status(&status); + if (status != AsyncStatus::Started) + return status; + ::Sleep(blocking_tick_ms); + } +} + +static HRESULT winrt_open_file_stream( + const std::wstring &path, + ABI::Windows::Storage::FileAccessMode mode, + ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream) +{ + // Get the file factory. + Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFileStatics> fileFactory; + HRESULT hr = winrt_get_activation_factory(L"Windows.Storage.StorageFile", fileFactory.GetAddressOf()); + if (FAILED(hr)) return hr; + + // Open the file asynchronously. + HSTRING hstr_path; + hr = (*s_WindowsCreateString)(path.c_str(), path.size(), &hstr_path); + if (FAILED(hr)) return hr; + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFile*>> fileOpenAsync; + hr = fileFactory->GetFileFromPathAsync(hstr_path, fileOpenAsync.GetAddressOf()); + if (FAILED(hr)) return hr; + (*s_WindowsDeleteString)(hstr_path); + + // Wait until the file gets open, get the actual file. + AsyncStatus status = winrt_async_await(fileOpenAsync); + Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFile> storageFile; + if (status == AsyncStatus::Completed) { + hr = fileOpenAsync->GetResults(storageFile.GetAddressOf()); + } else { + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo; + hr = fileOpenAsync.As(&asyncInfo); + if (FAILED(hr)) return hr; + HRESULT err; + hr = asyncInfo->get_ErrorCode(&err); + return FAILED(hr) ? hr : err; + } + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> fileStreamAsync; + hr = storageFile->OpenAsync(mode, fileStreamAsync.GetAddressOf()); + if (FAILED(hr)) return hr; + + status = winrt_async_await(fileStreamAsync); + if (status == AsyncStatus::Completed) { + hr = fileStreamAsync->GetResults(fileStream); + } else { + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo; + hr = fileStreamAsync.As(&asyncInfo); + if (FAILED(hr)) return hr; + HRESULT err; + hr = asyncInfo->get_ErrorCode(&err); + if (!FAILED(hr)) + hr = err; + } + return hr; +} + +bool is_windows10() +{ + HKEY hKey; + LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey); + if (lRes == ERROR_SUCCESS) { + WCHAR szBuffer[512]; + DWORD dwBufferSize = sizeof(szBuffer); + lRes = RegQueryValueExW(hKey, L"ProductName", 0, nullptr, (LPBYTE)szBuffer, &dwBufferSize); + if (lRes == ERROR_SUCCESS) + return wcsncmp(szBuffer, L"Windows 10", 10) == 0; + RegCloseKey(hKey); + } + return false; +} + +bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst) +{ + if (! is_windows10()) { + return false; + } + + if (! winrt_load_runtime_object_library()) { + printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n"); + return -1; + } + + (*s_RoInitialize)(RO_INIT_MULTITHREADED); + { + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> fileStream; + HRESULT hr = winrt_open_file_stream(L"D:\\3dprint\\bug.3mf", ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf()); + + Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3D3MFPackage> printing3d3mfpackage; + hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf()); + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Graphics::Printing3D::Printing3DModel*>> modelAsync; + hr = printing3d3mfpackage->LoadModelFromPackageAsync(fileStream.Get(), modelAsync.GetAddressOf()); + + AsyncStatus status = winrt_async_await(modelAsync); + Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3DModel> model; + if (status == AsyncStatus::Completed) { + hr = modelAsync->GetResults(model.GetAddressOf()); + } else { + printf("Failed loading the input model. Exiting.\n"); + return -1; + } + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> meshes; + hr = model->get_Meshes(meshes.GetAddressOf()); + unsigned num_meshes = 0; + hr = meshes->get_Size(&num_meshes); + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> repairAsync; + hr = model->RepairAsync(repairAsync.GetAddressOf()); + status = winrt_async_await(repairAsync); + if (status != AsyncStatus::Completed) { + printf("Mesh repair failed. Exiting.\n"); + return -1; + } + printf("Mesh repair finished successfully.\n"); + repairAsync->GetResults(); + + // Verify the number of meshes returned after the repair action. + meshes.Reset(); + hr = model->get_Meshes(meshes.GetAddressOf()); + hr = meshes->get_Size(&num_meshes); + + // Save model to this class' Printing3D3MFPackage. + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> 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 -1; + } + hr = saveToPackageAsync->GetResults(); + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> 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 -1; + } + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> generatorStream; + hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf()); + + // Go to the beginning of the stream. + generatorStream->Seek(0); + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IInputStream> inputStream; + hr = generatorStream.As(&inputStream); + + // Get the buffer factory. + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory; + hr = winrt_get_activation_factory(L"Windows.Storage.Streams.Buffer", bufferFactory.GetAddressOf()); + + // Open the destination file. + FILE *fout = ::fopen("D:\\3dprint\\bug-repaired.3mf", "wb"); + + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; + byte *buffer_ptr; + bufferFactory->Create(65536 * 2048, buffer.GetAddressOf()); + { + Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess; + buffer.As(&bufferByteAccess); + hr = bufferByteAccess->Buffer(&buffer_ptr); + } + uint32_t length; + hr = buffer->get_Length(&length); + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Storage::Streams::IBuffer*, UINT32>> 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 -1; + } + hr = buffer->get_Length(&length); + if (length == 0) + break; + fwrite(buffer_ptr, length, 1, fout); + } + fclose(fout); + // Here all the COM objects will be released through the ComPtr destructors. + } + (*s_RoUninitialize)(); + return 0; +} + +#endif /* HAS_WIN10SDK */ diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp new file mode 100644 index 000000000..268b614ef --- /dev/null +++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp @@ -0,0 +1,18 @@ +#ifndef slic3r_GUI_Utils_FixModelByWin10_hpp_ +#define slic3r_GUI_Utils_FixModelByWin10_hpp_ + +#include <string> + +#ifdef HAS_WIN10SDK + +extern bool is_windows10(); +extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst); + +#else /* HAS_WIN10SDK */ + +inline bool is_windows10() { return false; } +inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; } + +#endif /* HAS_WIN10SDK * + +#endif /* slic3r_GUI_Utils_FixModelByWin10_hpp_ */ From d05d3cb652de8dfc156b62e940aaf8a7422dee20 Mon Sep 17 00:00:00 2001 From: bubnikv <bubnikv@gmail.com> Date: Mon, 4 Jun 2018 21:22:42 +0200 Subject: [PATCH 2/3] Initial working implementation of the "Fix by Netfabb" function. --- CMakeLists.txt | 16 ++++++ lib/Slic3r/GUI/Plater.pm | 33 ++++++++++++ xs/CMakeLists.txt | 7 +++ xs/src/slic3r/Utils/FixModelByWin10.cpp | 72 +++++++++++++++++++++---- xs/src/slic3r/Utils/FixModelByWin10.hpp | 12 ++++- xs/xsp/GUI.xsp | 7 +++ 6 files changed, 136 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e8b2a6faa..60d67553b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,22 @@ if(NOT DEFINED CMAKE_PREFIX_PATH) endif() endif() +# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory. +# We pick it from environment if it is not defined in another way +if(WIN32) + if(NOT DEFINED WIN10SDK_PATH) + if(DEFINED ENV{WIN10SDK_PATH}) + set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}") + endif() + endif() + if(DEFINED WIN10SDK_PATH AND NOT EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h") + message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}") + message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found") + message("STL fixing by the Netfabb service will not be compiled") + unset(WIN10SDK_PATH) + endif() +endif() + add_subdirectory(xs) get_filename_component(PERL_BIN_PATH "${PERL_EXECUTABLE}" DIRECTORY) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d27be785c..40c2a1351 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1620,6 +1620,34 @@ sub export_object_stl { $self->statusbar->SetStatusText(L("STL file exported to ").$output_file); } +sub fix_through_netfabb { + my ($self) = @_; + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_fixed = Slic3r::Model->new; + Slic3r::GUI::fix_model_by_win10_sdk_gui($model_object, $self->{print}, $model_fixed); + + my @new_obj_idx = $self->load_model_objects(@{$model_fixed->objects}); + return if !@new_obj_idx; + + foreach my $new_obj_idx (@new_obj_idx) { + my $o = $self->{model}->objects->[$new_obj_idx]; + $o->clear_instances; + $o->add_instance($_) for @{$model_object->instances}; + #$o->invalidate_bounding_box; + + if ($o->volumes_count == $model_object->volumes_count) { + for my $i (0..($o->volumes_count-1)) { + $o->get_volume($i)->config->apply($model_object->get_volume($i)->config); + } + } + #FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid, + } + + $self->remove($obj_idx); +} + sub export_amf { my ($self) = @_; return if !@{$self->{objects}}; @@ -2178,6 +2206,11 @@ sub object_menu { $frame->_append_menu_item($menu, L("Export object as STL…"), L('Export this single object as STL file'), sub { $self->export_object_stl; }, undef, 'brick_go.png'); + if (Slic3r::GUI::is_windows10) { + $frame->_append_menu_item($menu, L("Fix STL through Netfabb"), L('Fix the model by sending it to a Netfabb cloud service through Windows 10 API'), sub { + $self->fix_through_netfabb; + }, undef, 'brick_go.png'); + } return $menu; } diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 64f4b9274..866374584 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -27,6 +27,13 @@ if(WIN32) # BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking. add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB) # -D_ITERATOR_DEBUG_LEVEL) + if(WIN10SDK_PATH) + message("Building with Win10 Netfabb STL fixing service support") + add_definitions(-DHAS_WIN10SDK) + include_directories("${WIN10SDK_PATH}/Include") + else() + message("Building without Win10 Netfabb STL fixing service support") + endif() endif() add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE) diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp index e4b1952a4..27a79f84e 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.cpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp @@ -1,10 +1,15 @@ #ifdef HAS_WIN10SDK +#ifndef NOMINMAX +# define NOMINMAX +#endif + #include "FixModelByWin10.hpp" #include <roapi.h> #include <string> #include <cstdint> +#include <thread> // for ComPtr #include <wrl/client.h> // from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ @@ -12,6 +17,18 @@ #include <winrt/windows.storage.provider.h> #include <winrt/windows.graphics.printing3d.h> +#include <boost/filesystem.hpp> +#include <boost/nowide/convert.hpp> +#include <boost/nowide/cstdio.hpp> + +#include "libslic3r/Model.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/Format/3mf.hpp" +#include "../GUI/GUI.hpp" +#include "../GUI/PresetBundle.hpp" + +#include <wx/progdlg.h> + extern "C"{ // from rapi.h typedef HRESULT (__stdcall* FunctionRoInitialize)(int); @@ -23,6 +40,8 @@ extern "C"{ typedef HRESULT (__stdcall* FunctionWindowsDelteString)(HSTRING string); } +namespace Slic3r { + HMODULE s_hRuntimeObjectLibrary = nullptr; FunctionRoInitialize s_RoInitialize = nullptr; FunctionRoUninitialize s_RoUninitialize = nullptr; @@ -178,13 +197,13 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path if (! winrt_load_runtime_object_library()) { printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n"); - return -1; + return false; } - (*s_RoInitialize)(RO_INIT_MULTITHREADED); + HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED); { Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> fileStream; - HRESULT hr = winrt_open_file_stream(L"D:\\3dprint\\bug.3mf", 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()); Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3D3MFPackage> printing3d3mfpackage; hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf()); @@ -198,7 +217,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path hr = modelAsync->GetResults(model.GetAddressOf()); } else { printf("Failed loading the input model. Exiting.\n"); - return -1; + return false; } Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> meshes; @@ -211,7 +230,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(repairAsync); if (status != AsyncStatus::Completed) { printf("Mesh repair failed. Exiting.\n"); - return -1; + return false; } printf("Mesh repair finished successfully.\n"); repairAsync->GetResults(); @@ -227,7 +246,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(saveToPackageAsync); if (status != AsyncStatus::Completed) { printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return -1; + return false; } hr = saveToPackageAsync->GetResults(); @@ -236,7 +255,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(generatorStreamAsync); if (status != AsyncStatus::Completed) { printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return -1; + return false; } Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> generatorStream; hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf()); @@ -251,7 +270,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path hr = winrt_get_activation_factory(L"Windows.Storage.Streams.Buffer", bufferFactory.GetAddressOf()); // Open the destination file. - FILE *fout = ::fopen("D:\\3dprint\\bug-repaired.3mf", "wb"); + FILE *fout = boost::nowide::fopen(path_dst.c_str(), "wb"); Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; byte *buffer_ptr; @@ -270,7 +289,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(asyncRead); if (status != AsyncStatus::Completed) { printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return -1; + return false; } hr = buffer->get_Length(&length); if (length == 0) @@ -281,7 +300,40 @@ 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 0; + return true; } +void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result) +{ + enum { PROGRESS_RANGE = 1000 }; + wxProgressDialog progress_dialog( + _(L("Model fixing")), + _(L("Exporting model...")), + PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); + progress_dialog.Pulse(); + + // 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*>(&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); + }); + worker.join(); +} + +} // namespace Slic3r + #endif /* HAS_WIN10SDK */ diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp index 268b614ef..299c9b75b 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.hpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp @@ -3,16 +3,26 @@ #include <string> +namespace Slic3r { + +class Model; +class ModelObject; +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 * +#endif /* HAS_WIN10SDK */ + +} // namespace Slic3r #endif /* slic3r_GUI_Utils_FixModelByWin10_hpp_ */ diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index 897e63693..1d860d284 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -4,6 +4,7 @@ #include <xsinit.h> #include "slic3r/GUI/GUI.hpp" #include "slic3r/Utils/ASCIIFolding.hpp" +#include "slic3r/Utils/FixModelByWin10.hpp" #include "slic3r/Utils/Serial.hpp" %} @@ -28,6 +29,9 @@ bool debugged() void break_to_debugger() %code{% Slic3r::GUI::break_to_debugger(); %}; +bool is_windows10() + %code{% RETVAL=Slic3r::is_windows10(); %}; + void set_wxapp(SV *ui) %code%{ Slic3r::GUI::set_wxapp((wxApp*)wxPli_sv_2_object(aTHX_ ui, "Wx::App")); %}; @@ -98,3 +102,6 @@ int get_export_option(SV *ui) void desktop_open_datadir_folder() %code%{ Slic3r::GUI::desktop_open_datadir_folder(); %}; + +void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst) + %code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %}; From ce6a23ef3bc0e0caefdc96efc002a9a98339d50f Mon Sep 17 00:00:00 2001 From: bubnikv <bubnikv@gmail.com> Date: Wed, 6 Jun 2018 15:19:06 +0200 Subject: [PATCH 3/3] Repair by the netfabb service: Implemented progress dialog and cancelation. --- xs/src/slic3r/Utils/FixModelByWin10.cpp | 201 ++++++++++++++++-------- xs/src/slic3r/Utils/FixModelByWin10.hpp | 2 - 2 files changed, 132 insertions(+), 71 deletions(-) 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 <roapi.h> -#include <string> +#include <atomic> +#include <chrono> #include <cstdint> +#include <condition_variable> +#include <exception> +#include <string> #include <thread> + +#include <boost/filesystem.hpp> +#include <boost/nowide/convert.hpp> +#include <boost/nowide/cstdio.hpp> + +#include <roapi.h> // for ComPtr #include <wrl/client.h> // from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ @@ -17,16 +26,13 @@ #include <winrt/windows.storage.provider.h> #include <winrt/windows.graphics.printing3d.h> -#include <boost/filesystem.hpp> -#include <boost/nowide/convert.hpp> -#include <boost/nowide/cstdio.hpp> - #include "libslic3r/Model.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/Format/3mf.hpp" #include "../GUI/GUI.hpp" #include "../GUI/PresetBundle.hpp" +#include <wx/msgdlg.h> #include <wx/progdlg.h> 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<void**>(pinst)); } +// To be called often to test whether to cancel the operation. +typedef std::function<void ()> ThrowOnCancelFn; + template<typename T> -static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncAction, int blocking_tick_ms = 300) +static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncAction, ThrowOnCancelFn throw_on_cancel, int blocking_tick_ms = 100) { Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo; asyncAction.As(&asyncInfo); @@ -118,6 +127,7 @@ static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &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<T> &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<ABI::Windows::Storage::IStorageFileStatics> 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<ABI::Windows::Storage::IStorageFile> 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<void (const char * /* message */, unsigned /* progress */)> 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<ABI::Windows::Storage::Streams::IRandomAccessStream> 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<ABI::Windows::Graphics::Printing3D::IPrinting3D3MFPackage> 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<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Graphics::Printing3D::Printing3DModel*>> 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<ABI::Windows::Graphics::Printing3D::IPrinting3DModel> 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<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> 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<ABI::Windows::Foundation::IAsyncAction> 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<ABI::Windows::Foundation::IAsyncAction> 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<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> 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<ABI::Windows::Storage::Streams::IRandomAccessStream> 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<ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Storage::Streams::IBuffer*, UINT32>> 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<std::mutex> lock(mutex); + struct Progress { + std::string message; + int percent = 0; + bool updated = false; + } progress; + std::atomic<bool> canceled = false; + std::atomic<bool> 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*>(&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<std::mutex> 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*>(&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 */