diff --git a/src/libslic3r/BuildVolume.cpp b/src/libslic3r/BuildVolume.cpp index 513af79abf..8cb1715a08 100644 --- a/src/libslic3r/BuildVolume.cpp +++ b/src/libslic3r/BuildVolume.cpp @@ -292,8 +292,14 @@ BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set& i if (bed_idx) *bed_idx = -1; - for (int bed_id = 0; bed_id <= std::min(s_multiple_beds.get_number_of_beds(), s_multiple_beds.get_max_beds() - 1); ++bed_id) { + // When loading an old project with more than the maximum number of beds, + // we still want to move the objects to the respective positions. + // Max beds number is momentarily increased when doing the rearrange, so use it. + const int max_bed = s_multiple_beds.get_loading_project_flag() + ? s_multiple_beds.get_number_of_beds() - 1 + : std::min(s_multiple_beds.get_number_of_beds(), s_multiple_beds.get_max_beds() - 1); + for (int bed_id = 0; bed_id <= max_bed; ++bed_id) { Transform3f trafo = trafo_orig; @@ -336,10 +342,6 @@ BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set& i } if (out != ObjectState::Outside) { - if (bed_id == s_multiple_beds.get_number_of_beds()) { - // The object is on the next bed to be added. - s_multiple_beds.request_next_bed(true); - } if (bed_idx) *bed_idx = bed_id; break; @@ -352,7 +354,7 @@ BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set& i return out; } -BuildVolume::ObjectState BuildVolume::volume_state_bbox(const BoundingBoxf3 volume_bbox_orig, bool ignore_bottom) const +BuildVolume::ObjectState BuildVolume::volume_state_bbox(const BoundingBoxf3 volume_bbox_orig, bool ignore_bottom, int* bed_idx) const { assert(m_type == Type::Rectangle); BoundingBox3Base build_volume = this->bounding_volume().inflated(SceneEpsilon); @@ -362,10 +364,10 @@ BuildVolume::ObjectState BuildVolume::volume_state_bbox(const BoundingBoxf3 volu build_volume.min.z() = -std::numeric_limits::max(); ObjectState state = ObjectState::Outside; - int bed_idx = 0; - for (bed_idx = 0; bed_idx <= std::min(s_multiple_beds.get_number_of_beds(), s_multiple_beds.get_max_beds() - 1); ++bed_idx) { + int bed_id = 0; + for (bed_id = 0; bed_id <= std::min(s_multiple_beds.get_number_of_beds(), s_multiple_beds.get_max_beds() - 1); ++bed_id) { BoundingBoxf3 volume_bbox = volume_bbox_orig; - volume_bbox.translate(-s_multiple_beds.get_bed_translation(bed_idx)); + volume_bbox.translate(-s_multiple_beds.get_bed_translation(bed_id)); state = build_volume.max.z() <= -SceneEpsilon ? ObjectState::Below : build_volume.contains(volume_bbox) ? ObjectState::Inside : @@ -374,10 +376,8 @@ BuildVolume::ObjectState BuildVolume::volume_state_bbox(const BoundingBoxf3 volu break; } - if (bed_idx == s_multiple_beds.get_number_of_beds()) { - // The object is on the next bed to be added. - s_multiple_beds.request_next_bed(true); - } + if (bed_idx) + *bed_idx = bed_id; return state; } diff --git a/src/libslic3r/BuildVolume.hpp b/src/libslic3r/BuildVolume.hpp index c8af4c2f74..0630e50987 100644 --- a/src/libslic3r/BuildVolume.hpp +++ b/src/libslic3r/BuildVolume.hpp @@ -92,7 +92,7 @@ public: ObjectState object_state(const indexed_triangle_set &its, const Transform3f& trafo, bool may_be_below_bed, bool ignore_bottom = true, int* bed_idx = nullptr) const; // Called by GLVolumeCollection::check_outside_state() after an object is manipulated with gizmos for example. // Called for a rectangular bed: - ObjectState volume_state_bbox(BoundingBoxf3 volume_bbox, bool ignore_bottom = true) const; + ObjectState volume_state_bbox(BoundingBoxf3 volume_bbox, bool ignore_bottom, int* bed_idx) const; // 2) Test called on G-code paths. // Using BedEpsilon for all tests. diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 2e392b72ba..77951101e5 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1624,7 +1624,7 @@ unsigned int ModelObject::update_instances_print_volume_state(const BuildVolume inside_outside == INSIDE ? ModelInstancePVS_Inside : ModelInstancePVS_Fully_Outside; if (inside_outside == INSIDE) ++num_printable; - if (bed_idx != -1) + if (bed_idx != -1 && model_instance->is_printable()) s_multiple_beds.set_instance_bed(model_instance->id(), bed_idx); } return num_printable; diff --git a/src/libslic3r/MultipleBeds.cpp b/src/libslic3r/MultipleBeds.cpp index b42fb01887..30919eccc1 100644 --- a/src/libslic3r/MultipleBeds.cpp +++ b/src/libslic3r/MultipleBeds.cpp @@ -42,7 +42,7 @@ Vec3d MultipleBeds::get_bed_translation(int id) const else x=id-a-1; } - return Vec3d(x * m_build_volume_bb.size().x() * (1. + bed_gap_x), y * m_build_volume_bb.size().y() * (1. + bed_gap_y), 0.); + 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.); } @@ -137,15 +137,42 @@ bool MultipleBeds::is_glvolume_on_thumbnail_bed(const Model& model, int obj_idx, } - -bool MultipleBeds::rearrange_linear_to_grid_if_possible(Model& model, const BuildVolume& build_volume) +// 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) { + int original_number_of_beds = m_number_of_beds; + int stash_active = get_active_bed(); + Slic3r::ScopeGuard guard([&]() { + m_layout_linear = false; + m_number_of_beds = get_max_beds(); + model.update_print_volume_state(build_volume); + int max_bed = 0; + for (const auto& [oid, bed_id] : m_inst_to_bed) + max_bed = std::max(bed_id, max_bed); + m_number_of_beds = std::min(get_max_beds(), max_bed + 1); + model.update_print_volume_state(build_volume); + request_next_bed(false); + set_active_bed(m_number_of_beds != original_number_of_beds ? 0 : stash_active); + update_fn(); + }); + m_layout_linear = true; - int old_number_of_beds = m_number_of_beds; - m_number_of_beds = get_max_beds(); - Slic3r::ScopeGuard guard([this]() { m_layout_linear = false; }); - model.update_print_volume_state(build_volume); - m_number_of_beds = old_number_of_beds; + std::swap(m_build_volume_bb, m_build_volume_bb_incl_model); + int abs_max = get_max_beds(); + while (true) { + // This is to ensure that even objects on linear bed with higher than + // allowed index will be rearranged. + m_number_of_beds = abs_max; + model.update_print_volume_state(build_volume); + int max_bed = 0; + for (const auto& [oid, bed_id] : m_inst_to_bed) + max_bed = std::max(bed_id, max_bed); + if (max_bed + 1 < abs_max) + break; + abs_max += get_max_beds(); + } + m_number_of_beds = 1; + std::swap(m_build_volume_bb, m_build_volume_bb_incl_model); int max_bed = 0; @@ -163,17 +190,18 @@ bool MultipleBeds::rearrange_linear_to_grid_if_possible(Model& model, const Buil } } - if (max_bed == 0) { - // All instances are on the first bed. No need to do anything. - return false; - } + // Now do the rearrangement m_number_of_beds = max_bed + 1; assert(m_number_of_beds <= get_max_beds()); + if (m_number_of_beds == 1) + return false; // All instances are on some bed, at least two are used. 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); 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; @@ -181,7 +209,6 @@ bool MultipleBeds::rearrange_linear_to_grid_if_possible(Model& model, const Buil auto& [mi, bed_idx] = mi_and_bed; mi->set_offset(mi->get_offset() + get_bed_translation(bed_idx)); } - model.update_print_volume_state(build_volume); return true; } diff --git a/src/libslic3r/MultipleBeds.hpp b/src/libslic3r/MultipleBeds.hpp index 49c9b2b4ba..86f8257cc6 100644 --- a/src/libslic3r/MultipleBeds.hpp +++ b/src/libslic3r/MultipleBeds.hpp @@ -43,11 +43,11 @@ public: void set_last_hovered_bed(int i) { m_last_hovered_bed = i; } int get_last_hovered_bed() const { return m_last_hovered_bed; } - bool rearrange_linear_to_grid_if_possible(Model& model, const BuildVolume& build_volume); + bool update_after_load_or_arrange(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) { m_build_volume_bb = build_volume_bb; } + void update_build_volume(const BoundingBoxf& build_volume_bb, const BoundingBoxf& build_volume_bb_incl_model) { m_build_volume_bb = build_volume_bb; m_build_volume_bb_incl_model = build_volume_bb_incl_model; } @@ -65,6 +65,7 @@ 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_loading_project = false; }; diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 0346d07a90..93d83ee878 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -33,6 +33,7 @@ static const Slic3r::ColorRGBA DEFAULT_MODEL_COLOR = Slic3r::ColorRG static const Slic3r::ColorRGBA PICKING_MODEL_COLOR = Slic3r::ColorRGBA::BLACK(); static const Slic3r::ColorRGBA DEFAULT_SOLID_GRID_COLOR = { 0.9f, 0.9f, 0.9f, 1.0f }; static const Slic3r::ColorRGBA DEFAULT_TRANSPARENT_GRID_COLOR = { 0.9f, 0.9f, 0.9f, 0.6f }; +static const Slic3r::ColorRGBA DISABLED_MODEL_COLOR = { 0.6f, 0.6f, 0.6f, 0.75f }; namespace Slic3r { namespace GUI { @@ -84,8 +85,6 @@ bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, c m_model_filename = model_filename; m_extended_bounding_box = this->calc_extended_bounding_box(); - s_multiple_beds.update_build_volume(m_build_volume.bounding_volume2d()); - m_contour = ExPolygon(Polygon::new_scale(bed_shape)); const BoundingBox bbox = m_contour.contour.bounding_box(); if (!bbox.defined) @@ -97,13 +96,20 @@ bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, c m_texture.reset(); m_model.reset(); + // unregister from picking + wxGetApp().plater()->canvas3D()->remove_raycasters_for_picking(SceneRaycaster::EType::Bed); + + 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); + // Set the origin and size for rendering the coordinate system axes. m_axes.set_origin({ 0.0, 0.0, static_cast(GROUND_Z) }); m_axes.set_stem_length(0.1f * static_cast(m_build_volume.bounding_volume().max_size())); - // unregister from picking - wxGetApp().plater()->canvas3D()->remove_raycasters_for_picking(SceneRaycaster::EType::Bed); - // Let the calee to update the UI. return true; } @@ -145,8 +151,11 @@ void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, glsafe(::glEnable(GL_DEPTH_TEST)); m_model.model.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR); - if (!picking && ! active) - m_model.model.set_color(ColorRGBA(.6f, .6f, 0.6f, 0.5f)); + m_triangles.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR); + if (!picking && !active) { + m_model.model.set_color(DISABLED_MODEL_COLOR); + m_triangles.set_color(DISABLED_MODEL_COLOR); + } switch (m_type) { @@ -229,14 +238,6 @@ void Bed3D::init_triangles() register_raycasters_for_picking(init_data, Transform3d::Identity()); m_triangles.init_from(std::move(init_data)); - m_triangles.set_color(DEFAULT_MODEL_COLOR); - - BoundingBoxf bb = m_build_volume.bounding_volume2d(); - double y = bb.size().y(); - double ym = m_model.model.get_bounding_box().size().y(); - double diff = std::max(0., ym - y); - bb.max = Vec2d(bb.max.x(), bb.max.y() + diff); - s_multiple_beds.update_build_volume(bb); } void Bed3D::init_gridlines() @@ -454,7 +455,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& v } } -void Bed3D::render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix) +void Bed3D::init_internal_model_from_file() { if (m_model_filename.empty()) return; @@ -479,6 +480,14 @@ void Bed3D::render_model(const Transform3d& view_matrix, const Transform3d& proj // update extended bounding box m_extended_bounding_box = this->calc_extended_bounding_box(); } +} + +void Bed3D::render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix) +{ + if (m_model_filename.empty()) + return; + + init_internal_model_from_file(); if (!m_model.model.get_filename().empty()) { GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 27e6714712..2b4aecaa20 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -84,6 +84,7 @@ private: void init_triangles(); void init_gridlines(); void init_contourlines(); + void init_internal_model_from_file(); static std::tuple detect_type(const Pointfs& shape); void render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_texture, bool picking, bool active); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6fe4e233ef..79189cc094 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1493,19 +1493,20 @@ bool GLCanvas3D::check_volumes_outside_state(GLVolumeCollection& volumes, ModelI GLVolume* volume = volumes.volumes[vol_idx]; if (!volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (!volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) { BuildVolume::ObjectState state; + int bed_idx = -1; if (volume_below(*volume)) state = BuildVolume::ObjectState::Below; else { switch (build_volume.type()) { case BuildVolume::Type::Rectangle: //FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects. - state = build_volume.volume_state_bbox(volume_bbox(*volume)); + state = build_volume.volume_state_bbox(volume_bbox(*volume), true, &bed_idx); break; case BuildVolume::Type::Circle: case BuildVolume::Type::Convex: //FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently. case BuildVolume::Type::Custom: - state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast(), volume_sinking(*volume)); + state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast(), volume_sinking(*volume), true, &bed_idx); break; default: // Ignore, don't produce any collision. @@ -1521,6 +1522,9 @@ bool GLCanvas3D::check_volumes_outside_state(GLVolumeCollection& volumes, ModelI if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding) overall_state = ModelInstancePVS_Partly_Outside; contained_min_one |= !volume->is_outside; + + if (! volume->is_outside && bed_idx != -1 && bed_idx == s_multiple_beds.get_number_of_beds()) + s_multiple_beds.request_next_bed(true); } } else if (volume->is_modifier) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e8442a811a..6f3ea69cca 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1599,7 +1599,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.rearrange_linear_to_grid_if_possible(model, q->build_volume())) + if (s_multiple_beds.update_after_load_or_arrange(model, q->build_volume(), [this]() {q->canvas3D()->check_volumes_outside_state(); })) update(); return obj_idxs; @@ -2966,8 +2966,7 @@ void Plater::priv::set_current_panel(wxPanel* panel) bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside; if (!model.objects.empty() && !export_in_progress && model_fits) { preview->get_canvas3d()->init_gcode_viewer(); - if (! this->background_process.finished()) - preview->load_gcode_shells(); + preview->load_gcode_shells(); q->reslice(); } // keeps current gcode preview, if any @@ -6791,7 +6790,7 @@ void Plater::arrange(Worker &w, bool selected) concat_strings(names, "\n"))); } - s_multiple_beds.rearrange_linear_to_grid_if_possible(model(), build_volume()); + s_multiple_beds.update_after_load_or_arrange(model(), build_volume(), [this]() { canvas3D()->check_volumes_outside_state(); }); update(static_cast(UpdateParams::FORCE_FULL_SCREEN_REFRESH)); wxGetApp().obj_manipul()->set_dirty();