Merge remote-tracking branch 'origin/vb_3dscene_partial_update' into tm_sla_supports_backend
# Conflicts: # src/libslic3r/SLAPrint.cpp
This commit is contained in:
commit
ab94391fd0
@ -1467,4 +1467,52 @@ Transform3d ModelInstance::get_matrix(bool dont_translate, bool dont_rotate, boo
|
|||||||
}
|
}
|
||||||
#endif // !ENABLE_MODELVOLUME_TRANSFORM
|
#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_COPY_MOVE_CLONE
|
||||||
#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE
|
#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
|
#endif
|
||||||
|
@ -27,7 +27,7 @@ template class PrintState<PrintObjectStep, posCount>;
|
|||||||
|
|
||||||
void Print::clear()
|
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.
|
// The following call should stop background processing if it is running.
|
||||||
this->invalidate_all_steps();
|
this->invalidate_all_steps();
|
||||||
for (PrintObject *object : m_objects)
|
for (PrintObject *object : m_objects)
|
||||||
@ -43,7 +43,7 @@ void Print::reload_object(size_t /* idx */)
|
|||||||
{
|
{
|
||||||
ModelObjectPtrs model_objects;
|
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.
|
// The following call should stop background processing if it is running.
|
||||||
this->invalidate_all_steps();
|
this->invalidate_all_steps();
|
||||||
/* TODO: this method should check whether the per-object config and per-material configs
|
/* 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())
|
if (m_objects.empty())
|
||||||
return false;
|
return false;
|
||||||
|
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||||
for (const PrintObject *object : m_objects)
|
for (const PrintObject *object : m_objects)
|
||||||
if (!object->m_state.is_done(step))
|
if (! object->m_state.is_done_unguarded(step))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -374,7 +375,7 @@ static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig
|
|||||||
// and have explicit instance positions.
|
// and have explicit instance positions.
|
||||||
void Print::add_model_object(ModelObject* model_object, int idx)
|
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.
|
// Initialize a new print object and store it at the given position.
|
||||||
PrintObject *object = new PrintObject(this, model_object);
|
PrintObject *object = new PrintObject(this, model_object);
|
||||||
if (idx != -1) {
|
if (idx != -1) {
|
||||||
@ -435,7 +436,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
|
|||||||
|
|
||||||
bool Print::apply_config(DynamicPrintConfig config)
|
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
|
// we get a copy of the config object so we can modify it safely
|
||||||
config.normalize();
|
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());
|
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)
|
Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
@ -804,7 +757,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||||||
update_apply_status(false);
|
update_apply_status(false);
|
||||||
|
|
||||||
// Grab the lock for the Print / PrintObject milestones.
|
// 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.
|
// The following call may stop the background processing.
|
||||||
update_apply_status(this->invalidate_state_by_config_options(print_diff));
|
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.";
|
BOOST_LOG_TRIVIAL(info) << "Staring the slicing process.";
|
||||||
for (PrintObject *obj : m_objects)
|
for (PrintObject *obj : m_objects)
|
||||||
obj->make_perimeters();
|
obj->make_perimeters();
|
||||||
this->throw_if_canceled();
|
|
||||||
this->set_status(70, "Infilling layers");
|
this->set_status(70, "Infilling layers");
|
||||||
for (PrintObject *obj : m_objects)
|
for (PrintObject *obj : m_objects)
|
||||||
obj->infill();
|
obj->infill();
|
||||||
this->throw_if_canceled();
|
|
||||||
for (PrintObject *obj : m_objects)
|
for (PrintObject *obj : m_objects)
|
||||||
obj->generate_support_material();
|
obj->generate_support_material();
|
||||||
this->throw_if_canceled();
|
if (this->set_started(psSkirt)) {
|
||||||
if (! this->is_step_done(psSkirt)) {
|
|
||||||
this->set_started(psSkirt);
|
|
||||||
m_skirt.clear();
|
m_skirt.clear();
|
||||||
if (this->has_skirt()) {
|
if (this->has_skirt()) {
|
||||||
this->set_status(88, "Generating skirt");
|
this->set_status(88, "Generating skirt");
|
||||||
@ -1596,9 +1545,7 @@ void Print::process()
|
|||||||
}
|
}
|
||||||
this->set_done(psSkirt);
|
this->set_done(psSkirt);
|
||||||
}
|
}
|
||||||
this->throw_if_canceled();
|
if (this->set_started(psBrim)) {
|
||||||
if (! this->is_step_done(psBrim)) {
|
|
||||||
this->set_started(psBrim);
|
|
||||||
m_brim.clear();
|
m_brim.clear();
|
||||||
if (m_config.brim_width > 0) {
|
if (m_config.brim_width > 0) {
|
||||||
this->set_status(88, "Generating brim");
|
this->set_status(88, "Generating brim");
|
||||||
@ -1606,9 +1553,7 @@ void Print::process()
|
|||||||
}
|
}
|
||||||
this->set_done(psBrim);
|
this->set_done(psBrim);
|
||||||
}
|
}
|
||||||
this->throw_if_canceled();
|
if (this->set_started(psWipeTower)) {
|
||||||
if (! this->is_step_done(psWipeTower)) {
|
|
||||||
this->set_started(psWipeTower);
|
|
||||||
m_wipe_tower_data.clear();
|
m_wipe_tower_data.clear();
|
||||||
if (this->has_wipe_tower()) {
|
if (this->has_wipe_tower()) {
|
||||||
//this->set_status(95, "Generating 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.
|
// It is up to the caller to show an error message.
|
||||||
void Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
|
void Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
|
||||||
{
|
{
|
||||||
// prerequisites
|
|
||||||
this->process();
|
|
||||||
|
|
||||||
// output everything to a G-code file
|
// output everything to a G-code file
|
||||||
// The following call may die if the output_filename_format template substitution fails.
|
// The following call may die if the output_filename_format template substitution fails.
|
||||||
std::string path = this->output_filepath(path_template);
|
std::string path = this->output_filepath(path_template);
|
||||||
|
@ -97,8 +97,6 @@ public:
|
|||||||
|
|
||||||
Vec3crd size; // XYZ in scaled coordinates
|
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 PrintObjectConfig& config() const { return m_config; }
|
||||||
const LayerPtrs& layers() const { return m_layers; }
|
const LayerPtrs& layers() const { return m_layers; }
|
||||||
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
|
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
|
||||||
@ -197,7 +195,6 @@ private:
|
|||||||
void combine_infill();
|
void combine_infill();
|
||||||
void _generate_support_material();
|
void _generate_support_material();
|
||||||
|
|
||||||
ModelObject *m_model_object;
|
|
||||||
PrintObjectConfig m_config;
|
PrintObjectConfig m_config;
|
||||||
// Translation in Z + Rotation + Scaling / Mirroring.
|
// Translation in Z + Rotation + Scaling / Mirroring.
|
||||||
Transform3d m_trafo = Transform3d::Identity();
|
Transform3d m_trafo = Transform3d::Identity();
|
||||||
@ -381,7 +378,6 @@ private:
|
|||||||
// Declared here to have access to Model / ModelObject / ModelInstance
|
// 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);
|
static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src);
|
||||||
|
|
||||||
Model m_model;
|
|
||||||
PrintConfig m_config;
|
PrintConfig m_config;
|
||||||
PrintObjectConfig m_default_object_config;
|
PrintObjectConfig m_default_object_config;
|
||||||
PrintRegionConfig m_default_region_config;
|
PrintRegionConfig m_default_region_config;
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
namespace Slic3r
|
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)
|
std::function<void()> PrintObjectBase::cancel_callback(PrintBase *print)
|
||||||
@ -13,4 +15,9 @@ std::function<void()> PrintObjectBase::cancel_callback(PrintBase *print)
|
|||||||
return print->cancel_callback();
|
return print->cancel_callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrintObjectBase::throw_if_canceled(PrintBase *print)
|
||||||
|
{
|
||||||
|
print->throw_if_canceled();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -2,13 +2,11 @@
|
|||||||
#define slic3r_PrintBase_hpp_
|
#define slic3r_PrintBase_hpp_
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
#include <atomic>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#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.
|
// tbb/mutex.h includes Windows, which in turn defines min/max macros. Convince Windows.h to not define these min/max macros.
|
||||||
#ifndef NOMINMAX
|
#ifndef NOMINMAX
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
@ -25,68 +23,117 @@ public:
|
|||||||
const char* what() const throw() { return "Background processing has been canceled"; }
|
const char* what() const throw() { return "Background processing has been canceled"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// To be instantiated over PrintStep or PrintObjectStep enums.
|
class PrintStateBase {
|
||||||
template <class StepType, size_t COUNT>
|
|
||||||
class PrintState
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i].store(INVALID, std::memory_order_relaxed); }
|
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
INVALID,
|
INVALID,
|
||||||
STARTED,
|
STARTED,
|
||||||
DONE,
|
DONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
// With full memory barrier.
|
// A new unique timestamp is being assigned to the step every time the step changes its state.
|
||||||
bool is_done(StepType step) const { return m_state[step] == DONE; }
|
struct StateWithTimeStamp
|
||||||
|
{
|
||||||
|
StateWithTimeStamp() : state(INVALID), timestamp(0) {}
|
||||||
|
State state;
|
||||||
|
size_t 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
|
// Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being
|
||||||
// modified by the UI thread.
|
// modified by the UI thread.
|
||||||
// This is necessary to block until the Print::apply_config() updates its state, which may
|
// This is necessary to block until the Print::apply_config() updates its state, which may
|
||||||
// influence the processing step being entered.
|
// influence the processing step being entered.
|
||||||
void set_started(StepType step, tbb::mutex &mtx) {
|
template<typename ThrowIfCanceled>
|
||||||
mtx.lock();
|
bool set_started(StepType step, tbb::mutex &mtx, ThrowIfCanceled throw_if_canceled) {
|
||||||
m_state[step].store(STARTED, std::memory_order_relaxed);
|
tbb::mutex::scoped_lock lock(mtx);
|
||||||
mtx.unlock();
|
// 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
|
// Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being
|
||||||
// modified by the UI thread.
|
// modified by the UI thread.
|
||||||
void set_done(StepType step, tbb::mutex &mtx) {
|
template<typename ThrowIfCanceled>
|
||||||
mtx.lock();
|
void set_done(StepType step, tbb::mutex &mtx, ThrowIfCanceled throw_if_canceled) {
|
||||||
m_state[step].store(DONE, std::memory_order_relaxed);
|
tbb::mutex::scoped_lock lock(mtx);
|
||||||
mtx.unlock();
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the step invalid.
|
// 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
|
// In case the step has already been entered or finished, cancel the background
|
||||||
// processing by calling the cancel callback.
|
// processing by calling the cancel callback.
|
||||||
template<typename CancelationCallback>
|
template<typename CancelationCallback>
|
||||||
bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback cancel) {
|
bool invalidate(StepType step, CancelationCallback cancel) {
|
||||||
bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID;
|
bool invalidated = m_state[step].state != INVALID;
|
||||||
if (invalidated) {
|
if (invalidated) {
|
||||||
#if 0
|
#if 0
|
||||||
if (mtx.state != mtx.HELD) {
|
if (mtx.state != mtx.HELD) {
|
||||||
printf("Not held!\n");
|
printf("Not held!\n");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
m_state[step].state = INVALID;
|
||||||
|
m_state[step].timestamp = ++ g_last_timestamp;
|
||||||
// Raise the mutex, so that the following cancel() callback could cancel
|
// Raise the mutex, so that the following cancel() callback could cancel
|
||||||
// the background processing.
|
// the background processing.
|
||||||
mtx.unlock();
|
// Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
|
||||||
|
// the working thread to proceed.
|
||||||
cancel();
|
cancel();
|
||||||
m_state[step] = INVALID;
|
|
||||||
mtx.lock();
|
|
||||||
}
|
}
|
||||||
return invalidated;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CancelationCallback, typename StepTypeIterator>
|
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;
|
bool invalidated = false;
|
||||||
for (StepTypeIterator it = step_begin; ! invalidated && it != step_end; ++ it)
|
for (StepTypeIterator it = step_begin; it != step_end; ++ it) {
|
||||||
invalidated = m_state[*it].load(std::memory_order_relaxed) != INVALID;
|
StateWithTimeStamp &state = m_state[*it];
|
||||||
|
if (state.state != INVALID) {
|
||||||
|
invalidated = true;
|
||||||
|
state.state = INVALID;
|
||||||
|
state.timestamp = ++ g_last_timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (invalidated) {
|
if (invalidated) {
|
||||||
#if 0
|
#if 0
|
||||||
if (mtx.state != mtx.HELD) {
|
if (mtx.state != mtx.HELD) {
|
||||||
@ -95,50 +142,56 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
// Raise the mutex, so that the following cancel() callback could cancel
|
// Raise the mutex, so that the following cancel() callback could cancel
|
||||||
// the background processing.
|
// the background processing.
|
||||||
mtx.unlock();
|
// Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
|
||||||
|
// the working thread to proceed.
|
||||||
cancel();
|
cancel();
|
||||||
for (StepTypeIterator it = step_begin; it != step_end; ++ it)
|
|
||||||
m_state[*it] = INVALID;
|
|
||||||
mtx.lock();
|
|
||||||
}
|
}
|
||||||
return invalidated;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make all steps invalid.
|
// 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
|
// In case any step has already been entered or finished, cancel the background
|
||||||
// processing by calling the cancel callback.
|
// processing by calling the cancel callback.
|
||||||
template<typename CancelationCallback>
|
template<typename CancelationCallback>
|
||||||
bool invalidate_all(tbb::mutex &mtx, CancelationCallback cancel) {
|
bool invalidate_all(CancelationCallback cancel) {
|
||||||
bool invalidated = false;
|
bool invalidated = false;
|
||||||
for (size_t i = 0; i < COUNT; ++ i)
|
for (size_t i = 0; i < COUNT; ++ i) {
|
||||||
if (m_state[i].load(std::memory_order_relaxed) != INVALID) {
|
StateWithTimeStamp &state = m_state[i];
|
||||||
|
if (state.state != INVALID) {
|
||||||
invalidated = true;
|
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;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<State> m_state[COUNT];
|
StateWithTimeStamp m_state[COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
class PrintBase;
|
class PrintBase;
|
||||||
|
|
||||||
class PrintObjectBase
|
class PrintObjectBase
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
const ModelObject* model_object() const { return m_model_object; }
|
||||||
|
ModelObject* model_object() { return m_model_object; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
PrintObjectBase(ModelObject *model_object) : m_model_object(model_object) {}
|
||||||
virtual ~PrintObjectBase() {}
|
virtual ~PrintObjectBase() {}
|
||||||
// Declared here to allow access from PrintBase through friendship.
|
// 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);
|
static std::function<void()> cancel_callback(PrintBase *print);
|
||||||
|
// 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.
|
||||||
|
static void throw_if_canceled(PrintBase *print);
|
||||||
|
|
||||||
|
ModelObject *m_model_object;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,6 +232,7 @@ public:
|
|||||||
APPLY_STATUS_INVALIDATED,
|
APPLY_STATUS_INVALIDATED,
|
||||||
};
|
};
|
||||||
virtual ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) = 0;
|
virtual ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) = 0;
|
||||||
|
const Model& model() const { return m_model; }
|
||||||
|
|
||||||
virtual void process() = 0;
|
virtual void process() = 0;
|
||||||
|
|
||||||
@ -220,8 +274,9 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class PrintObjectBase;
|
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; }
|
std::function<void()> cancel_callback() { return m_cancel_callback; }
|
||||||
void call_cancell_callback() { m_cancel_callback(); }
|
void call_cancell_callback() { m_cancel_callback(); }
|
||||||
|
|
||||||
@ -229,6 +284,8 @@ protected:
|
|||||||
// To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
|
// 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(); }
|
void throw_if_canceled() const { if (m_cancel_status) throw CanceledException(); }
|
||||||
|
|
||||||
|
Model m_model;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
tbb::atomic<CancelStatus> m_cancel_status;
|
tbb::atomic<CancelStatus> m_cancel_status;
|
||||||
// Callback to be evoked regularly to update state of the UI thread.
|
// Callback to be evoked regularly to update state of the UI thread.
|
||||||
@ -240,27 +297,28 @@ private:
|
|||||||
// Mutex used for synchronization of the worker thread with the UI thread:
|
// 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
|
// The mutex will be used to guard the worker thread against entering a stage
|
||||||
// while the data influencing the stage is modified.
|
// 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>
|
template<typename PrintStepEnum, const size_t COUNT>
|
||||||
class PrintBaseWithState : public PrintBase
|
class PrintBaseWithState : public PrintBase
|
||||||
{
|
{
|
||||||
public:
|
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:
|
protected:
|
||||||
void set_started(PrintStepEnum step) { m_state.set_started(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(); }); }
|
||||||
void set_done(PrintStepEnum step) { m_state.set_done(step, this->cancel_mutex()); throw_if_canceled(); }
|
void set_done(PrintStepEnum step) { m_state.set_done(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); }
|
||||||
bool invalidate_step(PrintStepEnum step)
|
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>
|
template<typename StepTypeIterator>
|
||||||
bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
|
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)
|
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()
|
bool invalidate_all_steps()
|
||||||
{ return m_state.invalidate_all(this->cancel_mutex(), this->cancel_callback()); }
|
{ return m_state.invalidate_all(this->cancel_callback()); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PrintState<PrintStepEnum, COUNT> m_state;
|
PrintState<PrintStepEnum, COUNT> m_state;
|
||||||
@ -273,22 +331,27 @@ public:
|
|||||||
PrintType* print() { return m_print; }
|
PrintType* print() { return m_print; }
|
||||||
const PrintType* print() const { 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:
|
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)); }
|
bool set_started(PrintObjectStepEnum step)
|
||||||
void set_done(PrintObjectStepEnum step) { m_state.set_done(step, PrintObjectBase::cancel_mutex(m_print)); }
|
{ return m_state.set_started(step, PrintObjectBase::state_mutex(m_print), [this](){ PrintObjectBase::throw_if_canceled(this->m_print); }); }
|
||||||
|
void set_done(PrintObjectStepEnum step)
|
||||||
|
{ m_state.set_done(step, PrintObjectBase::state_mutex(m_print), [this](){ PrintObjectBase::throw_if_canceled(this->m_print); }); }
|
||||||
|
|
||||||
bool invalidate_step(PrintObjectStepEnum step)
|
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>
|
template<typename StepTypeIterator>
|
||||||
bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
|
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)
|
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)); }
|
{ 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_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
|
bool invalidate_all_steps()
|
||||||
|
{ return m_state.invalidate_all(PrintObjectBase::cancel_callback(m_print)); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend PrintType;
|
friend PrintType;
|
||||||
|
@ -35,9 +35,8 @@
|
|||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
PrintObject::PrintObject(Print* print, ModelObject* model_object) :
|
PrintObject::PrintObject(Print* print, ModelObject* model_object) :
|
||||||
PrintObjectBaseWithState(print),
|
PrintObjectBaseWithState(print, model_object),
|
||||||
typed_slices(false),
|
typed_slices(false),
|
||||||
m_model_object(model_object),
|
|
||||||
size(Vec3crd::Zero()),
|
size(Vec3crd::Zero()),
|
||||||
layer_height_profile_valid(false)
|
layer_height_profile_valid(false)
|
||||||
{
|
{
|
||||||
@ -103,9 +102,8 @@ bool PrintObject::set_copies(const Points &points)
|
|||||||
// this should be idempotent
|
// this should be idempotent
|
||||||
void PrintObject::slice()
|
void PrintObject::slice()
|
||||||
{
|
{
|
||||||
if (this->is_step_done(posSlice))
|
if (! this->set_started(posSlice))
|
||||||
return;
|
return;
|
||||||
this->set_started(posSlice);
|
|
||||||
m_print->set_status(10, "Processing triangulated mesh");
|
m_print->set_status(10, "Processing triangulated mesh");
|
||||||
this->_slice();
|
this->_slice();
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
@ -131,10 +129,9 @@ void PrintObject::make_perimeters()
|
|||||||
// prerequisites
|
// prerequisites
|
||||||
this->slice();
|
this->slice();
|
||||||
|
|
||||||
if (this->is_step_done(posPerimeters))
|
if (! this->set_started(posPerimeters))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this->set_started(posPerimeters);
|
|
||||||
m_print->set_status(20, "Generating perimeters");
|
m_print->set_status(20, "Generating perimeters");
|
||||||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters...";
|
BOOST_LOG_TRIVIAL(info) << "Generating perimeters...";
|
||||||
|
|
||||||
@ -242,10 +239,9 @@ void PrintObject::make_perimeters()
|
|||||||
|
|
||||||
void PrintObject::prepare_infill()
|
void PrintObject::prepare_infill()
|
||||||
{
|
{
|
||||||
if (this->is_step_done(posPrepareInfill))
|
if (! this->set_started(posPrepareInfill))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this->set_started(posPrepareInfill);
|
|
||||||
m_print->set_status(30, "Preparing infill");
|
m_print->set_status(30, "Preparing infill");
|
||||||
|
|
||||||
// This will assign a type (top/bottom/internal) to $layerm->slices.
|
// This will assign a type (top/bottom/internal) to $layerm->slices.
|
||||||
@ -361,8 +357,7 @@ void PrintObject::infill()
|
|||||||
// prerequisites
|
// prerequisites
|
||||||
this->prepare_infill();
|
this->prepare_infill();
|
||||||
|
|
||||||
if (! this->is_step_done(posInfill)) {
|
if (this->set_started(posInfill)) {
|
||||||
this->set_started(posInfill);
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
|
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||||
@ -384,8 +379,7 @@ void PrintObject::infill()
|
|||||||
|
|
||||||
void PrintObject::generate_support_material()
|
void PrintObject::generate_support_material()
|
||||||
{
|
{
|
||||||
if (! this->is_step_done(posSupportMaterial)) {
|
if (this->set_started(posSupportMaterial)) {
|
||||||
this->set_started(posSupportMaterial);
|
|
||||||
this->clear_support_layers();
|
this->clear_support_layers();
|
||||||
if ((m_config.support_material || m_config.raft_layers > 0) && m_layers.size() > 1) {
|
if ((m_config.support_material || m_config.raft_layers > 0) && m_layers.size() > 1) {
|
||||||
m_print->set_status(85, "Generating support material");
|
m_print->set_status(85, "Generating support material");
|
||||||
@ -1706,9 +1700,8 @@ void PrintObject::_simplify_slices(double distance)
|
|||||||
|
|
||||||
void PrintObject::_make_perimeters()
|
void PrintObject::_make_perimeters()
|
||||||
{
|
{
|
||||||
if (this->is_step_done(posPerimeters))
|
if (! this->set_started(posPerimeters))
|
||||||
return;
|
return;
|
||||||
this->set_started(posPerimeters);
|
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters...";
|
BOOST_LOG_TRIVIAL(info) << "Generating perimeters...";
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ const std::array<std::string, slapsCount> PRINT_STEP_LABELS =
|
|||||||
|
|
||||||
void SLAPrint::clear()
|
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.
|
// The following call should stop background processing if it is running.
|
||||||
this->invalidate_all_steps();
|
this->invalidate_all_steps();
|
||||||
|
|
||||||
@ -78,8 +78,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model,
|
|||||||
// return APPLY_STATUS_UNCHANGED;
|
// return APPLY_STATUS_UNCHANGED;
|
||||||
|
|
||||||
// Grab the lock for the Print / PrintObject milestones.
|
// Grab the lock for the Print / PrintObject milestones.
|
||||||
tbb::mutex::scoped_lock lock(this->cancel_mutex());
|
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||||
if(m_objects.empty() && model.objects.empty())
|
if (m_objects.empty() && model.objects.empty() && m_model.objects.empty())
|
||||||
return APPLY_STATUS_UNCHANGED;
|
return APPLY_STATUS_UNCHANGED;
|
||||||
|
|
||||||
// Temporary: just to have to correct layer height for the rasterization
|
// Temporary: just to have to correct layer height for the rasterization
|
||||||
@ -430,8 +430,7 @@ void SLAPrint::process()
|
|||||||
}
|
}
|
||||||
|
|
||||||
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
||||||
Inherited(print),
|
Inherited(print, model_object),
|
||||||
m_model_object(model_object),
|
|
||||||
m_stepmask(slaposCount, true)
|
m_stepmask(slaposCount, true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -456,6 +455,32 @@ const std::vector<ExPolygons> &SLAPrintObject::get_model_slices() const
|
|||||||
return m_model_slices;
|
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 SLAPrintObject::support_mesh() const
|
||||||
{
|
{
|
||||||
TriangleMesh trm;
|
TriangleMesh trm;
|
||||||
|
@ -35,8 +35,6 @@ private: // Prevents erroneous use by other classes.
|
|||||||
using Inherited = _SLAPrintObjectBase;
|
using Inherited = _SLAPrintObjectBase;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const ModelObject* model_object() const { return m_model_object; }
|
|
||||||
ModelObject* model_object() { return m_model_object; }
|
|
||||||
const Transform3d& trafo() const { return m_trafo; }
|
const Transform3d& trafo() const { return m_trafo; }
|
||||||
|
|
||||||
struct Instance {
|
struct Instance {
|
||||||
@ -50,6 +48,9 @@ public:
|
|||||||
};
|
};
|
||||||
const std::vector<Instance>& instances() const { return m_instances; }
|
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.
|
// 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.
|
// Support mesh is only valid if this->is_step_done(slaposSupportTree) is true.
|
||||||
TriangleMesh support_mesh() const;
|
TriangleMesh support_mesh() const;
|
||||||
@ -92,8 +93,6 @@ protected:
|
|||||||
bool invalidate_step(SLAPrintObjectStep step);
|
bool invalidate_step(SLAPrintObjectStep step);
|
||||||
|
|
||||||
private:
|
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.
|
// Object specific configuration, pulled from the configuration layer.
|
||||||
SLAPrintObjectConfig m_config;
|
SLAPrintObjectConfig m_config;
|
||||||
// Translation in Z + Rotation by Y and Z + Scaling / Mirroring.
|
// Translation in Z + Rotation by Y and Z + Scaling / Mirroring.
|
||||||
@ -156,7 +155,6 @@ private:
|
|||||||
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
|
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
|
||||||
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
|
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
|
||||||
|
|
||||||
Model m_model;
|
|
||||||
SLAPrinterConfig m_printer_config;
|
SLAPrinterConfig m_printer_config;
|
||||||
SLAMaterialConfig m_material_config;
|
SLAMaterialConfig m_material_config;
|
||||||
PrintObjects m_objects;
|
PrintObjects m_objects;
|
||||||
|
@ -521,6 +521,7 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& t) const
|
|||||||
|
|
||||||
if (stl.stats.shared_vertices > 0)
|
if (stl.stats.shared_vertices > 0)
|
||||||
{
|
{
|
||||||
|
assert(stl.v_shared != nullptr);
|
||||||
stl_vertex* vertex_ptr = stl.v_shared;
|
stl_vertex* vertex_ptr = stl.v_shared;
|
||||||
for (int i = 0; i < stl.stats.shared_vertices; ++i)
|
for (int i = 0; i < stl.stats.shared_vertices; ++i)
|
||||||
{
|
{
|
||||||
|
@ -65,6 +65,7 @@ public:
|
|||||||
void reset_repair_stats();
|
void reset_repair_stats();
|
||||||
bool needed_repair() const;
|
bool needed_repair() const;
|
||||||
size_t facets_count() const { return this->stl.stats.number_of_facets; }
|
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 true, if there are two and more connected patches in the mesh.
|
||||||
// Returns false, if one or zero connected patch is 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()
|
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);
|
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);
|
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);
|
glDeleteBuffers(1, &this->quad_indices_VBO_id);
|
||||||
|
this->quad_indices_VBO_id = 0;
|
||||||
|
}
|
||||||
this->clear();
|
this->clear();
|
||||||
this->shrink_to_fit();
|
this->shrink_to_fit();
|
||||||
}
|
}
|
||||||
@ -210,9 +216,9 @@ GLVolume::GLVolume(float r, float g, float b, float a)
|
|||||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||||
, m_transformed_convex_hull_bounding_box_dirty(true)
|
, m_transformed_convex_hull_bounding_box_dirty(true)
|
||||||
, m_convex_hull(nullptr)
|
, m_convex_hull(nullptr)
|
||||||
, object_id(-1)
|
, m_convex_hull_owned(false)
|
||||||
, volume_id(-1)
|
// geometry_id == 0 -> invalid
|
||||||
, instance_id(-1)
|
, geometry_id(std::pair<size_t, size_t>(0, 0))
|
||||||
, extruder_id(0)
|
, extruder_id(0)
|
||||||
, selected(false)
|
, selected(false)
|
||||||
, disabled(false)
|
, disabled(false)
|
||||||
@ -234,6 +240,12 @@ GLVolume::GLVolume(float r, float g, float b, float a)
|
|||||||
set_render_color(r, g, b, 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)
|
void GLVolume::set_render_color(float r, float g, float b, float a)
|
||||||
{
|
{
|
||||||
render_color[0] = r;
|
render_color[0] = r;
|
||||||
@ -360,9 +372,10 @@ void GLVolume::set_mirror(Axis axis, double mirror)
|
|||||||
}
|
}
|
||||||
#endif // !ENABLE_MODELVOLUME_TRANSFORM
|
#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
|
#if !ENABLE_MODELVOLUME_TRANSFORM
|
||||||
@ -705,6 +718,25 @@ std::vector<int> GLVolumeCollection::load_object(
|
|||||||
const std::vector<int> &instance_idxs,
|
const std::vector<int> &instance_idxs,
|
||||||
const std::string &color_by,
|
const std::string &color_by,
|
||||||
bool use_VBOs)
|
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] = {
|
static float colors[4][4] = {
|
||||||
{ 1.0f, 1.0f, 0.0f, 1.f },
|
{ 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 }
|
{ 0.5f, 0.5f, 1.0f, 1.f }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Object will have a single common layer height texture for all volumes.
|
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
||||||
std::shared_ptr<LayersTexture> layer_height_texture = std::make_shared<LayersTexture>();
|
|
||||||
|
|
||||||
std::vector<int> volumes_idx;
|
int extruder_id = -1;
|
||||||
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) {
|
if (model_volume->is_model_part())
|
||||||
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
{
|
||||||
|
const ConfigOption *opt = model_volume->config.option("extruder");
|
||||||
int extruder_id = -1;
|
if (opt == nullptr)
|
||||||
if (model_volume->is_model_part())
|
opt = model_object->config.option("extruder");
|
||||||
{
|
extruder_id = (opt == nullptr) ? 0 : opt->getInt();
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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).
|
// Load SLA auxiliary GLVolumes (for support trees or pad).
|
||||||
std::vector<int> GLVolumeCollection::load_object_auxiliary(
|
// This function produces volumes for multiple instances in a single shot,
|
||||||
const ModelObject *model_object,
|
// as some object specific mesh conversions may be expensive.
|
||||||
const SLAPrintObject *print_object,
|
void GLVolumeCollection::load_object_auxiliary(
|
||||||
int obj_idx,
|
const SLAPrintObject *print_object,
|
||||||
SLAPrintObjectStep milestone,
|
int obj_idx,
|
||||||
bool use_VBOs)
|
// 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;
|
assert(print_object->is_step_done(milestone));
|
||||||
// Find the SLAPrintObject's instance to it.
|
// Get the support mesh.
|
||||||
if (print_object->is_step_done(milestone)) {
|
TriangleMesh mesh;
|
||||||
// Get the support mesh.
|
switch (milestone) {
|
||||||
TriangleMesh mesh;
|
case slaposSupportTree: mesh = print_object->support_mesh(); break;
|
||||||
switch (milestone) {
|
case slaposBasePool: mesh = print_object->pad_mesh(); break;
|
||||||
case slaposSupportTree: mesh = print_object->support_mesh(); break;
|
default:
|
||||||
case slaposBasePool: mesh = print_object->pad_mesh(); break;
|
assert(false);
|
||||||
default:
|
}
|
||||||
assert(false);
|
// Convex hull is required for out of print bed detection.
|
||||||
}
|
TriangleMesh convex_hull = mesh.convex_hull_3d();
|
||||||
// Convex hull is required for out of print bed detection.
|
for (const std::pair<size_t, size_t> &instance_idx : instances) {
|
||||||
TriangleMesh convex_hull = mesh.convex_hull_3d();
|
const ModelInstance &model_instance = *print_object->model_object()->instances[instance_idx.first];
|
||||||
const std::vector<SLAPrintObject::Instance> &instances = print_object->instances();
|
const SLAPrintObject::Instance &print_instance = print_object->instances()[instance_idx.second];
|
||||||
std::map<ModelID, int> map_instances;
|
float color[4] { 0.f, 0.f, 1.f, 1.f };
|
||||||
for (int i = 0; i < (int)model_object->instances.size(); ++ i)
|
this->volumes.emplace_back(new GLVolume(color));
|
||||||
map_instances[model_object->instances[i]->id()] = i;
|
GLVolume &v = *this->volumes.back();
|
||||||
for (const SLAPrintObject::Instance &instance : instances) {
|
if (use_VBOs)
|
||||||
auto model_instance_it = map_instances.find(instance.instance_id);
|
v.indexed_vertex_array.load_mesh_full_shading(mesh);
|
||||||
assert(model_instance_it != map_instances.end());
|
else
|
||||||
const int instance_idx = model_instance_it->second;
|
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
|
||||||
const ModelInstance *model_instance = model_object->instances[instance_idx];
|
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
|
||||||
volumes_idx.push_back(int(this->volumes.size()));
|
v.bounding_box = v.indexed_vertex_array.bounding_box();
|
||||||
float color[4] { 0.f, 0.f, 1.f, 1.f };
|
v.indexed_vertex_array.finalize_geometry(use_VBOs);
|
||||||
this->volumes.emplace_back(new GLVolume(color));
|
v.composite_id = GLVolume::CompositeID(obj_idx, -1, (int)instance_idx.first);
|
||||||
GLVolume &v = *this->volumes.back();
|
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
|
||||||
if (use_VBOs)
|
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
|
||||||
v.indexed_vertex_array.load_mesh_full_shading(mesh);
|
v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true);
|
||||||
else
|
v.is_modifier = false;
|
||||||
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
|
v.shader_outside_printer_detection_enabled = true;
|
||||||
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
|
//FIXME adjust with print_instance?
|
||||||
v.bounding_box = v.indexed_vertex_array.bounding_box();
|
v.set_instance_transformation(model_instance.get_transformation());
|
||||||
v.indexed_vertex_array.finalize_geometry(use_VBOs);
|
// Leave the volume transformation at identity.
|
||||||
v.object_id = obj_idx;
|
// v.set_volume_transformation(model_volume->get_transformation());
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return volumes_idx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int GLVolumeCollection::load_wipe_tower_preview(
|
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().
|
// 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.bounding_box = v.indexed_vertex_array.bounding_box();
|
||||||
v.indexed_vertex_array.finalize_geometry(use_VBOs);
|
v.indexed_vertex_array.finalize_geometry(use_VBOs);
|
||||||
v.object_id = obj_idx;
|
v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0);
|
||||||
v.volume_id = 0;
|
|
||||||
v.instance_id = 0;
|
|
||||||
v.is_wipe_tower = true;
|
v.is_wipe_tower = true;
|
||||||
v.shader_outside_printer_detection_enabled = ! size_unknown;
|
v.shader_outside_printer_detection_enabled = ! size_unknown;
|
||||||
return int(this->volumes.size() - 1);
|
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(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(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
|
||||||
|
~GLVolume();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||||
@ -280,7 +281,9 @@ private:
|
|||||||
// Whether or not is needed to recalculate the transformed bounding box.
|
// Whether or not is needed to recalculate the transformed bounding box.
|
||||||
mutable bool m_transformed_bounding_box_dirty;
|
mutable bool m_transformed_bounding_box_dirty;
|
||||||
// Pointer to convex hull of the original mesh, if any.
|
// 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;
|
const TriangleMesh* m_convex_hull;
|
||||||
|
bool m_convex_hull_owned;
|
||||||
// Bounding box of this volume, in unscaled coordinates.
|
// Bounding box of this volume, in unscaled coordinates.
|
||||||
mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box;
|
mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box;
|
||||||
// Whether or not is needed to recalculate the 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];
|
float color[4];
|
||||||
// Color used to render this volume.
|
// Color used to render this volume.
|
||||||
float render_color[4];
|
float render_color[4];
|
||||||
// Object ID, which is equal to the index of the respective ModelObject in Model.objects array.
|
struct CompositeID {
|
||||||
int object_id;
|
CompositeID(int object_id, int volume_id, int instance_id) : object_id(object_id), volume_id(volume_id), instance_id(instance_id) {}
|
||||||
// Volume ID, which is equal to the index of the respective ModelVolume in ModelObject.volumes array.
|
CompositeID() : object_id(-1), volume_id(-1), instance_id(-1) {}
|
||||||
// If negative, it is an index of a geometry produced by the PrintObject for the respective ModelObject,
|
// Object ID, which is equal to the index of the respective ModelObject in Model.objects array.
|
||||||
// and which has no associated ModelVolume in ModelObject.volumes. For example, SLA supports.
|
int object_id;
|
||||||
// Volume with a negative volume_id cannot be picked independently, it will pick the associated instance.
|
// Volume ID, which is equal to the index of the respective ModelVolume in ModelObject.volumes array.
|
||||||
int volume_id;
|
// If negative, it is an index of a geometry produced by the PrintObject for the respective ModelObject,
|
||||||
// Instance ID, which is equal to the index of the respective ModelInstance in ModelObject.instances array.
|
// and which has no associated ModelVolume in ModelObject.volumes. For example, SLA supports.
|
||||||
int instance_id;
|
// 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).
|
// An ID containing the extruder ID (used to select color).
|
||||||
int extruder_id;
|
int extruder_id;
|
||||||
// Is this object selected?
|
// Is this object selected?
|
||||||
@ -412,11 +425,11 @@ public:
|
|||||||
void set_offset(const Vec3d& offset);
|
void set_offset(const Vec3d& offset);
|
||||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
#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 object_idx() const { return this->composite_id.object_id; }
|
||||||
int volume_idx() const { return this->volume_id; }
|
int volume_idx() const { return this->composite_id.volume_id; }
|
||||||
int instance_idx() const { return this->instance_id; }
|
int instance_idx() const { return this->composite_id.instance_id; }
|
||||||
|
|
||||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||||
Transform3d world_matrix() const { return m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); }
|
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,
|
const std::string &color_by,
|
||||||
bool use_VBOs);
|
bool use_VBOs);
|
||||||
|
|
||||||
// Load SLA auxiliary GLVolumes (for support trees or pad).
|
int load_object_volume(
|
||||||
std::vector<int> load_object_auxiliary(
|
|
||||||
const ModelObject *model_object,
|
const ModelObject *model_object,
|
||||||
const SLAPrintObject *print_object,
|
std::shared_ptr<LayersTexture> &layer_height_texture,
|
||||||
int obj_idx,
|
int obj_idx,
|
||||||
SLAPrintObjectStep milestone,
|
int volume_idx,
|
||||||
|
int instance_idx,
|
||||||
|
const std::string &color_by,
|
||||||
bool use_VBOs);
|
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 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);
|
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);
|
||||||
|
|
||||||
|
@ -60,24 +60,21 @@ void BackgroundSlicingProcess::process_fff()
|
|||||||
{
|
{
|
||||||
assert(m_print == m_fff_print);
|
assert(m_print == m_fff_print);
|
||||||
m_print->process();
|
m_print->process();
|
||||||
if (! m_print->canceled()) {
|
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_sliced_id));
|
||||||
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);
|
||||||
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
|
if (this->set_step_started(bspsGCodeFinalize)) {
|
||||||
if (! m_print->canceled() && ! this->is_step_done(bspsGCodeFinalize)) {
|
if (! m_export_path.empty()) {
|
||||||
this->set_step_started(bspsGCodeFinalize);
|
//FIXME localize the messages
|
||||||
if (! m_export_path.empty()) {
|
if (copy_file(m_temp_output_path, m_export_path) != 0)
|
||||||
//FIXME localize the messages
|
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
|
||||||
if (copy_file(m_temp_output_path, m_export_path) != 0)
|
m_print->set_status(95, "Running post-processing scripts");
|
||||||
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
|
run_post_process_scripts(m_export_path, m_fff_print->config());
|
||||||
m_print->set_status(95, "Running post-processing scripts");
|
m_print->set_status(100, "G-code file exported to " + m_export_path);
|
||||||
run_post_process_scripts(m_export_path, m_fff_print->config());
|
} else {
|
||||||
m_print->set_status(100, "G-code file exported to " + m_export_path);
|
m_print->set_status(100, "Slicing complete");
|
||||||
} else {
|
|
||||||
m_print->set_status(100, "Slicing complete");
|
|
||||||
}
|
|
||||||
this->set_step_done(bspsGCodeFinalize);
|
|
||||||
}
|
}
|
||||||
}
|
this->set_step_done(bspsGCodeFinalize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pseudo type for specializing LayerWriter trait class
|
// Pseudo type for specializing LayerWriter trait class
|
||||||
@ -120,11 +117,11 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void BackgroundSlicingProcess::process_sla() {
|
void BackgroundSlicingProcess::process_sla()
|
||||||
|
{
|
||||||
assert(m_print == m_sla_print);
|
assert(m_print == m_sla_print);
|
||||||
m_print->process();
|
m_print->process();
|
||||||
if(!m_print->canceled() && ! this->is_step_done(bspsGCodeFinalize)) {
|
if (this->set_step_started(bspsGCodeFinalize)) {
|
||||||
this->set_step_started(bspsGCodeFinalize);
|
|
||||||
if (! m_export_path.empty()) {
|
if (! m_export_path.empty()) {
|
||||||
m_sla_print->export_raster<SLAZipFmt>(m_export_path);
|
m_sla_print->export_raster<SLAZipFmt>(m_export_path);
|
||||||
m_print->set_status(100, "Zip file exported to " + m_export_path);
|
m_print->set_status(100, "Zip file exported to " + m_export_path);
|
||||||
@ -246,6 +243,7 @@ bool BackgroundSlicingProcess::start()
|
|||||||
|
|
||||||
bool BackgroundSlicingProcess::stop()
|
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);
|
std::unique_lock<std::mutex> lck(m_mutex);
|
||||||
if (m_state == STATE_INITIAL) {
|
if (m_state == STATE_INITIAL) {
|
||||||
// this->m_export_path.clear();
|
// this->m_export_path.clear();
|
||||||
@ -282,12 +280,23 @@ bool BackgroundSlicingProcess::reset()
|
|||||||
// This function shall not trigger any UI update through the wxWidgets event.
|
// This function shall not trigger any UI update through the wxWidgets event.
|
||||||
void BackgroundSlicingProcess::stop_internal()
|
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);
|
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);
|
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) {
|
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();
|
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.
|
// Wait until the background processing stops by being canceled.
|
||||||
m_condition.wait(lck, [this](){ return m_state == STATE_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".
|
// In the "Canceled" state. Reset the state to "Idle".
|
||||||
m_state = STATE_IDLE;
|
m_state = STATE_IDLE;
|
||||||
@ -324,7 +333,7 @@ void BackgroundSlicingProcess::schedule_export(const std::string &path)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Guard against entering the export step before changing the export path.
|
// 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);
|
this->invalidate_step(bspsGCodeFinalize);
|
||||||
m_export_path = path;
|
m_export_path = path;
|
||||||
}
|
}
|
||||||
@ -335,34 +344,35 @@ void BackgroundSlicingProcess::reset_export()
|
|||||||
if (! this->running()) {
|
if (! this->running()) {
|
||||||
m_export_path.clear();
|
m_export_path.clear();
|
||||||
// invalidate_step expects the mutex to be locked.
|
// 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);
|
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);
|
return m_step_state.set_started(step, m_print->state_mutex(), [this](){ this->throw_if_canceled(); });
|
||||||
if (m_print->canceled())
|
|
||||||
throw CanceledException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackgroundSlicingProcess::set_step_done(BackgroundSlicingProcessStep step)
|
void BackgroundSlicingProcess::set_step_done(BackgroundSlicingProcessStep step)
|
||||||
{
|
{
|
||||||
m_step_state.set_done(step, m_step_state_mutex);
|
m_step_state.set_done(step, m_print->state_mutex(), [this](){ this->throw_if_canceled(); });
|
||||||
if (m_print->canceled())
|
}
|
||||||
throw CanceledException();
|
|
||||||
|
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 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;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BackgroundSlicingProcess::invalidate_all_steps()
|
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
|
}; // namespace Slic3r
|
||||||
|
@ -125,11 +125,14 @@ private:
|
|||||||
|
|
||||||
PrintState<BackgroundSlicingProcessStep, bspsCount> m_step_state;
|
PrintState<BackgroundSlicingProcessStep, bspsCount> m_step_state;
|
||||||
mutable tbb::mutex m_step_state_mutex;
|
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);
|
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_step(BackgroundSlicingProcessStep step);
|
||||||
bool invalidate_all_steps();
|
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.
|
// 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_sliced_id = 0;
|
||||||
|
@ -1393,6 +1393,47 @@ void GLCanvas3D::Selection::clear()
|
|||||||
m_bounding_box_dirty = true;
|
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
|
bool GLCanvas3D::Selection::is_single_full_instance() const
|
||||||
{
|
{
|
||||||
if (m_type == SingleFullInstance)
|
if (m_type == SingleFullInstance)
|
||||||
@ -1826,7 +1867,10 @@ void GLCanvas3D::Selection::erase()
|
|||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list)
|
||||||
{
|
{
|
||||||
const GLVolume* v = (*m_volumes)[i];
|
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;
|
std::vector<ItemForDelete> items;
|
||||||
@ -2049,10 +2093,6 @@ void GLCanvas3D::Selection::_set_caches()
|
|||||||
|
|
||||||
void GLCanvas3D::Selection::_add_volume(unsigned int volume_idx)
|
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_list.insert(volume_idx);
|
||||||
(*m_volumes)[volume_idx]->selected = true;
|
(*m_volumes)[volume_idx]->selected = true;
|
||||||
}
|
}
|
||||||
@ -3683,13 +3723,6 @@ std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
|
|||||||
return std::vector<int>();
|
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)
|
void GLCanvas3D::mirror_selection(Axis axis)
|
||||||
{
|
{
|
||||||
m_selection.mirror(axis);
|
m_selection.mirror(axis);
|
||||||
@ -3697,6 +3730,12 @@ void GLCanvas3D::mirror_selection(Axis axis)
|
|||||||
wxGetApp().obj_manipul()->update_settings_value(m_selection);
|
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)
|
void GLCanvas3D::reload_scene(bool force)
|
||||||
{
|
{
|
||||||
if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr))
|
if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr))
|
||||||
@ -3707,39 +3746,215 @@ void GLCanvas3D::reload_scene(bool force)
|
|||||||
return;
|
return;
|
||||||
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
|
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
|
||||||
|
|
||||||
if (m_regenerate_volumes)
|
struct ModelVolumeState {
|
||||||
{
|
ModelVolumeState(const GLVolume *volume) :
|
||||||
reset_volumes();
|
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
|
// SLA steps to pull the preview meshes for.
|
||||||
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
|
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 = ! m_canvas->IsShown() && ! force;
|
||||||
{
|
|
||||||
m_reload_delayed = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_reload_delayed = false;
|
|
||||||
|
|
||||||
PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
|
PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
|
||||||
|
|
||||||
if (m_regenerate_volumes)
|
if (m_regenerate_volumes)
|
||||||
{
|
{
|
||||||
for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++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.
|
||||||
load_object(*m_model, obj_idx);
|
for (int object_idx = 0; object_idx < (int)m_model->objects.size(); ++ object_idx) {
|
||||||
if (printer_technology == ptSLA)
|
const ModelObject *model_object = m_model->objects[object_idx];
|
||||||
load_support_meshes(*m_model, obj_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)
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 (printer_technology == ptFFF && m_config->has("nozzle_diameter"))
|
if (printer_technology == ptFFF && m_config->has("nozzle_diameter"))
|
||||||
{
|
{
|
||||||
// Should the wipe tower be visualized ?
|
// Should the wipe tower be visualized ?
|
||||||
@ -3768,7 +3983,14 @@ void GLCanvas3D::reload_scene(bool force)
|
|||||||
}
|
}
|
||||||
|
|
||||||
update_volumes_colors_by_extruder();
|
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
|
// checks for geometry outside the print volume to render it accordingly
|
||||||
if (!m_volumes.empty())
|
if (!m_volumes.empty())
|
||||||
@ -3799,6 +4021,8 @@ void GLCanvas3D::reload_scene(bool force)
|
|||||||
|
|
||||||
// restore to default value
|
// restore to default value
|
||||||
m_regenerate_volumes = true;
|
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)
|
void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors)
|
||||||
|
@ -496,6 +496,9 @@ public:
|
|||||||
void add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection = true);
|
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);
|
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();
|
void clear();
|
||||||
|
|
||||||
bool is_empty() const { return m_type == Empty; }
|
bool is_empty() const { return m_type == Empty; }
|
||||||
@ -712,6 +715,7 @@ private:
|
|||||||
SLAPrint* m_sla_print;
|
SLAPrint* m_sla_print;
|
||||||
Model* m_model;
|
Model* m_model;
|
||||||
|
|
||||||
|
// Screen is only refreshed from the OnIdle handler if it is dirty.
|
||||||
bool m_dirty;
|
bool m_dirty;
|
||||||
bool m_initialized;
|
bool m_initialized;
|
||||||
bool m_use_VBOs;
|
bool m_use_VBOs;
|
||||||
@ -826,9 +830,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 ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
|
||||||
std::vector<int> load_object(const Model& model, int obj_idx);
|
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 mirror_selection(Axis axis);
|
||||||
|
|
||||||
void reload_scene(bool force);
|
void reload_scene(bool force);
|
||||||
|
@ -1304,7 +1304,10 @@ void ObjectList::update_selections()
|
|||||||
const auto gl_vol = selection.get_volume(idx);
|
const auto gl_vol = selection.get_volume(idx);
|
||||||
if (selection.is_multiple_full_object())
|
if (selection.is_multiple_full_object())
|
||||||
sels.Add(m_objects_model->GetItemById(gl_vol->object_idx()));
|
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()));
|
sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ DECLARE_VARIANT_OBJECT(PrusaDataViewBitmapText)
|
|||||||
// PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel
|
// PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
enum ItemType{
|
enum ItemType {
|
||||||
itUndef = 0,
|
itUndef = 0,
|
||||||
itObject = 1,
|
itObject = 1,
|
||||||
itVolume = 2,
|
itVolume = 2,
|
||||||
|
Loading…
Reference in New Issue
Block a user