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:
parent
cbe3c4f238
commit
3349644964
@ -382,7 +382,7 @@ int CLI::run(int argc, char **argv)
|
|||||||
} else if (opt_key == "align_xy") {
|
} else if (opt_key == "align_xy") {
|
||||||
const Vec2d &p = m_config.option<ConfigOptionPoint>("align_xy")->value;
|
const Vec2d &p = m_config.option<ConfigOptionPoint>("align_xy")->value;
|
||||||
for (auto &model : m_models) {
|
for (auto &model : m_models) {
|
||||||
BoundingBoxf3 bb = model.bounding_box();
|
BoundingBoxf3 bb = model.bounding_box_exact();
|
||||||
// this affects volumes:
|
// this affects volumes:
|
||||||
model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z());
|
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") {
|
} else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
|
||||||
std::vector<Model> new_models;
|
std::vector<Model> new_models;
|
||||||
for (auto &model : m_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();
|
size_t num_objects = model.objects.size();
|
||||||
for (size_t i = 0; i < num_objects; ++ i) {
|
for (size_t i = 0; i < num_objects; ++ i) {
|
||||||
|
|
||||||
|
@ -323,14 +323,30 @@ bool Model::add_default_instances()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this returns the bounding box of the *transformed* instances
|
// this returns the bounding box of the *transformed* instances
|
||||||
BoundingBoxf3 Model::bounding_box() const
|
BoundingBoxf3 Model::bounding_box_approx() const
|
||||||
{
|
{
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb;
|
||||||
for (ModelObject *o : this->objects)
|
for (ModelObject *o : this->objects)
|
||||||
bb.merge(o->bounding_box());
|
bb.merge(o->bounding_box_approx());
|
||||||
return bb;
|
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 Model::update_print_volume_state(const BuildVolume &build_volume)
|
||||||
{
|
{
|
||||||
unsigned int num_printable = 0;
|
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();
|
ModelObject* object = this->objects.front();
|
||||||
object->clear_instances();
|
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 x_copy = 1; x_copy <= x; ++x_copy) {
|
||||||
for (size_t y_copy = 1; y_copy <= y; ++y_copy) {
|
for (size_t y_copy = 1; y_copy <= y; ++y_copy) {
|
||||||
@ -548,13 +564,13 @@ void Model::adjust_min_z()
|
|||||||
if (objects.empty())
|
if (objects.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (bounding_box().min(2) < 0.0)
|
if (this->bounding_box_exact().min.z() < 0.0)
|
||||||
{
|
{
|
||||||
for (ModelObject* obj : objects)
|
for (ModelObject* obj : objects)
|
||||||
{
|
{
|
||||||
if (obj != nullptr)
|
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)
|
if (obj_min_z < 0.0)
|
||||||
obj->translate_instances(Vec3d(0.0, 0.0, -obj_min_z));
|
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->printable = rhs.printable;
|
||||||
this->origin_translation = rhs.origin_translation;
|
this->origin_translation = rhs.origin_translation;
|
||||||
this->cut_id.copy(rhs.cut_id);
|
this->cut_id.copy(rhs.cut_id);
|
||||||
m_bounding_box = rhs.m_bounding_box;
|
m_bounding_box_approx = rhs.m_bounding_box_approx;
|
||||||
m_bounding_box_valid = rhs.m_bounding_box_valid;
|
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 = rhs.m_raw_bounding_box;
|
||||||
m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid;
|
m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid;
|
||||||
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
|
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->layer_height_profile = std::move(rhs.layer_height_profile);
|
||||||
this->printable = std::move(rhs.printable);
|
this->printable = std::move(rhs.printable);
|
||||||
this->origin_translation = std::move(rhs.origin_translation);
|
this->origin_translation = std::move(rhs.origin_translation);
|
||||||
m_bounding_box = std::move(rhs.m_bounding_box);
|
m_bounding_box_approx = std::move(rhs.m_bounding_box_approx);
|
||||||
m_bounding_box_valid = std::move(rhs.m_bounding_box_valid);
|
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 = rhs.m_raw_bounding_box;
|
||||||
m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid;
|
m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid;
|
||||||
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
|
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.
|
// Returns the bounding box of the transformed instances.
|
||||||
// This bounding box is approximate and not snug.
|
// 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) {
|
if (! m_bounding_box_approx_valid) {
|
||||||
m_bounding_box_valid = true;
|
m_bounding_box_approx_valid = true;
|
||||||
BoundingBoxf3 raw_bbox = this->raw_mesh_bounding_box();
|
BoundingBoxf3 raw_bbox = this->raw_mesh_bounding_box();
|
||||||
m_bounding_box.reset();
|
m_bounding_box_approx.reset();
|
||||||
for (const ModelInstance *i : this->instances)
|
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.
|
// 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 (allow_negative_z) {
|
||||||
if (parts_count() == 1) {
|
if (parts_count() == 1) {
|
||||||
const double min_z = get_min_z();
|
const double min_z = this->min_z();
|
||||||
const double max_z = get_max_z();
|
const double max_z = this->max_z();
|
||||||
if (min_z >= SINKING_Z_THRESHOLD || max_z < 0.0)
|
if (min_z >= SINKING_Z_THRESHOLD || max_z < 0.0)
|
||||||
z_offset = -min_z;
|
z_offset = -min_z;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const double max_z = get_max_z();
|
const double max_z = this->max_z();
|
||||||
if (max_z < SINKING_MIN_Z_THRESHOLD)
|
if (max_z < SINKING_MIN_Z_THRESHOLD)
|
||||||
z_offset = SINKING_MIN_Z_THRESHOLD - max_z;
|
z_offset = SINKING_MIN_Z_THRESHOLD - max_z;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
z_offset = -get_min_z();
|
z_offset = -this->min_z();
|
||||||
|
|
||||||
if (z_offset != 0.0)
|
if (z_offset != 0.0)
|
||||||
translate_instances(z_offset * Vec3d::UnitZ());
|
translate_instances(z_offset * Vec3d::UnitZ());
|
||||||
@ -1070,8 +1149,10 @@ void ModelObject::translate(double x, double y, double z)
|
|||||||
v->translate(x, y, z);
|
v->translate(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_bounding_box_valid)
|
if (m_bounding_box_approx_valid)
|
||||||
m_bounding_box.translate(x, y, z);
|
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)
|
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();
|
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 ModelObject::get_instance_min_z(size_t instance_idx) const
|
||||||
{
|
{
|
||||||
double min_z = DBL_MAX;
|
double min_z = DBL_MAX;
|
||||||
@ -2268,7 +2323,7 @@ void ModelVolume::scale(const Vec3d& scaling_factors)
|
|||||||
|
|
||||||
void ModelObject::scale_to_fit(const Vec3d &size)
|
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(
|
double factor = std::min(
|
||||||
size.x() / orig_size.x(),
|
size.x() / orig_size.x(),
|
||||||
std::min(
|
std::min(
|
||||||
@ -2373,37 +2428,6 @@ void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) cons
|
|||||||
#endif // ENABLE_WORLD_COORDINATE
|
#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
|
BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const
|
||||||
{
|
{
|
||||||
#if ENABLE_WORLD_COORDINATE
|
#if ENABLE_WORLD_COORDINATE
|
||||||
|
@ -168,7 +168,7 @@ private:
|
|||||||
friend class cereal::access;
|
friend class cereal::access;
|
||||||
friend class UndoRedo::StackImpl;
|
friend class UndoRedo::StackImpl;
|
||||||
// Create an object for deserialization, don't allocate IDs for ModelMaterial and its config.
|
// 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) {
|
template<class Archive> void serialize(Archive &ar) {
|
||||||
assert(this->id().invalid()); assert(this->config.id().invalid());
|
assert(this->id().invalid()); assert(this->config.id().invalid());
|
||||||
Internal::StaticSerializationWrapper<ModelConfigObject> config_wrapper(config);
|
Internal::StaticSerializationWrapper<ModelConfigObject> config_wrapper(config);
|
||||||
@ -343,7 +343,7 @@ public:
|
|||||||
// The pairs of <z, layer_height> are packed into a 1D array.
|
// The pairs of <z, layer_height> are packed into a 1D array.
|
||||||
LayerHeightProfile layer_height_profile;
|
LayerHeightProfile layer_height_profile;
|
||||||
// Whether or not this object is printable
|
// 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
|
// This vector holds position of selected support points for SLA. The data are
|
||||||
// saved in mesh coordinates to allow using them for several instances.
|
// saved in mesh coordinates to allow using them for several instances.
|
||||||
@ -397,11 +397,22 @@ public:
|
|||||||
void delete_last_instance();
|
void delete_last_instance();
|
||||||
void clear_instances();
|
void clear_instances();
|
||||||
|
|
||||||
// Returns the bounding box of the transformed instances.
|
// Returns the bounding box of the transformed instances. This bounding box is approximate and not snug, it is being cached.
|
||||||
// This bounding box is approximate and not snug.
|
const BoundingBoxf3& bounding_box_approx() const;
|
||||||
// This bounding box is being cached.
|
// Returns an exact bounding box of the transformed instances. The result it is being cached.
|
||||||
const BoundingBoxf3& bounding_box() const;
|
const BoundingBoxf3& bounding_box_exact() const;
|
||||||
void invalidate_bounding_box() { m_bounding_box_valid = false; m_raw_bounding_box_valid = false; m_raw_mesh_bounding_box_valid = false; }
|
// 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.
|
// A mesh containing all transformed instances of this object.
|
||||||
TriangleMesh mesh() const;
|
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.
|
// 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);
|
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_min_z(size_t instance_idx) const;
|
||||||
double get_instance_max_z(size_t instance_idx) const;
|
double get_instance_max_z(size_t instance_idx) const;
|
||||||
|
|
||||||
@ -500,14 +509,13 @@ public:
|
|||||||
private:
|
private:
|
||||||
friend class Model;
|
friend class Model;
|
||||||
// This constructor assigns new ID to this ModelObject and its config.
|
// This constructor assigns new ID to this ModelObject and its config.
|
||||||
explicit ModelObject(Model* model) : m_model(model), printable(true), origin_translation(Vec3d::Zero()),
|
explicit ModelObject(Model* model) : m_model(model), origin_translation(Vec3d::Zero())
|
||||||
m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
|
|
||||||
{
|
{
|
||||||
assert(this->id().valid());
|
assert(this->id().valid());
|
||||||
assert(this->config.id().valid());
|
assert(this->config.id().valid());
|
||||||
assert(this->layer_height_profile.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->id().invalid());
|
||||||
assert(this->config.id().invalid());
|
assert(this->config.id().invalid());
|
||||||
@ -585,15 +593,18 @@ private:
|
|||||||
OBJECTBASE_DERIVED_COPY_MOVE_CLONE(ModelObject)
|
OBJECTBASE_DERIVED_COPY_MOVE_CLONE(ModelObject)
|
||||||
|
|
||||||
// Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized.
|
// 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.
|
// Bounding box, cached.
|
||||||
mutable BoundingBoxf3 m_bounding_box;
|
mutable BoundingBoxf3 m_bounding_box_approx;
|
||||||
mutable bool m_bounding_box_valid;
|
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 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 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.
|
// Called by Print::apply() to set the model pointer after making a copy.
|
||||||
friend class Print;
|
friend class Print;
|
||||||
@ -605,8 +616,7 @@ private:
|
|||||||
friend class UndoRedo::StackImpl;
|
friend class UndoRedo::StackImpl;
|
||||||
// Used for deserialization -> Don't allocate any IDs for the ModelObject or its config.
|
// Used for deserialization -> Don't allocate any IDs for the ModelObject or its config.
|
||||||
ModelObject() :
|
ModelObject() :
|
||||||
ObjectBase(-1), config(-1), layer_height_profile(-1),
|
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) {
|
|
||||||
assert(this->id().invalid());
|
assert(this->id().invalid());
|
||||||
assert(this->config.id().invalid());
|
assert(this->config.id().invalid());
|
||||||
assert(this->layer_height_profile.id().invalid());
|
assert(this->layer_height_profile.id().invalid());
|
||||||
@ -617,12 +627,17 @@ private:
|
|||||||
Internal::StaticSerializationWrapper<LayerHeightProfile> layer_heigth_profile_wrapper(layer_height_profile);
|
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,
|
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,
|
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);
|
cut_connectors, cut_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by Print::validate() from the UI thread.
|
// Called by Print::validate() from the UI thread.
|
||||||
unsigned int update_instances_print_volume_state(const BuildVolume &build_volume);
|
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 {
|
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())
|
// 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;
|
ModelInstanceEPrintVolumeState print_volume_state;
|
||||||
// Whether or not this instance is printable
|
// Whether or not this instance is printable
|
||||||
bool printable;
|
bool printable { true };
|
||||||
|
|
||||||
ModelObject* get_object() const { return this->object; }
|
ModelObject* get_object() const { return this->object; }
|
||||||
|
|
||||||
@ -1156,9 +1171,7 @@ public:
|
|||||||
|
|
||||||
// To be called on an external mesh
|
// To be called on an external mesh
|
||||||
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
|
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.
|
// Transform an external bounding box, thus the resulting bounding box is no more snug.
|
||||||
BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh& mesh, bool dont_translate = false) const;
|
|
||||||
// Transform an external bounding box.
|
|
||||||
BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const;
|
BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const;
|
||||||
// Transform an external vector.
|
// Transform an external vector.
|
||||||
Vec3d transform_vector(const Vec3d& v, bool dont_translate = false) const;
|
Vec3d transform_vector(const Vec3d& v, bool dont_translate = false) const;
|
||||||
@ -1201,7 +1214,7 @@ private:
|
|||||||
ModelObject* object;
|
ModelObject* object;
|
||||||
|
|
||||||
// Constructor, which assigns a new unique ID.
|
// 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.
|
// Constructor, which assigns a new unique ID.
|
||||||
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
|
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()); }
|
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 delete_material(t_model_material_id material_id);
|
||||||
void clear_materials();
|
void clear_materials();
|
||||||
bool add_default_instances();
|
bool add_default_instances();
|
||||||
// Returns approximate axis aligned bounding box of this model
|
// Returns approximate axis aligned bounding box of this model.
|
||||||
BoundingBoxf3 bounding_box() const;
|
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,
|
// Set the print_volume_state of PrintObject::instances,
|
||||||
// return total number of printable objects.
|
// return total number of printable objects.
|
||||||
unsigned int update_print_volume_state(const BuildVolume &build_volume);
|
unsigned int update_print_volume_state(const BuildVolume &build_volume);
|
||||||
|
@ -77,7 +77,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor
|
|||||||
Vec3d bbox_center = bbox.center();
|
Vec3d bbox_center = bbox.center();
|
||||||
// We may need to rotate the bbox / bbox_center from the original instance to the current instance.
|
// 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());
|
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());
|
auto z_rot = Eigen::AngleAxisd(z_diff, Vec3d::UnitZ());
|
||||||
bbox = bbox.transformed(Transform3d(z_rot));
|
bbox = bbox.transformed(Transform3d(z_rot));
|
||||||
bbox_center = (z_rot * bbox_center).eval();
|
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());
|
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.
|
// 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 = (bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
|
||||||
|
m_size.z() = model_object->max_z();
|
||||||
|
|
||||||
this->set_instances(std::move(instances));
|
this->set_instances(std::move(instances));
|
||||||
}
|
}
|
||||||
@ -1736,7 +1737,7 @@ void PrintObject::update_slicing_parameters()
|
|||||||
{
|
{
|
||||||
if (!m_slicing_params.valid)
|
if (!m_slicing_params.valid)
|
||||||
m_slicing_params = SlicingParameters::create_from_config(
|
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)
|
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
|
||||||
|
@ -2246,7 +2246,7 @@ void GCodeViewer::load_shells(const Print& print)
|
|||||||
|
|
||||||
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF) {
|
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF) {
|
||||||
// adds wipe tower's volume
|
// 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 PrintConfig& config = print.config();
|
||||||
const size_t extruders_count = config.nozzle_diameter.size();
|
const size_t extruders_count = config.nozzle_diameter.size();
|
||||||
if (extruders_count > 1 && config.wipe_tower && !config.complete_objects) {
|
if (extruders_count > 1 && config.wipe_tower && !config.complete_objects) {
|
||||||
|
@ -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.
|
// 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.
|
// 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.
|
// 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 ||
|
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())) {
|
(model_object_new != nullptr && m_model_object->id() != model_object_new->id())) {
|
||||||
m_layer_height_profile.clear();
|
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) {
|
if (extruders_count > 1 && wt && !co) {
|
||||||
// Height of a print (Show at least a slab)
|
// 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 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;
|
const float y = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_y"))->value;
|
||||||
|
@ -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];
|
ModelObject* old_model_object = model.objects[object_idx];
|
||||||
ModelVolume* old_volume = old_model_object->volumes[volume_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();
|
ModelObject* new_model_object = new_model.objects.front();
|
||||||
old_model_object->add_volume(*new_model_object->volumes.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];
|
ModelObject* old_model_object = model.objects[obj_idx];
|
||||||
ModelVolume* old_volume = old_model_object->volumes[vol_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_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());
|
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;
|
return false;
|
||||||
|
|
||||||
int obj_idx = get_selected_object_idx();
|
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();
|
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) {
|
for (size_t obj_idx : object_idxs) {
|
||||||
if (obj_idx < p->model.objects.size()) {
|
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
|
// re - align to Z = 0
|
||||||
p->model.objects[obj_idx]->ensure_on_bed();
|
p->model.objects[obj_idx]->ensure_on_bed();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user