mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 12:41:20 -06:00 
			
		
		
		
	Merge branch 'vb_3dscene_partial_update'
This commit is contained in:
		
						commit
						18f14482d0
					
				
					 37 changed files with 1061 additions and 711 deletions
				
			
		|  | @ -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}) | ||||
|  |  | |||
|  | @ -1,2 +1 @@ | |||
| add_subdirectory(slabasebed) | ||||
| add_subdirectory(slasupporttree) | ||||
|  |  | |||
|  | @ -1,2 +0,0 @@ | |||
| add_executable(slasupporttree EXCLUDE_FROM_ALL slasupporttree.cpp) | ||||
| target_link_libraries(slasupporttree libslic3r) | ||||
|  | @ -1,48 +0,0 @@ | |||
| #include <iostream> | ||||
| #include <string> | ||||
| 
 | ||||
| #include <libslic3r.h> | ||||
| #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; | ||||
| } | ||||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ | |||
| #include <ctype.h> | ||||
| #include <sys/types.h> | ||||
| 
 | ||||
| #if !defined(WIN32NATIVE) | ||||
| #if !defined(WIN32NATIVE) || defined(__GNUC__) | ||||
| #  include <sys/time.h> | ||||
| #  include <sys/types.h> | ||||
| #  include <sys/stat.h> | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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<ModelID> 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 */ | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ template class PrintState<PrintObjectStep, posCount>; | |||
| 
 | ||||
| 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<PrintInstances> print_objects_from_model_object(const ModelOb | |||
|     return std::vector<PrintInstances>(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<ModelID> 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); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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<void()> PrintObjectBase::cancel_callback(PrintBase *print) | ||||
|  |  | |||
|  | @ -2,13 +2,11 @@ | |||
| #define slic3r_PrintBase_hpp_ | ||||
| 
 | ||||
| #include "libslic3r.h" | ||||
| #include <atomic> | ||||
| #include <set> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <functional> | ||||
| 
 | ||||
| #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 StepType, size_t COUNT> | ||||
| 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 StepType, size_t COUNT> | ||||
| 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<typename ThrowIfCanceled> | ||||
|     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<typename ThrowIfCanceled> | ||||
| 	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<typename CancelationCallback> | ||||
|     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<typename CancelationCallback, typename StepTypeIterator> | ||||
|     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<typename CancelationCallback> | ||||
|     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<State>          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<void()>  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<void(int, const std::string&)>  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<void(const SlicingStatus&)>  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<void()>  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<CancelStatus>               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<typename PrintStepEnum, const size_t COUNT> | ||||
| 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<typename StepTypeIterator> | ||||
|     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<PrintStepEnum> 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<PrintStepEnum, COUNT> 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<PrintObjectStepEnum, COUNT> 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<typename StepTypeIterator> | ||||
|     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<PrintObjectStepEnum> 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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 = ""; | ||||
|  |  | |||
|  | @ -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()); | ||||
|         } | ||||
|  |  | |||
|  | @ -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<size_t>(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..."; | ||||
|      | ||||
|  |  | |||
|  | @ -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<float> heights = {float(bb.min(Z))}; | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -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<Vec3d> &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<double>()); | ||||
| } | ||||
| 
 | ||||
| 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<double>(); | ||||
| //        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<float> 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; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -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<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign> V;
 | ||||
| //    Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign> F;
 | ||||
| }; | ||||
| 
 | ||||
| //using PointSet = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign>; //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<Vec3d>&); | ||||
| 
 | ||||
| 
 | ||||
| // 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<Vec3d>&); | ||||
| 
 | ||||
| 
 | ||||
| /* ************************************************************************** */ | ||||
|  | @ -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 = {}, | ||||
|  |  | |||
|  | @ -3,6 +3,8 @@ | |||
| #include "SLA/SLABasePool.hpp" | ||||
| 
 | ||||
| #include <tbb/parallel_for.h> | ||||
| #include <boost/log/trivial.hpp> | ||||
| 
 | ||||
| //#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
 | ||||
| 
 | ||||
| #include "I18N.hpp" | ||||
|  | @ -63,7 +65,7 @@ const std::array<std::string, slapsCount> 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<ConfigOptionFloat>("layer_height"); | ||||
|     m_material_config.initial_layer_height.set( | ||||
|                 config.opt<ConfigOptionFloat>("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<float> heights = {gnd}; | ||||
|         for(float h = gnd + ilh; h < gnd + H; h += lh) heights.emplace_back(h); | ||||
| 
 | ||||
|         std::vector<float> 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<SLAPrintObject::Instance>; | ||||
|         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<LayerRef>; | ||||
| 
 | ||||
|         // layers according to quantized height levels
 | ||||
|         std::map<long long, LayerRefs> levels; | ||||
|         std::map<LevelID, LayerRefs> 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<long long>(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<LevelID> 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<long long>(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<ExPolygons> &SLAPrintObject::get_support_slices() const
 | ||||
| //{
 | ||||
| //    // I don't want to return a copy but the points may not exist, so ...
 | ||||
| //    static const std::vector<ExPolygons> dummy_empty;
 | ||||
| 
 | ||||
| //    if(!m_supportdata) return dummy_empty;
 | ||||
| //    return m_supportdata->support_slices;
 | ||||
| //}
 | ||||
| 
 | ||||
| //const std::vector<ExPolygons> &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; | ||||
|  |  | |||
|  | @ -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<Instance>& 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<Vec3d>      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<ExPolygons>& get_support_slices() const;
 | ||||
| //    const std::vector<ExPolygons>& 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<FilePrinterFormat::SLA_PNGZIP>; | ||||
|     using SLAPrinterPtr = std::unique_ptr<SLAPrinter>; | ||||
| 
 | ||||
|     Model                           m_model; | ||||
|     SLAPrinterConfig                m_printer_config; | ||||
|     SLAMaterialConfig               m_material_config; | ||||
|     PrintObjects                    m_objects; | ||||
|  |  | |||
|  | @ -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) | ||||
|         { | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
|  | @ -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<size_t, size_t>(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<int> GLVolumeCollection::load_object( | |||
|     const std::vector<int>  &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<LayersTexture> layer_height_texture = std::make_shared<LayersTexture>(); | ||||
|     std::vector<int> 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<LayersTexture> &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<int> 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<LayersTexture> layer_height_texture = std::make_shared<LayersTexture>(); | ||||
|     const ModelVolume *model_volume = model_object->volumes[volume_idx]; | ||||
| 
 | ||||
|     std::vector<int> 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<int> 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 <instance_idx, print_instance_idx>
 | ||||
|     const std::vector<std::pair<size_t, size_t>> &instances, | ||||
|     SLAPrintObjectStep              milestone, | ||||
|     // Timestamp of the last change of the milestone
 | ||||
|     size_t                          timestamp, | ||||
|     bool                            use_VBOs) | ||||
| { | ||||
|     std::vector<int> 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<SLAPrintObject::Instance> &instances = print_object->instances(); | ||||
|         std::map<ModelID, int> 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<size_t, size_t> &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<size_t, size_t>(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); | ||||
|  |  | |||
|  | @ -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<size_t, size_t> 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<int> load_object_auxiliary( | ||||
|     int load_object_volume( | ||||
|         const ModelObject       *model_object, | ||||
|         const SLAPrintObject    *print_object, | ||||
|         std::shared_ptr<LayersTexture> &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 <instance_idx, print_instance_idx>
 | ||||
|         const std::vector<std::pair<size_t, size_t>> &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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ | |||
| #include "GUI_App.hpp" | ||||
| 
 | ||||
| #include <wx/app.h> | ||||
| #include <wx/event.h> | ||||
| #include <wx/panel.h> | ||||
| #include <wx/stdpaths.h> | ||||
| 
 | ||||
|  | @ -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<SLAZipFmt>(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<std::mutex> 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<std::mutex> 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
 | ||||
|  |  | |||
|  | @ -6,6 +6,8 @@ | |||
| #include <mutex> | ||||
| #include <thread> | ||||
| 
 | ||||
| #include <wx/event.h> | ||||
| 
 | ||||
| #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<BackgroundSlicingProcessStep, bspsCount>   	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
 | ||||
|  |  | |||
|  | @ -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<size_t> &map_volume_old_to_new) | ||||
| { | ||||
|     assert(m_valid); | ||||
| 
 | ||||
|     // 1) Update the selection set.
 | ||||
|     IndicesList list_new; | ||||
|     std::vector<std::pair<unsigned int, unsigned int>> 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<int, int> &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<ItemForDelete> 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<int> GLCanvas3D::load_object(const Model& model, int obj_idx) | |||
|     return std::vector<int>(); | ||||
| } | ||||
| 
 | ||||
| std::vector<int> GLCanvas3D::load_support_meshes(const Model& model, int obj_idx) | ||||
| { | ||||
|     std::vector<int> 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<size_t, size_t>   geometry_id; | ||||
|         GLVolume::CompositeID       composite_id; | ||||
|         // Volume index in the new GLVolume vector.
 | ||||
| 		size_t                      volume_idx; | ||||
|     }; | ||||
|     std::vector<ModelVolumeState> model_volume_state; | ||||
| 	std::vector<ModelVolumeState> 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<SLAPrintObjectStep, 2> SLASteps; | ||||
| 	SLASteps sla_steps = { slaposSupportTree, slaposBasePool }; | ||||
|     struct SLASupportState { | ||||
| 		std::array<PrintStateBase::StateWithTimeStamp, std::tuple_size<SLASteps>::value> step; | ||||
|     }; | ||||
|     // State of the sla_steps for all SLAPrintObjects.
 | ||||
|     std::vector<SLASupportState>   sla_support_state; | ||||
| 
 | ||||
|     set_bed_shape(dynamic_cast<const ConfigOptionPoints*>(m_config->option("bed_shape"))->values); | ||||
|     std::vector<size_t> map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1)); | ||||
|     std::vector<GLVolume*> 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<const ConfigOptionPoints*>(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<LayersTexture> 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<LayersTexture>(); | ||||
|                         } | ||||
|                         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 <instance_idx, print_instance_idx>
 | ||||
| 				std::vector<std::pair<size_t, size_t>> instances[std::tuple_size<SLASteps>::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<size_t, size_t>(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<std::string>& str_tool_colors) | ||||
|  |  | |||
|  | @ -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<size_t> &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<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs); | ||||
|     std::vector<int> 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<int> load_support_meshes(const Model& model, int obj_idx); | ||||
| 
 | ||||
|     void mirror_selection(Axis axis); | ||||
| 
 | ||||
|     void reload_scene(bool force); | ||||
|  |  | |||
|  | @ -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())); | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -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<PrinterTechnology>(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<ConfigOptionPoints>(opt_key)->values); | ||||
|  |  | |||
|  | @ -411,6 +411,8 @@ const std::vector<std::string>& 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", | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -200,7 +200,7 @@ DECLARE_VARIANT_OBJECT(PrusaDataViewBitmapText) | |||
| // PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| 
 | ||||
| enum ItemType{ | ||||
| enum ItemType { | ||||
|     itUndef = 0, | ||||
|     itObject = 1, | ||||
|     itVolume = 2, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bubnikv
						bubnikv