WIP: Synchronization of Model / ModelObject with Print / PrintObject

This commit is contained in:
bubnikv 2018-10-18 14:36:46 +02:00
parent 7ed2752b2b
commit bb1dfa3522
2 changed files with 228 additions and 126 deletions

View file

@ -376,6 +376,16 @@ double Print::max_allowed_layer_height() const
return nozzle_diameter_max; return nozzle_diameter_max;
} }
static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume)
{
PrintRegionConfig config = default_region_config;
normalize_and_apply_config(config, volume.get_object()->config);
normalize_and_apply_config(config, volume.config);
if (! volume.material_id().empty())
normalize_and_apply_config(config, volume.material()->config);
return config;
}
// Caller is responsible for supplying models whose objects don't collide // Caller is responsible for supplying models whose objects don't collide
// and have explicit instance positions. // and have explicit instance positions.
void Print::add_model_object(ModelObject* model_object, int idx) void Print::add_model_object(ModelObject* model_object, int idx)
@ -397,7 +407,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
if (! volume->is_model_part() && ! volume->is_modifier()) if (! volume->is_model_part() && ! volume->is_modifier())
continue; continue;
// Get the config applied to this volume. // Get the config applied to this volume.
PrintRegionConfig config = this->_region_config_from_model_volume(*volume); PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume);
// Find an existing print region with the same config. // Find an existing print region with the same config.
size_t region_id = size_t(-1); size_t region_id = size_t(-1);
for (size_t i = 0; i < m_regions.size(); ++ i) for (size_t i = 0; i < m_regions.size(); ++ i)
@ -424,35 +434,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
object->config_apply(src_normalized, true); object->config_apply(src_normalized, true);
} }
// update placeholders this->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::apply_config(DynamicPrintConfig config) bool Print::apply_config(DynamicPrintConfig config)
@ -514,12 +496,12 @@ bool Print::apply_config(DynamicPrintConfig config)
// If the new config for this volume differs from the other // If the new config for this volume differs from the other
// volume configs currently associated to this region, it means // volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore. // the region subdivision does not make sense anymore.
if (! this_region_config.equals(this->_region_config_from_model_volume(volume))) { if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume))) {
rearrange_regions = true; rearrange_regions = true;
goto exit_for_rearrange_regions; goto exit_for_rearrange_regions;
} }
} else { } else {
this_region_config = this->_region_config_from_model_volume(volume); this_region_config = region_config_from_model_volume(m_default_region_config, volume);
this_region_config_set = true; this_region_config_set = true;
} }
for (const PrintRegionConfig &cfg : other_region_configs) { for (const PrintRegionConfig &cfg : other_region_configs) {
@ -681,7 +663,7 @@ static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &
struct PrintInstances struct PrintInstances
{ {
Transform3d trafo; Transform3d trafo;
Points instances; Points copies;
bool operator<(const PrintInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); } bool operator<(const PrintInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
}; };
@ -690,26 +672,46 @@ static std::vector<PrintInstances> print_objects_from_model_object(const ModelOb
{ {
std::set<PrintInstances> trafos; std::set<PrintInstances> trafos;
PrintInstances trafo; PrintInstances trafo;
trafo.instances.assign(1, Point()); trafo.copies.assign(1, Point());
for (ModelInstance *model_instance : model_object.instances) for (ModelInstance *model_instance : model_object.instances)
if (model_instance->is_printable()) { if (model_instance->is_printable()) {
const Vec3d &offst = model_instance->get_offset(); const Vec3d &offst = model_instance->get_offset();
trafo.trafo = model_instance->world_matrix(true); trafo.trafo = model_instance->world_matrix(true);
trafo.instances.front() = Point::new_scale(offst(0), offst(1)); trafo.copies.front() = Point::new_scale(offst(0), offst(1));
auto it = trafos.find(trafo); auto it = trafos.find(trafo);
if (it == trafos.end()) if (it == trafos.end())
trafos.emplace(trafo); trafos.emplace(trafo);
else else
const_cast<PrintInstances&>(*it).instances.emplace_back(trafo.instances.front()); const_cast<PrintInstances&>(*it).copies.emplace_back(trafo.copies.front());
} }
return std::vector<PrintInstances>(trafos.begin(), trafos.end()); return std::vector<PrintInstances>(trafos.begin(), trafos.end());
} }
bool Print::apply(const Model &model, const DynamicPrintConfig &config) bool Print::apply(const Model &model, const DynamicPrintConfig &config_in)
{ {
// Make a copy of the config, normalize it.
DynamicPrintConfig config(config_in);
config.normalize();
// Collect changes to print config.
t_config_option_keys print_diff = m_config.diff(config);
t_config_option_keys object_diff = m_default_object_config.diff(config);
t_config_option_keys region_diff = m_default_region_config.diff(config);
// Grab the lock for the Print / PrintObject milestones. // Grab the lock for the Print / PrintObject milestones.
tbb::mutex::scoped_lock lock(m_mutex); tbb::mutex::scoped_lock lock(m_mutex);
// The following call may stop the background processing.
bool invalidated = this->invalidate_state_by_config_options(print_diff);
// Apply variables to placeholder parser. The placeholder parser is used by G-code export,
// which should be stopped if print_diff is not empty.
m_placeholder_parser.apply_config(config);
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
m_config.apply_only(config, print_diff, true);
// Handle changes to object config defaults
m_default_object_config.apply_only(config, object_diff, true);
// Handle changes to regions config defaults
m_default_region_config.apply_only(config, region_diff, true);
struct ModelObjectStatus { struct ModelObjectStatus {
enum Status { enum Status {
Unknown, Unknown,
@ -719,8 +721,9 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config)
Deleted, Deleted,
}; };
ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {} ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
ModelID id; ModelID id;
Status status; Status status;
t_config_option_keys object_config_diff;
// Search by id. // Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; } bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
}; };
@ -729,7 +732,8 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config)
// 1) Synchronize model objects. // 1) Synchronize model objects.
if (model.id() != m_model.id()) { if (model.id() != m_model.id()) {
// Kill everything, initialize from scratch. // Kill everything, initialize from scratch.
// The following call shall kill any computation if running. // Stop background processing.
m_cancel_callback();
this->invalidate_all_steps(); this->invalidate_all_steps();
for (PrintObject *object : m_objects) { for (PrintObject *object : m_objects) {
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted); model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
@ -746,6 +750,8 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config)
} else if (model_object_list_extended(m_model, model)) { } else if (model_object_list_extended(m_model, model)) {
// Add new objects. Their volumes and configs will be synchronized later. // Add new objects. Their volumes and configs will be synchronized later.
this->invalidate_step(psGCodeExport); this->invalidate_step(psGCodeExport);
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) { for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New); model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
m_model.objects.emplace_back(model.objects[i]->clone(&m_model)); m_model.objects.emplace_back(model.objects[i]->clone(&m_model));
@ -770,7 +776,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config)
} else { } else {
// Existing ModelObject re-added (possibly moved in the list). // Existing ModelObject re-added (possibly moved in the list).
m_model.objects.emplace_back(*it); m_model.objects.emplace_back(*it);
model_object_status.emplace(mobj->id(), ModelObjectStatus::Old); model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved);
} }
} }
bool deleted_any = false; bool deleted_any = false;
@ -835,7 +841,7 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config)
// PrintObject instances will be added in the next loop. // PrintObject instances will be added in the next loop.
continue; continue;
// Update the ModelObject instance, possibly invalidate the linked PrintObjects. // Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Moved); assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
const ModelObject &model_object_new = *model.objects[idx_model_object]; 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. // 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 model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::MODEL_PART);
@ -864,87 +870,187 @@ bool Print::apply(const Model &model, const DynamicPrintConfig &config)
} }
if (! model_parts_differ && ! modifiers_differ) { if (! model_parts_differ && ! modifiers_differ) {
// Synchronize the remaining data of ModelVolumes (name, config, m_type, m_material_id) // Synchronize the remaining data of ModelVolumes (name, config, m_type, m_material_id)
// Synchronize Object's config.
t_config_option_keys &this_object_config_diff = const_cast<ModelObjectStatus&>(*it_status).object_config_diff;
this_object_config_diff = model_object.config.diff(model_object_new.config);
if (! this_object_config_diff.empty())
model_object.config.apply_only(model_object_new.config, this_object_config_diff, true);
if (! this_object_config_diff.empty() || ! this_object_config_diff.empty()) {
PrintObjectConfig new_config = m_default_object_config;
normalize_and_apply_config(new_config, model_object.config);
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
t_config_option_keys diff = it->print_object->config().diff(new_config);
invalidated |= it->print_object->invalidate_state_by_config_options(diff);
it->print_object->config_apply_only(new_config, diff, true);
}
}
} }
} }
// 4) Generate PrintObjects from ModelObjects and their instances. // 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())); std::vector<PrintObject*> print_objects_new;
// Walk over all new model objects and check, whether there are matching PrintObjects. print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
for (ModelObject *model_object : m_model.objects) { // Walk over all new model objects and check, whether there are matching PrintObjects.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id())); for (ModelObject *model_object : m_model.objects) {
std::vector<const PrintObjectStatus*> old; auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id()));
if (range.first != range.second) { std::vector<const PrintObjectStatus*> old;
old.reserve(print_object_status.count(PrintObjectStatus(model_object->id()))); if (range.first != range.second) {
for (auto it = range.first; it != range.second; ++ it) old.reserve(print_object_status.count(PrintObjectStatus(model_object->id())));
if (it->status != PrintObjectStatus::Deleted) for (auto it = range.first; it != range.second; ++ it)
old.emplace_back(&(*it)); if (it->status != PrintObjectStatus::Deleted)
old.emplace_back(&(*it));
}
// Generate a list of trafos and XY offsets for instances of a ModelObject
PrintObjectConfig config = m_default_object_config;
normalize_and_apply_config(config, model_object->config);
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_object->set_copies(print_instances.copies);
print_object->config_apply(config);
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.
auto it_old = old.begin();
for (const PrintInstances &new_instances : new_print_instances) {
for (; transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
if (! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
// This is a new instance (or a set of instances with the same trafo). Just add it.
PrintObject *print_object = new PrintObject(this, model_object, model_object->raw_bounding_box());
print_object->set_copies(new_instances.copies);
print_object->config_apply(config);
print_objects_new.emplace_back(print_object);
print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
} else if ((*it_old)->print_object->copies() != new_instances.copies) {
// The PrintObject already exists and the copies differ. The only step currently sensitive to the order is the G-code generator.
// Stop it.
this->invalidate_step(psGCodeExport);
(*it_old)->print_object->set_copies(new_instances.copies);
}
}
} }
// Generate a list of trafos and XY offsets for instances of a ModelObject if (m_objects != print_objects_new) {
std::vector<PrintInstances> new_print_instances = print_objects_from_model_object(*model_object); m_cancel_callback();
if (old.empty()) { m_objects = print_objects_new;
// 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); // 5) Synchronize configs of ModelVolumes, synchronize AMF / 3MF materials (and their configs), refresh PrintRegions.
print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); // Update reference counts of regions from the remaining PrintObjects and their volumes.
// Regions with zero references could and should be reused.
for (PrintRegion *region : m_regions)
region->m_refcnt = 0;
for (PrintObject *print_object : m_objects) {
int idx_region = 0;
for (const auto &volumes : print_object->region_volumes) {
if (! volumes.empty())
++ m_regions[idx_region];
++ idx_region;
}
}
// All regions now have distinct settings.
// Check whether applying the new region config defaults we'd get different regions.
for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) {
PrintRegion &region = *m_regions[region_id];
PrintRegionConfig this_region_config;
bool this_region_config_set = false;
for (PrintObject *print_object : m_objects) {
if (region_id < print_object->region_volumes.size()) {
for (int volume_id : print_object->region_volumes[region_id]) {
const ModelVolume &volume = *print_object->model_object()->volumes[volume_id];
if (this_region_config_set) {
// If the new config for this volume differs from the other
// volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore.
if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume)))
// Regions were split. Reset this print_object.
goto print_object_end;
} else {
this_region_config = region_config_from_model_volume(m_default_region_config, volume);
for (size_t i = 0; i < region_id; ++ i)
if (m_regions[i]->config().equals(this_region_config))
// Regions were merged. Reset this print_object.
goto print_object_end;
this_region_config_set = true;
}
}
} }
continue; continue;
print_object_end:
print_object->invalidate_all_steps();
// Decrease the references to regions from this volume.
int ireg = 0;
for (const std::vector<int> &volumes : print_object->region_volumes) {
if (! volumes.empty())
-- m_regions[ireg];
++ ireg;
}
print_object->region_volumes.clear();
}
if (this_region_config_set) {
t_config_option_keys diff = region.config().diff(this_region_config);
if (! diff.empty()) {
region.config_apply_only(this_region_config, diff, false);
for (PrintObject *print_object : m_objects)
if (region_id < print_object->region_volumes.size() && ! print_object->region_volumes[region_id].empty())
invalidated |= print_object->invalidate_state_by_config_options(diff);
}
} }
// 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. // Possibly add new regions for the newly added or resetted PrintObjects.
for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
#if 0 PrintObject &print_object0 = *m_objects[idx_print_object];
{ const ModelObject &model_object = *print_object0.model_object();
m_model = model; std::vector<int> map_volume_to_region(model_object.volumes.size(), -1);
for (const ModelObject *model_object : m_model.objects) { for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) {
PrintObject *object = new PrintObject(this, model_object, model_object->raw_bounding_box()); PrintObject &print_object = *m_objects[i];
m_objects.emplace_back(object); unsigned int volume_id = 0;
size_t volume_id = 0; for (const ModelVolume *volume : model_object.volumes) {
for (const ModelVolume *volume : model_object->volumes) {
if (! volume->is_model_part() && ! volume->is_modifier()) if (! volume->is_model_part() && ! volume->is_modifier())
continue; continue;
// Get the config applied to this volume. int region_id = -1;
PrintRegionConfig config = this->_region_config_from_model_volume(*volume); if (&print_object == &print_object0) {
// Find an existing print region with the same config. // Get the config applied to this volume.
size_t region_id = size_t(-1); PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume);
for (size_t i = 0; i < m_regions.size(); ++ i) // Find an existing print region with the same config.
if (config.equals(m_regions[i]->config())) { for (int i = 0; i < (int)m_regions.size(); ++ i)
region_id = i; if (config.equals(m_regions[i]->config())) {
break; region_id = i;
break;
}
// If no region exists with the same config, create a new one.
if (region_id == size_t(-1)) {
for (region_id = 0; region_id < m_regions.size(); ++ region_id)
if (m_regions[region_id]->m_refcnt == 0) {
// An empty slot was found.
m_regions[region_id]->set_config(std::move(config));
break;
}
if (region_id == m_regions.size())
this->add_region(config);
} }
// If no region exists with the same config, create a new one. map_volume_to_region[volume_id] = region_id;
if (region_id == size_t(-1)) { } else
region_id = m_regions.size(); region_id = map_volume_to_region[volume_id];
this->add_region(config);
}
// Assign volume to a region. // Assign volume to a region.
object->add_region_volume(region_id, volume_id); if (volume_id >= print_object.region_volumes.size())
print_object.add_region_volume(region_id, volume_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(); this->update_object_placeholders();
} }
@ -1284,16 +1390,6 @@ Flow Print::skirt_flow() const
); );
} }
PrintRegionConfig Print::_region_config_from_model_volume(const ModelVolume &volume)
{
PrintRegionConfig config = this->default_region_config();
normalize_and_apply_config(config, volume.get_object()->config);
normalize_and_apply_config(config, volume.config);
if (! volume.material_id().empty())
normalize_and_apply_config(config, volume.material()->config);
return config;
}
bool Print::has_support_material() const bool Print::has_support_material() const
{ {
for (const PrintObject *object : m_objects) for (const PrintObject *object : m_objects)

View file

@ -139,19 +139,25 @@ public:
// Average diameter of nozzles participating on extruding this region. // Average diameter of nozzles participating on extruding this region.
coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
// Average diameter of nozzles participating on extruding this region. // Average diameter of nozzles participating on extruding this region.
coordf_t bridging_height_avg(const PrintConfig &print_config) const; coordf_t bridging_height_avg(const PrintConfig &print_config) const;
// Methods modifying the PrintRegion's state: // Methods modifying the PrintRegion's state:
public: public:
Print* print() { return m_print; } Print* print() { return m_print; }
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); } void set_config(const PrintRegionConfig &config) { m_config = config; }
void set_config(PrintRegionConfig &&config) { m_config = std::move(config); }
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); }
protected:
size_t m_refcnt;
private: private:
Print *m_print; Print *m_print;
PrintRegionConfig m_config; PrintRegionConfig m_config;
PrintRegion(Print* print) : m_print(print) {} PrintRegion(Print* print) : m_refcnt(0), m_print(print) {}
PrintRegion(Print* print, const PrintRegionConfig &config) : m_print(print), m_config(config) {} PrintRegion(Print* print, const PrintRegionConfig &config) : m_refcnt(0), m_print(print), m_config(config) {}
~PrintRegion() {} ~PrintRegion() {}
}; };
@ -196,12 +202,13 @@ public:
const SupportLayerPtrs& support_layers() const { return m_support_layers; } const SupportLayerPtrs& support_layers() const { return m_support_layers; }
const Transform3d& trafo() const { return m_trafo; } const Transform3d& trafo() const { return m_trafo; }
const Points& copies() const { return m_copies; } const Points& copies() const { return m_copies; }
bool add_copy(const Vec2d &point); bool add_copy(const Vec2d &point);
bool delete_last_copy(); bool delete_last_copy();
bool delete_all_copies() { return this->set_copies(Points()); } bool delete_all_copies() { return this->set_copies(Points()); }
bool set_copies(const Points &points); bool set_copies(const Points &points);
bool reload_model_instances(); bool reload_model_instances();
// since the object is aligned to origin, bounding box coincides with size // since the object is aligned to origin, bounding box coincides with size
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); } BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
@ -486,7 +493,6 @@ private:
void update_object_placeholders(); void update_object_placeholders();
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys); bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
// If the background processing stop was requested, throw CanceledException. // 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. // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.