Reworked handling of approximate / snug bounding boxes

at Model / ModelObject / PrintObject
Fixes #9467 #9519 #9646
Also fixed variable layer height editing for case such as mentioned
in the github issues above.
Also likely fixed some of the sinking objects issues.
This commit is contained in:
Vojtech Bubnik 2023-02-28 11:27:15 +01:00
parent cbe3c4f238
commit 3349644964
7 changed files with 159 additions and 117 deletions

View File

@ -382,7 +382,7 @@ int CLI::run(int argc, char **argv)
} else if (opt_key == "align_xy") {
const Vec2d &p = m_config.option<ConfigOptionPoint>("align_xy")->value;
for (auto &model : m_models) {
BoundingBoxf3 bb = model.bounding_box();
BoundingBoxf3 bb = model.bounding_box_exact();
// this affects volumes:
model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z());
}
@ -423,7 +423,7 @@ int CLI::run(int argc, char **argv)
} else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
std::vector<Model> new_models;
for (auto &model : m_models) {
model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
model.translate(0, 0, -model.bounding_box_exact().min.z()); // align to z = 0
size_t num_objects = model.objects.size();
for (size_t i = 0; i < num_objects; ++ i) {

View File

@ -323,14 +323,30 @@ bool Model::add_default_instances()
}
// this returns the bounding box of the *transformed* instances
BoundingBoxf3 Model::bounding_box() const
BoundingBoxf3 Model::bounding_box_approx() const
{
BoundingBoxf3 bb;
for (ModelObject *o : this->objects)
bb.merge(o->bounding_box());
bb.merge(o->bounding_box_approx());
return bb;
}
BoundingBoxf3 Model::bounding_box_exact() const
{
BoundingBoxf3 bb;
for (ModelObject *o : this->objects)
bb.merge(o->bounding_box_exact());
return bb;
}
double Model::max_z() const
{
double z = 0;
for (ModelObject *o : this->objects)
z = std::max(z, o->max_z());
return z;
}
unsigned int Model::update_print_volume_state(const BuildVolume &build_volume)
{
unsigned int num_printable = 0;
@ -377,7 +393,7 @@ void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
ModelObject* object = this->objects.front();
object->clear_instances();
Vec3d ext_size = object->bounding_box().size() + dist * Vec3d::Ones();
Vec3d ext_size = object->bounding_box_exact().size() + dist * Vec3d::Ones();
for (size_t x_copy = 1; x_copy <= x; ++x_copy) {
for (size_t y_copy = 1; y_copy <= y; ++y_copy) {
@ -548,13 +564,13 @@ void Model::adjust_min_z()
if (objects.empty())
return;
if (bounding_box().min(2) < 0.0)
if (this->bounding_box_exact().min.z() < 0.0)
{
for (ModelObject* obj : objects)
{
if (obj != nullptr)
{
coordf_t obj_min_z = obj->bounding_box().min(2);
coordf_t obj_min_z = obj->min_z();
if (obj_min_z < 0.0)
obj->translate_instances(Vec3d(0.0, 0.0, -obj_min_z));
}
@ -627,8 +643,11 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
this->printable = rhs.printable;
this->origin_translation = rhs.origin_translation;
this->cut_id.copy(rhs.cut_id);
m_bounding_box = rhs.m_bounding_box;
m_bounding_box_valid = rhs.m_bounding_box_valid;
m_bounding_box_approx = rhs.m_bounding_box_approx;
m_bounding_box_approx_valid = rhs.m_bounding_box_approx_valid;
m_bounding_box_exact = rhs.m_bounding_box_exact;
m_bounding_box_exact_valid = rhs.m_bounding_box_exact_valid;
m_min_max_z_valid = rhs.m_min_max_z_valid;
m_raw_bounding_box = rhs.m_raw_bounding_box;
m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid;
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
@ -668,8 +687,11 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
this->layer_height_profile = std::move(rhs.layer_height_profile);
this->printable = std::move(rhs.printable);
this->origin_translation = std::move(rhs.origin_translation);
m_bounding_box = std::move(rhs.m_bounding_box);
m_bounding_box_valid = std::move(rhs.m_bounding_box_valid);
m_bounding_box_approx = std::move(rhs.m_bounding_box_approx);
m_bounding_box_approx_valid = std::move(rhs.m_bounding_box_approx_valid);
m_bounding_box_exact = std::move(rhs.m_bounding_box_exact);
m_bounding_box_exact_valid = std::move(rhs.m_bounding_box_exact_valid);
m_min_max_z_valid = rhs.m_min_max_z_valid;
m_raw_bounding_box = rhs.m_raw_bounding_box;
m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid;
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
@ -864,16 +886,73 @@ void ModelObject::clear_instances()
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
const BoundingBoxf3& ModelObject::bounding_box() const
const BoundingBoxf3& ModelObject::bounding_box_approx() const
{
if (! m_bounding_box_valid) {
m_bounding_box_valid = true;
if (! m_bounding_box_approx_valid) {
m_bounding_box_approx_valid = true;
BoundingBoxf3 raw_bbox = this->raw_mesh_bounding_box();
m_bounding_box.reset();
m_bounding_box_approx.reset();
for (const ModelInstance *i : this->instances)
m_bounding_box.merge(i->transform_bounding_box(raw_bbox));
m_bounding_box_approx.merge(i->transform_bounding_box(raw_bbox));
}
return m_bounding_box_approx;
}
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
const BoundingBoxf3& ModelObject::bounding_box_exact() const
{
if (! m_bounding_box_exact_valid) {
m_bounding_box_exact_valid = true;
m_min_max_z_valid = true;
BoundingBoxf3 raw_bbox = this->raw_mesh_bounding_box();
m_bounding_box_exact.reset();
for (size_t i = 0; i < this->instances.size(); ++ i)
m_bounding_box_exact.merge(this->instance_bounding_box(i));
}
return m_bounding_box_exact;
}
double ModelObject::min_z() const
{
const_cast<ModelObject*>(this)->update_min_max_z();
return m_bounding_box_exact.min.z();
}
double ModelObject::max_z() const
{
const_cast<ModelObject*>(this)->update_min_max_z();
return m_bounding_box_exact.max.z();
}
void ModelObject::update_min_max_z()
{
assert(! this->instances.empty());
if (! m_min_max_z_valid && ! this->instances.empty()) {
m_min_max_z_valid = true;
const Transform3d mat_instance = this->instances.front()->get_transformation().get_matrix();
double global_min_z = std::numeric_limits<double>::max();
double global_max_z = - std::numeric_limits<double>::max();
for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) {
const Transform3d m = mat_instance * v->get_matrix();
const Vec3d row_z = m.linear().row(2).cast<double>();
const double shift_z = m.translation().z();
double this_min_z = std::numeric_limits<double>::max();
double this_max_z = - std::numeric_limits<double>::max();
for (const Vec3f &p : v->mesh().its.vertices) {
double z = row_z.dot(p.cast<double>());
this_min_z = std::min(this_min_z, z);
this_max_z = std::max(this_max_z, z);
}
this_min_z += shift_z;
this_max_z += shift_z;
global_min_z = std::min(global_min_z, this_min_z);
global_max_z = std::max(global_max_z, this_max_z);
}
m_bounding_box_exact.min.z() = global_min_z;
m_bounding_box_exact.max.z() = global_max_z;
}
return m_bounding_box;
}
// A mesh containing all transformed instances of this object.
@ -1031,19 +1110,19 @@ void ModelObject::ensure_on_bed(bool allow_negative_z)
if (allow_negative_z) {
if (parts_count() == 1) {
const double min_z = get_min_z();
const double max_z = get_max_z();
const double min_z = this->min_z();
const double max_z = this->max_z();
if (min_z >= SINKING_Z_THRESHOLD || max_z < 0.0)
z_offset = -min_z;
}
else {
const double max_z = get_max_z();
const double max_z = this->max_z();
if (max_z < SINKING_MIN_Z_THRESHOLD)
z_offset = SINKING_MIN_Z_THRESHOLD - max_z;
}
}
else
z_offset = -get_min_z();
z_offset = -this->min_z();
if (z_offset != 0.0)
translate_instances(z_offset * Vec3d::UnitZ());
@ -1070,8 +1149,10 @@ void ModelObject::translate(double x, double y, double z)
v->translate(x, y, z);
}
if (m_bounding_box_valid)
m_bounding_box.translate(x, y, z);
if (m_bounding_box_approx_valid)
m_bounding_box_approx.translate(x, y, z);
if (m_bounding_box_exact_valid)
m_bounding_box_exact.translate(x, y, z);
}
void ModelObject::scale(const Vec3d &versor)
@ -1866,32 +1947,6 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx)
this->invalidate_bounding_box();
}
double ModelObject::get_min_z() const
{
if (instances.empty())
return 0.0;
else {
double min_z = DBL_MAX;
for (size_t i = 0; i < instances.size(); ++i) {
min_z = std::min(min_z, get_instance_min_z(i));
}
return min_z;
}
}
double ModelObject::get_max_z() const
{
if (instances.empty())
return 0.0;
else {
double max_z = -DBL_MAX;
for (size_t i = 0; i < instances.size(); ++i) {
max_z = std::max(max_z, get_instance_max_z(i));
}
return max_z;
}
}
double ModelObject::get_instance_min_z(size_t instance_idx) const
{
double min_z = DBL_MAX;
@ -2268,7 +2323,7 @@ void ModelVolume::scale(const Vec3d& scaling_factors)
void ModelObject::scale_to_fit(const Vec3d &size)
{
Vec3d orig_size = this->bounding_box().size();
Vec3d orig_size = this->bounding_box_exact().size();
double factor = std::min(
size.x() / orig_size.x(),
std::min(
@ -2373,37 +2428,6 @@ void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) cons
#endif // ENABLE_WORLD_COORDINATE
}
BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh& mesh, bool dont_translate) const
{
// Rotate around mesh origin.
TriangleMesh copy(mesh);
#if ENABLE_WORLD_COORDINATE
copy.transform(get_transformation().get_rotation_matrix());
#else
copy.transform(get_matrix(true, false, true, true));
#endif // ENABLE_WORLD_COORDINATE
BoundingBoxf3 bbox = copy.bounding_box();
if (!empty(bbox)) {
// Scale the bounding box along the three axes.
for (unsigned int i = 0; i < 3; ++i)
{
if (std::abs(get_scaling_factor((Axis)i)-1.0) > EPSILON)
{
bbox.min(i) *= get_scaling_factor((Axis)i);
bbox.max(i) *= get_scaling_factor((Axis)i);
}
}
// Translate the bounding box.
if (! dont_translate) {
bbox.min += get_offset();
bbox.max += get_offset();
}
}
return bbox;
}
BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const
{
#if ENABLE_WORLD_COORDINATE

View File

@ -168,7 +168,7 @@ private:
friend class cereal::access;
friend class UndoRedo::StackImpl;
// Create an object for deserialization, don't allocate IDs for ModelMaterial and its config.
ModelMaterial() : ObjectBase(-1), config(-1), m_model(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); }
ModelMaterial() : ObjectBase(-1), config(-1) { assert(this->id().invalid()); assert(this->config.id().invalid()); }
template<class Archive> void serialize(Archive &ar) {
assert(this->id().invalid()); assert(this->config.id().invalid());
Internal::StaticSerializationWrapper<ModelConfigObject> config_wrapper(config);
@ -343,7 +343,7 @@ public:
// The pairs of <z, layer_height> are packed into a 1D array.
LayerHeightProfile layer_height_profile;
// Whether or not this object is printable
bool printable;
bool printable { true };
// This vector holds position of selected support points for SLA. The data are
// saved in mesh coordinates to allow using them for several instances.
@ -397,11 +397,22 @@ public:
void delete_last_instance();
void clear_instances();
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
// This bounding box is being cached.
const BoundingBoxf3& bounding_box() const;
void invalidate_bounding_box() { m_bounding_box_valid = false; m_raw_bounding_box_valid = false; m_raw_mesh_bounding_box_valid = false; }
// Returns the bounding box of the transformed instances. This bounding box is approximate and not snug, it is being cached.
const BoundingBoxf3& bounding_box_approx() const;
// Returns an exact bounding box of the transformed instances. The result it is being cached.
const BoundingBoxf3& bounding_box_exact() const;
// Return minimum / maximum of a printable object transformed into the world coordinate system.
// All instances share the same min / max Z.
double min_z() const;
double max_z() const;
void invalidate_bounding_box() {
m_bounding_box_approx_valid = false;
m_bounding_box_exact_valid = false;
m_min_max_z_valid = false;
m_raw_bounding_box_valid = false;
m_raw_mesh_bounding_box_valid = false;
}
// A mesh containing all transformed instances of this object.
TriangleMesh mesh() const;
@ -477,8 +488,6 @@ public:
// Rotation and mirroring is being baked in. In case the instance scaling was non-uniform, it is baked in as well.
void bake_xy_rotation_into_meshes(size_t instance_idx);
double get_min_z() const;
double get_max_z() const;
double get_instance_min_z(size_t instance_idx) const;
double get_instance_max_z(size_t instance_idx) const;
@ -500,14 +509,13 @@ public:
private:
friend class Model;
// This constructor assigns new ID to this ModelObject and its config.
explicit ModelObject(Model* model) : m_model(model), printable(true), origin_translation(Vec3d::Zero()),
m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
explicit ModelObject(Model* model) : m_model(model), origin_translation(Vec3d::Zero())
{
assert(this->id().valid());
assert(this->config.id().valid());
assert(this->layer_height_profile.id().valid());
}
explicit ModelObject(int) : ObjectBase(-1), config(-1), layer_height_profile(-1), m_model(nullptr), printable(true), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
explicit ModelObject(int) : ObjectBase(-1), config(-1), layer_height_profile(-1), origin_translation(Vec3d::Zero())
{
assert(this->id().invalid());
assert(this->config.id().invalid());
@ -585,15 +593,18 @@ private:
OBJECTBASE_DERIVED_COPY_MOVE_CLONE(ModelObject)
// Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized.
Model *m_model = nullptr;
Model *m_model { nullptr };
// Bounding box, cached.
mutable BoundingBoxf3 m_bounding_box;
mutable bool m_bounding_box_valid;
mutable BoundingBoxf3 m_bounding_box_approx;
mutable bool m_bounding_box_approx_valid { false };
mutable BoundingBoxf3 m_bounding_box_exact;
mutable bool m_bounding_box_exact_valid { false };
mutable bool m_min_max_z_valid { false };
mutable BoundingBoxf3 m_raw_bounding_box;
mutable bool m_raw_bounding_box_valid;
mutable bool m_raw_bounding_box_valid { false };
mutable BoundingBoxf3 m_raw_mesh_bounding_box;
mutable bool m_raw_mesh_bounding_box_valid;
mutable bool m_raw_mesh_bounding_box_valid { false };
// Called by Print::apply() to set the model pointer after making a copy.
friend class Print;
@ -605,8 +616,7 @@ private:
friend class UndoRedo::StackImpl;
// Used for deserialization -> Don't allocate any IDs for the ModelObject or its config.
ModelObject() :
ObjectBase(-1), config(-1), layer_height_profile(-1),
m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {
ObjectBase(-1), config(-1), layer_height_profile(-1) {
assert(this->id().invalid());
assert(this->config.id().invalid());
assert(this->layer_height_profile.id().invalid());
@ -617,12 +627,17 @@ private:
Internal::StaticSerializationWrapper<LayerHeightProfile> layer_heigth_profile_wrapper(layer_height_profile);
ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper,
sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation,
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid,
m_bounding_box_approx, m_bounding_box_approx_valid,
m_bounding_box_exact, m_bounding_box_exact_valid, m_min_max_z_valid,
m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid,
cut_connectors, cut_id);
}
// Called by Print::validate() from the UI thread.
unsigned int update_instances_print_volume_state(const BuildVolume &build_volume);
// Called by min_z(), max_z()
void update_min_max_z();
};
enum class EnforcerBlockerType : int8_t {
@ -1106,7 +1121,7 @@ public:
// flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
ModelInstanceEPrintVolumeState print_volume_state;
// Whether or not this instance is printable
bool printable;
bool printable { true };
ModelObject* get_object() const { return this->object; }
@ -1156,9 +1171,7 @@ public:
// To be called on an external mesh
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
// Calculate a bounding box of a transformed mesh. To be called on an external mesh.
BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh& mesh, bool dont_translate = false) const;
// Transform an external bounding box.
// Transform an external bounding box, thus the resulting bounding box is no more snug.
BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const;
// Transform an external vector.
Vec3d transform_vector(const Vec3d& v, bool dont_translate = false) const;
@ -1201,7 +1214,7 @@ private:
ModelObject* object;
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject* object) : print_volume_state(ModelInstancePVS_Inside), printable(true), object(object) { assert(this->id().valid()); }
explicit ModelInstance(ModelObject* object) : print_volume_state(ModelInstancePVS_Inside), object(object) { assert(this->id().valid()); }
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
m_transformation(other.m_transformation), print_volume_state(ModelInstancePVS_Inside), printable(other.printable), object(object) { assert(this->id().valid() && this->id() != other.id()); }
@ -1316,8 +1329,12 @@ public:
void delete_material(t_model_material_id material_id);
void clear_materials();
bool add_default_instances();
// Returns approximate axis aligned bounding box of this model
BoundingBoxf3 bounding_box() const;
// Returns approximate axis aligned bounding box of this model.
BoundingBoxf3 bounding_box_approx() const;
// Returns exact axis aligned bounding box of this model.
BoundingBoxf3 bounding_box_exact() const;
// Return maximum height of all printable objects.
double max_z() const;
// Set the print_volume_state of PrintObject::instances,
// return total number of printable objects.
unsigned int update_print_volume_state(const BuildVolume &build_volume);

View File

@ -77,7 +77,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor
Vec3d bbox_center = bbox.center();
// We may need to rotate the bbox / bbox_center from the original instance to the current instance.
double z_diff = Geometry::rotation_diff_z(model_object->instances.front()->get_matrix(), instances.front().model_instance->get_matrix());
if (std::abs(z_diff) > EPSILON) {
if (std::abs(z_diff) > EPSILON) {
auto z_rot = Eigen::AngleAxisd(z_diff, Vec3d::UnitZ());
bbox = bbox.transformed(Transform3d(z_rot));
bbox_center = (z_rot * bbox_center).eval();
@ -87,6 +87,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor
m_center_offset = Point::new_scale(bbox_center.x(), bbox_center.y());
// Size of the transformed mesh. This bounding may not be snug in XY plane, but it is snug in Z.
m_size = (bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
m_size.z() = model_object->max_z();
this->set_instances(std::move(instances));
}
@ -1736,7 +1737,7 @@ void PrintObject::update_slicing_parameters()
{
if (!m_slicing_params.valid)
m_slicing_params = SlicingParameters::create_from_config(
this->print()->config(), m_config, this->model_object()->bounding_box().max.z(), this->object_extruders());
this->print()->config(), m_config, this->model_object()->max_z(), this->object_extruders());
}
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)

View File

@ -2246,7 +2246,7 @@ void GCodeViewer::load_shells(const Print& print)
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF) {
// adds wipe tower's volume
const double max_z = print.objects()[0]->model_object()->get_model()->bounding_box().max(2);
const double max_z = print.objects()[0]->model_object()->get_model()->max_z();
const PrintConfig& config = print.config();
const size_t extruders_count = config.nozzle_diameter.size();
if (extruders_count > 1 && config.wipe_tower && !config.complete_objects) {

View File

@ -140,7 +140,7 @@ void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id)
// Maximum height of an object changes when the object gets rotated or scaled.
// Changing maximum height of an object will invalidate the layer heigth editing profile.
// m_model_object->bounding_box() is cached, therefore it is cheap even if this method is called frequently.
const float new_max_z = (model_object_new == nullptr) ? 0.0f : static_cast<float>(model_object_new->bounding_box().max.z());
const float new_max_z = (model_object_new == nullptr) ? 0.0f : static_cast<float>(model_object_new->max_z());
if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z ||
(model_object_new != nullptr && m_model_object->id() != model_object_new->id())) {
m_layer_height_profile.clear();
@ -1977,7 +1977,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
if (extruders_count > 1 && wt && !co) {
// Height of a print (Show at least a slab)
const double height = std::max(m_model->bounding_box().max.z(), 10.0);
const double height = std::max(m_model->max_z(), 10.0);
const float x = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_x"))->value;
const float y = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_y"))->value;

View File

@ -3520,7 +3520,7 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const
ModelObject* old_model_object = model.objects[object_idx];
ModelVolume* old_volume = old_model_object->volumes[volume_idx];
bool sinking = old_model_object->bounding_box().min.z() < SINKING_Z_THRESHOLD;
bool sinking = old_model_object->min_z() < SINKING_Z_THRESHOLD;
ModelObject* new_model_object = new_model.objects.front();
old_model_object->add_volume(*new_model_object->volumes.front());
@ -3835,7 +3835,7 @@ void Plater::priv::reload_from_disk()
ModelObject* old_model_object = model.objects[obj_idx];
ModelVolume* old_volume = old_model_object->volumes[vol_idx];
bool sinking = old_model_object->bounding_box().min.z() < SINKING_Z_THRESHOLD;
bool sinking = old_model_object->min_z() < SINKING_Z_THRESHOLD;
bool has_source = !old_volume->source.input_file.empty() && boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string());
bool has_name = !old_volume->name.empty() && boost::algorithm::iequals(old_volume->name, fs::path(path).filename().string());
@ -4816,7 +4816,7 @@ bool Plater::priv::layers_height_allowed() const
return false;
int obj_idx = get_selected_object_idx();
return 0 <= obj_idx && obj_idx < (int)model.objects.size() && model.objects[obj_idx]->bounding_box().max.z() > SINKING_Z_THRESHOLD &&
return 0 <= obj_idx && obj_idx < (int)model.objects.size() && model.objects[obj_idx]->max_z() > SINKING_Z_THRESHOLD &&
config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed();
}
@ -7435,7 +7435,7 @@ void Plater::changed_objects(const std::vector<size_t>& object_idxs)
for (size_t obj_idx : object_idxs) {
if (obj_idx < p->model.objects.size()) {
if (p->model.objects[obj_idx]->bounding_box().min.z() >= SINKING_Z_THRESHOLD)
if (p->model.objects[obj_idx]->min_z() >= SINKING_Z_THRESHOLD)
// re - align to Z = 0
p->model.objects[obj_idx]->ensure_on_bed();
}