2018-11-08 13:23:17 +00:00
# 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"
2020-07-14 09:52:34 +00:00
# include "ObjectID.hpp"
2018-11-08 13:23:17 +00:00
# include "Model.hpp"
2018-12-03 12:14:28 +00:00
# include "PlaceholderParser.hpp"
2018-11-08 13:23:17 +00:00
# include "PrintConfig.hpp"
namespace Slic3r {
class CanceledException : public std : : exception {
public :
const char * what ( ) const throw ( ) { return " Background processing has been canceled " ; }
} ;
2018-11-16 17:28:50 +00:00
class PrintStateBase {
2018-11-08 13:23:17 +00:00
public :
enum State {
INVALID ,
STARTED ,
DONE ,
} ;
2018-11-16 17:28:50 +00:00
2020-07-14 09:52:34 +00:00
enum class WarningLevel {
NON_CRITICAL ,
CRITICAL
} ;
2018-11-20 12:22:26 +00:00
typedef size_t TimeStamp ;
2018-11-16 17:28:50 +00:00
// A new unique timestamp is being assigned to the step every time the step changes its state.
struct StateWithTimeStamp
{
StateWithTimeStamp ( ) : state ( INVALID ) , timestamp ( 0 ) { }
2018-11-20 12:22:26 +00:00
State state ;
TimeStamp timestamp ;
2018-11-16 17:28:50 +00:00
} ;
2020-07-14 09:52:34 +00:00
struct Warning
{
2020-07-15 16:03:39 +00:00
// Critical warnings will be displayed on G-code export in a modal dialog, so that the user cannot miss them.
2020-07-14 09:52:34 +00:00
WarningLevel level ;
2020-07-15 16:03:39 +00:00
// If the warning is not current, then it is in an unknown state. It may or may not be valid.
// A current warning will become non-current if its milestone gets invalidated.
// A non-current warning will either become current or it will be removed at the end of a milestone.
bool current ;
// Message to be shown to the user, UTF8, localized.
2020-07-14 09:52:34 +00:00
std : : string message ;
2020-07-15 16:03:39 +00:00
// If message_id == 0, then the message is expected to identify the warning uniquely.
// Otherwise message_id identifies the message. For example, if the message contains a varying number, then
// it cannot itself identify the message type.
int message_id ;
2020-07-14 09:52:34 +00:00
} ;
struct StateWithWarnings : public StateWithTimeStamp
{
2020-07-15 16:03:39 +00:00
void mark_warnings_non_current ( ) { for ( auto & w : warnings ) w . current = false ; }
2020-07-14 09:52:34 +00:00
std : : vector < Warning > warnings ;
} ;
2018-11-16 17:28:50 +00:00
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 ( ) { }
2020-07-14 09:52:34 +00:00
StateWithTimeStamp state_with_timestamp ( StepType step , tbb : : mutex & mtx ) const {
2018-11-16 17:28:50 +00:00
tbb : : mutex : : scoped_lock lock ( mtx ) ;
StateWithTimeStamp state = m_state [ step ] ;
return state ;
}
2020-07-14 09:52:34 +00:00
StateWithWarnings state_with_warnings ( StepType step , tbb : : mutex & mtx ) const {
tbb : : mutex : : scoped_lock lock ( mtx ) ;
StateWithWarnings state = m_state [ step ] ;
return state ;
}
2019-02-21 07:44:07 +00:00
bool is_started ( StepType step , tbb : : mutex & mtx ) const {
return this - > state_with_timestamp ( step , mtx ) . state = = STARTED ;
}
2018-11-16 17:28:50 +00:00
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 ] ;
}
2019-02-21 07:44:07 +00:00
bool is_started_unguarded ( StepType step ) const {
return this - > state_with_timestamp_unguarded ( step ) . state = = STARTED ;
}
2018-11-16 17:28:50 +00:00
bool is_done_unguarded ( StepType step ) const {
return this - > state_with_timestamp_unguarded ( step ) . state = = DONE ;
}
2018-11-08 13:23:17 +00:00
// Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being
// modified by the UI thread.
2019-05-22 14:43:14 +00:00
// This is necessary to block until the Print::apply() updates its state, which may
2018-11-08 13:23:17 +00:00
// influence the processing step being entered.
2018-11-16 17:28:50 +00:00
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 ( ) ;
2020-07-14 09:52:34 +00:00
# ifndef NDEBUG
// The following test is not necessarily valid after the background processing thread
// is stopped with throw_if_canceled(), as the CanceledException is not being catched
// by the Print or PrintObject to update m_step_active or m_state[...].state.
// This should not be a problem as long as the caller calls set_started() / set_done() /
// active_step_add_warning() consistently. From the robustness point of view it would be
// be better to catch CanceledException and do the updates. From the performance point of view,
// the current implementation is optimal.
//
// assert(m_step_active == -1);
// for (int i = 0; i < int(COUNT); ++ i)
// assert(m_state[i].state != STARTED);
# endif // NDEBUG
2018-11-16 17:28:50 +00:00
if ( m_state [ step ] . state = = DONE )
return false ;
2020-07-14 09:52:34 +00:00
PrintStateBase : : StateWithWarnings & state = m_state [ step ] ;
state . state = STARTED ;
state . timestamp = + + g_last_timestamp ;
2020-07-15 16:03:39 +00:00
state . mark_warnings_non_current ( ) ;
2020-07-14 09:52:34 +00:00
m_step_active = static_cast < int > ( step ) ;
2018-11-16 17:28:50 +00:00
return true ;
2018-11-08 13:23:17 +00:00
}
// Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being
// modified by the UI thread.
2020-07-15 16:03:39 +00:00
// Return value:
// Timestamp when this stepentered the DONE state.
// bool indicates whether the UI has to update the slicing warnings of this step or not.
2018-11-16 17:28:50 +00:00
template < typename ThrowIfCanceled >
2020-07-15 16:03:39 +00:00
std : : pair < TimeStamp , bool > set_done ( StepType step , tbb : : mutex & mtx , ThrowIfCanceled throw_if_canceled ) {
2018-11-16 17:28:50 +00:00
tbb : : mutex : : scoped_lock lock ( mtx ) ;
// If canceled, throw before changing the step state.
throw_if_canceled ( ) ;
2020-07-14 09:52:34 +00:00
assert ( m_state [ step ] . state = = STARTED ) ;
assert ( m_step_active = = static_cast < int > ( step ) ) ;
PrintStateBase : : StateWithWarnings & state = m_state [ step ] ;
state . state = DONE ;
state . timestamp = + + g_last_timestamp ;
m_step_active = - 1 ;
2020-07-15 16:03:39 +00:00
// Remove all non-current warnings.
auto it = std : : remove_if ( state . warnings . begin ( ) , state . warnings . end ( ) , [ ] ( const auto & w ) { return ! w . current ; } ) ;
bool update_warning_ui = false ;
if ( it ! = state . warnings . end ( ) ) {
state . warnings . erase ( it , state . warnings . end ( ) ) ;
update_warning_ui = true ;
}
return std : : make_pair ( state . timestamp , update_warning_ui ) ;
2018-11-08 13:23:17 +00:00
}
// Make the step invalid.
2018-11-16 17:28:50 +00:00
// PrintBase::m_state_mutex should be locked at this point, guarding access to m_state.
2018-11-08 13:23:17 +00:00
// In case the step has already been entered or finished, cancel the background
// processing by calling the cancel callback.
template < typename CancelationCallback >
2018-11-16 17:28:50 +00:00
bool invalidate ( StepType step , CancelationCallback cancel ) {
bool invalidated = m_state [ step ] . state ! = INVALID ;
2018-11-08 13:23:17 +00:00
if ( invalidated ) {
#if 0
if ( mtx . state ! = mtx . HELD ) {
printf ( " Not held! \n " ) ;
}
# endif
2020-07-14 09:52:34 +00:00
PrintStateBase : : StateWithWarnings & state = m_state [ step ] ;
state . state = INVALID ;
state . timestamp = + + g_last_timestamp ;
2018-11-08 13:23:17 +00:00
// Raise the mutex, so that the following cancel() callback could cancel
// the background processing.
2018-11-16 17:28:50 +00:00
// Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
2020-07-14 09:52:34 +00:00
// the working thread proceed.
2018-11-08 13:23:17 +00:00
cancel ( ) ;
2020-07-14 09:52:34 +00:00
// Now the worker thread should be stopped, therefore it cannot write into the warnings field.
2020-07-15 16:03:39 +00:00
// It is safe to modify it.
state . mark_warnings_non_current ( ) ;
2020-07-14 09:52:34 +00:00
m_step_active = - 1 ;
2018-11-08 13:23:17 +00:00
}
return invalidated ;
}
template < typename CancelationCallback , typename StepTypeIterator >
2018-11-16 17:28:50 +00:00
bool invalidate_multiple ( StepTypeIterator step_begin , StepTypeIterator step_end , CancelationCallback cancel ) {
2018-11-08 13:23:17 +00:00
bool invalidated = false ;
2018-11-16 17:28:50 +00:00
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 ;
}
}
2018-11-08 13:23:17 +00:00
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.
2018-11-16 17:28:50 +00:00
// Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
// the working thread to proceed.
2018-11-08 13:23:17 +00:00
cancel ( ) ;
2020-07-14 09:52:34 +00:00
// Now the worker thread should be stopped, therefore it cannot write into the warnings field.
2020-07-15 16:03:39 +00:00
// It is safe to modify the warnings.
2020-07-14 09:52:34 +00:00
for ( StepTypeIterator it = step_begin ; it ! = step_end ; + + it )
2020-07-15 16:03:39 +00:00
m_state [ * it ] . mark_warnings_non_current ( ) ;
2020-07-14 09:52:34 +00:00
m_step_active = - 1 ;
2018-11-08 13:23:17 +00:00
}
return invalidated ;
}
// Make all steps invalid.
2018-11-16 17:28:50 +00:00
// PrintBase::m_state_mutex should be locked at this point, guarding access to m_state.
2018-11-08 13:23:17 +00:00
// In case any step has already been entered or finished, cancel the background
// processing by calling the cancel callback.
template < typename CancelationCallback >
2018-11-16 17:28:50 +00:00
bool invalidate_all ( CancelationCallback cancel ) {
2018-11-08 13:23:17 +00:00
bool invalidated = false ;
2018-11-16 17:28:50 +00:00
for ( size_t i = 0 ; i < COUNT ; + + i ) {
StateWithTimeStamp & state = m_state [ i ] ;
if ( state . state ! = INVALID ) {
2018-11-08 13:23:17 +00:00
invalidated = true ;
2018-11-16 17:28:50 +00:00
state . state = INVALID ;
state . timestamp = + + g_last_timestamp ;
2018-11-08 13:23:17 +00:00
}
}
2020-07-14 09:52:34 +00:00
if ( invalidated ) {
2018-11-16 17:28:50 +00:00
cancel ( ) ;
2020-07-14 09:52:34 +00:00
// Now the worker thread should be stopped, therefore it cannot write into the warnings field.
2020-07-15 16:03:39 +00:00
// It is safe to modify the warnings.
2020-07-14 09:52:34 +00:00
for ( size_t i = 0 ; i < COUNT ; + + i )
2020-07-15 16:03:39 +00:00
m_state [ i ] . mark_warnings_non_current ( ) ;
2020-07-14 09:52:34 +00:00
m_step_active = - 1 ;
}
2018-11-08 13:23:17 +00:00
return invalidated ;
}
2020-07-15 16:03:39 +00:00
// Update list of warnings of the current milestone with a new warning.
// The warning may already exist in the list, marked as current or not current.
// If it already exists, mark it as current.
// Return value:
// Current milestone (StepType).
// bool indicates whether the UI has to be updated or not.
std : : pair < StepType , bool > active_step_add_warning ( PrintStateBase : : WarningLevel warning_level , const std : : string & message , int message_id , tbb : : mutex & mtx )
2020-07-14 09:52:34 +00:00
{
tbb : : mutex : : scoped_lock lock ( mtx ) ;
assert ( m_step_active ! = - 1 ) ;
2020-07-15 16:03:39 +00:00
StateWithWarnings & state = m_state [ m_step_active ] ;
assert ( state . state = = STARTED ) ;
std : : pair < StepType , bool > retval ( static_cast < StepType > ( m_step_active ) , true ) ;
// Does a warning of the same level and message or message_id exist already?
auto it = ( message_id = = 0 ) ?
std : : find_if ( state . warnings . begin ( ) , state . warnings . end ( ) , [ & message ] ( const auto & w ) { return w . message_id = = 0 & & w . message = = message ; } ) :
std : : find_if ( state . warnings . begin ( ) , state . warnings . end ( ) , [ message_id ] ( const auto & w ) { return w . message_id = = message_id ; } ) ;
if ( it = = state . warnings . end ( ) )
// No, create a new warning and update UI.
state . warnings . emplace_back ( PrintStateBase : : Warning { warning_level , true , message , message_id } ) ;
else if ( it - > message ! = message | | it - > level ! = warning_level ) {
// Yes, however it needs an update.
it - > message = message ;
it - > level = warning_level ;
it - > current = true ;
} else if ( it - > current )
// Yes, and it is current. Don't update UI.
retval . second = false ;
else
// Yes, but it is not current. Mark it as current.
it - > current = true ;
return retval ;
2020-07-14 09:52:34 +00:00
}
2018-11-08 13:23:17 +00:00
private :
2020-07-14 09:52:34 +00:00
StateWithWarnings m_state [ COUNT ] ;
// Active class StepType or -1 if none is active.
// If the background processing is canceled, m_step_active may not be resetted
// to -1, see the comment in this->set_started().
int m_step_active = - 1 ;
2018-11-08 13:23:17 +00:00
} ;
class PrintBase ;
2020-07-21 13:33:28 +00:00
class PrintObjectBase : public ObjectBase
2018-11-08 13:23:17 +00:00
{
2018-11-16 17:28:50 +00:00
public :
const ModelObject * model_object ( ) const { return m_model_object ; }
ModelObject * model_object ( ) { return m_model_object ; }
2018-11-08 13:23:17 +00:00
protected :
2018-11-16 17:28:50 +00:00
PrintObjectBase ( ModelObject * model_object ) : m_model_object ( model_object ) { }
2018-11-08 13:23:17 +00:00
virtual ~ PrintObjectBase ( ) { }
// Declared here to allow access from PrintBase through friendship.
2020-07-15 16:03:39 +00:00
static tbb : : mutex & state_mutex ( PrintBase * print ) ;
static std : : function < void ( ) > cancel_callback ( PrintBase * print ) ;
// Notify UI about a new warning of a milestone "step" on this PrintObjectBase.
// The UI will be notified by calling a status callback registered on print.
// If no status callback is registered, the message is printed to console.
void status_update_warnings ( PrintBase * print , int step , PrintStateBase : : WarningLevel warning_level , const std : : string & message ) ;
2018-11-16 17:28:50 +00:00
ModelObject * m_model_object ;
2018-11-08 13:23:17 +00:00
} ;
/**
* @ 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 .
*
*/
2020-07-21 13:33:28 +00:00
class PrintBase : public ObjectBase
2018-11-08 13:23:17 +00:00
{
public :
2019-07-25 12:39:19 +00:00
PrintBase ( ) : m_placeholder_parser ( & m_full_print_config ) { this - > restart ( ) ; }
2018-11-08 13:23:17 +00:00
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 ;
2018-11-08 19:18:40 +00:00
// 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 ( ) ; }
2018-11-08 13:23:17 +00:00
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 ,
} ;
2019-07-25 12:39:19 +00:00
virtual ApplyStatus apply ( const Model & model , DynamicPrintConfig config ) = 0 ;
2018-11-16 17:28:50 +00:00
const Model & model ( ) const { return m_model ; }
2018-11-08 13:23:17 +00:00
2019-02-21 07:44:07 +00:00
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.
2019-06-27 09:02:45 +00:00
ObjectID single_model_object ;
2019-02-21 07:44:07 +00:00
// 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 ;
} ;
2019-02-21 10:40:56 +00:00
// After calling the apply() function, call set_task() to limit the task to be processed by process().
virtual void set_task ( const TaskParams & params ) { }
// Perform the calculation. This is the only method that is to be called at a worker thread.
2018-11-08 13:23:17 +00:00
virtual void process ( ) = 0 ;
2019-02-21 10:40:56 +00:00
// 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 ( ) { }
2018-11-08 13:23:17 +00:00
2018-11-20 15:27:30 +00:00
struct SlicingStatus {
SlicingStatus ( int percent , const std : : string & text , unsigned int flags = 0 ) : percent ( percent ) , text ( text ) , flags ( flags ) { }
2020-07-14 09:52:34 +00:00
SlicingStatus ( const PrintBase & print , int warning_step ) :
2020-07-21 13:33:28 +00:00
flags ( UPDATE_PRINT_STEP_WARNINGS ) , warning_object_id ( print . id ( ) ) , warning_step ( warning_step ) { }
2020-07-14 09:52:34 +00:00
SlicingStatus ( const PrintObjectBase & print_object , int warning_step ) :
2020-07-21 13:33:28 +00:00
flags ( UPDATE_PRINT_OBJECT_STEP_WARNINGS ) , warning_object_id ( print_object . id ( ) ) , warning_step ( warning_step ) { }
2020-07-14 09:52:34 +00:00
int percent { - 1 } ;
2018-11-20 12:22:26 +00:00
std : : string text ;
// Bitmap of flags.
enum FlagBits {
2020-07-14 09:52:34 +00:00
DEFAULT = 0 ,
RELOAD_SCENE = 1 < < 1 ,
RELOAD_SLA_SUPPORT_POINTS = 1 < < 2 ,
RELOAD_SLA_PREVIEW = 1 < < 3 ,
// UPDATE_PRINT_STEP_WARNINGS is mutually exclusive with UPDATE_PRINT_OBJECT_STEP_WARNINGS.
UPDATE_PRINT_STEP_WARNINGS = 1 < < 4 ,
UPDATE_PRINT_OBJECT_STEP_WARNINGS = 1 < < 5
2018-11-20 12:22:26 +00:00
} ;
// Bitmap of FlagBits
unsigned int flags ;
2020-07-14 09:52:34 +00:00
// set to an ObjectID of a Print or a PrintObject based on flags
// (whether UPDATE_PRINT_STEP_WARNINGS or UPDATE_PRINT_OBJECT_STEP_WARNINGS is set).
ObjectID warning_object_id ;
// For which Print or PrintObject step a new warning is beeing issued?
int warning_step { - 1 } ;
2018-11-20 12:22:26 +00:00
} ;
2018-11-20 15:27:30 +00:00
typedef std : : function < void ( const SlicingStatus & ) > status_callback_type ;
2018-11-08 13:23:17 +00:00
// 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.
2018-11-20 15:27:30 +00:00
void set_status_silent ( ) { m_status_callback = [ ] ( const SlicingStatus & ) { } ; }
2018-11-08 13:23:17 +00:00
// 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.
2018-11-21 11:10:10 +00:00
void set_status ( int percent , const std : : string & message , unsigned int flags = SlicingStatus : : DEFAULT ) {
2018-11-20 15:27:30 +00:00
if ( m_status_callback ) m_status_callback ( SlicingStatus ( percent , message , flags ) ) ;
2018-11-08 13:23:17 +00:00
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.
2018-12-03 12:14:28 +00:00
void set_cancel_callback ( cancel_callback_type cancel_callback ) { m_cancel_callback = cancel_callback ; }
2018-11-08 13:23:17 +00:00
// 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
} ;
2018-12-03 12:14:28 +00:00
CancelStatus cancel_status ( ) const { return m_cancel_status ; }
2018-11-08 13:23:17 +00:00
// Has the calculation been canceled?
2018-12-03 12:14:28 +00:00
bool canceled ( ) const { return m_cancel_status ! = NOT_CANCELED ; }
2018-11-08 13:23:17 +00:00
// Cancel the running computation. Stop execution of all the background threads.
2018-12-03 12:14:28 +00:00
void cancel ( ) { m_cancel_status = CANCELED_BY_USER ; }
void cancel_internal ( ) { m_cancel_status = CANCELED_INTERNAL ; }
2018-11-08 13:23:17 +00:00
// Cancel the running computation. Stop execution of all the background threads.
2018-12-03 12:14:28 +00:00
void restart ( ) { m_cancel_status = NOT_CANCELED ; }
2018-12-11 16:49:31 +00:00
// Returns true if the last step was finished with success.
virtual bool finished ( ) const = 0 ;
2018-12-03 12:14:28 +00:00
const PlaceholderParser & placeholder_parser ( ) const { return m_placeholder_parser ; }
2019-07-25 12:39:19 +00:00
const DynamicPrintConfig & full_print_config ( ) const { return m_full_print_config ; }
2018-12-03 12:14:28 +00:00
2019-05-17 14:27:00 +00:00
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 ;
2018-11-08 13:23:17 +00:00
protected :
friend class PrintObjectBase ;
2018-11-16 17:28:50 +00:00
friend class BackgroundSlicingProcess ;
2018-11-08 13:23:17 +00:00
2018-11-16 17:28:50 +00:00
tbb : : mutex & state_mutex ( ) const { return m_state_mutex ; }
2018-11-08 13:23:17 +00:00
std : : function < void ( ) > cancel_callback ( ) { return m_cancel_callback ; }
2019-02-21 11:39:38 +00:00
void call_cancel_callback ( ) { m_cancel_callback ( ) ; }
2020-07-15 16:03:39 +00:00
// Notify UI about a new warning of a milestone "step" on this PrintBase.
// The UI will be notified by calling a status callback.
// If no status callback is registered, the message is printed to console.
void status_update_warnings ( ObjectID object_id , int step , PrintStateBase : : WarningLevel warning_level , const std : : string & message ) ;
2018-11-08 13:23:17 +00:00
// 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 ( ) ; }
2018-12-03 12:14:28 +00:00
// To be called by this->output_filename() with the format string pulled from the configuration layer.
2019-05-17 14:27:00 +00:00
std : : string output_filename ( const std : : string & format , const std : : string & default_ext , const std : : string & filename_base , const DynamicConfig * config_override = nullptr ) const ;
2018-12-03 12:14:28 +00:00
// Update "scale", "input_filename", "input_filename_base" placeholders from the current printable ModelObjects.
2019-05-17 14:27:00 +00:00
void update_object_placeholders ( DynamicConfig & config , const std : : string & default_ext ) const ;
2018-12-03 12:14:28 +00:00
2018-11-16 17:28:50 +00:00
Model m_model ;
2019-07-25 12:39:19 +00:00
DynamicPrintConfig m_full_print_config ;
2019-07-25 15:08:31 +00:00
PlaceholderParser m_placeholder_parser ;
2018-11-16 17:28:50 +00:00
2018-11-08 13:23:17 +00:00
// Callback to be evoked regularly to update state of the UI thread.
status_callback_type m_status_callback ;
2020-07-14 09:52:34 +00:00
private :
tbb : : atomic < CancelStatus > m_cancel_status ;
2018-11-08 13:23:17 +00:00
// 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.
2018-11-16 17:28:50 +00:00
mutable tbb : : mutex m_state_mutex ;
2018-11-08 13:23:17 +00:00
} ;
template < typename PrintStepEnum , const size_t COUNT >
class PrintBaseWithState : public PrintBase
{
public :
2018-11-16 17:28:50 +00:00
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 ( ) ) ; }
2020-07-14 09:52:34 +00:00
PrintStateBase : : StateWithWarnings step_state_with_warnings ( PrintStepEnum step ) const { return m_state . state_with_warnings ( step , this - > state_mutex ( ) ) ; }
2018-11-08 13:23:17 +00:00
protected :
2018-11-16 17:28:50 +00:00
bool set_started ( PrintStepEnum step ) { return m_state . set_started ( step , this - > state_mutex ( ) , [ this ] ( ) { this - > throw_if_canceled ( ) ; } ) ; }
2020-07-15 16:03:39 +00:00
PrintStateBase : : TimeStamp set_done ( PrintStepEnum step ) {
std : : pair < PrintStateBase : : TimeStamp , bool > status = m_state . set_done ( step , this - > state_mutex ( ) , [ this ] ( ) { this - > throw_if_canceled ( ) ; } ) ;
2020-08-19 15:15:01 +00:00
if ( status . second )
this - > status_update_warnings ( this - > id ( ) , static_cast < int > ( step ) , PrintStateBase : : WarningLevel : : NON_CRITICAL , std : : string ( ) ) ;
return status . first ;
2020-07-15 16:03:39 +00:00
}
2018-11-08 13:23:17 +00:00
bool invalidate_step ( PrintStepEnum step )
2018-11-16 17:28:50 +00:00
{ return m_state . invalidate ( step , this - > cancel_callback ( ) ) ; }
2018-11-08 13:23:17 +00:00
template < typename StepTypeIterator >
bool invalidate_steps ( StepTypeIterator step_begin , StepTypeIterator step_end )
2018-11-16 17:28:50 +00:00
{ return m_state . invalidate_multiple ( step_begin , step_end , this - > cancel_callback ( ) ) ; }
2018-11-08 13:23:17 +00:00
bool invalidate_steps ( std : : initializer_list < PrintStepEnum > il )
2018-11-16 17:28:50 +00:00
{ return m_state . invalidate_multiple ( il . begin ( ) , il . end ( ) , this - > cancel_callback ( ) ) ; }
2018-11-08 13:23:17 +00:00
bool invalidate_all_steps ( )
2018-11-16 17:28:50 +00:00
{ return m_state . invalidate_all ( this - > cancel_callback ( ) ) ; }
2018-11-08 13:23:17 +00:00
2019-02-21 07:44:07 +00:00
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 ) ; }
2020-07-14 09:52:34 +00:00
// Add a slicing warning to the active Print step and send a status notification.
// This method could be called multiple times between this->set_started() and this->set_done().
2020-07-15 16:03:39 +00:00
void active_step_add_warning ( PrintStateBase : : WarningLevel warning_level , const std : : string & message , int message_id = 0 ) {
2020-07-15 16:13:11 +00:00
std : : pair < PrintStepEnum , bool > active_step = m_state . active_step_add_warning ( warning_level , message , message_id , this - > state_mutex ( ) ) ;
2020-07-15 16:03:39 +00:00
if ( active_step . second )
// Update UI.
2020-07-21 13:33:28 +00:00
this - > status_update_warnings ( this - > id ( ) , static_cast < int > ( active_step . first ) , warning_level , message ) ;
2020-07-14 09:52:34 +00:00
}
2018-11-08 13:23:17 +00:00
private :
PrintState < PrintStepEnum , COUNT > m_state ;
} ;
template < typename PrintType , typename PrintObjectStepEnum , const size_t COUNT >
class PrintObjectBaseWithState : public PrintObjectBase
{
public :
2018-11-09 11:02:42 +00:00
PrintType * print ( ) { return m_print ; }
const PrintType * print ( ) const { return m_print ; }
2018-11-08 13:23:17 +00:00
2018-11-16 17:28:50 +00:00
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 ) ) ; }
2020-07-14 09:52:34 +00:00
PrintStateBase : : StateWithWarnings step_state_with_warnings ( PrintObjectStepEnum step ) const { return m_state . state_with_warnings ( step , PrintObjectBase : : state_mutex ( m_print ) ) ; }
2018-11-08 13:23:17 +00:00
protected :
2018-11-16 17:28:50 +00:00
PrintObjectBaseWithState ( PrintType * print , ModelObject * model_object ) : PrintObjectBase ( model_object ) , m_print ( print ) { }
2018-11-08 13:23:17 +00:00
2018-11-16 17:28:50 +00:00
bool set_started ( PrintObjectStepEnum step )
2018-11-20 14:18:46 +00:00
{ return m_state . set_started ( step , PrintObjectBase : : state_mutex ( m_print ) , [ this ] ( ) { this - > throw_if_canceled ( ) ; } ) ; }
2020-07-15 16:03:39 +00:00
PrintStateBase : : TimeStamp set_done ( PrintObjectStepEnum step ) {
std : : pair < PrintStateBase : : TimeStamp , bool > status = m_state . set_done ( step , PrintObjectBase : : state_mutex ( m_print ) , [ this ] ( ) { this - > throw_if_canceled ( ) ; } ) ;
2020-08-19 15:15:01 +00:00
if ( status . second )
this - > status_update_warnings ( m_print , static_cast < int > ( step ) , PrintStateBase : : WarningLevel : : NON_CRITICAL , std : : string ( ) ) ;
return status . first ;
2020-07-15 16:03:39 +00:00
}
2018-11-08 13:23:17 +00:00
bool invalidate_step ( PrintObjectStepEnum step )
2018-11-16 17:28:50 +00:00
{ return m_state . invalidate ( step , PrintObjectBase : : cancel_callback ( m_print ) ) ; }
2018-11-08 13:23:17 +00:00
template < typename StepTypeIterator >
bool invalidate_steps ( StepTypeIterator step_begin , StepTypeIterator step_end )
2018-11-16 17:28:50 +00:00
{ return m_state . invalidate_multiple ( step_begin , step_end , PrintObjectBase : : cancel_callback ( m_print ) ) ; }
2018-11-08 13:23:17 +00:00
bool invalidate_steps ( std : : initializer_list < PrintObjectStepEnum > il )
2018-11-16 17:28:50 +00:00
{ 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 ) ) ; }
2018-11-08 13:23:17 +00:00
2019-02-21 07:44:07 +00:00
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 ) ; }
2020-07-14 09:52:34 +00:00
// Add a slicing warning to the active PrintObject step and send a status notification.
// This method could be called multiple times between this->set_started() and this->set_done().
2020-07-15 16:03:39 +00:00
void active_step_add_warning ( PrintStateBase : : WarningLevel warning_level , const std : : string & message , int message_id = 0 ) {
2020-07-15 16:13:11 +00:00
std : : pair < PrintObjectStepEnum , bool > active_step = m_state . active_step_add_warning ( warning_level , message , message_id , PrintObjectBase : : state_mutex ( m_print ) ) ;
2020-07-15 16:03:39 +00:00
if ( active_step . second )
this - > status_update_warnings ( m_print , static_cast < int > ( active_step . first ) , warning_level , message ) ;
2020-07-14 09:52:34 +00:00
}
2018-11-08 13:23:17 +00:00
protected :
2018-11-20 14:18:46 +00:00
// 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 ( ) ; }
2018-11-08 13:57:42 +00:00
friend PrintType ;
2018-11-08 13:23:17 +00:00
PrintType * m_print ;
private :
PrintState < PrintObjectStepEnum , COUNT > m_state ;
} ;
} // namespace Slic3r
# endif /* slic3r_PrintBase_hpp_ */