mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-27 10:41:15 -06:00
Merge branch 'vb_3dscene_partial_update'
This commit is contained in:
commit
18f14482d0
37 changed files with 1061 additions and 711 deletions
|
|
@ -126,8 +126,16 @@ private:
|
|||
static bool is_end_of_line(char c) { return c == '\r' || c == '\n' || c == 0; }
|
||||
static bool is_end_of_gcode_line(char c) { return c == ';' || is_end_of_line(c); }
|
||||
static bool is_end_of_word(char c) { return is_whitespace(c) || is_end_of_gcode_line(c); }
|
||||
static const char* skip_whitespaces(const char *c) { for (; is_whitespace(*c); ++ c); return c; }
|
||||
static const char* skip_word(const char *c) { for (; ! is_end_of_word(*c); ++ c); return c; }
|
||||
static const char* skip_whitespaces(const char *c) {
|
||||
for (; is_whitespace(*c); ++ c)
|
||||
; // silence -Wempty-body
|
||||
return c;
|
||||
}
|
||||
static const char* skip_word(const char *c) {
|
||||
for (; ! is_end_of_word(*c); ++ c)
|
||||
; // silence -Wempty-body
|
||||
return c;
|
||||
}
|
||||
|
||||
GCodeConfig m_config;
|
||||
char m_extrusion_axis;
|
||||
|
|
|
|||
|
|
@ -1513,4 +1513,52 @@ Transform3d ModelInstance::get_matrix(bool dont_translate, bool dont_rotate, boo
|
|||
}
|
||||
#endif // !ENABLE_MODELVOLUME_TRANSFORM
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
||||
void check_model_ids_validity(const Model &model)
|
||||
{
|
||||
std::set<ModelID> ids;
|
||||
auto check = [&ids](ModelID id) {
|
||||
assert(id.id > 0);
|
||||
assert(ids.find(id) == ids.end());
|
||||
ids.insert(id);
|
||||
};
|
||||
for (const ModelObject *model_object : model.objects) {
|
||||
check(model_object->id());
|
||||
for (const ModelVolume *model_volume : model_object->volumes)
|
||||
check(model_volume->id());
|
||||
for (const ModelInstance *model_instance : model_object->instances)
|
||||
check(model_instance->id());
|
||||
}
|
||||
for (const auto mm : model.materials)
|
||||
check(mm.second->id());
|
||||
}
|
||||
|
||||
void check_model_ids_equal(const Model &model1, const Model &model2)
|
||||
{
|
||||
// Verify whether the IDs of model1 and model match.
|
||||
assert(model1.objects.size() == model2.objects.size());
|
||||
for (size_t idx_model = 0; idx_model < model2.objects.size(); ++ idx_model) {
|
||||
const ModelObject &model_object1 = *model1.objects[idx_model];
|
||||
const ModelObject &model_object2 = * model2.objects[idx_model];
|
||||
assert(model_object1.id() == model_object2.id());
|
||||
assert(model_object1.volumes.size() == model_object2.volumes.size());
|
||||
assert(model_object1.instances.size() == model_object2.instances.size());
|
||||
for (size_t i = 0; i < model_object1.volumes.size(); ++ i)
|
||||
assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id());
|
||||
for (size_t i = 0; i < model_object1.instances.size(); ++ i)
|
||||
assert(model_object1.instances[i]->id() == model_object2.instances[i]->id());
|
||||
}
|
||||
assert(model1.materials.size() == model2.materials.size());
|
||||
{
|
||||
auto it1 = model1.materials.begin();
|
||||
auto it2 = model2.materials.begin();
|
||||
for (; it1 != model1.materials.end(); ++ it1, ++ it2) {
|
||||
assert(it1->first == it2->first); // compare keys
|
||||
assert(it1->second->id() == it2->second->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* _DEBUG */
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -652,6 +652,12 @@ private:
|
|||
#undef MODELBASE_DERIVED_COPY_MOVE_CLONE
|
||||
#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
||||
void check_model_ids_validity(const Model &model);
|
||||
void check_model_ids_equal(const Model &model1, const Model &model2);
|
||||
#endif /* _DEBUG */
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ template class PrintState<PrintObjectStep, posCount>;
|
|||
|
||||
void Print::clear()
|
||||
{
|
||||
tbb::mutex::scoped_lock lock(this->cancel_mutex());
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
// The following call should stop background processing if it is running.
|
||||
this->invalidate_all_steps();
|
||||
for (PrintObject *object : m_objects)
|
||||
|
|
@ -43,7 +43,7 @@ void Print::reload_object(size_t /* idx */)
|
|||
{
|
||||
ModelObjectPtrs model_objects;
|
||||
{
|
||||
tbb::mutex::scoped_lock lock(this->cancel_mutex());
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
// The following call should stop background processing if it is running.
|
||||
this->invalidate_all_steps();
|
||||
/* TODO: this method should check whether the per-object config and per-material configs
|
||||
|
|
@ -271,8 +271,9 @@ bool Print::is_step_done(PrintObjectStep step) const
|
|||
{
|
||||
if (m_objects.empty())
|
||||
return false;
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
for (const PrintObject *object : m_objects)
|
||||
if (!object->m_state.is_done(step))
|
||||
if (! object->m_state.is_done_unguarded(step))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -374,7 +375,7 @@ static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig
|
|||
// and have explicit instance positions.
|
||||
void Print::add_model_object(ModelObject* model_object, int idx)
|
||||
{
|
||||
tbb::mutex::scoped_lock lock(this->cancel_mutex());
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
// Initialize a new print object and store it at the given position.
|
||||
PrintObject *object = new PrintObject(this, model_object);
|
||||
if (idx != -1) {
|
||||
|
|
@ -435,7 +436,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
|
|||
|
||||
bool Print::apply_config(DynamicPrintConfig config)
|
||||
{
|
||||
tbb::mutex::scoped_lock lock(this->cancel_mutex());
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
|
||||
// we get a copy of the config object so we can modify it safely
|
||||
config.normalize();
|
||||
|
|
@ -734,54 +735,6 @@ static std::vector<PrintInstances> print_objects_from_model_object(const ModelOb
|
|||
return std::vector<PrintInstances>(trafos.begin(), trafos.end());
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
||||
static inline void check_model_ids_validity(const Model &model)
|
||||
{
|
||||
std::set<ModelID> ids;
|
||||
auto check = [&ids](ModelID id) {
|
||||
assert(id.id > 0);
|
||||
assert(ids.find(id) == ids.end());
|
||||
ids.insert(id);
|
||||
};
|
||||
for (const ModelObject *model_object : model.objects) {
|
||||
check(model_object->id());
|
||||
for (const ModelVolume *model_volume : model_object->volumes)
|
||||
check(model_volume->id());
|
||||
for (const ModelInstance *model_instance : model_object->instances)
|
||||
check(model_instance->id());
|
||||
}
|
||||
for (const auto mm : model.materials)
|
||||
check(mm.second->id());
|
||||
}
|
||||
|
||||
static inline void check_model_ids_equal(const Model &model1, const Model &model2)
|
||||
{
|
||||
// Verify whether the IDs of model1 and model match.
|
||||
assert(model1.objects.size() == model2.objects.size());
|
||||
for (size_t idx_model = 0; idx_model < model2.objects.size(); ++ idx_model) {
|
||||
const ModelObject &model_object1 = *model1.objects[idx_model];
|
||||
const ModelObject &model_object2 = * model2.objects[idx_model];
|
||||
assert(model_object1.id() == model_object2.id());
|
||||
assert(model_object1.volumes.size() == model_object2.volumes.size());
|
||||
assert(model_object1.instances.size() == model_object2.instances.size());
|
||||
for (size_t i = 0; i < model_object1.volumes.size(); ++ i)
|
||||
assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id());
|
||||
for (size_t i = 0; i < model_object1.instances.size(); ++ i)
|
||||
assert(model_object1.instances[i]->id() == model_object2.instances[i]->id());
|
||||
}
|
||||
assert(model1.materials.size() == model2.materials.size());
|
||||
{
|
||||
auto it1 = model1.materials.begin();
|
||||
auto it2 = model2.materials.begin();
|
||||
for (; it1 != model1.materials.end(); ++ it1, ++ it2) {
|
||||
assert(it1->first == it2->first); // compare keys
|
||||
assert(it1->second->id() == it2->second->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* _DEBUG */
|
||||
|
||||
Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
|
|
@ -804,7 +757,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
update_apply_status(false);
|
||||
|
||||
// Grab the lock for the Print / PrintObject milestones.
|
||||
tbb::mutex::scoped_lock lock(this->cancel_mutex());
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
|
||||
// The following call may stop the background processing.
|
||||
update_apply_status(this->invalidate_state_by_config_options(print_diff));
|
||||
|
|
@ -1579,16 +1532,12 @@ void Print::process()
|
|||
BOOST_LOG_TRIVIAL(info) << "Staring the slicing process.";
|
||||
for (PrintObject *obj : m_objects)
|
||||
obj->make_perimeters();
|
||||
this->throw_if_canceled();
|
||||
this->set_status(70, "Infilling layers");
|
||||
for (PrintObject *obj : m_objects)
|
||||
obj->infill();
|
||||
this->throw_if_canceled();
|
||||
for (PrintObject *obj : m_objects)
|
||||
obj->generate_support_material();
|
||||
this->throw_if_canceled();
|
||||
if (! this->is_step_done(psSkirt)) {
|
||||
this->set_started(psSkirt);
|
||||
if (this->set_started(psSkirt)) {
|
||||
m_skirt.clear();
|
||||
if (this->has_skirt()) {
|
||||
this->set_status(88, "Generating skirt");
|
||||
|
|
@ -1596,9 +1545,7 @@ void Print::process()
|
|||
}
|
||||
this->set_done(psSkirt);
|
||||
}
|
||||
this->throw_if_canceled();
|
||||
if (! this->is_step_done(psBrim)) {
|
||||
this->set_started(psBrim);
|
||||
if (this->set_started(psBrim)) {
|
||||
m_brim.clear();
|
||||
if (m_config.brim_width > 0) {
|
||||
this->set_status(88, "Generating brim");
|
||||
|
|
@ -1606,9 +1553,7 @@ void Print::process()
|
|||
}
|
||||
this->set_done(psBrim);
|
||||
}
|
||||
this->throw_if_canceled();
|
||||
if (! this->is_step_done(psWipeTower)) {
|
||||
this->set_started(psWipeTower);
|
||||
if (this->set_started(psWipeTower)) {
|
||||
m_wipe_tower_data.clear();
|
||||
if (this->has_wipe_tower()) {
|
||||
//this->set_status(95, "Generating wipe tower");
|
||||
|
|
@ -1625,9 +1570,6 @@ void Print::process()
|
|||
// It is up to the caller to show an error message.
|
||||
void Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
|
||||
{
|
||||
// prerequisites
|
||||
this->process();
|
||||
|
||||
// output everything to a G-code file
|
||||
// The following call may die if the output_filename_format template substitution fails.
|
||||
std::string path = this->output_filepath(path_template);
|
||||
|
|
|
|||
|
|
@ -97,8 +97,6 @@ public:
|
|||
|
||||
Vec3crd size; // XYZ in scaled coordinates
|
||||
|
||||
const ModelObject* model_object() const { return m_model_object; }
|
||||
ModelObject* model_object() { return m_model_object; }
|
||||
const PrintObjectConfig& config() const { return m_config; }
|
||||
const LayerPtrs& layers() const { return m_layers; }
|
||||
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
|
||||
|
|
@ -197,7 +195,6 @@ private:
|
|||
void combine_infill();
|
||||
void _generate_support_material();
|
||||
|
||||
ModelObject *m_model_object;
|
||||
PrintObjectConfig m_config;
|
||||
// Translation in Z + Rotation + Scaling / Mirroring.
|
||||
Transform3d m_trafo = Transform3d::Identity();
|
||||
|
|
@ -381,7 +378,6 @@ private:
|
|||
// Declared here to have access to Model / ModelObject / ModelInstance
|
||||
static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src);
|
||||
|
||||
Model m_model;
|
||||
PrintConfig m_config;
|
||||
PrintObjectConfig m_default_object_config;
|
||||
PrintRegionConfig m_default_region_config;
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
namespace Slic3r
|
||||
{
|
||||
|
||||
tbb::mutex& PrintObjectBase::cancel_mutex(PrintBase *print)
|
||||
size_t PrintStateBase::g_last_timestamp = 0;
|
||||
|
||||
tbb::mutex& PrintObjectBase::state_mutex(PrintBase *print)
|
||||
{
|
||||
return print->cancel_mutex();
|
||||
return print->state_mutex();
|
||||
}
|
||||
|
||||
std::function<void()> PrintObjectBase::cancel_callback(PrintBase *print)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@
|
|||
#define slic3r_PrintBase_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include <atomic>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include "tbb/atomic.h"
|
||||
// tbb/mutex.h includes Windows, which in turn defines min/max macros. Convince Windows.h to not define these min/max macros.
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
|
|
@ -25,68 +23,120 @@ public:
|
|||
const char* what() const throw() { return "Background processing has been canceled"; }
|
||||
};
|
||||
|
||||
// To be instantiated over PrintStep or PrintObjectStep enums.
|
||||
template <class StepType, size_t COUNT>
|
||||
class PrintState
|
||||
{
|
||||
class PrintStateBase {
|
||||
public:
|
||||
PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i].store(INVALID, std::memory_order_relaxed); }
|
||||
|
||||
enum State {
|
||||
INVALID,
|
||||
STARTED,
|
||||
DONE,
|
||||
};
|
||||
|
||||
// With full memory barrier.
|
||||
bool is_done(StepType step) const { return m_state[step] == DONE; }
|
||||
|
||||
typedef size_t TimeStamp;
|
||||
|
||||
// A new unique timestamp is being assigned to the step every time the step changes its state.
|
||||
struct StateWithTimeStamp
|
||||
{
|
||||
StateWithTimeStamp() : state(INVALID), timestamp(0) {}
|
||||
State state;
|
||||
TimeStamp timestamp;
|
||||
};
|
||||
|
||||
protected:
|
||||
//FIXME last timestamp is shared between Print & SLAPrint,
|
||||
// and if multiple Print or SLAPrint instances are executed in parallel, modification of g_last_timestamp
|
||||
// is not synchronized!
|
||||
static size_t g_last_timestamp;
|
||||
};
|
||||
|
||||
// To be instantiated over PrintStep or PrintObjectStep enums.
|
||||
template <class StepType, size_t COUNT>
|
||||
class PrintState : public PrintStateBase
|
||||
{
|
||||
public:
|
||||
PrintState() {}
|
||||
|
||||
StateWithTimeStamp state_with_timestamp(StepType step, tbb::mutex &mtx) const {
|
||||
tbb::mutex::scoped_lock lock(mtx);
|
||||
StateWithTimeStamp state = m_state[step];
|
||||
return state;
|
||||
}
|
||||
|
||||
bool is_done(StepType step, tbb::mutex &mtx) const {
|
||||
return this->state_with_timestamp(step, mtx).state == DONE;
|
||||
}
|
||||
|
||||
StateWithTimeStamp state_with_timestamp_unguarded(StepType step) const {
|
||||
return m_state[step];
|
||||
}
|
||||
|
||||
bool is_done_unguarded(StepType step) const {
|
||||
return this->state_with_timestamp_unguarded(step).state == DONE;
|
||||
}
|
||||
|
||||
// Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being
|
||||
// modified by the UI thread.
|
||||
// This is necessary to block until the Print::apply_config() updates its state, which may
|
||||
// influence the processing step being entered.
|
||||
void set_started(StepType step, tbb::mutex &mtx) {
|
||||
mtx.lock();
|
||||
m_state[step].store(STARTED, std::memory_order_relaxed);
|
||||
mtx.unlock();
|
||||
template<typename ThrowIfCanceled>
|
||||
bool set_started(StepType step, tbb::mutex &mtx, ThrowIfCanceled throw_if_canceled) {
|
||||
tbb::mutex::scoped_lock lock(mtx);
|
||||
// If canceled, throw before changing the step state.
|
||||
throw_if_canceled();
|
||||
if (m_state[step].state == DONE)
|
||||
return false;
|
||||
m_state[step].state = STARTED;
|
||||
m_state[step].timestamp = ++ g_last_timestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being
|
||||
// modified by the UI thread.
|
||||
void set_done(StepType step, tbb::mutex &mtx) {
|
||||
mtx.lock();
|
||||
m_state[step].store(DONE, std::memory_order_relaxed);
|
||||
mtx.unlock();
|
||||
template<typename ThrowIfCanceled>
|
||||
TimeStamp set_done(StepType step, tbb::mutex &mtx, ThrowIfCanceled throw_if_canceled) {
|
||||
tbb::mutex::scoped_lock lock(mtx);
|
||||
// If canceled, throw before changing the step state.
|
||||
throw_if_canceled();
|
||||
assert(m_state[step].state != DONE);
|
||||
m_state[step].state = DONE;
|
||||
m_state[step].timestamp = ++ g_last_timestamp;
|
||||
return m_state[step].timestamp;
|
||||
}
|
||||
|
||||
// Make the step invalid.
|
||||
// The provided mutex should be locked at this point, guarding access to m_state.
|
||||
// PrintBase::m_state_mutex should be locked at this point, guarding access to m_state.
|
||||
// In case the step has already been entered or finished, cancel the background
|
||||
// processing by calling the cancel callback.
|
||||
template<typename CancelationCallback>
|
||||
bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback cancel) {
|
||||
bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID;
|
||||
bool invalidate(StepType step, CancelationCallback cancel) {
|
||||
bool invalidated = m_state[step].state != INVALID;
|
||||
if (invalidated) {
|
||||
#if 0
|
||||
if (mtx.state != mtx.HELD) {
|
||||
printf("Not held!\n");
|
||||
}
|
||||
#endif
|
||||
m_state[step].state = INVALID;
|
||||
m_state[step].timestamp = ++ g_last_timestamp;
|
||||
// Raise the mutex, so that the following cancel() callback could cancel
|
||||
// the background processing.
|
||||
mtx.unlock();
|
||||
// Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
|
||||
// the working thread to proceed.
|
||||
cancel();
|
||||
m_state[step] = INVALID;
|
||||
mtx.lock();
|
||||
}
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
template<typename CancelationCallback, typename StepTypeIterator>
|
||||
bool invalidate_multiple(StepTypeIterator step_begin, StepTypeIterator step_end, tbb::mutex &mtx, CancelationCallback cancel) {
|
||||
bool invalidate_multiple(StepTypeIterator step_begin, StepTypeIterator step_end, CancelationCallback cancel) {
|
||||
bool invalidated = false;
|
||||
for (StepTypeIterator it = step_begin; ! invalidated && it != step_end; ++ it)
|
||||
invalidated = m_state[*it].load(std::memory_order_relaxed) != INVALID;
|
||||
for (StepTypeIterator it = step_begin; it != step_end; ++ it) {
|
||||
StateWithTimeStamp &state = m_state[*it];
|
||||
if (state.state != INVALID) {
|
||||
invalidated = true;
|
||||
state.state = INVALID;
|
||||
state.timestamp = ++ g_last_timestamp;
|
||||
}
|
||||
}
|
||||
if (invalidated) {
|
||||
#if 0
|
||||
if (mtx.state != mtx.HELD) {
|
||||
|
|
@ -95,50 +145,53 @@ public:
|
|||
#endif
|
||||
// Raise the mutex, so that the following cancel() callback could cancel
|
||||
// the background processing.
|
||||
mtx.unlock();
|
||||
// Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
|
||||
// the working thread to proceed.
|
||||
cancel();
|
||||
for (StepTypeIterator it = step_begin; it != step_end; ++ it)
|
||||
m_state[*it] = INVALID;
|
||||
mtx.lock();
|
||||
}
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
// Make all steps invalid.
|
||||
// The provided mutex should be locked at this point, guarding access to m_state.
|
||||
// PrintBase::m_state_mutex should be locked at this point, guarding access to m_state.
|
||||
// In case any step has already been entered or finished, cancel the background
|
||||
// processing by calling the cancel callback.
|
||||
template<typename CancelationCallback>
|
||||
bool invalidate_all(tbb::mutex &mtx, CancelationCallback cancel) {
|
||||
bool invalidate_all(CancelationCallback cancel) {
|
||||
bool invalidated = false;
|
||||
for (size_t i = 0; i < COUNT; ++ i)
|
||||
if (m_state[i].load(std::memory_order_relaxed) != INVALID) {
|
||||
for (size_t i = 0; i < COUNT; ++ i) {
|
||||
StateWithTimeStamp &state = m_state[i];
|
||||
if (state.state != INVALID) {
|
||||
invalidated = true;
|
||||
break;
|
||||
state.state = INVALID;
|
||||
state.timestamp = ++ g_last_timestamp;
|
||||
}
|
||||
if (invalidated) {
|
||||
mtx.unlock();
|
||||
cancel();
|
||||
for (size_t i = 0; i < COUNT; ++ i)
|
||||
m_state[i].store(INVALID, std::memory_order_relaxed);
|
||||
mtx.lock();
|
||||
}
|
||||
if (invalidated)
|
||||
cancel();
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<State> m_state[COUNT];
|
||||
StateWithTimeStamp m_state[COUNT];
|
||||
};
|
||||
|
||||
class PrintBase;
|
||||
|
||||
class PrintObjectBase
|
||||
{
|
||||
public:
|
||||
const ModelObject* model_object() const { return m_model_object; }
|
||||
ModelObject* model_object() { return m_model_object; }
|
||||
|
||||
protected:
|
||||
PrintObjectBase(ModelObject *model_object) : m_model_object(model_object) {}
|
||||
virtual ~PrintObjectBase() {}
|
||||
// Declared here to allow access from PrintBase through friendship.
|
||||
static tbb::mutex& cancel_mutex(PrintBase *print);
|
||||
static tbb::mutex& state_mutex(PrintBase *print);
|
||||
static std::function<void()> cancel_callback(PrintBase *print);
|
||||
|
||||
ModelObject *m_model_object;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -179,19 +232,31 @@ public:
|
|||
APPLY_STATUS_INVALIDATED,
|
||||
};
|
||||
virtual ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) = 0;
|
||||
const Model& model() const { return m_model; }
|
||||
|
||||
virtual void process() = 0;
|
||||
|
||||
typedef std::function<void(int, const std::string&)> status_callback_type;
|
||||
struct SlicingStatus {
|
||||
SlicingStatus(int percent, const std::string &text, unsigned int flags = 0) : percent(percent), text(text), flags(flags) {}
|
||||
int percent;
|
||||
std::string text;
|
||||
// Bitmap of flags.
|
||||
enum FlagBits {
|
||||
RELOAD_SCENE = 1,
|
||||
};
|
||||
// Bitmap of FlagBits
|
||||
unsigned int flags;
|
||||
};
|
||||
typedef std::function<void(const SlicingStatus&)> status_callback_type;
|
||||
// Default status console print out in the form of percent => message.
|
||||
void set_status_default() { m_status_callback = nullptr; }
|
||||
// No status output or callback whatsoever, useful mostly for automatic tests.
|
||||
void set_status_silent() { m_status_callback = [](int, const std::string&){}; }
|
||||
void set_status_silent() { m_status_callback = [](const SlicingStatus&){}; }
|
||||
// Register a custom status callback.
|
||||
void set_status_callback(status_callback_type cb) { m_status_callback = cb; }
|
||||
// Calls a registered callback to update the status, or print out the default message.
|
||||
void set_status(int percent, const std::string &message) {
|
||||
if (m_status_callback) m_status_callback(percent, message);
|
||||
void set_status(int percent, const std::string &message, unsigned int flags = 0) {
|
||||
if (m_status_callback) m_status_callback(SlicingStatus(percent, message, flags));
|
||||
else printf("%d => %s\n", percent, message.c_str());
|
||||
}
|
||||
|
||||
|
|
@ -220,8 +285,9 @@ public:
|
|||
|
||||
protected:
|
||||
friend class PrintObjectBase;
|
||||
friend class BackgroundSlicingProcess;
|
||||
|
||||
tbb::mutex& cancel_mutex() { return m_cancel_mutex; }
|
||||
tbb::mutex& state_mutex() const { return m_state_mutex; }
|
||||
std::function<void()> cancel_callback() { return m_cancel_callback; }
|
||||
void call_cancell_callback() { m_cancel_callback(); }
|
||||
|
||||
|
|
@ -229,6 +295,8 @@ protected:
|
|||
// To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
|
||||
void throw_if_canceled() const { if (m_cancel_status) throw CanceledException(); }
|
||||
|
||||
Model m_model;
|
||||
|
||||
private:
|
||||
tbb::atomic<CancelStatus> m_cancel_status;
|
||||
// Callback to be evoked regularly to update state of the UI thread.
|
||||
|
|
@ -240,27 +308,28 @@ private:
|
|||
// Mutex used for synchronization of the worker thread with the UI thread:
|
||||
// The mutex will be used to guard the worker thread against entering a stage
|
||||
// while the data influencing the stage is modified.
|
||||
mutable tbb::mutex m_cancel_mutex;
|
||||
mutable tbb::mutex m_state_mutex;
|
||||
};
|
||||
|
||||
template<typename PrintStepEnum, const size_t COUNT>
|
||||
class PrintBaseWithState : public PrintBase
|
||||
{
|
||||
public:
|
||||
bool is_step_done(PrintStepEnum step) const { return m_state.is_done(step); }
|
||||
bool is_step_done(PrintStepEnum step) const { return m_state.is_done(step, this->state_mutex()); }
|
||||
PrintStateBase::StateWithTimeStamp step_state_with_timestamp(PrintStepEnum step) const { return m_state.state_with_timestamp(step, this->state_mutex()); }
|
||||
|
||||
protected:
|
||||
void set_started(PrintStepEnum step) { m_state.set_started(step, this->cancel_mutex()); throw_if_canceled(); }
|
||||
void set_done(PrintStepEnum step) { m_state.set_done(step, this->cancel_mutex()); throw_if_canceled(); }
|
||||
bool set_started(PrintStepEnum step) { return m_state.set_started(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); }
|
||||
PrintStateBase::TimeStamp set_done(PrintStepEnum step) { return m_state.set_done(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); }
|
||||
bool invalidate_step(PrintStepEnum step)
|
||||
{ return m_state.invalidate(step, this->cancel_mutex(), this->cancel_callback()); }
|
||||
{ return m_state.invalidate(step, this->cancel_callback()); }
|
||||
template<typename StepTypeIterator>
|
||||
bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
|
||||
{ return m_state.invalidate_multiple(step_begin, step_end, this->cancel_mutex(), this->cancel_callback()); }
|
||||
{ return m_state.invalidate_multiple(step_begin, step_end, this->cancel_callback()); }
|
||||
bool invalidate_steps(std::initializer_list<PrintStepEnum> il)
|
||||
{ return m_state.invalidate_multiple(il.begin(), il.end(), this->cancel_mutex(), this->cancel_callback()); }
|
||||
{ return m_state.invalidate_multiple(il.begin(), il.end(), this->cancel_callback()); }
|
||||
bool invalidate_all_steps()
|
||||
{ return m_state.invalidate_all(this->cancel_mutex(), this->cancel_callback()); }
|
||||
{ return m_state.invalidate_all(this->cancel_callback()); }
|
||||
|
||||
private:
|
||||
PrintState<PrintStepEnum, COUNT> m_state;
|
||||
|
|
@ -273,24 +342,33 @@ public:
|
|||
PrintType* print() { return m_print; }
|
||||
const PrintType* print() const { return m_print; }
|
||||
|
||||
bool is_step_done(PrintObjectStepEnum step) const { return m_state.is_done(step); }
|
||||
typedef PrintState<PrintObjectStepEnum, COUNT> PrintObjectState;
|
||||
bool is_step_done(PrintObjectStepEnum step) const { return m_state.is_done(step, PrintObjectBase::state_mutex(m_print)); }
|
||||
PrintStateBase::StateWithTimeStamp step_state_with_timestamp(PrintObjectStepEnum step) const { return m_state.state_with_timestamp(step, PrintObjectBase::state_mutex(m_print)); }
|
||||
|
||||
protected:
|
||||
PrintObjectBaseWithState(PrintType *print) : m_print(print) {}
|
||||
PrintObjectBaseWithState(PrintType *print, ModelObject *model_object) : PrintObjectBase(model_object), m_print(print) {}
|
||||
|
||||
void set_started(PrintObjectStepEnum step) { m_state.set_started(step, PrintObjectBase::cancel_mutex(m_print)); }
|
||||
void set_done(PrintObjectStepEnum step) { m_state.set_done(step, PrintObjectBase::cancel_mutex(m_print)); }
|
||||
bool set_started(PrintObjectStepEnum step)
|
||||
{ return m_state.set_started(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); }); }
|
||||
PrintStateBase::TimeStamp set_done(PrintObjectStepEnum step)
|
||||
{ return m_state.set_done(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); }); }
|
||||
|
||||
bool invalidate_step(PrintObjectStepEnum step)
|
||||
{ return m_state.invalidate(step, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
|
||||
{ return m_state.invalidate(step, PrintObjectBase::cancel_callback(m_print)); }
|
||||
template<typename StepTypeIterator>
|
||||
bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
|
||||
{ return m_state.invalidate_multiple(step_begin, step_end, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
|
||||
{ return m_state.invalidate_multiple(step_begin, step_end, PrintObjectBase::cancel_callback(m_print)); }
|
||||
bool invalidate_steps(std::initializer_list<PrintObjectStepEnum> il)
|
||||
{ return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
|
||||
bool invalidate_all_steps() { return m_state.invalidate_all(PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
|
||||
{ return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_callback(m_print)); }
|
||||
bool invalidate_all_steps()
|
||||
{ return m_state.invalidate_all(PrintObjectBase::cancel_callback(m_print)); }
|
||||
|
||||
protected:
|
||||
// If the background processing stop was requested, throw CanceledException.
|
||||
// To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
|
||||
void throw_if_canceled() { if (m_print->canceled()) throw CanceledException(); }
|
||||
|
||||
friend PrintType;
|
||||
PrintType *m_print;
|
||||
|
||||
|
|
|
|||
|
|
@ -2515,7 +2515,22 @@ void PrintConfigDef::init_sla_params()
|
|||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloat();
|
||||
def->default_value = new ConfigOptionFloat(15.0);
|
||||
|
||||
def = this->add("support_object_elevation", coFloat);
|
||||
def->label = L("Object elevation");
|
||||
def->tooltip = L("How much the supports should lift up the supported object.");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloat(5.0);
|
||||
|
||||
def = this->add("pad_enable", coBool);
|
||||
def->label = L("Use pad");
|
||||
def->tooltip = L("Add a pad underneath the supported model");
|
||||
def->sidetext = L("");
|
||||
def->cli = "";
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("pad_wall_thickness", coFloat);
|
||||
def->label = L("Pad wall thickness");
|
||||
|
|
@ -2542,7 +2557,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->default_value = new ConfigOptionFloat(50.0);
|
||||
|
||||
def = this->add("pad_edge_radius", coFloat);
|
||||
def->label = L("pad edge radius");
|
||||
def->label = L("Pad edge radius");
|
||||
def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ enum PrinterTechnology
|
|||
};
|
||||
|
||||
enum GCodeFlavor {
|
||||
gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit,
|
||||
gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit,
|
||||
gcfSmoothie, gcfNoExtrusion,
|
||||
};
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ private:
|
|||
// This definition is constant.
|
||||
extern const PrintConfigDef print_config_def;
|
||||
|
||||
// Slic3r dynamic configuration, used to override the configuration
|
||||
// Slic3r dynamic configuration, used to override the configuration
|
||||
// per object, per modification volume or per printing material.
|
||||
// The dynamic configuration is also used to store user modifications of the print global parameters,
|
||||
// so the modified configuration values may be diffed against the active configuration
|
||||
|
|
@ -274,12 +274,12 @@ protected:
|
|||
m_defaults = defaults;
|
||||
m_keys.clear();
|
||||
m_keys.reserve(m_map_name_to_offset.size());
|
||||
for (const auto &kvp : defs->options) {
|
||||
// Find the option given the option name kvp.first by an offset from (char*)m_defaults.
|
||||
ConfigOption *opt = this->optptr(kvp.first, m_defaults);
|
||||
if (opt == nullptr)
|
||||
// This option is not defined by the ConfigBase of type T.
|
||||
continue;
|
||||
for (const auto &kvp : defs->options) {
|
||||
// Find the option given the option name kvp.first by an offset from (char*)m_defaults.
|
||||
ConfigOption *opt = this->optptr(kvp.first, m_defaults);
|
||||
if (opt == nullptr)
|
||||
// This option is not defined by the ConfigBase of type T.
|
||||
continue;
|
||||
m_keys.emplace_back(kvp.first);
|
||||
const ConfigOptionDef *def = defs->get(kvp.first);
|
||||
assert(def != nullptr);
|
||||
|
|
@ -463,7 +463,7 @@ public:
|
|||
ConfigOptionInt top_solid_layers;
|
||||
ConfigOptionFloatOrPercent top_solid_infill_speed;
|
||||
ConfigOptionBool wipe_into_infill;
|
||||
|
||||
|
||||
protected:
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
|
|
@ -768,7 +768,7 @@ public:
|
|||
ConfigOptionInt pixel_height;
|
||||
ConfigOptionFloat exp_time;
|
||||
ConfigOptionFloat exp_time_first;
|
||||
|
||||
|
||||
protected:
|
||||
PrintConfig(int) : GCodeConfig(1) {}
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
|
|
@ -859,7 +859,7 @@ public:
|
|||
ConfigOptionString printhost_cafile;
|
||||
ConfigOptionString serial_port;
|
||||
ConfigOptionInt serial_speed;
|
||||
|
||||
|
||||
protected:
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
|
|
@ -873,14 +873,14 @@ protected:
|
|||
};
|
||||
|
||||
// This object is mapped to Perl as Slic3r::Config::Full.
|
||||
class FullPrintConfig :
|
||||
public PrintObjectConfig,
|
||||
class FullPrintConfig :
|
||||
public PrintObjectConfig,
|
||||
public PrintRegionConfig,
|
||||
public PrintConfig,
|
||||
public HostConfig
|
||||
{
|
||||
STATIC_PRINT_CONFIG_CACHE_DERIVED(FullPrintConfig)
|
||||
FullPrintConfig() : PrintObjectConfig(0), PrintRegionConfig(0), PrintConfig(0), HostConfig(0) { initialize_cache(); *this = s_cache_FullPrintConfig.defaults(); }
|
||||
FullPrintConfig() : PrintObjectConfig(0), PrintRegionConfig(0), PrintConfig(0), HostConfig(0) { initialize_cache(); *this = s_cache_FullPrintConfig.defaults(); }
|
||||
|
||||
public:
|
||||
// Validate the FullPrintConfig. Returns an empty string on success, otherwise an error message is returned.
|
||||
|
|
@ -910,14 +910,15 @@ public:
|
|||
// How much the pinhead has to penetrate the model surface
|
||||
ConfigOptionFloat support_head_penetration /*= 0.2*/;
|
||||
|
||||
// Radius of the back side of the 3d arrow.
|
||||
// Radius of the back side of the 3d arrow. TODO: consider renaming this
|
||||
// to actual pillar radius, because that's what it boils down to.
|
||||
ConfigOptionFloat support_head_back_radius /*= 0.5*/;
|
||||
|
||||
// Width in mm from the back sphere center to the front sphere center.
|
||||
ConfigOptionFloat support_head_width /*= 1.0*/;
|
||||
|
||||
// Radius in mm of the support pillars.
|
||||
// TODO: This parameter is invalid. The pillar radius will be dynamic in
|
||||
// TODO: This parameter is questionable. The pillar radius will be dynamic in
|
||||
// nature. Merged pillars will have an increased thickness. This parameter
|
||||
// may serve as the maximum radius, or maybe an increase when two are merged
|
||||
// The default radius will be derived from head_back_radius_mm
|
||||
|
|
@ -930,17 +931,18 @@ public:
|
|||
ConfigOptionFloat support_base_height /*= 1.0*/;
|
||||
|
||||
// The default angle for connecting support sticks and junctions.
|
||||
ConfigOptionFloat support_critical_angle /*= M_PI/4*/;
|
||||
ConfigOptionFloat support_critical_angle /*= 45*/;
|
||||
|
||||
// The max length of a bridge in mm
|
||||
ConfigOptionFloat support_max_bridge_length /*= 15.0*/;
|
||||
|
||||
// The elevation in Z direction upwards. This is the space between the pad
|
||||
// and the model object's bounding box bottom.
|
||||
ConfigOptionFloat support_object_elevation;
|
||||
// and the model object's bounding box bottom. Units in mm.
|
||||
ConfigOptionFloat support_object_elevation /*= 5.0*/;
|
||||
|
||||
// Now for the base pool (plate) ///////////////////////////////////////////
|
||||
// Now for the base pool (pad) /////////////////////////////////////////////
|
||||
|
||||
ConfigOptionBool pad_enable;
|
||||
ConfigOptionFloat pad_wall_thickness /*= 2*/;
|
||||
ConfigOptionFloat pad_wall_height /*= 5*/;
|
||||
ConfigOptionFloat pad_max_merge_distance /*= 50*/;
|
||||
|
|
@ -959,6 +961,8 @@ protected:
|
|||
OPT_PTR(support_base_height);
|
||||
OPT_PTR(support_critical_angle);
|
||||
OPT_PTR(support_max_bridge_length);
|
||||
OPT_PTR(support_object_elevation);
|
||||
OPT_PTR(pad_enable);
|
||||
OPT_PTR(pad_wall_thickness);
|
||||
OPT_PTR(pad_wall_height);
|
||||
OPT_PTR(pad_max_merge_distance);
|
||||
|
|
@ -1068,7 +1072,7 @@ public:
|
|||
ConfigOptionFloat scale;
|
||||
// ConfigOptionPoint3 scale_to_fit;
|
||||
ConfigOptionBool slice;
|
||||
|
||||
|
||||
CLIConfig() : ConfigBase(), StaticConfig()
|
||||
{
|
||||
this->set_defaults();
|
||||
|
|
@ -1076,7 +1080,7 @@ public:
|
|||
|
||||
// Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here.
|
||||
const ConfigDef* def() const override { return &cli_config_def; }
|
||||
t_config_option_keys keys() const override { return cli_config_def.keys(); }
|
||||
t_config_option_keys keys() const override { return cli_config_def.keys(); }
|
||||
|
||||
ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override
|
||||
{
|
||||
|
|
@ -1118,7 +1122,7 @@ private:
|
|||
class PrintAndCLIConfigDef : public ConfigDef
|
||||
{
|
||||
public:
|
||||
PrintAndCLIConfigDef() {
|
||||
PrintAndCLIConfigDef() {
|
||||
this->options.insert(print_config_def.options.begin(), print_config_def.options.end());
|
||||
this->options.insert(cli_config_def.options.begin(), cli_config_def.options.end());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,9 +35,8 @@
|
|||
namespace Slic3r {
|
||||
|
||||
PrintObject::PrintObject(Print* print, ModelObject* model_object) :
|
||||
PrintObjectBaseWithState(print),
|
||||
PrintObjectBaseWithState(print, model_object),
|
||||
typed_slices(false),
|
||||
m_model_object(model_object),
|
||||
size(Vec3crd::Zero()),
|
||||
layer_height_profile_valid(false)
|
||||
{
|
||||
|
|
@ -103,9 +102,8 @@ bool PrintObject::set_copies(const Points &points)
|
|||
// this should be idempotent
|
||||
void PrintObject::slice()
|
||||
{
|
||||
if (this->is_step_done(posSlice))
|
||||
if (! this->set_started(posSlice))
|
||||
return;
|
||||
this->set_started(posSlice);
|
||||
m_print->set_status(10, "Processing triangulated mesh");
|
||||
this->_slice();
|
||||
m_print->throw_if_canceled();
|
||||
|
|
@ -131,10 +129,9 @@ void PrintObject::make_perimeters()
|
|||
// prerequisites
|
||||
this->slice();
|
||||
|
||||
if (this->is_step_done(posPerimeters))
|
||||
if (! this->set_started(posPerimeters))
|
||||
return;
|
||||
|
||||
this->set_started(posPerimeters);
|
||||
m_print->set_status(20, "Generating perimeters");
|
||||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters...";
|
||||
|
||||
|
|
@ -242,10 +239,9 @@ void PrintObject::make_perimeters()
|
|||
|
||||
void PrintObject::prepare_infill()
|
||||
{
|
||||
if (this->is_step_done(posPrepareInfill))
|
||||
if (! this->set_started(posPrepareInfill))
|
||||
return;
|
||||
|
||||
this->set_started(posPrepareInfill);
|
||||
m_print->set_status(30, "Preparing infill");
|
||||
|
||||
// This will assign a type (top/bottom/internal) to $layerm->slices.
|
||||
|
|
@ -361,8 +357,7 @@ void PrintObject::infill()
|
|||
// prerequisites
|
||||
this->prepare_infill();
|
||||
|
||||
if (! this->is_step_done(posInfill)) {
|
||||
this->set_started(posInfill);
|
||||
if (this->set_started(posInfill)) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
|
|
@ -384,8 +379,7 @@ void PrintObject::infill()
|
|||
|
||||
void PrintObject::generate_support_material()
|
||||
{
|
||||
if (! this->is_step_done(posSupportMaterial)) {
|
||||
this->set_started(posSupportMaterial);
|
||||
if (this->set_started(posSupportMaterial)) {
|
||||
this->clear_support_layers();
|
||||
if ((m_config.support_material || m_config.raft_layers > 0) && m_layers.size() > 1) {
|
||||
m_print->set_status(85, "Generating support material");
|
||||
|
|
@ -1706,9 +1700,8 @@ void PrintObject::_simplify_slices(double distance)
|
|||
|
||||
void PrintObject::_make_perimeters()
|
||||
{
|
||||
if (this->is_step_done(posPerimeters))
|
||||
if (! this->set_started(posPerimeters))
|
||||
return;
|
||||
this->set_started(posPerimeters);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters...";
|
||||
|
||||
|
|
|
|||
|
|
@ -424,12 +424,6 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
|
|||
TriangleMesh m = mesh;
|
||||
TriangleMeshSlicer slicer(&m);
|
||||
|
||||
// TriangleMesh upper, lower;
|
||||
// slicer.cut(h, &upper, &lower);
|
||||
|
||||
// TODO: this might be slow (in fact it was)
|
||||
// output = lower.horizontal_projection();
|
||||
|
||||
auto bb = mesh.bounding_box();
|
||||
float gnd = float(bb.min(Z));
|
||||
std::vector<float> heights = {float(bb.min(Z))};
|
||||
|
|
|
|||
|
|
@ -23,6 +23,13 @@ struct PoolConfig {
|
|||
double min_wall_height_mm = 5;
|
||||
double max_merge_distance_mm = 50;
|
||||
double edge_radius_mm = 1;
|
||||
|
||||
inline PoolConfig() {}
|
||||
inline PoolConfig(double wt, double wh, double md, double er):
|
||||
min_wall_thickness_mm(wt),
|
||||
min_wall_height_mm(wh),
|
||||
max_merge_distance_mm(md),
|
||||
edge_radius_mm(er) {}
|
||||
};
|
||||
|
||||
/// Calculate the pool for the mesh for SLA printing
|
||||
|
|
@ -31,6 +38,15 @@ void create_base_pool(const ExPolygons& base_plate,
|
|||
const PoolConfig& = PoolConfig()
|
||||
);
|
||||
|
||||
/// TODO: Currently the base plate of the pool will have half the height of the
|
||||
/// whole pool. So the carved out space has also half the height. This is not
|
||||
/// a particularly elegant solution, the thickness should be exactly
|
||||
/// min_wall_thickness and it should be corrected in the future. This method
|
||||
/// will return the correct value for further processing.
|
||||
inline double get_pad_elevation(const PoolConfig& cfg) {
|
||||
return cfg.min_wall_height_mm / 2.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,6 +213,7 @@ struct Head {
|
|||
double r_back_mm = 1;
|
||||
double r_pin_mm = 0.5;
|
||||
double width_mm = 2;
|
||||
double penetration_mm = 0.5;
|
||||
|
||||
// For identification purposes. This will be used as the index into the
|
||||
// container holding the head structures. See SLASupportTree::Impl
|
||||
|
|
@ -224,11 +225,13 @@ struct Head {
|
|||
Head(double r_big_mm,
|
||||
double r_small_mm,
|
||||
double length_mm,
|
||||
double penetration,
|
||||
Vec3d direction = {0, 0, -1}, // direction (normal to the dull end )
|
||||
Vec3d offset = {0, 0, 0}, // displacement
|
||||
const size_t circlesteps = 45):
|
||||
steps(circlesteps), dir(direction), tr(offset),
|
||||
r_back_mm(r_big_mm), r_pin_mm(r_small_mm), width_mm(length_mm)
|
||||
r_back_mm(r_big_mm), r_pin_mm(r_small_mm), width_mm(length_mm),
|
||||
penetration_mm(penetration)
|
||||
{
|
||||
|
||||
// We create two spheres which will be connected with a robe that fits
|
||||
|
|
@ -281,7 +284,7 @@ struct Head {
|
|||
|
||||
// To simplify further processing, we translate the mesh so that the
|
||||
// last vertex of the pointing sphere (the pinpoint) will be at (0,0,0)
|
||||
for(auto& p : mesh.points) { z(p) -= (h + 0.5 * r_small_mm); }
|
||||
for(auto& p : mesh.points) z(p) -= (h + r_small_mm - penetration_mm);
|
||||
}
|
||||
|
||||
void transform()
|
||||
|
|
@ -298,11 +301,11 @@ struct Head {
|
|||
}
|
||||
|
||||
double fullwidth() const {
|
||||
return 1.5 * r_pin_mm + width_mm + 2*r_back_mm;
|
||||
return 2 * r_pin_mm + width_mm + 2*r_back_mm - penetration_mm;
|
||||
}
|
||||
|
||||
Vec3d junction_point() const {
|
||||
return tr + ( 1.5 * r_pin_mm + width_mm + r_back_mm)*dir;
|
||||
return tr + ( 2 * r_pin_mm + width_mm + r_back_mm - penetration_mm)*dir;
|
||||
}
|
||||
|
||||
double request_pillar_radius(double radius) const {
|
||||
|
|
@ -507,7 +510,9 @@ struct Pad {
|
|||
Pad(const TriangleMesh& object_support_mesh,
|
||||
const ExPolygons& baseplate,
|
||||
double ground_level,
|
||||
const PoolConfig& cfg) : zlevel(ground_level + cfg.min_wall_height_mm/2)
|
||||
const PoolConfig& pcfg) :
|
||||
cfg(pcfg),
|
||||
zlevel(ground_level + sla::get_pad_elevation(pcfg))
|
||||
{
|
||||
ExPolygons basep;
|
||||
base_plate(object_support_mesh, basep,
|
||||
|
|
@ -538,19 +543,6 @@ EigenMesh3D to_eigenmesh(const Contour3D& cntr) {
|
|||
return emesh;
|
||||
}
|
||||
|
||||
void create_head(TriangleMesh& out, double r1_mm, double r2_mm, double width_mm)
|
||||
{
|
||||
Head head(r1_mm, r2_mm, width_mm, {0, std::sqrt(0.5), -std::sqrt(0.5)},
|
||||
{0, 0, 30});
|
||||
out.merge(mesh(head.mesh));
|
||||
|
||||
Pillar cst(head, {0, 0, 0});
|
||||
cst.add_base();
|
||||
|
||||
out.merge(mesh(cst.mesh));
|
||||
out.merge(mesh(cst.base));
|
||||
}
|
||||
|
||||
// The minimum distance for two support points to remain valid.
|
||||
static const double /*constexpr*/ D_SP = 0.1;
|
||||
|
||||
|
|
@ -593,21 +585,6 @@ EigenMesh3D to_eigenmesh(const ModelObject& modelobj) {
|
|||
return to_eigenmesh(modelobj.raw_mesh());
|
||||
}
|
||||
|
||||
EigenMesh3D to_eigenmesh(const Model& model) {
|
||||
TriangleMesh combined_mesh;
|
||||
|
||||
for(ModelObject *o : model.objects) {
|
||||
TriangleMesh tmp = o->raw_mesh();
|
||||
for(ModelInstance * inst: o->instances) {
|
||||
TriangleMesh ttmp(tmp);
|
||||
inst->transform_mesh(&ttmp);
|
||||
combined_mesh.merge(ttmp);
|
||||
}
|
||||
}
|
||||
|
||||
return to_eigenmesh(combined_mesh);
|
||||
}
|
||||
|
||||
PointSet to_point_set(const std::vector<Vec3d> &v)
|
||||
{
|
||||
PointSet ret(v.size(), 3);
|
||||
|
|
@ -619,43 +596,6 @@ Vec3d model_coord(const ModelInstance& object, const Vec3f& mesh_coord) {
|
|||
return object.transform_vector(mesh_coord.cast<double>());
|
||||
}
|
||||
|
||||
PointSet support_points(const Model& model) {
|
||||
size_t sum = 0;
|
||||
for(auto *o : model.objects)
|
||||
sum += o->instances.size() * o->sla_support_points.size();
|
||||
|
||||
PointSet ret(sum, 3);
|
||||
|
||||
for(ModelObject *o : model.objects)
|
||||
for(ModelInstance *inst : o->instances) {
|
||||
int i = 0;
|
||||
for(Vec3f& msource : o->sla_support_points) {
|
||||
ret.row(i++) = model_coord(*inst, msource);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PointSet support_points(const ModelObject& modelobject)
|
||||
{
|
||||
PointSet ret(modelobject.sla_support_points.size(), 3);
|
||||
auto rot = modelobject.instances.front()->get_rotation();
|
||||
// auto scaling = modelobject.instances.front()->get_scaling_factor();
|
||||
|
||||
// Transform3d tr;
|
||||
// tr.rotate(Eigen::AngleAxisd(rot(X), Vec3d::UnitX()) *
|
||||
// Eigen::AngleAxisd(rot(Y), Vec3d::UnitY()));
|
||||
|
||||
long i = 0;
|
||||
for(const Vec3f& msource : modelobject.sla_support_points) {
|
||||
Vec3d&& p = msource.cast<double>();
|
||||
// p = tr * p;
|
||||
ret.row(i++) = p;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
double ray_mesh_intersect(const Vec3d& s,
|
||||
const Vec3d& dir,
|
||||
const EigenMesh3D& m);
|
||||
|
|
@ -1154,6 +1094,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||
cfg.head_back_radius_mm,
|
||||
cfg.head_front_radius_mm,
|
||||
cfg.head_width_mm,
|
||||
cfg.head_penetration_mm,
|
||||
nmls.row(i), // dir
|
||||
head_pos.row(i) // displacement
|
||||
);
|
||||
|
|
@ -1521,6 +1462,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||
Head base_head(cfg.head_back_radius_mm,
|
||||
cfg.head_front_radius_mm,
|
||||
cfg.head_width_mm,
|
||||
cfg.head_penetration_mm,
|
||||
{0.0, 0.0, 1.0},
|
||||
{headend(X), headend(Y), headend(Z) - gh});
|
||||
|
||||
|
|
@ -1692,7 +1634,7 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
|
|||
const auto modelh = float(stree.full_height());
|
||||
auto gndlvl = float(this->m_impl->ground_level);
|
||||
const Pad& pad = m_impl->pad();
|
||||
if(!pad.empty()) gndlvl -= float(pad.cfg.min_wall_height_mm/2);
|
||||
if(!pad.empty()) gndlvl -= float(get_pad_elevation(pad.cfg));
|
||||
|
||||
std::vector<float> heights = {gndlvl};
|
||||
heights.reserve(size_t(modelh/layerh) + 1);
|
||||
|
|
@ -1719,10 +1661,10 @@ const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
|
|||
TriangleMesh mm;
|
||||
merged_mesh(mm);
|
||||
PoolConfig pcfg;
|
||||
// pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
|
||||
// pcfg.min_wall_height_mm = min_wall_height_mm;
|
||||
// pcfg.max_merge_distance_mm = max_merge_distance_mm;
|
||||
// pcfg.edge_radius_mm = edge_radius_mm;
|
||||
pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
|
||||
pcfg.min_wall_height_mm = min_wall_height_mm;
|
||||
pcfg.max_merge_distance_mm = max_merge_distance_mm;
|
||||
pcfg.edge_radius_mm = edge_radius_mm;
|
||||
return m_impl->create_pad(mm, baseplate, pcfg).tmesh;
|
||||
}
|
||||
|
||||
|
|
@ -1731,21 +1673,6 @@ const TriangleMesh &SLASupportTree::get_pad() const
|
|||
return m_impl->pad().tmesh;
|
||||
}
|
||||
|
||||
double SLASupportTree::get_elevation() const
|
||||
{
|
||||
double ph = m_impl->pad().empty()? 0 :
|
||||
m_impl->pad().cfg.min_wall_height_mm/2.0;
|
||||
return -m_impl->ground_level + ph;
|
||||
}
|
||||
|
||||
SLASupportTree::SLASupportTree(const Model& model,
|
||||
const SupportConfig& cfg,
|
||||
const Controller& ctl):
|
||||
m_impl(new Impl()), m_ctl(ctl)
|
||||
{
|
||||
generate(support_points(model), to_eigenmesh(model), cfg, ctl);
|
||||
}
|
||||
|
||||
SLASupportTree::SLASupportTree(const PointSet &points,
|
||||
const EigenMesh3D& emesh,
|
||||
const SupportConfig &cfg,
|
||||
|
|
@ -1767,66 +1694,5 @@ SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c)
|
|||
|
||||
SLASupportTree::~SLASupportTree() {}
|
||||
|
||||
void add_sla_supports(Model &model,
|
||||
const SupportConfig &cfg,
|
||||
const Controller &ctl)
|
||||
{
|
||||
Benchmark bench;
|
||||
|
||||
bench.start();
|
||||
SLASupportTree _stree(model, cfg, ctl);
|
||||
bench.stop();
|
||||
|
||||
std::cout << "Support tree creation time: " << bench.getElapsedSec()
|
||||
<< " seconds" << std::endl;
|
||||
|
||||
bench.start();
|
||||
ModelObject* o = model.add_object();
|
||||
o->add_instance();
|
||||
|
||||
TriangleMesh streemsh;
|
||||
_stree.merged_mesh(streemsh);
|
||||
o->add_volume(streemsh);
|
||||
|
||||
bench.stop();
|
||||
std::cout << "support tree added to model in: " << bench.getElapsedSec()
|
||||
<< " seconds" << std::endl;
|
||||
|
||||
// TODO this would roughly be the code for the base pool
|
||||
ExPolygons plate;
|
||||
auto modelmesh = model.mesh();
|
||||
TriangleMesh poolmesh;
|
||||
sla::PoolConfig poolcfg;
|
||||
poolcfg.min_wall_height_mm = 1;
|
||||
poolcfg.edge_radius_mm = 0.1;
|
||||
poolcfg.min_wall_thickness_mm = 0.8;
|
||||
|
||||
bench.start();
|
||||
sla::base_plate(modelmesh, plate);
|
||||
bench.stop();
|
||||
|
||||
std::cout << "Base plate calculation time: " << bench.getElapsedSec()
|
||||
<< " seconds." << std::endl;
|
||||
|
||||
bench.start();
|
||||
sla::create_base_pool(plate, poolmesh, poolcfg);
|
||||
bench.stop();
|
||||
|
||||
std::cout << "Pool generation completed in " << bench.getElapsedSec()
|
||||
<< " second." << std::endl;
|
||||
|
||||
bench.start();
|
||||
poolmesh.translate(.0f, .0f, float(poolcfg.min_wall_height_mm / 2));
|
||||
o->add_volume(poolmesh);
|
||||
bench.stop();
|
||||
|
||||
// TODO: will cause incorrect placement of the model;
|
||||
// o->translate({0, 0, poolcfg.min_wall_height_mm / 2});
|
||||
|
||||
std::cout << "Added pool to model in " << bench.getElapsedSec()
|
||||
<< " seconds." << std::endl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ struct SupportConfig {
|
|||
double head_front_radius_mm = 0.2;
|
||||
|
||||
// How much the pinhead has to penetrate the model surface
|
||||
double head_penetraiton = 0.2;
|
||||
double head_penetration_mm = 0.5;
|
||||
|
||||
// Radius of the back side of the 3d arrow.
|
||||
double head_back_radius_mm = 0.5;
|
||||
|
|
@ -90,34 +90,17 @@ struct EigenMesh3D {
|
|||
Eigen::MatrixXd V;
|
||||
Eigen::MatrixXi F;
|
||||
double ground_level = 0;
|
||||
|
||||
// igl crashes with the following data types:
|
||||
// Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign> V;
|
||||
// Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign> F;
|
||||
};
|
||||
|
||||
//using PointSet = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign>; //Eigen::MatrixXd;
|
||||
using PointSet = Eigen::MatrixXd;
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* TODO: May not be needed: */
|
||||
/* ************************************************************************** */
|
||||
|
||||
void create_head(TriangleMesh&, double r1_mm, double r2_mm, double width_mm);
|
||||
|
||||
/// Add support volumes to the model directly
|
||||
void add_sla_supports(Model& model, const SupportConfig& cfg = {},
|
||||
const Controller& ctl = {});
|
||||
|
||||
EigenMesh3D to_eigenmesh(const TriangleMesh& m);
|
||||
PointSet to_point_set(const std::vector<Vec3d>&);
|
||||
|
||||
|
||||
// obsolete, not used anymore
|
||||
EigenMesh3D to_eigenmesh(const Model& model);
|
||||
// needed for find best rotation
|
||||
EigenMesh3D to_eigenmesh(const ModelObject& model);
|
||||
PointSet support_points(const ModelObject& modelobject);
|
||||
PointSet support_points(const Model& model);
|
||||
|
||||
// Simple conversion of 'vector of points' to an Eigen matrix
|
||||
PointSet to_point_set(const std::vector<Vec3d>&);
|
||||
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
|
@ -149,11 +132,6 @@ class SLASupportTree {
|
|||
const Controller& ctl = {});
|
||||
public:
|
||||
|
||||
// Constructors will throw if the stop condition becomes true.
|
||||
SLASupportTree(const Model& model,
|
||||
const SupportConfig& cfg = {},
|
||||
const Controller& ctl = {});
|
||||
|
||||
SLASupportTree(const PointSet& pts,
|
||||
const EigenMesh3D& em,
|
||||
const SupportConfig& cfg = {},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
#include "SLA/SLABasePool.hpp"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
//#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
|
||||
|
||||
#include "I18N.hpp"
|
||||
|
|
@ -63,7 +65,7 @@ const std::array<std::string, slapsCount> PRINT_STEP_LABELS =
|
|||
|
||||
void SLAPrint::clear()
|
||||
{
|
||||
tbb::mutex::scoped_lock lock(this->cancel_mutex());
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
// The following call should stop background processing if it is running.
|
||||
this->invalidate_all_steps();
|
||||
|
||||
|
|
@ -78,14 +80,15 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model,
|
|||
// return APPLY_STATUS_UNCHANGED;
|
||||
|
||||
// Grab the lock for the Print / PrintObject milestones.
|
||||
tbb::mutex::scoped_lock lock(this->cancel_mutex());
|
||||
if(m_objects.empty() && model.objects.empty())
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
if (m_objects.empty() && model.objects.empty() && m_model.objects.empty())
|
||||
return APPLY_STATUS_UNCHANGED;
|
||||
|
||||
// Temporary: just to have to correct layer height for the rasterization
|
||||
DynamicPrintConfig config(config_in);
|
||||
config.normalize();
|
||||
auto lh = config.opt<ConfigOptionFloat>("layer_height");
|
||||
m_material_config.initial_layer_height.set(
|
||||
config.opt<ConfigOptionFloat>("initial_layer_height"));
|
||||
|
||||
// Temporary quick fix, just invalidate everything.
|
||||
{
|
||||
|
|
@ -102,7 +105,10 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model,
|
|||
// Generate new SLAPrintObjects.
|
||||
for (ModelObject *model_object : m_model.objects) {
|
||||
auto po = new SLAPrintObject(this, model_object);
|
||||
po->m_config.layer_height.set(lh);
|
||||
|
||||
// po->m_config.layer_height.set(lh);
|
||||
po->m_config.apply(config, true);
|
||||
|
||||
m_objects.emplace_back(po);
|
||||
for (ModelInstance *oinst : model_object->instances) {
|
||||
Point tr = Point::new_scale(oinst->get_offset()(X),
|
||||
|
|
@ -124,26 +130,36 @@ void SLAPrint::process()
|
|||
// the model objects we have to process and the instances are also filtered
|
||||
|
||||
// shortcut to initial layer height
|
||||
auto ilh = float(m_material_config.initial_layer_height.getFloat());
|
||||
double ilhd = m_material_config.initial_layer_height.getFloat();
|
||||
auto ilh = float(ilhd);
|
||||
|
||||
// Slicing the model object. This method is oversimplified and needs to
|
||||
// be compared with the fff slicing algorithm for verification
|
||||
auto slice_model = [this, ilh](SLAPrintObject& po) {
|
||||
auto lh = float(po.m_config.layer_height.getFloat());
|
||||
auto slice_model = [this, ilh, ilhd](SLAPrintObject& po) {
|
||||
double lh = po.m_config.layer_height.getFloat();
|
||||
|
||||
TriangleMesh mesh = po.transformed_mesh();
|
||||
TriangleMeshSlicer slicer(&mesh);
|
||||
auto bb3d = mesh.bounding_box();
|
||||
|
||||
auto H = bb3d.max(Z) - bb3d.min(Z);
|
||||
double elevation = po.get_elevation();
|
||||
|
||||
float minZ = float(bb3d.min(Z)) - float(elevation);
|
||||
float maxZ = float(bb3d.max(Z)) ;
|
||||
auto flh = float(lh);
|
||||
auto gnd = float(bb3d.min(Z));
|
||||
std::vector<float> heights = {gnd};
|
||||
for(float h = gnd + ilh; h < gnd + H; h += lh) heights.emplace_back(h);
|
||||
|
||||
std::vector<float> heights;
|
||||
|
||||
// The first layer (the one before the initial height) is added only
|
||||
// if the there is no pad and no elevation value
|
||||
if(minZ >= gnd) heights.emplace_back(minZ);
|
||||
|
||||
for(float h = minZ + ilh; h < maxZ; h += flh)
|
||||
if(h >= gnd) heights.emplace_back(h);
|
||||
|
||||
auto& layers = po.m_model_slices;
|
||||
slicer.slice(heights, &layers, [this](){
|
||||
throw_if_canceled();
|
||||
});
|
||||
slicer.slice(heights, &layers, [this](){ throw_if_canceled(); });
|
||||
};
|
||||
|
||||
auto support_points = [](SLAPrintObject& po) {
|
||||
|
|
@ -169,7 +185,17 @@ void SLAPrint::process()
|
|||
auto& emesh = po.m_supportdata->emesh;
|
||||
auto& pts = po.m_supportdata->support_points; // nowhere filled yet
|
||||
try {
|
||||
SupportConfig scfg; // TODO fill or replace with po.m_config
|
||||
sla::SupportConfig scfg;
|
||||
SLAPrintObjectConfig& c = po.m_config;
|
||||
|
||||
scfg.head_front_radius_mm = c.support_head_front_radius.getFloat();
|
||||
scfg.head_back_radius_mm = c.support_head_back_radius.getFloat();
|
||||
scfg.head_penetration_mm = c.support_head_penetration.getFloat();
|
||||
scfg.head_width_mm = c.support_head_width.getFloat();
|
||||
scfg.object_elevation_mm = c.support_object_elevation.getFloat();
|
||||
scfg.tilt = c.support_critical_angle.getFloat() * PI / 180.0 ;
|
||||
scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat();
|
||||
scfg.pillar_radius_mm = c.support_pillar_radius.getFloat();
|
||||
|
||||
sla::Controller ctl;
|
||||
ctl.statuscb = [this](unsigned st, const std::string& msg) {
|
||||
|
|
@ -196,6 +222,7 @@ void SLAPrint::process()
|
|||
// repeated)
|
||||
|
||||
if(po.is_step_done(slaposSupportTree) &&
|
||||
po.m_config.pad_enable.getBool() &&
|
||||
po.m_supportdata &&
|
||||
po.m_supportdata->support_tree_ptr)
|
||||
{
|
||||
|
|
@ -205,11 +232,12 @@ void SLAPrint::process()
|
|||
double er = po.m_config.pad_edge_radius.getFloat();
|
||||
double lh = po.m_config.layer_height.getFloat();
|
||||
double elevation = po.m_config.support_object_elevation.getFloat();
|
||||
sla::PoolConfig pcfg(wt, h, md, er);
|
||||
|
||||
sla::ExPolygons bp;
|
||||
if(elevation < h/2)
|
||||
sla::base_plate(po.transformed_mesh(), bp,
|
||||
float(h/2), float(lh));
|
||||
double pad_h = sla::get_pad_elevation(pcfg);
|
||||
if(elevation < pad_h) sla::base_plate(po.transformed_mesh(), bp,
|
||||
float(pad_h), float(lh));
|
||||
|
||||
po.m_supportdata->support_tree_ptr->add_pad(bp, wt, h, md, er);
|
||||
}
|
||||
|
|
@ -227,7 +255,7 @@ void SLAPrint::process()
|
|||
};
|
||||
|
||||
// Rasterizing the model objects, and their supports
|
||||
auto rasterize = [this, ilh]() {
|
||||
auto rasterize = [this, ilh, ilhd]() {
|
||||
using Layer = sla::ExPolygons;
|
||||
using LayerCopies = std::vector<SLAPrintObject::Instance>;
|
||||
struct LayerRef {
|
||||
|
|
@ -237,43 +265,68 @@ void SLAPrint::process()
|
|||
lref(std::cref(lyr)), copies(std::cref(cp)) {}
|
||||
};
|
||||
|
||||
using LevelID = long long;
|
||||
using LayerRefs = std::vector<LayerRef>;
|
||||
|
||||
// layers according to quantized height levels
|
||||
std::map<long long, LayerRefs> levels;
|
||||
std::map<LevelID, LayerRefs> levels;
|
||||
|
||||
auto sih = LevelID(scale_(ilh));
|
||||
|
||||
// For all print objects, go through its initial layers and place them
|
||||
// into the layers hash
|
||||
for(SLAPrintObject *o : m_objects) {
|
||||
|
||||
double gndlvl = o->transformed_mesh().bounding_box().min(Z);
|
||||
|
||||
auto bb = o->transformed_mesh().bounding_box();
|
||||
double modelgnd = bb.min(Z);
|
||||
double elevation = o->get_elevation();
|
||||
double lh = o->m_config.layer_height.getFloat();
|
||||
SlicedModel & oslices = o->m_model_slices;
|
||||
for(int i = 0; i < oslices.size(); ++i) {
|
||||
int a = i == 0 ? 0 : 1;
|
||||
int b = i == 0 ? 0 : i - 1;
|
||||
double minZ = modelgnd - elevation;
|
||||
|
||||
double h = gndlvl + ilh * a + b * lh;
|
||||
long long lyridx = static_cast<long long>(scale_(h));
|
||||
auto& lyrs = levels[lyridx]; // this initializes a new record
|
||||
// scaled values:
|
||||
auto sminZ = LevelID(scale_(minZ));
|
||||
auto smaxZ = LevelID(scale_(bb.max(Z)));
|
||||
auto smodelgnd = LevelID(scale_(modelgnd));
|
||||
auto slh = LevelID(scale_(lh));
|
||||
|
||||
// It is important that the next levels math the levels in
|
||||
// model_slice method. Only difference is that here it works with
|
||||
// scaled coordinates
|
||||
std::vector<LevelID> levelids;
|
||||
if(sminZ >= smodelgnd) levelids.emplace_back(sminZ);
|
||||
for(LevelID h = sminZ + sih; h < smaxZ; h += slh)
|
||||
if(h >= smodelgnd) levelids.emplace_back(h);
|
||||
|
||||
SlicedModel & oslices = o->m_model_slices;
|
||||
|
||||
// If everything went well this code should not run at all, but
|
||||
// let's be robust...
|
||||
assert(levelids.size() == oslices.size());
|
||||
if(levelids.size() < oslices.size()) { // extend the levels until...
|
||||
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< "Height level mismatch at rasterization!\n";
|
||||
|
||||
LevelID lastlvl = levelids.back();
|
||||
while(levelids.size() < oslices.size()) {
|
||||
lastlvl += slh;
|
||||
levelids.emplace_back(lastlvl);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < oslices.size(); ++i) {
|
||||
LevelID h = levelids[i];
|
||||
auto& lyrs = levels[h]; // this initializes a new record
|
||||
lyrs.emplace_back(oslices[i], o->m_instances);
|
||||
}
|
||||
|
||||
if(o->m_supportdata) { // deal with the support slices if present
|
||||
auto& sslices = o->m_supportdata->support_slices;
|
||||
double el = o->m_config.support_object_elevation.getFloat();
|
||||
//TODO: remove next line:
|
||||
el = SupportConfig().object_elevation_mm;
|
||||
|
||||
for(int i = 0; i < sslices.size(); ++i) {
|
||||
int a = i == 0 ? 0 : 1;
|
||||
int b = i == 0 ? 0 : i - 1;
|
||||
LevelID h = sminZ + a * sih + b * slh;
|
||||
|
||||
double h = gndlvl - el + ilh * a + b * lh;
|
||||
|
||||
long long lyridx = static_cast<long long>(scale_(h));
|
||||
auto& lyrs = levels[lyridx];
|
||||
auto& lyrs = levels[h];
|
||||
lyrs.emplace_back(sslices[i], o->m_instances);
|
||||
}
|
||||
}
|
||||
|
|
@ -430,14 +483,73 @@ void SLAPrint::process()
|
|||
}
|
||||
|
||||
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
||||
Inherited(print),
|
||||
m_model_object(model_object),
|
||||
Inherited(print, model_object),
|
||||
m_stepmask(slaposCount, true)
|
||||
{
|
||||
}
|
||||
|
||||
SLAPrintObject::~SLAPrintObject() {}
|
||||
|
||||
double SLAPrintObject::get_elevation() const {
|
||||
double ret = m_config.support_object_elevation.getFloat();
|
||||
|
||||
// if the pad is enabled, then half of the pad height is its base plate
|
||||
if(m_config.pad_enable.getBool()) {
|
||||
// Normally the elevation for the pad itself would be the thickness of
|
||||
// its walls but currently it is half of its thickness. Whatever it
|
||||
// will be in the future, we provide the config to the get_pad_elevation
|
||||
// method and we will have the correct value
|
||||
sla::PoolConfig pcfg;
|
||||
pcfg.min_wall_height_mm = m_config.pad_wall_height.getFloat();
|
||||
pcfg.min_wall_thickness_mm = m_config.pad_wall_thickness.getFloat();
|
||||
pcfg.edge_radius_mm = m_config.pad_edge_radius.getFloat();
|
||||
pcfg.max_merge_distance_mm = m_config.pad_max_merge_distance.getFloat();
|
||||
ret += sla::get_pad_elevation(pcfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const
|
||||
//{
|
||||
// // I don't want to return a copy but the points may not exist, so ...
|
||||
// static const std::vector<ExPolygons> dummy_empty;
|
||||
|
||||
// if(!m_supportdata) return dummy_empty;
|
||||
// return m_supportdata->support_slices;
|
||||
//}
|
||||
|
||||
//const std::vector<ExPolygons> &SLAPrintObject::get_model_slices() const
|
||||
//{
|
||||
// return m_model_slices;
|
||||
//}
|
||||
|
||||
bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const
|
||||
{
|
||||
switch (step) {
|
||||
case slaposSupportTree:
|
||||
// return m_supportdata && m_supportdata->support_tree_ptr && ! m_supportdata->support_tree_ptr->get().merged_mesh().empty();
|
||||
return ! this->support_mesh().empty();
|
||||
case slaposBasePool:
|
||||
// return m_supportdata && m_supportdata->support_tree_ptr && ! m_supportdata->support_tree_ptr->get_pad().empty();
|
||||
return ! this->pad_mesh().empty();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const
|
||||
{
|
||||
switch (step) {
|
||||
case slaposSupportTree:
|
||||
return this->support_mesh();
|
||||
case slaposBasePool:
|
||||
return this->pad_mesh();
|
||||
default:
|
||||
return TriangleMesh();
|
||||
}
|
||||
}
|
||||
|
||||
TriangleMesh SLAPrintObject::support_mesh() const
|
||||
{
|
||||
TriangleMesh trm;
|
||||
|
|
|
|||
|
|
@ -35,8 +35,6 @@ private: // Prevents erroneous use by other classes.
|
|||
using Inherited = _SLAPrintObjectBase;
|
||||
|
||||
public:
|
||||
const ModelObject* model_object() const { return m_model_object; }
|
||||
ModelObject* model_object() { return m_model_object; }
|
||||
const Transform3d& trafo() const { return m_trafo; }
|
||||
|
||||
struct Instance {
|
||||
|
|
@ -50,6 +48,9 @@ public:
|
|||
};
|
||||
const std::vector<Instance>& instances() const { return m_instances; }
|
||||
|
||||
bool has_mesh(SLAPrintObjectStep step) const;
|
||||
TriangleMesh get_mesh(SLAPrintObjectStep step) const;
|
||||
|
||||
// Get a support mesh centered around origin in XY, and with zero rotation around Z applied.
|
||||
// Support mesh is only valid if this->is_step_done(slaposSupportTree) is true.
|
||||
TriangleMesh support_mesh() const;
|
||||
|
|
@ -62,6 +63,15 @@ public:
|
|||
|
||||
std::vector<Vec3d> transformed_support_points() const;
|
||||
|
||||
// Get the needed Z elevation for the model geometry if supports should be
|
||||
// displayed. This Z offset should also be applied to the support
|
||||
// geometries. Note that this is not the same as the value stored in config
|
||||
// as the pad height also needs to be considered.
|
||||
double get_elevation() const;
|
||||
|
||||
// const std::vector<ExPolygons>& get_support_slices() const;
|
||||
// const std::vector<ExPolygons>& get_model_slices() const;
|
||||
|
||||
// I refuse to grantee copying (Tamas)
|
||||
SLAPrintObject(const SLAPrintObject&) = delete;
|
||||
SLAPrintObject& operator=(const SLAPrintObject&) = delete;
|
||||
|
|
@ -83,8 +93,7 @@ protected:
|
|||
bool invalidate_step(SLAPrintObjectStep step);
|
||||
|
||||
private:
|
||||
// Points to the instance owned by a Model stored at the parent SLAPrint instance.
|
||||
ModelObject *m_model_object;
|
||||
|
||||
// Object specific configuration, pulled from the configuration layer.
|
||||
SLAPrintObjectConfig m_config;
|
||||
// Translation in Z + Rotation by Y and Z + Scaling / Mirroring.
|
||||
|
|
@ -147,7 +156,6 @@ private:
|
|||
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
|
||||
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
|
||||
|
||||
Model m_model;
|
||||
SLAPrinterConfig m_printer_config;
|
||||
SLAMaterialConfig m_material_config;
|
||||
PrintObjects m_objects;
|
||||
|
|
|
|||
|
|
@ -521,6 +521,7 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& t) const
|
|||
|
||||
if (stl.stats.shared_vertices > 0)
|
||||
{
|
||||
assert(stl.v_shared != nullptr);
|
||||
stl_vertex* vertex_ptr = stl.v_shared;
|
||||
for (int i = 0; i < stl.stats.shared_vertices; ++i)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ public:
|
|||
void reset_repair_stats();
|
||||
bool needed_repair() const;
|
||||
size_t facets_count() const { return this->stl.stats.number_of_facets; }
|
||||
bool empty() const { return this->facets_count() == 0; }
|
||||
|
||||
// Returns true, if there are two and more connected patches in the mesh.
|
||||
// Returns false, if one or zero connected patch is in the mesh.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue