Create text volume with feature per letter transformation.

This commit is contained in:
Filip Sykala - NTB T15p 2023-05-23 10:30:14 +02:00
parent 2bfe762dfb
commit 35044e29c3
6 changed files with 76 additions and 45 deletions

View File

@ -163,6 +163,8 @@ static constexpr const char *BOLDNESS_ATTR = "boldness";
static constexpr const char *SKEW_ATTR = "skew"; static constexpr const char *SKEW_ATTR = "skew";
static constexpr const char *DISTANCE_ATTR = "distance"; static constexpr const char *DISTANCE_ATTR = "distance";
static constexpr const char *ANGLE_ATTR = "angle"; static constexpr const char *ANGLE_ATTR = "angle";
static constexpr const char *PER_GLYPH_ATTR = "per_glyph";
static constexpr const char *ALIGN_ATTR = "align";
static constexpr const char *COLLECTION_NUMBER_ATTR = "collection"; static constexpr const char *COLLECTION_NUMBER_ATTR = "collection";
static constexpr const char *FONT_FAMILY_ATTR = "family"; static constexpr const char *FONT_FAMILY_ATTR = "family";
@ -3530,6 +3532,10 @@ void TextConfigurationSerialization::to_xml(std::stringstream &stream, const Tex
stream << DISTANCE_ATTR << "=\"" << *fp.distance << "\" "; stream << DISTANCE_ATTR << "=\"" << *fp.distance << "\" ";
if (fp.angle.has_value()) if (fp.angle.has_value())
stream << ANGLE_ATTR << "=\"" << *fp.angle << "\" "; stream << ANGLE_ATTR << "=\"" << *fp.angle << "\" ";
if (fp.per_glyph)
stream << PER_GLYPH_ATTR << "=\"" << 1 << "\" ";
if (fp.align != FontProp().align) // differ to default value? back compatibility
stream << ALIGN_ATTR << "=\"" << static_cast<int>(fp.align) << "\" ";
if (fp.collection_number.has_value()) if (fp.collection_number.has_value())
stream << COLLECTION_NUMBER_ATTR << "=\"" << *fp.collection_number << "\" "; stream << COLLECTION_NUMBER_ATTR << "=\"" << *fp.collection_number << "\" ";
// font descriptor // font descriptor
@ -3609,6 +3615,11 @@ std::optional<TextConfiguration> TextConfigurationSerialization::read(const char
float angle = get_attribute_value_float(attributes, num_attributes, ANGLE_ATTR); float angle = get_attribute_value_float(attributes, num_attributes, ANGLE_ATTR);
if (std::fabs(angle) > std::numeric_limits<float>::epsilon()) if (std::fabs(angle) > std::numeric_limits<float>::epsilon())
fp.angle = angle; fp.angle = angle;
int per_glyph = get_attribute_value_int(attributes, num_attributes, PER_GLYPH_ATTR);
if (per_glyph == 1) fp.per_glyph = true;
int align = get_attribute_value_int(attributes, num_attributes, ALIGN_ATTR);
fp.align = static_cast<FontProp::Align>(align);
int collection_number = get_attribute_value_int(attributes, num_attributes, COLLECTION_NUMBER_ATTR); int collection_number = get_attribute_value_int(attributes, num_attributes, COLLECTION_NUMBER_ATTR);
if (collection_number > 0) fp.collection_number = static_cast<unsigned int>(collection_number); if (collection_number > 0) fp.collection_number = static_cast<unsigned int>(collection_number);

View File

@ -63,20 +63,23 @@ struct FontProp
// Distiguish projection per glyph // Distiguish projection per glyph
bool per_glyph; bool per_glyph;
// Enumerate type of allowed text align
enum class Align { enum class Align {
// NOTE: default value must be zero - 3mf store
first_line_center = 0, // use Y zero same as first letter
first_line_left, // it depends on position of zero for first letter (no shape move) first_line_left, // it depends on position of zero for first letter (no shape move)
first_line_right, // use Y zero same as first letter first_line_right, // use Y zero same as first letter
first_line_center, // use Y zero same as first letter center_center,
center_left, center_left,
center_right, center_right,
center_center, top_center,
top_left, top_left,
top_right, top_right,
top_center, bottom_center,
bottom_left, bottom_left,
bottom_right, bottom_right
bottom_center
}; };
// change pivot of text // change pivot of text
// When not set, center is used and is not stored // When not set, center is used and is not stored
Align align = Align::first_line_center; Align align = Align::first_line_center;
@ -110,6 +113,8 @@ struct FontProp
char_gap == other.char_gap && char_gap == other.char_gap &&
line_gap == other.line_gap && line_gap == other.line_gap &&
use_surface == other.use_surface && use_surface == other.use_surface &&
per_glyph == other.per_glyph &&
align == other.align &&
is_approx(emboss, other.emboss) && is_approx(emboss, other.emboss) &&
is_approx(size_in_mm, other.size_in_mm) && is_approx(size_in_mm, other.size_in_mm) &&
is_approx(boldness, other.boldness) && is_approx(boldness, other.boldness) &&
@ -121,7 +126,7 @@ struct FontProp
// undo / redo stack recovery // undo / redo stack recovery
template<class Archive> void save(Archive &ar) const template<class Archive> void save(Archive &ar) const
{ {
ar(emboss, use_surface, size_in_mm); ar(emboss, use_surface, size_in_mm, per_glyph, align);
cereal::save(ar, char_gap); cereal::save(ar, char_gap);
cereal::save(ar, line_gap); cereal::save(ar, line_gap);
cereal::save(ar, boldness); cereal::save(ar, boldness);
@ -136,7 +141,7 @@ struct FontProp
} }
template<class Archive> void load(Archive &ar) template<class Archive> void load(Archive &ar)
{ {
ar(emboss, use_surface, size_in_mm); ar(emboss, use_surface, size_in_mm, per_glyph, align);
cereal::load(ar, char_gap); cereal::load(ar, char_gap);
cereal::load(ar, line_gap); cereal::load(ar, line_gap);
cereal::load(ar, boldness); cereal::load(ar, boldness);

View File

@ -146,9 +146,15 @@ namespace priv {
/// </summary> /// </summary>
/// <param name="text">Text to emboss</param> /// <param name="text">Text to emboss</param>
/// <param name="style_manager">Keep actual selected style</param> /// <param name="style_manager">Keep actual selected style</param>
/// <param name="text_lines">Needed when transform per glyph</param>
/// <param name="selection">Needed for transform per glyph</param>
/// <param name="cancel">Cancel for previous job</param> /// <param name="cancel">Cancel for previous job</param>
/// <returns>Base data for emboss text</returns> /// <returns>Base data for emboss text</returns>
static DataBase create_emboss_data_base(const std::string &text, StyleManager &style_manager, std::shared_ptr<std::atomic<bool>> &cancel); static DataBase create_emboss_data_base(const std::string &text,
StyleManager &style_manager,
TextLinesModel &text_lines,
const Selection &selection,
std::shared_ptr<std::atomic<bool>> &cancel);
/// <summary> /// <summary>
/// Start job for add new volume to object with given transformation /// Start job for add new volume to object with given transformation
@ -242,13 +248,17 @@ static bool apply_camera_dir(const Camera &camera, GLCanvas3D &canvas, bool keep
} // namespace priv } // namespace priv
namespace {
void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines=0);
}
void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos) void GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos)
{ {
if (!init_create(volume_type)) if (!init_create(volume_type))
return; return;
const GLVolume *gl_volume = get_first_hovered_gl_volume(m_parent); const GLVolume *gl_volume = get_first_hovered_gl_volume(m_parent);
DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel);
bool is_simple_mode = wxGetApp().get_mode() == comSimple; bool is_simple_mode = wxGetApp().get_mode() == comSimple;
if (gl_volume != nullptr && !is_simple_mode) { if (gl_volume != nullptr && !is_simple_mode) {
// Try to cast ray into scene and find object for add volume // Try to cast ray into scene and find object for add volume
@ -275,7 +285,7 @@ void GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
Size s = m_parent.get_canvas_size(); Size s = m_parent.get_canvas_size();
Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.); Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.);
DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel); DataBase emboss_data = priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel);
const ModelObjectPtrs &objects = selection.get_model()->objects; const ModelObjectPtrs &objects = selection.get_model()->objects;
bool is_simple_mode = wxGetApp().get_mode() == comSimple; bool is_simple_mode = wxGetApp().get_mode() == comSimple;
// No selected object so create new object // No selected object so create new object
@ -559,7 +569,7 @@ void GLGizmoEmboss::volume_transformation_changing()
} }
const FontProp &prop = m_volume->text_configuration->style.prop; const FontProp &prop = m_volume->text_configuration->style.prop;
if (prop.per_glyph) if (prop.per_glyph)
init_text_lines(m_text_lines.get_lines().size()); init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, m_text_lines.get_lines().size());
} }
void GLGizmoEmboss::volume_transformation_changed() void GLGizmoEmboss::volume_transformation_changed()
@ -571,7 +581,7 @@ void GLGizmoEmboss::volume_transformation_changed()
const FontProp &prop = m_volume->text_configuration->style.prop; const FontProp &prop = m_volume->text_configuration->style.prop;
if (prop.per_glyph) if (prop.per_glyph)
init_text_lines(m_text_lines.get_lines().size()); init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, m_text_lines.get_lines().size());
// Update surface by new position // Update surface by new position
if (prop.use_surface || prop.per_glyph) if (prop.use_surface || prop.per_glyph)
@ -1047,11 +1057,13 @@ EmbossStyles GLGizmoEmboss::create_default_styles()
return styles; return styles;
} }
void GLGizmoEmboss::init_text_lines(unsigned count_lines){ namespace {
assert(m_style_manager.is_active_font()); void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines)
if (!m_style_manager.is_active_font()) {
assert(style_manager.is_active_font());
if (!style_manager.is_active_font())
return; return;
const auto& ffc = m_style_manager.get_font_file_with_cache(); const auto &ffc = style_manager.get_font_file_with_cache();
assert(ffc.has_value()); assert(ffc.has_value());
if (!ffc.has_value()) if (!ffc.has_value())
return; return;
@ -1060,14 +1072,12 @@ void GLGizmoEmboss::init_text_lines(unsigned count_lines){
if (ff_ptr == nullptr) if (ff_ptr == nullptr)
return; return;
if (m_volume->is_the_only_one_part()) const FontProp &fp = style_manager.get_font_prop();
return;
const FontProp& fp = m_style_manager.get_font_prop();
const FontFile &ff = *ff_ptr; const FontFile &ff = *ff_ptr;
double line_height = TextLinesModel::calc_line_height(ff, fp); double line_height = TextLinesModel::calc_line_height(ff, fp);
m_text_lines.init(m_parent.get_selection(), line_height, count_lines); text_lines.init(selection, line_height, count_lines);
}
} }
void GLGizmoEmboss::set_volume_by_selection() void GLGizmoEmboss::set_volume_by_selection()
@ -1209,7 +1219,7 @@ void GLGizmoEmboss::set_volume_by_selection()
m_volume_id = volume->id(); m_volume_id = volume->id();
if (tc.style.prop.per_glyph) if (tc.style.prop.per_glyph)
init_text_lines(); init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager);
// Calculate current angle of up vector // Calculate current angle of up vector
assert(m_style_manager.is_active_font()); assert(m_style_manager.is_active_font());
@ -1292,12 +1302,8 @@ bool GLGizmoEmboss::process()
// exist loaded font file? // exist loaded font file?
if (!m_style_manager.is_active_font()) return false; if (!m_style_manager.is_active_font()) return false;
DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel), m_volume->id()}; DataUpdate data{priv::create_emboss_data_base(m_text, m_style_manager, m_text_lines, m_parent.get_selection(), m_job_cancel),
if (data.text_configuration.style.prop.per_glyph){ m_volume->id()};
if (!m_text_lines.is_init())
init_text_lines();
data.text_lines = m_text_lines.get_lines(); // copy
}
std::unique_ptr<Job> job = nullptr; std::unique_ptr<Job> job = nullptr;
// check cutting from source mesh // check cutting from source mesh
@ -1547,7 +1553,7 @@ void GLGizmoEmboss::draw_text_input()
unsigned count_lines = get_count_lines(m_text); unsigned count_lines = get_count_lines(m_text);
if (count_lines != m_text_lines.get_lines().size()) if (count_lines != m_text_lines.get_lines().size())
// Necesarry to initialize count by given number (differ from stored in volume at the moment) // Necesarry to initialize count by given number (differ from stored in volume at the moment)
init_text_lines(count_lines); init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, count_lines);
} }
process(); process();
range_text = create_range_text_prep(); range_text = create_range_text_prep();
@ -3231,7 +3237,7 @@ void GLGizmoEmboss::draw_advanced()
if (ImGui::Checkbox("##PerGlyph", per_glyph)) { if (ImGui::Checkbox("##PerGlyph", per_glyph)) {
if (*per_glyph) { if (*per_glyph) {
if (!m_text_lines.is_init()) if (!m_text_lines.is_init())
init_text_lines(); init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager);
} }
process(); process();
} else if (ImGui::IsItemHovered()) { } else if (ImGui::IsItemHovered()) {
@ -3240,7 +3246,7 @@ void GLGizmoEmboss::draw_advanced()
} else { } else {
ImGui::SetTooltip("%s", _u8L("Set position and orientation of projection per Glyph.").c_str()); ImGui::SetTooltip("%s", _u8L("Set position and orientation of projection per Glyph.").c_str());
if (!m_text_lines.is_init()) if (!m_text_lines.is_init())
init_text_lines(); init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager);
} }
} else if (!*per_glyph && m_text_lines.is_init()) } else if (!*per_glyph && m_text_lines.is_init())
m_text_lines.reset(); m_text_lines.reset();
@ -3248,24 +3254,24 @@ void GLGizmoEmboss::draw_advanced()
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemWidth(100); ImGui::SetNextItemWidth(100);
if (ImGui::SliderFloat("##base_line_y_offset", &m_text_lines.offset, -10.f, 10.f, "%f mm")) { if (ImGui::SliderFloat("##base_line_y_offset", &m_text_lines.offset, -10.f, 10.f, "%f mm")) {
init_text_lines(m_text_lines.get_lines().size()); init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, m_text_lines.get_lines().size());
process(); process();
} else if (ImGui::IsItemHovered()) } else if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("Move base line (up/down) for allign letters").c_str()); ImGui::SetTooltip("%s", _u8L("Move base line (up/down) for allign letters").c_str());
// order must match align enum // order must match align enum
const char* align_names[] = {"first_line_left", const char* align_names[] = { "first_line_center",
"first_line_left",
"first_line_right", "first_line_right",
"first_line_center", "center_center",
"center_left", "center_left",
"center_right", "center_right",
"center_center",
"top_left",
"top_right",
"top_center", "top_center",
"top_left",
"top_right",
"bottom_center",
"bottom_left", "bottom_left",
"bottom_right", "bottom_right"};
"bottom_center"};
int selected_align = static_cast<int>(font_prop.align); int selected_align = static_cast<int>(font_prop.align);
if (ImGui::Combo("align", &selected_align, align_names, IM_ARRAYSIZE(align_names))) { if (ImGui::Combo("align", &selected_align, align_names, IM_ARRAYSIZE(align_names))) {
font_prop.align = static_cast<FontProp::Align>(selected_align); font_prop.align = static_cast<FontProp::Align>(selected_align);
@ -3542,7 +3548,7 @@ bool priv::draw_button(const IconManager::VIcons &icons, IconType type, bool dis
// priv namespace implementation // priv namespace implementation
/////////////// ///////////////
DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &style_manager, std::shared_ptr<std::atomic<bool>>& cancel) DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &style_manager, TextLinesModel& text_lines, const Selection& selection, std::shared_ptr<std::atomic<bool>>& cancel)
{ {
// create volume_name // create volume_name
std::string volume_name = text; // copy std::string volume_name = text; // copy
@ -3565,6 +3571,12 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st
assert(es.path.compare(WxFontUtils::store_wxFont(style_manager.get_wx_font())) == 0); assert(es.path.compare(WxFontUtils::store_wxFont(style_manager.get_wx_font())) == 0);
TextConfiguration tc{es, text}; TextConfiguration tc{es, text};
if (es.prop.per_glyph) {
if (!text_lines.is_init())
init_text_lines(text_lines, selection, style_manager);
} else
text_lines.reset();
// Cancel previous Job, when it is in process // Cancel previous Job, when it is in process
// worker.cancel(); --> Use less in this case I want cancel only previous EmbossJob no other jobs // worker.cancel(); --> Use less in this case I want cancel only previous EmbossJob no other jobs
// Cancel only EmbossUpdateJob no others // Cancel only EmbossUpdateJob no others
@ -3572,7 +3584,7 @@ DataBase priv::create_emboss_data_base(const std::string &text, StyleManager &st
cancel->store(true); cancel->store(true);
// create new shared ptr to cancel new job // create new shared ptr to cancel new job
cancel = std::make_shared<std::atomic<bool>>(false); cancel = std::make_shared<std::atomic<bool>>(false);
return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), tc, volume_name, cancel}; return Slic3r::GUI::Emboss::DataBase{style_manager.get_font_file_with_cache(), tc, volume_name, cancel, text_lines.get_lines()};
} }
void priv::start_create_object_job(DataBase &emboss_data, const Vec2d &coor) void priv::start_create_object_job(DataBase &emboss_data, const Vec2d &coor)

View File

@ -317,7 +317,6 @@ private:
// Keep information about curvature of text line around surface // Keep information about curvature of text line around surface
TextLinesModel m_text_lines; TextLinesModel m_text_lines;
void init_text_lines(unsigned count_lines = 0);
// Rotation gizmo // Rotation gizmo
GLGizmoRotate m_rotate_gizmo; GLGizmoRotate m_rotate_gizmo;

View File

@ -253,6 +253,9 @@ GLModel::Geometry create_geometry(const TextLines &lines)
void TextLinesModel::init(const Selection &selection, double line_height, unsigned count_lines) void TextLinesModel::init(const Selection &selection, double line_height, unsigned count_lines)
{ {
m_model.reset();
m_lines.clear();
const GLVolume *gl_volume_ptr = selection.get_first_volume(); const GLVolume *gl_volume_ptr = selection.get_first_volume();
if (gl_volume_ptr == nullptr) if (gl_volume_ptr == nullptr)
return; return;
@ -267,6 +270,8 @@ void TextLinesModel::init(const Selection &selection, double line_height, unsign
if (mv_ptr == nullptr) if (mv_ptr == nullptr)
return; return;
const ModelVolume &mv = *mv_ptr; const ModelVolume &mv = *mv_ptr;
if (mv.is_the_only_one_part())
return;
// calculate count lines when not set // calculate count lines when not set
if (count_lines == 0) { if (count_lines == 0) {
@ -317,7 +322,6 @@ void TextLinesModel::init(const Selection &selection, double line_height, unsign
for (size_t i = 0; i < count_lines; ++i) for (size_t i = 0; i < count_lines; ++i)
m_lines[i].y = line_centers[i]; m_lines[i].y = line_centers[i];
m_model.reset();
//* //*
GLModel::Geometry geometry = create_geometry(m_lines); GLModel::Geometry geometry = create_geometry(m_lines);
if (geometry.vertices_count() == 0 || geometry.indices_count() == 0) if (geometry.vertices_count() == 0 || geometry.indices_count() == 0)

View File

@ -25,7 +25,7 @@ public:
void render(const Transform3d &text_world); void render(const Transform3d &text_world);
bool is_init() const { return m_model.is_initialized(); } bool is_init() const { return m_model.is_initialized(); }
void reset() { m_model.reset(); } void reset() { m_model.reset(); m_lines.clear(); }
const Slic3r::Emboss::TextLines &get_lines() const { return m_lines; } const Slic3r::Emboss::TextLines &get_lines() const { return m_lines; }
static double calc_line_height(const Slic3r::Emboss::FontFile& ff, const FontProp& fp); static double calc_line_height(const Slic3r::Emboss::FontFile& ff, const FontProp& fp);
private: private: