#include "EmbossJob.hpp" #include <libslic3r/Model.hpp> #include <libslic3r/Format/OBJ.hpp> // load_obj for default mesh #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/NotificationManager.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/MainFrame.hpp" #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp" #include "slic3r/GUI/CameraUtils.hpp" #include "slic3r/GUI/format.hpp" #include "slic3r/Utils/UndoRedo.hpp" using namespace Slic3r; using namespace GUI; void EmbossUpdateJob::process(Ctl &ctl) { // check if exist valid font if (m_input->font_file == nullptr) return; const TextConfiguration &cfg = m_input->text_configuration; m_result = EmbossCreateJob::create_mesh( cfg.text.c_str(), *m_input->font_file, cfg.font_item.prop, ctl); if (m_result.its.empty()) return; if (ctl.was_canceled()) return; // center triangle mesh Vec3d shift = m_result.bounding_box().center(); m_result.translate(-shift.cast<float>()); } void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &) { if (canceled) return; // for sure that some object is created from shape if (m_result.its.indices.empty()) return; GUI_App & app = wxGetApp(); // may be move to input Plater * plater = app.plater(); ObjectList * obj_list = app.obj_list(); GLCanvas3D * canvas = plater->canvas3D(); GLGizmosManager &manager = canvas->get_gizmos_manager(); // Check emboss gizmo is still open if (manager.get_current_type() != GLGizmosManager::Emboss) return; Plater::TakeSnapshot snapshot(plater, GUI::format(_L("Text: %1%"), m_input->text_configuration.text), UndoRedo::SnapshotType::GizmoAction); ModelVolume *volume = m_input->volume; // find volume by object id - NOT WORK // -> edit text change volume id so could apper not found volume // ModelVolume *volume = nullptr; // Model &model = plater->model(); // for (auto obj : model.objects) // for (auto vol : obj->volumes) // if (vol->id() == volume_id) { // volume = vol; // break; // } // if (volume == nullptr) return; assert(volume != nullptr); // update volume volume->set_mesh(std::move(m_result)); volume->set_new_unique_id(); volume->calculate_convex_hull(); volume->get_object()->invalidate_bounding_box(); volume->name = m_input->volume_name; volume->text_configuration = m_input->text_configuration; // update volume in right panel( volume / object name) const Selection &selection = canvas->get_selection(); const GLVolume * gl_volume = selection.get_volume( *selection.get_volume_idxs().begin()); int object_idx = gl_volume->object_idx(); int volume_idx = gl_volume->volume_idx(); obj_list->update_name_in_list(object_idx, volume_idx); // update printable state on canvas if (volume->type() == ModelVolumeType::MODEL_PART) canvas->update_instance_printable_state_for_object( (size_t) object_idx); // redraw scene canvas->reload_scene(true); } void EmbossCreateJob::process(Ctl &ctl) { // It is neccessary to create some shape // Emboss text window is opened by creation new emboss text object m_result = (m_input->font_file == nullptr) ? create_default_mesh() : create_mesh(m_input->text_configuration.text.c_str(), *m_input->font_file, m_input->text_configuration.font_item.prop, ctl); if (m_result.its.empty()) m_result = create_default_mesh(); if (ctl.was_canceled()) return; std::optional<RaycastManager::Hit> hit; if (m_input->object_idx.has_value()) { // By position of cursor create transformation to put text on surface of model ModelObject *obj = wxGetApp().plater()->model().objects[*m_input->object_idx]; m_input->raycast_manager->actualize(obj); if (ctl.was_canceled()) return; hit = m_input->raycast_manager->unproject(m_input->screen_coor, m_input->camera); // context menu for add text could be open only by right click on an // object. After right click, object is selected and object_idx is set // also hit must exist. But there is proper behavior when hit doesn't // exists. When this assert appear distquish remove of it. assert(hit.has_value()); if (!hit.has_value()) m_input->object_idx.reset(); } if (!hit.has_value()) { // create new object // calculate X,Y offset position for lay on platter in place of // mouse click Vec2d bed_coor = CameraUtils::get_z0_position(m_input->camera, m_input->screen_coor); // check point is on build plate: Points bed_shape_; bed_shape_.reserve(m_input->bed_shape.size()); for (const Vec2d &p : m_input->bed_shape) bed_shape_.emplace_back(p.cast<int>()); Polygon bed(bed_shape_); if (!bed.contains(bed_coor.cast<int>())) // mouse pose is out of build plate so create object in center of plate bed_coor = bed.centroid().cast<double>(); double z = m_input->text_configuration.font_item.prop.emboss / 2; Vec3d offset(bed_coor.x(), bed_coor.y(), z); offset -= m_result.center(); Transform3d::TranslationType tt(offset.x(), offset.y(), offset.z()); m_transformation = Transform3d(tt); } else { //m_transformation = Emboss::create_transformation_onto_surface(hit->position, hit->normal); assert(m_input->hit_vol_tr.has_value()); if (m_input->hit_vol_tr.has_value()) { Transform3d object_trmat = m_input->raycast_manager->get_transformation(hit->tr_key); const FontProp &font_prop = m_input->text_configuration.font_item.prop; Transform3d surface_trmat = Emboss::create_transformation_onto_surface(hit->position, hit->normal); Emboss::apply_transformation(font_prop, surface_trmat); m_transformation = m_input->hit_vol_tr->inverse() * object_trmat * surface_trmat; } } } void EmbossCreateJob::finalize(bool canceled, std::exception_ptr &) { if (canceled) return; GUI_App & app = wxGetApp(); Plater * plater = app.plater(); ObjectList *obj_list = app.obj_list(); GLCanvas3D *canvas = plater->canvas3D(); // decide if create object or volume bool create_object = !m_input->object_idx.has_value(); if (create_object) { Plater::TakeSnapshot snapshot(plater, _L("Add Emboss text object")); // Create new object and change selection bool center = false; obj_list->load_mesh_object(std::move(m_result), m_input->volume_name, center, &m_input->text_configuration, &m_transformation); // When add new object selection is empty. // When cursor move and no one object is selected than Manager::reset_all() // So Gizmo could be closed on end of creation object GLGizmosManager &manager = canvas->get_gizmos_manager(); if (manager.get_current_type() != GLGizmosManager::Emboss) manager.open_gizmo(GLGizmosManager::Emboss); } else { // create volume in object size_t object_idx = *m_input->object_idx; ModelVolumeType type = m_input->volume_type; // create new volume inside of object Model &model = plater->model(); if (model.objects.size() <= object_idx) return; ModelObject *obj = model.objects[object_idx]; ModelVolume *volume = obj->add_volume(std::move(m_result)); // set a default extruder value, since user can't add it manually volume->config.set_key_value("extruder", new ConfigOptionInt(0)); // do not allow model reload from disk volume->source.is_from_builtin_objects = true; volume->set_type(type); volume->name = m_input->volume_name; volume->text_configuration = m_input->text_configuration; volume->set_transformation(m_transformation); // update volume name in object list // updata selection after new volume added // change name of volume in right panel // select only actual volume // when new volume is created change selection to this volume auto add_to_selection = [volume](const ModelVolume *vol) { return vol == volume; }; wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection((int) object_idx, add_to_selection); if (!sel.IsEmpty()) obj_list->select_item(sel.front()); // update printable state on canvas if (type == ModelVolumeType::MODEL_PART) canvas->update_instance_printable_state_for_object(object_idx); obj_list->selection_changed(); // WHY selection_changed set manipulation to world ??? // so I set it back to local --> RotationGizmo need it ObjectManipulation *manipul = wxGetApp().obj_manipul(); manipul->set_coordinates_type(ECoordinatesType::Local); } // redraw scene canvas->reload_scene(true); } TriangleMesh EmbossCreateJob::create_default_mesh() { // When cant load any font use default object loaded from file std::string path = Slic3r::resources_dir() + "/data/embossed_text.stl"; TriangleMesh triangle_mesh; if (!load_obj(path.c_str(), &triangle_mesh)) { // when can't load mesh use cube return TriangleMesh(its_make_cube(36., 4., 2.5)); } return triangle_mesh; } TriangleMesh EmbossCreateJob::create_mesh(const char * text, Emboss::FontFile &font, const FontProp & font_prop, Ctl & ctl) { ExPolygons shapes = Emboss::text2shapes(font, text, font_prop); if (shapes.empty()) return {}; if (ctl.was_canceled()) return {}; float scale = font_prop.size_in_mm / font.unit_per_em; float depth = font_prop.emboss / scale; auto projectZ = std::make_unique<Emboss::ProjectZ>(depth); Emboss::ProjectScale project(std::move(projectZ), scale); if (ctl.was_canceled()) return {}; return TriangleMesh(Emboss::polygons2model(shapes, project)); }