From 48f1be6ccce86fc1b02d279dd433cabb0c93d2db Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 31 Aug 2021 17:59:45 +0200 Subject: [PATCH] Add Loading fonts file Add visualization of national characters(wchar) --- src/libslic3r/Emboss.cpp | 18 ++- src/libslic3r/Emboss.hpp | 6 +- src/slic3r/GUI/GUI_App.cpp | 1 + src/slic3r/GUI/GUI_App.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 169 +++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 15 ++- 6 files changed, 154 insertions(+), 56 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index bc86d4515..62382db07 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -1,5 +1,6 @@ #include "Emboss.hpp" #include +#include #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation #include "imgui/imstb_truetype.h" // stbtt_fontinfo @@ -145,7 +146,8 @@ Polygons Emboss::letter2polygons(const Font &font, char letter) return glyph_opt->polygons; } -Polygons Emboss::text2polygons(const Font &font, const std::string &text) +#include +Polygons Emboss::text2polygons(const Font &font, const char *text) { auto font_info_opt = Privat::load_font_info(font); if (!font_info_opt.has_value()) return Polygons(); @@ -153,10 +155,16 @@ Polygons Emboss::text2polygons(const Font &font, const std::string &text) Point cursor(0, 0); Polygons result; - for (const char &letter : text) { - if (letter == '\0') break; - auto glyph_opt = Privat::get_glyph(*font_info_opt, (int) letter, font.flatness); + std::wstring ws = boost::nowide::widen(text); + for (wchar_t wc: ws){ + if (wc == '\n') { + cursor.x() = 0; + cursor.y() -= font.ascent - font.descent + font.linegap; + continue; + } + int unicode = static_cast(wc); + auto glyph_opt = Privat::get_glyph(*font_info_opt, unicode, font.flatness); if (!glyph_opt.has_value()) continue; // move glyph to cursor position @@ -323,7 +331,7 @@ void its_remove_edge_triangles(indexed_triangle_set &its) std::sort(rem.begin(), rem.end()); uint32_t offset = 0; for (uint32_t i : rem) { - its.indices.erase(its.indices.begin() + i - offset); + its.indices.erase(its.indices.begin() + (i - offset)); ++offset; } } diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index 069f2ae23..f96525026 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -55,7 +55,7 @@ public: /// convert letter into polygons /// /// Define fonts - /// Character to convert + /// One character to convert /// inner polygon ccw(outer cw) static Polygons letter2polygons(const Font &font, char letter); @@ -63,9 +63,9 @@ public: /// Convert text into polygons /// /// Define fonts - /// Character to convert + /// Characters to convert /// inner polygon ccw(outer cw) - static Polygons text2polygons(const Font &font, const std::string &text); + static Polygons text2polygons(const Font &font, const char *text); /// /// Project 2d point into space diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 102026a0c..ebc28c8cf 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -433,6 +433,7 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension) /* FT_GCODE */ "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC", /* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA", /* FT_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF", + /* FT_FONTS */ "Font files (*.ttf, *.ttc)|*.ttf;*.TTF;*.ttc;*.TTC", /* FT_GALLERY */ "Known files (*.stl, *.obj)|*.stl;*.STL;*.obj;*.OBJ", /* FT_INI */ "INI files (*.ini)|*.ini;*.INI", diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 1d8c2fcf7..e3b75e662 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -59,6 +59,7 @@ enum FileType FT_GCODE, FT_MODEL, FT_PROJECT, + FT_FONTS, FT_GALLERY, FT_INI, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index b6eb3be33..9e6b008f7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -7,7 +7,6 @@ #include "slic3r/GUI/Plater.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/Emboss.hpp" namespace Slic3r::GUI { @@ -15,14 +14,22 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D & parent, const std::string &icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) - , m_fonts({{"NotoSans Regular", - Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf"}, - {"Arial", "C:/windows/fonts/arialbd.ttf"}}) - , m_selected(1) - , m_text(_u8L("Embossed text")) + , m_fonts({ + {"NotoSans Regular", Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf"}, + {"NotoSans CJK", Slic3r::resources_dir() + "/fonts/NotoSansCJK-Regular.ttc"}, + //{"Font Awsome", Slic3r::resources_dir() + "/fonts/fa-solid-900.ttf"}, + {"Arial", "C:/windows/fonts/arialbd.ttf"}}) + , m_fonts_selected(0) + , m_text_size(255) + , m_text(new char[m_text_size]) + , m_scale(0.01f) + , m_emboss(5.f) { + load_font(); // TODO: suggest to use https://fontawesome.com/ - m_text.reserve(255); + int index = 0; + for (char &c : _u8L("Embossed text")) { m_text[index++] = c; } + m_text[index] = '\0'; } GLGizmoEmboss::~GLGizmoEmboss() {} @@ -47,64 +54,71 @@ 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); - ImGui::Text("welcome in emboss text"); - auto& current = m_fonts[m_selected]; + + auto& current = m_fonts[m_fonts_selected]; if (ImGui::BeginCombo("##font_selector", current.name.c_str())) { for (const MyFont &f : m_fonts) { ImGui::PushID((void*)&f.name); if (ImGui::Selectable(f.name.c_str(), &f == ¤t)) { - m_selected = &f - &m_fonts.front(); + m_fonts_selected = &f - &m_fonts.front(); + load_font(); } ImGui::PopID(); } ImGui::EndCombo(); } ImGui::SameLine(); - ImGui::Button("Add"); - static float scale = 0.01f; - ImGui::InputFloat("Scale", &scale); - static float emboss = 100.f; - ImGui::InputFloat("Emboss", &emboss); - - if(ImGui::Button("Preview")){ - - auto project = std::make_unique( - std::make_unique(emboss) - ,scale); - - auto font_path = m_fonts[m_selected].file_path.c_str(); - auto font_opt = Emboss::load_font(font_path); - if (font_opt.has_value()) { - Polygons polygons = Emboss::text2polygons(*font_opt, m_text); - indexed_triangle_set its = Emboss::polygons2model(polygons, *project); - // add object - TriangleMesh tm(its); - tm.repair(); - - tm.WriteOBJFile("text_preview.obj"); - - const Selection &selection = m_parent.get_selection(); - int object_idx = selection.get_object_idx(); - ModelObject *obj = wxGetApp().plater()->model().objects[object_idx]; - ModelVolume *v = obj->volumes.front(); - v->set_mesh(tm); - v->set_new_unique_id(); - obj->invalidate_bounding_box(); - m_parent.reload_scene(true); - } + m_imgui->disabled_begin(!m_font.has_value() || m_font->count == 1); + if (ImGui::BeginCombo("##font_collection_selector", std::to_string(m_font->index).c_str())) { + for (size_t i = 0; i < m_font->count; ++i) { + ImGui::PushID(1<<10 + i); + if (ImGui::Selectable(std::to_string(i).c_str(), i == m_font->index)) { + m_font->index = i; + } + ImGui::PopID(); + } + ImGui::EndCombo(); } + m_imgui->disabled_end(); + ImGui::SameLine(); + draw_add_button(); + + ImGui::InputFloat("Scale", &m_scale); + ImGui::InputFloat("Emboss", &m_emboss); + m_imgui->disabled_begin(!m_font.has_value()); + if (ImGui::Button("Preview")) process(); + m_imgui->disabled_end(); ImVec2 input_size(-FLT_MIN, ImGui::GetTextLineHeight() * 6); ImGuiInputTextFlags flags = ImGuiInputTextFlags_::ImGuiInputTextFlags_AllowTabInput - | ImGuiInputTextFlags_::ImGuiInputTextFlags_AutoSelectAll + | ImGuiInputTextFlags_::ImGuiInputTextFlags_AutoSelectAll + //| ImGuiInputTextFlags_::ImGuiInputTextFlags_CallbackResize //|ImGuiInputTextFlags_::ImGuiInputTextFlags_CtrlEnterForNewLine ; - if (ImGui::InputTextMultiline("##Text", (char *) m_text.c_str(), - m_text.capacity() + 1, input_size, flags)) { - + + + if (ImGui::InputTextMultiline("##Text", m_text.get(), m_text_size, input_size, flags)) { + process(); } + // change text size + int max_text_size = m_text_size; + if (ImGui::InputInt("max text size", &max_text_size, 8, 64)) { + if (max_text_size < 4) max_text_size = 4; + std::unique_ptr newData(new char[max_text_size]); + size_t index = 0; + while (index < max_text_size-1) { + if (m_text.get()[index] == '\0') break; + newData.get()[index] = m_text.get()[index]; + ++index; + } + newData.get()[index] = '\0'; + m_text = std::move(newData); + m_text_size = max_text_size; + } + + // draw 2d triangle in IMGUI ImVec2 t0(25, 25); ImVec2 t1(150, 5); ImVec2 t2(10, 100); @@ -142,9 +156,72 @@ void GLGizmoEmboss::on_set_state() } } +void GLGizmoEmboss::process() { + auto project = std::make_unique( + std::make_unique(m_emboss/m_scale), m_scale); + + Polygons polygons = Emboss::text2polygons(*m_font, m_text.get()); + if (polygons.empty()) return; + + indexed_triangle_set its = Emboss::polygons2model(polygons, *project); + if (its.indices.empty()) return; + + // add object + TriangleMesh tm(its); + tm.repair(); + + tm.WriteOBJFile("text_preview.obj"); + + const Selection &selection = m_parent.get_selection(); + int object_idx = selection.get_object_idx(); + ModelObject * obj = wxGetApp().plater()->model().objects[object_idx]; + ModelVolume * v = obj->volumes.front(); + v->set_mesh(tm); + v->set_new_unique_id(); + obj->invalidate_bounding_box(); + m_parent.reload_scene(true); +} + void GLGizmoEmboss::close() { // close gizmo == open it again GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager(); gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); } + +void GLGizmoEmboss::draw_add_button() { + if (ImGui::Button(_u8L("Add").c_str())) { + wxArrayString input_files; + wxString fontDir = wxEmptyString; + wxString selectedFile = wxEmptyString; + wxFileDialog dialog( + nullptr, _L("Choose one or more files (TTF, TTC):"), + fontDir, selectedFile, + file_wildcards(FT_FONTS), + wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() == wxID_OK) dialog.GetPaths(input_files); + if (input_files.IsEmpty()) return; + + for (auto &input_file : input_files) { + std::string name = input_file.AfterLast('\\').c_str(); + std::string path = input_file.c_str(); + m_fonts.emplace_back(name, path); + } + // set last added font as active + m_fonts_selected = m_fonts.size() - 1; + load_font(); + //load_files(input_files); + } + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::Text("add file with font(.ttf, .ttc)"); + ImGui::EndTooltip(); + } +} + +void GLGizmoEmboss::load_font() +{ + auto font_path = m_fonts[m_fonts_selected].file_path.c_str(); + m_font = Emboss::load_font(font_path); +} + } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 3f8b67ec6..51fd3e5a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -8,6 +8,8 @@ #include #include +#include "libslic3r/Emboss.hpp" + namespace Slic3r { class ModelVolume; namespace GUI { @@ -28,7 +30,10 @@ protected: virtual void on_set_state() override; private: + void process(); void close(); + void draw_add_button(); + void load_font(); // This configs holds GUI layout size given by translated texts. // etc. When language changes, GUI is recreated and this class constructed again, @@ -50,9 +55,15 @@ private: }; std::vector m_fonts; - size_t m_selected;// index to m_fonts + size_t m_fonts_selected;// index to m_fonts - std::string m_text; + std::optional m_font; + + size_t m_text_size; + std::unique_ptr m_text; + + float m_scale; + float m_emboss; }; } // namespace GUI