diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 0ac5c0e22..15eedaa2d 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -712,18 +712,32 @@ void ModelObject::center_around_origin() if (!this->instances.empty()) { for (ModelInstance *i : this->instances) { -#if ENABLE_MIRROR i->set_offset(i->get_offset() - shift); -#else - // apply rotation and scaling to vector as well before translating instance, - // in order to leave final position unaltered - i->set_offset(i->get_offset() + i->transform_vector(-shift, true)); -#endif // ENABLE_MIRROR } this->invalidate_bounding_box(); } } +void ModelObject::ensure_on_bed() +{ + translate_instances(Vec3d(0.0, 0.0, -get_min_z())); +} + +void ModelObject::translate_instances(const Vec3d& vector) +{ + for (size_t i = 0; i < instances.size(); ++i) + { + translate_instance(i, vector); + } +} + +void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector) +{ + ModelInstance* i = instances[instance_idx]; + i->set_offset(i->get_offset() + vector); + invalidate_bounding_box(); +} + void ModelObject::translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelVolume *v : this->volumes) @@ -895,6 +909,42 @@ void ModelObject::repair() v->mesh.repair(); } +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_instance_min_z(size_t instance_idx) const +{ + double min_z = DBL_MAX; + + ModelInstance* inst = instances[instance_idx]; + Vec3d local_unit_z = (inst->world_matrix(true).inverse() * Vec3d::UnitZ()).normalized(); + + for (ModelVolume *v : volumes) + { + for (uint32_t f = 0; f < v->mesh.stl.stats.number_of_facets; ++f) + { + const stl_facet* facet = v->mesh.stl.facet_start + f; + min_z = std::min(min_z, local_unit_z.dot(facet->vertex[0].cast())); + min_z = std::min(min_z, local_unit_z.dot(facet->vertex[1].cast())); + min_z = std::min(min_z, local_unit_z.dot(facet->vertex[2].cast())); + } + } + + return min_z + inst->get_offset(Z); +} + unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume) { unsigned int num_printable = 0; @@ -1136,7 +1186,11 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes { // Rotate around mesh origin. TriangleMesh copy(*mesh); +#if ENABLE_MIRROR + copy.transform(world_matrix(true, false, true, true).cast()); +#else copy.transform(world_matrix(true, false, true).cast()); +#endif // ENABLE_MIRROR BoundingBoxf3 bbox = copy.bounding_box(); if (!empty(bbox)) { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index e6ec9b0ae..89b068c40 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -153,6 +153,9 @@ public: // A snug bounding box around the transformed non-modifier object volumes. BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const; void center_around_origin(); + void ensure_on_bed(); + void translate_instances(const Vec3d& vector); + void translate_instance(size_t instance_idx, const Vec3d& vector); void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); } void translate(coordf_t x, coordf_t y, coordf_t z); void scale(const Vec3d &versor); @@ -167,6 +170,9 @@ public: void split(ModelObjectPtrs* new_objects); void repair(); + double get_min_z() const; + double get_instance_min_z(size_t instance_idx) const; + // Called by Print::validate() from the UI thread. unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fad99f6fe..ca593e8a9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1478,6 +1478,98 @@ void GLCanvas3D::Selection::mirror(Axis axis) } #endif // ENABLE_MIRROR +void GLCanvas3D::Selection::translate(unsigned int object_idx, const Vec3d& displacement) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() == object_idx) + v->set_offset(v->get_offset() + displacement); + } + + std::set done; // prevent processing volumes twice + done.insert(m_list.begin(), m_list.end()); + + for (unsigned int i : m_list) + { + if (done.size() == m_volumes->size()) + break; + + const GLVolume* volume = (*m_volumes)[i]; + int object_idx = volume->object_idx(); + if (object_idx >= 1000) + continue; + + // Process unselected volumes of the object. + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (done.size() == m_volumes->size()) + break; + + if (done.find(j) != done.end()) + continue; + + GLVolume* v = (*m_volumes)[j]; + if (v->object_idx() != object_idx) + continue; + + v->set_offset(v->get_offset() + displacement); + done.insert(j); + } + } + + m_bounding_box_dirty = true; +} + +void GLCanvas3D::Selection::translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + v->set_offset(v->get_offset() + displacement); + } + + std::set done; // prevent processing volumes twice + done.insert(m_list.begin(), m_list.end()); + + for (unsigned int i : m_list) + { + if (done.size() == m_volumes->size()) + break; + + const GLVolume* volume = (*m_volumes)[i]; + int object_idx = volume->object_idx(); + if (object_idx >= 1000) + continue; + + // Process unselected volumes of the object. + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (done.size() == m_volumes->size()) + break; + + if (done.find(j) != done.end()) + continue; + + GLVolume* v = (*m_volumes)[j]; + if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx)) + continue; + + v->set_offset(v->get_offset() + displacement); + done.insert(j); + } + } + + m_bounding_box_dirty = true; +} + void GLCanvas3D::Selection::render(bool show_indirect_selection) const { if (is_empty()) @@ -6666,6 +6758,14 @@ void GLCanvas3D::_on_move() wipe_tower_origin = v->get_offset(); } + for (const std::pair& i : done) + { + ModelObject* m = m_model->objects[i.first]; + Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); + m_selection.translate(i.first, i.second, shift); + m->translate_instance(i.second, shift); + } + if (object_moved) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED)); @@ -6700,10 +6800,19 @@ void GLCanvas3D::_on_rotate() if (model_object != nullptr) { model_object->instances[instance_idx]->set_rotation(v->get_rotation()); + model_object->instances[instance_idx]->set_offset(v->get_offset()); model_object->invalidate_bounding_box(); } } + for (const std::pair& i : done) + { + ModelObject* m = m_model->objects[i.first]; + Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); + m_selection.translate(i.first, i.second, shift); + m->translate_instance(i.second, shift); + } + post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } @@ -6734,10 +6843,19 @@ void GLCanvas3D::_on_scale() if (model_object != nullptr) { model_object->instances[instance_idx]->set_scaling_factor(v->get_scaling_factor()); + model_object->instances[instance_idx]->set_offset(v->get_offset()); model_object->invalidate_bounding_box(); } } + for (const std::pair& i : done) + { + ModelObject* m = m_model->objects[i.first]; + Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); + m_selection.translate(i.first, i.second, shift); + m->translate_instance(i.second, shift); + } + post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index b47a11a1d..4d2185825 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -508,6 +508,9 @@ public: void mirror(Axis axis); #endif // ENABLE_MIRROR + void translate(unsigned int object_idx, const Vec3d& displacement); + void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement); + void render(bool show_indirect_selection) const; private: diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 6c9077bad..1bd934213 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -706,6 +706,7 @@ void ObjectList::load_part( ModelObject* model_object, if (model_object->origin_translation != Vec3d::Zero()) { object->center_around_origin(); + object->ensure_on_bed(); delta = model_object->origin_translation - object->origin_translation; } for (auto volume : object->volumes) { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 59ebe10b2..a26cde07c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1151,6 +1151,7 @@ std::vector Plater::priv::load_files(const std::vector &input_ if (type_3mf) { for (ModelObject* model_object : model.objects) { model_object->center_around_origin(); + model_object->ensure_on_bed(); } } @@ -1235,6 +1236,8 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode } } + object->ensure_on_bed(); + // print.auto_assign_extruders(object); // print.add_model_object(object); } @@ -1628,6 +1631,7 @@ void Plater::priv::split_object() { m->name = current_model_object->name + "_" + std::to_string(counter++); m->center_around_origin(); + m->ensure_on_bed(); } remove(obj_idx); @@ -2552,6 +2556,7 @@ void Plater::changed_object_settings(int obj_idx) // recenter and re - align to Z = 0 auto model_object = p->model.objects[obj_idx]; model_object->center_around_origin(); + model_object->ensure_on_bed(); } // update print