mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 12:55:59 +08:00
Choose font by wxWidgetDialog
This commit is contained in:
parent
578abe4cce
commit
9d52ceee44
@ -25,13 +25,7 @@ public:
|
|||||||
Privat() = delete;
|
Privat() = delete;
|
||||||
|
|
||||||
static std::optional<stbtt_fontinfo> load_font_info(const Emboss::Font &font);
|
static std::optional<stbtt_fontinfo> load_font_info(const Emboss::Font &font);
|
||||||
|
static std::optional<Emboss::Glyph> get_glyph(stbtt_fontinfo &font_info, int unicode_letter, float flatness = 2.f);
|
||||||
struct Glyph
|
|
||||||
{
|
|
||||||
Polygons polygons;
|
|
||||||
int advance_width, left_side_bearing;
|
|
||||||
};
|
|
||||||
static std::optional<Glyph> get_glyph(stbtt_fontinfo &font_info, int unicode_letter, float flatness = 2.f);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<stbtt_fontinfo> Privat::load_font_info(const Emboss::Font &font)
|
std::optional<stbtt_fontinfo> Privat::load_font_info(const Emboss::Font &font)
|
||||||
@ -49,7 +43,7 @@ std::optional<stbtt_fontinfo> Privat::load_font_info(const Emboss::Font &font)
|
|||||||
return font_info;
|
return font_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Privat::Glyph> Privat::get_glyph(stbtt_fontinfo &font_info, int unicode_letter, float flatness)
|
std::optional<Emboss::Glyph> Privat::get_glyph(stbtt_fontinfo &font_info, int unicode_letter, float flatness)
|
||||||
{
|
{
|
||||||
int glyph_index = stbtt_FindGlyphIndex(&font_info, unicode_letter);
|
int glyph_index = stbtt_FindGlyphIndex(&font_info, unicode_letter);
|
||||||
if (glyph_index == 0) {
|
if (glyph_index == 0) {
|
||||||
@ -58,7 +52,7 @@ std::optional<Privat::Glyph> Privat::get_glyph(stbtt_fontinfo &font_info, int un
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Privat::Glyph glyph;
|
Emboss::Glyph glyph;
|
||||||
stbtt_GetGlyphHMetrics(&font_info, glyph_index, &glyph.advance_width, &glyph.left_side_bearing);
|
stbtt_GetGlyphHMetrics(&font_info, glyph_index, &glyph.advance_width, &glyph.left_side_bearing);
|
||||||
|
|
||||||
stbtt_vertex *vertices;
|
stbtt_vertex *vertices;
|
||||||
@ -96,16 +90,15 @@ std::optional<Privat::Glyph> Privat::get_glyph(stbtt_fontinfo &font_info, int un
|
|||||||
|
|
||||||
// change outer cw to ccw and inner ccw to cw order
|
// change outer cw to ccw and inner ccw to cw order
|
||||||
std::reverse(pts.begin(), pts.end());
|
std::reverse(pts.begin(), pts.end());
|
||||||
|
|
||||||
glyph.polygons.emplace_back(pts);
|
glyph.polygons.emplace_back(pts);
|
||||||
}
|
}
|
||||||
|
// fix for bad defined fonts
|
||||||
|
glyph.polygons = union_(glyph.polygons);
|
||||||
// inner cw - hole
|
// inner cw - hole
|
||||||
// outer ccw - contour
|
// outer ccw - contour
|
||||||
return glyph;
|
return glyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <wingdi.h>
|
#include <wingdi.h>
|
||||||
@ -210,23 +203,46 @@ void choose_font_dlg() {
|
|||||||
|
|
||||||
void get_OS_font()
|
void get_OS_font()
|
||||||
{
|
{
|
||||||
HGDIOBJ g_hfFont = GetStockObject(DEFAULT_GUI_FONT);
|
|
||||||
LOGFONT lf;
|
LOGFONT lf;
|
||||||
|
HGDIOBJ g_hfFont = GetStockObject(DEFAULT_GUI_FONT);
|
||||||
GetObject(g_hfFont, sizeof(LOGFONT), &lf);
|
GetObject(g_hfFont, sizeof(LOGFONT), &lf);
|
||||||
std::wcout << "Default WIN font is " << (std::wstring) lf.lfFaceName
|
std::wcout << "DEFAULT_GUI_FONT is " << (std::wstring) lf.lfFaceName << std::endl;
|
||||||
<< std::endl;
|
|
||||||
|
g_hfFont = GetStockObject(OEM_FIXED_FONT);
|
||||||
|
GetObject(g_hfFont, sizeof(LOGFONT), &lf);
|
||||||
|
std::wcout << "OEM_FIXED_FONT is " << (std::wstring) lf.lfFaceName << std::endl;
|
||||||
|
|
||||||
|
g_hfFont = GetStockObject(ANSI_FIXED_FONT);
|
||||||
|
GetObject(g_hfFont, sizeof(LOGFONT), &lf);
|
||||||
|
std::wcout << "ANSI_FIXED_FONT is " << (std::wstring) lf.lfFaceName << std::endl;
|
||||||
|
|
||||||
|
g_hfFont = GetStockObject(ANSI_VAR_FONT);
|
||||||
|
GetObject(g_hfFont, sizeof(LOGFONT), &lf);
|
||||||
|
std::wcout << "ANSI_VAR_FONT is " << (std::wstring) lf.lfFaceName << std::endl;
|
||||||
|
|
||||||
|
g_hfFont = GetStockObject(SYSTEM_FONT);
|
||||||
|
GetObject(g_hfFont, sizeof(LOGFONT), &lf);
|
||||||
|
std::wcout << "SYSTEM_FONT is " << (std::wstring) lf.lfFaceName << std::endl;
|
||||||
|
|
||||||
|
g_hfFont = GetStockObject(DEVICE_DEFAULT_FONT);
|
||||||
|
GetObject(g_hfFont, sizeof(LOGFONT), &lf);
|
||||||
|
std::wcout << "DEVICE_DEFAULT_FONT is " << (std::wstring) lf.lfFaceName << std::endl;
|
||||||
|
|
||||||
|
g_hfFont = GetStockObject(SYSTEM_FIXED_FONT);
|
||||||
|
GetObject(g_hfFont, sizeof(LOGFONT), &lf);
|
||||||
|
std::wcout << "SYSTEM_FIXED_FONT is " << (std::wstring) lf.lfFaceName << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#include <wx/fontdlg.h>
|
|
||||||
|
|
||||||
Emboss::FontList Emboss::get_font_list()
|
Emboss::FontList Emboss::get_font_list()
|
||||||
{
|
{
|
||||||
//auto a = get_font_path(L"none");
|
//auto a = get_font_path(L"none");
|
||||||
//get_OS_font();
|
get_OS_font();
|
||||||
//choose_font_dlg();
|
//choose_font_dlg();
|
||||||
//FontList list1 = get_font_list_by_enumeration();
|
//FontList list1 = get_font_list_by_enumeration();
|
||||||
//FontList list2 = get_font_list_by_register();
|
//FontList list2 = get_font_list_by_register();
|
||||||
//FontList list3 = get_font_list_by_folder();
|
//FontList list3 = get_font_list_by_folder();
|
||||||
|
|
||||||
return get_font_list_by_register();
|
return get_font_list_by_register();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,9 +353,11 @@ Emboss::FontList Emboss::get_font_list_by_folder() {
|
|||||||
std::wstring search_dir = std::wstring(winDir, winDir_size) + L"\\Fonts\\";
|
std::wstring search_dir = std::wstring(winDir, winDir_size) + L"\\Fonts\\";
|
||||||
WIN32_FIND_DATA fd;
|
WIN32_FIND_DATA fd;
|
||||||
HANDLE hFind;
|
HANDLE hFind;
|
||||||
auto iterate_files = [&hFind, &fd, &search_dir, &result]() {
|
// By https://en.wikipedia.org/wiki/TrueType has also suffix .tte
|
||||||
if (hFind == INVALID_HANDLE_VALUE) return;
|
std::vector<std::wstring> suffixes = {L"*.ttf", L"*.ttc", L"*.tte"};
|
||||||
// read all (real) files in current folder
|
for (const std::wstring &suffix : suffixes) {
|
||||||
|
hFind = ::FindFirstFile((search_dir + suffix).c_str(), &fd);
|
||||||
|
if (hFind == INVALID_HANDLE_VALUE) continue;
|
||||||
do {
|
do {
|
||||||
// skip folder . and ..
|
// skip folder . and ..
|
||||||
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;
|
||||||
@ -348,12 +366,7 @@ Emboss::FontList Emboss::get_font_list_by_folder() {
|
|||||||
result.emplace_back(file_name, search_dir + file_name);
|
result.emplace_back(file_name, search_dir + file_name);
|
||||||
} while (::FindNextFile(hFind, &fd));
|
} while (::FindNextFile(hFind, &fd));
|
||||||
::FindClose(hFind);
|
::FindClose(hFind);
|
||||||
};
|
}
|
||||||
|
|
||||||
hFind = ::FindFirstFile((search_dir + L"*.ttf").c_str(), &fd);
|
|
||||||
iterate_files();
|
|
||||||
hFind = ::FindFirstFile((search_dir + L"*.ttc").c_str(), &fd);
|
|
||||||
iterate_files();
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,6 +379,34 @@ std::optional<std::wstring> Emboss::get_font_path(const std::wstring &font_face_
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
std::optional<Emboss::Font> Emboss::load_font(std::vector<unsigned char> data)
|
||||||
|
{
|
||||||
|
Font res;
|
||||||
|
res.buffer = std::move(data);
|
||||||
|
|
||||||
|
unsigned int index = 0;
|
||||||
|
int font_offset = 0;
|
||||||
|
while (font_offset >= 0) {
|
||||||
|
font_offset = stbtt_GetFontOffsetForIndex(res.buffer.data(), index++);
|
||||||
|
}
|
||||||
|
// at least one font must be inside collection
|
||||||
|
if (index < 1) {
|
||||||
|
std::cerr << "There is no font collection inside file.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
// select default font on index 0
|
||||||
|
res.index = 0;
|
||||||
|
res.count = index;
|
||||||
|
|
||||||
|
auto font_info = Privat::load_font_info(res);
|
||||||
|
if (!font_info.has_value()) return {};
|
||||||
|
|
||||||
|
// load information about line gap
|
||||||
|
stbtt_GetFontVMetrics(&(*font_info), &res.ascent, &res.descent,
|
||||||
|
&res.linegap);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<Emboss::Font> Emboss::load_font(const char *file_path)
|
std::optional<Emboss::Font> Emboss::load_font(const char *file_path)
|
||||||
{
|
{
|
||||||
FILE *file = fopen(file_path, "rb");
|
FILE *file = fopen(file_path, "rb");
|
||||||
@ -386,51 +427,62 @@ std::optional<Emboss::Font> Emboss::load_font(const char *file_path)
|
|||||||
}
|
}
|
||||||
rewind(file);
|
rewind(file);
|
||||||
|
|
||||||
Font res;
|
std::vector<unsigned char> buffer(size);
|
||||||
res.buffer = std::vector<unsigned char>(size);
|
size_t count_loaded_bytes = fread((void *) &buffer.front(), 1, size, file);
|
||||||
size_t count_loaded_bytes = fread((void *) &res.buffer.front(), 1, size, file);
|
if (count_loaded_bytes != size) {
|
||||||
|
std::cerr << "Different loaded(from file) data size.";
|
||||||
unsigned int index = 0;
|
|
||||||
int font_offset = 0;
|
|
||||||
while (font_offset >= 0) {
|
|
||||||
font_offset = stbtt_GetFontOffsetForIndex(res.buffer.data(), index++);
|
|
||||||
}
|
|
||||||
// at least one font must be inside collection
|
|
||||||
if (index < 1) {
|
|
||||||
std::cerr << "There is no font collection inside file.";
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
// select default font on index 0
|
return load_font(std::move(buffer));
|
||||||
res.index = 0;
|
|
||||||
res.count = index;
|
|
||||||
|
|
||||||
auto font_info = Privat::load_font_info(res);
|
|
||||||
if (!font_info.has_value()) return {};
|
|
||||||
|
|
||||||
// load information about line gap
|
|
||||||
stbtt_GetFontVMetrics(&(*font_info), &res.ascent, &res.descent, &res.linegap);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygons Emboss::letter2polygons(const Font &font, char letter, float flatness)
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::optional<Emboss::Font> Emboss::load_font(HFONT hfont)
|
||||||
|
{
|
||||||
|
HDC hdc = ::CreateCompatibleDC(NULL);
|
||||||
|
if (hdc == NULL) {
|
||||||
|
std::cerr << "Can't create HDC by CreateCompatibleDC(NULL).";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
::SelectObject(hdc, hfont);
|
||||||
|
size_t size = ::GetFontData(hdc, 0, 0, NULL, 0);
|
||||||
|
if (size == 0) {
|
||||||
|
std::cerr << "HFONT doesn't have size.";
|
||||||
|
::DeleteDC(hdc);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> buffer(size);
|
||||||
|
size_t loaded_size = ::GetFontData(hdc, 0, 0, buffer.data(), size);
|
||||||
|
::DeleteDC(hdc);
|
||||||
|
|
||||||
|
if (size != loaded_size) {
|
||||||
|
std::cerr << "Different loaded(from HFONT) data size.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return load_font(std::move(buffer));
|
||||||
|
}
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
std::optional<Emboss::Glyph> Emboss::letter2glyph(const Font &font,
|
||||||
|
int letter,
|
||||||
|
float flatness)
|
||||||
{
|
{
|
||||||
auto font_info_opt = Privat::load_font_info(font);
|
auto font_info_opt = Privat::load_font_info(font);
|
||||||
if (!font_info_opt.has_value()) return Polygons();
|
if (!font_info_opt.has_value()) return {};
|
||||||
stbtt_fontinfo *font_info = &(*font_info_opt);
|
stbtt_fontinfo *font_info = &(*font_info_opt);
|
||||||
|
return Privat::get_glyph(*font_info_opt, (int) letter, flatness);
|
||||||
auto glyph_opt = Privat::get_glyph(*font_info_opt, (int) letter, flatness);
|
|
||||||
if (!glyph_opt.has_value()) return Polygons();
|
|
||||||
|
|
||||||
return union_(glyph_opt->polygons);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygons Emboss::text2polygons(const Font & font,
|
Polygons Emboss::text2polygons(const Font & font,
|
||||||
const char * text,
|
const char * text,
|
||||||
const FontProp &font_prop)
|
const FontProp &font_prop,
|
||||||
|
Glyphs & cache)
|
||||||
{
|
{
|
||||||
auto font_info_opt = Privat::load_font_info(font);
|
std::optional<stbtt_fontinfo> font_info_opt;
|
||||||
if (!font_info_opt.has_value()) return Polygons();
|
|
||||||
stbtt_fontinfo *font_info = &(*font_info_opt);
|
|
||||||
|
|
||||||
Point cursor(0, 0);
|
Point cursor(0, 0);
|
||||||
Polygons result;
|
Polygons result;
|
||||||
@ -443,16 +495,27 @@ Polygons Emboss::text2polygons(const Font & font,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int unicode = static_cast<int>(wc);
|
int unicode = static_cast<int>(wc);
|
||||||
auto glyph_opt = Privat::get_glyph(*font_info_opt, unicode, font_prop.flatness);
|
std::optional<Glyph> glyph_opt;
|
||||||
if (!glyph_opt.has_value()) continue;
|
auto glyph_item = cache.find(unicode);
|
||||||
|
if (glyph_item != cache.end()) glyph_opt = glyph_item->second;
|
||||||
|
else {
|
||||||
|
if (!font_info_opt.has_value()) {
|
||||||
|
font_info_opt = Privat::load_font_info(font);
|
||||||
|
// can load font info?
|
||||||
|
if (!font_info_opt.has_value()) return Polygons();
|
||||||
|
}
|
||||||
|
glyph_opt = Privat::get_glyph(*font_info_opt, unicode,
|
||||||
|
font_prop.flatness);
|
||||||
|
// has definition inside of font?
|
||||||
|
if (!glyph_opt.has_value()) continue;
|
||||||
|
cache[unicode] = *glyph_opt;
|
||||||
|
}
|
||||||
|
|
||||||
// move glyph to cursor position
|
// move glyph to cursor position
|
||||||
Polygons polygons = glyph_opt->polygons; // copy
|
Polygons polygons = glyph_opt->polygons; // copy
|
||||||
for (Polygon &polygon : polygons)
|
for (Polygon &polygon : polygons)
|
||||||
for (Point &p : polygon.points) p += cursor;
|
polygon.translate(cursor);
|
||||||
|
|
||||||
cursor.x() += glyph_opt->advance_width + font_prop.char_gap;
|
cursor.x() += glyph_opt->advance_width + font_prop.char_gap;
|
||||||
|
|
||||||
polygons_append(result, polygons);
|
polygons_append(result, polygons);
|
||||||
}
|
}
|
||||||
return union_(result);
|
return union_(result);
|
||||||
|
@ -75,30 +75,49 @@ public:
|
|||||||
FontProp() = default;
|
FontProp() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// description of one letter
|
||||||
|
struct Glyph
|
||||||
|
{
|
||||||
|
Polygons polygons;
|
||||||
|
int advance_width, left_side_bearing;
|
||||||
|
};
|
||||||
|
// cache for glyph by unicode
|
||||||
|
using Glyphs = std::map<int, Glyph>;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Load font file into buffer
|
/// Load font file into buffer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="file_path">Location of .ttf or .ttc font file</param>
|
/// <param name="file_path">Location of .ttf or .ttc font file</param>
|
||||||
/// <returns>Font object when loaded.</returns>
|
/// <returns>Font object when loaded.</returns>
|
||||||
static std::optional<Font> load_font(const char *file_path);
|
static std::optional<Font> load_font(const char *file_path);
|
||||||
|
static std::optional<Font> load_font(std::vector<unsigned char> data);
|
||||||
|
#ifdef _WIN32
|
||||||
|
// fix for unknown pointer HFONT
|
||||||
|
using HFONT = void*;
|
||||||
|
static std::optional<Font> load_font(HFONT hfont);
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// convert letter into polygons
|
/// convert letter into polygons
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="font">Define fonts</param>
|
/// <param name="font">Define fonts</param>
|
||||||
/// <param name="letter">One character to convert</param>
|
/// <param name="letter">One character defined by unicode codepoint</param>
|
||||||
/// <param name="flatness">Precision of lettter outline curve in conversion to lines</param>
|
/// <param name="flatness">Precision of lettter outline curve in conversion to lines</param>
|
||||||
/// <returns>inner polygon cw(outer ccw)</returns>
|
/// <returns>inner polygon cw(outer ccw)</returns>
|
||||||
static Polygons letter2polygons(const Font &font, char letter, float flatness);
|
static std::optional<Glyph> letter2glyph(const Font &font, int letter, float flatness);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert text into polygons
|
/// Convert text into polygons
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="font">Define fonts</param>
|
/// <param name="font">Define fonts</param>
|
||||||
/// <param name="text">Characters to convert</param>
|
/// <param name="text">Characters to convert</param>
|
||||||
/// <param name="font_prop">User defined property of font</param>
|
/// <param name="font_prop">User defined property of the font</param>
|
||||||
|
/// <param name="cache">Cache for letter polygons</param>
|
||||||
/// <returns>Inner polygon cw(outer ccw)</returns>
|
/// <returns>Inner polygon cw(outer ccw)</returns>
|
||||||
static Polygons text2polygons(const Font &font, const char *text, const FontProp& font_prop);
|
static Polygons text2polygons(const Font & font,
|
||||||
|
const char * text,
|
||||||
|
const FontProp &font_prop,
|
||||||
|
Glyphs & cache = Glyphs());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Project 2d point into space
|
/// Project 2d point into space
|
||||||
|
@ -433,7 +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_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_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_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF",
|
||||||
/* FT_FONTS */ "Font files (*.ttf, *.ttc)|*.ttf;*.TTF;*.ttc;*.TTC",
|
/* FT_FONTS */ "Font files (*.ttf, *.tte, *.ttc, *.dfont)|*.ttf;*.tte;*.ttc;*.dfont;*.TTF;*.TTE;*.TTC;*.DFONT",
|
||||||
/* FT_GALLERY */ "Known files (*.stl, *.obj)|*.stl;*.STL;*.obj;*.OBJ",
|
/* FT_GALLERY */ "Known files (*.stl, *.obj)|*.stl;*.STL;*.obj;*.OBJ",
|
||||||
|
|
||||||
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
|
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
|
|
||||||
|
#include <wx/font.h>
|
||||||
|
#include <wx/fontdlg.h>
|
||||||
|
|
||||||
namespace Slic3r::GUI {
|
namespace Slic3r::GUI {
|
||||||
|
|
||||||
GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D & parent,
|
GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D & parent,
|
||||||
@ -22,6 +25,7 @@ GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D & parent,
|
|||||||
, m_text(new char[m_text_size])
|
, m_text(new char[m_text_size])
|
||||||
, m_scale(0.01f)
|
, m_scale(0.01f)
|
||||||
, m_emboss(5.f)
|
, m_emboss(5.f)
|
||||||
|
, m_volume(nullptr)
|
||||||
{
|
{
|
||||||
// TODO: suggest to use https://fontawesome.com/
|
// TODO: suggest to use https://fontawesome.com/
|
||||||
// (copy & paste) unicode symbols from web
|
// (copy & paste) unicode symbols from web
|
||||||
@ -63,18 +67,17 @@ void GLGizmoEmboss::on_render_for_picking() {}
|
|||||||
|
|
||||||
void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
|
void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
|
||||||
{
|
{
|
||||||
|
if (!m_gui_cfg.has_value()) m_gui_cfg.emplace(GuiCfg());
|
||||||
|
|
||||||
int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
|
int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
|
||||||
ImGuiWindowFlags_NoCollapse;
|
ImGuiWindowFlags_NoCollapse;
|
||||||
m_imgui->begin(on_get_name(), flag);
|
m_imgui->begin(on_get_name(), flag);
|
||||||
|
|
||||||
size_t max_font_name = 20; // count characters
|
|
||||||
|
|
||||||
auto& current = m_font_list[m_font_selected];
|
auto& current = m_font_list[m_font_selected];
|
||||||
if (ImGui::BeginCombo("##font_selector", current.name.c_str())) {
|
if (ImGui::BeginCombo("##font_selector", current.name.c_str())) {
|
||||||
for (const Emboss::FontItem &f : m_font_list) {
|
for (const Emboss::FontItem &f : m_font_list) {
|
||||||
ImGui::PushID((void*)&f.name);
|
ImGui::PushID((void*)&f.name);
|
||||||
std::string name = (f.name.size() < max_font_name) ?
|
std::string name = (f.name.size() < m_gui_cfg->max_font_name) ?
|
||||||
f.name : (f.name.substr(0,max_font_name - 3) + " ..");
|
f.name : (f.name.substr(0,m_gui_cfg->max_font_name - 3) + " ..");
|
||||||
if (ImGui::Selectable(name.c_str(), &f == ¤t)) {
|
if (ImGui::Selectable(name.c_str(), &f == ¤t)) {
|
||||||
m_font_selected = &f - &m_font_list.front();
|
m_font_selected = &f - &m_font_list.front();
|
||||||
load_font();
|
load_font();
|
||||||
@ -90,28 +93,52 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
|
|||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
m_imgui->disabled_begin(!m_font.has_value() || m_font->count == 1);
|
if (m_font.has_value()) {
|
||||||
if (ImGui::BeginCombo("##font_collection_selector", std::to_string(m_font->index).c_str())) {
|
if (ImGui::BeginCombo("##font_collection_selector", std::to_string(m_font->index).c_str())) {
|
||||||
for (size_t i = 0; i < m_font->count; ++i) {
|
for (size_t i = 0; i < m_font->count; ++i) {
|
||||||
ImGui::PushID(1<<10 + i);
|
ImGui::PushID(1 << 10 + i);
|
||||||
if (ImGui::Selectable(std::to_string(i).c_str(), i == m_font->index)) {
|
if (ImGui::Selectable(std::to_string(i).c_str(),
|
||||||
m_font->index = i;
|
i == m_font->index)) {
|
||||||
|
m_font->index = i;
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
ImGui::EndCombo();
|
|
||||||
}
|
}
|
||||||
m_imgui->disabled_end();
|
|
||||||
|
static std::string fontName;
|
||||||
|
if (ImGui::Button(_L("choose font").c_str())) {
|
||||||
|
static wxFontData data; // keep last selected font
|
||||||
|
wxFontDialog font_dialog(nullptr, data);
|
||||||
|
font_dialog.SetTitle(_L("Select font FFF"));
|
||||||
|
if (font_dialog.ShowModal() == wxID_OK) {
|
||||||
|
data = font_dialog.GetFontData();
|
||||||
|
wxFont font = data.GetChosenFont();
|
||||||
|
fontName = boost::nowide::narrow(font.GetFaceName());
|
||||||
|
m_font = Emboss::load_font(font.GetHFONT());
|
||||||
|
m_font_glyph_cache.clear();
|
||||||
|
process();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fontName.empty()) ImGui::Text(fontName.c_str());
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
draw_add_button();
|
draw_add_button();
|
||||||
|
|
||||||
ImGui::InputFloat("Scale", &m_scale);
|
ImGui::InputFloat("Scale", &m_scale);
|
||||||
ImGui::InputFloat("Emboss", &m_emboss);
|
ImGui::InputFloat("Emboss", &m_emboss);
|
||||||
ImGui::InputFloat("Flatness", &m_font_prop.flatness);
|
if (ImGui::InputFloat("Flatness", &m_font_prop.flatness))
|
||||||
|
m_font_glyph_cache.clear();
|
||||||
ImGui::InputInt("CharGap", &m_font_prop.char_gap);
|
ImGui::InputInt("CharGap", &m_font_prop.char_gap);
|
||||||
ImGui::InputInt("LineGap", &m_font_prop.line_gap);
|
ImGui::InputInt("LineGap", &m_font_prop.line_gap);
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
m_imgui->disabled_begin(!m_font.has_value());
|
m_imgui->disabled_begin(!m_font.has_value());
|
||||||
if (ImGui::Button("Preview")) process();
|
if (ImGui::Button("Preview")) process();
|
||||||
m_imgui->disabled_end();
|
m_imgui->disabled_end();
|
||||||
@ -129,12 +156,12 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// change text size
|
// change text size
|
||||||
int max_text_size = m_text_size;
|
int max_text_size = static_cast<int>(m_text_size);
|
||||||
if (ImGui::InputInt("max text size", &max_text_size, 8, 64)) {
|
if (ImGui::InputInt("max text size", &max_text_size, 8, 64)) {
|
||||||
if (max_text_size < 4) max_text_size = 4;
|
if (max_text_size < 4) max_text_size = 4;
|
||||||
std::unique_ptr<char[]> newData(new char[max_text_size]);
|
std::unique_ptr<char[]> newData(new char[max_text_size]);
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
while (index < max_text_size-1) {
|
while ((index+1) < max_text_size) {
|
||||||
if (m_text.get()[index] == '\0') break;
|
if (m_text.get()[index] == '\0') break;
|
||||||
newData.get()[index] = m_text.get()[index];
|
newData.get()[index] = m_text.get()[index];
|
||||||
++index;
|
++index;
|
||||||
@ -165,7 +192,7 @@ void GLGizmoEmboss::on_set_state()
|
|||||||
// Closing gizmo. e.g. selecting another one
|
// Closing gizmo. e.g. selecting another one
|
||||||
if (GLGizmoBase::m_state == GLGizmoBase::Off) {
|
if (GLGizmoBase::m_state == GLGizmoBase::Off) {
|
||||||
|
|
||||||
// refuse outgoing during simlification
|
// refuse outgoing during text preview
|
||||||
if (false) {
|
if (false) {
|
||||||
GLGizmoBase::m_state = GLGizmoBase::On;
|
GLGizmoBase::m_state = GLGizmoBase::On;
|
||||||
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
||||||
@ -175,36 +202,115 @@ void GLGizmoEmboss::on_set_state()
|
|||||||
_u8L("ERROR: Wait until ends or Cancel process."));
|
_u8L("ERROR: Wait until ends or Cancel process."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_volume = nullptr;
|
||||||
} else if (GLGizmoBase::m_state == GLGizmoBase::On) {
|
} else if (GLGizmoBase::m_state == GLGizmoBase::On) {
|
||||||
// when open by hyperlink it needs to show up
|
// when open by hyperlink it needs to show up
|
||||||
//request_rerender();
|
//request_rerender();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoEmboss::process() {
|
// IMPROVE: Do not use gizmo_event - especialy smth with prefix SLA,
|
||||||
auto project = std::make_unique<Emboss::ProjectScale>(
|
// use Bind into wxGLCanvas?
|
||||||
std::make_unique<Emboss::ProjectZ>(m_emboss/m_scale), m_scale);
|
bool GLGizmoEmboss::gizmo_event(SLAGizmoEventType action,
|
||||||
|
const Vec2d & mouse_position,
|
||||||
|
bool shift_down,
|
||||||
|
bool alt_down,
|
||||||
|
bool control_down)
|
||||||
|
{
|
||||||
|
/* if (action == SLAGizmoEventType::LeftUp) {
|
||||||
|
const Camera & camera = wxGetApp().plater()->get_camera();
|
||||||
|
const Selection & selection = m_parent.get_selection();
|
||||||
|
const ModelObject * mo = m_c->selection_info()->model_object();
|
||||||
|
const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
|
||||||
|
const Transform3d & instance_trafo = mi->get_transformation()
|
||||||
|
.get_matrix();
|
||||||
|
|
||||||
Polygons polygons = Emboss::text2polygons(*m_font, m_text.get(), m_font_prop);
|
// Precalculate transformations of individual meshes.
|
||||||
|
std::vector<Transform3d> trafo_matrices;
|
||||||
|
for (const ModelVolume *mv : mo->volumes)
|
||||||
|
if (mv->is_model_part())
|
||||||
|
trafo_matrices.emplace_back(instance_trafo * mv->get_matrix());
|
||||||
|
|
||||||
|
Vec3f normal = Vec3f::Zero();
|
||||||
|
Vec3f hit = Vec3f::Zero();
|
||||||
|
size_t facet = 0;
|
||||||
|
Vec3f closest_hit = Vec3f::Zero();
|
||||||
|
double closest_hit_squared_distance =
|
||||||
|
std::numeric_limits<double>::max();
|
||||||
|
size_t closest_facet = 0;
|
||||||
|
int closest_hit_mesh_id = -1;
|
||||||
|
|
||||||
|
// Cast a ray on all meshes, pick the closest hit and save it for the
|
||||||
|
// respective mesh
|
||||||
|
for (int mesh_id = 0; mesh_id < int(trafo_matrices.size());
|
||||||
|
++mesh_id) {
|
||||||
|
if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(
|
||||||
|
mouse_position, trafo_matrices[mesh_id], camera, hit,
|
||||||
|
normal, m_c->object_clipper()->get_clipping_plane(),
|
||||||
|
&facet)) {
|
||||||
|
|
||||||
|
// In case this hit is clipped, skip it.
|
||||||
|
|
||||||
|
// Is this hit the closest to the camera so far?
|
||||||
|
double hit_squared_distance = (camera.get_position() -
|
||||||
|
trafo_matrices[mesh_id] *
|
||||||
|
hit.cast<double>())
|
||||||
|
.squaredNorm();
|
||||||
|
if (hit_squared_distance < closest_hit_squared_distance) {
|
||||||
|
closest_hit_squared_distance = hit_squared_distance;
|
||||||
|
closest_facet = facet;
|
||||||
|
closest_hit_mesh_id = mesh_id;
|
||||||
|
closest_hit = hit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get intersection
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLGizmoEmboss::process() {
|
||||||
|
if (!m_font.has_value()) return;
|
||||||
|
|
||||||
|
Polygons polygons = Emboss::text2polygons(*m_font, m_text.get(), m_font_prop, m_font_glyph_cache);
|
||||||
if (polygons.empty()) return;
|
if (polygons.empty()) return;
|
||||||
|
|
||||||
|
auto project = std::make_unique<Emboss::ProjectScale>(
|
||||||
|
std::make_unique<Emboss::ProjectZ>(m_emboss/m_scale), m_scale);
|
||||||
indexed_triangle_set its = Emboss::polygons2model(polygons, *project);
|
indexed_triangle_set its = Emboss::polygons2model(polygons, *project);
|
||||||
if (its.indices.empty()) return;
|
if (its.indices.empty()) return;
|
||||||
|
|
||||||
// add object
|
// add object
|
||||||
TriangleMesh tm(its);
|
TriangleMesh tm(std::move(its));
|
||||||
tm.repair();
|
tm.repair();
|
||||||
|
|
||||||
tm.WriteOBJFile("text_preview.obj");
|
//tm.WriteOBJFile("text_preview.obj");
|
||||||
|
|
||||||
const Selection &selection = m_parent.get_selection();
|
const Selection &selection = m_parent.get_selection();
|
||||||
int object_idx = selection.get_object_idx();
|
int object_idx = selection.get_object_idx();
|
||||||
ModelObject * obj = wxGetApp().plater()->model().objects[object_idx];
|
ModelObject * obj = wxGetApp().plater()->model().objects[object_idx];
|
||||||
ModelVolume * v = obj->volumes.front();
|
|
||||||
v->set_mesh(tm);
|
//if (m_volume != nullptr) {
|
||||||
v->set_new_unique_id();
|
// // TODO: fix index of m_volume
|
||||||
obj->invalidate_bounding_box();
|
// size_t m_volume_index = obj->volumes.size() - 1;
|
||||||
|
// obj->delete_volume(m_volume_index);
|
||||||
|
//}
|
||||||
|
//m_volume = obj->add_volume(std::move(tm), ModelVolumeType::MODEL_PART);
|
||||||
|
|
||||||
|
if (m_volume == nullptr) {
|
||||||
|
m_volume = obj->add_volume(std::move(tm), ModelVolumeType::MODEL_PART);
|
||||||
|
} else {
|
||||||
|
m_volume->set_mesh(std::move(tm));
|
||||||
|
m_volume->set_new_unique_id();
|
||||||
|
m_volume->translate(-m_volume->source.mesh_offset);
|
||||||
|
|
||||||
|
m_volume->center_geometry_after_creation(true);
|
||||||
|
m_volume->calculate_convex_hull();
|
||||||
|
m_volume->get_object()->invalidate_bounding_box();
|
||||||
|
}
|
||||||
m_parent.reload_scene(true);
|
m_parent.reload_scene(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,7 +341,7 @@ void GLGizmoEmboss::draw_add_button() {
|
|||||||
font_list.emplace_back(name, path);
|
font_list.emplace_back(name, path);
|
||||||
}
|
}
|
||||||
// set last added font as active
|
// set last added font as active
|
||||||
m_font_selected = m_font_list.size() - 1;
|
m_font_selected = m_font_list.size() + font_list.size() - 1;
|
||||||
add_fonts(font_list);
|
add_fonts(font_list);
|
||||||
load_font();
|
load_font();
|
||||||
}
|
}
|
||||||
@ -250,6 +356,7 @@ bool GLGizmoEmboss::load_font()
|
|||||||
{
|
{
|
||||||
auto font_path = m_font_list[m_font_selected].path.c_str();
|
auto font_path = m_font_list[m_font_selected].path.c_str();
|
||||||
m_font = Emboss::load_font(font_path);
|
m_font = Emboss::load_font(font_path);
|
||||||
|
m_font_glyph_cache.clear();
|
||||||
return m_font.has_value();
|
return m_font.has_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,5 +386,4 @@ void GLGizmoEmboss::add_fonts(const Emboss::FontList &font_list) {
|
|||||||
sort_fonts();
|
sort_fonts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Slic3r::GUI
|
} // namespace Slic3r::GUI
|
||||||
|
@ -4,11 +4,14 @@
|
|||||||
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code,
|
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code,
|
||||||
// which overrides our localization "L" macro.
|
// which overrides our localization "L" macro.
|
||||||
#include "GLGizmoBase.hpp"
|
#include "GLGizmoBase.hpp"
|
||||||
|
#include "GLGizmosCommon.hpp"
|
||||||
|
|
||||||
#include "admesh/stl.h" // indexed_triangle_set
|
#include "admesh/stl.h" // indexed_triangle_set
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "libslic3r/Emboss.hpp"
|
#include "libslic3r/Emboss.hpp"
|
||||||
|
#include "libslic3r/Point.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
class ModelVolume;
|
class ModelVolume;
|
||||||
@ -19,6 +22,8 @@ class GLGizmoEmboss : public GLGizmoBase
|
|||||||
public:
|
public:
|
||||||
GLGizmoEmboss(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
GLGizmoEmboss(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||||
virtual ~GLGizmoEmboss();
|
virtual ~GLGizmoEmboss();
|
||||||
|
// pseudo virtual function, no inheritance
|
||||||
|
virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
||||||
protected:
|
protected:
|
||||||
virtual bool on_init() override;
|
virtual bool on_init() override;
|
||||||
virtual std::string on_get_name() const override;
|
virtual std::string on_get_name() const override;
|
||||||
@ -42,7 +47,8 @@ private:
|
|||||||
// so the change takes effect. (info by GLGizmoFdmSupports.hpp)
|
// so the change takes effect. (info by GLGizmoFdmSupports.hpp)
|
||||||
struct GuiCfg
|
struct GuiCfg
|
||||||
{
|
{
|
||||||
|
const size_t max_font_name = 20; // count characters
|
||||||
|
GuiCfg() = default;
|
||||||
};
|
};
|
||||||
std::optional<GuiCfg> m_gui_cfg;
|
std::optional<GuiCfg> m_gui_cfg;
|
||||||
|
|
||||||
@ -50,14 +56,28 @@ private:
|
|||||||
size_t m_font_selected;// index to m_font_list
|
size_t m_font_selected;// index to m_font_list
|
||||||
|
|
||||||
std::optional<Emboss::Font> m_font;
|
std::optional<Emboss::Font> m_font;
|
||||||
|
Emboss::Glyphs m_font_glyph_cache;
|
||||||
|
|
||||||
size_t m_text_size;
|
size_t m_text_size;
|
||||||
std::unique_ptr<char[]> m_text;
|
std::unique_ptr<char[]> m_text;
|
||||||
|
|
||||||
Emboss::FontProp m_font_prop;
|
Emboss::FontProp m_font_prop;
|
||||||
|
|
||||||
|
// text position
|
||||||
|
struct Orientation
|
||||||
|
{
|
||||||
|
Vec3f origin = Vec3f(0.f, 0.f, 0.f);
|
||||||
|
Vec3f normal = Vec3f(0.f, 0.f, 1.f);
|
||||||
|
Vec3f up = Vec3f(0.f, 1.f, 0.f);
|
||||||
|
Orientation() = default;
|
||||||
|
};
|
||||||
|
Orientation m_orientation;
|
||||||
|
|
||||||
float m_scale;
|
float m_scale;
|
||||||
float m_emboss;
|
float m_emboss;
|
||||||
|
|
||||||
|
// actual volume
|
||||||
|
ModelVolume *m_volume;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
|
@ -54,7 +54,7 @@ TEST_CASE("Add TriangleMeshes", "[MeshBoolean]")
|
|||||||
Polygons ttf2polygons(const char * font_name, char letter, float flatness = 1.f) {
|
Polygons ttf2polygons(const char * font_name, char letter, float flatness = 1.f) {
|
||||||
auto font = Emboss::load_font(font_name);
|
auto font = Emboss::load_font(font_name);
|
||||||
if (!font.has_value()) return Polygons();
|
if (!font.has_value()) return Polygons();
|
||||||
return Emboss::letter2polygons(*font, letter, flatness);
|
return Emboss::letter2glyph(*font, letter, flatness)->polygons;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "libslic3r/SVG.hpp"
|
#include "libslic3r/SVG.hpp"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user