diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index bffc45cde..ff752ef27 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -46,4 +46,7 @@ #define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA8 && ENABLE_TEXTURES_FROM_SVG) +// Enable scale object to fit print volume +#define ENABLE_SCALE_TO_FIT_PRINT_VOLUME 1 + #endif // _technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 1a431708a..ceffd6e0d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -847,6 +847,19 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas) break; } +#if ENABLE_SCALE_TO_FIT_PRINT_VOLUME + case 'F': + case 'f': + { + if (m_current == Scale) + { + wxGetApp().plater()->scale_selection_to_fit_print_volume(); + processed = true; + } + + break; + } +#endif // ENABLE_SCALE_TO_FIT_PRINT_VOLUME } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b60dd8bbb..6ed1ca898 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1302,7 +1302,10 @@ struct Plater::priv void sla_optimize_rotation(); void split_object(); void split_volume(); - bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; } +#if ENABLE_SCALE_TO_FIT_PRINT_VOLUME + void scale_selection_to_fit_print_volume(); +#endif // ENABLE_SCALE_TO_FIT_PRINT_VOLUME + bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; } void update_print_volume_state(); void schedule_background_process(); // Update background processing thread from the current config and Model. @@ -2364,6 +2367,13 @@ void Plater::priv::split_volume() wxGetApp().obj_list()->split(); } +#if ENABLE_SCALE_TO_FIT_PRINT_VOLUME +void Plater::priv::scale_selection_to_fit_print_volume() +{ + this->view3D->get_canvas3d()->get_selection().scale_to_fit_print_volume(*config); +} +#endif // ENABLE_SCALE_TO_FIT_PRINT_VOLUME + void Plater::priv::schedule_background_process() { delayed_error_message.clear(); @@ -3026,6 +3036,11 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu); +#if ENABLE_SCALE_TO_FIT_PRINT_VOLUME + append_menu_item(menu, wxID_ANY, _(L("Scale to print volume")), _(L("Scale the selected object to fit the print volume")), + [this](wxCommandEvent&) { scale_selection_to_fit_print_volume(); }, "", menu); +#endif // ENABLE_SCALE_TO_FIT_PRINT_VOLUME + wxMenu* mirror_menu = new wxMenu(); if (mirror_menu == nullptr) return false; @@ -3465,6 +3480,13 @@ bool Plater::is_selection_empty() const return p->get_selection().is_empty() || p->get_selection().is_wipe_tower(); } +#if ENABLE_SCALE_TO_FIT_PRINT_VOLUME +void Plater::scale_selection_to_fit_print_volume() +{ + p->scale_selection_to_fit_print_volume(); +} +#endif // ENABLE_SCALE_TO_FIT_PRINT_VOLUME + void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower) { wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index bc6d4b942..b836649db 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -162,6 +162,9 @@ public: void decrease_instances(size_t num = 1); void set_number_of_copies(/*size_t num*/); bool is_selection_empty() const; +#if ENABLE_SCALE_TO_FIT_PRINT_VOLUME + void scale_selection_to_fit_print_volume(); +#endif // ENABLE_SCALE_TO_FIT_PRINT_VOLUME void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 00fbaa42a..836f1d572 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -662,14 +662,34 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type { GLVolume &volume = *(*m_volumes)[i]; if (is_single_full_instance()) { - assert(transformation_type.absolute()); - if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) { - // Non-uniform scaling. Transform the scaling factors into the local coordinate system. - // This is only possible, if the instance rotation is mulitples of ninety degrees. - assert(Geometry::is_rotation_ninety_degrees(volume.get_instance_rotation())); - volume.set_instance_scaling_factor((volume.get_instance_transformation().get_matrix(true, false, true, true).matrix().block<3, 3>(0, 0).transpose() * scale).cwiseAbs()); - } else - volume.set_instance_scaling_factor(scale); +#if ENABLE_SCALE_TO_FIT_PRINT_VOLUME + if (transformation_type.relative()) + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); + Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); + // extracts scaling factors from the composed transformation + Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); + if (transformation_type.joint()) + volume.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); + + volume.set_instance_scaling_factor(new_scale); + } + else + { +#else + assert(transformation_type.absolute()); +#endif // ENABLE_SCALE_TO_FIT_PRINT_VOLUME + if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) { + // Non-uniform scaling. Transform the scaling factors into the local coordinate system. + // This is only possible, if the instance rotation is mulitples of ninety degrees. + assert(Geometry::is_rotation_ninety_degrees(volume.get_instance_rotation())); + volume.set_instance_scaling_factor((volume.get_instance_transformation().get_matrix(true, false, true, true).matrix().block<3, 3>(0, 0).transpose() * scale).cwiseAbs()); + } + else + volume.set_instance_scaling_factor(scale); +#if ENABLE_SCALE_TO_FIT_PRINT_VOLUME + } +#endif // ENABLE_SCALE_TO_FIT_PRINT_VOLUME } else if (is_single_volume() || is_single_modifier()) volume.set_volume_scaling_factor(scale); @@ -713,6 +733,51 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type this->set_bounding_boxes_dirty(); } +#if ENABLE_SCALE_TO_FIT_PRINT_VOLUME +void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config) +{ + if (is_empty() || (m_mode == Volume)) + return; + + // adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings + Vec3d box_size = get_bounding_box().size() + 0.01 * Vec3d::Ones(); + + const ConfigOptionPoints* opt = dynamic_cast(config.option("bed_shape")); + if (opt != nullptr) + { + BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); + BoundingBoxf3 print_volume(Vec3d(unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0), Vec3d(unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config.opt_float("max_print_height"))); + Vec3d print_volume_size = print_volume.size(); + double sx = (box_size(0) != 0.0) ? print_volume_size(0) / box_size(0) : 0.0; + double sy = (box_size(1) != 0.0) ? print_volume_size(1) / box_size(1) : 0.0; + double sz = (box_size(2) != 0.0) ? print_volume_size(2) / box_size(2) : 0.0; + if ((sx != 0.0) && (sy != 0.0) && (sz != 0.0)) + { + double s = std::min(sx, std::min(sy, sz)); + if (s != 1.0) + { + TransformationType type; + type.set_world(); + type.set_relative(); + type.set_joint(); + + // apply scale + start_dragging(); + scale(s * Vec3d::Ones(), type); + wxGetApp().plater()->canvas3D()->do_scale(); + + // center selection on print bed + start_dragging(); + translate(print_volume.center() - get_bounding_box().center()); + wxGetApp().plater()->canvas3D()->do_move(); + + wxGetApp().obj_manipul()->set_dirty(); + } + } + } +} +#endif // ENABLE_SCALE_TO_FIT_PRINT_VOLUME + void Selection::mirror(Axis axis) { if (!m_valid) diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 99d939acc..03967b4d4 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -287,6 +287,9 @@ public: void rotate(const Vec3d& rotation, TransformationType transformation_type); void flattening_rotate(const Vec3d& normal); void scale(const Vec3d& scale, TransformationType transformation_type); +#if ENABLE_SCALE_TO_FIT_PRINT_VOLUME + void scale_to_fit_print_volume(const DynamicPrintConfig& config); +#endif // ENABLE_SCALE_TO_FIT_PRINT_VOLUME void mirror(Axis axis); void translate(unsigned int object_idx, const Vec3d& displacement);