#include "OrientJob.hpp" #include "libslic3r/Model.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/NotificationManager.hpp" #include "libslic3r/PresetBundle.hpp" namespace Slic3r { namespace GUI { void OrientJob::clear_input() { const Model &model = m_plater->model(); size_t count = 0, cunprint = 0; // To know how much space to reserve for (auto obj : model.objects) for (auto mi : obj->instances) mi->printable ? count++ : cunprint++; m_selected.clear(); m_unselected.clear(); m_unprintable.clear(); m_selected.reserve(count); m_unselected.reserve(count); m_unprintable.reserve(cunprint); } //BBS: add only one plate mode and lock logic void OrientJob::prepare_selection(std::vector obj_sel, bool only_one_plate) { Model& model = m_plater->model(); PartPlateList& plate_list = m_plater->get_partplate_list(); //OrientMeshs selected_in_lock, unselect_in_lock; bool selected_is_locked = false; // Go through the objects and check if inside the selection for (size_t oidx = 0; oidx < obj_sel.size(); ++oidx) { bool selected = obj_sel[oidx]; ModelObject* mo = model.objects[oidx]; for (size_t inst_idx = 0; inst_idx < mo->instances.size(); ++inst_idx) { ModelInstance* mi = mo->instances[inst_idx]; OrientMesh&& om = get_orient_mesh(mi); bool locked = false; if (!only_one_plate) { int plate_index = plate_list.find_instance(oidx, inst_idx); if ((plate_index >= 0)&&(plate_index < plate_list.get_plate_count())) { if (plate_list.is_locked(plate_index)) { if (selected) { //selected_in_lock.emplace_back(std::move(om)); selected_is_locked = true; } //else // unselect_in_lock.emplace_back(std::move(om)); continue; } } } auto& cont = mo->printable ? (selected ? m_selected : m_unselected) : m_unprintable; cont.emplace_back(std::move(om)); } } // If the selection was empty orient everything if (m_selected.empty()) { if (!selected_is_locked) { m_selected.swap(m_unselected); //m_unselected.insert(m_unselected.begin(), unselect_in_lock.begin(), unselect_in_lock.end()); } else { m_plater->get_notification_manager()->push_notification(NotificationType::BBLPlateInfo, NotificationManager::NotificationLevel::WarningNotificationLevel, into_u8(_L("All the selected objects are on the locked plate,\nWe can not do auto-orient on these objects."))); } } } void OrientJob::prepare_selected() { clear_input(); Model &model = m_plater->model(); std::vector obj_sel(model.objects.size(), false); for (auto &s : m_plater->get_selection().get_content()) if (s.first < int(obj_sel.size())) obj_sel[size_t(s.first)] = !s.second.empty(); //BBS: add only one plate mode prepare_selection(obj_sel, false); } //BBS: prepare current part plate for orienting void OrientJob::prepare_partplate() { clear_input(); PartPlateList& plate_list = m_plater->get_partplate_list(); PartPlate* plate = plate_list.get_curr_plate(); assert(plate != nullptr); if (plate->empty()) { //no instances on this plate BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": no instances in current plate!"); return; } if (plate->is_locked()) { m_plater->get_notification_manager()->push_notification(NotificationType::BBLPlateInfo, NotificationManager::NotificationLevel::WarningNotificationLevel, into_u8(_L("This plate is locked,\nWe can not do auto-orient on this plate."))); return; } Model& model = m_plater->model(); std::vector obj_sel(model.objects.size(), false); // Go through the objects and check if inside the selection for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { ModelObject* mo = model.objects[oidx]; for (size_t inst_idx = 0; inst_idx < mo->instances.size(); ++inst_idx) { obj_sel[oidx] = plate->contain_instance(oidx, inst_idx); } } prepare_selection(obj_sel, true); } //BBS: add partplate logic void OrientJob::prepare() { int state = m_plater->get_prepare_state(); m_plater->get_notification_manager()->bbl_close_plateinfo_notification(); if (state == Job::JobPrepareState::PREPARE_STATE_DEFAULT) { only_on_partplate = false; prepare_selected(); } else if (state == Job::JobPrepareState::PREPARE_STATE_MENU) { only_on_partplate = true; // only arrange items on current plate prepare_partplate(); } } void OrientJob::on_exception(const std::exception_ptr &eptr) { try { if (eptr) std::rethrow_exception(eptr); } catch (std::exception &) { PlaterJob::on_exception(eptr); } } void OrientJob::process() { auto start = std::chrono::steady_clock::now(); static const auto arrangestr = _(L("Orienting...")); const GLCanvas3D::OrientSettings& settings = m_plater->canvas3D()->get_orient_settings(); orientation::OrientParams params; orientation::OrientParamsArea params_area; if (settings.min_area) { memcpy(¶ms, ¶ms_area, sizeof(params)); params.min_volume = false; } else { params.min_volume = true; } auto count = unsigned(m_selected.size() + m_unprintable.size()); params.stopcondition = [this]() { return was_canceled(); }; params.progressind = [this, count](unsigned st, std::string orientstr) { st += m_unprintable.size(); if (st > 0) update_status(int(st / float(count) * 100), _L("Orienting") + " " + orientstr); }; orientation::orient(m_selected, m_unselected, params); auto time_elapsed = std::chrono::duration_cast(std::chrono::steady_clock::now() - start); std::stringstream ss; if (!m_selected.empty()) ss << std::fixed << std::setprecision(3) << "Orient " << m_selected.back().name << " in " << time_elapsed.count() << " seconds. " << "Orientation: " << m_selected.back().orientation.transpose() << "; v,phi: " << m_selected.back().axis.transpose() << ", " << m_selected.back().angle << "; euler: " << m_selected.back().euler_angles.transpose(); // finalize just here. //update_status(int(count), // was_canceled() ? _(L("Orienting canceled.")) // : _(L(ss.str().c_str()))); wxGetApp().plater()->show_status_message(was_canceled() ? "Orienting canceled." : ss.str()); } void OrientJob::finalize() { // Ignore the arrange result if aborted. if (!was_canceled()) { for (OrientMesh& mesh : m_selected) { mesh.apply(); } m_plater->update(); // BBS //wxGetApp().obj_manipul()->set_dirty(); } Job::finalize(); } orientation::OrientMesh OrientJob::get_orient_mesh(ModelInstance* instance) { using OrientMesh = orientation::OrientMesh; OrientMesh om; auto obj = instance->get_object(); om.name = obj->name; om.mesh = obj->mesh(); // don't know the difference to obj->raw_mesh(). Both seem OK if (obj->config.has("support_threshold_angle")) om.overhang_angle = obj->config.opt_int("support_threshold_angle"); else { const Slic3r::DynamicPrintConfig& config = wxGetApp().preset_bundle->full_config(); om.overhang_angle = config.opt_int("support_threshold_angle"); } om.setter = [instance](const OrientMesh& p) { instance->rotate(p.rotation_matrix); instance->get_object()->ensure_on_bed(); }; return om; } }} // namespace Slic3r::GUI