diff --git a/CMakeLists.txt b/CMakeLists.txt index 520b913c1..18a3be3a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,7 +152,7 @@ if(SLIC3R_STATIC) # set(Boost_USE_STATIC_RUNTIME ON) endif() #set(Boost_DEBUG ON) -set(Boost_COMPILER "-vc120") +# set(Boost_COMPILER "-vc120") find_package(Boost REQUIRED COMPONENTS system filesystem thread log locale regex) if(Boost_FOUND) include_directories(${Boost_INCLUDE_DIRS}) diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt index 5905c438e..0e1f52d6c 100644 --- a/sandboxes/CMakeLists.txt +++ b/sandboxes/CMakeLists.txt @@ -1,2 +1 @@ add_subdirectory(slabasebed) -add_subdirectory(slasupporttree) diff --git a/sandboxes/slasupporttree/CMakeLists.txt b/sandboxes/slasupporttree/CMakeLists.txt deleted file mode 100644 index a3323a83d..000000000 --- a/sandboxes/slasupporttree/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_executable(slasupporttree EXCLUDE_FROM_ALL slasupporttree.cpp) -target_link_libraries(slasupporttree libslic3r) \ No newline at end of file diff --git a/sandboxes/slasupporttree/slasupporttree.cpp b/sandboxes/slasupporttree/slasupporttree.cpp deleted file mode 100644 index 0cfc60f7a..000000000 --- a/sandboxes/slasupporttree/slasupporttree.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include - -#include -#include "TriangleMesh.hpp" -#include "Model.hpp" -#include "callback.hpp" -#include "SLA/SLASupportTree.hpp" -#include "benchmark.h" - -const std::string USAGE_STR = { - "Usage: slasupporttree stlfilename.stl" -}; - -void confess_at(const char * /*file*/, - int /*line*/, - const char * /*func*/, - const char * /*pat*/, - ...) {} - -namespace Slic3r { - -void PerlCallback::deregister_callback() {} -} - -int main(const int argc, const char *argv[]) { - using namespace Slic3r; - using std::cout; using std::endl; - - if(argc < 2) { - cout << USAGE_STR << endl; - return EXIT_SUCCESS; - } - - Benchmark bench; - TriangleMesh result; - - bench.start(); - sla::create_head(result, 3, 1, 4); - bench.stop(); - - cout << "Support tree creation time: " << std::setprecision(10) - << bench.getElapsedSec() << " seconds." << endl; - - result.write_ascii("out.stl"); - - return EXIT_SUCCESS; -} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 32d2c5b03..21ee023d1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -80,7 +80,7 @@ elseif (MSVC) # Manifest is provided through slic3r.rc, don't generate your own. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") else () - target_link_libraries(slic3r -ldl -lstdc++) + target_link_libraries(slic3r ${CMAKE_DL_LIBS} -lstdc++) endif () # Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries. diff --git a/src/avrdude/CMakeLists.txt b/src/avrdude/CMakeLists.txt index d88563368..ad0835ec0 100644 --- a/src/avrdude/CMakeLists.txt +++ b/src/avrdude/CMakeLists.txt @@ -66,7 +66,7 @@ set(AVRDUDE_SOURCES avrdude-slic3r.hpp avrdude-slic3r.cpp ) -if (WIN32) +if (MSVC) set(AVRDUDE_SOURCES ${AVRDUDE_SOURCES} windows/unistd.cpp windows/getopt.c diff --git a/src/avrdude/main.c b/src/avrdude/main.c index ebda0ba19..9ada27be3 100644 --- a/src/avrdude/main.c +++ b/src/avrdude/main.c @@ -43,7 +43,7 @@ #include #include -#if !defined(WIN32NATIVE) +#if !defined(WIN32NATIVE) || defined(__GNUC__) # include # include # include diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index 84ed89a7c..13f9e7dd7 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -126,8 +126,16 @@ private: static bool is_end_of_line(char c) { return c == '\r' || c == '\n' || c == 0; } static bool is_end_of_gcode_line(char c) { return c == ';' || is_end_of_line(c); } static bool is_end_of_word(char c) { return is_whitespace(c) || is_end_of_gcode_line(c); } - static const char* skip_whitespaces(const char *c) { for (; is_whitespace(*c); ++ c); return c; } - static const char* skip_word(const char *c) { for (; ! is_end_of_word(*c); ++ c); return c; } + static const char* skip_whitespaces(const char *c) { + for (; is_whitespace(*c); ++ c) + ; // silence -Wempty-body + return c; + } + static const char* skip_word(const char *c) { + for (; ! is_end_of_word(*c); ++ c) + ; // silence -Wempty-body + return c; + } GCodeConfig m_config; char m_extrusion_axis; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index e9e3b395f..03fd1bd97 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1513,4 +1513,52 @@ Transform3d ModelInstance::get_matrix(bool dont_translate, bool dont_rotate, boo } #endif // !ENABLE_MODELVOLUME_TRANSFORM +#ifdef _DEBUG +// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. +void check_model_ids_validity(const Model &model) +{ + std::set ids; + auto check = [&ids](ModelID id) { + assert(id.id > 0); + assert(ids.find(id) == ids.end()); + ids.insert(id); + }; + for (const ModelObject *model_object : model.objects) { + check(model_object->id()); + for (const ModelVolume *model_volume : model_object->volumes) + check(model_volume->id()); + for (const ModelInstance *model_instance : model_object->instances) + check(model_instance->id()); + } + for (const auto mm : model.materials) + check(mm.second->id()); +} + +void check_model_ids_equal(const Model &model1, const Model &model2) +{ + // Verify whether the IDs of model1 and model match. + assert(model1.objects.size() == model2.objects.size()); + for (size_t idx_model = 0; idx_model < model2.objects.size(); ++ idx_model) { + const ModelObject &model_object1 = *model1.objects[idx_model]; + const ModelObject &model_object2 = * model2.objects[idx_model]; + assert(model_object1.id() == model_object2.id()); + assert(model_object1.volumes.size() == model_object2.volumes.size()); + assert(model_object1.instances.size() == model_object2.instances.size()); + for (size_t i = 0; i < model_object1.volumes.size(); ++ i) + assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id()); + for (size_t i = 0; i < model_object1.instances.size(); ++ i) + assert(model_object1.instances[i]->id() == model_object2.instances[i]->id()); + } + assert(model1.materials.size() == model2.materials.size()); + { + auto it1 = model1.materials.begin(); + auto it2 = model2.materials.begin(); + for (; it1 != model1.materials.end(); ++ it1, ++ it2) { + assert(it1->first == it2->first); // compare keys + assert(it1->second->id() == it2->second->id()); + } + } +} +#endif /* _DEBUG */ + } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 8e3ee3f30..bf6656cfd 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -652,6 +652,12 @@ private: #undef MODELBASE_DERIVED_COPY_MOVE_CLONE #undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE +#ifdef _DEBUG +// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. +void check_model_ids_validity(const Model &model); +void check_model_ids_equal(const Model &model1, const Model &model2); +#endif /* _DEBUG */ + } #endif diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index c661da5ca..4e1685e45 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -27,7 +27,7 @@ template class PrintState; void Print::clear() { - tbb::mutex::scoped_lock lock(this->cancel_mutex()); + tbb::mutex::scoped_lock lock(this->state_mutex()); // The following call should stop background processing if it is running. this->invalidate_all_steps(); for (PrintObject *object : m_objects) @@ -43,7 +43,7 @@ void Print::reload_object(size_t /* idx */) { ModelObjectPtrs model_objects; { - tbb::mutex::scoped_lock lock(this->cancel_mutex()); + tbb::mutex::scoped_lock lock(this->state_mutex()); // The following call should stop background processing if it is running. this->invalidate_all_steps(); /* TODO: this method should check whether the per-object config and per-material configs @@ -271,8 +271,9 @@ bool Print::is_step_done(PrintObjectStep step) const { if (m_objects.empty()) return false; + tbb::mutex::scoped_lock lock(this->state_mutex()); for (const PrintObject *object : m_objects) - if (!object->m_state.is_done(step)) + if (! object->m_state.is_done_unguarded(step)) return false; return true; } @@ -374,7 +375,7 @@ static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig // and have explicit instance positions. void Print::add_model_object(ModelObject* model_object, int idx) { - tbb::mutex::scoped_lock lock(this->cancel_mutex()); + tbb::mutex::scoped_lock lock(this->state_mutex()); // Initialize a new print object and store it at the given position. PrintObject *object = new PrintObject(this, model_object); if (idx != -1) { @@ -435,7 +436,7 @@ void Print::add_model_object(ModelObject* model_object, int idx) bool Print::apply_config(DynamicPrintConfig config) { - tbb::mutex::scoped_lock lock(this->cancel_mutex()); + tbb::mutex::scoped_lock lock(this->state_mutex()); // we get a copy of the config object so we can modify it safely config.normalize(); @@ -734,54 +735,6 @@ static std::vector print_objects_from_model_object(const ModelOb return std::vector(trafos.begin(), trafos.end()); } -#ifdef _DEBUG -// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. -static inline void check_model_ids_validity(const Model &model) -{ - std::set ids; - auto check = [&ids](ModelID id) { - assert(id.id > 0); - assert(ids.find(id) == ids.end()); - ids.insert(id); - }; - for (const ModelObject *model_object : model.objects) { - check(model_object->id()); - for (const ModelVolume *model_volume : model_object->volumes) - check(model_volume->id()); - for (const ModelInstance *model_instance : model_object->instances) - check(model_instance->id()); - } - for (const auto mm : model.materials) - check(mm.second->id()); -} - -static inline void check_model_ids_equal(const Model &model1, const Model &model2) -{ - // Verify whether the IDs of model1 and model match. - assert(model1.objects.size() == model2.objects.size()); - for (size_t idx_model = 0; idx_model < model2.objects.size(); ++ idx_model) { - const ModelObject &model_object1 = *model1.objects[idx_model]; - const ModelObject &model_object2 = * model2.objects[idx_model]; - assert(model_object1.id() == model_object2.id()); - assert(model_object1.volumes.size() == model_object2.volumes.size()); - assert(model_object1.instances.size() == model_object2.instances.size()); - for (size_t i = 0; i < model_object1.volumes.size(); ++ i) - assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id()); - for (size_t i = 0; i < model_object1.instances.size(); ++ i) - assert(model_object1.instances[i]->id() == model_object2.instances[i]->id()); - } - assert(model1.materials.size() == model2.materials.size()); - { - auto it1 = model1.materials.begin(); - auto it2 = model2.materials.begin(); - for (; it1 != model1.materials.end(); ++ it1, ++ it2) { - assert(it1->first == it2->first); // compare keys - assert(it1->second->id() == it2->second->id()); - } - } -} -#endif /* _DEBUG */ - Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in) { #ifdef _DEBUG @@ -804,7 +757,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co update_apply_status(false); // Grab the lock for the Print / PrintObject milestones. - tbb::mutex::scoped_lock lock(this->cancel_mutex()); + tbb::mutex::scoped_lock lock(this->state_mutex()); // The following call may stop the background processing. update_apply_status(this->invalidate_state_by_config_options(print_diff)); @@ -1579,16 +1532,12 @@ void Print::process() BOOST_LOG_TRIVIAL(info) << "Staring the slicing process."; for (PrintObject *obj : m_objects) obj->make_perimeters(); - this->throw_if_canceled(); this->set_status(70, "Infilling layers"); for (PrintObject *obj : m_objects) obj->infill(); - this->throw_if_canceled(); for (PrintObject *obj : m_objects) obj->generate_support_material(); - this->throw_if_canceled(); - if (! this->is_step_done(psSkirt)) { - this->set_started(psSkirt); + if (this->set_started(psSkirt)) { m_skirt.clear(); if (this->has_skirt()) { this->set_status(88, "Generating skirt"); @@ -1596,9 +1545,7 @@ void Print::process() } this->set_done(psSkirt); } - this->throw_if_canceled(); - if (! this->is_step_done(psBrim)) { - this->set_started(psBrim); + if (this->set_started(psBrim)) { m_brim.clear(); if (m_config.brim_width > 0) { this->set_status(88, "Generating brim"); @@ -1606,9 +1553,7 @@ void Print::process() } this->set_done(psBrim); } - this->throw_if_canceled(); - if (! this->is_step_done(psWipeTower)) { - this->set_started(psWipeTower); + if (this->set_started(psWipeTower)) { m_wipe_tower_data.clear(); if (this->has_wipe_tower()) { //this->set_status(95, "Generating wipe tower"); @@ -1625,9 +1570,6 @@ void Print::process() // It is up to the caller to show an error message. void Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data) { - // prerequisites - this->process(); - // output everything to a G-code file // The following call may die if the output_filename_format template substitution fails. std::string path = this->output_filepath(path_template); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index b2f9f9501..0f6c0afbd 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -97,8 +97,6 @@ public: Vec3crd size; // XYZ in scaled coordinates - const ModelObject* model_object() const { return m_model_object; } - ModelObject* model_object() { return m_model_object; } const PrintObjectConfig& config() const { return m_config; } const LayerPtrs& layers() const { return m_layers; } const SupportLayerPtrs& support_layers() const { return m_support_layers; } @@ -197,7 +195,6 @@ private: void combine_infill(); void _generate_support_material(); - ModelObject *m_model_object; PrintObjectConfig m_config; // Translation in Z + Rotation + Scaling / Mirroring. Transform3d m_trafo = Transform3d::Identity(); @@ -381,7 +378,6 @@ private: // Declared here to have access to Model / ModelObject / ModelInstance static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src); - Model m_model; PrintConfig m_config; PrintObjectConfig m_default_object_config; PrintRegionConfig m_default_region_config; diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index d4fffca94..a44f42fbd 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -3,9 +3,11 @@ namespace Slic3r { -tbb::mutex& PrintObjectBase::cancel_mutex(PrintBase *print) +size_t PrintStateBase::g_last_timestamp = 0; + +tbb::mutex& PrintObjectBase::state_mutex(PrintBase *print) { - return print->cancel_mutex(); + return print->state_mutex(); } std::function PrintObjectBase::cancel_callback(PrintBase *print) diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 8e9a6693a..dc3fb64e9 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -2,13 +2,11 @@ #define slic3r_PrintBase_hpp_ #include "libslic3r.h" -#include #include #include #include #include -#include "tbb/atomic.h" // tbb/mutex.h includes Windows, which in turn defines min/max macros. Convince Windows.h to not define these min/max macros. #ifndef NOMINMAX #define NOMINMAX @@ -25,68 +23,120 @@ public: const char* what() const throw() { return "Background processing has been canceled"; } }; -// To be instantiated over PrintStep or PrintObjectStep enums. -template -class PrintState -{ +class PrintStateBase { public: - PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i].store(INVALID, std::memory_order_relaxed); } - enum State { INVALID, STARTED, DONE, }; - - // With full memory barrier. - bool is_done(StepType step) const { return m_state[step] == DONE; } + + typedef size_t TimeStamp; + + // A new unique timestamp is being assigned to the step every time the step changes its state. + struct StateWithTimeStamp + { + StateWithTimeStamp() : state(INVALID), timestamp(0) {} + State state; + TimeStamp timestamp; + }; + +protected: + //FIXME last timestamp is shared between Print & SLAPrint, + // and if multiple Print or SLAPrint instances are executed in parallel, modification of g_last_timestamp + // is not synchronized! + static size_t g_last_timestamp; +}; + +// To be instantiated over PrintStep or PrintObjectStep enums. +template +class PrintState : public PrintStateBase +{ +public: + PrintState() {} + + StateWithTimeStamp state_with_timestamp(StepType step, tbb::mutex &mtx) const { + tbb::mutex::scoped_lock lock(mtx); + StateWithTimeStamp state = m_state[step]; + return state; + } + + bool is_done(StepType step, tbb::mutex &mtx) const { + return this->state_with_timestamp(step, mtx).state == DONE; + } + + StateWithTimeStamp state_with_timestamp_unguarded(StepType step) const { + return m_state[step]; + } + + bool is_done_unguarded(StepType step) const { + return this->state_with_timestamp_unguarded(step).state == DONE; + } // Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being // modified by the UI thread. // This is necessary to block until the Print::apply_config() updates its state, which may // influence the processing step being entered. - void set_started(StepType step, tbb::mutex &mtx) { - mtx.lock(); - m_state[step].store(STARTED, std::memory_order_relaxed); - mtx.unlock(); + template + bool set_started(StepType step, tbb::mutex &mtx, ThrowIfCanceled throw_if_canceled) { + tbb::mutex::scoped_lock lock(mtx); + // If canceled, throw before changing the step state. + throw_if_canceled(); + if (m_state[step].state == DONE) + return false; + m_state[step].state = STARTED; + m_state[step].timestamp = ++ g_last_timestamp; + return true; } // Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being // modified by the UI thread. - void set_done(StepType step, tbb::mutex &mtx) { - mtx.lock(); - m_state[step].store(DONE, std::memory_order_relaxed); - mtx.unlock(); + template + TimeStamp set_done(StepType step, tbb::mutex &mtx, ThrowIfCanceled throw_if_canceled) { + tbb::mutex::scoped_lock lock(mtx); + // If canceled, throw before changing the step state. + throw_if_canceled(); + assert(m_state[step].state != DONE); + m_state[step].state = DONE; + m_state[step].timestamp = ++ g_last_timestamp; + return m_state[step].timestamp; } // Make the step invalid. - // The provided mutex should be locked at this point, guarding access to m_state. + // PrintBase::m_state_mutex should be locked at this point, guarding access to m_state. // In case the step has already been entered or finished, cancel the background // processing by calling the cancel callback. template - bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback cancel) { - bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID; + bool invalidate(StepType step, CancelationCallback cancel) { + bool invalidated = m_state[step].state != INVALID; if (invalidated) { #if 0 if (mtx.state != mtx.HELD) { printf("Not held!\n"); } #endif + m_state[step].state = INVALID; + m_state[step].timestamp = ++ g_last_timestamp; // Raise the mutex, so that the following cancel() callback could cancel // the background processing. - mtx.unlock(); + // Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let + // the working thread to proceed. cancel(); - m_state[step] = INVALID; - mtx.lock(); } return invalidated; } template - bool invalidate_multiple(StepTypeIterator step_begin, StepTypeIterator step_end, tbb::mutex &mtx, CancelationCallback cancel) { + bool invalidate_multiple(StepTypeIterator step_begin, StepTypeIterator step_end, CancelationCallback cancel) { bool invalidated = false; - for (StepTypeIterator it = step_begin; ! invalidated && it != step_end; ++ it) - invalidated = m_state[*it].load(std::memory_order_relaxed) != INVALID; + for (StepTypeIterator it = step_begin; it != step_end; ++ it) { + StateWithTimeStamp &state = m_state[*it]; + if (state.state != INVALID) { + invalidated = true; + state.state = INVALID; + state.timestamp = ++ g_last_timestamp; + } + } if (invalidated) { #if 0 if (mtx.state != mtx.HELD) { @@ -95,50 +145,53 @@ public: #endif // Raise the mutex, so that the following cancel() callback could cancel // the background processing. - mtx.unlock(); + // Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let + // the working thread to proceed. cancel(); - for (StepTypeIterator it = step_begin; it != step_end; ++ it) - m_state[*it] = INVALID; - mtx.lock(); } return invalidated; } // Make all steps invalid. - // The provided mutex should be locked at this point, guarding access to m_state. + // PrintBase::m_state_mutex should be locked at this point, guarding access to m_state. // In case any step has already been entered or finished, cancel the background // processing by calling the cancel callback. template - bool invalidate_all(tbb::mutex &mtx, CancelationCallback cancel) { + bool invalidate_all(CancelationCallback cancel) { bool invalidated = false; - for (size_t i = 0; i < COUNT; ++ i) - if (m_state[i].load(std::memory_order_relaxed) != INVALID) { + for (size_t i = 0; i < COUNT; ++ i) { + StateWithTimeStamp &state = m_state[i]; + if (state.state != INVALID) { invalidated = true; - break; + state.state = INVALID; + state.timestamp = ++ g_last_timestamp; } - if (invalidated) { - mtx.unlock(); - cancel(); - for (size_t i = 0; i < COUNT; ++ i) - m_state[i].store(INVALID, std::memory_order_relaxed); - mtx.lock(); } + if (invalidated) + cancel(); return invalidated; } private: - std::atomic m_state[COUNT]; + StateWithTimeStamp m_state[COUNT]; }; class PrintBase; class PrintObjectBase { +public: + const ModelObject* model_object() const { return m_model_object; } + ModelObject* model_object() { return m_model_object; } + protected: + PrintObjectBase(ModelObject *model_object) : m_model_object(model_object) {} virtual ~PrintObjectBase() {} // Declared here to allow access from PrintBase through friendship. - static tbb::mutex& cancel_mutex(PrintBase *print); + static tbb::mutex& state_mutex(PrintBase *print); static std::function cancel_callback(PrintBase *print); + + ModelObject *m_model_object; }; /** @@ -179,19 +232,31 @@ public: APPLY_STATUS_INVALIDATED, }; virtual ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) = 0; + const Model& model() const { return m_model; } virtual void process() = 0; - typedef std::function status_callback_type; + struct SlicingStatus { + SlicingStatus(int percent, const std::string &text, unsigned int flags = 0) : percent(percent), text(text), flags(flags) {} + int percent; + std::string text; + // Bitmap of flags. + enum FlagBits { + RELOAD_SCENE = 1, + }; + // Bitmap of FlagBits + unsigned int flags; + }; + typedef std::function status_callback_type; // Default status console print out in the form of percent => message. void set_status_default() { m_status_callback = nullptr; } // No status output or callback whatsoever, useful mostly for automatic tests. - void set_status_silent() { m_status_callback = [](int, const std::string&){}; } + void set_status_silent() { m_status_callback = [](const SlicingStatus&){}; } // Register a custom status callback. void set_status_callback(status_callback_type cb) { m_status_callback = cb; } // Calls a registered callback to update the status, or print out the default message. - void set_status(int percent, const std::string &message) { - if (m_status_callback) m_status_callback(percent, message); + void set_status(int percent, const std::string &message, unsigned int flags = 0) { + if (m_status_callback) m_status_callback(SlicingStatus(percent, message, flags)); else printf("%d => %s\n", percent, message.c_str()); } @@ -220,8 +285,9 @@ public: protected: friend class PrintObjectBase; + friend class BackgroundSlicingProcess; - tbb::mutex& cancel_mutex() { return m_cancel_mutex; } + tbb::mutex& state_mutex() const { return m_state_mutex; } std::function cancel_callback() { return m_cancel_callback; } void call_cancell_callback() { m_cancel_callback(); } @@ -229,6 +295,8 @@ protected: // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly. void throw_if_canceled() const { if (m_cancel_status) throw CanceledException(); } + Model m_model; + private: tbb::atomic m_cancel_status; // Callback to be evoked regularly to update state of the UI thread. @@ -240,27 +308,28 @@ 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. - mutable tbb::mutex m_cancel_mutex; + mutable tbb::mutex m_state_mutex; }; template class PrintBaseWithState : public PrintBase { public: - bool is_step_done(PrintStepEnum step) const { return m_state.is_done(step); } + bool is_step_done(PrintStepEnum step) const { return m_state.is_done(step, this->state_mutex()); } + PrintStateBase::StateWithTimeStamp step_state_with_timestamp(PrintStepEnum step) const { return m_state.state_with_timestamp(step, this->state_mutex()); } protected: - void set_started(PrintStepEnum step) { m_state.set_started(step, this->cancel_mutex()); throw_if_canceled(); } - void set_done(PrintStepEnum step) { m_state.set_done(step, this->cancel_mutex()); throw_if_canceled(); } + bool set_started(PrintStepEnum step) { return m_state.set_started(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); } + PrintStateBase::TimeStamp set_done(PrintStepEnum step) { return m_state.set_done(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); } bool invalidate_step(PrintStepEnum step) - { return m_state.invalidate(step, this->cancel_mutex(), this->cancel_callback()); } + { return m_state.invalidate(step, this->cancel_callback()); } template bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end) - { return m_state.invalidate_multiple(step_begin, step_end, this->cancel_mutex(), this->cancel_callback()); } + { return m_state.invalidate_multiple(step_begin, step_end, this->cancel_callback()); } bool invalidate_steps(std::initializer_list il) - { return m_state.invalidate_multiple(il.begin(), il.end(), this->cancel_mutex(), this->cancel_callback()); } + { return m_state.invalidate_multiple(il.begin(), il.end(), this->cancel_callback()); } bool invalidate_all_steps() - { return m_state.invalidate_all(this->cancel_mutex(), this->cancel_callback()); } + { return m_state.invalidate_all(this->cancel_callback()); } private: PrintState m_state; @@ -273,24 +342,33 @@ public: PrintType* print() { return m_print; } const PrintType* print() const { return m_print; } - bool is_step_done(PrintObjectStepEnum step) const { return m_state.is_done(step); } + typedef PrintState PrintObjectState; + bool is_step_done(PrintObjectStepEnum step) const { return m_state.is_done(step, PrintObjectBase::state_mutex(m_print)); } + PrintStateBase::StateWithTimeStamp step_state_with_timestamp(PrintObjectStepEnum step) const { return m_state.state_with_timestamp(step, PrintObjectBase::state_mutex(m_print)); } protected: - PrintObjectBaseWithState(PrintType *print) : m_print(print) {} + PrintObjectBaseWithState(PrintType *print, ModelObject *model_object) : PrintObjectBase(model_object), m_print(print) {} - void set_started(PrintObjectStepEnum step) { m_state.set_started(step, PrintObjectBase::cancel_mutex(m_print)); } - void set_done(PrintObjectStepEnum step) { m_state.set_done(step, PrintObjectBase::cancel_mutex(m_print)); } + bool set_started(PrintObjectStepEnum step) + { return m_state.set_started(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); }); } + PrintStateBase::TimeStamp set_done(PrintObjectStepEnum step) + { return m_state.set_done(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); }); } bool invalidate_step(PrintObjectStepEnum step) - { return m_state.invalidate(step, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); } + { return m_state.invalidate(step, PrintObjectBase::cancel_callback(m_print)); } template bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end) - { return m_state.invalidate_multiple(step_begin, step_end, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); } + { return m_state.invalidate_multiple(step_begin, step_end, PrintObjectBase::cancel_callback(m_print)); } bool invalidate_steps(std::initializer_list il) - { return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); } - bool invalidate_all_steps() { return m_state.invalidate_all(PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); } + { return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_callback(m_print)); } + bool invalidate_all_steps() + { return m_state.invalidate_all(PrintObjectBase::cancel_callback(m_print)); } protected: + // If the background processing stop was requested, throw CanceledException. + // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly. + void throw_if_canceled() { if (m_print->canceled()) throw CanceledException(); } + friend PrintType; PrintType *m_print; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1e8f26d38..055d7e46a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2515,7 +2515,22 @@ void PrintConfigDef::init_sla_params() def->sidetext = L("mm"); def->cli = ""; def->min = 0; - def->default_value = new ConfigOptionFloat(); + def->default_value = new ConfigOptionFloat(15.0); + + def = this->add("support_object_elevation", coFloat); + def->label = L("Object elevation"); + def->tooltip = L("How much the supports should lift up the supported object."); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(5.0); + + def = this->add("pad_enable", coBool); + def->label = L("Use pad"); + def->tooltip = L("Add a pad underneath the supported model"); + def->sidetext = L(""); + def->cli = ""; + def->default_value = new ConfigOptionBool(true); def = this->add("pad_wall_thickness", coFloat); def->label = L("Pad wall thickness"); @@ -2542,7 +2557,7 @@ void PrintConfigDef::init_sla_params() def->default_value = new ConfigOptionFloat(50.0); def = this->add("pad_edge_radius", coFloat); - def->label = L("pad edge radius"); + def->label = L("Pad edge radius"); def->tooltip = L(""); def->sidetext = L("mm"); def->cli = ""; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 545f6d8be..f1d1c9784 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -31,7 +31,7 @@ enum PrinterTechnology }; enum GCodeFlavor { - gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit, + gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit, gcfSmoothie, gcfNoExtrusion, }; @@ -167,7 +167,7 @@ private: // This definition is constant. extern const PrintConfigDef print_config_def; -// Slic3r dynamic configuration, used to override the configuration +// Slic3r dynamic configuration, used to override the configuration // per object, per modification volume or per printing material. // The dynamic configuration is also used to store user modifications of the print global parameters, // so the modified configuration values may be diffed against the active configuration @@ -274,12 +274,12 @@ protected: m_defaults = defaults; m_keys.clear(); m_keys.reserve(m_map_name_to_offset.size()); - for (const auto &kvp : defs->options) { - // Find the option given the option name kvp.first by an offset from (char*)m_defaults. - ConfigOption *opt = this->optptr(kvp.first, m_defaults); - if (opt == nullptr) - // This option is not defined by the ConfigBase of type T. - continue; + for (const auto &kvp : defs->options) { + // Find the option given the option name kvp.first by an offset from (char*)m_defaults. + ConfigOption *opt = this->optptr(kvp.first, m_defaults); + if (opt == nullptr) + // This option is not defined by the ConfigBase of type T. + continue; m_keys.emplace_back(kvp.first); const ConfigOptionDef *def = defs->get(kvp.first); assert(def != nullptr); @@ -463,7 +463,7 @@ public: ConfigOptionInt top_solid_layers; ConfigOptionFloatOrPercent top_solid_infill_speed; ConfigOptionBool wipe_into_infill; - + protected: void initialize(StaticCacheBase &cache, const char *base_ptr) { @@ -768,7 +768,7 @@ public: ConfigOptionInt pixel_height; ConfigOptionFloat exp_time; ConfigOptionFloat exp_time_first; - + protected: PrintConfig(int) : GCodeConfig(1) {} void initialize(StaticCacheBase &cache, const char *base_ptr) @@ -859,7 +859,7 @@ public: ConfigOptionString printhost_cafile; ConfigOptionString serial_port; ConfigOptionInt serial_speed; - + protected: void initialize(StaticCacheBase &cache, const char *base_ptr) { @@ -873,14 +873,14 @@ protected: }; // This object is mapped to Perl as Slic3r::Config::Full. -class FullPrintConfig : - public PrintObjectConfig, +class FullPrintConfig : + public PrintObjectConfig, public PrintRegionConfig, public PrintConfig, public HostConfig { STATIC_PRINT_CONFIG_CACHE_DERIVED(FullPrintConfig) - FullPrintConfig() : PrintObjectConfig(0), PrintRegionConfig(0), PrintConfig(0), HostConfig(0) { initialize_cache(); *this = s_cache_FullPrintConfig.defaults(); } + FullPrintConfig() : PrintObjectConfig(0), PrintRegionConfig(0), PrintConfig(0), HostConfig(0) { initialize_cache(); *this = s_cache_FullPrintConfig.defaults(); } public: // Validate the FullPrintConfig. Returns an empty string on success, otherwise an error message is returned. @@ -910,14 +910,15 @@ public: // How much the pinhead has to penetrate the model surface ConfigOptionFloat support_head_penetration /*= 0.2*/; - // Radius of the back side of the 3d arrow. + // Radius of the back side of the 3d arrow. TODO: consider renaming this + // to actual pillar radius, because that's what it boils down to. ConfigOptionFloat support_head_back_radius /*= 0.5*/; // Width in mm from the back sphere center to the front sphere center. ConfigOptionFloat support_head_width /*= 1.0*/; // Radius in mm of the support pillars. - // TODO: This parameter is invalid. The pillar radius will be dynamic in + // TODO: This parameter is questionable. The pillar radius will be dynamic in // nature. Merged pillars will have an increased thickness. This parameter // may serve as the maximum radius, or maybe an increase when two are merged // The default radius will be derived from head_back_radius_mm @@ -930,17 +931,18 @@ public: ConfigOptionFloat support_base_height /*= 1.0*/; // The default angle for connecting support sticks and junctions. - ConfigOptionFloat support_critical_angle /*= M_PI/4*/; + ConfigOptionFloat support_critical_angle /*= 45*/; // The max length of a bridge in mm ConfigOptionFloat support_max_bridge_length /*= 15.0*/; // The elevation in Z direction upwards. This is the space between the pad - // and the model object's bounding box bottom. - ConfigOptionFloat support_object_elevation; + // and the model object's bounding box bottom. Units in mm. + ConfigOptionFloat support_object_elevation /*= 5.0*/; - // Now for the base pool (plate) /////////////////////////////////////////// + // Now for the base pool (pad) ///////////////////////////////////////////// + ConfigOptionBool pad_enable; ConfigOptionFloat pad_wall_thickness /*= 2*/; ConfigOptionFloat pad_wall_height /*= 5*/; ConfigOptionFloat pad_max_merge_distance /*= 50*/; @@ -959,6 +961,8 @@ protected: OPT_PTR(support_base_height); OPT_PTR(support_critical_angle); OPT_PTR(support_max_bridge_length); + OPT_PTR(support_object_elevation); + OPT_PTR(pad_enable); OPT_PTR(pad_wall_thickness); OPT_PTR(pad_wall_height); OPT_PTR(pad_max_merge_distance); @@ -1068,7 +1072,7 @@ public: ConfigOptionFloat scale; // ConfigOptionPoint3 scale_to_fit; ConfigOptionBool slice; - + CLIConfig() : ConfigBase(), StaticConfig() { this->set_defaults(); @@ -1076,7 +1080,7 @@ public: // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. const ConfigDef* def() const override { return &cli_config_def; } - t_config_option_keys keys() const override { return cli_config_def.keys(); } + t_config_option_keys keys() const override { return cli_config_def.keys(); } ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override { @@ -1118,7 +1122,7 @@ private: class PrintAndCLIConfigDef : public ConfigDef { public: - PrintAndCLIConfigDef() { + PrintAndCLIConfigDef() { this->options.insert(print_config_def.options.begin(), print_config_def.options.end()); this->options.insert(cli_config_def.options.begin(), cli_config_def.options.end()); } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a71fa194f..371e62ff9 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -35,9 +35,8 @@ namespace Slic3r { PrintObject::PrintObject(Print* print, ModelObject* model_object) : - PrintObjectBaseWithState(print), + PrintObjectBaseWithState(print, model_object), typed_slices(false), - m_model_object(model_object), size(Vec3crd::Zero()), layer_height_profile_valid(false) { @@ -103,9 +102,8 @@ bool PrintObject::set_copies(const Points &points) // this should be idempotent void PrintObject::slice() { - if (this->is_step_done(posSlice)) + if (! this->set_started(posSlice)) return; - this->set_started(posSlice); m_print->set_status(10, "Processing triangulated mesh"); this->_slice(); m_print->throw_if_canceled(); @@ -131,10 +129,9 @@ void PrintObject::make_perimeters() // prerequisites this->slice(); - if (this->is_step_done(posPerimeters)) + if (! this->set_started(posPerimeters)) return; - this->set_started(posPerimeters); m_print->set_status(20, "Generating perimeters"); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; @@ -242,10 +239,9 @@ void PrintObject::make_perimeters() void PrintObject::prepare_infill() { - if (this->is_step_done(posPrepareInfill)) + if (! this->set_started(posPrepareInfill)) return; - this->set_started(posPrepareInfill); m_print->set_status(30, "Preparing infill"); // This will assign a type (top/bottom/internal) to $layerm->slices. @@ -361,8 +357,7 @@ void PrintObject::infill() // prerequisites this->prepare_infill(); - if (! this->is_step_done(posInfill)) { - this->set_started(posInfill); + if (this->set_started(posInfill)) { BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), @@ -384,8 +379,7 @@ void PrintObject::infill() void PrintObject::generate_support_material() { - if (! this->is_step_done(posSupportMaterial)) { - this->set_started(posSupportMaterial); + if (this->set_started(posSupportMaterial)) { this->clear_support_layers(); if ((m_config.support_material || m_config.raft_layers > 0) && m_layers.size() > 1) { m_print->set_status(85, "Generating support material"); @@ -1706,9 +1700,8 @@ void PrintObject::_simplify_slices(double distance) void PrintObject::_make_perimeters() { - if (this->is_step_done(posPerimeters)) + if (! this->set_started(posPerimeters)) return; - this->set_started(posPerimeters); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index 3a92f371e..6ecc63576 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -424,12 +424,6 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h, TriangleMesh m = mesh; TriangleMeshSlicer slicer(&m); -// TriangleMesh upper, lower; -// slicer.cut(h, &upper, &lower); - - // TODO: this might be slow (in fact it was) -// output = lower.horizontal_projection(); - auto bb = mesh.bounding_box(); float gnd = float(bb.min(Z)); std::vector heights = {float(bb.min(Z))}; diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp index e773de29c..132a7aeac 100644 --- a/src/libslic3r/SLA/SLABasePool.hpp +++ b/src/libslic3r/SLA/SLABasePool.hpp @@ -23,6 +23,13 @@ struct PoolConfig { double min_wall_height_mm = 5; double max_merge_distance_mm = 50; double edge_radius_mm = 1; + + inline PoolConfig() {} + inline PoolConfig(double wt, double wh, double md, double er): + min_wall_thickness_mm(wt), + min_wall_height_mm(wh), + max_merge_distance_mm(md), + edge_radius_mm(er) {} }; /// Calculate the pool for the mesh for SLA printing @@ -31,6 +38,15 @@ void create_base_pool(const ExPolygons& base_plate, const PoolConfig& = PoolConfig() ); +/// TODO: Currently the base plate of the pool will have half the height of the +/// whole pool. So the carved out space has also half the height. This is not +/// a particularly elegant solution, the thickness should be exactly +/// min_wall_thickness and it should be corrected in the future. This method +/// will return the correct value for further processing. +inline double get_pad_elevation(const PoolConfig& cfg) { + return cfg.min_wall_height_mm / 2.0; +} + } } diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index a4c5abc9b..f42f3b4ac 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -213,6 +213,7 @@ struct Head { double r_back_mm = 1; double r_pin_mm = 0.5; double width_mm = 2; + double penetration_mm = 0.5; // For identification purposes. This will be used as the index into the // container holding the head structures. See SLASupportTree::Impl @@ -224,11 +225,13 @@ struct Head { Head(double r_big_mm, double r_small_mm, double length_mm, + double penetration, Vec3d direction = {0, 0, -1}, // direction (normal to the dull end ) Vec3d offset = {0, 0, 0}, // displacement const size_t circlesteps = 45): steps(circlesteps), dir(direction), tr(offset), - r_back_mm(r_big_mm), r_pin_mm(r_small_mm), width_mm(length_mm) + r_back_mm(r_big_mm), r_pin_mm(r_small_mm), width_mm(length_mm), + penetration_mm(penetration) { // We create two spheres which will be connected with a robe that fits @@ -281,7 +284,7 @@ struct Head { // To simplify further processing, we translate the mesh so that the // last vertex of the pointing sphere (the pinpoint) will be at (0,0,0) - for(auto& p : mesh.points) { z(p) -= (h + 0.5 * r_small_mm); } + for(auto& p : mesh.points) z(p) -= (h + r_small_mm - penetration_mm); } void transform() @@ -298,11 +301,11 @@ struct Head { } double fullwidth() const { - return 1.5 * r_pin_mm + width_mm + 2*r_back_mm; + return 2 * r_pin_mm + width_mm + 2*r_back_mm - penetration_mm; } Vec3d junction_point() const { - return tr + ( 1.5 * r_pin_mm + width_mm + r_back_mm)*dir; + return tr + ( 2 * r_pin_mm + width_mm + r_back_mm - penetration_mm)*dir; } double request_pillar_radius(double radius) const { @@ -507,7 +510,9 @@ struct Pad { Pad(const TriangleMesh& object_support_mesh, const ExPolygons& baseplate, double ground_level, - const PoolConfig& cfg) : zlevel(ground_level + cfg.min_wall_height_mm/2) + const PoolConfig& pcfg) : + cfg(pcfg), + zlevel(ground_level + sla::get_pad_elevation(pcfg)) { ExPolygons basep; base_plate(object_support_mesh, basep, @@ -538,19 +543,6 @@ EigenMesh3D to_eigenmesh(const Contour3D& cntr) { return emesh; } -void create_head(TriangleMesh& out, double r1_mm, double r2_mm, double width_mm) -{ - Head head(r1_mm, r2_mm, width_mm, {0, std::sqrt(0.5), -std::sqrt(0.5)}, - {0, 0, 30}); - out.merge(mesh(head.mesh)); - - Pillar cst(head, {0, 0, 0}); - cst.add_base(); - - out.merge(mesh(cst.mesh)); - out.merge(mesh(cst.base)); -} - // The minimum distance for two support points to remain valid. static const double /*constexpr*/ D_SP = 0.1; @@ -593,21 +585,6 @@ EigenMesh3D to_eigenmesh(const ModelObject& modelobj) { return to_eigenmesh(modelobj.raw_mesh()); } -EigenMesh3D to_eigenmesh(const Model& model) { - TriangleMesh combined_mesh; - - for(ModelObject *o : model.objects) { - TriangleMesh tmp = o->raw_mesh(); - for(ModelInstance * inst: o->instances) { - TriangleMesh ttmp(tmp); - inst->transform_mesh(&ttmp); - combined_mesh.merge(ttmp); - } - } - - return to_eigenmesh(combined_mesh); -} - PointSet to_point_set(const std::vector &v) { PointSet ret(v.size(), 3); @@ -619,43 +596,6 @@ Vec3d model_coord(const ModelInstance& object, const Vec3f& mesh_coord) { return object.transform_vector(mesh_coord.cast()); } -PointSet support_points(const Model& model) { - size_t sum = 0; - for(auto *o : model.objects) - sum += o->instances.size() * o->sla_support_points.size(); - - PointSet ret(sum, 3); - - for(ModelObject *o : model.objects) - for(ModelInstance *inst : o->instances) { - int i = 0; - for(Vec3f& msource : o->sla_support_points) { - ret.row(i++) = model_coord(*inst, msource); - } - } - - return ret; -} - -PointSet support_points(const ModelObject& modelobject) -{ - PointSet ret(modelobject.sla_support_points.size(), 3); - auto rot = modelobject.instances.front()->get_rotation(); -// auto scaling = modelobject.instances.front()->get_scaling_factor(); - -// Transform3d tr; -// tr.rotate(Eigen::AngleAxisd(rot(X), Vec3d::UnitX()) * -// Eigen::AngleAxisd(rot(Y), Vec3d::UnitY())); - - long i = 0; - for(const Vec3f& msource : modelobject.sla_support_points) { - Vec3d&& p = msource.cast(); -// p = tr * p; - ret.row(i++) = p; - } - return ret; -} - double ray_mesh_intersect(const Vec3d& s, const Vec3d& dir, const EigenMesh3D& m); @@ -1154,6 +1094,7 @@ bool SLASupportTree::generate(const PointSet &points, cfg.head_back_radius_mm, cfg.head_front_radius_mm, cfg.head_width_mm, + cfg.head_penetration_mm, nmls.row(i), // dir head_pos.row(i) // displacement ); @@ -1521,6 +1462,7 @@ bool SLASupportTree::generate(const PointSet &points, Head base_head(cfg.head_back_radius_mm, cfg.head_front_radius_mm, cfg.head_width_mm, + cfg.head_penetration_mm, {0.0, 0.0, 1.0}, {headend(X), headend(Y), headend(Z) - gh}); @@ -1692,7 +1634,7 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const const auto modelh = float(stree.full_height()); auto gndlvl = float(this->m_impl->ground_level); const Pad& pad = m_impl->pad(); - if(!pad.empty()) gndlvl -= float(pad.cfg.min_wall_height_mm/2); + if(!pad.empty()) gndlvl -= float(get_pad_elevation(pad.cfg)); std::vector heights = {gndlvl}; heights.reserve(size_t(modelh/layerh) + 1); @@ -1719,10 +1661,10 @@ const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate, TriangleMesh mm; merged_mesh(mm); PoolConfig pcfg; -// pcfg.min_wall_thickness_mm = min_wall_thickness_mm; -// pcfg.min_wall_height_mm = min_wall_height_mm; -// pcfg.max_merge_distance_mm = max_merge_distance_mm; -// pcfg.edge_radius_mm = edge_radius_mm; + pcfg.min_wall_thickness_mm = min_wall_thickness_mm; + pcfg.min_wall_height_mm = min_wall_height_mm; + pcfg.max_merge_distance_mm = max_merge_distance_mm; + pcfg.edge_radius_mm = edge_radius_mm; return m_impl->create_pad(mm, baseplate, pcfg).tmesh; } @@ -1731,21 +1673,6 @@ const TriangleMesh &SLASupportTree::get_pad() const return m_impl->pad().tmesh; } -double SLASupportTree::get_elevation() const -{ - double ph = m_impl->pad().empty()? 0 : - m_impl->pad().cfg.min_wall_height_mm/2.0; - return -m_impl->ground_level + ph; -} - -SLASupportTree::SLASupportTree(const Model& model, - const SupportConfig& cfg, - const Controller& ctl): - m_impl(new Impl()), m_ctl(ctl) -{ - generate(support_points(model), to_eigenmesh(model), cfg, ctl); -} - SLASupportTree::SLASupportTree(const PointSet &points, const EigenMesh3D& emesh, const SupportConfig &cfg, @@ -1767,66 +1694,5 @@ SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c) SLASupportTree::~SLASupportTree() {} -void add_sla_supports(Model &model, - const SupportConfig &cfg, - const Controller &ctl) -{ - Benchmark bench; - - bench.start(); - SLASupportTree _stree(model, cfg, ctl); - bench.stop(); - - std::cout << "Support tree creation time: " << bench.getElapsedSec() - << " seconds" << std::endl; - - bench.start(); - ModelObject* o = model.add_object(); - o->add_instance(); - - TriangleMesh streemsh; - _stree.merged_mesh(streemsh); - o->add_volume(streemsh); - - bench.stop(); - std::cout << "support tree added to model in: " << bench.getElapsedSec() - << " seconds" << std::endl; - - // TODO this would roughly be the code for the base pool - ExPolygons plate; - auto modelmesh = model.mesh(); - TriangleMesh poolmesh; - sla::PoolConfig poolcfg; - poolcfg.min_wall_height_mm = 1; - poolcfg.edge_radius_mm = 0.1; - poolcfg.min_wall_thickness_mm = 0.8; - - bench.start(); - sla::base_plate(modelmesh, plate); - bench.stop(); - - std::cout << "Base plate calculation time: " << bench.getElapsedSec() - << " seconds." << std::endl; - - bench.start(); - sla::create_base_pool(plate, poolmesh, poolcfg); - bench.stop(); - - std::cout << "Pool generation completed in " << bench.getElapsedSec() - << " second." << std::endl; - - bench.start(); - poolmesh.translate(.0f, .0f, float(poolcfg.min_wall_height_mm / 2)); - o->add_volume(poolmesh); - bench.stop(); - - // TODO: will cause incorrect placement of the model; -// o->translate({0, 0, poolcfg.min_wall_height_mm / 2}); - - std::cout << "Added pool to model in " << bench.getElapsedSec() - << " seconds." << std::endl; - -} - } } diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index f0605a356..ce562946c 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -33,7 +33,7 @@ struct SupportConfig { double head_front_radius_mm = 0.2; // How much the pinhead has to penetrate the model surface - double head_penetraiton = 0.2; + double head_penetration_mm = 0.5; // Radius of the back side of the 3d arrow. double head_back_radius_mm = 0.5; @@ -90,34 +90,17 @@ struct EigenMesh3D { Eigen::MatrixXd V; Eigen::MatrixXi F; double ground_level = 0; - - // igl crashes with the following data types: -// Eigen::Matrix V; -// Eigen::Matrix F; }; -//using PointSet = Eigen::Matrix; //Eigen::MatrixXd; using PointSet = Eigen::MatrixXd; -/* ************************************************************************** */ -/* TODO: May not be needed: */ -/* ************************************************************************** */ - -void create_head(TriangleMesh&, double r1_mm, double r2_mm, double width_mm); - -/// Add support volumes to the model directly -void add_sla_supports(Model& model, const SupportConfig& cfg = {}, - const Controller& ctl = {}); - EigenMesh3D to_eigenmesh(const TriangleMesh& m); -PointSet to_point_set(const std::vector&); - -// obsolete, not used anymore -EigenMesh3D to_eigenmesh(const Model& model); +// needed for find best rotation EigenMesh3D to_eigenmesh(const ModelObject& model); -PointSet support_points(const ModelObject& modelobject); -PointSet support_points(const Model& model); + +// Simple conversion of 'vector of points' to an Eigen matrix +PointSet to_point_set(const std::vector&); /* ************************************************************************** */ @@ -149,11 +132,6 @@ class SLASupportTree { const Controller& ctl = {}); public: - // Constructors will throw if the stop condition becomes true. - SLASupportTree(const Model& model, - const SupportConfig& cfg = {}, - const Controller& ctl = {}); - SLASupportTree(const PointSet& pts, const EigenMesh3D& em, const SupportConfig& cfg = {}, diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index da24e71c6..568ff6566 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -3,6 +3,8 @@ #include "SLA/SLABasePool.hpp" #include +#include + //#include //#include "tbb/mutex.h" #include "I18N.hpp" @@ -63,7 +65,7 @@ const std::array PRINT_STEP_LABELS = void SLAPrint::clear() { - tbb::mutex::scoped_lock lock(this->cancel_mutex()); + tbb::mutex::scoped_lock lock(this->state_mutex()); // The following call should stop background processing if it is running. this->invalidate_all_steps(); @@ -78,14 +80,15 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, // return APPLY_STATUS_UNCHANGED; // Grab the lock for the Print / PrintObject milestones. - tbb::mutex::scoped_lock lock(this->cancel_mutex()); - if(m_objects.empty() && model.objects.empty()) + tbb::mutex::scoped_lock lock(this->state_mutex()); + if (m_objects.empty() && model.objects.empty() && m_model.objects.empty()) return APPLY_STATUS_UNCHANGED; // Temporary: just to have to correct layer height for the rasterization DynamicPrintConfig config(config_in); config.normalize(); - auto lh = config.opt("layer_height"); + m_material_config.initial_layer_height.set( + config.opt("initial_layer_height")); // Temporary quick fix, just invalidate everything. { @@ -102,7 +105,10 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, // Generate new SLAPrintObjects. for (ModelObject *model_object : m_model.objects) { auto po = new SLAPrintObject(this, model_object); - po->m_config.layer_height.set(lh); + + // po->m_config.layer_height.set(lh); + po->m_config.apply(config, true); + m_objects.emplace_back(po); for (ModelInstance *oinst : model_object->instances) { Point tr = Point::new_scale(oinst->get_offset()(X), @@ -124,26 +130,36 @@ void SLAPrint::process() // the model objects we have to process and the instances are also filtered // shortcut to initial layer height - auto ilh = float(m_material_config.initial_layer_height.getFloat()); + double ilhd = m_material_config.initial_layer_height.getFloat(); + auto ilh = float(ilhd); // Slicing the model object. This method is oversimplified and needs to // be compared with the fff slicing algorithm for verification - auto slice_model = [this, ilh](SLAPrintObject& po) { - auto lh = float(po.m_config.layer_height.getFloat()); + auto slice_model = [this, ilh, ilhd](SLAPrintObject& po) { + double lh = po.m_config.layer_height.getFloat(); TriangleMesh mesh = po.transformed_mesh(); TriangleMeshSlicer slicer(&mesh); auto bb3d = mesh.bounding_box(); - auto H = bb3d.max(Z) - bb3d.min(Z); + double elevation = po.get_elevation(); + + float minZ = float(bb3d.min(Z)) - float(elevation); + float maxZ = float(bb3d.max(Z)) ; + auto flh = float(lh); auto gnd = float(bb3d.min(Z)); - std::vector heights = {gnd}; - for(float h = gnd + ilh; h < gnd + H; h += lh) heights.emplace_back(h); + + std::vector heights; + + // The first layer (the one before the initial height) is added only + // if the there is no pad and no elevation value + if(minZ >= gnd) heights.emplace_back(minZ); + + for(float h = minZ + ilh; h < maxZ; h += flh) + if(h >= gnd) heights.emplace_back(h); auto& layers = po.m_model_slices; - slicer.slice(heights, &layers, [this](){ - throw_if_canceled(); - }); + slicer.slice(heights, &layers, [this](){ throw_if_canceled(); }); }; auto support_points = [](SLAPrintObject& po) { @@ -169,7 +185,17 @@ void SLAPrint::process() auto& emesh = po.m_supportdata->emesh; auto& pts = po.m_supportdata->support_points; // nowhere filled yet try { - SupportConfig scfg; // TODO fill or replace with po.m_config + sla::SupportConfig scfg; + SLAPrintObjectConfig& c = po.m_config; + + scfg.head_front_radius_mm = c.support_head_front_radius.getFloat(); + scfg.head_back_radius_mm = c.support_head_back_radius.getFloat(); + scfg.head_penetration_mm = c.support_head_penetration.getFloat(); + scfg.head_width_mm = c.support_head_width.getFloat(); + scfg.object_elevation_mm = c.support_object_elevation.getFloat(); + scfg.tilt = c.support_critical_angle.getFloat() * PI / 180.0 ; + scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat(); + scfg.pillar_radius_mm = c.support_pillar_radius.getFloat(); sla::Controller ctl; ctl.statuscb = [this](unsigned st, const std::string& msg) { @@ -196,6 +222,7 @@ void SLAPrint::process() // repeated) if(po.is_step_done(slaposSupportTree) && + po.m_config.pad_enable.getBool() && po.m_supportdata && po.m_supportdata->support_tree_ptr) { @@ -205,11 +232,12 @@ void SLAPrint::process() double er = po.m_config.pad_edge_radius.getFloat(); double lh = po.m_config.layer_height.getFloat(); double elevation = po.m_config.support_object_elevation.getFloat(); + sla::PoolConfig pcfg(wt, h, md, er); sla::ExPolygons bp; - if(elevation < h/2) - sla::base_plate(po.transformed_mesh(), bp, - float(h/2), float(lh)); + double pad_h = sla::get_pad_elevation(pcfg); + if(elevation < pad_h) sla::base_plate(po.transformed_mesh(), bp, + float(pad_h), float(lh)); po.m_supportdata->support_tree_ptr->add_pad(bp, wt, h, md, er); } @@ -227,7 +255,7 @@ void SLAPrint::process() }; // Rasterizing the model objects, and their supports - auto rasterize = [this, ilh]() { + auto rasterize = [this, ilh, ilhd]() { using Layer = sla::ExPolygons; using LayerCopies = std::vector; struct LayerRef { @@ -237,43 +265,68 @@ void SLAPrint::process() lref(std::cref(lyr)), copies(std::cref(cp)) {} }; + using LevelID = long long; using LayerRefs = std::vector; // layers according to quantized height levels - std::map levels; + std::map levels; + + auto sih = LevelID(scale_(ilh)); // For all print objects, go through its initial layers and place them // into the layers hash for(SLAPrintObject *o : m_objects) { - - double gndlvl = o->transformed_mesh().bounding_box().min(Z); - + auto bb = o->transformed_mesh().bounding_box(); + double modelgnd = bb.min(Z); + double elevation = o->get_elevation(); double lh = o->m_config.layer_height.getFloat(); - SlicedModel & oslices = o->m_model_slices; - for(int i = 0; i < oslices.size(); ++i) { - int a = i == 0 ? 0 : 1; - int b = i == 0 ? 0 : i - 1; + double minZ = modelgnd - elevation; - double h = gndlvl + ilh * a + b * lh; - long long lyridx = static_cast(scale_(h)); - auto& lyrs = levels[lyridx]; // this initializes a new record + // scaled values: + auto sminZ = LevelID(scale_(minZ)); + auto smaxZ = LevelID(scale_(bb.max(Z))); + auto smodelgnd = LevelID(scale_(modelgnd)); + auto slh = LevelID(scale_(lh)); + + // It is important that the next levels math the levels in + // model_slice method. Only difference is that here it works with + // scaled coordinates + std::vector levelids; + if(sminZ >= smodelgnd) levelids.emplace_back(sminZ); + for(LevelID h = sminZ + sih; h < smaxZ; h += slh) + if(h >= smodelgnd) levelids.emplace_back(h); + + SlicedModel & oslices = o->m_model_slices; + + // If everything went well this code should not run at all, but + // let's be robust... + assert(levelids.size() == oslices.size()); + if(levelids.size() < oslices.size()) { // extend the levels until... + + BOOST_LOG_TRIVIAL(warning) + << "Height level mismatch at rasterization!\n"; + + LevelID lastlvl = levelids.back(); + while(levelids.size() < oslices.size()) { + lastlvl += slh; + levelids.emplace_back(lastlvl); + } + } + + for(int i = 0; i < oslices.size(); ++i) { + LevelID h = levelids[i]; + auto& lyrs = levels[h]; // this initializes a new record lyrs.emplace_back(oslices[i], o->m_instances); } if(o->m_supportdata) { // deal with the support slices if present auto& sslices = o->m_supportdata->support_slices; - double el = o->m_config.support_object_elevation.getFloat(); - //TODO: remove next line: - el = SupportConfig().object_elevation_mm; - for(int i = 0; i < sslices.size(); ++i) { int a = i == 0 ? 0 : 1; int b = i == 0 ? 0 : i - 1; + LevelID h = sminZ + a * sih + b * slh; - double h = gndlvl - el + ilh * a + b * lh; - - long long lyridx = static_cast(scale_(h)); - auto& lyrs = levels[lyridx]; + auto& lyrs = levels[h]; lyrs.emplace_back(sslices[i], o->m_instances); } } @@ -430,14 +483,73 @@ void SLAPrint::process() } SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object): - Inherited(print), - m_model_object(model_object), + Inherited(print, model_object), m_stepmask(slaposCount, true) { } SLAPrintObject::~SLAPrintObject() {} +double SLAPrintObject::get_elevation() const { + double ret = m_config.support_object_elevation.getFloat(); + + // if the pad is enabled, then half of the pad height is its base plate + if(m_config.pad_enable.getBool()) { + // Normally the elevation for the pad itself would be the thickness of + // its walls but currently it is half of its thickness. Whatever it + // will be in the future, we provide the config to the get_pad_elevation + // method and we will have the correct value + sla::PoolConfig pcfg; + pcfg.min_wall_height_mm = m_config.pad_wall_height.getFloat(); + pcfg.min_wall_thickness_mm = m_config.pad_wall_thickness.getFloat(); + pcfg.edge_radius_mm = m_config.pad_edge_radius.getFloat(); + pcfg.max_merge_distance_mm = m_config.pad_max_merge_distance.getFloat(); + ret += sla::get_pad_elevation(pcfg); + } + + return ret; +} + +//const std::vector &SLAPrintObject::get_support_slices() const +//{ +// // I don't want to return a copy but the points may not exist, so ... +// static const std::vector dummy_empty; + +// if(!m_supportdata) return dummy_empty; +// return m_supportdata->support_slices; +//} + +//const std::vector &SLAPrintObject::get_model_slices() const +//{ +// return m_model_slices; +//} + +bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const +{ + switch (step) { + case slaposSupportTree: +// return m_supportdata && m_supportdata->support_tree_ptr && ! m_supportdata->support_tree_ptr->get().merged_mesh().empty(); + return ! this->support_mesh().empty(); + case slaposBasePool: +// return m_supportdata && m_supportdata->support_tree_ptr && ! m_supportdata->support_tree_ptr->get_pad().empty(); + return ! this->pad_mesh().empty(); + default: + return false; + } +} + +TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const +{ + switch (step) { + case slaposSupportTree: + return this->support_mesh(); + case slaposBasePool: + return this->pad_mesh(); + default: + return TriangleMesh(); + } +} + TriangleMesh SLAPrintObject::support_mesh() const { TriangleMesh trm; diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index de8c6cdae..ef3094309 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -35,8 +35,6 @@ private: // Prevents erroneous use by other classes. using Inherited = _SLAPrintObjectBase; public: - const ModelObject* model_object() const { return m_model_object; } - ModelObject* model_object() { return m_model_object; } const Transform3d& trafo() const { return m_trafo; } struct Instance { @@ -50,6 +48,9 @@ public: }; const std::vector& instances() const { return m_instances; } + bool has_mesh(SLAPrintObjectStep step) const; + TriangleMesh get_mesh(SLAPrintObjectStep step) const; + // Get a support mesh centered around origin in XY, and with zero rotation around Z applied. // Support mesh is only valid if this->is_step_done(slaposSupportTree) is true. TriangleMesh support_mesh() const; @@ -62,6 +63,15 @@ public: std::vector transformed_support_points() const; + // Get the needed Z elevation for the model geometry if supports should be + // displayed. This Z offset should also be applied to the support + // geometries. Note that this is not the same as the value stored in config + // as the pad height also needs to be considered. + double get_elevation() const; + +// const std::vector& get_support_slices() const; +// const std::vector& get_model_slices() const; + // I refuse to grantee copying (Tamas) SLAPrintObject(const SLAPrintObject&) = delete; SLAPrintObject& operator=(const SLAPrintObject&) = delete; @@ -83,8 +93,7 @@ protected: bool invalidate_step(SLAPrintObjectStep step); private: - // Points to the instance owned by a Model stored at the parent SLAPrint instance. - ModelObject *m_model_object; + // Object specific configuration, pulled from the configuration layer. SLAPrintObjectConfig m_config; // Translation in Z + Rotation by Y and Z + Scaling / Mirroring. @@ -147,7 +156,6 @@ private: using SLAPrinter = FilePrinter; using SLAPrinterPtr = std::unique_ptr; - Model m_model; SLAPrinterConfig m_printer_config; SLAMaterialConfig m_material_config; PrintObjects m_objects; diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 425967f9f..0da2334f8 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -521,6 +521,7 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& t) const if (stl.stats.shared_vertices > 0) { + assert(stl.v_shared != nullptr); stl_vertex* vertex_ptr = stl.v_shared; for (int i = 0; i < stl.stats.shared_vertices; ++i) { diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index fd312d0e0..e2bf47014 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -65,6 +65,7 @@ public: void reset_repair_stats(); bool needed_repair() const; size_t facets_count() const { return this->stl.stats.number_of_facets; } + bool empty() const { return this->facets_count() == 0; } // Returns true, if there are two and more connected patches in the mesh. // Returns false, if one or zero connected patch is in the mesh. diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 2350cdffc..486f7286c 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -98,12 +98,18 @@ void GLIndexedVertexArray::finalize_geometry(bool use_VBOs) void GLIndexedVertexArray::release_geometry() { - if (this->vertices_and_normals_interleaved_VBO_id) + if (this->vertices_and_normals_interleaved_VBO_id) { glDeleteBuffers(1, &this->vertices_and_normals_interleaved_VBO_id); - if (this->triangle_indices_VBO_id) + this->vertices_and_normals_interleaved_VBO_id = 0; + } + if (this->triangle_indices_VBO_id) { glDeleteBuffers(1, &this->triangle_indices_VBO_id); - if (this->quad_indices_VBO_id) + this->triangle_indices_VBO_id = 0; + } + if (this->quad_indices_VBO_id) { glDeleteBuffers(1, &this->quad_indices_VBO_id); + this->quad_indices_VBO_id = 0; + } this->clear(); this->shrink_to_fit(); } @@ -210,9 +216,9 @@ GLVolume::GLVolume(float r, float g, float b, float a) #endif // ENABLE_MODELVOLUME_TRANSFORM , m_transformed_convex_hull_bounding_box_dirty(true) , m_convex_hull(nullptr) - , object_id(-1) - , volume_id(-1) - , instance_id(-1) + , m_convex_hull_owned(false) + // geometry_id == 0 -> invalid + , geometry_id(std::pair(0, 0)) , extruder_id(0) , selected(false) , disabled(false) @@ -234,6 +240,12 @@ GLVolume::GLVolume(float r, float g, float b, float a) set_render_color(r, g, b, a); } +GLVolume::~GLVolume() +{ + if (m_convex_hull_owned) + delete m_convex_hull; +} + void GLVolume::set_render_color(float r, float g, float b, float a) { render_color[0] = r; @@ -360,9 +372,10 @@ void GLVolume::set_mirror(Axis axis, double mirror) } #endif // !ENABLE_MODELVOLUME_TRANSFORM -void GLVolume::set_convex_hull(const TriangleMesh& convex_hull) +void GLVolume::set_convex_hull(const TriangleMesh *convex_hull, bool owned) { - m_convex_hull = &convex_hull; + m_convex_hull = convex_hull; + m_convex_hull_owned = owned; } #if !ENABLE_MODELVOLUME_TRANSFORM @@ -705,6 +718,25 @@ std::vector GLVolumeCollection::load_object( const std::vector &instance_idxs, const std::string &color_by, bool use_VBOs) +{ + // Object will share a single common layer height texture between all printable volumes. + std::shared_ptr layer_height_texture = std::make_shared(); + std::vector volumes_idx; + for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) + for (int instance_idx : instance_idxs) + volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, layer_height_texture, obj_idx, volume_idx, instance_idx, color_by, use_VBOs)); + return volumes_idx; +} + +int GLVolumeCollection::load_object_volume( + const ModelObject *model_object, + // Layer height texture is shared between all printable volumes of a single ModelObject. + std::shared_ptr &layer_height_texture, + int obj_idx, + int volume_idx, + int instance_idx, + const std::string &color_by, + bool use_VBOs) { static float colors[4][4] = { { 1.0f, 1.0f, 0.0f, 1.f }, @@ -713,132 +745,117 @@ std::vector GLVolumeCollection::load_object( { 0.5f, 0.5f, 1.0f, 1.f } }; - // Object will have a single common layer height texture for all volumes. - std::shared_ptr layer_height_texture = std::make_shared(); + const ModelVolume *model_volume = model_object->volumes[volume_idx]; - std::vector volumes_idx; - for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) { - const ModelVolume *model_volume = model_object->volumes[volume_idx]; - - int extruder_id = -1; - if (model_volume->is_model_part()) - { - extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0; - if (extruder_id == 0) - extruder_id = model_object->config.has("extruder") ? model_object->config.option("extruder")->getInt() : 0; - } - - for (int instance_idx : instance_idxs) { - const ModelInstance *instance = model_object->instances[instance_idx]; -#if ENABLE_MODELVOLUME_TRANSFORM - const TriangleMesh& mesh = model_volume->mesh; -#else - TriangleMesh mesh = model_volume->mesh; -#endif // ENABLE_MODELVOLUME_TRANSFORM - volumes_idx.push_back(int(this->volumes.size())); - float color[4]; - memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); - if (model_volume->is_support_blocker()) { - color[0] = 1.0f; - color[1] = 0.2f; - color[2] = 0.2f; - } else if (model_volume->is_support_enforcer()) { - color[0] = 0.2f; - color[1] = 0.2f; - color[2] = 1.0f; - } - color[3] = model_volume->is_model_part() ? 1.f : 0.5f; - this->volumes.emplace_back(new GLVolume(color)); - GLVolume &v = *this->volumes.back(); - if (use_VBOs) - v.indexed_vertex_array.load_mesh_full_shading(mesh); - else - v.indexed_vertex_array.load_mesh_flat_shading(mesh); - - // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). - v.bounding_box = v.indexed_vertex_array.bounding_box(); - v.indexed_vertex_array.finalize_geometry(use_VBOs); - v.object_id = obj_idx; - v.volume_id = volume_idx; - v.instance_id = instance_idx; - if (model_volume->is_model_part()) - { - v.set_convex_hull(model_volume->get_convex_hull()); - v.layer_height_texture = layer_height_texture; - if (extruder_id != -1) - v.extruder_id = extruder_id; - } - v.is_modifier = ! model_volume->is_model_part(); - v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); -#if ENABLE_MODELVOLUME_TRANSFORM - v.set_instance_transformation(instance->get_transformation()); - v.set_volume_transformation(model_volume->get_transformation()); -#else - v.set_offset(instance->get_offset()); - v.set_rotation(instance->get_rotation()); - v.set_scaling_factor(instance->get_scaling_factor()); - v.set_mirror(instance->get_mirror()); -#endif // ENABLE_MODELVOLUME_TRANSFORM - } + int extruder_id = -1; + if (model_volume->is_model_part()) + { + const ConfigOption *opt = model_volume->config.option("extruder"); + if (opt == nullptr) + opt = model_object->config.option("extruder"); + extruder_id = (opt == nullptr) ? 0 : opt->getInt(); } - - return volumes_idx; + + const ModelInstance *instance = model_object->instances[instance_idx]; +#if ENABLE_MODELVOLUME_TRANSFORM + const TriangleMesh& mesh = model_volume->mesh; +#else + TriangleMesh mesh = model_volume->mesh; +#endif // ENABLE_MODELVOLUME_TRANSFORM + float color[4]; + memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); + if (model_volume->is_support_blocker()) { + color[0] = 1.0f; + color[1] = 0.2f; + color[2] = 0.2f; + } else if (model_volume->is_support_enforcer()) { + color[0] = 0.2f; + color[1] = 0.2f; + color[2] = 1.0f; + } + color[3] = model_volume->is_model_part() ? 1.f : 0.5f; + this->volumes.emplace_back(new GLVolume(color)); + GLVolume &v = *this->volumes.back(); + if (use_VBOs) + v.indexed_vertex_array.load_mesh_full_shading(mesh); + else + v.indexed_vertex_array.load_mesh_flat_shading(mesh); + + // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). + v.bounding_box = v.indexed_vertex_array.bounding_box(); + v.indexed_vertex_array.finalize_geometry(use_VBOs); + v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx); + if (model_volume->is_model_part()) + { + // GLVolume will reference a convex hull from model_volume! + v.set_convex_hull(&model_volume->get_convex_hull(), false); + if (extruder_id != -1) + v.extruder_id = extruder_id; + v.layer_height_texture = layer_height_texture; + } + v.is_modifier = ! model_volume->is_model_part(); + v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); +#if ENABLE_MODELVOLUME_TRANSFORM + v.set_instance_transformation(instance->get_transformation()); + v.set_volume_transformation(model_volume->get_transformation()); +#else + v.set_offset(instance->get_offset()); + v.set_rotation(instance->get_rotation()); + v.set_scaling_factor(instance->get_scaling_factor()); + v.set_mirror(instance->get_mirror()); +#endif // ENABLE_MODELVOLUME_TRANSFORM + + return int(this->volumes.size() - 1); } // Load SLA auxiliary GLVolumes (for support trees or pad). -std::vector GLVolumeCollection::load_object_auxiliary( - const ModelObject *model_object, - const SLAPrintObject *print_object, - int obj_idx, - SLAPrintObjectStep milestone, - bool use_VBOs) +// This function produces volumes for multiple instances in a single shot, +// as some object specific mesh conversions may be expensive. +void GLVolumeCollection::load_object_auxiliary( + const SLAPrintObject *print_object, + int obj_idx, + // pairs of + const std::vector> &instances, + SLAPrintObjectStep milestone, + // Timestamp of the last change of the milestone + size_t timestamp, + bool use_VBOs) { - std::vector volumes_idx; - // Find the SLAPrintObject's instance to it. - if (print_object->is_step_done(milestone)) { - // Get the support mesh. - TriangleMesh mesh; - switch (milestone) { - case slaposSupportTree: mesh = print_object->support_mesh(); break; - case slaposBasePool: mesh = print_object->pad_mesh(); break; - default: - assert(false); - } - // Convex hull is required for out of print bed detection. - TriangleMesh convex_hull = mesh.convex_hull_3d(); - const std::vector &instances = print_object->instances(); - std::map map_instances; - for (int i = 0; i < (int)model_object->instances.size(); ++ i) - map_instances[model_object->instances[i]->id()] = i; - for (const SLAPrintObject::Instance &instance : instances) { - auto model_instance_it = map_instances.find(instance.instance_id); - assert(model_instance_it != map_instances.end()); - const int instance_idx = model_instance_it->second; - const ModelInstance *model_instance = model_object->instances[instance_idx]; - volumes_idx.push_back(int(this->volumes.size())); - float color[4] { 0.f, 0.f, 1.f, 1.f }; - this->volumes.emplace_back(new GLVolume(color)); - GLVolume &v = *this->volumes.back(); - if (use_VBOs) - v.indexed_vertex_array.load_mesh_full_shading(mesh); - else - v.indexed_vertex_array.load_mesh_flat_shading(mesh); - // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). - v.bounding_box = v.indexed_vertex_array.bounding_box(); - v.indexed_vertex_array.finalize_geometry(use_VBOs); - v.object_id = obj_idx; - v.volume_id = -1; // SLA supports - v.instance_id = instance_idx; - v.set_convex_hull(convex_hull); - v.is_modifier = false; - v.shader_outside_printer_detection_enabled = true; - v.set_instance_transformation(model_instance->get_transformation()); - // Leave the volume transformation at identity. - // v.set_volume_transformation(model_volume->get_transformation()); - } + assert(print_object->is_step_done(milestone)); + // Get the support mesh. + TriangleMesh mesh; + switch (milestone) { + case slaposSupportTree: mesh = print_object->support_mesh(); break; + case slaposBasePool: mesh = print_object->pad_mesh(); break; + default: + assert(false); + } + // Convex hull is required for out of print bed detection. + TriangleMesh convex_hull = mesh.convex_hull_3d(); + for (const std::pair &instance_idx : instances) { + const ModelInstance &model_instance = *print_object->model_object()->instances[instance_idx.first]; + const SLAPrintObject::Instance &print_instance = print_object->instances()[instance_idx.second]; + float color[4] { 0.f, 0.f, 1.f, 1.f }; + this->volumes.emplace_back(new GLVolume(color)); + GLVolume &v = *this->volumes.back(); + if (use_VBOs) + v.indexed_vertex_array.load_mesh_full_shading(mesh); + else + v.indexed_vertex_array.load_mesh_flat_shading(mesh); + // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). + v.bounding_box = v.indexed_vertex_array.bounding_box(); + v.indexed_vertex_array.finalize_geometry(use_VBOs); + v.composite_id = GLVolume::CompositeID(obj_idx, -1, (int)instance_idx.first); + v.geometry_id = std::pair(timestamp, model_instance.id().id); + // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. + v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true); + v.is_modifier = false; + v.shader_outside_printer_detection_enabled = true; + //FIXME adjust with print_instance? + v.set_instance_transformation(model_instance.get_transformation()); + // Leave the volume transformation at identity. + // v.set_volume_transformation(model_volume->get_transformation()); } - - return volumes_idx; } int GLVolumeCollection::load_wipe_tower_preview( @@ -911,9 +928,7 @@ int GLVolumeCollection::load_wipe_tower_preview( // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); - v.object_id = obj_idx; - v.volume_id = 0; - v.instance_id = 0; + v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0); v.is_wipe_tower = true; v.shader_outside_printer_detection_enabled = ! size_unknown; return int(this->volumes.size() - 1); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 16523af68..6ebb6def0 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -256,6 +256,7 @@ public: GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f); GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} + ~GLVolume(); private: #if ENABLE_MODELVOLUME_TRANSFORM @@ -280,7 +281,9 @@ private: // Whether or not is needed to recalculate the transformed bounding box. mutable bool m_transformed_bounding_box_dirty; // Pointer to convex hull of the original mesh, if any. + // This object may or may not own the convex hull instance based on m_convex_hull_owned const TriangleMesh* m_convex_hull; + bool m_convex_hull_owned; // Bounding box of this volume, in unscaled coordinates. mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box; // Whether or not is needed to recalculate the transformed convex hull bounding box. @@ -293,15 +296,25 @@ public: float color[4]; // Color used to render this volume. float render_color[4]; - // Object ID, which is equal to the index of the respective ModelObject in Model.objects array. - int object_id; - // Volume ID, which is equal to the index of the respective ModelVolume in ModelObject.volumes array. - // If negative, it is an index of a geometry produced by the PrintObject for the respective ModelObject, - // and which has no associated ModelVolume in ModelObject.volumes. For example, SLA supports. - // Volume with a negative volume_id cannot be picked independently, it will pick the associated instance. - int volume_id; - // Instance ID, which is equal to the index of the respective ModelInstance in ModelObject.instances array. - int instance_id; + struct CompositeID { + CompositeID(int object_id, int volume_id, int instance_id) : object_id(object_id), volume_id(volume_id), instance_id(instance_id) {} + CompositeID() : object_id(-1), volume_id(-1), instance_id(-1) {} + // Object ID, which is equal to the index of the respective ModelObject in Model.objects array. + int object_id; + // Volume ID, which is equal to the index of the respective ModelVolume in ModelObject.volumes array. + // If negative, it is an index of a geometry produced by the PrintObject for the respective ModelObject, + // and which has no associated ModelVolume in ModelObject.volumes. For example, SLA supports. + // Volume with a negative volume_id cannot be picked independently, it will pick the associated instance. + int volume_id; + // Instance ID, which is equal to the index of the respective ModelInstance in ModelObject.instances array. + int instance_id; + }; + CompositeID composite_id; + // Fingerprint of the source geometry. For ModelVolumes, it is the ModelVolume::ID and ModelInstanceID, + // for generated volumes it is the timestamp generated by PrintState::invalidate() or PrintState::set_done(), + // and the associated ModelInstanceID. + // Valid geometry_id should always be positive. + std::pair geometry_id; // An ID containing the extruder ID (used to select color). int extruder_id; // Is this object selected? @@ -412,11 +425,11 @@ public: void set_offset(const Vec3d& offset); #endif // ENABLE_MODELVOLUME_TRANSFORM - void set_convex_hull(const TriangleMesh& convex_hull); + void set_convex_hull(const TriangleMesh *convex_hull, bool owned); - int object_idx() const { return this->object_id; } - int volume_idx() const { return this->volume_id; } - int instance_idx() const { return this->instance_id; } + int object_idx() const { return this->composite_id.object_id; } + int volume_idx() const { return this->composite_id.volume_id; } + int instance_idx() const { return this->composite_id.instance_id; } #if ENABLE_MODELVOLUME_TRANSFORM Transform3d world_matrix() const { return m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); } @@ -499,14 +512,26 @@ public: const std::string &color_by, bool use_VBOs); - // Load SLA auxiliary GLVolumes (for support trees or pad). - std::vector load_object_auxiliary( + int load_object_volume( const ModelObject *model_object, - const SLAPrintObject *print_object, + std::shared_ptr &layer_height_texture, int obj_idx, - SLAPrintObjectStep milestone, + int volume_idx, + int instance_idx, + const std::string &color_by, bool use_VBOs); + // Load SLA auxiliary GLVolumes (for support trees or pad). + void load_object_auxiliary( + const SLAPrintObject *print_object, + int obj_idx, + // pairs of + const std::vector> &instances, + SLAPrintObjectStep milestone, + // Timestamp of the last change of the milestone + size_t timestamp, + bool use_VBOs); + int load_wipe_tower_preview( int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 5dcef44a1..35dfcdf0e 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -2,7 +2,6 @@ #include "GUI_App.hpp" #include -#include #include #include @@ -60,24 +59,21 @@ void BackgroundSlicingProcess::process_fff() { assert(m_print == m_fff_print); m_print->process(); - if (! m_print->canceled()) { - wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_sliced_id)); - m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); - if (! m_print->canceled() && ! this->is_step_done(bspsGCodeFinalize)) { - this->set_step_started(bspsGCodeFinalize); - if (! m_export_path.empty()) { - //FIXME localize the messages - if (copy_file(m_temp_output_path, m_export_path) != 0) - throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); - m_print->set_status(95, "Running post-processing scripts"); - run_post_process_scripts(m_export_path, m_fff_print->config()); - m_print->set_status(100, "G-code file exported to " + m_export_path); - } else { - m_print->set_status(100, "Slicing complete"); - } - this->set_step_done(bspsGCodeFinalize); + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id)); + m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); + if (this->set_step_started(bspsGCodeFinalize)) { + if (! m_export_path.empty()) { + //FIXME localize the messages + if (copy_file(m_temp_output_path, m_export_path) != 0) + throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); + m_print->set_status(95, "Running post-processing scripts"); + run_post_process_scripts(m_export_path, m_fff_print->config()); + m_print->set_status(100, "G-code file exported to " + m_export_path); + } else { + m_print->set_status(100, "Slicing complete"); } - } + this->set_step_done(bspsGCodeFinalize); + } } // Pseudo type for specializing LayerWriter trait class @@ -120,11 +116,11 @@ public: } }; -void BackgroundSlicingProcess::process_sla() { +void BackgroundSlicingProcess::process_sla() +{ assert(m_print == m_sla_print); m_print->process(); - if(!m_print->canceled() && ! this->is_step_done(bspsGCodeFinalize)) { - this->set_step_started(bspsGCodeFinalize); + if (this->set_step_started(bspsGCodeFinalize)) { if (! m_export_path.empty()) { m_sla_print->export_raster(m_export_path); m_print->set_status(100, "Zip file exported to " + m_export_path); @@ -246,6 +242,7 @@ bool BackgroundSlicingProcess::start() bool BackgroundSlicingProcess::stop() { + // m_print->state_mutex() shall NOT be held. Unfortunately there is no interface to test for it. std::unique_lock lck(m_mutex); if (m_state == STATE_INITIAL) { // this->m_export_path.clear(); @@ -282,12 +279,23 @@ bool BackgroundSlicingProcess::reset() // This function shall not trigger any UI update through the wxWidgets event. void BackgroundSlicingProcess::stop_internal() { + // m_print->state_mutex() shall be held. Unfortunately there is no interface to test for it. + if (m_state == STATE_IDLE) + // The worker thread is waiting on m_mutex/m_condition for wake up. The following lock of the mutex would block. + return; std::unique_lock lck(m_mutex); assert(m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED); if (m_state == STATE_STARTED || m_state == STATE_RUNNING) { + // At this point of time the worker thread may be blocking on m_print->state_mutex(). + // Set the print state to canceled before unlocking the state_mutex(), so when the worker thread wakes up, + // it throws the CanceledException(). m_print->cancel_internal(); + // Allow the worker thread to wake up if blocking on a milestone. + m_print->state_mutex().unlock(); // Wait until the background processing stops by being canceled. m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; }); + // Lock it back to be in a consistent state. + m_print->state_mutex().lock(); } // In the "Canceled" state. Reset the state to "Idle". m_state = STATE_IDLE; @@ -324,7 +332,7 @@ void BackgroundSlicingProcess::schedule_export(const std::string &path) return; // Guard against entering the export step before changing the export path. - tbb::mutex::scoped_lock lock(m_step_state_mutex); + tbb::mutex::scoped_lock lock(m_print->state_mutex()); this->invalidate_step(bspsGCodeFinalize); m_export_path = path; } @@ -335,34 +343,35 @@ void BackgroundSlicingProcess::reset_export() if (! this->running()) { m_export_path.clear(); // invalidate_step expects the mutex to be locked. - tbb::mutex::scoped_lock lock(m_step_state_mutex); + tbb::mutex::scoped_lock lock(m_print->state_mutex()); this->invalidate_step(bspsGCodeFinalize); } } -void BackgroundSlicingProcess::set_step_started(BackgroundSlicingProcessStep step) +bool BackgroundSlicingProcess::set_step_started(BackgroundSlicingProcessStep step) { - m_step_state.set_started(step, m_step_state_mutex); - if (m_print->canceled()) - throw CanceledException(); + return m_step_state.set_started(step, m_print->state_mutex(), [this](){ this->throw_if_canceled(); }); } void BackgroundSlicingProcess::set_step_done(BackgroundSlicingProcessStep step) { - m_step_state.set_done(step, m_step_state_mutex); - if (m_print->canceled()) - throw CanceledException(); + m_step_state.set_done(step, m_print->state_mutex(), [this](){ this->throw_if_canceled(); }); +} + +bool BackgroundSlicingProcess::is_step_done(BackgroundSlicingProcessStep step) const +{ + return m_step_state.is_done(step, m_print->state_mutex()); } bool BackgroundSlicingProcess::invalidate_step(BackgroundSlicingProcessStep step) { - bool invalidated = m_step_state.invalidate(step, m_step_state_mutex, [this](){ this->stop(); }); + bool invalidated = m_step_state.invalidate(step, [this](){ this->stop_internal(); }); return invalidated; } bool BackgroundSlicingProcess::invalidate_all_steps() { - return m_step_state.invalidate_all(m_step_state_mutex, [this](){ this->stop(); }); + return m_step_state.invalidate_all([this](){ this->stop_internal(); }); } }; // namespace Slic3r diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 92bc512d7..652d85487 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -6,6 +6,8 @@ #include #include +#include + #include "Print.hpp" namespace Slic3r { @@ -15,6 +17,18 @@ class GCodePreviewData; class Model; class SLAPrint; +class SlicingStatusEvent : public wxEvent +{ +public: + SlicingStatusEvent(wxEventType eventType, int winid, const PrintBase::SlicingStatus &status) : + wxEvent(winid, eventType), status(std::move(status)) {} + virtual wxEvent *Clone() const { return new SlicingStatusEvent(*this); } + + PrintBase::SlicingStatus status; +}; + +wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent); + // Print step IDs for keeping track of the print state. enum BackgroundSlicingProcessStep { bspsGCodeFinalize, bspsCount, @@ -35,7 +49,7 @@ public: // The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished // and the background processing will transition into G-code export. // The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed. - void set_sliced_event(int event_id) { m_event_sliced_id = event_id; } + void set_slicing_completed_event(int event_id) { m_event_slicing_completed_id = event_id; } // The following wxCommandEvent will be sent to the UI thread / Platter window, when the G-code export is finished. // 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; } @@ -125,16 +139,18 @@ private: PrintState m_step_state; mutable tbb::mutex m_step_state_mutex; - void set_step_started(BackgroundSlicingProcessStep step); + bool set_step_started(BackgroundSlicingProcessStep step); void set_step_done(BackgroundSlicingProcessStep step); - bool is_step_done(BackgroundSlicingProcessStep step) const { return m_step_state.is_done(step); } + bool is_step_done(BackgroundSlicingProcessStep step) const; bool invalidate_step(BackgroundSlicingProcessStep step); bool invalidate_all_steps(); + // If the background processing stop was requested, throw CanceledException. + void throw_if_canceled() const { if (m_print->canceled()) throw CanceledException(); } // wxWidgets command ID to be sent to the platter to inform that the slicing is finished, and the G-code export will continue. - int m_event_sliced_id = 0; + int m_event_slicing_completed_id = 0; // wxWidgets command ID to be sent to the platter to inform that the task finished. - int m_event_finished_id = 0; + int m_event_finished_id = 0; }; }; // namespace Slic3r diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4fd65ea77..c4c2d6e22 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1329,6 +1329,47 @@ void GLCanvas3D::Selection::clear() m_bounding_box_dirty = true; } +// Update the selection based on the map from old indices to new indices after m_volumes changed. +// If the current selection is by instance, this call may select newly added volumes, if they belong to already selected instances. +void GLCanvas3D::Selection::volumes_changed(const std::vector &map_volume_old_to_new) +{ + assert(m_valid); + + // 1) Update the selection set. + IndicesList list_new; + std::vector> model_instances; + for (unsigned int idx : m_list) { + if (map_volume_old_to_new[idx] != size_t(-1)) { + unsigned int new_idx = (unsigned int)map_volume_old_to_new[idx]; + list_new.insert(new_idx); + if (m_mode == Instance) { + // Save the object_idx / instance_idx pair of selected old volumes, + // so we may add the newly added volumes of the same object_idx / instance_idx pair + // to the selection. + const GLVolume *volume = (*m_volumes)[new_idx]; + model_instances.emplace_back(volume->object_idx(), volume->instance_idx()); + } + } + } + m_list = std::move(list_new); + + if (! model_instances.empty()) { + // Instance selection mode. Add the newly added volumes of the same object_idx / instance_idx pair + // to the selection. + assert(m_mode == Instance); + sort_remove_duplicates(model_instances); + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++ i) { + const GLVolume* volume = (*m_volumes)[i]; + for (const std::pair &model_instance : model_instances) + if (volume->object_idx() == model_instance.first && volume->instance_idx() == model_instance.second) + this->_add_volume(i); + } + } + + _update_type(); + m_bounding_box_dirty = true; +} + bool GLCanvas3D::Selection::is_single_full_instance() const { if (m_type == SingleFullInstance) @@ -1774,7 +1815,10 @@ void GLCanvas3D::Selection::erase() for (unsigned int i : m_list) { const GLVolume* v = (*m_volumes)[i]; - volumes_idxs.insert(std::make_pair(v->object_idx(), v->volume_idx())); + // Only remove volumes associated with ModelVolumes from the object list. + // Temporary meshes (SLA supports or pads) are not managed by the object list. + if (v->volume_idx() >= 0) + volumes_idxs.insert(std::make_pair(v->object_idx(), v->volume_idx())); } std::vector items; @@ -1997,10 +2041,6 @@ void GLCanvas3D::Selection::_set_caches() void GLCanvas3D::Selection::_add_volume(unsigned int volume_idx) { - // check if the given idx is already selected - if (m_list.find(volume_idx) != m_list.end()) - return; - m_list.insert(volume_idx); (*m_volumes)[volume_idx]->selected = true; } @@ -3665,13 +3705,6 @@ std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) return std::vector(); } -std::vector GLCanvas3D::load_support_meshes(const Model& model, int obj_idx) -{ - std::vector volumes = m_volumes.load_object_auxiliary(model.objects[obj_idx], m_sla_print->objects()[obj_idx], obj_idx, slaposSupportTree, m_use_VBOs && m_initialized); - append(volumes, m_volumes.load_object_auxiliary(model.objects[obj_idx], m_sla_print->objects()[obj_idx], obj_idx, slaposBasePool, m_use_VBOs && m_initialized)); - return volumes; -} - void GLCanvas3D::mirror_selection(Axis axis) { m_selection.mirror(axis); @@ -3679,6 +3712,12 @@ void GLCanvas3D::mirror_selection(Axis axis) wxGetApp().obj_manipul()->update_settings_value(m_selection); } +// Reload the 3D scene of +// 1) Model / ModelObjects / ModelInstances / ModelVolumes +// 2) Print bed +// 3) SLA support meshes for their respective ModelObjects / ModelInstances +// 4) Wipe tower preview +// 5) Out of bed collision status & message overlay (texture) void GLCanvas3D::reload_scene(bool force) { if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) @@ -3689,39 +3728,231 @@ void GLCanvas3D::reload_scene(bool force) return; #endif // !ENABLE_USE_UNIQUE_GLCONTEXT - if (m_regenerate_volumes) - { - reset_volumes(); + struct ModelVolumeState { + ModelVolumeState(const GLVolume *volume) : + geometry_id(volume->geometry_id), volume_idx(-1) {} + ModelVolumeState(const ModelID &volume_id, const ModelID &instance_id, const GLVolume::CompositeID &composite_id) : + geometry_id(std::make_pair(volume_id.id, instance_id.id)), composite_id(composite_id), volume_idx(-1) {} + ModelVolumeState(const ModelID &volume_id, const ModelID &instance_id) : + geometry_id(std::make_pair(volume_id.id, instance_id.id)), volume_idx(-1) {} + bool new_geometry() const { return this->volume_idx == size_t(-1); } + // ModelID of ModelVolume + ModelID of ModelInstance + // or timestamp of an SLAPrintObjectStep + ModelID of ModelInstance + std::pair geometry_id; + GLVolume::CompositeID composite_id; + // Volume index in the new GLVolume vector. + size_t volume_idx; + }; + std::vector model_volume_state; + std::vector aux_volume_state; - // to update the toolbar - post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); - } + // SLA steps to pull the preview meshes for. + typedef std::array SLASteps; + SLASteps sla_steps = { slaposSupportTree, slaposBasePool }; + struct SLASupportState { + std::array::value> step; + }; + // State of the sla_steps for all SLAPrintObjects. + std::vector sla_support_state; - set_bed_shape(dynamic_cast(m_config->option("bed_shape"))->values); + std::vector map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1)); + std::vector glvolumes_new; + glvolumes_new.reserve(m_volumes.volumes.size()); + auto model_volume_state_lower = [](const ModelVolumeState &m1, const ModelVolumeState &m2) { return m1.geometry_id < m2.geometry_id; }; - if (!m_canvas->IsShown() && !force) - { - m_reload_delayed = true; - return; - } - - m_reload_delayed = false; + m_reload_delayed = ! m_canvas->IsShown() && ! force; PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); + if (m_regenerate_volumes) { - for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) - { - load_object(*m_model, obj_idx); - if (printer_technology == ptSLA) - load_support_meshes(*m_model, obj_idx); + // Release invalidated volumes to conserve GPU memory in case of delayed refresh (see m_reload_delayed). + // First initialize model_volumes_new_sorted & model_instances_new_sorted. + for (int object_idx = 0; object_idx < (int)m_model->objects.size(); ++ object_idx) { + const ModelObject *model_object = m_model->objects[object_idx]; + for (int instance_idx = 0; instance_idx < (int)model_object->instances.size(); ++ instance_idx) { + const ModelInstance *model_instance = model_object->instances[instance_idx]; + for (int volume_idx = 0; volume_idx < (int)model_object->volumes.size(); ++ volume_idx) { + const ModelVolume *model_volume = model_object->volumes[volume_idx]; + model_volume_state.emplace_back(model_volume->id(), model_instance->id(), GLVolume::CompositeID(object_idx, volume_idx, instance_idx)); + } + } + } + if (printer_technology == ptSLA) { + #ifdef _DEBUG + // Verify that the SLAPrint object is synchronized with m_model. + check_model_ids_equal(*m_model, m_sla_print->model()); + #endif /* _DEBUG */ + sla_support_state.reserve(m_sla_print->objects().size()); + for (const SLAPrintObject *print_object : m_sla_print->objects()) { + SLASupportState state; + for (size_t istep = 0; istep < sla_steps.size(); ++ istep) { + state.step[istep] = print_object->step_state_with_timestamp(sla_steps[istep]); + if (state.step[istep].state == PrintStateBase::DONE) { + if (! print_object->has_mesh(sla_steps[istep])) + // Consider the DONE step without a valid mesh as invalid for the purpose + // of mesh visualization. + state.step[istep].state = PrintStateBase::INVALID; + else + for (const ModelInstance *model_instance : print_object->model_object()->instances) + aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id()); + } + } + sla_support_state.emplace_back(state); + } + } + std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower); + std::sort(aux_volume_state .begin(), aux_volume_state .end(), model_volume_state_lower); + // Release all ModelVolume based GLVolumes not found in the current Model. + for (size_t volume_id = 0; volume_id < m_volumes.volumes.size(); ++ volume_id) { + GLVolume *volume = m_volumes.volumes[volume_id]; + ModelVolumeState key(volume); + ModelVolumeState *mvs = nullptr; + if (volume->volume_idx() < 0) { + auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); + if (it != aux_volume_state.end() && it->geometry_id == key.geometry_id) + mvs = &(*it); + } else { + auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); + if (it != model_volume_state.end() && it->geometry_id == key.geometry_id) + mvs = &(*it); + } + if (mvs == nullptr) { + // This GLVolume will be released. + volume->release_geometry(); + if (! m_reload_delayed) + delete volume; + } else { + // This GLVolume will be reused. + map_glvolume_old_to_new[volume_id] = glvolumes_new.size(); + mvs->volume_idx = glvolumes_new.size(); + glvolumes_new.emplace_back(volume); + } } } - _update_gizmos_data(); + if (m_reload_delayed) + return; + + set_bed_shape(dynamic_cast(m_config->option("bed_shape"))->values); if (m_regenerate_volumes) { + m_volumes.volumes = std::move(glvolumes_new); + for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++ obj_idx) { + const ModelObject &model_object = *m_model->objects[obj_idx]; + // Object will share a single common layer height texture between all printable volumes. + std::shared_ptr layer_height_texture; + for (int volume_idx = 0; volume_idx < (int)model_object.volumes.size(); ++ volume_idx) { + const ModelVolume &model_volume = *model_object.volumes[volume_idx]; + for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) { + const ModelInstance &model_instance = *model_object.instances[instance_idx]; + ModelVolumeState key(model_volume.id(), model_instance.id()); + auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); + assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); + if (it->new_geometry()) { + // New volume. + if (model_volume.is_model_part() && ! layer_height_texture) { + // New object part needs to have the layer height texture assigned, which is shared with the other volumes of the same part. + // Search for the layer height texture in the other volumes. + for (int iv = volume_idx; iv < (int)model_object.volumes.size(); ++ iv) { + const ModelVolume &mv = *model_object.volumes[iv]; + if (mv.is_model_part()) + for (int ii = instance_idx; ii < (int)model_object.instances.size(); ++ ii) { + const ModelInstance &mi = *model_object.instances[ii]; + ModelVolumeState key(mv.id(), mi.id()); + auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); + assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); + if (! it->new_geometry()) { + // Found an old printable GLVolume (existing before this function was called). + assert(m_volumes.volumes[it->volume_idx]->geometry_id == key.geometry_id); + // Reuse the layer height texture. + const GLVolume *volume = m_volumes.volumes[it->volume_idx]; + assert(volume->layer_height_texture); + layer_height_texture = volume->layer_height_texture; + goto iv_end; + } + } + } + iv_end: + if (! layer_height_texture) + layer_height_texture = std::make_shared(); + } + m_volumes.load_object_volume(&model_object, layer_height_texture, obj_idx, volume_idx, instance_idx, m_color_by, m_use_VBOs && m_initialized); + m_volumes.volumes.back()->geometry_id = key.geometry_id; + } else { + // Recycling an old GLVolume. + GLVolume &existing_volume = *m_volumes.volumes[it->volume_idx]; + assert(existing_volume.geometry_id == key.geometry_id); + // Update the Object/Volume/Instance indices into the current Model. + existing_volume.composite_id = it->composite_id; + if (model_volume.is_model_part() && ! layer_height_texture) { + assert(existing_volume.layer_height_texture); + // cache its layer height texture + layer_height_texture = existing_volume.layer_height_texture; + } + } + } + } + } + if (printer_technology == ptSLA) { + size_t idx = 0; + for (const SLAPrintObject *print_object : m_sla_print->objects()) { + SLASupportState &state = sla_support_state[idx ++]; + const ModelObject *model_object = print_object->model_object(); + // Find an index of the ModelObject + int object_idx; + if (std::all_of(state.step.begin(), state.step.end(), [](const PrintStateBase::StateWithTimeStamp &state){ return state.state != PrintStateBase::DONE; })) + continue; + // There may be new SLA volumes added to the scene for this print_object. + // Find the object index of this print_object in the Model::objects list. + auto it = std::find(m_sla_print->model().objects.begin(), m_sla_print->model().objects.end(), model_object); + assert(it != m_sla_print->model().objects.end()); + object_idx = it - m_sla_print->model().objects.begin(); + // Collect indices of this print_object's instances, for which the SLA support meshes are to be added to the scene. + // pairs of + std::vector> instances[std::tuple_size::value]; + for (size_t print_instance_idx = 0; print_instance_idx < print_object->instances().size(); ++ print_instance_idx) { + const SLAPrintObject::Instance &instance = print_object->instances()[print_instance_idx]; + // Find index of ModelInstance corresponding to this SLAPrintObject::Instance. + auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), + [&instance](const ModelInstance *mi) { return mi->id() == instance.instance_id; }); + assert(it != model_object->instances.end()); + int instance_idx = it - model_object->instances.begin(); + for (size_t istep = 0; istep < sla_steps.size(); ++ istep) + if (state.step[istep].state == PrintStateBase::DONE) { + ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); + auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); + assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); + if (it->new_geometry()) + instances[istep].emplace_back(std::pair(instance_idx, print_instance_idx)); + else + // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. + m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, -1, instance_idx); + } + } + + // stores the current volumes count + size_t volumes_count = m_volumes.volumes.size(); + + for (size_t istep = 0; istep < sla_steps.size(); ++istep) + if (!instances[istep].empty()) + m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_use_VBOs && m_initialized); + + if (volumes_count != m_volumes.volumes.size()) + { + // If any volume has been added + // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed + Vec3d shift_z(0.0, 0.0, print_object->get_elevation()); + for (GLVolume* volume : m_volumes.volumes) + { + if (volume->object_idx() == object_idx) + volume->set_instance_offset(volume->get_instance_offset() + shift_z); + } + } + } + } + if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) { // Should the wipe tower be visualized ? @@ -3750,7 +3981,14 @@ void GLCanvas3D::reload_scene(bool force) } update_volumes_colors_by_extruder(); - } + // Update selection indices based on the old/new GLVolumeCollection. + m_selection.volumes_changed(map_glvolume_old_to_new); + } + + _update_gizmos_data(); + + // Update the toolbar + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); // checks for geometry outside the print volume to render it accordingly if (!m_volumes.empty()) @@ -3781,6 +4019,8 @@ void GLCanvas3D::reload_scene(bool force) // restore to default value m_regenerate_volumes = true; + // and force this canvas to be redrawn. + m_dirty = true; } void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d74fee764..4e7a2034a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -482,6 +482,9 @@ public: void add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection = true); void remove_volume(unsigned int object_idx, unsigned int volume_idx); + // Update the selection based on the map from old indices to new indices after m_volumes changed. + // If the current selection is by instance, this call may select newly added volumes, if they belong to already selected instances. + void volumes_changed(const std::vector &map_volume_old_to_new); void clear(); bool is_empty() const { return m_type == Empty; } @@ -700,6 +703,7 @@ private: SLAPrint* m_sla_print; Model* m_model; + // Screen is only refreshed from the OnIdle handler if it is dirty. bool m_dirty; bool m_initialized; bool m_use_VBOs; @@ -814,9 +818,6 @@ public: std::vector load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs); std::vector load_object(const Model& model, int obj_idx); - // Load SLA support tree and SLA pad meshes into the scene, if available at the respective SLAPrintObject instances. - std::vector load_support_meshes(const Model& model, int obj_idx); - void mirror_selection(Axis axis); void reload_scene(bool force); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 06f1907d8..94b2d32f2 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1306,7 +1306,10 @@ void ObjectList::update_selections() const auto gl_vol = selection.get_volume(idx); if (selection.is_multiple_full_object()) sels.Add(m_objects_model->GetItemById(gl_vol->object_idx())); - else + else if (gl_vol->volume_idx() >= 0) + // Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids + // are not associated with ModelVolumes, but they are temporarily generated by the backend + // (for example, SLA supports or SLA pad). sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3b44f0c83..bbb3aac88 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -65,8 +65,7 @@ using Slic3r::Preset; namespace Slic3r { namespace GUI { - -wxDEFINE_EVENT(EVT_PROGRESS_BAR, wxCommandEvent); +wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent); wxDEFINE_EVENT(EVT_SLICING_COMPLETED, wxCommandEvent); wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, wxCommandEvent); @@ -981,8 +980,8 @@ struct Plater::priv void on_notebook_changed(wxBookCtrlEvent&); void on_select_preset(wxCommandEvent&); - void on_progress_event(wxCommandEvent&); - void on_update_print_preview(wxCommandEvent&); + void on_slicing_update(SlicingStatusEvent&); + void on_slicing_completed(wxCommandEvent&); void on_process_completed(wxCommandEvent&); void on_layer_editing_toggled(bool enable); @@ -1040,21 +1039,18 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) background_process.set_fff_print(&print); background_process.set_sla_print(&sla_print); background_process.set_gcode_preview_data(&gcode_preview_data); - background_process.set_sliced_event(EVT_SLICING_COMPLETED); + background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED); background_process.set_finished_event(EVT_PROCESS_COMPLETED); // Default printer technology for default config. background_process.select_technology(this->printer_technology); // Register progress callback from the Print class to the Platter. - auto statuscb = [this](int percent, const std::string &message) { - wxCommandEvent event(EVT_PROGRESS_BAR); - event.SetInt(percent); - event.SetString(message); - wxQueueEvent(this->q, event.Clone()); + auto statuscb = [this](const Slic3r::PrintBase::SlicingStatus &status) { + wxQueueEvent(this->q, new Slic3r::SlicingStatusEvent(EVT_SLICING_UPDATE, 0, status)); }; print.set_status_callback(statuscb); sla_print.set_status_callback(statuscb); - this->q->Bind(EVT_PROGRESS_BAR, &priv::on_progress_event, this); + this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this); _3DScene::add_canvas(canvas3D); _3DScene::allow_multisample(canvas3D, GLCanvas3DManager::can_multisample()); @@ -1138,7 +1134,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // Preview events: preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); - q->Bind(EVT_SLICING_COMPLETED, &priv::on_update_print_preview, this); + q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this); q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this); // Drop target: @@ -1906,21 +1902,43 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) wxGetApp().plater()->on_config_change(wxGetApp().preset_bundle->full_config()); } -void Plater::priv::on_progress_event(wxCommandEvent &evt) +void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) { - this->statusbar()->set_progress(evt.GetInt()); - this->statusbar()->set_status_text(evt.GetString() + wxString::FromUTF8("…")); + this->statusbar()->set_progress(evt.status.percent); + this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8("…")); + if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SCENE) { + switch (this->printer_technology) { + case ptFFF: + if (this->preview != nullptr) + this->preview->reload_print(); + break; + case ptSLA: + // Refresh the scene lazily by updating only SLA meshes. + //FIXME update SLAPrint? + _3DScene::reload_scene(canvas3D, true); + break; + } + } } -void Plater::priv::on_update_print_preview(wxCommandEvent &) +void Plater::priv::on_slicing_completed(wxCommandEvent &) { - if (this->preview != nullptr) - this->preview->reload_print(); - // in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: -// auto selections = collect_selections(); -// _3DScene::set_objects_selections(canvas3D, selections); -// if (canvas3D) -// _3DScene::reload_scene(canvas3D, true); + switch (this->printer_technology) { + case ptFFF: + if (this->preview != nullptr) + this->preview->reload_print(); + // in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: + // auto selections = collect_selections(); + // _3DScene::set_objects_selections(canvas3D, selections); + // if (canvas3D) + // _3DScene::reload_scene(canvas3D, true); + break; + case ptSLA: + // Refresh the scene lazily by updating only SLA meshes. + //FIXME update SLAPrint? + _3DScene::reload_scene(canvas3D, true); + break; + } } void Plater::priv::on_process_completed(wxCommandEvent &evt) @@ -2567,6 +2585,8 @@ void Plater::on_config_change(const DynamicPrintConfig &config) if (opt_key == "printer_technology") { p->printer_technology = config.opt_enum(opt_key); p->background_process.select_technology(this->printer_technology()); + //FIXME for SLA synchronize + //p->background_process.apply(Model)! } else if (opt_key == "bed_shape") { if (p->canvas3D) _3DScene::set_bed_shape(p->canvas3D, p->config->option(opt_key)->values); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 1599af0c4..4ed00e834 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -411,6 +411,8 @@ const std::vector& Preset::sla_print_options() "support_base_height", "support_critical_angle", "support_max_bridge_length", + "support_object_elevation", + "pad_enable", "pad_wall_thickness", "pad_wall_height", "pad_max_merge_distance", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4c9e56bbd..85220b8ad 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3005,12 +3005,14 @@ void TabSLAPrint::build() optgroup->append_single_option_line("support_pillar_radius"); optgroup->append_single_option_line("support_base_radius"); optgroup->append_single_option_line("support_base_height"); + optgroup->append_single_option_line("support_object_elevation"); optgroup = page->new_optgroup(_(L("Connection of the support sticks and junctions"))); optgroup->append_single_option_line("support_critical_angle"); optgroup->append_single_option_line("support_max_bridge_length"); optgroup = page->new_optgroup(_(L("Pad"))); + optgroup->append_single_option_line("pad_enable"); optgroup->append_single_option_line("pad_wall_thickness"); optgroup->append_single_option_line("pad_wall_height"); optgroup->append_single_option_line("pad_max_merge_distance"); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 5d0b44d47..b80976e65 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -308,7 +308,7 @@ bool PrusaCollapsiblePaneMSW::Create(wxWindow *parent, wxWindowID id, const wxSt m_pPane = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER, wxT("wxCollapsiblePanePane")); - wxColour& clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + wxColour&& clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); m_pDisclosureTriangleButton->SetBackgroundColour(clr); this->SetBackgroundColour(clr); m_pPane->SetBackgroundColour(clr); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index d9f996b27..b035de685 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -200,7 +200,7 @@ DECLARE_VARIANT_OBJECT(PrusaDataViewBitmapText) // PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel // ---------------------------------------------------------------------------- -enum ItemType{ +enum ItemType { itUndef = 0, itObject = 1, itVolume = 2,