From 53ee0092b08d95d6664b8450464ea235bd951b07 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Fri, 28 Jan 2022 18:00:10 +0100 Subject: [PATCH] Create emboss object on cursor position --- src/slic3r/GUI/CameraUtils.cpp | 26 +++ src/slic3r/GUI/CameraUtils.hpp | 17 ++ src/slic3r/GUI/GUI_Factories.cpp | 11 +- src/slic3r/GUI/GUI_ObjectList.cpp | 22 ++- src/slic3r/GUI/GUI_ObjectList.hpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 205 ++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 7 +- src/slic3r/GUI/ImGuiWrapper.cpp | 2 +- src/slic3r/GUI/MeshUtils.cpp | 2 +- src/slic3r/GUI/MeshUtils.hpp | 4 +- 10 files changed, 212 insertions(+), 87 deletions(-) diff --git a/src/slic3r/GUI/CameraUtils.cpp b/src/slic3r/GUI/CameraUtils.cpp index c4b54677af..768c491c6d 100644 --- a/src/slic3r/GUI/CameraUtils.cpp +++ b/src/slic3r/GUI/CameraUtils.cpp @@ -72,3 +72,29 @@ Slic3r::Polygon CameraUtils::create_hull2d(const Camera & camera, Points vertices_2d = project(camera, vertices); return Geometry::convex_hull(vertices_2d); } + +#include +Vec3d CameraUtils::create_ray(const Camera &camera, const Vec2d &coor) { + Matrix4d modelview = camera.get_view_matrix().matrix(); + Matrix4d projection = camera.get_projection_matrix().matrix(); + Vec4i viewport(camera.get_viewport().data()); + + Vec3d scene_point(coor.x(), viewport[3] - coor.y(), 0.); + Vec3d unprojected_point; + igl::unproject(scene_point, modelview, projection, viewport, unprojected_point); + + Vec3d p0 = camera.get_position(); + Vec3d dir = unprojected_point - p0; + dir.normalize(); + return dir; +} + +Vec2d CameraUtils::get_z0_position(const Camera &camera, const Vec2d & coor) +{ + Vec3d dir = CameraUtils::create_ray(camera, coor); + Vec3d p0 = camera.get_position(); + // find position of ray cross plane(z = 0) + double t = p0.z() / dir.z(); + Vec3d p = p0 - t * dir; + return Vec2d(p.x(), p.y()); +} diff --git a/src/slic3r/GUI/CameraUtils.hpp b/src/slic3r/GUI/CameraUtils.hpp index 53b8e6a349..262c1ceff6 100644 --- a/src/slic3r/GUI/CameraUtils.hpp +++ b/src/slic3r/GUI/CameraUtils.hpp @@ -33,6 +33,23 @@ public: /// Outline by 3d object /// Polygon around object static Polygon create_hull2d(const Camera &camera, const GLVolume &volume); + + /// + /// Unproject screen coordinate to scene direction start from camera position + /// + /// Projection params + /// Coordinate on screen + /// Scene direction + static Vec3d create_ray(const Camera &camera, const Vec2d &coor); + + /// + /// Unproject mouse coordinate to get position in space where z coor is zero + /// Platter surface should be in z == 0 + /// + /// Projection params + /// Mouse position + /// Position on platter under mouse + static Vec2d get_z0_position(const Camera &camera, const Vec2d &coor); }; } // Slic3r::GUI diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 0d7140398a..0c81d792be 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -488,13 +488,18 @@ wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType ty GLCanvas3D * canvas = plater()->canvas3D(); GLGizmosManager &mng = canvas->get_gizmos_manager(); if ((mng.get_current_type() == GLGizmosManager::Emboss || - mng.open_gizmo(GLGizmosManager::Emboss)) && - type != ModelVolumeType::INVALID) { + mng.open_gizmo(GLGizmosManager::Emboss))) { GLGizmoEmboss *emboss = dynamic_cast(mng.get_current()); + assert(emboss != nullptr); if (emboss == nullptr) return; auto screen_position = canvas->get_popup_menu_position(); assert(screen_position.has_value()); - emboss->create_volume(type, *screen_position); + if(!screen_position.has_value()) return; + ModelVolumeType volume_type = type; + // no selected object means create new object + if (volume_type == ModelVolumeType::INVALID) + volume_type = ModelVolumeType::MODEL_PART; + emboss->create_volume(volume_type, *screen_position); } }; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 12a9a8cfad..85c64ae278 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1750,7 +1750,12 @@ void ObjectList::load_shape_object_from_gallery(const wxArrayString& input_files wxGetApp().mainframe->update_title(); } -void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center, const TextConfiguration* text_config/* = nullptr*/) +void ObjectList::load_mesh_object( + const TriangleMesh & mesh, + const wxString & name, + bool center, + const TextConfiguration *text_config /* = nullptr*/, + const Transform3d * transformation /* = nullptr*/) { // Add mesh to model as a new object Model& model = wxGetApp().plater()->model(); @@ -1760,7 +1765,6 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name #endif /* _DEBUG */ std::vector object_idxs; - auto bb = mesh.bounding_box(); ModelObject* new_object = model.add_object(); new_object->name = into_u8(name); new_object->add_instance(); // each object should have at list one instance @@ -1773,11 +1777,17 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); new_object->invalidate_bounding_box(); - new_object->translate(-bb.center()); - - new_object->instances[0]->set_offset(center ? - to_3d(wxGetApp().plater()->build_volume().bounding_volume2d().center(), -new_object->origin_translation.z()) : + if (transformation) { + assert(!center); + Slic3r::Geometry::Transformation tr(*transformation); + new_object->instances[0]->set_transformation(tr); + } else { + auto bb = mesh.bounding_box(); + new_object->translate(-bb.center()); + new_object->instances[0]->set_offset( + center ? to_3d(wxGetApp().plater()->build_volume().bounding_volume2d().center(), -new_object->origin_translation.z()) : bb.center()); + } new_object->ensure_on_bed(); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 86e4139ed3..3f65379c59 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -257,7 +257,8 @@ public: void load_shape_object(const std::string &type_name); void load_shape_object_from_gallery(); void load_shape_object_from_gallery(const wxArrayString& input_files); - void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true, const TextConfiguration* text_config = nullptr); + void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true, + const TextConfiguration* text_config = nullptr, const Transform3d* transformation = nullptr); void del_object(const int obj_idx); void del_subobject_item(wxDataViewItem& item); void del_settings_from_config(const wxDataViewItem& parent_item); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 46a74e645f..5d91666db1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -42,6 +42,7 @@ #define ALLOW_ADD_FONT_BY_OS_SELECTOR #define SHOW_IMGUI_ATLAS #define SHOW_FINE_POSITION +#define DRAW_PLACE_TO_ADD_TEXT #endif // ALLOW_DEBUG_MODE #define ALLOW_ADD_FONT_BY_FILE @@ -89,7 +90,7 @@ void GLGizmoEmboss::set_fine_position() ImGuiWrapper::draw(rect); } -#ifdef ALLOW_DEBUG_MODE +#ifdef SHOW_FINE_POSITION static void draw_fine_position(const Selection &selection) { const Selection::IndicesList indices = selection.get_volume_idxs(); @@ -111,38 +112,110 @@ static void draw_fine_position(const Selection &selection) ImGuiWrapper::draw(hull); ImGuiWrapper::draw(rect); } -#endif // ALLOW_DEBUG_MODE +#endif // SHOW_FINE_POSITION +#include "libslic3r/BuildVolume.hpp" void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos) { assert(volume_type == ModelVolumeType::MODEL_PART || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER); - if (!m_is_initialized) initialize(); - const Selection &selection = m_parent.get_selection(); - if(selection.is_empty()) return; - + std::shared_ptr &font_file = m_font_manager.get_font_file(); set_default_text(); - - // By position of cursor create transformation to put text on surface of model - Transform3d transformation; - const ModelObjectPtrs &objects = wxGetApp().plater()->model().objects; - m_raycast_manager.actualize(objects); - auto hit = m_raycast_manager.unproject(mouse_pos); - if (hit.has_value()) { - transformation = Emboss::create_transformation_onto_surface(hit->position, hit->normal); - } else { - // there is no hit with object - // TODO: calculate X,Y offset position for lay on platter by mouse position - transformation = Transform3d::Identity(); + + Vec2d screen_coor = mouse_pos; + if (mouse_pos.x() < 0 || mouse_pos.y() < 0) { + // use center of screen + auto screen_size = m_parent.get_canvas_size(); + screen_coor.x() = screen_size.get_width() / 2.; + screen_coor.y() = screen_size.get_height() / 2.; } - create_emboss_volume(create_mesh(), transformation, create_volume_name(), - create_configuration(), volume_type, - selection.get_object_idx()); + std::optional object_idx; + const Selection &selection = m_parent.get_selection(); + if (!selection.is_empty()) object_idx = selection.get_object_idx(); + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + queue_job( worker, [volume_type, + screen_coor, + object_idx, + ff = font_file, + text = m_text, + &raycast_manager = m_raycast_manager, + name = create_volume_name(), + tc = create_configuration(), + fi = m_font_manager.get_font_item() + ](Job::Ctl &ctl) { + // It is neccessary to create some shape + // Emboss text window is opened by creation new embosstext object + TriangleMesh tm = (ff == nullptr) ? + create_default_mesh() : + create_mesh(text.c_str(), *ff, fi.prop); + if (tm.its.empty()) tm = create_default_mesh(); + if (ctl.was_canceled()) return; + + std::optional hit; + if (object_idx.has_value()) { + // By position of cursor create transformation to put text on surface of model + const ModelObjectPtrs &objects = wxGetApp().plater()->model().objects; + raycast_manager.actualize(objects); + if (ctl.was_canceled()) return; + hit = raycast_manager.unproject(screen_coor); + + // 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()) { + // create new object + // calculate X,Y offset position for lay on platter in place of + // mouse click + const Camera &camera = wxGetApp().plater()->get_camera(); + Vec2d bed_coor = CameraUtils::get_z0_position(camera, screen_coor); + + // check point is on build plate: + Pointfs bed_shape = wxGetApp().plater()->build_volume().bed_shape(); + Points bed_shape_; + bed_shape_.reserve(bed_shape.size()); + for (const Vec2d &p : bed_shape) + bed_shape_.emplace_back(p.cast()); + Polygon bed(bed_shape_); + if (!bed.contains(bed_coor.cast())) + // mouse pose is out of build plate so create object in center of plate + bed_coor = bed.centroid().cast(); + + double z = tc.font_item.prop.emboss / 2; + Vec3d offset(bed_coor.x(), bed_coor.y(), z); + offset -= tm.center(); + Transform3d::TranslationType tt(offset.x(), offset.y(), offset.z()); + Transform3d trmat(tt); + create_emboss_object(std::move(tm), trmat, name, tc); + // Gizmo will open when successfuly create new object + // Gizmo can't be open when selection is empty + } else { + Transform3d transformation = Emboss::create_transformation_onto_surface(hit->position, hit->normal); + create_emboss_volume(std::move(tm), transformation, name, tc, volume_type, *object_idx); + } + }); } +#ifdef DRAW_PLACE_TO_ADD_TEXT +static void draw_place_to_add_text() { + ImVec2 mp = ImGui::GetMousePos(); + Vec2d mouse_pos(mp.x, mp.y); + const Camera &camera = wxGetApp().plater()->get_camera(); + Vec3d p1 = CameraUtils::get_z0_position(camera, mouse_pos); + std::vector rect3d{p1 + Vec3d(5, 5, 0), p1 + Vec3d(-5, 5, 0), + p1 + Vec3d(-5, -5, 0), p1 + Vec3d(5, -5, 0)}; + Points rect2d = CameraUtils::project(camera, rect3d); + ImGuiWrapper::draw(Slic3r::Polygon(rect2d)); +} +#endif // DRAW_PLACE_TO_ADD_TEXT + + bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) { if (mouse_event.Dragging()) { @@ -319,6 +392,10 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) // draw suggested position of window draw_fine_position(m_parent.get_selection()); #endif // SHOW_FINE_POSITION +#ifdef DRAW_PLACE_TO_ADD_TEXT + draw_place_to_add_text(); +#endif // DRAW_PLACE_TO_ADD_TEXT + // check if is set window offset if (m_set_window_offset.has_value()) { @@ -375,18 +452,6 @@ void GLGizmoEmboss::on_set_state() // to reload fonts from system, when install new one wxFontEnumerator::InvalidateCache(); - const Selection &selection = m_parent.get_selection(); - bool create_new_object = selection.is_empty(); - // When add Text on empty plate, Create new object with volume - if (create_new_object) { - set_default_text(); - create_emboss_object(create_mesh(), create_volume_name(), create_configuration()); - - // gizmo will open when successfuly create new object - GLGizmoBase::m_state = GLGizmoBase::Off; - return; - } - // Try(when exist) set configuration by volume load_configuration(get_selected_volume()); @@ -395,7 +460,7 @@ void GLGizmoEmboss::on_set_state() // when open by hyperlink it needs to show up // or after key 'T' windows doesn't appear - m_parent.reload_scene(true); + m_parent.set_as_dirty(); } } @@ -514,7 +579,7 @@ Slic3r::TriangleMesh GLGizmoEmboss::create_mesh() // Emboss text window is opened by creation new embosstext object std::shared_ptr& font_file = m_font_manager.get_font_file(); if (font_file == nullptr) return create_default_mesh(); - const FontItem &fi = m_font_manager.get_font_item(); + const FontItem &fi = m_font_manager.get_font_item(); TriangleMesh result = create_mesh(m_text.c_str(), *font_file, fi.prop); if (result.its.empty()) return create_default_mesh(); return result; @@ -652,15 +717,8 @@ void GLGizmoEmboss::draw_window() m_imgui->disabled_begin(!exist_font_file); if (m_volume == nullptr) { ImGui::SameLine(); - if (ImGui::Button(_u8L("Generate preview").c_str())) { - const Selection &s = m_parent.get_selection(); - auto selected_indices = s.get_instance_idxs(); - if (selected_indices.empty()) { - create_emboss_object(create_mesh(), create_volume_name(), create_configuration()); - } else { - create_volume(ModelVolumeType::MODEL_PART); - } - } + if (ImGui::Button(_u8L("Generate object").c_str())) + create_volume(ModelVolumeType::MODEL_PART); } m_imgui->disabled_end(); @@ -1238,6 +1296,7 @@ const ImVec2 &GLGizmoEmboss::get_minimal_window_size() const m_gui_cfg->minimal_window_size; } +#ifdef ALLOW_ADD_FONT_BY_OS_SELECTOR bool GLGizmoEmboss::choose_font_by_wxdialog() { wxFontData data; @@ -1287,7 +1346,9 @@ bool GLGizmoEmboss::choose_font_by_wxdialog() return true; } +#endif // ALLOW_ADD_FONT_BY_OS_SELECTOR +#ifdef ALLOW_ADD_FONT_BY_FILE bool GLGizmoEmboss::choose_true_type_file() { wxArrayString input_files; @@ -1318,6 +1379,7 @@ bool GLGizmoEmboss::choose_true_type_file() if (font_loaded) process(); return font_loaded; } +#endif // ALLOW_ADD_FONT_BY_FILE bool GLGizmoEmboss::choose_svg_file() { @@ -1618,29 +1680,31 @@ public: Priv() = delete; struct EmbossObject { - TriangleMesh mesh; - std::string name; + TriangleMesh mesh; + Transform3d transformation; + std::string name; TextConfiguration cfg; - EmbossObject(TriangleMesh && mesh, - std::string name, - TextConfiguration cfg) - : mesh(std::move(mesh)), name(name), cfg(cfg) + const Transform3d& transformation, + const std::string& name, + const TextConfiguration& cfg) + : mesh(std::move(mesh)) + , transformation(transformation) // copy + , name(name) // copy + , cfg(cfg) // copy {} }; struct EmbossVolume : public EmbossObject { ModelVolumeType type; size_t object_idx; - Transform3d transformation; EmbossVolume(TriangleMesh && mesh, - Transform3d transformation, - std::string name, - TextConfiguration cfg, + const Transform3d& transformation, + const std::string& name, + const TextConfiguration& cfg, ModelVolumeType type, size_t object_idx) - : EmbossObject(std::move(mesh), name, cfg) - , transformation(transformation) + : EmbossObject(std::move(mesh), transformation, name, cfg) , type(type) , object_idx(object_idx) {} @@ -1651,13 +1715,14 @@ public: } // namespace Slic3r -void GLGizmoEmboss::create_emboss_object(TriangleMesh && mesh, - std::string name, - TextConfiguration cfg) +void GLGizmoEmboss::create_emboss_object(TriangleMesh &&mesh, + const Transform3d& transformation, + const std::string& name, + const TextConfiguration& cfg) { // Move data to call after is not working - // data are owen by lambda - auto data = new Priv::EmbossObject(std::move(mesh), name, cfg); + // data are owned by lambda + auto data = new Priv::EmbossObject(std::move(mesh), transformation, name, cfg); wxGetApp().plater()->CallAfter([data]() { ScopeGuard sg([data]() { delete data; }); Priv::create_emboss_object(*data); @@ -1673,8 +1738,7 @@ void GLGizmoEmboss::create_emboss_volume(TriangleMesh && mesh, { // Move data to call after is not working // data are owen by lambda - auto data = new Priv::EmbossVolume(std::move(mesh), transformation, name, cfg, type, - object_idx); + auto data = new Priv::EmbossVolume(std::move(mesh), transformation, name, cfg, type, object_idx); wxGetApp().plater()->CallAfter([data]() { ScopeGuard sg([data]() { delete data; }); Priv::create_emboss_volume(*data); @@ -1687,17 +1751,18 @@ void Priv::create_emboss_object(EmbossObject &data) Plater * plater = app.plater(); ObjectList * obj_list = app.obj_list(); GLCanvas3D * canvas = plater->canvas3D(); - GLGizmosManager &manager = canvas->get_gizmos_manager(); plater->take_snapshot(_L("Add Emboss text object")); // Create new object and change selection - bool center = true; - obj_list->load_mesh_object(std::move(data.mesh), data.name, center, - &data.cfg); + bool center = false; + obj_list->load_mesh_object(std::move(data.mesh), data.name, center, &data.cfg, &data.transformation); - // new object successfuly added so open gizmo - assert(manager.get_current_type() != GLGizmosManager::Emboss); - manager.open_gizmo(GLGizmosManager::Emboss); + // When add new object selection is empty. + // Gizmo is automaticaly close when Selection is empty + // new object successfuly added so open gizmo when it was closed + GLGizmosManager &manager = canvas->get_gizmos_manager(); + if(manager.get_current_type() != GLGizmosManager::Emboss) + manager.open_gizmo(GLGizmosManager::Emboss); // redraw scene canvas->reload_scene(true); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index f7f149b2da..91a65b5072 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -76,7 +76,7 @@ protected: private: void initialize(); void set_default_text(); - TriangleMesh create_default_mesh(); + static TriangleMesh create_default_mesh(); TriangleMesh create_mesh(); /// @@ -221,8 +221,9 @@ private: // call after functions to work outside of drawing static void create_emboss_object(TriangleMesh && mesh, - std::string name, - TextConfiguration cfg); + const Transform3d &transformation, + const std::string &name, + const TextConfiguration &cfg); static void create_emboss_volume(TriangleMesh && mesh, Transform3d transformation, std::string name, diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index bd3676c085..395daeb199 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1178,7 +1178,7 @@ std::string ImGuiWrapper::trunc(const std::string &text, return "Should not be accessible"; } -ImVec2 ImGuiWrapper::suggest_location(const ImVec2 & dialog_size, +ImVec2 ImGuiWrapper::suggest_location(const ImVec2 &dialog_size, const Slic3r::Polygon &interest) { Plater * plater = wxGetApp().plater(); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 440b0ec0db..c385eaa78e 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -181,7 +181,7 @@ Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const } void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3d& point, Vec3d& direction) const + Vec3d& point, Vec3d& direction) { Matrix4d modelview = camera.get_view_matrix().matrix(); Matrix4d projection= camera.get_projection_matrix().matrix(); diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index bb8a1aa618..dc4f59cebe 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -121,8 +121,8 @@ public: { } - void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3d& point, Vec3d& direction) const; + static void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3d& point, Vec3d& direction); // Given a mouse position, this returns true in case it is on the mesh. bool unproject_on_mesh(