Add boldness and skew(italic) for embossed text

Change line_gap and char_gap to optional value
This commit is contained in:
Filip Sykala 2022-01-05 16:56:20 +01:00
parent 7b70083c78
commit 82ee1c5e4a
6 changed files with 177 additions and 15 deletions

View File

@ -9,6 +9,9 @@
#include <Triangulation.hpp> // CGAL project
#include "libslic3r.h"
#include "ClipperUtils.hpp" // for boldness - polygon extend(offset)
using namespace Slic3r;
double Emboss::SHAPE_SCALE = 0.001;//SCALING_FACTOR;
@ -541,8 +544,11 @@ ExPolygons Emboss::text2shapes(Font & font,
std::wstring ws = boost::nowide::widen(text);
for (wchar_t wc: ws){
if (wc == '\n') {
int line_height = font.ascent - font.descent + font.linegap;
if (font_prop.line_gap.has_value())
line_height += *font_prop.line_gap;
cursor.x() = 0;
cursor.y() -= (font.ascent - font.descent + font.linegap + font_prop.line_gap) / SHAPE_SCALE;
cursor.y() -= static_cast<int>(line_height / SHAPE_SCALE);
continue;
}
if (wc == '\t') {
@ -550,7 +556,10 @@ ExPolygons Emboss::text2shapes(Font & font,
const int count_spaces = 4;
std::optional<Glyph> space_opt = Private::get_glyph(int(' '), font, font_prop, font.cache, font_info_opt);
if (!space_opt.has_value()) continue;
cursor.x() += (count_spaces *(space_opt->advance_width + font_prop.char_gap)) / SHAPE_SCALE;
int width = space_opt->advance_width;
if (font_prop.char_gap.has_value())
width += *font_prop.char_gap;
cursor.x() += static_cast<int>((count_spaces * width) / SHAPE_SCALE);
continue;
}
@ -562,7 +571,30 @@ ExPolygons Emboss::text2shapes(Font & font,
ExPolygons expolygons = glyph_opt->shape; // copy
for (ExPolygon &expolygon : expolygons)
expolygon.translate(cursor);
cursor.x() += (glyph_opt->advance_width + font_prop.char_gap) / SHAPE_SCALE;
if (font_prop.boldness.has_value()) {
float delta = *font_prop.boldness / SHAPE_SCALE;
expolygons = offset_ex(expolygons, delta);
}
if (font_prop.skew.has_value()) {
const float& ratio = *font_prop.skew;
auto skew = [&ratio](Polygon &polygon) {
for (Point &p : polygon.points) {
p.x() += p.y() * ratio;
}
};
for (ExPolygon &expolygon : expolygons) {
skew(expolygon.contour);
for (Polygon &hole : expolygon.holes)
skew(hole);
}
}
int width = glyph_opt->advance_width;
if (font_prop.char_gap.has_value())
width += *font_prop.char_gap;
cursor.x() += static_cast<int>(width / SHAPE_SCALE);
expolygons_append(result, expolygons);
}
result = Slic3r::union_ex(result);

View File

@ -156,6 +156,8 @@ static constexpr const char *CHAR_GAP_ATTR = "char_gap";
static constexpr const char *LINE_GAP_ATTR = "line_gap";
static constexpr const char *LINE_HEIGHT_ATTR = "line_height";
static constexpr const char *DEPTH_ATTR = "depth";
static constexpr const char *BOLDNESS_ATTR = "boldness";
static constexpr const char *SKEW_ATTR = "skew";
static constexpr const char *FONT_FAMILY_ATTR = "family";
static constexpr const char *FONT_FACE_NAME_ATTR = "face_name";
@ -3254,10 +3256,18 @@ void TextConfigurationSerialization::to_xml(std::stringstream &stream, const Tex
// font property
const FontProp &fp = tc.font_prop;
stream << CHAR_GAP_ATTR << "=\"" << fp.char_gap << "\" ";
stream << LINE_GAP_ATTR << "=\"" << fp.line_gap << "\" ";
if (fp.char_gap.has_value())
stream << CHAR_GAP_ATTR << "=\"" << *fp.char_gap << "\" ";
if (fp.line_gap.has_value())
stream << LINE_GAP_ATTR << "=\"" << *fp.line_gap << "\" ";
stream << LINE_HEIGHT_ATTR << "=\"" << fp.size_in_mm << "\" ";
stream << DEPTH_ATTR << "=\"" << fp.emboss << "\" ";
if (fp.boldness.has_value())
stream << BOLDNESS_ATTR << "=\"" << *fp.boldness << "\" ";
if (fp.skew.has_value())
stream << SKEW_ATTR << "=\"" << *fp.skew << "\" ";
// font descriptor
if (fp.family.has_value())
stream << FONT_FAMILY_ATTR << "=\"" << *fp.family << "\" ";
@ -3280,8 +3290,17 @@ std::optional<TextConfiguration> TextConfigurationSerialization::read(const char
FontItem fi(font_name, font_descriptor, type);
FontProp fp;
fp.char_gap = get_attribute_value_int(attributes, num_attributes, CHAR_GAP_ATTR);
fp.line_gap = get_attribute_value_int(attributes, num_attributes, LINE_GAP_ATTR);
int char_gap = get_attribute_value_int(attributes, num_attributes, CHAR_GAP_ATTR);
if (char_gap != 0) fp.char_gap = char_gap;
int line_gap = get_attribute_value_int(attributes, num_attributes, LINE_GAP_ATTR);
if (line_gap != 0) fp.line_gap = line_gap;
float boldness = get_attribute_value_float(attributes, num_attributes, BOLDNESS_ATTR);
if (std::fabs(boldness) > std::numeric_limits<float>::epsilon())
fp.boldness = boldness;
float skew = get_attribute_value_float(attributes, num_attributes, SKEW_ATTR);
if (std::fabs(skew) > std::numeric_limits<float>::epsilon())
fp.skew = skew;
fp.size_in_mm = get_attribute_value_float(attributes, num_attributes, LINE_HEIGHT_ATTR);
fp.emboss = get_attribute_value_float(attributes, num_attributes, DEPTH_ATTR);

View File

@ -38,11 +38,20 @@ using FontList = std::vector<FontItem>;
struct FontProp
{
// define extra space between letters, negative mean closer letter
int char_gap = 0;
std::optional<int> char_gap = 0;
// define extra space between lines, negative mean closer lines
int line_gap = 0;
std::optional<int> line_gap = 0;
// Z depth of text [in mm]
float emboss = 5;
// positive value mean wider character shape
// negative value mean tiner character shape
std::optional<float> boldness = 0.f; // [in mm]
// positive value mean italic of character (CW)
// negative value mean CCW skew (unItalic)
std::optional<float> skew = 0.f;
// TODO: add enum class Align: center/left/right
//////

View File

@ -423,7 +423,7 @@ void GLGizmoEmboss::initialize()
m_gui_cfg->text_size.y +
style.WindowPadding.y * 2.f;
m_gui_cfg->minimal_window_size = ImVec2(window_width, window_height);
float advance_height = (input_height + style.ItemSpacing.y) * 4.f;
float advance_height = (input_height + style.ItemSpacing.y) * 6.f;
m_gui_cfg->minimal_window_size_with_advance =
ImVec2(window_width, window_height + advance_height);
@ -765,13 +765,21 @@ void GLGizmoEmboss::draw_advanced()
ImGui::SetNextItemWidth(m_gui_cfg->advanced_input_width);
if (ImGui::InputFloat(_u8L("Emboss[in mm]").c_str(), &m_font_prop.emboss))
process();
ImGui::SetNextItemWidth(2*m_gui_cfg->advanced_input_width);
if (ImGui::InputInt(_u8L("CharGap[in font points]").c_str(),
&m_font_prop.char_gap))
ImGui::SetNextItemWidth(2 * m_gui_cfg->advanced_input_width);
if(ImGuiWrapper::input_optional_int(_u8L("CharGap[in font points]").c_str(), m_font_prop.char_gap))
process();
ImGui::SetNextItemWidth(2*m_gui_cfg->advanced_input_width);
if (ImGui::InputInt(_u8L("LineGap[in font points]").c_str(),
&m_font_prop.line_gap))
if (ImGuiWrapper::input_optional_int(_u8L("LineGap[in font points]").c_str(), m_font_prop.line_gap))
process();
ImGui::SetNextItemWidth(2 * m_gui_cfg->advanced_input_width);
if (m_imgui->slider_optional_float(_u8L("Boldness[in font points]").c_str(), m_font_prop.boldness, -200.f, 200.f, "%.0f", 1.f, false, _L("tiny / wide chars")))
process();
ImGui::SetNextItemWidth(2 * m_gui_cfg->advanced_input_width);
if (m_imgui->slider_optional_float(_u8L("Skew ratio").c_str(), m_font_prop.skew, -1.f, 1.f, "%.2f", 1.f, false, _L("italic strength")))
process();
// when more collection add selector
@ -793,6 +801,7 @@ void GLGizmoEmboss::draw_advanced()
}
}
#ifdef ALLOW_DEBUG_MODE
std::string descriptor = m_font_list[m_font_selected].path;
ImGui::Text("family = %s", (m_font_prop.family.has_value() ?

View File

@ -1041,6 +1041,93 @@ bool ImGuiWrapper::want_any_input() const
return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput;
}
template <typename T, typename Func>
static bool input_optional(std::optional<T> &v, Func& f, std::function<bool(const T&)> is_default)
{
if (v.has_value()) {
if (f(*v)) {
if (is_default(*v)) v.reset();
return true;
}
} else {
T val = 0;
if (f(val)) {
if (!is_default(val)) v = val;
return true;
}
}
return false;
}
bool ImGuiWrapper::input_optional_int(const char * label,
std::optional<int>& v,
int step,
int step_fast,
ImGuiInputTextFlags flags)
{
auto func = [&](int &value) {
return ImGui::InputInt(label, &value, step, step_fast, flags);
};
std::function<bool(const int &)> is_default =
[](const int &value) -> bool { return value == 0; };
return input_optional(v, func, is_default);
}
bool ImGuiWrapper::input_optional_float(const char * label,
std::optional<float> &v,
float step,
float step_fast,
const char * format,
ImGuiInputTextFlags flags)
{
auto func = [&](float &value) {
return ImGui::InputFloat(label, &value, step, step_fast, format, flags);
};
std::function<bool(const float &)> is_default =
[](const float &value) -> bool {
return std::fabs(value) < std::numeric_limits<float>::epsilon();
};
return input_optional(v, func, is_default);
}
bool ImGuiWrapper::drag_optional_float(const char * label,
std::optional<float> &v,
float v_speed,
float v_min,
float v_max,
const char * format,
float power)
{
auto func = [&](float &value) {
return ImGui::DragFloat(label, &value, v_speed, v_min, v_max, format, power);
};
std::function<bool(const float &)> is_default =
[](const float &value) -> bool {
return std::fabs(value) < std::numeric_limits<float>::epsilon();
};
return input_optional(v, func, is_default);
}
bool ImGuiWrapper::slider_optional_float(const char * label,
std::optional<float> &v,
float v_min,
float v_max,
const char * format,
float power,
bool clamp,
const wxString & tooltip,
bool show_edit_btn)
{
auto func = [&](float &value) {
return slider_float(label, &value, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
};
std::function<bool(const float &)> is_default =
[](const float &value) -> bool {
return std::fabs(value) < std::numeric_limits<float>::epsilon();
};
return input_optional(v, func, is_default);
}
std::string ImGuiWrapper::trunc(const std::string &text,
float width,
const char * tail)

View File

@ -128,6 +128,12 @@ public:
bool want_text_input() const;
bool want_any_input() const;
// Input [optional] int for nonzero value more info in ImGui::InputInt
static bool input_optional_int(const char *label, std::optional<int>& v, int step=1, int step_fast=100, ImGuiInputTextFlags flags=0);
// Input [optional] float for nonzero value more info in ImGui::InputFloat
static bool input_optional_float(const char* label, std::optional<float> &v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0);
static bool drag_optional_float(const char* label, std::optional<float> &v, float v_speed, float v_min, float v_max, const char* format, float power);
bool slider_optional_float(const char* label, std::optional<float> &v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true);
/// <summary>
/// Truncate text by ImGui draw function to specific width
/// NOTE 1: ImGui must be initialized