Polishing:

- virtual bed shows even when dragging which was already outside
- when loading old-arranged project with more than max number of beds, a virtual bed would still be offered
- loading a project file with multiple beds only showed 2 beds initially
- nonprintable objects are not considered into the bed creation logic
- drag and dropping an object does not change active bed
- shells in preview are now shown correctly when switching between beds
- spacing in x takes into account model size (except during the initial rearrange)
- update volumes outside state after project loading
- highlight of selected bed for default beds
This commit is contained in:
Lukas Matena 2024-10-14 15:21:51 +02:00
parent cc73ff8f4f
commit 360f7a84b4
9 changed files with 93 additions and 52 deletions

View File

@ -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<Vec3d> 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<double>::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;
}

View File

@ -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.

View File

@ -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;

View File

@ -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<void()> 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;
}

View File

@ -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<void()> 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<PrintBase*, size_t> 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;
};

View File

@ -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<double>(GROUND_Z) });
m_axes.set_stem_length(0.1f * static_cast<float>(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");

View File

@ -84,6 +84,7 @@ private:
void init_triangles();
void init_gridlines();
void init_contourlines();
void init_internal_model_from_file();
static std::tuple<Type, std::string, std::string> 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);

View File

@ -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<float>(), volume_sinking(*volume));
state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast<float>(), 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)

View File

@ -1599,7 +1599,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& 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<unsigned int>(UpdateParams::FORCE_FULL_SCREEN_REFRESH));
wxGetApp().obj_manipul()->set_dirty();