WIP: Reconstruction of background processing update timer,

added unique IDs to the Model / ModelObject / ModelVolume objects,
added a copy of Model hierarchy at the Print,
WIP: new Print::apply() method to update the Print's copy of Model,
to update the Print's PrintObjects, to update status of the Print and
PrintObjects, and to possibly stop the background processing.
This commit is contained in:
bubnikv 2018-10-17 11:12:38 +02:00
parent 404ef147b3
commit 7ed2752b2b
8 changed files with 602 additions and 111 deletions

View File

@ -21,6 +21,8 @@ namespace Slic3r {
unsigned int Model::s_auto_extruder_id = 1;
ModelID ModelBase::s_last_id = 0;
Model::Model(const Model &other)
{
// copy materials
@ -503,6 +505,7 @@ void Model::reset_auto_extruder_id()
}
ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) :
ModelBase(other), // copy the id
name(other.name),
input_file(other.input_file),
instances(),
@ -518,13 +521,13 @@ ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volum
{
if (copy_volumes) {
this->volumes.reserve(other.volumes.size());
for (ModelVolumePtrs::const_iterator i = other.volumes.begin(); i != other.volumes.end(); ++i)
this->add_volume(**i);
for (ModelVolume *model_volume : other.volumes)
this->add_volume(*model_volume);
}
this->instances.reserve(other.instances.size());
for (ModelInstancePtrs::const_iterator i = other.instances.begin(); i != other.instances.end(); ++i)
this->add_instance(**i);
for (const ModelInstance *model_instance : other.instances)
this->add_instance(*model_instance);
}
ModelObject& ModelObject::operator=(ModelObject other)
@ -535,6 +538,7 @@ ModelObject& ModelObject::operator=(ModelObject other)
void ModelObject::swap(ModelObject &other)
{
std::swap(this->m_id, other.m_id);
std::swap(this->input_file, other.input_file);
std::swap(this->instances, other.instances);
std::swap(this->volumes, other.volumes);
@ -553,6 +557,13 @@ ModelObject::~ModelObject()
this->clear_instances();
}
// Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
// Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
ModelObject* ModelObject::clone(Model *parent)
{
return new ModelObject(parent, *this, true);
}
ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
{
ModelVolume* v = new ModelVolume(this, mesh);
@ -903,7 +914,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
new_volume->name = volume->name;
new_volume->config = volume->config;
new_volume->set_type(volume->type());
new_volume->material_id(volume->material_id());
new_volume->set_material_id(volume->material_id());
new_objects->push_back(new_object);
delete mesh;
@ -982,22 +993,22 @@ void ModelObject::print_info() const
cout << "volume = " << mesh.volume() << endl;
}
void ModelVolume::material_id(t_model_material_id material_id)
void ModelVolume::set_material_id(t_model_material_id material_id)
{
this->_material_id = material_id;
m_material_id = material_id;
// ensure this->_material_id references an existing material
// ensure m_material_id references an existing material
(void)this->object->get_model()->add_material(material_id);
}
ModelMaterial* ModelVolume::material() const
{
return this->object->get_model()->get_material(this->_material_id);
return this->object->get_model()->get_material(m_material_id);
}
void ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material)
{
this->_material_id = material_id;
m_material_id = material_id;
(void)this->object->get_model()->add_material(material_id, material);
}
@ -1006,8 +1017,8 @@ ModelMaterial* ModelVolume::assign_unique_material()
Model* model = this->get_object()->get_model();
// as material-id "0" is reserved by the AMF spec we start from 1
this->_material_id = 1 + model->materials.size(); // watchout for implicit cast
return model->add_material(this->_material_id);
m_material_id = 1 + model->materials.size(); // watchout for implicit cast
return model->add_material(m_material_id);
}
void ModelVolume::calculate_convex_hull()

View File

@ -20,6 +20,7 @@ class ModelMaterial;
class ModelObject;
class ModelVolume;
class PresetBundle;
class Print;
typedef std::string t_model_material_id;
typedef std::string t_model_material_attribute;
@ -30,8 +31,27 @@ typedef std::vector<ModelObject*> ModelObjectPtrs;
typedef std::vector<ModelVolume*> ModelVolumePtrs;
typedef std::vector<ModelInstance*> ModelInstancePtrs;
// Unique identifier of a Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial.
// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject)
typedef size_t ModelID;
// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
class ModelBase
{
public:
ModelID id() const { return m_id; }
protected:
ModelID m_id = generate_new_id();
private:
static inline ModelID generate_new_id() { return s_last_id ++; }
static ModelID s_last_id;
};
// Material, which may be shared across multiple ModelObjects of a single Model.
class ModelMaterial
class ModelMaterial : public ModelBase
{
friend class Model;
public:
@ -56,7 +76,7 @@ private:
// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed,
// different rotation and different uniform scaling.
class ModelObject
class ModelObject : public ModelBase
{
friend class Model;
public:
@ -86,20 +106,20 @@ public:
when user expects that. */
Vec3d origin_translation;
Model* get_model() const { return m_model; };
Model* get_model() const { return m_model; };
ModelVolume* add_volume(const TriangleMesh &mesh);
ModelVolume* add_volume(TriangleMesh &&mesh);
ModelVolume* add_volume(const ModelVolume &volume);
void delete_volume(size_t idx);
void clear_volumes();
ModelVolume* add_volume(const TriangleMesh &mesh);
ModelVolume* add_volume(TriangleMesh &&mesh);
ModelVolume* add_volume(const ModelVolume &volume);
void delete_volume(size_t idx);
void clear_volumes();
ModelInstance* add_instance();
ModelInstance* add_instance(const ModelInstance &instance);
ModelInstance* add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation);
void delete_instance(size_t idx);
void delete_last_instance();
void clear_instances();
ModelInstance* add_instance();
ModelInstance* add_instance(const ModelInstance &instance);
ModelInstance* add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation);
void delete_instance(size_t idx);
void delete_last_instance();
void clear_instances();
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
@ -138,6 +158,13 @@ public:
// Print object statistics to console.
void print_info() const;
protected:
friend class Print;
// Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
// Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
ModelObject* clone(Model *parent);
void set_model(Model *model) { m_model = model; }
private:
ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {}
ModelObject(Model *model, const ModelObject &other, bool copy_volumes = true);
@ -146,7 +173,7 @@ private:
~ModelObject();
// Parent object, owning this ModelObject.
Model *m_model;
Model *m_model;
// Bounding box, cached.
mutable BoundingBoxf3 m_bounding_box;
@ -155,20 +182,20 @@ private:
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
// ModelVolume instances are owned by a ModelObject.
class ModelVolume
class ModelVolume : public ModelBase
{
friend class ModelObject;
// The convex hull of this model's mesh.
TriangleMesh m_convex_hull;
TriangleMesh m_convex_hull;
public:
std::string name;
std::string name;
// The triangular model.
TriangleMesh mesh;
TriangleMesh mesh;
// Configuration parameters specific to an object model geometry or a modifier volume,
// overriding the global Slic3r settings and the ModelObject settings.
DynamicPrintConfig config;
DynamicPrintConfig config;
enum Type {
MODEL_TYPE_INVALID = -1,
@ -178,6 +205,9 @@ public:
SUPPORT_BLOCKER,
};
// Clone this ModelVolume, keep the ID identical, set the parent to the cloned volume.
ModelVolume* clone(ModelObject *parent) { return new ModelVolume(parent, *this); }
// A parent object owning this modifier volume.
ModelObject* get_object() const { return this->object; };
Type type() const { return m_type; }
@ -186,8 +216,9 @@ public:
bool is_modifier() const { return m_type == PARAMETER_MODIFIER; }
bool is_support_enforcer() const { return m_type == SUPPORT_ENFORCER; }
bool is_support_blocker() const { return m_type == SUPPORT_BLOCKER; }
t_model_material_id material_id() const { return this->_material_id; }
void material_id(t_model_material_id material_id);
bool is_support_modifier() const { return m_type == SUPPORT_BLOCKER || m_type == SUPPORT_ENFORCER; }
t_model_material_id material_id() const { return m_material_id; }
void set_material_id(t_model_material_id material_id);
ModelMaterial* material() const;
void set_material(t_model_material_id material_id, const ModelMaterial &material);
// Split this volume, append the result to the object owning this volume.
@ -199,7 +230,7 @@ public:
void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const;
TriangleMesh& get_convex_hull();
TriangleMesh& get_convex_hull();
// Helpers for loading / storing into AMF / 3MF files.
static Type type_from_string(const std::string &s);
@ -210,23 +241,26 @@ private:
ModelObject* object;
// Is it an object to be printed, or a modifier volume?
Type m_type;
t_model_material_id _material_id;
t_model_material_id m_material_id;
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object)
{
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {}
ModelVolume(ModelObject *object, const ModelVolume &other) :
ModelBase(other), // copy the ID
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object)
{
this->material_id(other.material_id());
this->set_material_id(other.material_id());
}
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
ModelBase(other), // copy the ID
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object)
{
this->material_id(other.material_id());
this->set_material_id(other.material_id());
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
@ -234,7 +268,7 @@ private:
// A single instance of a ModelObject.
// Knows the affine transformation of an object.
class ModelInstance
class ModelInstance : public ModelBase
{
public:
enum EPrintVolumeState : unsigned char
@ -321,7 +355,7 @@ private:
// and with multiple modifier meshes.
// A model groups multiple objects, each object having possibly multiple instances,
// all objects may share mutliple materials.
class Model
class Model : public ModelBase
{
static unsigned int s_auto_extruder_id;

View File

@ -571,6 +571,415 @@ exit_for_rearrange_regions:
return invalidated;
}
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
static inline bool model_object_list_equal(const Model &model_old, const Model &model_new)
{
if (model_old.objects.size() != model_new.objects.size())
return false;
for (size_t i = 0; i < model_old.objects.size(); ++ i)
if (model_old.objects[i]->id() != model_new.objects[i]->id())
return false;
return true;
}
// Test whether the new model is just an extension of the old model (new objects were added
// to the end of the original list. In that case it is not necessary to kill the background processing.
static inline bool model_object_list_extended(const Model &model_old, const Model &model_new)
{
if (model_old.objects.size() >= model_new.objects.size())
return false;
for (size_t i = 0; i < model_old.objects.size(); ++ i)
if (model_old.objects[i]->id() != model_new.objects[i]->id())
return false;
return true;
}
static inline bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type)
{
bool modifiers_differ = false;
size_t i_old, i_new;
for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) {
const ModelVolume &mv_old = *model_object_old.volumes[i_old];
const ModelVolume &mv_new = *model_object_new.volumes[i_old];
if (mv_old.type() != type) {
++ i_old;
continue;
}
if (mv_new.type() != type) {
++ i_new;
continue;
}
if (mv_old.id() != mv_new.id())
return true;
//FIXME test for the content of the mesh!
//FIXME test for the transformation matrices!
++ i_old;
++ i_new;
}
for (; i_old < model_object_old.volumes.size(); ++ i_old) {
const ModelVolume &mv_old = *model_object_old.volumes[i_old];
if (mv_old.type() == type)
// ModelVolume was deleted.
return true;
}
for (; i_new < model_object_new.volumes.size(); ++ i_new) {
const ModelVolume &mv_new = *model_object_new.volumes[i_new];
if (mv_new.type() == type)
// ModelVolume was added.
return true;
}
return false;
}
static inline void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src)
{
// 1) Delete the support volumes from model_object_dst.
{
std::vector<ModelVolume*> dst;
dst.reserve(model_object_dst.volumes.size());
for (ModelVolume *vol : model_object_dst.volumes) {
if (vol->is_support_modifier())
dst.emplace_back(vol);
else
delete vol;
}
model_object_dst.volumes = std::move(dst);
}
// 2) Copy the support volumes from model_object_src to the end of model_object_dst.
for (ModelVolume *vol : model_object_src.volumes) {
if (vol->is_support_modifier())
model_object_dst.volumes.emplace_back(vol->clone(&model_object_dst));
}
}
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
const T *rv = rhs.data();
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) {
if (*lv < *rv)
return true;
else if (*lv > *rv)
return false;
}
return false;
}
static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
const T *rv = rhs.data();
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv)
if (*lv != *rv)
return false;
return true;
}
struct PrintInstances
{
Transform3d trafo;
Points instances;
bool operator<(const PrintInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
};
// Generate a list of trafos and XY offsets for instances of a ModelObject
static std::vector<PrintInstances> print_objects_from_model_object(const ModelObject &model_object)
{
std::set<PrintInstances> trafos;
PrintInstances trafo;
trafo.instances.assign(1, Point());
for (ModelInstance *model_instance : model_object.instances)
if (model_instance->is_printable()) {
const Vec3d &offst = model_instance->get_offset();
trafo.trafo = model_instance->world_matrix(true);
trafo.instances.front() = Point::new_scale(offst(0), offst(1));
auto it = trafos.find(trafo);
if (it == trafos.end())
trafos.emplace(trafo);
else
const_cast<PrintInstances&>(*it).instances.emplace_back(trafo.instances.front());
}
return std::vector<PrintInstances>(trafos.begin(), trafos.end());
}
bool Print::apply(const Model &model, const DynamicPrintConfig &config)
{
// Grab the lock for the Print / PrintObject milestones.
tbb::mutex::scoped_lock lock(m_mutex);
struct ModelObjectStatus {
enum Status {
Unknown,
Old,
New,
Moved,
Deleted,
};
ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
ModelID id;
Status status;
// Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
};
std::set<ModelObjectStatus> model_object_status;
// 1) Synchronize model objects.
if (model.id() != m_model.id()) {
// Kill everything, initialize from scratch.
// The following call shall kill any computation if running.
this->invalidate_all_steps();
for (PrintObject *object : m_objects) {
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
delete object;
}
m_objects.clear();
for (PrintRegion *region : m_regions)
delete region;
m_regions.clear();
m_model = model;
} else {
if (model_object_list_equal(m_model, model)) {
// The object list did not change.
} else if (model_object_list_extended(m_model, model)) {
// Add new objects. Their volumes and configs will be synchronized later.
this->invalidate_step(psGCodeExport);
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
m_model.objects.emplace_back(model.objects[i]->clone(&m_model));
}
} else {
// Reorder the objects, add new objects.
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
m_cancel_callback();
this->invalidate_step(psGCodeExport);
// Second create a new list of objects.
std::vector<ModelObject*> old(std::move(m_model.objects));
m_model.objects.clear();
m_model.objects.reserve(model.objects.size());
auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
std::sort(old.begin(), old.end(), by_id_lower);
for (const ModelObject *mobj : model.objects) {
auto it = std::lower_bound(old.begin(), old.end(), mobj, by_id_lower);
if (it == old.end() || (*it)->id() != mobj->id()) {
// New ModelObject added.
m_model.objects.emplace_back((*it)->clone(&m_model));
model_object_status.emplace(mobj->id(), ModelObjectStatus::New);
} else {
// Existing ModelObject re-added (possibly moved in the list).
m_model.objects.emplace_back(*it);
model_object_status.emplace(mobj->id(), ModelObjectStatus::Old);
}
}
bool deleted_any = false;
for (ModelObject *mobj : old)
if (model_object_status.find(ModelObjectStatus(mobj->id())) == model_object_status.end()) {
model_object_status.emplace(mobj->id(), ModelObjectStatus::Deleted);
delete mobj;
deleted_any = true;
}
if (deleted_any) {
// Delete PrintObjects of the deleted ModelObjects.
std::vector<PrintObject*> old = std::move(m_objects);
m_objects.clear();
m_objects.reserve(old.size());
for (PrintObject *print_object : old) {
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
assert(it_status != model_object_status.end());
if (it_status->status == ModelObjectStatus::Deleted) {
print_object->invalidate_all_steps();
delete print_object;
} else
m_objects.emplace_back(print_object);
}
}
}
}
// 2) Map print objects including their transformation matrices.
struct PrintObjectStatus {
enum Status {
Unknown,
Deleted,
New
};
PrintObjectStatus(PrintObject *print_object, Status status = Unknown) :
id(print_object->model_object()->id()),
print_object(print_object),
trafo(print_object->trafo()),
status(status) {}
PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
// ID of the ModelObject & PrintObject
ModelID id;
// Pointer to the old PrintObject
PrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
Transform3d trafo;
Status status;
// Search by id.
bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; }
};
std::multiset<PrintObjectStatus> print_object_status;
for (PrintObject *print_object : m_objects)
print_object_status.emplace(PrintObjectStatus(print_object));
// 3) Synchronize ModelObjects & PrintObjects.
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
ModelObject &model_object = *m_model.objects[idx_model_object];
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop.
continue;
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Moved);
const ModelObject &model_object_new = *model.objects[idx_model_object];
// Check whether a model part volume was added or removed, their transformations or order changed.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::MODEL_PART);
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::PARAMETER_MODIFIER);
bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::SUPPORT_BLOCKER);
bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::SUPPORT_ENFORCER);
if (model_parts_differ || modifiers_differ) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
it->print_object->invalidate_all_steps();
const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted;
}
// Copy content of the ModelObject including its ID, reset the parent.
model_object = model_object_new;
model_object.set_model(&m_model);
} else if (support_blockers_differ || support_enforcers_differ) {
// First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
m_cancel_callback();
// Invalidate just the supports step.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it)
it->print_object->invalidate_step(posSupportMaterial);
// Copy just the support volumes.
model_volume_list_update_supports(model_object, model_object_new);
}
if (! model_parts_differ && ! modifiers_differ) {
// Synchronize the remaining data of ModelVolumes (name, config, m_type, m_material_id)
}
}
// 4) Generate PrintObjects from ModelObjects and their instances.
std::vector<PrintObject*> print_objects_new;
print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
// Walk over all new model objects and check, whether there are matching PrintObjects.
for (ModelObject *model_object : m_model.objects) {
auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id()));
std::vector<const PrintObjectStatus*> old;
if (range.first != range.second) {
old.reserve(print_object_status.count(PrintObjectStatus(model_object->id())));
for (auto it = range.first; it != range.second; ++ it)
if (it->status != PrintObjectStatus::Deleted)
old.emplace_back(&(*it));
}
// Generate a list of trafos and XY offsets for instances of a ModelObject
std::vector<PrintInstances> new_print_instances = print_objects_from_model_object(*model_object);
if (old.empty()) {
// Simple case, just generate new instances.
for (const PrintInstances &print_instances : new_print_instances) {
PrintObject *print_object = new PrintObject(this, model_object, model_object->raw_bounding_box());
print_objects_new.emplace_back(print_object);
print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
}
continue;
}
// Complex case, try to merge the two lists.
// Sort the old lexicographically by their trafos.
std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
// Merge the old / new lists.
}
if (m_objects != print_objects_new) {
m_cancel_callback();
m_objects = print_objects_new;
}
// Synchronize materials.
#if 0
{
m_model = model;
for (const ModelObject *model_object : m_model.objects) {
PrintObject *object = new PrintObject(this, model_object, model_object->raw_bounding_box());
m_objects.emplace_back(object);
size_t volume_id = 0;
for (const ModelVolume *volume : model_object->volumes) {
if (! volume->is_model_part() && ! volume->is_modifier())
continue;
// Get the config applied to this volume.
PrintRegionConfig config = this->_region_config_from_model_volume(*volume);
// Find an existing print region with the same config.
size_t region_id = size_t(-1);
for (size_t i = 0; i < m_regions.size(); ++ i)
if (config.equals(m_regions[i]->config())) {
region_id = i;
break;
}
// If no region exists with the same config, create a new one.
if (region_id == size_t(-1)) {
region_id = m_regions.size();
this->add_region(config);
}
// Assign volume to a region.
object->add_region_volume(region_id, volume_id);
++ volume_id;
}
// Apply config to print object.
object->config_apply(this->default_object_config());
{
//normalize_and_apply_config(object->config(), model_object->config);
DynamicPrintConfig src_normalized(model_object->config);
src_normalized.normalize();
object->config_apply(src_normalized, true);
}
}
} else {
// Synchronize m_model.objects with model.objects
}
#endif
this->update_object_placeholders();
}
// Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects.
void Print::update_object_placeholders()
{
// get the first input file name
std::string input_file;
std::vector<std::string> v_scale;
for (const PrintObject *object : m_objects) {
const ModelObject &mobj = *object->model_object();
#if ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM
// CHECK_ME -> Is the following correct ?
v_scale.push_back("x:" + boost::lexical_cast<std::string>(mobj.instances[0]->get_scaling_factor(X) * 100) +
"% y:" + boost::lexical_cast<std::string>(mobj.instances[0]->get_scaling_factor(Y) * 100) +
"% z:" + boost::lexical_cast<std::string>(mobj.instances[0]->get_scaling_factor(Z) * 100) + "%");
#else
v_scale.push_back(boost::lexical_cast<std::string>(mobj.instances[0]->scaling_factor * 100) + "%");
#endif // ENABLE_MODELINSTANCE_3D_FULL_TRANSFORM
if (input_file.empty())
input_file = mobj.input_file;
}
PlaceholderParser &pp = m_placeholder_parser;
pp.set("scale", v_scale);
if (! input_file.empty()) {
// get basename with and without suffix
const std::string input_basename = boost::filesystem::path(input_file).filename().string();
pp.set("input_filename", input_basename);
const std::string input_basename_base = input_basename.substr(0, input_basename.find_last_of("."));
pp.set("input_filename_base", input_basename_base);
}
}
bool Print::has_infinite_skirt() const
{
return (m_config.skirt_height == -1 && m_config.skirts > 0)

View File

@ -194,6 +194,7 @@ public:
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); }
const LayerPtrs& layers() const { return m_layers; }
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
const Transform3d& trafo() const { return m_trafo; }
const Points& copies() const { return m_copies; }
bool add_copy(const Vec2d &point);
@ -285,6 +286,8 @@ private:
Print *m_print;
ModelObject *m_model_object;
PrintObjectConfig m_config;
// Translation in Z + Rotation + Scaling / Mirroring.
Transform3d m_trafo = Transform3d::Identity();
// Slic3r::Point objects in scaled G-code coordinates
Points m_copies;
// scaled coordinates to add to copies (to compensate for the alignment
@ -382,6 +385,8 @@ public:
bool reload_model_instances();
void add_model_object(ModelObject* model_object, int idx = -1);
bool apply_config(DynamicPrintConfig config);
bool apply(const Model &model, const DynamicPrintConfig &config);
void process();
void export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
// SLA export, temporary.
@ -477,6 +482,9 @@ protected:
PrintRegion* add_region(const PrintRegionConfig &config);
private:
// Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects.
void update_object_placeholders();
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
@ -503,6 +511,7 @@ private:
// Callback to be evoked to stop the background processing before a state is updated.
cancel_callback_type m_cancel_callback = [](){};
Model m_model;
PrintConfig m_config;
PrintObjectConfig m_default_object_config;
PrintRegionConfig m_default_region_config;

View File

@ -159,7 +159,7 @@ void MainFrame::init_tabpanel()
if (m_plater) {
// load initial config
auto full_config = wxGetApp().preset_bundle->full_config();
m_plater->on_config_change(&full_config);
m_plater->on_config_change(full_config);
// Show a correct number of filament fields.
// nozzle_diameter is undefined when SLA printer is selected
@ -530,9 +530,10 @@ void MainFrame::quick_slice(const int qs){
// Slic3r::GUI::catch_error(this, [](){ if (m_progress_dialog) m_progress_dialog->Destroy(); });
}
void MainFrame::reslice_now(){
// if (m_plater)
// m_plater->reslice();
void MainFrame::reslice_now()
{
if (m_plater)
m_plater->reslice();
}
void MainFrame::repair_stl()
@ -611,12 +612,13 @@ void MainFrame::load_config_file(wxString file/* = wxEmptyString*/)
file = dlg->GetPath();
dlg->Destroy();
}
// eval{
try {
wxGetApp().preset_bundle->load_config_file(file.ToStdString());
// };
// Dont proceed further if the config file cannot be loaded.
// if (Slic3r::GUI::catch_error(this))
// return;
} catch (std::exception & /* ex */) {
// Dont proceed further if the config file cannot be loaded.
// if (Slic3r::GUI::catch_error(this))
// return;
}
for (auto tab : m_options_tabs )
tab.second->load_current_preset();
wxGetApp().app_config->update_config_dir(get_dir_name(file));
@ -644,18 +646,20 @@ void MainFrame::export_configbundle()
dlg->Destroy();
if (!file.IsEmpty()) {
// Export the config bundle.
wxGetApp().app_config->update_config_dir(get_dir_name(file));
// eval{
wxGetApp().app_config->update_config_dir(get_dir_name(file));
try {
wxGetApp().preset_bundle->export_configbundle(file.ToStdString());
// };
} catch (std::exception & /* ex */) {
// Slic3r::GUI::catch_error(this);
}
}
}
// Loading a config bundle with an external file name used to be used
// to auto - install a config bundle on a fresh user account,
// but that behavior was not documented and likely buggy.
void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/){
void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/)
{
if (!wxGetApp().check_unsaved_changes())
return;
if (file.IsEmpty()) {
@ -671,10 +675,11 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re
wxGetApp().app_config->update_config_dir(get_dir_name(file));
auto presets_imported = 0;
// eval{
try {
presets_imported = wxGetApp().preset_bundle->load_configbundle(file.ToStdString());
// };
} catch (std::exception & /* ex */) {
// Slic3r::GUI::catch_error(this) and return;
}
// Load the currently selected preset into the GUI, update the preset selection box.
for (auto tab : m_options_tabs)
@ -686,20 +691,24 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re
// Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset.
// Also update the platter with the new presets.
void MainFrame::load_config(const DynamicPrintConfig& config){
void MainFrame::load_config(const DynamicPrintConfig& config)
{
for (auto tab : m_options_tabs)
tab.second->load_config(config);
// if (m_plater) m_plater->on_config_change(config);
if (m_plater)
m_plater->on_config_change(config);
}
void MainFrame::select_tab(size_t tab) const{
void MainFrame::select_tab(size_t tab) const
{
m_tabpanel->SetSelection(tab);
}
// Set a camera direction, zoom to all objects.
void MainFrame::select_view(const std::string& direction){
// if (m_plater)
// m_plater->select_view(direction);
void MainFrame::select_view(const std::string& direction)
{
// if (m_plater)
// m_plater->select_view(direction);
}
wxMenuItem* MainFrame::append_menu_item(wxMenu* menu,
@ -743,8 +752,7 @@ void MainFrame::on_presets_changed(SimpleEvent &event)
if (preset_type == Slic3r::Preset::TYPE_PRINTER) {
// Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
// XXX: Do this in a more C++ way
std::vector<std::string> tab_names_other = { "print", "filament", "sla_material" };
for (const auto tab_name_other : tab_names_other) {
for (const auto tab_name_other : { "print", "filament", "sla_material" }) {
Tab* cur_tab = m_options_tabs[tab_name_other];
// If the printer tells us that the print or filament preset has been switched or invalidated,
// refresh the print or filament tab page.Otherwise just refresh the combo box.
@ -756,8 +764,7 @@ void MainFrame::on_presets_changed(SimpleEvent &event)
cur_tab->load_current_preset();
}
}
// XXX: ?
// m_plater->on_config_change(tab->get_config());
m_plater->on_config_change(*tab->get_config());
}
}
@ -769,18 +776,19 @@ void MainFrame::on_value_changed(wxCommandEvent& event)
return;
auto opt_key = event.GetString();
auto config = tab->get_config();
if (m_plater) {
m_plater->on_config_change(config); // propagate config change events to the plater
if (opt_key == "extruders_count"){
auto value = event.GetInt();
m_plater->on_extruders_change(value);
}
m_plater->on_config_change(*tab->get_config()); // propagate config change events to the plater
if (opt_key == "extruders_count"){
auto value = event.GetInt();
m_plater->on_extruders_change(value);
}
}
// Don't save while loading for the first time.
if (m_loaded) {
AppConfig &cfg = *wxGetApp().app_config;
if (cfg.get("autosave") == "1")
cfg.save();
}
// don't save while loading for the first time
// #ys_FIXME ?autosave?
// if (wxGetApp().autosave && m_loaded)
// m_config->save(wxGetApp().autosave);
}
// Called after the Preferences dialog is closed and the program settings are saved.

View File

@ -209,6 +209,7 @@ PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) :
if (dialog->ShowModal() == wxID_OK) {
DynamicPrintConfig cfg = *wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config();
//FIXME this is too expensive to call full_config to get just the extruder color!
auto colors = static_cast<ConfigOptionStrings*>(wxGetApp().preset_bundle->full_config().option("extruder_colour")->clone());
colors->values[extruder_idx] = dialog->GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX);
@ -216,7 +217,7 @@ PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) :
wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg);
wxGetApp().preset_bundle->update_platter_filament_ui(extruder_idx, this);
wxGetApp().plater()->on_config_change(&cfg);
wxGetApp().plater()->on_config_change(cfg);
}
dialog->Destroy();
});
@ -741,7 +742,8 @@ struct Plater::priv
Sidebar *sidebar;
wxGLCanvas *canvas3D; // TODO: Use GLCanvas3D when we can
Preview *preview;
BackgroundSlicingProcess background_process;
BackgroundSlicingProcess background_process;
wxTimer background_process_timer;
static const std::regex pattern_bundle;
static const std::regex pattern_3mf;
@ -855,15 +857,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) :
_3DScene::enable_shader(canvas3D, true);
_3DScene::enable_force_zoom_to_bed(canvas3D, true);
// XXX: apply_config_timer
// {
// my $timer_id = Wx::NewId();
// $self->{apply_config_timer} = Wx::Timer->new($self, $timer_id);
// EVT_TIMER($self, $timer_id, sub {
// my ($self, $event) = @_;
// $self->async_apply_config;
// });
// }
background_process_timer.Bind(wxEVT_TIMER, [this](wxTimerEvent &evt){ this->async_apply_config(); }, 0);
auto *bed_shape = config->opt<ConfigOptionPoints>("bed_shape");
_3DScene::set_bed_shape(canvas3D, bed_shape->values);
@ -961,7 +955,7 @@ void Plater::priv::update(bool force_autocenter)
preview->reset_gcode_preview_data();
preview->reload_print();
// schedule_background_process(); // TODO
schedule_background_process();
}
void Plater::priv::update_ui_from_settings()
@ -1172,7 +1166,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
_3DScene::zoom_to_volumes(canvas3D);
object_list_changed();
// $self->schedule_background_process;
this->schedule_background_process();
return obj_idxs;
}
@ -1449,12 +1443,40 @@ void Plater::priv::split_object()
void Plater::priv::schedule_background_process()
{
// TODO
// Trigger the timer event after 0.5s
this->background_process_timer.Start(500, wxTIMER_ONE_SHOT);
}
void Plater::priv::async_apply_config()
{
// TODO
// Apply new config to the possibly running background task.
bool was_running = this->background_process.running();
bool invalidated = this->background_process.apply_config(wxGetApp().preset_bundle->full_config());
// Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile.
if (Slic3r::_3DScene::is_layers_editing_enabled(this->canvas3D))
this->canvas3D->Refresh();
// If the apply_config caused the calculation to stop, or it was not running yet:
if (invalidated) {
if (was_running) {
// Hide the slicing results if the current slicing status is no more valid.
this->sidebar->show_info_sizers(false);
}
if (this->get_config("background_processing") == "1")
this->background_process.start();
if (was_running) {
// Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared.
// Otherwise they will be just refreshed.
this->gcode_preview_data.reset();
if (this->preview != nullptr)
this->preview->reload_print();
// We also need to reload 3D scene because of the wipe tower preview box
if (this->config->opt_bool("wipe_tower")) {
std::vector<int> selections = this->collect_selections();
Slic3r::_3DScene::set_objects_selections(this->canvas3D, selections);
Slic3r::_3DScene::reload_scene(this->canvas3D, 1);
}
}
}
}
void Plater::priv::start_background_process()
@ -1542,8 +1564,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
// Synchronize config.ini with the current selections.
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config);
// update plater with new config
auto config = wxGetApp().preset_bundle->full_config();
wxGetApp().plater()->on_config_change(&config);
wxGetApp().plater()->on_config_change(wxGetApp().preset_bundle->full_config());
}
void Plater::priv::on_progress_event()
@ -1677,7 +1698,8 @@ void Plater::priv::on_scale_uniformly(SimpleEvent&)
// $self->selection_changed(1); # refresh info (size, volume etc.)
// $self->update;
// $self->schedule_background_process;
this->schedule_background_process();
}
void Plater::priv::on_wipetower_moved(Vec3dEvent &evt)
@ -1756,7 +1778,7 @@ void Plater::increase(size_t num)
p->selection_changed();
// $self->schedule_background_process;
this->p->schedule_background_process();
}
void Plater::decrease(size_t num)
@ -1933,11 +1955,11 @@ void Plater::on_extruders_change(int num_extruders)
GetParent()->Layout();
}
void Plater::on_config_change(DynamicPrintConfig* config)
void Plater::on_config_change(const DynamicPrintConfig &config)
{
bool update_scheduled = false;
for ( auto opt_key: p->config->diff(*config)) {
p->config->set_key_value(opt_key, config->option(opt_key)->clone());
for (auto opt_key : p->config->diff(config)) {
p->config->set_key_value(opt_key, config.option(opt_key)->clone());
if (opt_key == "bed_shape") {
if (p->canvas3D) _3DScene::set_bed_shape(p->canvas3D, p->config->option<ConfigOptionPoints>(opt_key)->values);
if (p->preview) p->preview->set_bed_shape(p->config->option<ConfigOptionPoints>(opt_key)->values);
@ -1948,7 +1970,7 @@ void Plater::on_config_change(DynamicPrintConfig* config)
update_scheduled = true;
}
// else if(opt_key == "serial_port") {
// sidebar()->p->btn_print->Show(config->get("serial_port")); // ???: btn_print is removed
// sidebar()->p->btn_print->Show(config.get("serial_port")); // ???: btn_print is removed
// Layout();
// }
else if (opt_key == "print_host") {
@ -1982,10 +2004,8 @@ void Plater::on_config_change(DynamicPrintConfig* config)
if (update_scheduled)
update();
if (!p->main_frame->is_loaded()) return ;
// (re)start timer
// schedule_background_process();
if (p->main_frame->is_loaded())
this->p->schedule_background_process();
}
wxGLCanvas* Plater::canvas3D()
@ -2012,7 +2032,7 @@ void Plater::changed_object_settings(int obj_idx)
if (list->is_parts_changed() || list->is_part_settings_changed()) {
// stop_background_process();
// $self->{print}->reload_object($obj_idx);
// schedule_background_process();
this->p->schedule_background_process();
#if !ENABLE_EXTENDED_SELECTION
if (p->canvas3D) _3DScene::reload_scene(p->canvas3D, true);
auto selections = p->collect_selections();
@ -2021,7 +2041,7 @@ void Plater::changed_object_settings(int obj_idx)
_3DScene::reload_scene(p->canvas3D, false);
}
else {
// schedule_background_process();
this->p->schedule_background_process();
}
}

View File

@ -123,7 +123,7 @@ public:
void send_gcode();
void on_extruders_change(int extruders_count);
void on_config_change(DynamicPrintConfig* config);
void on_config_change(const DynamicPrintConfig &config);
wxGLCanvas* canvas3D();
private:

View File

@ -262,7 +262,7 @@ ModelMaterial::attributes()
%code%{ THIS->name = value; %};
t_model_material_id material_id();
void set_material_id(t_model_material_id material_id)
%code%{ THIS->material_id(material_id); %};
%code%{ THIS->set_material_id(material_id); %};
Ref<ModelMaterial> material();
Ref<DynamicPrintConfig> config()