mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	 dbc51a140d
			
		
	
	
		dbc51a140d
		
	
	
	
	
		
			
			Also, PrintBase::SlicingStatus enum had two entries assigned to 0 - removed the one that was never used
		
			
				
	
	
		
			433 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			433 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifndef slic3r_PrintBase_hpp_
 | |
| #define slic3r_PrintBase_hpp_
 | |
| 
 | |
| #include "libslic3r.h"
 | |
| #include <set>
 | |
| #include <vector>
 | |
| #include <string>
 | |
| #include <functional>
 | |
| 
 | |
| // 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
 | |
| #endif
 | |
| #include "tbb/mutex.h"
 | |
| 
 | |
| #include "Model.hpp"
 | |
| #include "PlaceholderParser.hpp"
 | |
| #include "PrintConfig.hpp"
 | |
| 
 | |
| namespace Slic3r {
 | |
| 
 | |
| class CanceledException : public std::exception {
 | |
| public:
 | |
|    const char* what() const throw() { return "Background processing has been canceled"; }
 | |
| };
 | |
| 
 | |
| class PrintStateBase {
 | |
| public:
 | |
|     enum State {
 | |
|         INVALID,
 | |
|         STARTED,
 | |
|         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_started(StepType step, tbb::mutex &mtx) const {
 | |
|         return this->state_with_timestamp(step, mtx).state == STARTED;
 | |
|     }
 | |
| 
 | |
|     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_started_unguarded(StepType step) const {
 | |
|         return this->state_with_timestamp_unguarded(step).state == STARTED;
 | |
|     }
 | |
| 
 | |
|     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() updates its state, which may
 | |
|     // influence the processing step being entered.
 | |
|     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.
 | |
| 	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.
 | |
|     // 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, 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.
 | |
|             // Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
 | |
|             // the working thread to proceed.
 | |
|             cancel();
 | |
|         }
 | |
|         return invalidated;
 | |
|     }
 | |
| 
 | |
|     template<typename CancelationCallback, typename StepTypeIterator>
 | |
|     bool invalidate_multiple(StepTypeIterator step_begin, StepTypeIterator step_end, CancelationCallback cancel) {
 | |
|         bool invalidated = false;
 | |
|         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) {
 | |
|                 printf("Not held!\n");
 | |
|             }
 | |
| #endif
 | |
|             // Raise the mutex, so that the following cancel() callback could cancel
 | |
|             // the background processing.
 | |
|             // Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
 | |
|             // the working thread to proceed.
 | |
|             cancel();
 | |
|         }
 | |
|         return invalidated;
 | |
|     }
 | |
| 
 | |
|     // Make all steps invalid.
 | |
|     // 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(CancelationCallback cancel) {
 | |
|         bool invalidated = false;
 | |
|         for (size_t i = 0; i < COUNT; ++ i) {
 | |
|             StateWithTimeStamp &state = m_state[i];
 | |
|             if (state.state != INVALID) {
 | |
|                 invalidated = true;
 | |
|                 state.state = INVALID;
 | |
|                 state.timestamp = ++ g_last_timestamp;
 | |
|             }
 | |
|         }
 | |
|         if (invalidated)
 | |
|             cancel();
 | |
|         return invalidated;
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     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&            state_mutex(PrintBase *print);
 | |
| 	static std::function<void()>  cancel_callback(PrintBase *print);
 | |
| 
 | |
|     ModelObject                  *m_model_object;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * @brief Printing involves slicing and export of device dependent instructions.
 | |
|  *
 | |
|  * Every technology has a potentially different set of requirements for
 | |
|  * slicing, support structures and output print instructions. The pipeline
 | |
|  * however remains roughly the same:
 | |
|  *      slice -> convert to instructions -> send to printer
 | |
|  *
 | |
|  * The PrintBase class will abstract this flow for different technologies.
 | |
|  *
 | |
|  */
 | |
| class PrintBase
 | |
| {
 | |
| public:
 | |
| 	PrintBase() : m_placeholder_parser(&m_full_print_config) { this->restart(); }
 | |
|     inline virtual ~PrintBase() {}
 | |
| 
 | |
|     virtual PrinterTechnology technology() const noexcept = 0;
 | |
| 
 | |
|     // Reset the print status including the copy of the Model / ModelObject hierarchy.
 | |
|     virtual void            clear() = 0;
 | |
|     // The Print is empty either after clear() or after apply() over an empty model,
 | |
|     // or after apply() over a model, where no object is printable (all outside the print volume).
 | |
|     virtual bool            empty() const = 0;
 | |
| 
 | |
|     // Validate the print, return empty string if valid, return error if process() cannot (or should not) be started.
 | |
|     virtual std::string     validate() const { return std::string(); }
 | |
| 
 | |
|     enum ApplyStatus {
 | |
|         // No change after the Print::apply() call.
 | |
|         APPLY_STATUS_UNCHANGED,
 | |
|         // Some of the Print / PrintObject / PrintObjectInstance data was changed,
 | |
|         // but no result was invalidated (only data influencing not yet calculated results were changed).
 | |
|         APPLY_STATUS_CHANGED,
 | |
|         // Some data was changed, which in turn invalidated already calculated steps.
 | |
|         APPLY_STATUS_INVALIDATED,
 | |
|     };
 | |
|     virtual ApplyStatus     apply(const Model &model, DynamicPrintConfig config) = 0;
 | |
|     const Model&            model() const { return m_model; }
 | |
| 
 | |
|     struct TaskParams {
 | |
| 		TaskParams() : single_model_object(0), single_model_instance_only(false), to_object_step(-1), to_print_step(-1) {}
 | |
|         // If non-empty, limit the processing to this ModelObject.
 | |
|         ObjectID                single_model_object;
 | |
| 		// If set, only process single_model_object. Otherwise process everything, but single_model_object first.
 | |
| 		bool					single_model_instance_only;
 | |
|         // If non-negative, stop processing at the successive object step.
 | |
|         int                     to_object_step;
 | |
|         // If non-negative, stop processing at the successive print step.
 | |
|         int                     to_print_step;
 | |
|     };
 | |
|     // After calling the apply() function, call set_task() to limit the task to be processed by process().
 | |
|     virtual void            set_task(const TaskParams ¶ms) {}
 | |
|     // Perform the calculation. This is the only method that is to be called at a worker thread.
 | |
|     virtual void            process() = 0;
 | |
|     // Clean up after process() finished, either with success, error or if canceled.
 | |
|     // The adjustments on the Print / PrintObject data due to set_task() are to be reverted here.
 | |
|     virtual void            finalize() {}
 | |
| 
 | |
|     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 {
 | |
|             DEFAULT                         = 0,
 | |
|             RELOAD_SCENE                    = 1 << 1,
 | |
|             RELOAD_SLA_SUPPORT_POINTS       = 1 << 2,
 | |
|             RELOAD_SLA_PREVIEW              = 1 << 3,
 | |
|         };
 | |
|         // 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 = [](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, unsigned int flags = SlicingStatus::DEFAULT) {
 | |
| 		if (m_status_callback) m_status_callback(SlicingStatus(percent, message, flags));
 | |
|         else printf("%d => %s\n", percent, message.c_str());
 | |
|     }
 | |
| 
 | |
|     typedef std::function<void()>  cancel_callback_type;
 | |
|     // Various methods will call this callback to stop the background processing (the Print::process() call)
 | |
|     // in case a successive change of the Print / PrintObject / PrintRegion instances changed
 | |
|     // the state of the finished or running calculations.
 | |
|     void                       set_cancel_callback(cancel_callback_type cancel_callback) { m_cancel_callback = cancel_callback; }
 | |
|     // Has the calculation been canceled?
 | |
| 	enum CancelStatus {
 | |
| 		// No cancelation, background processing should run.
 | |
| 		NOT_CANCELED = 0,
 | |
| 		// Canceled by user from the user interface (user pressed the "Cancel" button or user closed the application).
 | |
| 		CANCELED_BY_USER = 1,
 | |
| 		// Canceled internally from Print::apply() through the Print/PrintObject::invalidate_step() or ::invalidate_all_steps().
 | |
| 		CANCELED_INTERNAL = 2
 | |
| 	};
 | |
|     CancelStatus               cancel_status() const { return m_cancel_status; }
 | |
|     // Has the calculation been canceled?
 | |
| 	bool                       canceled() const { return m_cancel_status != NOT_CANCELED; }
 | |
|     // Cancel the running computation. Stop execution of all the background threads.
 | |
| 	void                       cancel() { m_cancel_status = CANCELED_BY_USER; }
 | |
| 	void                       cancel_internal() { m_cancel_status = CANCELED_INTERNAL; }
 | |
|     // Cancel the running computation. Stop execution of all the background threads.
 | |
| 	void                       restart() { m_cancel_status = NOT_CANCELED; }
 | |
|     // Returns true if the last step was finished with success.
 | |
|     virtual bool               finished() const = 0;
 | |
| 
 | |
|     const PlaceholderParser&   placeholder_parser() const { return m_placeholder_parser; }
 | |
|     const DynamicPrintConfig&  full_print_config() const { return m_full_print_config; }
 | |
| 
 | |
|     virtual std::string        output_filename(const std::string &filename_base = std::string()) const = 0;
 | |
|     // If the filename_base is set, it is used as the input for the template processing. In that case the path is expected to be the directory (may be empty).
 | |
|     // If filename_set is empty, than the path may be a file or directory. If it is a file, then the macro will not be processed.
 | |
|     std::string                output_filepath(const std::string &path, const std::string &filename_base = std::string()) const;
 | |
| 
 | |
| protected:
 | |
| 	friend class PrintObjectBase;
 | |
|     friend class BackgroundSlicingProcess;
 | |
| 
 | |
|     tbb::mutex&            state_mutex() const { return m_state_mutex; }
 | |
|     std::function<void()>  cancel_callback() { return m_cancel_callback; }
 | |
| 	void				   call_cancel_callback() { m_cancel_callback(); }
 | |
| 
 | |
|     // 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() const { if (m_cancel_status) throw CanceledException(); }
 | |
| 
 | |
|     // To be called by this->output_filename() with the format string pulled from the configuration layer.
 | |
|     std::string            output_filename(const std::string &format, const std::string &default_ext, const std::string &filename_base, const DynamicConfig *config_override = nullptr) const;
 | |
|     // Update "scale", "input_filename", "input_filename_base" placeholders from the current printable ModelObjects.
 | |
|     void                   update_object_placeholders(DynamicConfig &config, const std::string &default_ext) const;
 | |
| 
 | |
| 	Model                                   m_model;
 | |
| 	DynamicPrintConfig						m_full_print_config;
 | |
|     PlaceholderParser                       m_placeholder_parser;
 | |
| 
 | |
| private:
 | |
|     tbb::atomic<CancelStatus>               m_cancel_status;
 | |
|     // Callback to be evoked regularly to update state of the UI thread.
 | |
|     status_callback_type                    m_status_callback;
 | |
| 
 | |
|     // Callback to be evoked to stop the background processing before a state is updated.
 | |
|     cancel_callback_type                    m_cancel_callback = [](){};
 | |
| 
 | |
|     // 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_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, this->state_mutex()); }
 | |
| 	PrintStateBase::StateWithTimeStamp step_state_with_timestamp(PrintStepEnum step) const { return m_state.state_with_timestamp(step, this->state_mutex()); }
 | |
| 
 | |
| protected:
 | |
|     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_callback()); }
 | |
|     template<typename StepTypeIterator>
 | |
|     bool            invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end) 
 | |
|         { 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_callback()); }
 | |
|     bool            invalidate_all_steps() 
 | |
|         { return m_state.invalidate_all(this->cancel_callback()); }
 | |
| 
 | |
| 	bool            is_step_started_unguarded(PrintStepEnum step) const { return m_state.is_started_unguarded(step); }
 | |
| 	bool            is_step_done_unguarded(PrintStepEnum step) const { return m_state.is_done_unguarded(step); }
 | |
| 
 | |
| private:
 | |
|     PrintState<PrintStepEnum, COUNT> m_state;
 | |
| };
 | |
| 
 | |
| template<typename PrintType, typename PrintObjectStepEnum, const size_t COUNT>
 | |
| class PrintObjectBaseWithState : public PrintObjectBase
 | |
| {
 | |
| public:
 | |
|     PrintType*       print()         { return m_print; }
 | |
|     const PrintType* print() const   { return m_print; }
 | |
| 
 | |
|     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, ModelObject *model_object) : PrintObjectBase(model_object), m_print(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_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_callback(m_print)); }
 | |
|     bool            invalidate_steps(std::initializer_list<PrintObjectStepEnum> il) 
 | |
|         { 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)); }
 | |
| 
 | |
|     bool            is_step_started_unguarded(PrintObjectStepEnum step) const { return m_state.is_started_unguarded(step); }
 | |
|     bool            is_step_done_unguarded(PrintObjectStepEnum step) const { return m_state.is_done_unguarded(step); }
 | |
| 
 | |
| 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;
 | |
| 
 | |
| private:
 | |
|     PrintState<PrintObjectStepEnum, COUNT>   m_state;
 | |
| };
 | |
| 
 | |
| } // namespace Slic3r
 | |
| 
 | |
| #endif /* slic3r_PrintBase_hpp_ */
 |