Partialy calculated distance on the fly.

This commit is contained in:
Filip Sykala - NTB T15p 2023-03-22 09:11:12 +01:00
parent 1b34b1970c
commit e0a4ac0313
11 changed files with 418 additions and 368 deletions

View File

@ -144,6 +144,21 @@ Pointf3s transform(const Pointf3s& points, const Transform3d& t);
/// <returns>Is positive determinant</returns>
inline bool has_reflection(const Transform3d &transform) { return transform.matrix().determinant() < 0; }
/// <summary>
/// Getter on base of transformation matrix
/// </summary>
/// <param name="index">column index</param>
/// <param name="transform">source transformation</param>
/// <returns>Base of transformation matrix</returns>
inline const Vec3d &get_base(unsigned index, const Transform3d &transform) { return transform.linear().col(index); }
inline const Vec3d &get_base(unsigned index, const Transform3d::LinearPart &transform) { return transform.col(index); }
inline const Vec3d& get_x_base(const Transform3d &transform) { return get_base(0, transform); }
inline const Vec3d& get_y_base(const Transform3d &transform) { return get_base(1, transform); }
inline const Vec3d& get_z_base(const Transform3d &transform) { return get_base(2, transform); }
inline const Vec3d &get_x_base(const Transform3d::LinearPart &transform) { return get_base(0, transform); }
inline const Vec3d &get_y_base(const Transform3d::LinearPart &transform) { return get_base(1, transform); }
inline const Vec3d &get_z_base(const Transform3d::LinearPart &transform) { return get_base(2, transform); }
template<int N, class T> using Vec = Eigen::Matrix<T, N, 1, Eigen::DontAlign, N, 1>;
class Point : public Vec2crd

View File

@ -154,14 +154,13 @@ const IconManager::Icon &get_icon(const IconManager::VIcons& icons, IconType typ
static bool draw_button(const IconManager::VIcons& icons, IconType type, bool disable = false);
} // namespace priv
CreateVolumeParams create_input(GLCanvas3D &canvas, StyleManager &styler, RaycastManager& raycaster, ModelVolumeType volume_type)
CreateVolumeParams create_input(GLCanvas3D &canvas, const StyleManager::Style &style, RaycastManager& raycaster, ModelVolumeType volume_type)
{
auto gizmo = static_cast<unsigned char>(GLGizmosManager::Emboss);
const GLVolume *gl_volume = get_first_hovered_gl_volume(canvas);
const FontProp &fp = styler.get_style().prop;
Plater *plater = wxGetApp().plater();
return CreateVolumeParams{canvas, plater->get_camera(), plater->build_volume(),
plater->get_ui_job_worker(), volume_type, raycaster, gizmo, gl_volume, fp.distance, fp.angle};
plater->get_ui_job_worker(), volume_type, raycaster, gizmo, gl_volume, style.distance, style.angle};
}
bool GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mouse_pos)
@ -171,7 +170,7 @@ bool GLGizmoEmboss::create_volume(ModelVolumeType volume_type, const Vec2d& mous
// NOTE: change style manager - be carefull with order changes
DataBasePtr base = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
CreateVolumeParams input = create_input(m_parent, m_style_manager, m_raycast_manager, volume_type);
CreateVolumeParams input = create_input(m_parent, m_style_manager.get_style(), m_raycast_manager, volume_type);
return start_create_volume(input, std::move(base), mouse_pos);
}
@ -183,7 +182,7 @@ bool GLGizmoEmboss::create_volume(ModelVolumeType volume_type)
// NOTE: change style manager - be carefull with order changes
DataBasePtr base = priv::create_emboss_data_base(m_text, m_style_manager, m_job_cancel);
CreateVolumeParams input = create_input(m_parent, m_style_manager, m_raycast_manager, volume_type);
CreateVolumeParams input = create_input(m_parent, m_style_manager.get_style(), m_raycast_manager, volume_type);
return start_create_volume_without_position(input, std::move(base));
}
@ -197,9 +196,9 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
if (mouse_event.Dragging()) {
if (!m_rotate_start_angle.has_value()) {
// when m_rotate_start_angle is not set mean it is not Dragging
// when angle_opt is not set mean angle is Zero
// when angle_opt is not set than angle is Zero
const std::optional<float> &angle_opt = m_style_manager.get_font_prop().angle;
m_rotate_start_angle = angle_opt.has_value() ? *angle_opt : 0.f;
m_rotate_start_angle = angle_opt.value_or(0.f);
}
double angle = m_rotate_gizmo.get_angle();
@ -269,7 +268,7 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
if (gl_volume == nullptr || !m_style_manager.is_active_font())
return res;
m_style_manager.get_style().prop.angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit);
m_style_manager.get_style().angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit);
}
}
return res;
@ -576,7 +575,7 @@ void GLGizmoEmboss::on_stop_dragging()
assert(m_style_manager.is_active_font());
assert(gl_volume != nullptr);
if (m_style_manager.is_active_font() && gl_volume != nullptr)
m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit);
m_style_manager.get_style().angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit);
m_rotate_start_angle.reset();
@ -754,15 +753,66 @@ EmbossStyles GLGizmoEmboss::create_default_styles()
void GLGizmoEmboss::set_default_text(){ m_text = _u8L("Embossed text"); }
namespace {
/// <summary>
/// Throow ray by embossing params to object and find surface point
/// </summary>
/// <param name="gl_volume">Define embossed volume</param>
/// <param name="raycaster">Way to cast rays to object</param>
/// <param name="canvas">Contain model</param>
/// <returns>Calculated distance from surface</returns>
std::optional<float> calc_distance(const GLVolume &gl_volume, RaycastManager &raycaster, GLCanvas3D& canvas)
{
const ModelObject *object = get_model_object(gl_volume, canvas.get_model()->objects);
assert(object != nullptr);
if (object == nullptr)
return {};
const ModelInstance *instance = get_model_instance(gl_volume, *object);
const ModelVolume *volume = get_model_volume(gl_volume, *object);
assert(instance != nullptr && volume != nullptr);
if (object == nullptr || instance == nullptr || volume == nullptr)
return {};
if (volume->is_the_only_one_part())
return {};
const ModelVolumePtrs &volumes = object->volumes;
std::vector<size_t> allowed_volumes_id;
allowed_volumes_id.reserve(volumes.size() - 1);
for (const ModelVolume *v : volumes) {
// skip actual selected object
if (v->id() == volume->id())
continue;
// collect hit only from object parts not modifiers neither negative
if (!v->is_model_part())
continue;
allowed_volumes_id.emplace_back(v->id().id);
}
RaycastManager::AllowVolumes condition(std::move(allowed_volumes_id));
RaycastManager::Meshes meshes = create_meshes(canvas, condition);
raycaster.actualize(*instance, &condition, &meshes);
Transform3d w = gl_volume.world_matrix();
Vec3d p = w.translation();
const Vec3d& dir = get_z_base(w);
auto hit_opt = raycaster.first_hit(p, dir, &condition);
if (!hit_opt.has_value())
return {};
}
} // namespace
void GLGizmoEmboss::set_volume_by_selection()
{
const Selection &selection = m_parent.get_selection();
const GLVolume *gl_volume = get_selected_gl_volume(selection);
const GLVolume *gl_volume = get_selected_gl_volume(selection);
if (gl_volume == nullptr)
return reset_volume();
const ModelObjectPtrs &objects = selection.get_model()->objects;
ModelVolume *volume =get_model_volume(*gl_volume, objects);
ModelVolume *volume = get_model_volume(*gl_volume, objects);
if (volume == nullptr)
return reset_volume();
@ -774,24 +824,28 @@ void GLGizmoEmboss::set_volume_by_selection()
remove_notification_not_valid_font();
// Do not use focused input value when switch volume(it must swith value)
if (m_volume != nullptr &&
m_volume != volume) // when update volume it changed id BUT not pointer
if (m_volume != nullptr && m_volume != volume) // when update volume it changed id BUT not pointer
ImGuiWrapper::left_inputs();
// Is selected volume text volume?
const std::optional<TextConfiguration>& tc_opt = volume->text_configuration;
if (!tc_opt.has_value())
const std::optional<TextConfiguration> &tc_opt = volume->text_configuration;
if (!tc_opt.has_value())
return reset_volume();
// Emboss shape must be setted
assert(volume->emboss_shape.has_value());
if (!volume->emboss_shape.has_value())
return;
const TextConfiguration &tc = *tc_opt;
const EmbossStyle &style = tc.style;
// Could exist OS without getter on face_name,
// but it is able to restore font from descriptor
// Soo default value must be TRUE
bool is_font_installed = true;
wxString face_name;
std::optional<std::string> face_name_opt = style.prop.face_name;
bool is_font_installed = true;
wxString face_name;
const std::optional<std::string> &face_name_opt = style.prop.face_name;
if (face_name_opt.has_value()) {
face_name = wxString(face_name_opt->c_str());
@ -800,21 +854,20 @@ void GLGizmoEmboss::set_volume_by_selection()
init_face_names(m_face_names);
m_face_names.is_init = false;
auto cmp = [](const FaceName &fn, const wxString& face_name)->bool { return fn.wx_name < face_name; };
auto cmp = [](const FaceName &fn, const wxString &face_name) -> bool { return fn.wx_name < face_name; };
const std::vector<FaceName> &faces = m_face_names.faces;
auto it = std::lower_bound(faces.begin(), faces.end(), face_name, cmp);
is_font_installed = it != faces.end() && it->wx_name == face_name;
if (!is_font_installed) {
const std::vector<wxString> &bad = m_face_names.bad;
auto it_bad = std::lower_bound(bad.begin(), bad.end(), face_name);
if (it_bad == bad.end() || *it_bad != face_name){
const std::vector<wxString> &bad = m_face_names.bad;
auto it_bad = std::lower_bound(bad.begin(), bad.end(), face_name);
if (it_bad == bad.end() || *it_bad != face_name) {
// check if wx allowed to set it up - another encoding of name
wxFontEnumerator::InvalidateCache();
wxFont wx_font_; // temporary structure
if (wx_font_.SetFaceName(face_name) &&
WxFontUtils::create_font_file(wx_font_) != nullptr // can load TTF file?
) {
wxFont wx_font_; // temporary structure
if (wx_font_.SetFaceName(face_name) && WxFontUtils::create_font_file(wx_font_) != nullptr // can load TTF file?
) {
is_font_installed = true;
// QUESTION: add this name to allowed faces?
// Could create twin of font face name
@ -826,8 +879,8 @@ void GLGizmoEmboss::set_volume_by_selection()
wxFont wx_font;
// load wxFont from same OS when font name is installed
if (style.type == WxFontUtils::get_actual_type() && is_font_installed)
wx_font = WxFontUtils::load_wxFont(style.path);
if (style.type == WxFontUtils::get_current_type() && is_font_installed)
wx_font = WxFontUtils::load_wxFont(style.path);
// Flag that is selected same font
bool is_exact_font = true;
@ -837,7 +890,7 @@ void GLGizmoEmboss::set_volume_by_selection()
// Try create similar wx font by FontFamily
wx_font = WxFontUtils::create_wxFont(style);
if (is_font_installed)
is_exact_font = wx_font.SetFaceName(face_name);
is_exact_font = wx_font.SetFaceName(face_name);
// Have to use some wxFont
if (!wx_font.IsOk())
@ -846,25 +899,28 @@ void GLGizmoEmboss::set_volume_by_selection()
assert(wx_font.IsOk());
// Load style to style manager
const auto& styles = m_style_manager.get_styles();
auto has_same_name = [&style](const StyleManager::Item &style_item) -> bool {
const EmbossStyle &es = style_item.style;
return es.name == style.name;
};
const auto &styles = m_style_manager.get_styles();
auto has_same_name = [&name = style.name](const StyleManager::Style &style_item) { return style_item.name == name; };
StyleManager::Style style_{style};
style_.projection = volume->emboss_shape->projection;
style_.angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit);
style_.distance = calc_distance(*gl_volume, m_raycast_manager, m_parent);
auto it = std::find_if(styles.begin(), styles.end(), has_same_name);
if (it == styles.end()) {
// style was not found
m_style_manager.load_style(style, wx_font);
m_style_manager.load_style(style_, wx_font);
} else {
// style name is in styles list
size_t style_index = it - styles.begin();
if (!m_style_manager.load_style(style_index)) {
// can`t load stored style
m_style_manager.erase(style_index);
m_style_manager.load_style(style, wx_font);
m_style_manager.load_style(style_, wx_font);
} else {
// stored style is loaded, now set modification of style
m_style_manager.get_style() = style;
m_style_manager.get_style() = style_;
m_style_manager.set_wx_font(wx_font);
}
}
@ -890,11 +946,6 @@ void GLGizmoEmboss::set_volume_by_selection()
m_volume = volume;
m_volume_id = volume->id();
// Calculate current angle of up vector
assert(m_style_manager.is_active_font());
if (m_style_manager.is_active_font())
m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit);
// calculate scale for height and depth inside of scaled object instance
calculate_scale();
}
@ -1858,14 +1909,10 @@ void GLGizmoEmboss::draw_style_rename_popup() {
const std::string &old_name = m_style_manager.get_stored_style()->name;
std::string text_in_popup = GUI::format(_L("Rename style(%1%) for embossing text: "), old_name);
ImGui::Text("%s", text_in_popup.c_str());
bool is_unique = true;
for (const auto &item : m_style_manager.get_styles()) {
const EmbossStyle &style = item.style;
if (&style == &m_style_manager.get_style())
continue; // could be same as original name
if (style.name == new_name) is_unique = false;
}
bool is_unique = (new_name == old_name) || // could be same as before rename
m_style_manager.is_unique_style_name(new_name);
bool allow_change = false;
if (new_name.empty()) {
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_DARK, _u8L("Name can't be empty."));
@ -1946,11 +1993,7 @@ void GLGizmoEmboss::draw_style_save_as_popup() {
// use name inside of volume configuration as temporary new name
std::string &new_name = m_volume->text_configuration->style.name;
bool is_unique = true;
for (const auto &item : m_style_manager.get_styles())
if (item.style.name == new_name) is_unique = false;
bool is_unique = m_style_manager.is_unique_style_name(new_name);
bool allow_change = false;
if (new_name.empty()) {
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_DARK, _u8L("Name can't be empty."));
@ -1988,7 +2031,7 @@ void GLGizmoEmboss::draw_style_add_button()
bool only_add_style = !m_style_manager.exist_stored_style();
bool can_add = true;
if (only_add_style &&
m_volume->text_configuration->style.type != WxFontUtils::get_actual_type())
m_volume->text_configuration->style.type != WxFontUtils::get_current_type())
can_add = false;
std::string title = _u8L("Save as new style");
@ -2080,46 +2123,46 @@ void GLGizmoEmboss::draw_delete_style_button() {
}
}
// FIX IT: it should not change volume position before successfull change
void GLGizmoEmboss::fix_transformation(const FontProp &from,
const FontProp &to)
{
namespace {
// FIX IT: It should not change volume position before successfull change volume by process
void fix_transformation(const StyleManager::Style &from, const StyleManager::Style &to, GLCanvas3D &canvas) {
// fix Z rotation when exists difference in styles
const std::optional<float> &f_angle_opt = from.angle;
const std::optional<float> &t_angle_opt = to.angle;
if (!is_approx(f_angle_opt, t_angle_opt)) {
// fix rotation
float f_angle = f_angle_opt.has_value() ? *f_angle_opt : .0f;
float t_angle = t_angle_opt.has_value() ? *t_angle_opt : .0f;
do_local_z_rotate(m_parent, t_angle - f_angle);
float f_angle = f_angle_opt.value_or(.0f);
float t_angle = t_angle_opt.value_or(.0f);
do_local_z_rotate(canvas, t_angle - f_angle);
}
// fix distance (Z move) when exists difference in styles
const std::optional<float> &f_move_opt = from.distance;
const std::optional<float> &t_move_opt = to.distance;
if (!is_approx(f_move_opt, t_move_opt)) {
float f_move = f_move_opt.has_value() ? *f_move_opt : .0f;
float t_move = t_move_opt.has_value() ? *t_move_opt : .0f;
do_local_z_move(m_parent, t_move - f_move);
float f_move = f_move_opt.value_or(.0f);
float t_move = t_move_opt.value_or(.0f);
do_local_z_move(canvas, t_move - f_move);
}
}
} // namesapce
void GLGizmoEmboss::draw_style_list() {
if (!m_style_manager.is_active_font()) return;
const EmbossStyle *stored_style = nullptr;
const StyleManager::Style *stored_style = nullptr;
bool is_stored = m_style_manager.exist_stored_style();
if (is_stored)
stored_style = m_style_manager.get_stored_style();
const EmbossStyle &actual_style = m_style_manager.get_style();
bool is_changed = (stored_style)? !(*stored_style == actual_style) : true;
const StyleManager::Style &current_style = m_style_manager.get_style();
bool is_changed = (stored_style)? !(*stored_style == current_style) : true;
bool is_modified = is_stored && is_changed;
const float &max_style_name_width = m_gui_cfg->max_style_name_width;
std::string &trunc_name = m_style_manager.get_truncated_name();
if (trunc_name.empty()) {
// generate trunc name
std::string current_name = actual_style.name;
std::string current_name = current_style.name;
ImGuiWrapper::escape_double_hash(current_name);
trunc_name = ImGuiWrapper::trunc(current_name, max_style_name_width);
}
@ -2140,19 +2183,19 @@ void GLGizmoEmboss::draw_style_list() {
m_style_manager.init_style_images(m_gui_cfg->max_style_image_size, m_text);
m_style_manager.init_trunc_names(max_style_name_width);
std::optional<std::pair<size_t,size_t>> swap_indexes;
const std::vector<StyleManager::Item> &styles = m_style_manager.get_styles();
for (const auto &item : styles) {
size_t index = &item - &styles.front();
const EmbossStyle &style = item.style;
const StyleManager::Styles &styles = m_style_manager.get_styles();
for (const StyleManager::Style &style : styles) {
size_t index = &style - &styles.front();
const std::string &actual_style_name = style.name;
ImGui::PushID(actual_style_name.c_str());
bool is_selected = (index == m_style_manager.get_style_index());
ImVec2 select_size(0,m_gui_cfg->max_style_image_size.y()); // 0,0 --> calculate in draw
const std::optional<StyleManager::StyleImage> &img = item.image;
float select_height = static_cast<float>(m_gui_cfg->max_style_image_size.y());
ImVec2 select_size(0.f, select_height); // 0,0 --> calculate in draw
const std::optional<StyleManager::StyleImage> &img = style.image;
// allow click delete button
ImGuiSelectableFlags_ flags = ImGuiSelectableFlags_AllowItemOverlap;
if (ImGui::Selectable(item.truncated_name.c_str(), is_selected, flags, select_size)) {
if (ImGui::Selectable(style.truncated_name.c_str(), is_selected, flags, select_size)) {
selected_style_index = index;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", actual_style_name.c_str());
@ -2184,10 +2227,10 @@ void GLGizmoEmboss::draw_style_list() {
// do not keep in memory style images when no combo box open
m_style_manager.free_style_images();
if (ImGui::IsItemHovered()) {
std::string style_name = add_text_modify(actual_style.name);
std::string style_name = add_text_modify(current_style.name);
std::string tooltip = is_modified?
GUI::format(_L("Modified style \"%1%\""), actual_style.name):
GUI::format(_L("Current style is \"%1%\""), actual_style.name);
GUI::format(_L("Modified style \"%1%\""), current_style.name):
GUI::format(_L("Current style is \"%1%\""), current_style.name);
ImGui::SetTooltip(" %s", tooltip.c_str());
}
}
@ -2195,7 +2238,7 @@ void GLGizmoEmboss::draw_style_list() {
// Check whether user wants lose actual style modification
if (selected_style_index.has_value() && is_modified) {
wxString title = _L("Style modification will be lost.");
const EmbossStyle &style = m_style_manager.get_styles()[*selected_style_index].style;
const EmbossStyle &style = m_style_manager.get_styles()[*selected_style_index];
wxString message = GUI::format_wxstr(_L("Changing style to '%1%' will discard current style modification.\n\n Would you like to continue anyway?"), style.name);
MessageDialog not_loaded_style_message(nullptr, message, title, wxICON_WARNING | wxYES|wxNO);
if (not_loaded_style_message.ShowModal() != wxID_YES)
@ -2204,12 +2247,12 @@ void GLGizmoEmboss::draw_style_list() {
// selected style from combo box
if (selected_style_index.has_value()) {
const EmbossStyle &style = m_style_manager.get_styles()[*selected_style_index].style;
const StyleManager::Style &style = m_style_manager.get_styles()[*selected_style_index];
// create copy to be able do fix transformation only when successfully load style
FontProp act_prop = actual_style.prop; // copy
FontProp new_prop = style.prop; // copy
StyleManager::Style cur_s = current_style; // copy
StyleManager::Style new_s = style; // copy
if (m_style_manager.load_style(*selected_style_index)) {
fix_transformation(act_prop, new_prop);
::fix_transformation(cur_s, new_s, m_parent);
process();
} else {
wxString title = _L("Not valid style.");
@ -2667,11 +2710,10 @@ void GLGizmoEmboss::draw_advanced()
return;
}
FontProp &font_prop = m_style_manager.get_style().prop;
const auto &cn = m_style_manager.get_font_prop().collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
const auto &font_info = ff.font_file->infos[font_index];
StyleManager::Style &current_style = m_style_manager.get_style();
FontProp &current_prop = current_style.prop;
const FontFile::Info &font_info = ff.font_file->infos[current_prop.collection_number.value_or(0)];
#ifdef SHOW_FONT_FILE_PROPERTY
ImGui::SameLine();
int cache_size = ff.has_value()? (int)ff.cache->size() : 0;
@ -2682,8 +2724,8 @@ void GLGizmoEmboss::draw_advanced()
", unitPerEm=" + std::to_string(font_info.unit_per_em) +
", cache(" + std::to_string(cache_size) + " glyphs)";
if (font_file->infos.size() > 1) {
unsigned int collection = font_prop.collection_number.has_value() ?
*font_prop.collection_number : 0;
unsigned int collection = current_prop.collection_number.has_value() ?
*current_prop.collection_number : 0;
ff_property += ", collect=" + std::to_string(collection+1) + "/" + std::to_string(font_file->infos.size());
}
m_imgui->text_colored(ImGuiWrapper::COL_GREY_DARK, ff_property);
@ -2692,26 +2734,22 @@ void GLGizmoEmboss::draw_advanced()
bool exist_change = false;
auto &tr = m_gui_cfg->translations;
const EmbossStyle *stored_style = nullptr;
const StyleManager::Style *stored_style = nullptr;
if (m_style_manager.exist_stored_style())
stored_style = m_style_manager.get_stored_style();
bool can_use_surface = (m_volume==nullptr)? false :
(font_prop.use_surface)? true : // already used surface must have option to uncheck
(m_volume->get_object()->volumes.size() > 1);
bool can_use_surface = (m_volume == nullptr)? false :
(m_volume->emboss_shape->projection.use_surface)? true : // already used surface must have option to uncheck
!m_volume->is_the_only_one_part();
m_imgui->disabled_begin(!can_use_surface);
const bool *def_use_surface = stored_style ?
&stored_style->prop.use_surface : nullptr;
if (rev_checkbox(tr.use_surface, font_prop.use_surface, def_use_surface,
&stored_style->projection.use_surface : nullptr;
bool &use_surface = current_style.projection.use_surface;
if (rev_checkbox(tr.use_surface, use_surface, def_use_surface,
_u8L("Revert using of model surface."))) {
if (font_prop.use_surface) {
if (use_surface)
// when using surface distance is not used
font_prop.distance.reset();
// there should be minimal embossing depth
if (font_prop.emboss < 0.1)
font_prop.emboss = 1;
}
current_style.distance.reset();
process();
}
m_imgui->disabled_end(); // !can_use_surface
@ -2724,13 +2762,14 @@ void GLGizmoEmboss::draw_advanced()
&stored_style->prop.char_gap : nullptr;
int half_ascent = font_info.ascent / 2;
int min_char_gap = -half_ascent, max_char_gap = half_ascent;
if (rev_slider(tr.char_gap, font_prop.char_gap, def_char_gap, _u8L("Revert gap between letters"),
int min_char_gap = -half_ascent;
int max_char_gap = half_ascent;
if (rev_slider(tr.char_gap, current_prop.char_gap, def_char_gap, _u8L("Revert gap between letters"),
min_char_gap, max_char_gap, units_fmt, _L("Distance between letters"))){
// Condition prevent recalculation when insertint out of limits value by imgui input
if (!apply(font_prop.char_gap, priv::limits.char_gap) ||
!m_volume->text_configuration->style.prop.char_gap.has_value() ||
m_volume->text_configuration->style.prop.char_gap != font_prop.char_gap) {
const std::optional<int> &volume_char_gap = m_volume->text_configuration->style.prop.char_gap;
if (!apply(current_prop.char_gap, priv::limits.char_gap) ||
!volume_char_gap.has_value() || volume_char_gap != current_prop.char_gap) {
// char gap is stored inside of imgui font atlas
m_style_manager.clear_imgui_font();
exist_change = true;
@ -2740,13 +2779,14 @@ void GLGizmoEmboss::draw_advanced()
// input gap between lines
auto def_line_gap = stored_style ?
&stored_style->prop.line_gap : nullptr;
int min_line_gap = -half_ascent, max_line_gap = half_ascent;
if (rev_slider(tr.line_gap, font_prop.line_gap, def_line_gap, _u8L("Revert gap between lines"),
int min_line_gap = -half_ascent;
int max_line_gap = half_ascent;
if (rev_slider(tr.line_gap, current_prop.line_gap, def_line_gap, _u8L("Revert gap between lines"),
min_line_gap, max_line_gap, units_fmt, _L("Distance between lines"))){
// Condition prevent recalculation when insertint out of limits value by imgui input
if (!apply(font_prop.line_gap, priv::limits.line_gap) ||
!m_volume->text_configuration->style.prop.line_gap.has_value() ||
m_volume->text_configuration->style.prop.line_gap != font_prop.line_gap) {
const std::optional<int> &volume_line_gap = m_volume->text_configuration->style.prop.line_gap;
if (!apply(current_prop.line_gap, priv::limits.line_gap) ||
!volume_line_gap.has_value() || volume_line_gap != current_prop.line_gap) {
// line gap is planed to be stored inside of imgui font atlas
m_style_manager.clear_imgui_font();
exist_change = true;
@ -2756,37 +2796,34 @@ void GLGizmoEmboss::draw_advanced()
// input boldness
auto def_boldness = stored_style ?
&stored_style->prop.boldness : nullptr;
if (rev_slider(tr.boldness, font_prop.boldness, def_boldness, _u8L("Undo boldness"),
if (rev_slider(tr.boldness, current_prop.boldness, def_boldness, _u8L("Undo boldness"),
priv::limits.boldness.gui.min, priv::limits.boldness.gui.max, units_fmt, _L("Tiny / Wide glyphs"))){
if (!apply(font_prop.boldness, priv::limits.boldness.values) ||
!m_volume->text_configuration->style.prop.boldness.has_value() ||
m_volume->text_configuration->style.prop.boldness != font_prop.boldness)
const std::optional<float> &volume_boldness = m_volume->text_configuration->style.prop.boldness;
if (!apply(current_prop.boldness, priv::limits.boldness.values) ||
!volume_boldness.has_value() || volume_boldness != current_prop.boldness)
exist_change = true;
}
// input italic
auto def_skew = stored_style ?
&stored_style->prop.skew : nullptr;
if (rev_slider(tr.skew_ration, font_prop.skew, def_skew, _u8L("Undo letter's skew"),
if (rev_slider(tr.skew_ration, current_prop.skew, def_skew, _u8L("Undo letter's skew"),
priv::limits.skew.gui.min, priv::limits.skew.gui.max, "%.2f", _L("Italic strength ratio"))){
if (!apply(font_prop.skew, priv::limits.skew.values) ||
!m_volume->text_configuration->style.prop.skew.has_value() ||
m_volume->text_configuration->style.prop.skew != font_prop.skew)
const std::optional<float> &volume_skew = m_volume->text_configuration->style.prop.skew;
if (!apply(current_prop.skew, priv::limits.skew.values) ||
!volume_skew.has_value() ||volume_skew != current_prop.skew)
exist_change = true;
}
// input surface distance
bool allowe_surface_distance =
!m_volume->text_configuration->style.prop.use_surface &&
!m_volume->is_the_only_one_part();
std::optional<float> &distance = font_prop.distance;
float prev_distance = distance.has_value() ? *distance : .0f,
min_distance = -2 * font_prop.emboss,
max_distance = 2 * font_prop.emboss;
bool allowe_surface_distance = !use_surface && !m_volume->is_the_only_one_part();
std::optional<float> &distance = current_style.distance;
float prev_distance = distance.value_or(.0f);
float min_distance = static_cast<float>(-2 * current_style.projection.depth);
float max_distance = static_cast<float>(2 * current_style.projection.depth);
auto def_distance = stored_style ?
&stored_style->prop.distance : nullptr;
m_imgui->disabled_begin(!allowe_surface_distance);
&stored_style->distance : nullptr;
m_imgui->disabled_begin(!allowe_surface_distance);
bool use_inch = wxGetApp().app_config->get_bool("use_inches");
const std::string undo_move_tooltip = _u8L("Undo translation");
const wxString move_tooltip = _L("Distance of the center of text from model surface");
@ -2803,9 +2840,9 @@ void GLGizmoEmboss::draw_advanced()
max_distance *= ObjectManipulation::mm_to_in;
if (rev_slider(tr.from_surface, distance_inch, def_distance, undo_move_tooltip, min_distance, max_distance, "%.3f in", move_tooltip)) {
if (distance_inch.has_value()) {
font_prop.distance = *distance_inch * ObjectManipulation::in_to_mm;
distance = *distance_inch * ObjectManipulation::in_to_mm;
} else {
font_prop.distance.reset();
distance.reset();
}
is_moved = true;
}
@ -2814,23 +2851,19 @@ void GLGizmoEmboss::draw_advanced()
min_distance, max_distance, "%.2f mm", move_tooltip)) is_moved = true;
}
if (is_moved){
m_volume->text_configuration->style.prop.distance = font_prop.distance;
float act_distance = font_prop.distance.has_value() ? *font_prop.distance : .0f;
do_local_z_move(m_parent, act_distance - prev_distance);
}
m_imgui->disabled_end();
if (is_moved)
do_local_z_move(m_parent, distance.value_or(.0f) - prev_distance);
m_imgui->disabled_end(); // allowe_surface_distance
// slider for Clock-wise angle in degress
// stored angle is optional CCW and in radians
// Convert stored value to degress
// minus create clock-wise roation from CCW
const std::optional<float> &angle_opt = m_style_manager.get_font_prop().angle;
float angle = angle_opt.has_value() ? *angle_opt: 0.f;
float angle = current_style.angle.value_or(0.f);
float angle_deg = static_cast<float>(-angle * 180 / M_PI);
float def_angle_deg_val =
(!stored_style || !stored_style->prop.angle.has_value()) ?
0.f : (*stored_style->prop.angle * -180 / M_PI);
(!stored_style || !stored_style->angle.has_value()) ?
0.f : (*stored_style->angle * -180 / M_PI);
float* def_angle_deg = stored_style ?
&def_angle_deg_val : nullptr;
if (rev_slider(tr.rotation, angle_deg, def_angle_deg, _u8L("Undo rotation"),
@ -2848,21 +2881,16 @@ void GLGizmoEmboss::draw_advanced()
assert(gl_volume != nullptr);
assert(m_style_manager.is_active_font());
if (m_style_manager.is_active_font() && gl_volume != nullptr)
m_style_manager.get_font_prop().angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit);
m_style_manager.get_style().angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit);
// recalculate for surface cut
if (font_prop.use_surface)
if (use_surface)
process();
}
ImGui::Text("%s", tr.keep_up.c_str());
ImGui::SameLine(m_gui_cfg->advanced_input_offset);
if (ImGui::Checkbox("##keep_up", &m_keep_up)) {
if (m_keep_up) {
// copy angle to volume
m_volume->text_configuration->style.prop.angle = font_prop.angle;
}
}
ImGui::Checkbox("##keep_up", &m_keep_up);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("Keep text orientation during surface dragging.\nNot stable between horizontal and vertical alignment.").c_str());
@ -2871,15 +2899,15 @@ void GLGizmoEmboss::draw_advanced()
ImGui::Text("%s", tr.collection.c_str());
ImGui::SameLine(m_gui_cfg->advanced_input_offset);
ImGui::SetNextItemWidth(m_gui_cfg->input_width);
unsigned int selected = font_prop.collection_number.has_value() ?
*font_prop.collection_number : 0;
unsigned int selected = current_prop.collection_number.has_value() ?
*current_prop.collection_number : 0;
if (ImGui::BeginCombo("## Font collection", std::to_string(selected).c_str())) {
for (unsigned int i = 0; i < ff.font_file->infos.size(); ++i) {
ImGui::PushID(1 << (10 + i));
bool is_selected = (i == selected);
if (ImGui::Selectable(std::to_string(i).c_str(), is_selected)) {
if (i == 0) font_prop.collection_number.reset();
else font_prop.collection_number = i;
if (i == 0) current_prop.collection_number.reset();
else current_prop.collection_number = i;
exist_change = true;
}
ImGui::PopID();
@ -2897,25 +2925,24 @@ void GLGizmoEmboss::draw_advanced()
if (ImGui::Button(_u8L("Set text to face camera").c_str())) {
assert(get_selected_volume(m_parent.get_selection()) == m_volume);
const Camera &cam = wxGetApp().plater()->get_camera();
bool use_surface = m_style_manager.get_style().prop.use_surface;
const Camera &cam = wxGetApp().plater()->get_camera();
if (face_selected_volume_to_camera(cam, m_parent) && use_surface)
process();
} else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s", _u8L("Use camera direction for text orientation").c_str());
}
#ifdef ALLOW_DEBUG_MODE
ImGui::Text("family = %s", (font_prop.family.has_value() ?
font_prop.family->c_str() :
ImGui::Text("family = %s", (current_prop.family.has_value() ?
current_prop.family->c_str() :
" --- "));
ImGui::Text("face name = %s", (font_prop.face_name.has_value() ?
font_prop.face_name->c_str() :
ImGui::Text("face name = %s", (current_prop.face_name.has_value() ?
current_prop.face_name->c_str() :
" --- "));
ImGui::Text("style = %s",
(font_prop.style.has_value() ? font_prop.style->c_str() :
(current_prop.style.has_value() ? current_prop.style->c_str() :
" --- "));
ImGui::Text("weight = %s", (font_prop.weight.has_value() ?
font_prop.weight->c_str() :
ImGui::Text("weight = %s", (current_prop.weight.has_value() ?
current_prop.weight->c_str() :
" --- "));
std::string descriptor = style.path;
@ -2961,7 +2988,7 @@ bool GLGizmoEmboss::choose_font_by_wxdialog()
data.RestrictSelection(wxFONTRESTRICT_SCALABLE);
// set previous selected font
EmbossStyle &selected_style = m_style_manager.get_style();
if (selected_style.type == WxFontUtils::get_actual_type()) {
if (selected_style.type == WxFontUtils::get_current_type()) {
std::optional<wxFont> selected_font = WxFontUtils::load_wxFont(
selected_style.path);
if (selected_font.has_value()) data.SetInitialFont(*selected_font);
@ -3179,9 +3206,9 @@ priv::TextDataBase::TextDataBase(DataBase &&parent, const FontFileWithCache &fon
// partialy fill shape from text configuration
const FontProp &fp = this->text_configuration.style.prop;
shape.depth = fp.emboss;
shape.use_surface = fp.use_surface;
shape.distance = fp.distance;
EmbossProjection &p = shape.projection;
p.depth = fp.emboss;
p.use_surface = fp.use_surface;
const FontFile &ff = *this->font_file.font_file;
shape.scale = get_text_shape_scale(fp, ff);
@ -3219,8 +3246,8 @@ void priv::TextDataBase::write(ModelVolume &volume) const
volume.text_configuration->style.prop.angle.reset();
// only temporary solution
volume.text_configuration->style.prop.use_surface = shape.use_surface;
volume.text_configuration->style.prop.distance = shape.distance;
volume.text_configuration->style.prop.use_surface = shape.projection.use_surface; // copy
volume.text_configuration->style.prop.emboss = static_cast<float>(shape.projection.depth); // copy
DataBase::write(volume);
}

View File

@ -32,7 +32,7 @@ namespace Slic3r::GUI {
class GLGizmoEmboss : public GLGizmoBase
{
public:
GLGizmoEmboss(GLCanvas3D& parent);
explicit GLGizmoEmboss(GLCanvas3D& parent);
/// <summary>
/// Create new embossed text volume by type on position of mouse
@ -51,8 +51,8 @@ protected:
bool on_init() override;
std::string on_get_name() const override;
void on_render() override;
virtual void on_register_raycasters_for_picking() override;
virtual void on_unregister_raycasters_for_picking() override;
void on_register_raycasters_for_picking() override;
void on_unregister_raycasters_for_picking() override;
void on_render_input_window(float x, float y, float bottom_limit) override;
bool on_is_activable() const override { return true; }
bool on_is_selectable() const override { return false; }
@ -90,7 +90,6 @@ private:
void draw_window();
void draw_text_input();
void draw_model_type();
void fix_transformation(const FontProp &from, const FontProp &to);
void draw_style_list();
void draw_delete_style_button();
void draw_style_rename_popup();
@ -308,8 +307,8 @@ private:
// Keep data about dragging only during drag&drop
std::optional<SurfaceDrag> m_surface_drag;
// TODO: it should be accessible by other gizmo too.
// May be move to plater?
// Keep old scene triangle data in AABB trees,
// all the time it need actualize before use.
RaycastManager m_raycast_manager;
// For text on scaled objects

View File

@ -398,7 +398,7 @@ void GLGizmoSVG::on_stop_dragging()
// recalculate for surface cut
if (m_volume != nullptr &&
m_volume->emboss_shape.has_value() &&
m_volume->emboss_shape->use_surface)
m_volume->emboss_shape->projection.use_surface)
process();
}
void GLGizmoSVG::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); }
@ -710,9 +710,8 @@ ExPolygons priv::default_shape() {
EmbossShape priv::select_shape() {
EmbossShape shape;
shape.depth = 10.;
shape.distance = 0;
shape.use_surface = false;
shape.projection.depth = 10.;
shape.projection.use_surface = false;
shape.svg_file_path = choose_svg_file();
if (shape.svg_file_path.empty())

View File

@ -825,10 +825,10 @@ void update_volume(TriangleMesh &&mesh, const DataUpdate &data, const Transform3
volume->set_transformation(*tr);
} else {
// apply fix matrix made by store to .3mf
const auto &tc = volume->text_configuration;
assert(tc.has_value());
if (tc.has_value() && tc->fix_3mf_tr.has_value())
volume->set_transformation(volume->get_matrix() * tc->fix_3mf_tr->inverse());
const std::optional<EmbossShape> &emboss_shape = volume->emboss_shape;
assert(emboss_shape.has_value());
if (emboss_shape.has_value() && emboss_shape->fix_3mf_tr.has_value())
volume->set_transformation(volume->get_matrix() * emboss_shape->fix_3mf_tr->inverse());
}
UpdateJob::update_volume(volume, std::move(mesh), *data.base);

View File

@ -37,8 +37,8 @@ class DataBase
public:
DataBase(const std::string& volume_name, std::shared_ptr<std::atomic<bool>> cancel) : volume_name(volume_name), cancel(std::move(cancel)) {}
DataBase(const std::string& volume_name, std::shared_ptr<std::atomic<bool>> cancel, EmbossShape&& shape)
: volume_name(volume_name), cancel(std::move(cancel)), shape(std::move(shape))
{}
: volume_name(volume_name), cancel(std::move(cancel)), shape(std::move(shape)){}
DataBase(DataBase &&) = default;
virtual ~DataBase() = default;
/// <summary>

View File

@ -93,25 +93,28 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
// detect start text dragging
if (mouse_event.LeftDown()) {
// selected volume
GLVolume *gl_volume = get_selected_gl_volume(canvas);
if (gl_volume == nullptr)
GLVolume *gl_volume_ptr = get_selected_gl_volume(canvas);
if (gl_volume_ptr == nullptr)
return false;
const GLVolume &gl_volume = *gl_volume_ptr;
// is selected volume closest hovered?
const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes;
int hovered_idx = canvas.get_first_hover_volume_idx();
if (hovered_idx < 0 ||
hovered_idx >= gl_volumes.size() ||
gl_volumes[hovered_idx] != gl_volume)
const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes;
if (int hovered_idx = canvas.get_first_hover_volume_idx();
hovered_idx < 0)
return false;
else if (auto hovered_idx_ = static_cast<size_t>(hovered_idx);
hovered_idx_ >= gl_volumes.size() ||
gl_volumes[hovered_idx_] != gl_volume_ptr)
return false;
const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects);
const ModelObject *object = get_model_object(gl_volume, canvas.get_model()->objects);
assert(object != nullptr);
if (object == nullptr)
return false;
const ModelInstance *instance = get_model_instance(*gl_volume, *object);
const ModelVolume *volume = get_model_volume(*gl_volume, *object);
const ModelInstance *instance = get_model_instance(gl_volume, *object);
const ModelVolume *volume = get_model_volume(gl_volume, *object);
assert(instance != nullptr && volume != nullptr);
if (object == nullptr || instance == nullptr || volume == nullptr)
return false;
@ -124,7 +127,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
std::vector<size_t> allowed_volumes_id;
if (volumes.size() > 1) {
allowed_volumes_id.reserve(volumes.size() - 1);
for (auto &v : volumes) {
for (const ModelVolume *v : volumes) {
// skip actual selected object
if (v->id() == volume->id())
continue;
@ -146,7 +149,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
Vec2d mouse_pos = mouse_coord.cast<double>();
Vec2d mouse_offset = calc_screen_offset_to_volume_center(mouse_pos, *volume, camera);
Transform3d volume_tr = gl_volume->get_volume_transformation().get_matrix();
Transform3d volume_tr = gl_volume.get_volume_transformation().get_matrix();
if (volume->text_configuration.has_value()) {
const TextConfiguration &tc = *volume->text_configuration;
@ -161,7 +164,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
std::optional<float> start_angle;
if (up_limit.has_value())
start_angle = Emboss::calc_up(world_tr, *up_limit);
surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume, condition, start_angle};
surface_drag = SurfaceDrag{mouse_offset, world_tr, instance_tr_inv, gl_volume_ptr, condition, start_angle};
// disable moving with object by mouse
canvas.enable_moving(false);

View File

@ -14,13 +14,9 @@ using namespace Slic3r;
using namespace Slic3r::Emboss;
using namespace Slic3r::GUI::Emboss;
StyleManager::StyleManager(const ImWchar *language_glyph_range, std::function<EmbossStyles()> create_default_styles)
: m_imgui_init_glyph_range(language_glyph_range)
, m_create_default_styles(create_default_styles)
, m_exist_style_images(false)
, m_temp_style_images(nullptr)
, m_app_config(nullptr)
, m_last_style_index(std::numeric_limits<size_t>::max())
StyleManager::StyleManager(const ImWchar *language_glyph_range, const std::function<EmbossStyles()>& create_default_styles)
: m_create_default_styles(create_default_styles)
, m_imgui_init_glyph_range(language_glyph_range)
{}
StyleManager::~StyleManager() {
@ -32,24 +28,26 @@ StyleManager::~StyleManager() {
/// For store/load emboss style to/from AppConfig
/// </summary>
namespace {
void store_style_index(AppConfig &cfg, unsigned index);
void store_style_index(AppConfig &cfg, size_t index);
::std::optional<size_t> load_style_index(const AppConfig &cfg);
EmbossStyles load_styles(const AppConfig &cfg);
void store_styles(AppConfig &cfg, const EmbossStyles &styles);
StyleManager::Styles load_styles(const AppConfig &cfg);
void store_styles(AppConfig &cfg, const StyleManager::Styles &styles);
void make_unique_name(const StyleManager::Styles &styles, std::string &name);
} // namespace
void StyleManager::init(AppConfig *app_config)
{
m_app_config = app_config;
EmbossStyles styles = (app_config != nullptr) ?
::load_styles(*app_config) :
EmbossStyles{};
if (styles.empty())
styles = m_create_default_styles();
for (EmbossStyle &style : styles) {
make_unique_name(style.name);
m_style_items.push_back({style});
m_styles = ::load_styles(*app_config);
if (m_styles.empty()) {
// No styles loaded from ini file so use default
EmbossStyles styles = m_create_default_styles();
for (EmbossStyle &style : styles) {
::make_unique_name(m_styles, style.name);
m_styles.push_back({style});
}
}
std::optional<size_t> active_index_opt = (app_config != nullptr) ?
@ -58,14 +56,14 @@ void StyleManager::init(AppConfig *app_config)
size_t active_index = 0;
if (active_index_opt.has_value()) active_index = *active_index_opt;
if (active_index >= m_style_items.size()) active_index = 0;
if (active_index >= m_styles.size()) active_index = 0;
// find valid font item
if (load_style(active_index))
return; // style is loaded
// Try to fix that style can't be loaded
m_style_items.erase(m_style_items.begin() + active_index);
m_styles.erase(m_styles.begin() + active_index);
load_valid_style();
}
@ -77,14 +75,14 @@ bool StyleManager::store_styles_to_app_config(bool use_modification, bool store_
if (use_modification) {
if (exist_stored_style()) {
// update stored item
m_style_items[m_style_cache.style_index].style = m_style_cache.style;
m_styles[m_style_cache.style_index] = m_style_cache.style;
} else {
// add new into stored list
EmbossStyle &style = m_style_cache.style;
make_unique_name(style.name);
::make_unique_name(m_styles, style.name);
m_style_cache.truncated_name.clear();
m_style_cache.style_index = m_style_items.size();
m_style_items.push_back({style});
m_style_cache.style_index = m_styles.size();
m_styles.push_back({style});
}
m_style_cache.stored_wx_font = m_style_cache.wx_font;
}
@ -97,27 +95,24 @@ bool StyleManager::store_styles_to_app_config(bool use_modification, bool store_
store_style_index(*m_app_config, style_index);
}
EmbossStyles styles;
styles.reserve(m_style_items.size());
for (const Item &item : m_style_items) styles.push_back(item.style);
store_styles(*m_app_config, styles);
store_styles(*m_app_config, m_styles);
return true;
}
void StyleManager::add_style(const std::string &name) {
EmbossStyle& style = m_style_cache.style;
style.name = name;
make_unique_name(style.name);
m_style_cache.style_index = m_style_items.size();
::make_unique_name(m_styles, style.name);
m_style_cache.style_index = m_styles.size();
m_style_cache.stored_wx_font = m_style_cache.wx_font;
m_style_cache.truncated_name.clear();
m_style_items.push_back({style});
m_styles.push_back({style});
}
void StyleManager::swap(size_t i1, size_t i2) {
if (i1 >= m_style_items.size() ||
i2 >= m_style_items.size()) return;
std::swap(m_style_items[i1], m_style_items[i2]);
if (i1 >= m_styles.size() ||
i2 >= m_styles.size()) return;
std::swap(m_styles[i1], m_styles[i2]);
// fix selected index
if (!exist_stored_style()) return;
if (m_style_cache.style_index == i1) {
@ -141,7 +136,7 @@ void StyleManager::discard_style_changes() {
}
void StyleManager::erase(size_t index) {
if (index >= m_style_items.size()) return;
if (index >= m_styles.size()) return;
// fix selected index
if (exist_stored_style()) {
@ -150,15 +145,15 @@ void StyleManager::erase(size_t index) {
else if (index == i) i = std::numeric_limits<size_t>::max();
}
m_style_items.erase(m_style_items.begin() + index);
m_styles.erase(m_styles.begin() + index);
}
void StyleManager::rename(const std::string& name) {
m_style_cache.style.name = name;
m_style_cache.truncated_name.clear();
if (exist_stored_style()) {
Item &it = m_style_items[m_style_cache.style_index];
it.style.name = name;
Style &it = m_styles[m_style_cache.style_index];
it.name = name;
it.truncated_name.clear();
}
}
@ -166,28 +161,28 @@ void StyleManager::rename(const std::string& name) {
void StyleManager::load_valid_style()
{
// iterate over all known styles
while (!m_style_items.empty()) {
while (!m_styles.empty()) {
if (load_style(0))
return;
// can't load so erase it from list
m_style_items.erase(m_style_items.begin());
m_styles.erase(m_styles.begin());
}
// no one style is loadable
// set up default font list
EmbossStyles def_style = m_create_default_styles();
for (EmbossStyle &style : def_style) {
make_unique_name(style.name);
m_style_items.push_back({std::move(style)});
::make_unique_name(m_styles, style.name);
m_styles.push_back({std::move(style)});
}
// iterate over default styles
// There have to be option to use build in font
while (!m_style_items.empty()) {
while (!m_styles.empty()) {
if (load_style(0))
return;
// can't load so erase it from list
m_style_items.erase(m_style_items.begin());
m_styles.erase(m_styles.begin());
}
// This OS doesn't have TTF as default font,
@ -197,15 +192,15 @@ void StyleManager::load_valid_style()
bool StyleManager::load_style(size_t style_index)
{
if (style_index >= m_style_items.size()) return false;
if (!load_style(m_style_items[style_index].style)) return false;
if (style_index >= m_styles.size()) return false;
if (!load_style(m_styles[style_index])) return false;
m_style_cache.style_index = style_index;
m_style_cache.stored_wx_font = m_style_cache.wx_font; // copy
m_last_style_index = style_index;
return true;
}
bool StyleManager::load_style(const EmbossStyle &style) {
bool StyleManager::load_style(const Style &style) {
if (style.type == EmbossStyle::Type::file_path) {
std::unique_ptr<FontFile> font_ptr =
create_font_file(style.path.c_str());
@ -218,13 +213,13 @@ bool StyleManager::load_style(const EmbossStyle &style) {
m_style_cache.stored_wx_font = {};
return true;
}
if (style.type != WxFontUtils::get_actual_type()) return false;
if (style.type != WxFontUtils::get_current_type()) return false;
std::optional<wxFont> wx_font_opt = WxFontUtils::load_wxFont(style.path);
if (!wx_font_opt.has_value()) return false;
return load_style(style, *wx_font_opt);
}
bool StyleManager::load_style(const EmbossStyle &style, const wxFont &font)
bool StyleManager::load_style(const Style &style, const wxFont &font)
{
m_style_cache.style = style; // copy
@ -275,12 +270,19 @@ bool StyleManager::is_font_changed() const
return is_bold != is_stored_bold;
}
bool StyleManager::is_unique_style_name(const std::string &name) const {
for (const StyleManager::Style &style : m_styles)
if (style.name == name)
return false;
return true;
}
bool StyleManager::is_active_font() { return m_style_cache.font_file.has_value(); }
const EmbossStyle* StyleManager::get_stored_style() const
const StyleManager::Style *StyleManager::get_stored_style() const
{
if (m_style_cache.style_index >= m_style_items.size()) return nullptr;
return &m_style_items[m_style_cache.style_index].style;
if (m_style_cache.style_index >= m_styles.size()) return nullptr;
return &m_styles[m_style_cache.style_index];
}
void StyleManager::clear_glyphs_cache()
@ -308,44 +310,11 @@ ImFont *StyleManager::get_imgui_font()
return font;
}
const std::vector<StyleManager::Item> &StyleManager::get_styles() const{ return m_style_items; }
void StyleManager::make_unique_name(std::string &name)
{
auto is_unique = [&](const std::string &name) -> bool {
for (const Item &it : m_style_items)
if (it.style.name == name) return false;
return true;
};
// Style name can't be empty so default name is set
if (name.empty()) name = "Text style";
// When name is already unique, nothing need to be changed
if (is_unique(name)) return;
// when there is previous version of style name only find number
const char *prefix = " (";
const char suffix = ')';
auto pos = name.find_last_of(prefix);
if (name.c_str()[name.size() - 1] == suffix &&
pos != std::string::npos) {
// short name by ord number
name = name.substr(0, pos);
}
int order = 1; // start with value 2 to represents same font name
std::string new_name;
do {
new_name = name + prefix + std::to_string(++order) + suffix;
} while (!is_unique(new_name));
name = new_name;
}
const StyleManager::Styles &StyleManager::get_styles() const{ return m_styles; }
void StyleManager::init_trunc_names(float max_width) {
for (auto &s : m_style_items)
for (auto &s : m_styles)
if (s.truncated_name.empty()) {
std::string name = s.style.name;
std::string name = s.name;
ImGuiWrapper::escape_double_hash(name);
s.truncated_name = ImGuiWrapper::trunc(name, max_width);
}
@ -378,9 +347,9 @@ void StyleManager::init_style_images(const Vec2i &max_size,
StyleImagesData::Item &style = m_temp_style_images->styles[index];
// find style in font list and copy to it
for (auto &it : m_style_items) {
if (it.style.name != style.text ||
!(it.style.prop == style.prop))
for (auto &it : m_styles) {
if (it.name != style.text ||
!(it.prop == style.prop))
continue;
it.image = image;
break;
@ -397,9 +366,8 @@ void StyleManager::init_style_images(const Vec2i &max_size,
// create job for init images
m_temp_style_images = std::make_shared<StyleImagesData::StyleImages>();
StyleImagesData::Items styles;
styles.reserve(m_style_items.size());
for (const Item &item : m_style_items) {
const EmbossStyle &style = item.style;
styles.reserve(m_styles.size());
for (const Style &style : m_styles) {
std::optional<wxFont> wx_font_opt = WxFontUtils::load_wxFont(style.path);
if (!wx_font_opt.has_value()) continue;
std::unique_ptr<FontFile> font_file =
@ -426,7 +394,7 @@ void StyleManager::init_style_images(const Vec2i &max_size,
void StyleManager::free_style_images() {
if (!m_exist_style_images) return;
GLuint tex_id = 0;
for (Item &it : m_style_items) {
for (Style &it : m_styles) {
if (tex_id == 0 && it.image.has_value())
tex_id = (GLuint)(intptr_t) it.image->texture_id;
it.image.reset();
@ -546,7 +514,7 @@ bool StyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr<FontFile>
FontFileWithCache(std::move(font_file));
EmbossStyle &style = m_style_cache.style;
style.type = WxFontUtils::get_actual_type();
style.type = WxFontUtils::get_current_type();
// update string path
style.path = WxFontUtils::store_wxFont(wx_font);
WxFontUtils::update_property(style.prop, wx_font);
@ -664,51 +632,55 @@ bool read(const Section &section, const std::string &key, std::optional<float> &
return true;
}
std::optional<EmbossStyle> load_style(const Section &app_cfg_section)
std::optional<StyleManager::Style> load_style(const Section &app_cfg_section)
{
auto path_it = app_cfg_section.find(APP_CONFIG_FONT_DESCRIPTOR);
if (path_it == app_cfg_section.end())
return {};
const std::string &path = path_it->second;
auto name_it = app_cfg_section.find(APP_CONFIG_FONT_NAME);
StyleManager::Style s;
EmbossProjection& ep = s.projection;
FontProp& fp = s.prop;
s.path = path_it->second;
s.type = WxFontUtils::get_current_type();
auto name_it = app_cfg_section.find(APP_CONFIG_FONT_NAME);
const std::string default_name = "font_name";
const std::string &name = (name_it == app_cfg_section.end()) ? default_name : name_it->second;
s.name = (name_it == app_cfg_section.end()) ? default_name : name_it->second;
FontProp fp;
float depth;
read(app_cfg_section, APP_CONFIG_FONT_LINE_HEIGHT, fp.size_in_mm);
read(app_cfg_section, APP_CONFIG_FONT_DEPTH, fp.emboss);
read(app_cfg_section, APP_CONFIG_FONT_USE_SURFACE, fp.use_surface);
read(app_cfg_section, APP_CONFIG_FONT_DEPTH, depth);ep.depth = depth;
read(app_cfg_section, APP_CONFIG_FONT_USE_SURFACE, ep.use_surface);
read(app_cfg_section, APP_CONFIG_FONT_BOLDNESS, fp.boldness);
read(app_cfg_section, APP_CONFIG_FONT_SKEW, fp.skew);
read(app_cfg_section, APP_CONFIG_FONT_DISTANCE, fp.distance);
read(app_cfg_section, APP_CONFIG_FONT_ANGLE, fp.angle);
read(app_cfg_section, APP_CONFIG_FONT_DISTANCE, s.distance);
read(app_cfg_section, APP_CONFIG_FONT_ANGLE, s.angle);
read(app_cfg_section, APP_CONFIG_FONT_COLLECTION, fp.collection_number);
read(app_cfg_section, APP_CONFIG_FONT_CHAR_GAP, fp.char_gap);
read(app_cfg_section, APP_CONFIG_FONT_LINE_GAP, fp.line_gap);
EmbossStyle::Type type = WxFontUtils::get_actual_type();
return EmbossStyle{name, path, type, fp};
return s;
}
void store_style(AppConfig &cfg, const EmbossStyle &fi, unsigned index)
void store_style(AppConfig &cfg, const StyleManager::Style &s, unsigned index)
{
const EmbossProjection &ep = s.projection;
Section data;
data[APP_CONFIG_FONT_NAME] = fi.name;
data[APP_CONFIG_FONT_DESCRIPTOR] = fi.path;
const FontProp &fp = fi.prop;
data[APP_CONFIG_FONT_NAME] = s.name;
data[APP_CONFIG_FONT_DESCRIPTOR] = s.path;
const FontProp &fp = s.prop;
data[APP_CONFIG_FONT_LINE_HEIGHT] = std::to_string(fp.size_in_mm);
data[APP_CONFIG_FONT_DEPTH] = std::to_string(fp.emboss);
if (fp.use_surface)
data[APP_CONFIG_FONT_DEPTH] = std::to_string(ep.depth);
if (ep.use_surface)
data[APP_CONFIG_FONT_USE_SURFACE] = "true";
if (fp.boldness.has_value())
data[APP_CONFIG_FONT_BOLDNESS] = std::to_string(*fp.boldness);
if (fp.skew.has_value())
data[APP_CONFIG_FONT_SKEW] = std::to_string(*fp.skew);
if (fp.distance.has_value())
data[APP_CONFIG_FONT_DISTANCE] = std::to_string(*fp.distance);
if (fp.angle.has_value())
data[APP_CONFIG_FONT_ANGLE] = std::to_string(*fp.angle);
if (s.distance.has_value())
data[APP_CONFIG_FONT_DISTANCE] = std::to_string(*s.distance);
if (s.angle.has_value())
data[APP_CONFIG_FONT_ANGLE] = std::to_string(*s.angle);
if (fp.collection_number.has_value())
data[APP_CONFIG_FONT_COLLECTION] = std::to_string(*fp.collection_number);
if (fp.char_gap.has_value())
@ -718,7 +690,7 @@ void store_style(AppConfig &cfg, const EmbossStyle &fi, unsigned index)
cfg.set_section(create_section_name(index), std::move(data));
}
void store_style_index(AppConfig &cfg, unsigned index)
void store_style_index(AppConfig &cfg, size_t index)
{
// store actual font index
// active font first index is +1 to correspond with section name
@ -742,29 +714,34 @@ std::optional<size_t> load_style_index(const AppConfig &cfg)
return active_font - 1;
}
EmbossStyles load_styles(const AppConfig &cfg)
::StyleManager::Styles load_styles(const AppConfig &cfg)
{
EmbossStyles result;
StyleManager::Styles result;
// human readable index inside of config starts from 1 !!
unsigned index = 1;
std::string section_name = create_section_name(index);
while (cfg.has_section(section_name)) {
std::optional<EmbossStyle> style_opt = load_style(cfg.get_section(section_name));
if (style_opt.has_value())
std::optional<StyleManager::Style> style_opt = load_style(cfg.get_section(section_name));
if (style_opt.has_value()) {
make_unique_name(result, style_opt->name);
result.emplace_back(*style_opt);
}
section_name = create_section_name(++index);
}
return result;
}
void store_styles(AppConfig &cfg, const EmbossStyles &styles)
void store_styles(AppConfig &cfg, const StyleManager::Styles &styles)
{
EmbossStyle::Type current_type = WxFontUtils::get_current_type();
// store styles
unsigned index = 1;
for (const EmbossStyle &style : styles) {
for (const StyleManager::Style &style : styles) {
// skip file paths + fonts from other OS(loaded from .3mf)
assert(style.type == WxFontUtils::get_actual_type());
// if (style_opt.type != WxFontUtils::get_actual_type()) continue;
assert(style.type == current_type);
if (style.type != current_type)
continue;
store_style(cfg, style, index);
++index;
}
@ -778,4 +755,36 @@ void store_styles(AppConfig &cfg, const EmbossStyles &styles)
}
}
void make_unique_name(const StyleManager::Styles& styles, std::string &name)
{
auto is_unique = [&styles](const std::string &name){
for (const StyleManager::Style &it : styles)
if (it.name == name) return false;
return true;
};
// Style name can't be empty so default name is set
if (name.empty()) name = "Text style";
// When name is already unique, nothing need to be changed
if (is_unique(name)) return;
// when there is previous version of style name only find number
const char *prefix = " (";
const char suffix = ')';
auto pos = name.find_last_of(prefix);
if (name.c_str()[name.size() - 1] == suffix &&
pos != std::string::npos) {
// short name by ord number
name = name.substr(0, pos);
}
int order = 1; // start with value 2 to represents same font name
std::string new_name;
do {
new_name = name + prefix + std::to_string(++order) + suffix;
} while (!is_unique(new_name));
name = new_name;
}
} // namespace

View File

@ -24,11 +24,10 @@ namespace Slic3r::GUI::Emboss {
class StyleManager
{
friend class CreateFontStyleImagesJob; // access to StyleImagesData
public:
/// <param name="language_glyph_range">Character to load for imgui when initialize imgui font</param>
/// <param name="create_default_styles">Function to create default styles</param>
StyleManager(const ImWchar *language_glyph_range, std::function<EmbossStyles()> create_default_styles);
StyleManager(const ImWchar *language_glyph_range, const std::function<EmbossStyles()>& create_default_styles);
/// <summary>
/// Release imgui font and style images from GPU
@ -59,11 +58,11 @@ public:
void add_style(const std::string& name);
/// <summary>
/// Change order of style item in m_style_items.
/// Change order of style item in m_styles.
/// Fix selected font index when (i1 || i2) == m_font_selected
/// </summary>
/// <param name="i1">First index to m_style_items</param>
/// <param name="i2">Second index to m_style_items</param>
/// <param name="i1">First index to m_styles</param>
/// <param name="i2">Second index to m_styles</param>
void swap(size_t i1, size_t i2);
/// <summary>
@ -73,7 +72,7 @@ public:
void discard_style_changes();
/// <summary>
/// Remove style from m_style_items.
/// Remove style from m_styles.
/// Fix selected font index when index is under m_font_selected
/// </summary>
/// <param name="index">Index of style to be removed</param>
@ -94,13 +93,14 @@ public:
/// Change active font
/// When font not loaded roll back activ font
/// </summary>
/// <param name="font_index">New font index(from m_style_items range)</param>
/// <param name="font_index">New font index(from m_styles range)</param>
/// <returns>True on succes. False on fail load font</returns>
bool load_style(size_t font_index);
// load font style not stored in list
bool load_style(const EmbossStyle &style);
struct Style;
bool load_style(const Style &style);
// fastering load font on index by wxFont, ignore type and descriptor
bool load_style(const EmbossStyle &style, const wxFont &font);
bool load_style(const Style &style, const wxFont &font);
// clear actual selected glyphs cache
void clear_glyphs_cache();
@ -109,10 +109,10 @@ public:
void clear_imgui_font();
// getters for private data
const EmbossStyle *get_stored_style() const;
const Style *get_stored_style() const;
const EmbossStyle &get_style() const { return m_style_cache.style; }
EmbossStyle &get_style() { return m_style_cache.style; }
const Style &get_style() const { return m_style_cache.style; }
Style &get_style() { return m_style_cache.style; }
size_t get_style_index() const { return m_style_cache.style_index; }
std::string &get_truncated_name() { return m_style_cache.truncated_name; }
const ImFontAtlas &get_atlas() const { return m_style_cache.atlas; }
@ -133,6 +133,8 @@ public:
/// <returns></returns>
bool is_font_changed() const;
bool is_unique_style_name(const std::string &name) const;
/// <summary>
/// Setter on wx_font when changed
/// </summary>
@ -167,33 +169,28 @@ public:
void init_style_images(const Vec2i& max_size, const std::string &text);
void free_style_images();
struct Item;
// access to all managed font styles
const std::vector<Item> &get_styles() const;
const std::vector<Style> &get_styles() const;
/// <summary>
/// Describe image in GPU to show settings of style
/// </summary>
struct StyleImage
{
void* texture_id = 0; // GLuint
void* texture_id = nullptr; // GLuint
BoundingBox bounding_box;
ImVec2 tex_size;
ImVec2 uv0;
ImVec2 uv1;
Point offset = Point(0, 0);
StyleImage() = default;
Point offset = Point(0, 0);
};
/// <summary>
/// All connected with one style
/// keep temporary data and caches for style
/// </summary>
struct Item
struct Style : public EmbossStyle
{
// parent Text style
EmbossStyle style;
// Define how to emboss shape
EmbossProjection projection;
@ -208,9 +205,12 @@ public:
// When not set value is zero and is not stored
std::optional<float> angle; // [in radians] form -Pi to Pi
bool operator==(const Item &other) const
bool operator==(const Style &other) const
{
return style == other.style && projection == other.projection && distance == other.distance && angle == other.angle;
return EmbossStyle::operator==(other) &&
projection == other.projection &&
distance == other.distance &&
angle == other.angle;
}
// cache for view font name with maximal width in imgui
@ -219,6 +219,7 @@ public:
// visualization of style
std::optional<StyleImage> image;
};
using Styles = std::vector<Style>;
// check if exist selected font style in manager
bool is_active_font();
@ -230,7 +231,10 @@ public:
static float get_imgui_font_size(const FontProp &prop, const Slic3r::Emboss::FontFile &file, double scale);
private:
// function to create default style list
std::function<EmbossStyles()> m_create_default_styles;
// keep language dependent glyph range
const ImWchar *m_imgui_init_glyph_range;
/// <summary>
/// Cache data from style to reduce amount of:
@ -256,22 +260,20 @@ private:
std::string truncated_name;
// actual used font item
EmbossStyle style = {};
Style style = {};
// cache for stored wx font to not create every frame
wxFont stored_wx_font = {};
// index into m_style_items
// index into m_styles
size_t style_index = std::numeric_limits<size_t>::max();
} m_style_cache;
void make_unique_name(std::string &name);
// Privat member
std::vector<Item> m_style_items;
AppConfig *m_app_config;
size_t m_last_style_index;
Styles m_styles;
AppConfig *m_app_config = nullptr;
size_t m_last_style_index = std::numeric_limits<size_t>::max();
/// <summary>
/// Keep data needed to create Font Style Images in Job
@ -310,12 +312,8 @@ private:
// pixel per milimeter (scaled DPI)
double ppm;
};
std::shared_ptr<StyleImagesData::StyleImages> m_temp_style_images;
bool m_exist_style_images;
// store all font GLImages
//ImFontAtlas m_imgui_font_atlas;
const ImWchar *m_imgui_init_glyph_range;
std::shared_ptr<StyleImagesData::StyleImages> m_temp_style_images = nullptr;
bool m_exist_style_images = false;
};
} // namespace Slic3r

View File

@ -108,7 +108,7 @@ std::unique_ptr<Emboss::FontFile> WxFontUtils::create_font_file(const wxFont &fo
#endif
}
EmbossStyle::Type WxFontUtils::get_actual_type()
EmbossStyle::Type WxFontUtils::get_current_type()
{
#ifdef _WIN32
return EmbossStyle::Type::wx_win_font_descr;
@ -125,7 +125,7 @@ EmbossStyle WxFontUtils::create_emboss_style(const wxFont &font, const std::stri
{
std::string name_item = name.empty()? get_human_readable_name(font) : name;
std::string fontDesc = store_wxFont(font);
EmbossStyle::Type type = get_actual_type();
EmbossStyle::Type type = get_current_type();
// synchronize font property with actual font
FontProp font_prop;

View File

@ -24,7 +24,7 @@ public:
// os specific load of wxFont
static std::unique_ptr<Slic3r::Emboss::FontFile> create_font_file(const wxFont &font);
static EmbossStyle::Type get_actual_type();
static EmbossStyle::Type get_current_type();
static EmbossStyle create_emboss_style(const wxFont &font, const std::string& name = "");
static std::string get_human_readable_name(const wxFont &font);