diff --git a/src/libslic3r/TextConfiguration.hpp b/src/libslic3r/TextConfiguration.hpp index b333b7cf77..ae8a6c0cb2 100644 --- a/src/libslic3r/TextConfiguration.hpp +++ b/src/libslic3r/TextConfiguration.hpp @@ -7,55 +7,87 @@ namespace Slic3r { -// user defined font property +/// +/// User modifiable property of text style +/// struct FontProp { // define extra space between letters, negative mean closer letter - std::optional char_gap; + // When not set value is zero and is not stored + std::optional char_gap; // [in font point] + // define extra space between lines, negative mean closer lines - std::optional line_gap; - // Z depth of text [in mm] - float emboss; + // When not set value is zero and is not stored + std::optional line_gap; // [in font point] + + // Z depth of text + float emboss; // [in mm] // positive value mean wider character shape // negative value mean tiner character shape + // When not set value is zero and is not stored std::optional boldness; // [in mm] // positive value mean italic of character (CW) // negative value mean CCW skew (unItalic) - std::optional skew; + // When not set value is zero and is not stored + std::optional skew; // [ration x:y] // distance from surface point // used for move over model surface - std::optional distance; + // When not set value is zero and is not stored + std::optional distance; // [in mm] - // change up vector of font - std::optional angle; + // change up vector direction of font + // When not set value is zero and is not stored + std::optional angle; // [in radians] - // TODO: add enum class Align: center/left/right + //enum class Align { + // left, + // right, + // center, + // top_left, + // top_right, + // top_center, + // bottom_left, + // bottom_right, + // bottom_center + //}; + //// change pivot of text + //// When not set, center is used and is not stored + //std::optional align; ////// // Duplicit data to wxFontDescriptor // used for store/load .3mf file ////// - // Height of letter [in mm], + // Height of text line (letters) // duplicit to wxFont::PointSize - float size_in_mm; - // Define type of font + float size_in_mm; // [in mm] + + // Additional data about font to be able to find substitution, + // when same font is not installed std::optional family; std::optional face_name; std::optional style; std::optional weight; + /// + /// Only constructor with restricted values + /// + /// Y size of text [in mm] + /// Z size of text [in mm] FontProp(float line_height = 10.f, float depth = 2.f) : emboss(depth), size_in_mm(line_height) {} bool operator==(const FontProp& other) const { + // compare float values auto is_equal = [](const float &v1, const float &v2) { return fabs(v1 - v2) < std::numeric_limits::epsilon(); }; + // compare optional float values auto is_equal_ = [&is_equal](const std::optional &v1, const std::optional &v2) { return (!v1.has_value() && !v2.has_value()) || @@ -71,22 +103,31 @@ struct FontProp } // undo / redo stack recovery - template void serialize(Archive &ar) - { - ar(char_gap, line_gap, emboss, boldness, skew, size_in_mm, family, face_name, style, weight); - } + //template void serialize(Archive &ar) + //{ + // ar(char_gap, line_gap, emboss, boldness, skew, size_in_mm, family, face_name, style, weight); + //} }; -// represent selected font -// Name must be human readable is visible in gui -// (Path + Type) must define how to open font for using on different OS -// NOTE: OnEdit fix serializations: FontListSerializable, TextConfigurationSerialization +/// +/// Style of embossed text +/// (Path + Type) must define how to open font for using on different OS +/// NOTE: OnEdit fix serializations: FontListSerializable, TextConfigurationSerialization +/// struct FontItem { + // Human readable name of style it is shown in GUI std::string name; + + // Define how to open font + // Meaning depend on type std::string path; + enum class Type; + // Define what is stored in path Type type; + + // User modification of font style FontProp prop; FontItem() : type(Type::undefined){} // set undefined type @@ -97,17 +138,23 @@ struct FontItem const std::string &path, Type type, const FontProp & prop) - : name(name), path(path), type(type), prop(prop) + : name(name), path(path), type(type), prop(prop) // copy values {} // define data stored in path + // when wx change way of storing add new descriptor Type enum class Type { undefined = 0, - file_path, // TrueTypeFont file loacation on computer - for privacy: path is NOT stored into 3mf + // wx font descriptors are platform dependent - wx_win_font_descr, // path is font descriptor generated by wxWidgets on windows - wx_lin_font_descr, // path is font descriptor generated by wxWidgets on windows - wx_mac_font_descr // path is font descriptor generated by wxWidgets on windows + // path is font descriptor generated by wxWidgets + wx_win_font_descr, // on Windows + wx_lin_font_descr, // on Linux + wx_mac_font_descr, // on Max OS + + // TrueTypeFont file loacation on computer + // for privacy: only filename is stored into .3mf + file_path }; bool operator==(const FontItem &other) const { @@ -119,11 +166,11 @@ struct FontItem ; } - // undo / redo stack recovery - template void serialize(Archive &ar) - { - ar(name, path, (int) type, prop); - } + //// undo / redo stack recovery + //template void serialize(Archive &ar) + //{ + // ar(name, path, (int) type, prop); + //} }; // Font item name inside list is unique @@ -131,14 +178,17 @@ struct FontItem // It is stored into AppConfig by FontListSerializable using FontList = std::vector; -// define how to create 'Text volume' -// It is stored into .3mf by TextConfigurationSerialization -// Also it is stored into undo / redo stack by cereal +/// +/// Define how to create 'Text volume' +/// It is stored into .3mf by TextConfigurationSerialization +/// It is part of ModelVolume optional data +/// struct TextConfiguration { - // define font + // Style of embossed text FontItem font_item; + // Embossed text value std::string text = "None"; TextConfiguration() = default; // optional needs empty constructor @@ -147,7 +197,7 @@ struct TextConfiguration {} // undo / redo stack recovery - template void serialize(Archive &ar){ ar(text, font_item); } + //template void serialize(Archive &ar){ ar(text, font_item); } }; } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 27f9771796..513f7cfdf3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -179,19 +179,45 @@ static void draw_place_to_add_text() { bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event) { + if (mouse_event.Moving()) return false; + if (!m_dragging) return false; + + assert(m_volume != nullptr); + assert(m_volume->text_configuration.has_value()); + + static std::optional start_angle; if (mouse_event.Dragging()) { - if (m_dragging) { - // temporary rotation - TransformationType transformation_type( - TransformationType::Local_Relative_Independent); - Vec3d rotation(0., 0., m_rotate_gizmo.get_angle()); - m_parent.get_selection().rotate(rotation, transformation_type); + auto &angle_opt = m_volume->text_configuration->font_item.prop.angle; + if (!start_angle.has_value()) { + start_angle = angle_opt.has_value() ? *angle_opt : 0.f; } + // temporary rotation + TransformationType transformation_type( + TransformationType::Local_Relative_Independent); + double angle = m_rotate_gizmo.get_angle(); + m_parent.get_selection().rotate(Vec3d(0., 0., angle), transformation_type); + + // propagate angle into property + angle_opt = static_cast(*start_angle + angle); + // move to range <-M_PI, M_PI> + if (*angle_opt > M_PI) { + *angle_opt -= 2 * M_PI; + } else if (*angle_opt < -M_PI) { + *angle_opt += 2 * M_PI; + } + // do not store zero + if (std::fabs(*angle_opt) < std::numeric_limits::epsilon()) + angle_opt.reset(); + + // set into activ style + if (m_font_manager.is_activ_font()) { + m_font_manager.get_font_prop().angle = angle_opt; + } + } else if (mouse_event.LeftUp()) { - if (m_dragging) { - // apply rotation - m_parent.do_rotate(L("Text-Rotate")); - } + // apply rotation + m_parent.do_rotate(L("Text-Rotate")); + start_angle.reset(); } return false; } @@ -252,7 +278,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event) const FontProp& font_prop = m_volume->text_configuration->font_item.prop; if (font_prop.angle.has_value()) { - double angle_z = M_PI / 180.0 * (*font_prop.angle); + double angle_z = *font_prop.angle; trmat *= Eigen::AngleAxisd(angle_z, Vec3d::UnitZ()); } if (font_prop.distance.has_value()) { @@ -1308,19 +1334,19 @@ void GLGizmoEmboss::draw_advanced() if (m_imgui->slider_optional_float(_u8L("Italic [Skew ratio]").c_str(), font_prop.skew, -1.f, 1.f, "%.2f", 1.f, false, _L("Italic strength ratio"))) exist_change = true; - // Property of text configuration - FontProp &tc_prop = m_volume->text_configuration->font_item.prop; - float prev_distance = font_prop.distance.has_value() ? *font_prop.distance : .0f; float min_distance = -2 * font_prop.emboss, max_distance = 2 * font_prop.emboss; ImGui::SetNextItemWidth(item_width); if (m_imgui->slider_optional_float(_u8L("Surface distance").c_str(), font_prop.distance, - min_distance, max_distance, "%.2f mm", 1.f, false, _L("Distance from model surface"))) { + min_distance, max_distance, "%.2f mm", 1.f, false, _L("Distance from model surface")) && + m_volume != nullptr && m_volume->text_configuration.has_value()) { + + FontProp& tc_prop = m_volume->text_configuration->font_item.prop; tc_prop.distance = font_prop.distance; - float act_distance = font_prop.distance.has_value() ? - *font_prop.distance : .0f; + + float act_distance = font_prop.distance.has_value() ? *font_prop.distance : .0f; float diff = act_distance - prev_distance; Vec3d displacement_rot = Vec3d::UnitZ() * diff; @@ -1335,23 +1361,35 @@ void GLGizmoEmboss::draw_advanced() m_parent.do_move(snapshot_name); } - float prev_angle = font_prop.angle.has_value() ? *font_prop.angle : .0f; + std::optional &angle = font_prop.angle; + float prev_angle = angle.has_value() ? *angle : .0f; + float angle_deg = prev_angle * 180 / M_PI; ImGui::SetNextItemWidth(item_width); - if (m_imgui->slider_optional_float(_u8L("Angle").c_str(), font_prop.angle, - -180.f, 180.f, u8"%.2f °", 1.f, false, _L("Rotation of text"))) { - tc_prop.angle = font_prop.angle; - float act_angle = font_prop.angle.has_value() ? *font_prop.angle : .0f; - float diff = act_angle-prev_angle; + if (m_imgui->slider_float(_u8L("Angle").c_str(), &angle_deg, + -180.f, 180.f, u8"%.2f °", 1.f, false, _L("Rotation of text")) ){ + if (std::fabs(angle_deg) < std::numeric_limits::epsilon()) { + angle.reset(); + } else { + // convert to radians + angle = angle_deg * M_PI / 180.0; + } + if (m_volume != nullptr && m_volume->text_configuration.has_value()) { + m_volume->text_configuration->font_item.prop.angle = angle; + float act_angle = angle.has_value() ? *angle : .0f; + float diff = act_angle - prev_angle; - Selection &selection = m_parent.get_selection(); - selection.start_dragging(); - selection.rotate(Vec3d(0., 0., M_PI / 180.0 * diff), TransformationType::Local); - selection.stop_dragging(); + Selection &selection = m_parent.get_selection(); + selection.start_dragging(); + selection.rotate(Vec3d(0., 0., diff), + TransformationType::Local); + selection.stop_dragging(); - std::string snapshot_name; // empty meand no store undo / redo - // NOTE: it use L instead of _L macro because prefix _ is appended inside function do_move - //snapshot_name = L("Set surface distance"); - m_parent.do_rotate(snapshot_name); + std::string snapshot_name; // empty meand no store undo / redo + // NOTE: it use L instead of _L macro because prefix _ is appended + // inside function do_move + // snapshot_name = L("Set surface distance"); + m_parent.do_rotate(snapshot_name); + } } // when more collection add selector