diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 5cbacc3d7..ede2dc661 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -135,6 +135,7 @@ add_library(libslic3r STATIC Model.hpp ModelArrange.hpp ModelArrange.cpp + ModelVolumeType.hpp MultiMaterialSegmentation.cpp MultiMaterialSegmentation.hpp CustomGCode.cpp diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index dd875074b..4dd3a534e 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -14,6 +14,7 @@ #include "Arrange.hpp" #include "CustomGCode.hpp" #include "enum_bitmask.hpp" +#include "ModelVolumeType.hpp" #include "TextConfiguration.hpp" #include @@ -220,16 +221,6 @@ private: friend class ModelObject; }; -// Declared outside of ModelVolume, so it could be forward declared. -enum class ModelVolumeType : int { - INVALID = -1, - MODEL_PART = 0, - NEGATIVE_VOLUME, - PARAMETER_MODIFIER, - SUPPORT_BLOCKER, - SUPPORT_ENFORCER, -}; - enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower }; using ModelObjectCutAttributes = enum_bitmask; ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); @@ -801,7 +792,8 @@ private: ObjectBase(other), name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation), - supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets) + supported_facets(other.supported_facets), seam_facets(other.seam_facets), + mmu_segmentation_facets(other.mmu_segmentation_facets), text_configuration(other.text_configuration) { assert(this->id().valid()); assert(this->config.id().valid()); @@ -821,7 +813,9 @@ private: } // Providing a new mesh, therefore this volume will get a new unique ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : - name(other.name), source(other.source), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) + name(other.name), source(other.source), m_mesh(new TriangleMesh(std::move(mesh))), + config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation), + text_configuration(other.text_configuration) { assert(this->id().valid()); assert(this->config.id().valid()); diff --git a/src/libslic3r/ModelVolumeType.hpp b/src/libslic3r/ModelVolumeType.hpp new file mode 100644 index 000000000..5ae1c6440 --- /dev/null +++ b/src/libslic3r/ModelVolumeType.hpp @@ -0,0 +1,16 @@ +#ifndef slic3r_ModelVolumeType_hpp_ +#define slic3r_ModelVolumeType_hpp_ + +namespace Slic3r { + +enum class ModelVolumeType : int { + INVALID = -1, + MODEL_PART = 0, + NEGATIVE_VOLUME, + PARAMETER_MODIFIER, + SUPPORT_BLOCKER, + SUPPORT_ENFORCER, +}; + +} // namespace Slic3r +#endif /* slic3r_ModelVolumeType_hpp_ */ diff --git a/src/libslic3r/TextConfiguration.hpp b/src/libslic3r/TextConfiguration.hpp index e4e334877..9f5505474 100644 --- a/src/libslic3r/TextConfiguration.hpp +++ b/src/libslic3r/TextConfiguration.hpp @@ -56,9 +56,9 @@ struct TextConfiguration // user modification of font FontProp font_prop; - std::string text; + std::string text = "None"; - TextConfiguration() = default; + TextConfiguration() = default; // optional needs empty constructor TextConfiguration(const FontItem & font_item, const FontProp & font_prop, const std::string &text) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 551760ee9..989642bab 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2858,6 +2858,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { if (!m_initialized || !_set_current()) return; + #if ENABLE_RETINA_GL const float scale = m_retina_helper->get_scale_factor(); @@ -3281,6 +3282,18 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } mouse_up_cleanup(); + } else if (evt.LeftDClick()) { + // open text editation + int vol_id = get_first_hover_volume_idx(); + const auto & cid = m_volumes.volumes[vol_id]->composite_id; + const ModelObject *obj = m_model->objects[cid.object_id]; + const ModelVolume *mv = obj->volumes[cid.volume_id]; + if (mv->text_configuration.has_value()) { + // select volume and open emboss gizmo + deselect_all(); + m_selection.add(vol_id); + m_gizmos.open_gizmo(GLGizmosManager::EType::Emboss); + } } else if (evt.Moving()) { m_mouse.position = pos.cast(); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index dcc57db07..5d42b6afe 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -469,12 +469,9 @@ wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType ty auto add_text = [type](wxCommandEvent &) { GLGizmosManager &mng = plater()->canvas3D()->get_gizmos_manager(); - if (mng.open_gizmo(GLGizmosManager::Emboss)) { - GLGizmoBase * base = mng.get_current(); - GLGizmoEmboss *emboss = dynamic_cast(base); - if (emboss == nullptr) { - int j = 42; - } else + if (mng.get_current_type() == GLGizmosManager::Emboss || + mng.open_gizmo(GLGizmosManager::Emboss)) { + GLGizmoEmboss *emboss = dynamic_cast(mng.get_current()); emboss->set_volume_type(type); } }; @@ -898,6 +895,26 @@ void MenuFactory::append_menu_items_mirror(wxMenu* menu) []() { return plater()->can_mirror(); }, m_parent); } +void MenuFactory::append_menu_item_edit_text(wxMenu *menu) { + wxString name = _L("Edit text"); + wxString description = _L("Ability to change text, font, size, ..."); + std::string icon = ""; + append_menu_item( + menu, wxID_ANY, name, description, + [](wxCommandEvent &) { + plater()->canvas3D()->get_gizmos_manager().open_gizmo(GLGizmosManager::Emboss); + }, + icon, nullptr, + []() { + const auto& sel = plater()->get_selection(); + if (sel.volumes_count() != 1) return false; + auto cid = sel.get_volume(*sel.get_volume_idxs().begin()); + const ModelVolume *vol = plater()->canvas3D()->get_model() + ->objects[cid->object_idx()]->volumes[cid->volume_idx()]; + return vol->text_configuration.has_value(); + }, m_parent); +} + MenuFactory::MenuFactory() { for (int i = 0; i < mtCount; i++) { @@ -980,6 +997,7 @@ void MenuFactory::create_part_menu() #ifdef __WXOSX__ append_menu_items_osx(menu); #endif // __WXOSX__ + append_menu_item_edit_text(menu); append_menu_item_delete(menu); append_menu_item_reload_from_disk(menu); append_menu_item_replace_with_stl(menu); diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index 0c478a97b..726eb0931 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -103,6 +103,7 @@ private: void append_menu_item_merge_to_multipart_object(wxMenu *menu); // void append_menu_item_merge_to_single_object(wxMenu *menu); void append_menu_items_mirror(wxMenu *menu); + void append_menu_item_edit_text(wxMenu *menu); void append_menu_items_instance_manipulation(wxMenu *menu); void update_menu_items_instance_manipulation(MenuType type); }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index e523fe932..a9c3b562f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -73,7 +73,7 @@ GLGizmoEmboss::~GLGizmoEmboss() {} bool GLGizmoEmboss::on_init() { //m_grabbers.emplace_back(); - m_shortcut_key = WXK_CONTROL_Q; + m_shortcut_key = WXK_CONTROL_T; return true; } @@ -92,95 +92,9 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse; m_imgui->begin(on_get_name(), flag); - - if (!m_font.has_value()) { - ImGui::Text("Warning: No font is selected. Select correct one."); - } + draw_window(); - draw_font_list(); - - if (ImGui::Button(_L("choose font").c_str())) { - choose_font_by_dialog(); - } - - ImGui::SameLine(); - if (ImGui::Button(_L("use system font").c_str())) { - wxSystemSettings ss; - wxFont f = ss.GetFont(wxSYS_DEFAULT_GUI_FONT); - size_t font_index = m_font_list.size(); - FontItem fi = WxFontUtils::get_font_item(f); - m_font_list.emplace_back(fi); - bool loaded = load_font(font_index); - } - - ImGui::SameLine(); - draw_add_button(); - - if (ImGui::Button("add svg")) { - std::string filePath = - "C:/Users/filip/Downloads/fontawesome-free-5.15.4-web/" - "fontawesome-free-5.15.4-web/svgs/solid/bicycle.svg"; - filePath = "C:/Users/filip/Downloads/circle.svg"; - NSVGimage *image = nsvgParseFromFile(filePath.c_str(), "mm", 96.0f); - ExPolygons polys = NSVGUtils::to_ExPolygons(image); - - for (auto &poly : polys) poly.scale(1e5); - SVG svg("converted.svg", BoundingBox(polys.front().contour.points)); - svg.draw(polys); - - nsvgDelete(image); - } - - if (ImGui::InputFloat("Size[in mm]", &m_font_prop.size_in_mm)) { - if (m_font_prop.size_in_mm < 0.1) m_font_prop.size_in_mm = 10; - process(); - } - if (ImGui::InputFloat("Emboss[in mm]", &m_font_prop.emboss)) process(); - if (ImGui::InputFloat("Flatness", &m_font_prop.flatness)) { - if (m_font.has_value()) m_font->cache.clear(); - process(); - } - if (ImGui::InputInt("CharGap[in font points]", &m_font_prop.char_gap)) process(); - if (ImGui::InputInt("LineGap[in font points]", &m_font_prop.line_gap)) process(); - - //ImGui::InputFloat3("Origin", m_orientation.origin.data()); - //if (ImGui::InputFloat3("Normal", m_normal.data())) m_normal.normalize(); - //if (ImGui::InputFloat3("Up", m_up.data())) m_up.normalize(); - - ImVec2 input_size(-FLT_MIN, ImGui::GetTextLineHeight() * 6); - ImGuiInputTextFlags flags = - ImGuiInputTextFlags_::ImGuiInputTextFlags_AllowTabInput - | ImGuiInputTextFlags_::ImGuiInputTextFlags_AutoSelectAll - //| ImGuiInputTextFlags_::ImGuiInputTextFlags_CallbackResize - //|ImGuiInputTextFlags_::ImGuiInputTextFlags_CtrlEnterForNewLine - ; - - - if (ImGui::InputTextMultiline("##Text", m_text.get(), m_text_size, input_size, flags)) { - process(); - } - - // change text size - int max_text_size = static_cast(m_text_size); - if (ImGui::InputInt("max text size", &max_text_size, 8, 64)) { - set_max_text_size(static_cast(max_text_size)); - } - - // draw 2d triangle in IMGUI - ImVec2 t0(25, 25); - ImVec2 t1(150, 5); - ImVec2 t2(10, 100); - ImU32 c = ImGui::ColorConvertFloat4ToU32(ImVec4(.0, .8, .2, 1.)); - ImGui::GetOverlayDrawList()->AddTriangleFilled(t0, t1, t2, c); - - // create text volume when reselect volumes - m_imgui->disabled_begin(!m_font.has_value()); - if (m_volume == nullptr) { - if (ImGui::Button("Generate preview")) process(); - } - m_imgui->disabled_end(); - - m_imgui->end(); + m_imgui->end(); // } void GLGizmoEmboss::on_set_state() @@ -211,7 +125,7 @@ void GLGizmoEmboss::on_set_state() } // Try set selected volume - if (!set_volume()) { + if (!load_configuration(get_selected_volume())) { // No volume with text selected, create new one set_default_configuration(); process(); @@ -314,6 +228,7 @@ bool GLGizmoEmboss::gizmo_event(SLAGizmoEventType action, void GLGizmoEmboss::set_default_configuration() { set_text(_u8L("Embossed text")); m_font_prop = FontProp(); + m_volume_type = ModelVolumeType::MODEL_PART; // may be set default font? } @@ -328,10 +243,9 @@ void GLGizmoEmboss::check_selection() if (m_volume != nullptr) ImGui::ClearActiveID(); - // is selected volume embossed? - if (vol!= nullptr && vol->text_configuration.has_value()) { - m_volume = vol; - load_configuration(*vol->text_configuration); + // is select embossed volume? + if (load_configuration(vol)) { + // successfull load volume for editing return; } @@ -427,7 +341,7 @@ bool GLGizmoEmboss::process() { // select new added volume ModelObject *mo = m_volume->get_object(); // Editing object volume change its name - if (mo->volumes.size() == 1) mo->name = volume_name; + if (mo->volumes.size() == 1) mo->name = volume_name; ObjectList *obj_list = app.obj_list(); const ModelObjectPtrs &objs = *obj_list->objects(); auto item = find(objs.begin(), objs.end(), mo); @@ -441,7 +355,7 @@ bool GLGizmoEmboss::process() { return items.front(); }); - if (m_volume_type == ModelVolumeType::MODEL_PART) + if (m_volume->type() == ModelVolumeType::MODEL_PART) // update printable state on canvas m_parent.update_instance_printable_state_for_object((size_t) object_idx); @@ -490,6 +404,89 @@ void GLGizmoEmboss::draw_add_button() { } } +void GLGizmoEmboss::draw_window() +{ + if (!m_font.has_value()) { + ImGui::Text("Warning: No font is selected. Select correct one."); + } + + draw_font_list(); + + if (ImGui::Button(_L("choose font").c_str())) { choose_font_by_dialog(); } + + ImGui::SameLine(); + if (ImGui::Button(_L("use system font").c_str())) { + wxSystemSettings ss; + wxFont f = ss.GetFont(wxSYS_DEFAULT_GUI_FONT); + size_t font_index = m_font_list.size(); + FontItem fi = WxFontUtils::get_font_item(f); + m_font_list.emplace_back(fi); + bool loaded = load_font(font_index); + } + + ImGui::SameLine(); + draw_add_button(); + + if (ImGui::Button("add svg")) { + std::string filePath = + "C:/Users/filip/Downloads/fontawesome-free-5.15.4-web/" + "fontawesome-free-5.15.4-web/svgs/solid/bicycle.svg"; + filePath = "C:/Users/filip/Downloads/circle.svg"; + NSVGimage *image = nsvgParseFromFile(filePath.c_str(), "mm", 96.0f); + ExPolygons polys = NSVGUtils::to_ExPolygons(image); + + for (auto &poly : polys) poly.scale(1e5); + SVG svg("converted.svg", BoundingBox(polys.front().contour.points)); + svg.draw(polys); + + nsvgDelete(image); + } + + if (ImGui::InputFloat("Size[in mm]", &m_font_prop.size_in_mm)) { + if (m_font_prop.size_in_mm < 0.1) m_font_prop.size_in_mm = 10; + process(); + } + if (ImGui::InputFloat("Emboss[in mm]", &m_font_prop.emboss)) process(); + if (ImGui::InputFloat("Flatness", &m_font_prop.flatness)) { + if (m_font.has_value()) m_font->cache.clear(); + process(); + } + if (ImGui::InputInt("CharGap[in font points]", &m_font_prop.char_gap)) + process(); + if (ImGui::InputInt("LineGap[in font points]", &m_font_prop.line_gap)) + process(); + + // ImGui::InputFloat3("Origin", m_orientation.origin.data()); + // if (ImGui::InputFloat3("Normal", m_normal.data())) m_normal.normalize(); + // if (ImGui::InputFloat3("Up", m_up.data())) m_up.normalize(); + + ImVec2 input_size(-FLT_MIN, ImGui::GetTextLineHeight() * 6); + ImGuiInputTextFlags flags = + ImGuiInputTextFlags_::ImGuiInputTextFlags_AllowTabInput | + ImGuiInputTextFlags_::ImGuiInputTextFlags_AutoSelectAll + //| ImGuiInputTextFlags_::ImGuiInputTextFlags_CallbackResize + //|ImGuiInputTextFlags_::ImGuiInputTextFlags_CtrlEnterForNewLine + ; + + if (ImGui::InputTextMultiline("##Text", m_text.get(), m_text_size, + input_size, flags)) { + process(); + } + + // change text size + int max_text_size = static_cast(m_text_size); + if (ImGui::InputInt("max text size", &max_text_size, 8, 64)) { + set_max_text_size(static_cast(max_text_size)); + } + + // Option to create text volume when reselect volumes + m_imgui->disabled_begin(!m_font.has_value()); + if (m_volume == nullptr) { + if (ImGui::Button("Generate preview")) process(); + } + m_imgui->disabled_end(); +} + void GLGizmoEmboss::draw_font_list() { auto ¤t = m_font_list[m_font_selected]; @@ -623,28 +620,16 @@ void GLGizmoEmboss::add_fonts(const FontList &font_list) { sort_fonts(); } -bool GLGizmoEmboss::set_volume() -{ - ModelVolume *vol = get_selected_volume(); - // Is selected only one volume - if (vol == nullptr) return false; - - // Is volume created by Emboss? - if (!vol->text_configuration.has_value()) return false; - - // set selected volume - m_volume = vol; - load_configuration(*vol->text_configuration); - return true; -} - TextConfiguration GLGizmoEmboss::create_configuration() { std::string text((const char *) m_text.get()); return TextConfiguration(m_font_list[m_font_selected], m_font_prop, text); } -bool GLGizmoEmboss::load_configuration(const TextConfiguration &configuration) +bool GLGizmoEmboss::load_configuration(ModelVolume *volume) { + if (volume == nullptr) return false; + if (!volume->text_configuration.has_value()) return false; + const TextConfiguration &configuration = *volume->text_configuration; size_t index = m_font_list.size(); for (const auto &font_item : m_font_list) { if (font_item.type == configuration.font_item.type && @@ -670,6 +655,8 @@ bool GLGizmoEmboss::load_configuration(const TextConfiguration &configuration) m_font_prop = configuration.font_prop; set_text(configuration.text); + m_volume_type = volume->type(); // not neccesary + m_volume = volume; return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index ddc7b8290..01f93460c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -46,6 +46,7 @@ private: // create volume from text - main functionality bool process(); void close(); + void draw_window(); void draw_font_list(); void draw_add_button(); bool load_font(); @@ -59,11 +60,9 @@ private: void sort_fonts(); void add_fonts(const FontList &font_list); - bool set_volume(); - // Create object described how to make a Volume TextConfiguration create_configuration(); - bool load_configuration(const TextConfiguration& configuration); + bool load_configuration(ModelVolume *volume); std::string create_volume_name(); @@ -99,6 +98,8 @@ private: // actual volume ModelVolume *m_volume; + + // Only for new created volume ModelVolumeType m_volume_type; bool m_is_initialized; diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 0bed7eb74..e86b37a25 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -154,6 +154,7 @@ void KBShortcutsDialog::fill_shortcuts() { "L", L("Gizmo FDM paint-on supports") }, { "P", L("Gizmo FDM paint-on seam") }, { "N", L("Gizmo Multi Material painting") }, + { "T", L("Gizmo Emboss Text / edit text")}, { "Esc", L("Unselect gizmo or clear selection") }, { "K", L("Change camera type (perspective, orthographic)") }, { "B", L("Zoom to Bed") },