diff --git a/src/libslic3r/MultipleBeds.cpp b/src/libslic3r/MultipleBeds.cpp index a512c9c000..c84087ef16 100644 --- a/src/libslic3r/MultipleBeds.cpp +++ b/src/libslic3r/MultipleBeds.cpp @@ -96,14 +96,20 @@ Vec3d MultipleBeds::get_bed_translation(int id) const return Vec3d::Zero(); int x = 0; int y = 0; - if (m_layout_linear) + if (m_legacy_layout) x = id; else { BedsGrid::GridCoords coords{BedsGrid::index2grid_coords(id)}; x = coords.x(); y = coords.y(); } - return Vec3d(x * m_build_volume_bb_incl_model.size().x() * (1. + bed_gap_x), y * m_build_volume_bb_incl_model.size().y() * (1. + bed_gap_y), 0.); + + // As for the m_legacy_layout switch, see comments at definition of bed_gap_relative. + Vec2d gap = bed_gap(); + double gap_x = (m_legacy_layout ? m_build_volume_bb.size().x() * (2./10.) : gap.x()); + return Vec3d(x * (m_build_volume_bb.size().x() + gap_x), + y * (m_build_volume_bb.size().y() + gap.y()), // When using legacy layout, y is zero anyway. + 0.); } void MultipleBeds::clear_inst_map() @@ -207,13 +213,12 @@ void MultipleBeds::update_shown_beds(Model& model, const BuildVolume& build_volu set_active_bed(m_number_of_beds != original_number_of_beds ? 0 : stash_active); } -// Beware! This function is also needed for proper update of bed when normal grid project is loaded! -bool MultipleBeds::update_after_load_or_arrange(Model& model, const BuildVolume& build_volume, std::function update_fn) +bool MultipleBeds::rearrange_after_load(Model& model, const BuildVolume& build_volume, std::function update_fn) { int original_number_of_beds = m_number_of_beds; int stash_active = get_active_bed(); Slic3r::ScopeGuard guard([&]() { - m_layout_linear = false; + m_legacy_layout = false; m_number_of_beds = get_max_beds(); model.update_print_volume_state(build_volume); int max_bed = 0; @@ -226,8 +231,7 @@ bool MultipleBeds::update_after_load_or_arrange(Model& model, const BuildVolume& update_fn(); }); - m_layout_linear = true; - std::swap(m_build_volume_bb, m_build_volume_bb_incl_model); + m_legacy_layout = true; int abs_max = get_max_beds(); while (true) { // This is to ensure that even objects on linear bed with higher than @@ -242,10 +246,11 @@ bool MultipleBeds::update_after_load_or_arrange(Model& model, const BuildVolume& abs_max += get_max_beds(); } m_number_of_beds = 1; - std::swap(m_build_volume_bb, m_build_volume_bb_incl_model); + m_legacy_layout = false; int max_bed = 0; + // Check that no instances are out of any bed. std::map> id_to_ptr_and_bed; for (ModelObject* mo : model.objects) { for (ModelInstance* mi : mo->instances) { @@ -267,27 +272,34 @@ bool MultipleBeds::update_after_load_or_arrange(Model& model, const BuildVolume& return false; // All instances are on some bed, at least two are used. + // Move everything as if its bed was in the first position. for (auto& [oid, mi_and_bed] : id_to_ptr_and_bed) { auto& [mi, bed_idx] = mi_and_bed; - std::swap(m_build_volume_bb, m_build_volume_bb_incl_model); + m_legacy_layout = true; mi->set_offset(mi->get_offset() - get_bed_translation(bed_idx)); - std::swap(m_build_volume_bb, m_build_volume_bb_incl_model); - } - - m_layout_linear = false; - for (auto& [oid, mi_and_bed] : id_to_ptr_and_bed) { - auto& [mi, bed_idx] = mi_and_bed; + m_legacy_layout = false; mi->set_offset(mi->get_offset() + get_bed_translation(bed_idx)); } return true; } + +Vec2d MultipleBeds::bed_gap() const +{ + // This is the only function that defines how far apart should the beds be. Used in scene and arrange. + // Note that the spacing is momentarily switched to legacy value of 2/10 when a project is loaded. + // Slicers before 2.9.0 used this value for arrange, and there are existing projects with objects spaced that way (controlled by the m_legacy_layout flag). + + // TOUCHING THIS WILL BREAK LOADING OF EXISTING PROJECTS !!! + + double gap = std::min(100., m_build_volume_bb.size().norm() * (3./10.)); + return Vec2d::Ones() * gap; +} + + Vec2crd MultipleBeds::get_bed_gap() const { - const Vec2d size_with_gap{ - m_build_volume_bb_incl_model.size().cwiseProduct( - Vec2d::Ones() + Vec2d{bed_gap_x, bed_gap_y})}; - return scaled(Vec2d{(size_with_gap - m_build_volume_bb.size()) / 2.0}); + return scaled(Vec2d{bed_gap() / 2.0}); }; void MultipleBeds::ensure_wipe_towers_on_beds(Model& model, const std::vector>& prints) diff --git a/src/libslic3r/MultipleBeds.hpp b/src/libslic3r/MultipleBeds.hpp index 1b9f1ee4a6..9c6cdd6120 100644 --- a/src/libslic3r/MultipleBeds.hpp +++ b/src/libslic3r/MultipleBeds.hpp @@ -53,15 +53,15 @@ public: int get_last_hovered_bed() const { return m_last_hovered_bed; } void update_shown_beds(Model& model, const BuildVolume& build_volume); - bool update_after_load_or_arrange(Model& model, const BuildVolume& build_volume, std::function update_fn); + bool rearrange_after_load(Model& model, const BuildVolume& build_volume, std::function update_fn); void set_loading_project_flag(bool project) { m_loading_project = project; } bool get_loading_project_flag() const { return m_loading_project; } - void update_build_volume(const BoundingBoxf& build_volume_bb, const BoundingBoxf& build_volume_bb_incl_model) { + void update_build_volume(const BoundingBoxf& build_volume_bb) { m_build_volume_bb = build_volume_bb; - m_build_volume_bb_incl_model = build_volume_bb_incl_model; } - Vec2crd get_bed_gap() const; + Vec2d bed_gap() const; + Vec2crd get_bed_gap() const; void ensure_wipe_towers_on_beds(Model& model, const std::vector>& prints); private: @@ -75,17 +75,9 @@ private: std::map m_printbase_to_texture; int m_last_hovered_bed = -1; BoundingBoxf m_build_volume_bb; - BoundingBoxf m_build_volume_bb_incl_model; - bool m_layout_linear = false; + bool m_legacy_layout = false; bool m_loading_project = false; - // The x value is bed gap as multiples of the actual printable area bounding box, - // so it can be matched to how the old slicer arranged things (in SceneBuilder.cpp). - // The y value is a multiple of the larger of printable area BB and bed model BB - - // this is to make sure that the bed models do not overlap. - const double bed_gap_x = 2./10; - const double bed_gap_y = 2./10; - }; extern MultipleBeds s_multiple_beds; diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 93d83ee878..81b39dec6d 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -102,9 +102,18 @@ bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, c init_internal_model_from_file(); init_triangles(); - BoundingBoxf bb = m_build_volume.bounding_volume2d(); - bb.max = Vec2d(bb.max.x() + std::max(0., m_model.model.get_bounding_box().size().x() - bb.size().x()), bb.max.y() + std::max(0., m_model.model.get_bounding_box().size().y() - bb.size().y())); - s_multiple_beds.update_build_volume(m_build_volume.bounding_volume2d(), bb); + s_multiple_beds.update_build_volume(m_build_volume.bounding_volume2d()); + + m_models_overlap = false; + if (! m_model_filename.empty()) { + // Calculate bb of the bed model and figure out if the models would overlap when rendered next to each other. + const BoundingBoxf3& mdl_bb3 = m_model.model.get_bounding_box(); + const BoundingBoxf model_bb(Vec2d(mdl_bb3.min.x(), mdl_bb3.min.y()), Vec2d(mdl_bb3.max.x(), mdl_bb3.max.y())); + BoundingBoxf bed_bb = m_build_volume.bounding_volume2d(); + bed_bb.translate(-m_model_offset.x(), -m_model_offset.y()); + Vec2d gap = unscale(s_multiple_beds.get_bed_gap()); + m_models_overlap = (model_bb.size().x() - bed_bb.size().x() > 2 * gap.x() || model_bb.size().y() - bed_bb.size().y() > 2 * gap.y()); + } // Set the origin and size for rendering the coordinate system axes. m_axes.set_origin({ 0.0, 0.0, static_cast(GROUND_Z) }); @@ -345,6 +354,11 @@ void Bed3D::render_axes() void Bed3D::render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool is_active) { + if (m_models_overlap && s_multiple_beds.get_number_of_beds() + int(s_multiple_beds.should_show_next_bed()) > 1) { + render_default(bottom, false, show_texture, view_matrix, projection_matrix); + return; + } + if (!bottom) render_model(view_matrix, projection_matrix); @@ -507,7 +521,8 @@ void Bed3D::render_model(const Transform3d& view_matrix, const Transform3d& proj void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool picking, bool is_active) { - if (m_texture_filename.empty() && m_model_filename.empty()) { + if ((m_texture_filename.empty() && m_model_filename.empty()) + || (m_models_overlap && s_multiple_beds.get_number_of_beds() + int(s_multiple_beds.should_show_next_bed()) > 1)) { render_default(bottom, picking, show_texture, view_matrix, projection_matrix); return; } diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 2b4aecaa20..95f39f082c 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -37,6 +37,7 @@ private: Type m_type{ Type::Custom }; std::string m_texture_filename; std::string m_model_filename; + bool m_models_overlap; // Print volume bounding box exteded with axes and model. BoundingBoxf3 m_extended_bounding_box; // Print bed polygon diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7c2c9e2cab..946062f3a3 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1600,7 +1600,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ GLGizmoSimplify::add_simplify_suggestion_notification( obj_idxs, model.objects, *notification_manager); - if (s_multiple_beds.update_after_load_or_arrange(model, q->build_volume(), [this]() { + if (s_multiple_beds.rearrange_after_load(model, q->build_volume(), [this]() { q->canvas3D()->check_volumes_outside_state(); s_multiple_beds.ensure_wipe_towers_on_beds(model, fff_prints); })) @@ -4207,11 +4207,11 @@ void Plater::load_project(const wxString& filename) // Take the Undo / Redo snapshot. Plater::TakeSnapshot snapshot(this, _L("Load Project") + ": " + wxString::FromUTF8(into_path(filename).stem().string().c_str()), UndoRedo::SnapshotType::ProjectSeparator); + p->reset(); + s_multiple_beds.set_loading_project_flag(true); ScopeGuard guard([](){ s_multiple_beds.set_loading_project_flag(false);}); - p->reset(); - if (! load_files({ into_path(filename) }).empty()) { // At least one file was loaded. p->set_project_filename(filename);